diff options
Diffstat (limited to 'drivers/video')
80 files changed, 11407 insertions, 799 deletions
diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c index 0dda73da8628..7f907fb23b8a 100644 --- a/drivers/video/68328fb.c +++ b/drivers/video/68328fb.c @@ -60,7 +60,7 @@ static u_long videomemory; static u_long videomemorysize; static struct fb_info fb_info; -static u32 mc68x328fb_pseudo_palette[17]; +static u32 mc68x328fb_pseudo_palette[16]; static struct fb_var_screeninfo mc68x328fb_default __initdata = { .red = { 0, 8, 0 }, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 403dac787ebf..564cc9b51822 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -12,6 +12,13 @@ config VGASTATE tristate default n +config VIDEO_OUTPUT_CONTROL + tristate "Lowlevel video output switch controls" + default m + help + This framework adds support for low-level control of the video + output switch. + config FB tristate "Support for frame buffer devices" ---help--- @@ -812,7 +819,7 @@ config FB_PVR2 config FB_EPSON1355 bool "Epson 1355 framebuffer support" - depends on (FB = y) && (SUPERH || ARCH_CEIVA) + depends on (FB = y) && ARCH_CEIVA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -849,6 +856,16 @@ config FB_INTSRAM Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want to let frame buffer in external SDRAM. +config FB_ATMEL_STN + bool "Use a STN display with AT91/AT32 LCD Controller" + depends on FB_ATMEL && MACH_AT91SAM9261EK + default n + help + Say Y if you want to connect a STN LCD display to the AT91/AT32 LCD + Controller. Say N if you want to connect a TFT. + + If unsure, say N. + config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI @@ -1790,19 +1807,20 @@ config FB_IBM_GXT4500 adaptor, found on some IBM System P (pSeries) machines. config FB_PS3 - bool "PS3 GPU framebuffer driver" - depends on (FB = y) && PS3_PS3AV + tristate "PS3 GPU framebuffer driver" + depends on FB && PS3_PS3AV select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT select FB_SYS_FOPS + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE ---help--- Include support for the virtual frame buffer in the PS3 platform. config FB_PS3_DEFAULT_SIZE_M int "PS3 default frame buffer size (in MiB)" depends on FB_PS3 - default 18 + default 9 ---help--- This is the default size (in MiB) of the virtual frame buffer in the PS3. @@ -1820,6 +1838,10 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. +if ARCH_OMAP + source "drivers/video/omap/Kconfig" +endif + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index bd8b05229500..518933d4905f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o +obj-$(CONFIG_FB_OMAP) += omap/ # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o @@ -122,3 +123,6 @@ obj-$(CONFIG_FB_OF) += offb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o + +#video output switch sysfs driver +obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 6c9dc2e69c82..a7a1c891bfa2 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -447,13 +447,12 @@ static int clcdfb_probe(struct amba_device *dev, void *id) goto out; } - fb = kmalloc(sizeof(struct clcd_fb), GFP_KERNEL); + fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL); if (!fb) { printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n"); ret = -ENOMEM; goto free_region; } - memset(fb, 0, sizeof(struct clcd_fb)); fb->dev = dev; fb->board = board; diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index e1d5bd0c98c4..235b618b4117 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -79,6 +79,29 @@ static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { .accel = FB_ACCEL_NONE, }; +static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2) +{ + unsigned long value; + + if (!(cpu_is_at91sam9261() || cpu_is_at32ap7000())) + return xres; + + value = xres; + if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) { + /* STN display */ + if ((lcdcon2 & ATMEL_LCDC_DISTYPE) == ATMEL_LCDC_DISTYPE_STNCOLOR) { + value *= 3; + } + if ( (lcdcon2 & ATMEL_LCDC_IFWIDTH) == ATMEL_LCDC_IFWIDTH_4 + || ( (lcdcon2 & ATMEL_LCDC_IFWIDTH) == ATMEL_LCDC_IFWIDTH_8 + && (lcdcon2 & ATMEL_LCDC_SCANMOD) == ATMEL_LCDC_SCANMOD_DUAL )) + value = DIV_ROUND_UP(value, 4); + else + value = DIV_ROUND_UP(value, 8); + } + + return value; +} static void atmel_lcdfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var) @@ -181,6 +204,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->xoffset = var->yoffset = 0; switch (var->bits_per_pixel) { + case 1: case 2: case 4: case 8: @@ -195,8 +219,11 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->blue.offset = 10; var->red.length = var->green.length = var->blue.length = 5; break; - case 24: case 32: + var->transp.offset = 24; + var->transp.length = 8; + /* fall through */ + case 24: var->red.offset = 0; var->green.offset = 8; var->blue.offset = 16; @@ -228,8 +255,10 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, static int atmel_lcdfb_set_par(struct fb_info *info) { struct atmel_lcdfb_info *sinfo = info->par; + unsigned long hozval_linesz; unsigned long value; unsigned long clk_value_khz; + unsigned long bits_per_line; dev_dbg(info->device, "%s:\n", __func__); dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", @@ -241,12 +270,15 @@ static int atmel_lcdfb_set_par(struct fb_info *info) lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); - if (info->var.bits_per_pixel <= 8) + if (info->var.bits_per_pixel == 1) + info->fix.visual = FB_VISUAL_MONO01; + else if (info->var.bits_per_pixel <= 8) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else info->fix.visual = FB_VISUAL_TRUECOLOR; - info->fix.line_length = info->var.xres_virtual * (info->var.bits_per_pixel / 8); + bits_per_line = info->var.xres_virtual * info->var.bits_per_pixel; + info->fix.line_length = DIV_ROUND_UP(bits_per_line, 8); /* Re-initialize the DMA engine... */ dev_dbg(info->device, " * update DMA engine\n"); @@ -262,18 +294,21 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Set pixel clock */ clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; - value = clk_value_khz / PICOS2KHZ(info->var.pixclock); - - if (clk_value_khz % PICOS2KHZ(info->var.pixclock)) - value++; + value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock)); value = (value / 2) - 1; + dev_dbg(info->device, " * programming CLKVAL = 0x%08lx\n", value); if (value <= 0) { dev_notice(info->device, "Bypassing pixel clock divider\n"); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); - } else + } else { lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); + info->var.pixclock = KHZ2PICOS(clk_value_khz / (2 * (value + 1))); + dev_dbg(info->device, " updated pixclk: %lu KHz\n", + PICOS2KHZ(info->var.pixclock)); + } + /* Initialize control register 2 */ value = sinfo->default_lcdcon2; @@ -311,9 +346,14 @@ static int atmel_lcdfb_set_par(struct fb_info *info) dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); + /* Horizontal value (aka line size) */ + hozval_linesz = compute_hozval(info->var.xres, + lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); + /* Display size */ - value = (info->var.xres - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; value |= info->var.yres - 1; + dev_dbg(info->device, " * LCDFRMCFG = %08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); /* FIFO Threshold: Use formula from data sheet */ @@ -421,6 +461,15 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, ret = 0; } break; + + case FB_VISUAL_MONO01: + if (regno < 2) { + val = (regno == 0) ? 0x00 : 0x1F; + lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); + ret = 0; + } + break; + } return ret; diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h index 90e7df22f508..685a754991c6 100644 --- a/drivers/video/aty/ati_ids.h +++ b/drivers/video/aty/ati_ids.h @@ -204,6 +204,7 @@ #define PCI_CHIP_RV280_5961 0x5961 #define PCI_CHIP_RV280_5962 0x5962 #define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RS485_5975 0x5975 #define PCI_CHIP_RV280_5C61 0x5C61 #define PCI_CHIP_RV280_5C63 0x5C63 #define PCI_CHIP_R423_5D57 0x5D57 diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index d2c68c3d8d76..bc6f0096aa04 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -541,7 +541,7 @@ static char ram_off[] __devinitdata = "OFF"; #endif /* CONFIG_FB_ATY_CT */ -static u32 pseudo_palette[17]; +static u32 pseudo_palette[16]; #ifdef CONFIG_FB_ATY_GX static char *aty_gx_ram[8] __devinitdata = { @@ -2913,10 +2913,6 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, int node, len, i, j, ret; u32 mem, chip_id; - /* Do not attach when we have a serial console. */ - if (!con_is_present()) - return -ENXIO; - /* * Map memory-mapped registers. */ @@ -2937,12 +2933,11 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, /* nothing */ ; j = i + 4; - par->mmap_map = kmalloc(j * sizeof(*par->mmap_map), GFP_ATOMIC); + par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC); if (!par->mmap_map) { PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n"); return -ENOMEM; } - memset(par->mmap_map, 0, j * sizeof(*par->mmap_map)); for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) { struct resource *rp = &pdev->resource[i]; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 2349e71b0083..47ca62fe7c3e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -153,6 +153,8 @@ static struct pci_device_id radeonfb_pci_table[] = { /* Mobility 9200 (M9+) */ CHIP_DEF(PCI_CHIP_RV280_5C61, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), CHIP_DEF(PCI_CHIP_RV280_5C63, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /*Mobility Xpress 200 */ + CHIP_DEF(PCI_CHIP_RS485_5975, R300, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), /* 9200 */ CHIP_DEF(PCI_CHIP_RV280_5960, RV280, CHIP_HAS_CRTC2), CHIP_DEF(PCI_CHIP_RV280_5961, RV280, CHIP_HAS_CRTC2), diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index 7ebffcdfd1e3..7c922c7b460b 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -301,7 +301,7 @@ struct radeonfb_info { void __iomem *bios_seg; int fp_bios_start; - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; struct { u8 red, green, blue, pad; } palette[256]; diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index dbf4ec3f6d57..03e57ef88378 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1589,11 +1589,10 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) return -EFAULT; } - fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + fbi->pseudo_palette = kcalloc(16, sizeof(u32), 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)", diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index 5b94567beafd..b7904da51b23 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -174,7 +174,7 @@ static int cr_backlight_probe(struct platform_device *pdev) struct cr_panel *crp; u8 dev_en; - crp = kzalloc(sizeof(crp), GFP_KERNEL); + crp = kzalloc(sizeof(*crp), GFP_KERNEL); if (crp == NULL) return -ENOMEM; diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c index 50b78af0fa24..dea6579941b7 100644 --- a/drivers/video/clps711xfb.c +++ b/drivers/video/clps711xfb.c @@ -366,11 +366,10 @@ int __init clps711xfb_init(void) if (fb_get_options("clps711xfb", NULL)) return -ENODEV; - cfb = kmalloc(sizeof(*cfb), GFP_KERNEL); + cfb = kzalloc(sizeof(*cfb), GFP_KERNEL); if (!cfb) goto out; - memset(cfb, 0, sizeof(*cfb)); strcpy(cfb->fix.id, "clps711x"); cfb->fbops = &clps7111fb_ops; diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index d3b8a6be2916..49643969f9f8 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -118,6 +118,22 @@ config FRAMEBUFFER_CONSOLE help Low-level framebuffer-based console driver. +config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY + bool "Map the console to the primary display device" + depends on FRAMEBUFFER_CONSOLE + default n + ---help--- + If this option is selected, the framebuffer console will + automatically select the primary display device (if the architecture + supports this feature). Otherwise, the framebuffer console will + always select the first framebuffer driver that is loaded. The latter + is the default behavior. + + You can always override the automatic selection of the primary device + by using the fbcon=map: boot option. + + If unsure, select n. + config FRAMEBUFFER_CONSOLE_ROTATION bool "Framebuffer Console Rotation" depends on FRAMEBUFFER_CONSOLE diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 73813c60d03a..decfdc8eb9cc 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -75,6 +75,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/crc32.h> /* For counting font checksums */ +#include <asm/fb.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -125,6 +126,8 @@ static int first_fb_vc; static int last_fb_vc = MAX_NR_CONSOLES - 1; static int fbcon_is_default = 1; static int fbcon_has_exited; +static int primary_device = -1; +static int map_override; /* font data */ static char fontname[40]; @@ -152,6 +155,7 @@ static int fbcon_set_origin(struct vc_data *); #define DEFAULT_CURSOR_BLINK_RATE (20) static int vbl_cursor_cnt; +static int fbcon_cursor_noblink; #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) @@ -188,16 +192,14 @@ static __inline__ void ypan_down(struct vc_data *vc, int count); static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break); static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, - struct vc_data *vc); -static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var, - int unit); + int unit); static void fbcon_redraw_move(struct vc_data *vc, struct display *p, int line, int count, int dy); static void fbcon_modechanged(struct fb_info *info); static void fbcon_set_all_vcs(struct fb_info *info); static void fbcon_start(void); static void fbcon_exit(void); -static struct class_device *fbcon_class_device; +static struct device *fbcon_device; #ifdef CONFIG_MAC /* @@ -441,7 +443,8 @@ static void fbcon_add_cursor_timer(struct fb_info *info) struct fbcon_ops *ops = info->fbcon_par; if ((!info->queue.func || info->queue.func == fb_flashcursor) && - !(ops->flags & FBCON_FLAGS_CURSOR_TIMER)) { + !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) && + !fbcon_cursor_noblink) { if (!info->queue.func) INIT_WORK(&info->queue, fb_flashcursor); @@ -495,13 +498,17 @@ static int __init fb_console_setup(char *this_opt) if (!strncmp(options, "map:", 4)) { options += 4; - if (*options) + if (*options) { for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { if (!options[j]) j = 0; con2fb_map_boot[i] = (options[j++]-'0') % FB_MAX; } + + map_override = 1; + } + return 1; } @@ -736,7 +743,9 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, if (!err) { info->fbcon_par = ops; - set_blitting_type(vc, info); + + if (vc) + set_blitting_type(vc, info); } if (err) { @@ -798,11 +807,7 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, ops->flags |= FBCON_FLAGS_INIT; ops->graphics = 0; - - if (vc) - fbcon_set_disp(info, &info->var, vc); - else - fbcon_preset_disp(info, &info->var, unit); + fbcon_set_disp(info, &info->var, unit); if (show_logo) { struct vc_data *fg_vc = vc_cons[fg_console].d; @@ -1107,6 +1112,9 @@ static void fbcon_init(struct vc_data *vc, int init) if (var_to_display(p, &info->var, info)) return; + if (!info->fbcon_par) + con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); + /* If we are not the first console on this fb, copy the font from that console */ t = &fb_display[fg_console]; @@ -1349,6 +1357,11 @@ static void fbcon_cursor(struct vc_data *vc, int mode) if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) return; + if (vc->vc_cursor_type & 0x10) + fbcon_del_cursor_timer(info); + else + fbcon_add_cursor_timer(info); + ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; if (mode & CM_SOFTBACK) { mode &= ~CM_SOFTBACK; @@ -1368,36 +1381,29 @@ static int scrollback_phys_max = 0; static int scrollback_max = 0; static int scrollback_current = 0; -/* - * If no vc is existent yet, just set struct display - */ -static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var, - int unit) -{ - struct display *p = &fb_display[unit]; - struct display *t = &fb_display[fg_console]; - - if (var_to_display(p, var, info)) - return; - - p->fontdata = t->fontdata; - p->userfont = t->userfont; - if (p->userfont) - REFCOUNT(p->fontdata)++; -} - static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, - struct vc_data *vc) + int unit) { - struct display *p = &fb_display[vc->vc_num], *t; - struct vc_data **default_mode = vc->vc_display_fg; - struct vc_data *svc = *default_mode; + struct display *p, *t; + struct vc_data **default_mode, *vc; + struct vc_data *svc; struct fbcon_ops *ops = info->fbcon_par; int rows, cols, charcnt = 256; + p = &fb_display[unit]; + if (var_to_display(p, var, info)) return; + + vc = vc_cons[unit].d; + + if (!vc) + return; + + default_mode = vc->vc_display_fg; + svc = *default_mode; t = &fb_display[svc->vc_num]; + if (!vc->vc_font.data) { vc->vc_font.data = (void *)(p->fontdata = t->fontdata); vc->vc_font.width = (*default_mode)->vc_font.width; @@ -1704,6 +1710,56 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p, } } +static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, + struct display *p, int line, int count, int ycount) +{ + int offset = ycount * vc->vc_cols; + unsigned short *d = (unsigned short *) + (vc->vc_origin + vc->vc_size_row * line); + unsigned short *s = d + offset; + struct fbcon_ops *ops = info->fbcon_par; + + while (count--) { + unsigned short *start = s; + unsigned short *le = advance_row(s, 1); + unsigned short c; + int x = 0; + + do { + c = scr_readw(s); + + if (c == scr_readw(d)) { + if (s > start) { + ops->bmove(vc, info, line + ycount, x, + line, x, 1, s-start); + x += s - start + 1; + start = s + 1; + } else { + x++; + start++; + } + } + + scr_writew(c, d); + console_conditional_schedule(); + s++; + d++; + } while (s < le); + if (s > start) + ops->bmove(vc, info, line + ycount, x, line, x, 1, + s-start); + console_conditional_schedule(); + if (ycount > 0) + line++; + else { + line--; + /* NOTE: We subtract two lines from these pointers */ + s -= vc->vc_size_row; + d -= vc->vc_size_row; + } + } +} + static void fbcon_redraw(struct vc_data *vc, struct display *p, int line, int count, int offset) { @@ -1789,7 +1845,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; if (fbcon_is_inactive(vc, info)) @@ -1813,10 +1868,15 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, goto redraw_up; switch (p->scrollmode) { case SCROLL_MOVE: - ops->bmove(vc, info, t + count, 0, t, 0, - b - t - count, vc->vc_cols); - ops->clear(vc, info, b - count, 0, count, - vc->vc_cols); + fbcon_redraw_blit(vc, info, p, t, b - t - count, + count); + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + scr_memsetw((unsigned short *) (vc->vc_origin + + vc->vc_size_row * + (b - count)), + vc->vc_video_erase_char, + vc->vc_size_row * count); + return 1; break; case SCROLL_WRAP_MOVE: @@ -1899,9 +1959,15 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, goto redraw_down; switch (p->scrollmode) { case SCROLL_MOVE: - ops->bmove(vc, info, t, 0, t + count, 0, - b - t - count, vc->vc_cols); - ops->clear(vc, info, t, 0, count, vc->vc_cols); + fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, + -count); + fbcon_clear(vc, t, 0, count, vc->vc_cols); + scr_memsetw((unsigned short *) (vc->vc_origin + + vc->vc_size_row * + t), + vc->vc_video_erase_char, + vc->vc_size_row * count); + return 1; break; case SCROLL_WRAP_MOVE: @@ -2937,9 +3003,48 @@ static int fbcon_mode_deleted(struct fb_info *info, return found; } -static int fbcon_fb_unregistered(int idx) +#ifdef CONFIG_VT_HW_CONSOLE_BINDING +static int fbcon_unbind(void) { - int i; + int ret; + + ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + return ret; +} +#else +static inline int fbcon_unbind(void) +{ + return -EINVAL; +} +#endif /* CONFIG_VT_HW_CONSOLE_BINDING */ + +static int fbcon_fb_unbind(int idx) +{ + int i, new_idx = -1, ret = 0; + + for (i = first_fb_vc; i <= last_fb_vc; i++) { + if (con2fb_map[i] != idx && + con2fb_map[i] != -1) { + new_idx = i; + break; + } + } + + if (new_idx != -1) { + for (i = first_fb_vc; i <= last_fb_vc; i++) { + if (con2fb_map[i] == idx) + set_con2fb_map(i, new_idx, 0); + } + } else + ret = fbcon_unbind(); + + return ret; +} + +static int fbcon_fb_unregistered(struct fb_info *info) +{ + int i, idx = info->node; for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map[i] == idx) @@ -2967,12 +3072,48 @@ static int fbcon_fb_unregistered(int idx) if (!num_registered_fb) unregister_con_driver(&fb_con); + + if (primary_device == idx) + primary_device = -1; + return 0; } -static int fbcon_fb_registered(int idx) +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY +static void fbcon_select_primary(struct fb_info *info) { - int ret = 0, i; + if (!map_override && primary_device == -1 && + fb_is_primary_device(info)) { + int i; + + printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n", + info->fix.id, info->node); + primary_device = info->node; + + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map_boot[i] = primary_device; + + if (con_is_bound(&fb_con)) { + printk(KERN_INFO "fbcon: Remapping primary device, " + "fb%i, to tty %i-%i\n", info->node, + first_fb_vc + 1, last_fb_vc + 1); + info_idx = primary_device; + } + } + +} +#else +static inline void fbcon_select_primary(struct fb_info *info) +{ + return; +} +#endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ + +static int fbcon_fb_registered(struct fb_info *info) +{ + int ret = 0, i, idx = info->node; + + fbcon_select_primary(info); if (info_idx == -1) { for (i = first_fb_vc; i <= last_fb_vc; i++) { @@ -2986,8 +3127,7 @@ static int fbcon_fb_registered(int idx) ret = fbcon_takeover(1); } else { for (i = first_fb_vc; i <= last_fb_vc; i++) { - if (con2fb_map_boot[i] == idx && - con2fb_map[i] == -1) + if (con2fb_map_boot[i] == idx) set_con2fb_map(i, idx, 0); } } @@ -3034,12 +3174,7 @@ static void fbcon_new_modelist(struct fb_info *info) mode = fb_find_nearest_mode(fb_display[i].mode, &info->modelist); fb_videomode_to_var(&var, mode); - - if (vc) - fbcon_set_disp(info, &var, vc); - else - fbcon_preset_disp(info, &var, i); - + fbcon_set_disp(info, &var, vc->vc_num); } } @@ -3114,11 +3249,14 @@ static int fbcon_event_notify(struct notifier_block *self, mode = event->data; ret = fbcon_mode_deleted(info, mode); break; + case FB_EVENT_FB_UNBIND: + ret = fbcon_fb_unbind(info->node); + break; case FB_EVENT_FB_REGISTERED: - ret = fbcon_fb_registered(info->node); + ret = fbcon_fb_registered(info); break; case FB_EVENT_FB_UNREGISTERED: - ret = fbcon_fb_unregistered(info->node); + ret = fbcon_fb_unregistered(info); break; case FB_EVENT_SET_CONSOLE_MAP: con2fb = event->data; @@ -3179,8 +3317,9 @@ static struct notifier_block fbcon_event_notifier = { .notifier_call = fbcon_event_notify, }; -static ssize_t store_rotate(struct class_device *class_device, - const char *buf, size_t count) +static ssize_t store_rotate(struct device *device, + struct device_attribute *attr, const char *buf, + size_t count) { struct fb_info *info; int rotate, idx; @@ -3203,8 +3342,9 @@ err: return count; } -static ssize_t store_rotate_all(struct class_device *class_device, - const char *buf, size_t count) +static ssize_t store_rotate_all(struct device *device, + struct device_attribute *attr,const char *buf, + size_t count) { struct fb_info *info; int rotate, idx; @@ -3227,7 +3367,8 @@ err: return count; } -static ssize_t show_rotate(struct class_device *class_device, char *buf) +static ssize_t show_rotate(struct device *device, + struct device_attribute *attr,char *buf) { struct fb_info *info; int rotate = 0, idx; @@ -3248,20 +3389,86 @@ err: return snprintf(buf, PAGE_SIZE, "%d\n", rotate); } -static struct class_device_attribute class_device_attrs[] = { +static ssize_t show_cursor_blink(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info; + struct fbcon_ops *ops; + int idx, blink = -1; + + if (fbcon_has_exited) + return 0; + + acquire_console_sem(); + idx = con2fb_map[fg_console]; + + if (idx == -1 || registered_fb[idx] == NULL) + goto err; + + info = registered_fb[idx]; + ops = info->fbcon_par; + + if (!ops) + goto err; + + blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; +err: + release_console_sem(); + return snprintf(buf, PAGE_SIZE, "%d\n", blink); +} + +static ssize_t store_cursor_blink(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info; + int blink, idx; + char **last = NULL; + + if (fbcon_has_exited) + return count; + + acquire_console_sem(); + idx = con2fb_map[fg_console]; + + if (idx == -1 || registered_fb[idx] == NULL) + goto err; + + info = registered_fb[idx]; + + if (!info->fbcon_par) + goto err; + + blink = simple_strtoul(buf, last, 0); + + if (blink) { + fbcon_cursor_noblink = 0; + fbcon_add_cursor_timer(info); + } else { + fbcon_cursor_noblink = 1; + fbcon_del_cursor_timer(info); + } + +err: + release_console_sem(); + return count; +} + +static struct device_attribute device_attrs[] = { __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), + __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink, + store_cursor_blink), }; -static int fbcon_init_class_device(void) +static int fbcon_init_device(void) { int i, error = 0; fbcon_has_sysfs = 1; - for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) { - error = class_device_create_file(fbcon_class_device, - &class_device_attrs[i]); + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + error = device_create_file(fbcon_device, &device_attrs[i]); if (error) break; @@ -3269,8 +3476,7 @@ static int fbcon_init_class_device(void) if (error) { while (--i >= 0) - class_device_remove_file(fbcon_class_device, - &class_device_attrs[i]); + device_remove_file(fbcon_device, &device_attrs[i]); fbcon_has_sysfs = 0; } @@ -3356,16 +3562,15 @@ static int __init fb_console_init(void) acquire_console_sem(); fb_register_client(&fbcon_event_notifier); - fbcon_class_device = - class_device_create(fb_class, NULL, MKDEV(0, 0), NULL, "fbcon"); + fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), "fbcon"); - if (IS_ERR(fbcon_class_device)) { - printk(KERN_WARNING "Unable to create class_device " + if (IS_ERR(fbcon_device)) { + printk(KERN_WARNING "Unable to create device " "for fbcon; errno = %ld\n", - PTR_ERR(fbcon_class_device)); - fbcon_class_device = NULL; + PTR_ERR(fbcon_device)); + fbcon_device = NULL; } else - fbcon_init_class_device(); + fbcon_init_device(); for (i = 0; i < MAX_NR_CONSOLES; i++) con2fb_map[i] = -1; @@ -3379,14 +3584,13 @@ module_init(fb_console_init); #ifdef MODULE -static void __exit fbcon_deinit_class_device(void) +static void __exit fbcon_deinit_device(void) { int i; if (fbcon_has_sysfs) { - for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) - class_device_remove_file(fbcon_class_device, - &class_device_attrs[i]); + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) + device_remove_file(fbcon_device, &device_attrs[i]); fbcon_has_sysfs = 0; } @@ -3396,8 +3600,8 @@ static void __exit fb_console_exit(void) { acquire_console_sem(); fb_unregister_client(&fbcon_event_notifier); - fbcon_deinit_class_device(); - class_device_destroy(fb_class, MKDEV(0, 0)); + fbcon_deinit_device(); + device_destroy(fb_class, MKDEV(0, 0)); fbcon_exit(); release_console_sem(); unregister_con_driver(&fb_con); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index f46fe95f69fb..d18b73aafa0d 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -187,7 +187,11 @@ static void vgacon_scrollback_init(int pitch) } } -static void vgacon_scrollback_startup(void) +/* + * Called only duing init so call of alloc_bootmen is ok. + * Marked __init_refok to silence modpost. + */ +static void __init_refok vgacon_scrollback_startup(void) { vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024); diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 8b762739b1e0..b0be7eac32d8 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -94,7 +94,7 @@ static inline int VAR_MATCH(struct fb_var_screeninfo *x, struct fb_var_screeninf struct fb_info_control { struct fb_info info; struct fb_par_control par; - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; struct cmap_regs __iomem *cmap_regs; unsigned long cmap_regs_phys; diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 7a6eeda5ae9a..30ede6e8830f 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -1221,11 +1221,10 @@ cyberpro_alloc_fb_info(unsigned int id, char *name) { struct cfb_info *cfb; - cfb = kmalloc(sizeof(struct cfb_info), GFP_KERNEL); + cfb = kzalloc(sizeof(struct cfb_info), GFP_KERNEL); if (!cfb) return NULL; - memset(cfb, 0, sizeof(struct cfb_info)); cfb->id = id; diff --git a/drivers/video/cyblafb.c b/drivers/video/cyblafb.c index 94a66c2d2cf5..e23324d10be2 100644 --- a/drivers/video/cyblafb.c +++ b/drivers/video/cyblafb.c @@ -1068,15 +1068,18 @@ static int cyblafb_setcolreg(unsigned regno, unsigned red, unsigned green, out8(0x3C9, green >> 10); out8(0x3C9, blue >> 10); - } else if (bpp == 16) // RGB 565 - ((u32 *) info->pseudo_palette)[regno] = - (red & 0xF800) | - ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11); - else if (bpp == 32) // ARGB 8888 - ((u32 *) info->pseudo_palette)[regno] = - ((transp & 0xFF00) << 16) | - ((red & 0xFF00) << 8) | - ((green & 0xFF00)) | ((blue & 0xFF00) >> 8); + } else if (regno < 16) { + if (bpp == 16) // RGB 565 + ((u32 *) info->pseudo_palette)[regno] = + (red & 0xF800) | + ((green & 0xFC00) >> 5) | + ((blue & 0xF800) >> 11); + else if (bpp == 32) // ARGB 8888 + ((u32 *) info->pseudo_palette)[regno] = + ((transp & 0xFF00) << 16) | + ((red & 0xFF00) << 8) | + ((green & 0xFF00)) | ((blue & 0xFF00) >> 8); + } return 0; } diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c index ca2c54ce508e..33be46ccb54f 100644 --- a/drivers/video/epson1355fb.c +++ b/drivers/video/epson1355fb.c @@ -63,23 +63,12 @@ struct epson1355_par { unsigned long reg_addr; + u32 pseudo_palette[16]; }; /* ------------------------------------------------------------------------- */ -#ifdef CONFIG_SUPERH - -static inline u8 epson1355_read_reg(int index) -{ - return ctrl_inb(par.reg_addr + index); -} - -static inline void epson1355_write_reg(u8 data, int index) -{ - ctrl_outb(data, par.reg_addr + index); -} - -#elif defined(CONFIG_ARM) +#if defined(CONFIG_ARM) # ifdef CONFIG_ARCH_CEIVA # include <asm/arch/hardware.h> @@ -289,7 +278,7 @@ static int epson1355fb_blank(int blank_mode, struct fb_info *info) struct epson1355_par *par = info->par; switch (blank_mode) { - case FB_BLANK_UNBLANKING: + case FB_BLANK_UNBLANK: case FB_BLANK_NORMAL: lcd_enable(par, 1); backlight_enable(1); @@ -635,7 +624,7 @@ int __init epson1355fb_probe(struct platform_device *dev) goto bail; } - info = framebuffer_alloc(sizeof(struct epson1355_par) + sizeof(u32) * 256, &dev->dev); + info = framebuffer_alloc(sizeof(struct epson1355_par), &dev->dev); if (!info) { rc = -ENOMEM; goto bail; @@ -648,7 +637,7 @@ int __init epson1355fb_probe(struct platform_device *dev) rc = -ENOMEM; goto bail; } - info->pseudo_palette = (void *)(default_par + 1); + info->pseudo_palette = default_par->pseudo_palette; info->screen_base = ioremap(EPSON1355FB_FB_PHYS, EPSON1355FB_FB_LEN); if (!info->screen_base) { diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 38c2e2558f5e..215ac579f901 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -33,17 +33,10 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/efi.h> +#include <linux/fb.h> -#if defined(__mc68000__) || defined(CONFIG_APUS) -#include <asm/setup.h> -#endif +#include <asm/fb.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/page.h> -#include <asm/pgtable.h> - -#include <linux/fb.h> /* * Frame buffer device initialization and setup routines @@ -411,10 +404,146 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, } } +static int fb_show_logo_line(struct fb_info *info, int rotate, + const struct linux_logo *logo, int y, + unsigned int n) +{ + u32 *palette = NULL, *saved_pseudo_palette = NULL; + unsigned char *logo_new = NULL, *logo_rotate = NULL; + struct fb_image image; + + /* Return if the frame buffer is not mapped or suspended */ + if (logo == NULL || info->state != FBINFO_STATE_RUNNING || + info->flags & FBINFO_MODULE) + return 0; + + image.depth = 8; + image.data = logo->data; + + if (fb_logo.needs_cmapreset) + fb_set_logocmap(info, logo); + + if (fb_logo.needs_truepalette || + fb_logo.needs_directpalette) { + palette = kmalloc(256 * 4, GFP_KERNEL); + if (palette == NULL) + return 0; + + if (fb_logo.needs_truepalette) + fb_set_logo_truepalette(info, logo, palette); + else + fb_set_logo_directpalette(info, logo, palette); + + saved_pseudo_palette = info->pseudo_palette; + info->pseudo_palette = palette; + } + + if (fb_logo.depth <= 4) { + logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); + if (logo_new == NULL) { + kfree(palette); + if (saved_pseudo_palette) + info->pseudo_palette = saved_pseudo_palette; + return 0; + } + image.data = logo_new; + fb_set_logo(info, logo, logo_new, fb_logo.depth); + } + + image.dx = 0; + image.dy = y; + image.width = logo->width; + image.height = logo->height; + + if (rotate) { + logo_rotate = kmalloc(logo->width * + logo->height, GFP_KERNEL); + if (logo_rotate) + fb_rotate_logo(info, logo_rotate, &image, rotate); + } + + fb_do_show_logo(info, &image, rotate, n); + + kfree(palette); + if (saved_pseudo_palette != NULL) + info->pseudo_palette = saved_pseudo_palette; + kfree(logo_new); + kfree(logo_rotate); + return logo->height; +} + + +#ifdef CONFIG_FB_LOGO_EXTRA + +#define FB_LOGO_EX_NUM_MAX 10 +static struct logo_data_extra { + const struct linux_logo *logo; + unsigned int n; +} fb_logo_ex[FB_LOGO_EX_NUM_MAX]; +static unsigned int fb_logo_ex_num; + +void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n) +{ + if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX) + return; + + fb_logo_ex[fb_logo_ex_num].logo = logo; + fb_logo_ex[fb_logo_ex_num].n = n; + fb_logo_ex_num++; +} + +static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height, + unsigned int yres) +{ + unsigned int i; + + /* FIXME: logo_ex supports only truecolor fb. */ + if (info->fix.visual != FB_VISUAL_TRUECOLOR) + fb_logo_ex_num = 0; + + for (i = 0; i < fb_logo_ex_num; i++) { + height += fb_logo_ex[i].logo->height; + if (height > yres) { + height -= fb_logo_ex[i].logo->height; + fb_logo_ex_num = i; + break; + } + } + return height; +} + +static int fb_show_extra_logos(struct fb_info *info, int y, int rotate) +{ + unsigned int i; + + for (i = 0; i < fb_logo_ex_num; i++) + y += fb_show_logo_line(info, rotate, + fb_logo_ex[i].logo, y, fb_logo_ex[i].n); + + return y; +} + +#else /* !CONFIG_FB_LOGO_EXTRA */ + +static inline int fb_prepare_extra_logos(struct fb_info *info, + unsigned int height, + unsigned int yres) +{ + return height; +} + +static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate) +{ + return y; +} + +#endif /* CONFIG_FB_LOGO_EXTRA */ + + int fb_prepare_logo(struct fb_info *info, int rotate) { int depth = fb_get_color_depth(&info->var, &info->fix); - int yres; + unsigned int yres; memset(&fb_logo, 0, sizeof(struct logo_data)); @@ -456,7 +585,7 @@ int fb_prepare_logo(struct fb_info *info, int rotate) if (!fb_logo.logo) { return 0; } - + if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD) yres = info->var.yres; else @@ -473,75 +602,20 @@ int fb_prepare_logo(struct fb_info *info, int rotate) else if (fb_logo.logo->type == LINUX_LOGO_VGA16) fb_logo.depth = 4; else - fb_logo.depth = 1; - return fb_logo.logo->height; + fb_logo.depth = 1; + + return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); } int fb_show_logo(struct fb_info *info, int rotate) { - u32 *palette = NULL, *saved_pseudo_palette = NULL; - unsigned char *logo_new = NULL, *logo_rotate = NULL; - struct fb_image image; - - /* Return if the frame buffer is not mapped or suspended */ - if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING || - info->flags & FBINFO_MODULE) - return 0; - - image.depth = 8; - image.data = fb_logo.logo->data; - - if (fb_logo.needs_cmapreset) - fb_set_logocmap(info, fb_logo.logo); - - if (fb_logo.needs_truepalette || - fb_logo.needs_directpalette) { - palette = kmalloc(256 * 4, GFP_KERNEL); - if (palette == NULL) - return 0; - - if (fb_logo.needs_truepalette) - fb_set_logo_truepalette(info, fb_logo.logo, palette); - else - fb_set_logo_directpalette(info, fb_logo.logo, palette); - - saved_pseudo_palette = info->pseudo_palette; - info->pseudo_palette = palette; - } - - if (fb_logo.depth <= 4) { - logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, - GFP_KERNEL); - if (logo_new == NULL) { - kfree(palette); - if (saved_pseudo_palette) - info->pseudo_palette = saved_pseudo_palette; - return 0; - } - image.data = logo_new; - fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); - } + int y; - image.dx = 0; - image.dy = 0; - image.width = fb_logo.logo->width; - image.height = fb_logo.logo->height; + y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, + num_online_cpus()); + y = fb_show_extra_logos(info, y, rotate); - if (rotate) { - logo_rotate = kmalloc(fb_logo.logo->width * - fb_logo.logo->height, GFP_KERNEL); - if (logo_rotate) - fb_rotate_logo(info, logo_rotate, &image, rotate); - } - - fb_do_show_logo(info, &image, rotate, num_online_cpus()); - - kfree(palette); - if (saved_pseudo_palette != NULL) - info->pseudo_palette = saved_pseudo_palette; - kfree(logo_new); - kfree(logo_rotate); - return fb_logo.logo->height; + return y; } #else int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; } @@ -1155,17 +1229,15 @@ fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #endif -static int +static int fb_mmap(struct file *file, struct vm_area_struct * vma) { int fbidx = iminor(file->f_path.dentry->d_inode); struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; unsigned long off; -#if !defined(__sparc__) || defined(__sparc_v9__) unsigned long start; u32 len; -#endif if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; @@ -1180,12 +1252,6 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) return res; } -#if defined(__sparc__) && !defined(__sparc_v9__) - /* Should never get here, all fb drivers should have their own - mmap routines */ - return -EINVAL; -#else - /* !sparc32... */ lock_kernel(); /* frame buffer memory */ @@ -1209,50 +1275,11 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO | VM_RESERVED; -#if defined(__mc68000__) -#if defined(CONFIG_SUN3) - pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; -#elif defined(CONFIG_MMU) - if (CPU_IS_020_OR_030) - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; - if (CPU_IS_040_OR_060) { - pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; - /* Use no-cache mode, serialized */ - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; - } -#endif -#elif defined(__powerpc__) - vma->vm_page_prot = phys_mem_access_prot(file, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); -#elif defined(__alpha__) - /* Caching is off in the I/O space quadrant by design. */ -#elif defined(__i386__) || defined(__x86_64__) - if (boot_cpu_data.x86 > 3) - pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; -#elif defined(__mips__) || defined(__sparc_v9__) - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#elif defined(__hppa__) - pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; -#elif defined(__arm__) || defined(__sh__) || defined(__m32r__) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); -#elif defined(__avr32__) - vma->vm_page_prot = __pgprot((pgprot_val(vma->vm_page_prot) - & ~_PAGE_CACHABLE) - | (_PAGE_BUFFER | _PAGE_DIRTY)); -#elif defined(__ia64__) - if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - else - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#else -#warning What do we have to do here?? -#endif + fb_pgprotect(file, vma, off); if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; -#endif /* !sparc32 */ } static int @@ -1388,17 +1415,34 @@ register_framebuffer(struct fb_info *fb_info) * * Returns negative errno on error, or zero for success. * + * This function will also notify the framebuffer console + * to release the driver. + * + * This is meant to be called within a driver's module_exit() + * function. If this is called outside module_exit(), ensure + * that the driver implements fb_open() and fb_release() to + * check that no processes are using the device. */ int unregister_framebuffer(struct fb_info *fb_info) { struct fb_event event; - int i; + int i, ret = 0; i = fb_info->node; - if (!registered_fb[i]) - return -EINVAL; + if (!registered_fb[i]) { + ret = -EINVAL; + goto done; + } + + event.info = fb_info; + ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + + if (ret) { + ret = -EINVAL; + goto done; + } if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) @@ -1410,7 +1454,8 @@ unregister_framebuffer(struct fb_info *fb_info) device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); - return 0; +done: + return ret; } /** diff --git a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c index 70ff55b14596..6c91c61cdb63 100644 --- a/drivers/video/fm2fb.c +++ b/drivers/video/fm2fb.c @@ -195,13 +195,15 @@ static int fm2fb_blank(int blank, struct fb_info *info) static int fm2fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { - if (regno > info->cmap.len) - return 1; - red >>= 8; - green >>= 8; - blue >>= 8; + if (regno < 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + ((u32*)(info->pseudo_palette))[regno] = (red << 16) | + (green << 8) | blue; + } - ((u32*)(info->pseudo_palette))[regno] = (red << 16) | (green << 8) | blue; return 0; } @@ -237,7 +239,7 @@ static int __devinit fm2fb_probe(struct zorro_dev *z, if (!zorro_request_device(z,"fm2fb")) return -ENXIO; - info = framebuffer_alloc(256 * sizeof(u32), &z->dev); + info = framebuffer_alloc(16 * sizeof(u32), &z->dev); if (!info) { zorro_release_device(z); return -ENOMEM; diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c index bf0e60b5a3b6..b9b572b293d4 100644 --- a/drivers/video/gbefb.c +++ b/drivers/video/gbefb.c @@ -86,7 +86,7 @@ static int gbe_revision; static int ypan, ywrap; -static uint32_t pseudo_palette[256]; +static uint32_t pseudo_palette[16]; static char *mode_option __initdata = NULL; @@ -854,8 +854,7 @@ static int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green, green >>= 8; blue >>= 8; - switch (info->var.bits_per_pixel) { - case 8: + if (info->var.bits_per_pixel <= 8) { /* wait for the color map FIFO to have a free entry */ for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++) udelay(10); @@ -864,23 +863,25 @@ static int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green, return 1; } gbe->cmap[regno] = (red << 24) | (green << 16) | (blue << 8); - break; - case 15: - case 16: - red >>= 3; - green >>= 3; - blue >>= 3; - pseudo_palette[regno] = - (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset); - break; - case 32: - pseudo_palette[regno] = - (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset); - break; + } else if (regno < 16) { + switch (info->var.bits_per_pixel) { + case 15: + case 16: + red >>= 3; + green >>= 3; + blue >>= 3; + pseudo_palette[regno] = + (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + break; + case 32: + pseudo_palette[regno] = + (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + break; + } } return 0; diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h index 889e4ea5edc1..328ae6c673ec 100644 --- a/drivers/video/i810/i810.h +++ b/drivers/video/i810/i810.h @@ -266,7 +266,7 @@ struct i810fb_par { struct i810fb_i2c_chan chan[3]; struct mutex open_lock; unsigned int use_count; - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; unsigned long mmio_start_phys; u8 __iomem *mmio_start_virtual; u8 *edid; diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index eb1a4812ad1d..b87ea21d3d78 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -379,10 +379,6 @@ int __init igafb_init(void) if (fb_get_options("igafb", NULL)) return -ENODEV; - /* Do not attach when we have a serial console. */ - if (!con_is_present()) - return -ENXIO; - pdev = pci_get_device(PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682, 0); if (pdev == NULL) { diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index 80b94c19a9fa..6148300fadd6 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h @@ -302,7 +302,7 @@ struct intelfb_info { u32 ring_lockup; /* palette */ - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; /* chip info */ int pci_chipset; diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index 9397bcef3018..9de1c114f809 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -10,6 +10,11 @@ menuconfig LOGO if LOGO +config FB_LOGO_EXTRA + bool + depends on FB=y + default y if SPU_BASE + config LOGO_LINUX_MONO bool "Standard black and white Linux logo" default y diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index b985dfad6c63..a5fc4edf84e6 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o obj-$(CONFIG_LOGO_M32R_CLUT224) += logo_m32r_clut224.o +obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o + # How to generate logo's # Use logo-cfiles to retrieve list of .c files to be built diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 80c03618eb53..2b0f799aa8da 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -34,8 +34,11 @@ extern const struct linux_logo logo_superh_vga16; extern const struct linux_logo logo_superh_clut224; extern const struct linux_logo logo_m32r_clut224; - -const struct linux_logo *fb_find_logo(int depth) +/* logo's are marked __initdata. Use __init_refok to tell + * modpost that it is intended that this function uses data + * marked __initdata. + */ +const struct linux_logo * __init_refok fb_find_logo(int depth) { const struct linux_logo *logo = NULL; diff --git a/drivers/video/logo/logo_spe_clut224.ppm b/drivers/video/logo/logo_spe_clut224.ppm new file mode 100644 index 000000000000..d36ad624a79c --- /dev/null +++ b/drivers/video/logo/logo_spe_clut224.ppm @@ -0,0 +1,283 @@ +P3 +40 40 +255 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 6 6 6 +15 15 15 21 21 21 19 19 19 14 14 14 6 6 6 2 2 2 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 21 21 21 55 55 55 +56 56 56 54 54 54 53 53 53 60 60 60 56 56 56 25 25 25 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 2 2 2 27 27 27 62 62 62 17 17 19 +2 2 6 2 2 6 2 2 6 2 2 6 16 16 18 57 57 57 +45 45 45 8 8 8 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 16 16 16 62 62 62 8 8 10 2 2 6 +2 2 6 2 2 6 2 2 6 12 12 14 67 67 67 16 16 17 +45 45 45 41 41 41 4 4 4 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 2 2 2 35 35 35 40 40 40 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 15 15 17 70 70 70 27 27 27 +3 3 6 62 62 62 20 20 20 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 4 4 4 58 58 58 12 12 14 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 4 4 7 4 4 7 2 2 6 +2 2 6 34 34 36 40 40 40 3 3 3 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 64 64 64 2 2 6 5 5 5 17 17 17 +3 3 6 2 2 6 2 2 6 15 15 15 21 21 21 7 7 10 +2 2 6 8 8 10 62 62 62 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 66 66 66 5 5 8 122 122 122 122 122 122 +9 9 11 3 3 6 104 96 81 179 179 179 122 122 122 13 13 13 +2 2 6 2 2 6 67 67 67 10 10 10 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 65 65 65 41 41 43 152 149 142 192 191 189 +48 48 49 23 23 24 228 210 210 86 86 86 192 191 189 59 59 61 +2 2 6 2 2 6 64 64 64 14 14 14 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 66 66 66 59 59 59 59 59 61 86 86 86 +99 84 50 78 66 28 152 149 142 5 5 8 122 122 122 104 96 81 +2 2 6 2 2 6 67 67 67 14 14 14 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 63 63 63 24 24 24 152 149 142 175 122 13 +238 184 12 220 170 13 226 181 52 112 86 32 194 165 151 46 46 47 +2 2 6 2 2 6 65 65 65 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 59 59 59 21 21 21 175 122 13 231 174 11 +240 192 13 237 183 61 240 192 13 240 192 13 234 179 16 81 64 9 +2 2 6 2 2 6 63 63 63 25 25 25 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 54 54 54 51 48 39 189 138 9 238 184 12 +240 192 13 240 192 13 240 192 13 215 161 11 207 152 19 81 64 9 +16 16 18 5 5 8 40 40 40 44 44 44 4 4 4 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 59 59 59 27 27 27 126 107 64 187 136 12 +220 170 13 201 147 20 189 138 9 198 154 46 199 182 125 70 70 70 +27 27 27 104 96 81 12 12 14 70 70 70 16 16 16 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 17 17 17 70 70 70 12 12 12 168 168 168 174 135 135 +175 122 13 175 122 13 178 151 83 192 191 189 233 233 233 179 179 179 +3 3 6 29 29 31 3 3 6 41 41 41 44 44 44 5 5 5 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 8 8 53 53 53 44 44 44 59 59 59 238 238 238 192 191 189 +192 191 189 192 191 189 221 205 205 240 240 240 253 253 253 253 253 253 +70 70 70 2 2 6 2 2 6 5 5 8 67 67 67 22 22 22 +2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 +38 38 38 56 56 56 7 7 9 221 205 205 253 253 253 233 233 233 +221 205 205 233 233 233 251 251 251 253 253 253 253 253 253 253 253 253 +192 191 189 2 2 6 2 2 6 2 2 6 25 25 25 64 64 64 +15 15 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 27 27 27 +66 66 66 7 7 9 86 86 86 252 252 252 253 253 253 253 253 253 +252 252 252 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +244 244 244 19 19 21 2 2 6 2 2 6 2 2 6 38 38 38 +54 54 54 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 14 14 14 62 62 62 +10 10 12 3 3 6 122 122 122 235 235 235 251 251 251 248 248 248 +235 235 235 248 248 248 252 252 252 246 246 246 233 233 233 237 228 228 +223 207 207 70 70 70 2 2 6 2 2 6 2 2 6 2 2 6 +46 46 47 38 38 38 4 4 4 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 33 33 33 44 44 44 +4 4 7 9 9 11 168 168 168 240 240 240 252 252 252 252 252 252 +246 246 246 253 253 253 253 253 253 251 251 251 245 241 241 233 233 233 +221 205 205 192 191 189 29 29 31 27 27 27 9 9 12 2 2 6 +3 3 6 65 65 65 15 15 15 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 59 59 59 19 19 21 +24 24 24 86 86 86 249 249 249 253 253 253 253 253 253 253 253 253 +253 253 253 228 210 210 241 230 230 253 253 253 253 253 253 253 253 253 +251 251 251 228 210 210 152 149 142 5 5 8 27 27 27 4 4 7 +2 2 6 46 46 47 34 34 34 2 2 2 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 16 16 16 67 67 67 19 19 21 +12 12 14 223 207 207 254 20 20 254 20 20 253 127 127 242 223 223 +254 20 20 253 127 127 254 48 48 242 223 223 254 86 86 254 20 20 +254 20 20 253 137 137 233 233 233 32 32 32 35 35 35 23 23 24 +2 2 6 15 15 15 60 60 60 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 4 4 4 38 38 38 48 48 49 22 22 22 +86 86 86 253 253 253 254 20 20 241 230 230 227 216 186 253 137 137 +253 137 137 253 253 253 253 137 137 253 137 137 254 48 48 253 253 253 +253 253 253 253 253 253 253 253 253 62 62 62 2 2 6 23 23 24 +2 2 6 2 2 6 62 62 62 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 14 14 14 70 70 70 14 14 14 16 16 18 +179 179 179 253 253 253 227 216 186 254 48 48 240 219 160 253 127 127 +254 20 20 253 137 137 254 86 86 231 203 141 254 20 20 254 20 20 +253 137 137 253 253 253 253 253 253 104 96 81 2 2 6 23 23 24 +2 2 6 2 2 6 46 46 47 27 27 27 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 4 4 4 39 39 39 42 42 43 19 19 21 13 13 13 +228 210 210 242 223 223 253 253 253 242 223 223 253 127 127 253 127 127 +253 127 127 253 127 127 253 137 137 253 253 253 254 48 48 253 253 253 +228 210 210 253 253 253 253 253 253 122 122 122 2 2 6 19 19 19 +2 2 6 2 2 6 39 39 39 38 38 38 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 8 8 8 60 60 60 3 3 6 33 33 33 38 38 38 +253 137 137 254 86 86 253 137 137 254 86 86 253 137 137 209 197 168 +253 127 127 253 253 253 253 253 253 253 253 253 253 127 127 254 86 86 +254 86 86 253 137 137 253 253 253 122 122 122 2 2 6 17 17 17 +2 2 6 2 2 6 34 34 36 42 42 43 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 13 13 13 59 59 59 2 2 6 9 9 12 56 56 56 +252 252 252 240 219 160 253 137 137 240 219 160 253 253 253 237 228 228 +254 86 86 253 253 253 253 253 253 253 253 253 253 253 253 242 223 223 +227 216 186 249 249 249 253 253 253 122 122 122 16 16 17 17 17 17 +12 12 14 3 3 6 39 39 39 38 38 38 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 +5 5 5 22 22 22 104 96 81 187 136 12 207 152 19 51 48 39 +221 205 205 253 253 253 253 253 253 253 253 253 253 253 253 240 240 240 +250 247 243 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 250 247 243 240 219 160 99 84 50 5 5 8 2 2 6 +7 7 9 46 46 47 58 58 58 35 35 35 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 8 8 8 33 33 33 +58 58 58 86 86 86 170 136 53 239 182 13 246 190 14 220 170 13 +44 38 29 179 179 179 253 253 253 253 253 253 253 253 253 240 240 240 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 240 219 160 240 192 13 112 86 32 2 2 6 2 2 6 +3 3 6 41 33 20 220 170 13 53 53 53 4 4 4 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 32 32 32 150 116 44 +215 161 11 215 161 11 228 170 11 245 188 14 246 190 14 246 190 14 +187 136 12 9 9 11 122 122 122 251 251 251 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +248 248 248 211 196 135 239 182 13 175 122 13 6 5 6 2 2 6 +16 14 12 187 136 12 238 184 12 84 78 65 10 10 10 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 4 4 4 53 53 53 207 152 19 +242 185 13 245 188 14 246 190 14 246 190 14 246 190 14 246 190 14 +240 192 13 81 64 9 2 2 6 86 86 86 244 244 244 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +233 233 233 199 182 125 231 174 11 207 152 19 175 122 13 175 122 13 +201 147 20 239 182 13 244 187 14 150 116 44 35 35 35 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 5 5 5 53 53 53 201 147 20 +242 185 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 220 170 13 13 11 10 2 2 6 152 149 142 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +235 235 235 199 182 125 228 170 11 234 177 12 226 168 11 226 168 11 +234 177 12 246 190 14 246 190 14 234 179 16 126 107 64 36 36 36 +6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 3 3 3 48 48 49 189 142 35 +242 185 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 140 112 39 36 36 36 192 191 189 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +192 191 189 112 86 32 226 168 11 244 187 14 244 187 14 244 187 14 +245 188 14 246 190 14 246 190 14 246 190 14 242 185 13 150 116 44 +27 27 27 2 2 2 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 58 58 58 189 142 35 +239 182 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 239 188 14 209 197 168 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 252 252 252 168 168 168 +16 16 18 97 67 8 228 170 11 245 188 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 244 187 14 198 154 46 +35 35 35 3 3 3 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 13 13 13 84 78 65 215 161 11 +244 187 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 238 184 12 187 136 12 168 168 168 244 244 244 +253 253 253 252 252 252 240 240 240 179 179 179 67 67 67 2 2 6 +2 2 6 97 67 8 228 170 11 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 245 188 14 234 177 12 189 142 35 86 77 61 +16 16 16 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 13 13 13 103 92 56 207 152 19 +228 170 11 234 177 12 239 182 13 242 186 14 245 188 14 246 190 14 +246 190 14 246 190 14 239 182 13 189 138 9 41 33 20 10 10 12 +30 30 31 23 23 24 5 5 8 2 2 6 2 2 6 2 2 6 +4 4 6 112 86 32 215 161 11 245 188 14 246 190 14 245 188 14 +239 182 13 228 170 11 189 142 35 104 96 81 48 48 49 17 17 17 +2 2 2 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 5 5 5 39 39 39 103 92 56 +141 109 44 175 122 13 187 136 12 189 138 9 207 152 19 228 170 11 +239 182 13 239 182 13 215 161 11 175 122 13 41 33 20 2 2 6 +15 15 17 20 20 22 20 20 22 20 20 22 20 20 22 8 8 10 +4 4 6 97 67 8 189 138 9 231 174 11 239 182 13 226 168 11 +189 138 9 126 107 64 59 59 59 21 21 21 5 5 5 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 17 17 17 +34 34 34 57 57 57 84 78 65 103 92 56 125 101 41 140 112 39 +175 122 13 175 122 13 175 122 13 97 67 8 72 67 58 84 78 65 +60 60 60 56 56 56 56 56 56 56 56 56 57 57 57 65 65 65 +86 86 86 95 73 34 175 122 13 187 136 12 187 136 12 175 122 13 +103 92 56 41 41 41 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +2 2 2 4 4 4 12 12 12 24 24 24 40 40 40 70 70 70 +86 77 61 95 73 34 88 72 41 72 67 58 36 36 36 10 10 10 +5 5 5 5 5 5 5 5 5 4 4 4 5 5 5 6 6 6 +22 22 22 61 61 59 88 72 41 112 86 32 112 86 32 84 78 65 +32 32 32 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 10 10 10 +21 21 21 33 33 33 31 31 31 16 16 16 2 2 2 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +2 2 2 12 12 12 30 30 31 40 40 40 32 32 32 16 16 16 +2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index f7d647dda978..aa8c714d6245 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -170,7 +170,7 @@ static struct fb_fix_screeninfo macfb_fix = { }; static struct fb_info fb_info; -static u32 pseudo_palette[17]; +static u32 pseudo_palette[16]; static int inverse = 0; static int vidtest = 0; @@ -529,56 +529,63 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green, if (regno >= fb_info->cmap.len) return 1; - switch (fb_info->var.bits_per_pixel) { - case 1: - /* We shouldn't get here */ - break; - case 2: - case 4: - case 8: - if (macfb_setpalette) - macfb_setpalette(regno, red, green, blue, fb_info); - else - return 1; - break; - case 16: - if (fb_info->var.red.offset == 10) { - /* 1:5:5:5 */ - ((u32*) (fb_info->pseudo_palette))[regno] = + if (fb_info->var.bits_per_pixel <= 8) { + switch (fb_info->var.bits_per_pixel) { + case 1: + /* We shouldn't get here */ + break; + case 2: + case 4: + case 8: + if (macfb_setpalette) + macfb_setpalette(regno, red, green, blue, + fb_info); + else + return 1; + break; + } + } else if (regno < 16) { + switch (fb_info->var.bits_per_pixel) { + case 16: + if (fb_info->var.red.offset == 10) { + /* 1:5:5:5 */ + ((u32*) (fb_info->pseudo_palette))[regno] = ((red & 0xf800) >> 1) | ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11) | ((transp != 0) << 15); - } else { - /* 0:5:6:5 */ - ((u32*) (fb_info->pseudo_palette))[regno] = + } else { + /* 0:5:6:5 */ + ((u32*) (fb_info->pseudo_palette))[regno] = ((red & 0xf800) ) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); + } + break; + /* I'm pretty sure that one or the other of these + doesn't exist on 68k Macs */ + case 24: + red >>= 8; + green >>= 8; + blue >>= 8; + ((u32 *)(fb_info->pseudo_palette))[regno] = + (red << fb_info->var.red.offset) | + (green << fb_info->var.green.offset) | + (blue << fb_info->var.blue.offset); + break; + case 32: + red >>= 8; + green >>= 8; + blue >>= 8; + ((u32 *)(fb_info->pseudo_palette))[regno] = + (red << fb_info->var.red.offset) | + (green << fb_info->var.green.offset) | + (blue << fb_info->var.blue.offset); + break; } - break; - /* I'm pretty sure that one or the other of these - doesn't exist on 68k Macs */ - case 24: - red >>= 8; - green >>= 8; - blue >>= 8; - ((u32 *)(fb_info->pseudo_palette))[regno] = - (red << fb_info->var.red.offset) | - (green << fb_info->var.green.offset) | - (blue << fb_info->var.blue.offset); - break; - case 32: - red >>= 8; - green >>= 8; - blue >>= 8; - ((u32 *)(fb_info->pseudo_palette))[regno] = - (red << fb_info->var.red.offset) | - (green << fb_info->var.green.offset) | - (blue << fb_info->var.blue.offset); - break; - } - return 0; + } + + return 0; } static struct fb_ops macfb_ops = { diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c index ab2149531a04..083f60321ed8 100644 --- a/drivers/video/macmodes.c +++ b/drivers/video/macmodes.c @@ -369,9 +369,8 @@ EXPORT_SYMBOL(mac_map_monitor_sense); * */ -int __devinit mac_find_mode(struct fb_var_screeninfo *var, - struct fb_info *info, const char *mode_option, - unsigned int default_bpp) +int mac_find_mode(struct fb_var_screeninfo *var, struct fb_info *info, + const char *mode_option, unsigned int default_bpp) { const struct fb_videomode *db = NULL; unsigned int dbsize = 0; diff --git a/drivers/video/macmodes.h b/drivers/video/macmodes.h index babeb81f467d..b86ba08aac9e 100644 --- a/drivers/video/macmodes.h +++ b/drivers/video/macmodes.h @@ -55,10 +55,10 @@ extern int mac_vmode_to_var(int vmode, int cmode, extern int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode, int *cmode); extern int mac_map_monitor_sense(int sense); -extern int __devinit mac_find_mode(struct fb_var_screeninfo *var, - struct fb_info *info, - const char *mode_option, - unsigned int default_bpp); +extern int mac_find_mode(struct fb_var_screeninfo *var, + struct fb_info *info, + const char *mode_option, + unsigned int default_bpp); /* diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c index c57aaadf410c..3660d2673bdc 100644 --- a/drivers/video/matrox/matroxfb_accel.c +++ b/drivers/video/matrox/matroxfb_accel.c @@ -91,7 +91,6 @@ static inline void matrox_cfb4_pal(u_int32_t* pal) { for (i = 0; i < 16; i++) { pal[i] = i * 0x11111111U; } - pal[i] = 0xFFFFFFFF; } static inline void matrox_cfb8_pal(u_int32_t* pal) { @@ -100,7 +99,6 @@ static inline void matrox_cfb8_pal(u_int32_t* pal) { for (i = 0; i < 16; i++) { pal[i] = i * 0x01010101U; } - pal[i] = 0x0F0F0F0F; } static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area); @@ -145,13 +143,10 @@ void matrox_cfbX_init(WPMINFO2) { ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit; } break; - case 16: if (ACCESS_FBINFO(fbcon).var.green.length == 5) { + case 16: if (ACCESS_FBINFO(fbcon).var.green.length == 5) maccess = 0xC0000001; - ACCESS_FBINFO(cmap[16]) = 0x7FFF7FFF; - } else { + else maccess = 0x40000001; - ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF; - } mopmode = M_OPMODE_16BPP; if (accel) { ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; @@ -161,7 +156,6 @@ void matrox_cfbX_init(WPMINFO2) { break; case 24: maccess = 0x00000003; mopmode = M_OPMODE_24BPP; - ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF; if (accel) { ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; @@ -170,7 +164,6 @@ void matrox_cfbX_init(WPMINFO2) { break; case 32: maccess = 0x00000002; mopmode = M_OPMODE_32BPP; - ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF; if (accel) { ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 886e475f22f2..86ca7b179000 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -679,6 +679,8 @@ static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, mga_outb(M_DAC_VAL, blue); break; case 16: + if (regno >= 16) + break; { u_int16_t col = (red << ACCESS_FBINFO(fbcon).var.red.offset) | @@ -690,6 +692,8 @@ static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, break; case 24: case 32: + if (regno >= 16) + break; ACCESS_FBINFO(cmap[regno]) = (red << ACCESS_FBINFO(fbcon).var.red.offset) | (green << ACCESS_FBINFO(fbcon).var.green.offset) | diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index 9c25c2f7966b..d59577c8de86 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -518,7 +518,7 @@ struct matrox_fb_info { dll:1; } memory; } values; - u_int32_t cmap[17]; + u_int32_t cmap[16]; }; #define info2minfo(info) container_of(info, struct matrox_fb_info, fbcon) diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c index 03ae55b168ff..4b3344e03695 100644 --- a/drivers/video/matrox/matroxfb_crtc2.c +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -163,11 +163,6 @@ static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004; } -static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info) { - /* no acceleration for secondary head... */ - m2info->cmap[16] = 0xFFFFFFFF; -} - static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, struct fb_var_screeninfo* var) { unsigned int pos; @@ -385,7 +380,6 @@ static int matroxfb_dh_set_par(struct fb_info* info) { } } up_read(&ACCESS_FBINFO(altout).lock); - matroxfb_dh_cfbX_init(m2info); } m2info->initialized = 1; return 0; diff --git a/drivers/video/matrox/matroxfb_crtc2.h b/drivers/video/matrox/matroxfb_crtc2.h index 177177609be7..1005582e843e 100644 --- a/drivers/video/matrox/matroxfb_crtc2.h +++ b/drivers/video/matrox/matroxfb_crtc2.h @@ -28,7 +28,7 @@ struct matroxfb_dh_fb_info { unsigned int interlaced:1; - u_int32_t cmap[17]; + u_int32_t cmap[16]; }; #endif /* __MATROXFB_CRTC2_H__ */ diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 5d29a26b8cdf..de0d755f9019 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -273,8 +273,11 @@ static int matroxfb_PLL_mavenclock(const struct matrox_pll_features2* pll, } } } + + /* if h2/post/in/feed have not been assigned, return zero (error) */ if (besth2 < 2) return 0; + dprintk(KERN_ERR "clk: %02X %02X %02X %d %d\n", *in, *feed, *post, fxtal, fwant); return fxtal * (*feed) / (*in) * ctl->den; } @@ -284,7 +287,7 @@ static unsigned int matroxfb_mavenclock(const struct matrox_pll_ctl* ctl, unsigned int* in, unsigned int* feed, unsigned int* post, unsigned int* htotal2) { unsigned int fvco; - unsigned int p; + unsigned int uninitialized_var(p); fvco = matroxfb_PLL_mavenclock(&maven1000_pll, ctl, htotal, vtotal, in, feed, &p, htotal2); if (!fvco) @@ -715,7 +718,9 @@ static int maven_find_exact_clocks(unsigned int ht, unsigned int vt, m->regs[0x82] = 0x81; for (x = 0; x < 8; x++) { - unsigned int a, b, c, h2; + unsigned int c; + unsigned int uninitialized_var(a), uninitialized_var(b), + uninitialized_var(h2); unsigned int h = ht + 2 + x; if (!matroxfb_mavenclock((m->mode == MATROXFB_OUTPUT_MODE_PAL) ? &maven_PAL : &maven_NTSC, h, vt, &a, &b, &c, &h2)) { diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c index aff11bbf59a7..d1a10549f543 100644 --- a/drivers/video/nvidia/nv_hw.c +++ b/drivers/video/nvidia/nv_hw.c @@ -150,8 +150,7 @@ static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk, M = pll & 0xFF; N = (pll >> 8) & 0xFF; if (((par->Chipset & 0xfff0) == 0x0290) || - ((par->Chipset & 0xfff0) == 0x0390) || - ((par->Chipset & 0xfff0) == 0x02E0)) { + ((par->Chipset & 0xfff0) == 0x0390)) { MB = 1; NB = 1; } else { @@ -161,7 +160,7 @@ static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk, *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P; pll = NV_RD32(par->PMC, 0x4000); - P = (pll >> 16) & 0x03; + P = (pll >> 16) & 0x07; pll = NV_RD32(par->PMC, 0x4004); M = pll & 0xFF; N = (pll >> 8) & 0xFF; @@ -892,11 +891,17 @@ void NVCalcStateExt(struct nvidia_par *par, state->general = bpp == 16 ? 0x00101100 : 0x00100100; state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00; break; + case NV_ARCH_40: + if (!par->FlatPanel) + state->control = NV_RD32(par->PRAMDAC0, 0x0580) & + 0xeffffeff; + /* fallthrough */ case NV_ARCH_10: case NV_ARCH_20: case NV_ARCH_30: default: - if ((par->Chipset & 0xfff0) == 0x0240) { + if ((par->Chipset & 0xfff0) == 0x0240 || + (par->Chipset & 0xfff0) == 0x03d0) { state->arbitration0 = 256; state->arbitration1 = 0x0480; } else if (((par->Chipset & 0xffff) == 0x01A0) || @@ -939,7 +944,7 @@ void NVCalcStateExt(struct nvidia_par *par, void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) { - int i; + int i, j; NV_WR32(par->PMC, 0x0140, 0x00000000); NV_WR32(par->PMC, 0x0200, 0xFFFF00FF); @@ -951,7 +956,8 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) NV_WR32(par->PTIMER, 0x0100 * 4, 0xFFFFFFFF); if (par->Architecture == NV_ARCH_04) { - NV_WR32(par->PFB, 0x0200, state->config); + if (state) + NV_WR32(par->PFB, 0x0200, state->config); } else if ((par->Architecture < NV_ARCH_40) || (par->Chipset & 0xfff0) == 0x0040) { for (i = 0; i < 8; i++) { @@ -964,8 +970,9 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) if (((par->Chipset & 0xfff0) == 0x0090) || ((par->Chipset & 0xfff0) == 0x01D0) || - ((par->Chipset & 0xfff0) == 0x02E0) || - ((par->Chipset & 0xfff0) == 0x0290)) + ((par->Chipset & 0xfff0) == 0x0290) || + ((par->Chipset & 0xfff0) == 0x0390) || + ((par->Chipset & 0xfff0) == 0x03D0)) regions = 15; for(i = 0; i < regions; i++) { NV_WR32(par->PFB, 0x0600 + (i * 0x10), 0); @@ -1206,16 +1213,20 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) NV_WR32(par->PGRAPH, 0x0608, 0xFFFFFFFF); } else { if (par->Architecture >= NV_ARCH_40) { - u32 tmp; - NV_WR32(par->PGRAPH, 0x0084, 0x401287c0); NV_WR32(par->PGRAPH, 0x008C, 0x60de8051); NV_WR32(par->PGRAPH, 0x0090, 0x00008000); NV_WR32(par->PGRAPH, 0x0610, 0x00be3c5f); + NV_WR32(par->PGRAPH, 0x0bc4, + NV_RD32(par->PGRAPH, 0x0bc4) | + 0x00008000); - tmp = NV_RD32(par->REGS, 0x1540) & 0xff; - for(i = 0; tmp && !(tmp & 1); tmp >>= 1, i++); - NV_WR32(par->PGRAPH, 0x5000, i); + j = NV_RD32(par->REGS, 0x1540) & 0xff; + + if (j) { + for (i = 0; !(j & 1); j >>= 1, i++); + NV_WR32(par->PGRAPH, 0x5000, i); + } if ((par->Chipset & 0xfff0) == 0x0040) { NV_WR32(par->PGRAPH, 0x09b0, @@ -1250,6 +1261,7 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) case 0x0160: case 0x01D0: case 0x0240: + case 0x03D0: NV_WR32(par->PMC, 0x1700, NV_RD32(par->PFB, 0x020C)); NV_WR32(par->PMC, 0x1704, 0); @@ -1269,7 +1281,6 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) 0x00000108); break; case 0x0220: - case 0x0230: NV_WR32(par->PGRAPH, 0x0860, 0); NV_WR32(par->PGRAPH, 0x0864, 0); NV_WR32(par->PRAMDAC, 0x0608, @@ -1277,8 +1288,8 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) 0x00100000); break; case 0x0090: - case 0x02E0: case 0x0290: + case 0x0390: NV_WR32(par->PRAMDAC, 0x0608, NV_RD32(par->PRAMDAC, 0x0608) | 0x00100000); @@ -1355,8 +1366,9 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) } else { if (((par->Chipset & 0xfff0) == 0x0090) || ((par->Chipset & 0xfff0) == 0x01D0) || - ((par->Chipset & 0xfff0) == 0x02E0) || - ((par->Chipset & 0xfff0) == 0x0290)) { + ((par->Chipset & 0xfff0) == 0x0290) || + ((par->Chipset & 0xfff0) == 0x0390) || + ((par->Chipset & 0xfff0) == 0x03D0)) { for (i = 0; i < 60; i++) { NV_WR32(par->PGRAPH, 0x0D00 + i*4, @@ -1407,8 +1419,8 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) } else { if ((par->Chipset & 0xfff0) == 0x0090 || (par->Chipset & 0xfff0) == 0x01D0 || - (par->Chipset & 0xfff0) == 0x02E0 || - (par->Chipset & 0xfff0) == 0x0290) { + (par->Chipset & 0xfff0) == 0x0290 || + (par->Chipset & 0xfff0) == 0x0390) { NV_WR32(par->PGRAPH, 0x0DF0, NV_RD32(par->PFB, 0x0200)); NV_WR32(par->PGRAPH, 0x0DF4, @@ -1495,6 +1507,12 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) NV_WR32(par->PFIFO, 0x0494 * 4, 0x00000001); NV_WR32(par->PFIFO, 0x0495 * 4, 0x00000001); NV_WR32(par->PFIFO, 0x0140 * 4, 0x00000001); + + if (!state) { + par->CurrentState = NULL; + return; + } + if (par->Architecture >= NV_ARCH_10) { if (par->twoHeads) { NV_WR32(par->PCRTC0, 0x0860, state->head); @@ -1566,6 +1584,9 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) VGA_WR08(par->PCIO, 0x03D5, state->interlace); if (!par->FlatPanel) { + if (par->Architecture >= NV_ARCH_40) + NV_WR32(par->PRAMDAC0, 0x0580, state->control); + NV_WR32(par->PRAMDAC0, 0x050C, state->pllsel); NV_WR32(par->PRAMDAC0, 0x0508, state->vpll); if (par->twoHeads) @@ -1631,6 +1652,9 @@ void NVUnloadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) { state->scale = NV_RD32(par->PRAMDAC, 0x0848); state->config = NV_RD32(par->PFB, 0x0200); + if (par->Architecture >= NV_ARCH_40 && !par->FlatPanel) + state->control = NV_RD32(par->PRAMDAC0, 0x0580); + if (par->Architecture >= NV_ARCH_10) { if (par->twoHeads) { state->head = NV_RD32(par->PCRTC0, 0x0860); diff --git a/drivers/video/nvidia/nv_setup.c b/drivers/video/nvidia/nv_setup.c index 707e2c8a13ed..82579d3a9970 100644 --- a/drivers/video/nvidia/nv_setup.c +++ b/drivers/video/nvidia/nv_setup.c @@ -166,11 +166,13 @@ u8 NVReadDacData(struct nvidia_par *par) static int NVIsConnected(struct nvidia_par *par, int output) { volatile u32 __iomem *PRAMDAC = par->PRAMDAC0; - u32 reg52C, reg608; + u32 reg52C, reg608, dac0_reg608 = 0; int present; - if (output) - PRAMDAC += 0x800; + if (output) { + dac0_reg608 = NV_RD32(PRAMDAC, 0x0608); + PRAMDAC += 0x800; + } reg52C = NV_RD32(PRAMDAC, 0x052C); reg608 = NV_RD32(PRAMDAC, 0x0608); @@ -194,8 +196,8 @@ static int NVIsConnected(struct nvidia_par *par, int output) else printk("nvidiafb: CRTC%i analog not found\n", output); - NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) & - 0x0000EFFF); + if (output) + NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608); NV_WR32(PRAMDAC, 0x052C, reg52C); NV_WR32(PRAMDAC, 0x0608, reg608); diff --git a/drivers/video/nvidia/nv_type.h b/drivers/video/nvidia/nv_type.h index 38f7cc0a2331..2fdf77ec39fc 100644 --- a/drivers/video/nvidia/nv_type.h +++ b/drivers/video/nvidia/nv_type.h @@ -86,6 +86,7 @@ typedef struct _riva_hw_state { u32 timingV; u32 displayV; u32 crtcSync; + u32 control; } RIVA_HW_STATE; struct riva_regs { diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 41f63658572f..a7fe214f0f77 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -674,6 +674,7 @@ static int nvidiafb_set_par(struct fb_info *info) info->fbops->fb_sync = nvidiafb_sync; info->pixmap.scan_align = 4; info->flags &= ~FBINFO_HWACCEL_DISABLED; + info->flags |= FBINFO_READS_FAST; NVResetGraphics(info); } else { info->fbops->fb_imageblit = cfb_imageblit; @@ -682,6 +683,7 @@ static int nvidiafb_set_par(struct fb_info *info) info->fbops->fb_sync = NULL; info->pixmap.scan_align = 1; info->flags |= FBINFO_HWACCEL_DISABLED; + info->flags &= ~FBINFO_READS_FAST; } par->cursor_reset = 1; @@ -1193,7 +1195,8 @@ static u32 __devinit nvidia_get_chipset(struct fb_info *info) printk(KERN_INFO PFX "Device ID: %x \n", id); - if ((id & 0xfff0) == 0x00f0) { + if ((id & 0xfff0) == 0x00f0 || + (id & 0xfff0) == 0x02e0) { /* pci-e */ id = NV_RD32(par->REGS, 0x1800); @@ -1238,18 +1241,16 @@ static u32 __devinit nvidia_get_arch(struct fb_info *info) case 0x0040: /* GeForce 6800 */ case 0x00C0: /* GeForce 6800 */ case 0x0120: /* GeForce 6800 */ - case 0x0130: case 0x0140: /* GeForce 6600 */ case 0x0160: /* GeForce 6200 */ case 0x01D0: /* GeForce 7200, 7300, 7400 */ - case 0x02E0: /* GeForce 7300 GT */ case 0x0090: /* GeForce 7800 */ case 0x0210: /* GeForce 6800 */ case 0x0220: /* GeForce 6200 */ - case 0x0230: case 0x0240: /* GeForce 6100 */ case 0x0290: /* GeForce 7900 */ case 0x0390: /* GeForce 7600 */ + case 0x03D0: arch = NV_ARCH_40; break; case 0x0020: /* TNT, TNT2 */ diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 885b42836cbb..452433d46973 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -271,7 +271,7 @@ static void __init offb_init_fb(const char *name, const char *full_name, return; } - size = sizeof(struct fb_info) + sizeof(u32) * 17; + size = sizeof(struct fb_info) + sizeof(u32) * 16; info = kmalloc(size, GFP_ATOMIC); diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig new file mode 100644 index 000000000000..7f4d25b8a184 --- /dev/null +++ b/drivers/video/omap/Kconfig @@ -0,0 +1,58 @@ +config FB_OMAP + tristate "OMAP frame buffer support (EXPERIMENTAL)" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Frame buffer driver for OMAP based boards. + +config FB_OMAP_BOOTLOADER_INIT + bool "Check bootloader initializaion" + depends on FB_OMAP + help + Say Y here if you want to enable checking if the bootloader has + already initialized the display controller. In this case the + driver will skip the initialization. + +config FB_OMAP_CONSISTENT_DMA_SIZE + int "Consistent DMA memory size (MB)" + depends on FB_OMAP + range 1 14 + default 2 + help + Increase the DMA consistent memory size according to your video + memory needs, for example if you want to use multiple planes. + The size must be 2MB aligned. + If unsure say 1. + +config FB_OMAP_DMA_TUNE + bool "Set DMA SDRAM access priority high" + depends on FB_OMAP && ARCH_OMAP1 + help + On systems in which video memory is in system memory + (SDRAM) this will speed up graphics DMA operations. + If you have such a system and want to use rotation + answer yes. Answer no if you have a dedicated video + memory, or don't use any of the accelerated features. + +config FB_OMAP_LCDC_EXTERNAL + bool "External LCD controller support" + depends on FB_OMAP + help + Say Y here, if you want to have support for boards with an + external LCD controller connected to the SoSSI/RFBI interface. + +config FB_OMAP_LCDC_HWA742 + bool "Epson HWA742 LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson HWA742 LCD controller. + +config FB_OMAP_LCDC_BLIZZARD + bool "Epson Blizzard LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson Blizzard LCD controller. diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile new file mode 100644 index 000000000000..99da8b6d2c36 --- /dev/null +++ b/drivers/video/omap/Makefile @@ -0,0 +1,29 @@ +# +# Makefile for the new OMAP framebuffer device driver +# + +obj-$(CONFIG_FB_OMAP) += omapfb.o + +objs-yy := omapfb_main.o + +objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o +objs-y$(CONFIG_ARCH_OMAP2) += dispc.o + +objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o +objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o + +objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o +objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o + +objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o +objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o +objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o +objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o +objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o +objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o +objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o +objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o +objs-y$(CONFIG_MACH_SX1) += lcd_sx1.o + +omapfb-objs := $(objs-yy) + diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c new file mode 100644 index 000000000000..e682940a97a4 --- /dev/null +++ b/drivers/video/omap/blizzard.c @@ -0,0 +1,1568 @@ +/* + * Epson Blizzard LCD controller driver + * + * Copyright (C) 2004-2005 Nokia Corporation + * Authors: Juha Yrjola <juha.yrjola@nokia.com> + * Imre Deak <imre.deak@nokia.com> + * YUV support: Jussi Laako <jussi.laako@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/clk.h> + +#include <asm/arch/dma.h> +#include <asm/arch/omapfb.h> +#include <asm/arch/blizzard.h> + +#include "dispc.h" + +#define MODULE_NAME "blizzard" + +#define BLIZZARD_REV_CODE 0x00 +#define BLIZZARD_CONFIG 0x02 +#define BLIZZARD_PLL_DIV 0x04 +#define BLIZZARD_PLL_LOCK_RANGE 0x06 +#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 +#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a +#define BLIZZARD_PLL_MODE 0x0c +#define BLIZZARD_CLK_SRC 0x0e +#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 +#define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_HDISP 0x2a +#define BLIZZARD_HNDP 0x2c +#define BLIZZARD_VDISP0 0x2e +#define BLIZZARD_VDISP1 0x30 +#define BLIZZARD_VNDP 0x32 +#define BLIZZARD_HSW 0x34 +#define BLIZZARD_VSW 0x38 +#define BLIZZARD_DISPLAY_MODE 0x68 +#define BLIZZARD_INPUT_WIN_X_START_0 0x6c +#define BLIZZARD_DATA_SOURCE_SELECT 0x8e +#define BLIZZARD_DISP_MEM_DATA_PORT 0x90 +#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 +#define BLIZZARD_POWER_SAVE 0xE6 +#define BLIZZARD_NDISP_CTRL_STATUS 0xE8 + +/* Data source select */ +/* For S1D13745 */ +#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 +#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 +#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 +#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 +/* For S1D13744 */ +#define BLIZZARD_SRC_WRITE_LCD 0x00 +#define BLIZZARD_SRC_BLT_LCD 0x06 + +#define BLIZZARD_COLOR_RGB565 0x01 +#define BLIZZARD_COLOR_YUV420 0x09 + +#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ +#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ + +#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20) + +/* Reserve 4 request slots for requests in irq context */ +#define REQ_POOL_SIZE 24 +#define IRQ_REQ_POOL_SIZE 4 + +#define REQ_FROM_IRQ_POOL 0x01 + +#define REQ_COMPLETE 0 +#define REQ_PENDING 1 + +struct blizzard_reg_list { + int start; + int end; +}; + +/* These need to be saved / restored separately from the rest. */ +static struct blizzard_reg_list blizzard_pll_regs[] = { + { + .start = 0x04, /* Don't save PLL ctrl (0x0C) */ + .end = 0x0a, + }, + { + .start = 0x0e, /* Clock configuration */ + .end = 0x0e, + }, +}; + +static struct blizzard_reg_list blizzard_gen_regs[] = { + { + .start = 0x18, /* SDRAM control */ + .end = 0x20, + }, + { + .start = 0x28, /* LCD Panel configuration */ + .end = 0x5a, /* HSSI interface, TV configuration */ + }, +}; + +static u8 blizzard_reg_cache[0x5a / 2]; + +struct update_param { + int plane; + int x, y, width, height; + int out_x, out_y; + int out_width, out_height; + int color_mode; + int bpp; + int flags; +}; + +struct blizzard_request { + struct list_head entry; + unsigned int flags; + + int (*handler)(struct blizzard_request *req); + void (*complete)(void *data); + void *complete_data; + + union { + struct update_param update; + struct completion *sync; + } par; +}; + +struct plane_info { + unsigned long offset; + int pos_x, pos_y; + int width, height; + int out_width, out_height; + int scr_width; + int color_mode; + int bpp; +}; + +struct blizzard_struct { + enum omapfb_update_mode update_mode; + enum omapfb_update_mode update_mode_before_suspend; + + struct timer_list auto_update_timer; + int stop_auto_update; + struct omapfb_update_window auto_update_window; + int enabled_planes; + int vid_nonstd_color; + int vid_scaled; + int last_color_mode; + int zoom_on; + int screen_width; + int screen_height; + unsigned te_connected:1; + unsigned vsync_only:1; + + struct plane_info plane[OMAPFB_PLANE_NUM]; + + struct blizzard_request req_pool[REQ_POOL_SIZE]; + struct list_head pending_req_list; + struct list_head free_req_list; + struct semaphore req_sema; + spinlock_t req_lock; + + unsigned long sys_ck_rate; + struct extif_timings reg_timings, lut_timings; + + u32 max_transmit_size; + u32 extif_clk_period; + int extif_clk_div; + unsigned long pix_tx_time; + unsigned long line_upd_time; + + struct omapfb_device *fbdev; + struct lcd_ctrl_extif *extif; + struct lcd_ctrl *int_ctrl; + + void (*power_up)(struct device *dev); + void (*power_down)(struct device *dev); + + int version; +} blizzard; + +struct lcd_ctrl blizzard_ctrl; + +static u8 blizzard_read_reg(u8 reg) +{ + u8 data; + + blizzard.extif->set_bits_per_cycle(8); + blizzard.extif->write_command(®, 1); + blizzard.extif->read_data(&data, 1); + + return data; +} + +static void blizzard_write_reg(u8 reg, u8 val) +{ + blizzard.extif->set_bits_per_cycle(8); + blizzard.extif->write_command(®, 1); + blizzard.extif->write_data(&val, 1); +} + +static void blizzard_restart_sdram(void) +{ + unsigned long tmo; + + blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); + udelay(50); + blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1); + tmo = jiffies + msecs_to_jiffies(200); + while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) { + if (time_after(jiffies, tmo)) { + dev_err(blizzard.fbdev->dev, + "s1d1374x: SDRAM not ready"); + break; + } + msleep(1); + } +} + +static void blizzard_stop_sdram(void) +{ + blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); +} + +/* Wait until the last window was completely written into the controllers + * SDRAM and we can start transferring the next window. + */ +static void blizzard_wait_line_buffer(void) +{ + unsigned long tmo = jiffies + msecs_to_jiffies(30); + + while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) { + if (time_after(jiffies, tmo)) { + if (printk_ratelimit()) + dev_err(blizzard.fbdev->dev, + "s1d1374x: line buffer not ready\n"); + break; + } + } +} + +/* Wait until the YYC color space converter is idle. */ +static void blizzard_wait_yyc(void) +{ + unsigned long tmo = jiffies + msecs_to_jiffies(30); + + while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) { + if (time_after(jiffies, tmo)) { + if (printk_ratelimit()) + dev_err(blizzard.fbdev->dev, + "s1d1374x: YYC not ready\n"); + break; + } + } +} + +static void disable_overlay(void) +{ + blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT, + BLIZZARD_SRC_DISABLE_OVERLAY); +} + +static void set_window_regs(int x_start, int y_start, int x_end, int y_end, + int x_out_start, int y_out_start, + int x_out_end, int y_out_end, int color_mode, + int zoom_off, int flags) +{ + u8 tmp[18]; + u8 cmd; + + x_end--; + y_end--; + tmp[0] = x_start; + tmp[1] = x_start >> 8; + tmp[2] = y_start; + tmp[3] = y_start >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + x_out_end--; + y_out_end--; + tmp[8] = x_out_start; + tmp[9] = x_out_start >> 8; + tmp[10] = y_out_start; + tmp[11] = y_out_start >> 8; + tmp[12] = x_out_end; + tmp[13] = x_out_end >> 8; + tmp[14] = y_out_end; + tmp[15] = y_out_end >> 8; + + tmp[16] = color_mode; + if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745) + tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; + else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY) + tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE; + else + tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ? + BLIZZARD_SRC_WRITE_LCD : + BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; + + blizzard.extif->set_bits_per_cycle(8); + cmd = BLIZZARD_INPUT_WIN_X_START_0; + blizzard.extif->write_command(&cmd, 1); + blizzard.extif->write_data(tmp, 18); +} + +static void enable_tearsync(int y, int width, int height, int screen_height, + int out_height, int force_vsync) +{ + u8 b; + + b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); + b |= 1 << 3; + blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); + + if (likely(blizzard.vsync_only || force_vsync)) { + blizzard.extif->enable_tearsync(1, 0); + return; + } + + if (width * blizzard.pix_tx_time < blizzard.line_upd_time) { + blizzard.extif->enable_tearsync(1, 0); + return; + } + + if ((width * blizzard.pix_tx_time / 1000) * height < + (y + out_height) * (blizzard.line_upd_time / 1000)) { + blizzard.extif->enable_tearsync(1, 0); + return; + } + + blizzard.extif->enable_tearsync(1, y + 1); +} + +static void disable_tearsync(void) +{ + u8 b; + + blizzard.extif->enable_tearsync(0, 0); + b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); + b &= ~(1 << 3); + blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); + b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); +} + +static inline void set_extif_timings(const struct extif_timings *t); + +static inline struct blizzard_request *alloc_req(void) +{ + unsigned long flags; + struct blizzard_request *req; + int req_flags = 0; + + if (!in_interrupt()) + down(&blizzard.req_sema); + else + req_flags = REQ_FROM_IRQ_POOL; + + spin_lock_irqsave(&blizzard.req_lock, flags); + BUG_ON(list_empty(&blizzard.free_req_list)); + req = list_entry(blizzard.free_req_list.next, + struct blizzard_request, entry); + list_del(&req->entry); + spin_unlock_irqrestore(&blizzard.req_lock, flags); + + INIT_LIST_HEAD(&req->entry); + req->flags = req_flags; + + return req; +} + +static inline void free_req(struct blizzard_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&blizzard.req_lock, flags); + + list_del(&req->entry); + list_add(&req->entry, &blizzard.free_req_list); + if (!(req->flags & REQ_FROM_IRQ_POOL)) + up(&blizzard.req_sema); + + spin_unlock_irqrestore(&blizzard.req_lock, flags); +} + +static void process_pending_requests(void) +{ + unsigned long flags; + + spin_lock_irqsave(&blizzard.req_lock, flags); + + while (!list_empty(&blizzard.pending_req_list)) { + struct blizzard_request *req; + void (*complete)(void *); + void *complete_data; + + req = list_entry(blizzard.pending_req_list.next, + struct blizzard_request, entry); + spin_unlock_irqrestore(&blizzard.req_lock, flags); + + if (req->handler(req) == REQ_PENDING) + return; + + complete = req->complete; + complete_data = req->complete_data; + free_req(req); + + if (complete) + complete(complete_data); + + spin_lock_irqsave(&blizzard.req_lock, flags); + } + + spin_unlock_irqrestore(&blizzard.req_lock, flags); +} + +static void submit_req_list(struct list_head *head) +{ + unsigned long flags; + int process = 1; + + spin_lock_irqsave(&blizzard.req_lock, flags); + if (likely(!list_empty(&blizzard.pending_req_list))) + process = 0; + list_splice_init(head, blizzard.pending_req_list.prev); + spin_unlock_irqrestore(&blizzard.req_lock, flags); + + if (process) + process_pending_requests(); +} + +static void request_complete(void *data) +{ + struct blizzard_request *req = (struct blizzard_request *)data; + void (*complete)(void *); + void *complete_data; + + complete = req->complete; + complete_data = req->complete_data; + + free_req(req); + + if (complete) + complete(complete_data); + + process_pending_requests(); +} + + +static int do_full_screen_update(struct blizzard_request *req) +{ + int i; + int flags; + + for (i = 0; i < 3; i++) { + struct plane_info *p = &blizzard.plane[i]; + if (!(blizzard.enabled_planes & (1 << i))) { + blizzard.int_ctrl->enable_plane(i, 0); + continue; + } + dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n", + p->width, p->height); + blizzard.int_ctrl->setup_plane(i, + OMAPFB_CHANNEL_OUT_LCD, p->offset, + p->scr_width, p->pos_x, p->pos_y, + p->width, p->height, + p->color_mode); + blizzard.int_ctrl->enable_plane(i, 1); + } + + dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n", + blizzard.screen_width, blizzard.screen_height); + blizzard_wait_line_buffer(); + flags = req->par.update.flags; + if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) + enable_tearsync(0, blizzard.screen_width, + blizzard.screen_height, + blizzard.screen_height, + blizzard.screen_height, + flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); + else + disable_tearsync(); + + set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height, + 0, 0, blizzard.screen_width, blizzard.screen_height, + BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags); + blizzard.zoom_on = 0; + + blizzard.extif->set_bits_per_cycle(16); + /* set_window_regs has left the register index at the right + * place, so no need to set it here. + */ + blizzard.extif->transfer_area(blizzard.screen_width, + blizzard.screen_height, + request_complete, req); + return REQ_PENDING; +} + +/* Setup all planes with an overlapping area with the update window. */ +static int do_partial_update(struct blizzard_request *req, int plane, + int x, int y, int w, int h, + int x_out, int y_out, int w_out, int h_out, + int wnd_color_mode, int bpp) +{ + int i; + int gx1, gy1, gx2, gy2; + int gx1_out, gy1_out, gx2_out, gy2_out; + int color_mode; + int flags; + int zoom_off; + + /* Global coordinates, relative to pixel 0,0 of the LCD */ + gx1 = x + blizzard.plane[plane].pos_x; + gy1 = y + blizzard.plane[plane].pos_y; + gx2 = gx1 + w; + gy2 = gy1 + h; + + flags = req->par.update.flags; + if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { + gx1_out = gx1; + gy1_out = gy1; + gx2_out = gx1 + w * 2; + gy2_out = gy1 + h * 2; + } else { + gx1_out = x_out + blizzard.plane[plane].pos_x; + gy1_out = y_out + blizzard.plane[plane].pos_y; + gx2_out = gx1_out + w_out; + gy2_out = gy1_out + h_out; + } + zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 && + w == blizzard.screen_width && h == blizzard.screen_height; + blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) || + (w < w_out || h < h_out); + + for (i = 0; i < OMAPFB_PLANE_NUM; i++) { + struct plane_info *p = &blizzard.plane[i]; + int px1, py1; + int px2, py2; + int pw, ph; + int pposx, pposy; + unsigned long offset; + + if (!(blizzard.enabled_planes & (1 << i)) || + (wnd_color_mode && i != plane)) { + blizzard.int_ctrl->enable_plane(i, 0); + continue; + } + /* Plane coordinates */ + if (i == plane) { + /* Plane in which we are doing the update. + * Local coordinates are the one in the update + * request. + */ + px1 = x; + py1 = y; + px2 = x + w; + py2 = y + h; + pposx = 0; + pposy = 0; + } else { + /* Check if this plane has an overlapping part */ + px1 = gx1 - p->pos_x; + py1 = gy1 - p->pos_y; + px2 = gx2 - p->pos_x; + py2 = gy2 - p->pos_y; + if (px1 >= p->width || py1 >= p->height || + px2 <= 0 || py2 <= 0) { + blizzard.int_ctrl->enable_plane(i, 0); + continue; + } + /* Calculate the coordinates for the overlapping + * part in the plane's local coordinates. + */ + pposx = -px1; + pposy = -py1; + if (px1 < 0) + px1 = 0; + if (py1 < 0) + py1 = 0; + if (px2 > p->width) + px2 = p->width; + if (py2 > p->height) + py2 = p->height; + if (pposx < 0) + pposx = 0; + if (pposy < 0) + pposy = 0; + } + pw = px2 - px1; + ph = py2 - py1; + offset = p->offset + (p->scr_width * py1 + px1) * p->bpp / 8; + if (wnd_color_mode) + /* Window embedded in the plane with a differing + * color mode / bpp. Calculate the number of DMA + * transfer elements in terms of the plane's bpp. + */ + pw = (pw + 1) * bpp / p->bpp; +#ifdef VERBOSE + dev_dbg(blizzard.fbdev->dev, + "plane %d offset %#08lx pposx %d pposy %d " + "px1 %d py1 %d pw %d ph %d\n", + i, offset, pposx, pposy, px1, py1, pw, ph); +#endif + blizzard.int_ctrl->setup_plane(i, + OMAPFB_CHANNEL_OUT_LCD, offset, + p->scr_width, + pposx, pposy, pw, ph, + p->color_mode); + + blizzard.int_ctrl->enable_plane(i, 1); + } + + switch (wnd_color_mode) { + case OMAPFB_COLOR_YUV420: + color_mode = BLIZZARD_COLOR_YUV420; + /* Currently only the 16 bits/pixel cycle format is + * supported on the external interface. Adjust the number + * of transfer elements per line for 12bpp format. + */ + w = (w + 1) * 3 / 4; + break; + default: + color_mode = BLIZZARD_COLOR_RGB565; + break; + } + + blizzard_wait_line_buffer(); + if (blizzard.last_color_mode == BLIZZARD_COLOR_YUV420) + blizzard_wait_yyc(); + blizzard.last_color_mode = color_mode; + if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) + enable_tearsync(gy1, w, h, + blizzard.screen_height, + h_out, + flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); + else + disable_tearsync(); + + set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out, + color_mode, zoom_off, flags); + + blizzard.extif->set_bits_per_cycle(16); + /* set_window_regs has left the register index at the right + * place, so no need to set it here. + */ + blizzard.extif->transfer_area(w, h, request_complete, req); + + return REQ_PENDING; +} + +static int send_frame_handler(struct blizzard_request *req) +{ + struct update_param *par = &req->par.update; + int plane = par->plane; + +#ifdef VERBOSE + dev_dbg(blizzard.fbdev->dev, + "send_frame: x %d y %d w %d h %d " + "x_out %d y_out %d w_out %d h_out %d " + "color_mode %04x flags %04x planes %01x\n", + par->x, par->y, par->width, par->height, + par->out_x, par->out_y, par->out_width, par->out_height, + par->color_mode, par->flags, blizzard.enabled_planes); +#endif + if (par->flags & OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY) + disable_overlay(); + + if ((blizzard.enabled_planes & blizzard.vid_nonstd_color) || + (blizzard.enabled_planes & blizzard.vid_scaled)) + return do_full_screen_update(req); + + return do_partial_update(req, plane, par->x, par->y, + par->width, par->height, + par->out_x, par->out_y, + par->out_width, par->out_height, + par->color_mode, par->bpp); +} + +static void send_frame_complete(void *data) +{ +} + +#define ADD_PREQ(_x, _y, _w, _h, _x_out, _y_out, _w_out, _h_out) do { \ + req = alloc_req(); \ + req->handler = send_frame_handler; \ + req->complete = send_frame_complete; \ + req->par.update.plane = plane_idx; \ + req->par.update.x = _x; \ + req->par.update.y = _y; \ + req->par.update.width = _w; \ + req->par.update.height = _h; \ + req->par.update.out_x = _x_out; \ + req->par.update.out_y = _y_out; \ + req->par.update.out_width = _w_out; \ + req->par.update.out_height = _h_out; \ + req->par.update.bpp = bpp; \ + req->par.update.color_mode = color_mode;\ + req->par.update.flags = flags; \ + list_add_tail(&req->entry, req_head); \ +} while(0) + +static void create_req_list(int plane_idx, + struct omapfb_update_window *win, + struct list_head *req_head) +{ + struct blizzard_request *req; + int x = win->x; + int y = win->y; + int width = win->width; + int height = win->height; + int x_out = win->out_x; + int y_out = win->out_y; + int width_out = win->out_width; + int height_out = win->out_height; + int color_mode; + int bpp; + int flags; + unsigned int ystart = y; + unsigned int yspan = height; + unsigned int ystart_out = y_out; + unsigned int yspan_out = height_out; + + flags = win->format & ~OMAPFB_FORMAT_MASK; + color_mode = win->format & OMAPFB_FORMAT_MASK; + switch (color_mode) { + case OMAPFB_COLOR_YUV420: + /* Embedded window with different color mode */ + bpp = 12; + /* X, Y, height must be aligned at 2, width at 4 pixels */ + x &= ~1; + y &= ~1; + height = yspan = height & ~1; + width = width & ~3; + break; + default: + /* Same as the plane color mode */ + bpp = blizzard.plane[plane_idx].bpp; + break; + } + if (width * height * bpp / 8 > blizzard.max_transmit_size) { + yspan = blizzard.max_transmit_size / (width * bpp / 8); + yspan_out = yspan * height_out / height; + ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, + width_out, yspan_out); + ystart += yspan; + ystart_out += yspan_out; + yspan = height - yspan; + yspan_out = height_out - yspan_out; + flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; + } + + ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, + width_out, yspan_out); +} + +static void auto_update_complete(void *data) +{ + if (!blizzard.stop_auto_update) + mod_timer(&blizzard.auto_update_timer, + jiffies + BLIZZARD_AUTO_UPDATE_TIME); +} + +static void blizzard_update_window_auto(unsigned long arg) +{ + LIST_HEAD(req_list); + struct blizzard_request *last; + struct omapfb_plane_struct *plane; + + plane = blizzard.fbdev->fb_info[0]->par; + create_req_list(plane->idx, + &blizzard.auto_update_window, &req_list); + last = list_entry(req_list.prev, struct blizzard_request, entry); + + last->complete = auto_update_complete; + last->complete_data = NULL; + + submit_req_list(&req_list); +} + +int blizzard_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + LIST_HEAD(req_list); + struct blizzard_request *last; + struct omapfb_plane_struct *plane = fbi->par; + + if (unlikely(blizzard.update_mode != OMAPFB_MANUAL_UPDATE)) + return -EINVAL; + if (unlikely(!blizzard.te_connected && + (win->format & OMAPFB_FORMAT_FLAG_TEARSYNC))) + return -EINVAL; + + create_req_list(plane->idx, win, &req_list); + last = list_entry(req_list.prev, struct blizzard_request, entry); + + last->complete = complete_callback; + last->complete_data = (void *)complete_callback_data; + + submit_req_list(&req_list); + + return 0; +} +EXPORT_SYMBOL(blizzard_update_window_async); + +static int update_full_screen(void) +{ + return blizzard_update_window_async(blizzard.fbdev->fb_info[0], + &blizzard.auto_update_window, NULL, NULL); + +} + +static int blizzard_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + struct plane_info *p; + +#ifdef VERBOSE + dev_dbg(blizzard.fbdev->dev, + "plane %d ch_out %d offset %#08lx scr_width %d " + "pos_x %d pos_y %d width %d height %d color_mode %d\n", + plane, channel_out, offset, screen_width, + pos_x, pos_y, width, height, color_mode); +#endif + if ((unsigned)plane > OMAPFB_PLANE_NUM) + return -EINVAL; + p = &blizzard.plane[plane]; + + switch (color_mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + p->bpp = 16; + blizzard.vid_nonstd_color &= ~(1 << plane); + break; + case OMAPFB_COLOR_YUV420: + p->bpp = 12; + blizzard.vid_nonstd_color |= 1 << plane; + break; + case OMAPFB_COLOR_RGB565: + p->bpp = 16; + blizzard.vid_nonstd_color &= ~(1 << plane); + break; + default: + return -EINVAL; + } + + p->offset = offset; + p->pos_x = pos_x; + p->pos_y = pos_y; + p->width = width; + p->height = height; + p->scr_width = screen_width; + if (!p->out_width) + p->out_width = width; + if (!p->out_height) + p->out_height = height; + + p->color_mode = color_mode; + + return 0; +} + +static int blizzard_set_scale(int plane, int orig_w, int orig_h, + int out_w, int out_h) +{ + struct plane_info *p = &blizzard.plane[plane]; + int r; + + dev_dbg(blizzard.fbdev->dev, + "plane %d orig_w %d orig_h %d out_w %d out_h %d\n", + plane, orig_w, orig_h, out_w, out_h); + if ((unsigned)plane > OMAPFB_PLANE_NUM) + return -ENODEV; + + r = blizzard.int_ctrl->set_scale(plane, orig_w, orig_h, out_w, out_h); + if (r < 0) + return r; + + p->width = orig_w; + p->height = orig_h; + p->out_width = out_w; + p->out_height = out_h; + if (orig_w == out_w && orig_h == out_h) + blizzard.vid_scaled &= ~(1 << plane); + else + blizzard.vid_scaled |= 1 << plane; + + return 0; +} + +static int blizzard_enable_plane(int plane, int enable) +{ + if (enable) + blizzard.enabled_planes |= 1 << plane; + else + blizzard.enabled_planes &= ~(1 << plane); + + return 0; +} + +static int sync_handler(struct blizzard_request *req) +{ + complete(req->par.sync); + return REQ_COMPLETE; +} + +static void blizzard_sync(void) +{ + LIST_HEAD(req_list); + struct blizzard_request *req; + struct completion comp; + + req = alloc_req(); + + req->handler = sync_handler; + req->complete = NULL; + init_completion(&comp); + req->par.sync = ∁ + + list_add(&req->entry, &req_list); + submit_req_list(&req_list); + + wait_for_completion(&comp); +} + + +static void blizzard_bind_client(struct omapfb_notifier_block *nb) +{ + if (blizzard.update_mode == OMAPFB_MANUAL_UPDATE) { + omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); + } +} + +static int blizzard_set_update_mode(enum omapfb_update_mode mode) +{ + if (unlikely(mode != OMAPFB_MANUAL_UPDATE && + mode != OMAPFB_AUTO_UPDATE && + mode != OMAPFB_UPDATE_DISABLED)) + return -EINVAL; + + if (mode == blizzard.update_mode) + return 0; + + dev_info(blizzard.fbdev->dev, "s1d1374x: setting update mode to %s\n", + mode == OMAPFB_UPDATE_DISABLED ? "disabled" : + (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); + + switch (blizzard.update_mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_DISABLED); + break; + case OMAPFB_AUTO_UPDATE: + blizzard.stop_auto_update = 1; + del_timer_sync(&blizzard.auto_update_timer); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + blizzard.update_mode = mode; + blizzard_sync(); + blizzard.stop_auto_update = 0; + + switch (mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); + break; + case OMAPFB_AUTO_UPDATE: + blizzard_update_window_auto(0); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + return 0; +} + +static enum omapfb_update_mode blizzard_get_update_mode(void) +{ + return blizzard.update_mode; +} + +static inline void set_extif_timings(const struct extif_timings *t) +{ + blizzard.extif->set_timings(t); +} + +static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = blizzard.extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 12.2 ns (regs), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 12 ns (regs), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 2*SYSCLK (regs), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns */ + + systim = 1000000000 / (sysclk / 1000); + dev_dbg(blizzard.fbdev->dev, + "Blizzard systim %lu ps extif_clk_period %u div %d\n", + systim, blizzard.extif_clk_period, div); + + t = &blizzard.reg_timings; + memset(t, 0, sizeof(*t)); + + t->clk_div = div; + + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 13000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + dev_dbg(blizzard.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + dev_dbg(blizzard.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + dev_dbg(blizzard.fbdev->dev, "[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return blizzard.extif->convert_timings(t); +} + +static int calc_lut_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns */ + + systim = 1000000000 / (sysclk / 1000); + dev_dbg(blizzard.fbdev->dev, + "Blizzard systim %lu ps extif_clk_period %u div %d\n", + systim, blizzard.extif_clk_period, div); + + t = &blizzard.lut_timings; + memset(t, 0, sizeof(*t)); + + t->clk_div = div; + + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + dev_dbg(blizzard.fbdev->dev, + "[lut]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + dev_dbg(blizzard.fbdev->dev, + "[lut]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + dev_dbg(blizzard.fbdev->dev, "[lut]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return blizzard.extif->convert_timings(t); +} + +static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) +{ + int max_clk_div; + int div; + + blizzard.extif->get_clk_info(&blizzard.extif_clk_period, &max_clk_div); + for (div = 1; div <= max_clk_div; div++) { + if (calc_reg_timing(sysclk, div) == 0) + break; + } + if (div > max_clk_div) { + dev_dbg(blizzard.fbdev->dev, "reg timing failed\n"); + goto err; + } + *extif_mem_div = div; + + for (div = 1; div <= max_clk_div; div++) { + if (calc_lut_timing(sysclk, div) == 0) + break; + } + + if (div > max_clk_div) + goto err; + + blizzard.extif_clk_div = div; + + return 0; +err: + dev_err(blizzard.fbdev->dev, "can't setup timings\n"); + return -1; +} + +static void calc_blizzard_clk_rates(unsigned long ext_clk, + unsigned long *sys_clk, unsigned long *pix_clk) +{ + int pix_clk_src; + int sys_div = 0, sys_mul = 0; + int pix_div; + + pix_clk_src = blizzard_read_reg(BLIZZARD_CLK_SRC); + pix_div = ((pix_clk_src >> 3) & 0x1f) + 1; + if ((pix_clk_src & (0x3 << 1)) == 0) { + /* Source is the PLL */ + sys_div = (blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x3f) + 1; + sys_mul = blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_0); + sys_mul |= ((blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_1) + & 0x0f) << 11); + *sys_clk = ext_clk * sys_mul / sys_div; + } else /* else source is ext clk, or oscillator */ + *sys_clk = ext_clk; + + *pix_clk = *sys_clk / pix_div; /* HZ */ + dev_dbg(blizzard.fbdev->dev, + "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n", + ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul); + dev_dbg(blizzard.fbdev->dev, "sys_clk %ld pix_clk %ld\n", + *sys_clk, *pix_clk); +} + +static int setup_tearsync(unsigned long pix_clk, int extif_div) +{ + int hdisp, vdisp; + int hndp, vndp; + int hsw, vsw; + int hs, vs; + int hs_pol_inv, vs_pol_inv; + int use_hsvs, use_ndp; + u8 b; + + hsw = blizzard_read_reg(BLIZZARD_HSW); + vsw = blizzard_read_reg(BLIZZARD_VSW); + hs_pol_inv = !(hsw & 0x80); + vs_pol_inv = !(vsw & 0x80); + hsw = hsw & 0x7f; + vsw = vsw & 0x3f; + + hdisp = blizzard_read_reg(BLIZZARD_HDISP) * 8; + vdisp = blizzard_read_reg(BLIZZARD_VDISP0) + + ((blizzard_read_reg(BLIZZARD_VDISP1) & 0x3) << 8); + + hndp = blizzard_read_reg(BLIZZARD_HNDP) & 0x3f; + vndp = blizzard_read_reg(BLIZZARD_VNDP); + + /* time to transfer one pixel (16bpp) in ps */ + blizzard.pix_tx_time = blizzard.reg_timings.we_cycle_time; + if (blizzard.extif->get_max_tx_rate != NULL) { + /* The external interface might have a rate limitation, + * if so, we have to maximize our transfer rate. + */ + unsigned long min_tx_time; + unsigned long max_tx_rate = blizzard.extif->get_max_tx_rate(); + + dev_dbg(blizzard.fbdev->dev, "max_tx_rate %ld HZ\n", + max_tx_rate); + min_tx_time = 1000000000 / (max_tx_rate / 1000); /* ps */ + if (blizzard.pix_tx_time < min_tx_time) + blizzard.pix_tx_time = min_tx_time; + } + + /* time to update one line in ps */ + blizzard.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000); + blizzard.line_upd_time *= 1000; + if (hdisp * blizzard.pix_tx_time > blizzard.line_upd_time) + /* transfer speed too low, we might have to use both + * HS and VS */ + use_hsvs = 1; + else + /* decent transfer speed, we'll always use only VS */ + use_hsvs = 0; + + if (use_hsvs && (hs_pol_inv || vs_pol_inv)) { + /* HS or'ed with VS doesn't work, use the active high + * TE signal based on HNDP / VNDP */ + use_ndp = 1; + hs_pol_inv = 0; + vs_pol_inv = 0; + hs = hndp; + vs = vndp; + } else { + /* Use HS or'ed with VS as a TE signal if both are needed + * or VNDP if only vsync is needed. */ + use_ndp = 0; + hs = hsw; + vs = vsw; + if (!use_hsvs) { + hs_pol_inv = 0; + vs_pol_inv = 0; + } + } + + hs = hs * 1000000 / (pix_clk / 1000); /* ps */ + hs *= 1000; + + vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */ + vs *= 1000; + + if (vs <= hs) + return -EDOM; + /* set VS to 120% of HS to minimize VS detection time */ + vs = hs * 12 / 10; + /* minimize HS too */ + if (hs > 10000) + hs = 10000; + + b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); + b &= ~0x3; + b |= use_hsvs ? 1 : 0; + b |= (use_ndp && use_hsvs) ? 0 : 2; + blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); + + blizzard.vsync_only = !use_hsvs; + + dev_dbg(blizzard.fbdev->dev, + "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n", + pix_clk, blizzard.pix_tx_time, blizzard.line_upd_time); + dev_dbg(blizzard.fbdev->dev, + "hs %d ps vs %d ps mode %d vsync_only %d\n", + hs, vs, b & 0x3, !use_hsvs); + + return blizzard.extif->setup_tearsync(1, hs, vs, + hs_pol_inv, vs_pol_inv, + extif_div); +} + +static void blizzard_get_caps(int plane, struct omapfb_caps *caps) +{ + blizzard.int_ctrl->get_caps(plane, caps); + caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | + OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | + OMAPFB_CAPS_WINDOW_SCALE | + OMAPFB_CAPS_WINDOW_OVERLAY; + if (blizzard.te_connected) + caps->ctrl |= OMAPFB_CAPS_TEARSYNC; + caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | + (1 << OMAPFB_COLOR_YUV420); +} + +static void _save_regs(struct blizzard_reg_list *list, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++, list++) { + int reg; + for (reg = list->start; reg <= list->end; reg += 2) + blizzard_reg_cache[reg / 2] = blizzard_read_reg(reg); + } +} + +static void _restore_regs(struct blizzard_reg_list *list, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++, list++) { + int reg; + for (reg = list->start; reg <= list->end; reg += 2) + blizzard_write_reg(reg, blizzard_reg_cache[reg / 2]); + } +} + +static void blizzard_save_all_regs(void) +{ + _save_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); + _save_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); +} + +static void blizzard_restore_pll_regs(void) +{ + _restore_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); +} + +static void blizzard_restore_gen_regs(void) +{ + _restore_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); +} + +static void blizzard_suspend(void) +{ + u32 l; + unsigned long tmo; + + if (blizzard.last_color_mode) { + update_full_screen(); + blizzard_sync(); + } + blizzard.update_mode_before_suspend = blizzard.update_mode; + /* the following will disable clocks as well */ + blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); + + blizzard_save_all_regs(); + + blizzard_stop_sdram(); + + l = blizzard_read_reg(BLIZZARD_POWER_SAVE); + /* Standby, Sleep. We assume we use an external clock. */ + l |= 0x03; + blizzard_write_reg(BLIZZARD_POWER_SAVE, l); + + tmo = jiffies + msecs_to_jiffies(100); + while (!(blizzard_read_reg(BLIZZARD_PLL_MODE) & (1 << 1))) { + if (time_after(jiffies, tmo)) { + dev_err(blizzard.fbdev->dev, + "s1d1374x: sleep timeout, stopping PLL manually\n"); + l = blizzard_read_reg(BLIZZARD_PLL_MODE); + l &= ~0x03; + /* Disable PLL, counter function */ + l |= 0x2; + blizzard_write_reg(BLIZZARD_PLL_MODE, l); + break; + } + msleep(1); + } + + if (blizzard.power_down != NULL) + blizzard.power_down(blizzard.fbdev->dev); +} + +static void blizzard_resume(void) +{ + u32 l; + + if (blizzard.power_up != NULL) + blizzard.power_up(blizzard.fbdev->dev); + + l = blizzard_read_reg(BLIZZARD_POWER_SAVE); + /* Standby, Sleep */ + l &= ~0x03; + blizzard_write_reg(BLIZZARD_POWER_SAVE, l); + + blizzard_restore_pll_regs(); + l = blizzard_read_reg(BLIZZARD_PLL_MODE); + l &= ~0x03; + /* Enable PLL, counter function */ + l |= 0x1; + blizzard_write_reg(BLIZZARD_PLL_MODE, l); + + while (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & (1 << 7))) + msleep(1); + + blizzard_restart_sdram(); + + blizzard_restore_gen_regs(); + + /* Enable display */ + blizzard_write_reg(BLIZZARD_DISPLAY_MODE, 0x01); + + /* the following will enable clocks as necessary */ + blizzard_set_update_mode(blizzard.update_mode_before_suspend); + + /* Force a background update */ + blizzard.zoom_on = 1; + update_full_screen(); + blizzard_sync(); +} + +static int blizzard_init(struct omapfb_device *fbdev, int ext_mode, + struct omapfb_mem_desc *req_vram) +{ + int r = 0, i; + u8 rev, conf; + unsigned long ext_clk; + int extif_div; + unsigned long sys_clk, pix_clk; + struct omapfb_platform_data *omapfb_conf; + struct blizzard_platform_data *ctrl_conf; + + blizzard.fbdev = fbdev; + + BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); + + blizzard.fbdev = fbdev; + blizzard.extif = fbdev->ext_if; + blizzard.int_ctrl = fbdev->int_ctrl; + + omapfb_conf = fbdev->dev->platform_data; + ctrl_conf = omapfb_conf->ctrl_platform_data; + if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) { + dev_err(fbdev->dev, "s1d1374x: missing platform data\n"); + r = -ENOENT; + goto err1; + } + + blizzard.power_down = ctrl_conf->power_down; + blizzard.power_up = ctrl_conf->power_up; + + spin_lock_init(&blizzard.req_lock); + + if ((r = blizzard.int_ctrl->init(fbdev, 1, req_vram)) < 0) + goto err1; + + if ((r = blizzard.extif->init(fbdev)) < 0) + goto err2; + + blizzard_ctrl.set_color_key = blizzard.int_ctrl->set_color_key; + blizzard_ctrl.get_color_key = blizzard.int_ctrl->get_color_key; + blizzard_ctrl.setup_mem = blizzard.int_ctrl->setup_mem; + blizzard_ctrl.mmap = blizzard.int_ctrl->mmap; + + ext_clk = ctrl_conf->get_clock_rate(fbdev->dev); + if ((r = calc_extif_timings(ext_clk, &extif_div)) < 0) + goto err3; + + set_extif_timings(&blizzard.reg_timings); + + if (blizzard.power_up != NULL) + blizzard.power_up(fbdev->dev); + + calc_blizzard_clk_rates(ext_clk, &sys_clk, &pix_clk); + + if ((r = calc_extif_timings(sys_clk, &extif_div)) < 0) + goto err3; + set_extif_timings(&blizzard.reg_timings); + + if (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x80)) { + dev_err(fbdev->dev, + "controller not initialized by the bootloader\n"); + r = -ENODEV; + goto err3; + } + + if (ctrl_conf->te_connected) { + if ((r = setup_tearsync(pix_clk, extif_div)) < 0) + goto err3; + blizzard.te_connected = 1; + } + + rev = blizzard_read_reg(BLIZZARD_REV_CODE); + conf = blizzard_read_reg(BLIZZARD_CONFIG); + + switch (rev & 0xfc) { + case 0x9c: + blizzard.version = BLIZZARD_VERSION_S1D13744; + pr_info("omapfb: s1d13744 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + case 0xa4: + blizzard.version = BLIZZARD_VERSION_S1D13745; + pr_info("omapfb: s1d13745 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + default: + dev_err(fbdev->dev, "invalid s1d1374x revision %02x\n", + rev); + r = -ENODEV; + goto err3; + } + + blizzard.max_transmit_size = blizzard.extif->max_transmit_size; + + blizzard.update_mode = OMAPFB_UPDATE_DISABLED; + + blizzard.auto_update_window.x = 0; + blizzard.auto_update_window.y = 0; + blizzard.auto_update_window.width = fbdev->panel->x_res; + blizzard.auto_update_window.height = fbdev->panel->y_res; + blizzard.auto_update_window.out_x = 0; + blizzard.auto_update_window.out_x = 0; + blizzard.auto_update_window.out_width = fbdev->panel->x_res; + blizzard.auto_update_window.out_height = fbdev->panel->y_res; + blizzard.auto_update_window.format = 0; + + blizzard.screen_width = fbdev->panel->x_res; + blizzard.screen_height = fbdev->panel->y_res; + + init_timer(&blizzard.auto_update_timer); + blizzard.auto_update_timer.function = blizzard_update_window_auto; + blizzard.auto_update_timer.data = 0; + + INIT_LIST_HEAD(&blizzard.free_req_list); + INIT_LIST_HEAD(&blizzard.pending_req_list); + for (i = 0; i < ARRAY_SIZE(blizzard.req_pool); i++) + list_add(&blizzard.req_pool[i].entry, &blizzard.free_req_list); + BUG_ON(i <= IRQ_REQ_POOL_SIZE); + sema_init(&blizzard.req_sema, i - IRQ_REQ_POOL_SIZE); + + return 0; +err3: + if (blizzard.power_down != NULL) + blizzard.power_down(fbdev->dev); + blizzard.extif->cleanup(); +err2: + blizzard.int_ctrl->cleanup(); +err1: + return r; +} + +static void blizzard_cleanup(void) +{ + blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); + blizzard.extif->cleanup(); + blizzard.int_ctrl->cleanup(); + if (blizzard.power_down != NULL) + blizzard.power_down(blizzard.fbdev->dev); +} + +struct lcd_ctrl blizzard_ctrl = { + .name = "blizzard", + .init = blizzard_init, + .cleanup = blizzard_cleanup, + .bind_client = blizzard_bind_client, + .get_caps = blizzard_get_caps, + .set_update_mode = blizzard_set_update_mode, + .get_update_mode = blizzard_get_update_mode, + .setup_plane = blizzard_setup_plane, + .set_scale = blizzard_set_scale, + .enable_plane = blizzard_enable_plane, + .update_window = blizzard_update_window_async, + .sync = blizzard_sync, + .suspend = blizzard_suspend, + .resume = blizzard_resume, +}; + diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c new file mode 100644 index 000000000000..f4c23434de6f --- /dev/null +++ b/drivers/video/omap/dispc.c @@ -0,0 +1,1502 @@ +/* + * OMAP2 display controller support + * + * Copyright (C) 2005 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/arch/sram.h> +#include <asm/arch/omapfb.h> +#include <asm/arch/board.h> + +#include "dispc.h" + +#define MODULE_NAME "dispc" + +#define DSS_BASE 0x48050000 +#define DSS_SYSCONFIG 0x0010 + +#define DISPC_BASE 0x48050400 + +/* DISPC common */ +#define DISPC_REVISION 0x0000 +#define DISPC_SYSCONFIG 0x0010 +#define DISPC_SYSSTATUS 0x0014 +#define DISPC_IRQSTATUS 0x0018 +#define DISPC_IRQENABLE 0x001C +#define DISPC_CONTROL 0x0040 +#define DISPC_CONFIG 0x0044 +#define DISPC_CAPABLE 0x0048 +#define DISPC_DEFAULT_COLOR0 0x004C +#define DISPC_DEFAULT_COLOR1 0x0050 +#define DISPC_TRANS_COLOR0 0x0054 +#define DISPC_TRANS_COLOR1 0x0058 +#define DISPC_LINE_STATUS 0x005C +#define DISPC_LINE_NUMBER 0x0060 +#define DISPC_TIMING_H 0x0064 +#define DISPC_TIMING_V 0x0068 +#define DISPC_POL_FREQ 0x006C +#define DISPC_DIVISOR 0x0070 +#define DISPC_SIZE_DIG 0x0078 +#define DISPC_SIZE_LCD 0x007C + +#define DISPC_DATA_CYCLE1 0x01D4 +#define DISPC_DATA_CYCLE2 0x01D8 +#define DISPC_DATA_CYCLE3 0x01DC + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 0x0080 +#define DISPC_GFX_BA1 0x0084 +#define DISPC_GFX_POSITION 0x0088 +#define DISPC_GFX_SIZE 0x008C +#define DISPC_GFX_ATTRIBUTES 0x00A0 +#define DISPC_GFX_FIFO_THRESHOLD 0x00A4 +#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8 +#define DISPC_GFX_ROW_INC 0x00AC +#define DISPC_GFX_PIXEL_INC 0x00B0 +#define DISPC_GFX_WINDOW_SKIP 0x00B4 +#define DISPC_GFX_TABLE_BA 0x00B8 + +/* DISPC Video plane 1/2 */ +#define DISPC_VID1_BASE 0x00BC +#define DISPC_VID2_BASE 0x014C + +/* Offsets into DISPC_VID1/2_BASE */ +#define DISPC_VID_BA0 0x0000 +#define DISPC_VID_BA1 0x0004 +#define DISPC_VID_POSITION 0x0008 +#define DISPC_VID_SIZE 0x000C +#define DISPC_VID_ATTRIBUTES 0x0010 +#define DISPC_VID_FIFO_THRESHOLD 0x0014 +#define DISPC_VID_FIFO_SIZE_STATUS 0x0018 +#define DISPC_VID_ROW_INC 0x001C +#define DISPC_VID_PIXEL_INC 0x0020 +#define DISPC_VID_FIR 0x0024 +#define DISPC_VID_PICTURE_SIZE 0x0028 +#define DISPC_VID_ACCU0 0x002C +#define DISPC_VID_ACCU1 0x0030 + +/* 8 elements in 8 byte increments */ +#define DISPC_VID_FIR_COEF_H0 0x0034 +/* 8 elements in 8 byte increments */ +#define DISPC_VID_FIR_COEF_HV0 0x0038 +/* 5 elements in 4 byte increments */ +#define DISPC_VID_CONV_COEF0 0x0074 + +#define DISPC_IRQ_FRAMEMASK 0x0001 +#define DISPC_IRQ_VSYNC 0x0002 +#define DISPC_IRQ_EVSYNC_EVEN 0x0004 +#define DISPC_IRQ_EVSYNC_ODD 0x0008 +#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010 +#define DISPC_IRQ_PROG_LINE_NUM 0x0020 +#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040 +#define DISPC_IRQ_GFX_END_WIN 0x0080 +#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100 +#define DISPC_IRQ_OCP_ERR 0x0200 +#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400 +#define DISPC_IRQ_VID1_END_WIN 0x0800 +#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000 +#define DISPC_IRQ_VID2_END_WIN 0x2000 +#define DISPC_IRQ_SYNC_LOST 0x4000 + +#define DISPC_IRQ_MASK_ALL 0x7fff + +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST) + +#define RFBI_CONTROL 0x48050040 + +#define MAX_PALETTE_SIZE (256 * 16) + +#define FLD_MASK(pos, len) (((1 << len) - 1) << pos) + +#define MOD_REG_FLD(reg, mask, val) \ + dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val)); + +#define OMAP2_SRAM_START 0x40200000 +/* Maximum size, in reality this is smaller if SRAM is partially locked. */ +#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ + +/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */ +#define DISPC_MEMTYPE_NUM 2 + +#define RESMAP_SIZE(_page_cnt) \ + ((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8) +#define RESMAP_PTR(_res_map, _page_nr) \ + (((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8)) +#define RESMAP_MASK(_page_nr) \ + (1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1))) + +struct resmap { + unsigned long start; + unsigned page_cnt; + unsigned long *map; +}; + +static struct { + u32 base; + + struct omapfb_mem_desc mem_desc; + struct resmap *res_map[DISPC_MEMTYPE_NUM]; + atomic_t map_count[OMAPFB_PLANE_NUM]; + + dma_addr_t palette_paddr; + void *palette_vaddr; + + int ext_mode; + + unsigned long enabled_irqs; + void (*irq_callback)(void *); + void *irq_callback_data; + struct completion frame_done; + + int fir_hinc[OMAPFB_PLANE_NUM]; + int fir_vinc[OMAPFB_PLANE_NUM]; + + struct clk *dss_ick, *dss1_fck; + struct clk *dss_54m_fck; + + enum omapfb_update_mode update_mode; + struct omapfb_device *fbdev; + + struct omapfb_color_key color_key; +} dispc; + +static void enable_lcd_clocks(int enable); + +static void inline dispc_write_reg(int idx, u32 val) +{ + __raw_writel(val, dispc.base + idx); +} + +static u32 inline dispc_read_reg(int idx) +{ + u32 l = __raw_readl(dispc.base + idx); + return l; +} + +/* Select RFBI or bypass mode */ +static void enable_rfbi_mode(int enable) +{ + u32 l; + + l = dispc_read_reg(DISPC_CONTROL); + /* Enable RFBI, GPIO0/1 */ + l &= ~((1 << 11) | (1 << 15) | (1 << 16)); + l |= enable ? (1 << 11) : 0; + /* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */ + l |= 1 << 15; + l |= enable ? 0 : (1 << 16); + dispc_write_reg(DISPC_CONTROL, l); + + /* Set bypass mode in RFBI module */ + l = __raw_readl(io_p2v(RFBI_CONTROL)); + l |= enable ? 0 : (1 << 1); + __raw_writel(l, io_p2v(RFBI_CONTROL)); +} + +static void set_lcd_data_lines(int data_lines) +{ + u32 l; + int code = 0; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + } + + l = dispc_read_reg(DISPC_CONTROL); + l &= ~(0x03 << 8); + l |= code << 8; + dispc_write_reg(DISPC_CONTROL, l); +} + +static void set_load_mode(int mode) +{ + BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY | + DISPC_LOAD_CLUT_ONCE_FRAME)); + MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1); +} + +void omap_dispc_set_lcd_size(int x, int y) +{ + BUG_ON((x > (1 << 11)) || (y > (1 << 11))); + enable_lcd_clocks(1); + MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((y - 1) << 16) | (x - 1)); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_set_lcd_size); + +void omap_dispc_set_digit_size(int x, int y) +{ + BUG_ON((x > (1 << 11)) || (y > (1 << 11))); + enable_lcd_clocks(1); + MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((y - 1) << 16) | (x - 1)); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_set_digit_size); + +static void setup_plane_fifo(int plane, int ext_mode) +{ + const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD, + DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD }; + const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS, + DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS }; + int low, high; + u32 l; + + BUG_ON(plane > 2); + + l = dispc_read_reg(fsz_reg[plane]); + l &= FLD_MASK(0, 9); + if (ext_mode) { + low = l * 3 / 4; + high = l; + } else { + low = l / 4; + high = l * 3 / 4; + } + MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9), + (high << 16) | low); +} + +void omap_dispc_enable_lcd_out(int enable) +{ + enable_lcd_clocks(1); + MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_enable_lcd_out); + +void omap_dispc_enable_digit_out(int enable) +{ + enable_lcd_clocks(1); + MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_enable_digit_out); + +static inline int _setup_plane(int plane, int channel_out, + u32 paddr, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, + DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; + const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0, + DISPC_VID2_BASE + DISPC_VID_BA0 }; + const u32 ps_reg[] = { DISPC_GFX_POSITION, + DISPC_VID1_BASE + DISPC_VID_POSITION, + DISPC_VID2_BASE + DISPC_VID_POSITION }; + const u32 sz_reg[] = { DISPC_GFX_SIZE, + DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE, + DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE }; + const u32 ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID1_BASE + DISPC_VID_ROW_INC, + DISPC_VID2_BASE + DISPC_VID_ROW_INC }; + const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, + DISPC_VID2_BASE + DISPC_VID_SIZE }; + + int chout_shift, burst_shift; + int chout_val; + int color_code; + int bpp; + int cconv_en; + int set_vsize; + u32 l; + +#ifdef VERBOSE + dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d" + " pos_x %d pos_y %d width %d height %d color_mode %d\n", + plane, channel_out, paddr, screen_width, pos_x, pos_y, + width, height, color_mode); +#endif + + set_vsize = 0; + switch (plane) { + case OMAPFB_PLANE_GFX: + burst_shift = 6; + chout_shift = 8; + break; + case OMAPFB_PLANE_VID1: + case OMAPFB_PLANE_VID2: + burst_shift = 14; + chout_shift = 16; + set_vsize = 1; + break; + default: + return -EINVAL; + } + + switch (channel_out) { + case OMAPFB_CHANNEL_OUT_LCD: + chout_val = 0; + break; + case OMAPFB_CHANNEL_OUT_DIGIT: + chout_val = 1; + break; + default: + return -EINVAL; + } + + cconv_en = 0; + switch (color_mode) { + case OMAPFB_COLOR_RGB565: + color_code = DISPC_RGB_16_BPP; + bpp = 16; + break; + case OMAPFB_COLOR_YUV422: + if (plane == 0) + return -EINVAL; + color_code = DISPC_UYVY_422; + cconv_en = 1; + bpp = 16; + break; + case OMAPFB_COLOR_YUY422: + if (plane == 0) + return -EINVAL; + color_code = DISPC_YUV2_422; + cconv_en = 1; + bpp = 16; + break; + default: + return -EINVAL; + } + + l = dispc_read_reg(at_reg[plane]); + + l &= ~(0x0f << 1); + l |= color_code << 1; + l &= ~(1 << 9); + l |= cconv_en << 9; + + l &= ~(0x03 << burst_shift); + l |= DISPC_BURST_8x32 << burst_shift; + + l &= ~(1 << chout_shift); + l |= chout_val << chout_shift; + + dispc_write_reg(at_reg[plane], l); + + dispc_write_reg(ba_reg[plane], paddr); + MOD_REG_FLD(ps_reg[plane], + FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x); + + MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((height - 1) << 16) | (width - 1)); + + if (set_vsize) { + /* Set video size if set_scale hasn't set it */ + if (!dispc.fir_vinc[plane]) + MOD_REG_FLD(vs_reg[plane], + FLD_MASK(16, 11), (height - 1) << 16); + if (!dispc.fir_hinc[plane]) + MOD_REG_FLD(vs_reg[plane], + FLD_MASK(0, 11), width - 1); + } + + dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1); + + return height * screen_width * bpp / 8; +} + +static int omap_dispc_setup_plane(int plane, int channel_out, + unsigned long offset, + int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + u32 paddr; + int r; + + if ((unsigned)plane > dispc.mem_desc.region_cnt) + return -EINVAL; + paddr = dispc.mem_desc.region[plane].paddr + offset; + enable_lcd_clocks(1); + r = _setup_plane(plane, channel_out, paddr, + screen_width, + pos_x, pos_y, width, height, color_mode); + enable_lcd_clocks(0); + return r; +} + +static void write_firh_reg(int plane, int reg, u32 value) +{ + u32 base; + + if (plane == 1) + base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0; + else + base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0; + dispc_write_reg(base + reg * 8, value); +} + +static void write_firhv_reg(int plane, int reg, u32 value) +{ + u32 base; + + if (plane == 1) + base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0; + else + base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0; + dispc_write_reg(base + reg * 8, value); +} + +static void set_upsampling_coef_table(int plane) +{ + const u32 coef[][2] = { + { 0x00800000, 0x00800000 }, + { 0x0D7CF800, 0x037B02FF }, + { 0x1E70F5FF, 0x0C6F05FE }, + { 0x335FF5FE, 0x205907FB }, + { 0xF74949F7, 0x00404000 }, + { 0xF55F33FB, 0x075920FE }, + { 0xF5701EFE, 0x056F0CFF }, + { 0xF87C0DFF, 0x027B0300 }, + }; + int i; + + for (i = 0; i < 8; i++) { + write_firh_reg(plane, i, coef[i][0]); + write_firhv_reg(plane, i, coef[i][1]); + } +} + +static int omap_dispc_set_scale(int plane, + int orig_width, int orig_height, + int out_width, int out_height) +{ + const u32 at_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, + DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; + const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, + DISPC_VID2_BASE + DISPC_VID_SIZE }; + const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR, + DISPC_VID2_BASE + DISPC_VID_FIR }; + + u32 l; + int fir_hinc; + int fir_vinc; + + if ((unsigned)plane > OMAPFB_PLANE_NUM) + return -ENODEV; + + if (plane == OMAPFB_PLANE_GFX && + (out_width != orig_width || out_height != orig_height)) + return -EINVAL; + + enable_lcd_clocks(1); + if (orig_width < out_width) { + /* + * Upsampling. + * Currently you can only scale both dimensions in one way. + */ + if (orig_height > out_height || + orig_width * 8 < out_width || + orig_height * 8 < out_height) { + enable_lcd_clocks(0); + return -EINVAL; + } + set_upsampling_coef_table(plane); + } else if (orig_width > out_width) { + /* Downsampling not yet supported + */ + + enable_lcd_clocks(0); + return -EINVAL; + } + if (!orig_width || orig_width == out_width) + fir_hinc = 0; + else + fir_hinc = 1024 * orig_width / out_width; + if (!orig_height || orig_height == out_height) + fir_vinc = 0; + else + fir_vinc = 1024 * orig_height / out_height; + dispc.fir_hinc[plane] = fir_hinc; + dispc.fir_vinc[plane] = fir_vinc; + + MOD_REG_FLD(fir_reg[plane], + FLD_MASK(16, 12) | FLD_MASK(0, 12), + ((fir_vinc & 4095) << 16) | + (fir_hinc & 4095)); + + dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d " + "orig_height %d fir_hinc %d fir_vinc %d\n", + out_width, out_height, orig_width, orig_height, + fir_hinc, fir_vinc); + + MOD_REG_FLD(vs_reg[plane], + FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((out_height - 1) << 16) | (out_width - 1)); + + l = dispc_read_reg(at_reg[plane]); + l &= ~(0x03 << 5); + l |= fir_hinc ? (1 << 5) : 0; + l |= fir_vinc ? (1 << 6) : 0; + dispc_write_reg(at_reg[plane], l); + + enable_lcd_clocks(0); + return 0; +} + +static int omap_dispc_enable_plane(int plane, int enable) +{ + const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, + DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; + if ((unsigned int)plane > dispc.mem_desc.region_cnt) + return -EINVAL; + + enable_lcd_clocks(1); + MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); + enable_lcd_clocks(0); + + return 0; +} + +static int omap_dispc_set_color_key(struct omapfb_color_key *ck) +{ + u32 df_reg, tr_reg; + int shift, val; + + switch (ck->channel_out) { + case OMAPFB_CHANNEL_OUT_LCD: + df_reg = DISPC_DEFAULT_COLOR0; + tr_reg = DISPC_TRANS_COLOR0; + shift = 10; + break; + case OMAPFB_CHANNEL_OUT_DIGIT: + df_reg = DISPC_DEFAULT_COLOR1; + tr_reg = DISPC_TRANS_COLOR1; + shift = 12; + break; + default: + return -EINVAL; + } + switch (ck->key_type) { + case OMAPFB_COLOR_KEY_DISABLED: + val = 0; + break; + case OMAPFB_COLOR_KEY_GFX_DST: + val = 1; + break; + case OMAPFB_COLOR_KEY_VID_SRC: + val = 3; + break; + default: + return -EINVAL; + } + enable_lcd_clocks(1); + MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift); + + if (val != 0) + dispc_write_reg(tr_reg, ck->trans_key); + dispc_write_reg(df_reg, ck->background); + enable_lcd_clocks(0); + + dispc.color_key = *ck; + + return 0; +} + +static int omap_dispc_get_color_key(struct omapfb_color_key *ck) +{ + *ck = dispc.color_key; + return 0; +} + +static void load_palette(void) +{ +} + +static int omap_dispc_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + if (mode != dispc.update_mode) { + switch (mode) { + case OMAPFB_AUTO_UPDATE: + case OMAPFB_MANUAL_UPDATE: + enable_lcd_clocks(1); + omap_dispc_enable_lcd_out(1); + dispc.update_mode = mode; + break; + case OMAPFB_UPDATE_DISABLED: + init_completion(&dispc.frame_done); + omap_dispc_enable_lcd_out(0); + if (!wait_for_completion_timeout(&dispc.frame_done, + msecs_to_jiffies(500))) { + dev_err(dispc.fbdev->dev, + "timeout waiting for FRAME DONE\n"); + } + dispc.update_mode = mode; + enable_lcd_clocks(0); + break; + default: + r = -EINVAL; + } + } + + return r; +} + +static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps) +{ + caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM; + if (plane > 0) + caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE; + caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) | + (1 << OMAPFB_COLOR_YUV422) | + (1 << OMAPFB_COLOR_YUY422); + if (plane == 0) + caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) | + (1 << OMAPFB_COLOR_CLUT_4BPP) | + (1 << OMAPFB_COLOR_CLUT_2BPP) | + (1 << OMAPFB_COLOR_CLUT_1BPP) | + (1 << OMAPFB_COLOR_RGB444); +} + +static enum omapfb_update_mode omap_dispc_get_update_mode(void) +{ + return dispc.update_mode; +} + +static void setup_color_conv_coef(void) +{ + u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11); + int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0; + int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0; + int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES; + int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES; + const struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; + } ctbl_bt601_5 = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + const struct color_conv_coef *ct; +#define CVAL(x, y) (((x & 2047) << 16) | (y & 2047)) + + ct = &ctbl_bt601_5; + + MOD_REG_FLD(cf1_reg, mask, CVAL(ct->rcr, ct->ry)); + MOD_REG_FLD(cf1_reg + 4, mask, CVAL(ct->gy, ct->rcb)); + MOD_REG_FLD(cf1_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); + MOD_REG_FLD(cf1_reg + 12, mask, CVAL(ct->bcr, ct->by)); + MOD_REG_FLD(cf1_reg + 16, mask, CVAL(0, ct->bcb)); + + MOD_REG_FLD(cf2_reg, mask, CVAL(ct->rcr, ct->ry)); + MOD_REG_FLD(cf2_reg + 4, mask, CVAL(ct->gy, ct->rcb)); + MOD_REG_FLD(cf2_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); + MOD_REG_FLD(cf2_reg + 12, mask, CVAL(ct->bcr, ct->by)); + MOD_REG_FLD(cf2_reg + 16, mask, CVAL(0, ct->bcb)); +#undef CVAL + + MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range); + MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range); +} + +static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) +{ + unsigned long fck, lck; + + *lck_div = 1; + pck = max(1, pck); + fck = clk_get_rate(dispc.dss1_fck); + lck = fck; + *pck_div = (lck + pck - 1) / pck; + if (is_tft) + *pck_div = max(2, *pck_div); + else + *pck_div = max(3, *pck_div); + if (*pck_div > 255) { + *pck_div = 255; + lck = pck * *pck_div; + *lck_div = fck / lck; + BUG_ON(*lck_div < 1); + if (*lck_div > 255) { + *lck_div = 255; + dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n", + pck / 1000); + } + } +} + +static void set_lcd_tft_mode(int enable) +{ + u32 mask; + + mask = 1 << 3; + MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0); +} + +static void set_lcd_timings(void) +{ + u32 l; + int lck_div, pck_div; + struct lcd_panel *panel = dispc.fbdev->panel; + int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; + unsigned long fck; + + l = dispc_read_reg(DISPC_TIMING_H); + l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); + l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0; + l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8; + l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20; + dispc_write_reg(DISPC_TIMING_H, l); + + l = dispc_read_reg(DISPC_TIMING_V); + l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); + l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0; + l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8; + l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20; + dispc_write_reg(DISPC_TIMING_V, l); + + l = dispc_read_reg(DISPC_POL_FREQ); + l &= ~FLD_MASK(12, 6); + l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12; + l |= panel->acb & 0xff; + dispc_write_reg(DISPC_POL_FREQ, l); + + calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div); + + l = dispc_read_reg(DISPC_DIVISOR); + l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8)); + l |= (lck_div << 16) | (pck_div << 0); + dispc_write_reg(DISPC_DIVISOR, l); + + /* update panel info with the exact clock */ + fck = clk_get_rate(dispc.dss1_fck); + panel->pixel_clock = fck / lck_div / pck_div / 1000; +} + +int omap_dispc_request_irq(void (*callback)(void *data), void *data) +{ + int r = 0; + + BUG_ON(callback == NULL); + + if (dispc.irq_callback) + r = -EBUSY; + else { + dispc.irq_callback = callback; + dispc.irq_callback_data = data; + } + + return r; +} +EXPORT_SYMBOL(omap_dispc_request_irq); + +void omap_dispc_enable_irqs(int irq_mask) +{ + enable_lcd_clocks(1); + dispc.enabled_irqs = irq_mask; + irq_mask |= DISPC_IRQ_MASK_ERROR; + MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_enable_irqs); + +void omap_dispc_disable_irqs(int irq_mask) +{ + enable_lcd_clocks(1); + dispc.enabled_irqs &= ~irq_mask; + irq_mask &= ~DISPC_IRQ_MASK_ERROR; + MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_disable_irqs); + +void omap_dispc_free_irq(void) +{ + enable_lcd_clocks(1); + omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL); + dispc.irq_callback = NULL; + dispc.irq_callback_data = NULL; + enable_lcd_clocks(0); +} +EXPORT_SYMBOL(omap_dispc_free_irq); + +static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) +{ + u32 stat = dispc_read_reg(DISPC_IRQSTATUS); + + if (stat & DISPC_IRQ_FRAMEMASK) + complete(&dispc.frame_done); + + if (stat & DISPC_IRQ_MASK_ERROR) { + if (printk_ratelimit()) { + dev_err(dispc.fbdev->dev, "irq error status %04x\n", + stat & 0x7fff); + } + } + + if ((stat & dispc.enabled_irqs) && dispc.irq_callback) + dispc.irq_callback(dispc.irq_callback_data); + + dispc_write_reg(DISPC_IRQSTATUS, stat); + + return IRQ_HANDLED; +} + +static int get_dss_clocks(void) +{ + if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, "dss_ick")))) { + dev_err(dispc.fbdev->dev, "can't get dss_ick"); + return PTR_ERR(dispc.dss_ick); + } + + if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck")))) { + dev_err(dispc.fbdev->dev, "can't get dss1_fck"); + clk_put(dispc.dss_ick); + return PTR_ERR(dispc.dss1_fck); + } + + if (IS_ERR((dispc.dss_54m_fck = + clk_get(dispc.fbdev->dev, "dss_54m_fck")))) { + dev_err(dispc.fbdev->dev, "can't get dss_54m_fck"); + clk_put(dispc.dss_ick); + clk_put(dispc.dss1_fck); + return PTR_ERR(dispc.dss_54m_fck); + } + + return 0; +} + +static void put_dss_clocks(void) +{ + clk_put(dispc.dss_54m_fck); + clk_put(dispc.dss1_fck); + clk_put(dispc.dss_ick); +} + +static void enable_lcd_clocks(int enable) +{ + if (enable) + clk_enable(dispc.dss1_fck); + else + clk_disable(dispc.dss1_fck); +} + +static void enable_interface_clocks(int enable) +{ + if (enable) + clk_enable(dispc.dss_ick); + else + clk_disable(dispc.dss_ick); +} + +static void enable_digit_clocks(int enable) +{ + if (enable) + clk_enable(dispc.dss_54m_fck); + else + clk_disable(dispc.dss_54m_fck); +} + +static void omap_dispc_suspend(void) +{ + if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { + init_completion(&dispc.frame_done); + omap_dispc_enable_lcd_out(0); + if (!wait_for_completion_timeout(&dispc.frame_done, + msecs_to_jiffies(500))) { + dev_err(dispc.fbdev->dev, + "timeout waiting for FRAME DONE\n"); + } + enable_lcd_clocks(0); + } +} + +static void omap_dispc_resume(void) +{ + if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { + enable_lcd_clocks(1); + if (!dispc.ext_mode) { + set_lcd_timings(); + load_palette(); + } + omap_dispc_enable_lcd_out(1); + } +} + + +static int omap_dispc_update_window(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0; +} + +static int mmap_kern(struct omapfb_mem_region *region) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + kvma = get_vm_area(region->size, VM_IOREMAP); + if (kvma == NULL) { + dev_err(dispc.fbdev->dev, "can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + vaddr = (unsigned long)kvma->addr; + + pgprot = pgprot_writecombine(pgprot_kernel); + vma.vm_start = vaddr; + vma.vm_end = vaddr + region->size; + if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT, + region->size, pgprot) < 0) { + dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n"); + return -EAGAIN; + } + region->vaddr = (void *)vaddr; + + return 0; +} + +static void mmap_user_open(struct vm_area_struct *vma) +{ + int plane = (int)vma->vm_private_data; + + atomic_inc(&dispc.map_count[plane]); +} + +static void mmap_user_close(struct vm_area_struct *vma) +{ + int plane = (int)vma->vm_private_data; + + atomic_dec(&dispc.map_count[plane]); +} + +static struct vm_operations_struct mmap_user_ops = { + .open = mmap_user_open, + .close = mmap_user_close, +}; + +static int omap_dispc_mmap_user(struct fb_info *info, + struct vm_area_struct *vma) +{ + struct omapfb_plane_struct *plane = info->par; + unsigned long off; + unsigned long start; + u32 len; + + if (vma->vm_end - vma->vm_start == 0) + return 0; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + off = vma->vm_pgoff << PAGE_SHIFT; + + start = info->fix.smem_start; + len = info->fix.smem_len; + if (off >= len) + return -EINVAL; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mmap_user_ops; + vma->vm_private_data = (void *)plane->idx; + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + /* vm_ops.open won't be called for mmap itself. */ + atomic_inc(&dispc.map_count[plane->idx]); + return 0; +} + +static void unmap_kern(struct omapfb_mem_region *region) +{ + vunmap(region->vaddr); +} + +static int alloc_palette_ram(void) +{ + dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL); + if (dispc.palette_vaddr == NULL) { + dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE, + dispc.palette_vaddr, dispc.palette_paddr); +} + +static int alloc_fbmem(struct omapfb_mem_region *region) +{ + region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + region->size, ®ion->paddr, GFP_KERNEL); + + if (region->vaddr == NULL) { + dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void free_fbmem(struct omapfb_mem_region *region) +{ + dma_free_writecombine(dispc.fbdev->dev, region->size, + region->vaddr, region->paddr); +} + +static struct resmap *init_resmap(unsigned long start, size_t size) +{ + unsigned page_cnt; + struct resmap *res_map; + + page_cnt = PAGE_ALIGN(size) / PAGE_SIZE; + res_map = + kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL); + if (res_map == NULL) + return NULL; + res_map->start = start; + res_map->page_cnt = page_cnt; + res_map->map = (unsigned long *)(res_map + 1); + return res_map; +} + +static void cleanup_resmap(struct resmap *res_map) +{ + kfree(res_map); +} + +static inline int resmap_mem_type(unsigned long start) +{ + if (start >= OMAP2_SRAM_START && + start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) + return OMAPFB_MEMTYPE_SRAM; + else + return OMAPFB_MEMTYPE_SDRAM; +} + +static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr) +{ + return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0; +} + +static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr) +{ + BUG_ON(resmap_page_reserved(res_map, page_nr)); + *RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr); +} + +static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr) +{ + BUG_ON(!resmap_page_reserved(res_map, page_nr)); + *RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr); +} + +static void resmap_reserve_region(unsigned long start, size_t size) +{ + + struct resmap *res_map; + unsigned start_page; + unsigned end_page; + int mtype; + unsigned i; + + mtype = resmap_mem_type(start); + res_map = dispc.res_map[mtype]; + dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n", + mtype, start, size); + start_page = (start - res_map->start) / PAGE_SIZE; + end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; + for (i = start_page; i < end_page; i++) + resmap_reserve_page(res_map, i); +} + +static void resmap_free_region(unsigned long start, size_t size) +{ + struct resmap *res_map; + unsigned start_page; + unsigned end_page; + unsigned i; + int mtype; + + mtype = resmap_mem_type(start); + res_map = dispc.res_map[mtype]; + dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n", + mtype, start, size); + start_page = (start - res_map->start) / PAGE_SIZE; + end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; + for (i = start_page; i < end_page; i++) + resmap_free_page(res_map, i); +} + +static unsigned long resmap_alloc_region(int mtype, size_t size) +{ + unsigned i; + unsigned total; + unsigned start_page; + unsigned long start; + struct resmap *res_map = dispc.res_map[mtype]; + + BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size); + + size = PAGE_ALIGN(size) / PAGE_SIZE; + start_page = 0; + total = 0; + for (i = 0; i < res_map->page_cnt; i++) { + if (resmap_page_reserved(res_map, i)) { + start_page = i + 1; + total = 0; + } else if (++total == size) + break; + } + if (total < size) + return 0; + + start = res_map->start + start_page * PAGE_SIZE; + resmap_reserve_region(start, size * PAGE_SIZE); + + return start; +} + +/* Note that this will only work for user mappings, we don't deal with + * kernel mappings here, so fbcon will keep using the old region. + */ +static int omap_dispc_setup_mem(int plane, size_t size, int mem_type, + unsigned long *paddr) +{ + struct omapfb_mem_region *rg; + unsigned long new_addr = 0; + + if ((unsigned)plane > dispc.mem_desc.region_cnt) + return -EINVAL; + if (mem_type >= DISPC_MEMTYPE_NUM) + return -EINVAL; + if (dispc.res_map[mem_type] == NULL) + return -ENOMEM; + rg = &dispc.mem_desc.region[plane]; + if (size == rg->size && mem_type == rg->type) + return 0; + if (atomic_read(&dispc.map_count[plane])) + return -EBUSY; + if (rg->size != 0) + resmap_free_region(rg->paddr, rg->size); + if (size != 0) { + new_addr = resmap_alloc_region(mem_type, size); + if (!new_addr) { + /* Reallocate old region. */ + resmap_reserve_region(rg->paddr, rg->size); + return -ENOMEM; + } + } + rg->paddr = new_addr; + rg->size = size; + rg->type = mem_type; + + *paddr = new_addr; + + return 0; +} + +static int setup_fbmem(struct omapfb_mem_desc *req_md) +{ + struct omapfb_mem_region *rg; + int i; + int r; + unsigned long mem_start[DISPC_MEMTYPE_NUM]; + unsigned long mem_end[DISPC_MEMTYPE_NUM]; + + if (!req_md->region_cnt) { + dev_err(dispc.fbdev->dev, "no memory regions defined\n"); + return -ENOENT; + } + + rg = &req_md->region[0]; + memset(mem_start, 0xff, sizeof(mem_start)); + memset(mem_end, 0, sizeof(mem_end)); + + for (i = 0; i < req_md->region_cnt; i++, rg++) { + int mtype; + if (rg->paddr) { + rg->alloc = 0; + if (rg->vaddr == NULL) { + rg->map = 1; + if ((r = mmap_kern(rg)) < 0) + return r; + } + } else { + if (rg->type != OMAPFB_MEMTYPE_SDRAM) { + dev_err(dispc.fbdev->dev, + "unsupported memory type\n"); + return -EINVAL; + } + rg->alloc = rg->map = 1; + if ((r = alloc_fbmem(rg)) < 0) + return r; + } + mtype = rg->type; + + if (rg->paddr < mem_start[mtype]) + mem_start[mtype] = rg->paddr; + if (rg->paddr + rg->size > mem_end[mtype]) + mem_end[mtype] = rg->paddr + rg->size; + } + + for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { + unsigned long start; + size_t size; + if (mem_end[i] == 0) + continue; + start = mem_start[i]; + size = mem_end[i] - start; + dispc.res_map[i] = init_resmap(start, size); + r = -ENOMEM; + if (dispc.res_map[i] == NULL) + goto fail; + /* Initial state is that everything is reserved. This + * includes possible holes as well, which will never be + * freed. + */ + resmap_reserve_region(start, size); + } + + dispc.mem_desc = *req_md; + + return 0; +fail: + for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { + if (dispc.res_map[i] != NULL) + cleanup_resmap(dispc.res_map[i]); + } + return r; +} + +static void cleanup_fbmem(void) +{ + struct omapfb_mem_region *rg; + int i; + + for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { + if (dispc.res_map[i] != NULL) + cleanup_resmap(dispc.res_map[i]); + } + rg = &dispc.mem_desc.region[0]; + for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) { + if (rg->alloc) + free_fbmem(rg); + else { + if (rg->map) + unmap_kern(rg); + } + } +} + +static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, + struct omapfb_mem_desc *req_vram) +{ + int r; + u32 l; + struct lcd_panel *panel = fbdev->panel; + int tmo = 10000; + int skip_init = 0; + int i; + + memset(&dispc, 0, sizeof(dispc)); + + dispc.base = io_p2v(DISPC_BASE); + dispc.fbdev = fbdev; + dispc.ext_mode = ext_mode; + + init_completion(&dispc.frame_done); + + if ((r = get_dss_clocks()) < 0) + return r; + + enable_interface_clocks(1); + enable_lcd_clocks(1); + +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + l = dispc_read_reg(DISPC_CONTROL); + /* LCD enabled ? */ + if (l & 1) { + pr_info("omapfb: skipping hardware initialization\n"); + skip_init = 1; + } +#endif + + if (!skip_init) { + /* Reset monitoring works only w/ the 54M clk */ + enable_digit_clocks(1); + + /* Soft reset */ + MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); + + while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { + if (!--tmo) { + dev_err(dispc.fbdev->dev, "soft reset failed\n"); + r = -ENODEV; + enable_digit_clocks(0); + goto fail1; + } + } + + enable_digit_clocks(0); + } + + /* Enable smart idle and autoidle */ + l = dispc_read_reg(DISPC_CONTROL); + l &= ~((3 << 12) | (3 << 3)); + l |= (2 << 12) | (2 << 3) | (1 << 0); + dispc_write_reg(DISPC_SYSCONFIG, l); + omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG); + + /* Set functional clock autogating */ + l = dispc_read_reg(DISPC_CONFIG); + l |= 1 << 9; + dispc_write_reg(DISPC_CONFIG, l); + + l = dispc_read_reg(DISPC_IRQSTATUS); + dispc_write_reg(l, DISPC_IRQSTATUS); + + /* Enable those that we handle always */ + omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK); + + if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, + 0, MODULE_NAME, fbdev)) < 0) { + dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n"); + goto fail1; + } + + /* L3 firewall setting: enable access to OCM RAM */ + __raw_writel(0x402000b0, io_p2v(0x680050a0)); + + if ((r = alloc_palette_ram()) < 0) + goto fail2; + + if ((r = setup_fbmem(req_vram)) < 0) + goto fail3; + + if (!skip_init) { + for (i = 0; i < dispc.mem_desc.region_cnt; i++) { + memset(dispc.mem_desc.region[i].vaddr, 0, + dispc.mem_desc.region[i].size); + } + + /* Set logic clock to fck, pixel clock to fck/2 for now */ + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16); + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0); + + setup_plane_fifo(0, ext_mode); + setup_plane_fifo(1, ext_mode); + setup_plane_fifo(2, ext_mode); + + setup_color_conv_coef(); + + set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT); + set_load_mode(DISPC_LOAD_FRAME_ONLY); + + if (!ext_mode) { + set_lcd_data_lines(panel->data_lines); + omap_dispc_set_lcd_size(panel->x_res, panel->y_res); + set_lcd_timings(); + } else + set_lcd_data_lines(panel->bpp); + enable_rfbi_mode(ext_mode); + } + + l = dispc_read_reg(DISPC_REVISION); + pr_info("omapfb: DISPC version %d.%d initialized\n", + l >> 4 & 0x0f, l & 0x0f); + enable_lcd_clocks(0); + + return 0; +fail3: + free_palette_ram(); +fail2: + free_irq(INT_24XX_DSS_IRQ, fbdev); +fail1: + enable_lcd_clocks(0); + enable_interface_clocks(0); + put_dss_clocks(); + + return r; +} + +static void omap_dispc_cleanup(void) +{ + int i; + + omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED); + /* This will also disable clocks that are on */ + for (i = 0; i < dispc.mem_desc.region_cnt; i++) + omap_dispc_enable_plane(i, 0); + cleanup_fbmem(); + free_palette_ram(); + free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); + enable_interface_clocks(0); + put_dss_clocks(); +} + +const struct lcd_ctrl omap2_int_ctrl = { + .name = "internal", + .init = omap_dispc_init, + .cleanup = omap_dispc_cleanup, + .get_caps = omap_dispc_get_caps, + .set_update_mode = omap_dispc_set_update_mode, + .get_update_mode = omap_dispc_get_update_mode, + .update_window = omap_dispc_update_window, + .suspend = omap_dispc_suspend, + .resume = omap_dispc_resume, + .setup_plane = omap_dispc_setup_plane, + .setup_mem = omap_dispc_setup_mem, + .set_scale = omap_dispc_set_scale, + .enable_plane = omap_dispc_enable_plane, + .set_color_key = omap_dispc_set_color_key, + .get_color_key = omap_dispc_get_color_key, + .mmap = omap_dispc_mmap_user, +}; diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h new file mode 100644 index 000000000000..eb1512b56ce8 --- /dev/null +++ b/drivers/video/omap/dispc.h @@ -0,0 +1,43 @@ +#ifndef _DISPC_H +#define _DISPC_H + +#include <linux/interrupt.h> + +#define DISPC_PLANE_GFX 0 +#define DISPC_PLANE_VID1 1 +#define DISPC_PLANE_VID2 2 + +#define DISPC_RGB_1_BPP 0x00 +#define DISPC_RGB_2_BPP 0x01 +#define DISPC_RGB_4_BPP 0x02 +#define DISPC_RGB_8_BPP 0x03 +#define DISPC_RGB_12_BPP 0x04 +#define DISPC_RGB_16_BPP 0x06 +#define DISPC_RGB_24_BPP 0x08 +#define DISPC_RGB_24_BPP_UNPACK_32 0x09 +#define DISPC_YUV2_422 0x0a +#define DISPC_UYVY_422 0x0b + +#define DISPC_BURST_4x32 0 +#define DISPC_BURST_8x32 1 +#define DISPC_BURST_16x32 2 + +#define DISPC_LOAD_CLUT_AND_FRAME 0x00 +#define DISPC_LOAD_CLUT_ONLY 0x01 +#define DISPC_LOAD_FRAME_ONLY 0x02 +#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03 + +#define DISPC_TFT_DATA_LINES_12 0 +#define DISPC_TFT_DATA_LINES_16 1 +#define DISPC_TFT_DATA_LINES_18 2 +#define DISPC_TFT_DATA_LINES_24 3 + +extern void omap_dispc_set_lcd_size(int width, int height); + +extern void omap_dispc_enable_lcd_out(int enable); +extern void omap_dispc_enable_digit_out(int enable); + +extern int omap_dispc_request_irq(void (*callback)(void *data), void *data); +extern void omap_dispc_free_irq(void); + +#endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c new file mode 100644 index 000000000000..dc48e02f215c --- /dev/null +++ b/drivers/video/omap/hwa742.c @@ -0,0 +1,1077 @@ +/* + * Epson HWA742 LCD controller driver + * + * Copyright (C) 2004-2005 Nokia Corporation + * Authors: Juha Yrjölä <juha.yrjola@nokia.com> + * Imre Deak <imre.deak@nokia.com> + * YUV support: Jussi Laako <jussi.laako@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/clk.h> + +#include <asm/arch/dma.h> +#include <asm/arch/omapfb.h> +#include <asm/arch/hwa742.h> + +#define HWA742_REV_CODE_REG 0x0 +#define HWA742_CONFIG_REG 0x2 +#define HWA742_PLL_DIV_REG 0x4 +#define HWA742_PLL_0_REG 0x6 +#define HWA742_PLL_1_REG 0x8 +#define HWA742_PLL_2_REG 0xa +#define HWA742_PLL_3_REG 0xc +#define HWA742_PLL_4_REG 0xe +#define HWA742_CLK_SRC_REG 0x12 +#define HWA742_PANEL_TYPE_REG 0x14 +#define HWA742_H_DISP_REG 0x16 +#define HWA742_H_NDP_REG 0x18 +#define HWA742_V_DISP_1_REG 0x1a +#define HWA742_V_DISP_2_REG 0x1c +#define HWA742_V_NDP_REG 0x1e +#define HWA742_HS_W_REG 0x20 +#define HWA742_HP_S_REG 0x22 +#define HWA742_VS_W_REG 0x24 +#define HWA742_VP_S_REG 0x26 +#define HWA742_PCLK_POL_REG 0x28 +#define HWA742_INPUT_MODE_REG 0x2a +#define HWA742_TRANSL_MODE_REG1 0x2e +#define HWA742_DISP_MODE_REG 0x34 +#define HWA742_WINDOW_TYPE 0x36 +#define HWA742_WINDOW_X_START_0 0x38 +#define HWA742_WINDOW_X_START_1 0x3a +#define HWA742_WINDOW_Y_START_0 0x3c +#define HWA742_WINDOW_Y_START_1 0x3e +#define HWA742_WINDOW_X_END_0 0x40 +#define HWA742_WINDOW_X_END_1 0x42 +#define HWA742_WINDOW_Y_END_0 0x44 +#define HWA742_WINDOW_Y_END_1 0x46 +#define HWA742_MEMORY_WRITE_LSB 0x48 +#define HWA742_MEMORY_WRITE_MSB 0x49 +#define HWA742_MEMORY_READ_0 0x4a +#define HWA742_MEMORY_READ_1 0x4c +#define HWA742_MEMORY_READ_2 0x4e +#define HWA742_POWER_SAVE 0x56 +#define HWA742_NDP_CTRL 0x58 + +#define HWA742_AUTO_UPDATE_TIME (HZ / 20) + +/* Reserve 4 request slots for requests in irq context */ +#define REQ_POOL_SIZE 24 +#define IRQ_REQ_POOL_SIZE 4 + +#define REQ_FROM_IRQ_POOL 0x01 + +#define REQ_COMPLETE 0 +#define REQ_PENDING 1 + +struct update_param { + int x, y, width, height; + int color_mode; + int flags; +}; + +struct hwa742_request { + struct list_head entry; + unsigned int flags; + + int (*handler)(struct hwa742_request *req); + void (*complete)(void *data); + void *complete_data; + + union { + struct update_param update; + struct completion *sync; + } par; +}; + +struct { + enum omapfb_update_mode update_mode; + enum omapfb_update_mode update_mode_before_suspend; + + struct timer_list auto_update_timer; + int stop_auto_update; + struct omapfb_update_window auto_update_window; + unsigned te_connected:1; + unsigned vsync_only:1; + + struct hwa742_request req_pool[REQ_POOL_SIZE]; + struct list_head pending_req_list; + struct list_head free_req_list; + struct semaphore req_sema; + spinlock_t req_lock; + + struct extif_timings reg_timings, lut_timings; + + int prev_color_mode; + int prev_flags; + int window_type; + + u32 max_transmit_size; + u32 extif_clk_period; + unsigned long pix_tx_time; + unsigned long line_upd_time; + + + struct omapfb_device *fbdev; + struct lcd_ctrl_extif *extif; + struct lcd_ctrl *int_ctrl; + + void (*power_up)(struct device *dev); + void (*power_down)(struct device *dev); +} hwa742; + +struct lcd_ctrl hwa742_ctrl; + +static u8 hwa742_read_reg(u8 reg) +{ + u8 data; + + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->read_data(&data, 1); + + return data; +} + +static void hwa742_write_reg(u8 reg, u8 data) +{ + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->write_data(&data, 1); +} + +static void set_window_regs(int x_start, int y_start, int x_end, int y_end) +{ + u8 tmp[8]; + u8 cmd; + + x_end--; + y_end--; + tmp[0] = x_start; + tmp[1] = x_start >> 8; + tmp[2] = y_start; + tmp[3] = y_start >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + hwa742.extif->set_bits_per_cycle(8); + cmd = HWA742_WINDOW_X_START_0; + + hwa742.extif->write_command(&cmd, 1); + + hwa742.extif->write_data(tmp, 8); +} + +static void set_format_regs(int conv, int transl, int flags) +{ + if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { + hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01); +#ifdef VERBOSE + dev_dbg(hwa742.fbdev->dev, "hwa742: enabled pixel doubling\n"); +#endif + } else { + hwa742.window_type = (hwa742.window_type & 0xfc); +#ifdef VERBOSE + dev_dbg(hwa742.fbdev->dev, "hwa742: disabled pixel doubling\n"); +#endif + } + + hwa742_write_reg(HWA742_INPUT_MODE_REG, conv); + hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl); + hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type); +} + +static void enable_tearsync(int y, int width, int height, int screen_height, + int force_vsync) +{ + u8 b; + + b = hwa742_read_reg(HWA742_NDP_CTRL); + b |= 1 << 2; + hwa742_write_reg(HWA742_NDP_CTRL, b); + + if (likely(hwa742.vsync_only || force_vsync)) { + hwa742.extif->enable_tearsync(1, 0); + return; + } + + if (width * hwa742.pix_tx_time < hwa742.line_upd_time) { + hwa742.extif->enable_tearsync(1, 0); + return; + } + + if ((width * hwa742.pix_tx_time / 1000) * height < + (y + height) * (hwa742.line_upd_time / 1000)) { + hwa742.extif->enable_tearsync(1, 0); + return; + } + + hwa742.extif->enable_tearsync(1, y + 1); +} + +static void disable_tearsync(void) +{ + u8 b; + + hwa742.extif->enable_tearsync(0, 0); + + b = hwa742_read_reg(HWA742_NDP_CTRL); + b &= ~(1 << 2); + hwa742_write_reg(HWA742_NDP_CTRL, b); +} + +static inline struct hwa742_request *alloc_req(void) +{ + unsigned long flags; + struct hwa742_request *req; + int req_flags = 0; + + if (!in_interrupt()) + down(&hwa742.req_sema); + else + req_flags = REQ_FROM_IRQ_POOL; + + spin_lock_irqsave(&hwa742.req_lock, flags); + BUG_ON(list_empty(&hwa742.free_req_list)); + req = list_entry(hwa742.free_req_list.next, + struct hwa742_request, entry); + list_del(&req->entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + INIT_LIST_HEAD(&req->entry); + req->flags = req_flags; + + return req; +} + +static inline void free_req(struct hwa742_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&hwa742.req_lock, flags); + + list_del(&req->entry); + list_add(&req->entry, &hwa742.free_req_list); + if (!(req->flags & REQ_FROM_IRQ_POOL)) + up(&hwa742.req_sema); + + spin_unlock_irqrestore(&hwa742.req_lock, flags); +} + +static void process_pending_requests(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hwa742.req_lock, flags); + + while (!list_empty(&hwa742.pending_req_list)) { + struct hwa742_request *req; + void (*complete)(void *); + void *complete_data; + + req = list_entry(hwa742.pending_req_list.next, + struct hwa742_request, entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (req->handler(req) == REQ_PENDING) + return; + + complete = req->complete; + complete_data = req->complete_data; + free_req(req); + + if (complete) + complete(complete_data); + + spin_lock_irqsave(&hwa742.req_lock, flags); + } + + spin_unlock_irqrestore(&hwa742.req_lock, flags); +} + +static void submit_req_list(struct list_head *head) +{ + unsigned long flags; + int process = 1; + + spin_lock_irqsave(&hwa742.req_lock, flags); + if (likely(!list_empty(&hwa742.pending_req_list))) + process = 0; + list_splice_init(head, hwa742.pending_req_list.prev); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (process) + process_pending_requests(); +} + +static void request_complete(void *data) +{ + struct hwa742_request *req = (struct hwa742_request *)data; + void (*complete)(void *); + void *complete_data; + + complete = req->complete; + complete_data = req->complete_data; + + free_req(req); + + if (complete) + complete(complete_data); + + process_pending_requests(); +} + +static int send_frame_handler(struct hwa742_request *req) +{ + struct update_param *par = &req->par.update; + int x = par->x; + int y = par->y; + int w = par->width; + int h = par->height; + int bpp; + int conv, transl; + unsigned long offset; + int color_mode = par->color_mode; + int flags = par->flags; + int scr_width = hwa742.fbdev->panel->x_res; + int scr_height = hwa742.fbdev->panel->y_res; + +#ifdef VERBOSE + dev_dbg(hwa742.fbdev->dev, "x %d y %d w %d h %d scr_width %d " + "color_mode %d flags %d\n", + x, y, w, h, scr_width, color_mode, flags); +#endif + + switch (color_mode) { + case OMAPFB_COLOR_YUV422: + bpp = 16; + conv = 0x08; + transl = 0x25; + break; + case OMAPFB_COLOR_YUV420: + bpp = 12; + conv = 0x09; + transl = 0x25; + break; + case OMAPFB_COLOR_RGB565: + bpp = 16; + conv = 0x01; + transl = 0x05; + break; + default: + return -EINVAL; + } + + if (hwa742.prev_flags != flags || + hwa742.prev_color_mode != color_mode) { + set_format_regs(conv, transl, flags); + hwa742.prev_color_mode = color_mode; + hwa742.prev_flags = flags; + } + flags = req->par.update.flags; + if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) + enable_tearsync(y, scr_width, h, scr_height, + flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); + else + disable_tearsync(); + + set_window_regs(x, y, x + w, y + h); + + offset = (scr_width * y + x) * bpp / 8; + + hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX, + OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h, + color_mode); + + hwa742.extif->set_bits_per_cycle(16); + + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1); + hwa742.extif->transfer_area(w, h, request_complete, req); + + return REQ_PENDING; +} + +static void send_frame_complete(void *data) +{ + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0); +} + +#define ADD_PREQ(_x, _y, _w, _h) do { \ + req = alloc_req(); \ + req->handler = send_frame_handler; \ + req->complete = send_frame_complete; \ + req->par.update.x = _x; \ + req->par.update.y = _y; \ + req->par.update.width = _w; \ + req->par.update.height = _h; \ + req->par.update.color_mode = color_mode;\ + req->par.update.flags = flags; \ + list_add_tail(&req->entry, req_head); \ +} while(0) + +static void create_req_list(struct omapfb_update_window *win, + struct list_head *req_head) +{ + struct hwa742_request *req; + int x = win->x; + int y = win->y; + int width = win->width; + int height = win->height; + int color_mode; + int flags; + + flags = win->format & ~OMAPFB_FORMAT_MASK; + color_mode = win->format & OMAPFB_FORMAT_MASK; + + if (x & 1) { + ADD_PREQ(x, y, 1, height); + width--; + x++; + flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; + } + if (width & ~1) { + unsigned int xspan = width & ~1; + unsigned int ystart = y; + unsigned int yspan = height; + + if (xspan * height * 2 > hwa742.max_transmit_size) { + yspan = hwa742.max_transmit_size / (xspan * 2); + ADD_PREQ(x, ystart, xspan, yspan); + ystart += yspan; + yspan = height - yspan; + flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; + } + + ADD_PREQ(x, ystart, xspan, yspan); + x += xspan; + width -= xspan; + flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; + } + if (width) + ADD_PREQ(x, y, 1, height); +} + +static void auto_update_complete(void *data) +{ + if (!hwa742.stop_auto_update) + mod_timer(&hwa742.auto_update_timer, + jiffies + HWA742_AUTO_UPDATE_TIME); +} + +static void hwa742_update_window_auto(unsigned long arg) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + + create_req_list(&hwa742.auto_update_window, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = auto_update_complete; + last->complete_data = NULL; + + submit_req_list(&req_list); +} + +int hwa742_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + int r = 0; + + if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) { + dev_dbg(hwa742.fbdev->dev, "invalid update mode\n"); + r = -EINVAL; + goto out; + } + if (unlikely(win->format & + ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE | + OMAPFB_FORMAT_FLAG_TEARSYNC | OMAPFB_FORMAT_FLAG_FORCE_VSYNC))) { + dev_dbg(hwa742.fbdev->dev, "invalid window flag"); + r = -EINVAL; + goto out; + } + + create_req_list(win, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = complete_callback; + last->complete_data = (void *)complete_callback_data; + + submit_req_list(&req_list); + +out: + return r; +} +EXPORT_SYMBOL(hwa742_update_window_async); + +static int hwa742_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + if (plane != OMAPFB_PLANE_GFX || + channel_out != OMAPFB_CHANNEL_OUT_LCD) + return -EINVAL; + + return 0; +} + +static int hwa742_enable_plane(int plane, int enable) +{ + if (plane != 0) + return -EINVAL; + + hwa742.int_ctrl->enable_plane(plane, enable); + + return 0; +} + +static int sync_handler(struct hwa742_request *req) +{ + complete(req->par.sync); + return REQ_COMPLETE; +} + +static void hwa742_sync(void) +{ + LIST_HEAD(req_list); + struct hwa742_request *req; + struct completion comp; + + req = alloc_req(); + + req->handler = sync_handler; + req->complete = NULL; + init_completion(&comp); + req->par.sync = ∁ + + list_add(&req->entry, &req_list); + submit_req_list(&req_list); + + wait_for_completion(&comp); +} + +static void hwa742_bind_client(struct omapfb_notifier_block *nb) +{ + dev_dbg(hwa742.fbdev->dev, "update_mode %d\n", hwa742.update_mode); + if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) { + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY); + } +} + +static int hwa742_set_update_mode(enum omapfb_update_mode mode) +{ + if (mode != OMAPFB_MANUAL_UPDATE && mode != OMAPFB_AUTO_UPDATE && + mode != OMAPFB_UPDATE_DISABLED) + return -EINVAL; + + if (mode == hwa742.update_mode) + return 0; + + dev_info(hwa742.fbdev->dev, "HWA742: setting update mode to %s\n", + mode == OMAPFB_UPDATE_DISABLED ? "disabled" : + (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); + + switch (hwa742.update_mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_DISABLED); + break; + case OMAPFB_AUTO_UPDATE: + hwa742.stop_auto_update = 1; + del_timer_sync(&hwa742.auto_update_timer); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + hwa742.update_mode = mode; + hwa742_sync(); + hwa742.stop_auto_update = 0; + + switch (mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY); + break; + case OMAPFB_AUTO_UPDATE: + hwa742_update_window_auto(0); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + return 0; +} + +static enum omapfb_update_mode hwa742_get_update_mode(void) +{ + return hwa742.update_mode; +} + +static unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = hwa742.extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 12.2 ns (regs), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 16 ns (regs), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 2*SYSCLK (regs), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns */ + systim = 1000000000 / (sysclk / 1000); + dev_dbg(hwa742.fbdev->dev, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.reg_timings; + memset(t, 0, sizeof(*t)); + t->clk_div = div; + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 16000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + dev_dbg(hwa742.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + dev_dbg(hwa742.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + dev_dbg(hwa742.fbdev->dev, "[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_lut_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns + */ + systim = 1000000000 / (sysclk / 1000); + dev_dbg(hwa742.fbdev->dev, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.lut_timings; + memset(t, 0, sizeof(*t)); + + t->clk_div = div; + + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + dev_dbg(hwa742.fbdev->dev, "[lut]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + dev_dbg(hwa742.fbdev->dev, "[lut]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + dev_dbg(hwa742.fbdev->dev, "[lut]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) +{ + int max_clk_div; + int div; + + hwa742.extif->get_clk_info(&hwa742.extif_clk_period, &max_clk_div); + for (div = 1; div < max_clk_div; div++) { + if (calc_reg_timing(sysclk, div) == 0) + break; + } + if (div > max_clk_div) + goto err; + + *extif_mem_div = div; + + for (div = 1; div < max_clk_div; div++) { + if (calc_lut_timing(sysclk, div) == 0) + break; + } + + if (div > max_clk_div) + goto err; + + return 0; + +err: + dev_err(hwa742.fbdev->dev, "can't setup timings\n"); + return -1; +} + +static void calc_hwa742_clk_rates(unsigned long ext_clk, + unsigned long *sys_clk, unsigned long *pix_clk) +{ + int pix_clk_src; + int sys_div = 0, sys_mul = 0; + int pix_div; + + pix_clk_src = hwa742_read_reg(HWA742_CLK_SRC_REG); + pix_div = ((pix_clk_src >> 3) & 0x1f) + 1; + if ((pix_clk_src & (0x3 << 1)) == 0) { + /* Source is the PLL */ + sys_div = (hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x3f) + 1; + sys_mul = (hwa742_read_reg(HWA742_PLL_4_REG) & 0x7f) + 1; + *sys_clk = ext_clk * sys_mul / sys_div; + } else /* else source is ext clk, or oscillator */ + *sys_clk = ext_clk; + + *pix_clk = *sys_clk / pix_div; /* HZ */ + dev_dbg(hwa742.fbdev->dev, + "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n", + ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul); + dev_dbg(hwa742.fbdev->dev, "sys_clk %ld pix_clk %ld\n", + *sys_clk, *pix_clk); +} + + +static int setup_tearsync(unsigned long pix_clk, int extif_div) +{ + int hdisp, vdisp; + int hndp, vndp; + int hsw, vsw; + int hs, vs; + int hs_pol_inv, vs_pol_inv; + int use_hsvs, use_ndp; + u8 b; + + hsw = hwa742_read_reg(HWA742_HS_W_REG); + vsw = hwa742_read_reg(HWA742_VS_W_REG); + hs_pol_inv = !(hsw & 0x80); + vs_pol_inv = !(vsw & 0x80); + hsw = hsw & 0x7f; + vsw = vsw & 0x3f; + + hdisp = (hwa742_read_reg(HWA742_H_DISP_REG) & 0x7f) * 8; + vdisp = hwa742_read_reg(HWA742_V_DISP_1_REG) + + ((hwa742_read_reg(HWA742_V_DISP_2_REG) & 0x3) << 8); + + hndp = hwa742_read_reg(HWA742_H_NDP_REG) & 0x7f; + vndp = hwa742_read_reg(HWA742_V_NDP_REG); + + /* time to transfer one pixel (16bpp) in ps */ + hwa742.pix_tx_time = hwa742.reg_timings.we_cycle_time; + if (hwa742.extif->get_max_tx_rate != NULL) { + /* + * The external interface might have a rate limitation, + * if so, we have to maximize our transfer rate. + */ + unsigned long min_tx_time; + unsigned long max_tx_rate = hwa742.extif->get_max_tx_rate(); + + dev_dbg(hwa742.fbdev->dev, "max_tx_rate %ld HZ\n", + max_tx_rate); + min_tx_time = 1000000000 / (max_tx_rate / 1000); /* ps */ + if (hwa742.pix_tx_time < min_tx_time) + hwa742.pix_tx_time = min_tx_time; + } + + /* time to update one line in ps */ + hwa742.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000); + hwa742.line_upd_time *= 1000; + if (hdisp * hwa742.pix_tx_time > hwa742.line_upd_time) + /* + * transfer speed too low, we might have to use both + * HS and VS + */ + use_hsvs = 1; + else + /* decent transfer speed, we'll always use only VS */ + use_hsvs = 0; + + if (use_hsvs && (hs_pol_inv || vs_pol_inv)) { + /* + * HS or'ed with VS doesn't work, use the active high + * TE signal based on HNDP / VNDP + */ + use_ndp = 1; + hs_pol_inv = 0; + vs_pol_inv = 0; + hs = hndp; + vs = vndp; + } else { + /* + * Use HS or'ed with VS as a TE signal if both are needed + * or VNDP if only vsync is needed. + */ + use_ndp = 0; + hs = hsw; + vs = vsw; + if (!use_hsvs) { + hs_pol_inv = 0; + vs_pol_inv = 0; + } + } + + hs = hs * 1000000 / (pix_clk / 1000); /* ps */ + hs *= 1000; + + vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */ + vs *= 1000; + + if (vs <= hs) + return -EDOM; + /* set VS to 120% of HS to minimize VS detection time */ + vs = hs * 12 / 10; + /* minimize HS too */ + hs = 10000; + + b = hwa742_read_reg(HWA742_NDP_CTRL); + b &= ~0x3; + b |= use_hsvs ? 1 : 0; + b |= (use_ndp && use_hsvs) ? 0 : 2; + hwa742_write_reg(HWA742_NDP_CTRL, b); + + hwa742.vsync_only = !use_hsvs; + + dev_dbg(hwa742.fbdev->dev, + "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n", + pix_clk, hwa742.pix_tx_time, hwa742.line_upd_time); + dev_dbg(hwa742.fbdev->dev, + "hs %d ps vs %d ps mode %d vsync_only %d\n", + hs, vs, (b & 0x3), !use_hsvs); + + return hwa742.extif->setup_tearsync(1, hs, vs, + hs_pol_inv, vs_pol_inv, extif_div); +} + +static void hwa742_get_caps(int plane, struct omapfb_caps *caps) +{ + hwa742.int_ctrl->get_caps(plane, caps); + caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | + OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE; + if (hwa742.te_connected) + caps->ctrl |= OMAPFB_CAPS_TEARSYNC; + caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | + (1 << OMAPFB_COLOR_YUV420); +} + +static void hwa742_suspend(void) +{ + hwa742.update_mode_before_suspend = hwa742.update_mode; + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + /* Enable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1); + if (hwa742.power_down != NULL) + hwa742.power_down(hwa742.fbdev->dev); +} + +static void hwa742_resume(void) +{ + if (hwa742.power_up != NULL) + hwa742.power_up(hwa742.fbdev->dev); + /* Disable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 0); + while (1) { + /* Loop until PLL output is stabilized */ + if (hwa742_read_reg(HWA742_PLL_DIV_REG) & (1 << 7)) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(5)); + } + hwa742_set_update_mode(hwa742.update_mode_before_suspend); +} + +static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, + struct omapfb_mem_desc *req_vram) +{ + int r = 0, i; + u8 rev, conf; + unsigned long ext_clk; + unsigned long sys_clk, pix_clk; + int extif_mem_div; + struct omapfb_platform_data *omapfb_conf; + struct hwa742_platform_data *ctrl_conf; + + BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); + + hwa742.fbdev = fbdev; + hwa742.extif = fbdev->ext_if; + hwa742.int_ctrl = fbdev->int_ctrl; + + omapfb_conf = fbdev->dev->platform_data; + ctrl_conf = omapfb_conf->ctrl_platform_data; + + if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) { + dev_err(fbdev->dev, "HWA742: missing platform data\n"); + r = -ENOENT; + goto err1; + } + + hwa742.power_down = ctrl_conf->power_down; + hwa742.power_up = ctrl_conf->power_up; + + spin_lock_init(&hwa742.req_lock); + + if ((r = hwa742.int_ctrl->init(fbdev, 1, req_vram)) < 0) + goto err1; + + if ((r = hwa742.extif->init(fbdev)) < 0) + goto err2; + + ext_clk = ctrl_conf->get_clock_rate(fbdev->dev); + if ((r = calc_extif_timings(ext_clk, &extif_mem_div)) < 0) + goto err3; + hwa742.extif->set_timings(&hwa742.reg_timings); + if (hwa742.power_up != NULL) + hwa742.power_up(fbdev->dev); + + calc_hwa742_clk_rates(ext_clk, &sys_clk, &pix_clk); + if ((r = calc_extif_timings(sys_clk, &extif_mem_div)) < 0) + goto err4; + hwa742.extif->set_timings(&hwa742.reg_timings); + + rev = hwa742_read_reg(HWA742_REV_CODE_REG); + if ((rev & 0xfc) != 0x80) { + dev_err(fbdev->dev, "HWA742: invalid revision %02x\n", rev); + r = -ENODEV; + goto err4; + } + + + if (!(hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x80)) { + dev_err(fbdev->dev, + "HWA742: controller not initialized by the bootloader\n"); + r = -ENODEV; + goto err4; + } + + if (ctrl_conf->te_connected) { + if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) { + dev_err(hwa742.fbdev->dev, + "HWA742: can't setup tearing synchronization\n"); + goto err4; + } + hwa742.te_connected = 1; + } + + hwa742.max_transmit_size = hwa742.extif->max_transmit_size; + + hwa742.update_mode = OMAPFB_UPDATE_DISABLED; + + hwa742.auto_update_window.x = 0; + hwa742.auto_update_window.y = 0; + hwa742.auto_update_window.width = fbdev->panel->x_res; + hwa742.auto_update_window.height = fbdev->panel->y_res; + hwa742.auto_update_window.format = 0; + + init_timer(&hwa742.auto_update_timer); + hwa742.auto_update_timer.function = hwa742_update_window_auto; + hwa742.auto_update_timer.data = 0; + + hwa742.prev_color_mode = -1; + hwa742.prev_flags = 0; + + hwa742.fbdev = fbdev; + + INIT_LIST_HEAD(&hwa742.free_req_list); + INIT_LIST_HEAD(&hwa742.pending_req_list); + for (i = 0; i < ARRAY_SIZE(hwa742.req_pool); i++) + list_add(&hwa742.req_pool[i].entry, &hwa742.free_req_list); + BUG_ON(i <= IRQ_REQ_POOL_SIZE); + sema_init(&hwa742.req_sema, i - IRQ_REQ_POOL_SIZE); + + conf = hwa742_read_reg(HWA742_CONFIG_REG); + dev_info(fbdev->dev, ": Epson HWA742 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + + return 0; +err4: + if (hwa742.power_down != NULL) + hwa742.power_down(fbdev->dev); +err3: + hwa742.extif->cleanup(); +err2: + hwa742.int_ctrl->cleanup(); +err1: + return r; +} + +static void hwa742_cleanup(void) +{ + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + hwa742.extif->cleanup(); + hwa742.int_ctrl->cleanup(); + if (hwa742.power_down != NULL) + hwa742.power_down(hwa742.fbdev->dev); +} + +struct lcd_ctrl hwa742_ctrl = { + .name = "hwa742", + .init = hwa742_init, + .cleanup = hwa742_cleanup, + .bind_client = hwa742_bind_client, + .get_caps = hwa742_get_caps, + .set_update_mode = hwa742_set_update_mode, + .get_update_mode = hwa742_get_update_mode, + .setup_plane = hwa742_setup_plane, + .enable_plane = hwa742_enable_plane, + .update_window = hwa742_update_window_async, + .sync = hwa742_sync, + .suspend = hwa742_suspend, + .resume = hwa742_resume, +}; + diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c new file mode 100644 index 000000000000..51807b4e26d1 --- /dev/null +++ b/drivers/video/omap/lcd_h3.c @@ -0,0 +1,141 @@ +/* + * LCD panel support for the TI OMAP H3 board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/tps65010.h> +#include <asm/arch/omapfb.h> + +#define MODULE_NAME "omapfb-lcd_h3" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static int h3_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) +{ + return 0; +} + +static void h3_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int h3_panel_enable(struct lcd_panel *panel) +{ + int r = 0; + + /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */ + r = tps65010_set_gpio_out_value(GPIO1, HIGH); + if (!r) + r = tps65010_set_gpio_out_value(GPIO2, HIGH); + if (r) + pr_err("Unable to turn on LCD panel\n"); + + return r; +} + +static void h3_panel_disable(struct lcd_panel *panel) +{ + int r = 0; + + /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */ + r = tps65010_set_gpio_out_value(GPIO1, LOW); + if (!r) + tps65010_set_gpio_out_value(GPIO2, LOW); + if (r) + pr_err("Unable to turn off LCD panel\n"); +} + +static unsigned long h3_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel h3_panel = { + .name = "h3", + .config = OMAP_LCDC_PANEL_TFT, + + .data_lines = 16, + .bpp = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12000, + .hsw = 12, + .hfp = 14, + .hbp = 72 - 12, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 0, + + .init = h3_panel_init, + .cleanup = h3_panel_cleanup, + .enable = h3_panel_enable, + .disable = h3_panel_disable, + .get_caps = h3_panel_get_caps, +}; + +static int h3_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&h3_panel); + return 0; +} + +static int h3_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int h3_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver h3_panel_driver = { + .probe = h3_panel_probe, + .remove = h3_panel_remove, + .suspend = h3_panel_suspend, + .resume = h3_panel_resume, + .driver = { + .name = "lcd_h3", + .owner = THIS_MODULE, + }, +}; + +static int h3_panel_drv_init(void) +{ + return platform_driver_register(&h3_panel_driver); +} + +static void h3_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h3_panel_driver); +} + +module_init(h3_panel_drv_init); +module_exit(h3_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c new file mode 100644 index 000000000000..fd6f0eb16de1 --- /dev/null +++ b/drivers/video/omap/lcd_h4.c @@ -0,0 +1,117 @@ +/* + * LCD panel support for the TI OMAP H4 board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/arch/omapfb.h> + +static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) +{ + return 0; +} + +static void h4_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int h4_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void h4_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long h4_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel h4_panel = { + .name = "h4", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 6250, + .hsw = 15, + .hfp = 15, + .hbp = 60, + .vsw = 1, + .vfp = 1, + .vbp = 1, + + .init = h4_panel_init, + .cleanup = h4_panel_cleanup, + .enable = h4_panel_enable, + .disable = h4_panel_disable, + .get_caps = h4_panel_get_caps, +}; + +static int h4_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&h4_panel); + return 0; +} + +static int h4_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int h4_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver h4_panel_driver = { + .probe = h4_panel_probe, + .remove = h4_panel_remove, + .suspend = h4_panel_suspend, + .resume = h4_panel_resume, + .driver = { + .name = "lcd_h4", + .owner = THIS_MODULE, + }, +}; + +static int h4_panel_drv_init(void) +{ + return platform_driver_register(&h4_panel_driver); +} + +static void h4_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h4_panel_driver); +} + +module_init(h4_panel_drv_init); +module_exit(h4_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c new file mode 100644 index 000000000000..551f385861d1 --- /dev/null +++ b/drivers/video/omap/lcd_inn1510.c @@ -0,0 +1,124 @@ +/* + * LCD panel support for the TI OMAP1510 Innovator board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <asm/arch/fpga.h> +#include <asm/arch/omapfb.h> + +static int innovator1510_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void innovator1510_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int innovator1510_panel_enable(struct lcd_panel *panel) +{ + fpga_write(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL); + return 0; +} + +static void innovator1510_panel_disable(struct lcd_panel *panel) +{ + fpga_write(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL); +} + +static unsigned long innovator1510_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel innovator1510_panel = { + .name = "inn1510", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = innovator1510_panel_init, + .cleanup = innovator1510_panel_cleanup, + .enable = innovator1510_panel_enable, + .disable = innovator1510_panel_disable, + .get_caps = innovator1510_panel_get_caps, +}; + +static int innovator1510_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&innovator1510_panel); + return 0; +} + +static int innovator1510_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int innovator1510_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int innovator1510_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver innovator1510_panel_driver = { + .probe = innovator1510_panel_probe, + .remove = innovator1510_panel_remove, + .suspend = innovator1510_panel_suspend, + .resume = innovator1510_panel_resume, + .driver = { + .name = "lcd_inn1510", + .owner = THIS_MODULE, + }, +}; + +static int innovator1510_panel_drv_init(void) +{ + return platform_driver_register(&innovator1510_panel_driver); +} + +static void innovator1510_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1510_panel_driver); +} + +module_init(innovator1510_panel_drv_init); +module_exit(innovator1510_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c new file mode 100644 index 000000000000..95604ca43301 --- /dev/null +++ b/drivers/video/omap/lcd_inn1610.c @@ -0,0 +1,150 @@ +/* + * LCD panel support for the TI OMAP1610 Innovator board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/omapfb.h> + +#define MODULE_NAME "omapfb-lcd_h3" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static int innovator1610_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + int r = 0; + + if (omap_request_gpio(14)) { + pr_err("can't request GPIO 14\n"); + r = -1; + goto exit; + } + if (omap_request_gpio(15)) { + pr_err("can't request GPIO 15\n"); + omap_free_gpio(14); + r = -1; + goto exit; + } + /* configure GPIO(14, 15) as outputs */ + omap_set_gpio_direction(14, 0); + omap_set_gpio_direction(15, 0); +exit: + return r; +} + +static void innovator1610_panel_cleanup(struct lcd_panel *panel) +{ + omap_free_gpio(15); + omap_free_gpio(14); +} + +static int innovator1610_panel_enable(struct lcd_panel *panel) +{ + /* set GPIO14 and GPIO15 high */ + omap_set_gpio_dataout(14, 1); + omap_set_gpio_dataout(15, 1); + return 0; +} + +static void innovator1610_panel_disable(struct lcd_panel *panel) +{ + /* set GPIO13, GPIO14 and GPIO15 low */ + omap_set_gpio_dataout(14, 0); + omap_set_gpio_dataout(15, 0); +} + +static unsigned long innovator1610_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel innovator1610_panel = { + .name = "inn1610", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 320, + .y_res = 240, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = innovator1610_panel_init, + .cleanup = innovator1610_panel_cleanup, + .enable = innovator1610_panel_enable, + .disable = innovator1610_panel_disable, + .get_caps = innovator1610_panel_get_caps, +}; + +static int innovator1610_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&innovator1610_panel); + return 0; +} + +static int innovator1610_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int innovator1610_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int innovator1610_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver innovator1610_panel_driver = { + .probe = innovator1610_panel_probe, + .remove = innovator1610_panel_remove, + .suspend = innovator1610_panel_suspend, + .resume = innovator1610_panel_resume, + .driver = { + .name = "lcd_inn1610", + .owner = THIS_MODULE, + }, +}; + +static int innovator1610_panel_drv_init(void) +{ + return platform_driver_register(&innovator1610_panel_driver); +} + +static void innovator1610_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1610_panel_driver); +} + +module_init(innovator1610_panel_drv_init); +module_exit(innovator1610_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c new file mode 100644 index 000000000000..a38038840fd6 --- /dev/null +++ b/drivers/video/omap/lcd_osk.c @@ -0,0 +1,144 @@ +/* + * LCD panel support for the TI OMAP OSK board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * Adapted for OSK by <dirk.behme@de.bosch.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/mux.h> +#include <asm/arch/omapfb.h> + +static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) +{ + return 0; +} + +static void osk_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int osk_panel_enable(struct lcd_panel *panel) +{ + /* configure PWL pin */ + omap_cfg_reg(PWL); + + /* Enable PWL unit */ + omap_writeb(0x01, OMAP_PWL_CLK_ENABLE); + + /* Set PWL level */ + omap_writeb(0xFF, OMAP_PWL_ENABLE); + + /* configure GPIO2 as output */ + omap_set_gpio_direction(2, 0); + + /* set GPIO2 high */ + omap_set_gpio_dataout(2, 1); + + return 0; +} + +static void osk_panel_disable(struct lcd_panel *panel) +{ + /* Set PWL level to zero */ + omap_writeb(0x00, OMAP_PWL_ENABLE); + + /* Disable PWL unit */ + omap_writeb(0x00, OMAP_PWL_CLK_ENABLE); + + /* set GPIO2 low */ + omap_set_gpio_dataout(2, 0); +} + +static unsigned long osk_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel osk_panel = { + .name = "osk", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = osk_panel_init, + .cleanup = osk_panel_cleanup, + .enable = osk_panel_enable, + .disable = osk_panel_disable, + .get_caps = osk_panel_get_caps, +}; + +static int osk_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&osk_panel); + return 0; +} + +static int osk_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int osk_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver osk_panel_driver = { + .probe = osk_panel_probe, + .remove = osk_panel_remove, + .suspend = osk_panel_suspend, + .resume = osk_panel_resume, + .driver = { + .name = "lcd_osk", + .owner = THIS_MODULE, + }, +}; + +static int osk_panel_drv_init(void) +{ + return platform_driver_register(&osk_panel_driver); +} + +static void osk_panel_drv_cleanup(void) +{ + platform_driver_unregister(&osk_panel_driver); +} + +module_init(osk_panel_drv_init); +module_exit(osk_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c new file mode 100644 index 000000000000..52bdfdac42c9 --- /dev/null +++ b/drivers/video/omap/lcd_palmte.c @@ -0,0 +1,123 @@ +/* + * LCD panel support for the Palm Tungsten E + * + * Original version : Romain Goyet <r.goyet@gmail.com> + * Current version : Laurent Gonzalez <palmte.linux@free.fr> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <asm/arch/fpga.h> +#include <asm/arch/omapfb.h> + +static int palmte_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void palmte_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int palmte_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void palmte_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long palmte_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel palmte_panel = { + .name = "palmte", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | + OMAP_LCDC_HSVS_OPPOSITE, + + .data_lines = 16, + .bpp = 8, + .pixel_clock = 12000, + .x_res = 320, + .y_res = 320, + .hsw = 4, + .hfp = 8, + .hbp = 28, + .vsw = 1, + .vfp = 8, + .vbp = 7, + .pcd = 0, + + .init = palmte_panel_init, + .cleanup = palmte_panel_cleanup, + .enable = palmte_panel_enable, + .disable = palmte_panel_disable, + .get_caps = palmte_panel_get_caps, +}; + +static int palmte_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&palmte_panel); + return 0; +} + +static int palmte_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int palmte_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver palmte_panel_driver = { + .probe = palmte_panel_probe, + .remove = palmte_panel_remove, + .suspend = palmte_panel_suspend, + .resume = palmte_panel_resume, + .driver = { + .name = "lcd_palmte", + .owner = THIS_MODULE, + }, +}; + +static int palmte_panel_drv_init(void) +{ + return platform_driver_register(&palmte_panel_driver); +} + +static void palmte_panel_drv_cleanup(void) +{ + platform_driver_unregister(&palmte_panel_driver); +} + +module_init(palmte_panel_drv_init); +module_exit(palmte_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c new file mode 100644 index 000000000000..4bb349f54356 --- /dev/null +++ b/drivers/video/omap/lcd_palmtt.c @@ -0,0 +1,127 @@ +/* + * LCD panel support for Palm Tungsten|T + * Current version : Marek Vasut <marek.vasut@gmail.com> + * + * Modified from lcd_inn1510.c + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* +GPIO11 - backlight +GPIO12 - screen blanking +GPIO13 - screen blanking +*/ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/io.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/omapfb.h> + +static int palmtt_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void palmtt_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int palmtt_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void palmtt_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long palmtt_panel_get_caps(struct lcd_panel *panel) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +struct lcd_panel palmtt_panel = { + .name = "palmtt", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | + OMAP_LCDC_HSVS_OPPOSITE, + .bpp = 16, + .data_lines = 16, + .x_res = 320, + .y_res = 320, + .pixel_clock = 10000, + .hsw = 4, + .hfp = 8, + .hbp = 28, + .vsw = 1, + .vfp = 8, + .vbp = 7, + .pcd = 0, + + .init = palmtt_panel_init, + .cleanup = palmtt_panel_cleanup, + .enable = palmtt_panel_enable, + .disable = palmtt_panel_disable, + .get_caps = palmtt_panel_get_caps, +}; + +static int palmtt_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&palmtt_panel); + return 0; +} + +static int palmtt_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int palmtt_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int palmtt_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver palmtt_panel_driver = { + .probe = palmtt_panel_probe, + .remove = palmtt_panel_remove, + .suspend = palmtt_panel_suspend, + .resume = palmtt_panel_resume, + .driver = { + .name = "lcd_palmtt", + .owner = THIS_MODULE, + }, +}; + +static int palmtt_panel_drv_init(void) +{ + return platform_driver_register(&palmtt_panel_driver); +} + +static void palmtt_panel_drv_cleanup(void) +{ + platform_driver_unregister(&palmtt_panel_driver); +} + +module_init(palmtt_panel_drv_init); +module_exit(palmtt_panel_drv_cleanup); diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c new file mode 100644 index 000000000000..ea6170ddff35 --- /dev/null +++ b/drivers/video/omap/lcd_palmz71.c @@ -0,0 +1,123 @@ +/* + * LCD panel support for the Palm Zire71 + * + * Original version : Romain Goyet + * Current version : Laurent Gonzalez + * Modified for zire71 : Marek Vasut + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <asm/arch/omapfb.h> + +static int palmz71_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void palmz71_panel_cleanup(struct lcd_panel *panel) +{ + +} + +static int palmz71_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void palmz71_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long palmz71_panel_get_caps(struct lcd_panel *panel) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +struct lcd_panel palmz71_panel = { + .name = "palmz71", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | + OMAP_LCDC_HSVS_OPPOSITE, + .data_lines = 16, + .bpp = 16, + .pixel_clock = 24000, + .x_res = 320, + .y_res = 320, + .hsw = 4, + .hfp = 8, + .hbp = 28, + .vsw = 1, + .vfp = 8, + .vbp = 7, + .pcd = 0, + + .init = palmz71_panel_init, + .cleanup = palmz71_panel_cleanup, + .enable = palmz71_panel_enable, + .disable = palmz71_panel_disable, + .get_caps = palmz71_panel_get_caps, +}; + +static int palmz71_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&palmz71_panel); + return 0; +} + +static int palmz71_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int palmz71_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int palmz71_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver palmz71_panel_driver = { + .probe = palmz71_panel_probe, + .remove = palmz71_panel_remove, + .suspend = palmz71_panel_suspend, + .resume = palmz71_panel_resume, + .driver = { + .name = "lcd_palmz71", + .owner = THIS_MODULE, + }, +}; + +static int palmz71_panel_drv_init(void) +{ + return platform_driver_register(&palmz71_panel_driver); +} + +static void palmz71_panel_drv_cleanup(void) +{ + platform_driver_unregister(&palmz71_panel_driver); +} + +module_init(palmz71_panel_drv_init); +module_exit(palmz71_panel_drv_cleanup); diff --git a/drivers/video/omap/lcd_sx1.c b/drivers/video/omap/lcd_sx1.c new file mode 100644 index 000000000000..c4f306a4e5c9 --- /dev/null +++ b/drivers/video/omap/lcd_sx1.c @@ -0,0 +1,334 @@ +/* + * LCD panel support for the Siemens SX1 mobile phone + * + * Current version : Vovan888@gmail.com, great help from FCA00000 + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/omapfb.h> +#include <asm/arch/mcbsp.h> +#include <asm/arch/mux.h> + +/* + * OMAP310 GPIO registers + */ +#define GPIO_DATA_INPUT 0xfffce000 +#define GPIO_DATA_OUTPUT 0xfffce004 +#define GPIO_DIR_CONTROL 0xfffce008 +#define GPIO_INT_CONTROL 0xfffce00c +#define GPIO_INT_MASK 0xfffce010 +#define GPIO_INT_STATUS 0xfffce014 +#define GPIO_PIN_CONTROL 0xfffce018 + + +#define A_LCD_SSC_RD 3 +#define A_LCD_SSC_SD 7 +#define _A_LCD_RESET 9 +#define _A_LCD_SSC_CS 12 +#define _A_LCD_SSC_A0 13 + +#define DSP_REG 0xE1017024 + +const unsigned char INIT_1[12] = { + 0x1C, 0x02, 0x88, 0x00, 0x1E, 0xE0, 0x00, 0xDC, 0x00, 0x02, 0x00 +}; + +const unsigned char INIT_2[127] = { + 0x15, 0x00, 0x29, 0x00, 0x3E, 0x00, 0x51, 0x00, + 0x65, 0x00, 0x7A, 0x00, 0x8D, 0x00, 0xA1, 0x00, + 0xB6, 0x00, 0xC7, 0x00, 0xD8, 0x00, 0xEB, 0x00, + 0xFB, 0x00, 0x0B, 0x01, 0x1B, 0x01, 0x27, 0x01, + 0x34, 0x01, 0x41, 0x01, 0x4C, 0x01, 0x55, 0x01, + 0x5F, 0x01, 0x68, 0x01, 0x70, 0x01, 0x78, 0x01, + 0x7E, 0x01, 0x86, 0x01, 0x8C, 0x01, 0x94, 0x01, + 0x9B, 0x01, 0xA1, 0x01, 0xA4, 0x01, 0xA9, 0x01, + 0xAD, 0x01, 0xB2, 0x01, 0xB7, 0x01, 0xBC, 0x01, + 0xC0, 0x01, 0xC4, 0x01, 0xC8, 0x01, 0xCB, 0x01, + 0xCF, 0x01, 0xD2, 0x01, 0xD5, 0x01, 0xD8, 0x01, + 0xDB, 0x01, 0xE0, 0x01, 0xE3, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xEB, 0x01, 0xEE, 0x01, 0xF1, 0x01, + 0xF3, 0x01, 0xF8, 0x01, 0xF9, 0x01, 0xFC, 0x01, + 0x00, 0x02, 0x03, 0x02, 0x07, 0x02, 0x09, 0x02, + 0x0E, 0x02, 0x13, 0x02, 0x1C, 0x02, 0x00 +}; + +const unsigned char INIT_3[15] = { + 0x14, 0x26, 0x33, 0x3D, 0x45, 0x4D, 0x53, 0x59, + 0x5E, 0x63, 0x67, 0x6D, 0x71, 0x78, 0xFF +}; + +static void epson_sendbyte(int flag, unsigned char byte) +{ + int i, shifter = 0x80; + + if (!flag) + omap_set_gpio_dataout(_A_LCD_SSC_A0, 0); + mdelay(2); + omap_set_gpio_dataout(A_LCD_SSC_RD, 1); + + omap_set_gpio_dataout(A_LCD_SSC_SD, flag); + + OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200); + OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202); + for (i = 0; i < 8; i++) { + OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200); + omap_set_gpio_dataout(A_LCD_SSC_SD, shifter & byte); + OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202); + shifter >>= 1; + } + omap_set_gpio_dataout(_A_LCD_SSC_A0, 1); +} + +static void init_system(void) +{ + omap_mcbsp_request(OMAP_MCBSP3); + omap_mcbsp_stop(OMAP_MCBSP3); +} + +static void setup_GPIO(void) +{ + /* new wave */ + omap_request_gpio(A_LCD_SSC_RD); + omap_request_gpio(A_LCD_SSC_SD); + omap_request_gpio(_A_LCD_RESET); + omap_request_gpio(_A_LCD_SSC_CS); + omap_request_gpio(_A_LCD_SSC_A0); + + /* set all GPIOs to output */ + omap_set_gpio_direction(A_LCD_SSC_RD, 0); + omap_set_gpio_direction(A_LCD_SSC_SD, 0); + omap_set_gpio_direction(_A_LCD_RESET, 0); + omap_set_gpio_direction(_A_LCD_SSC_CS, 0); + omap_set_gpio_direction(_A_LCD_SSC_A0, 0); + + /* set GPIO data */ + omap_set_gpio_dataout(A_LCD_SSC_RD, 1); + omap_set_gpio_dataout(A_LCD_SSC_SD, 0); + omap_set_gpio_dataout(_A_LCD_RESET, 0); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_A0, 1); +} + +static void display_init(void) +{ + int i; + + omap_cfg_reg(MCBSP3_CLKX); + + mdelay(2); + setup_GPIO(); + mdelay(2); + + /* reset LCD */ + omap_set_gpio_dataout(A_LCD_SSC_SD, 1); + epson_sendbyte(0, 0x25); + + omap_set_gpio_dataout(_A_LCD_RESET, 0); + mdelay(10); + omap_set_gpio_dataout(_A_LCD_RESET, 1); + + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + mdelay(2); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD, phase 1 */ + epson_sendbyte(0, 0xCA); + for (i = 0; i < 10; i++) + epson_sendbyte(1, INIT_1[i]); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 2 */ + epson_sendbyte(0, 0xCB); + for (i = 0; i < 125; i++) + epson_sendbyte(1, INIT_2[i]); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 2a */ + epson_sendbyte(0, 0xCC); + for (i = 0; i < 14; i++) + epson_sendbyte(1, INIT_3[i]); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 3 */ + epson_sendbyte(0, 0xBC); + epson_sendbyte(1, 0x08); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 4 */ + epson_sendbyte(0, 0x07); + epson_sendbyte(1, 0x05); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 5 */ + epson_sendbyte(0, 0x94); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 6 */ + epson_sendbyte(0, 0xC6); + epson_sendbyte(1, 0x80); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + mdelay(100); /* used to be 1000 */ + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 7 */ + epson_sendbyte(0, 0x16); + epson_sendbyte(1, 0x02); + epson_sendbyte(1, 0x00); + epson_sendbyte(1, 0xB1); + epson_sendbyte(1, 0x00); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 8 */ + epson_sendbyte(0, 0x76); + epson_sendbyte(1, 0x00); + epson_sendbyte(1, 0x00); + epson_sendbyte(1, 0xDB); + epson_sendbyte(1, 0x00); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + /* init LCD phase 9 */ + epson_sendbyte(0, 0xAF); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); +} + +static int sx1_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) +{ + return 0; +} + +static void sx1_panel_cleanup(struct lcd_panel *panel) +{ +} + +static void sx1_panel_disable(struct lcd_panel *panel) +{ + printk(KERN_INFO "SX1: LCD panel disable\n"); + sx1_setmmipower(0); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + + epson_sendbyte(0, 0x25); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + epson_sendbyte(0, 0xAE); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); + mdelay(100); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 0); + + epson_sendbyte(0, 0x95); + omap_set_gpio_dataout(_A_LCD_SSC_CS, 1); +} + +static int sx1_panel_enable(struct lcd_panel *panel) +{ + printk(KERN_INFO "lcd_sx1: LCD panel enable\n"); + init_system(); + display_init(); + + sx1_setmmipower(1); + sx1_setbacklight(0x18); + sx1_setkeylight (0x06); + return 0; +} + + +static unsigned long sx1_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel sx1_panel = { + .name = "sx1", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC | OMAP_LCDC_INV_PIX_CLOCK | + OMAP_LCDC_INV_OUTPUT_EN, + + .x_res = 176, + .y_res = 220, + .data_lines = 16, + .bpp = 16, + .hsw = 5, + .hfp = 5, + .hbp = 5, + .vsw = 2, + .vfp = 1, + .vbp = 1, + .pixel_clock = 1500, + + .init = sx1_panel_init, + .cleanup = sx1_panel_cleanup, + .enable = sx1_panel_enable, + .disable = sx1_panel_disable, + .get_caps = sx1_panel_get_caps, +}; + +static int sx1_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&sx1_panel); + return 0; +} + +static int sx1_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int sx1_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int sx1_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver sx1_panel_driver = { + .probe = sx1_panel_probe, + .remove = sx1_panel_remove, + .suspend = sx1_panel_suspend, + .resume = sx1_panel_resume, + .driver = { + .name = "lcd_sx1", + .owner = THIS_MODULE, + }, +}; + +static int sx1_panel_drv_init(void) +{ + return platform_driver_register(&sx1_panel_driver); +} + +static void sx1_panel_drv_cleanup(void) +{ + platform_driver_unregister(&sx1_panel_driver); +} + +module_init(sx1_panel_drv_init); +module_exit(sx1_panel_drv_cleanup); diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c new file mode 100644 index 000000000000..9085188d815e --- /dev/null +++ b/drivers/video/omap/lcdc.c @@ -0,0 +1,893 @@ +/* + * OMAP1 internal LCD controller + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> + +#include <asm/arch/dma.h> +#include <asm/arch/omapfb.h> + +#include <asm/mach-types.h> + +#define MODULE_NAME "lcdc" + +#define OMAP_LCDC_BASE 0xfffec000 +#define OMAP_LCDC_SIZE 256 +#define OMAP_LCDC_IRQ INT_LCD_CTRL + +#define OMAP_LCDC_CONTROL (OMAP_LCDC_BASE + 0x00) +#define OMAP_LCDC_TIMING0 (OMAP_LCDC_BASE + 0x04) +#define OMAP_LCDC_TIMING1 (OMAP_LCDC_BASE + 0x08) +#define OMAP_LCDC_TIMING2 (OMAP_LCDC_BASE + 0x0c) +#define OMAP_LCDC_STATUS (OMAP_LCDC_BASE + 0x10) +#define OMAP_LCDC_SUBPANEL (OMAP_LCDC_BASE + 0x14) +#define OMAP_LCDC_LINE_INT (OMAP_LCDC_BASE + 0x18) +#define OMAP_LCDC_DISPLAY_STATUS (OMAP_LCDC_BASE + 0x1c) + +#define OMAP_LCDC_STAT_DONE (1 << 0) +#define OMAP_LCDC_STAT_VSYNC (1 << 1) +#define OMAP_LCDC_STAT_SYNC_LOST (1 << 2) +#define OMAP_LCDC_STAT_ABC (1 << 3) +#define OMAP_LCDC_STAT_LINE_INT (1 << 4) +#define OMAP_LCDC_STAT_FUF (1 << 5) +#define OMAP_LCDC_STAT_LOADED_PALETTE (1 << 6) + +#define OMAP_LCDC_CTRL_LCD_EN (1 << 0) +#define OMAP_LCDC_CTRL_LCD_TFT (1 << 7) +#define OMAP_LCDC_CTRL_LINE_IRQ_CLR_SEL (1 << 10) + +#define OMAP_LCDC_IRQ_VSYNC (1 << 2) +#define OMAP_LCDC_IRQ_DONE (1 << 3) +#define OMAP_LCDC_IRQ_LOADED_PALETTE (1 << 4) +#define OMAP_LCDC_IRQ_LINE_NIRQ (1 << 5) +#define OMAP_LCDC_IRQ_LINE (1 << 6) +#define OMAP_LCDC_IRQ_MASK (((1 << 5) - 1) << 2) + +#define MAX_PALETTE_SIZE PAGE_SIZE + +enum lcdc_load_mode { + OMAP_LCDC_LOAD_PALETTE, + OMAP_LCDC_LOAD_FRAME, + OMAP_LCDC_LOAD_PALETTE_AND_FRAME +}; + +static struct omap_lcd_controller { + enum omapfb_update_mode update_mode; + int ext_mode; + + unsigned long frame_offset; + int screen_width; + int xres; + int yres; + + enum omapfb_color_format color_mode; + int bpp; + void *palette_virt; + dma_addr_t palette_phys; + int palette_code; + int palette_size; + + unsigned int irq_mask; + struct completion last_frame_complete; + struct completion palette_load_complete; + struct clk *lcd_ck; + struct omapfb_device *fbdev; + + void (*dma_callback)(void *data); + void *dma_callback_data; + + int fbmem_allocated; + dma_addr_t vram_phys; + void *vram_virt; + unsigned long vram_size; +} lcdc; + +static void inline enable_irqs(int mask) +{ + lcdc.irq_mask |= mask; +} + +static void inline disable_irqs(int mask) +{ + lcdc.irq_mask &= ~mask; +} + +static void set_load_mode(enum lcdc_load_mode mode) +{ + u32 l; + + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~(3 << 20); + switch (mode) { + case OMAP_LCDC_LOAD_PALETTE: + l |= 1 << 20; + break; + case OMAP_LCDC_LOAD_FRAME: + l |= 2 << 20; + break; + case OMAP_LCDC_LOAD_PALETTE_AND_FRAME: + break; + default: + BUG(); + } + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void enable_controller(void) +{ + u32 l; + + l = omap_readl(OMAP_LCDC_CONTROL); + l |= OMAP_LCDC_CTRL_LCD_EN; + l &= ~OMAP_LCDC_IRQ_MASK; + l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */ + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void disable_controller_async(void) +{ + u32 l; + u32 mask; + + l = omap_readl(OMAP_LCDC_CONTROL); + mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK; + /* + * Preserve the DONE mask, since we still want to get the + * final DONE irq. It will be disabled in the IRQ handler. + */ + mask &= ~OMAP_LCDC_IRQ_DONE; + l &= ~mask; + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void disable_controller(void) +{ + init_completion(&lcdc.last_frame_complete); + disable_controller_async(); + if (!wait_for_completion_timeout(&lcdc.last_frame_complete, + msecs_to_jiffies(500))) + dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n"); +} + +static void reset_controller(u32 status) +{ + static unsigned long reset_count; + static unsigned long last_jiffies; + + disable_controller_async(); + reset_count++; + if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) { + dev_err(lcdc.fbdev->dev, + "resetting (status %#010x,reset count %lu)\n", + status, reset_count); + last_jiffies = jiffies; + } + if (reset_count < 100) { + enable_controller(); + } else { + reset_count = 0; + dev_err(lcdc.fbdev->dev, + "too many reset attempts, giving up.\n"); + } +} + +/* + * Configure the LCD DMA according to the current mode specified by parameters + * in lcdc.fbdev and fbdev->var. + */ +static void setup_lcd_dma(void) +{ + static const int dma_elem_type[] = { + 0, + OMAP_DMA_DATA_TYPE_S8, + OMAP_DMA_DATA_TYPE_S16, + 0, + OMAP_DMA_DATA_TYPE_S32, + }; + struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par; + struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var; + unsigned long src; + int esize, xelem, yelem; + + src = lcdc.vram_phys + lcdc.frame_offset; + + switch (var->rotate) { + case 0: + if (plane->info.mirror || (src & 3) || + lcdc.color_mode == OMAPFB_COLOR_YUV420 || + (lcdc.xres & 1)) + esize = 2; + else + esize = 4; + xelem = lcdc.xres * lcdc.bpp / 8 / esize; + yelem = lcdc.yres; + break; + case 90: + case 180: + case 270: + if (cpu_is_omap15xx()) { + BUG(); + } + esize = 2; + xelem = lcdc.yres * lcdc.bpp / 16; + yelem = lcdc.xres; + break; + default: + BUG(); + return; + } +#ifdef VERBOSE + dev_dbg(lcdc.fbdev->dev, + "setup_dma: src %#010lx esize %d xelem %d yelem %d\n", + src, esize, xelem, yelem); +#endif + omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]); + if (!cpu_is_omap15xx()) { + int bpp = lcdc.bpp; + + /* + * YUV support is only for external mode when we have the + * YUV window embedded in a 16bpp frame buffer. + */ + if (lcdc.color_mode == OMAPFB_COLOR_YUV420) + bpp = 16; + /* Set virtual xres elem size */ + omap_set_lcd_dma_b1_vxres( + lcdc.screen_width * bpp / 8 / esize); + /* Setup transformations */ + omap_set_lcd_dma_b1_rotation(var->rotate); + omap_set_lcd_dma_b1_mirror(plane->info.mirror); + } + omap_setup_lcd_dma(); +} + +static irqreturn_t lcdc_irq_handler(int irq, void *dev_id) +{ + u32 status; + + status = omap_readl(OMAP_LCDC_STATUS); + + if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST)) + reset_controller(status); + else { + if (status & OMAP_LCDC_STAT_DONE) { + u32 l; + + /* + * Disable IRQ_DONE. The status bit will be cleared + * only when the controller is reenabled and we don't + * want to get more interrupts. + */ + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~OMAP_LCDC_IRQ_DONE; + omap_writel(l, OMAP_LCDC_CONTROL); + complete(&lcdc.last_frame_complete); + } + if (status & OMAP_LCDC_STAT_LOADED_PALETTE) { + disable_controller_async(); + complete(&lcdc.palette_load_complete); + } + } + + /* + * Clear these interrupt status bits. + * Sync_lost, FUF bits were cleared by disabling the LCD controller + * LOADED_PALETTE can be cleared this way only in palette only + * load mode. In other load modes it's cleared by disabling the + * controller. + */ + status &= ~(OMAP_LCDC_STAT_VSYNC | + OMAP_LCDC_STAT_LOADED_PALETTE | + OMAP_LCDC_STAT_ABC | + OMAP_LCDC_STAT_LINE_INT); + omap_writel(status, OMAP_LCDC_STATUS); + return IRQ_HANDLED; +} + +/* + * Change to a new video mode. We defer this to a later time to avoid any + * flicker and not to mess up the current LCD DMA context. For this we disable + * the LCD controler, which will generate a DONE irq after the last frame has + * been transferred. Then it'll be safe to reconfigure both the LCD controller + * as well as the LCD DMA. + */ +static int omap_lcdc_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var; + struct lcd_panel *panel = lcdc.fbdev->panel; + int rot_x, rot_y; + + if (var->rotate == 0) { + rot_x = panel->x_res; + rot_y = panel->y_res; + } else { + rot_x = panel->y_res; + rot_y = panel->x_res; + } + if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 || + width > rot_x || height > rot_y) { +#ifdef VERBOSE + dev_dbg(lcdc.fbdev->dev, + "invalid plane params plane %d pos_x %d pos_y %d " + "w %d h %d\n", plane, pos_x, pos_y, width, height); +#endif + return -EINVAL; + } + + lcdc.frame_offset = offset; + lcdc.xres = width; + lcdc.yres = height; + lcdc.screen_width = screen_width; + lcdc.color_mode = color_mode; + + switch (color_mode) { + case OMAPFB_COLOR_CLUT_8BPP: + lcdc.bpp = 8; + lcdc.palette_code = 0x3000; + lcdc.palette_size = 512; + break; + case OMAPFB_COLOR_RGB565: + lcdc.bpp = 16; + lcdc.palette_code = 0x4000; + lcdc.palette_size = 32; + break; + case OMAPFB_COLOR_RGB444: + lcdc.bpp = 16; + lcdc.palette_code = 0x4000; + lcdc.palette_size = 32; + break; + case OMAPFB_COLOR_YUV420: + if (lcdc.ext_mode) { + lcdc.bpp = 12; + break; + } + /* fallthrough */ + case OMAPFB_COLOR_YUV422: + if (lcdc.ext_mode) { + lcdc.bpp = 16; + break; + } + /* fallthrough */ + default: + /* FIXME: other BPPs. + * bpp1: code 0, size 256 + * bpp2: code 0x1000 size 256 + * bpp4: code 0x2000 size 256 + * bpp12: code 0x4000 size 32 + */ + dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode); + BUG(); + return -1; + } + + if (lcdc.ext_mode) { + setup_lcd_dma(); + return 0; + } + + if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + disable_controller(); + omap_stop_lcd_dma(); + setup_lcd_dma(); + enable_controller(); + } + + return 0; +} + +static int omap_lcdc_enable_plane(int plane, int enable) +{ + dev_dbg(lcdc.fbdev->dev, + "plane %d enable %d update_mode %d ext_mode %d\n", + plane, enable, lcdc.update_mode, lcdc.ext_mode); + if (plane != OMAPFB_PLANE_GFX) + return -EINVAL; + + return 0; +} + +/* + * Configure the LCD DMA for a palette load operation and do the palette + * downloading synchronously. We don't use the frame+palette load mode of + * the controller, since the palette can always be downloaded seperately. + */ +static void load_palette(void) +{ + u16 *palette; + + palette = (u16 *)lcdc.palette_virt; + + *(u16 *)palette &= 0x0fff; + *(u16 *)palette |= lcdc.palette_code; + + omap_set_lcd_dma_b1(lcdc.palette_phys, + lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32); + + omap_set_lcd_dma_single_transfer(1); + omap_setup_lcd_dma(); + + init_completion(&lcdc.palette_load_complete); + enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); + set_load_mode(OMAP_LCDC_LOAD_PALETTE); + enable_controller(); + if (!wait_for_completion_timeout(&lcdc.palette_load_complete, + msecs_to_jiffies(500))) + dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n"); + /* The controller gets disabled in the irq handler */ + disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); + omap_stop_lcd_dma(); + + omap_set_lcd_dma_single_transfer(lcdc.ext_mode); +} + +/* Used only in internal controller mode */ +static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue, + u16 transp, int update_hw_pal) +{ + u16 *palette; + + if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255) + return -EINVAL; + + palette = (u16 *)lcdc.palette_virt; + + palette[regno] &= ~0x0fff; + palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) | + (blue >> 12); + + if (update_hw_pal) { + disable_controller(); + omap_stop_lcd_dma(); + load_palette(); + setup_lcd_dma(); + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_controller(); + } + + return 0; +} + +static void calc_ck_div(int is_tft, int pck, int *pck_div) +{ + unsigned long lck; + + pck = max(1, pck); + lck = clk_get_rate(lcdc.lcd_ck); + *pck_div = (lck + pck - 1) / pck; + if (is_tft) + *pck_div = max(2, *pck_div); + else + *pck_div = max(3, *pck_div); + if (*pck_div > 255) { + /* FIXME: try to adjust logic clock divider as well */ + *pck_div = 255; + dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n", + pck / 1000); + } +} + +static void inline setup_regs(void) +{ + u32 l; + struct lcd_panel *panel = lcdc.fbdev->panel; + int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; + unsigned long lck; + int pcd; + + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~OMAP_LCDC_CTRL_LCD_TFT; + l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0; +#ifdef CONFIG_MACH_OMAP_PALMTE +/* FIXME:if (machine_is_omap_palmte()) { */ + /* PalmTE uses alternate TFT setting in 8BPP mode */ + l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0; +/* } */ +#endif + omap_writel(l, OMAP_LCDC_CONTROL); + + l = omap_readl(OMAP_LCDC_TIMING2); + l &= ~(((1 << 6) - 1) << 20); + l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20; + omap_writel(l, OMAP_LCDC_TIMING2); + + l = panel->x_res - 1; + l |= (panel->hsw - 1) << 10; + l |= (panel->hfp - 1) << 16; + l |= (panel->hbp - 1) << 24; + omap_writel(l, OMAP_LCDC_TIMING0); + + l = panel->y_res - 1; + l |= (panel->vsw - 1) << 10; + l |= panel->vfp << 16; + l |= panel->vbp << 24; + omap_writel(l, OMAP_LCDC_TIMING1); + + l = omap_readl(OMAP_LCDC_TIMING2); + l &= ~0xff; + + lck = clk_get_rate(lcdc.lcd_ck); + + if (!panel->pcd) + calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd); + else { + dev_warn(lcdc.fbdev->dev, + "Pixel clock divider value is obsolete.\n" + "Try to set pixel_clock to %lu and pcd to 0 " + "in drivers/video/omap/lcd_%s.c and submit a patch.\n", + lck / panel->pcd / 1000, panel->name); + + pcd = panel->pcd; + } + l |= pcd & 0xff; + l |= panel->acb << 8; + omap_writel(l, OMAP_LCDC_TIMING2); + + /* update panel info with the exact clock */ + panel->pixel_clock = lck / pcd / 1000; +} + +/* + * Configure the LCD controller, download the color palette and start a looped + * DMA transfer of the frame image data. Called only in internal + * controller mode. + */ +static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + if (mode != lcdc.update_mode) { + switch (mode) { + case OMAPFB_AUTO_UPDATE: + setup_regs(); + load_palette(); + + /* Setup and start LCD DMA */ + setup_lcd_dma(); + + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_irqs(OMAP_LCDC_IRQ_DONE); + /* This will start the actual DMA transfer */ + enable_controller(); + lcdc.update_mode = mode; + break; + case OMAPFB_UPDATE_DISABLED: + disable_controller(); + omap_stop_lcd_dma(); + lcdc.update_mode = mode; + break; + default: + r = -EINVAL; + } + } + + return r; +} + +static enum omapfb_update_mode omap_lcdc_get_update_mode(void) +{ + return lcdc.update_mode; +} + +/* PM code called only in internal controller mode */ +static void omap_lcdc_suspend(void) +{ + if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + disable_controller(); + omap_stop_lcd_dma(); + } +} + +static void omap_lcdc_resume(void) +{ + if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + setup_regs(); + load_palette(); + setup_lcd_dma(); + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_irqs(OMAP_LCDC_IRQ_DONE); + enable_controller(); + } +} + +static void omap_lcdc_get_caps(int plane, struct omapfb_caps *caps) +{ + return; +} + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data) +{ + BUG_ON(callback == NULL); + + if (lcdc.dma_callback) + return -EBUSY; + else { + lcdc.dma_callback = callback; + lcdc.dma_callback_data = data; + } + return 0; +} +EXPORT_SYMBOL(omap_lcdc_set_dma_callback); + +void omap_lcdc_free_dma_callback(void) +{ + lcdc.dma_callback = NULL; +} +EXPORT_SYMBOL(omap_lcdc_free_dma_callback); + +static void lcdc_dma_handler(u16 status, void *data) +{ + if (lcdc.dma_callback) + lcdc.dma_callback(lcdc.dma_callback_data); +} + +static int mmap_kern(void) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + kvma = get_vm_area(lcdc.vram_size, VM_IOREMAP); + if (kvma == NULL) { + dev_err(lcdc.fbdev->dev, "can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + vaddr = (unsigned long)kvma->addr; + vma.vm_start = vaddr; + vma.vm_end = vaddr + lcdc.vram_size; + + pgprot = pgprot_writecombine(pgprot_kernel); + if (io_remap_pfn_range(&vma, vaddr, + lcdc.vram_phys >> PAGE_SHIFT, + lcdc.vram_size, pgprot) < 0) { + dev_err(lcdc.fbdev->dev, "kernel mmap for FB memory failed\n"); + return -EAGAIN; + } + + lcdc.vram_virt = (void *)vaddr; + + return 0; +} + +static void unmap_kern(void) +{ + vunmap(lcdc.vram_virt); +} + +static int alloc_palette_ram(void) +{ + lcdc.palette_virt = dma_alloc_writecombine(lcdc.fbdev->dev, + MAX_PALETTE_SIZE, &lcdc.palette_phys, GFP_KERNEL); + if (lcdc.palette_virt == NULL) { + dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n"); + return -ENOMEM; + } + memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE); + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(lcdc.fbdev->dev, MAX_PALETTE_SIZE, + lcdc.palette_virt, lcdc.palette_phys); +} + +static int alloc_fbmem(struct omapfb_mem_region *region) +{ + int bpp; + int frame_size; + struct lcd_panel *panel = lcdc.fbdev->panel; + + bpp = panel->bpp; + if (bpp == 12) + bpp = 16; + frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res); + if (region->size > frame_size) + frame_size = region->size; + lcdc.vram_size = frame_size; + lcdc.vram_virt = dma_alloc_writecombine(lcdc.fbdev->dev, + lcdc.vram_size, &lcdc.vram_phys, GFP_KERNEL); + if (lcdc.vram_virt == NULL) { + dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n"); + return -ENOMEM; + } + region->size = frame_size; + region->paddr = lcdc.vram_phys; + region->vaddr = lcdc.vram_virt; + region->alloc = 1; + + memset(lcdc.vram_virt, 0, lcdc.vram_size); + + return 0; +} + +static void free_fbmem(void) +{ + dma_free_writecombine(lcdc.fbdev->dev, lcdc.vram_size, + lcdc.vram_virt, lcdc.vram_phys); +} + +static int setup_fbmem(struct omapfb_mem_desc *req_md) +{ + int r; + + if (!req_md->region_cnt) { + dev_err(lcdc.fbdev->dev, "no memory regions defined\n"); + return -EINVAL; + } + + if (req_md->region_cnt > 1) { + dev_err(lcdc.fbdev->dev, "only one plane is supported\n"); + req_md->region_cnt = 1; + } + + if (req_md->region[0].paddr == 0) { + lcdc.fbmem_allocated = 1; + if ((r = alloc_fbmem(&req_md->region[0])) < 0) + return r; + return 0; + } + + lcdc.vram_phys = req_md->region[0].paddr; + lcdc.vram_size = req_md->region[0].size; + + if ((r = mmap_kern()) < 0) + return r; + + dev_dbg(lcdc.fbdev->dev, "vram at %08x size %08lx mapped to 0x%p\n", + lcdc.vram_phys, lcdc.vram_size, lcdc.vram_virt); + + return 0; +} + +static void cleanup_fbmem(void) +{ + if (lcdc.fbmem_allocated) + free_fbmem(); + else + unmap_kern(); +} + +static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, + struct omapfb_mem_desc *req_vram) +{ + int r; + u32 l; + int rate; + struct clk *tc_ck; + + lcdc.irq_mask = 0; + + lcdc.fbdev = fbdev; + lcdc.ext_mode = ext_mode; + + l = 0; + omap_writel(l, OMAP_LCDC_CONTROL); + + /* FIXME: + * According to errata some platforms have a clock rate limitiation + */ + lcdc.lcd_ck = clk_get(NULL, "lcd_ck"); + if (IS_ERR(lcdc.lcd_ck)) { + dev_err(fbdev->dev, "unable to access LCD clock\n"); + r = PTR_ERR(lcdc.lcd_ck); + goto fail0; + } + + tc_ck = clk_get(NULL, "tc_ck"); + if (IS_ERR(tc_ck)) { + dev_err(fbdev->dev, "unable to access TC clock\n"); + r = PTR_ERR(tc_ck); + goto fail1; + } + + rate = clk_get_rate(tc_ck); + clk_put(tc_ck); + + if (machine_is_ams_delta()) + rate /= 4; + if (machine_is_omap_h3()) + rate /= 3; + r = clk_set_rate(lcdc.lcd_ck, rate); + if (r) { + dev_err(fbdev->dev, "failed to adjust LCD rate\n"); + goto fail1; + } + clk_enable(lcdc.lcd_ck); + + r = request_irq(OMAP_LCDC_IRQ, lcdc_irq_handler, 0, MODULE_NAME, fbdev); + if (r) { + dev_err(fbdev->dev, "unable to get IRQ\n"); + goto fail2; + } + + r = omap_request_lcd_dma(lcdc_dma_handler, NULL); + if (r) { + dev_err(fbdev->dev, "unable to get LCD DMA\n"); + goto fail3; + } + + omap_set_lcd_dma_single_transfer(ext_mode); + omap_set_lcd_dma_ext_controller(ext_mode); + + if (!ext_mode) + if ((r = alloc_palette_ram()) < 0) + goto fail4; + + if ((r = setup_fbmem(req_vram)) < 0) + goto fail5; + + pr_info("omapfb: LCDC initialized\n"); + + return 0; +fail5: + if (!ext_mode) + free_palette_ram(); +fail4: + omap_free_lcd_dma(); +fail3: + free_irq(OMAP_LCDC_IRQ, lcdc.fbdev); +fail2: + clk_disable(lcdc.lcd_ck); +fail1: + clk_put(lcdc.lcd_ck); +fail0: + return r; +} + +static void omap_lcdc_cleanup(void) +{ + if (!lcdc.ext_mode) + free_palette_ram(); + cleanup_fbmem(); + omap_free_lcd_dma(); + free_irq(OMAP_LCDC_IRQ, lcdc.fbdev); + clk_disable(lcdc.lcd_ck); + clk_put(lcdc.lcd_ck); +} + +const struct lcd_ctrl omap1_int_ctrl = { + .name = "internal", + .init = omap_lcdc_init, + .cleanup = omap_lcdc_cleanup, + .get_caps = omap_lcdc_get_caps, + .set_update_mode = omap_lcdc_set_update_mode, + .get_update_mode = omap_lcdc_get_update_mode, + .update_window = NULL, + .suspend = omap_lcdc_suspend, + .resume = omap_lcdc_resume, + .setup_plane = omap_lcdc_setup_plane, + .enable_plane = omap_lcdc_enable_plane, + .setcolreg = omap_lcdc_setcolreg, +}; diff --git a/drivers/video/omap/lcdc.h b/drivers/video/omap/lcdc.h new file mode 100644 index 000000000000..adb731e5314a --- /dev/null +++ b/drivers/video/omap/lcdc.h @@ -0,0 +1,7 @@ +#ifndef LCDC_H +#define LCDC_H + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data); +void omap_lcdc_free_dma_callback(void); + +#endif diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c new file mode 100644 index 000000000000..14d0f7a11145 --- /dev/null +++ b/drivers/video/omap/omapfb_main.c @@ -0,0 +1,1941 @@ +/* + * Framebuffer driver for TI OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * Acknowledgements: + * Alex McMains <aam@ridgerun.com> - Original driver + * Juha Yrjola <juha.yrjola@nokia.com> - Original driver and improvements + * Dirk Behme <dirk.behme@de.bosch.com> - changes for 2.6 kernel API + * Texas Instruments - H3 support + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> + +#include <asm/mach-types.h> +#include <asm/arch/dma.h> +#include <asm/arch/omapfb.h> + +#define MODULE_NAME "omapfb" + +static unsigned int def_accel; +static unsigned long def_vram[OMAPFB_PLANE_NUM]; +static int def_vram_cnt; +static unsigned long def_vxres; +static unsigned long def_vyres; +static unsigned int def_rotate; +static unsigned int def_mirror; + +#ifdef CONFIG_FB_OMAP_MANUAL_UPDATE +static int manual_update = 1; +#else +static int manual_update; +#endif + +static struct platform_device *fbdev_pdev; +static struct lcd_panel *fbdev_panel; +static struct omapfb_device *omapfb_dev; + +struct caps_table_struct { + unsigned long flag; + const char *name; +}; + +static struct caps_table_struct ctrl_caps[] = { + { OMAPFB_CAPS_MANUAL_UPDATE, "manual update" }, + { OMAPFB_CAPS_TEARSYNC, "tearing synchronization" }, + { OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" }, + { OMAPFB_CAPS_PLANE_SCALE, "scale plane" }, + { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" }, + { OMAPFB_CAPS_WINDOW_SCALE, "scale window" }, + { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" }, + { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" }, +}; + +static struct caps_table_struct color_caps[] = { + { 1 << OMAPFB_COLOR_RGB565, "RGB565", }, + { 1 << OMAPFB_COLOR_YUV422, "YUV422", }, + { 1 << OMAPFB_COLOR_YUV420, "YUV420", }, + { 1 << OMAPFB_COLOR_CLUT_8BPP, "CLUT8", }, + { 1 << OMAPFB_COLOR_CLUT_4BPP, "CLUT4", }, + { 1 << OMAPFB_COLOR_CLUT_2BPP, "CLUT2", }, + { 1 << OMAPFB_COLOR_CLUT_1BPP, "CLUT1", }, + { 1 << OMAPFB_COLOR_RGB444, "RGB444", }, + { 1 << OMAPFB_COLOR_YUY422, "YUY422", }, +}; + +/* + * --------------------------------------------------------------------------- + * LCD panel + * --------------------------------------------------------------------------- + */ +extern struct lcd_ctrl omap1_int_ctrl; +extern struct lcd_ctrl omap2_int_ctrl; +extern struct lcd_ctrl hwa742_ctrl; +extern struct lcd_ctrl blizzard_ctrl; + +static struct lcd_ctrl *ctrls[] = { +#ifdef CONFIG_ARCH_OMAP1 + &omap1_int_ctrl, +#else + &omap2_int_ctrl, +#endif + +#ifdef CONFIG_FB_OMAP_LCDC_HWA742 + &hwa742_ctrl, +#endif +#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD + &blizzard_ctrl, +#endif +}; + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL +#ifdef CONFIG_ARCH_OMAP1 +extern struct lcd_ctrl_extif omap1_ext_if; +#else +extern struct lcd_ctrl_extif omap2_ext_if; +#endif +#endif + +static void omapfb_rqueue_lock(struct omapfb_device *fbdev) +{ + mutex_lock(&fbdev->rqueue_mutex); +} + +static void omapfb_rqueue_unlock(struct omapfb_device *fbdev) +{ + mutex_unlock(&fbdev->rqueue_mutex); +} + +/* + * --------------------------------------------------------------------------- + * LCD controller and LCD DMA + * --------------------------------------------------------------------------- + */ +/* Lookup table to map elem size to elem type. */ +static const int dma_elem_type[] = { + 0, + OMAP_DMA_DATA_TYPE_S8, + OMAP_DMA_DATA_TYPE_S16, + 0, + OMAP_DMA_DATA_TYPE_S32, +}; + +/* + * Allocate resources needed for LCD controller and LCD DMA operations. Video + * memory is allocated from system memory according to the virtual display + * size, except if a bigger memory size is specified explicitly as a kernel + * parameter. + */ +static int ctrl_init(struct omapfb_device *fbdev) +{ + int r; + int i; + + /* kernel/module vram parameters override boot tags/board config */ + if (def_vram_cnt) { + for (i = 0; i < def_vram_cnt; i++) + fbdev->mem_desc.region[i].size = + PAGE_ALIGN(def_vram[i]); + fbdev->mem_desc.region_cnt = i; + } else { + struct omapfb_platform_data *conf; + + conf = fbdev->dev->platform_data; + fbdev->mem_desc = conf->mem_desc; + } + + if (!fbdev->mem_desc.region_cnt) { + struct lcd_panel *panel = fbdev->panel; + int def_size; + int bpp = panel->bpp; + + /* 12 bpp is packed in 16 bits */ + if (bpp == 12) + bpp = 16; + def_size = def_vxres * def_vyres * bpp / 8; + fbdev->mem_desc.region_cnt = 1; + fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size); + } + r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc); + if (r < 0) { + dev_err(fbdev->dev, "controller initialization failed (%d)\n", + r); + return r; + } + +#ifdef DEBUG + for (i = 0; i < fbdev->mem_desc.region_cnt; i++) { + dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n", + i, + fbdev->mem_desc.region[i].paddr, + fbdev->mem_desc.region[i].vaddr, + fbdev->mem_desc.region[i].size); + } +#endif + return 0; +} + +static void ctrl_cleanup(struct omapfb_device *fbdev) +{ + fbdev->ctrl->cleanup(); +} + +/* Must be called with fbdev->rqueue_mutex held. */ +static int ctrl_change_mode(struct fb_info *fbi) +{ + int r; + unsigned long offset; + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct fb_var_screeninfo *var = &fbi->var; + + offset = var->yoffset * fbi->fix.line_length + + var->xoffset * var->bits_per_pixel / 8; + + if (fbdev->ctrl->sync) + fbdev->ctrl->sync(); + r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out, + offset, var->xres_virtual, + plane->info.pos_x, plane->info.pos_y, + var->xres, var->yres, plane->color_mode); + if (fbdev->ctrl->set_scale != NULL) + r = fbdev->ctrl->set_scale(plane->idx, + var->xres, var->yres, + plane->info.out_width, + plane->info.out_height); + + return r; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks and the ioctl interface + * --------------------------------------------------------------------------- + */ +/* Called each time the omapfb device is opened */ +static int omapfb_open(struct fb_info *info, int user) +{ + return 0; +} + +static void omapfb_sync(struct fb_info *info); + +/* Called when the omapfb device is closed. We make sure that any pending + * gfx DMA operations are ended, before we return. */ +static int omapfb_release(struct fb_info *info, int user) +{ + omapfb_sync(info); + return 0; +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + struct omapfb_plane_struct *plane = info->par; + struct omapfb_device *fbdev = plane->fbdev; + struct fb_var_screeninfo *var = &info->var; + int r = 0; + + switch (plane->color_mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + case OMAPFB_COLOR_YUY422: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + /* Fallthrough */ + case OMAPFB_COLOR_RGB565: + case OMAPFB_COLOR_RGB444: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> (16 - var->red.length)) << + var->red.offset) | + ((green >> (16 - var->green.length)) << + var->green.offset) | + (blue >> (16 - var->blue.length)); + ((u32 *)(info->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + return _setcolreg(info, regno, red, green, blue, transp, 1); +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static int omapfb_update_full_screen(struct fb_info *fbi); + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + int do_update = 0; + int r = 0; + + omapfb_rqueue_lock(fbdev); + switch (blank) { + case VESA_NO_BLANKING: + if (fbdev->state == OMAPFB_SUSPENDED) { + if (fbdev->ctrl->resume) + fbdev->ctrl->resume(); + fbdev->panel->enable(fbdev->panel); + fbdev->state = OMAPFB_ACTIVE; + if (fbdev->ctrl->get_update_mode() == + OMAPFB_MANUAL_UPDATE) + do_update = 1; + } + break; + case VESA_POWERDOWN: + if (fbdev->state == OMAPFB_ACTIVE) { + fbdev->panel->disable(fbdev->panel); + if (fbdev->ctrl->suspend) + fbdev->ctrl->suspend(); + fbdev->state = OMAPFB_SUSPENDED; + } + break; + default: + r = -EINVAL; + } + omapfb_rqueue_unlock(fbdev); + + if (r == 0 && do_update) + r = omapfb_update_full_screen(fbi); + + return r; +} + +static void omapfb_sync(struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + + omapfb_rqueue_lock(fbdev); + if (fbdev->ctrl->sync) + fbdev->ctrl->sync(); + omapfb_rqueue_unlock(fbdev); +} + +/* + * Set fb_info.fix fields and also updates fbdev. + * When calling this fb_info.var must be set up already. + */ +static void set_fb_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_mem_region *rg; + int bpp; + + rg = &plane->fbdev->mem_desc.region[plane->idx]; + fbi->screen_base = (char __iomem *)rg->vaddr; + fix->smem_start = rg->paddr; + fix->smem_len = rg->size; + + fix->type = FB_TYPE_PACKED_PIXELS; + bpp = var->bits_per_pixel; + if (var->nonstd) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else switch (var->bits_per_pixel) { + case 16: + case 12: + fix->visual = FB_VISUAL_TRUECOLOR; + /* 12bpp is stored in 16 bits */ + bpp = 16; + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + fix->accel = FB_ACCEL_OMAP1610; + fix->line_length = var->xres_virtual * bpp / 8; +} + +static int set_color_mode(struct omapfb_plane_struct *plane, + struct fb_var_screeninfo *var) +{ + switch (var->nonstd) { + case 0: + break; + case OMAPFB_COLOR_YUV422: + var->bits_per_pixel = 16; + plane->color_mode = var->nonstd; + return 0; + case OMAPFB_COLOR_YUV420: + var->bits_per_pixel = 12; + plane->color_mode = var->nonstd; + return 0; + case OMAPFB_COLOR_YUY422: + var->bits_per_pixel = 16; + plane->color_mode = var->nonstd; + return 0; + default: + return -EINVAL; + } + + switch (var->bits_per_pixel) { + case 1: + plane->color_mode = OMAPFB_COLOR_CLUT_1BPP; + return 0; + case 2: + plane->color_mode = OMAPFB_COLOR_CLUT_2BPP; + return 0; + case 4: + plane->color_mode = OMAPFB_COLOR_CLUT_4BPP; + return 0; + case 8: + plane->color_mode = OMAPFB_COLOR_CLUT_8BPP; + return 0; + case 12: + var->bits_per_pixel = 16; + plane->color_mode = OMAPFB_COLOR_RGB444; + return 0; + case 16: + plane->color_mode = OMAPFB_COLOR_RGB565; + return 0; + default: + return -EINVAL; + } +} + +/* + * Check the values in var against our capabilities and in case of out of + * bound values try to adjust them. + */ +static int set_fb_var(struct fb_info *fbi, + struct fb_var_screeninfo *var) +{ + int bpp; + unsigned long max_frame_size; + unsigned long line_size; + int xres_min, xres_max; + int yres_min, yres_max; + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct lcd_panel *panel = fbdev->panel; + + if (set_color_mode(plane, var) < 0) + return -EINVAL; + + bpp = var->bits_per_pixel; + if (plane->color_mode == OMAPFB_COLOR_RGB444) + bpp = 16; + + switch (var->rotate) { + case 0: + case 180: + xres_min = OMAPFB_PLANE_XRES_MIN; + xres_max = panel->x_res; + yres_min = OMAPFB_PLANE_YRES_MIN; + yres_max = panel->y_res; + if (cpu_is_omap15xx()) { + var->xres = panel->x_res; + var->yres = panel->y_res; + } + break; + case 90: + case 270: + xres_min = OMAPFB_PLANE_YRES_MIN; + xres_max = panel->y_res; + yres_min = OMAPFB_PLANE_XRES_MIN; + yres_max = panel->x_res; + if (cpu_is_omap15xx()) { + var->xres = panel->y_res; + var->yres = panel->x_res; + } + break; + default: + return -EINVAL; + } + + if (var->xres < xres_min) + var->xres = xres_min; + if (var->yres < yres_min) + var->yres = yres_min; + if (var->xres > xres_max) + var->xres = xres_max; + if (var->yres > yres_max) + var->yres = yres_max; + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + max_frame_size = fbdev->mem_desc.region[plane->idx].size; + line_size = var->xres_virtual * bpp / 8; + if (line_size * var->yres_virtual > max_frame_size) { + /* Try to keep yres_virtual first */ + line_size = max_frame_size / var->yres_virtual; + var->xres_virtual = line_size * 8 / bpp; + if (var->xres_virtual < var->xres) { + /* Still doesn't fit. Shrink yres_virtual too */ + var->xres_virtual = var->xres; + line_size = var->xres * bpp / 8; + var->yres_virtual = max_frame_size / line_size; + } + /* Recheck this, as the virtual size changed. */ + if (var->xres_virtual < var->xres) + var->xres = var->xres_virtual; + if (var->yres_virtual < var->yres) + var->yres = var->yres_virtual; + if (var->xres < xres_min || var->yres < yres_min) + return -EINVAL; + } + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + line_size = var->xres * bpp / 8; + + if (plane->color_mode == OMAPFB_COLOR_RGB444) { + var->red.offset = 8; var->red.length = 4; + var->red.msb_right = 0; + var->green.offset = 4; var->green.length = 4; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 4; + var->blue.msb_right = 0; + } else { + var->red.offset = 11; var->red.length = 5; + var->red.msb_right = 0; + var->green.offset = 5; var->green.length = 6; + var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 5; + var->blue.msb_right = 0; + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = 10000000 / (panel->pixel_clock / 100); + var->left_margin = panel->hfp; + var->right_margin = panel->hbp; + var->upper_margin = panel->vfp; + var->lower_margin = panel->vbp; + var->hsync_len = panel->hsw; + var->vsync_len = panel->vsw; + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + + +/* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */ +static void omapfb_rotate(struct fb_info *fbi, int rotate) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + + omapfb_rqueue_lock(fbdev); + if (cpu_is_omap15xx() && rotate != fbi->var.rotate) { + struct fb_var_screeninfo *new_var = &fbdev->new_var; + + memcpy(new_var, &fbi->var, sizeof(*new_var)); + new_var->rotate = rotate; + if (set_fb_var(fbi, new_var) == 0 && + memcmp(new_var, &fbi->var, sizeof(*new_var))) { + memcpy(&fbi->var, new_var, sizeof(*new_var)); + ctrl_change_mode(fbi); + } + } + omapfb_rqueue_unlock(fbdev); +} + +/* + * Set new x,y offsets in the virtual display for the visible area and switch + * to the new mode. + */ +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + int r = 0; + + omapfb_rqueue_lock(fbdev); + if (var->xoffset != fbi->var.xoffset || + var->yoffset != fbi->var.yoffset) { + struct fb_var_screeninfo *new_var = &fbdev->new_var; + + memcpy(new_var, &fbi->var, sizeof(*new_var)); + new_var->xoffset = var->xoffset; + new_var->yoffset = var->yoffset; + if (set_fb_var(fbi, new_var)) + r = -EINVAL; + else { + memcpy(&fbi->var, new_var, sizeof(*new_var)); + ctrl_change_mode(fbi); + } + } + omapfb_rqueue_unlock(fbdev); + + return r; +} + +/* Set mirror to vertical axis and switch to the new mode. */ +static int omapfb_mirror(struct fb_info *fbi, int mirror) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + int r = 0; + + omapfb_rqueue_lock(fbdev); + mirror = mirror ? 1 : 0; + if (cpu_is_omap15xx()) + r = -EINVAL; + else if (mirror != plane->info.mirror) { + plane->info.mirror = mirror; + r = ctrl_change_mode(fbi); + } + omapfb_rqueue_unlock(fbdev); + + return r; +} + +/* + * Check values in var, try to adjust them in case of out of bound values if + * possible, or return error. + */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + int r; + + omapfb_rqueue_lock(fbdev); + if (fbdev->ctrl->sync != NULL) + fbdev->ctrl->sync(); + r = set_fb_var(fbi, var); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +/* + * Switch to a new mode. The parameters for it has been check already by + * omapfb_check_var. + */ +static int omapfb_set_par(struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + int r = 0; + + omapfb_rqueue_lock(fbdev); + set_fb_fix(fbi); + r = ctrl_change_mode(fbi); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +int omapfb_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct fb_var_screeninfo *var; + + var = &fbi->var; + if (win->x >= var->xres || win->y >= var->yres || + win->out_x > var->xres || win->out_y >= var->yres) + return -EINVAL; + + if (!fbdev->ctrl->update_window || + fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) + return -ENODEV; + + if (win->x + win->width >= var->xres) + win->width = var->xres - win->x; + if (win->y + win->height >= var->yres) + win->height = var->yres - win->y; + /* The out sizes should be cropped to the LCD size */ + if (win->out_x + win->out_width > fbdev->panel->x_res) + win->out_width = fbdev->panel->x_res - win->out_x; + if (win->out_y + win->out_height > fbdev->panel->y_res) + win->out_height = fbdev->panel->y_res - win->out_y; + if (!win->width || !win->height || !win->out_width || !win->out_height) + return 0; + + return fbdev->ctrl->update_window(fbi, win, callback, callback_data); +} +EXPORT_SYMBOL(omapfb_update_window_async); + +static int omapfb_update_win(struct fb_info *fbi, + struct omapfb_update_window *win) +{ + struct omapfb_plane_struct *plane = fbi->par; + int ret; + + omapfb_rqueue_lock(plane->fbdev); + ret = omapfb_update_window_async(fbi, win, NULL, 0); + omapfb_rqueue_unlock(plane->fbdev); + + return ret; +} + +static int omapfb_update_full_screen(struct fb_info *fbi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct omapfb_update_window win; + int r; + + if (!fbdev->ctrl->update_window || + fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) + return -ENODEV; + + win.x = 0; + win.y = 0; + win.width = fbi->var.xres; + win.height = fbi->var.yres; + win.out_x = 0; + win.out_y = 0; + win.out_width = fbi->var.xres; + win.out_height = fbi->var.yres; + win.format = 0; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->update_window(fbi, &win, NULL, 0); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct lcd_panel *panel = fbdev->panel; + struct omapfb_plane_info old_info; + int r = 0; + + if (pi->pos_x + pi->out_width > panel->x_res || + pi->pos_y + pi->out_height > panel->y_res) + return -EINVAL; + + omapfb_rqueue_lock(fbdev); + if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) { + /* + * This plane's memory was freed, can't enable it + * until it's reallocated. + */ + r = -EINVAL; + goto out; + } + old_info = plane->info; + plane->info = *pi; + if (pi->enabled) { + r = ctrl_change_mode(fbi); + if (r < 0) { + plane->info = old_info; + goto out; + } + } + r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled); + if (r < 0) { + plane->info = old_info; + goto out; + } +out: + omapfb_rqueue_unlock(fbdev); + return r; +} + +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_plane_struct *plane = fbi->par; + + *pi = plane->info; + return 0; +} + +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx]; + size_t size; + int r = 0; + + if (fbdev->ctrl->setup_mem == NULL) + return -ENODEV; + if (mi->type > OMAPFB_MEMTYPE_MAX) + return -EINVAL; + + size = PAGE_ALIGN(mi->size); + omapfb_rqueue_lock(fbdev); + if (plane->info.enabled) { + r = -EBUSY; + goto out; + } + if (rg->size != size || rg->type != mi->type) { + struct fb_var_screeninfo *new_var = &fbdev->new_var; + unsigned long old_size = rg->size; + u8 old_type = rg->type; + unsigned long paddr; + + rg->size = size; + rg->type = mi->type; + /* + * size == 0 is a special case, for which we + * don't check / adjust the screen parameters. + * This isn't a problem since the plane can't + * be reenabled unless its size is > 0. + */ + if (old_size != size && size) { + if (size) { + memcpy(new_var, &fbi->var, sizeof(*new_var)); + r = set_fb_var(fbi, new_var); + if (r < 0) + goto out; + } + } + + if (fbdev->ctrl->sync) + fbdev->ctrl->sync(); + r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr); + if (r < 0) { + /* Revert changes. */ + rg->size = old_size; + rg->type = old_type; + goto out; + } + rg->paddr = paddr; + + if (old_size != size) { + if (size) { + memcpy(&fbi->var, new_var, sizeof(fbi->var)); + set_fb_fix(fbi); + } else { + /* + * Set these explicitly to indicate that the + * plane memory is dealloce'd, the other + * screen parameters in var / fix are invalid. + */ + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + } + } + } +out: + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct omapfb_mem_region *rg; + + rg = &fbdev->mem_desc.region[plane->idx]; + memset(mi, 0, sizeof(*mi)); + mi->size = rg->size; + mi->type = rg->type; + + return 0; +} + +static int omapfb_set_color_key(struct omapfb_device *fbdev, + struct omapfb_color_key *ck) +{ + int r; + + if (!fbdev->ctrl->set_color_key) + return -ENODEV; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->set_color_key(ck); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static int omapfb_get_color_key(struct omapfb_device *fbdev, + struct omapfb_color_key *ck) +{ + int r; + + if (!fbdev->ctrl->get_color_key) + return -ENODEV; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->get_color_key(ck); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM]; +static int notifier_inited; + +static void omapfb_init_notifier(void) +{ + int i; + + for (i = 0; i < OMAPFB_PLANE_NUM; i++) + BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]); +} + +int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb, + omapfb_notifier_callback_t callback, + void *callback_data) +{ + int r; + + if ((unsigned)omapfb_nb->plane_idx > OMAPFB_PLANE_NUM) + return -EINVAL; + + if (!notifier_inited) { + omapfb_init_notifier(); + notifier_inited = 1; + } + + omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *, + unsigned long, void *))callback; + omapfb_nb->data = callback_data; + r = blocking_notifier_chain_register( + &omapfb_client_list[omapfb_nb->plane_idx], + &omapfb_nb->nb); + if (r) + return r; + if (omapfb_dev != NULL && + omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) { + omapfb_dev->ctrl->bind_client(omapfb_nb); + } + + return 0; +} +EXPORT_SYMBOL(omapfb_register_client); + +int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb) +{ + return blocking_notifier_chain_unregister( + &omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb); +} +EXPORT_SYMBOL(omapfb_unregister_client); + +void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event) +{ + int i; + + if (!notifier_inited) + /* no client registered yet */ + return; + + for (i = 0; i < OMAPFB_PLANE_NUM; i++) + blocking_notifier_call_chain(&omapfb_client_list[i], event, + fbdev->fb_info[i]); +} +EXPORT_SYMBOL(omapfb_notify_clients); + +static int omapfb_set_update_mode(struct omapfb_device *fbdev, + enum omapfb_update_mode mode) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->set_update_mode(mode); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->get_update_mode(); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static void omapfb_get_caps(struct omapfb_device *fbdev, int plane, + struct omapfb_caps *caps) +{ + memset(caps, 0, sizeof(*caps)); + fbdev->ctrl->get_caps(plane, caps); + caps->ctrl |= fbdev->panel->get_caps(fbdev->panel); +} + +/* For lcd testing */ +void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval) +{ + omapfb_rqueue_lock(fbdev); + *(u16 *)fbdev->mem_desc.region[0].vaddr = pixval; + if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) { + struct omapfb_update_window win; + + memset(&win, 0, sizeof(win)); + win.width = 2; + win.height = 2; + win.out_width = 2; + win.out_height = 2; + fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, 0); + } + omapfb_rqueue_unlock(fbdev); +} +EXPORT_SYMBOL(omapfb_write_first_pixel); + +/* + * Ioctl interface. Part of the kernel mode frame buffer API is duplicated + * here to be accessible by user mode code. + */ +static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, + unsigned long arg) +{ + struct omapfb_plane_struct *plane = fbi->par; + struct omapfb_device *fbdev = plane->fbdev; + struct fb_ops *ops = fbi->fbops; + union { + struct omapfb_update_window update_window; + struct omapfb_plane_info plane_info; + struct omapfb_mem_info mem_info; + struct omapfb_color_key color_key; + enum omapfb_update_mode update_mode; + struct omapfb_caps caps; + unsigned int mirror; + int plane_out; + int enable_plane; + } p; + int r = 0; + + BUG_ON(!ops); + switch (cmd) { + case OMAPFB_MIRROR: + if (get_user(p.mirror, (int __user *)arg)) + r = -EFAULT; + else + omapfb_mirror(fbi, p.mirror); + break; + case OMAPFB_SYNC_GFX: + omapfb_sync(fbi); + break; + case OMAPFB_VSYNC: + break; + case OMAPFB_SET_UPDATE_MODE: + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbdev, p.update_mode); + break; + case OMAPFB_GET_UPDATE_MODE: + p.update_mode = omapfb_get_update_mode(fbdev); + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + case OMAPFB_UPDATE_WINDOW_OLD: + if (copy_from_user(&p.update_window, (void __user *)arg, + sizeof(struct omapfb_update_window_old))) + r = -EFAULT; + else { + struct omapfb_update_window *u = &p.update_window; + u->out_x = u->x; + u->out_y = u->y; + u->out_width = u->width; + u->out_height = u->height; + memset(u->reserved, 0, sizeof(u->reserved)); + r = omapfb_update_win(fbi, u); + } + break; + case OMAPFB_UPDATE_WINDOW: + if (copy_from_user(&p.update_window, (void __user *)arg, + sizeof(p.update_window))) + r = -EFAULT; + else + r = omapfb_update_win(fbi, &p.update_window); + break; + case OMAPFB_SETUP_PLANE: + if (copy_from_user(&p.plane_info, (void __user *)arg, + sizeof(p.plane_info))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbi, &p.plane_info); + break; + case OMAPFB_QUERY_PLANE: + if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0) + break; + if (copy_to_user((void __user *)arg, &p.plane_info, + sizeof(p.plane_info))) + r = -EFAULT; + break; + case OMAPFB_SETUP_MEM: + if (copy_from_user(&p.mem_info, (void __user *)arg, + sizeof(p.mem_info))) + r = -EFAULT; + else + r = omapfb_setup_mem(fbi, &p.mem_info); + break; + case OMAPFB_QUERY_MEM: + if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0) + break; + if (copy_to_user((void __user *)arg, &p.mem_info, + sizeof(p.mem_info))) + r = -EFAULT; + break; + case OMAPFB_SET_COLOR_KEY: + if (copy_from_user(&p.color_key, (void __user *)arg, + sizeof(p.color_key))) + r = -EFAULT; + else + r = omapfb_set_color_key(fbdev, &p.color_key); + break; + case OMAPFB_GET_COLOR_KEY: + if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0) + break; + if (copy_to_user((void __user *)arg, &p.color_key, + sizeof(p.color_key))) + r = -EFAULT; + break; + case OMAPFB_GET_CAPS: + omapfb_get_caps(fbdev, plane->idx, &p.caps); + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) + r = -EFAULT; + break; + case OMAPFB_LCD_TEST: + { + int test_num; + + if (get_user(test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!fbdev->panel->run_test) { + r = -EINVAL; + break; + } + r = fbdev->panel->run_test(fbdev->panel, test_num); + break; + } + case OMAPFB_CTRL_TEST: + { + int test_num; + + if (get_user(test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!fbdev->ctrl->run_test) { + r = -EINVAL; + break; + } + r = fbdev->ctrl->run_test(test_num); + break; + } + default: + r = -EINVAL; + } + + return r; +} + +static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct omapfb_plane_struct *plane = info->par; + struct omapfb_device *fbdev = plane->fbdev; + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->mmap(info, vma); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +/* + * Callback table for the frame buffer framework. Some of these pointers + * will be changed according to the current setting of fb_info->accel_flags. + */ +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_rotate = omapfb_rotate, + .fb_pan_display = omapfb_pan_display, +}; + +/* + * --------------------------------------------------------------------------- + * Sysfs interface + * --------------------------------------------------------------------------- + */ +/* omapfbX sysfs entries */ +static ssize_t omapfb_show_caps_num(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int plane; + size_t size; + struct omapfb_caps caps; + + plane = 0; + size = 0; + while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) { + omapfb_get_caps(fbdev, plane, &caps); + size += snprintf(&buf[size], PAGE_SIZE - size, + "plane#%d %#010x %#010x %#010x\n", + plane, caps.ctrl, caps.plane_color, caps.wnd_color); + plane++; + } + return size; +} + +static ssize_t omapfb_show_caps_text(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int i; + struct omapfb_caps caps; + int plane; + size_t size; + + plane = 0; + size = 0; + while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) { + omapfb_get_caps(fbdev, plane, &caps); + size += snprintf(&buf[size], PAGE_SIZE - size, + "plane#%d:\n", plane); + for (i = 0; i < ARRAY_SIZE(ctrl_caps) && + size < PAGE_SIZE; i++) { + if (ctrl_caps[i].flag & caps.ctrl) + size += snprintf(&buf[size], PAGE_SIZE - size, + " %s\n", ctrl_caps[i].name); + } + size += snprintf(&buf[size], PAGE_SIZE - size, + " plane colors:\n"); + for (i = 0; i < ARRAY_SIZE(color_caps) && + size < PAGE_SIZE; i++) { + if (color_caps[i].flag & caps.plane_color) + size += snprintf(&buf[size], PAGE_SIZE - size, + " %s\n", color_caps[i].name); + } + size += snprintf(&buf[size], PAGE_SIZE - size, + " window colors:\n"); + for (i = 0; i < ARRAY_SIZE(color_caps) && + size < PAGE_SIZE; i++) { + if (color_caps[i].flag & caps.wnd_color) + size += snprintf(&buf[size], PAGE_SIZE - size, + " %s\n", color_caps[i].name); + } + + plane++; + } + return size; +} + +static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL); +static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL); + +/* panel sysfs entries */ +static ssize_t omapfb_show_panel_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + + return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name); +} + +static ssize_t omapfb_show_bklight_level(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->get_bklight_level) { + r = snprintf(buf, PAGE_SIZE, "%d\n", + fbdev->panel->get_bklight_level(fbdev->panel)); + } else + r = -ENODEV; + return r; +} + +static ssize_t omapfb_store_bklight_level(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->set_bklight_level) { + unsigned int level; + + if (sscanf(buf, "%10d", &level) == 1) { + r = fbdev->panel->set_bklight_level(fbdev->panel, + level); + } else + r = -EINVAL; + } else + r = -ENODEV; + return r ? r : size; +} + +static ssize_t omapfb_show_bklight_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->get_bklight_level) { + r = snprintf(buf, PAGE_SIZE, "%d\n", + fbdev->panel->get_bklight_max(fbdev->panel)); + } else + r = -ENODEV; + return r; +} + +static struct device_attribute dev_attr_panel_name = + __ATTR(name, 0444, omapfb_show_panel_name, NULL); +static DEVICE_ATTR(backlight_level, 0664, + omapfb_show_bklight_level, omapfb_store_bklight_level); +static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL); + +static struct attribute *panel_attrs[] = { + &dev_attr_panel_name.attr, + &dev_attr_backlight_level.attr, + &dev_attr_backlight_max.attr, + NULL, +}; + +static struct attribute_group panel_attr_grp = { + .name = "panel", + .attrs = panel_attrs, +}; + +/* ctrl sysfs entries */ +static ssize_t omapfb_show_ctrl_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + + return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name); +} + +static struct device_attribute dev_attr_ctrl_name = + __ATTR(name, 0444, omapfb_show_ctrl_name, NULL); + +static struct attribute *ctrl_attrs[] = { + &dev_attr_ctrl_name.attr, + NULL, +}; + +static struct attribute_group ctrl_attr_grp = { + .name = "ctrl", + .attrs = ctrl_attrs, +}; + +static int omapfb_register_sysfs(struct omapfb_device *fbdev) +{ + int r; + + if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num))) + goto fail0; + + if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text))) + goto fail1; + + if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp))) + goto fail2; + + if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp))) + goto fail3; + + return 0; +fail3: + sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp); +fail2: + device_remove_file(fbdev->dev, &dev_attr_caps_text); +fail1: + device_remove_file(fbdev->dev, &dev_attr_caps_num); +fail0: + dev_err(fbdev->dev, "unable to register sysfs interface\n"); + return r; +} + +static void omapfb_unregister_sysfs(struct omapfb_device *fbdev) +{ + sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp); + sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp); + device_remove_file(fbdev->dev, &dev_attr_caps_num); + device_remove_file(fbdev->dev, &dev_attr_caps_text); +} + +/* + * --------------------------------------------------------------------------- + * LDM callbacks + * --------------------------------------------------------------------------- + */ +/* Initialize system fb_info object and set the default video mode. + * The frame buffer memory already allocated by lcddma_init + */ +static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct fb_fix_screeninfo *fix = &info->fix; + int r = 0; + + info->fbops = &omapfb_ops; + info->flags = FBINFO_FLAG_DEFAULT; + + strncpy(fix->id, MODULE_NAME, sizeof(fix->id)); + + info->pseudo_palette = fbdev->pseudo_palette; + + var->accel_flags = def_accel ? FB_ACCELF_TEXT : 0; + var->xres = def_vxres; + var->yres = def_vyres; + var->xres_virtual = def_vxres; + var->yres_virtual = def_vyres; + var->rotate = def_rotate; + var->bits_per_pixel = fbdev->panel->bpp; + + set_fb_var(info, var); + set_fb_fix(info); + + r = fb_alloc_cmap(&info->cmap, 16, 0); + if (r != 0) + dev_err(fbdev->dev, "unable to allocate color map memory\n"); + + return r; +} + +/* Release the fb_info object */ +static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi) +{ + fb_dealloc_cmap(&fbi->cmap); +} + +static void planes_cleanup(struct omapfb_device *fbdev) +{ + int i; + + for (i = 0; i < fbdev->mem_desc.region_cnt; i++) { + if (fbdev->fb_info[i] == NULL) + break; + fbinfo_cleanup(fbdev, fbdev->fb_info[i]); + framebuffer_release(fbdev->fb_info[i]); + } +} + +static int planes_init(struct omapfb_device *fbdev) +{ + struct fb_info *fbi; + int i; + int r; + + for (i = 0; i < fbdev->mem_desc.region_cnt; i++) { + struct omapfb_plane_struct *plane; + fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct), + fbdev->dev); + if (fbi == NULL) { + dev_err(fbdev->dev, + "unable to allocate memory for plane info\n"); + planes_cleanup(fbdev); + return -ENOMEM; + } + plane = fbi->par; + plane->idx = i; + plane->fbdev = fbdev; + plane->info.mirror = def_mirror; + fbdev->fb_info[i] = fbi; + + if ((r = fbinfo_init(fbdev, fbi)) < 0) { + framebuffer_release(fbi); + planes_cleanup(fbdev); + return r; + } + plane->info.out_width = fbi->var.xres; + plane->info.out_height = fbi->var.yres; + } + return 0; +} + +/* + * Free driver resources. Can be called to rollback an aborted initialization + * sequence. + */ +static void omapfb_free_resources(struct omapfb_device *fbdev, int state) +{ + int i; + + switch (state) { + case OMAPFB_ACTIVE: + for (i = 0; i < fbdev->mem_desc.region_cnt; i++) + unregister_framebuffer(fbdev->fb_info[i]); + case 7: + omapfb_unregister_sysfs(fbdev); + case 6: + fbdev->panel->disable(fbdev->panel); + case 5: + omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED); + case 4: + planes_cleanup(fbdev); + case 3: + ctrl_cleanup(fbdev); + case 2: + fbdev->panel->cleanup(fbdev->panel); + case 1: + dev_set_drvdata(fbdev->dev, NULL); + kfree(fbdev); + case 0: + /* nothing to free */ + break; + default: + BUG(); + } +} + +static int omapfb_find_ctrl(struct omapfb_device *fbdev) +{ + struct omapfb_platform_data *conf; + char name[17]; + int i; + + conf = fbdev->dev->platform_data; + + fbdev->ctrl = NULL; + + strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + + if (strcmp(name, "internal") == 0) { + fbdev->ctrl = fbdev->int_ctrl; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(ctrls); i++) { + dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name); + if (strcmp(ctrls[i]->name, name) == 0) { + fbdev->ctrl = ctrls[i]; + break; + } + } + + if (fbdev->ctrl == NULL) { + dev_dbg(fbdev->dev, "ctrl %s not supported\n", name); + return -1; + } + + return 0; +} + +static void check_required_callbacks(struct omapfb_device *fbdev) +{ +#define _C(x) (fbdev->ctrl->x != NULL) +#define _P(x) (fbdev->panel->x != NULL) + BUG_ON(fbdev->ctrl == NULL || fbdev->panel == NULL); + BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) && + _C(set_update_mode) && _C(setup_plane) && _C(enable_plane) && + _P(init) && _P(cleanup) && _P(enable) && _P(disable) && + _P(get_caps))); +#undef _P +#undef _C +} + +/* + * Called by LDM binding to probe and attach a new device. + * Initialization sequence: + * 1. allocate system omapfb_device structure + * 2. select controller type according to platform configuration + * init LCD panel + * 3. init LCD controller and LCD DMA + * 4. init system fb_info structure for all planes + * 5. setup video mode for first plane and enable it + * 6. enable LCD panel + * 7. register sysfs attributes + * OMAPFB_ACTIVE: register system fb_info structure for all planes + */ +static int omapfb_do_probe(struct platform_device *pdev, + struct lcd_panel *panel) +{ + struct omapfb_device *fbdev = NULL; + int init_state; + unsigned long phz, hhz, vhz; + unsigned long vram; + int i; + int r = 0; + + init_state = 0; + + if (pdev->num_resources != 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + goto cleanup; + } + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + r = -ENOENT; + goto cleanup; + } + + fbdev = kzalloc(sizeof(struct omapfb_device), GFP_KERNEL); + if (fbdev == NULL) { + dev_err(&pdev->dev, + "unable to allocate memory for device info\n"); + r = -ENOMEM; + goto cleanup; + } + init_state++; + + fbdev->dev = &pdev->dev; + fbdev->panel = panel; + platform_set_drvdata(pdev, fbdev); + + mutex_init(&fbdev->rqueue_mutex); + +#ifdef CONFIG_ARCH_OMAP1 + fbdev->int_ctrl = &omap1_int_ctrl; +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + fbdev->ext_if = &omap1_ext_if; +#endif +#else /* OMAP2 */ + fbdev->int_ctrl = &omap2_int_ctrl; +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + fbdev->ext_if = &omap2_ext_if; +#endif +#endif + if (omapfb_find_ctrl(fbdev) < 0) { + dev_err(fbdev->dev, + "LCD controller not found, board not supported\n"); + r = -ENODEV; + goto cleanup; + } + + r = fbdev->panel->init(fbdev->panel, fbdev); + if (r) + goto cleanup; + + pr_info("omapfb: configured for panel %s\n", fbdev->panel->name); + + def_vxres = def_vxres ? : fbdev->panel->x_res; + def_vyres = def_vyres ? : fbdev->panel->y_res; + + init_state++; + + r = ctrl_init(fbdev); + if (r) + goto cleanup; + if (fbdev->ctrl->mmap != NULL) + omapfb_ops.fb_mmap = omapfb_mmap; + init_state++; + + check_required_callbacks(fbdev); + + r = planes_init(fbdev); + if (r) + goto cleanup; + init_state++; + +#ifdef CONFIG_FB_OMAP_DMA_TUNE + /* Set DMA priority for EMIFF access to highest */ + if (cpu_class_is_omap1()) + omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15); +#endif + + r = ctrl_change_mode(fbdev->fb_info[0]); + if (r) { + dev_err(fbdev->dev, "mode setting failed\n"); + goto cleanup; + } + + /* GFX plane is enabled by default */ + r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1); + if (r) + goto cleanup; + + omapfb_set_update_mode(fbdev, manual_update ? + OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE); + init_state++; + + r = fbdev->panel->enable(fbdev->panel); + if (r) + goto cleanup; + init_state++; + + r = omapfb_register_sysfs(fbdev); + if (r) + goto cleanup; + init_state++; + + vram = 0; + for (i = 0; i < fbdev->mem_desc.region_cnt; i++) { + r = register_framebuffer(fbdev->fb_info[i]); + if (r != 0) { + dev_err(fbdev->dev, + "registering framebuffer %d failed\n", i); + goto cleanup; + } + vram += fbdev->mem_desc.region[i].size; + } + + fbdev->state = OMAPFB_ACTIVE; + + panel = fbdev->panel; + phz = panel->pixel_clock * 1000; + hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw); + vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw); + + omapfb_dev = fbdev; + + pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n", + vram, fbdev->mem_desc.region_cnt); + pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz " + "vfreq %lu.%lu Hz\n", + phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10); + + return 0; + +cleanup: + omapfb_free_resources(fbdev, init_state); + + return r; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + BUG_ON(fbdev_pdev != NULL); + + /* Delay actual initialization until the LCD is registered */ + fbdev_pdev = pdev; + if (fbdev_panel != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); + return 0; +} + +void omapfb_register_panel(struct lcd_panel *panel) +{ + BUG_ON(fbdev_panel != NULL); + + fbdev_panel = panel; + if (fbdev_pdev != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); +} + +/* Called when the device is being detached from the driver */ +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + enum omapfb_state saved_state = fbdev->state; + + /* FIXME: wait till completion of pending events */ + + fbdev->state = OMAPFB_DISABLED; + omapfb_free_resources(fbdev, saved_state); + + return 0; +} + +/* PM suspend */ +static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + + omapfb_blank(VESA_POWERDOWN, fbdev->fb_info[0]); + + return 0; +} + +/* PM resume */ +static int omapfb_resume(struct platform_device *pdev) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + + omapfb_blank(VESA_NO_BLANKING, fbdev->fb_info[0]); + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .suspend = omapfb_suspend, + .resume = omapfb_resume, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +#ifndef MODULE + +/* Process kernel command line parameters */ +static int __init omapfb_setup(char *options) +{ + char *this_opt = NULL; + int r = 0; + + pr_debug("omapfb: options %s\n", options); + + if (!options || !*options) + return 0; + + while (!r && (this_opt = strsep(&options, ",")) != NULL) { + if (!strncmp(this_opt, "accel", 5)) + def_accel = 1; + else if (!strncmp(this_opt, "vram:", 5)) { + char *suffix; + unsigned long vram; + vram = (simple_strtoul(this_opt + 5, &suffix, 0)); + switch (suffix[0]) { + case '\0': + break; + case 'm': + case 'M': + vram *= 1024; + /* Fall through */ + case 'k': + case 'K': + vram *= 1024; + break; + default: + pr_debug("omapfb: invalid vram suffix %c\n", + suffix[0]); + r = -1; + } + def_vram[def_vram_cnt++] = vram; + } + else if (!strncmp(this_opt, "vxres:", 6)) + def_vxres = simple_strtoul(this_opt + 6, NULL, 0); + else if (!strncmp(this_opt, "vyres:", 6)) + def_vyres = simple_strtoul(this_opt + 6, NULL, 0); + else if (!strncmp(this_opt, "rotate:", 7)) + def_rotate = (simple_strtoul(this_opt + 7, NULL, 0)); + else if (!strncmp(this_opt, "mirror:", 7)) + def_mirror = (simple_strtoul(this_opt + 7, NULL, 0)); + else if (!strncmp(this_opt, "manual_update", 13)) + manual_update = 1; + else { + pr_debug("omapfb: invalid option\n"); + r = -1; + } + } + + return r; +} + +#endif + +/* Register both the driver and the device */ +static int __init omapfb_init(void) +{ +#ifndef MODULE + char *option; + + if (fb_get_options("omapfb", &option)) + return -ENODEV; + omapfb_setup(option); +#endif + /* Register the driver with LDM */ + if (platform_driver_register(&omapfb_driver)) { + pr_debug("failed to register omapfb driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit omapfb_cleanup(void) +{ + platform_driver_unregister(&omapfb_driver); +} + +module_param_named(accel, def_accel, uint, 0664); +module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664); +module_param_named(vxres, def_vxres, long, 0664); +module_param_named(vyres, def_vyres, long, 0664); +module_param_named(rotate, def_rotate, uint, 0664); +module_param_named(mirror, def_mirror, uint, 0664); +module_param_named(manual_update, manual_update, bool, 0664); + +module_init(omapfb_init); +module_exit(omapfb_cleanup); + +MODULE_DESCRIPTION("TI OMAP framebuffer driver"); +MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c new file mode 100644 index 000000000000..2b4269813b22 --- /dev/null +++ b/drivers/video/omap/rfbi.c @@ -0,0 +1,588 @@ +/* + * OMAP2 Remote Frame Buffer Interface support + * + * Copyright (C) 2005 Nokia Corporation + * Author: Juha Yrjölä <juha.yrjola@nokia.com> + * Imre Deak <imre.deak@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/arch/omapfb.h> + +#include "dispc.h" + +/* To work around an RFBI transfer rate limitation */ +#define OMAP_RFBI_RATE_LIMIT 1 + +#define RFBI_BASE 0x48050800 +#define RFBI_REVISION 0x0000 +#define RFBI_SYSCONFIG 0x0010 +#define RFBI_SYSSTATUS 0x0014 +#define RFBI_CONTROL 0x0040 +#define RFBI_PIXEL_CNT 0x0044 +#define RFBI_LINE_NUMBER 0x0048 +#define RFBI_CMD 0x004c +#define RFBI_PARAM 0x0050 +#define RFBI_DATA 0x0054 +#define RFBI_READ 0x0058 +#define RFBI_STATUS 0x005c +#define RFBI_CONFIG0 0x0060 +#define RFBI_ONOFF_TIME0 0x0064 +#define RFBI_CYCLE_TIME0 0x0068 +#define RFBI_DATA_CYCLE1_0 0x006c +#define RFBI_DATA_CYCLE2_0 0x0070 +#define RFBI_DATA_CYCLE3_0 0x0074 +#define RFBI_VSYNC_WIDTH 0x0090 +#define RFBI_HSYNC_WIDTH 0x0094 + +#define DISPC_BASE 0x48050400 +#define DISPC_CONTROL 0x0040 + +static struct { + u32 base; + void (*lcdc_callback)(void *data); + void *lcdc_callback_data; + unsigned long l4_khz; + int bits_per_cycle; + struct omapfb_device *fbdev; + struct clk *dss_ick; + struct clk *dss1_fck; + unsigned tearsync_pin_cnt; + unsigned tearsync_mode; +} rfbi; + +static inline void rfbi_write_reg(int idx, u32 val) +{ + __raw_writel(val, rfbi.base + idx); +} + +static inline u32 rfbi_read_reg(int idx) +{ + return __raw_readl(rfbi.base + idx); +} + +static int rfbi_get_clocks(void) +{ + if (IS_ERR((rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "dss_ick")))) { + dev_err(rfbi.fbdev->dev, "can't get dss_ick"); + return PTR_ERR(rfbi.dss_ick); + } + + if (IS_ERR((rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck")))) { + dev_err(rfbi.fbdev->dev, "can't get dss1_fck"); + clk_put(rfbi.dss_ick); + return PTR_ERR(rfbi.dss1_fck); + } + + return 0; +} + +static void rfbi_put_clocks(void) +{ + clk_put(rfbi.dss1_fck); + clk_put(rfbi.dss_ick); +} + +static void rfbi_enable_clocks(int enable) +{ + if (enable) { + clk_enable(rfbi.dss_ick); + clk_enable(rfbi.dss1_fck); + } else { + clk_disable(rfbi.dss1_fck); + clk_disable(rfbi.dss_ick); + } +} + + +#ifdef VERBOSE +static void rfbi_print_timings(void) +{ + u32 l; + u32 time; + + l = rfbi_read_reg(RFBI_CONFIG0); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time); + l = rfbi_read_reg(RFBI_ONOFF_TIME0); + dev_dbg(rfbi.fbdev->dev, + "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " + "REONTIME %d, REOFFTIME %d\n", + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, + (l >> 20) & 0x0f, (l >> 24) & 0x3f); + + l = rfbi_read_reg(RFBI_CYCLE_TIME0); + dev_dbg(rfbi.fbdev->dev, + "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " + "ACCESSTIME %d\n", + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, + (l >> 22) & 0x3f); +} +#else +static void rfbi_print_timings(void) {} +#endif + +static void rfbi_set_timings(const struct extif_timings *t) +{ + u32 l; + + BUG_ON(!t->converted); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); + + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(1 << 4); + l |= (t->tim[2] ? 1 : 0) << 4; + rfbi_write_reg(RFBI_CONFIG0, l); + + rfbi_print_timings(); + rfbi_enable_clocks(0); +} + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; +} + +#ifdef OMAP_RFBI_RATE_LIMIT +static unsigned long rfbi_get_max_tx_rate(void) +{ + unsigned long l4_rate, dss1_rate; + int min_l4_ticks = 0; + int i; + + /* According to TI this can't be calculated so make the + * adjustments for a couple of known frequencies and warn for + * others. + */ + static const struct { + unsigned long l4_clk; /* HZ */ + unsigned long dss1_clk; /* HZ */ + unsigned long min_l4_ticks; + } ftab[] = { + { 55, 132, 7, }, /* 7.86 MPix/s */ + { 110, 110, 12, }, /* 9.16 MPix/s */ + { 110, 132, 10, }, /* 11 Mpix/s */ + { 120, 120, 10, }, /* 12 Mpix/s */ + { 133, 133, 10, }, /* 13.3 Mpix/s */ + }; + + l4_rate = rfbi.l4_khz / 1000; + dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000; + + for (i = 0; i < ARRAY_SIZE(ftab); i++) { + /* Use a window instead of an exact match, to account + * for different DPLL multiplier / divider pairs. + */ + if (abs(ftab[i].l4_clk - l4_rate) < 3 && + abs(ftab[i].dss1_clk - dss1_rate) < 3) { + min_l4_ticks = ftab[i].min_l4_ticks; + break; + } + } + if (i == ARRAY_SIZE(ftab)) { + /* Can't be sure, return anyway the maximum not + * rate-limited. This might cause a problem only for the + * tearing synchronisation. + */ + dev_err(rfbi.fbdev->dev, + "can't determine maximum RFBI transfer rate\n"); + return rfbi.l4_khz * 1000; + } + return rfbi.l4_khz * 1000 / min_l4_ticks; +} +#else +static int rfbi_get_max_tx_rate(void) +{ + return rfbi.l4_khz * 1000; +} +#endif + + +static int rfbi_convert_timings(struct extif_timings *t) +{ + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; +} + +static int rfbi_setup_tearsync(unsigned pin_cnt, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int extif_div) +{ + int hs, vs; + int min; + u32 l; + + if (pin_cnt != 1 && pin_cnt != 2) + return -EINVAL; + + hs = ps_to_rfbi_ticks(hs_pulse_time, 1); + vs = ps_to_rfbi_ticks(vs_pulse_time, 1); + if (hs < 2) + return -EDOM; + if (pin_cnt == 2) + min = 2; + else + min = 4; + if (vs < min) + return -EDOM; + if (vs == hs) + return -EINVAL; + rfbi.tearsync_pin_cnt = pin_cnt; + dev_dbg(rfbi.fbdev->dev, + "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n", + pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); + rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); + + l = rfbi_read_reg(RFBI_CONFIG0); + if (hs_pol_inv) + l &= ~(1 << 21); + else + l |= 1 << 21; + if (vs_pol_inv) + l &= ~(1 << 20); + else + l |= 1 << 20; + rfbi_enable_clocks(0); + + return 0; +} + +static int rfbi_enable_tearsync(int enable, unsigned line) +{ + u32 l; + + dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n", + enable, line, rfbi.tearsync_mode); + if (line > (1 << 11) - 1) + return -EINVAL; + + rfbi_enable_clocks(1); + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(0x3 << 2); + if (enable) { + rfbi.tearsync_mode = rfbi.tearsync_pin_cnt; + l |= rfbi.tearsync_mode << 2; + } else + rfbi.tearsync_mode = 0; + rfbi_write_reg(RFBI_CONFIG0, l); + rfbi_write_reg(RFBI_LINE_NUMBER, line); + rfbi_enable_clocks(0); + + return 0; +} + +static void rfbi_write_command(const void *buf, unsigned int len) +{ + rfbi_enable_clocks(1); + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + } + rfbi_enable_clocks(0); +} + +static void rfbi_read_data(void *buf, unsigned int len) +{ + rfbi_enable_clocks(1); + if (rfbi.bits_per_cycle == 16) { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + } else { + u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + } + rfbi_enable_clocks(0); +} + +static void rfbi_write_data(const void *buf, unsigned int len) +{ + rfbi_enable_clocks(1); + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + } + rfbi_enable_clocks(0); +} + +static void rfbi_transfer_area(int width, int height, + void (callback)(void * data), void *data) +{ + u32 w; + + BUG_ON(callback == NULL); + + rfbi_enable_clocks(1); + omap_dispc_set_lcd_size(width, height); + + rfbi.lcdc_callback = callback; + rfbi.lcdc_callback_data = data; + + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); + + w = rfbi_read_reg(RFBI_CONTROL); + w |= 1; /* enable */ + if (!rfbi.tearsync_mode) + w |= 1 << 4; /* internal trigger, reset by HW */ + rfbi_write_reg(RFBI_CONTROL, w); + + omap_dispc_enable_lcd_out(1); +} + +static inline void _stop_transfer(void) +{ + u32 w; + + w = rfbi_read_reg(RFBI_CONTROL); + rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0)); + rfbi_enable_clocks(0); +} + +static void rfbi_dma_callback(void *data) +{ + _stop_transfer(); + rfbi.lcdc_callback(rfbi.lcdc_callback_data); +} + +static void rfbi_set_bits_per_cycle(int bpc) +{ + u32 l; + + rfbi_enable_clocks(1); + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(0x03 << 0); + + switch (bpc) { + case 8: + break; + case 16: + l |= 3; + break; + default: + BUG(); + } + rfbi_write_reg(RFBI_CONFIG0, l); + rfbi.bits_per_cycle = bpc; + rfbi_enable_clocks(0); +} + +static int rfbi_init(struct omapfb_device *fbdev) +{ + u32 l; + int r; + + rfbi.fbdev = fbdev; + rfbi.base = io_p2v(RFBI_BASE); + + if ((r = rfbi_get_clocks()) < 0) + return r; + rfbi_enable_clocks(1); + + rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000; + + /* Reset */ + rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1); + while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0))); + + l = rfbi_read_reg(RFBI_SYSCONFIG); + /* Enable autoidle and smart-idle */ + l |= (1 << 0) | (2 << 3); + rfbi_write_reg(RFBI_SYSCONFIG, l); + + /* 16-bit interface, ITE trigger mode, 16-bit data */ + l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7); + l |= (0 << 9) | (1 << 20) | (1 << 21); + rfbi_write_reg(RFBI_CONFIG0, l); + + rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); + + l = rfbi_read_reg(RFBI_CONTROL); + /* Select CS0, clear bypass mode */ + l = (0x01 << 2); + rfbi_write_reg(RFBI_CONTROL, l); + + if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) { + dev_err(fbdev->dev, "can't get DISPC irq\n"); + rfbi_enable_clocks(0); + return r; + } + + l = rfbi_read_reg(RFBI_REVISION); + pr_info("omapfb: RFBI version %d.%d initialized\n", + (l >> 4) & 0x0f, l & 0x0f); + + rfbi_enable_clocks(0); + + return 0; +} + +static void rfbi_cleanup(void) +{ + omap_dispc_free_irq(); + rfbi_put_clocks(); +} + +const struct lcd_ctrl_extif omap2_ext_if = { + .init = rfbi_init, + .cleanup = rfbi_cleanup, + .get_clk_info = rfbi_get_clk_info, + .get_max_tx_rate = rfbi_get_max_tx_rate, + .set_bits_per_cycle = rfbi_set_bits_per_cycle, + .convert_timings = rfbi_convert_timings, + .set_timings = rfbi_set_timings, + .write_command = rfbi_write_command, + .read_data = rfbi_read_data, + .write_data = rfbi_write_data, + .transfer_area = rfbi_transfer_area, + .setup_tearsync = rfbi_setup_tearsync, + .enable_tearsync = rfbi_enable_tearsync, + + .max_transmit_size = (u32) ~0, +}; + diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c new file mode 100644 index 000000000000..81dbcf53cf0e --- /dev/null +++ b/drivers/video/omap/sossi.c @@ -0,0 +1,686 @@ +/* + * OMAP1 Special OptimiSed Screen Interface support + * + * Copyright (C) 2004-2005 Nokia Corporation + * Author: Juha Yrjölä <juha.yrjola@nokia.com> + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/clk.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <asm/arch/dma.h> +#include <asm/arch/omapfb.h> + +#include "lcdc.h" + +#define MODULE_NAME "omapfb-sossi" + +#define OMAP_SOSSI_BASE 0xfffbac00 +#define SOSSI_ID_REG 0x00 +#define SOSSI_INIT1_REG 0x04 +#define SOSSI_INIT2_REG 0x08 +#define SOSSI_INIT3_REG 0x0c +#define SOSSI_FIFO_REG 0x10 +#define SOSSI_REOTABLE_REG 0x14 +#define SOSSI_TEARING_REG 0x18 +#define SOSSI_INIT1B_REG 0x1c +#define SOSSI_FIFOB_REG 0x20 + +#define DMA_GSCR 0xfffedc04 +#define DMA_LCD_CCR 0xfffee3c2 +#define DMA_LCD_CTRL 0xfffee3c4 +#define DMA_LCD_LCH_CTRL 0xfffee3ea + +#define CONF_SOSSI_RESET_R (1 << 23) + +#define RD_ACCESS 0 +#define WR_ACCESS 1 + +#define SOSSI_MAX_XMIT_BYTES (512 * 1024) + +static struct { + void __iomem *base; + struct clk *fck; + unsigned long fck_hz; + spinlock_t lock; + int bus_pick_count; + int bus_pick_width; + int tearsync_mode; + int tearsync_line; + void (*lcdc_callback)(void *data); + void *lcdc_callback_data; + int vsync_dma_pending; + /* timing for read and write access */ + int clk_div; + u8 clk_tw0[2]; + u8 clk_tw1[2]; + /* + * if last_access is the same as current we don't have to change + * the timings + */ + int last_access; + + struct omapfb_device *fbdev; +} sossi; + +static inline u32 sossi_read_reg(int reg) +{ + return readl(sossi.base + reg); +} + +static inline u16 sossi_read_reg16(int reg) +{ + return readw(sossi.base + reg); +} + +static inline u8 sossi_read_reg8(int reg) +{ + return readb(sossi.base + reg); +} + +static inline void sossi_write_reg(int reg, u32 value) +{ + writel(value, sossi.base + reg); +} + +static inline void sossi_write_reg16(int reg, u16 value) +{ + writew(value, sossi.base + reg); +} + +static inline void sossi_write_reg8(int reg, u8 value) +{ + writeb(value, sossi.base + reg); +} + +static void sossi_set_bits(int reg, u32 bits) +{ + sossi_write_reg(reg, sossi_read_reg(reg) | bits); +} + +static void sossi_clear_bits(int reg, u32 bits) +{ + sossi_write_reg(reg, sossi_read_reg(reg) & ~bits); +} + +#define HZ_TO_PS(x) (1000000000 / (x / 1000)) + +static u32 ps_to_sossi_ticks(u32 ps, int div) +{ + u32 clk_period = HZ_TO_PS(sossi.fck_hz) * div; + return (clk_period + ps - 1) / clk_period; +} + +static int calc_rd_timings(struct extif_timings *t) +{ + u32 tw0, tw1; + int reon, reoff, recyc, actim; + int div = t->clk_div; + + /* + * Make sure that after conversion it still holds that: + * reoff > reon, recyc >= reoff, actim > reon + */ + reon = ps_to_sossi_ticks(t->re_on_time, div); + /* reon will be exactly one sossi tick */ + if (reon > 1) + return -1; + + reoff = ps_to_sossi_ticks(t->re_off_time, div); + + if (reoff <= reon) + reoff = reon + 1; + + tw0 = reoff - reon; + if (tw0 > 0x10) + return -1; + + recyc = ps_to_sossi_ticks(t->re_cycle_time, div); + if (recyc <= reoff) + recyc = reoff + 1; + + tw1 = recyc - tw0; + /* values less then 3 result in the SOSSI block resetting itself */ + if (tw1 < 3) + tw1 = 3; + if (tw1 > 0x40) + return -1; + + actim = ps_to_sossi_ticks(t->access_time, div); + if (actim < reoff) + actim++; + /* + * access time (data hold time) will be exactly one sossi + * tick + */ + if (actim - reoff > 1) + return -1; + + t->tim[0] = tw0 - 1; + t->tim[1] = tw1 - 1; + + return 0; +} + +static int calc_wr_timings(struct extif_timings *t) +{ + u32 tw0, tw1; + int weon, weoff, wecyc; + int div = t->clk_div; + + /* + * Make sure that after conversion it still holds that: + * weoff > weon, wecyc >= weoff + */ + weon = ps_to_sossi_ticks(t->we_on_time, div); + /* weon will be exactly one sossi tick */ + if (weon > 1) + return -1; + + weoff = ps_to_sossi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + tw0 = weoff - weon; + if (tw0 > 0x10) + return -1; + + wecyc = ps_to_sossi_ticks(t->we_cycle_time, div); + if (wecyc <= weoff) + wecyc = weoff + 1; + + tw1 = wecyc - tw0; + /* values less then 3 result in the SOSSI block resetting itself */ + if (tw1 < 3) + tw1 = 3; + if (tw1 > 0x40) + return -1; + + t->tim[2] = tw0 - 1; + t->tim[3] = tw1 - 1; + + return 0; +} + +static void _set_timing(int div, int tw0, int tw1) +{ + u32 l; + +#ifdef VERBOSE + dev_dbg(sossi.fbdev->dev, "Using TW0 = %d, TW1 = %d, div = %d\n", + tw0 + 1, tw1 + 1, div); +#endif + + clk_set_rate(sossi.fck, sossi.fck_hz / div); + clk_enable(sossi.fck); + l = sossi_read_reg(SOSSI_INIT1_REG); + l &= ~((0x0f << 20) | (0x3f << 24)); + l |= (tw0 << 20) | (tw1 << 24); + sossi_write_reg(SOSSI_INIT1_REG, l); + clk_disable(sossi.fck); +} + +static void _set_bits_per_cycle(int bus_pick_count, int bus_pick_width) +{ + u32 l; + + l = sossi_read_reg(SOSSI_INIT3_REG); + l &= ~0x3ff; + l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f); + sossi_write_reg(SOSSI_INIT3_REG, l); +} + +static void _set_tearsync_mode(int mode, unsigned line) +{ + u32 l; + + l = sossi_read_reg(SOSSI_TEARING_REG); + l &= ~(((1 << 11) - 1) << 15); + l |= line << 15; + l &= ~(0x3 << 26); + l |= mode << 26; + sossi_write_reg(SOSSI_TEARING_REG, l); + if (mode) + sossi_set_bits(SOSSI_INIT2_REG, 1 << 6); /* TE logic */ + else + sossi_clear_bits(SOSSI_INIT2_REG, 1 << 6); +} + +static inline void set_timing(int access) +{ + if (access != sossi.last_access) { + sossi.last_access = access; + _set_timing(sossi.clk_div, + sossi.clk_tw0[access], sossi.clk_tw1[access]); + } +} + +static void sossi_start_transfer(void) +{ + /* WE */ + sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4); + /* CS active low */ + sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30); +} + +static void sossi_stop_transfer(void) +{ + /* WE */ + sossi_set_bits(SOSSI_INIT2_REG, 1 << 4); + /* CS active low */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 30); +} + +static void wait_end_of_write(void) +{ + /* Before reading we must check if some writings are going on */ + while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3))); +} + +static void send_data(const void *data, unsigned int len) +{ + while (len >= 4) { + sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data); + len -= 4; + data += 4; + } + while (len >= 2) { + sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data); + len -= 2; + data += 2; + } + while (len) { + sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data); + len--; + data++; + } +} + +static void set_cycles(unsigned int len) +{ + unsigned long nr_cycles = len / (sossi.bus_pick_width / 8); + + BUG_ON((nr_cycles - 1) & ~0x3ffff); + + sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff); + sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff); +} + +static int sossi_convert_timings(struct extif_timings *t) +{ + int r = 0; + int div = t->clk_div; + + t->converted = 0; + + if (div <= 0 || div > 8) + return -1; + + /* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */ + if ((r = calc_rd_timings(t)) < 0) + return r; + + if ((r = calc_wr_timings(t)) < 0) + return r; + + t->tim[4] = div; + + t->converted = 1; + + return 0; +} + +static void sossi_set_timings(const struct extif_timings *t) +{ + BUG_ON(!t->converted); + + sossi.clk_tw0[RD_ACCESS] = t->tim[0]; + sossi.clk_tw1[RD_ACCESS] = t->tim[1]; + + sossi.clk_tw0[WR_ACCESS] = t->tim[2]; + sossi.clk_tw1[WR_ACCESS] = t->tim[3]; + + sossi.clk_div = t->tim[4]; +} + +static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = HZ_TO_PS(sossi.fck_hz); + *max_clk_div = 8; +} + +static void sossi_set_bits_per_cycle(int bpc) +{ + int bus_pick_count, bus_pick_width; + + /* + * We set explicitly the the bus_pick_count as well, although + * with remapping/reordering disabled it will be calculated by HW + * as (32 / bus_pick_width). + */ + switch (bpc) { + case 8: + bus_pick_count = 4; + bus_pick_width = 8; + break; + case 16: + bus_pick_count = 2; + bus_pick_width = 16; + break; + default: + BUG(); + return; + } + sossi.bus_pick_width = bus_pick_width; + sossi.bus_pick_count = bus_pick_count; +} + +static int sossi_setup_tearsync(unsigned pin_cnt, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int div) +{ + int hs, vs; + u32 l; + + if (pin_cnt != 1 || div < 1 || div > 8) + return -EINVAL; + + hs = ps_to_sossi_ticks(hs_pulse_time, div); + vs = ps_to_sossi_ticks(vs_pulse_time, div); + if (vs < 8 || vs <= hs || vs >= (1 << 12)) + return -EDOM; + vs /= 8; + vs--; + if (hs > 8) + hs = 8; + if (hs) + hs--; + + dev_dbg(sossi.fbdev->dev, + "setup_tearsync: hs %d vs %d hs_inv %d vs_inv %d\n", + hs, vs, hs_pol_inv, vs_pol_inv); + + clk_enable(sossi.fck); + l = sossi_read_reg(SOSSI_TEARING_REG); + l &= ~((1 << 15) - 1); + l |= vs << 3; + l |= hs; + if (hs_pol_inv) + l |= 1 << 29; + else + l &= ~(1 << 29); + if (vs_pol_inv) + l |= 1 << 28; + else + l &= ~(1 << 28); + sossi_write_reg(SOSSI_TEARING_REG, l); + clk_disable(sossi.fck); + + return 0; +} + +static int sossi_enable_tearsync(int enable, unsigned line) +{ + int mode; + + dev_dbg(sossi.fbdev->dev, "tearsync %d line %d\n", enable, line); + if (line >= 1 << 11) + return -EINVAL; + if (enable) { + if (line) + mode = 2; /* HS or VS */ + else + mode = 3; /* VS only */ + } else + mode = 0; + sossi.tearsync_line = line; + sossi.tearsync_mode = mode; + + return 0; +} + +static void sossi_write_command(const void *data, unsigned int len) +{ + clk_enable(sossi.fck); + set_timing(WR_ACCESS); + _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); + /* CMD#/DATA */ + sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); + clk_disable(sossi.fck); +} + +static void sossi_write_data(const void *data, unsigned int len) +{ + clk_enable(sossi.fck); + set_timing(WR_ACCESS); + _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); + clk_disable(sossi.fck); +} + +static void sossi_transfer_area(int width, int height, + void (callback)(void *data), void *data) +{ + BUG_ON(callback == NULL); + + sossi.lcdc_callback = callback; + sossi.lcdc_callback_data = data; + + clk_enable(sossi.fck); + set_timing(WR_ACCESS); + _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); + _set_tearsync_mode(sossi.tearsync_mode, sossi.tearsync_line); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(width * height * sossi.bus_pick_width / 8); + + sossi_start_transfer(); + if (sossi.tearsync_mode) { + /* + * Wait for the sync signal and start the transfer only + * then. We can't seem to be able to use HW sync DMA for + * this since LCD DMA shows huge latencies, as if it + * would ignore some of the DMA requests from SoSSI. + */ + unsigned long flags; + + spin_lock_irqsave(&sossi.lock, flags); + sossi.vsync_dma_pending++; + spin_unlock_irqrestore(&sossi.lock, flags); + } else + /* Just start the transfer right away. */ + omap_enable_lcd_dma(); +} + +static void sossi_dma_callback(void *data) +{ + omap_stop_lcd_dma(); + sossi_stop_transfer(); + clk_disable(sossi.fck); + sossi.lcdc_callback(sossi.lcdc_callback_data); +} + +static void sossi_read_data(void *data, unsigned int len) +{ + clk_enable(sossi.fck); + set_timing(RD_ACCESS); + _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + while (len >= 4) { + *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG); + len -= 4; + data += 4; + } + while (len >= 2) { + *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG); + len -= 2; + data += 2; + } + while (len) { + *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG); + len--; + data++; + } + sossi_stop_transfer(); + clk_disable(sossi.fck); +} + +static irqreturn_t sossi_match_irq(int irq, void *data) +{ + unsigned long flags; + + spin_lock_irqsave(&sossi.lock, flags); + if (sossi.vsync_dma_pending) { + sossi.vsync_dma_pending--; + omap_enable_lcd_dma(); + } + spin_unlock_irqrestore(&sossi.lock, flags); + return IRQ_HANDLED; +} + +static int sossi_init(struct omapfb_device *fbdev) +{ + u32 l, k; + struct clk *fck; + struct clk *dpll1out_ck; + int r; + + sossi.base = (void __iomem *)IO_ADDRESS(OMAP_SOSSI_BASE); + sossi.fbdev = fbdev; + spin_lock_init(&sossi.lock); + + dpll1out_ck = clk_get(fbdev->dev, "ck_dpll1out"); + if (IS_ERR(dpll1out_ck)) { + dev_err(fbdev->dev, "can't get DPLL1OUT clock\n"); + return PTR_ERR(dpll1out_ck); + } + /* + * We need the parent clock rate, which we might divide further + * depending on the timing requirements of the controller. See + * _set_timings. + */ + sossi.fck_hz = clk_get_rate(dpll1out_ck); + clk_put(dpll1out_ck); + + fck = clk_get(fbdev->dev, "ck_sossi"); + if (IS_ERR(fck)) { + dev_err(fbdev->dev, "can't get SoSSI functional clock\n"); + return PTR_ERR(fck); + } + sossi.fck = fck; + + /* Reset and enable the SoSSI module */ + l = omap_readl(MOD_CONF_CTRL_1); + l |= CONF_SOSSI_RESET_R; + omap_writel(l, MOD_CONF_CTRL_1); + l &= ~CONF_SOSSI_RESET_R; + omap_writel(l, MOD_CONF_CTRL_1); + + clk_enable(sossi.fck); + l = omap_readl(ARM_IDLECT2); + l &= ~(1 << 8); /* DMACK_REQ */ + omap_writel(l, ARM_IDLECT2); + + l = sossi_read_reg(SOSSI_INIT2_REG); + /* Enable and reset the SoSSI block */ + l |= (1 << 0) | (1 << 1); + sossi_write_reg(SOSSI_INIT2_REG, l); + /* Take SoSSI out of reset */ + l &= ~(1 << 1); + sossi_write_reg(SOSSI_INIT2_REG, l); + + sossi_write_reg(SOSSI_ID_REG, 0); + l = sossi_read_reg(SOSSI_ID_REG); + k = sossi_read_reg(SOSSI_ID_REG); + + if (l != 0x55555555 || k != 0xaaaaaaaa) { + dev_err(fbdev->dev, + "invalid SoSSI sync pattern: %08x, %08x\n", l, k); + r = -ENODEV; + goto err; + } + + if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) { + dev_err(fbdev->dev, "can't get LCDC IRQ\n"); + r = -ENODEV; + goto err; + } + + l = sossi_read_reg(SOSSI_ID_REG); /* Component code */ + l = sossi_read_reg(SOSSI_ID_REG); + dev_info(fbdev->dev, "SoSSI version %d.%d initialized\n", + l >> 16, l & 0xffff); + + l = sossi_read_reg(SOSSI_INIT1_REG); + l |= (1 << 19); /* DMA_MODE */ + l &= ~(1 << 31); /* REORDERING */ + sossi_write_reg(SOSSI_INIT1_REG, l); + + if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq, + IRQT_FALLING, + "sossi_match", sossi.fbdev->dev)) < 0) { + dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n"); + goto err; + } + + clk_disable(sossi.fck); + return 0; + +err: + clk_disable(sossi.fck); + clk_put(sossi.fck); + return r; +} + +static void sossi_cleanup(void) +{ + omap_lcdc_free_dma_callback(); + clk_put(sossi.fck); +} + +struct lcd_ctrl_extif omap1_ext_if = { + .init = sossi_init, + .cleanup = sossi_cleanup, + .get_clk_info = sossi_get_clk_info, + .convert_timings = sossi_convert_timings, + .set_timings = sossi_set_timings, + .set_bits_per_cycle = sossi_set_bits_per_cycle, + .setup_tearsync = sossi_setup_tearsync, + .enable_tearsync = sossi_enable_tearsync, + .write_command = sossi_write_command, + .read_data = sossi_read_data, + .write_data = sossi_write_data, + .transfer_area = sossi_transfer_area, + + .max_transmit_size = SOSSI_MAX_XMIT_BYTES, +}; + diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index e64f8b5d0056..8503e733a172 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -52,7 +52,7 @@ struct fb_info_platinum { struct { __u8 red, green, blue; } palette[256]; - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; volatile struct cmap_regs __iomem *cmap_regs; unsigned long cmap_regs_phys; diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 0a04483aa3e0..10c0cc6e93fc 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -24,7 +24,7 @@ * License. See the file COPYING in the main directory of this archive for * more details. * - * + * */ #include <linux/module.h> @@ -58,7 +58,7 @@ #endif /* - * Driver data + * Driver data */ static char *mode __devinitdata = NULL; @@ -82,12 +82,12 @@ struct pm2fb_par { pm2type_t type; /* Board type */ unsigned char __iomem *v_regs;/* virtual address of p_regs */ - u32 memclock; /* memclock */ + u32 memclock; /* memclock */ u32 video; /* video flags before blanking */ u32 mem_config; /* MemConfig reg at probe */ u32 mem_control; /* MemControl reg at probe */ u32 boot_address; /* BootAddress reg at probe */ - u32 palette[16]; + u32 palette[16]; }; /* @@ -95,12 +95,12 @@ struct pm2fb_par * if we don't use modedb. */ static struct fb_fix_screeninfo pm2fb_fix __devinitdata = { - .id = "", + .id = "", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, .xpanstep = 1, .ypanstep = 1, - .ywrapstep = 0, + .ywrapstep = 0, .accel = FB_ACCEL_3DLABS_PERMEDIA2, }; @@ -109,26 +109,26 @@ static struct fb_fix_screeninfo pm2fb_fix __devinitdata = { */ static struct fb_var_screeninfo pm2fb_var __devinitdata = { /* "640x480, 8 bpp @ 60 Hz */ - .xres = 640, - .yres = 480, - .xres_virtual = 640, - .yres_virtual = 480, - .bits_per_pixel =8, - .red = {0, 8, 0}, - .blue = {0, 8, 0}, - .green = {0, 8, 0}, - .activate = FB_ACTIVATE_NOW, - .height = -1, - .width = -1, - .accel_flags = 0, - .pixclock = 39721, - .left_margin = 40, - .right_margin = 24, - .upper_margin = 32, - .lower_margin = 11, - .hsync_len = 96, - .vsync_len = 2, - .vmode = FB_VMODE_NONINTERLACED + .xres = 640, + .yres = 480, + .xres_virtual = 640, + .yres_virtual = 480, + .bits_per_pixel = 8, + .red = {0, 8, 0}, + .blue = {0, 8, 0}, + .green = {0, 8, 0}, + .activate = FB_ACTIVATE_NOW, + .height = -1, + .width = -1, + .accel_flags = 0, + .pixclock = 39721, + .left_margin = 40, + .right_margin = 24, + .upper_margin = 32, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED }; /* @@ -166,7 +166,7 @@ static inline u32 pm2_RDAC_RD(struct pm2fb_par* p, s32 idx) pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); index = PM2VR_RD_INDEXED_DATA; break; - } + } mb(); return pm2_RD(p, index); } @@ -182,7 +182,7 @@ static inline void pm2_RDAC_WR(struct pm2fb_par* p, s32 idx, u32 v) pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); index = PM2VR_RD_INDEXED_DATA; break; - } + } wmb(); pm2_WR(p, index, v); wmb(); @@ -197,7 +197,7 @@ static inline void pm2v_RDAC_WR(struct pm2fb_par* p, s32 idx, u32 v) } #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT -#define WAIT_FIFO(p,a) +#define WAIT_FIFO(p, a) #else static inline void WAIT_FIFO(struct pm2fb_par* p, u32 a) { @@ -209,7 +209,7 @@ static inline void WAIT_FIFO(struct pm2fb_par* p, u32 a) /* * partial products for the supported horizontal resolutions. */ -#define PACKPP(p0,p1,p2) (((p2) << 6) | ((p1) << 3) | (p0)) +#define PACKPP(p0, p1, p2) (((p2) << 6) | ((p1) << 3) | (p0)) static const struct { u16 width; u16 pp; @@ -357,7 +357,7 @@ static void reset_card(struct pm2fb_par* p) static void reset_config(struct pm2fb_par* p) { WAIT_FIFO(p, 52); - pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG)& + pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG) & ~(PM2F_VGA_ENABLE|PM2F_VGA_FIXED)); pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L)); @@ -367,7 +367,7 @@ static void reset_config(struct pm2fb_par* p) pm2_WR(p, PM2R_RASTERIZER_MODE, 0); pm2_WR(p, PM2R_DELTA_MODE, PM2F_DELTA_ORDER_RGB); pm2_WR(p, PM2R_LB_READ_FORMAT, 0); - pm2_WR(p, PM2R_LB_WRITE_FORMAT, 0); + pm2_WR(p, PM2R_LB_WRITE_FORMAT, 0); pm2_WR(p, PM2R_LB_READ_MODE, 0); pm2_WR(p, PM2R_LB_SOURCE_OFFSET, 0); pm2_WR(p, PM2R_FB_SOURCE_OFFSET, 0); @@ -535,7 +535,7 @@ static void set_video(struct pm2fb_par* p, u32 video) { vsync = video; DPRINTK("video = 0x%x\n", video); - + /* * The hardware cursor needs +vsync to recognise vert retrace. * We may not be using the hardware cursor, but the X Glint @@ -574,9 +574,9 @@ static void set_video(struct pm2fb_par* p, u32 video) { */ /** - * pm2fb_check_var - Optional function. Validates a var passed in. - * @var: frame buffer variable screen structure - * @info: frame buffer structure that represents a single frame buffer + * pm2fb_check_var - Optional function. Validates a var passed in. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer * * Checks to see if the hardware supports the state requested by * var passed in. @@ -615,23 +615,23 @@ static int pm2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */ lpitch = var->xres * ((var->bits_per_pixel + 7)>>3); - + if (var->xres < 320 || var->xres > 1600) { DPRINTK("width not supported: %u\n", var->xres); return -EINVAL; } - + if (var->yres < 200 || var->yres > 1200) { DPRINTK("height not supported: %u\n", var->yres); return -EINVAL; } - + if (lpitch * var->yres_virtual > info->fix.smem_len) { DPRINTK("no memory for screen (%ux%ux%u)\n", var->xres, var->yres_virtual, var->bits_per_pixel); return -EINVAL; } - + if (PICOS2KHZ(var->pixclock) > PM2_MAX_PIXCLOCK) { DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock)); return -EINVAL; @@ -672,17 +672,17 @@ static int pm2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) break; } var->height = var->width = -1; - + var->accel_flags = 0; /* Can't mmap if this is on */ - + DPRINTK("Checking graphics mode at %dx%d depth %d\n", var->xres, var->yres, var->bits_per_pixel); return 0; } /** - * pm2fb_set_par - Alters the hardware state. - * @info: frame buffer structure that represents a single frame buffer + * pm2fb_set_par - Alters the hardware state. + * @info: frame buffer structure that represents a single frame buffer * * Using the fb_var_screeninfo in fb_info we set the resolution of the * this particular framebuffer. @@ -709,7 +709,7 @@ static int pm2fb_set_par(struct fb_info *info) clear_palette(par); if ( par->memclock ) set_memclock(par, par->memclock); - + width = (info->var.xres_virtual + 7) & ~7; height = info->var.yres_virtual; depth = (info->var.bits_per_pixel + 7) & ~7; @@ -722,7 +722,7 @@ static int pm2fb_set_par(struct fb_info *info) DPRINTK("pixclock too high (%uKHz)\n", pixclock); return -EINVAL; } - + hsstart = to3264(info->var.right_margin, depth, data64); hsend = hsstart + to3264(info->var.hsync_len, depth, data64); hbend = hsend + to3264(info->var.left_margin, depth, data64); @@ -737,7 +737,7 @@ static int pm2fb_set_par(struct fb_info *info) base = to3264(info->var.yoffset * xres + info->var.xoffset, depth, 1); if (data64) video |= PM2F_DATA_64_ENABLE; - + if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) { if (lowhsync) { DPRINTK("ignoring +hsync, using -hsync.\n"); @@ -778,9 +778,9 @@ static int pm2fb_set_par(struct fb_info *info) WAIT_FIFO(par, 1); pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0); } - + set_aperture(par, depth); - + mb(); WAIT_FIFO(par, 19); pm2_RDAC_WR(par, PM2I_RD_COLOR_KEY_CONTROL, @@ -847,22 +847,22 @@ static int pm2fb_set_par(struct fb_info *info) set_pixclock(par, pixclock); DPRINTK("Setting graphics mode at %dx%d depth %d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); - return 0; + return 0; } /** - * pm2fb_setcolreg - Sets a color register. - * @regno: boolean, 0 copy local, 1 get_user() function - * @red: frame buffer colormap structure - * @green: The green value which can be up to 16 bits wide + * pm2fb_setcolreg - Sets a color register. + * @regno: boolean, 0 copy local, 1 get_user() function + * @red: frame buffer colormap structure + * @green: The green value which can be up to 16 bits wide * @blue: The blue value which can be up to 16 bits wide. - * @transp: If supported the alpha value which can be up to 16 bits wide. - * @info: frame buffer info structure - * - * Set a single color register. The values supplied have a 16 bit - * magnitude which needs to be scaled in this function for the hardware. + * @transp: If supported the alpha value which can be up to 16 bits wide. + * @info: frame buffer info structure + * + * Set a single color register. The values supplied have a 16 bit + * magnitude which needs to be scaled in this function for the hardware. * Pretty much a direct lift from tdfxfb.c. - * + * * Returns negative errno on error, or zero on success. */ static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -906,7 +906,7 @@ static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, * (blue << blue.offset) | (transp << transp.offset) * RAMDAC does not exist */ -#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) +#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF -(val)) >> 16) switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: case FB_VISUAL_PSEUDOCOLOR: @@ -916,9 +916,9 @@ static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, transp = CNVT_TOHW(transp, info->var.transp.length); break; case FB_VISUAL_DIRECTCOLOR: - /* example here assumes 8 bit DAC. Might be different - * for your hardware */ - red = CNVT_TOHW(red, 8); + /* example here assumes 8 bit DAC. Might be different + * for your hardware */ + red = CNVT_TOHW(red, 8); green = CNVT_TOHW(green, 8); blue = CNVT_TOHW(blue, 8); /* hey, there is bug in transp handling... */ @@ -940,11 +940,11 @@ static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, switch (info->var.bits_per_pixel) { case 8: - break; - case 16: + break; + case 16: case 24: - case 32: - par->palette[regno] = v; + case 32: + par->palette[regno] = v; break; } return 0; @@ -956,15 +956,15 @@ static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, } /** - * pm2fb_pan_display - Pans the display. - * @var: frame buffer variable screen structure - * @info: frame buffer structure that represents a single frame buffer + * pm2fb_pan_display - Pans the display. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer * * Pan (or wrap, depending on the `vmode' field) the display using the - * `xoffset' and `yoffset' fields of the `var' structure. - * If the values don't fit, return -EINVAL. + * `xoffset' and `yoffset' fields of the `var' structure. + * If the values don't fit, return -EINVAL. * - * Returns negative errno on error, or zero on success. + * Returns negative errno on error, or zero on success. * */ static int pm2fb_pan_display(struct fb_var_screeninfo *var, @@ -980,24 +980,24 @@ static int pm2fb_pan_display(struct fb_var_screeninfo *var, depth = (depth > 32) ? 32 : depth; base = to3264(var->yoffset * xres + var->xoffset, depth, 1); WAIT_FIFO(p, 1); - pm2_WR(p, PM2R_SCREEN_BASE, base); + pm2_WR(p, PM2R_SCREEN_BASE, base); return 0; } /** - * pm2fb_blank - Blanks the display. - * @blank_mode: the blank mode we want. - * @info: frame buffer structure that represents a single frame buffer + * pm2fb_blank - Blanks the display. + * @blank_mode: the blank mode we want. + * @info: frame buffer structure that represents a single frame buffer * - * Blank the screen if blank_mode != 0, else unblank. Return 0 if - * blanking succeeded, != 0 if un-/blanking failed due to e.g. a - * video mode which doesn't support it. Implements VESA suspend - * and powerdown modes on hardware that supports disabling hsync/vsync: - * blank_mode == 2: suspend vsync - * blank_mode == 3: suspend hsync - * blank_mode == 4: powerdown + * Blank the screen if blank_mode != 0, else unblank. Return 0 if + * blanking succeeded, != 0 if un-/blanking failed due to e.g. a + * video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown * - * Returns negative errno on error, or zero on success. + * Returns negative errno on error, or zero on success. * */ static int pm2fb_blank(int blank_mode, struct fb_info *info) @@ -1071,7 +1071,7 @@ static void pm2fb_block_op(struct fb_info* info, int copy, pm2_WR(par, PM2R_RECTANGLE_ORIGIN, (y << 16) | x); pm2_WR(par, PM2R_RECTANGLE_SIZE, (h << 16) | w); wmb(); - pm2_WR(par, PM2R_RENDER,PM2F_RENDER_RECTANGLE | + pm2_WR(par, PM2R_RENDER, PM2F_RENDER_RECTANGLE | (x<xsrc ? PM2F_INCREASE_X : 0) | (y<ysrc ? PM2F_INCREASE_Y : 0) | (copy ? 0 : PM2F_RENDER_FASTFILL)); @@ -1234,7 +1234,7 @@ static int __devinit pm2fb_probe(struct pci_dev *pdev, DPRINTK("Adjusting register base for big-endian.\n"); #endif DPRINTK("Register base at 0x%lx\n", pm2fb_fix.mmio_start); - + /* Registers - request region and map it. */ if ( !request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len, "pm2fb regbase") ) { @@ -1317,17 +1317,17 @@ static int __devinit pm2fb_probe(struct pci_dev *pdev, } info->fbops = &pm2fb_ops; - info->fix = pm2fb_fix; + info->fix = pm2fb_fix; info->pseudo_palette = default_par->palette; info->flags = FBINFO_DEFAULT | - FBINFO_HWACCEL_YPAN | - FBINFO_HWACCEL_COPYAREA | - FBINFO_HWACCEL_FILLRECT; + FBINFO_HWACCEL_YPAN | + FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_FILLRECT; if (!mode) mode = "640x480@60"; - - err = fb_find_mode(&info->var, info, mode, NULL, 0, NULL, 8); + + err = fb_find_mode(&info->var, info, mode, NULL, 0, NULL, 8); if (!err || err == 4) info->var = pm2fb_var; @@ -1348,8 +1348,8 @@ static int __devinit pm2fb_probe(struct pci_dev *pdev, return 0; err_exit_all: - fb_dealloc_cmap(&info->cmap); - err_exit_both: + fb_dealloc_cmap(&info->cmap); + err_exit_both: iounmap(info->screen_base); release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len); err_exit_mmio: @@ -1374,7 +1374,7 @@ static void __devexit pm2fb_remove(struct pci_dev *pdev) struct pm2fb_par *par = info->par; unregister_framebuffer(info); - + iounmap(info->screen_base); release_mem_region(fix->smem_start, fix->smem_len); iounmap(par->v_regs); @@ -1402,9 +1402,9 @@ static struct pci_device_id pm2fb_id_table[] = { static struct pci_driver pm2fb_driver = { .name = "pm2fb", - .id_table = pm2fb_id_table, - .probe = pm2fb_probe, - .remove = __devexit_p(pm2fb_remove), + .id_table = pm2fb_id_table, + .probe = pm2fb_probe, + .remove = __devexit_p(pm2fb_remove), }; MODULE_DEVICE_TABLE(pci, pm2fb_id_table); @@ -1423,7 +1423,7 @@ static int __init pm2fb_setup(char *options) if (!options || !*options) return 0; - while ((this_opt = strsep(&options, ",")) != NULL) { + while ((this_opt = strsep(&options, ",")) != NULL) { if (!*this_opt) continue; if(!strcmp(this_opt, "lowhsync")) { diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index b52e883f0a52..5b3f54c0918e 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -77,7 +77,7 @@ static struct fb_fix_screeninfo pm3fb_fix __devinitdata = { .xpanstep = 1, .ypanstep = 1, .ywrapstep = 0, - .accel = FB_ACCEL_NONE, + .accel = FB_ACCEL_3DLABS_PERMEDIA3, }; /* @@ -185,6 +185,238 @@ static inline int pm3fb_shift_bpp(unsigned bpp, int v) return 0; } +/* acceleration */ +static int pm3fb_sync(struct fb_info *info) +{ + struct pm3_par *par = info->par; + + PM3_WAIT(par, 2); + PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync); + PM3_WRITE_REG(par, PM3Sync, 0); + mb(); + do { + while ((PM3_READ_REG(par, PM3OutFIFOWords)) == 0); + rmb(); + } while ((PM3_READ_REG(par, PM3OutputFifo)) != PM3Sync_Tag); + + return 0; +} + +static void pm3fb_init_engine(struct fb_info *info) +{ + struct pm3_par *par = info->par; + const u32 width = (info->var.xres_virtual + 7) & ~7; + + PM3_WAIT(par, 50); + PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync); + PM3_WRITE_REG(par, PM3StatisticMode, 0x0); + PM3_WRITE_REG(par, PM3DeltaMode, 0x0); + PM3_WRITE_REG(par, PM3RasterizerMode, 0x0); + PM3_WRITE_REG(par, PM3ScissorMode, 0x0); + PM3_WRITE_REG(par, PM3LineStippleMode, 0x0); + PM3_WRITE_REG(par, PM3AreaStippleMode, 0x0); + PM3_WRITE_REG(par, PM3GIDMode, 0x0); + PM3_WRITE_REG(par, PM3DepthMode, 0x0); + PM3_WRITE_REG(par, PM3StencilMode, 0x0); + PM3_WRITE_REG(par, PM3StencilData, 0x0); + PM3_WRITE_REG(par, PM3ColorDDAMode, 0x0); + PM3_WRITE_REG(par, PM3TextureCoordMode, 0x0); + PM3_WRITE_REG(par, PM3TextureIndexMode0, 0x0); + PM3_WRITE_REG(par, PM3TextureIndexMode1, 0x0); + PM3_WRITE_REG(par, PM3TextureReadMode, 0x0); + PM3_WRITE_REG(par, PM3LUTMode, 0x0); + PM3_WRITE_REG(par, PM3TextureFilterMode, 0x0); + PM3_WRITE_REG(par, PM3TextureCompositeMode, 0x0); + PM3_WRITE_REG(par, PM3TextureApplicationMode, 0x0); + PM3_WRITE_REG(par, PM3TextureCompositeColorMode1, 0x0); + PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode1, 0x0); + PM3_WRITE_REG(par, PM3TextureCompositeColorMode0, 0x0); + PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode0, 0x0); + PM3_WRITE_REG(par, PM3FogMode, 0x0); + PM3_WRITE_REG(par, PM3ChromaTestMode, 0x0); + PM3_WRITE_REG(par, PM3AlphaTestMode, 0x0); + PM3_WRITE_REG(par, PM3AntialiasMode, 0x0); + PM3_WRITE_REG(par, PM3YUVMode, 0x0); + PM3_WRITE_REG(par, PM3AlphaBlendColorMode, 0x0); + PM3_WRITE_REG(par, PM3AlphaBlendAlphaMode, 0x0); + PM3_WRITE_REG(par, PM3DitherMode, 0x0); + PM3_WRITE_REG(par, PM3LogicalOpMode, 0x0); + PM3_WRITE_REG(par, PM3RouterMode, 0x0); + PM3_WRITE_REG(par, PM3Window, 0x0); + + PM3_WRITE_REG(par, PM3Config2D, 0x0); + + PM3_WRITE_REG(par, PM3SpanColorMask, 0xffffffff); + + PM3_WRITE_REG(par, PM3XBias, 0x0); + PM3_WRITE_REG(par, PM3YBias, 0x0); + PM3_WRITE_REG(par, PM3DeltaControl, 0x0); + + PM3_WRITE_REG(par, PM3BitMaskPattern, 0xffffffff); + + PM3_WRITE_REG(par, PM3FBDestReadEnables, + PM3FBDestReadEnables_E(0xff) | + PM3FBDestReadEnables_R(0xff) | + PM3FBDestReadEnables_ReferenceAlpha(0xff)); + PM3_WRITE_REG(par, PM3FBDestReadBufferAddr0, 0x0); + PM3_WRITE_REG(par, PM3FBDestReadBufferOffset0, 0x0); + PM3_WRITE_REG(par, PM3FBDestReadBufferWidth0, + PM3FBDestReadBufferWidth_Width(width)); + + PM3_WRITE_REG(par, PM3FBDestReadMode, + PM3FBDestReadMode_ReadEnable | + PM3FBDestReadMode_Enable0); + PM3_WRITE_REG(par, PM3FBSourceReadBufferAddr, 0x0); + PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset, 0x0); + PM3_WRITE_REG(par, PM3FBSourceReadBufferWidth, + PM3FBSourceReadBufferWidth_Width(width)); + PM3_WRITE_REG(par, PM3FBSourceReadMode, + PM3FBSourceReadMode_Blocking | + PM3FBSourceReadMode_ReadEnable); + + PM3_WAIT(par, 2); + { + unsigned long rm = 1; + switch (info->var.bits_per_pixel) { + case 8: + PM3_WRITE_REG(par, PM3PixelSize, + PM3PixelSize_GLOBAL_8BIT); + break; + case 16: + PM3_WRITE_REG(par, PM3PixelSize, + PM3PixelSize_GLOBAL_16BIT); + break; + case 32: + PM3_WRITE_REG(par, PM3PixelSize, + PM3PixelSize_GLOBAL_32BIT); + break; + default: + DPRINTK(1, "Unsupported depth %d\n", + info->var.bits_per_pixel); + break; + } + PM3_WRITE_REG(par, PM3RasterizerMode, rm); + } + + PM3_WAIT(par, 20); + PM3_WRITE_REG(par, PM3FBSoftwareWriteMask, 0xffffffff); + PM3_WRITE_REG(par, PM3FBHardwareWriteMask, 0xffffffff); + PM3_WRITE_REG(par, PM3FBWriteMode, + PM3FBWriteMode_WriteEnable | + PM3FBWriteMode_OpaqueSpan | + PM3FBWriteMode_Enable0); + PM3_WRITE_REG(par, PM3FBWriteBufferAddr0, 0x0); + PM3_WRITE_REG(par, PM3FBWriteBufferOffset0, 0x0); + PM3_WRITE_REG(par, PM3FBWriteBufferWidth0, + PM3FBWriteBufferWidth_Width(width)); + + PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 0x0); + { + /* size in lines of FB */ + unsigned long sofb = info->screen_size / + info->fix.line_length; + if (sofb > 4095) + PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 4095); + else + PM3_WRITE_REG(par, PM3SizeOfFramebuffer, sofb); + + switch (info->var.bits_per_pixel) { + case 8: + PM3_WRITE_REG(par, PM3DitherMode, + (1 << 10) | (2 << 3)); + break; + case 16: + PM3_WRITE_REG(par, PM3DitherMode, + (1 << 10) | (1 << 3)); + break; + case 32: + PM3_WRITE_REG(par, PM3DitherMode, + (1 << 10) | (0 << 3)); + break; + default: + DPRINTK(1, "Unsupported depth %d\n", + info->current_par->depth); + break; + } + } + + PM3_WRITE_REG(par, PM3dXDom, 0x0); + PM3_WRITE_REG(par, PM3dXSub, 0x0); + PM3_WRITE_REG(par, PM3dY, (1 << 16)); + PM3_WRITE_REG(par, PM3StartXDom, 0x0); + PM3_WRITE_REG(par, PM3StartXSub, 0x0); + PM3_WRITE_REG(par, PM3StartY, 0x0); + PM3_WRITE_REG(par, PM3Count, 0x0); + +/* Disable LocalBuffer. better safe than sorry */ + PM3_WRITE_REG(par, PM3LBDestReadMode, 0x0); + PM3_WRITE_REG(par, PM3LBDestReadEnables, 0x0); + PM3_WRITE_REG(par, PM3LBSourceReadMode, 0x0); + PM3_WRITE_REG(par, PM3LBWriteMode, 0x0); + + pm3fb_sync(info); +} + +static void pm3fb_fillrect (struct fb_info *info, + const struct fb_fillrect *region) +{ + struct pm3_par *par = info->par; + struct fb_fillrect modded; + int vxres, vyres; + u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? + ((u32*)info->pseudo_palette)[region->color] : region->color; + + if (info->state != FBINFO_STATE_RUNNING) + return; + if ((info->flags & FBINFO_HWACCEL_DISABLED) || + region->rop != ROP_COPY ) { + cfb_fillrect(info, region); + return; + } + + vxres = info->var.xres_virtual; + vyres = info->var.yres_virtual; + + memcpy(&modded, region, sizeof(struct fb_fillrect)); + + if(!modded.width || !modded.height || + modded.dx >= vxres || modded.dy >= vyres) + return; + + if(modded.dx + modded.width > vxres) + modded.width = vxres - modded.dx; + if(modded.dy + modded.height > vyres) + modded.height = vyres - modded.dy; + + if(info->var.bits_per_pixel == 8) + color |= color << 8; + if(info->var.bits_per_pixel <= 16) + color |= color << 16; + + PM3_WAIT(par, 4); + + PM3_WRITE_REG(par, PM3Config2D, + PM3Config2D_UseConstantSource | + PM3Config2D_ForegroundROPEnable | + (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ + PM3Config2D_FBWriteEnable); + + PM3_WRITE_REG(par, PM3ForegroundColor, color); + + PM3_WRITE_REG(par, PM3RectanglePosition, + (PM3RectanglePosition_XOffset(modded.dx)) | + (PM3RectanglePosition_YOffset(modded.dy))); + + PM3_WRITE_REG(par, PM3Render2D, + PM3Render2D_XPositive | + PM3Render2D_YPositive | + PM3Render2D_Operation_Normal | + PM3Render2D_SpanOperation | + (PM3Render2D_Width(modded.width)) | + (PM3Render2D_Height(modded.height))); +} +/* end of acceleration functions */ + /* write the mode to registers */ static void pm3fb_write_mode(struct fb_info *info) { @@ -380,8 +612,6 @@ static void pm3fb_write_mode(struct fb_info *info) /* * hardware independent functions */ -int pm3fb_init(void); - static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { u32 lpitch; @@ -528,6 +758,7 @@ static int pm3fb_set_par(struct fb_info *info) pm3fb_clear_colormap(par, 0, 0, 0); PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, PM3RD_CursorMode_CURSOR_DISABLE); + pm3fb_init_engine(info); pm3fb_write_mode(info); return 0; } @@ -675,10 +906,11 @@ static struct fb_ops pm3fb_ops = { .fb_set_par = pm3fb_set_par, .fb_setcolreg = pm3fb_setcolreg, .fb_pan_display = pm3fb_pan_display, - .fb_fillrect = cfb_fillrect, + .fb_fillrect = pm3fb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_blank = pm3fb_blank, + .fb_sync = pm3fb_sync, }; /* ------------------------------------------------------------------------- */ @@ -847,7 +1079,8 @@ static int __devinit pm3fb_probe(struct pci_dev *dev, info->fix = pm3fb_fix; info->pseudo_palette = par->palette; - info->flags = FBINFO_DEFAULT;/* | FBINFO_HWACCEL_YPAN;*/ + info->flags = FBINFO_DEFAULT | + FBINFO_HWACCEL_FILLRECT;/* | FBINFO_HWACCEL_YPAN;*/ /* * This should give a reasonable default video mode. The following is @@ -935,35 +1168,12 @@ static struct pci_driver pm3fb_driver = { MODULE_DEVICE_TABLE(pci, pm3fb_id_table); -#ifndef MODULE - /* - * Setup - */ - -/* - * Only necessary if your driver takes special options, - * otherwise we fall back on the generic fb_setup(). - */ -static int __init pm3fb_setup(char *options) +static int __init pm3fb_init(void) { - /* Parse user speficied options (`video=pm3fb:') */ - return 0; -} -#endif /* MODULE */ - -int __init pm3fb_init(void) -{ - /* - * For kernel boot options (in 'video=pm3fb:<options>' format) - */ #ifndef MODULE - char *option = NULL; - - if (fb_get_options("pm3fb", &option)) + if (fb_get_options("pm3fb", NULL)) return -ENODEV; - pm3fb_setup(option); #endif - return pci_register_driver(&pm3fb_driver); } diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 9cf92ba5d6e3..646ec823c168 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -27,7 +27,6 @@ #include <linux/vmalloc.h> #include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/console.h> #include <linux/ioctl.h> #include <linux/notifier.h> @@ -46,6 +45,9 @@ #include <asm/ps3fb.h> #include <asm/ps3.h> + +#define DEVICE_NAME "ps3fb" + #ifdef PS3FB_DEBUG #define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args) #else @@ -126,7 +128,6 @@ struct gpu_driver_info { struct ps3fb_priv { unsigned int irq_no; - void *dev; u64 context_handle, memory_handle; void *xdr_ea; @@ -171,7 +172,7 @@ static const struct ps3fb_res_table ps3fb_res[] = { { 0, 0, 0, 0 , 0} }; /* default resolution */ -#define GPU_RES_INDEX 0 /* 720 x 480 */ +#define GPU_RES_INDEX 0 /* 720 x 480 */ static const struct fb_videomode ps3fb_modedb[] = { /* 60 Hz broadcast modes (modes "1" to "5") */ @@ -298,10 +299,9 @@ static const struct fb_videomode ps3fb_modedb[] = { #define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET) static int ps3fb_mode; -module_param(ps3fb_mode, bool, 0); - -static char *mode_option __initdata; +module_param(ps3fb_mode, int, 0); +static char *mode_option __devinitdata; static int ps3fb_get_res_table(u32 xres, u32 yres) { @@ -681,15 +681,15 @@ int ps3fb_wait_for_vsync(u32 crtc) EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync); -void ps3fb_flip_ctl(int on) +void ps3fb_flip_ctl(int on, void *data) { + struct ps3fb_priv *priv = data; if (on) - atomic_dec_if_positive(&ps3fb.ext_flip); + atomic_dec_if_positive(&priv->ext_flip); else - atomic_inc(&ps3fb.ext_flip); + atomic_inc(&priv->ext_flip); } -EXPORT_SYMBOL_GPL(ps3fb_flip_ctl); /* * ioctl @@ -812,6 +812,7 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, static int ps3fbd(void *arg) { + set_freezable(); while (!kthread_should_stop()) { try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); @@ -851,37 +852,9 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -#ifndef MODULE -static int __init ps3fb_setup(char *options) -{ - char *this_opt; - int mode = 0; - - if (!options || !*options) - return 0; /* no options */ - - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "mode:", 5)) - mode = simple_strtoul(this_opt + 5, NULL, 0); - else - mode_option = this_opt; - } - return mode; -} -#endif /* MODULE */ - - /* - * Initialisation - */ -static void ps3fb_platform_release(struct device *device) -{ - /* This is called when the reference count goes to zero. */ -} - -static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) +static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, + struct ps3_system_bus_device *dev) { int error; @@ -897,7 +870,6 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) return -EINVAL; } - ps3fb.dev = dev; error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, &ps3fb.irq_no); if (error) { @@ -907,7 +879,7 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) } error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, - "ps3fb vsync", ps3fb.dev); + DEVICE_NAME, dev); if (error) { printk(KERN_ERR "%s: request_irq failed %d\n", __func__, error); @@ -966,16 +938,45 @@ static struct fb_ops ps3fb_ops = { }; static struct fb_fix_screeninfo ps3fb_fix __initdata = { - .id = "PS3 FB", + .id = DEVICE_NAME, .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, }; -static int __init ps3fb_probe(struct platform_device *dev) +static int ps3fb_set_sync(void) +{ + int status; + +#ifdef HEAD_A + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC " + "failed: %d\n", __func__, status); + return -1; + } +#endif +#ifdef HEAD_B + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE " + "failed: %d\n", __func__, status); + return -1; + } +#endif + return 0; +} + +static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) { struct fb_info *info; int retval = -ENOMEM; + u32 xres, yres; u64 ddr_lpar = 0; u64 lpar_dma_control = 0; u64 lpar_driver_info = 0; @@ -986,6 +987,30 @@ static int __init ps3fb_probe(struct platform_device *dev) unsigned long offset; struct task_struct *task; + status = ps3_open_hv_device(dev); + if (status) { + printk(KERN_ERR "%s: ps3_open_hv_device failed\n", __func__); + goto err; + } + + if (!ps3fb_mode) + ps3fb_mode = ps3av_get_mode(); + DPRINTK("ps3av_mode:%d\n", ps3fb_mode); + + if (ps3fb_mode > 0 && + !ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) { + ps3fb.res_index = ps3fb_get_res_table(xres, yres); + DPRINTK("res_index:%d\n", ps3fb.res_index); + } else + ps3fb.res_index = GPU_RES_INDEX; + + atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ + atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ + init_waitqueue_head(&ps3fb.wait_vsync); + ps3fb.num_frames = 1; + + ps3fb_set_sync(); + /* get gpu context handle */ status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0, &ps3fb.memory_handle, &ddr_lpar); @@ -1029,7 +1054,7 @@ static int __init ps3fb_probe(struct platform_device *dev) * leakage into userspace */ memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); - info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); + info = framebuffer_alloc(sizeof(u32) * 16, &dev->core); if (!info) goto err_free_irq; @@ -1042,7 +1067,7 @@ static int __init ps3fb_probe(struct platform_device *dev) info->fix.smem_len = ps3fb_videomemory.size - offset; info->pseudo_palette = info->par; info->par = NULL; - info->flags = FBINFO_FLAG_DEFAULT; + info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST; retval = fb_alloc_cmap(&info->cmap, 256, 0); if (retval < 0) @@ -1061,19 +1086,20 @@ static int __init ps3fb_probe(struct platform_device *dev) if (retval < 0) goto err_fb_dealloc; - platform_set_drvdata(dev, info); + dev->core.driver_data = info; printk(KERN_INFO "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n", info->node, ps3fb_videomemory.size >> 10); - task = kthread_run(ps3fbd, info, "ps3fbd"); + task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { retval = PTR_ERR(task); goto err_unregister_framebuffer; } ps3fb.task = task; + ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb); return 0; @@ -1084,7 +1110,7 @@ err_fb_dealloc: err_framebuffer_release: framebuffer_release(info); err_free_irq: - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1096,26 +1122,30 @@ err: return retval; } -static void ps3fb_shutdown(struct platform_device *dev) +static int ps3fb_shutdown(struct ps3_system_bus_device *dev) { - ps3fb_flip_ctl(0); /* flip off */ + int status; + struct fb_info *info = dev->core.driver_data; + + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + + ps3fb_flip_ctl(0, &ps3fb); /* flip off */ ps3fb.dinfo->irq.mask = 0; - free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_irq_plug_destroy(ps3fb.irq_no); - iounmap((u8 __iomem *)ps3fb.dinfo); -} -void ps3fb_cleanup(void) -{ - int status; + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + ps3av_register_flip_ctl(NULL, NULL); if (ps3fb.task) { struct task_struct *task = ps3fb.task; ps3fb.task = NULL; kthread_stop(task); } if (ps3fb.irq_no) { - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); } iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1128,134 +1158,69 @@ void ps3fb_cleanup(void) if (status) DPRINTK("lv1_gpu_memory_free failed: %d\n", status); - ps3av_dev_close(); -} + ps3_close_hv_device(dev); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); -EXPORT_SYMBOL_GPL(ps3fb_cleanup); - -static int ps3fb_remove(struct platform_device *dev) -{ - struct fb_info *info = platform_get_drvdata(dev); - - if (info) { - unregister_framebuffer(info); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - ps3fb_cleanup(); return 0; } -static struct platform_driver ps3fb_driver = { - .probe = ps3fb_probe, - .remove = ps3fb_remove, - .shutdown = ps3fb_shutdown, - .driver = { .name = "ps3fb" } -}; - -static struct platform_device ps3fb_device = { - .name = "ps3fb", - .id = 0, - .dev = { .release = ps3fb_platform_release } +static struct ps3_system_bus_driver ps3fb_driver = { + .match_id = PS3_MATCH_ID_GRAPHICS, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3fb_probe, + .remove = ps3fb_shutdown, + .shutdown = ps3fb_shutdown, }; -int ps3fb_set_sync(void) +static int __init ps3fb_setup(void) { - int status; + char *options; -#ifdef HEAD_A - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: %d\n", - __func__, status); - return -1; - } -#endif -#ifdef HEAD_B - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n", - __func__, status); - return -1; - } -#endif +#ifdef MODULE return 0; -} - -EXPORT_SYMBOL_GPL(ps3fb_set_sync); - -static int __init ps3fb_init(void) -{ - int error; -#ifndef MODULE - int mode; - char *option = NULL; - - if (fb_get_options("ps3fb", &option)) - goto err; #endif - if (!ps3fb_videomemory.address) - goto err; - - error = ps3av_dev_open(); - if (error) { - printk(KERN_ERR "%s: ps3av_dev_open failed\n", __func__); - goto err; - } + if (fb_get_options(DEVICE_NAME, &options)) + return -ENXIO; - ps3fb_mode = ps3av_get_mode(); - DPRINTK("ps3av_mode:%d\n", ps3fb_mode); -#ifndef MODULE - mode = ps3fb_setup(option); /* check boot option */ - if (mode) - ps3fb_mode = mode; -#endif - if (ps3fb_mode > 0) { - u32 xres, yres; - ps3av_video_mode2res(ps3fb_mode, &xres, &yres); - ps3fb.res_index = ps3fb_get_res_table(xres, yres); - DPRINTK("res_index:%d\n", ps3fb.res_index); - } else - ps3fb.res_index = GPU_RES_INDEX; + if (!options || !*options) + return 0; - atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ - atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ - init_waitqueue_head(&ps3fb.wait_vsync); - ps3fb.num_frames = 1; + while (1) { + char *this_opt = strsep(&options, ","); - error = platform_driver_register(&ps3fb_driver); - if (!error) { - error = platform_device_register(&ps3fb_device); - if (error) - platform_driver_unregister(&ps3fb_driver); + if (!this_opt) + break; + if (!*this_opt) + continue; + if (!strncmp(this_opt, "mode:", 5)) + ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0); + else + mode_option = this_opt; } + return 0; +} - ps3fb_set_sync(); - - return error; +static int __init ps3fb_init(void) +{ + if (!ps3fb_videomemory.address || ps3fb_setup()) + return -ENXIO; -err: - return -ENXIO; + return ps3_system_bus_driver_register(&ps3fb_driver); } -module_init(ps3fb_init); - -#ifdef MODULE static void __exit ps3fb_exit(void) { - platform_device_unregister(&ps3fb_device); - platform_driver_unregister(&ps3fb_driver); + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + ps3_system_bus_driver_unregister(&ps3fb_driver); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); } +module_init(ps3fb_init); module_exit(ps3fb_exit); MODULE_LICENSE("GPL"); -#endif /* MODULE */ +MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS); diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c index df2909ae704c..f9300266044d 100644 --- a/drivers/video/pvr2fb.c +++ b/drivers/video/pvr2fb.c @@ -115,11 +115,11 @@ enum { VO_PAL, VO_NTSC, VO_VGA }; enum { PAL_ARGB1555, PAL_RGB565, PAL_ARGB4444, PAL_ARGB8888 }; struct pvr2_params { unsigned int val; char *name; }; -static struct pvr2_params cables[] __initdata = { +static struct pvr2_params cables[] __devinitdata = { { CT_VGA, "VGA" }, { CT_RGB, "RGB" }, { CT_COMPOSITE, "COMPOSITE" }, }; -static struct pvr2_params outputs[] __initdata = { +static struct pvr2_params outputs[] __devinitdata = { { VO_PAL, "PAL" }, { VO_NTSC, "NTSC" }, { VO_VGA, "VGA" }, }; @@ -147,16 +147,16 @@ static struct pvr2fb_par { static struct fb_info *fb_info; -static struct fb_fix_screeninfo pvr2_fix __initdata = { +static struct fb_fix_screeninfo pvr2_fix __devinitdata = { .id = "NEC PowerVR2", - .type = FB_TYPE_PACKED_PIXELS, - .visual = FB_VISUAL_TRUECOLOR, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, .ypanstep = 1, .ywrapstep = 1, - .accel = FB_ACCEL_NONE, + .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo pvr2_var __initdata = { +static struct fb_var_screeninfo pvr2_var __devinitdata = { .xres = 640, .yres = 480, .xres_virtual = 640, @@ -195,10 +195,6 @@ static unsigned int shdma = PVR2_CASCADE_CHAN; static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS; #endif -/* Interface used by the world */ - -int pvr2fb_setup(char*); - static int pvr2fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info); static int pvr2fb_blank(int blank, struct fb_info *info); @@ -227,12 +223,12 @@ static struct fb_ops pvr2fb_ops = { #ifdef CONFIG_SH_DMA .fb_write = pvr2fb_write, #endif - .fb_fillrect = cfb_fillrect, + .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, }; -static struct fb_videomode pvr2_modedb[] __initdata = { +static struct fb_videomode pvr2_modedb[] __devinitdata = { /* * Broadcast video modes (PAL and NTSC). I'm unfamiliar with * PAL-M and PAL-N, but from what I've read both modes parallel PAL and @@ -252,7 +248,7 @@ static struct fb_videomode pvr2_modedb[] __initdata = { /* 640x480 @ 60hz (VGA) */ "vga_640x480", 60, 640, 480, VGA_CLK, 38, 33, 0, 18, 146, 26, 0, FB_VMODE_YWRAP - }, + }, }; #define NUM_TOTAL_MODES ARRAY_SIZE(pvr2_modedb) @@ -262,7 +258,7 @@ static struct fb_videomode pvr2_modedb[] __initdata = { #define DEFMODE_VGA 2 static int defmode = DEFMODE_NTSC; -static char *mode_option __initdata = NULL; +static char *mode_option __devinitdata = NULL; static inline void pvr2fb_set_pal_type(unsigned int type) { @@ -293,7 +289,7 @@ static void set_color_bitfields(struct fb_var_screeninfo *var) { switch (var->bits_per_pixel) { case 16: /* RGB 565 */ - pvr2fb_set_pal_type(PAL_RGB565); + pvr2fb_set_pal_type(PAL_RGB565); var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; @@ -306,7 +302,7 @@ static void set_color_bitfields(struct fb_var_screeninfo *var) var->transp.offset = 0; var->transp.length = 0; break; case 32: /* ARGB 8888 */ - pvr2fb_set_pal_type(PAL_ARGB8888); + pvr2fb_set_pal_type(PAL_ARGB8888); var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; @@ -337,24 +333,25 @@ static int pvr2fb_setcolreg(unsigned int regno, unsigned int red, ((blue & 0xf800) >> 11); pvr2fb_set_pal_entry(par, regno, tmp); - ((u16*)(info->pseudo_palette))[regno] = tmp; break; case 24: /* RGB 888 */ red >>= 8; green >>= 8; blue >>= 8; - ((u32*)(info->pseudo_palette))[regno] = (red << 16) | (green << 8) | blue; + tmp = (red << 16) | (green << 8) | blue; break; case 32: /* ARGB 8888 */ red >>= 8; green >>= 8; blue >>= 8; tmp = (transp << 24) | (red << 16) | (green << 8) | blue; pvr2fb_set_pal_entry(par, regno, tmp); - ((u32*)(info->pseudo_palette))[regno] = tmp; break; default: pr_debug("Invalid bit depth %d?!?\n", info->var.bits_per_pixel); return 1; } + if (regno < 16) + ((u32*)(info->pseudo_palette))[regno] = tmp; + return 0; } @@ -379,13 +376,13 @@ static int pvr2fb_set_par(struct fb_info *info) var->vmode &= FB_VMODE_MASK; if (var->vmode & FB_VMODE_INTERLACED && video_output != VO_VGA) par->is_interlaced = 1; - /* + /* * XXX: Need to be more creative with this (i.e. allow doublecan for * PAL/NTSC output). */ if (var->vmode & FB_VMODE_DOUBLE && video_output == VO_VGA) par->is_doublescan = 1; - + par->hsync_total = var->left_margin + var->xres + var->right_margin + var->hsync_len; par->vsync_total = var->upper_margin + var->yres + var->lower_margin + @@ -408,7 +405,7 @@ static int pvr2fb_set_par(struct fb_info *info) } else { /* VGA mode */ /* XXX: What else needs to be checked? */ - /* + /* * XXX: We have a little freedom in VGA modes, what ranges * should be here (i.e. hsync/vsync totals, etc.)? */ @@ -419,8 +416,8 @@ static int pvr2fb_set_par(struct fb_info *info) /* Calculate the remainding offsets */ par->diwstart_h = par->borderstart_h + var->left_margin; par->diwstart_v = par->borderstart_v + var->upper_margin; - par->borderstop_h = par->diwstart_h + var->xres + - var->right_margin; + par->borderstop_h = par->diwstart_h + var->xres + + var->right_margin; par->borderstop_v = par->diwstart_v + var->yres + var->lower_margin; @@ -465,12 +462,12 @@ static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) set_color_bitfields(var); if (var->vmode & FB_VMODE_YWRAP) { - if (var->xoffset || var->yoffset < 0 || + if (var->xoffset || var->yoffset < 0 || var->yoffset >= var->yres_virtual) { var->xoffset = var->yoffset = 0; } else { if (var->xoffset > var->xres_virtual - var->xres || - var->yoffset > var->yres_virtual - var->yres || + var->yoffset > var->yres_virtual - var->yres || var->xoffset < 0 || var->yoffset < 0) var->xoffset = var->yoffset = 0; } @@ -478,7 +475,7 @@ static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xoffset = var->yoffset = 0; } - /* + /* * XXX: Need to be more creative with this (i.e. allow doublecan for * PAL/NTSC output). */ @@ -507,7 +504,7 @@ static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->vsync_len = par->borderstop_v + (par->vsync_total - par->borderstop_v); } - + hsync_total = var->left_margin + var->xres + var->right_margin + var->hsync_len; vtotal = var->upper_margin + var->yres + var->lower_margin + @@ -531,7 +528,7 @@ static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) } } } - + /* Check memory sizes */ line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); if (line_length * var->yres_virtual > info->fix.smem_len) @@ -552,7 +549,7 @@ static void pvr2_update_display(struct fb_info *info) DISP_DIWADDRS); } -/* +/* * Initialize the video mode. Currently, the 16bpp and 24bpp modes aren't * very stable. It's probably due to the fact that a lot of the 2D video * registers are still undocumented. @@ -592,18 +589,18 @@ static void pvr2_init_display(struct fb_info *info) /* display window start position */ fb_writel(par->diwstart_h, DISP_DIWHSTRT); fb_writel((par->diwstart_v << 16) | par->diwstart_v, DISP_DIWVSTRT); - + /* misc. settings */ fb_writel((0x16 << 16) | par->is_lowres, DISP_DIWCONF); /* clock doubler (for VGA), scan doubler, display enable */ - fb_writel(((video_output == VO_VGA) << 23) | + fb_writel(((video_output == VO_VGA) << 23) | (par->is_doublescan << 1) | 1, DISP_DIWMODE); /* bits per pixel */ fb_writel(fb_readl(DISP_DIWMODE) | (--bytesperpixel << 2), DISP_DIWMODE); - /* video enable, color sync, interlace, + /* video enable, color sync, interlace, * hsync and vsync polarity (currently unused) */ fb_writel(0x100 | ((par->is_interlaced /*|4*/) << 4), DISP_SYNCCONF); } @@ -657,7 +654,7 @@ static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id) static int pvr2_init_cable(void) { if (cable_type < 0) { - fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, + fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, PCTRA); cable_type = (fb_readw(PDTRA) >> 8) & 3; } @@ -687,7 +684,7 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, pages = kmalloc(nr_pages * sizeof(struct page *), GFP_KERNEL); if (!pages) return -ENOMEM; - + down_read(¤t->mm->mmap_sem); ret = get_user_pages(current, current->mm, (unsigned long)buf, nr_pages, WRITE, 0, pages, NULL); @@ -700,7 +697,7 @@ static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, } dma_configure_channel(shdma, 0x12c1); - + dst = (unsigned long)fb_info->screen_base + *ppos; start = (unsigned long)page_address(pages[0]); end = (unsigned long)page_address(pages[nr_pages]); @@ -744,7 +741,7 @@ out_unmap: kfree(pages); return ret; -} +} #endif /* CONFIG_SH_DMA */ /** @@ -765,21 +762,21 @@ out_unmap: * in for flexibility anyways. Who knows, maybe someone has tv-out on a * PCI-based version of these things ;-) */ -static int __init pvr2fb_common_init(void) +static int __devinit pvr2fb_common_init(void) { struct pvr2fb_par *par = currentpar; unsigned long modememused, rev; fb_info->screen_base = ioremap_nocache(pvr2_fix.smem_start, pvr2_fix.smem_len); - + if (!fb_info->screen_base) { printk(KERN_ERR "pvr2fb: Failed to remap smem space\n"); goto out_err; } par->mmio_base = (unsigned long)ioremap_nocache(pvr2_fix.mmio_start, - pvr2_fix.mmio_len); + pvr2_fix.mmio_len); if (!par->mmio_base) { printk(KERN_ERR "pvr2fb: Failed to remap mmio space\n"); goto out_err; @@ -820,7 +817,7 @@ static int __init pvr2fb_common_init(void) printk("fb%d: %s (rev %ld.%ld) frame buffer device, using %ldk/%ldk of video memory\n", fb_info->node, fb_info->fix.id, (rev >> 4) & 0x0f, rev & 0x0f, modememused >> 10, (unsigned long)(fb_info->fix.smem_len >> 10)); - printk("fb%d: Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n", + printk("fb%d: Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n", fb_info->node, fb_info->var.xres, fb_info->var.yres, fb_info->var.bits_per_pixel, get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel), @@ -878,8 +875,8 @@ static int __init pvr2fb_dc_init(void) video_output = VO_NTSC; } } - - /* + + /* * Nothing exciting about the DC PVR2 .. only a measly 8MiB. */ pvr2_fix.smem_start = 0xa5000000; /* RAM starts here */ @@ -903,7 +900,7 @@ static int __init pvr2fb_dc_init(void) return pvr2fb_common_init(); } -static void pvr2fb_dc_exit(void) +static void __exit pvr2fb_dc_exit(void) { if (fb_info->screen_base) { iounmap(fb_info->screen_base); @@ -987,13 +984,13 @@ static int __init pvr2fb_pci_init(void) return pci_register_driver(&pvr2fb_pci_driver); } -static void pvr2fb_pci_exit(void) +static void __exit pvr2fb_pci_exit(void) { pci_unregister_driver(&pvr2fb_pci_driver); } #endif /* CONFIG_PCI */ -static int __init pvr2_get_param(const struct pvr2_params *p, const char *s, +static int __devinit pvr2_get_param(const struct pvr2_params *p, const char *s, int val, int size) { int i; @@ -1021,7 +1018,7 @@ static int __init pvr2_get_param(const struct pvr2_params *p, const char *s, */ #ifndef MODULE -int __init pvr2fb_setup(char *options) +static int __init pvr2fb_setup(char *options) { char *this_opt; char cable_arg[80]; @@ -1061,7 +1058,7 @@ static struct pvr2_board { int (*init)(void); void (*exit)(void); char name[16]; -} board_list[] = { +} board_driver[] = { #ifdef CONFIG_SH_DREAMCAST { pvr2fb_dc_init, pvr2fb_dc_exit, "Sega DC PVR2" }, #endif @@ -1071,7 +1068,7 @@ static struct pvr2_board { { 0, }, }; -int __init pvr2fb_init(void) +static int __init pvr2fb_init(void) { int i, ret = -ENODEV; int size; @@ -1085,18 +1082,17 @@ int __init pvr2fb_init(void) #endif size = sizeof(struct fb_info) + sizeof(struct pvr2fb_par) + 16 * sizeof(u32); - fb_info = kmalloc(size, GFP_KERNEL); + fb_info = kzalloc(size, GFP_KERNEL); if (!fb_info) { printk(KERN_ERR "Failed to allocate memory for fb_info\n"); return -ENOMEM; } - memset(fb_info, 0, size); currentpar = (struct pvr2fb_par *)(fb_info + 1); - for (i = 0; i < ARRAY_SIZE(board_list); i++) { - struct pvr2_board *pvr_board = board_list + i; + for (i = 0; i < ARRAY_SIZE(board_driver); i++) { + struct pvr2_board *pvr_board = board_driver + i; if (!pvr_board->init) continue; @@ -1118,13 +1114,13 @@ static void __exit pvr2fb_exit(void) { int i; - for (i = 0; i < ARRAY_SIZE(board_list); i++) { - struct pvr2_board *pvr_board = board_list + i; + for (i = 0; i < ARRAY_SIZE(board_driver); i++) { + struct pvr2_board *pvr_board = board_driver + i; if (pvr_board->exit) pvr_board->exit(); } - + #ifdef CONFIG_SH_STORE_QUEUES sq_unmap(pvr2fb_map); #endif @@ -1139,4 +1135,3 @@ module_exit(pvr2fb_exit); MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); MODULE_DESCRIPTION("Framebuffer driver for NEC PowerVR 2 based graphics boards"); MODULE_LICENSE("GPL"); - diff --git a/drivers/video/q40fb.c b/drivers/video/q40fb.c index 48536c3e58a4..4beac1df617b 100644 --- a/drivers/video/q40fb.c +++ b/drivers/video/q40fb.c @@ -95,7 +95,7 @@ static int __init q40fb_probe(struct platform_device *dev) /* mapped in q40/config.c */ q40fb_fix.smem_start = Q40_PHYS_SCREEN_ADDR; - info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev); + info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); if (!info) return -ENOMEM; diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index d251174d8baa..5c47968e7f21 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -2146,7 +2146,7 @@ static void __devexit rivafb_remove(struct pci_dev *pd) * ------------------------------------------------------------------------- */ #ifndef MODULE -static int __init rivafb_setup(char *options) +static int __devinit rivafb_setup(char *options) { char *this_opt; diff --git a/drivers/video/riva/riva_hw.c b/drivers/video/riva/riva_hw.c index 70bfd78eca81..13307703a9f0 100644 --- a/drivers/video/riva/riva_hw.c +++ b/drivers/video/riva/riva_hw.c @@ -1223,6 +1223,8 @@ static int CalcVClock } } } + + /* non-zero: M/N/P/clock values assigned. zero: error (not set) */ return (DeltaOld != 0xFFFFFFFF); } /* @@ -1240,7 +1242,10 @@ int CalcStateExt int dotClock ) { - int pixelDepth, VClk, m, n, p; + int pixelDepth; + int uninitialized_var(VClk),uninitialized_var(m), + uninitialized_var(n), uninitialized_var(p); + /* * Save mode parameters. */ diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 3d7507ad55f6..b855f4a34afe 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -2174,11 +2174,10 @@ static int __devinit savage_init_fb_info(struct fb_info *info, #if defined(CONFIG_FB_SAVAGE_ACCEL) /* FIFO size + padding for commands */ - info->pixmap.addr = kmalloc(8*1024, GFP_KERNEL); + info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL); err = -ENOMEM; if (info->pixmap.addr) { - memset(info->pixmap.addr, 0, 8*1024); info->pixmap.size = 8*1024; info->pixmap.scan_align = 4; info->pixmap.buf_align = 4; diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c index ebb6756aea08..4fb16240c04d 100644 --- a/drivers/video/sgivwfb.c +++ b/drivers/video/sgivwfb.c @@ -752,7 +752,7 @@ static int __init sgivwfb_probe(struct platform_device *dev) struct fb_info *info; char *monitor; - info = framebuffer_alloc(sizeof(struct sgivw_par) + sizeof(u32) * 256, &dev->dev); + info = framebuffer_alloc(sizeof(struct sgivw_par) + sizeof(u32) * 16, &dev->dev); if (!info) return -ENOMEM; par = info->par; diff --git a/drivers/video/sis/sis.h b/drivers/video/sis/sis.h index d5e2d9c27847..d53bf6945f0c 100644 --- a/drivers/video/sis/sis.h +++ b/drivers/video/sis/sis.h @@ -479,7 +479,7 @@ struct sis_video_info { struct fb_var_screeninfo default_var; struct fb_fix_screeninfo sisfb_fix; - u32 pseudo_palette[17]; + u32 pseudo_palette[16]; struct sisfb_monitor { u16 hmin; diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 93d07ef85276..e8ccace01252 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -1405,12 +1405,18 @@ sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, } break; case 16: + if (regno >= 16) + break; + ((u32 *)(info->pseudo_palette))[regno] = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); break; case 32: + if (regno >= 16) + break; + red >>= 8; green >>= 8; blue >>= 8; diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 5c0dab628099..89facb73edfc 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -1634,7 +1634,7 @@ tgafb_register(struct device *dev) FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT; info->fbops = &tgafb_ops; info->screen_base = par->tga_fb_base; - info->pseudo_palette = (void *)(par + 1); + info->pseudo_palette = par->palette; /* This should give a reasonable default video mode. */ if (tga_bus_pci) { diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index 55e8aa450bfa..c699864b6f4a 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -976,7 +976,7 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green, return 1; - if (bpp==8) { + if (bpp == 8) { t_outb(0xFF,0x3C6); t_outb(regno,0x3C8); @@ -984,19 +984,21 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green, t_outb(green>>10,0x3C9); t_outb(blue>>10,0x3C9); - } else if (bpp == 16) { /* RGB 565 */ - u32 col; - - col = (red & 0xF800) | ((green & 0xFC00) >> 5) | - ((blue & 0xF800) >> 11); - col |= col << 16; - ((u32 *)(info->pseudo_palette))[regno] = col; - } else if (bpp == 32) /* ARGB 8888 */ - ((u32*)info->pseudo_palette)[regno] = - ((transp & 0xFF00) <<16) | - ((red & 0xFF00) << 8) | - ((green & 0xFF00)) | - ((blue & 0xFF00)>>8); + } else if (regno < 16) { + if (bpp == 16) { /* RGB 565 */ + u32 col; + + col = (red & 0xF800) | ((green & 0xFC00) >> 5) | + ((blue & 0xF800) >> 11); + col |= col << 16; + ((u32 *)(info->pseudo_palette))[regno] = col; + } else if (bpp == 32) /* ARGB 8888 */ + ((u32*)info->pseudo_palette)[regno] = + ((transp & 0xFF00) <<16) | + ((red & 0xFF00) << 8) | + ((green & 0xFF00)) | + ((blue & 0xFF00)>>8); + } // debug("exit\n"); return 0; diff --git a/drivers/video/tx3912fb.c b/drivers/video/tx3912fb.c index 07389ba01eff..e6f7c78da68b 100644 --- a/drivers/video/tx3912fb.c +++ b/drivers/video/tx3912fb.c @@ -291,7 +291,7 @@ int __init tx3912fb_init(void) fb_info.fbops = &tx3912fb_ops; fb_info.var = tx3912fb_var; fb_info.fix = tx3912fb_fix; - fb_info.pseudo_palette = pseudo_palette; + fb_info.pseudo_palette = cfb8; fb_info.flags = FBINFO_DEFAULT; /* Clear the framebuffer */ diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index ad66f070acb8..7b0cef9ca8f9 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -356,10 +356,9 @@ int __init valkyriefb_init(void) } #endif /* ppc (!CONFIG_MAC) */ - p = kmalloc(sizeof(*p), GFP_ATOMIC); + p = kzalloc(sizeof(*p), GFP_ATOMIC); if (p == 0) return -ENOMEM; - memset(p, 0, sizeof(*p)); /* Map in frame buffer and registers */ if (!request_mem_region(frame_buffer_phys, 0x100000, "valkyriefb")) { diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index 30c0b948852b..4c3a63308df1 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -68,26 +68,26 @@ static const struct svga_pll vt8623_pll = {2, 127, 2, 7, 0, 3, /* CRT timing register sets */ -struct vga_regset vt8623_h_total_regs[] = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END}; -struct vga_regset vt8623_h_display_regs[] = {{0x01, 0, 7}, VGA_REGSET_END}; -struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END}; -struct vga_regset vt8623_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END}; -struct vga_regset vt8623_h_sync_start_regs[] = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END}; -struct vga_regset vt8623_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; - -struct vga_regset vt8623_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END}; -struct vga_regset vt8623_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END}; -struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END}; -struct vga_regset vt8623_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; -struct vga_regset vt8623_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END}; -struct vga_regset vt8623_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; - -struct vga_regset vt8623_offset_regs[] = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END}; -struct vga_regset vt8623_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END}; -struct vga_regset vt8623_fetch_count_regs[] = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END}; -struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END}; - -struct svga_timing_regs vt8623_timing_regs = { +static struct vga_regset vt8623_h_total_regs[] = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END}; +static struct vga_regset vt8623_h_display_regs[] = {{0x01, 0, 7}, VGA_REGSET_END}; +static struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END}; +static struct vga_regset vt8623_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END}; +static struct vga_regset vt8623_h_sync_start_regs[] = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END}; +static struct vga_regset vt8623_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; + +static struct vga_regset vt8623_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END}; +static struct vga_regset vt8623_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END}; +static struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END}; +static struct vga_regset vt8623_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; +static struct vga_regset vt8623_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END}; +static struct vga_regset vt8623_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; + +static struct vga_regset vt8623_offset_regs[] = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END}; +static struct vga_regset vt8623_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END}; +static struct vga_regset vt8623_fetch_count_regs[] = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END}; +static struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END}; + +static struct svga_timing_regs vt8623_timing_regs = { vt8623_h_total_regs, vt8623_h_display_regs, vt8623_h_blank_start_regs, vt8623_h_blank_end_regs, vt8623_h_sync_start_regs, vt8623_h_sync_end_regs, vt8623_v_total_regs, vt8623_v_display_regs, vt8623_v_blank_start_regs, @@ -903,7 +903,7 @@ static void __exit vt8623fb_cleanup(void) /* Driver Initialisation */ -int __init vt8623fb_init(void) +static int __init vt8623fb_init(void) { #ifndef MODULE |