diff options
Diffstat (limited to 'drivers/video')
88 files changed, 11546 insertions, 967 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/aty128fb.c b/drivers/video/aty/aty128fb.c index 7fea4d8ae8e2..cfcbe37d2d70 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1733,7 +1733,7 @@ static int aty128_bl_get_level_brightness(struct aty128fb_par *par, static int aty128_bl_update_status(struct backlight_device *bd) { - struct aty128fb_par *par = class_get_devdata(&bd->class_dev); + struct aty128fb_par *par = bl_get_data(bd); unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); int level; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 2fbff6317433..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 = { @@ -2141,7 +2141,7 @@ static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) static int aty_bl_update_status(struct backlight_device *bd) { - struct atyfb_par *par = class_get_devdata(&bd->class_dev); + struct atyfb_par *par = bl_get_data(bd); unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par); int level; @@ -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_backlight.c b/drivers/video/aty/radeon_backlight.c index 0be25fa5540c..1a056adb61c8 100644 --- a/drivers/video/aty/radeon_backlight.c +++ b/drivers/video/aty/radeon_backlight.c @@ -47,7 +47,7 @@ static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata, static int radeon_bl_update_status(struct backlight_device *bd) { - struct radeon_bl_privdata *pdata = class_get_devdata(&bd->class_dev); + struct radeon_bl_privdata *pdata = bl_get_data(bd); struct radeonfb_info *rinfo = pdata->rinfo; u32 lvds_gen_cntl, tmpPixclksCntl; int level; @@ -206,7 +206,7 @@ void radeonfb_bl_exit(struct radeonfb_info *rinfo) if (bd) { struct radeon_bl_privdata *pdata; - pdata = class_get_devdata(&bd->class_dev); + pdata = bl_get_data(bd); backlight_device_unregister(bd); kfree(pdata); rinfo->info->bl_dev = NULL; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 2ce050193018..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), @@ -2102,7 +2104,9 @@ static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u } -static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t radeon_show_edid1(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct pci_dev *pdev = to_pci_dev(dev); @@ -2113,7 +2117,9 @@ static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, si } -static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t radeon_show_edid2(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct pci_dev *pdev = to_pci_dev(dev); @@ -2126,7 +2132,6 @@ static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, si static struct bin_attribute edid1_attr = { .attr = { .name = "edid1", - .owner = THIS_MODULE, .mode = 0444, }, .size = EDID_LENGTH, @@ -2136,7 +2141,6 @@ static struct bin_attribute edid1_attr = { static struct bin_attribute edid2_attr = { .attr = { .name = "edid2", - .owner = THIS_MODULE, .mode = 0444, }, .size = EDID_LENGTH, 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/Kconfig b/drivers/video/backlight/Kconfig index fbef663fc057..2580f5fa2486 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -8,26 +8,32 @@ menuconfig BACKLIGHT_LCD_SUPPORT Enable this to be able to choose the drivers for controlling the backlight and the LCD panel on some platforms, for example on PDAs. -config BACKLIGHT_CLASS_DEVICE - tristate "Lowlevel Backlight controls" +# +# LCD +# +config LCD_CLASS_DEVICE + tristate "Lowlevel LCD controls" depends on BACKLIGHT_LCD_SUPPORT default m help - This framework adds support for low-level control of the LCD - backlight. This includes support for brightness and power. + This framework adds support for low-level control of LCD. + Some framebuffer devices connect to platform-specific LCD modules + in order to have a platform-specific way to control the flat panel + (contrast and applying power to the LCD (not to the backlight!)). To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. -config LCD_CLASS_DEVICE - tristate "Lowlevel LCD controls" +# +# Backlight +# +config BACKLIGHT_CLASS_DEVICE + tristate "Lowlevel Backlight controls" depends on BACKLIGHT_LCD_SUPPORT default m help - This framework adds support for low-level control of LCD. - Some framebuffer devices connect to platform-specific LCD modules - in order to have a platform-specific way to control the flat panel - (contrast and applying power to the LCD (not to the backlight!)). + This framework adds support for low-level control of the LCD + backlight. This includes support for brightness and power. To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index c65e81ff3578..b26de8cf3112 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -69,18 +69,20 @@ static inline void backlight_unregister_fb(struct backlight_device *bd) } #endif /* CONFIG_FB */ -static ssize_t backlight_show_power(struct class_device *cdev, char *buf) +static ssize_t backlight_show_power(struct device *dev, + struct device_attribute *attr,char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.power); } -static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count) +static ssize_t backlight_store_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); int power = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -101,18 +103,20 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, return rc; } -static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) +static ssize_t backlight_show_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.brightness); } -static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count) +static ssize_t backlight_store_brightness(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); int brightness = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -138,18 +142,19 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char return rc; } -static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf) +static ssize_t backlight_show_max_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.max_brightness); } -static ssize_t backlight_show_actual_brightness(struct class_device *cdev, - char *buf) +static ssize_t backlight_show_actual_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { int rc = -ENXIO; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); mutex_lock(&bd->ops_lock); if (bd->ops && bd->ops->get_brightness) @@ -159,31 +164,22 @@ static ssize_t backlight_show_actual_brightness(struct class_device *cdev, return rc; } -static void backlight_class_release(struct class_device *dev) +struct class *backlight_class; + +static void bl_device_release(struct device *dev) { struct backlight_device *bd = to_backlight_device(dev); kfree(bd); } -static struct class backlight_class = { - .name = "backlight", - .release = backlight_class_release, -}; - -#define DECLARE_ATTR(_name,_mode,_show,_store) \ -{ \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ - .show = _show, \ - .store = _store, \ -} - -static const struct class_device_attribute bl_class_device_attributes[] = { - DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power), - DECLARE_ATTR(brightness, 0644, backlight_show_brightness, +static struct device_attribute bl_device_attributes[] = { + __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power), + __ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness), - DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness, + __ATTR(actual_brightness, 0444, backlight_show_actual_brightness, NULL), - DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), + __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), + __ATTR_NULL, }; /** @@ -191,22 +187,20 @@ static const struct class_device_attribute bl_class_device_attributes[] = { * backlight_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). - * @devdata: an optional pointer to be stored in the class_device. The - * methods may retrieve it by using class_get_devdata(&bd->class_dev). + * @devdata: an optional pointer to be stored for private driver use. The + * methods may retrieve it by using bl_get_data(bd). * @ops: the backlight operations structure. * - * Creates and registers new backlight class_device. Returns either an + * Creates and registers new backlight device. Returns either an * ERR_PTR() or a pointer to the newly allocated device. */ struct backlight_device *backlight_device_register(const char *name, - struct device *dev, - void *devdata, - struct backlight_ops *ops) + struct device *parent, void *devdata, struct backlight_ops *ops) { - int i, rc; struct backlight_device *new_bd; + int rc; - pr_debug("backlight_device_alloc: name=%s\n", name); + pr_debug("backlight_device_register: name=%s\n", name); new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); if (!new_bd) @@ -214,13 +208,14 @@ struct backlight_device *backlight_device_register(const char *name, mutex_init(&new_bd->update_lock); mutex_init(&new_bd->ops_lock); - new_bd->ops = ops; - new_bd->class_dev.class = &backlight_class; - new_bd->class_dev.dev = dev; - strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN); - class_set_devdata(&new_bd->class_dev, devdata); - rc = class_device_register(&new_bd->class_dev); + new_bd->dev.class = backlight_class; + new_bd->dev.parent = parent; + new_bd->dev.release = bl_device_release; + strlcpy(new_bd->dev.bus_id, name, BUS_ID_SIZE); + dev_set_drvdata(&new_bd->dev, devdata); + + rc = device_register(&new_bd->dev); if (rc) { kfree(new_bd); return ERR_PTR(rc); @@ -228,23 +223,11 @@ struct backlight_device *backlight_device_register(const char *name, rc = backlight_register_fb(new_bd); if (rc) { - class_device_unregister(&new_bd->class_dev); + device_unregister(&new_bd->dev); return ERR_PTR(rc); } - - for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) { - rc = class_device_create_file(&new_bd->class_dev, - &bl_class_device_attributes[i]); - if (rc) { - while (--i >= 0) - class_device_remove_file(&new_bd->class_dev, - &bl_class_device_attributes[i]); - class_device_unregister(&new_bd->class_dev); - /* No need to kfree(new_bd) since release() method was called */ - return ERR_PTR(rc); - } - } + new_bd->ops = ops; #ifdef CONFIG_PMAC_BACKLIGHT mutex_lock(&pmac_backlight_mutex); @@ -265,42 +248,40 @@ EXPORT_SYMBOL(backlight_device_register); */ void backlight_device_unregister(struct backlight_device *bd) { - int i; - if (!bd) return; - pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id); - #ifdef CONFIG_PMAC_BACKLIGHT mutex_lock(&pmac_backlight_mutex); if (pmac_backlight == bd) pmac_backlight = NULL; mutex_unlock(&pmac_backlight_mutex); #endif - - for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) - class_device_remove_file(&bd->class_dev, - &bl_class_device_attributes[i]); - mutex_lock(&bd->ops_lock); bd->ops = NULL; mutex_unlock(&bd->ops_lock); backlight_unregister_fb(bd); - - class_device_unregister(&bd->class_dev); + device_unregister(&bd->dev); } EXPORT_SYMBOL(backlight_device_unregister); static void __exit backlight_class_exit(void) { - class_unregister(&backlight_class); + class_destroy(backlight_class); } static int __init backlight_class_init(void) { - return class_register(&backlight_class); + backlight_class = class_create(THIS_MODULE, "backlight"); + if (IS_ERR(backlight_class)) { + printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", + PTR_ERR(backlight_class)); + return PTR_ERR(backlight_class); + } + + backlight_class->dev_attrs = bl_device_attributes; + return 0; } /* diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index e9bbc3455c94..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; @@ -202,7 +202,7 @@ static int cr_backlight_probe(struct platform_device *pdev) } crp->cr_lcd_device = lcd_device_register("cr-lcd", - &pdev->dev, + &pdev->dev, NULL, &cr_lcd_ops); if (IS_ERR(crp->cr_lcd_device)) { diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 6ef8f0a7a137..6f652c65fae1 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -61,10 +61,11 @@ static inline void lcd_unregister_fb(struct lcd_device *ld) } #endif /* CONFIG_FB */ -static ssize_t lcd_show_power(struct class_device *cdev, char *buf) +static ssize_t lcd_show_power(struct device *dev, struct device_attribute *attr, + char *buf) { int rc; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); mutex_lock(&ld->ops_lock); if (ld->ops && ld->ops->get_power) @@ -76,11 +77,12 @@ static ssize_t lcd_show_power(struct class_device *cdev, char *buf) return rc; } -static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count) +static ssize_t lcd_store_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); int power = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -100,10 +102,11 @@ static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_ return rc; } -static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) +static ssize_t lcd_show_contrast(struct device *dev, + struct device_attribute *attr, char *buf) { int rc = -ENXIO; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); mutex_lock(&ld->ops_lock); if (ld->ops && ld->ops->get_contrast) @@ -113,11 +116,12 @@ static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) return rc; } -static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count) +static ssize_t lcd_store_contrast(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); int contrast = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -137,53 +141,45 @@ static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, si return rc; } -static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf) +static ssize_t lcd_show_max_contrast(struct device *dev, + struct device_attribute *attr, char *buf) { - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); return sprintf(buf, "%d\n", ld->props.max_contrast); } -static void lcd_class_release(struct class_device *dev) +struct class *lcd_class; + +static void lcd_device_release(struct device *dev) { struct lcd_device *ld = to_lcd_device(dev); kfree(ld); } -static struct class lcd_class = { - .name = "lcd", - .release = lcd_class_release, -}; - -#define DECLARE_ATTR(_name,_mode,_show,_store) \ -{ \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ - .show = _show, \ - .store = _store, \ -} - -static const struct class_device_attribute lcd_class_device_attributes[] = { - DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power), - DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast), - DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL), +static struct device_attribute lcd_device_attributes[] = { + __ATTR(lcd_power, 0644, lcd_show_power, lcd_store_power), + __ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast), + __ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL), + __ATTR_NULL, }; /** * lcd_device_register - register a new object of lcd_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). - * @devdata: an optional pointer to be stored in the class_device. The - * methods may retrieve it by using class_get_devdata(ld->class_dev). + * @devdata: an optional pointer to be stored in the device. The + * methods may retrieve it by using lcd_get_data(ld). * @ops: the lcd operations structure. * - * Creates and registers a new lcd class_device. Returns either an ERR_PTR() + * Creates and registers a new lcd device. Returns either an ERR_PTR() * or a pointer to the newly allocated device. */ -struct lcd_device *lcd_device_register(const char *name, void *devdata, - struct lcd_ops *ops) +struct lcd_device *lcd_device_register(const char *name, struct device *parent, + void *devdata, struct lcd_ops *ops) { - int i, rc; struct lcd_device *new_ld; + int rc; pr_debug("lcd_device_register: name=%s\n", name); @@ -193,12 +189,14 @@ struct lcd_device *lcd_device_register(const char *name, void *devdata, mutex_init(&new_ld->ops_lock); mutex_init(&new_ld->update_lock); - new_ld->ops = ops; - new_ld->class_dev.class = &lcd_class; - strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN); - class_set_devdata(&new_ld->class_dev, devdata); - rc = class_device_register(&new_ld->class_dev); + new_ld->dev.class = lcd_class; + new_ld->dev.parent = parent; + new_ld->dev.release = lcd_device_release; + strlcpy(new_ld->dev.bus_id, name, BUS_ID_SIZE); + dev_set_drvdata(&new_ld->dev, devdata); + + rc = device_register(&new_ld->dev); if (rc) { kfree(new_ld); return ERR_PTR(rc); @@ -206,22 +204,11 @@ struct lcd_device *lcd_device_register(const char *name, void *devdata, rc = lcd_register_fb(new_ld); if (rc) { - class_device_unregister(&new_ld->class_dev); + device_unregister(&new_ld->dev); return ERR_PTR(rc); } - for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) { - rc = class_device_create_file(&new_ld->class_dev, - &lcd_class_device_attributes[i]); - if (rc) { - while (--i >= 0) - class_device_remove_file(&new_ld->class_dev, - &lcd_class_device_attributes[i]); - class_device_unregister(&new_ld->class_dev); - /* No need to kfree(new_ld) since release() method was called */ - return ERR_PTR(rc); - } - } + new_ld->ops = ops; return new_ld; } @@ -235,33 +222,34 @@ EXPORT_SYMBOL(lcd_device_register); */ void lcd_device_unregister(struct lcd_device *ld) { - int i; - if (!ld) return; - pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id); - - for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) - class_device_remove_file(&ld->class_dev, - &lcd_class_device_attributes[i]); - mutex_lock(&ld->ops_lock); ld->ops = NULL; mutex_unlock(&ld->ops_lock); lcd_unregister_fb(ld); - class_device_unregister(&ld->class_dev); + + device_unregister(&ld->dev); } EXPORT_SYMBOL(lcd_device_unregister); static void __exit lcd_class_exit(void) { - class_unregister(&lcd_class); + class_destroy(lcd_class); } static int __init lcd_class_init(void) { - return class_register(&lcd_class); + lcd_class = class_create(THIS_MODULE, "lcd"); + if (IS_ERR(lcd_class)) { + printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", + PTR_ERR(lcd_class)); + return PTR_ERR(lcd_class); + } + + lcd_class->dev_attrs = lcd_device_attributes; + return 0; } /* 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 63b85bf81a65..49643969f9f8 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -6,7 +6,7 @@ menu "Console display driver support" config VGA_CONSOLE bool "VGA text console" if EMBEDDED || !X86 - depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE && !SUPERH + depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE && !SUPERH && !BFIN default y help Saying Y here will allow you to use Linux in text mode through a @@ -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/kyro/STG4000InitDevice.c b/drivers/video/kyro/STG4000InitDevice.c index ab5285a7f1d6..1d3f2080aa6f 100644 --- a/drivers/video/kyro/STG4000InitDevice.c +++ b/drivers/video/kyro/STG4000InitDevice.c @@ -247,7 +247,6 @@ int SetCoreClockPLL(volatile STG4000REG __iomem *pSTGReg, struct pci_dev *pDev) u32 ulCoreClock; u32 tmp; u32 ulChipSpeed; - u8 rev; STG_WRITE_REG(IntMask, 0xFFFF); @@ -276,9 +275,9 @@ int SetCoreClockPLL(volatile STG4000REG __iomem *pSTGReg, struct pci_dev *pDev) PMX2_SOFTRESET_ROM_RST); pci_read_config_word(pDev, PCI_CONFIG_SUBSYS_ID, &sub); - pci_read_config_byte(pDev, PCI_REVISION_ID, &rev); - ulChipSpeed = InitSDRAMRegisters(pSTGReg, (u32)sub, (u32)rev); + ulChipSpeed = InitSDRAMRegisters(pSTGReg, (u32)sub, + (u32)pDev->revision); if (ulChipSpeed == 0) return -EINVAL; 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 c8559a756b75..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) | @@ -1994,7 +1998,6 @@ static void matroxfb_unregister_device(struct matrox_fb_info* minfo) { static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dummy) { struct board* b; - u_int8_t rev; u_int16_t svid; u_int16_t sid; struct matrox_fb_info* minfo; @@ -2005,11 +2008,10 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm #endif DBG(__FUNCTION__) - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); svid = pdev->subsystem_vendor; sid = pdev->subsystem_device; for (b = dev_list; b->vendor; b++) { - if ((b->vendor != pdev->vendor) || (b->device != pdev->device) || (b->rev < rev)) continue; + if ((b->vendor != pdev->vendor) || (b->device != pdev->device) || (b->rev < pdev->revision)) continue; if (b->svid) if ((b->svid != svid) || (b->sid != sid)) continue; break; 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 608e40bb20e9..1005582e843e 100644 --- a/drivers/video/matrox/matroxfb_crtc2.h +++ b/drivers/video/matrox/matroxfb_crtc2.h @@ -2,8 +2,6 @@ #define __MATROXFB_CRTC2_H__ #include <linux/ioctl.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include "matroxfb_base.h" struct matroxfb_dh_fb_info { @@ -30,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_backlight.c b/drivers/video/nvidia/nv_backlight.c index 43f62d8ee41d..443e3c85a9a0 100644 --- a/drivers/video/nvidia/nv_backlight.c +++ b/drivers/video/nvidia/nv_backlight.c @@ -50,7 +50,7 @@ static int nvidia_bl_get_level_brightness(struct nvidia_par *par, static int nvidia_bl_update_status(struct backlight_device *bd) { - struct nvidia_par *par = class_get_devdata(&bd->class_dev); + struct nvidia_par *par = bl_get_data(bd); u32 tmp_pcrt, tmp_pmc, fpcontrol; int level; 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 0fe547842c64..5c47968e7f21 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -307,7 +307,7 @@ static int riva_bl_get_level_brightness(struct riva_par *par, static int riva_bl_update_status(struct backlight_device *bd) { - struct riva_par *par = class_get_devdata(&bd->class_dev); + struct riva_par *par = bl_get_data(bd); U032 tmp_pcrt, tmp_pmc; int level; @@ -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 a30e1e13d8be..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; @@ -5789,7 +5795,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ivideo->warncount = 0; ivideo->chip_id = pdev->device; ivideo->chip_vendor = pdev->vendor; - pci_read_config_byte(pdev, PCI_REVISION_ID, &ivideo->revision_id); + ivideo->revision_id = pdev->revision; ivideo->SiS_Pr.ChipRevision = ivideo->revision_id; pci_read_config_word(pdev, PCI_COMMAND, ®16); ivideo->sisvga_enabled = reg16 & 0x01; diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 62fa5500361d..5eff28ce4f4d 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -1348,7 +1348,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev, f_ddprintk("found device : %s\n", spec->name); par->dev = pdev; - pci_read_config_byte(pdev, PCI_REVISION_ID, &par->revision); + par->revision = pdev->revision; fix->mmio_start = pci_resource_start(pdev,0); fix->mmio_len = 0x400000; diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index f0fde6ea7c36..89facb73edfc 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -1625,8 +1625,7 @@ tgafb_register(struct device *dev) par->tga_regs_base = mem_base + TGA_REGS_OFFSET; par->tga_type = tga_type; if (tga_bus_pci) - pci_read_config_byte(to_pci_dev(dev), PCI_REVISION_ID, - &par->tga_chip_rev); + par->tga_chip_rev = (to_pci_dev(dev))->revision; if (tga_bus_tc) par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff; @@ -1635,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 |