diff options
Diffstat (limited to 'drivers/video')
97 files changed, 13033 insertions, 4322 deletions
diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c index 0dda73da862..7f907fb23b8 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 9a256d2ff9d..2a237f09ee5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -3,6 +3,7 @@ # menu "Graphics support" + depends on HAS_IOMEM source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" @@ -703,6 +704,91 @@ config FB_CG6 This is the frame buffer device driver for the CGsix (GX, TurboGX) frame buffer. +config FB_FFB + bool "Creator/Creator3D/Elite3D support" + depends on FB_SBUS && SPARC64 + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the Creator, Creator3D, + and Elite3D graphics boards. + +config FB_TCX + bool "TCX (SS4/SS5 only) support" + depends on FB_SBUS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the TCX 24/8bit frame + buffer. + +config FB_CG14 + bool "CGfourteen (SX) support" + depends on FB_SBUS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the CGfourteen frame + buffer on Desktop SPARCsystems with the SX graphics option. + +config FB_P9100 + bool "P9100 (Sparcbook 3 only) support" + depends on FB_SBUS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the P9100 card + supported on Sparcbook 3 machines. + +config FB_LEO + bool "Leo (ZX) support" + depends on FB_SBUS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the SBUS-based Sun ZX + (leo) frame buffer cards. + +config FB_IGA + bool "IGA 168x display support" + depends on (FB = y) && SPARC32 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for the INTERGRAPHICS 1680 and + successor frame buffer cards. + +config FB_XVR500 + bool "Sun XVR-500 3DLABS Wildcat support" + depends on (FB = y) && PCI && SPARC64 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for the Sun XVR-500 and similar + graphics cards based upon the 3DLABS Wildcat chipset. The driver + only works on sparc64 systems where the system firwmare has + mostly initialized the card already. It is treated as a + completely dumb framebuffer device. + +config FB_XVR2500 + bool "Sun XVR-2500 3DLABS Wildcat support" + depends on (FB = y) && PCI && SPARC64 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for the Sun XVR-2500 and similar + graphics cards based upon the 3DLABS Wildcat chipset. The driver + only works on sparc64 systems where the system firwmare has + mostly initialized the card already. It is treated as a + completely dumb framebuffer device. + config FB_PVR2 tristate "NEC PowerVR 2 display support" depends on FB && SH_DREAMCAST @@ -726,7 +812,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 @@ -747,6 +833,32 @@ config FB_S1D13XXX working with S1D13806). Product specs at <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm> +config FB_ATMEL + tristate "AT91/AT32 LCD Controller support" + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This enables support for the AT91/AT32 LCD Controller. + +config FB_INTSRAM + bool "Frame Buffer in internal SRAM" + depends on FB_ATMEL && ARCH_AT91SAM9261 + help + 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 @@ -779,6 +891,15 @@ config FB_NVIDIA_I2C independently validate video mode parameters, you should say Y here. +config FB_NVIDIA_DEBUG + bool "Lots of debug output" + depends on FB_NVIDIA + default n + help + Say Y here if you want the nVidia driver to output all sorts + of debugging information to provide to the maintainer when + something goes wrong. + config FB_NVIDIA_BACKLIGHT bool "Support for backlight control" depends on FB_NVIDIA @@ -818,7 +939,7 @@ config FB_RIVA_I2C here. config FB_RIVA_DEBUG - bool "Lots of debug output from Riva(nVidia) driver" + bool "Lots of debug output" depends on FB_RIVA default n help @@ -1169,7 +1290,7 @@ config FB_ATY config FB_ATY_CT bool "Mach64 CT/VT/GT/LT (incl. 3D RAGE) support" depends on PCI && FB_ATY - default y if SPARC64 && FB_PCI + default y if SPARC64 && PCI help Say Y here to support use of ATI's 64-bit Rage boards (or other boards based on the Mach64 CT, VT, GT, and LT chipsets) as a @@ -1430,8 +1551,11 @@ config FB_ARK and ICS 5342 RAMDAC. config FB_PM3 - tristate "Permedia3 support" - depends on FB && PCI && BROKEN + tristate "Permedia3 support (EXPERIMENTAL)" + depends on FB && PCI && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT help This is the frame buffer device driver for the 3DLabs Permedia3 chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & @@ -1455,95 +1579,6 @@ config FB_AU1200 source "drivers/video/geode/Kconfig" -config FB_FFB - bool "Creator/Creator3D/Elite3D support" - depends on FB_SBUS && SPARC64 - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the frame buffer device driver for the Creator, Creator3D, - and Elite3D graphics boards. - -config FB_TCX - bool "TCX (SS4/SS5 only) support" - depends on FB_SBUS - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the frame buffer device driver for the TCX 24/8bit frame - buffer. - -config FB_CG14 - bool "CGfourteen (SX) support" - depends on FB_SBUS - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the frame buffer device driver for the CGfourteen frame - buffer on Desktop SPARCsystems with the SX graphics option. - -config FB_P9100 - bool "P9100 (Sparcbook 3 only) support" - depends on FB_SBUS - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the frame buffer device driver for the P9100 card - supported on Sparcbook 3 machines. - -config FB_LEO - bool "Leo (ZX) support" - depends on FB_SBUS - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the frame buffer device driver for the SBUS-based Sun ZX - (leo) frame buffer cards. - -config FB_XVR500 - bool "Sun XVR-500 3DLABS Wildcat support" - depends on FB && PCI && SPARC64 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the Sun XVR-500 and similar - graphics cards based upon the 3DLABS Wildcat chipset. The driver - only works on sparc64 systems where the system firwmare has - mostly initialized the card already. It is treated as a - completely dumb framebuffer device. - -config FB_XVR2500 - bool "Sun XVR-2500 3DLABS Wildcat support" - depends on FB && PCI && SPARC64 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the Sun XVR-2500 and similar - graphics cards based upon the 3DLABS Wildcat chipset. The driver - only works on sparc64 systems where the system firwmare has - mostly initialized the card already. It is treated as a - completely dumb framebuffer device. - -config FB_PCI - bool "PCI framebuffers" - depends on (FB = y) && PCI && SPARC - -config FB_IGA - bool "IGA 168x display support" - depends on SPARC32 && FB_PCI - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the INTERGRAPHICS 1680 and - successor frame buffer cards. - config FB_HIT tristate "HD64461 Frame Buffer support" depends on FB && HD64461 @@ -1765,18 +1800,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 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT + 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. @@ -1794,6 +1831,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 0b70567458f..a562f9d69d2 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_FB_G364) += g364fb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o +obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o @@ -112,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 diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 6c9dc2e69c8..a7a1c891bfa 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/arkfb.c b/drivers/video/arkfb.c index ba6fede5c46..8a1b07c7439 100644 --- a/drivers/video/arkfb.c +++ b/drivers/video/arkfb.c @@ -1055,9 +1055,10 @@ err_enable_device: static void __devexit ark_pci_remove(struct pci_dev *dev) { struct fb_info *info = pci_get_drvdata(dev); - struct arkfb_info *par = info->par; if (info) { + struct arkfb_info *par = info->par; + #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { mtrr_del(par->mtrr_reg, 0, 0); diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c new file mode 100644 index 00000000000..235b618b411 --- /dev/null +++ b/drivers/video/atmel_lcdfb.c @@ -0,0 +1,801 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/arch/board.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> + +#include <video/atmel_lcdc.h> + +#define lcdc_readl(sinfo, reg) __raw_readl((sinfo)->mmio+(reg)) +#define lcdc_writel(sinfo, reg, val) __raw_writel((val), (sinfo)->mmio+(reg)) + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 + +#if defined(CONFIG_ARCH_AT91SAM9263) +#define ATMEL_LCDC_FIFO_SIZE 2048 +#else +#define ATMEL_LCDC_FIFO_SIZE 512 +#endif + +#if defined(CONFIG_ARCH_AT91) +#define ATMEL_LCDFB_FBINFO_DEFAULT FBINFO_DEFAULT + +static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + +} +#elif defined(CONFIG_AVR32) +#define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ + | FBINFO_PARTIAL_PAN_OK \ + | FBINFO_HWACCEL_XPAN \ + | FBINFO_HWACCEL_YPAN) + +static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + u32 dma2dcfg; + u32 pixeloff; + + pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f; + + dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8; + dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET; + lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg); + + /* Update configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, + lcdc_readl(sinfo, ATMEL_LCDC_DMACON) + | ATMEL_LCDC_DMAUPDT); +} +#endif + + +static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .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) +{ + struct atmel_lcdfb_info *sinfo = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned long dma_addr; + + dma_addr = (fix->smem_start + var->yoffset * fix->line_length + + var->xoffset * var->bits_per_pixel / 8); + + dma_addr &= ~3UL; + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr); + + atmel_lcdfb_update_dma2d(sinfo, var); +} + +static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + + dma_free_writecombine(info->device, info->fix.smem_len, + info->screen_base, info->fix.smem_start); +} + +/** + * atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory + * @sinfo: the frame buffer to allocate memory for + */ +static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + struct fb_var_screeninfo *var = &info->var; + + info->fix.smem_len = (var->xres_virtual * var->yres_virtual + * ((var->bits_per_pixel + 7) / 8)); + + info->screen_base = dma_alloc_writecombine(info->device, info->fix.smem_len, + (dma_addr_t *)&info->fix.smem_start, GFP_KERNEL); + + if (!info->screen_base) { + return -ENOMEM; + } + + return 0; +} + +/** + * atmel_lcdfb_check_var - 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. This function does not alter the hardware + * state!!! This means the data stored in struct fb_info and + * struct atmel_lcdfb_info do not change. This includes the var + * inside of struct fb_info. Do NOT change these. This function + * can be called on its own if we intent to only test a mode and + * not actually set it. The stuff in modedb.c is a example of + * this. If the var passed in is slightly off by what the + * hardware can support then we alter the var PASSED in to what + * we can do. If the hardware doesn't support mode change a + * -EINVAL will be returned by the upper layers. You don't need + * to implement this function then. If you hardware doesn't + * support changing the resolution then this function is not + * needed. In this case the driver would just provide a var that + * represents the static state the screen is in. + * + * Returns negative errno on error, or zero on success. + */ +static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct device *dev = info->device; + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long clk_value_khz; + + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + dev_dbg(dev, "%s:\n", __func__); + dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); + dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); + dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); + dev_dbg(dev, " clk: %lu KHz\n", clk_value_khz); + + if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) { + dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; + } + + /* Force same alignment for each line */ + var->xres = (var->xres + 3) & ~3UL; + var->xres_virtual = (var->xres_virtual + 3) & ~3UL; + + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.msb_right = 0; + var->transp.offset = var->transp.length = 0; + var->xoffset = var->yoffset = 0; + + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length + = var->bits_per_pixel; + break; + case 15: + case 16: + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + var->red.length = var->green.length = var->blue.length = 5; + break; + 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; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + dev_err(dev, "color depth %d not supported\n", + var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +/** + * atmel_lcdfb_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. This function alters the + * par AND the fb_fix_screeninfo stored in fb_info. It doesn't + * not alter var in fb_info since we are using that data. This + * means we depend on the data in var inside fb_info to be + * supported by the hardware. atmel_lcdfb_check_var is always called + * before atmel_lcdfb_set_par to ensure this. Again if you can't + * change the resolution you don't need this function. + * + */ +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", + info->var.xres, info->var.yres, + info->var.xres_virtual, info->var.yres_virtual); + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); + + 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; + + 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"); + atmel_lcdfb_update_dma(info, &info->var); + + /* ...set frame size and burst length = 8 words (?) */ + value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); + + /* Now, the LCDC core... */ + + /* Set pixel clock */ + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + 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 { + 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; + + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + value |= ATMEL_LCDC_INVLINE_INVERTED; + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + value |= ATMEL_LCDC_INVFRAME_INVERTED; + + switch (info->var.bits_per_pixel) { + case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; + case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; + case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; + case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; + case 15: /* fall through */ + case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; + case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; + case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; + default: BUG(); break; + } + dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= info->var.lower_margin; + dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (info->var.left_margin - 1); + 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 = (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 */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); + + /* Set contrast */ + value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE; + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) + msleep(10); + + dev_dbg(info->device, " * re-enable DMA engine\n"); + /* ...and enable it with updated configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); + + dev_dbg(info->device, " * re-enable LCDC core\n"); + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); + + dev_dbg(info->device, " * DONE\n"); + + return 0; +} + +static inline unsigned int chan_to_field(unsigned int chan, const struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * atmel_lcdfb_setcolreg - Optional function. Sets a color register. + * @regno: Which register in the CLUT we are programming + * @red: The red value which can be up to 16 bits wide + * @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. + * Things to take into consideration are how many color registers, if + * any, are supported with the current color visual. With truecolor mode + * no color palettes are supported. Here a psuedo palette is created + * which we store the value in pseudo_palette in struct fb_info. For + * pseudocolor mode we have a limited color palette. To deal with this + * we can program what color is displayed for a particular pixel value. + * DirectColor is similar in that we can program each color field. If + * we have a static colormap we don't need to implement this function. + * + * Returns negative errno on error, or zero on success. In an + * ideal world, this would have been the case, but as it turns + * out, the other drivers return 1 on failure, so that's what + * we're going to do. + */ +static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned int val; + u32 *pal; + int ret = 1; + + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = ((red >> 11) & 0x001f); + val |= ((green >> 6) & 0x03e0); + val |= ((blue >> 1) & 0x7c00); + + /* + * TODO: intensity bit. Maybe something like + * ~(red[10] ^ green[10] ^ blue[10]) & 1 + */ + + lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); + 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; +} + +static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + dev_dbg(info->device, "%s\n", __func__); + + atmel_lcdfb_update_dma(info, var); + + return 0; +} + +static struct fb_ops atmel_lcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = atmel_lcdfb_check_var, + .fb_set_par = atmel_lcdfb_set_par, + .fb_setcolreg = atmel_lcdfb_setcolreg, + .fb_pan_display = atmel_lcdfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct atmel_lcdfb_info *sinfo = info->par; + u32 status; + + status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); + lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); + return IRQ_HANDLED; +} + +static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + int ret = 0; + + memset_io(info->screen_base, 0, info->fix.smem_len); + info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + + dev_info(info->device, + "%luKiB frame buffer at %08lx (mapped at %p)\n", + (unsigned long)info->fix.smem_len / 1024, + (unsigned long)info->fix.smem_start, + info->screen_base); + + /* Allocate colormap */ + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) + dev_err(info->device, "Alloc color map failed\n"); + + return ret; +} + +static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_enable(sinfo->bus_clk); + clk_enable(sinfo->lcdc_clk); +} + +static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_disable(sinfo->bus_clk); + clk_disable(sinfo->lcdc_clk); +} + + +static int __init atmel_lcdfb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info; + struct atmel_lcdfb_info *sinfo; + struct atmel_lcdfb_info *pdata_sinfo; + struct resource *regs = NULL; + struct resource *map = NULL; + int ret; + + dev_dbg(dev, "%s BEGIN\n", __func__); + + ret = -ENOMEM; + info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev); + if (!info) { + dev_err(dev, "cannot allocate memory\n"); + goto out; + } + + sinfo = info->par; + + if (dev->platform_data) { + pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data; + sinfo->default_bpp = pdata_sinfo->default_bpp; + sinfo->default_dmacon = pdata_sinfo->default_dmacon; + sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2; + sinfo->default_monspecs = pdata_sinfo->default_monspecs; + sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; + sinfo->guard_time = pdata_sinfo->guard_time; + } else { + dev_err(dev, "cannot get default configuration\n"); + goto free_info; + } + sinfo->info = info; + sinfo->pdev = pdev; + + strcpy(info->fix.id, sinfo->pdev->name); + info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; + info->pseudo_palette = sinfo->pseudo_palette; + info->fbops = &atmel_lcdfb_ops; + + memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); + info->fix = atmel_lcdfb_fix; + + /* Enable LCDC Clocks */ + if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) { + sinfo->bus_clk = clk_get(dev, "hck1"); + if (IS_ERR(sinfo->bus_clk)) { + ret = PTR_ERR(sinfo->bus_clk); + goto free_info; + } + } + sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); + if (IS_ERR(sinfo->lcdc_clk)) { + ret = PTR_ERR(sinfo->lcdc_clk); + goto put_bus_clk; + } + atmel_lcdfb_start_clock(sinfo); + + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + sinfo->default_bpp); + if (!ret) { + dev_err(dev, "no suitable video mode found\n"); + goto stop_clk; + } + + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "resources unusable\n"); + ret = -ENXIO; + goto stop_clk; + } + + sinfo->irq_base = platform_get_irq(pdev, 0); + if (sinfo->irq_base < 0) { + dev_err(dev, "unable to get irq\n"); + ret = sinfo->irq_base; + goto stop_clk; + } + + /* Initialize video memory */ + map = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (map) { + /* use a pre-allocated memory buffer */ + info->fix.smem_start = map->start; + info->fix.smem_len = map->end - map->start + 1; + if (!request_mem_region(info->fix.smem_start, + info->fix.smem_len, pdev->name)) { + ret = -EBUSY; + goto stop_clk; + } + + info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); + if (!info->screen_base) + goto release_intmem; + } else { + /* alocate memory buffer */ + ret = atmel_lcdfb_alloc_video_memory(sinfo); + if (ret < 0) { + dev_err(dev, "cannot allocate framebuffer: %d\n", ret); + goto stop_clk; + } + } + + /* LCDC registers */ + info->fix.mmio_start = regs->start; + info->fix.mmio_len = regs->end - regs->start + 1; + + if (!request_mem_region(info->fix.mmio_start, + info->fix.mmio_len, pdev->name)) { + ret = -EBUSY; + goto free_fb; + } + + sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); + if (!sinfo->mmio) { + dev_err(dev, "cannot map LCDC registers\n"); + goto release_mem; + } + + /* interrupt */ + ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); + if (ret) { + dev_err(dev, "request_irq failed: %d\n", ret); + goto unmap_mmio; + } + + ret = atmel_lcdfb_init_fbinfo(sinfo); + if (ret < 0) { + dev_err(dev, "init fbinfo failed: %d\n", ret); + goto unregister_irqs; + } + + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + atmel_lcdfb_check_var(&info->var, info); + + ret = fb_set_var(info, &info->var); + if (ret) { + dev_warn(dev, "unable to set display parameters\n"); + goto free_cmap; + } + + dev_set_drvdata(dev, info); + + /* + * Tell the world that we're ready to go + */ + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer device: %d\n", ret); + goto free_cmap; + } + + /* Power up the LCDC screen */ + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(1); + + dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); + + return 0; + + +free_cmap: + fb_dealloc_cmap(&info->cmap); +unregister_irqs: + free_irq(sinfo->irq_base, info); +unmap_mmio: + iounmap(sinfo->mmio); +release_mem: + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); +free_fb: + if (map) + iounmap(info->screen_base); + else + atmel_lcdfb_free_video_memory(sinfo); + +release_intmem: + if (map) + release_mem_region(info->fix.smem_start, info->fix.smem_len); +stop_clk: + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); +put_bus_clk: + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); +free_info: + framebuffer_release(info); +out: + dev_dbg(dev, "%s FAILED\n", __func__); + return ret; +} + +static int __exit atmel_lcdfb_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info = dev_get_drvdata(dev); + struct atmel_lcdfb_info *sinfo = info->par; + + if (!sinfo) + return 0; + + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(0); + unregister_framebuffer(info); + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); + fb_dealloc_cmap(&info->cmap); + free_irq(sinfo->irq_base, info); + iounmap(sinfo->mmio); + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) { + iounmap(info->screen_base); + release_mem_region(info->fix.smem_start, info->fix.smem_len); + } else { + atmel_lcdfb_free_video_memory(sinfo); + } + + dev_set_drvdata(dev, NULL); + framebuffer_release(info); + + return 0; +} + +static struct platform_driver atmel_lcdfb_driver = { + .remove = __exit_p(atmel_lcdfb_remove), + .driver = { + .name = "atmel_lcdfb", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_lcdfb_init(void) +{ + return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe); +} + +static void __exit atmel_lcdfb_exit(void) +{ + platform_driver_unregister(&atmel_lcdfb_driver); +} + +module_init(atmel_lcdfb_init); +module_exit(atmel_lcdfb_exit); + +MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver"); +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@rfo.atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h index 90e7df22f50..685a754991c 100644 --- a/drivers/video/aty/ati_ids.h +++ b/drivers/video/aty/ati_ids.h @@ -204,6 +204,7 @@ #define PCI_CHIP_RV280_5961 0x5961 #define PCI_CHIP_RV280_5962 0x5962 #define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RS485_5975 0x5975 #define PCI_CHIP_RV280_5C61 0x5C61 #define PCI_CHIP_RV280_5C63 0x5C63 #define PCI_CHIP_R423_5D57 0x5D57 diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 8d3455da663..13990697b5c 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 = { @@ -2290,15 +2290,6 @@ static int __devinit aty_init(struct fb_info *info) init_waitqueue_head(&par->vblank.wait); spin_lock_init(&par->int_lock); -#ifdef CONFIG_PPC_PMAC - /* The Apple iBook1 uses non-standard memory frequencies. We detect it - * and set the frequency manually. */ - if (machine_is_compatible("PowerBook2,1")) { - par->pll_limits.mclk = 70; - par->pll_limits.xclk = 53; - } -#endif - #ifdef CONFIG_FB_ATY_GX if (!M64_HAS(INTEGRATED)) { u32 stat0; @@ -2383,6 +2374,14 @@ static int __devinit aty_init(struct fb_info *info) par->pll_limits.xclk = (par->pll_limits.xclk + 1) >> 1; } #endif +#ifdef CONFIG_PPC_PMAC + /* The Apple iBook1 uses non-standard memory frequencies. We detect it + * and set the frequency manually. */ + if (machine_is_compatible("PowerBook2,1")) { + par->pll_limits.mclk = 70; + par->pll_limits.xclk = 53; + } +#endif /* Allow command line to override clocks. */ if (pll) @@ -2914,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. */ @@ -2938,12 +2933,11 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, /* nothing */ ; j = i + 4; - par->mmap_map = kmalloc(j * sizeof(*par->mmap_map), GFP_ATOMIC); + par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC); if (!par->mmap_map) { PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n"); return -ENOMEM; } - memset(par->mmap_map, 0, j * sizeof(*par->mmap_map)); for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) { struct resource *rp = &pdev->resource[i]; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 2ce05019301..47ca62fe7c3 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 7ebffcdfd1e..7c922c7b460 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 dbf4ec3f6d5..03e57ef8837 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/backlight.c b/drivers/video/backlight/backlight.c index c65e81ff357..7e06223bca9 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -172,7 +172,7 @@ static struct class backlight_class = { #define DECLARE_ATTR(_name,_mode,_show,_store) \ { \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index e9bbc3455c9..1b3f6586bc9 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -174,7 +174,7 @@ static int cr_backlight_probe(struct platform_device *pdev) struct cr_panel *crp; u8 dev_en; - crp = kzalloc(sizeof(crp), GFP_KERNEL); + crp = kzalloc(sizeof(*crp), GFP_KERNEL); if (crp == NULL) return -ENOMEM; diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 6ef8f0a7a13..648b53c1fde 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -157,7 +157,7 @@ static struct class lcd_class = { #define DECLARE_ATTR(_name,_mode,_show,_store) \ { \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index af313bf1a2d..f48e8c534c8 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -292,7 +292,7 @@ static void __init chips_hw_init(void) write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); } -static struct fb_fix_screeninfo chipsfb_fix __initdata = { +static struct fb_fix_screeninfo chipsfb_fix __devinitdata = { .id = "C&T 65550", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, @@ -309,7 +309,7 @@ static struct fb_fix_screeninfo chipsfb_fix __initdata = { .smem_len = 0x100000, /* 1MB */ }; -static struct fb_var_screeninfo chipsfb_var __initdata = { +static struct fb_var_screeninfo chipsfb_var __devinitdata = { .xres = 800, .yres = 600, .xres_virtual = 800, @@ -330,7 +330,7 @@ static struct fb_var_screeninfo chipsfb_var __initdata = { .vsync_len = 8, }; -static void __init init_chips(struct fb_info *p, unsigned long addr) +static void __devinit init_chips(struct fb_info *p, unsigned long addr) { memset(p->screen_base, 0, 0x100000); diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c index 50b78af0fa2..dea6579941b 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 aa3935df852..49643969f9f 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 @@ -19,13 +19,6 @@ config VGA_CONSOLE Say Y. -# if [ "$CONFIG_PCI" = "y" -a "$CONFIG_VGA_CONSOLE" = "y" ]; then -# bool ' Allow VGA on any bus?' CONFIG_VGA_HOSE -# if [ "$CONFIG_VGA_HOSE" = "y" ]; then -# define_bool CONFIG_DUMMY_CONSOLE y -# fi -# fi - config VGACON_SOFT_SCROLLBACK bool "Enable Scrollback Buffer in System RAM" depends on VGA_CONSOLE @@ -125,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/Makefile b/drivers/video/console/Makefile index 9b26dda18a3..ac46cc3f6a2 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -47,7 +47,7 @@ targets := promcon_tbl.c quiet_cmd_conmakehash = CNMKHSH $@ cmd_conmakehash = scripts/conmakehash $< | \ sed -e '/\#include <[^>]*>/p' -e 's/types/init/' \ - -e 's/dfont\(_uni.*\]\)/promfont\1 __initdata/' > $@ + -e 's/dfont\(_uni.*\]\)/promfont\1 /' > $@ $(obj)/promcon_tbl.c: $(src)/prom.uni $(call cmd,conmakehash) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 73813c60d03..decfdc8eb9c 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/fbcon.h b/drivers/video/console/fbcon.h index 71f24e00fcd..8e6ef4bc7a5 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -176,7 +176,6 @@ extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); #endif extern void fbcon_set_bitops(struct fbcon_ops *ops); extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor); -extern struct class *fb_class; #define FBCON_ATTRIBUTE_UNDERLINE 1 #define FBCON_ATTRIBUTE_REVERSE 2 diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index 717b360d041..870017d4497 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -240,7 +240,7 @@ static void sti_flush(unsigned long from, unsigned long len) flush_icache_range(from, from+len); } -void __init +void __devinit sti_rom_copy(unsigned long base, unsigned long count, void *dest) { unsigned long dest_len = count; @@ -269,7 +269,7 @@ sti_rom_copy(unsigned long base, unsigned long count, void *dest) static char default_sti_path[21] __read_mostly; #ifndef MODULE -static int __init sti_setup(char *str) +static int __devinit sti_setup(char *str) { if (str) strlcpy (default_sti_path, str, sizeof (default_sti_path)); @@ -288,12 +288,12 @@ __setup("sti=", sti_setup); -static char __initdata *font_name[MAX_STI_ROMS] = { "VGA8x16", }; -static int __initdata font_index[MAX_STI_ROMS], - font_height[MAX_STI_ROMS], - font_width[MAX_STI_ROMS]; +static char __devinitdata *font_name[MAX_STI_ROMS] = { "VGA8x16", }; +static int __devinitdata font_index[MAX_STI_ROMS], + font_height[MAX_STI_ROMS], + font_width[MAX_STI_ROMS]; #ifndef MODULE -static int __init sti_font_setup(char *str) +static int __devinit sti_font_setup(char *str) { char *x; int i = 0; @@ -346,7 +346,7 @@ __setup("sti_font=", sti_font_setup); -static void __init +static void __devinit sti_dump_globcfg(struct sti_glob_cfg *glob_cfg, unsigned int sti_mem_request) { struct sti_glob_cfg_ext *cfg; @@ -386,7 +386,7 @@ sti_dump_globcfg(struct sti_glob_cfg *glob_cfg, unsigned int sti_mem_request) cfg->sti_mem_addr, sti_mem_request)); } -static void __init +static void __devinit sti_dump_outptr(struct sti_struct *sti) { DPRINTK((KERN_INFO @@ -400,7 +400,7 @@ sti_dump_outptr(struct sti_struct *sti) sti->outptr.attributes)); } -static int __init +static int __devinit sti_init_glob_cfg(struct sti_struct *sti, unsigned long rom_address, unsigned long hpa) { @@ -482,7 +482,7 @@ sti_init_glob_cfg(struct sti_struct *sti, } #ifdef CONFIG_FB -struct sti_cooked_font * __init +struct sti_cooked_font * __devinit sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) { const struct font_desc *fbfont; @@ -538,14 +538,14 @@ sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) return cooked_font; } #else -struct sti_cooked_font * __init +struct sti_cooked_font * __devinit sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) { return NULL; } #endif -struct sti_cooked_font * __init +struct sti_cooked_font * __devinit sti_select_font(struct sti_cooked_rom *rom, int (*search_font_fnc) (struct sti_cooked_rom *,int,int) ) { @@ -572,7 +572,7 @@ sti_select_font(struct sti_cooked_rom *rom, } -static void __init +static void __devinit sti_dump_rom(struct sti_rom *rom) { printk(KERN_INFO " id %04x-%04x, conforms to spec rev. %d.%02x\n", @@ -590,7 +590,7 @@ sti_dump_rom(struct sti_rom *rom) } -static int __init +static int __devinit sti_cook_fonts(struct sti_cooked_rom *cooked_rom, struct sti_rom *raw_rom) { @@ -625,7 +625,7 @@ sti_cook_fonts(struct sti_cooked_rom *cooked_rom, } -static int __init +static int __devinit sti_search_font(struct sti_cooked_rom *rom, int height, int width) { struct sti_cooked_font *font; @@ -642,7 +642,7 @@ sti_search_font(struct sti_cooked_rom *rom, int height, int width) #define BMODE_RELOCATE(offset) offset = (offset) / 4; #define BMODE_LAST_ADDR_OFFS 0x50 -static void * __init +static void * __devinit sti_bmode_font_raw(struct sti_cooked_font *f) { unsigned char *n, *p, *q; @@ -660,7 +660,7 @@ sti_bmode_font_raw(struct sti_cooked_font *f) return n + 3; } -static void __init +static void __devinit sti_bmode_rom_copy(unsigned long base, unsigned long count, void *dest) { unsigned long dest_len = count; @@ -675,7 +675,7 @@ sti_bmode_rom_copy(unsigned long base, unsigned long count, void *dest) sti_flush(dest_start, dest_len); } -static struct sti_rom * __init +static struct sti_rom * __devinit sti_get_bmode_rom (unsigned long address) { struct sti_rom *raw; @@ -711,7 +711,7 @@ sti_get_bmode_rom (unsigned long address) return raw; } -struct sti_rom * __init +struct sti_rom * __devinit sti_get_wmode_rom (unsigned long address) { struct sti_rom *raw; @@ -727,7 +727,7 @@ sti_get_wmode_rom (unsigned long address) return raw; } -int __init +int __devinit sti_read_rom(int wordmode, struct sti_struct *sti, unsigned long address) { struct sti_cooked_rom *cooked; @@ -783,7 +783,7 @@ out_err: return 0; } -static struct sti_struct * __init +static struct sti_struct * __devinit sti_try_rom_generic(unsigned long address, unsigned long hpa, struct pci_dev *pd) { struct sti_struct *sti; @@ -898,7 +898,7 @@ out_err: return NULL; } -static void __init sticore_check_for_default_sti(struct sti_struct *sti, char *path) +static void __devinit sticore_check_for_default_sti(struct sti_struct *sti, char *path) { if (strcmp (path, default_sti_path) == 0) default_sti = sti; @@ -909,7 +909,7 @@ static void __init sticore_check_for_default_sti(struct sti_struct *sti, char *p * in the additional address field addr[1] while on * older Systems the PDC stores it in page0->proc_sti */ -static int __init sticore_pa_init(struct parisc_device *dev) +static int __devinit sticore_pa_init(struct parisc_device *dev) { char pa_path[21]; struct sti_struct *sti = NULL; @@ -1015,7 +1015,7 @@ static struct parisc_driver pa_sti_driver = { static int sticore_initialized __read_mostly; -static void __init sti_init_roms(void) +static void __devinit sti_init_roms(void) { if (sticore_initialized) return; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 2460b82a1d9..d18b73aafa0 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); @@ -368,9 +372,14 @@ static const char *vgacon_startup(void) #endif } + /* SCREEN_INFO initialized? */ + if ((ORIG_VIDEO_MODE == 0) && + (ORIG_VIDEO_LINES == 0) && + (ORIG_VIDEO_COLS == 0)) + goto no_vga; + /* VGA16 modes are not handled by VGACON */ - if ((ORIG_VIDEO_MODE == 0x00) || /* SCREEN_INFO not initialized */ - (ORIG_VIDEO_MODE == 0x0D) || /* 320x200/4 */ + if ((ORIG_VIDEO_MODE == 0x0D) || /* 320x200/4 */ (ORIG_VIDEO_MODE == 0x0E) || /* 640x200/4 */ (ORIG_VIDEO_MODE == 0x10) || /* 640x350/4 */ (ORIG_VIDEO_MODE == 0x12) || /* 640x480/4 */ diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 8b762739b1e..b0be7eac32d 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 7a6eeda5ae9..30ede6e8830 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 94a66c2d2cf..e23324d10be 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 ca2c54ce508..33be46ccb54 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 38c2e2558f5..215ac579f90 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/ffb.c b/drivers/video/ffb.c index 1d4e8354b56..3f6c98fad43 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -656,7 +656,7 @@ static int ffb_setcolreg(unsigned regno, { u32 value; - if (regno >= 256) + if (regno >= 16) return 1; red >>= 8; @@ -903,7 +903,7 @@ ffb_init_fix(struct fb_info *info) struct all_info { struct fb_info info; struct ffb_par par; - u32 pseudo_palette[256]; + u32 pseudo_palette[16]; }; static int ffb_init_one(struct of_device *op) diff --git a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c index 70ff55b1459..6c91c61cdb6 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 bf0e60b5a3b..b9b572b293d 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 889e4ea5edc..328ae6c673e 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 eb1a4812ad1..b87ea21d3d7 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/imxfb.c b/drivers/video/imxfb.c index 267c1ff9ebd..a1258989859 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -394,26 +394,18 @@ static void imxfb_setup_gpio(struct imxfb_info *fbi) /* initialize GPIOs */ imx_gpio_mode(PD6_PF_LSCLK); - imx_gpio_mode(PD10_PF_SPL_SPR); imx_gpio_mode(PD11_PF_CONTRAST); imx_gpio_mode(PD14_PF_FLM_VSYNC); imx_gpio_mode(PD13_PF_LP_HSYNC); - imx_gpio_mode(PD7_PF_REV); - imx_gpio_mode(PD8_PF_CLS); - -#ifndef CONFIG_MACH_PIMX1 - /* on PiMX1 used as buffers enable signal - */ - imx_gpio_mode(PD9_PF_PS); -#endif - -#ifndef CONFIG_MACH_MX1FS2 - /* on mx1fs2 this pin is used to (de)activate the display, so we need - * it as a normal gpio - */ imx_gpio_mode(PD12_PF_ACD_OE); -#endif + /* These are only needed for Sharp HR TFT displays */ + if (fbi->pcr & PCR_SHARP) { + imx_gpio_mode(PD7_PF_REV); + imx_gpio_mode(PD8_PF_CLS); + imx_gpio_mode(PD9_PF_PS); + imx_gpio_mode(PD10_PF_SPL_SPR); + } } #ifdef CONFIG_PM @@ -476,7 +468,6 @@ static int __init imxfb_init_fbinfo(struct device *dev) info->fbops = &imxfb_ops; info->flags = FBINFO_FLAG_DEFAULT; - info->pseudo_palette = (fbi + 1); fbi->rgb[RGB_16] = &def_rgb_16; fbi->rgb[RGB_8] = &def_rgb_8; @@ -499,6 +490,7 @@ static int __init imxfb_init_fbinfo(struct device *dev) info->var.sync = inf->sync; info->var.grayscale = inf->cmap_greyscale; fbi->cmap_inverse = inf->cmap_inverse; + fbi->cmap_static = inf->cmap_static; fbi->pcr = inf->pcr; fbi->lscr1 = inf->lscr1; fbi->dmacr = inf->dmacr; diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index 80b94c19a9f..6148300fadd 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 ab5285a7f1d..1d3f2080aa6 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 9397bcef301..9de1c114f80 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 b985dfad6c6..a5fc4edf84e 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 80c03618eb5..2b0f799aa8d 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 00000000000..d36ad624a79 --- /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 f7d647dda97..aa8c714d624 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 ab2149531a0..083f60321ed 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 babeb81f467..b86ba08aac9 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 c57aaadf410..3660d2673bd 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 c8559a756b7..86ca7b17900 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 9c25c2f7966..d59577c8de8 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 03ae55b168f..4b3344e0369 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 608e40bb20e..1005582e843 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 5d29a26b8cd..de0d755f901 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/neofb.c b/drivers/video/neofb.c index bd30aba242d..731d7a5c5aa 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -1286,34 +1286,36 @@ static int neofb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, if (regno >= fb->cmap.len || regno > 255) return -EINVAL; - switch (fb->var.bits_per_pixel) { - case 8: + if (fb->var.bits_per_pixel <= 8) { outb(regno, 0x3c8); outb(red >> 10, 0x3c9); outb(green >> 10, 0x3c9); outb(blue >> 10, 0x3c9); - break; - case 16: - ((u32 *) fb->pseudo_palette)[regno] = + } else if (regno < 16) { + switch (fb->var.bits_per_pixel) { + case 16: + ((u32 *) fb->pseudo_palette)[regno] = ((red & 0xf800)) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); - break; - case 24: - ((u32 *) fb->pseudo_palette)[regno] = + break; + case 24: + ((u32 *) fb->pseudo_palette)[regno] = ((red & 0xff00) << 8) | ((green & 0xff00)) | ((blue & 0xff00) >> 8); - break; + break; #ifdef NO_32BIT_SUPPORT_YET - case 32: - ((u32 *) fb->pseudo_palette)[regno] = + case 32: + ((u32 *) fb->pseudo_palette)[regno] = ((transp & 0xff00) << 16) | ((red & 0xff00) << 8) | ((green & 0xff00)) | ((blue & 0xff00) >> 8); - break; + break; #endif - default: - return 1; + default: + return 1; + } } + return 0; } diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c index c627955aa12..d1a10549f54 100644 --- a/drivers/video/nvidia/nv_hw.c +++ b/drivers/video/nvidia/nv_hw.c @@ -149,7 +149,8 @@ static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk, pll = NV_RD32(par->PMC, 0x4024); M = pll & 0xFF; N = (pll >> 8) & 0xFF; - if (((par->Chipset & 0xfff0) == 0x0290) || ((par->Chipset & 0xfff0) == 0x0390) || ((par->Chipset & 0xfff0) == 0x02E0)) { + if (((par->Chipset & 0xfff0) == 0x0290) || + ((par->Chipset & 0xfff0) == 0x0390)) { MB = 1; NB = 1; } else { @@ -159,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; @@ -890,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) || @@ -937,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); @@ -949,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++) { @@ -962,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); @@ -1204,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, @@ -1248,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); @@ -1267,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, @@ -1275,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); @@ -1353,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, @@ -1405,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, @@ -1493,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); @@ -1564,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) @@ -1629,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 707e2c8a13e..82579d3a997 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 38f7cc0a233..2fdf77ec39f 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 f85edf084da..a7fe214f0f7 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -37,7 +37,6 @@ #include "nv_proto.h" #include "nv_dma.h" -#undef CONFIG_FB_NVIDIA_DEBUG #ifdef CONFIG_FB_NVIDIA_DEBUG #define NVTRACE printk #else @@ -675,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; @@ -683,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; @@ -1194,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); @@ -1239,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 885b42836cb..452433d4697 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 00000000000..7f4d25b8a18 --- /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 00000000000..99da8b6d2c3 --- /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 00000000000..e682940a97a --- /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 00000000000..f4c23434de6 --- /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 00000000000..eb1512b56ce --- /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 00000000000..dc48e02f215 --- /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 00000000000..51807b4e26d --- /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 00000000000..fd6f0eb16de --- /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 00000000000..551f385861d --- /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 00000000000..95604ca4330 --- /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 00000000000..a38038840fd --- /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 00000000000..52bdfdac42c --- /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 00000000000..4bb349f5435 --- /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 00000000000..ea6170ddff3 --- /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 00000000000..c4f306a4e5c --- /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 00000000000..9085188d815 --- /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 00000000000..adb731e5314 --- /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 00000000000..14d0f7a1114 --- /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 00000000000..2b4269813b2 --- /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 00000000000..81dbcf53cf0 --- /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 e64f8b5d005..8503e733a17 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 1ac5264bb2c..10c0cc6e93f 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,20 +182,22 @@ 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; - } - mb(); + } + wmb(); pm2_WR(p, index, v); + wmb(); } static inline void pm2v_RDAC_WR(struct pm2fb_par* p, s32 idx, u32 v) { pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); - mb(); + wmb(); pm2_WR(p, PM2VR_RD_INDEXED_DATA, v); + wmb(); } #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) { @@ -204,21 +206,10 @@ static inline void WAIT_FIFO(struct pm2fb_par* p, u32 a) } #endif -static void wait_pm2(struct pm2fb_par* par) { - - WAIT_FIFO(par, 1); - pm2_WR(par, PM2R_SYNC, 0); - mb(); - do { - while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0); - rmb(); - } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); -} - /* * 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; @@ -366,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)); @@ -376,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); @@ -477,11 +468,9 @@ static void set_memclock(struct pm2fb_par* par, u32 clk) WAIT_FIFO(par, 8); pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_MCLK_CONTROL >> 8); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 0); - wmb(); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_PRESCALE, m); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_FEEDBACK, n); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_POSTSCALE, p); - wmb(); pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 1); rmb(); for (i = 256; @@ -494,12 +483,9 @@ static void set_memclock(struct pm2fb_par* par, u32 clk) pm2_mnp(clk, &m, &n, &p); WAIT_FIFO(par, 10); pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 6); - wmb(); pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_1, m); pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_2, n); - wmb(); pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 8|p); - wmb(); pm2_RDAC_RD(par, PM2I_RD_MEMORY_CLOCK_STATUS); rmb(); for (i = 256; @@ -520,12 +506,9 @@ static void set_pixclock(struct pm2fb_par* par, u32 clk) pm2_mnp(clk, &m, &n, &p); WAIT_FIFO(par, 8); pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 0); - wmb(); pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A1, m); pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A2, n); - wmb(); pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 8|p); - wmb(); pm2_RDAC_RD(par, PM2I_RD_PIXEL_CLOCK_STATUS); rmb(); for (i = 256; @@ -552,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 @@ -591,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. @@ -632,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; @@ -689,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. @@ -726,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; @@ -739,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); @@ -754,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"); @@ -795,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, @@ -864,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, @@ -923,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: @@ -933,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... */ @@ -957,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; @@ -973,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, @@ -997,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) @@ -1050,20 +1033,36 @@ static int pm2fb_blank(int blank_mode, struct fb_info *info) return 0; } +static int pm2fb_sync(struct fb_info *info) +{ + struct pm2fb_par *par = info->par; + + WAIT_FIFO(par, 1); + pm2_WR(par, PM2R_SYNC, 0); + mb(); + do { + while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0) + udelay(10); + rmb(); + } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); + + return 0; +} + /* * block operation. copy=0: rectangle fill, copy=1: rectangle copy. */ -static void pm2fb_block_op(struct pm2fb_par* par, int copy, +static void pm2fb_block_op(struct fb_info* info, int copy, s32 xsrc, s32 ysrc, s32 x, s32 y, s32 w, s32 h, u32 color) { + struct pm2fb_par *par = info->par; if (!w || !h) return; - WAIT_FIFO(par, 6); + WAIT_FIFO(par, 5); pm2_WR(par, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE | PM2F_CONFIG_FB_READ_SOURCE_ENABLE); - pm2_WR(par, PM2R_FB_PIXEL_OFFSET, 0); if (copy) pm2_WR(par, PM2R_FB_SOURCE_DELTA, ((ysrc-y) & 0xfff) << 16 | ((xsrc-x) & 0xfff)); @@ -1072,17 +1071,15 @@ static void pm2fb_block_op(struct pm2fb_par* par, 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)); - wait_pm2(par); } static void pm2fb_fillrect (struct fb_info *info, const struct fb_fillrect *region) { - struct pm2fb_par *par = info->par; struct fb_fillrect modded; int vxres, vyres; u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? @@ -1116,7 +1113,7 @@ static void pm2fb_fillrect (struct fb_info *info, color |= color << 16; if(info->var.bits_per_pixel != 24) - pm2fb_block_op(par, 0, 0, 0, + pm2fb_block_op(info, 0, 0, 0, modded.dx, modded.dy, modded.width, modded.height, color); else @@ -1126,7 +1123,6 @@ static void pm2fb_fillrect (struct fb_info *info, static void pm2fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) { - struct pm2fb_par *par = info->par; struct fb_copyarea modded; u32 vxres, vyres; @@ -1156,7 +1152,7 @@ static void pm2fb_copyarea(struct fb_info *info, if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; - pm2fb_block_op(par, 1, modded.sx, modded.sy, + pm2fb_block_op(info, 1, modded.sx, modded.sy, modded.dx, modded.dy, modded.width, modded.height, 0); } @@ -1177,6 +1173,7 @@ static struct fb_ops pm2fb_ops = { .fb_fillrect = pm2fb_fillrect, .fb_copyarea = pm2fb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_sync = pm2fb_sync, }; /* @@ -1237,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") ) { @@ -1320,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; @@ -1351,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: @@ -1377,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); @@ -1405,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); @@ -1426,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 bd787e80177..5b3f54c0918 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -1,55 +1,25 @@ /* * linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device - * - * Copyright (C) 2001 Romain Dolbeau <dolbeau@irisa.fr> + * + * Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>. + * + * Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl> + * based on pm2fb.c + * * Based on code written by: - * Sven Luther, <luther@dpt-info.u-strasbg.fr> - * Alan Hourihane, <alanh@fairlite.demon.co.uk> - * Russell King, <rmk@arm.linux.org.uk> + * Sven Luther, <luther@dpt-info.u-strasbg.fr> + * Alan Hourihane, <alanh@fairlite.demon.co.uk> + * Russell King, <rmk@arm.linux.org.uk> * Based on linux/drivers/video/skeletonfb.c: * Copyright (C) 1997 Geert Uytterhoeven * Based on linux/driver/video/pm2fb.c: - * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) - * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) + * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * - * $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $ - * - * CHANGELOG: - * Mon Feb 11 10:35:48 MET 2002, v 1.4.11B: Cosmetic update. - * Wed Jan 23 14:16:59 MET 2002, v 1.4.11: Preliminary 2.5.x support, patch for 2.5.2. - * Wed Nov 28 11:08:29 MET 2001, v 1.4.10: potential bug fix for SDRAM-based board, patch for 2.4.16. - * Thu Sep 20 10:24:42 MET DST 2001, v 1.4.9: sync bug fix, preliminary flatpanel support, better timings. - * Tue Aug 28 10:13:01 MET DST 2001, v 1.4.8: memory timings check, minor bug fixes. - * Wed Jul 18 19:06:14 CEST 2001, v 1.4.7: Mode fix (800x600-100, 1024x768-100 changed), using HW panning + accel bug fix. - * Mon Jun 25 10:33:56 MET DST 2001, v 1.4.6: Depth 12 fix, chip reset ioctl, moved memory erase ioctl to DEBUG. - * Wed Jun 20 11:13:08 MET DST 2001, v 1.4.5: Fixed missing blinking cursor in 8bpp, code cleaning, memory erase IOCTL. - * Mon Jun 18 16:00:27 CEST 2001, v 1.4.4: Depth 12 (RGBA 4444) support, code cleaning. - * Fri Jun 15 13:53:01 CEST 2001, v 1.4.3: Removed warnings, depth 15 support, add 'depth' option. - * Thu Jun 14 10:13:52 MET DST 2001, v 1.4.2: Fixed depth switching bug, preliminary 15bpp (RGB5551) support. - * Thu Apr 12 11:16:45 MET DST 2001, v 1.4.1B: Doc updates. - * Fri Apr 6 11:12:53 MET DST 2001, v 1.4.1: Configure.help, minor cleanup - * Thu Mar 29 10:56:50 MET DST 2001, v 1.4.0: Module & module options support (note: linux patch changed, 2.2.19 added). - * Thu Mar 15 15:30:31 MET 2001, v 1.3.2: Fixed mirroring bug on little-endian. - * Wed Mar 14 21:25:54 CET 2001, v 1.3.1: Fixed bug in BlockMove (_bmov). - * Tue Mar 13 10:53:19 MET 2001, v 1.3.0: Character drawing hardware support (in all width between 1 and 16), fixes. - * Thu Mar 8 10:20:16 MET 2001, v 1.2.2: Better J2000 support, "font:" option. - * Tue Mar 6 21:25:04 CET 2001, v 1.2.1: Better acceleration support. - * Mon Mar 5 21:54:17 CET 2001, v 1.2.0: Partial acceleration support (clear & bmove) - * Mon Mar 5 12:52:15 CET 2001, v 1.1.3: Big pan_display fix. - * Sun Mar 4 22:21:50 CET 2001, v 1.1.2: (numerous) bug fixes. - * Fri Mar 2 15:54:07 CET 2001, v 1.1.1: Might have Appian J2000 support, resource mangement in 2.4 - * Wed Feb 28 18:21:35 CET 2001, v 1.1.0: Might have multiple boards support (added, but not yest tested) - * Tue Feb 27 17:31:12 CET 2001, v 1.0.6: fixes boot-time mode select, add more default mode - * Tue Feb 27 14:01:36 CET 2001, v 1.0.5: fixes (1.0.4 was broken for 2.2), cleaning up - * Mon Feb 26 23:17:36 CET 2001, v 1.0.4: preliminary 2.4.x support, dropped (useless on pm3) partial product, more OF fix - * Mon Feb 26 20:59:05 CET 2001, v 1.0.3: No more shadow register (and wasted memory), endianess fix, use OF-preset resolution by default - * Wed Feb 21 22:09:30 CET 2001, v 1.0.2: Code cleaning for future multiboard support, better OF support, bugs fix - * Wed Feb 21 19:58:56 CET 2001, v 1.0.1: OpenFirmware support, fixed memory detection, better debug support, code cleaning - * Wed Feb 21 14:47:06 CET 2001, v 1.0.0: First working version */ #include <linux/module.h> @@ -58,1057 +28,454 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/delay.h> -#include <linux/interrupt.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/ctype.h> - -#include <video/fbcon.h> -#include <video/fbcon-mfb.h> -#include <video/fbcon-cfb2.h> -#include <video/fbcon-cfb4.h> -#include <video/fbcon-cfb8.h> -#include <video/fbcon-cfb16.h> -#include <video/fbcon-cfb24.h> -#include <video/fbcon-cfb32.h> -#include <video/pm3fb.h> - -#include <asm/io.h> -#include <asm/uaccess.h> - -#ifdef CONFIG_FB_OF -#include <asm/prom.h> -#endif - -/* ************************************* */ -/* ***** The various "global" data ***** */ -/* ************************************* */ - -/* those will need a rework for multiple board support */ -/* Driver name */ -static const char permedia3_name[16] = "Permedia3"; - -/* the fb_par struct, mandatory */ -struct pm3fb_par { - u32 pixclock; /* pixclock in KHz */ - - u32 width; /* width of virtual screen */ - u32 height; /* height of virtual screen */ - - u32 hsstart; /* horiz. sync start */ - u32 hsend; /* horiz. sync end */ - u32 hbend; /* horiz. blank end (also gate end) */ - u32 htotal; /* total width (w/ sync & blank) */ - u32 vsstart; /* vert. sync start */ - u32 vsend; /* vert. sync end */ - u32 vbend; /* vert. blank end */ - u32 vtotal; /* total height (w/ sync & blank) */ - - u32 stride; /* screen stride */ - u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ - /* NOTE : unlike other pm3 stuff above, stored *after* shiftbpp. don't ask */ - u32 depth; /* screen depth (8, 12, 15, 16 or 32) */ - u32 video; /* video control (hsync,vsync) */ -}; +#include <video/pm3fb.h> -/* memory timings */ -struct pm3fb_timings -{ - unsigned long caps; - unsigned long timings; - unsigned long control; - unsigned long refresh; - unsigned long powerdown; -}; -typedef enum pm3fb_timing_result { pm3fb_timing_ok, pm3fb_timing_problem, pm3fb_timing_retry } pm3fb_timing_result; -#define PM3FB_UNKNOWN_TIMING_VALUE ((unsigned long)-1) -#define PM3FB_UNKNOWN_TIMINGS { PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE } - -/* the fb_info struct, mandatory */ -struct pm3fb_info { - struct fb_info_gen gen; - unsigned long board_num; /* internal board number */ - unsigned long use_current; - struct pm3fb_par *current_par; - struct pci_dev *dev; /* PCI device */ - unsigned long board_type; /* index in the cardbase */ - unsigned char *fb_base; /* framebuffer memory base */ - u32 fb_size; /* framebuffer memory size */ - unsigned char *p_fb; /* physical address of frame buffer */ - unsigned char *v_fb; /* virtual address of frame buffer */ - unsigned char *pIOBase; /* physical address of registers region, must be rg_base or rg_base+PM2_REGS_SIZE depending on the host endianness */ - unsigned char *vIOBase; /* address of registers after ioremap() */ - struct { - u8 transp; - u8 red; - u8 green; - u8 blue; - } palette[256]; - union { -#ifdef FBCON_HAS_CFB16 - u16 cmap12[16]; /* RGBA 4444 */ - u16 cmap15[16]; /* RGBA 5551 */ - u16 cmap16[16]; /* RGBA 5650 */ -#endif -#ifdef FBCON_HAS_CFB32 - u32 cmap32[16]; +#if !defined(CONFIG_PCI) +#error "Only generic PCI cards supported." #endif - } cmap; - struct pm3fb_timings memt; -}; - -/* regular resolution database*/ -static struct { - char name[16]; - struct pm3fb_par user_mode; -} mode_base[] __initdata = { - { - "default-800x600", { - 49500, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74-32", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 32, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_32BIT}}, -/* Generated mode : "1600x1024", for the SGI 1600SW flat panel*/ - { - "SGI1600SW", { - 108000, 1600, 1024, 16, 56, 104, 1704, 3, 6, 32, - 1056, 1600, 0, 8, - PM3VideoControl_ENABLE| - PM3VideoControl_HSYNC_ACTIVE_LOW|PM3VideoControl_VSYNC_ACTIVE_LOW| - PM3VideoControl_PIXELSIZE_32BIT}}, -/* ##### auto-generated mode, by fbtimings2pm3 */ -/* Generated mode : "640x480-60" */ - { - "640x480-60", { - 25174, 640, 480, 16, 112, 160, 800, 10, 12, 45, - 525, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-72" */ - { - "640x480-72", { - 31199, 640, 480, 24, 64, 192, 832, 9, 12, 40, 520, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-75" */ - { - "640x480-75", { - 31499, 640, 480, 16, 80, 200, 840, 1, 4, 20, 500, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-90" */ - { - "640x480-90", { - 39909, 640, 480, 32, 72, 192, 832, 25, 39, 53, 533, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-100" */ - { - "640x480-100", { - 44899, 640, 480, 32, 160, 208, 848, 22, 34, 51, - 531, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-48-lace" */ -/* INTERLACED NOT SUPPORTED - {"800x600-48-lace", {35999, 800, 600, 80, 208, 264, 1064, 11, 23, 102, 702, 800, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "800x600-56" */ - { - "800x600-56", { - 35999, 800, 600, 24, 96, 224, 1024, 1, 3, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-60" */ - { - "800x600-60", { - 40000, 800, 600, 40, 168, 256, 1056, 1, 5, 28, 628, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-70" */ - { - "800x600-70", { - 44899, 800, 600, 24, 168, 208, 1008, 9, 21, 36, - 636, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-72" */ - { - "800x600-72", { - 50000, 800, 600, 56, 176, 240, 1040, 37, 43, 66, - 666, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-75" */ - { - "800x600-75", { - 49497, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-90" */ - { - "800x600-90", { - 56637, 800, 600, 8, 72, 192, 992, 8, 19, 35, 635, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-100", from /etc/fb.modes */ -/* DISABLED, hsstart == 0 - { - "800x600-100", { - 67499, 800, 600, 0, 64, 280, 1080, 7, 11, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "800x600-100", from ??? */ - { - "800x600-100", { - 69650, 800, 600, 64, 128, 288, 1088, 4, 10, 40, 640, 800, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1024x768-43-lace", {44899, 1024, 768, 8, 184, 240, 1264, 1, 9, 49, 817, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1024x768-60" */ - { - "1024x768-60", { - 64998, 1024, 768, 24, 160, 320, 1344, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-70" */ - { - "1024x768-70", { - 74996, 1024, 768, 24, 160, 304, 1328, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-72" */ - { - "1024x768-72", { - 74996, 10224, 768, 24, 160, 264, 10488, 3, 9, 38, - 806, 10224, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-75" */ - { - "1024x768-75", { - 78746, 1024, 768, 16, 112, 288, 1312, 1, 4, 32, - 800, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-90" */ - { - "1024x768-90", { - 100000, 1024, 768, 0, 96, 288, 1312, 21, 36, 77, - 845, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-100", from /etc/fb.modes */ -/* DISABLED, vsstart == 0 - { - "1024x768-100", { - 109998, 1024, 768, 0, 88, 368, 1392, 0, 8, 24, 792, - 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "1024x768-100", from ??? */ - { - "1024x768-100", { - 115500, 1024, 768, 32, 224, 416, 1440, 3, 13, 34, 802, 1024, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-43-lace", {64998, 1152, 864, 72, 200, 264, 1416, 78, 87, 191, 1055, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-47-lace", {64998, 1152, 864, 88, 216, 296, 1448, 30, 39, 83, 947, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-60" */ - { - "1152x864-60", { - 80000, 1152, 864, 64, 176, 304, 1456, 6, 11, 52, - 916, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-70" */ - { - "1152x864-70", { - 100000, 1152, 864, 40, 192, 360, 1512, 13, 24, 81, - 945, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-75" */ - { - "1152x864-75", { - 109998, 1152, 864, 24, 168, 312, 1464, 45, 53, 138, - 1002, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-80" */ - { - "1152x864-80", { - 109998, 1152, 864, 16, 128, 288, 1440, 30, 37, 94, - 958, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-43-lace", {80000, 1024, 1024, 80, 160, 320, 1344, 50, 60, 125, 1149, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-47-lace", {80000, 1280, 1024, 80, 160, 320, 1600, 1, 11, 29, 1053, 1280, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-60" */ - { - "1280x1024-60", { - 107991, 1280, 1024, 48, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-70" */ - { - "1280x1024-70", { - 125992, 1280, 1024, 80, 192, 408, 1688, 1, 6, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-74" */ - { - "1280x1024-74", { - 134989, 1280, 1024, 32, 176, 432, 1712, 0, 30, 40, - 1064, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-75" */ - { - "1280x1024-75", { - 134989, 1280, 1024, 16, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-60" */ - { - "1600x1200-60", { - 155981, 1600, 1200, 32, 192, 448, 2048, 10, 18, 70, - 1270, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-66" */ - { - "1600x1200-66", { - 171998, 1600, 1200, 40, 176, 480, 2080, 3, 6, 53, - 1253, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-76" */ - { - "1600x1200-76", { - 197980, 1600, 1200, 40, 176, 480, 2080, 3, 8, 50, - 1250, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* ##### end of auto-generated mode */ - { - "\0",} -}; -/* more mandatory stuff (see skeletonfb.c + framebuffer driver HOWTO */ -static struct pm3fb_info fb_info[PM3_MAX_BOARD]; -static struct pm3fb_par current_par[PM3_MAX_BOARD]; -static int current_par_valid[PM3_MAX_BOARD]; -/* to allow explicit filtering of board */ -short bus[PM3_MAX_BOARD]; -short slot[PM3_MAX_BOARD]; -short func[PM3_MAX_BOARD]; -short disable[PM3_MAX_BOARD]; -short noaccel[PM3_MAX_BOARD]; -char fontn[PM3_MAX_BOARD][PM3_FONTNAME_SIZE]; -short depth[PM3_MAX_BOARD]; -short flatpanel[PM3_MAX_BOARD]; -static struct display disp[PM3_MAX_BOARD]; -static char g_options[PM3_OPTIONS_SIZE] __initdata = "pm3fb,dummy"; -short printtimings = 0; -short forcesize[PM3_MAX_BOARD]; - -/* ********************* */ -/* ***** prototype ***** */ -/* ********************* */ -/* card-specific */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info); -/* permedia3-specific */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info); -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info); -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info); -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r); -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ); -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc); -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b); -static void pm3fb_common_init(struct pm3fb_info *l_fb_info); -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info); -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info); -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info); +#undef PM3FB_MASTER_DEBUG +#ifdef PM3FB_MASTER_DEBUG +#define DPRINTK(a,b...) printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) #endif -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info); -static void pm3fb_write_mode(struct pm3fb_info *l_fb_info); -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar); -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info); -/* accelerated permedia3-specific */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info); -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info); -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width); -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx); -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx); -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy); -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* pre-init */ -static void pm3fb_mode_setup(char *mode, unsigned long board_num); -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num); -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn); -static void pm3fb_real_setup(char *options); -/* fbdev */ -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info); -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info); -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d); -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info); -static void pm3fb_get_par(void *par, struct fb_info_gen *info); -static void pm3fb_set_par(const void *par, struct fb_info_gen *info); -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b); -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info); -static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info); -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info); -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info); -static void pm3fb_detect(void); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info); -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg); - - -/* the struct that hold them together */ -struct fbgen_hwswitch pm3fb_switch = { - pm3fb_detect, pm3fb_encode_fix, pm3fb_decode_var, pm3fb_encode_var, - pm3fb_get_par, pm3fb_set_par, pm3fb_getcolreg, - pm3fb_pan_display, pm3fb_blank, pm3fb_set_disp -}; - -static struct fb_ops pm3fb_ops = { - .owner = THIS_MODULE, - .fb_get_fix = fbgen_get_fix, - .fb_get_var = fbgen_get_var, - .fb_set_var = fbgen_set_var, - .fb_get_cmap = fbgen_get_cmap, - .fb_set_cmap = fbgen_set_cmap, - .fb_setcolreg = pm3fb_setcolreg, - .fb_pan_display =fbgen_pan_display, - .fb_blank = fbgen_blank, - .fb_ioctl = pm3fb_ioctl, -}; -#ifdef PM3FB_USE_ACCEL -#ifdef FBCON_HAS_CFB32 -static struct display_switch pm3fb_cfb32 = { - fbcon_cfb32_setup, pm3fb_cfbX_bmove, pm3fb_cfb32_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb32_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static struct display_switch pm3fb_cfb16 = { - fbcon_cfb16_setup, pm3fb_cfbX_bmove, pm3fb_cfb16_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb16_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static struct display_switch pm3fb_cfb8 = { - fbcon_cfb8_setup, pm3fb_cfbX_bmove, pm3fb_cfb8_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb8_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB8 */ -#endif /* PM3FB_USE_ACCEL */ - -/* ****************************** */ -/* ***** card-specific data ***** */ -/* ****************************** */ -struct pm3fb_card_timings { - unsigned long memsize; /* 0 for last value (i.e. default) */ - struct pm3fb_timings memt; -}; +/* + * Driver data + */ +static char *mode_option __devinitdata; -static struct pm3fb_card_timings t_FormacProFormance3[] = { - { 16, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} }, - { 0, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} } /* from 16 MB PF3 */ +/* + * This structure defines the hardware state of the graphics card. Normally + * you place this in a header file in linux/include/video. This file usually + * also includes register information. That allows other driver subsystems + * and userland applications the ability to use the same header file to + * avoid duplicate work and easy porting of software. + */ +struct pm3_par { + unsigned char __iomem *v_regs;/* virtual address of p_regs */ + u32 video; /* video flags before blanking */ + u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ + u32 palette[16]; }; -static struct pm3fb_card_timings t_AppianJeronimo2000[] = { - { 32, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} }, - { 0, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} } /* from 32MB J2000 */ +/* + * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo + * if we don't use modedb. If we do use modedb see pm3fb_init how to use it + * to get a fb_var_screeninfo. Otherwise define a default var as well. + */ +static struct fb_fix_screeninfo pm3fb_fix __devinitdata = { + .id = "Permedia3", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_3DLABS_PERMEDIA3, }; -static struct pm3fb_card_timings t_3DLabsOxygenVX1[] = { - { 32, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} }, - { 0, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} } /* from 32MB VX1 */ -}; +/* + * Utility functions + */ -static struct { - char cardname[32]; /* recognized card name */ - u16 subvendor; /* subvendor of the card */ - u16 subdevice; /* subdevice of the card */ - u8 func; /* function of the card to which the extra init apply */ - void (*specific_setup)(struct pm3fb_info *l_fb_info); /* card/func specific setup, done before _any_ FB access */ - struct pm3fb_card_timings *c_memt; /* defauls timings for the boards */ -} cardbase[] = { - { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, NULL }, - { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL, - t_AppianJeronimo2000 - }, - { "Appian Jeronimo 2000 head 2", 0x1097, 0x3d32, 2, pm3fb_j2000_setup, - t_AppianJeronimo2000 - }, - { "Formac ProFormance 3", PCI_VENDOR_ID_3DLABS, 0x000a, 0, NULL, /* Formac use 3DLabs ID ?!? */ - t_FormacProFormance3 - }, - { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, NULL }, - { "3DLabs Oxygen VX1 PCI", PCI_VENDOR_ID_3DLABS, 0x0121, 0, NULL, - t_3DLabsOxygenVX1 - }, - { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, NULL }, - { "\0", 0x0, 0x0, 0, NULL, NULL } -}; +static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off) +{ + return fb_readl(par->v_regs + off); +} -/* ********************************** */ -/* ***** card-specific function ***** */ -/* ********************************** */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info) -{ /* the appian j2000 require more initialization of the second head */ - /* l_fb_info must point to the _second_ head of the J2000 */ - - DTRACE; - - l_fb_info->memt = t_AppianJeronimo2000[0].memt; /* 32 MB, first and only j2000 ? */ - - pm3fb_write_memory_timings(l_fb_info); +static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v) +{ + fb_writel(v, par->v_regs + off); } -/* *************************************** */ -/* ***** permedia3-specific function ***** */ -/* *************************************** */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_WAIT(struct pm3_par *par, u32 n) { - l_fb_info->memt.caps = PM3_READ_REG(PM3LocalMemCaps); - l_fb_info->memt.timings = PM3_READ_REG(PM3LocalMemTimings); - l_fb_info->memt.control = PM3_READ_REG(PM3LocalMemControl); - l_fb_info->memt.refresh = PM3_READ_REG(PM3LocalMemRefresh); - l_fb_info->memt.powerdown = PM3_READ_REG(PM3LocalMemPowerDown); - - if ((l_fb_info->memt.caps == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.timings == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.control == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.refresh == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.powerdown == PM3FB_UNKNOWN_TIMING_VALUE)) - { - printk(KERN_ERR "pm3fb: invalid memory timings in permedia3 board #%ld\n", l_fb_info->board_num); - return(pm3fb_try_memory_timings(l_fb_info)); - } - return(pm3fb_timing_ok); + while (PM3_READ_REG(par, PM3InFIFOSpace) < n); } -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v) { - if (cardbase[l_fb_info->board_type].c_memt) - { - int i = 0, done = 0; - while (!done) - { - if ((cardbase[l_fb_info->board_type].c_memt[i].memsize == l_fb_info->fb_size) - || !(cardbase[l_fb_info->board_type].c_memt[i].memsize)) - { /* will use the 0-sized timings by default */ - done = 1; - l_fb_info->memt = cardbase[l_fb_info->board_type].c_memt[i].memt; - printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s, %ld MB)\n", - l_fb_info->board_num, - cardbase[l_fb_info->board_type].cardname, - cardbase[l_fb_info->board_type].c_memt[i].memsize); - pm3fb_write_memory_timings(l_fb_info); - return(pm3fb_timing_retry); - } - i++; - } - } else - return(pm3fb_timing_problem); - return(pm3fb_timing_ok); + PM3_WAIT(par, 3); + PM3_WRITE_REG(par, PM3RD_IndexHigh, (r >> 8) & 0xff); + PM3_WRITE_REG(par, PM3RD_IndexLow, r & 0xff); + wmb(); + PM3_WRITE_REG(par, PM3RD_IndexedData, v); + wmb(); } -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info) +static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno, + unsigned char r, unsigned char g, unsigned char b) { - unsigned char m, n, p; - unsigned long clockused; - - PM3_SLOW_WRITE_REG(PM3LocalMemCaps, l_fb_info->memt.caps); - PM3_SLOW_WRITE_REG(PM3LocalMemTimings, l_fb_info->memt.timings); - PM3_SLOW_WRITE_REG(PM3LocalMemControl, l_fb_info->memt.control); - PM3_SLOW_WRITE_REG(PM3LocalMemRefresh, l_fb_info->memt.refresh); - PM3_SLOW_WRITE_REG(PM3LocalMemPowerDown, l_fb_info->memt.powerdown); - - clockused = - pm3fb_CalculateClock(l_fb_info, 2 * 105000, PM3_REF_CLOCK, &m, - &n, &p); - - PM3_WRITE_DAC_REG(PM3RD_KClkPreScale, m); - PM3_WRITE_DAC_REG(PM3RD_KClkFeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_KClkPostScale, p); - PM3_WRITE_DAC_REG(PM3RD_KClkControl, - PM3RD_KClkControl_STATE_RUN | - PM3RD_KClkControl_SOURCE_PLL | - PM3RD_KClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_MClkControl, - PM3RD_MClkControl_STATE_RUN | - PM3RD_MClkControl_SOURCE_KCLK | - PM3RD_MClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_SClkControl, - PM3RD_SClkControl_STATE_RUN | - PM3RD_SClkControl_SOURCE_PCLK | - PM3RD_SClkControl_ENABLE); + PM3_WAIT(par, 4); + PM3_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno); + wmb(); + PM3_WRITE_REG(par, PM3RD_PaletteData, r); + wmb(); + PM3_WRITE_REG(par, PM3RD_PaletteData, g); + wmb(); + PM3_WRITE_REG(par, PM3RD_PaletteData, b); + wmb(); } -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r) +static void pm3fb_clear_colormap(struct pm3_par *par, + unsigned char r, unsigned char g, unsigned char b) { - DASSERT((l_fb_info->vIOBase != (unsigned char *) (-1)), - "l_fb_info->vIOBase mapped in read dac reg\n"); - PM3_SET_INDEX(r); - mb(); - return (PM3_READ_REG(PM3RD_IndexedData)); + int i; + + for (i = 0; i < 256 ; i++) + pm3fb_set_color(par, i, r, g, b); + } /* Calculating various clock parameter */ -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ) +static void pm3fb_calculate_clock(unsigned long reqclock, + unsigned char *prescale, + unsigned char *feedback, + unsigned char *postscale) { int f, pre, post; unsigned long freq; long freqerr = 1000; - unsigned long actualclock = 0; - - DTRACE; + long currerr; for (f = 1; f < 256; f++) { for (pre = 1; pre < 256; pre++) { for (post = 0; post < 5; post++) { - freq = - ((2 * refclock * f) / - (pre * (1 << post))); - if ((reqclock > freq - freqerr) - && (reqclock < freq + freqerr)) { - freqerr = - (reqclock > - freq) ? reqclock - - freq : freq - reqclock; + freq = ((2*PM3_REF_CLOCK * f) >> post) / pre; + currerr = (reqclock > freq) + ? reqclock - freq + : freq - reqclock; + if (currerr < freqerr) { + freqerr = currerr; *feedback = f; *prescale = pre; *postscale = post; - actualclock = freq; } } } } +} + +static inline int pm3fb_depth(const struct fb_var_screeninfo *var) +{ + if ( var->bits_per_pixel == 16 ) + return var->red.length + var->green.length + + var->blue.length; - return (actualclock); + return var->bits_per_pixel; } -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) +static inline int pm3fb_shift_bpp(unsigned bpp, int v) { - DTRACE; - - switch (depth) { + switch (bpp) { case 8: return (v >> 4); - case 12: - case 15: case 16: return (v >> 3); case 32: return (v >> 2); } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); + DPRINTK("Unsupported depth %u\n", bpp); + return 0; } -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) +/* acceleration */ +static int pm3fb_sync(struct fb_info *info) { - DTRACE; + struct pm3_par *par = info->par; - switch (depth) { - case 8: - return (v << 4); - case 12: - case 15: - case 16: - return (v << 3); - case 32: - return (v << 2); - } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); -} + 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); -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - l_fb_info->vIOBase = - ioremap((unsigned long) l_fb_info->pIOBase, PM3_REGS_SIZE); - l_fb_info->v_fb = - ioremap((unsigned long) l_fb_info->p_fb, l_fb_info->fb_size); - DPRINTK(2, "IO mapping : IOBase %lx / %lx, fb %lx / %lx\n", - (unsigned long) l_fb_info->pIOBase, - (unsigned long) l_fb_info->vIOBase, - (unsigned long) l_fb_info->p_fb, - (unsigned long) l_fb_info->v_fb); + return 0; } -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info) +static void pm3fb_init_engine(struct fb_info *info) { - DTRACE; + 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)); - iounmap(l_fb_info->vIOBase); - iounmap(l_fb_info->v_fb); - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->v_fb = (unsigned char *) -1; -} + 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); -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info) -{ - DPRINTK(2, "PM3Aperture0: 0x%08x\n", PM3_READ_REG(PM3Aperture0)); - DPRINTK(2, "PM3Aperture1: 0x%08x\n", PM3_READ_REG(PM3Aperture1)); - DPRINTK(2, "PM3ByAperture1Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture1Mode)); - DPRINTK(2, "PM3ByAperture2Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture2Mode)); - DPRINTK(2, "PM3ChipConfig: 0x%08x\n", PM3_READ_REG(PM3ChipConfig)); - DPRINTK(2, "PM3FIFODis: 0x%08x\n", PM3_READ_REG(PM3FIFODis)); - DPRINTK(2, "PM3HTotal: 0x%08x\n", PM3_READ_REG(PM3HTotal)); - DPRINTK(2, "PM3HbEnd: 0x%08x\n", PM3_READ_REG(PM3HbEnd)); - DPRINTK(2, "PM3HgEnd: 0x%08x\n", PM3_READ_REG(PM3HgEnd)); - DPRINTK(2, "PM3HsEnd: 0x%08x\n", PM3_READ_REG(PM3HsEnd)); - DPRINTK(2, "PM3HsStart: 0x%08x\n", PM3_READ_REG(PM3HsStart)); - DPRINTK(2, "PM3MemBypassWriteMask: 0x%08x\n", - PM3_READ_REG(PM3MemBypassWriteMask)); - DPRINTK(2, "PM3RD_IndexControl: 0x%08x\n", - PM3_READ_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3ScreenBase: 0x%08x\n", PM3_READ_REG(PM3ScreenBase)); - DPRINTK(2, "PM3ScreenStride: 0x%08x\n", - PM3_READ_REG(PM3ScreenStride)); - DPRINTK(2, "PM3VClkCtl: 0x%08x\n", PM3_READ_REG(PM3VClkCtl)); - DPRINTK(2, "PM3VTotal: 0x%08x\n", PM3_READ_REG(PM3VTotal)); - DPRINTK(2, "PM3VbEnd: 0x%08x\n", PM3_READ_REG(PM3VbEnd)); - DPRINTK(2, "PM3VideoControl: 0x%08x\n", - PM3_READ_REG(PM3VideoControl)); - DPRINTK(2, "PM3VsEnd: 0x%08x\n", PM3_READ_REG(PM3VsEnd)); - DPRINTK(2, "PM3VsStart: 0x%08x\n", PM3_READ_REG(PM3VsStart)); - - DPRINTK(2, "PM3RD_ColorFormat: %ld\n", - PM3_READ_DAC_REG(PM3RD_ColorFormat)); - DPRINTK(2, "PM3RD_DACControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_DACControl)); - DPRINTK(2, "PM3RD_DClk0FeedbackScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale)); - DPRINTK(2, "PM3RD_DClk0PostScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PostScale)); - DPRINTK(2, "PM3RD_DClk0PreScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PreScale)); - DPRINTK(2, "[not set] PM3RD_IndexControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3RD_MiscControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_MiscControl)); - DPRINTK(2, "PM3RD_PixelSize: %ld\n", - PM3_READ_DAC_REG(PM3RD_PixelSize)); - DPRINTK(2, "PM3RD_SyncControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_SyncControl)); + 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); } -#endif /* defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) */ -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info) +static void pm3fb_fillrect (struct fb_info *info, + const struct fb_fillrect *region) { - u16 subvendor, subdevice; - - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - /* well, nothing... */ - } else { - subvendor = subdevice = (u16)-1; + 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; } - printk(KERN_INFO "pm3fb: memory timings for board #%ld (subvendor: 0x%hx, subdevice: 0x%hx)\n", l_fb_info->board_num, subvendor, subdevice); - printk(KERN_INFO " PM3LocalMemCaps: 0x%08x\n", - PM3_READ_REG(PM3LocalMemCaps)); - printk(KERN_INFO " PM3LocalMemTimings: 0x%08x\n", - PM3_READ_REG(PM3LocalMemTimings)); - printk(KERN_INFO " PM3LocalMemControl: 0x%08x\n", - PM3_READ_REG(PM3LocalMemControl)); - printk(KERN_INFO " PM3LocalMemRefresh: 0x%08x\n", - PM3_READ_REG(PM3LocalMemRefresh)); - printk(KERN_INFO " PM3LocalMemPowerDown: 0x%08x\n", - PM3_READ_REG(PM3LocalMemPowerDown)); + 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 pm3fb_info *l_fb_info) +static void pm3fb_write_mode(struct fb_info *info) { + struct pm3_par *par = info->par; char tempsync = 0x00, tempmisc = 0x00; - DTRACE; - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3Aperture0, 0x00000000); - PM3_SLOW_WRITE_REG(PM3Aperture1, 0x00000000); - PM3_SLOW_WRITE_REG(PM3FIFODis, 0x00000007); - - PM3_SLOW_WRITE_REG(PM3HTotal, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->htotal - - 1)); - PM3_SLOW_WRITE_REG(PM3HsEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hsend)); - PM3_SLOW_WRITE_REG(PM3HsStart, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par-> - hsstart)); - PM3_SLOW_WRITE_REG(PM3HbEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3HgEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3ScreenStride, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->stride)); - PM3_SLOW_WRITE_REG(PM3VTotal, l_fb_info->current_par->vtotal - 1); - PM3_SLOW_WRITE_REG(PM3VsEnd, l_fb_info->current_par->vsend - 1); - PM3_SLOW_WRITE_REG(PM3VsStart, - l_fb_info->current_par->vsstart - 1); - PM3_SLOW_WRITE_REG(PM3VbEnd, l_fb_info->current_par->vbend); - - switch (l_fb_info->current_par->depth) { + const u32 hsstart = info->var.right_margin; + const u32 hsend = hsstart + info->var.hsync_len; + const u32 hbend = hsend + info->var.left_margin; + const u32 xres = (info->var.xres + 31) & ~31; + const u32 htotal = xres + hbend; + const u32 vsstart = info->var.lower_margin; + const u32 vsend = vsstart + info->var.vsync_len; + const u32 vbend = vsend + info->var.upper_margin; + const u32 vtotal = info->var.yres + vbend; + const u32 width = (info->var.xres_virtual + 7) & ~7; + const unsigned bpp = info->var.bits_per_pixel; + + PM3_WAIT(par, 20); + PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff); + PM3_WRITE_REG(par, PM3Aperture0, 0x00000000); + PM3_WRITE_REG(par, PM3Aperture1, 0x00000000); + PM3_WRITE_REG(par, PM3FIFODis, 0x00000007); + + PM3_WRITE_REG(par, PM3HTotal, + pm3fb_shift_bpp(bpp, htotal - 1)); + PM3_WRITE_REG(par, PM3HsEnd, + pm3fb_shift_bpp(bpp, hsend)); + PM3_WRITE_REG(par, PM3HsStart, + pm3fb_shift_bpp(bpp, hsstart)); + PM3_WRITE_REG(par, PM3HbEnd, + pm3fb_shift_bpp(bpp, hbend)); + PM3_WRITE_REG(par, PM3HgEnd, + pm3fb_shift_bpp(bpp, hbend)); + PM3_WRITE_REG(par, PM3ScreenStride, + pm3fb_shift_bpp(bpp, width)); + PM3_WRITE_REG(par, PM3VTotal, vtotal - 1); + PM3_WRITE_REG(par, PM3VsEnd, vsend - 1); + PM3_WRITE_REG(par, PM3VsStart, vsstart - 1); + PM3_WRITE_REG(par, PM3VbEnd, vbend); + + switch (bpp) { case 8: - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_8BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_8BIT); break; - case 12: - case 15: case 16: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); #endif /* ! __BIG_ENDIAN */ @@ -1116,23 +483,22 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) case 32: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); #endif /* ! __BIG_ENDIAN */ break; default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); + DPRINTK("Unsupported depth %d\n", bpp); break; } @@ -1143,95 +509,87 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) * sync options in PM3RD_SyncControl. --rmk */ { - unsigned int video = l_fb_info->current_par->video; + unsigned int video = par->video; video &= ~(PM3VideoControl_HSYNC_MASK | PM3VideoControl_VSYNC_MASK); video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - PM3_SLOW_WRITE_REG(PM3VideoControl, video); + PM3_WRITE_REG(par, PM3VideoControl, video); } - PM3_SLOW_WRITE_REG(PM3VClkCtl, - (PM3_READ_REG(PM3VClkCtl) & 0xFFFFFFFC)); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - PM3_SLOW_WRITE_REG(PM3ChipConfig, - (PM3_READ_REG(PM3ChipConfig) & 0xFFFFFFFD)); + PM3_WRITE_REG(par, PM3VClkCtl, + (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC)); + PM3_WRITE_REG(par, PM3ScreenBase, par->base); + PM3_WRITE_REG(par, PM3ChipConfig, + (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD)); + wmb(); { - unsigned char m; /* ClkPreScale */ - unsigned char n; /* ClkFeedBackScale */ - unsigned char p; /* ClkPostScale */ - (void)pm3fb_CalculateClock(l_fb_info, l_fb_info->current_par->pixclock, PM3_REF_CLOCK, &m, &n, &p); - - DPRINTK(2, - "Pixclock: %d, Pre: %d, Feedback: %d, Post: %d\n", - l_fb_info->current_par->pixclock, (int) m, (int) n, - (int) p); - - PM3_WRITE_DAC_REG(PM3RD_DClk0PreScale, m); - PM3_WRITE_DAC_REG(PM3RD_DClk0FeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_DClk0PostScale, p); + unsigned char uninitialized_var(m); /* ClkPreScale */ + unsigned char uninitialized_var(n); /* ClkFeedBackScale */ + unsigned char uninitialized_var(p); /* ClkPostScale */ + unsigned long pixclock = PICOS2KHZ(info->var.pixclock); + + (void)pm3fb_calculate_clock(pixclock, &m, &n, &p); + + DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n", + pixclock, (int) m, (int) n, (int) p); + + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p); } /* - PM3_WRITE_DAC_REG(PM3RD_IndexControl, 0x00); + PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00); */ /* - PM3_SLOW_WRITE_REG(PM3RD_IndexControl, 0x00); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00); */ - if ((l_fb_info->current_par->video & PM3VideoControl_HSYNC_MASK) == + if ((par->video & PM3VideoControl_HSYNC_MASK) == PM3VideoControl_HSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH; - if ((l_fb_info->current_par->video & PM3VideoControl_VSYNC_MASK) == + if ((par->video & PM3VideoControl_VSYNC_MASK) == PM3VideoControl_VSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH; - - PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync); - DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync); - - if (flatpanel[l_fb_info->board_num]) - { - PM3_WRITE_DAC_REG(PM3RD_DACControl, PM3RD_DACControl_BLANK_PEDESTAL_ENABLE); - PM3_WAIT(2); - PM3_WRITE_REG(PM3VSConfiguration, 0x06); - PM3_WRITE_REG(0x5a00, 1 << 14); /* black magic... */ - tempmisc = PM3RD_MiscControl_VSB_OUTPUT_ENABLE; - } - else - PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00); - switch (l_fb_info->current_par->depth) { + PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync); + DPRINTK("PM3RD_SyncControl: %d\n", tempsync); + + PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00); + + switch (pm3fb_depth(&info->var)) { case 8: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_8_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_CI8_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 12: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_4444_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 15: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_5551_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 16: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_565_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); @@ -1239,1936 +597,263 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 32: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_32_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_8888_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; } - PM3_WRITE_DAC_REG(PM3RD_MiscControl, tempmisc); - - PM3_SHOW_CUR_MODE; -} - -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar) -{ - unsigned long pixsize1, pixsize2, clockused; - unsigned long pre, feedback, post; - - DTRACE; - - clockused = PM3_READ_REG(PM3VClkCtl); - - switch (clockused) { - case 3: - pre = PM3_READ_DAC_REG(PM3RD_DClk3PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk3FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk3PostScale); - - DPRINTK(2, - "DClk3 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); - break; - case 2: - pre = PM3_READ_DAC_REG(PM3RD_DClk2PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk2FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk2PostScale); - - DPRINTK(2, - "DClk2 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); - break; - case 1: - pre = PM3_READ_DAC_REG(PM3RD_DClk1PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk1FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk1PostScale); - - DPRINTK(2, - "DClk1 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); - break; - case 0: - pre = PM3_READ_DAC_REG(PM3RD_DClk0PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk0PostScale); - - DPRINTK(2, - "DClk0 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); - break; - default: - pre = feedback = post = 0; - DPRINTK(1, "Unknowk D clock used : %ld\n", clockused); - break; - } - - curpar->pixclock = PM3_SCALE_TO_CLOCK(pre, feedback, post); - - pixsize1 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture1Mode)); - pixsize2 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture2Mode)); - - DASSERT((pixsize1 == pixsize2), - "pixsize the same in both aperture\n"); - - if (pixsize1 & PM3ByApertureMode_PIXELSIZE_32BIT) - curpar->depth = 32; - else if (pixsize1 & PM3ByApertureMode_PIXELSIZE_16BIT) - { - curpar->depth = 16; - } - else - curpar->depth = 8; - - /* not sure if I need to add one on the next ; it give better result with */ - curpar->htotal = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - 1 + PM3_READ_REG(PM3HTotal)); - curpar->hsend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsEnd)); - curpar->hsstart = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsStart)); - curpar->hbend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HbEnd)); - - curpar->stride = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3ScreenStride)); - - curpar->vtotal = 1 + PM3_READ_REG(PM3VTotal); - curpar->vsend = 1 + PM3_READ_REG(PM3VsEnd); - curpar->vsstart = 1 + PM3_READ_REG(PM3VsStart); - curpar->vbend = PM3_READ_REG(PM3VbEnd); - - curpar->video = PM3_READ_REG(PM3VideoControl); - - curpar->base = PM3_READ_REG(PM3ScreenBase); - curpar->width = curpar->htotal - curpar->hbend; /* make virtual == displayed resolution */ - curpar->height = curpar->vtotal - curpar->vbend; - - DPRINTK(2, "Found : %d * %d, %d Khz, stride is %08x\n", - curpar->width, curpar->height, curpar->pixclock, - curpar->stride); -} - -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info) -{ - unsigned long memsize = 0, tempBypass, i, temp1, temp2; - u16 subvendor, subdevice; - pm3fb_timing_result ptr; - - DTRACE; - - l_fb_info->fb_size = 64 * 1024 * 1024; /* pm3 aperture always 64 MB */ - pm3fb_mapIO(l_fb_info); /* temporary map IO */ - - DASSERT((l_fb_info->vIOBase != NULL), - "IO successfully mapped before mem detect\n"); - DASSERT((l_fb_info->v_fb != NULL), - "FB successfully mapped before mem detect\n"); - - /* card-specific stuff, *before* accessing *any* FB memory */ - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - i = 0; l_fb_info->board_type = 0; - while ((cardbase[i].cardname[0]) && !(l_fb_info->board_type)) { - if ((cardbase[i].subvendor == subvendor) && - (cardbase[i].subdevice == subdevice) && - (cardbase[i].func == PCI_FUNC(l_fb_info->dev->devfn))) { - DPRINTK(2, "Card #%ld is an %s\n", - l_fb_info->board_num, - cardbase[i].cardname); - if (cardbase[i].specific_setup) - cardbase[i].specific_setup(l_fb_info); - l_fb_info->board_type = i; - } - i++; - } - if (!l_fb_info->board_type) { - DPRINTK(1, "Card #%ld is an unknown 0x%04x / 0x%04x\n", - l_fb_info->board_num, subvendor, subdevice); - } - } else { - printk(KERN_ERR "pm3fb: Error: pci_read_config_word failed, board #%ld\n", - l_fb_info->board_num); - } - - if (printtimings) - pm3fb_show_cur_timing(l_fb_info); - - /* card-specific setup is done, we preserve the final - memory timing for future reference */ - if ((ptr = pm3fb_preserve_memory_timings(l_fb_info)) == pm3fb_timing_problem) { /* memory timings were wrong ! oops.... */ - return(0); - } - - tempBypass = PM3_READ_REG(PM3MemBypassWriteMask); - - DPRINTK(2, "PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xFFFFFFFF); - - /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ - for (i = 0; i < 32; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = fb_readl((l_fb_info->v_fb + (i * 1048576))); - - /* Let's check for wrapover, write will fail at 16MB boundary */ - if (temp1 == (i * 0x00345678)) - memsize = i; - else - break; - } - - DPRINTK(2, "First detect pass already got %ld MB\n", memsize + 1); - - if (memsize == i) { - for (i = 0; i < 32; i++) { - /* Clear first 32MB ; 0 is 0, no need to byteswap */ - writel(0x0000000, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - } - - for (i = 32; i < 64; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = - fb_readl((l_fb_info->v_fb + (i * 1048576))); - temp2 = - fb_readl((l_fb_info->v_fb + - ((i - 32) * 1048576))); - if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) /* different value, different RAM... */ - memsize = i; - else - break; - } - } - - DPRINTK(2, "Second detect pass got %ld MB\n", memsize + 1); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, tempBypass); - - pm3fb_unmapIO(l_fb_info); - memsize = 1048576 * (memsize + 1); - - DPRINTK(2, "Returning 0x%08lx bytes\n", memsize); - - if (forcesize[l_fb_info->board_num] && ((forcesize[l_fb_info->board_num] * 1048576) != memsize)) - { - printk(KERN_WARNING "pm3fb: mismatch between probed (%ld MB) and specified (%hd MB) memory size, using SPECIFIED !\n", memsize, forcesize[l_fb_info->board_num]); - memsize = 1048576 * forcesize[l_fb_info->board_num]; - } - - l_fb_info->fb_size = memsize; - - if (ptr == pm3fb_timing_retry) - { - printk(KERN_WARNING "pm3fb: retrying memory timings check"); - if (pm3fb_try_memory_timings(l_fb_info) == pm3fb_timing_problem) - return(0); - } - - return (memsize); -} - -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc) -{ - int i; - - DTRACE; - - for (i = 0; i < (l_fb_info->fb_size / sizeof(u32)) ; i++) /* clear entire FB memory to black */ - { - fb_writel(cc, (l_fb_info->v_fb + (i * sizeof(u32)))); - } -} - -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b) -{ - int i; - - DTRACE; - - for (i = 0; i < 256 ; i++) /* fill color map with white */ - pm3fb_set_color(l_fb_info, i, r, g, b); - -} - -/* common initialisation */ -static void pm3fb_common_init(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - DPRINTK(2, "Initializing board #%ld @ %lx\n", l_fb_info->board_num, - (unsigned long) l_fb_info); - - strcpy(l_fb_info->gen.info.modename, permedia3_name); - disp[l_fb_info->board_num].scrollmode = 0; /* SCROLL_YNOMOVE; *//* 0 means "let fbcon choose" */ - l_fb_info->gen.parsize = sizeof(struct pm3fb_par); - l_fb_info->gen.info.changevar = NULL; - l_fb_info->gen.info.fbops = &pm3fb_ops; - l_fb_info->gen.info.disp = &(disp[l_fb_info->board_num]); - if (fontn[l_fb_info->board_num][0]) - strcpy(l_fb_info->gen.info.fontname, - fontn[l_fb_info->board_num]); - l_fb_info->gen.info.switch_con = &fbgen_switch; - l_fb_info->gen.info.updatevar = &fbgen_update_var; /* */ - l_fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT; - - pm3fb_mapIO(l_fb_info); - - pm3fb_clear_memory(l_fb_info, 0); - pm3fb_clear_colormap(l_fb_info, 0, 0, 0); - - (void) fbgen_get_var(&(disp[l_fb_info->board_num]).var, -1, - &l_fb_info->gen.info); - - if (depth[l_fb_info->board_num]) /* override mode-defined depth */ - { - pm3fb_encode_depth(&(disp[l_fb_info->board_num]).var, depth[l_fb_info->board_num]); - (disp[l_fb_info->board_num]).var.bits_per_pixel = depth2bpp(depth[l_fb_info->board_num]); - } - - (void) fbgen_do_set_var(&(disp[l_fb_info->board_num]).var, 1, - &l_fb_info->gen); - - fbgen_set_disp(-1, &l_fb_info->gen); - - do_install_cmap(0, &l_fb_info->gen.info); - - if (register_framebuffer(&l_fb_info->gen.info) < 0) { - DPRINTK(1, "Couldn't register framebuffer\n"); - return; - } - - PM3_WRITE_DAC_REG(PM3RD_CursorMode, - PM3RD_CursorMode_CURSOR_DISABLE); - - PM3_SHOW_CUR_MODE; - - pm3fb_write_mode(l_fb_info); - - printk("fb%d: %s, using %uK of video memory (%s)\n", - l_fb_info->gen.info.node, - permedia3_name, (u32) (l_fb_info->fb_size >> 10), - cardbase[l_fb_info->board_type].cardname); + PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc); } -/* **************************************************** */ -/* ***** accelerated permedia3-specific functions ***** */ -/* **************************************************** */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info) +/* + * hardware independent functions + */ +static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - DTRACE; + u32 lpitch; + unsigned bpp = var->red.length + var->green.length + + var->blue.length + var->transp.length; - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3Sync, 0); - mb(); - do { - while ((PM3_READ_REG(PM3OutFIFOWords)) == 0); - rmb(); - } while ((PM3_READ_REG(PM3OutputFifo)) != PM3Sync_Tag); -} + if ( bpp != var->bits_per_pixel ) { + /* set predefined mode for bits_per_pixel settings */ -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info) -{ - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3StatisticMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RasterizerMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ScissorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LineStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AreaStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3GIDMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DepthMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilData, 0x0); - PM3_SLOW_WRITE_REG(PM3ColorDDAMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCoordMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LUTMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureFilterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureApplicationMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3FogMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ChromaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AntialiasMode, 0x0); - PM3_SLOW_WRITE_REG(PM3YUVMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendColorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendAlphaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DitherMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LogicalOpMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RouterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3Window, 0x0); - - PM3_SLOW_WRITE_REG(PM3Config2D, 0x0); - - PM3_SLOW_WRITE_REG(PM3SpanColorMask, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3XBias, 0x0); - PM3_SLOW_WRITE_REG(PM3YBias, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaControl, 0x0); - - PM3_SLOW_WRITE_REG(PM3BitMaskPattern, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3FBDestReadEnables, - PM3FBDestReadEnables_E(0xff) | - PM3FBDestReadEnables_R(0xff) | - PM3FBDestReadEnables_ReferenceAlpha(0xff)); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferWidth0, - PM3FBDestReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3FBDestReadMode, - PM3FBDestReadMode_ReadEnable | - PM3FBDestReadMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferAddr, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferOffset, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferWidth, - PM3FBSourceReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - PM3_SLOW_WRITE_REG(PM3FBSourceReadMode, - PM3FBSourceReadMode_Blocking | - PM3FBSourceReadMode_ReadEnable); - - { - unsigned long rm = 1; - switch (l_fb_info->current_par->depth) { + switch(var->bits_per_pixel) { case 8: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_8BIT); + var->red.length = var->green.length = var->blue.length = 8; + var->red.offset = var->green.offset = var->blue.offset = 0; + var->transp.offset = 0; + var->transp.length = 0; break; - case 12: - case 15: case 16: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_16BIT); + var->red.length = var->blue.length = 5; + var->green.length = 6; + var->transp.length = 0; break; case 32: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_32BIT); + var->red.length = var->green.length = var->blue.length = 8; + var->transp.length = 8; break; default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; + DPRINTK("depth not supported: %u\n", var->bits_per_pixel); + return -EINVAL; } - PM3_SLOW_WRITE_REG(PM3RasterizerMode, rm); } - - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBWriteMode, - PM3FBWriteMode_WriteEnable | - PM3FBWriteMode_OpaqueSpan | - PM3FBWriteMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 0x0); + /* it is assumed BGRA order */ + if (var->bits_per_pixel > 8 ) { - unsigned long sofb = (8UL * l_fb_info->fb_size) / - ((depth2bpp(l_fb_info->current_par->depth)) - * l_fb_info->current_par->width); /* size in lines of FB */ - if (sofb > 4095) - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 4095); - else - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, sofb); - - switch (l_fb_info->current_par->depth) { - case 8: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (2 << 3)); - break; - case 12: - case 15: - case 16: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (1 << 3)); - break; - case 32: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (0 << 3)); - break; - default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; - } - } - - PM3_SLOW_WRITE_REG(PM3dXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3dXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3dY, (1 << 16)); - PM3_SLOW_WRITE_REG(PM3StartXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3StartXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3StartY, 0x0); - PM3_SLOW_WRITE_REG(PM3Count, 0x0); - -/* Disable LocalBuffer. better safe than sorry */ - PM3_SLOW_WRITE_REG(PM3LBDestReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBDestReadEnables, 0x0); - PM3_SLOW_WRITE_REG(PM3LBSourceReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBWriteMode, 0x0); - - pm3fb_wait_pm3(l_fb_info); -} - -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - /* block fills in 32bpp are hard, but in low res (width <= 1600 :-) - we can use 16bpp operations, but not if NoWriteMask is on (SDRAM) */ - if ((l_fb_info->current_par->width > 1600) || - (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)) { - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - } else { - PM3_WAIT(8); - - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_16BIT); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width << 1)); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx << 1)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width << 1)) | - (PM3Render2D_Height(height))); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_32BIT); - } - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - } - - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - PM3_WAIT(4); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - } - - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - } - - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int x_align, o_x, o_y; - - DTRACE; - - sx = sx * fontwidth(p); - dx = dx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - dy = dy * fontheight(p); - height = height * fontheight(p); - - o_x = sx - dx; /*(sx > dx ) ? (sx - dx) : (dx - sx); */ - o_y = sy - dy; /*(sy > dy ) ? (sy - dy) : (dy - sy); */ - - x_align = (sx & 0x1f); - - PM3_WAIT(6); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UserScissorEnable | - PM3Config2D_ForegroundROPEnable | - PM3Config2D_Blocking | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ScissorMinXY, - ((dy & 0x0fff) << 16) | (dx & 0x0fff)); - PM3_WRITE_REG(PM3ScissorMaxXY, - (((dy + height) & 0x0fff) << 16) | - ((dx + width) & 0x0fff)); - - PM3_WRITE_REG(PM3FBSourceReadBufferOffset, - PM3FBSourceReadBufferOffset_XOffset(o_x) | - PM3FBSourceReadBufferOffset_YOffset(o_y)); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(dx - x_align)) | - (PM3RectanglePosition_YOffset(dy))); - - PM3_WRITE_REG(PM3Render2D, - ((sx > dx) ? PM3Render2D_XPositive : 0) | - ((sy > dy) ? PM3Render2D_YPositive : 0) | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - PM3Render2D_FBSourceReadEnable | - (PM3Render2D_Width(width + x_align)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i; - - DTRACE; - - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, c)]; - else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, c)]; - - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, c)]; - else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, c)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putcs, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sx = xx * fontwidth(p); - sy = yy * fontheight(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - if (fontwidth(p) <= 8) { - cdat = p->fontdata + (c & p->charmask) * fontheight(p); - } else { - cdat = - p->fontdata + - ((c & p->charmask) * (fontheight(p) << 1)); - } - - PM3_WAIT(2 + fontheight(p)); - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i, j; - u16 sc; - - DTRACE; - - sc = scr_readw(s); - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, sc)]; - else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, sc)]; - - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, sc)]; - else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, sc)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | - PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putc, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sy = yy * fontheight(p); - - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - for (j = 0; j < count; j++) { - sc = scr_readw(s + j); - if (fontwidth(p) <= 8) - cdat = p->fontdata + - (sc & p->charmask) * fontheight(p); - else - cdat = p->fontdata + - ((sc & p->charmask) * fontheight(p) << 1); - - sx = (xx + j) * fontwidth(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - - PM3_WAIT(3 + fontheight(p)); - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); + var->blue.offset = 0; + var->green.offset = var->blue.length; + var->red.offset = var->green.offset + var->green.length; + var->transp.offset = var->red.offset + var->red.length; } - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - - xx = xx * fontwidth(p); - yy = yy * fontheight(p); + var->height = var->width = -1; - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0x0F0F0F0F); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F); + if (var->xres != var->xres_virtual) { + DPRINTK("virtual x resolution != physical x resolution not supported\n"); + return -EINVAL; } - PM3_WAIT(3); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0xa)) | /* Oxa is GXinvert */ - PM3Config2D_FBDestReadEnable | - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(xx)) | - (PM3RectanglePosition_YOffset(yy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); - - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xFFFFFFFF); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF); + if (var->yres > var->yres_virtual) { + DPRINTK("virtual y resolution < physical y resolution not possible\n"); + return -EINVAL; } -} -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* *********************************** */ -/* ***** pre-init board(s) setup ***** */ -/* *********************************** */ - -static void pm3fb_mode_setup(char *mode, unsigned long board_num) -{ - struct pm3fb_info *l_fb_info = &(fb_info[board_num]); - struct pm3fb_par *l_fb_par = &(current_par[board_num]); - unsigned long i = 0; - - current_par_valid[board_num] = 0; - - if (!strncmp(mode, "current", 7)) { - l_fb_info->use_current = 1; /* default w/ OpenFirmware */ - } else { - while ((mode_base[i].name[0]) - && (!current_par_valid[board_num])) { - if (! - (strncmp - (mode, mode_base[i].name, - strlen(mode_base[i].name)))) { - memcpy(l_fb_par, &(mode_base[i].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[board_num] = 1; - DPRINTK(2, "Mode set to %s\n", - mode_base[i].name); - } - i++; - } - DASSERT(current_par_valid[board_num], - "Valid mode on command line\n"); + if (var->xoffset) { + DPRINTK("xoffset not supported\n"); + return -EINVAL; } -} -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num) -{ - short l_bus = -1, l_slot = -1, l_func = -1; - char *next; - - if (pciid) { - l_bus = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_slot = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_func = - simple_strtoul(pciid, (char **) NULL, - 10); - } - } - } else - return; - - if ((l_bus >= 0) && (l_slot >= 0) && (l_func >= 0)) { - bus[board_num] = l_bus; - slot[board_num] = l_slot; - func[board_num] = l_func; - DPRINTK(2, "Board #%ld will be PciId: %hd:%hd:%hd\n", - board_num, l_bus, l_slot, l_func); - } else { - DPRINTK(1, "Invalid PciId: %hd:%hd:%hd for board #%ld\n", - l_bus, l_slot, l_func, board_num); + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; } -} -static void pm3fb_font_setup(char *lf, unsigned long board_num) -{ - unsigned long lfs = strlen(lf); + var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */ + lpitch = var->xres * ((var->bits_per_pixel + 7)>>3); - if (lfs > (PM3_FONTNAME_SIZE - 1)) { - DPRINTK(1, "Fontname %s too long\n", lf); - return; + if (var->xres < 200 || var->xres > 2048) { + DPRINTK("width not supported: %u\n", var->xres); + return -EINVAL; } - strlcpy(fontn[board_num], lf, lfs + 1); -} - -static void pm3fb_bootdepth_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - if (!(depth_supported(bd))) { - printk(KERN_WARNING "pm3fb: ignoring invalid depth %s for board #%ld\n", - bds, board_num); - return; + if (var->yres < 200 || var->yres > 4095) { + DPRINTK("height not supported: %u\n", var->yres); + return -EINVAL; } - depth[board_num] = bd; -} -static void pm3fb_forcesize_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - - if (bd > 64) { - printk(KERN_WARNING "pm3fb: ignoring invalid memory size %s for board #%ld\n", - bds, board_num); - return; + 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; } - forcesize[board_num] = bd; -} - -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn) -{ - char *next; - if (!(isdigit(options[0]))) { - (*bn) = 0; - return (options); + if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) { + DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; } - (*bn) = simple_strtoul(options, &next, 10); + var->accel_flags = 0; /* Can't mmap if this is on */ - if (next && (next[0] == ':') && ((*bn) >= 0) - && ((*bn) <= PM3_MAX_BOARD)) { - DPRINTK(2, "Board_num seen as %ld\n", (*bn)); - return (next + 1); - } else { - (*bn) = 0; - DPRINTK(2, "Board_num default to %ld\n", (*bn)); - return (options); - } + DPRINTK("Checking graphics mode at %dx%d depth %d\n", + var->xres, var->yres, var->bits_per_pixel); + return 0; } -static void pm3fb_real_setup(char *options) +static int pm3fb_set_par(struct fb_info *info) { - char *next; - unsigned long i, bn; - struct pm3fb_info *l_fb_info; - - DTRACE; - - DPRINTK(2, "Options : %s\n", options); - - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - memset(l_fb_info, 0, sizeof(struct pm3fb_info)); - l_fb_info->gen.fbhw = &pm3fb_switch; - l_fb_info->board_num = i; - current_par_valid[i] = 0; - slot[i] = -1; - func[i] = -1; - bus[i] = -1; - disable[i] = 0; - noaccel[i] = 0; - fontn[i][0] = '\0'; - depth[i] = 0; - l_fb_info->current_par = &(current_par[i]); - } - - /* eat up prefix pm3fb and whatever is used as separator i.e. :,= */ - if (!strncmp(options, "pm3fb", 5)) { - options += 5; - while (((*options) == ',') || ((*options) == ':') - || ((*options) == '=')) - options++; - } + struct pm3_par *par = info->par; + const u32 xres = (info->var.xres + 31) & ~31; + const unsigned bpp = info->var.bits_per_pixel; - while (options) { - bn = 0; - if ((next = strchr(options, ','))) { - (*next) = '\0'; - next++; - } + par->base = pm3fb_shift_bpp(bpp,(info->var.yoffset * xres) + + info->var.xoffset); + par->video = 0; - if (!strncmp(options, "mode:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - DPRINTK(2, "Setting mode for board #%ld\n", bn); - pm3fb_mode_setup(options, bn); - } else if (!strncmp(options, "off:", 4)) { - options = pm3fb_boardnum_setup(options + 4, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "off", 3)) { /* disable everything */ - for (i = 0; i < PM3_MAX_BOARD; i++) - disable[i] = 1; - } else if (!strncmp(options, "disable:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "pciid:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - DPRINTK(2, "Setting PciID for board #%ld\n", bn); - pm3fb_pciid_setup(options, bn); - } else if (!strncmp(options, "noaccel:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - noaccel[bn] = 1; - } else if (!strncmp(options, "font:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - pm3fb_font_setup(options, bn); - } else if (!strncmp(options, "depth:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - pm3fb_bootdepth_setup(options, bn); - } else if (!strncmp(options, "printtimings", 12)) { - printtimings = 1; - } else if (!strncmp(options, "flatpanel:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - flatpanel[bn] = 1; - } else if (!strncmp(options, "forcesize:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - pm3fb_forcesize_setup(options, bn); - } - options = next; - } -} - -/* ********************************************** */ -/* ***** framebuffer API standard functions ***** */ -/* ********************************************** */ - -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - - DTRACE; - - strcpy(fix->id, permedia3_name); - fix->smem_start = (unsigned long)l_fb_info->p_fb; - fix->smem_len = l_fb_info->fb_size; - fix->mmio_start = (unsigned long)l_fb_info->pIOBase; - fix->mmio_len = PM3_REGS_SIZE; -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - fix->accel = FB_ACCEL_3DLABS_PERMEDIA3; + if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) + par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; else -#endif /* PM3FB_USE_ACCEL */ - fix->accel = FB_ACCEL_NONE; - fix->type = FB_TYPE_PACKED_PIXELS; - fix->visual = - (p->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - if (current_par_valid[l_fb_info->board_num]) - fix->line_length = - l_fb_info->current_par->width * - depth2ByPP(l_fb_info->current_par->depth); - else - fix->line_length = 0; - fix->xpanstep = 64 / depth2bpp(p->depth); - fix->ypanstep = 1; - fix->ywrapstep = 0; - return (0); -} - -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_par temp_p; - u32 xres; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((l_fb_info != NULL), "pm3fb_info* not NULL"); + par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - memset(&temp_p, 0, sizeof(struct pm3fb_par)); - temp_p.width = (var->xres_virtual + 7) & ~7; - temp_p.height = var->yres_virtual; - - if (!(depth_supported(var->bits_per_pixel))) /* round unsupported up to a multiple of 8 */ - temp_p.depth = depth2bpp(var->bits_per_pixel); + if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) + par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; else - temp_p.depth = var->bits_per_pixel; - - temp_p.depth = (temp_p.depth > 32) ? 32 : temp_p.depth; /* max 32 */ - temp_p.depth = (temp_p.depth == 24) ? 32 : temp_p.depth; /* 24 unsupported, round-up to 32 */ - - if ((temp_p.depth == 16) && (var->red.length == 5) && (var->green.length == 5) && (var->blue.length == 5)) - temp_p.depth = 15; /* RGBA 5551 is stored as depth 15 */ - - if ((temp_p.depth == 16) && (var->red.length == 4) && (var->green.length == 4) && (var->blue.length == 4)) - temp_p.depth = 12; /* RGBA 4444 is stored as depth 12 */ - - - DPRINTK(2, - "xres: %d, yres: %d, vxres: %d, vyres: %d ; xoffset:%d, yoffset: %d\n", - var->xres, var->yres, var->xres_virtual, var->yres_virtual, - var->xoffset, var->yoffset); - - xres = (var->xres + 31) & ~31; - if (temp_p.width < xres + var->xoffset) - temp_p.width = xres + var->xoffset; - if (temp_p.height < var->yres + var->yoffset) - temp_p.height = var->yres + var->yoffset; - - if (temp_p.width > 2048) { - DPRINTK(1, "virtual width not supported: %u\n", - temp_p.width); - return (-EINVAL); - } - if (var->yres < 200) { - DPRINTK(1, "height not supported: %u\n", (u32) var->yres); - return (-EINVAL); - } - if (temp_p.height < 200 || temp_p.height > 4095) { - DPRINTK(1, "virtual height not supported: %u\n", - temp_p.height); - return (-EINVAL); - } - if (!(depth_supported(temp_p.depth))) { - DPRINTK(1, "depth not supported: %u\n", temp_p.depth); - return (-EINVAL); - } - if ((temp_p.width * temp_p.height * depth2ByPP(temp_p.depth)) > - l_fb_info->fb_size) { - DPRINTK(1, "no memory for screen (%ux%ux%u)\n", - temp_p.width, temp_p.height, temp_p.depth); - return (-EINVAL); - } + par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - if ((!var->pixclock) || - (!var->right_margin) || - (!var->hsync_len) || - (!var->left_margin) || - (!var->lower_margin) || - (!var->vsync_len) || (!var->upper_margin) - ) { - unsigned long i = 0, done = 0; - printk(KERN_WARNING "pm3fb: refusing to use a likely wrong timing\n"); - - while ((mode_base[i].user_mode.width) && !done) { - if ((mode_base[i].user_mode.width == temp_p.width) - && (mode_base[i].user_mode.height == - temp_p.height)) { - printk(KERN_NOTICE "pm3fb: using close match %s\n", - mode_base[i].name); - temp_p = mode_base[i].user_mode; - done = 1; - } - i++; - } - if (!done) - return (-EINVAL); - } else { - temp_p.pixclock = PICOS2KHZ(var->pixclock); - if (temp_p.pixclock > PM3_MAX_PIXCLOCK) { - DPRINTK(1, "pixclock too high (%uKHz)\n", - temp_p.pixclock); - return (-EINVAL); - } - - temp_p.hsstart = var->right_margin; - temp_p.hsend = var->right_margin + var->hsync_len; - temp_p.hbend = - var->right_margin + var->hsync_len + var->left_margin; - temp_p.htotal = xres + temp_p.hbend; - - temp_p.vsstart = var->lower_margin; - temp_p.vsend = var->lower_margin + var->vsync_len; - temp_p.vbend = - var->lower_margin + var->vsync_len + var->upper_margin; - temp_p.vtotal = var->yres + temp_p.vbend; - - temp_p.stride = temp_p.width; - - DPRINTK(2, "Using %d * %d, %d Khz, stride is %08x\n", - temp_p.width, temp_p.height, temp_p.pixclock, - temp_p.stride); - - temp_p.base = - pm3fb_Shiftbpp(l_fb_info, temp_p.depth, - (var->yoffset * xres) + var->xoffset); - - temp_p.video = 0; - - if (var->sync & FB_SYNC_HOR_HIGH_ACT) - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - - if (var->sync & FB_SYNC_VERT_HIGH_ACT) - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { - DPRINTK(1, "Interlaced mode not supported\n\n"); - return (-EINVAL); - } - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) - temp_p.video |= PM3VideoControl_LINE_DOUBLE_ON; - else - temp_p.video |= PM3VideoControl_LINE_DOUBLE_OFF; - - if (var->activate == FB_ACTIVATE_NOW) - temp_p.video |= PM3VideoControl_ENABLE; - else { - temp_p.video |= PM3VideoControl_DISABLE; - DPRINTK(2, "PM3Video disabled\n"); - } - - switch (temp_p.depth) { - case 8: - temp_p.video |= PM3VideoControl_PIXELSIZE_8BIT; - break; - case 12: - case 15: - case 16: - temp_p.video |= PM3VideoControl_PIXELSIZE_16BIT; - break; - case 32: - temp_p.video |= PM3VideoControl_PIXELSIZE_32BIT; - break; - default: - DPRINTK(1, "Unsupported depth\n"); - break; - } - } - (*p) = temp_p; - -#ifdef PM3FB_USE_ACCEL - if (var->accel_flags & FB_ACCELF_TEXT) - noaccel[l_fb_info->board_num] = 0; + if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) + par->video |= PM3VideoControl_LINE_DOUBLE_ON; else - noaccel[l_fb_info->board_num] = 1; -#endif /* PM3FB_USE_ACCEL */ - - return (0); -} + par->video |= PM3VideoControl_LINE_DOUBLE_OFF; -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d) -{ - switch (d) { + if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) + par->video |= PM3VideoControl_ENABLE; + else { + par->video |= PM3VideoControl_DISABLE; + DPRINTK("PM3Video disabled\n"); + } + switch (bpp) { case 8: - var->red.length = var->green.length = var->blue.length = 8; - var->red.offset = var->green.offset = var->blue.offset = 0; - var->transp.offset = var->transp.length = 0; - break; - - case 12: - var->red.offset = 8; - var->red.length = 4; - var->green.offset = 4; - var->green.length = 4; - var->blue.offset = 0; - var->blue.length = 4; - var->transp.offset = 12; - var->transp.length = 4; + par->video |= PM3VideoControl_PIXELSIZE_8BIT; break; - - case 15: - var->red.offset = 10; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 5; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 15; - var->transp.length = 1; - break; - case 16: - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = var->transp.length = 0; + par->video |= PM3VideoControl_PIXELSIZE_16BIT; break; - case 32: - var->transp.offset = 24; - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - var->red.length = var->green.length = - var->blue.length = var->transp.length = 8; + par->video |= PM3VideoControl_PIXELSIZE_32BIT; break; - default: - DPRINTK(1, "Unsupported depth %ld\n", d); + DPRINTK("Unsupported depth\n"); break; } -} - -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - u32 base; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((info != NULL), "fb_info_gen* not NULL"); - - memset(var, 0, sizeof(struct fb_var_screeninfo)); - -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - var->accel_flags |= FB_ACCELF_TEXT; -#endif /* PM3FB_USE_ACCEL */ - - var->xres_virtual = p->width; - var->yres_virtual = p->height; - var->xres = p->htotal - p->hbend; - var->yres = p->vtotal - p->vbend; - - DPRINTK(2, "xres = %d, yres : %d\n", var->xres, var->yres); - - var->right_margin = p->hsstart; - var->hsync_len = p->hsend - p->hsstart; - var->left_margin = p->hbend - p->hsend; - var->lower_margin = p->vsstart; - var->vsync_len = p->vsend - p->vsstart; - var->upper_margin = p->vbend - p->vsend; - var->bits_per_pixel = depth2bpp(p->depth); - - pm3fb_encode_depth(var, p->depth); - - base = pm3fb_Unshiftbpp(l_fb_info, p->depth, p->base); - - var->xoffset = base % var->xres; - var->yoffset = base / var->xres; - - var->height = var->width = -1; - - var->pixclock = KHZ2PICOS(p->pixclock); - - if ((p->video & PM3VideoControl_HSYNC_MASK) == - PM3VideoControl_HSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if ((p->video & PM3VideoControl_VSYNC_MASK) == - PM3VideoControl_VSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - if (p->video & PM3VideoControl_LINE_DOUBLE_ON) - var->vmode = FB_VMODE_DOUBLE; - - return (0); -} - -static void pm3fb_get_par(void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - if (!current_par_valid[l_fb_info->board_num]) { - if (l_fb_info->use_current) - pm3fb_read_mode(l_fb_info, l_fb_info->current_par); - else - memcpy(l_fb_info->current_par, - &(mode_base[0].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[l_fb_info->board_num] = 1; - } - *((struct pm3fb_par *) par) = *(l_fb_info->current_par); -} - -static void pm3fb_set_par(const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - *(l_fb_info->current_par) = *((struct pm3fb_par *) par); - current_par_valid[l_fb_info->board_num] = 1; - - pm3fb_write_mode(l_fb_info); -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ -} - -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b) -{ - DTRACE; + info->fix.visual = + (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = ((info->var.xres_virtual + 7) & ~7) + * bpp / 8; - PM3_SLOW_WRITE_REG(PM3RD_PaletteWriteAddress, regno); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, r); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, g); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, b); -} - -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - if (regno < 256) { - *red = - l_fb_info->palette[regno].red << 8 | l_fb_info-> - palette[regno].red; - *green = - l_fb_info->palette[regno].green << 8 | l_fb_info-> - palette[regno].green; - *blue = - l_fb_info->palette[regno].blue << 8 | l_fb_info-> - palette[regno].blue; - *transp = - l_fb_info->palette[regno].transp << 8 | l_fb_info-> - palette[regno].transp; - } - return (regno > 255); +/* pm3fb_clear_memory(info, 0);*/ + 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; } static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; + struct pm3_par *par = info->par; + + if (regno >= 256) /* no. of hw registers */ + return -EINVAL; + + /* grayscale works only partially under directcolor */ + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + /* Directcolor: + * var->{color}.offset contains start of bitfield + * var->{color}.length contains length of bitfield + * {hardwarespecific} contains width of DAC + * pseudo_palette[X] is programmed to (X << red.offset) | + * (X << green.offset) | + * (X << blue.offset) + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = SUM(var->{color}.length) + * + * Pseudocolor: + * var->{color}.offset is 0 + * var->{color}.length contains width of DAC or the number of unique + * colors available (color depth) + * pseudo_palette is not used + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = var->{color}.length + */ - if (regno < 16) { - switch (l_fb_info->current_par->depth) { -#ifdef FBCON_HAS_CFB8 + /* + * This is the point where the color is converted to something that + * is acceptable by the hardware. + */ +#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); +#undef CNVT_TOHW + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + u32 v; + + if (regno >= 16) + return -EINVAL; + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + switch (info->var.bits_per_pixel) { case 8: break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: - l_fb_info->cmap.cmap12[regno] = - (((u32) red & 0xf000) >> 4) | - (((u32) green & 0xf000) >> 8) | - (((u32) blue & 0xf000) >> 12); - break; - - case 15: - l_fb_info->cmap.cmap15[regno] = - (((u32) red & 0xf800) >> 1) | - (((u32) green & 0xf800) >> 6) | - (((u32) blue & 0xf800) >> 11); - break; - case 16: - l_fb_info->cmap.cmap16[regno] = - ((u32) red & 0xf800) | - (((u32) green & 0xfc00) >> 5) | - (((u32) blue & 0xf800) >> 11); - break; -#endif -#ifdef FBCON_HAS_CFB32 case 32: - l_fb_info->cmap.cmap32[regno] = - (((u32) transp & 0xff00) << 16) | - (((u32) red & 0xff00) << 8) | - (((u32) green & 0xff00)) | - (((u32) blue & 0xff00) >> 8); - break; -#endif - default: - DPRINTK(1, "bad depth %u\n", - l_fb_info->current_par->depth); + ((u32*)(info->pseudo_palette))[regno] = v; break; } + return 0; } - if (regno < 256) { - l_fb_info->palette[regno].red = red >> 8; - l_fb_info->palette[regno].green = green >> 8; - l_fb_info->palette[regno].blue = blue >> 8; - l_fb_info->palette[regno].transp = transp >> 8; - if (l_fb_info->current_par->depth == 8) - pm3fb_set_color(l_fb_info, regno, red >> 8, - green >> 8, blue >> 8); - } - return (regno > 255); + else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) + pm3fb_set_color(par, regno, red, green, blue); + + return 0; } -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) +static int pm3fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 video; - - DTRACE; - - if (!current_par_valid[l_fb_info->board_num]) - return (1); + struct pm3_par *par = info->par; + const u32 xres = (var->xres + 31) & ~31; + + par->base = pm3fb_shift_bpp(var->bits_per_pixel, + (var->yoffset * xres) + + var->xoffset); + PM3_WAIT(par, 1); + PM3_WRITE_REG(par, PM3ScreenBase, par->base); + return 0; +} - video = l_fb_info->current_par->video; +static int pm3fb_blank(int blank_mode, struct fb_info *info) +{ + struct pm3_par *par = info->par; + u32 video = par->video; /* * Oxygen VX1 - it appears that setting PM3VideoControl and @@ -3181,454 +866,323 @@ static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - if (blank_mode > 0) { - switch (blank_mode - 1) { - - case VESA_NO_BLANKING: /* FIXME */ - video = video & ~(PM3VideoControl_ENABLE); - break; - - case VESA_HSYNC_SUSPEND: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_VSYNC_SUSPEND: - video = video & ~(PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_POWERDOWN: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - default: - DPRINTK(1, "Unsupported blanking %d\n", - blank_mode); - return (1); - break; - } - } - - PM3_SLOW_WRITE_REG(PM3VideoControl, video); - - return (0); -} - -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - u32 flags; - - DTRACE; - - local_irq_save(flags); - info->info.screen_base = l_fb_info->v_fb; - switch (p->depth) { -#ifdef FBCON_HAS_CFB8 - case 8: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb8; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb8; + switch (blank_mode) { + case FB_BLANK_UNBLANK: + video |= PM3VideoControl_ENABLE; break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap12; + case FB_BLANK_NORMAL: + video &= ~(PM3VideoControl_ENABLE); break; - case 15: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap15; + case FB_BLANK_HSYNC_SUSPEND: + video &= ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; - case 16: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap16; + case FB_BLANK_VSYNC_SUSPEND: + video &= ~(PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif -#ifdef FBCON_HAS_CFB32 - case 32: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb32; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb32; - disp->dispsw_data = l_fb_info->cmap.cmap32; + case FB_BLANK_POWERDOWN: + video &= ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif /* FBCON_HAS_CFB32 */ default: - disp->dispsw = &fbcon_dummy; - DPRINTK(1, "Invalid depth, using fbcon_dummy\n"); - break; + DPRINTK("Unsupported blanking %d\n", blank_mode); + return 1; } - local_irq_restore(flags); + + PM3_WAIT(par, 1); + PM3_WRITE_REG(par,PM3VideoControl, video); + return 0; } -/* */ -static void pm3fb_detect(void) -{ - struct pci_dev *dev_array[PM3_MAX_BOARD]; - struct pci_dev *dev = NULL; - struct pm3fb_info *l_fb_info = &(fb_info[0]); - unsigned long i, j, done; + /* + * Frame buffer operations + */ - DTRACE; +static struct fb_ops pm3fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = pm3fb_check_var, + .fb_set_par = pm3fb_set_par, + .fb_setcolreg = pm3fb_setcolreg, + .fb_pan_display = pm3fb_pan_display, + .fb_fillrect = pm3fb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = pm3fb_blank, + .fb_sync = pm3fb_sync, +}; - for (i = 0; i < PM3_MAX_BOARD; i++) { - dev_array[i] = NULL; - fb_info[i].dev = NULL; - } +/* ------------------------------------------------------------------------- */ - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); + /* + * Initialization + */ - for (i = 0; ((i < PM3_MAX_BOARD) && dev); i++) { - dev_array[i] = dev; - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); - } +/* mmio register are already mapped when this function is called */ +/* the pm3fb_fix.smem_start is also set */ +static unsigned long pm3fb_size_memory(struct pm3_par *par) +{ + unsigned long memsize = 0, tempBypass, i, temp1, temp2; + unsigned char __iomem *screen_mem; - if (dev) { /* more than PM3_MAX_BOARD */ - printk(KERN_WARNING "pm3fb: Warning: more than %d boards found\n", - PM3_MAX_BOARD); + pm3fb_fix.smem_len = 64 * 1024l * 1024; /* request full aperture size */ + /* Linear frame buffer - request region and map it. */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + return 0; } - - if (!dev_array[0]) { /* not a single board, abort */ - return; + screen_mem = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!screen_mem) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + return 0; } - /* allocate user-defined boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if ((bus[i] >= 0) && (slot[i] >= 0) && (func[i] >= 0)) { - for (j = 0; j < PM3_MAX_BOARD; j++) { - if ((dev_array[j] != NULL) && - (dev_array[j]->bus->number == bus[i]) - && (PCI_SLOT(dev_array[j]->devfn) == - slot[i]) - && (PCI_FUNC(dev_array[j]->devfn) == - func[i])) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - } - } - } + /* TODO: card-specific stuff, *before* accessing *any* FB memory */ + /* For Appian Jeronimo 2000 board second head */ + + tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask); + + DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); + + PM3_WAIT(par, 1); + PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF); + + /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ + for (i = 0; i < 32; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = fb_readl((screen_mem + (i * 1048576))); + + /* Let's check for wrapover, write will fail at 16MB boundary */ + if (temp1 == (i * 0x00345678)) + memsize = i; + else + break; } - /* allocate remaining boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if (fb_info[i].dev == NULL) { - done = 0; - for (j = 0; ((j < PM3_MAX_BOARD) && (!done)); j++) { - if (dev_array[j] != NULL) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - done = 1; - } - } + + DPRINTK("First detect pass already got %ld MB\n", memsize + 1); + + if (memsize + 1 == i) { + for (i = 0; i < 32; i++) { + /* Clear first 32MB ; 0 is 0, no need to byteswap */ + writel(0x0000000, (screen_mem + (i * 1048576))); } - } + wmb(); - /* at that point, all PCI Permedia3 are detected and allocated */ - /* now, initialize... or not */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - if (l_fb_info->dev && !disable[i]) { /* PCI device was found and not disabled by user */ - DPRINTK(2, - "found @%lx Vendor %lx Device %lx ; base @ : %lx - %lx - %lx - %lx - %lx - %lx, irq %ld\n", - (unsigned long) l_fb_info->dev, - (unsigned long) l_fb_info->dev->vendor, - (unsigned long) l_fb_info->dev->device, - (unsigned long) - pci_resource_start(l_fb_info->dev, 0), - (unsigned long) - pci_resource_start(l_fb_info->dev, 1), - (unsigned long) - pci_resource_start(l_fb_info->dev, 2), - (unsigned long) - pci_resource_start(l_fb_info->dev, 3), - (unsigned long) - pci_resource_start(l_fb_info->dev, 4), - (unsigned long) - pci_resource_start(l_fb_info->dev, 5), - (unsigned long) l_fb_info->dev->irq); - - l_fb_info->pIOBase = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 0); -#ifdef __BIG_ENDIAN - l_fb_info->pIOBase += PM3_REGS_SIZE; -#endif - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->p_fb = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 1); - l_fb_info->v_fb = (unsigned char *) -1; - - if (!request_mem_region - ((unsigned long)l_fb_info->p_fb, 64 * 1024 * 1024, /* request full aperture size */ - "pm3fb")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request framebuffer memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (!request_mem_region - ((unsigned long)l_fb_info->pIOBase, PM3_REGS_SIZE, - "pm3fb I/O regs")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request IObase memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (forcesize[l_fb_info->board_num]) - l_fb_info->fb_size = forcesize[l_fb_info->board_num]; - - l_fb_info->fb_size = - pm3fb_size_memory(l_fb_info); - if (l_fb_info->fb_size) { - (void) pci_enable_device(l_fb_info->dev); - pm3fb_common_init(l_fb_info); - } else - printk(KERN_ERR "pm3fb: memory problem, not enabling board #%ld\n", l_fb_info->board_num); + for (i = 32; i < 64; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = + fb_readl((screen_mem + (i * 1048576))); + temp2 = + fb_readl((screen_mem + ((i - 32) * 1048576))); + /* different value, different RAM... */ + if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) + memsize = i; + else + break; } } -} + DPRINTK("Second detect pass got %ld MB\n", memsize + 1); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; + PM3_WAIT(par, 1); + PM3_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass); - DTRACE; + iounmap(screen_mem); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + memsize = 1048576 * (memsize + 1); - if (!current_par_valid[l_fb_info->board_num]) - return -EINVAL; + DPRINTK("Returning 0x%08lx bytes\n", memsize); - l_fb_info->current_par->base = /* in 128 bits chunk - i.e. AFTER Shiftbpp */ - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - (var->yoffset * l_fb_info->current_par->width) + - var->xoffset); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - return 0; + return memsize; } -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) +static int __devinit pm3fb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 cm, i; -#ifdef PM3FB_MASTER_DEBUG - char cc[3]; -#endif /* PM3FB_MASTER_DEBUG */ + struct fb_info *info; + struct pm3_par *par; + struct device* device = &dev->dev; /* for pci drivers */ + int err, retval = -ENXIO; - switch(cmd) - { -#ifdef PM3FB_MASTER_DEBUG - case PM3FBIO_CLEARMEMORY: - if (copy_from_user(&cm, (void *)arg, sizeof(u32))) - return(-EFAULT); - pm3fb_clear_memory(l_fb_info, cm); - return(0); - break; + err = pci_enable_device(dev); + if (err) { + printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err); + return err; + } + /* + * Dynamically allocate info and par + */ + info = framebuffer_alloc(sizeof(struct pm3_par), device); - case PM3FBIO_CLEARCMAP: - if (copy_from_user(cc, (void*)arg, 3 * sizeof(char))) - return(-EFAULT); - pm3fb_clear_colormap(l_fb_info, cc[0], cc[1], cc[2]); - return(0); - break; -#endif /* PM3FB_MASTER_DEBUG */ - - case PM3FBIO_RESETCHIP: - cm = 1; - PM3_SLOW_WRITE_REG(PM3ResetStatus, 1); - for (i = 0 ; (i < 10000) && cm ; i++) - { - PM3_DELAY(10); - cm = PM3_READ_REG(PM3ResetStatus); - } - if (cm) - { - printk(KERN_ERR "pm3fb: chip reset failed with status 0x%x\n", cm); - return(-EIO); - } - /* first thing first, reload memory timings */ - pm3fb_write_memory_timings(l_fb_info); -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ - pm3fb_write_mode(l_fb_info); - return(0); - break; + if (!info) + return -ENOMEM; + par = info->par; - default: - DPRINTK(2, "unknown ioctl: %d (%x)\n", cmd, cmd); - return(-EINVAL); + /* + * Here we set the screen_base to the virtual memory address + * for the framebuffer. + */ + pm3fb_fix.mmio_start = pci_resource_start(dev, 0); + pm3fb_fix.mmio_len = PM3_REGS_SIZE; + + /* Registers - request region and map it. */ + if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len, + "pm3fb regbase")) { + printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n"); + goto err_exit_neither; + } + par->v_regs = + ioremap_nocache(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + if (!par->v_regs) { + printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n", + pm3fb_fix.id); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + goto err_exit_neither; + } + +#if defined(__BIG_ENDIAN) + pm3fb_fix.mmio_start += PM3_REGS_SIZE; + DPRINTK("Adjusting register base for big-endian.\n"); +#endif + /* Linear frame buffer - request region and map it. */ + pm3fb_fix.smem_start = pci_resource_start(dev, 1); + pm3fb_fix.smem_len = pm3fb_size_memory(par); + if (!pm3fb_fix.smem_len) + { + printk(KERN_WARNING "pm3fb: Can't find memory on board.\n"); + goto err_exit_mmio; } -} - -/* ****************************************** */ -/* ***** standard FB API init functions ***** */ -/* ****************************************** */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + goto err_exit_mmio; + } + info->screen_base = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!info->screen_base) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + goto err_exit_mmio; + } + info->screen_size = pm3fb_fix.smem_len; -int __init pm3fb_setup(char *options) -{ - long opsi = strlen(options); + info->fbops = &pm3fb_ops; - DTRACE; + par->video = PM3_READ_REG(par, PM3VideoControl); - memcpy(g_options, options, - ((opsi + 1) > - PM3_OPTIONS_SIZE) ? PM3_OPTIONS_SIZE : (opsi + 1)); - g_options[PM3_OPTIONS_SIZE - 1] = 0; + info->fix = pm3fb_fix; + info->pseudo_palette = par->palette; + info->flags = FBINFO_DEFAULT | + FBINFO_HWACCEL_FILLRECT;/* | FBINFO_HWACCEL_YPAN;*/ - return (0); -} + /* + * This should give a reasonable default video mode. The following is + * done when we can set a video mode. + */ + if (!mode_option) + mode_option = "640x480@60"; -int __init pm3fb_init(void) -{ - DTRACE; + retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); - DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $"); + if (!retval || retval == 4) { + retval = -EINVAL; + goto err_exit_both; + } - pm3fb_real_setup(g_options); + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + retval = -ENOMEM; + goto err_exit_both; + } - pm3fb_detect(); + /* + * For drivers that can... + */ + pm3fb_check_var(&info->var, info); - if (!fb_info[0].dev) { /* not even one board ??? */ - DPRINTK(1, "No PCI Permedia3 board detected\n"); + if (register_framebuffer(info) < 0) { + retval = -EINVAL; + goto err_exit_all; } - return (0); -} + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + pci_set_drvdata(dev, info); + return 0; -/* ************************* */ -/* **** Module support ***** */ -/* ************************* */ - -#ifdef MODULE -MODULE_AUTHOR("Romain Dolbeau"); -MODULE_DESCRIPTION("Permedia3 framebuffer device driver"); -static char *mode[PM3_MAX_BOARD]; -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode,"video mode"); -module_param_array(disable, short, NULL, 0); -MODULE_PARM_DESC(disable,"disable board"); -static short off[PM3_MAX_BOARD]; -module_param_array(off, short, NULL, 0); -MODULE_PARM_DESC(off,"disable board"); -static char *pciid[PM3_MAX_BOARD]; -module_param_array(pciid, charp, NULL, 0); -MODULE_PARM_DESC(pciid,"board PCI Id"); -module_param_array(noaccel, short, NULL, 0); -MODULE_PARM_DESC(noaccel,"disable accel"); -static char *font[PM3_MAX_BOARD]; -module_param_array(font, charp, NULL, 0); -MODULE_PARM_DESC(font,"choose font"); -module_param(depth, short, NULL, 0); -MODULE_PARM_DESC(depth,"boot-time depth"); -module_param(printtimings, short, NULL, 0); -MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)"); -module_param(forcesize, short, NULL, 0); -MODULE_PARM_DESC(forcesize, "force specified memory size"); -/* -MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards") -MODULE_GENERIC_TABLE(gtype,name) -MODULE_DEVICE_TABLE(type,name) -*/ + err_exit_all: + fb_dealloc_cmap(&info->cmap); + err_exit_both: + iounmap(info->screen_base); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + err_exit_mmio: + iounmap(par->v_regs); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + err_exit_neither: + framebuffer_release(info); + return retval; +} -void pm3fb_build_options(void) + /* + * Cleanup + */ +static void __devexit pm3fb_remove(struct pci_dev *dev) { - int i; - char ts[128]; + struct fb_info *info = pci_get_drvdata(dev); - strcpy(g_options, "pm3fb"); - for (i = 0; i < PM3_MAX_BOARD ; i++) - { - if (mode[i]) - { - sprintf(ts, ",mode:%d:%s", i, mode[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (disable[i] || off[i]) - { - sprintf(ts, ",disable:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (pciid[i]) - { - sprintf(ts, ",pciid:%d:%s", i, pciid[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (noaccel[i]) - { - sprintf(ts, ",noaccel:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (font[i]) - { - sprintf(ts, ",font:%d:%s", i, font[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (depth[i]) - { - sprintf(ts, ",depth:%d:%d", i, depth[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } + if (info) { + struct fb_fix_screeninfo *fix = &info->fix; + struct pm3_par *par = info->par; + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + + iounmap(info->screen_base); + release_mem_region(fix->smem_start, fix->smem_len); + iounmap(par->v_regs); + release_mem_region(fix->mmio_start, fix->mmio_len); + + pci_set_drvdata(dev, NULL); + framebuffer_release(info); } - g_options[PM3_OPTIONS_SIZE - 1] = '\0'; - DPRINTK(1, "pm3fb use options: %s\n", g_options); } -int init_module(void) -{ - DTRACE; +static struct pci_device_id pm3fb_id_table[] = { + { PCI_VENDOR_ID_3DLABS, 0x0a, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, } +}; - pm3fb_build_options(); +/* For PCI drivers */ +static struct pci_driver pm3fb_driver = { + .name = "pm3fb", + .id_table = pm3fb_id_table, + .probe = pm3fb_probe, + .remove = __devexit_p(pm3fb_remove), +}; - pm3fb_init(); +MODULE_DEVICE_TABLE(pci, pm3fb_id_table); - return 0; +static int __init pm3fb_init(void) +{ +#ifndef MODULE + if (fb_get_options("pm3fb", NULL)) + return -ENODEV; +#endif + return pci_register_driver(&pm3fb_driver); } -void cleanup_module(void) +static void __exit pm3fb_exit(void) { - DTRACE; - { - unsigned long i; - struct pm3fb_info *l_fb_info; - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - pci_dev_put(l_fb_info->dev); - if (l_fb_info->dev != NULL && !(disable[l_fb_info->board_num])) { - if (l_fb_info->vIOBase != (unsigned char *) -1) { - pm3fb_unmapIO(l_fb_info); - release_mem_region(l_fb_info->p_fb, - l_fb_info->fb_size); - release_mem_region(l_fb_info->pIOBase, - PM3_REGS_SIZE); - } - unregister_framebuffer(&l_fb_info->gen.info); - } - } - } + pci_unregister_driver(&pm3fb_driver); } -#endif /* MODULE */ + +module_init(pm3fb_init); +module_exit(pm3fb_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 9756a728b74..646ec823c16 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); @@ -951,12 +923,14 @@ static int ps3fb_xdr_settings(u64 xdr_lpar) static struct fb_ops ps3fb_ops = { .fb_open = ps3fb_open, .fb_release = ps3fb_release, + .fb_read = fb_sys_read, + .fb_write = fb_sys_write, .fb_check_var = ps3fb_check_var, .fb_set_par = ps3fb_set_par, .fb_setcolreg = ps3fb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, .fb_mmap = ps3fb_mmap, .fb_blank = ps3fb_blank, .fb_ioctl = ps3fb_ioctl, @@ -964,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; @@ -984,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); @@ -1027,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; @@ -1040,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) @@ -1059,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; @@ -1082,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); @@ -1094,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); @@ -1126,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 df2909ae704..f9300266044 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 48536c3e58a..4beac1df617 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 0fe547842c6..41381e61832 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -2146,7 +2146,7 @@ static void __devexit rivafb_remove(struct pci_dev *pd) * ------------------------------------------------------------------------- */ #ifndef MODULE -static int __init rivafb_setup(char *options) +static int __devinit rivafb_setup(char *options) { char *this_opt; diff --git a/drivers/video/riva/riva_hw.c b/drivers/video/riva/riva_hw.c index 70bfd78eca8..13307703a9f 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/riva/rivafb-i2c.c b/drivers/video/riva/rivafb-i2c.c index 76e6ce353c8..a0e22ac483a 100644 --- a/drivers/video/riva/rivafb-i2c.c +++ b/drivers/video/riva/rivafb-i2c.c @@ -70,8 +70,6 @@ static int riva_gpio_getscl(void* data) if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04) val = 1; - val = VGA_RD08(par->riva.PCIO, 0x3d5); - return val; } diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 3d7507ad55f..b855f4a34af 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 ebb6756aea0..4fb16240c04 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 d5e2d9c2784..d53bf6945f0 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 a30e1e13d8b..e8ccace0125 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/skeletonfb.c b/drivers/video/skeletonfb.c index 836a612af97..64779e70408 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -132,7 +132,6 @@ static struct fb_info info; static struct xxx_par __initdata current_par; int xxxfb_init(void); -int xxxfb_setup(char*); /** * xxxfb_open - Optional function. Called when the framebuffer is @@ -975,6 +974,21 @@ static struct platform_device xxxfb_device = { .name = "xxxfb", }; +#ifndef MODULE + /* + * Setup + */ + +/* + * Only necessary if your driver takes special options, + * otherwise we fall back on the generic fb_setup(). + */ +int __init xxxfb_setup(char *options) +{ + /* Parse user speficied options (`video=xxxfb:') */ +} +#endif /* MODULE */ + static int __init xxxfb_init(void) { int ret; @@ -1006,21 +1020,6 @@ static void __exit xxxfb_exit(void) } #endif /* CONFIG_PCI */ -#ifdef MODULE - /* - * Setup - */ - -/* - * Only necessary if your driver takes special options, - * otherwise we fall back on the generic fb_setup(). - */ -int __init xxxfb_setup(char *options) -{ - /* Parse user speficied options (`video=xxxfb:') */ -} -#endif /* MODULE */ - /* ------------------------------------------------------------------------- */ diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 62fa5500361..5eff28ce4f4 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/sunxvr2500.c b/drivers/video/sunxvr2500.c index 4316c7fe8e2..c3869a96ab5 100644 --- a/drivers/video/sunxvr2500.c +++ b/drivers/video/sunxvr2500.c @@ -28,7 +28,7 @@ struct s3d_info { unsigned int depth; unsigned int fb_size; - u32 pseudo_palette[256]; + u32 pseudo_palette[16]; }; static int __devinit s3d_get_props(struct s3d_info *sp) @@ -52,15 +52,14 @@ static int s3d_setcolreg(unsigned regno, { u32 value; - if (regno >= 256) - return 1; + if (regno < 16) { + red >>= 8; + green >>= 8; + blue >>= 8; - red >>= 8; - green >>= 8; - blue >>= 8; - - value = (blue << 24) | (green << 16) | (red << 8); - ((u32 *)info->pseudo_palette)[regno] = value; + value = (blue << 24) | (green << 16) | (red << 8); + ((u32 *)info->pseudo_palette)[regno] = value; + } return 0; } diff --git a/drivers/video/sunxvr500.c b/drivers/video/sunxvr500.c index 08880a62bfa..71bf3f1f00b 100644 --- a/drivers/video/sunxvr500.c +++ b/drivers/video/sunxvr500.c @@ -50,7 +50,7 @@ struct e3d_info { u32 fb8_0_off; u32 fb8_1_off; - u32 pseudo_palette[256]; + u32 pseudo_palette[16]; }; static int __devinit e3d_get_props(struct e3d_info *ep) @@ -126,7 +126,9 @@ static int e3d_setcolreg(unsigned regno, blue_8 = blue >> 8; value = (blue_8 << 24) | (green_8 << 16) | (red_8 << 8); - ((u32 *)info->pseudo_palette)[regno] = value; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) + ((u32 *)info->pseudo_palette)[regno] = value; red_10 = red >> 6; diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index f0fde6ea7c3..89facb73edf 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 55e8aa450bf..c699864b6f4 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 07389ba01ef..e6f7c78da68 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 ad66f070acb..7b0cef9ca8f 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 5e9755e464a..4c3a63308df 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, @@ -778,9 +778,10 @@ err_enable_device: static void __devexit vt8623_pci_remove(struct pci_dev *dev) { struct fb_info *info = pci_get_drvdata(dev); - struct vt8623fb_info *par = info->par; if (info) { + struct vt8623fb_info *par = info->par; + #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { mtrr_del(par->mtrr_reg, 0, 0); @@ -902,7 +903,7 @@ static void __exit vt8623fb_cleanup(void) /* Driver Initialisation */ -int __init vt8623fb_init(void) +static int __init vt8623fb_init(void) { #ifndef MODULE diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index 5fc86ea2069..003c49a490e 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -660,7 +660,7 @@ int __init w100fb_probe(struct platform_device *pdev) err = -ENODEV; goto out; } - printk(" at 0x%08lx.\n", mem->start+W100_CFG_BASE); + printk(" at 0x%08lx.\n", (unsigned long) mem->start+W100_CFG_BASE); /* Remap the framebuffer */ remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE); @@ -753,10 +753,14 @@ int __init w100fb_probe(struct platform_device *pdev) goto out; } - device_create_file(&pdev->dev, &dev_attr_fastpllclk); - device_create_file(&pdev->dev, &dev_attr_reg_read); - device_create_file(&pdev->dev, &dev_attr_reg_write); - device_create_file(&pdev->dev, &dev_attr_flip); + err = device_create_file(&pdev->dev, &dev_attr_fastpllclk); + err |= device_create_file(&pdev->dev, &dev_attr_reg_read); + err |= device_create_file(&pdev->dev, &dev_attr_reg_write); + err |= device_create_file(&pdev->dev, &dev_attr_flip); + + if (err != 0) + printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n", + info->node, err); printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); return 0; |