From e041c683412d5bf44dc2b109053e3b837b71742d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 27 Mar 2006 01:16:30 -0800 Subject: [PATCH] Notifier chain update: API changes The kernel's implementation of notifier chains is unsafe. There is no protection against entries being added to or removed from a chain while the chain is in use. The issues were discussed in this thread: http://marc.theaimsgroup.com/?l=linux-kernel&m=113018709002036&w=2 We noticed that notifier chains in the kernel fall into two basic usage classes: "Blocking" chains are always called from a process context and the callout routines are allowed to sleep; "Atomic" chains can be called from an atomic context and the callout routines are not allowed to sleep. We decided to codify this distinction and make it part of the API. Therefore this set of patches introduces three new, parallel APIs: one for blocking notifiers, one for atomic notifiers, and one for "raw" notifiers (which is really just the old API under a new name). New kinds of data structures are used for the heads of the chains, and new routines are defined for registration, unregistration, and calling a chain. The three APIs are explained in include/linux/notifier.h and their implementation is in kernel/sys.c. With atomic and blocking chains, the implementation guarantees that the chain links will not be corrupted and that chain callers will not get messed up by entries being added or removed. For raw chains the implementation provides no guarantees at all; users of this API must provide their own protections. (The idea was that situations may come up where the assumptions of the atomic and blocking APIs are not appropriate, so it should be possible for users to handle these things in their own way.) There are some limitations, which should not be too hard to live with. For atomic/blocking chains, registration and unregistration must always be done in a process context since the chain is protected by a mutex/rwsem. Also, a callout routine for a non-raw chain must not try to register or unregister entries on its own chain. (This did happen in a couple of places and the code had to be changed to avoid it.) Since atomic chains may be called from within an NMI handler, they cannot use spinlocks for synchronization. Instead we use RCU. The overhead falls almost entirely in the unregister routine, which is okay since unregistration is much less frequent that calling a chain. Here is the list of chains that we adjusted and their classifications. None of them use the raw API, so for the moment it is only a placeholder. ATOMIC CHAINS ------------- arch/i386/kernel/traps.c: i386die_chain arch/ia64/kernel/traps.c: ia64die_chain arch/powerpc/kernel/traps.c: powerpc_die_chain arch/sparc64/kernel/traps.c: sparc64die_chain arch/x86_64/kernel/traps.c: die_chain drivers/char/ipmi/ipmi_si_intf.c: xaction_notifier_list kernel/panic.c: panic_notifier_list kernel/profile.c: task_free_notifier net/bluetooth/hci_core.c: hci_notifier net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_chain net/ipv4/netfilter/ip_conntrack_core.c: ip_conntrack_expect_chain net/ipv6/addrconf.c: inet6addr_chain net/netfilter/nf_conntrack_core.c: nf_conntrack_chain net/netfilter/nf_conntrack_core.c: nf_conntrack_expect_chain net/netlink/af_netlink.c: netlink_chain BLOCKING CHAINS --------------- arch/powerpc/platforms/pseries/reconfig.c: pSeries_reconfig_chain arch/s390/kernel/process.c: idle_chain arch/x86_64/kernel/process.c idle_notifier drivers/base/memory.c: memory_chain drivers/cpufreq/cpufreq.c cpufreq_policy_notifier_list drivers/cpufreq/cpufreq.c cpufreq_transition_notifier_list drivers/macintosh/adb.c: adb_client_list drivers/macintosh/via-pmu.c sleep_notifier_list drivers/macintosh/via-pmu68k.c sleep_notifier_list drivers/macintosh/windfarm_core.c wf_client_list drivers/usb/core/notify.c usb_notifier_list drivers/video/fbmem.c fb_notifier_list kernel/cpu.c cpu_chain kernel/module.c module_notify_list kernel/profile.c munmap_notifier kernel/profile.c task_exit_notifier kernel/sys.c reboot_notifier_list net/core/dev.c netdev_chain net/decnet/dn_dev.c: dnaddr_chain net/ipv4/devinet.c: inetaddr_chain It's possible that some of these classifications are wrong. If they are, please let us know or submit a patch to fix them. Note that any chain that gets called very frequently should be atomic, because the rwsem read-locking used for blocking chains is very likely to incur cache misses on SMP systems. (However, if the chain's callout routines may sleep then the chain cannot be atomic.) The patch set was written by Alan Stern and Chandra Seetharaman, incorporating material written by Keith Owens and suggestions from Paul McKenney and Andrew Morton. [jes@sgi.com: restructure the notifier chain initialization macros] Signed-off-by: Alan Stern Signed-off-by: Chandra Seetharaman Signed-off-by: Jes Sorensen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/fbmem.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 07d882b14396..b1a8dca76430 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -55,7 +55,7 @@ #define FBPIXMAPSIZE (1024 * 8) -static struct notifier_block *fb_notifier_list; +static BLOCKING_NOTIFIER_HEAD(fb_notifier_list); struct fb_info *registered_fb[FB_MAX]; int num_registered_fb; @@ -784,7 +784,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) event.info = info; event.data = &mode1; - ret = notifier_call_chain(&fb_notifier_list, + ret = blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_MODE_DELETE, &event); } @@ -830,8 +830,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; - notifier_call_chain(&fb_notifier_list, evnt, - &event); + blocking_notifier_call_chain(&fb_notifier_list, + evnt, &event); } } } @@ -854,7 +854,8 @@ fb_blank(struct fb_info *info, int blank) event.info = info; event.data = ␣ - notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_BLANK, &event); } return ret; @@ -925,7 +926,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, con2fb.framebuffer = -1; event.info = info; event.data = &con2fb; - notifier_call_chain(&fb_notifier_list, + blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_GET_CONSOLE_MAP, &event); return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; @@ -944,7 +945,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EINVAL; event.info = info; event.data = &con2fb; - return notifier_call_chain(&fb_notifier_list, + return blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_SET_CONSOLE_MAP, &event); case FBIOBLANK: @@ -1324,7 +1325,7 @@ register_framebuffer(struct fb_info *fb_info) devfs_mk_cdev(MKDEV(FB_MAJOR, i), S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); event.info = fb_info; - notifier_call_chain(&fb_notifier_list, + blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_FB_REGISTERED, &event); return 0; } @@ -1366,7 +1367,7 @@ unregister_framebuffer(struct fb_info *fb_info) */ int fb_register_client(struct notifier_block *nb) { - return notifier_chain_register(&fb_notifier_list, nb); + return blocking_notifier_chain_register(&fb_notifier_list, nb); } /** @@ -1375,7 +1376,7 @@ int fb_register_client(struct notifier_block *nb) */ int fb_unregister_client(struct notifier_block *nb) { - return notifier_chain_unregister(&fb_notifier_list, nb); + return blocking_notifier_chain_unregister(&fb_notifier_list, nb); } /** @@ -1393,11 +1394,13 @@ void fb_set_suspend(struct fb_info *info, int state) event.info = info; if (state) { - notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_SUSPEND, &event); info->state = FBINFO_STATE_SUSPENDED; } else { info->state = FBINFO_STATE_RUNNING; - notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); + blocking_notifier_call_chain(&fb_notifier_list, + FB_EVENT_RESUME, &event); } } @@ -1469,7 +1472,7 @@ int fb_new_modelist(struct fb_info *info) if (!list_empty(&info->modelist)) { event.info = info; - err = notifier_call_chain(&fb_notifier_list, + err = blocking_notifier_call_chain(&fb_notifier_list, FB_EVENT_NEW_MODELIST, &event); } @@ -1495,7 +1498,7 @@ int fb_con_duit(struct fb_info *info, int event, void *data) evnt.info = info; evnt.data = data; - return notifier_call_chain(&fb_notifier_list, event, &evnt); + return blocking_notifier_call_chain(&fb_notifier_list, event, &evnt); } EXPORT_SYMBOL(fb_con_duit); -- cgit v1.2.2 From 2115aea8185c8987b7688111953322742bd8795c Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 27 Mar 2006 01:17:19 -0800 Subject: [PATCH] vgacon: fix EGA cursor resize function This corrects cursor resize on ega boards: registers are write-only, so we shouldn't even try to read them. And on ega, 31/30 produces a flat cursor. Using 31/31 is better: except with 32 pixels high fonts, it shouldn't show up. Signed-off-by: Samuel Thibault Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/console/vgacon.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 5a86978537d2..61894d5bb219 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -433,17 +433,22 @@ static void vgacon_set_cursor_size(int xpos, int from, int to) cursor_size_lastto = to; spin_lock_irqsave(&vga_lock, flags); - outb_p(0x0a, vga_video_port_reg); /* Cursor start */ - curs = inb_p(vga_video_port_val); - outb_p(0x0b, vga_video_port_reg); /* Cursor end */ - cure = inb_p(vga_video_port_val); + if (vga_video_type >= VIDEO_TYPE_VGAC) { + outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg); + curs = inb_p(vga_video_port_val); + outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg); + cure = inb_p(vga_video_port_val); + } else { + curs = 0; + cure = 0; + } curs = (curs & 0xc0) | from; cure = (cure & 0xe0) | to; - outb_p(0x0a, vga_video_port_reg); /* Cursor start */ + outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg); outb_p(curs, vga_video_port_val); - outb_p(0x0b, vga_video_port_reg); /* Cursor end */ + outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg); outb_p(cure, vga_video_port_val); spin_unlock_irqrestore(&vga_lock, flags); } @@ -455,7 +460,10 @@ static void vgacon_cursor(struct vc_data *c, int mode) switch (mode) { case CM_ERASE: write_vga(14, (c->vc_pos - vga_vram_base) / 2); - vgacon_set_cursor_size(c->vc_x, 31, 30); + if (vga_video_type >= VIDEO_TYPE_VGAC) + vgacon_set_cursor_size(c->vc_x, 31, 30); + else + vgacon_set_cursor_size(c->vc_x, 31, 31); break; case CM_MOVE: @@ -493,7 +501,10 @@ static void vgacon_cursor(struct vc_data *c, int mode) 10 ? 1 : 2)); break; case CUR_NONE: - vgacon_set_cursor_size(c->vc_x, 31, 30); + if (vga_video_type >= VIDEO_TYPE_VGAC) + vgacon_set_cursor_size(c->vc_x, 31, 30); + else + vgacon_set_cursor_size(c->vc_x, 31, 31); break; default: vgacon_set_cursor_size(c->vc_x, 1, -- cgit v1.2.2 From 15bdab959c9bb909c0317480dd9b35748a8f7887 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:20 -0800 Subject: [PATCH] vgacon: Add support for soft scrollback The scrollback buffer of the VGA console is located in VGA RAM. This RAM is fixed in size and is very small. To make the scrollback buffer larger, it must be placed instead in System RAM. This patch adds this feature. The feature and the size of the buffer are made as a kernel config option. Besides consuming kernel memory, this feature will slow down the console by approximately 20%. Signed-off-by: Antonino Daplas Signed-off-by: Jiri Slaby Cc: Jindrich Makovicka Cc: Martin Mares Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/console/Kconfig | 24 ++++ drivers/video/console/vgacon.c | 244 +++++++++++++++++++++++++++++++++-------- 2 files changed, 225 insertions(+), 43 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 6ee449858a5c..4444bef68fba 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -26,6 +26,30 @@ config VGA_CONSOLE # fi # fi +config VGACON_SOFT_SCROLLBACK + bool "Enable Scrollback Buffer in System RAM" + depends on VGA_CONSOLE + default n + help + The scrollback buffer of the standard VGA console is located in + the VGA RAM. The size of this RAM is fixed and is quite small. + If you require a larger scrollback buffer, this can be placed in + System RAM which is dynamically allocated during intialization. + Placing the scrollback buffer in System RAM will slightly slow + down the console. + + If you want this feature, say 'Y' here and enter the amount of + RAM to allocate for this buffer. If unsure, say 'N'. + +config VGACON_SOFT_SCROLLBACK_SIZE + int "Scrollback Buffer Size (in KB)" + depends on VGACON_SOFT_SCROLLBACK + default "64" + help + Enter the amount of System RAM to allocate for the scrollback + buffer. Each 64KB will give you approximately 16 80x25 + screenfuls of scrollback buffer + config VIDEO_SELECT bool "Video mode selection support" depends on X86 && VGA_CONSOLE diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 61894d5bb219..d5a04b68c4d4 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -93,7 +93,6 @@ static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity, static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); static unsigned long vgacon_uni_pagedir[2]; - /* Description of the hardware situation */ static unsigned long vga_vram_base; /* Base of video memory */ static unsigned long vga_vram_end; /* End of video memory */ @@ -161,6 +160,201 @@ static inline void write_vga(unsigned char reg, unsigned int val) spin_unlock_irqrestore(&vga_lock, flags); } +static inline void vga_set_mem_top(struct vc_data *c) +{ + write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); +} + +#ifdef CONFIG_VGACON_SOFT_SCROLLBACK +#include +/* software scrollback */ +static void *vgacon_scrollback; +static int vgacon_scrollback_tail; +static int vgacon_scrollback_size; +static int vgacon_scrollback_rows; +static int vgacon_scrollback_cnt; +static int vgacon_scrollback_cur; +static int vgacon_scrollback_save; +static int vgacon_scrollback_restore; + +static void vgacon_scrollback_init(int pitch) +{ + int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch; + + if (vgacon_scrollback) { + vgacon_scrollback_cnt = 0; + vgacon_scrollback_tail = 0; + vgacon_scrollback_cur = 0; + vgacon_scrollback_rows = rows - 1; + vgacon_scrollback_size = rows * pitch; + } +} + +static void __init vgacon_scrollback_startup(void) +{ + vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE + * 1024); + vgacon_scrollback_init(vga_video_num_columns * 2); +} + +static void vgacon_scrollback_update(struct vc_data *c, int t, int count) +{ + void *p; + + if (!vgacon_scrollback_size || c->vc_num != fg_console) + return; + + p = (void *) (c->vc_origin + t * c->vc_size_row); + + while (count--) { + scr_memcpyw(vgacon_scrollback + vgacon_scrollback_tail, + p, c->vc_size_row); + vgacon_scrollback_cnt++; + p += c->vc_size_row; + vgacon_scrollback_tail += c->vc_size_row; + + if (vgacon_scrollback_tail >= vgacon_scrollback_size) + vgacon_scrollback_tail = 0; + + if (vgacon_scrollback_cnt > vgacon_scrollback_rows) + vgacon_scrollback_cnt = vgacon_scrollback_rows; + + vgacon_scrollback_cur = vgacon_scrollback_cnt; + } +} + +static void vgacon_restore_screen(struct vc_data *c) +{ + vgacon_scrollback_save = 0; + + if (!vga_is_gfx && !vgacon_scrollback_restore) { + scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, + c->vc_screenbuf_size > vga_vram_size ? + vga_vram_size : c->vc_screenbuf_size); + vgacon_scrollback_restore = 1; + vgacon_scrollback_cur = vgacon_scrollback_cnt; + } +} + +static int vgacon_scrolldelta(struct vc_data *c, int lines) +{ + int start, end, count, soff, diff; + void *d, *s; + + if (!lines) { + c->vc_visible_origin = c->vc_origin; + vga_set_mem_top(c); + return 1; + } + + if (!vgacon_scrollback) + return 1; + + if (!vgacon_scrollback_save) { + vgacon_cursor(c, CM_ERASE); + vgacon_save_screen(c); + vgacon_scrollback_save = 1; + } + + vgacon_scrollback_restore = 0; + start = vgacon_scrollback_cur + lines; + end = start + abs(lines); + + if (start < 0) + start = 0; + + if (start > vgacon_scrollback_cnt) + start = vgacon_scrollback_cnt; + + if (end < 0) + end = 0; + + if (end > vgacon_scrollback_cnt) + end = vgacon_scrollback_cnt; + + vgacon_scrollback_cur = start; + count = end - start; + soff = vgacon_scrollback_tail - ((vgacon_scrollback_cnt - end) * + c->vc_size_row); + soff -= count * c->vc_size_row; + + if (soff < 0) + soff += vgacon_scrollback_size; + + count = vgacon_scrollback_cnt - start; + + if (count > c->vc_rows) + count = c->vc_rows; + + diff = c->vc_rows - count; + + d = (void *) c->vc_origin; + s = (void *) c->vc_screenbuf; + + while (count--) { + scr_memcpyw(d, vgacon_scrollback + soff, c->vc_size_row); + d += c->vc_size_row; + soff += c->vc_size_row; + + if (soff >= vgacon_scrollback_size) + soff = 0; + } + + if (diff == c->vc_rows) { + vgacon_cursor(c, CM_MOVE); + } else { + while (diff--) { + scr_memcpyw(d, s, c->vc_size_row); + d += c->vc_size_row; + s += c->vc_size_row; + } + } + + return 1; +} +#else +#define vgacon_scrollback_startup(...) do { } while (0) +#define vgacon_scrollback_init(...) do { } while (0) +#define vgacon_scrollback_update(...) do { } while (0) + +static void vgacon_restore_screen(struct vc_data *c) +{ + if (c->vc_origin != c->vc_visible_origin) + vgacon_scrolldelta(c, 0); +} + +static int vgacon_scrolldelta(struct vc_data *c, int lines) +{ + if (!lines) /* Turn scrollback off */ + c->vc_visible_origin = c->vc_origin; + else { + int margin = c->vc_size_row * 4; + int ul, we, p, st; + + if (vga_rolled_over > + (c->vc_scr_end - vga_vram_base) + margin) { + ul = c->vc_scr_end - vga_vram_base; + we = vga_rolled_over + c->vc_size_row; + } else { + ul = 0; + we = vga_vram_size; + } + p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + + lines * c->vc_size_row; + st = (c->vc_origin - vga_vram_base - ul + we) % we; + if (st < 2 * margin) + margin = 0; + if (p < margin) + p = 0; + if (p > st - margin) + p = st; + c->vc_visible_origin = vga_vram_base + (p + ul) % we; + } + vga_set_mem_top(c); + return 1; +} +#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ + static const char __init *vgacon_startup(void) { const char *display_desc = NULL; @@ -330,7 +524,7 @@ static const char __init *vgacon_startup(void) vgacon_xres = ORIG_VIDEO_COLS * VGA_FONTWIDTH; vgacon_yres = vga_scan_lines; - + vgacon_scrollback_startup(); return display_desc; } @@ -357,11 +551,6 @@ static void vgacon_init(struct vc_data *c, int init) con_set_default_unimap(c); } -static inline void vga_set_mem_top(struct vc_data *c) -{ - write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); -} - static void vgacon_deinit(struct vc_data *c) { /* When closing the last console, reset video origin */ @@ -455,8 +644,8 @@ static void vgacon_set_cursor_size(int xpos, int from, int to) static void vgacon_cursor(struct vc_data *c, int mode) { - if (c->vc_origin != c->vc_visible_origin) - vgacon_scrolldelta(c, 0); + vgacon_restore_screen(c); + switch (mode) { case CM_ERASE: write_vga(14, (c->vc_pos - vga_vram_base) / 2); @@ -606,6 +795,7 @@ static int vgacon_switch(struct vc_data *c) vgacon_doresize(c, c->vc_cols, c->vc_rows); } + vgacon_scrollback_init(c->vc_size_row); return 0; /* Redrawing not needed */ } @@ -1073,37 +1263,6 @@ static int vgacon_resize(struct vc_data *c, unsigned int width, return 0; } -static int vgacon_scrolldelta(struct vc_data *c, int lines) -{ - if (!lines) /* Turn scrollback off */ - c->vc_visible_origin = c->vc_origin; - else { - int margin = c->vc_size_row * 4; - int ul, we, p, st; - - if (vga_rolled_over > - (c->vc_scr_end - vga_vram_base) + margin) { - ul = c->vc_scr_end - vga_vram_base; - we = vga_rolled_over + c->vc_size_row; - } else { - ul = 0; - we = vga_vram_size; - } - p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + - lines * c->vc_size_row; - st = (c->vc_origin - vga_vram_base - ul + we) % we; - if (st < 2 * margin) - margin = 0; - if (p < margin) - p = 0; - if (p > st - margin) - p = st; - c->vc_visible_origin = vga_vram_base + (p + ul) % we; - } - vga_set_mem_top(c); - return 1; -} - static int vgacon_set_origin(struct vc_data *c) { if (vga_is_gfx || /* We don't play origin tricks in graphic modes */ @@ -1146,15 +1305,14 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, if (t || b != c->vc_rows || vga_is_gfx) return 0; - if (c->vc_origin != c->vc_visible_origin) - vgacon_scrolldelta(c, 0); - if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) return 0; + vgacon_restore_screen(c); oldo = c->vc_origin; delta = lines * c->vc_size_row; if (dir == SM_UP) { + vgacon_scrollback_update(c, t, lines); if (c->vc_scr_end + delta >= vga_vram_end) { scr_memcpyw((u16 *) vga_vram_base, (u16 *) (oldo + delta), -- cgit v1.2.2 From 7a07cd786dbd0111b9dd977e114438220cb4eee5 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:22 -0800 Subject: [PATCH] nvidiafb: add suspend and resume hooks Add suspend and resume hooks to make software suspend more reliable. Resuming from standby should generally work. Resuming from mem and from disk requires that the GPU is disabled. Adding these to the suspend script... fbset -accel false -a /* suspend here */ fbset -accel true -a ... should generally work. In addition, resuming from mem requires that the video card has to be POSTed by the BIOS or some other utility. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/nvidia/nv_accel.c | 12 +++++ drivers/video/nvidia/nv_type.h | 1 + drivers/video/nvidia/nvidia.c | 115 +++++++++++++++++++++++++++++----------- 3 files changed, 97 insertions(+), 31 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/nvidia/nv_accel.c b/drivers/video/nvidia/nv_accel.c index f377a29ec97a..4aefb8f41637 100644 --- a/drivers/video/nvidia/nv_accel.c +++ b/drivers/video/nvidia/nv_accel.c @@ -300,6 +300,9 @@ int nvidiafb_sync(struct fb_info *info) { struct nvidia_par *par = info->par; + if (info->state != FBINFO_STATE_RUNNING) + return 0; + if (!par->lockup) NVFlush(par); @@ -313,6 +316,9 @@ void nvidiafb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { struct nvidia_par *par = info->par; + if (info->state != FBINFO_STATE_RUNNING) + return; + if (par->lockup) return cfb_copyarea(info, region); @@ -329,6 +335,9 @@ void nvidiafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) struct nvidia_par *par = info->par; u32 color; + if (info->state != FBINFO_STATE_RUNNING) + return; + if (par->lockup) return cfb_fillrect(info, rect); @@ -412,6 +421,9 @@ void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image) { struct nvidia_par *par = info->par; + if (info->state != FBINFO_STATE_RUNNING) + return; + if (image->depth == 1 && !par->lockup) nvidiafb_mono_color_expand(info, image); else diff --git a/drivers/video/nvidia/nv_type.h b/drivers/video/nvidia/nv_type.h index e4a5b1da71c4..acdc26693402 100644 --- a/drivers/video/nvidia/nv_type.h +++ b/drivers/video/nvidia/nv_type.h @@ -129,6 +129,7 @@ struct nvidia_par { int fpHeight; int PanelTweak; int paneltweak; + int pm_state; u32 crtcSync_read; u32 fpSyncs; u32 dmaPut; diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index a7c4e5e8ead6..c758b386f146 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_MTRR #include #endif @@ -615,6 +616,30 @@ static int nvidia_panel_tweak(struct nvidia_par *par, return tweak; } +static void nvidia_vga_protect(struct nvidia_par *par, int on) +{ + unsigned char tmp; + + if (on) { + /* + * Turn off screen and disable sequencer. + */ + tmp = NVReadSeq(par, 0x01); + + NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */ + NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */ + } else { + /* + * Reenable sequencer, then turn on screen. + */ + + tmp = NVReadSeq(par, 0x01); + + NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */ + NVWriteSeq(par, 0x00, 0x03); /* End Reset */ + } +} + static void nvidia_save_vga(struct nvidia_par *par, struct _riva_hw_state *state) { @@ -643,9 +668,9 @@ static void nvidia_save_vga(struct nvidia_par *par, #undef DUMP_REG -static void nvidia_write_regs(struct nvidia_par *par) +static void nvidia_write_regs(struct nvidia_par *par, + struct _riva_hw_state *state) { - struct _riva_hw_state *state = &par->ModeReg; int i; NVTRACE_ENTER(); @@ -694,32 +719,6 @@ static void nvidia_write_regs(struct nvidia_par *par) NVTRACE_LEAVE(); } -static void nvidia_vga_protect(struct nvidia_par *par, int on) -{ - unsigned char tmp; - - if (on) { - /* - * Turn off screen and disable sequencer. - */ - tmp = NVReadSeq(par, 0x01); - - NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */ - NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */ - } else { - /* - * Reenable sequencer, then turn on screen. - */ - - tmp = NVReadSeq(par, 0x01); - - NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */ - NVWriteSeq(par, 0x00, 0x03); /* End Reset */ - } -} - - - static int nvidia_calc_regs(struct fb_info *info) { struct nvidia_par *par = info->par; @@ -1068,7 +1067,8 @@ static int nvidiafb_set_par(struct fb_info *info) nvidia_vga_protect(par, 1); - nvidia_write_regs(par); + nvidia_write_regs(par, &par->ModeReg); + NVSetStartAddress(par, 0); #if defined (__BIG_ENDIAN) /* turn on LFB swapping */ @@ -1377,6 +1377,57 @@ static struct fb_ops nvidia_fb_ops = { .fb_sync = nvidiafb_sync, }; +#ifdef CONFIG_PM +static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct nvidia_par *par = info->par; + + acquire_console_sem(); + par->pm_state = state.event; + + if (state.event == PM_EVENT_FREEZE) { + dev->dev.power.power_state = state; + } else { + fb_set_suspend(info, 1); + nvidiafb_blank(FB_BLANK_POWERDOWN, info); + nvidia_write_regs(par, &par->SavedReg); + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + } + + release_console_sem(); + return 0; +} + +static int nvidiafb_resume(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct nvidia_par *par = info->par; + + acquire_console_sem(); + pci_set_power_state(dev, PCI_D0); + + if (par->pm_state != PM_EVENT_FREEZE) { + pci_restore_state(dev); + pci_enable_device(dev); + pci_set_master(dev); + } + + par->pm_state = PM_EVENT_ON; + nvidiafb_set_par(info); + fb_set_suspend (info, 0); + nvidiafb_blank(FB_BLANK_UNBLANK, info); + + release_console_sem(); + return 0; +} +#else +#define nvidiafb_suspend NULL +#define nvidiafb_resume NULL +#endif + static int __devinit nvidia_set_fbinfo(struct fb_info *info) { struct fb_monspecs *specs = &info->monspecs; @@ -1798,8 +1849,10 @@ static int __devinit nvidiafb_setup(char *options) static struct pci_driver nvidiafb_driver = { .name = "nvidiafb", .id_table = nvidiafb_pci_tbl, - .probe = nvidiafb_probe, - .remove = __exit_p(nvidiafb_remove), + .probe = nvidiafb_probe, + .suspend = nvidiafb_suspend, + .resume = nvidiafb_resume, + .remove = __exit_p(nvidiafb_remove), }; /* ------------------------------------------------------------------------- * -- cgit v1.2.2 From fc4effc7a98d0d320e478d1d42bc4a8a64380150 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 27 Mar 2006 01:17:23 -0800 Subject: [PATCH] fbdev: framebuffer driver for Geode GX A framebuffer driver for the display controller in AMD Geode GX processors (Geode GX533, Geode GX500 etc.). Tested at 640x480, 800x600, 1024x768 and 1280x1024 at 8, 16, and 24 bpp with both CRT and TFT. No accelerated features currently implemented and compression remains disabled. This driver requires that the BIOS (or the SoftVG/Firmbase code in the BIOS) has created an appropriate virtual PCI header. Signed-off-by: David Vrabel Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/geode/Kconfig | 17 +- drivers/video/geode/Makefile | 4 +- drivers/video/geode/display_gx.c | 156 +++++++++++++++ drivers/video/geode/display_gx.h | 96 +++++++++ drivers/video/geode/gxfb_core.c | 423 +++++++++++++++++++++++++++++++++++++++ drivers/video/geode/video_gx.c | 262 ++++++++++++++++++++++++ drivers/video/geode/video_gx.h | 47 +++++ 7 files changed, 1003 insertions(+), 2 deletions(-) create mode 100644 drivers/video/geode/display_gx.c create mode 100644 drivers/video/geode/display_gx.h create mode 100644 drivers/video/geode/gxfb_core.c create mode 100644 drivers/video/geode/video_gx.c create mode 100644 drivers/video/geode/video_gx.h (limited to 'drivers/video') diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig index 42fb9a89a792..4e173ef20a7d 100644 --- a/drivers/video/geode/Kconfig +++ b/drivers/video/geode/Kconfig @@ -8,9 +8,24 @@ config FB_GEODE Say 'Y' here to allow you to select framebuffer drivers for the AMD Geode family of processors. +config FB_GEODE_GX + tristate "AMD Geode GX framebuffer support (EXPERIMENTAL)" + depends on FB && FB_GEODE && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for the display controller integrated into the + AMD Geode GX processors. + + To compile this driver as a module, choose M here: the module will be + called gxfb. + + If unsure, say N. + config FB_GEODE_GX1 tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)" - depends on FB_GEODE && EXPERIMENTAL + depends on FB && FB_GEODE && EXPERIMENTAL select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile index 13ad501ea990..f896565bc312 100644 --- a/drivers/video/geode/Makefile +++ b/drivers/video/geode/Makefile @@ -1,5 +1,7 @@ # Makefile for the Geode family framebuffer drivers obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o +obj-$(CONFIG_FB_GEODE_GX) += gxfb.o -gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o +gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o +gxfb-objs := gxfb_core.o display_gx.o video_gx.o diff --git a/drivers/video/geode/display_gx.c b/drivers/video/geode/display_gx.c new file mode 100644 index 000000000000..825c3405f5c2 --- /dev/null +++ b/drivers/video/geode/display_gx.c @@ -0,0 +1,156 @@ +/* + * Geode GX display controller. + * + * Copyright (C) 2005 Arcom Control Systems Ltd. + * + * Portions from AMD's original 2.4 driver: + * Copyright (C) 2004 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by * the + * Free Software Foundation; either version 2 of the License, or * (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#include "geodefb.h" +#include "display_gx.h" + +int gx_frame_buffer_size(void) +{ + /* Assuming 16 MiB. */ + return 16*1024*1024; +} + +int gx_line_delta(int xres, int bpp) +{ + /* Must be a multiple of 8 bytes. */ + return (xres * (bpp >> 3) + 7) & ~0x7; +} + +static void gx_set_mode(struct fb_info *info) +{ + struct geodefb_par *par = info->par; + u32 gcfg, dcfg; + int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal; + int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal; + + /* Unlock the display controller registers. */ + readl(par->dc_regs + DC_UNLOCK); + writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK); + + gcfg = readl(par->dc_regs + DC_GENERAL_CFG); + dcfg = readl(par->dc_regs + DC_DISPLAY_CFG); + + /* Disable the timing generator. */ + dcfg &= ~(DC_DCFG_TGEN); + writel(dcfg, par->dc_regs + DC_DISPLAY_CFG); + + /* Wait for pending memory requests before disabling the FIFO load. */ + udelay(100); + + /* Disable FIFO load and compression. */ + gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE); + writel(gcfg, par->dc_regs + DC_GENERAL_CFG); + + /* Setup DCLK and its divisor. */ + par->vid_ops->set_dclk(info); + + /* + * Setup new mode. + */ + + /* Clear all unused feature bits. */ + gcfg &= DC_GCFG_YUVM | DC_GCFG_VDSE; + dcfg = 0; + + /* Set FIFO priority (default 6/5) and enable. */ + /* FIXME: increase fifo priority for 1280x1024 and higher modes? */ + gcfg |= (6 << DC_GCFG_DFHPEL_POS) | (5 << DC_GCFG_DFHPSL_POS) | DC_GCFG_DFLE; + + /* Framebuffer start offset. */ + writel(0, par->dc_regs + DC_FB_ST_OFFSET); + + /* Line delta and line buffer length. */ + writel(info->fix.line_length >> 3, par->dc_regs + DC_GFX_PITCH); + writel(((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2, + par->dc_regs + DC_LINE_SIZE); + + /* Enable graphics and video data and unmask address lines. */ + dcfg |= DC_DCFG_GDEN | DC_DCFG_VDEN | DC_DCFG_A20M | DC_DCFG_A18M; + + /* Set pixel format. */ + switch (info->var.bits_per_pixel) { + case 8: + dcfg |= DC_DCFG_DISP_MODE_8BPP; + break; + case 16: + dcfg |= DC_DCFG_DISP_MODE_16BPP; + dcfg |= DC_DCFG_16BPP_MODE_565; + break; + case 32: + dcfg |= DC_DCFG_DISP_MODE_24BPP; + dcfg |= DC_DCFG_PALB; + break; + } + + /* Enable timing generator. */ + dcfg |= DC_DCFG_TGEN; + + /* Horizontal and vertical timings. */ + hactive = info->var.xres; + hblankstart = hactive; + hsyncstart = hblankstart + info->var.right_margin; + hsyncend = hsyncstart + info->var.hsync_len; + hblankend = hsyncend + info->var.left_margin; + htotal = hblankend; + + vactive = info->var.yres; + vblankstart = vactive; + vsyncstart = vblankstart + info->var.lower_margin; + vsyncend = vsyncstart + info->var.vsync_len; + vblankend = vsyncend + info->var.upper_margin; + vtotal = vblankend; + + writel((hactive - 1) | ((htotal - 1) << 16), par->dc_regs + DC_H_ACTIVE_TIMING); + writel((hblankstart - 1) | ((hblankend - 1) << 16), par->dc_regs + DC_H_BLANK_TIMING); + writel((hsyncstart - 1) | ((hsyncend - 1) << 16), par->dc_regs + DC_H_SYNC_TIMING); + + writel((vactive - 1) | ((vtotal - 1) << 16), par->dc_regs + DC_V_ACTIVE_TIMING); + writel((vblankstart - 1) | ((vblankend - 1) << 16), par->dc_regs + DC_V_BLANK_TIMING); + writel((vsyncstart - 1) | ((vsyncend - 1) << 16), par->dc_regs + DC_V_SYNC_TIMING); + + /* Write final register values. */ + writel(dcfg, par->dc_regs + DC_DISPLAY_CFG); + writel(gcfg, par->dc_regs + DC_GENERAL_CFG); + + par->vid_ops->configure_display(info); + + /* Relock display controller registers */ + writel(0, par->dc_regs + DC_UNLOCK); +} + +static void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno, + unsigned red, unsigned green, unsigned blue) +{ + struct geodefb_par *par = info->par; + int val; + + /* Hardware palette is in RGB 8-8-8 format. */ + val = (red << 8) & 0xff0000; + val |= (green) & 0x00ff00; + val |= (blue >> 8) & 0x0000ff; + + writel(regno, par->dc_regs + DC_PAL_ADDRESS); + writel(val, par->dc_regs + DC_PAL_DATA); +} + +struct geode_dc_ops gx_dc_ops = { + .set_mode = gx_set_mode, + .set_palette_reg = gx_set_hw_palette_reg, +}; diff --git a/drivers/video/geode/display_gx.h b/drivers/video/geode/display_gx.h new file mode 100644 index 000000000000..86c623361305 --- /dev/null +++ b/drivers/video/geode/display_gx.h @@ -0,0 +1,96 @@ +/* + * Geode GX display controller + * + * Copyright (C) 2006 Arcom Control Systems Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __DISPLAY_GX_H__ +#define __DISPLAY_GX_H__ + +int gx_frame_buffer_size(void); +int gx_line_delta(int xres, int bpp); + +extern struct geode_dc_ops gx_dc_ops; + +/* Display controller registers */ + +#define DC_UNLOCK 0x00 +# define DC_UNLOCK_CODE 0x00004758 + +#define DC_GENERAL_CFG 0x04 +# define DC_GCFG_DFLE 0x00000001 +# define DC_GCFG_CURE 0x00000002 +# define DC_GCFG_ICNE 0x00000004 +# define DC_GCFG_VIDE 0x00000008 +# define DC_GCFG_CMPE 0x00000020 +# define DC_GCFG_DECE 0x00000040 +# define DC_GCFG_VGAE 0x00000080 +# define DC_GCFG_DFHPSL_MASK 0x00000F00 +# define DC_GCFG_DFHPSL_POS 8 +# define DC_GCFG_DFHPEL_MASK 0x0000F000 +# define DC_GCFG_DFHPEL_POS 12 +# define DC_GCFG_STFM 0x00010000 +# define DC_GCFG_FDTY 0x00020000 +# define DC_GCFG_VGAFT 0x00040000 +# define DC_GCFG_VDSE 0x00080000 +# define DC_GCFG_YUVM 0x00100000 +# define DC_GCFG_VFSL 0x00800000 +# define DC_GCFG_SIGE 0x01000000 +# define DC_GCFG_SGRE 0x02000000 +# define DC_GCFG_SGFR 0x04000000 +# define DC_GCFG_CRC_MODE 0x08000000 +# define DC_GCFG_DIAG 0x10000000 +# define DC_GCFG_CFRW 0x20000000 + +#define DC_DISPLAY_CFG 0x08 +# define DC_DCFG_TGEN 0x00000001 +# define DC_DCFG_GDEN 0x00000008 +# define DC_DCFG_VDEN 0x00000010 +# define DC_DCFG_TRUP 0x00000040 +# define DC_DCFG_DISP_MODE_MASK 0x00000300 +# define DC_DCFG_DISP_MODE_8BPP 0x00000000 +# define DC_DCFG_DISP_MODE_16BPP 0x00000100 +# define DC_DCFG_DISP_MODE_24BPP 0x00000200 +# define DC_DCFG_16BPP_MODE_MASK 0x00000c00 +# define DC_DCFG_16BPP_MODE_565 0x00000000 +# define DC_DCFG_16BPP_MODE_555 0x00000100 +# define DC_DCFG_16BPP_MODE_444 0x00000200 +# define DC_DCFG_DCEN 0x00080000 +# define DC_DCFG_PALB 0x02000000 +# define DC_DCFG_FRLK 0x04000000 +# define DC_DCFG_VISL 0x08000000 +# define DC_DCFG_FRSL 0x20000000 +# define DC_DCFG_A18M 0x40000000 +# define DC_DCFG_A20M 0x80000000 + +#define DC_FB_ST_OFFSET 0x10 + +#define DC_LINE_SIZE 0x30 +# define DC_LINE_SIZE_FB_LINE_SIZE_MASK 0x000007ff +# define DC_LINE_SIZE_FB_LINE_SIZE_POS 0 +# define DC_LINE_SIZE_CB_LINE_SIZE_MASK 0x007f0000 +# define DC_LINE_SIZE_CB_LINE_SIZE_POS 16 +# define DC_LINE_SIZE_VID_LINE_SIZE_MASK 0xff000000 +# define DC_LINE_SIZE_VID_LINE_SIZE_POS 24 + +#define DC_GFX_PITCH 0x34 +# define DC_GFX_PITCH_FB_PITCH_MASK 0x0000ffff +# define DC_GFX_PITCH_FB_PITCH_POS 0 +# define DC_GFX_PITCH_CB_PITCH_MASK 0xffff0000 +# define DC_GFX_PITCH_CB_PITCH_POS 16 + +#define DC_H_ACTIVE_TIMING 0x40 +#define DC_H_BLANK_TIMING 0x44 +#define DC_H_SYNC_TIMING 0x48 +#define DC_V_ACTIVE_TIMING 0x50 +#define DC_V_BLANK_TIMING 0x54 +#define DC_V_SYNC_TIMING 0x58 + +#define DC_PAL_ADDRESS 0x70 +#define DC_PAL_DATA 0x74 + +#endif /* !__DISPLAY_GX1_H__ */ diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c new file mode 100644 index 000000000000..89c34b15f5d4 --- /dev/null +++ b/drivers/video/geode/gxfb_core.c @@ -0,0 +1,423 @@ +/* + * Geode GX framebuffer driver. + * + * Copyright (C) 2006 Arcom Control Systems Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + * This driver assumes that the BIOS has created a virtual PCI device header + * for the video device. The PCI header is assumed to contain the following + * BARs: + * + * BAR0 - framebuffer memory + * BAR1 - graphics processor registers + * BAR2 - display controller registers + * BAR3 - video processor and flat panel control registers. + * + * 16 MiB of framebuffer memory is assumed to be available. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geodefb.h" +#include "display_gx.h" +#include "video_gx.h" + +static char mode_option[32] = "640x480-16@60"; + +/* Modes relevant to the GX (taken from modedb.c) */ +static const struct fb_videomode __initdata gx_modedb[] = { + /* 640x480-60 VESA */ + { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, + 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 640x480-75 VESA */ + { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3, + 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 640x480-85 VESA */ + { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3, + 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 800x600-60 VESA */ + { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 800x600-75 VESA */ + { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 800x600-85 VESA */ + { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1024x768-60 VESA */ + { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1024x768-75 VESA */ + { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1024x768-85 VESA */ + { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1280x960-60 VESA */ + { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1280x960-85 VESA */ + { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1280x1024-60 VESA */ + { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1280x1024-75 VESA */ + { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1280x1024-85 VESA */ + { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1600x1200-60 VESA */ + { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1600x1200-75 VESA */ + { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1600x1200-85 VESA */ + { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, +}; + +static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + if (var->xres > 1600 || var->yres > 1200) + return -EINVAL; + if ((var->xres > 1280 || var->yres > 1024) && var->bits_per_pixel > 16) + return -EINVAL; + + if (var->bits_per_pixel == 32) { + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + } else if (var->bits_per_pixel == 16) { + var->red.offset = 11; var->red.length = 5; + var->green.offset = 5; var->green.length = 6; + var->blue.offset = 0; var->blue.length = 5; + } else if (var->bits_per_pixel == 8) { + var->red.offset = 0; var->red.length = 8; + var->green.offset = 0; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + } else + return -EINVAL; + var->transp.offset = 0; var->transp.length = 0; + + /* Enough video memory? */ + if (gx_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len) + return -EINVAL; + + /* FIXME: Check timing parameters here? */ + + return 0; +} + +static int gxfb_set_par(struct fb_info *info) +{ + struct geodefb_par *par = info->par; + + if (info->var.bits_per_pixel > 8) { + info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_dealloc_cmap(&info->cmap); + } else { + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + fb_alloc_cmap(&info->cmap, 1<var.bits_per_pixel, 0); + } + + info->fix.line_length = gx_line_delta(info->var.xres, info->var.bits_per_pixel); + + par->dc_ops->set_mode(info); + + return 0; +} + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int gxfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct geodefb_par *par = info->par; + + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + /* Truecolor has hardware independent palette */ + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 *pal = info->pseudo_palette; + u32 v; + + if (regno >= 16) + return -EINVAL; + + v = chan_to_field(red, &info->var.red); + v |= chan_to_field(green, &info->var.green); + v |= chan_to_field(blue, &info->var.blue); + + pal[regno] = v; + } else { + if (regno >= 256) + return -EINVAL; + + par->dc_ops->set_palette_reg(info, regno, red, green, blue); + } + + return 0; +} + +static int gxfb_blank(int blank_mode, struct fb_info *info) +{ + struct geodefb_par *par = info->par; + + return par->vid_ops->blank_display(info, blank_mode); +} + +static int __init gxfb_map_video_memory(struct fb_info *info, struct pci_dev *dev) +{ + struct geodefb_par *par = info->par; + int fb_len; + int ret; + + ret = pci_enable_device(dev); + if (ret < 0) + return ret; + + ret = pci_request_region(dev, 3, "gxfb (video processor)"); + if (ret < 0) + return ret; + par->vid_regs = ioremap(pci_resource_start(dev, 3), + pci_resource_len(dev, 3)); + if (!par->vid_regs) + return -ENOMEM; + + ret = pci_request_region(dev, 2, "gxfb (display controller)"); + if (ret < 0) + return ret; + par->dc_regs = ioremap(pci_resource_start(dev, 2), pci_resource_len(dev, 2)); + if (!par->dc_regs) + return -ENOMEM; + + ret = pci_request_region(dev, 0, "gxfb (framebuffer)"); + if (ret < 0) + return ret; + if ((fb_len = gx_frame_buffer_size()) < 0) + return -ENOMEM; + info->fix.smem_start = pci_resource_start(dev, 0); + info->fix.smem_len = fb_len; + info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); + if (!info->screen_base) + return -ENOMEM; + + dev_info(&dev->dev, "%d Kibyte of video memory at 0x%lx\n", + info->fix.smem_len / 1024, info->fix.smem_start); + + return 0; +} + +static struct fb_ops gxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = gxfb_check_var, + .fb_set_par = gxfb_set_par, + .fb_setcolreg = gxfb_setcolreg, + .fb_blank = gxfb_blank, + /* No HW acceleration for now. */ + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static struct fb_info * __init gxfb_init_fbinfo(struct device *dev) +{ + struct geodefb_par *par; + struct fb_info *info; + + /* Alloc enough space for the pseudo palette. */ + info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, dev); + if (!info) + return NULL; + + par = info->par; + + strcpy(info->fix.id, "Geode GX"); + + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.type_aux = 0; + info->fix.xpanstep = 0; + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + + info->var.nonstd = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + info->var.accel_flags = 0; + info->var.vmode = FB_VMODE_NONINTERLACED; + + info->fbops = &gxfb_ops; + info->flags = FBINFO_DEFAULT; + info->node = -1; + + info->pseudo_palette = (void *)par + sizeof(struct geodefb_par); + + info->var.grayscale = 0; + + return info; +} + +static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct geodefb_par *par; + struct fb_info *info; + int ret; + + info = gxfb_init_fbinfo(&pdev->dev); + if (!info) + return -ENOMEM; + par = info->par; + + /* GX display controller and GX video device. */ + par->dc_ops = &gx_dc_ops; + par->vid_ops = &gx_vid_ops; + + if ((ret = gxfb_map_video_memory(info, pdev)) < 0) { + dev_err(&pdev->dev, "failed to map frame buffer or controller registers\n"); + goto err; + } + + ret = fb_find_mode(&info->var, info, mode_option, + gx_modedb, ARRAY_SIZE(gx_modedb), NULL, 16); + if (ret == 0 || ret == 4) { + dev_err(&pdev->dev, "could not find valid video mode\n"); + ret = -EINVAL; + goto err; + } + + /* Clear the frame buffer of garbage. */ + memset_io(info->screen_base, 0, info->fix.smem_len); + + gxfb_check_var(&info->var, info); + gxfb_set_par(info); + + if (register_framebuffer(info) < 0) { + ret = -EINVAL; + goto err; + } + pci_set_drvdata(pdev, info); + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); + return 0; + + err: + if (info->screen_base) { + iounmap(info->screen_base); + pci_release_region(pdev, 0); + } + if (par->vid_regs) { + iounmap(par->vid_regs); + pci_release_region(pdev, 3); + } + if (par->dc_regs) { + iounmap(par->dc_regs); + pci_release_region(pdev, 2); + } + + pci_disable_device(pdev); + + if (info) + framebuffer_release(info); + return ret; +} + +static void gxfb_remove(struct pci_dev *pdev) +{ + struct fb_info *info = pci_get_drvdata(pdev); + struct geodefb_par *par = info->par; + + unregister_framebuffer(info); + + iounmap((void __iomem *)info->screen_base); + pci_release_region(pdev, 0); + + iounmap(par->vid_regs); + pci_release_region(pdev, 3); + + iounmap(par->dc_regs); + pci_release_region(pdev, 2); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + framebuffer_release(info); +} + +static struct pci_device_id gxfb_id_table[] = { + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_VIDEO, + PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, + 0xff0000, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, gxfb_id_table); + +static struct pci_driver gxfb_driver = { + .name = "gxfb", + .id_table = gxfb_id_table, + .probe = gxfb_probe, + .remove = gxfb_remove, +}; + +static int __init gxfb_init(void) +{ +#ifndef MODULE + if (fb_get_options("gxfb", NULL)) + return -ENODEV; +#endif + return pci_register_driver(&gxfb_driver); +} + +static void __exit gxfb_cleanup(void) +{ + pci_unregister_driver(&gxfb_driver); +} + +module_init(gxfb_init); +module_exit(gxfb_cleanup); + +module_param_string(mode, mode_option, sizeof(mode_option), 0444); +MODULE_PARM_DESC(mode, "video mode (x[-][@])"); + +MODULE_DESCRIPTION("Framebuffer driver for the AMD Geode GX"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c new file mode 100644 index 000000000000..2b2a7880ea75 --- /dev/null +++ b/drivers/video/geode/video_gx.c @@ -0,0 +1,262 @@ +/* + * Geode GX video processor device. + * + * Copyright (C) 2006 Arcom Control Systems Ltd. + * + * Portions from AMD's original 2.4 driver: + * Copyright (C) 2004 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include + +#include "geodefb.h" +#include "video_gx.h" + + +/* + * Tables of register settings for various DOTCLKs. + */ +struct gx_pll_entry { + long pixclock; /* ps */ + u32 sys_rstpll_bits; + u32 dotpll_value; +}; + +#define POSTDIV3 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3) +#define PREMULT2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPREMULT2) +#define PREDIV2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3) + +static const struct gx_pll_entry gx_pll_table_48MHz[] = { + { 40123, POSTDIV3, 0x00000BF2 }, /* 24.9230 */ + { 39721, 0, 0x00000037 }, /* 25.1750 */ + { 35308, POSTDIV3|PREMULT2, 0x00000B1A }, /* 28.3220 */ + { 31746, POSTDIV3, 0x000002D2 }, /* 31.5000 */ + { 27777, POSTDIV3|PREMULT2, 0x00000FE2 }, /* 36.0000 */ + { 26666, POSTDIV3, 0x0000057A }, /* 37.5000 */ + { 25000, POSTDIV3, 0x0000030A }, /* 40.0000 */ + { 22271, 0, 0x00000063 }, /* 44.9000 */ + { 20202, 0, 0x0000054B }, /* 49.5000 */ + { 20000, 0, 0x0000026E }, /* 50.0000 */ + { 19860, PREMULT2, 0x00000037 }, /* 50.3500 */ + { 18518, POSTDIV3|PREMULT2, 0x00000B0D }, /* 54.0000 */ + { 17777, 0, 0x00000577 }, /* 56.2500 */ + { 17733, 0, 0x000007F7 }, /* 56.3916 */ + { 17653, 0, 0x0000057B }, /* 56.6444 */ + { 16949, PREMULT2, 0x00000707 }, /* 59.0000 */ + { 15873, POSTDIV3|PREMULT2, 0x00000B39 }, /* 63.0000 */ + { 15384, POSTDIV3|PREMULT2, 0x00000B45 }, /* 65.0000 */ + { 14814, POSTDIV3|PREMULT2, 0x00000FC1 }, /* 67.5000 */ + { 14124, POSTDIV3, 0x00000561 }, /* 70.8000 */ + { 13888, POSTDIV3, 0x000007E1 }, /* 72.0000 */ + { 13426, PREMULT2, 0x00000F4A }, /* 74.4810 */ + { 13333, 0, 0x00000052 }, /* 75.0000 */ + { 12698, 0, 0x00000056 }, /* 78.7500 */ + { 12500, POSTDIV3|PREMULT2, 0x00000709 }, /* 80.0000 */ + { 11135, PREMULT2, 0x00000262 }, /* 89.8000 */ + { 10582, 0, 0x000002D2 }, /* 94.5000 */ + { 10101, PREMULT2, 0x00000B4A }, /* 99.0000 */ + { 10000, PREMULT2, 0x00000036 }, /* 100.0000 */ + { 9259, 0, 0x000007E2 }, /* 108.0000 */ + { 8888, 0, 0x000007F6 }, /* 112.5000 */ + { 7692, POSTDIV3|PREMULT2, 0x00000FB0 }, /* 130.0000 */ + { 7407, POSTDIV3|PREMULT2, 0x00000B50 }, /* 135.0000 */ + { 6349, 0, 0x00000055 }, /* 157.5000 */ + { 6172, 0, 0x000009C1 }, /* 162.0000 */ + { 5787, PREMULT2, 0x0000002D }, /* 172.798 */ + { 5698, 0, 0x000002C1 }, /* 175.5000 */ + { 5291, 0, 0x000002D1 }, /* 189.0000 */ + { 4938, 0, 0x00000551 }, /* 202.5000 */ + { 4357, 0, 0x0000057D }, /* 229.5000 */ +}; + +static const struct gx_pll_entry gx_pll_table_14MHz[] = { + { 39721, 0, 0x00000037 }, /* 25.1750 */ + { 35308, 0, 0x00000B7B }, /* 28.3220 */ + { 31746, 0, 0x000004D3 }, /* 31.5000 */ + { 27777, 0, 0x00000BE3 }, /* 36.0000 */ + { 26666, 0, 0x0000074F }, /* 37.5000 */ + { 25000, 0, 0x0000050B }, /* 40.0000 */ + { 22271, 0, 0x00000063 }, /* 44.9000 */ + { 20202, 0, 0x0000054B }, /* 49.5000 */ + { 20000, 0, 0x0000026E }, /* 50.0000 */ + { 19860, 0, 0x000007C3 }, /* 50.3500 */ + { 18518, 0, 0x000007E3 }, /* 54.0000 */ + { 17777, 0, 0x00000577 }, /* 56.2500 */ + { 17733, 0, 0x000002FB }, /* 56.3916 */ + { 17653, 0, 0x0000057B }, /* 56.6444 */ + { 16949, 0, 0x0000058B }, /* 59.0000 */ + { 15873, 0, 0x0000095E }, /* 63.0000 */ + { 15384, 0, 0x0000096A }, /* 65.0000 */ + { 14814, 0, 0x00000BC2 }, /* 67.5000 */ + { 14124, 0, 0x0000098A }, /* 70.8000 */ + { 13888, 0, 0x00000BE2 }, /* 72.0000 */ + { 13333, 0, 0x00000052 }, /* 75.0000 */ + { 12698, 0, 0x00000056 }, /* 78.7500 */ + { 12500, 0, 0x0000050A }, /* 80.0000 */ + { 11135, 0, 0x0000078E }, /* 89.8000 */ + { 10582, 0, 0x000002D2 }, /* 94.5000 */ + { 10101, 0, 0x000011F6 }, /* 99.0000 */ + { 10000, 0, 0x0000054E }, /* 100.0000 */ + { 9259, 0, 0x000007E2 }, /* 108.0000 */ + { 8888, 0, 0x000002FA }, /* 112.5000 */ + { 7692, 0, 0x00000BB1 }, /* 130.0000 */ + { 7407, 0, 0x00000975 }, /* 135.0000 */ + { 6349, 0, 0x00000055 }, /* 157.5000 */ + { 6172, 0, 0x000009C1 }, /* 162.0000 */ + { 5698, 0, 0x000002C1 }, /* 175.5000 */ + { 5291, 0, 0x00000539 }, /* 189.0000 */ + { 4938, 0, 0x00000551 }, /* 202.5000 */ + { 4357, 0, 0x0000057D }, /* 229.5000 */ +}; + +static void gx_set_dclk_frequency(struct fb_info *info) +{ + const struct gx_pll_entry *pll_table; + int pll_table_len; + int i, best_i; + long min, diff; + u64 dotpll, sys_rstpll; + int timeout = 1000; + + /* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */ + if (cpu_data->x86_mask == 1) { + pll_table = gx_pll_table_14MHz; + pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz); + } else { + pll_table = gx_pll_table_48MHz; + pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz); + } + + /* Search the table for the closest pixclock. */ + best_i = 0; + min = abs(pll_table[0].pixclock - info->var.pixclock); + for (i = 1; i < pll_table_len; i++) { + diff = abs(pll_table[i].pixclock - info->var.pixclock); + if (diff < min) { + min = diff; + best_i = i; + } + } + + rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); + rdmsrl(MSR_GLCP_DOTPLL, dotpll); + + /* Program new M, N and P. */ + dotpll &= 0x00000000ffffffffull; + dotpll |= (u64)pll_table[best_i].dotpll_value << 32; + dotpll |= MSR_GLCP_DOTPLL_DOTRESET; + dotpll &= ~MSR_GLCP_DOTPLL_BYPASS; + + wrmsrl(MSR_GLCP_DOTPLL, dotpll); + + /* Program dividers. */ + sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 + | MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 + | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 ); + sys_rstpll |= pll_table[best_i].sys_rstpll_bits; + + wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); + + /* Clear reset bit to start PLL. */ + dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET); + wrmsrl(MSR_GLCP_DOTPLL, dotpll); + + /* Wait for LOCK bit. */ + do { + rdmsrl(MSR_GLCP_DOTPLL, dotpll); + } while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK)); +} + +static void gx_configure_display(struct fb_info *info) +{ + struct geodefb_par *par = info->par; + u32 dcfg, fp_pm; + + dcfg = readl(par->vid_regs + GX_DCFG); + + /* Clear bits from existing mode. */ + dcfg &= ~(GX_DCFG_CRT_SYNC_SKW_MASK + | GX_DCFG_CRT_HSYNC_POL | GX_DCFG_CRT_VSYNC_POL + | GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN); + + /* Set default sync skew. */ + dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT; + + /* Enable hsync and vsync. */ + dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN; + + /* Sync polarities. */ + if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) + dcfg |= GX_DCFG_CRT_HSYNC_POL; + if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) + dcfg |= GX_DCFG_CRT_VSYNC_POL; + + writel(dcfg, par->vid_regs + GX_DCFG); + + /* Power on flat panel. */ + fp_pm = readl(par->vid_regs + GX_FP_PM); + fp_pm |= GX_FP_PM_P; + writel(fp_pm, par->vid_regs + GX_FP_PM); +} + +static int gx_blank_display(struct fb_info *info, int blank_mode) +{ + struct geodefb_par *par = info->par; + u32 dcfg, fp_pm; + int blank, hsync, vsync; + + /* CRT power saving modes. */ + switch (blank_mode) { + case FB_BLANK_UNBLANK: + blank = 0; hsync = 1; vsync = 1; + break; + case FB_BLANK_NORMAL: + blank = 1; hsync = 1; vsync = 1; + break; + case FB_BLANK_VSYNC_SUSPEND: + blank = 1; hsync = 1; vsync = 0; + break; + case FB_BLANK_HSYNC_SUSPEND: + blank = 1; hsync = 0; vsync = 1; + break; + case FB_BLANK_POWERDOWN: + blank = 1; hsync = 0; vsync = 0; + break; + default: + return -EINVAL; + } + dcfg = readl(par->vid_regs + GX_DCFG); + dcfg &= ~(GX_DCFG_DAC_BL_EN + | GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN); + if (!blank) + dcfg |= GX_DCFG_DAC_BL_EN; + if (hsync) + dcfg |= GX_DCFG_HSYNC_EN; + if (vsync) + dcfg |= GX_DCFG_VSYNC_EN; + writel(dcfg, par->vid_regs + GX_DCFG); + + /* Power on/off flat panel. */ + fp_pm = readl(par->vid_regs + GX_FP_PM); + if (blank_mode == FB_BLANK_POWERDOWN) + fp_pm &= ~GX_FP_PM_P; + else + fp_pm |= GX_FP_PM_P; + writel(fp_pm, par->vid_regs + GX_FP_PM); + + return 0; +} + +struct geode_vid_ops gx_vid_ops = { + .set_dclk = gx_set_dclk_frequency, + .configure_display = gx_configure_display, + .blank_display = gx_blank_display, +}; diff --git a/drivers/video/geode/video_gx.h b/drivers/video/geode/video_gx.h new file mode 100644 index 000000000000..2d9211f3ed84 --- /dev/null +++ b/drivers/video/geode/video_gx.h @@ -0,0 +1,47 @@ +/* + * Geode GX video device + * + * Copyright (C) 2006 Arcom Control Systems Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VIDEO_GX_H__ +#define __VIDEO_GX_H__ + +extern struct geode_vid_ops gx_vid_ops; + +/* Geode GX video processor registers */ + +#define GX_DCFG 0x0008 +# define GX_DCFG_CRT_EN 0x00000001 +# define GX_DCFG_HSYNC_EN 0x00000002 +# define GX_DCFG_VSYNC_EN 0x00000004 +# define GX_DCFG_DAC_BL_EN 0x00000008 +# define GX_DCFG_CRT_HSYNC_POL 0x00000100 +# define GX_DCFG_CRT_VSYNC_POL 0x00000200 +# define GX_DCFG_CRT_SYNC_SKW_MASK 0x0001C000 +# define GX_DCFG_CRT_SYNC_SKW_DFLT 0x00010000 +# define GX_DCFG_VG_CK 0x00100000 +# define GX_DCFG_GV_GAM 0x00200000 +# define GX_DCFG_DAC_VREF 0x04000000 + +/* Geode GX flat panel display control registers */ +#define GX_FP_PM 0x410 +# define GX_FP_PM_P 0x01000000 + +/* Geode GX clock control MSRs */ + +#define MSR_GLCP_SYS_RSTPLL 0x4c000014 +# define MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 (0x0000000000000002ull) +# define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 (0x0000000000000004ull) +# define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 (0x0000000000000008ull) + +#define MSR_GLCP_DOTPLL 0x4c000015 +# define MSR_GLCP_DOTPLL_DOTRESET (0x0000000000000001ull) +# define MSR_GLCP_DOTPLL_BYPASS (0x0000000000008000ull) +# define MSR_GLCP_DOTPLL_LOCK (0x0000000002000000ull) + +#endif /* !__VIDEO_GX_H__ */ -- cgit v1.2.2 From 756e21a022aba5214bafcf803f114aed8a783b99 Mon Sep 17 00:00:00 2001 From: Arthur Othieno Date: Mon, 27 Mar 2006 01:17:24 -0800 Subject: [PATCH] matroxfb: simply return what i2c_add_driver() does insmod will tell us when the module failed to load. We do no further processing on the return from i2c_add_driver(), so just return what i2c_add_driver() did, instead of storing it. Add __init/__exit annotations while we're at it. Signed-off-by: Arthur Othieno Acked-by: Jean Delvare Acked-by: Petr Vandrovec Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/matrox/matroxfb_maven.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 6019710dc298..531a0c3b6ec1 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -1297,20 +1297,13 @@ static struct i2c_driver maven_driver={ .detach_client = maven_detach_client, }; -/* ************************** */ - -static int matroxfb_maven_init(void) { - int err; - - err = i2c_add_driver(&maven_driver); - if (err) { - printk(KERN_ERR "maven: Maven driver failed to register (%d).\n", err); - return err; - } - return 0; +static int __init matroxfb_maven_init(void) +{ + return i2c_add_driver(&maven_driver); } -static void matroxfb_maven_exit(void) { +static void __exit matroxfb_maven_exit(void) +{ i2c_del_driver(&maven_driver); } -- cgit v1.2.2 From d03c21ec0be7787ff6b75dcf56c0e96209ccbfbd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 27 Mar 2006 01:17:26 -0800 Subject: [PATCH] matrox maven: memory allocation and other cleanups A few cleanups which were done to almost all i2c drivers some times ago, but matroxfb_maven was forgotten: * Don't allocate two different structures at once. * Use kzalloc instead of kmalloc+memset. * Use strlcpy instead of strcpy. * Drop duplicate error message on client deregistration failure. Signed-off-by: Jean Delvare Acked-by: Petr Vandrovec Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/matrox/matroxfb_maven.c | 55 ++++++++++++++++------------------- 1 file changed, 25 insertions(+), 30 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 531a0c3b6ec1..d8b3429cbed3 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -129,7 +129,7 @@ static int get_ctrl_id(__u32 v4l2_id) { struct maven_data { struct matrox_fb_info* primary_head; - struct i2c_client* client; + struct i2c_client client; int version; }; @@ -970,7 +970,7 @@ static inline int maven_compute_timming(struct maven_data* md, static int maven_program_timming(struct maven_data* md, const struct mavenregs* m) { - struct i2c_client* c = md->client; + struct i2c_client* c = &md->client; if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) { LR(0x80); @@ -1007,7 +1007,7 @@ static int maven_program_timming(struct maven_data* md, } static inline int maven_resync(struct maven_data* md) { - struct i2c_client* c = md->client; + struct i2c_client* c = &md->client; maven_set_reg(c, 0x95, 0x20); /* start whole thing */ return 0; } @@ -1065,48 +1065,48 @@ static int maven_set_control (struct maven_data* md, maven_compute_bwlevel(md, &blacklevel, &whitelevel); blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8); whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8); - maven_set_reg_pair(md->client, 0x0e, blacklevel); - maven_set_reg_pair(md->client, 0x1e, whitelevel); + maven_set_reg_pair(&md->client, 0x0e, blacklevel); + maven_set_reg_pair(&md->client, 0x1e, whitelevel); } break; case V4L2_CID_SATURATION: { - maven_set_reg(md->client, 0x20, p->value); - maven_set_reg(md->client, 0x22, p->value); + maven_set_reg(&md->client, 0x20, p->value); + maven_set_reg(&md->client, 0x22, p->value); } break; case V4L2_CID_HUE: { - maven_set_reg(md->client, 0x25, p->value); + maven_set_reg(&md->client, 0x25, p->value); } break; case V4L2_CID_GAMMA: { const struct maven_gamma* g; g = maven_compute_gamma(md); - maven_set_reg(md->client, 0x83, g->reg83); - maven_set_reg(md->client, 0x84, g->reg84); - maven_set_reg(md->client, 0x85, g->reg85); - maven_set_reg(md->client, 0x86, g->reg86); - maven_set_reg(md->client, 0x87, g->reg87); - maven_set_reg(md->client, 0x88, g->reg88); - maven_set_reg(md->client, 0x89, g->reg89); - maven_set_reg(md->client, 0x8a, g->reg8a); - maven_set_reg(md->client, 0x8b, g->reg8b); + maven_set_reg(&md->client, 0x83, g->reg83); + maven_set_reg(&md->client, 0x84, g->reg84); + maven_set_reg(&md->client, 0x85, g->reg85); + maven_set_reg(&md->client, 0x86, g->reg86); + maven_set_reg(&md->client, 0x87, g->reg87); + maven_set_reg(&md->client, 0x88, g->reg88); + maven_set_reg(&md->client, 0x89, g->reg89); + maven_set_reg(&md->client, 0x8a, g->reg8a); + maven_set_reg(&md->client, 0x8b, g->reg8b); } break; case MATROXFB_CID_TESTOUT: { unsigned char val - = maven_get_reg (md->client,0x8d); + = maven_get_reg(&md->client,0x8d); if (p->value) val |= 0x10; else val &= ~0x10; - maven_set_reg (md->client, 0x8d, val); + maven_set_reg(&md->client, 0x8d, val); } break; case MATROXFB_CID_DEFLICKER: { - maven_set_reg(md->client, 0x93, maven_compute_deflicker(md)); + maven_set_reg(&md->client, 0x93, maven_compute_deflicker(md)); } break; } @@ -1185,7 +1185,6 @@ static int maven_init_client(struct i2c_client* clnt) { MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo); md->primary_head = MINFO; - md->client = clnt; down_write(&ACCESS_FBINFO(altout.lock)); ACCESS_FBINFO(outputs[1]).output = &maven_altout; ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; @@ -1243,19 +1242,17 @@ static int maven_detect_client(struct i2c_adapter* adapter, int address, int kin I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_PROTOCOL_MANGLING)) goto ERROR0; - if (!(new_client = (struct i2c_client*)kmalloc(sizeof(*new_client) + sizeof(*data), - GFP_KERNEL))) { + if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR0; } - memset(new_client, 0, sizeof(*new_client) + sizeof(*data)); - data = (struct maven_data*)(new_client + 1); + new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &maven_driver; new_client->flags = 0; - strcpy(new_client->name, "maven client"); + strlcpy(new_client->name, "maven", I2C_NAME_SIZE); if ((err = i2c_attach_client(new_client))) goto ERROR3; err = maven_init_client(new_client); @@ -1279,12 +1276,10 @@ static int maven_attach_adapter(struct i2c_adapter* adapter) { static int maven_detach_client(struct i2c_client* client) { int err; - if ((err = i2c_detach_client(client))) { - printk(KERN_ERR "maven: Cannot deregister client\n"); + if ((err = i2c_detach_client(client))) return err; - } maven_shutdown_client(client); - kfree(client); + kfree(i2c_get_clientdata(client)); return 0; } -- cgit v1.2.2 From f95ec3c6df271ae4e6290cd6b95c18a009c76dc9 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 27 Mar 2006 01:17:27 -0800 Subject: [PATCH] au1200fb: Alchemy Au1200 framebuffer driver Add support for Alchemy Au1200 framebuffer driver Signed-off-by: Ralf Baechle Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 11 + drivers/video/Makefile | 1 + drivers/video/au1200fb.c | 3844 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/video/au1200fb.h | 572 +++++++ 4 files changed, 4428 insertions(+) create mode 100644 drivers/video/au1200fb.c create mode 100644 drivers/video/au1200fb.h (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index fdebd60a3250..3a890610ebe7 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1202,6 +1202,17 @@ config FB_AU1100 bool "Au1100 LCD Driver" depends on (FB = y) && EXPERIMENTAL && PCI && MIPS && MIPS_PB1100=y +config FB_AU1200 + bool "Au1200 LCD Driver" + depends on FB && MIPS && SOC_AU1200 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer driver for the AMD Au1200 SOC. It can drive + various panels and CRTs by passing in kernel cmd line option + au1200fb:panel=. + source "drivers/video/geode/Kconfig" config FB_FFB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index aa434e725c0d..cb90218515ac 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o +obj-$(CONFIG_FB_AU1200) += au1200fb.o obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c new file mode 100644 index 000000000000..b367de30b98c --- /dev/null +++ b/drivers/video/au1200fb.c @@ -0,0 +1,3844 @@ +/* + * BRIEF MODULE DESCRIPTION + * Au1200 LCD Driver. + * + * Copyright 2004-2005 AMD + * Author: AMD + * + * Based on: + * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device + * Created 28 Dec 1997 by Geert Uytterhoeven + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "au1200fb.h" + +#ifdef CONFIG_PM +#include +#endif + +#ifndef CONFIG_FB_AU1200_DEVS +#define CONFIG_FB_AU1200_DEVS 4 +#endif + +#define DRIVER_NAME "au1200fb" +#define DRIVER_DESC "LCD controller driver for AU1200 processors" + +#define DEBUG 1 + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) + +#if DEBUG +#define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + + +#define AU1200_LCD_FB_IOCTL 0x46FF + +#define AU1200_LCD_SET_SCREEN 1 +#define AU1200_LCD_GET_SCREEN 2 +#define AU1200_LCD_SET_WINDOW 3 +#define AU1200_LCD_GET_WINDOW 4 +#define AU1200_LCD_SET_PANEL 5 +#define AU1200_LCD_GET_PANEL 6 + +#define SCREEN_SIZE (1<< 1) +#define SCREEN_BACKCOLOR (1<< 2) +#define SCREEN_BRIGHTNESS (1<< 3) +#define SCREEN_COLORKEY (1<< 4) +#define SCREEN_MASK (1<< 5) + +struct au1200_lcd_global_regs_t { + unsigned int flags; + unsigned int xsize; + unsigned int ysize; + unsigned int backcolor; + unsigned int brightness; + unsigned int colorkey; + unsigned int mask; + unsigned int panel_choice; + char panel_desc[80]; + +}; + +#define WIN_POSITION (1<< 0) +#define WIN_ALPHA_COLOR (1<< 1) +#define WIN_ALPHA_MODE (1<< 2) +#define WIN_PRIORITY (1<< 3) +#define WIN_CHANNEL (1<< 4) +#define WIN_BUFFER_FORMAT (1<< 5) +#define WIN_COLOR_ORDER (1<< 6) +#define WIN_PIXEL_ORDER (1<< 7) +#define WIN_SIZE (1<< 8) +#define WIN_COLORKEY_MODE (1<< 9) +#define WIN_DOUBLE_BUFFER_MODE (1<< 10) +#define WIN_RAM_ARRAY_MODE (1<< 11) +#define WIN_BUFFER_SCALE (1<< 12) +#define WIN_ENABLE (1<< 13) + +struct au1200_lcd_window_regs_t { + unsigned int flags; + unsigned int xpos; + unsigned int ypos; + unsigned int alpha_color; + unsigned int alpha_mode; + unsigned int priority; + unsigned int channel; + unsigned int buffer_format; + unsigned int color_order; + unsigned int pixel_order; + unsigned int xsize; + unsigned int ysize; + unsigned int colorkey_mode; + unsigned int double_buffer_mode; + unsigned int ram_array_mode; + unsigned int xscale; + unsigned int yscale; + unsigned int enable; +}; + + +struct au1200_lcd_iodata_t { + unsigned int subcmd; + struct au1200_lcd_global_regs_t global; + struct au1200_lcd_window_regs_t window; +}; + +#if defined(__BIG_ENDIAN) +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 +#else +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 +#endif +#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 + +/* Private, per-framebuffer management information (independent of the panel itself) */ +struct au1200fb_device { + struct fb_info fb_info; /* FB driver info record */ + + int plane; + unsigned char* fb_mem; /* FrameBuffer memory map */ + unsigned int fb_len; + dma_addr_t fb_phys; +}; + +static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; +/********************************************************************/ + +/* LCD controller restrictions */ +#define AU1200_LCD_MAX_XRES 1280 +#define AU1200_LCD_MAX_YRES 1024 +#define AU1200_LCD_MAX_BPP 32 +#define AU1200_LCD_MAX_CLK 96000000 /* fixme: this needs to go away ? */ +#define AU1200_LCD_NBR_PALETTE_ENTRIES 256 + +/* Default number of visible screen buffer to allocate */ +#define AU1200FB_NBR_VIDEO_BUFFERS 1 + +/********************************************************************/ + +static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; +static int window_index = 2; /* default is zero */ +static int panel_index = 2; /* default is zero */ +static struct window_settings *win; +static struct panel_settings *panel; +static int noblanking = 1; +static int nohwcursor = 0; + +struct window_settings { + unsigned char name[64]; + uint32 mode_backcolor; + uint32 mode_colorkey; + uint32 mode_colorkeymsk; + struct { + int xres; + int yres; + int xpos; + int ypos; + uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */ + uint32 mode_winenable; + } w[4]; +}; + +#if defined(__BIG_ENDIAN) +#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00 +#else +#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01 +#endif + +extern int board_au1200fb_panel_init (void); +extern int board_au1200fb_panel_shutdown (void); + +#ifdef CONFIG_PM +int au1200fb_pm_callback(au1xxx_power_dev_t *dev, + au1xxx_request_t request, void *data); +au1xxx_power_dev_t *LCD_pm_dev; +#endif + +/* + * Default window configurations + */ +static struct window_settings windows[] = { + { /* Index 0 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 100, 100, 100, 100, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ LCD_WINENABLE_WEN1, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + + { /* Index 1 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 320, 240, 5, 5, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP | + LCD_WINCTRL1_PO_00, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 + | LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 100, 100, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, + }, + { + /* xres, yres, xpos, ypos */ 200, 25, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + { /* Index 2 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_32BPP | + LCD_WINCTRL1_PO_00|LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + /* Need VGA 640 @ 24bpp, @ 32bpp */ + /* Need VGA 800 @ 24bpp, @ 32bpp */ + /* Need VGA 1024 @ 24bpp, @ 32bpp */ +}; + +/* + * Controller configurations for various panels. + */ + +struct panel_settings +{ + const char name[25]; /* Full name _ */ + + struct fb_monspecs monspecs; /* FB monitor specs */ + + /* panel timings */ + uint32 mode_screen; + uint32 mode_horztiming; + uint32 mode_verttiming; + uint32 mode_clkcontrol; + uint32 mode_pwmdiv; + uint32 mode_pwmhi; + uint32 mode_outmask; + uint32 mode_fifoctrl; + uint32 mode_toyclksrc; + uint32 mode_backlight; + uint32 mode_auxpll; + int (*device_init)(void); + int (*device_shutdown)(void); +#define Xres min_xres +#define Yres min_yres + u32 min_xres; /* Minimum horizontal resolution */ + u32 max_xres; /* Maximum horizontal resolution */ + u32 min_yres; /* Minimum vertical resolution */ + u32 max_yres; /* Maximum vertical resolution */ +}; + +/********************************************************************/ +/* fixme: Maybe a modedb for the CRT ? otherwise panels should be as-is */ + +/* List of panels known to work with the AU1200 LCD controller. + * To add a new panel, enter the same specifications as the + * Generic_TFT one, and MAKE SURE that it doesn't conflicts + * with the controller restrictions. Restrictions are: + * + * STN color panels: max_bpp <= 12 + * STN mono panels: max_bpp <= 4 + * TFT panels: max_bpp <= 16 + * max_xres <= 800 + * max_yres <= 600 + */ +static struct panel_settings known_lcd_panels[] = +{ + [0] = { /* QVGA 320x240 H:33.3kHz V:110Hz */ + .name = "QVGA_320x240", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(320) | + LCD_SCREEN_SY_N(240), + .mode_horztiming = 0x00c4623b, + .mode_verttiming = 0x00502814, + .mode_clkcontrol = 0x00020002, /* /4=24Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 320, 320, + 240, 240, + }, + + [1] = { /* VGA 640x480 H:30.3kHz V:58Hz */ + .name = "VGA_640x480", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x13f9df80, + .mode_horztiming = 0x003c5859, + .mode_verttiming = 0x00741201, + .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 640, 480, + 640, 480, + }, + + [2] = { /* SVGA 800x600 H:46.1kHz V:69Hz */ + .name = "SVGA_800x600", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x18fa5780, + .mode_horztiming = 0x00dc7e77, + .mode_verttiming = 0x00584805, + .mode_clkcontrol = 0x00020000, /* /2=48Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 800, 800, + 600, 600, + }, + + [3] = { /* XVGA 1024x768 H:56.2kHz V:70Hz */ + .name = "XVGA_1024x768", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x1ffaff80, + .mode_horztiming = 0x007d0e57, + .mode_verttiming = 0x00740a01, + .mode_clkcontrol = 0x000A0000, /* /1 */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 6, /* 72MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 1024, 1024, + 768, 768, + }, + + [4] = { /* XVGA XVGA 1280x1024 H:68.5kHz V:65Hz */ + .name = "XVGA_1280x1024", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x27fbff80, + .mode_horztiming = 0x00cdb2c7, + .mode_verttiming = 0x00600002, + .mode_clkcontrol = 0x000A0000, /* /1 */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 10, /* 120MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 1280, 1280, + 1024, 1024, + }, + + [5] = { /* Samsung 1024x768 TFT */ + .name = "Samsung_1024x768_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x1ffaff80, + .mode_horztiming = 0x018cc677, + .mode_verttiming = 0x00241217, + .mode_clkcontrol = 0x00000000, /* SCB 0x1 /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, /* SCB 0x0 */ + .mode_pwmhi = 0x03400000, /* SCB 0x0 */ + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 1024, 1024, + 768, 768, + }, + + [6] = { /* Toshiba 640x480 TFT */ + .name = "Toshiba_640x480_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(640) | + LCD_SCREEN_SY_N(480), + .mode_horztiming = LCD_HORZTIMING_HPW_N(96) | + LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51), + .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | + LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32), + .mode_clkcontrol = 0x00000000, /* /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 640, 480, + 640, 480, + }, + + [7] = { /* Sharp 320x240 TFT */ + .name = "Sharp_320x240_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 12500, + .hfmax = 20000, + .vfmin = 38, + .vfmax = 81, + .dclkmin = 4500000, + .dclkmax = 6800000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(320) | + LCD_SCREEN_SY_N(240), + .mode_horztiming = LCD_HORZTIMING_HPW_N(60) | + LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2), + .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | + LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5), + .mode_clkcontrol = LCD_CLKCONTROL_PCD_N(7), /*16=6Mhz*/ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 320, 320, + 240, 240, + }, + + [8] = { /* Toppoly TD070WGCB2 7" 856x480 TFT */ + .name = "Toppoly_TD070WGCB2", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(856) | + LCD_SCREEN_SY_N(480), + .mode_horztiming = LCD_HORZTIMING_HND2_N(43) | + LCD_HORZTIMING_HND1_N(43) | LCD_HORZTIMING_HPW_N(114), + .mode_verttiming = LCD_VERTTIMING_VND2_N(20) | + LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4), + .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 856, 856, + 480, 480, + }, +}; + +#define NUM_PANELS (ARRAY_SIZE(known_lcd_panels)) + +/********************************************************************/ + +#ifdef CONFIG_PM +static int set_brightness(unsigned int brightness) +{ + unsigned int hi1, divider; + + /* limit brightness pwm duty to >= 30/1600 */ + if (brightness < 30) { + brightness = 30; + } + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + hi1 = (lcd->pwmhi >> 16) + 1; + hi1 = (((brightness & 0xFF) + 1) * divider >> 8); + lcd->pwmhi &= 0xFFFF; + lcd->pwmhi |= (hi1 << 16); + + return brightness; +} +#endif /* CONFIG_PM */ + +static int winbpp (unsigned int winctrl1) +{ + int bits = 0; + + /* how many bits are needed for each pixel format */ + switch (winctrl1 & LCD_WINCTRL1_FRM) { + case LCD_WINCTRL1_FRM_1BPP: + bits = 1; + break; + case LCD_WINCTRL1_FRM_2BPP: + bits = 2; + break; + case LCD_WINCTRL1_FRM_4BPP: + bits = 4; + break; + case LCD_WINCTRL1_FRM_8BPP: + bits = 8; + break; + case LCD_WINCTRL1_FRM_12BPP: + case LCD_WINCTRL1_FRM_16BPP655: + case LCD_WINCTRL1_FRM_16BPP565: + case LCD_WINCTRL1_FRM_16BPP556: + case LCD_WINCTRL1_FRM_16BPPI1555: + case LCD_WINCTRL1_FRM_16BPPI5551: + case LCD_WINCTRL1_FRM_16BPPA1555: + case LCD_WINCTRL1_FRM_16BPPA5551: + bits = 16; + break; + case LCD_WINCTRL1_FRM_24BPP: + case LCD_WINCTRL1_FRM_32BPP: + bits = 32; + break; + } + + return bits; +} + +static int fbinfo2index (struct fb_info *fb_info) +{ + int i; + + for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { + if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) + return i; + } + printk("au1200fb: ERROR: fbinfo2index failed!\n"); + return -1; +} + +static int au1200_setlocation (struct au1200fb_device *fbdev, int plane, + int xpos, int ypos) +{ + uint32 winctrl0, winctrl1, winenable, fb_offset = 0; + int xsz, ysz; + + /* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */ + + winctrl0 = lcd->window[plane].winctrl0; + winctrl1 = lcd->window[plane].winctrl1; + winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN); + winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY); + + /* Check for off-screen adjustments */ + xsz = win->w[plane].xres; + ysz = win->w[plane].yres; + if ((xpos + win->w[plane].xres) > panel->Xres) { + /* Off-screen to the right */ + xsz = panel->Xres - xpos; /* off by 1 ??? */ + /*printk("off screen right\n");*/ + } + + if ((ypos + win->w[plane].yres) > panel->Yres) { + /* Off-screen to the bottom */ + ysz = panel->Yres - ypos; /* off by 1 ??? */ + /*printk("off screen bottom\n");*/ + } + + if (xpos < 0) { + /* Off-screen to the left */ + xsz = win->w[plane].xres + xpos; + fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8); + xpos = 0; + /*printk("off screen left\n");*/ + } + + if (ypos < 0) { + /* Off-screen to the top */ + ysz = win->w[plane].yres + ypos; + /* fixme: fb_offset += ((0-ypos)*fb_pars[plane].line_length); */ + ypos = 0; + /*printk("off screen top\n");*/ + } + + /* record settings */ + win->w[plane].xpos = xpos; + win->w[plane].ypos = ypos; + + xsz -= 1; + ysz -= 1; + winctrl0 |= (xpos << 21); + winctrl0 |= (ypos << 10); + winctrl1 |= (xsz << 11); + winctrl1 |= (ysz << 0); + + /* Disable the window while making changes, then restore WINEN */ + winenable = lcd->winenable & (1 << plane); + au_sync(); + lcd->winenable &= ~(1 << plane); + lcd->window[plane].winctrl0 = winctrl0; + lcd->window[plane].winctrl1 = winctrl1; + lcd->window[plane].winbuf0 = + lcd->window[plane].winbuf1 = fbdev->fb_phys; + lcd->window[plane].winbufctrl = 0; /* select winbuf0 */ + lcd->winenable |= winenable; + au_sync(); + + return 0; +} + +static void au1200_setpanel (struct panel_settings *newpanel) +{ + /* + * Perform global setup/init of LCD controller + */ + uint32 winenable; + + /* Make sure all windows disabled */ + winenable = lcd->winenable; + lcd->winenable = 0; + au_sync(); + /* + * Ensure everything is disabled before reconfiguring + */ + if (lcd->screen & LCD_SCREEN_SEN) { + /* Wait for vertical sync period */ + lcd->intstatus = LCD_INT_SS; + while ((lcd->intstatus & LCD_INT_SS) == 0) { + au_sync(); + } + + lcd->screen &= ~LCD_SCREEN_SEN; /*disable the controller*/ + + do { + lcd->intstatus = lcd->intstatus; /*clear interrupts*/ + au_sync(); + /*wait for controller to shut down*/ + } while ((lcd->intstatus & LCD_INT_SD) == 0); + + /* Call shutdown of current panel (if up) */ + /* this must occur last, because if an external clock is driving + the controller, the clock cannot be turned off before first + shutting down the controller. + */ + if (panel->device_shutdown != NULL) + panel->device_shutdown(); + } + + /* Newpanel == NULL indicates a shutdown operation only */ + if (newpanel == NULL) + return; + + panel = newpanel; + + printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres); + + /* + * Setup clocking if internal LCD clock source (assumes sys_auxpll valid) + */ + if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT)) + { + uint32 sys_clksrc; + au_writel(panel->mode_auxpll, SYS_AUXPLL); + sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; + sys_clksrc |= panel->mode_toyclksrc; + au_writel(sys_clksrc, SYS_CLKSRC); + } + + /* + * Configure panel timings + */ + lcd->screen = panel->mode_screen; + lcd->horztiming = panel->mode_horztiming; + lcd->verttiming = panel->mode_verttiming; + lcd->clkcontrol = panel->mode_clkcontrol; + lcd->pwmdiv = panel->mode_pwmdiv; + lcd->pwmhi = panel->mode_pwmhi; + lcd->outmask = panel->mode_outmask; + lcd->fifoctrl = panel->mode_fifoctrl; + au_sync(); + + /* fixme: Check window settings to make sure still valid + * for new geometry */ +#if 0 + au1200_setlocation(fbdev, 0, win->w[0].xpos, win->w[0].ypos); + au1200_setlocation(fbdev, 1, win->w[1].xpos, win->w[1].ypos); + au1200_setlocation(fbdev, 2, win->w[2].xpos, win->w[2].ypos); + au1200_setlocation(fbdev, 3, win->w[3].xpos, win->w[3].ypos); +#endif + lcd->winenable = winenable; + + /* + * Re-enable screen now that it is configured + */ + lcd->screen |= LCD_SCREEN_SEN; + au_sync(); + + /* Call init of panel */ + if (panel->device_init != NULL) panel->device_init(); + + /* FIX!!!! not appropriate on panel change!!! Global setup/init */ + lcd->intenable = 0; + lcd->intstatus = ~0; + lcd->backcolor = win->mode_backcolor; + + /* Setup Color Key - FIX!!! */ + lcd->colorkey = win->mode_colorkey; + lcd->colorkeymsk = win->mode_colorkeymsk; + + /* Setup HWCursor - FIX!!! Need to support this eventually */ + lcd->hwc.cursorctrl = 0; + lcd->hwc.cursorpos = 0; + lcd->hwc.cursorcolor0 = 0; + lcd->hwc.cursorcolor1 = 0; + lcd->hwc.cursorcolor2 = 0; + lcd->hwc.cursorcolor3 = 0; + + +#if 0 +#define D(X) printk("%25s: %08X\n", #X, X) + D(lcd->screen); + D(lcd->horztiming); + D(lcd->verttiming); + D(lcd->clkcontrol); + D(lcd->pwmdiv); + D(lcd->pwmhi); + D(lcd->outmask); + D(lcd->fifoctrl); + D(lcd->window[0].winctrl0); + D(lcd->window[0].winctrl1); + D(lcd->window[0].winctrl2); + D(lcd->window[0].winbuf0); + D(lcd->window[0].winbuf1); + D(lcd->window[0].winbufctrl); + D(lcd->window[1].winctrl0); + D(lcd->window[1].winctrl1); + D(lcd->window[1].winctrl2); + D(lcd->window[1].winbuf0); + D(lcd->window[1].winbuf1); + D(lcd->window[1].winbufctrl); + D(lcd->window[2].winctrl0); + D(lcd->window[2].winctrl1); + D(lcd->window[2].winctrl2); + D(lcd->window[2].winbuf0); + D(lcd->window[2].winbuf1); + D(lcd->window[2].winbufctrl); + D(lcd->window[3].winctrl0); + D(lcd->window[3].winctrl1); + D(lcd->window[3].winctrl2); + D(lcd->window[3].winbuf0); + D(lcd->window[3].winbuf1); + D(lcd->window[3].winbufctrl); + D(lcd->winenable); + D(lcd->intenable); + D(lcd->intstatus); + D(lcd->backcolor); + D(lcd->winenable); + D(lcd->colorkey); + D(lcd->colorkeymsk); + D(lcd->hwc.cursorctrl); + D(lcd->hwc.cursorpos); + D(lcd->hwc.cursorcolor0); + D(lcd->hwc.cursorcolor1); + D(lcd->hwc.cursorcolor2); + D(lcd->hwc.cursorcolor3); +#endif +} + +static void au1200_setmode(struct au1200fb_device *fbdev) +{ + int plane = fbdev->plane; + /* Window/plane setup */ + lcd->window[plane].winctrl1 = ( 0 + | LCD_WINCTRL1_PRI_N(plane) + | win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */ + ) ; + + au1200_setlocation(fbdev, plane, win->w[plane].xpos, win->w[plane].ypos); + + lcd->window[plane].winctrl2 = ( 0 + | LCD_WINCTRL2_CKMODE_00 + | LCD_WINCTRL2_DBM + | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) + | LCD_WINCTRL2_SCX_1 + | LCD_WINCTRL2_SCY_1 + ) ; + lcd->winenable |= win->w[plane].mode_winenable; + au_sync(); +} + + +/* Inline helpers */ + +/*#define panel_is_dual(panel) ((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ +/*#define panel_is_active(panel)((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ + +#define panel_is_color(panel) ((panel->mode_screen & LCD_SCREEN_PT) <= LCD_SCREEN_PT_CDSTN) + +/* Bitfields format supported by the controller. */ +static struct fb_bitfield rgb_bitfields[][4] = { + /* Red, Green, Blue, Transp */ + [LCD_WINCTRL1_FRM_16BPP655 >> 25] = + { { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPP565 >> 25] = + { { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPP556 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPI1555 >> 25] = + { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPI5551 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPA1555 >> 25] = + { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPA5551 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } }, + + [LCD_WINCTRL1_FRM_24BPP >> 25] = + { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_32BPP >> 25] = + { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 24, 0, 0 } }, +}; + +/*-------------------------------------------------------------------------*/ + +/* Helpers */ + +static void au1200fb_update_fbinfo(struct fb_info *fbi) +{ + /* FIX!!!! This also needs to take the window pixel format into account!!! */ + + /* Update var-dependent FB info */ + if (panel_is_color(panel)) { + if (fbi->var.bits_per_pixel <= 8) { + /* palettized */ + fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR; + fbi->fix.line_length = fbi->var.xres_virtual / + (8/fbi->var.bits_per_pixel); + } else { + /* non-palettized */ + fbi->fix.visual = FB_VISUAL_TRUECOLOR; + fbi->fix.line_length = fbi->var.xres_virtual * (fbi->var.bits_per_pixel / 8); + } + } else { + /* mono FIX!!! mono 8 and 4 bits */ + fbi->fix.visual = FB_VISUAL_MONO10; + fbi->fix.line_length = fbi->var.xres_virtual / 8; + } + + fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual; + print_dbg("line length: %d\n", fbi->fix.line_length); + print_dbg("bits_per_pixel: %d\n", fbi->var.bits_per_pixel); +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 framebuffer driver */ + +/* fb_check_var + * Validate var settings with hardware restrictions and modify it if necessary + */ +static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + u32 pixclock; + int screen_size, plane; + + plane = fbdev->plane; + + /* Make sure that the mode respect all LCD controller and + * panel restrictions. */ + var->xres = win->w[plane].xres; + var->yres = win->w[plane].yres; + + /* No need for virtual resolution support */ + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + var->bits_per_pixel = winbpp(win->w[plane].mode_winctrl1); + + screen_size = var->xres_virtual * var->yres_virtual; + if (var->bits_per_pixel > 8) screen_size *= (var->bits_per_pixel / 8); + else screen_size /= (8/var->bits_per_pixel); + + if (fbdev->fb_len < screen_size) + return -EINVAL; /* Virtual screen is to big, abort */ + + /* FIX!!!! what are the implicaitons of ignoring this for windows ??? */ + /* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel + * clock can only be obtain by dividing this value by an even integer. + * Fallback to a slower pixel clock if necessary. */ + pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); + pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); + + if (AU1200_LCD_MAX_CLK % pixclock) { + int diff = AU1200_LCD_MAX_CLK % pixclock; + pixclock -= diff; + } + + var->pixclock = KHZ2PICOS(pixclock/1000); +#if 0 + if (!panel_is_active(panel)) { + int pcd = AU1200_LCD_MAX_CLK / (pixclock * 2) - 1; + + if (!panel_is_color(panel) + && (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) { + /* STN 8bit mono panel support is up to 6MHz pixclock */ + var->pixclock = KHZ2PICOS(6000); + } else if (!pcd) { + /* Other STN panel support is up to 12MHz */ + var->pixclock = KHZ2PICOS(12000); + } + } +#endif + /* Set bitfield accordingly */ + switch (var->bits_per_pixel) { + case 16: + { + /* 16bpp True color. + * These must be set to MATCH WINCTRL[FORM] */ + int idx; + idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; + var->red = rgb_bitfields[idx][0]; + var->green = rgb_bitfields[idx][1]; + var->blue = rgb_bitfields[idx][2]; + var->transp = rgb_bitfields[idx][3]; + break; + } + + case 32: + { + /* 32bpp True color. + * These must be set to MATCH WINCTRL[FORM] */ + int idx; + idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; + var->red = rgb_bitfields[idx][0]; + var->green = rgb_bitfields[idx][1]; + var->blue = rgb_bitfields[idx][2]; + var->transp = rgb_bitfields[idx][3]; + break; + } + default: + print_dbg("Unsupported depth %dbpp", var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +/* fb_set_par + * Set hardware with var settings. This will enable the controller with a + * specific mode, normally validated with the fb_check_var method + */ +static int au1200fb_fb_set_par(struct fb_info *fbi) +{ + struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + + au1200fb_update_fbinfo(fbi); + au1200_setmode(fbdev); + + return 0; +} + +/* fb_setcolreg + * Set color in LCD palette. + */ +static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *fbi) +{ + volatile u32 *palette = lcd->palette; + u32 value; + + if (regno > (AU1200_LCD_NBR_PALETTE_ENTRIES - 1)) + return -EINVAL; + + if (fbi->var.grayscale) { + /* Convert color to grayscale */ + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + } + + if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + palette = (u32*) fbi->pseudo_palette; + + red >>= (16 - fbi->var.red.length); + green >>= (16 - fbi->var.green.length); + blue >>= (16 - fbi->var.blue.length); + + value = (red << fbi->var.red.offset) | + (green << fbi->var.green.offset)| + (blue << fbi->var.blue.offset); + value &= 0xFFFF; + + } else if (1 /*FIX!!! panel_is_active(fbdev->panel)*/) { + /* COLOR TFT PALLETTIZED (use RGB 565) */ + value = (red & 0xF800)|((green >> 5) & + 0x07E0)|((blue >> 11) & 0x001F); + value &= 0xFFFF; + + } else if (0 /*panel_is_color(fbdev->panel)*/) { + /* COLOR STN MODE */ + value = 0x1234; + value &= 0xFFF; + } else { + /* MONOCHROME MODE */ + value = (green >> 12) & 0x000F; + value &= 0xF; + } + + palette[regno] = value; + + return 0; +} + +/* fb_blank + * Blank the screen. Depending on the mode, the screen will be + * activated with the backlight color, or desactivated + */ +static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi) +{ + /* Short-circuit screen blanking */ + if (noblanking) + return 0; + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + /* printk("turn on panel\n"); */ + au1200_setpanel(panel); + break; + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + /* printk("turn off panel\n"); */ + au1200_setpanel(NULL); + break; + default: + break; + + } + + /* FB_BLANK_NORMAL is a soft blank */ + return (blank_mode == FB_BLANK_NORMAL) ? -EINVAL : 0; +} + +/* fb_mmap + * Map video memory in user space. We don't use the generic fb_mmap + * method mainly to allow the use of the TLB streaming flag (CCA=6) + */ +static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) + +{ + unsigned int len; + unsigned long start=0, off; + struct au1200fb_device *fbdev = (struct au1200fb_device *) info; + +#ifdef CONFIG_PM + au1xxx_pm_access(LCD_pm_dev); +#endif + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { + return -EINVAL; + } + + start = fbdev->fb_phys & PAGE_MASK; + len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); + + off = vma->vm_pgoff << PAGE_SHIFT; + + if ((vma->vm_end - vma->vm_start + off) > len) { + return -EINVAL; + } + + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */ + + vma->vm_flags |= VM_IO; + + return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + + return 0; +} + +static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) +{ + + unsigned int hi1, divider; + + /* SCREEN_SIZE: user cannot reset size, must switch panel choice */ + + if (pdata->flags & SCREEN_BACKCOLOR) + lcd->backcolor = pdata->backcolor; + + if (pdata->flags & SCREEN_BRIGHTNESS) { + + // limit brightness pwm duty to >= 30/1600 + if (pdata->brightness < 30) { + pdata->brightness = 30; + } + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + hi1 = (lcd->pwmhi >> 16) + 1; + hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8); + lcd->pwmhi &= 0xFFFF; + lcd->pwmhi |= (hi1 << 16); + } + + if (pdata->flags & SCREEN_COLORKEY) + lcd->colorkey = pdata->colorkey; + + if (pdata->flags & SCREEN_MASK) + lcd->colorkeymsk = pdata->mask; + au_sync(); +} + +static void get_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) +{ + unsigned int hi1, divider; + + pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1; + pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1; + + pdata->backcolor = lcd->backcolor; + pdata->colorkey = lcd->colorkey; + pdata->mask = lcd->colorkeymsk; + + // brightness + hi1 = (lcd->pwmhi >> 16) + 1; + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + pdata->brightness = ((hi1 << 8) / divider) - 1; + au_sync(); +} + +static void set_window(unsigned int plane, + struct au1200_lcd_window_regs_t *pdata) +{ + unsigned int val, bpp; + + /* Window control register 0 */ + if (pdata->flags & WIN_POSITION) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | + LCD_WINCTRL0_OY); + val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX); + val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY); + lcd->window[plane].winctrl0 = val; + } + if (pdata->flags & WIN_ALPHA_COLOR) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A); + val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A); + lcd->window[plane].winctrl0 = val; + } + if (pdata->flags & WIN_ALPHA_MODE) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN); + val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN); + lcd->window[plane].winctrl0 = val; + } + + /* Window control register 1 */ + if (pdata->flags & WIN_PRIORITY) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI); + val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_CHANNEL) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE); + val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_BUFFER_FORMAT) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM); + val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_COLOR_ORDER) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO); + val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_PIXEL_ORDER) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO); + val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_SIZE) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | + LCD_WINCTRL1_SZY); + val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX); + val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY); + lcd->window[plane].winctrl1 = val; + /* program buffer line width */ + bpp = winbpp(val) / 8; + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX); + val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX); + lcd->window[plane].winctrl2 = val; + } + + /* Window control register 2 */ + if (pdata->flags & WIN_COLORKEY_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE); + val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE); + lcd->window[plane].winctrl2 = val; + } + if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM); + val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM); + lcd->window[plane].winctrl2 = val; + } + if (pdata->flags & WIN_RAM_ARRAY_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM); + val |= ((pdata->ram_array_mode << 21) & LCD_WINCTRL2_RAM); + lcd->window[plane].winctrl2 = val; + } + + /* Buffer line width programmed with WIN_SIZE */ + + if (pdata->flags & WIN_BUFFER_SCALE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | + LCD_WINCTRL2_SCY); + val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX); + val |= ((pdata->ysize) & LCD_WINCTRL2_SCY); + lcd->window[plane].winctrl2 = val; + } + + if (pdata->flags & WIN_ENABLE) { + val = lcd->winenable; + val &= ~(1<enable & 1) << plane; + lcd->winenable = val; + } + au_sync(); +} + +static void get_window(unsigned int plane, + struct au1200_lcd_window_regs_t *pdata) +{ + /* Window control register 0 */ + pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21; + pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10; + pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2; + pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1; + + /* Window control register 1 */ + pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30; + pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29; + pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25; + pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24; + pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22; + pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1; + pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1; + + /* Window control register 2 */ + pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24; + pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23; + pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21; + + pdata->enable = (lcd->winenable >> plane) & 1; + au_sync(); +} + +static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int plane; + int val; + +#ifdef CONFIG_PM + au1xxx_pm_access(LCD_pm_dev); +#endif + + plane = fbinfo2index(info); + print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); + + if (cmd == AU1200_LCD_FB_IOCTL) { + struct au1200_lcd_iodata_t iodata; + + if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata))) + return -EFAULT; + + print_dbg("FB IOCTL called\n"); + + switch (iodata.subcmd) { + case AU1200_LCD_SET_SCREEN: + print_dbg("AU1200_LCD_SET_SCREEN\n"); + set_global(cmd, &iodata.global); + break; + + case AU1200_LCD_GET_SCREEN: + print_dbg("AU1200_LCD_GET_SCREEN\n"); + get_global(cmd, &iodata.global); + break; + + case AU1200_LCD_SET_WINDOW: + print_dbg("AU1200_LCD_SET_WINDOW\n"); + set_window(plane, &iodata.window); + break; + + case AU1200_LCD_GET_WINDOW: + print_dbg("AU1200_LCD_GET_WINDOW\n"); + get_window(plane, &iodata.window); + break; + + case AU1200_LCD_SET_PANEL: + print_dbg("AU1200_LCD_SET_PANEL\n"); + if ((iodata.global.panel_choice >= 0) && + (iodata.global.panel_choice < + NUM_PANELS)) + { + struct panel_settings *newpanel; + panel_index = iodata.global.panel_choice; + newpanel = &known_lcd_panels[panel_index]; + au1200_setpanel(newpanel); + } + break; + + case AU1200_LCD_GET_PANEL: + print_dbg("AU1200_LCD_GET_PANEL\n"); + iodata.global.panel_choice = panel_index; + break; + + default: + return -EINVAL; + } + + val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata)); + if (val) { + print_dbg("error: could not copy %d bytes\n", val); + return -EFAULT; + } + } + + return 0; +} + + +static struct fb_ops au1200fb_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = au1200fb_fb_check_var, + .fb_set_par = au1200fb_fb_set_par, + .fb_setcolreg = au1200fb_fb_setcolreg, + .fb_blank = au1200fb_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_sync = NULL, + .fb_ioctl = au1200fb_ioctl, + .fb_mmap = au1200fb_fb_mmap, +}; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs) +{ + /* Nothing to do for now, just clear any pending interrupt */ + lcd->intstatus = lcd->intstatus; + au_sync(); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 LCD device probe helpers */ + +static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) +{ + struct fb_info *fbi = &fbdev->fb_info; + int bpp; + + memset(fbi, 0, sizeof(struct fb_info)); + fbi->fbops = &au1200fb_fb_ops; + + bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); + + /* Copy monitor specs from panel data */ + /* fixme: we're setting up LCD controller windows, so these dont give a + damn as to what the monitor specs are (the panel itself does, but that + isnt done here...so maybe need a generic catchall monitor setting??? */ + memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs)); + + /* We first try the user mode passed in argument. If that failed, + * or if no one has been specified, we default to the first mode of the + * panel list. Note that after this call, var data will be set */ + if (!fb_find_mode(&fbi->var, + fbi, + NULL, /* drv_info.opt_mode, */ + fbi->monspecs.modedb, + fbi->monspecs.modedb_len, + fbi->monspecs.modedb, + bpp)) { + + print_err("Cannot find valid mode for panel %s", panel->name); + return -EFAULT; + } + + fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + if (!fbi->pseudo_palette) { + return -ENOMEM; + } + memset(fbi->pseudo_palette, 0, sizeof(u32) * 16); + + if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + print_err("Fail to allocate colormap (%d entries)", + AU1200_LCD_NBR_PALETTE_ENTRIES); + kfree(fbi->pseudo_palette); + return -EFAULT; + } + + strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); + fbi->fix.smem_start = fbdev->fb_phys; + fbi->fix.smem_len = fbdev->fb_len; + fbi->fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fix.xpanstep = 0; + fbi->fix.ypanstep = 0; + fbi->fix.mmio_start = 0; + fbi->fix.mmio_len = 0; + fbi->fix.accel = FB_ACCEL_NONE; + + fbi->screen_base = (char __iomem *) fbdev->fb_mem; + + au1200fb_update_fbinfo(fbi); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 LCD controller device driver */ + +static int au1200fb_drv_probe(struct device *dev) +{ + struct au1200fb_device *fbdev; + unsigned long page; + int bpp, plane, ret; + + if (!dev) + return -EINVAL; + + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { + bpp = winbpp(win->w[plane].mode_winctrl1); + if (win->w[plane].xres == 0) + win->w[plane].xres = panel->Xres; + if (win->w[plane].yres == 0) + win->w[plane].yres = panel->Yres; + + fbdev = &_au1200fb_devices[plane]; + memset(fbdev, 0, sizeof(struct au1200fb_device)); + fbdev->plane = plane; + + /* Allocate the framebuffer to the maximum screen size */ + fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; + + fbdev->fb_mem = dma_alloc_noncoherent(dev, + PAGE_ALIGN(fbdev->fb_len), + &fbdev->fb_phys, GFP_KERNEL); + if (!fbdev->fb_mem) { + print_err("fail to allocate frambuffer (size: %dK))", + fbdev->fb_len / 1024); + return -ENOMEM; + } + + /* + * Set page reserved so that mmap will work. This is necessary + * since we'll be remapping normal memory. + */ + for (page = (unsigned long)fbdev->fb_phys; + page < PAGE_ALIGN((unsigned long)fbdev->fb_phys + + fbdev->fb_len); + page += PAGE_SIZE) { + SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */ + } + print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); + print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); + + /* Init FB data */ + if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) + goto failed; + + /* Register new framebuffer */ + if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { + print_err("cannot register new framebuffer"); + goto failed; + } + + au1200fb_fb_set_par(&fbdev->fb_info); + +#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) + if (plane == 0) + if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { + /* Start display and show logo on boot */ + fb_set_cmap(&fbdev->fb_info.cmap, + &fbdev->fb_info); + + fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); + } +#endif + } + + /* Now hook interrupt too */ + if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, + SA_INTERRUPT | SA_SHIRQ, "lcd", (void *)dev)) < 0) { + print_err("fail to request interrupt line %d (err: %d)", + AU1200_LCD_INT, ret); + goto failed; + } + + return 0; + +failed: + /* NOTE: This only does the current plane/window that failed; others are still active */ + if (fbdev->fb_mem) + dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), + fbdev->fb_mem, fbdev->fb_phys); + if (fbdev->fb_info.cmap.len != 0) + fb_dealloc_cmap(&fbdev->fb_info.cmap); + if (fbdev->fb_info.pseudo_palette) + kfree(fbdev->fb_info.pseudo_palette); + if (plane == 0) + free_irq(AU1200_LCD_INT, (void*)dev); + return ret; +} + +static int au1200fb_drv_remove(struct device *dev) +{ + struct au1200fb_device *fbdev; + int plane; + + if (!dev) + return -ENODEV; + + /* Turn off the panel */ + au1200_setpanel(NULL); + + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) + { + fbdev = &_au1200fb_devices[plane]; + + /* Clean up all probe data */ + unregister_framebuffer(&fbdev->fb_info); + if (fbdev->fb_mem) + dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), + fbdev->fb_mem, fbdev->fb_phys); + if (fbdev->fb_info.cmap.len != 0) + fb_dealloc_cmap(&fbdev->fb_info.cmap); + if (fbdev->fb_info.pseudo_palette) + kfree(fbdev->fb_info.pseudo_palette); + } + + free_irq(AU1200_LCD_INT, (void *)dev); + + return 0; +} + +#ifdef CONFIG_PM +static int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level) +{ + /* TODO */ + return 0; +} + +static int au1200fb_drv_resume(struct device *dev, u32 level) +{ + /* TODO */ + return 0; +} +#endif /* CONFIG_PM */ + +static struct device_driver au1200fb_driver = { + .name = "au1200-lcd", + .bus = &platform_bus_type, + .probe = au1200fb_drv_probe, + .remove = au1200fb_drv_remove, +#ifdef CONFIG_PM + .suspend = au1200fb_drv_suspend, + .resume = au1200fb_drv_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +/* Kernel driver */ + +static void au1200fb_setup(void) +{ + char* options = NULL; + char* this_opt; + int num_panels = ARRAY_SIZE(known_lcd_panels); + int panel_idx = -1; + + fb_get_options(DRIVER_NAME, &options); + + if (options) { + while ((this_opt = strsep(&options,",")) != NULL) { + /* Panel option - can be panel name, + * "bs" for board-switch, or number/index */ + if (!strncmp(this_opt, "panel:", 6)) { + int i; + long int li; + char *endptr; + this_opt += 6; + /* First check for index, which allows + * to short circuit this mess */ + li = simple_strtol(this_opt, &endptr, 0); + if (*endptr == '\0') { + panel_idx = (int)li; + } + else if (strcmp(this_opt, "bs") == 0) { + extern int board_au1200fb_panel(void); + panel_idx = board_au1200fb_panel(); + } + + else + for (i = 0; i < num_panels; i++) { + if (!strcmp(this_opt, known_lcd_panels[i].name)) { + panel_idx = i; + break; + } + } + + if ((panel_idx < 0) || (panel_idx >= num_panels)) { + print_warn("Panel %s not supported!", this_opt); + } + else + panel_index = panel_idx; + } + + else if (strncmp(this_opt, "nohwcursor", 10) == 0) { + nohwcursor = 1; + } + + /* Unsupported option */ + else { + print_warn("Unsupported option \"%s\"", this_opt); + } + } + } +} + +#ifdef CONFIG_PM +static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, + au1xxx_request_t request, void *data) { + int retval = -1; + unsigned int d = 0; + unsigned int brightness = 0; + + if (request == AU1XXX_PM_SLEEP) { + board_au1200fb_panel_shutdown(); + } + else if (request == AU1XXX_PM_WAKEUP) { + if(dev->prev_state == SLEEP_STATE) + { + int plane; + au1200_setpanel(panel); + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { + struct au1200fb_device *fbdev; + fbdev = &_au1200fb_devices[plane]; + au1200fb_fb_set_par(&fbdev->fb_info); + } + } + + d = *((unsigned int*)data); + if(d <=10) brightness = 26; + else if(d<=20) brightness = 51; + else if(d<=30) brightness = 77; + else if(d<=40) brightness = 102; + else if(d<=50) brightness = 128; + else if(d<=60) brightness = 153; + else if(d<=70) brightness = 179; + else if(d<=80) brightness = 204; + else if(d<=90) brightness = 230; + else brightness = 255; + set_brightness(brightness); + } else if (request == AU1XXX_PM_GETSTATUS) { + return dev->cur_state; + } else if (request == AU1XXX_PM_ACCESS) { + if (dev->cur_state != SLEEP_STATE) + return retval; + else { + au1200_setpanel(panel); + } + } else if (request == AU1XXX_PM_IDLE) { + } else if (request == AU1XXX_PM_CLEANUP) { + } + + return retval; +} +#endif + +static int __init au1200fb_init(void) +{ + print_info("" DRIVER_DESC ""); + + /* Setup driver with options */ + au1200fb_setup(); + + /* Point to the panel selected */ + panel = &known_lcd_panels[panel_index]; + win = &windows[window_index]; + + printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); + printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); + + /* Kickstart the panel, the framebuffers/windows come soon enough */ + au1200_setpanel(panel); + + #ifdef CONFIG_PM + LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); + if ( LCD_pm_dev == NULL) + printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); + else + printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); + #endif + + return driver_register(&au1200fb_driver); +} + +static void __exit au1200fb_cleanup(void) +{ + driver_unregister(&au1200fb_driver); +} + +module_init(au1200fb_init); +module_exit(au1200fb_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +/* + * BRIEF MODULE DESCRIPTION + * Au1200 LCD Driver. + * + * Copyright 2004-2005 AMD + * Author: AMD + * + * Based on: + * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device + * Created 28 Dec 1997 by Geert Uytterhoeven + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "au1200fb.h" + +#ifdef CONFIG_PM +#include +#endif + +#ifndef CONFIG_FB_AU1200_DEVS +#define CONFIG_FB_AU1200_DEVS 4 +#endif + +#define DRIVER_NAME "au1200fb" +#define DRIVER_DESC "LCD controller driver for AU1200 processors" + +#define DEBUG 1 + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) + +#if DEBUG +#define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + + +#define AU1200_LCD_FB_IOCTL 0x46FF + +#define AU1200_LCD_SET_SCREEN 1 +#define AU1200_LCD_GET_SCREEN 2 +#define AU1200_LCD_SET_WINDOW 3 +#define AU1200_LCD_GET_WINDOW 4 +#define AU1200_LCD_SET_PANEL 5 +#define AU1200_LCD_GET_PANEL 6 + +#define SCREEN_SIZE (1<< 1) +#define SCREEN_BACKCOLOR (1<< 2) +#define SCREEN_BRIGHTNESS (1<< 3) +#define SCREEN_COLORKEY (1<< 4) +#define SCREEN_MASK (1<< 5) + +struct au1200_lcd_global_regs_t { + unsigned int flags; + unsigned int xsize; + unsigned int ysize; + unsigned int backcolor; + unsigned int brightness; + unsigned int colorkey; + unsigned int mask; + unsigned int panel_choice; + char panel_desc[80]; + +}; + +#define WIN_POSITION (1<< 0) +#define WIN_ALPHA_COLOR (1<< 1) +#define WIN_ALPHA_MODE (1<< 2) +#define WIN_PRIORITY (1<< 3) +#define WIN_CHANNEL (1<< 4) +#define WIN_BUFFER_FORMAT (1<< 5) +#define WIN_COLOR_ORDER (1<< 6) +#define WIN_PIXEL_ORDER (1<< 7) +#define WIN_SIZE (1<< 8) +#define WIN_COLORKEY_MODE (1<< 9) +#define WIN_DOUBLE_BUFFER_MODE (1<< 10) +#define WIN_RAM_ARRAY_MODE (1<< 11) +#define WIN_BUFFER_SCALE (1<< 12) +#define WIN_ENABLE (1<< 13) + +struct au1200_lcd_window_regs_t { + unsigned int flags; + unsigned int xpos; + unsigned int ypos; + unsigned int alpha_color; + unsigned int alpha_mode; + unsigned int priority; + unsigned int channel; + unsigned int buffer_format; + unsigned int color_order; + unsigned int pixel_order; + unsigned int xsize; + unsigned int ysize; + unsigned int colorkey_mode; + unsigned int double_buffer_mode; + unsigned int ram_array_mode; + unsigned int xscale; + unsigned int yscale; + unsigned int enable; +}; + + +struct au1200_lcd_iodata_t { + unsigned int subcmd; + struct au1200_lcd_global_regs_t global; + struct au1200_lcd_window_regs_t window; +}; + +#if defined(__BIG_ENDIAN) +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 +#else +#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 +#endif +#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 + +/* Private, per-framebuffer management information (independent of the panel itself) */ +struct au1200fb_device { + struct fb_info fb_info; /* FB driver info record */ + + int plane; + unsigned char* fb_mem; /* FrameBuffer memory map */ + unsigned int fb_len; + dma_addr_t fb_phys; +}; + +static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; +/********************************************************************/ + +/* LCD controller restrictions */ +#define AU1200_LCD_MAX_XRES 1280 +#define AU1200_LCD_MAX_YRES 1024 +#define AU1200_LCD_MAX_BPP 32 +#define AU1200_LCD_MAX_CLK 96000000 /* fixme: this needs to go away ? */ +#define AU1200_LCD_NBR_PALETTE_ENTRIES 256 + +/* Default number of visible screen buffer to allocate */ +#define AU1200FB_NBR_VIDEO_BUFFERS 1 + +/********************************************************************/ + +static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; +static int window_index = 2; /* default is zero */ +static int panel_index = 2; /* default is zero */ +static struct window_settings *win; +static struct panel_settings *panel; +static int noblanking = 1; +static int nohwcursor = 0; + +struct window_settings { + unsigned char name[64]; + uint32 mode_backcolor; + uint32 mode_colorkey; + uint32 mode_colorkeymsk; + struct { + int xres; + int yres; + int xpos; + int ypos; + uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */ + uint32 mode_winenable; + } w[4]; +}; + +#if defined(__BIG_ENDIAN) +#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00 +#else +#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01 +#endif + +extern int board_au1200fb_panel_init (void); +extern int board_au1200fb_panel_shutdown (void); + +#ifdef CONFIG_PM +int au1200fb_pm_callback(au1xxx_power_dev_t *dev, + au1xxx_request_t request, void *data); +au1xxx_power_dev_t *LCD_pm_dev; +#endif + +/* + * Default window configurations + */ +static struct window_settings windows[] = { + { /* Index 0 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 100, 100, 100, 100, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ LCD_WINENABLE_WEN1, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + + { /* Index 1 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 320, 240, 5, 5, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP | + LCD_WINCTRL1_PO_00, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 + | LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 100, 100, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, + }, + { + /* xres, yres, xpos, ypos */ 200, 25, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + { /* Index 2 */ + "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", + /* mode_backcolor */ 0x006600ff, + /* mode_colorkey,msk*/ 0, 0, + { + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ LCD_WINENABLE_WEN0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP, + /* mode_winenable*/ 0, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_32BPP | + LCD_WINCTRL1_PO_00|LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, + }, + { + /* xres, yres, xpos, ypos */ 0, 0, 0, 0, + /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | + LCD_WINCTRL1_PO_16BPP | + LCD_WINCTRL1_PIPE, + /* mode_winenable*/ 0, + }, + }, + }, + /* Need VGA 640 @ 24bpp, @ 32bpp */ + /* Need VGA 800 @ 24bpp, @ 32bpp */ + /* Need VGA 1024 @ 24bpp, @ 32bpp */ +}; + +/* + * Controller configurations for various panels. + */ + +struct panel_settings +{ + const char name[25]; /* Full name _ */ + + struct fb_monspecs monspecs; /* FB monitor specs */ + + /* panel timings */ + uint32 mode_screen; + uint32 mode_horztiming; + uint32 mode_verttiming; + uint32 mode_clkcontrol; + uint32 mode_pwmdiv; + uint32 mode_pwmhi; + uint32 mode_outmask; + uint32 mode_fifoctrl; + uint32 mode_toyclksrc; + uint32 mode_backlight; + uint32 mode_auxpll; + int (*device_init)(void); + int (*device_shutdown)(void); +#define Xres min_xres +#define Yres min_yres + u32 min_xres; /* Minimum horizontal resolution */ + u32 max_xres; /* Maximum horizontal resolution */ + u32 min_yres; /* Minimum vertical resolution */ + u32 max_yres; /* Maximum vertical resolution */ +}; + +/********************************************************************/ +/* fixme: Maybe a modedb for the CRT ? otherwise panels should be as-is */ + +/* List of panels known to work with the AU1200 LCD controller. + * To add a new panel, enter the same specifications as the + * Generic_TFT one, and MAKE SURE that it doesn't conflicts + * with the controller restrictions. Restrictions are: + * + * STN color panels: max_bpp <= 12 + * STN mono panels: max_bpp <= 4 + * TFT panels: max_bpp <= 16 + * max_xres <= 800 + * max_yres <= 600 + */ +static struct panel_settings known_lcd_panels[] = +{ + [0] = { /* QVGA 320x240 H:33.3kHz V:110Hz */ + .name = "QVGA_320x240", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(320) | + LCD_SCREEN_SY_N(240), + .mode_horztiming = 0x00c4623b, + .mode_verttiming = 0x00502814, + .mode_clkcontrol = 0x00020002, /* /4=24Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 320, 320, + 240, 240, + }, + + [1] = { /* VGA 640x480 H:30.3kHz V:58Hz */ + .name = "VGA_640x480", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x13f9df80, + .mode_horztiming = 0x003c5859, + .mode_verttiming = 0x00741201, + .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 640, 480, + 640, 480, + }, + + [2] = { /* SVGA 800x600 H:46.1kHz V:69Hz */ + .name = "SVGA_800x600", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x18fa5780, + .mode_horztiming = 0x00dc7e77, + .mode_verttiming = 0x00584805, + .mode_clkcontrol = 0x00020000, /* /2=48Mhz */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 800, 800, + 600, 600, + }, + + [3] = { /* XVGA 1024x768 H:56.2kHz V:70Hz */ + .name = "XVGA_1024x768", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x1ffaff80, + .mode_horztiming = 0x007d0e57, + .mode_verttiming = 0x00740a01, + .mode_clkcontrol = 0x000A0000, /* /1 */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 6, /* 72MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 1024, 1024, + 768, 768, + }, + + [4] = { /* XVGA XVGA 1280x1024 H:68.5kHz V:65Hz */ + .name = "XVGA_1280x1024", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x27fbff80, + .mode_horztiming = 0x00cdb2c7, + .mode_verttiming = 0x00600002, + .mode_clkcontrol = 0x000A0000, /* /1 */ + .mode_pwmdiv = 0x00000000, + .mode_pwmhi = 0x00000000, + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 10, /* 120MHz AUXPLL */ + .device_init = NULL, + .device_shutdown = NULL, + 1280, 1280, + 1024, 1024, + }, + + [5] = { /* Samsung 1024x768 TFT */ + .name = "Samsung_1024x768_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = 0x1ffaff80, + .mode_horztiming = 0x018cc677, + .mode_verttiming = 0x00241217, + .mode_clkcontrol = 0x00000000, /* SCB 0x1 /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, /* SCB 0x0 */ + .mode_pwmhi = 0x03400000, /* SCB 0x0 */ + .mode_outmask = 0x00FFFFFF, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 1024, 1024, + 768, 768, + }, + + [6] = { /* Toshiba 640x480 TFT */ + .name = "Toshiba_640x480_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(640) | + LCD_SCREEN_SY_N(480), + .mode_horztiming = LCD_HORZTIMING_HPW_N(96) | + LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51), + .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | + LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32), + .mode_clkcontrol = 0x00000000, /* /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 640, 480, + 640, 480, + }, + + [7] = { /* Sharp 320x240 TFT */ + .name = "Sharp_320x240_TFT", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 12500, + .hfmax = 20000, + .vfmin = 38, + .vfmax = 81, + .dclkmin = 4500000, + .dclkmax = 6800000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(320) | + LCD_SCREEN_SY_N(240), + .mode_horztiming = LCD_HORZTIMING_HPW_N(60) | + LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2), + .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | + LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5), + .mode_clkcontrol = LCD_CLKCONTROL_PCD_N(7), /*16=6Mhz*/ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 320, 320, + 240, 240, + }, + + [8] = { /* Toppoly TD070WGCB2 7" 856x480 TFT */ + .name = "Toppoly_TD070WGCB2", + .monspecs = { + .modedb = NULL, + .modedb_len = 0, + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 60, + .vfmax = 60, + .dclkmin = 6000000, + .dclkmax = 28000000, + .input = FB_DISP_RGB, + }, + .mode_screen = LCD_SCREEN_SX_N(856) | + LCD_SCREEN_SY_N(480), + .mode_horztiming = LCD_HORZTIMING_HND2_N(43) | + LCD_HORZTIMING_HND1_N(43) | LCD_HORZTIMING_HPW_N(114), + .mode_verttiming = LCD_VERTTIMING_VND2_N(20) | + LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4), + .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ + .mode_pwmdiv = 0x8000063f, + .mode_pwmhi = 0x03400000, + .mode_outmask = 0x00fcfcfc, + .mode_fifoctrl = 0x2f2f2f2f, + .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ + .mode_backlight = 0x00000000, + .mode_auxpll = 8, /* 96MHz AUXPLL */ + .device_init = board_au1200fb_panel_init, + .device_shutdown = board_au1200fb_panel_shutdown, + 856, 856, + 480, 480, + }, +}; + +#define NUM_PANELS (ARRAY_SIZE(known_lcd_panels)) + +/********************************************************************/ + +#ifdef CONFIG_PM +static int set_brightness(unsigned int brightness) +{ + unsigned int hi1, divider; + + /* limit brightness pwm duty to >= 30/1600 */ + if (brightness < 30) { + brightness = 30; + } + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + hi1 = (lcd->pwmhi >> 16) + 1; + hi1 = (((brightness & 0xFF) + 1) * divider >> 8); + lcd->pwmhi &= 0xFFFF; + lcd->pwmhi |= (hi1 << 16); + + return brightness; +} +#endif /* CONFIG_PM */ + +static int winbpp (unsigned int winctrl1) +{ + int bits = 0; + + /* how many bits are needed for each pixel format */ + switch (winctrl1 & LCD_WINCTRL1_FRM) { + case LCD_WINCTRL1_FRM_1BPP: + bits = 1; + break; + case LCD_WINCTRL1_FRM_2BPP: + bits = 2; + break; + case LCD_WINCTRL1_FRM_4BPP: + bits = 4; + break; + case LCD_WINCTRL1_FRM_8BPP: + bits = 8; + break; + case LCD_WINCTRL1_FRM_12BPP: + case LCD_WINCTRL1_FRM_16BPP655: + case LCD_WINCTRL1_FRM_16BPP565: + case LCD_WINCTRL1_FRM_16BPP556: + case LCD_WINCTRL1_FRM_16BPPI1555: + case LCD_WINCTRL1_FRM_16BPPI5551: + case LCD_WINCTRL1_FRM_16BPPA1555: + case LCD_WINCTRL1_FRM_16BPPA5551: + bits = 16; + break; + case LCD_WINCTRL1_FRM_24BPP: + case LCD_WINCTRL1_FRM_32BPP: + bits = 32; + break; + } + + return bits; +} + +static int fbinfo2index (struct fb_info *fb_info) +{ + int i; + + for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { + if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) + return i; + } + printk("au1200fb: ERROR: fbinfo2index failed!\n"); + return -1; +} + +static int au1200_setlocation (struct au1200fb_device *fbdev, int plane, + int xpos, int ypos) +{ + uint32 winctrl0, winctrl1, winenable, fb_offset = 0; + int xsz, ysz; + + /* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */ + + winctrl0 = lcd->window[plane].winctrl0; + winctrl1 = lcd->window[plane].winctrl1; + winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN); + winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY); + + /* Check for off-screen adjustments */ + xsz = win->w[plane].xres; + ysz = win->w[plane].yres; + if ((xpos + win->w[plane].xres) > panel->Xres) { + /* Off-screen to the right */ + xsz = panel->Xres - xpos; /* off by 1 ??? */ + /*printk("off screen right\n");*/ + } + + if ((ypos + win->w[plane].yres) > panel->Yres) { + /* Off-screen to the bottom */ + ysz = panel->Yres - ypos; /* off by 1 ??? */ + /*printk("off screen bottom\n");*/ + } + + if (xpos < 0) { + /* Off-screen to the left */ + xsz = win->w[plane].xres + xpos; + fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8); + xpos = 0; + /*printk("off screen left\n");*/ + } + + if (ypos < 0) { + /* Off-screen to the top */ + ysz = win->w[plane].yres + ypos; + /* fixme: fb_offset += ((0-ypos)*fb_pars[plane].line_length); */ + ypos = 0; + /*printk("off screen top\n");*/ + } + + /* record settings */ + win->w[plane].xpos = xpos; + win->w[plane].ypos = ypos; + + xsz -= 1; + ysz -= 1; + winctrl0 |= (xpos << 21); + winctrl0 |= (ypos << 10); + winctrl1 |= (xsz << 11); + winctrl1 |= (ysz << 0); + + /* Disable the window while making changes, then restore WINEN */ + winenable = lcd->winenable & (1 << plane); + au_sync(); + lcd->winenable &= ~(1 << plane); + lcd->window[plane].winctrl0 = winctrl0; + lcd->window[plane].winctrl1 = winctrl1; + lcd->window[plane].winbuf0 = + lcd->window[plane].winbuf1 = fbdev->fb_phys; + lcd->window[plane].winbufctrl = 0; /* select winbuf0 */ + lcd->winenable |= winenable; + au_sync(); + + return 0; +} + +static void au1200_setpanel (struct panel_settings *newpanel) +{ + /* + * Perform global setup/init of LCD controller + */ + uint32 winenable; + + /* Make sure all windows disabled */ + winenable = lcd->winenable; + lcd->winenable = 0; + au_sync(); + /* + * Ensure everything is disabled before reconfiguring + */ + if (lcd->screen & LCD_SCREEN_SEN) { + /* Wait for vertical sync period */ + lcd->intstatus = LCD_INT_SS; + while ((lcd->intstatus & LCD_INT_SS) == 0) { + au_sync(); + } + + lcd->screen &= ~LCD_SCREEN_SEN; /*disable the controller*/ + + do { + lcd->intstatus = lcd->intstatus; /*clear interrupts*/ + au_sync(); + /*wait for controller to shut down*/ + } while ((lcd->intstatus & LCD_INT_SD) == 0); + + /* Call shutdown of current panel (if up) */ + /* this must occur last, because if an external clock is driving + the controller, the clock cannot be turned off before first + shutting down the controller. + */ + if (panel->device_shutdown != NULL) + panel->device_shutdown(); + } + + /* Newpanel == NULL indicates a shutdown operation only */ + if (newpanel == NULL) + return; + + panel = newpanel; + + printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres); + + /* + * Setup clocking if internal LCD clock source (assumes sys_auxpll valid) + */ + if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT)) + { + uint32 sys_clksrc; + au_writel(panel->mode_auxpll, SYS_AUXPLL); + sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; + sys_clksrc |= panel->mode_toyclksrc; + au_writel(sys_clksrc, SYS_CLKSRC); + } + + /* + * Configure panel timings + */ + lcd->screen = panel->mode_screen; + lcd->horztiming = panel->mode_horztiming; + lcd->verttiming = panel->mode_verttiming; + lcd->clkcontrol = panel->mode_clkcontrol; + lcd->pwmdiv = panel->mode_pwmdiv; + lcd->pwmhi = panel->mode_pwmhi; + lcd->outmask = panel->mode_outmask; + lcd->fifoctrl = panel->mode_fifoctrl; + au_sync(); + + /* fixme: Check window settings to make sure still valid + * for new geometry */ +#if 0 + au1200_setlocation(fbdev, 0, win->w[0].xpos, win->w[0].ypos); + au1200_setlocation(fbdev, 1, win->w[1].xpos, win->w[1].ypos); + au1200_setlocation(fbdev, 2, win->w[2].xpos, win->w[2].ypos); + au1200_setlocation(fbdev, 3, win->w[3].xpos, win->w[3].ypos); +#endif + lcd->winenable = winenable; + + /* + * Re-enable screen now that it is configured + */ + lcd->screen |= LCD_SCREEN_SEN; + au_sync(); + + /* Call init of panel */ + if (panel->device_init != NULL) panel->device_init(); + + /* FIX!!!! not appropriate on panel change!!! Global setup/init */ + lcd->intenable = 0; + lcd->intstatus = ~0; + lcd->backcolor = win->mode_backcolor; + + /* Setup Color Key - FIX!!! */ + lcd->colorkey = win->mode_colorkey; + lcd->colorkeymsk = win->mode_colorkeymsk; + + /* Setup HWCursor - FIX!!! Need to support this eventually */ + lcd->hwc.cursorctrl = 0; + lcd->hwc.cursorpos = 0; + lcd->hwc.cursorcolor0 = 0; + lcd->hwc.cursorcolor1 = 0; + lcd->hwc.cursorcolor2 = 0; + lcd->hwc.cursorcolor3 = 0; + + +#if 0 +#define D(X) printk("%25s: %08X\n", #X, X) + D(lcd->screen); + D(lcd->horztiming); + D(lcd->verttiming); + D(lcd->clkcontrol); + D(lcd->pwmdiv); + D(lcd->pwmhi); + D(lcd->outmask); + D(lcd->fifoctrl); + D(lcd->window[0].winctrl0); + D(lcd->window[0].winctrl1); + D(lcd->window[0].winctrl2); + D(lcd->window[0].winbuf0); + D(lcd->window[0].winbuf1); + D(lcd->window[0].winbufctrl); + D(lcd->window[1].winctrl0); + D(lcd->window[1].winctrl1); + D(lcd->window[1].winctrl2); + D(lcd->window[1].winbuf0); + D(lcd->window[1].winbuf1); + D(lcd->window[1].winbufctrl); + D(lcd->window[2].winctrl0); + D(lcd->window[2].winctrl1); + D(lcd->window[2].winctrl2); + D(lcd->window[2].winbuf0); + D(lcd->window[2].winbuf1); + D(lcd->window[2].winbufctrl); + D(lcd->window[3].winctrl0); + D(lcd->window[3].winctrl1); + D(lcd->window[3].winctrl2); + D(lcd->window[3].winbuf0); + D(lcd->window[3].winbuf1); + D(lcd->window[3].winbufctrl); + D(lcd->winenable); + D(lcd->intenable); + D(lcd->intstatus); + D(lcd->backcolor); + D(lcd->winenable); + D(lcd->colorkey); + D(lcd->colorkeymsk); + D(lcd->hwc.cursorctrl); + D(lcd->hwc.cursorpos); + D(lcd->hwc.cursorcolor0); + D(lcd->hwc.cursorcolor1); + D(lcd->hwc.cursorcolor2); + D(lcd->hwc.cursorcolor3); +#endif +} + +static void au1200_setmode(struct au1200fb_device *fbdev) +{ + int plane = fbdev->plane; + /* Window/plane setup */ + lcd->window[plane].winctrl1 = ( 0 + | LCD_WINCTRL1_PRI_N(plane) + | win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */ + ) ; + + au1200_setlocation(fbdev, plane, win->w[plane].xpos, win->w[plane].ypos); + + lcd->window[plane].winctrl2 = ( 0 + | LCD_WINCTRL2_CKMODE_00 + | LCD_WINCTRL2_DBM + | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) + | LCD_WINCTRL2_SCX_1 + | LCD_WINCTRL2_SCY_1 + ) ; + lcd->winenable |= win->w[plane].mode_winenable; + au_sync(); +} + + +/* Inline helpers */ + +/*#define panel_is_dual(panel) ((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ +/*#define panel_is_active(panel)((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ + +#define panel_is_color(panel) ((panel->mode_screen & LCD_SCREEN_PT) <= LCD_SCREEN_PT_CDSTN) + +/* Bitfields format supported by the controller. */ +static struct fb_bitfield rgb_bitfields[][4] = { + /* Red, Green, Blue, Transp */ + [LCD_WINCTRL1_FRM_16BPP655 >> 25] = + { { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPP565 >> 25] = + { { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPP556 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPI1555 >> 25] = + { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPI5551 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPA1555 >> 25] = + { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } }, + + [LCD_WINCTRL1_FRM_16BPPA5551 >> 25] = + { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } }, + + [LCD_WINCTRL1_FRM_24BPP >> 25] = + { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 } }, + + [LCD_WINCTRL1_FRM_32BPP >> 25] = + { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 24, 0, 0 } }, +}; + +/*-------------------------------------------------------------------------*/ + +/* Helpers */ + +static void au1200fb_update_fbinfo(struct fb_info *fbi) +{ + /* FIX!!!! This also needs to take the window pixel format into account!!! */ + + /* Update var-dependent FB info */ + if (panel_is_color(panel)) { + if (fbi->var.bits_per_pixel <= 8) { + /* palettized */ + fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR; + fbi->fix.line_length = fbi->var.xres_virtual / + (8/fbi->var.bits_per_pixel); + } else { + /* non-palettized */ + fbi->fix.visual = FB_VISUAL_TRUECOLOR; + fbi->fix.line_length = fbi->var.xres_virtual * (fbi->var.bits_per_pixel / 8); + } + } else { + /* mono FIX!!! mono 8 and 4 bits */ + fbi->fix.visual = FB_VISUAL_MONO10; + fbi->fix.line_length = fbi->var.xres_virtual / 8; + } + + fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual; + print_dbg("line length: %d\n", fbi->fix.line_length); + print_dbg("bits_per_pixel: %d\n", fbi->var.bits_per_pixel); +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 framebuffer driver */ + +/* fb_check_var + * Validate var settings with hardware restrictions and modify it if necessary + */ +static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + u32 pixclock; + int screen_size, plane; + + plane = fbdev->plane; + + /* Make sure that the mode respect all LCD controller and + * panel restrictions. */ + var->xres = win->w[plane].xres; + var->yres = win->w[plane].yres; + + /* No need for virtual resolution support */ + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + var->bits_per_pixel = winbpp(win->w[plane].mode_winctrl1); + + screen_size = var->xres_virtual * var->yres_virtual; + if (var->bits_per_pixel > 8) screen_size *= (var->bits_per_pixel / 8); + else screen_size /= (8/var->bits_per_pixel); + + if (fbdev->fb_len < screen_size) + return -EINVAL; /* Virtual screen is to big, abort */ + + /* FIX!!!! what are the implicaitons of ignoring this for windows ??? */ + /* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel + * clock can only be obtain by dividing this value by an even integer. + * Fallback to a slower pixel clock if necessary. */ + pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); + pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); + + if (AU1200_LCD_MAX_CLK % pixclock) { + int diff = AU1200_LCD_MAX_CLK % pixclock; + pixclock -= diff; + } + + var->pixclock = KHZ2PICOS(pixclock/1000); +#if 0 + if (!panel_is_active(panel)) { + int pcd = AU1200_LCD_MAX_CLK / (pixclock * 2) - 1; + + if (!panel_is_color(panel) + && (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) { + /* STN 8bit mono panel support is up to 6MHz pixclock */ + var->pixclock = KHZ2PICOS(6000); + } else if (!pcd) { + /* Other STN panel support is up to 12MHz */ + var->pixclock = KHZ2PICOS(12000); + } + } +#endif + /* Set bitfield accordingly */ + switch (var->bits_per_pixel) { + case 16: + { + /* 16bpp True color. + * These must be set to MATCH WINCTRL[FORM] */ + int idx; + idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; + var->red = rgb_bitfields[idx][0]; + var->green = rgb_bitfields[idx][1]; + var->blue = rgb_bitfields[idx][2]; + var->transp = rgb_bitfields[idx][3]; + break; + } + + case 32: + { + /* 32bpp True color. + * These must be set to MATCH WINCTRL[FORM] */ + int idx; + idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; + var->red = rgb_bitfields[idx][0]; + var->green = rgb_bitfields[idx][1]; + var->blue = rgb_bitfields[idx][2]; + var->transp = rgb_bitfields[idx][3]; + break; + } + default: + print_dbg("Unsupported depth %dbpp", var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +/* fb_set_par + * Set hardware with var settings. This will enable the controller with a + * specific mode, normally validated with the fb_check_var method + */ +static int au1200fb_fb_set_par(struct fb_info *fbi) +{ + struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; + + au1200fb_update_fbinfo(fbi); + au1200_setmode(fbdev); + + return 0; +} + +/* fb_setcolreg + * Set color in LCD palette. + */ +static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *fbi) +{ + volatile u32 *palette = lcd->palette; + u32 value; + + if (regno > (AU1200_LCD_NBR_PALETTE_ENTRIES - 1)) + return -EINVAL; + + if (fbi->var.grayscale) { + /* Convert color to grayscale */ + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + } + + if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + palette = (u32*) fbi->pseudo_palette; + + red >>= (16 - fbi->var.red.length); + green >>= (16 - fbi->var.green.length); + blue >>= (16 - fbi->var.blue.length); + + value = (red << fbi->var.red.offset) | + (green << fbi->var.green.offset)| + (blue << fbi->var.blue.offset); + value &= 0xFFFF; + + } else if (1 /*FIX!!! panel_is_active(fbdev->panel)*/) { + /* COLOR TFT PALLETTIZED (use RGB 565) */ + value = (red & 0xF800)|((green >> 5) & + 0x07E0)|((blue >> 11) & 0x001F); + value &= 0xFFFF; + + } else if (0 /*panel_is_color(fbdev->panel)*/) { + /* COLOR STN MODE */ + value = 0x1234; + value &= 0xFFF; + } else { + /* MONOCHROME MODE */ + value = (green >> 12) & 0x000F; + value &= 0xF; + } + + palette[regno] = value; + + return 0; +} + +/* fb_blank + * Blank the screen. Depending on the mode, the screen will be + * activated with the backlight color, or desactivated + */ +static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi) +{ + /* Short-circuit screen blanking */ + if (noblanking) + return 0; + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + /* printk("turn on panel\n"); */ + au1200_setpanel(panel); + break; + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + /* printk("turn off panel\n"); */ + au1200_setpanel(NULL); + break; + default: + break; + + } + + /* FB_BLANK_NORMAL is a soft blank */ + return (blank_mode == FB_BLANK_NORMAL) ? -EINVAL : 0; +} + +/* fb_mmap + * Map video memory in user space. We don't use the generic fb_mmap + * method mainly to allow the use of the TLB streaming flag (CCA=6) + */ +static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) + +{ + unsigned int len; + unsigned long start=0, off; + struct au1200fb_device *fbdev = (struct au1200fb_device *) info; + +#ifdef CONFIG_PM + au1xxx_pm_access(LCD_pm_dev); +#endif + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { + return -EINVAL; + } + + start = fbdev->fb_phys & PAGE_MASK; + len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); + + off = vma->vm_pgoff << PAGE_SHIFT; + + if ((vma->vm_end - vma->vm_start + off) > len) { + return -EINVAL; + } + + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */ + + vma->vm_flags |= VM_IO; + + return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + + return 0; +} + +static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) +{ + + unsigned int hi1, divider; + + /* SCREEN_SIZE: user cannot reset size, must switch panel choice */ + + if (pdata->flags & SCREEN_BACKCOLOR) + lcd->backcolor = pdata->backcolor; + + if (pdata->flags & SCREEN_BRIGHTNESS) { + + // limit brightness pwm duty to >= 30/1600 + if (pdata->brightness < 30) { + pdata->brightness = 30; + } + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + hi1 = (lcd->pwmhi >> 16) + 1; + hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8); + lcd->pwmhi &= 0xFFFF; + lcd->pwmhi |= (hi1 << 16); + } + + if (pdata->flags & SCREEN_COLORKEY) + lcd->colorkey = pdata->colorkey; + + if (pdata->flags & SCREEN_MASK) + lcd->colorkeymsk = pdata->mask; + au_sync(); +} + +static void get_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) +{ + unsigned int hi1, divider; + + pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1; + pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1; + + pdata->backcolor = lcd->backcolor; + pdata->colorkey = lcd->colorkey; + pdata->mask = lcd->colorkeymsk; + + // brightness + hi1 = (lcd->pwmhi >> 16) + 1; + divider = (lcd->pwmdiv & 0x3FFFF) + 1; + pdata->brightness = ((hi1 << 8) / divider) - 1; + au_sync(); +} + +static void set_window(unsigned int plane, + struct au1200_lcd_window_regs_t *pdata) +{ + unsigned int val, bpp; + + /* Window control register 0 */ + if (pdata->flags & WIN_POSITION) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | + LCD_WINCTRL0_OY); + val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX); + val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY); + lcd->window[plane].winctrl0 = val; + } + if (pdata->flags & WIN_ALPHA_COLOR) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A); + val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A); + lcd->window[plane].winctrl0 = val; + } + if (pdata->flags & WIN_ALPHA_MODE) { + val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN); + val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN); + lcd->window[plane].winctrl0 = val; + } + + /* Window control register 1 */ + if (pdata->flags & WIN_PRIORITY) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI); + val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_CHANNEL) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE); + val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_BUFFER_FORMAT) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM); + val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_COLOR_ORDER) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO); + val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_PIXEL_ORDER) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO); + val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO); + lcd->window[plane].winctrl1 = val; + } + if (pdata->flags & WIN_SIZE) { + val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | + LCD_WINCTRL1_SZY); + val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX); + val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY); + lcd->window[plane].winctrl1 = val; + /* program buffer line width */ + bpp = winbpp(val) / 8; + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX); + val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX); + lcd->window[plane].winctrl2 = val; + } + + /* Window control register 2 */ + if (pdata->flags & WIN_COLORKEY_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE); + val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE); + lcd->window[plane].winctrl2 = val; + } + if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM); + val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM); + lcd->window[plane].winctrl2 = val; + } + if (pdata->flags & WIN_RAM_ARRAY_MODE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM); + val |= ((pdata->ram_array_mode << 21) & LCD_WINCTRL2_RAM); + lcd->window[plane].winctrl2 = val; + } + + /* Buffer line width programmed with WIN_SIZE */ + + if (pdata->flags & WIN_BUFFER_SCALE) { + val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | + LCD_WINCTRL2_SCY); + val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX); + val |= ((pdata->ysize) & LCD_WINCTRL2_SCY); + lcd->window[plane].winctrl2 = val; + } + + if (pdata->flags & WIN_ENABLE) { + val = lcd->winenable; + val &= ~(1<enable & 1) << plane; + lcd->winenable = val; + } + au_sync(); +} + +static void get_window(unsigned int plane, + struct au1200_lcd_window_regs_t *pdata) +{ + /* Window control register 0 */ + pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21; + pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10; + pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2; + pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1; + + /* Window control register 1 */ + pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30; + pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29; + pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25; + pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24; + pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22; + pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1; + pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1; + + /* Window control register 2 */ + pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24; + pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23; + pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21; + + pdata->enable = (lcd->winenable >> plane) & 1; + au_sync(); +} + +static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int plane; + int val; + +#ifdef CONFIG_PM + au1xxx_pm_access(LCD_pm_dev); +#endif + + plane = fbinfo2index(info); + print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); + + if (cmd == AU1200_LCD_FB_IOCTL) { + struct au1200_lcd_iodata_t iodata; + + if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata))) + return -EFAULT; + + print_dbg("FB IOCTL called\n"); + + switch (iodata.subcmd) { + case AU1200_LCD_SET_SCREEN: + print_dbg("AU1200_LCD_SET_SCREEN\n"); + set_global(cmd, &iodata.global); + break; + + case AU1200_LCD_GET_SCREEN: + print_dbg("AU1200_LCD_GET_SCREEN\n"); + get_global(cmd, &iodata.global); + break; + + case AU1200_LCD_SET_WINDOW: + print_dbg("AU1200_LCD_SET_WINDOW\n"); + set_window(plane, &iodata.window); + break; + + case AU1200_LCD_GET_WINDOW: + print_dbg("AU1200_LCD_GET_WINDOW\n"); + get_window(plane, &iodata.window); + break; + + case AU1200_LCD_SET_PANEL: + print_dbg("AU1200_LCD_SET_PANEL\n"); + if ((iodata.global.panel_choice >= 0) && + (iodata.global.panel_choice < + NUM_PANELS)) + { + struct panel_settings *newpanel; + panel_index = iodata.global.panel_choice; + newpanel = &known_lcd_panels[panel_index]; + au1200_setpanel(newpanel); + } + break; + + case AU1200_LCD_GET_PANEL: + print_dbg("AU1200_LCD_GET_PANEL\n"); + iodata.global.panel_choice = panel_index; + break; + + default: + return -EINVAL; + } + + val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata)); + if (val) { + print_dbg("error: could not copy %d bytes\n", val); + return -EFAULT; + } + } + + return 0; +} + + +static struct fb_ops au1200fb_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = au1200fb_fb_check_var, + .fb_set_par = au1200fb_fb_set_par, + .fb_setcolreg = au1200fb_fb_setcolreg, + .fb_blank = au1200fb_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_sync = NULL, + .fb_ioctl = au1200fb_ioctl, + .fb_mmap = au1200fb_fb_mmap, +}; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs) +{ + /* Nothing to do for now, just clear any pending interrupt */ + lcd->intstatus = lcd->intstatus; + au_sync(); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 LCD device probe helpers */ + +static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) +{ + struct fb_info *fbi = &fbdev->fb_info; + int bpp; + + memset(fbi, 0, sizeof(struct fb_info)); + fbi->fbops = &au1200fb_fb_ops; + + bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); + + /* Copy monitor specs from panel data */ + /* fixme: we're setting up LCD controller windows, so these dont give a + damn as to what the monitor specs are (the panel itself does, but that + isnt done here...so maybe need a generic catchall monitor setting??? */ + memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs)); + + /* We first try the user mode passed in argument. If that failed, + * or if no one has been specified, we default to the first mode of the + * panel list. Note that after this call, var data will be set */ + if (!fb_find_mode(&fbi->var, + fbi, + NULL, /* drv_info.opt_mode, */ + fbi->monspecs.modedb, + fbi->monspecs.modedb_len, + fbi->monspecs.modedb, + bpp)) { + + print_err("Cannot find valid mode for panel %s", panel->name); + return -EFAULT; + } + + fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + if (!fbi->pseudo_palette) { + return -ENOMEM; + } + memset(fbi->pseudo_palette, 0, sizeof(u32) * 16); + + if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + print_err("Fail to allocate colormap (%d entries)", + AU1200_LCD_NBR_PALETTE_ENTRIES); + kfree(fbi->pseudo_palette); + return -EFAULT; + } + + strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); + fbi->fix.smem_start = fbdev->fb_phys; + fbi->fix.smem_len = fbdev->fb_len; + fbi->fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fix.xpanstep = 0; + fbi->fix.ypanstep = 0; + fbi->fix.mmio_start = 0; + fbi->fix.mmio_len = 0; + fbi->fix.accel = FB_ACCEL_NONE; + + fbi->screen_base = (char __iomem *) fbdev->fb_mem; + + au1200fb_update_fbinfo(fbi); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* AU1200 LCD controller device driver */ + +static int au1200fb_drv_probe(struct device *dev) +{ + struct au1200fb_device *fbdev; + unsigned long page; + int bpp, plane, ret; + + if (!dev) + return -EINVAL; + + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { + bpp = winbpp(win->w[plane].mode_winctrl1); + if (win->w[plane].xres == 0) + win->w[plane].xres = panel->Xres; + if (win->w[plane].yres == 0) + win->w[plane].yres = panel->Yres; + + fbdev = &_au1200fb_devices[plane]; + memset(fbdev, 0, sizeof(struct au1200fb_device)); + fbdev->plane = plane; + + /* Allocate the framebuffer to the maximum screen size */ + fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; + + fbdev->fb_mem = dma_alloc_noncoherent(dev, + PAGE_ALIGN(fbdev->fb_len), + &fbdev->fb_phys, GFP_KERNEL); + if (!fbdev->fb_mem) { + print_err("fail to allocate frambuffer (size: %dK))", + fbdev->fb_len / 1024); + return -ENOMEM; + } + + /* + * Set page reserved so that mmap will work. This is necessary + * since we'll be remapping normal memory. + */ + for (page = (unsigned long)fbdev->fb_phys; + page < PAGE_ALIGN((unsigned long)fbdev->fb_phys + + fbdev->fb_len); + page += PAGE_SIZE) { + SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */ + } + print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); + print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); + + /* Init FB data */ + if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) + goto failed; + + /* Register new framebuffer */ + if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { + print_err("cannot register new framebuffer"); + goto failed; + } + + au1200fb_fb_set_par(&fbdev->fb_info); + +#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) + if (plane == 0) + if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { + /* Start display and show logo on boot */ + fb_set_cmap(&fbdev->fb_info.cmap, + &fbdev->fb_info); + + fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); + } +#endif + } + + /* Now hook interrupt too */ + if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, + SA_INTERRUPT | SA_SHIRQ, "lcd", (void *)dev)) < 0) { + print_err("fail to request interrupt line %d (err: %d)", + AU1200_LCD_INT, ret); + goto failed; + } + + return 0; + +failed: + /* NOTE: This only does the current plane/window that failed; others are still active */ + if (fbdev->fb_mem) + dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), + fbdev->fb_mem, fbdev->fb_phys); + if (fbdev->fb_info.cmap.len != 0) + fb_dealloc_cmap(&fbdev->fb_info.cmap); + if (fbdev->fb_info.pseudo_palette) + kfree(fbdev->fb_info.pseudo_palette); + if (plane == 0) + free_irq(AU1200_LCD_INT, (void*)dev); + return ret; +} + +static int au1200fb_drv_remove(struct device *dev) +{ + struct au1200fb_device *fbdev; + int plane; + + if (!dev) + return -ENODEV; + + /* Turn off the panel */ + au1200_setpanel(NULL); + + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) + { + fbdev = &_au1200fb_devices[plane]; + + /* Clean up all probe data */ + unregister_framebuffer(&fbdev->fb_info); + if (fbdev->fb_mem) + dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), + fbdev->fb_mem, fbdev->fb_phys); + if (fbdev->fb_info.cmap.len != 0) + fb_dealloc_cmap(&fbdev->fb_info.cmap); + if (fbdev->fb_info.pseudo_palette) + kfree(fbdev->fb_info.pseudo_palette); + } + + free_irq(AU1200_LCD_INT, (void *)dev); + + return 0; +} + +#ifdef CONFIG_PM +static int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level) +{ + /* TODO */ + return 0; +} + +static int au1200fb_drv_resume(struct device *dev, u32 level) +{ + /* TODO */ + return 0; +} +#endif /* CONFIG_PM */ + +static struct device_driver au1200fb_driver = { + .name = "au1200-lcd", + .bus = &platform_bus_type, + .probe = au1200fb_drv_probe, + .remove = au1200fb_drv_remove, +#ifdef CONFIG_PM + .suspend = au1200fb_drv_suspend, + .resume = au1200fb_drv_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +/* Kernel driver */ + +static void au1200fb_setup(void) +{ + char* options = NULL; + char* this_opt; + int num_panels = ARRAY_SIZE(known_lcd_panels); + int panel_idx = -1; + + fb_get_options(DRIVER_NAME, &options); + + if (options) { + while ((this_opt = strsep(&options,",")) != NULL) { + /* Panel option - can be panel name, + * "bs" for board-switch, or number/index */ + if (!strncmp(this_opt, "panel:", 6)) { + int i; + long int li; + char *endptr; + this_opt += 6; + /* First check for index, which allows + * to short circuit this mess */ + li = simple_strtol(this_opt, &endptr, 0); + if (*endptr == '\0') { + panel_idx = (int)li; + } + else if (strcmp(this_opt, "bs") == 0) { + extern int board_au1200fb_panel(void); + panel_idx = board_au1200fb_panel(); + } + + else + for (i = 0; i < num_panels; i++) { + if (!strcmp(this_opt, known_lcd_panels[i].name)) { + panel_idx = i; + break; + } + } + + if ((panel_idx < 0) || (panel_idx >= num_panels)) { + print_warn("Panel %s not supported!", this_opt); + } + else + panel_index = panel_idx; + } + + else if (strncmp(this_opt, "nohwcursor", 10) == 0) { + nohwcursor = 1; + } + + /* Unsupported option */ + else { + print_warn("Unsupported option \"%s\"", this_opt); + } + } + } +} + +#ifdef CONFIG_PM +static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, + au1xxx_request_t request, void *data) { + int retval = -1; + unsigned int d = 0; + unsigned int brightness = 0; + + if (request == AU1XXX_PM_SLEEP) { + board_au1200fb_panel_shutdown(); + } + else if (request == AU1XXX_PM_WAKEUP) { + if(dev->prev_state == SLEEP_STATE) + { + int plane; + au1200_setpanel(panel); + for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { + struct au1200fb_device *fbdev; + fbdev = &_au1200fb_devices[plane]; + au1200fb_fb_set_par(&fbdev->fb_info); + } + } + + d = *((unsigned int*)data); + if(d <=10) brightness = 26; + else if(d<=20) brightness = 51; + else if(d<=30) brightness = 77; + else if(d<=40) brightness = 102; + else if(d<=50) brightness = 128; + else if(d<=60) brightness = 153; + else if(d<=70) brightness = 179; + else if(d<=80) brightness = 204; + else if(d<=90) brightness = 230; + else brightness = 255; + set_brightness(brightness); + } else if (request == AU1XXX_PM_GETSTATUS) { + return dev->cur_state; + } else if (request == AU1XXX_PM_ACCESS) { + if (dev->cur_state != SLEEP_STATE) + return retval; + else { + au1200_setpanel(panel); + } + } else if (request == AU1XXX_PM_IDLE) { + } else if (request == AU1XXX_PM_CLEANUP) { + } + + return retval; +} +#endif + +static int __init au1200fb_init(void) +{ + print_info("" DRIVER_DESC ""); + + /* Setup driver with options */ + au1200fb_setup(); + + /* Point to the panel selected */ + panel = &known_lcd_panels[panel_index]; + win = &windows[window_index]; + + printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); + printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); + + /* Kickstart the panel, the framebuffers/windows come soon enough */ + au1200_setpanel(panel); + + #ifdef CONFIG_PM + LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); + if ( LCD_pm_dev == NULL) + printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); + else + printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); + #endif + + return driver_register(&au1200fb_driver); +} + +static void __exit au1200fb_cleanup(void) +{ + driver_unregister(&au1200fb_driver); +} + +module_init(au1200fb_init); +module_exit(au1200fb_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/au1200fb.h b/drivers/video/au1200fb.h new file mode 100644 index 000000000000..e2672714d8d4 --- /dev/null +++ b/drivers/video/au1200fb.h @@ -0,0 +1,572 @@ +/* + * BRIEF MODULE DESCRIPTION + * Hardware definitions for the Au1200 LCD controller + * + * Copyright 2004 AMD + * Author: AMD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _AU1200LCD_H +#define _AU1200LCD_H + +/********************************************************************/ +#define AU1200_LCD_ADDR 0xB5000000 + +#define uint8 unsigned char +#define uint32 unsigned int + +struct au1200_lcd { + volatile uint32 reserved0; + volatile uint32 screen; + volatile uint32 backcolor; + volatile uint32 horztiming; + volatile uint32 verttiming; + volatile uint32 clkcontrol; + volatile uint32 pwmdiv; + volatile uint32 pwmhi; + volatile uint32 reserved1; + volatile uint32 winenable; + volatile uint32 colorkey; + volatile uint32 colorkeymsk; + struct + { + volatile uint32 cursorctrl; + volatile uint32 cursorpos; + volatile uint32 cursorcolor0; + volatile uint32 cursorcolor1; + volatile uint32 cursorcolor2; + uint32 cursorcolor3; + } hwc; + volatile uint32 intstatus; + volatile uint32 intenable; + volatile uint32 outmask; + volatile uint32 fifoctrl; + uint32 reserved2[(0x0100-0x0058)/4]; + struct + { + volatile uint32 winctrl0; + volatile uint32 winctrl1; + volatile uint32 winctrl2; + volatile uint32 winbuf0; + volatile uint32 winbuf1; + volatile uint32 winbufctrl; + uint32 winreserved0; + uint32 winreserved1; + } window[4]; + + uint32 reserved3[(0x0400-0x0180)/4]; + + volatile uint32 palette[(0x0800-0x0400)/4]; + + volatile uint8 cursorpattern[256]; +}; + +/* lcd_screen */ +#define LCD_SCREEN_SEN (1<<31) +#define LCD_SCREEN_SX (0x07FF<<19) +#define LCD_SCREEN_SY (0x07FF<< 8) +#define LCD_SCREEN_SWP (1<<7) +#define LCD_SCREEN_SWD (1<<6) +#define LCD_SCREEN_PT (7<<0) +#define LCD_SCREEN_PT_TFT (0<<0) +#define LCD_SCREEN_SX_N(WIDTH) ((WIDTH-1)<<19) +#define LCD_SCREEN_SY_N(HEIGHT) ((HEIGHT-1)<<8) +#define LCD_SCREEN_PT_CSTN (1<<0) +#define LCD_SCREEN_PT_CDSTN (2<<0) +#define LCD_SCREEN_PT_M8STN (3<<0) +#define LCD_SCREEN_PT_M4STN (4<<0) + +/* lcd_backcolor */ +#define LCD_BACKCOLOR_SBGR (0xFF<<16) +#define LCD_BACKCOLOR_SBGG (0xFF<<8) +#define LCD_BACKCOLOR_SBGB (0xFF<<0) +#define LCD_BACKCOLOR_SBGR_N(N) ((N)<<16) +#define LCD_BACKCOLOR_SBGG_N(N) ((N)<<8) +#define LCD_BACKCOLOR_SBGB_N(N) ((N)<<0) + +/* lcd_winenable */ +#define LCD_WINENABLE_WEN3 (1<<3) +#define LCD_WINENABLE_WEN2 (1<<2) +#define LCD_WINENABLE_WEN1 (1<<1) +#define LCD_WINENABLE_WEN0 (1<<0) + +/* lcd_colorkey */ +#define LCD_COLORKEY_CKR (0xFF<<16) +#define LCD_COLORKEY_CKG (0xFF<<8) +#define LCD_COLORKEY_CKB (0xFF<<0) +#define LCD_COLORKEY_CKR_N(N) ((N)<<16) +#define LCD_COLORKEY_CKG_N(N) ((N)<<8) +#define LCD_COLORKEY_CKB_N(N) ((N)<<0) + +/* lcd_colorkeymsk */ +#define LCD_COLORKEYMSK_CKMR (0xFF<<16) +#define LCD_COLORKEYMSK_CKMG (0xFF<<8) +#define LCD_COLORKEYMSK_CKMB (0xFF<<0) +#define LCD_COLORKEYMSK_CKMR_N(N) ((N)<<16) +#define LCD_COLORKEYMSK_CKMG_N(N) ((N)<<8) +#define LCD_COLORKEYMSK_CKMB_N(N) ((N)<<0) + +/* lcd windows control 0 */ +#define LCD_WINCTRL0_OX (0x07FF<<21) +#define LCD_WINCTRL0_OY (0x07FF<<10) +#define LCD_WINCTRL0_A (0x00FF<<2) +#define LCD_WINCTRL0_AEN (1<<1) +#define LCD_WINCTRL0_OX_N(N) ((N)<<21) +#define LCD_WINCTRL0_OY_N(N) ((N)<<10) +#define LCD_WINCTRL0_A_N(N) ((N)<<2) + +/* lcd windows control 1 */ +#define LCD_WINCTRL1_PRI (3<<30) +#define LCD_WINCTRL1_PIPE (1<<29) +#define LCD_WINCTRL1_FRM (0xF<<25) +#define LCD_WINCTRL1_CCO (1<<24) +#define LCD_WINCTRL1_PO (3<<22) +#define LCD_WINCTRL1_SZX (0x07FF<<11) +#define LCD_WINCTRL1_SZY (0x07FF<<0) +#define LCD_WINCTRL1_FRM_1BPP (0<<25) +#define LCD_WINCTRL1_FRM_2BPP (1<<25) +#define LCD_WINCTRL1_FRM_4BPP (2<<25) +#define LCD_WINCTRL1_FRM_8BPP (3<<25) +#define LCD_WINCTRL1_FRM_12BPP (4<<25) +#define LCD_WINCTRL1_FRM_16BPP655 (5<<25) +#define LCD_WINCTRL1_FRM_16BPP565 (6<<25) +#define LCD_WINCTRL1_FRM_16BPP556 (7<<25) +#define LCD_WINCTRL1_FRM_16BPPI1555 (8<<25) +#define LCD_WINCTRL1_FRM_16BPPI5551 (9<<25) +#define LCD_WINCTRL1_FRM_16BPPA1555 (10<<25) +#define LCD_WINCTRL1_FRM_16BPPA5551 (11<<25) +#define LCD_WINCTRL1_FRM_24BPP (12<<25) +#define LCD_WINCTRL1_FRM_32BPP (13<<25) +#define LCD_WINCTRL1_PRI_N(N) ((N)<<30) +#define LCD_WINCTRL1_PO_00 (0<<22) +#define LCD_WINCTRL1_PO_01 (1<<22) +#define LCD_WINCTRL1_PO_10 (2<<22) +#define LCD_WINCTRL1_PO_11 (3<<22) +#define LCD_WINCTRL1_SZX_N(N) ((N-1)<<11) +#define LCD_WINCTRL1_SZY_N(N) ((N-1)<<0) + +/* lcd windows control 2 */ +#define LCD_WINCTRL2_CKMODE (3<<24) +#define LCD_WINCTRL2_DBM (1<<23) +#define LCD_WINCTRL2_RAM (3<<21) +#define LCD_WINCTRL2_BX (0x1FFF<<8) +#define LCD_WINCTRL2_SCX (0xF<<4) +#define LCD_WINCTRL2_SCY (0xF<<0) +#define LCD_WINCTRL2_CKMODE_00 (0<<24) +#define LCD_WINCTRL2_CKMODE_01 (1<<24) +#define LCD_WINCTRL2_CKMODE_10 (2<<24) +#define LCD_WINCTRL2_CKMODE_11 (3<<24) +#define LCD_WINCTRL2_RAM_NONE (0<<21) +#define LCD_WINCTRL2_RAM_PALETTE (1<<21) +#define LCD_WINCTRL2_RAM_GAMMA (2<<21) +#define LCD_WINCTRL2_RAM_BUFFER (3<<21) +#define LCD_WINCTRL2_BX_N(N) ((N)<<8) +#define LCD_WINCTRL2_SCX_1 (0<<4) +#define LCD_WINCTRL2_SCX_2 (1<<4) +#define LCD_WINCTRL2_SCX_4 (2<<4) +#define LCD_WINCTRL2_SCY_1 (0<<0) +#define LCD_WINCTRL2_SCY_2 (1<<0) +#define LCD_WINCTRL2_SCY_4 (2<<0) + +/* lcd windows buffer control */ +#define LCD_WINBUFCTRL_DB (1<<1) +#define LCD_WINBUFCTRL_DBN (1<<0) + +/* lcd_intstatus, lcd_intenable */ +#define LCD_INT_IFO (0xF<<14) +#define LCD_INT_IFU (0xF<<10) +#define LCD_INT_OFO (1<<9) +#define LCD_INT_OFU (1<<8) +#define LCD_INT_WAIT (1<<3) +#define LCD_INT_SD (1<<2) +#define LCD_INT_SA (1<<1) +#define LCD_INT_SS (1<<0) + +/* lcd_horztiming */ +#define LCD_HORZTIMING_HND2 (0x1FF<<18) +#define LCD_HORZTIMING_HND1 (0x1FF<<9) +#define LCD_HORZTIMING_HPW (0x1FF<<0) +#define LCD_HORZTIMING_HND2_N(N)(((N)-1)<<18) +#define LCD_HORZTIMING_HND1_N(N)(((N)-1)<<9) +#define LCD_HORZTIMING_HPW_N(N) (((N)-1)<<0) + +/* lcd_verttiming */ +#define LCD_VERTTIMING_VND2 (0x1FF<<18) +#define LCD_VERTTIMING_VND1 (0x1FF<<9) +#define LCD_VERTTIMING_VPW (0x1FF<<0) +#define LCD_VERTTIMING_VND2_N(N)(((N)-1)<<18) +#define LCD_VERTTIMING_VND1_N(N)(((N)-1)<<9) +#define LCD_VERTTIMING_VPW_N(N) (((N)-1)<<0) + +/* lcd_clkcontrol */ +#define LCD_CLKCONTROL_EXT (1<<22) +#define LCD_CLKCONTROL_DELAY (3<<20) +#define LCD_CLKCONTROL_CDD (1<<19) +#define LCD_CLKCONTROL_IB (1<<18) +#define LCD_CLKCONTROL_IC (1<<17) +#define LCD_CLKCONTROL_IH (1<<16) +#define LCD_CLKCONTROL_IV (1<<15) +#define LCD_CLKCONTROL_BF (0x1F<<10) +#define LCD_CLKCONTROL_PCD (0x3FF<<0) +#define LCD_CLKCONTROL_BF_N(N) (((N)-1)<<10) +#define LCD_CLKCONTROL_PCD_N(N) ((N)<<0) + +/* lcd_pwmdiv */ +#define LCD_PWMDIV_EN (1<<31) +#define LCD_PWMDIV_PWMDIV (0x1FFFF<<0) +#define LCD_PWMDIV_PWMDIV_N(N) ((N)<<0) + +/* lcd_pwmhi */ +#define LCD_PWMHI_PWMHI1 (0xFFFF<<16) +#define LCD_PWMHI_PWMHI0 (0xFFFF<<0) +#define LCD_PWMHI_PWMHI1_N(N) ((N)<<16) +#define LCD_PWMHI_PWMHI0_N(N) ((N)<<0) + +/* lcd_hwccon */ +#define LCD_HWCCON_EN (1<<0) + +/* lcd_cursorpos */ +#define LCD_CURSORPOS_HWCXOFF (0x1F<<27) +#define LCD_CURSORPOS_HWCXPOS (0x07FF<<16) +#define LCD_CURSORPOS_HWCYOFF (0x1F<<11) +#define LCD_CURSORPOS_HWCYPOS (0x07FF<<0) +#define LCD_CURSORPOS_HWCXOFF_N(N) ((N)<<27) +#define LCD_CURSORPOS_HWCXPOS_N(N) ((N)<<16) +#define LCD_CURSORPOS_HWCYOFF_N(N) ((N)<<11) +#define LCD_CURSORPOS_HWCYPOS_N(N) ((N)<<0) + +/* lcd_cursorcolor */ +#define LCD_CURSORCOLOR_HWCA (0xFF<<24) +#define LCD_CURSORCOLOR_HWCR (0xFF<<16) +#define LCD_CURSORCOLOR_HWCG (0xFF<<8) +#define LCD_CURSORCOLOR_HWCB (0xFF<<0) +#define LCD_CURSORCOLOR_HWCA_N(N) ((N)<<24) +#define LCD_CURSORCOLOR_HWCR_N(N) ((N)<<16) +#define LCD_CURSORCOLOR_HWCG_N(N) ((N)<<8) +#define LCD_CURSORCOLOR_HWCB_N(N) ((N)<<0) + +/* lcd_fifoctrl */ +#define LCD_FIFOCTRL_F3IF (1<<29) +#define LCD_FIFOCTRL_F3REQ (0x1F<<24) +#define LCD_FIFOCTRL_F2IF (1<<29) +#define LCD_FIFOCTRL_F2REQ (0x1F<<16) +#define LCD_FIFOCTRL_F1IF (1<<29) +#define LCD_FIFOCTRL_F1REQ (0x1F<<8) +#define LCD_FIFOCTRL_F0IF (1<<29) +#define LCD_FIFOCTRL_F0REQ (0x1F<<0) +#define LCD_FIFOCTRL_F3REQ_N(N) ((N-1)<<24) +#define LCD_FIFOCTRL_F2REQ_N(N) ((N-1)<<16) +#define LCD_FIFOCTRL_F1REQ_N(N) ((N-1)<<8) +#define LCD_FIFOCTRL_F0REQ_N(N) ((N-1)<<0) + +/* lcd_outmask */ +#define LCD_OUTMASK_MASK (0x00FFFFFF) + +/********************************************************************/ +#endif /* _AU1200LCD_H */ +/* + * BRIEF MODULE DESCRIPTION + * Hardware definitions for the Au1200 LCD controller + * + * Copyright 2004 AMD + * Author: AMD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _AU1200LCD_H +#define _AU1200LCD_H + +/********************************************************************/ +#define AU1200_LCD_ADDR 0xB5000000 + +#define uint8 unsigned char +#define uint32 unsigned int + +struct au1200_lcd { + volatile uint32 reserved0; + volatile uint32 screen; + volatile uint32 backcolor; + volatile uint32 horztiming; + volatile uint32 verttiming; + volatile uint32 clkcontrol; + volatile uint32 pwmdiv; + volatile uint32 pwmhi; + volatile uint32 reserved1; + volatile uint32 winenable; + volatile uint32 colorkey; + volatile uint32 colorkeymsk; + struct + { + volatile uint32 cursorctrl; + volatile uint32 cursorpos; + volatile uint32 cursorcolor0; + volatile uint32 cursorcolor1; + volatile uint32 cursorcolor2; + uint32 cursorcolor3; + } hwc; + volatile uint32 intstatus; + volatile uint32 intenable; + volatile uint32 outmask; + volatile uint32 fifoctrl; + uint32 reserved2[(0x0100-0x0058)/4]; + struct + { + volatile uint32 winctrl0; + volatile uint32 winctrl1; + volatile uint32 winctrl2; + volatile uint32 winbuf0; + volatile uint32 winbuf1; + volatile uint32 winbufctrl; + uint32 winreserved0; + uint32 winreserved1; + } window[4]; + + uint32 reserved3[(0x0400-0x0180)/4]; + + volatile uint32 palette[(0x0800-0x0400)/4]; + + volatile uint8 cursorpattern[256]; +}; + +/* lcd_screen */ +#define LCD_SCREEN_SEN (1<<31) +#define LCD_SCREEN_SX (0x07FF<<19) +#define LCD_SCREEN_SY (0x07FF<< 8) +#define LCD_SCREEN_SWP (1<<7) +#define LCD_SCREEN_SWD (1<<6) +#define LCD_SCREEN_PT (7<<0) +#define LCD_SCREEN_PT_TFT (0<<0) +#define LCD_SCREEN_SX_N(WIDTH) ((WIDTH-1)<<19) +#define LCD_SCREEN_SY_N(HEIGHT) ((HEIGHT-1)<<8) +#define LCD_SCREEN_PT_CSTN (1<<0) +#define LCD_SCREEN_PT_CDSTN (2<<0) +#define LCD_SCREEN_PT_M8STN (3<<0) +#define LCD_SCREEN_PT_M4STN (4<<0) + +/* lcd_backcolor */ +#define LCD_BACKCOLOR_SBGR (0xFF<<16) +#define LCD_BACKCOLOR_SBGG (0xFF<<8) +#define LCD_BACKCOLOR_SBGB (0xFF<<0) +#define LCD_BACKCOLOR_SBGR_N(N) ((N)<<16) +#define LCD_BACKCOLOR_SBGG_N(N) ((N)<<8) +#define LCD_BACKCOLOR_SBGB_N(N) ((N)<<0) + +/* lcd_winenable */ +#define LCD_WINENABLE_WEN3 (1<<3) +#define LCD_WINENABLE_WEN2 (1<<2) +#define LCD_WINENABLE_WEN1 (1<<1) +#define LCD_WINENABLE_WEN0 (1<<0) + +/* lcd_colorkey */ +#define LCD_COLORKEY_CKR (0xFF<<16) +#define LCD_COLORKEY_CKG (0xFF<<8) +#define LCD_COLORKEY_CKB (0xFF<<0) +#define LCD_COLORKEY_CKR_N(N) ((N)<<16) +#define LCD_COLORKEY_CKG_N(N) ((N)<<8) +#define LCD_COLORKEY_CKB_N(N) ((N)<<0) + +/* lcd_colorkeymsk */ +#define LCD_COLORKEYMSK_CKMR (0xFF<<16) +#define LCD_COLORKEYMSK_CKMG (0xFF<<8) +#define LCD_COLORKEYMSK_CKMB (0xFF<<0) +#define LCD_COLORKEYMSK_CKMR_N(N) ((N)<<16) +#define LCD_COLORKEYMSK_CKMG_N(N) ((N)<<8) +#define LCD_COLORKEYMSK_CKMB_N(N) ((N)<<0) + +/* lcd windows control 0 */ +#define LCD_WINCTRL0_OX (0x07FF<<21) +#define LCD_WINCTRL0_OY (0x07FF<<10) +#define LCD_WINCTRL0_A (0x00FF<<2) +#define LCD_WINCTRL0_AEN (1<<1) +#define LCD_WINCTRL0_OX_N(N) ((N)<<21) +#define LCD_WINCTRL0_OY_N(N) ((N)<<10) +#define LCD_WINCTRL0_A_N(N) ((N)<<2) + +/* lcd windows control 1 */ +#define LCD_WINCTRL1_PRI (3<<30) +#define LCD_WINCTRL1_PIPE (1<<29) +#define LCD_WINCTRL1_FRM (0xF<<25) +#define LCD_WINCTRL1_CCO (1<<24) +#define LCD_WINCTRL1_PO (3<<22) +#define LCD_WINCTRL1_SZX (0x07FF<<11) +#define LCD_WINCTRL1_SZY (0x07FF<<0) +#define LCD_WINCTRL1_FRM_1BPP (0<<25) +#define LCD_WINCTRL1_FRM_2BPP (1<<25) +#define LCD_WINCTRL1_FRM_4BPP (2<<25) +#define LCD_WINCTRL1_FRM_8BPP (3<<25) +#define LCD_WINCTRL1_FRM_12BPP (4<<25) +#define LCD_WINCTRL1_FRM_16BPP655 (5<<25) +#define LCD_WINCTRL1_FRM_16BPP565 (6<<25) +#define LCD_WINCTRL1_FRM_16BPP556 (7<<25) +#define LCD_WINCTRL1_FRM_16BPPI1555 (8<<25) +#define LCD_WINCTRL1_FRM_16BPPI5551 (9<<25) +#define LCD_WINCTRL1_FRM_16BPPA1555 (10<<25) +#define LCD_WINCTRL1_FRM_16BPPA5551 (11<<25) +#define LCD_WINCTRL1_FRM_24BPP (12<<25) +#define LCD_WINCTRL1_FRM_32BPP (13<<25) +#define LCD_WINCTRL1_PRI_N(N) ((N)<<30) +#define LCD_WINCTRL1_PO_00 (0<<22) +#define LCD_WINCTRL1_PO_01 (1<<22) +#define LCD_WINCTRL1_PO_10 (2<<22) +#define LCD_WINCTRL1_PO_11 (3<<22) +#define LCD_WINCTRL1_SZX_N(N) ((N-1)<<11) +#define LCD_WINCTRL1_SZY_N(N) ((N-1)<<0) + +/* lcd windows control 2 */ +#define LCD_WINCTRL2_CKMODE (3<<24) +#define LCD_WINCTRL2_DBM (1<<23) +#define LCD_WINCTRL2_RAM (3<<21) +#define LCD_WINCTRL2_BX (0x1FFF<<8) +#define LCD_WINCTRL2_SCX (0xF<<4) +#define LCD_WINCTRL2_SCY (0xF<<0) +#define LCD_WINCTRL2_CKMODE_00 (0<<24) +#define LCD_WINCTRL2_CKMODE_01 (1<<24) +#define LCD_WINCTRL2_CKMODE_10 (2<<24) +#define LCD_WINCTRL2_CKMODE_11 (3<<24) +#define LCD_WINCTRL2_RAM_NONE (0<<21) +#define LCD_WINCTRL2_RAM_PALETTE (1<<21) +#define LCD_WINCTRL2_RAM_GAMMA (2<<21) +#define LCD_WINCTRL2_RAM_BUFFER (3<<21) +#define LCD_WINCTRL2_BX_N(N) ((N)<<8) +#define LCD_WINCTRL2_SCX_1 (0<<4) +#define LCD_WINCTRL2_SCX_2 (1<<4) +#define LCD_WINCTRL2_SCX_4 (2<<4) +#define LCD_WINCTRL2_SCY_1 (0<<0) +#define LCD_WINCTRL2_SCY_2 (1<<0) +#define LCD_WINCTRL2_SCY_4 (2<<0) + +/* lcd windows buffer control */ +#define LCD_WINBUFCTRL_DB (1<<1) +#define LCD_WINBUFCTRL_DBN (1<<0) + +/* lcd_intstatus, lcd_intenable */ +#define LCD_INT_IFO (0xF<<14) +#define LCD_INT_IFU (0xF<<10) +#define LCD_INT_OFO (1<<9) +#define LCD_INT_OFU (1<<8) +#define LCD_INT_WAIT (1<<3) +#define LCD_INT_SD (1<<2) +#define LCD_INT_SA (1<<1) +#define LCD_INT_SS (1<<0) + +/* lcd_horztiming */ +#define LCD_HORZTIMING_HND2 (0x1FF<<18) +#define LCD_HORZTIMING_HND1 (0x1FF<<9) +#define LCD_HORZTIMING_HPW (0x1FF<<0) +#define LCD_HORZTIMING_HND2_N(N)(((N)-1)<<18) +#define LCD_HORZTIMING_HND1_N(N)(((N)-1)<<9) +#define LCD_HORZTIMING_HPW_N(N) (((N)-1)<<0) + +/* lcd_verttiming */ +#define LCD_VERTTIMING_VND2 (0x1FF<<18) +#define LCD_VERTTIMING_VND1 (0x1FF<<9) +#define LCD_VERTTIMING_VPW (0x1FF<<0) +#define LCD_VERTTIMING_VND2_N(N)(((N)-1)<<18) +#define LCD_VERTTIMING_VND1_N(N)(((N)-1)<<9) +#define LCD_VERTTIMING_VPW_N(N) (((N)-1)<<0) + +/* lcd_clkcontrol */ +#define LCD_CLKCONTROL_EXT (1<<22) +#define LCD_CLKCONTROL_DELAY (3<<20) +#define LCD_CLKCONTROL_CDD (1<<19) +#define LCD_CLKCONTROL_IB (1<<18) +#define LCD_CLKCONTROL_IC (1<<17) +#define LCD_CLKCONTROL_IH (1<<16) +#define LCD_CLKCONTROL_IV (1<<15) +#define LCD_CLKCONTROL_BF (0x1F<<10) +#define LCD_CLKCONTROL_PCD (0x3FF<<0) +#define LCD_CLKCONTROL_BF_N(N) (((N)-1)<<10) +#define LCD_CLKCONTROL_PCD_N(N) ((N)<<0) + +/* lcd_pwmdiv */ +#define LCD_PWMDIV_EN (1<<31) +#define LCD_PWMDIV_PWMDIV (0x1FFFF<<0) +#define LCD_PWMDIV_PWMDIV_N(N) ((N)<<0) + +/* lcd_pwmhi */ +#define LCD_PWMHI_PWMHI1 (0xFFFF<<16) +#define LCD_PWMHI_PWMHI0 (0xFFFF<<0) +#define LCD_PWMHI_PWMHI1_N(N) ((N)<<16) +#define LCD_PWMHI_PWMHI0_N(N) ((N)<<0) + +/* lcd_hwccon */ +#define LCD_HWCCON_EN (1<<0) + +/* lcd_cursorpos */ +#define LCD_CURSORPOS_HWCXOFF (0x1F<<27) +#define LCD_CURSORPOS_HWCXPOS (0x07FF<<16) +#define LCD_CURSORPOS_HWCYOFF (0x1F<<11) +#define LCD_CURSORPOS_HWCYPOS (0x07FF<<0) +#define LCD_CURSORPOS_HWCXOFF_N(N) ((N)<<27) +#define LCD_CURSORPOS_HWCXPOS_N(N) ((N)<<16) +#define LCD_CURSORPOS_HWCYOFF_N(N) ((N)<<11) +#define LCD_CURSORPOS_HWCYPOS_N(N) ((N)<<0) + +/* lcd_cursorcolor */ +#define LCD_CURSORCOLOR_HWCA (0xFF<<24) +#define LCD_CURSORCOLOR_HWCR (0xFF<<16) +#define LCD_CURSORCOLOR_HWCG (0xFF<<8) +#define LCD_CURSORCOLOR_HWCB (0xFF<<0) +#define LCD_CURSORCOLOR_HWCA_N(N) ((N)<<24) +#define LCD_CURSORCOLOR_HWCR_N(N) ((N)<<16) +#define LCD_CURSORCOLOR_HWCG_N(N) ((N)<<8) +#define LCD_CURSORCOLOR_HWCB_N(N) ((N)<<0) + +/* lcd_fifoctrl */ +#define LCD_FIFOCTRL_F3IF (1<<29) +#define LCD_FIFOCTRL_F3REQ (0x1F<<24) +#define LCD_FIFOCTRL_F2IF (1<<29) +#define LCD_FIFOCTRL_F2REQ (0x1F<<16) +#define LCD_FIFOCTRL_F1IF (1<<29) +#define LCD_FIFOCTRL_F1REQ (0x1F<<8) +#define LCD_FIFOCTRL_F0IF (1<<29) +#define LCD_FIFOCTRL_F0REQ (0x1F<<0) +#define LCD_FIFOCTRL_F3REQ_N(N) ((N-1)<<24) +#define LCD_FIFOCTRL_F2REQ_N(N) ((N-1)<<16) +#define LCD_FIFOCTRL_F1REQ_N(N) ((N-1)<<8) +#define LCD_FIFOCTRL_F0REQ_N(N) ((N-1)<<0) + +/* lcd_outmask */ +#define LCD_OUTMASK_MASK (0x00FFFFFF) + +/********************************************************************/ +#endif /* _AU1200LCD_H */ -- cgit v1.2.2 From 59153f7d7effdb5b3c81eb6d03914a866157b319 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:29 -0800 Subject: [PATCH] fbdev: Make BIOS EDID reading configurable DDC reading via the Video BIOS may take several tens of seconds with some combination of display cards and monitors. Make this option configurable. It defaults to `y' to minimise disruption. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 16 ++++++++++++++++ drivers/video/fbmon.c | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3a890610ebe7..22e9d696fdd2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -70,6 +70,22 @@ config FB_MACMODES depends on FB default n +config FB_FIRMWARE_EDID + bool "Enable firmware EDID" + depends on FB + default y + ---help--- + This enables access to the EDID transferred from the firmware. + On the i386, this is from the Video BIOS. Enable this if DDC/I2C + transfers do not work for your driver and if you are using + nvidiafb, i810fb or savagefb. + + In general, choosing Y for this option is safe. If you + experience extremely long delays while booting before you get + something on your display, try setting this to N. Matrox cards in + combination with certain motherboards and monitors are known to + suffer from this problem. + config FB_MODE_HELPERS bool "Enable Video Mode Handling Helpers" depends on FB diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 7c74e7325d95..53beeb4a9998 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -1281,7 +1281,7 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) -EINVAL : 0; } -#if defined(__i386__) +#if defined(CONFIG_FB_FIRMWARE_EDID) && defined(__i386__) #include /* @@ -1311,11 +1311,11 @@ const unsigned char *fb_firmware_edid(struct device *device) { return NULL; } -#endif /* _i386_ */ +#endif +EXPORT_SYMBOL(fb_firmware_edid); EXPORT_SYMBOL(fb_parse_edid); EXPORT_SYMBOL(fb_edid_to_monspecs); -EXPORT_SYMBOL(fb_firmware_edid); EXPORT_SYMBOL(fb_get_mode); EXPORT_SYMBOL(fb_validate_mode); EXPORT_SYMBOL(fb_destroy_modedb); -- cgit v1.2.2 From db77ec270d00098ff4fbf15f62f4506f6efb25d2 Mon Sep 17 00:00:00 2001 From: Alan Curry Date: Mon, 27 Mar 2006 01:17:30 -0800 Subject: [PATCH] framebuffer: cmap-setting return values A set of 3 small bugfixes, all of which are related to bogus return values of fb colormap-setting functions. First, fb_alloc_cmap returns -1 if memory allocation fails. This is a hard condition to reproduce since you'd have to be really low on memory, but from studying the contexts in which it is called, I think this function should be returning a negative errno, and the -1 will be seen as an EPERM. Switching it to -ENOMEM makes sense. Second, the store_cmap function which is called for writes to /sys/class/graphics/fb0/color_map returns 0 for success, but it should be returning the count of bytes written since its return value ends up in userspace as the result of the write() syscall. Third, radeonfb returns 1 instead of a negative errno when FBIOPUTCMAP is called with an oversized colormap. This is seen in userspace as a return value of 1 from the ioctl() syscall with errno left unchanged. A more useful return value would be -EINVAL. Signed-off-by: Alan Curry Cc: "Antonino A. Daplas" Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/aty/radeon_base.c | 6 +++--- drivers/video/fbcmap.c | 4 ++-- drivers/video/fbsysfs.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index c9f0c5a07e6e..9a6b5b39b88e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -1067,7 +1067,7 @@ static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green, if (regno > 255) - return 1; + return -EINVAL; red >>= 8; green >>= 8; @@ -1086,9 +1086,9 @@ static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green, pindex = regno * 8; if (rinfo->depth == 16 && regno > 63) - return 1; + return -EINVAL; if (rinfo->depth == 15 && regno > 31) - return 1; + return -EINVAL; /* For 565, the green component is mixed one order * below diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index c32a2a50bfa2..1f98392a43b3 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -85,7 +85,7 @@ static struct fb_cmap default_16_colors = { * Allocates memory for a colormap @cmap. @len is the * number of entries in the palette. * - * Returns -1 errno on error, or zero on success. + * Returns negative errno on error, or zero on success. * */ @@ -116,7 +116,7 @@ int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) fail: fb_dealloc_cmap(cmap); - return -1; + return -ENOMEM; } /** diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 6d26057337e2..b72b05250a9d 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -348,7 +348,7 @@ static ssize_t store_cmap(struct class_device *class_device, const char *buf, fb_copy_cmap(&umap, &fb_info->cmap); fb_dealloc_cmap(&umap); - return rc; + return rc ?: count; } for (i = 0; i < length; i++) { u16 red, blue, green, tsp; @@ -367,7 +367,7 @@ static ssize_t store_cmap(struct class_device *class_device, const char *buf, if (transp) fb_info->cmap.transp[i] = tsp; } - return 0; + return count; } static ssize_t show_cmap(struct class_device *class_device, char *buf) -- cgit v1.2.2 From 4d7b84d1cbb213fdfd2b25ffd1217f249ba1d55a Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:31 -0800 Subject: [PATCH] rivafb: Remove NULL check Remove unnecessary NULL check, as struct info will never be NULL. Coverity Bug 836 Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/riva/fbdev.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 6c19ab6afb01..f841f013b96f 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -2072,8 +2072,6 @@ static void __exit rivafb_remove(struct pci_dev *pd) struct riva_par *par = info->par; NVTRACE_ENTER(); - if (!info) - return; #ifdef CONFIG_FB_RIVA_I2C riva_delete_i2c_busses(par); -- cgit v1.2.2 From 8ff7727d2a92f8b99cc709b827859da5d3c46a8a Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:32 -0800 Subject: [PATCH] nvidiafb: Remove NULL check Remove unnecessary NULL check, as struct info will never be NULL. Coverity Bug 835 Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/nvidia/nvidia.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index c758b386f146..018dd8174c7d 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -1771,8 +1771,6 @@ static void __exit nvidiafb_remove(struct pci_dev *pd) struct nvidia_par *par = info->par; NVTRACE_ENTER(); - if (!info) - return; unregister_framebuffer(info); #ifdef CONFIG_MTRR -- cgit v1.2.2 From 3d5b191fff144634a04800e610b7ad0c1aefa6ed Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:33 -0800 Subject: [PATCH] nvidiafb: Remove NULL check #2 Remove unnecessary NULL check. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/nvidia/nv_i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c index bd9eca05e146..1edb1c432b75 100644 --- a/drivers/video/nvidia/nv_i2c.c +++ b/drivers/video/nvidia/nv_i2c.c @@ -218,8 +218,7 @@ int nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid) } } - if (out_edid) - *out_edid = edid; + *out_edid = edid; return (edid) ? 0 : 1; } -- cgit v1.2.2 From 7c518eb84ce75d4c8e8799f4fcad59837f6d1894 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:33 -0800 Subject: [PATCH] i810fb: Remove NULL check Remove unnecessary NULL check. Being a function private to the driver, out_edid can never be NULL. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/i810/i810-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c index e3c8b5f1ca76..3fe3ae1aff12 100644 --- a/drivers/video/i810/i810-i2c.c +++ b/drivers/video/i810/i810-i2c.c @@ -210,8 +210,7 @@ int i810_probe_i2c_connector(struct fb_info *info, u8 **out_edid, int conn) } } - if (out_edid) - *out_edid = edid; + *out_edid = edid; return (edid) ? 0 : 1; } -- cgit v1.2.2 From 0e4be28023c14624e03a09b4494e919e088513f0 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:34 -0800 Subject: [PATCH] savagefb: Remove NULL check Remove unnecessary NULL check. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/savage/savagefb-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c index 00719a91479f..21debed863ac 100644 --- a/drivers/video/savage/savagefb-i2c.c +++ b/drivers/video/savage/savagefb-i2c.c @@ -273,8 +273,7 @@ int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid) } } - if (out_edid) - *out_edid = edid; + *out_edid = edid; return (edid) ? 0 : 1; } -- cgit v1.2.2 From eba87e8e8d7024da827accb00ce7e3affd10b7de Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:35 -0800 Subject: [PATCH] atyfb: Remove dead code Remove code that can never be reached. Coverity Bug 67 Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/aty/atyfb_base.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 485be386a8ff..d790ee76cc70 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2298,6 +2298,10 @@ static int __init aty_init(struct fb_info *info, const char *name) case CLK_ATI18818_1: par->pll_ops = &aty_pll_ati18818_1; break; + case CLK_IBMRGB514: + par->pll_ops = &aty_pll_ibm514; + break; +#if 0 /* dead code */ case CLK_STG1703: par->pll_ops = &aty_pll_stg1703; break; @@ -2307,9 +2311,7 @@ static int __init aty_init(struct fb_info *info, const char *name) case CLK_ATT20C408: par->pll_ops = &aty_pll_att20c408; break; - case CLK_IBMRGB514: - par->pll_ops = &aty_pll_ibm514; - break; +#endif default: PRINTKI("aty_init: CLK type not implemented yet!"); par->pll_ops = &aty_pll_unsupported; -- cgit v1.2.2 From 6257ffacb9e1dc99a910d620ccb92ec88da20a4f Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Mon, 27 Mar 2006 01:17:36 -0800 Subject: [PATCH] imsttfb: Remove dead code clk_p is always 0. Coverity Bug 67 Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/imsttfb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index 7db42542eb19..859ba7edfb42 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -440,9 +440,9 @@ getclkMHz(struct imstt_par *par) static void setclkMHz(struct imstt_par *par, __u32 MHz) { - __u32 clk_m, clk_n, clk_p, x, stage, spilled; + __u32 clk_m, clk_n, x, stage, spilled; - clk_m = clk_n = clk_p = 0; + clk_m = clk_n = 0; stage = spilled = 0; for (;;) { switch (stage) { @@ -453,7 +453,7 @@ setclkMHz(struct imstt_par *par, __u32 MHz) clk_n++; break; } - x = 20 * (clk_m + 1) / ((clk_n + 1) * (clk_p ? 2 * clk_p : 1)); + x = 20 * (clk_m + 1) / (clk_n + 1); if (x == MHz) break; if (x > MHz) { @@ -466,7 +466,7 @@ setclkMHz(struct imstt_par *par, __u32 MHz) par->init.pclk_m = clk_m; par->init.pclk_n = clk_n; - par->init.pclk_p = clk_p; + par->init.pclk_p = 0; } static struct imstt_regvals * -- cgit v1.2.2 From ed49843b897da9969e349c279ffc832efcb93213 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Mon, 27 Mar 2006 01:17:36 -0800 Subject: [PATCH] Add ID for Quadro NVS280 Quadro NVS280 is a dual-head PCIe card with PCI ID 10de:00fd and subsystem ID 10de:0215. Signed-off-by: Pavel Roskin Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/nvidia/nvidia.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 018dd8174c7d..6d3e4890cb43 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -297,6 +297,8 @@ static struct pci_device_id nvidiafb_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_NVIDIA, 0x0252, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_NVIDIA, 0x0313, -- cgit v1.2.2 From ac3f9087d337a6ffa1a5fbdf2c923eb0aad1aef6 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 27 Mar 2006 01:17:37 -0800 Subject: [PATCH] sparse: Fix warnings in newport driver about non-static functions There are more sparse warnings but fixing those will require some more work than I want to do without hardware for testing at hand. Signed-off-by: Ralf Baechle Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/console/newport_con.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 762c7a593141..e99fe30e568c 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -149,7 +149,7 @@ static inline void newport_clear_lines(int ystart, int yend, int ci) newport_clear_screen(0, ystart, 1280 + 63, yend, ci); } -void newport_reset(void) +static void newport_reset(void) { unsigned short treg; int i; @@ -193,7 +193,7 @@ void newport_reset(void) * calculate the actual screen size by reading * the video timing out of the VC2 */ -void newport_get_screensize(void) +static void newport_get_screensize(void) { int i, cols; unsigned short ventry, treg; -- cgit v1.2.2 From b0c87978216836455ef5fbcac6df1ce6679750b0 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Mon, 27 Mar 2006 01:17:38 -0800 Subject: [PATCH] fbdev: add modeline for 1680x1050@60 Add a modeline for the Philips 200W display. aty128fb does not do DDC, it picks 1920x1440 or similar. It works ok with nvidiafb because it can ask for DDC data. mode "1680x1050-60" # D: 146.028 MHz, H: 65.191 kHz, V: 59.863 Hz geometry 1680 1050 1680 1050 16 timings 6848 280 104 30 3 176 6 hsync high vsync high rgba 5/11,6/5,5/0,0/0 endmode hwinfo --monitor 20: None 00.0: 10000 Monitor [Created at monitor.206] Unique ID: rdCR.pzUFTofo1S4 Parent ID: 002j.bJRsY88eNSC Hardware Class: monitor Model: "PHILIPS Philips 200W" Vendor: PHL "PHILIPS" Device: eisa 0x0832 "Philips 200W" Serial ID: "VN 016596" Resolution: 720x400@70Hz Resolution: 640x480@60Hz Resolution: 640x480@67Hz Resolution: 640x480@72Hz Resolution: 640x480@75Hz Resolution: 800x600@56Hz Resolution: 800x600@60Hz Resolution: 800x600@72Hz Resolution: 800x600@75Hz Resolution: 832x624@75Hz Resolution: 1024x768@60Hz Resolution: 1024x768@70Hz Resolution: 1024x768@75Hz Resolution: 1280x1024@75Hz Resolution: 1152x864@70Hz Resolution: 1152x864@75Hz Resolution: 1280x960@60Hz Resolution: 1280x1024@60Hz Resolution: 1680x1050@60Hz Size: 433x271 mm Driver Info #0: Max. Resolution: 1680x1050 Vert. Sync Range: 56-85 Hz Hor. Sync Range: 30-93 kHz Config Status: cfg=new, avail=yes, need=no, active=unknown Attached to: #5 (VGA compatible controller) Signed-off-by: Olaf Hering Acked-by: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/modedb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 1da2f84bdc25..244a21adc3fe 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -182,6 +182,10 @@ static const struct fb_videomode modedb[] = { /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */ NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + }, { + /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */ + NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6, + FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, { /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */ NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3, -- cgit v1.2.2 From d1ae418eef4ce763a95edec0b5fc095af5daca2e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 27 Mar 2006 01:17:39 -0800 Subject: [PATCH] drivers/video: Use ARRAY_SIZE macro Use ARRAY_SIZE macro instead of sizeof(x)/sizeof(x[0]) and remove duplicates of ARRAY_SIZE. Some coding style and trailing whitespaces are also fixed. Compile-tested where possible (some are other arch or BROKEN) Signed-off-by: Tobias Klauser Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/acornfb.c | 8 ++++---- drivers/video/asiliantfb.c | 14 ++++++-------- drivers/video/aty/aty128fb.c | 4 ++-- drivers/video/aty/atyfb_base.c | 8 ++++---- drivers/video/aty/mach64_gx.c | 3 +-- drivers/video/chipsfb.c | 14 ++++++-------- drivers/video/console/fonts.c | 2 +- drivers/video/imsttfb.c | 24 +++++++++++++++--------- drivers/video/macmodes.c | 2 +- drivers/video/matrox/matroxfb_g450.c | 2 +- drivers/video/matrox/matroxfb_maven.c | 6 +++--- drivers/video/modedb.c | 2 +- drivers/video/neofb.c | 8 ++++---- drivers/video/pmagb-b-fb.c | 2 +- drivers/video/radeonfb.c | 2 +- drivers/video/sstfb.c | 11 ++++++----- drivers/video/virgefb.c | 3 +-- 17 files changed, 58 insertions(+), 57 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index 76448d6ae896..98baecccb3fd 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -1308,7 +1308,7 @@ static int __init acornfb_probe(struct platform_device *dev) /* * Try to select a suitable default mode */ - for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) { + for (i = 0; i < ARRAY_SIZE(modedb); i++) { unsigned long hs; hs = modedb[i].refresh * @@ -1380,7 +1380,7 @@ static int __init acornfb_probe(struct platform_device *dev) */ free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE); #endif - + fb_info.fix.smem_len = size; current_par.palette_size = VIDC_PALETTE_SIZE; @@ -1391,7 +1391,7 @@ static int __init acornfb_probe(struct platform_device *dev) */ do { rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, - sizeof(modedb) / sizeof(*modedb), + ARRAY_SIZE(modedb), &acornfb_default_mode, DEFAULT_BPP); /* * If we found an exact match, all ok. @@ -1408,7 +1408,7 @@ static int __init acornfb_probe(struct platform_device *dev) break; rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, - sizeof(modedb) / sizeof(*modedb), + ARRAY_SIZE(modedb), &acornfb_default_mode, DEFAULT_BPP); if (rc) break; diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c index c924d81f7978..29f9f0dfe3b4 100644 --- a/drivers/video/asiliantfb.c +++ b/drivers/video/asiliantfb.c @@ -353,8 +353,6 @@ struct chips_init_reg { unsigned char data; }; -#define N_ELTS(x) (sizeof(x) / sizeof(x[0])) - static struct chips_init_reg chips_init_sr[] = { {0x00, 0x03}, /* Reset register */ @@ -460,22 +458,22 @@ static void __devinit chips_hw_init(struct fb_info *p) { int i; - for (i = 0; i < N_ELTS(chips_init_xr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i) write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); write_xr(0x81, 0x12); write_xr(0x82, 0x08); write_xr(0x20, 0x00); - for (i = 0; i < N_ELTS(chips_init_sr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i) write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); - for (i = 0; i < N_ELTS(chips_init_gr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i) write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); - for (i = 0; i < N_ELTS(chips_init_ar); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i) write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); /* Enable video output in attribute index register */ writeb(0x20, mmio_base + 0x780); - for (i = 0; i < N_ELTS(chips_init_cr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i) write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); - for (i = 0; i < N_ELTS(chips_init_fr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i) write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); } diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 620c9a934e0e..821c6da8e42c 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1725,9 +1725,9 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id * strcpy(video_card, "Rage128 XX "); video_card[8] = ent->device >> 8; video_card[9] = ent->device & 0xFF; - + /* range check to make sure */ - if (ent->driver_data < (sizeof(r128_family)/sizeof(char *))) + if (ent->driver_data < ARRAY_SIZE(r128_family)) strncat(video_card, r128_family[ent->driver_data], sizeof(video_card)); printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index d790ee76cc70..e799fcca365a 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -434,7 +434,7 @@ static int __devinit correct_chipset(struct atyfb_par *par) const char *name; int i; - for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) + for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--) if (par->pci_id == aty_chips[i].pci_id) break; @@ -2168,10 +2168,10 @@ static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk) if (IS_XL(par->pci_id) || IS_MOBILITY(par->pci_id)) { refresh_tbl = ragexl_tbl; - size = sizeof(ragexl_tbl)/sizeof(int); + size = ARRAY_SIZE(ragexl_tbl); } else { refresh_tbl = ragepro_tbl; - size = sizeof(ragepro_tbl)/sizeof(int); + size = ARRAY_SIZE(ragepro_tbl); } for (i=0; i < size; i++) { @@ -3399,7 +3399,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi struct atyfb_par *par; int i, rc = -ENOMEM; - for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) + for (i = ARRAY_SIZE(aty_chips); i >= 0; i--) if (pdev->device == aty_chips[i].pci_id) break; diff --git a/drivers/video/aty/mach64_gx.c b/drivers/video/aty/mach64_gx.c index 01fdff79483b..2045639cb671 100644 --- a/drivers/video/aty/mach64_gx.c +++ b/drivers/video/aty/mach64_gx.c @@ -149,8 +149,7 @@ static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per, }; int i; - for (i = 0; i < sizeof(RGB514_clocks) / sizeof(*RGB514_clocks); - i++) + for (i = 0; i < ARRAY_SIZE(RGB514_clocks); i++) if (vclk_per <= RGB514_clocks[i].limit) { pll->ibm514.m = RGB514_clocks[i].m; pll->ibm514.n = RGB514_clocks[i].n; diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index bc061d4ec786..72ff6bf75e5e 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -178,8 +178,6 @@ struct chips_init_reg { unsigned char data; }; -#define N_ELTS(x) (sizeof(x) / sizeof(x[0])) - static struct chips_init_reg chips_init_sr[] = { { 0x00, 0x03 }, { 0x01, 0x01 }, @@ -287,18 +285,18 @@ static void __init chips_hw_init(void) { int i; - for (i = 0; i < N_ELTS(chips_init_xr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i) write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); outb(0x29, 0x3c2); /* set misc output reg */ - for (i = 0; i < N_ELTS(chips_init_sr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i) write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); - for (i = 0; i < N_ELTS(chips_init_gr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i) write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); - for (i = 0; i < N_ELTS(chips_init_ar); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i) write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); - for (i = 0; i < N_ELTS(chips_init_cr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i) write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); - for (i = 0; i < N_ELTS(chips_init_fr); ++i) + for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i) write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); } diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c index 4fd07d9eca03..0cc1bfda76a6 100644 --- a/drivers/video/console/fonts.c +++ b/drivers/video/console/fonts.c @@ -66,7 +66,7 @@ static const struct font_desc *fonts[] = { #endif }; -#define num_fonts (sizeof(fonts)/sizeof(*fonts)) +#define num_fonts ARRAY_SIZE(fonts) #ifdef NO_FONTS #error No fonts configured. diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index 859ba7edfb42..f73c642b50c2 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -1372,18 +1372,24 @@ init_imstt(struct fb_info *info) write_reg_le32(par->dc_regs, STGCTL, tmp & ~0x1); write_reg_le32(par->dc_regs, SSR, 0); - /* set default values for DAC registers */ + /* set default values for DAC registers */ if (par->ramdac == IBM) { - par->cmap_regs[PPMASK] = 0xff; eieio(); - par->cmap_regs[PIDXHI] = 0; eieio(); - for (i = 0; i < sizeof(ibm_initregs) / sizeof(*ibm_initregs); i++) { - par->cmap_regs[PIDXLO] = ibm_initregs[i].addr; eieio(); - par->cmap_regs[PIDXDATA] = ibm_initregs[i].value; eieio(); + par->cmap_regs[PPMASK] = 0xff; + eieio(); + par->cmap_regs[PIDXHI] = 0; + eieio(); + for (i = 0; i < ARRAY_SIZE(ibm_initregs); i++) { + par->cmap_regs[PIDXLO] = ibm_initregs[i].addr; + eieio(); + par->cmap_regs[PIDXDATA] = ibm_initregs[i].value; + eieio(); } } else { - for (i = 0; i < sizeof(tvp_initregs) / sizeof(*tvp_initregs); i++) { - par->cmap_regs[TVPADDRW] = tvp_initregs[i].addr; eieio(); - par->cmap_regs[TVPIDATA] = tvp_initregs[i].value; eieio(); + for (i = 0; i < ARRAY_SIZE(tvp_initregs); i++) { + par->cmap_regs[TVPADDRW] = tvp_initregs[i].addr; + eieio(); + par->cmap_regs[TVPIDATA] = tvp_initregs[i].value; + eieio(); } } diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c index 2fc71081f7e7..c0385c6f7db5 100644 --- a/drivers/video/macmodes.c +++ b/drivers/video/macmodes.c @@ -380,7 +380,7 @@ int __init mac_find_mode(struct fb_var_screeninfo *var, struct fb_info *info, if (mode_option && !strncmp(mode_option, "mac", 3)) { mode_option += 3; db = mac_modedb; - dbsize = sizeof(mac_modedb)/sizeof(*mac_modedb); + dbsize = ARRAY_SIZE(mac_modedb); } return fb_find_mode(var, info, mode_option, db, dbsize, &mac_modedb[DEFAULT_MODEDB_INDEX], default_bpp); diff --git a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c index c122d8743dd2..4d610b405d45 100644 --- a/drivers/video/matrox/matroxfb_g450.c +++ b/drivers/video/matrox/matroxfb_g450.c @@ -59,7 +59,7 @@ static const struct mctl g450_controls[] = }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, }; -#define G450CTRLS (sizeof(g450_controls)/sizeof(g450_controls[0])) +#define G450CTRLS ARRAY_SIZE(g450_controls) /* Return: positive number: id found -EINVAL: id not found, return failure diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index d8b3429cbed3..5d29a26b8cdf 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -89,12 +89,12 @@ static const struct mctl maven_controls[] = }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, { { V4L2_CID_GAMMA, V4L2_CTRL_TYPE_INTEGER, "gamma", - 0, sizeof(maven_gamma)/sizeof(maven_gamma[0])-1, 1, 3, + 0, ARRAY_SIZE(maven_gamma) - 1, 1, 3, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) }, { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, "test output", - 0, 1, 1, 0, + 0, 1, 1, 0, 0, }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, { { MATROXFB_CID_DEFLICKER, V4L2_CTRL_TYPE_INTEGER, @@ -105,7 +105,7 @@ static const struct mctl maven_controls[] = }; -#define MAVCTRLS (sizeof(maven_controls)/sizeof(maven_controls[0])) +#define MAVCTRLS ARRAY_SIZE(maven_controls) /* Return: positive number: id found -EINVAL: id not found, return failure diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 244a21adc3fe..26a1c618a205 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -500,7 +500,7 @@ int fb_find_mode(struct fb_var_screeninfo *var, /* Set up defaults */ if (!db) { db = modedb; - dbsize = sizeof(modedb)/sizeof(*modedb); + dbsize = ARRAY_SIZE(modedb); } if (!default_mode) default_mode = &modedb[DEFAULT_MODEDB_INDEX]; diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index b961d5601bd9..24b12f71d5a8 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -165,20 +165,20 @@ static int neoFindMode(int xres, int yres, int depth) switch (depth) { case 8: - size = sizeof(bios8) / sizeof(biosMode); + size = ARRAY_SIZE(bios8); mode = bios8; break; case 16: - size = sizeof(bios16) / sizeof(biosMode); + size = ARRAY_SIZE(bios16); mode = bios16; break; case 24: - size = sizeof(bios24) / sizeof(biosMode); + size = ARRAY_SIZE(bios24); mode = bios24; break; #ifdef NO_32BIT_SUPPORT_YET case 32: - size = sizeof(bios32) / sizeof(biosMode); + size = ARRAY_SIZE(bios32); mode = bios32; break; #endif diff --git a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c index eeeac924b500..73e2d7d16608 100644 --- a/drivers/video/pmagb-b-fb.c +++ b/drivers/video/pmagb-b-fb.c @@ -228,7 +228,7 @@ static void __init pmagbbfb_osc_setup(struct fb_info *info) freq1 = (par->osc0 * count1 + count0 / 2) / count0; par->osc1 = freq1; - for (i = 0; i < sizeof(pmagbbfb_freqs) / sizeof(*pmagbbfb_freqs); i++) + for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++) if (freq1 >= pmagbbfb_freqs[i] - (pmagbbfb_freqs[i] + 128) / 256 && freq1 <= pmagbbfb_freqs[i] + diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index db9fb9074dbc..24982adb3aa2 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c @@ -759,7 +759,7 @@ static void __iomem *radeon_find_rom(struct radeonfb_info *rinfo) rom = rom_base; for (i = 0; (i < 512) && (stage != 4); i++) { - for(j = 0;j < sizeof(radeon_sig)/sizeof(char *);j++) { + for (j = 0; j < ARRAY_SIZE(radeon_sig); j++) { if (radeon_sig[j][0] == *rom) if (strncmp(radeon_sig[j], rom, strlen(radeon_sig[j])) == 0) { diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 8c1a8b5135c6..c44de90ca12e 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -1194,10 +1194,11 @@ static struct dac_switch dacs[] __devinitdata = { static int __devinit sst_detect_dactype(struct fb_info *info, struct sstfb_par *par) { int i, ret = 0; - - for (i=0; ipar; struct pci_dev *dev = par->dev; u32 pci_res[pci_s]; diff --git a/drivers/video/virgefb.c b/drivers/video/virgefb.c index ed78747487e2..5ea2345dab99 100644 --- a/drivers/video/virgefb.c +++ b/drivers/video/virgefb.c @@ -616,8 +616,7 @@ static struct { #endif }; -#define arraysize(x) (sizeof(x)/sizeof(*(x))) -#define NUM_TOTAL_MODES arraysize(virgefb_predefined) +#define NUM_TOTAL_MODES ARRAY_SIZE(virgefb_predefined) /* * Default to 800x600 for video=virge8:, virge16: or virge32: -- cgit v1.2.2 From e4ccde33de202fae1b1e2a940604ee9e295450d9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 27 Mar 2006 01:17:41 -0800 Subject: [PATCH] video/sis/init301.c:SiS_ChrontelDoSomething2(): remove dead code The Coverity checker spotted these two unused variables. Signed-off-by: Adrian Bunk Cc: "Antonino A. Daplas" Cc: Thomas Winischhofer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sis/init301.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sis/init301.c b/drivers/video/sis/init301.c index 2d88f908170a..c3e070a6effd 100644 --- a/drivers/video/sis/init301.c +++ b/drivers/video/sis/init301.c @@ -8564,11 +8564,9 @@ SiS_ChrontelDoSomething3(struct SiS_Private *SiS_Pr, unsigned short ModeNo) static void SiS_ChrontelDoSomething2(struct SiS_Private *SiS_Pr) { - unsigned short temp,tempcl,tempch; + unsigned short temp; SiS_LongDelay(SiS_Pr, 1); - tempcl = 3; - tempch = 0; do { temp = SiS_GetCH701x(SiS_Pr,0x66); @@ -8582,13 +8580,6 @@ SiS_ChrontelDoSomething2(struct SiS_Private *SiS_Pr) SiS_SetCH701xForLCD(SiS_Pr); - if(tempcl == 0) { - if(tempch == 3) break; - SiS_ChrontelResetDB(SiS_Pr); - tempcl = 3; - tempch++; - } - tempcl--; temp = SiS_GetCH701x(SiS_Pr,0x76); temp &= 0xfb; /* Reset PLL */ SiS_SetCH701x(SiS_Pr,0x76,temp); -- cgit v1.2.2