diff options
Diffstat (limited to 'drivers/video')
72 files changed, 9240 insertions, 2109 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0..d916ac04aba 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -11,6 +11,13 @@ config HAVE_FB_ATMEL config HAVE_FB_IMX bool +config SH_MIPI_DSI + tristate + depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK + +config SH_LCD_MIPI_DSI + bool + source "drivers/char/agp/Kconfig" source "drivers/gpu/vga/Kconfig" @@ -186,6 +193,14 @@ config FB_SYS_FOPS depends on FB default n +config FB_WMT_GE_ROPS + tristate + depends on FB + default n + ---help--- + Include functions for accelerated rectangle filling and area + copying using WonderMedia Graphics Engine operations. + config FB_DEFERRED_IO bool depends on FB @@ -406,7 +421,7 @@ config FB_SA1100 Y here. config FB_IMX - tristate "Motorola i.MX LCD support" + tristate "Freescale i.MX LCD support" depends on FB && (HAVE_FB_IMX || ARCH_MX1 || ARCH_MX2) select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -635,6 +650,72 @@ config FB_BFIN_LQ035Q1 To compile this driver as a module, choose M here: the module will be called bfin-lq035q1-fb. +config FB_BF537_LQ035 + tristate "SHARP LQ035 TFT LCD (BF537 STAMP)" + depends on FB && (BF534 || BF536 || BF537) && I2C_BLACKFIN_TWI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select BFIN_GPTIMERS + help + This is the framebuffer device for a SHARP LQ035Q7DB03 TFT LCD + attached to a BF537. + + To compile this driver as a module, choose M here: the + module will be called bf537-lq035. + +config FB_BFIN_7393 + tristate "Blackfin ADV7393 Video encoder" + depends on FB && BLACKFIN + select I2C + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for a ADV7393 video encoder + attached to a Blackfin on the PPI port. + If your Blackfin board has a ADV7393 select Y. + + To compile this driver as a module, choose M here: the + module will be called bfin_adv7393fb. + +choice + prompt "Video mode support" + depends on FB_BFIN_7393 + default NTSC + +config NTSC + bool 'NTSC 720x480' + +config PAL + bool 'PAL 720x576' + +config NTSC_640x480 + bool 'NTSC 640x480 (Experimental)' + +config PAL_640x480 + bool 'PAL 640x480 (Experimental)' + +config NTSC_YCBCR + bool 'NTSC 720x480 YCbCR input' + +config PAL_YCBCR + bool 'PAL 720x576 YCbCR input' + +endchoice + +choice + prompt "Size of ADV7393 frame buffer memory Single/Double Size" + depends on (FB_BFIN_7393) + default ADV7393_1XMEM + +config ADV7393_1XMEM + bool 'Single' + +config ADV7393_2XMEM + bool 'Double' +endchoice + config FB_STI tristate "HP STI frame buffer device support" depends on FB && PARISC @@ -750,24 +831,14 @@ config FB_N411 config FB_HGA tristate "Hercules mono graphics support" depends on FB && X86 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT help Say Y here if you have a Hercules mono graphics card. To compile this driver as a module, choose M here: the module will be called hgafb. - As this card technology is 15 years old, most people will answer N - here. - -config FB_HGA_ACCEL - bool "Hercules mono Acceleration functions (EXPERIMENTAL)" - depends on FB_HGA && EXPERIMENTAL - ---help--- - This will compile the Hercules mono graphics with - acceleration functions. + As this card technology is at least 25 years old, + most people will answer N here. config FB_SGIVW tristate "SGI Visual Workstation framebuffer support" @@ -1209,7 +1280,7 @@ config FB_MATROX module will be called matroxfb. You can pass several parameters to the driver at boot time or at - module load time. The parameters look like "video=matrox:XXX", and + module load time. The parameters look like "video=matroxfb:XXX", and are described in <file:Documentation/fb/matroxfb.txt>. config FB_MATROX_MILLENIUM @@ -1722,6 +1793,24 @@ config FB_AU1200 various panels and CRTs by passing in kernel cmd line option au1200fb:panel=<name>. +config FB_VT8500 + bool "VT8500 LCD Driver" + depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500 + select FB_WMT_GE_ROPS + select FB_SYS_IMAGEBLIT + help + This is the framebuffer driver for VIA VT8500 integrated LCD + controller. + +config FB_WM8505 + bool "WM8505 frame buffer support" + depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505 + select FB_WMT_GE_ROPS + select FB_SYS_IMAGEBLIT + help + This is the framebuffer driver for WonderMedia WM8505 + integrated LCD controller. + source "drivers/video/geode/Kconfig" config FB_HIT @@ -1850,6 +1939,16 @@ config FB_PXA_PARAMETERS <file:Documentation/fb/pxafb.txt> describes the available parameters. +config PXA3XX_GCU + tristate "PXA3xx 2D graphics accelerator driver" + depends on FB_PXA + help + Kernelspace driver for the 2D graphics controller unit (GCU) + found on PXA3xx processors. There is a counterpart driver in the + DirectFB suite, see http://www.directfb.org/ + + If you compile this as a module, it will be called pxa3xx_gcu. + config FB_MBX tristate "2700G LCD framebuffer support" depends on FB && ARCH_PXA @@ -1898,13 +1997,6 @@ config FB_W100 If unsure, say N. -config SH_MIPI_DSI - tristate - depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK - -config SH_LCD_MIPI_DSI - bool - config FB_SH_MOBILE_LCDC tristate "SuperH Mobile LCDC framebuffer support" depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK @@ -2034,6 +2126,20 @@ config FB_SM501 If unsure, say N. +config FB_UDL + tristate "Displaylink USB Framebuffer support" + depends on FB && USB + select FB_MODE_HELPERS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + ---help--- + This is a kernel framebuffer driver for DisplayLink USB devices. + Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and + mplayer -vo fbdev. Supports all USB 2.0 era DisplayLink devices. + To compile as a module, choose M here: the module name is udlfb. config FB_PNX4008_DUM tristate "Display Update Module support on Philips PNX4008 board" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318..8c8fabdff9d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o obj-$(CONFIG_FB_MACMODES) += macmodes.o obj-$(CONFIG_FB_DDC) += fb_ddc.o obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o +obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o # Hardware specific drivers go first obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o @@ -100,10 +101,13 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o +obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o obj-$(CONFIG_FB_AU1200) += au1200fb.o +obj-$(CONFIG_FB_VT8500) += vt8500lcdfb.o +obj-$(CONFIG_FB_WM8505) += wm8505fb.o obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o @@ -122,6 +126,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o +obj-$(CONFIG_FB_UDL) += udlfb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o @@ -141,9 +146,11 @@ obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_EFI) += efifb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o obj-$(CONFIG_FB_OF) += offb.o +obj-$(CONFIG_FB_BF537_LQ035) += bf537-lq035.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o +obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 8dce2512633..bac16345021 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -111,7 +111,7 @@ static int atmel_bl_get_brightness(struct backlight_device *bl) return lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); } -static struct backlight_ops atmel_lcdc_bl_ops = { +static const struct backlight_ops atmel_lcdc_bl_ops = { .update_status = atmel_bl_update_status, .get_brightness = atmel_bl_get_brightness, }; diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 34a0851bcbf..dd9de2e8058 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1786,7 +1786,7 @@ static int aty128_bl_get_brightness(struct backlight_device *bd) return bd->props.brightness; } -static struct backlight_ops aty128_bl_data = { +static const struct backlight_ops aty128_bl_data = { .get_brightness = aty128_bl_get_brightness, .update_status = aty128_bl_update_status, }; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 5bf91236c70..767ab4fb1a0 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2221,7 +2221,7 @@ static int aty_bl_get_brightness(struct backlight_device *bd) return bd->props.brightness; } -static struct backlight_ops aty_bl_data = { +static const struct backlight_ops aty_bl_data = { .get_brightness = aty_bl_get_brightness, .update_status = aty_bl_update_status, }; @@ -2969,10 +2969,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, { struct atyfb_par *par = info->par; struct device_node *dp; - char prop[128]; - phandle node; - int len, i, j, ret; u32 mem, chip_id; + int i, j, ret; /* * Map memory-mapped registers. @@ -3088,23 +3086,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, aty_st_le32(MEM_CNTL, mem, par); } - /* - * If this is the console device, we will set default video - * settings to what the PROM left us with. - */ - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "aliases"); - if (node) { - len = prom_getproperty(node, "screen", prop, sizeof(prop)); - if (len > 0) { - prop[len] = '\0'; - node = prom_finddevice(prop); - } else - node = 0; - } - dp = pci_device_to_OF_node(pdev); - if (node == dp->phandle) { + if (dp == of_console_device) { struct fb_var_screeninfo *var = &default_var; unsigned int N, P, Q, M, T, R; u32 v_total, h_total; @@ -3112,9 +3095,9 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, u8 pll_regs[16]; u8 clock_cntl; - crtc.vxres = prom_getintdefault(node, "width", 1024); - crtc.vyres = prom_getintdefault(node, "height", 768); - var->bits_per_pixel = prom_getintdefault(node, "depth", 8); + crtc.vxres = of_getintprop_default(dp, "width", 1024); + crtc.vyres = of_getintprop_default(dp, "height", 768); + var->bits_per_pixel = of_getintprop_default(dp, "depth", 8); var->xoffset = var->yoffset = 0; crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c index 256966e9667..9b811ddbce8 100644 --- a/drivers/video/aty/radeon_backlight.c +++ b/drivers/video/aty/radeon_backlight.c @@ -128,7 +128,7 @@ static int radeon_bl_get_brightness(struct backlight_device *bd) return bd->props.brightness; } -static struct backlight_ops radeon_bl_data = { +static const struct backlight_ops radeon_bl_data = { .get_brightness = radeon_bl_get_brightness, .update_status = radeon_bl_update_status, }; diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index 38ffc3fbcbe..c789c46e38a 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -155,7 +155,7 @@ out: return -EINVAL; } -static struct backlight_ops pm860x_backlight_ops = { +static const struct backlight_ops pm860x_backlight_ops = { .options = BL_CORE_SUSPENDRESUME, .update_status = pm860x_backlight_update_status, .get_brightness = pm860x_backlight_get_brightness, diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index c67801e57aa..98ad3e5f7c8 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -25,7 +25,7 @@ struct l4f00242t03_priv { struct spi_device *spi; struct lcd_device *ld; - int lcd_on:1; + int lcd_state; struct regulator *io_reg; struct regulator *core_reg; }; @@ -62,11 +62,36 @@ static void l4f00242t03_lcd_init(struct spi_device *spi) regulator_enable(priv->core_reg); } + l4f00242t03_reset(pdata->reset_gpio); + gpio_set_value(pdata->data_enable_gpio, 1); msleep(60); spi_write(spi, (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16)); } +static void l4f00242t03_lcd_powerdown(struct spi_device *spi) +{ + struct l4f00242t03_pdata *pdata = spi->dev.platform_data; + struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Powering down LCD\n"); + + gpio_set_value(pdata->data_enable_gpio, 0); + + if (priv->io_reg) + regulator_disable(priv->io_reg); + + if (priv->core_reg) + regulator_disable(priv->core_reg); +} + +static int l4f00242t03_lcd_power_get(struct lcd_device *ld) +{ + struct l4f00242t03_priv *priv = lcd_get_data(ld); + + return priv->lcd_state; +} + static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) { struct l4f00242t03_priv *priv = lcd_get_data(ld); @@ -79,35 +104,54 @@ static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) const u16 disoff = 0x28; if (power <= FB_BLANK_NORMAL) { - if (priv->lcd_on) - return 0; - - dev_dbg(&spi->dev, "turning on LCD\n"); - - spi_write(spi, (const u8 *)&slpout, sizeof(u16)); - msleep(60); - spi_write(spi, (const u8 *)&dison, sizeof(u16)); - - priv->lcd_on = 1; + if (priv->lcd_state <= FB_BLANK_NORMAL) { + /* Do nothing, the LCD is running */ + } else if (priv->lcd_state < FB_BLANK_POWERDOWN) { + dev_dbg(&spi->dev, "Resuming LCD\n"); + + spi_write(spi, (const u8 *)&slpout, sizeof(u16)); + msleep(60); + spi_write(spi, (const u8 *)&dison, sizeof(u16)); + } else { + /* priv->lcd_state == FB_BLANK_POWERDOWN */ + l4f00242t03_lcd_init(spi); + priv->lcd_state = FB_BLANK_VSYNC_SUSPEND; + l4f00242t03_lcd_power_set(priv->ld, power); + } + } else if (power < FB_BLANK_POWERDOWN) { + if (priv->lcd_state <= FB_BLANK_NORMAL) { + /* Send the display in standby */ + dev_dbg(&spi->dev, "Standby the LCD\n"); + + spi_write(spi, (const u8 *)&disoff, sizeof(u16)); + msleep(60); + spi_write(spi, (const u8 *)&slpin, sizeof(u16)); + } else if (priv->lcd_state < FB_BLANK_POWERDOWN) { + /* Do nothing, the LCD is already in standby */ + } else { + /* priv->lcd_state == FB_BLANK_POWERDOWN */ + l4f00242t03_lcd_init(spi); + priv->lcd_state = FB_BLANK_UNBLANK; + l4f00242t03_lcd_power_set(ld, power); + } } else { - if (!priv->lcd_on) - return 0; - - dev_dbg(&spi->dev, "turning off LCD\n"); - - spi_write(spi, (const u8 *)&disoff, sizeof(u16)); - msleep(60); - spi_write(spi, (const u8 *)&slpin, sizeof(u16)); - - priv->lcd_on = 0; + /* power == FB_BLANK_POWERDOWN */ + if (priv->lcd_state != FB_BLANK_POWERDOWN) { + /* Clear the screen before shutting down */ + spi_write(spi, (const u8 *)&disoff, sizeof(u16)); + msleep(60); + l4f00242t03_lcd_powerdown(spi); + } } + priv->lcd_state = power; + return 0; } static struct lcd_ops l4f_ops = { .set_power = l4f00242t03_lcd_power_set, - .get_power = NULL, + .get_power = l4f00242t03_lcd_power_get, }; static int __devinit l4f00242t03_probe(struct spi_device *spi) @@ -185,9 +229,9 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) } /* Init the LCD */ - l4f00242t03_reset(pdata->reset_gpio); l4f00242t03_lcd_init(spi); - l4f00242t03_lcd_power_set(priv->ld, 1); + priv->lcd_state = FB_BLANK_VSYNC_SUSPEND; + l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_UNBLANK); dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n"); @@ -214,9 +258,11 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi) struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data; - l4f00242t03_lcd_power_set(priv->ld, 0); + l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); lcd_device_unregister(priv->ld); + dev_set_drvdata(&spi->dev, NULL); + gpio_free(pdata->data_enable_gpio); gpio_free(pdata->reset_gpio); @@ -230,6 +276,15 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi) return 0; } +static void l4f00242t03_shutdown(struct spi_device *spi) +{ + struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + + if (priv) + l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); + +} + static struct spi_driver l4f00242t03_driver = { .driver = { .name = "l4f00242t03", @@ -237,6 +292,7 @@ static struct spi_driver l4f00242t03_driver = { }, .probe = l4f00242t03_probe, .remove = __devexit_p(l4f00242t03_remove), + .shutdown = l4f00242t03_shutdown, }; static __init int l4f00242t03_init(void) diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index b2b2c7ba1f6..209acc105cb 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -92,7 +92,7 @@ static int max8925_backlight_get_brightness(struct backlight_device *bl) return ret; } -static struct backlight_ops max8925_backlight_ops = { +static const struct backlight_ops max8925_backlight_ops = { .options = BL_CORE_SUSPENDRESUME, .update_status = max8925_backlight_update_status, .get_brightness = max8925_backlight_get_brightness, diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c new file mode 100644 index 00000000000..18c507874ff --- /dev/null +++ b/drivers/video/bf537-lq035.c @@ -0,0 +1,914 @@ +/* + * Analog Devices Blackfin(BF537 STAMP) + SHARP TFT LCD. + * http://docs.blackfin.uclinux.org/doku.php?id=hw:cards:tft-lcd + * + * Copyright 2006-2010 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/device.h> +#include <linux/backlight.h> +#include <linux/lcd.h> +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <asm/blackfin.h> +#include <asm/irq.h> +#include <asm/dpmc.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#define NO_BL 1 + +#define MAX_BRIGHENESS 95 +#define MIN_BRIGHENESS 5 +#define NBR_PALETTE 256 + +static const unsigned short ppi_pins[] = { + P_PPI0_CLK, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0 +}; + +static unsigned char *fb_buffer; /* RGB Buffer */ +static unsigned long *dma_desc_table; +static int t_conf_done, lq035_open_cnt; +static DEFINE_SPINLOCK(bfin_lq035_lock); + +static int landscape; +module_param(landscape, int, 0); +MODULE_PARM_DESC(landscape, + "LANDSCAPE use 320x240 instead of Native 240x320 Resolution"); + +static int bgr; +module_param(bgr, int, 0); +MODULE_PARM_DESC(bgr, + "BGR use 16-bit BGR-565 instead of RGB-565"); + +static int nocursor = 1; +module_param(nocursor, int, 0644); +MODULE_PARM_DESC(nocursor, "cursor enable/disable"); + +static unsigned long current_brightness; /* backlight */ + +/* AD5280 vcomm */ +static unsigned char vcomm_value = 150; +static struct i2c_client *ad5280_client; + +static void set_vcomm(void) +{ + int nr; + + if (!ad5280_client) + return; + + nr = i2c_smbus_write_byte_data(ad5280_client, 0x00, vcomm_value); + if (nr) + pr_err("i2c_smbus_write_byte_data fail: %d\n", nr); +} + +static int __devinit ad5280_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + ret = i2c_smbus_write_byte_data(client, 0x00, vcomm_value); + if (ret) { + dev_err(&client->dev, "write fail: %d\n", ret); + return ret; + } + + ad5280_client = client; + + return 0; +} + +static int __devexit ad5280_remove(struct i2c_client *client) +{ + ad5280_client = NULL; + return 0; +} + +static const struct i2c_device_id ad5280_id[] = { + {"bf537-lq035-ad5280", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad5280_id); + +static struct i2c_driver ad5280_driver = { + .driver = { + .name = "bf537-lq035-ad5280", + }, + .probe = ad5280_probe, + .remove = __devexit_p(ad5280_remove), + .id_table = ad5280_id, +}; + +#ifdef CONFIG_PNAV10 +#define MOD GPIO_PH13 + +#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER0_CONFIG +#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER0_WIDTH +#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER0_PERIOD +#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER0_COUNTER +#define TIMDIS_LP TIMDIS0 +#define TIMEN_LP TIMEN0 + +#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG +#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH +#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD +#define TIMDIS_SPS TIMDIS1 +#define TIMEN_SPS TIMEN1 + +#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER5_CONFIG +#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER5_WIDTH +#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER5_PERIOD +#define TIMDIS_SP TIMDIS5 +#define TIMEN_SP TIMEN5 + +#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER2_CONFIG +#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER2_WIDTH +#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER2_PERIOD +#define TIMDIS_PS_CLS TIMDIS2 +#define TIMEN_PS_CLS TIMEN2 + +#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER3_CONFIG +#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER3_WIDTH +#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER3_PERIOD +#define TIMDIS_REV TIMDIS3 +#define TIMEN_REV TIMEN3 +#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER3_COUNTER + +#define FREQ_PPI_CLK (5*1024*1024) /* PPI_CLK 5MHz */ + +#define TIMERS {P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR5, 0} + +#else + +#define UD GPIO_PF13 /* Up / Down */ +#define MOD GPIO_PF10 +#define LBR GPIO_PF14 /* Left Right */ + +#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER6_CONFIG +#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER6_WIDTH +#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER6_PERIOD +#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER6_COUNTER +#define TIMDIS_LP TIMDIS6 +#define TIMEN_LP TIMEN6 + +#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG +#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH +#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD +#define TIMDIS_SPS TIMDIS1 +#define TIMEN_SPS TIMEN1 + +#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER0_CONFIG +#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER0_WIDTH +#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER0_PERIOD +#define TIMDIS_SP TIMDIS0 +#define TIMEN_SP TIMEN0 + +#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER7_CONFIG +#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER7_WIDTH +#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER7_PERIOD +#define TIMDIS_PS_CLS TIMDIS7 +#define TIMEN_PS_CLS TIMEN7 + +#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER5_CONFIG +#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER5_WIDTH +#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER5_PERIOD +#define TIMDIS_REV TIMDIS5 +#define TIMEN_REV TIMEN5 +#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER5_COUNTER + +#define FREQ_PPI_CLK (6*1000*1000) /* PPI_CLK 6MHz */ +#define TIMERS {P_TMR0, P_TMR1, P_TMR5, P_TMR6, P_TMR7, 0} + +#endif + +#define LCD_X_RES 240 /* Horizontal Resolution */ +#define LCD_Y_RES 320 /* Vertical Resolution */ + +#define LCD_BBP 16 /* Bit Per Pixel */ + +/* the LCD and the DMA start counting differently; + * since one starts at 0 and the other starts at 1, + * we have a difference of 1 between START_LINES + * and U_LINES. + */ +#define START_LINES 8 /* lines for field flyback or field blanking signal */ +#define U_LINES 9 /* number of undisplayed blanking lines */ + +#define FRAMES_PER_SEC (60) + +#define DCLKS_PER_FRAME (FREQ_PPI_CLK/FRAMES_PER_SEC) +#define DCLKS_PER_LINE (DCLKS_PER_FRAME/(LCD_Y_RES+U_LINES)) + +#define PPI_CONFIG_VALUE (PORT_DIR|XFR_TYPE|DLEN_16|POLS) +#define PPI_DELAY_VALUE (0) +#define TIMER_CONFIG (PWM_OUT|PERIOD_CNT|TIN_SEL|CLK_SEL) + +#define ACTIVE_VIDEO_MEM_OFFSET (LCD_X_RES*START_LINES*(LCD_BBP/8)) +#define ACTIVE_VIDEO_MEM_SIZE (LCD_Y_RES*LCD_X_RES*(LCD_BBP/8)) +#define TOTAL_VIDEO_MEM_SIZE ((LCD_Y_RES+U_LINES)*LCD_X_RES*(LCD_BBP/8)) +#define TOTAL_DMA_DESC_SIZE (2 * sizeof(u32) * (LCD_Y_RES + U_LINES)) + +static void start_timers(void) /* CHECK with HW */ +{ + unsigned long flags; + + local_irq_save(flags); + + bfin_write_TIMER_ENABLE(TIMEN_REV); + SSYNC(); + + while (bfin_read_TIMER_REV_COUNTER() <= 11) + continue; + bfin_write_TIMER_ENABLE(TIMEN_LP); + SSYNC(); + + while (bfin_read_TIMER_LP_COUNTER() < 3) + continue; + bfin_write_TIMER_ENABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS); + SSYNC(); + t_conf_done = 1; + local_irq_restore(flags); +} + +static void config_timers(void) +{ + /* Stop timers */ + bfin_write_TIMER_DISABLE(TIMDIS_SP|TIMDIS_SPS|TIMDIS_REV| + TIMDIS_LP|TIMDIS_PS_CLS); + SSYNC(); + + /* LP, timer 6 */ + bfin_write_TIMER_LP_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_LP_WIDTH(1); + + bfin_write_TIMER_LP_PERIOD(DCLKS_PER_LINE); + SSYNC(); + + /* SPS, timer 1 */ + bfin_write_TIMER_SPS_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_SPS_WIDTH(DCLKS_PER_LINE*2); + bfin_write_TIMER_SPS_PERIOD((DCLKS_PER_LINE * (LCD_Y_RES+U_LINES))); + SSYNC(); + + /* SP, timer 0 */ + bfin_write_TIMER_SP_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_SP_WIDTH(1); + bfin_write_TIMER_SP_PERIOD(DCLKS_PER_LINE); + SSYNC(); + + /* PS & CLS, timer 7 */ + bfin_write_TIMER_PS_CLS_CONFIG(TIMER_CONFIG); + bfin_write_TIMER_PS_CLS_WIDTH(LCD_X_RES + START_LINES); + bfin_write_TIMER_PS_CLS_PERIOD(DCLKS_PER_LINE); + + SSYNC(); + +#ifdef NO_BL + /* REV, timer 5 */ + bfin_write_TIMER_REV_CONFIG(TIMER_CONFIG|PULSE_HI); + + bfin_write_TIMER_REV_WIDTH(DCLKS_PER_LINE); + bfin_write_TIMER_REV_PERIOD(DCLKS_PER_LINE*2); + + SSYNC(); +#endif +} + +static void config_ppi(void) +{ + bfin_write_PPI_DELAY(PPI_DELAY_VALUE); + bfin_write_PPI_COUNT(LCD_X_RES-1); + /* 0x10 -> PORT_CFG -> 2 or 3 frame syncs */ + bfin_write_PPI_CONTROL((PPI_CONFIG_VALUE|0x10) & (~POLS)); +} + +static int config_dma(void) +{ + u32 i; + + if (landscape) { + + for (i = 0; i < U_LINES; ++i) { + /* blanking lines point to first line of fb_buffer */ + dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; + dma_desc_table[2*i+1] = (unsigned long)fb_buffer; + } + + for (i = U_LINES; i < U_LINES + LCD_Y_RES; ++i) { + /* visible lines */ + dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; + dma_desc_table[2*i+1] = (unsigned long)fb_buffer + + (LCD_Y_RES+U_LINES-1-i)*2; + } + + /* last descriptor points to first */ + dma_desc_table[2*(LCD_Y_RES+U_LINES-1)] = (unsigned long)&dma_desc_table[0]; + + set_dma_x_count(CH_PPI, LCD_X_RES); + set_dma_x_modify(CH_PPI, LCD_Y_RES * (LCD_BBP / 8)); + set_dma_y_count(CH_PPI, 0); + set_dma_y_modify(CH_PPI, 0); + set_dma_next_desc_addr(CH_PPI, (void *)dma_desc_table[0]); + set_dma_config(CH_PPI, DMAFLOW_LARGE | NDSIZE_4 | WDSIZE_16); + + } else { + + set_dma_config(CH_PPI, set_bfin_dma_config(DIR_READ, + DMA_FLOW_AUTO, + INTR_DISABLE, + DIMENSION_2D, + DATA_SIZE_16, + DMA_NOSYNC_KEEP_DMA_BUF)); + set_dma_x_count(CH_PPI, LCD_X_RES); + set_dma_x_modify(CH_PPI, LCD_BBP / 8); + set_dma_y_count(CH_PPI, LCD_Y_RES+U_LINES); + set_dma_y_modify(CH_PPI, LCD_BBP / 8); + set_dma_start_addr(CH_PPI, (unsigned long) fb_buffer); + } + + return 0; +} + +static int __devinit request_ports(void) +{ + u16 tmr_req[] = TIMERS; + + /* + UD: PF13 + MOD: PF10 + LBR: PF14 + PPI_CLK: PF15 + */ + + if (peripheral_request_list(ppi_pins, KBUILD_MODNAME)) { + pr_err("requesting PPI peripheral failed\n"); + return -EBUSY; + } + + if (peripheral_request_list(tmr_req, KBUILD_MODNAME)) { + peripheral_free_list(ppi_pins); + pr_err("requesting timer peripheral failed\n"); + return -EBUSY; + } + +#if (defined(UD) && defined(LBR)) + if (gpio_request(UD, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", UD); + return -EBUSY; + } + + if (gpio_request(LBR, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", LBR); + gpio_free(UD); + return -EBUSY; + } + + gpio_direction_output(UD, 0); + gpio_direction_output(LBR, 1); + +#endif + + if (gpio_request(MOD, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", MOD); +#if (defined(UD) && defined(LBR)) + gpio_free(LBR); + gpio_free(UD); +#endif + return -EBUSY; + } + + gpio_direction_output(MOD, 1); + + SSYNC(); + return 0; +} + +static void free_ports(void) +{ + u16 tmr_req[] = TIMERS; + + peripheral_free_list(ppi_pins); + peripheral_free_list(tmr_req); + +#if defined(UD) && defined(LBR) + gpio_free(LBR); + gpio_free(UD); +#endif + gpio_free(MOD); +} + +static struct fb_info bfin_lq035_fb; + +static struct fb_var_screeninfo bfin_lq035_fb_defined = { + .bits_per_pixel = LCD_BBP, + .activate = FB_ACTIVATE_TEST, + .xres = LCD_X_RES, /*default portrait mode RGB*/ + .yres = LCD_Y_RES, + .xres_virtual = LCD_X_RES, + .yres_virtual = LCD_Y_RES, + .height = -1, + .width = -1, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +}; + +static struct fb_fix_screeninfo bfin_lq035_fb_fix __devinitdata = { + .id = KBUILD_MODNAME, + .smem_len = ACTIVE_VIDEO_MEM_SIZE, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .line_length = LCD_X_RES*(LCD_BBP/8), + .accel = FB_ACCEL_NONE, +}; + + +static int bfin_lq035_fb_open(struct fb_info *info, int user) +{ + unsigned long flags; + + spin_lock_irqsave(&bfin_lq035_lock, flags); + lq035_open_cnt++; + spin_unlock_irqrestore(&bfin_lq035_lock, flags); + + if (lq035_open_cnt <= 1) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + + set_vcomm(); + config_dma(); + config_ppi(); + + /* start dma */ + enable_dma(CH_PPI); + SSYNC(); + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); + SSYNC(); + + if (!t_conf_done) { + config_timers(); + start_timers(); + } + /* gpio_set_value(MOD,1); */ + } + + return 0; +} + +static int bfin_lq035_fb_release(struct fb_info *info, int user) +{ + unsigned long flags; + + spin_lock_irqsave(&bfin_lq035_lock, flags); + lq035_open_cnt--; + spin_unlock_irqrestore(&bfin_lq035_lock, flags); + + + if (lq035_open_cnt <= 0) { + + bfin_write_PPI_CONTROL(0); + SSYNC(); + + disable_dma(CH_PPI); + } + + return 0; +} + + +static int bfin_lq035_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + switch (var->bits_per_pixel) { + case 16:/* DIRECTCOLOUR, 64k */ + var->red.offset = info->var.red.offset; + var->green.offset = info->var.green.offset; + var->blue.offset = info->var.blue.offset; + var->red.length = info->var.red.length; + var->green.length = info->var.green.length; + var->blue.length = info->var.blue.length; + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + break; + default: + pr_debug("%s: depth not supported: %u BPP\n", __func__, + var->bits_per_pixel); + return -EINVAL; + } + + if (info->var.xres != var->xres || + info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_debug("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +/* fb_rotate + * Rotate the display of this angle. This doesn't seems to be used by the core, + * but as our hardware supports it, so why not implementing it... + */ +static void bfin_lq035_fb_rotate(struct fb_info *fbi, int angle) +{ + pr_debug("%s: %p %d", __func__, fbi, angle); +#if (defined(UD) && defined(LBR)) + switch (angle) { + + case 180: + gpio_set_value(LBR, 0); + gpio_set_value(UD, 1); + break; + default: + gpio_set_value(LBR, 1); + gpio_set_value(UD, 0); + break; + } +#endif +} + +static int bfin_lq035_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + if (nocursor) + return 0; + else + return -EINVAL; /* just to force soft_cursor() call */ +} + +static int bfin_lq035_fb_setcolreg(u_int regno, u_int red, u_int green, + u_int blue, u_int transp, + struct fb_info *info) +{ + if (regno >= NBR_PALETTE) + return -EINVAL; + + if (info->var.grayscale) + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + + u32 value; + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + + value = (red << info->var.red.offset) | + (green << info->var.green.offset)| + (blue << info->var.blue.offset); + value &= 0xFFFF; + + ((u32 *) (info->pseudo_palette))[regno] = value; + + } + + return 0; +} + +static struct fb_ops bfin_lq035_fb_ops = { + .owner = THIS_MODULE, + .fb_open = bfin_lq035_fb_open, + .fb_release = bfin_lq035_fb_release, + .fb_check_var = bfin_lq035_fb_check_var, + .fb_rotate = bfin_lq035_fb_rotate, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = bfin_lq035_fb_cursor, + .fb_setcolreg = bfin_lq035_fb_setcolreg, +}; + +static int bl_get_brightness(struct backlight_device *bd) +{ + return current_brightness; +} + +static const struct backlight_ops bfin_lq035fb_bl_ops = { + .get_brightness = bl_get_brightness, +}; + +static struct backlight_device *bl_dev; + +static int bfin_lcd_get_power(struct lcd_device *dev) +{ + return 0; +} + +static int bfin_lcd_set_power(struct lcd_device *dev, int power) +{ + return 0; +} + +static int bfin_lcd_get_contrast(struct lcd_device *dev) +{ + return (int)vcomm_value; +} + +static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) +{ + if (contrast > 255) + contrast = 255; + if (contrast < 0) + contrast = 0; + + vcomm_value = (unsigned char)contrast; + set_vcomm(); + return 0; +} + +static int bfin_lcd_check_fb(struct lcd_device *lcd, struct fb_info *fi) +{ + if (!fi || (fi == &bfin_lq035_fb)) + return 1; + return 0; +} + +static struct lcd_ops bfin_lcd_ops = { + .get_power = bfin_lcd_get_power, + .set_power = bfin_lcd_set_power, + .get_contrast = bfin_lcd_get_contrast, + .set_contrast = bfin_lcd_set_contrast, + .check_fb = bfin_lcd_check_fb, +}; + +static struct lcd_device *lcd_dev; + +static int __devinit bfin_lq035_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + dma_addr_t dma_handle; + + if (request_dma(CH_PPI, KBUILD_MODNAME)) { + pr_err("couldn't request PPI DMA\n"); + return -EFAULT; + } + + if (request_ports()) { + pr_err("couldn't request gpio port\n"); + free_dma(CH_PPI); + return -EFAULT; + } + + fb_buffer = dma_alloc_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, + &dma_handle, GFP_KERNEL); + if (fb_buffer == NULL) { + pr_err("couldn't allocate dma buffer\n"); + free_dma(CH_PPI); + free_ports(); + return -ENOMEM; + } + + if (L1_DATA_A_LENGTH) + dma_desc_table = l1_data_sram_zalloc(TOTAL_DMA_DESC_SIZE); + else + dma_desc_table = dma_alloc_coherent(NULL, TOTAL_DMA_DESC_SIZE, + &dma_handle, 0); + + if (dma_desc_table == NULL) { + pr_err("couldn't allocate dma descriptor\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + return -ENOMEM; + } + + bfin_lq035_fb.screen_base = (void *)fb_buffer; + bfin_lq035_fb_fix.smem_start = (int)fb_buffer; + if (landscape) { + bfin_lq035_fb_defined.xres = LCD_Y_RES; + bfin_lq035_fb_defined.yres = LCD_X_RES; + bfin_lq035_fb_defined.xres_virtual = LCD_Y_RES; + bfin_lq035_fb_defined.yres_virtual = LCD_X_RES; + + bfin_lq035_fb_fix.line_length = LCD_Y_RES*(LCD_BBP/8); + } else { + bfin_lq035_fb.screen_base += ACTIVE_VIDEO_MEM_OFFSET; + bfin_lq035_fb_fix.smem_start += ACTIVE_VIDEO_MEM_OFFSET; + } + + bfin_lq035_fb_defined.green.msb_right = 0; + bfin_lq035_fb_defined.red.msb_right = 0; + bfin_lq035_fb_defined.blue.msb_right = 0; + bfin_lq035_fb_defined.green.offset = 5; + bfin_lq035_fb_defined.green.length = 6; + bfin_lq035_fb_defined.red.length = 5; + bfin_lq035_fb_defined.blue.length = 5; + + if (bgr) { + bfin_lq035_fb_defined.red.offset = 0; + bfin_lq035_fb_defined.blue.offset = 11; + } else { + bfin_lq035_fb_defined.red.offset = 11; + bfin_lq035_fb_defined.blue.offset = 0; + } + + bfin_lq035_fb.fbops = &bfin_lq035_fb_ops; + bfin_lq035_fb.var = bfin_lq035_fb_defined; + + bfin_lq035_fb.fix = bfin_lq035_fb_fix; + bfin_lq035_fb.flags = FBINFO_DEFAULT; + + + bfin_lq035_fb.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); + if (bfin_lq035_fb.pseudo_palette == NULL) { + pr_err("failed to allocate pseudo_palette\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + return -ENOMEM; + } + + if (fb_alloc_cmap(&bfin_lq035_fb.cmap, NBR_PALETTE, 0) < 0) { + pr_err("failed to allocate colormap (%d entries)\n", + NBR_PALETTE); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + kfree(bfin_lq035_fb.pseudo_palette); + return -EFAULT; + } + + if (register_framebuffer(&bfin_lq035_fb) < 0) { + pr_err("unable to register framebuffer\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + fb_buffer = NULL; + kfree(bfin_lq035_fb.pseudo_palette); + fb_dealloc_cmap(&bfin_lq035_fb.cmap); + return -EINVAL; + } + + i2c_add_driver(&ad5280_driver); + + memset(&props, 0, sizeof(props)); + props.max_brightness = MAX_BRIGHENESS; + bl_dev = backlight_device_register("bf537-bl", NULL, NULL, + &bfin_lq035fb_bl_ops, &props); + + lcd_dev = lcd_device_register(KBUILD_MODNAME, &pdev->dev, NULL, + &bfin_lcd_ops); + lcd_dev->props.max_contrast = 255, + + pr_info("initialized"); + + return 0; +} + +static int __devexit bfin_lq035_remove(struct platform_device *pdev) +{ + if (fb_buffer != NULL) + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + + if (L1_DATA_A_LENGTH) + l1_data_sram_free(dma_desc_table); + else + dma_free_coherent(NULL, TOTAL_DMA_DESC_SIZE, NULL, 0); + + bfin_write_TIMER_DISABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS| + TIMEN_LP|TIMEN_REV); + t_conf_done = 0; + + free_dma(CH_PPI); + + + kfree(bfin_lq035_fb.pseudo_palette); + fb_dealloc_cmap(&bfin_lq035_fb.cmap); + + + lcd_device_unregister(lcd_dev); + backlight_device_unregister(bl_dev); + + unregister_framebuffer(&bfin_lq035_fb); + i2c_del_driver(&ad5280_driver); + + free_ports(); + + pr_info("unregistered LCD driver\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_lq035_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (lq035_open_cnt > 0) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + disable_dma(CH_PPI); + } + + return 0; +} + +static int bfin_lq035_resume(struct platform_device *pdev) +{ + if (lq035_open_cnt > 0) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + + config_dma(); + config_ppi(); + + enable_dma(CH_PPI); + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); + SSYNC(); + + config_timers(); + start_timers(); + } else { + t_conf_done = 0; + } + + return 0; +} +#else +# define bfin_lq035_suspend NULL +# define bfin_lq035_resume NULL +#endif + +static struct platform_driver bfin_lq035_driver = { + .probe = bfin_lq035_probe, + .remove = __devexit_p(bfin_lq035_remove), + .suspend = bfin_lq035_suspend, + .resume = bfin_lq035_resume, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init bfin_lq035_driver_init(void) +{ + request_module("i2c-bfin-twi"); + return platform_driver_register(&bfin_lq035_driver); +} +module_init(bfin_lq035_driver_init); + +static void __exit bfin_lq035_driver_cleanup(void) +{ + platform_driver_unregister(&bfin_lq035_driver); +} +module_exit(bfin_lq035_driver_cleanup); + +MODULE_DESCRIPTION("SHARP LQ035Q7DB03 TFT LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c new file mode 100644 index 00000000000..8486f541156 --- /dev/null +++ b/drivers/video/bfin_adv7393fb.c @@ -0,0 +1,832 @@ +/* + * Frame buffer driver for ADV7393/2 video encoder + * + * Copyright 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or late. + */ + +/* + * TODO: Remove Globals + * TODO: Code Cleanup + */ + +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <asm/blackfin.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <asm/portmux.h> + +#include <linux/dma-mapping.h> +#include <linux/proc_fs.h> +#include <linux/platform_device.h> + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> + +#include "bfin_adv7393fb.h" + +static int mode = VMODE; +static int mem = VMEM; +static int nocursor = 1; + +static const unsigned short ppi_pins[] = { + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + 0 +}; + +/* + * card parameters + */ + +static struct bfin_adv7393_fb_par { + /* structure holding blackfin / adv7393 paramters when + screen is blanked */ + struct { + u8 Mode; /* ntsc/pal/? */ + } vga_state; + atomic_t ref_count; +} bfin_par; + +/* --------------------------------------------------------------------- */ + +static struct fb_var_screeninfo bfin_adv7393_fb_defined = { + .xres = 720, + .yres = 480, + .xres_virtual = 720, + .yres_virtual = 480, + .bits_per_pixel = 16, + .activate = FB_ACTIVATE_TEST, + .height = -1, + .width = -1, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .vmode = FB_VMODE_INTERLACED, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +}; + +static struct fb_fix_screeninfo bfin_adv7393_fb_fix __devinitdata = { + .id = "BFIN ADV7393", + .smem_len = 720 * 480 * 2, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .line_length = 720 * 2, + .accel = FB_ACCEL_NONE +}; + +static struct fb_ops bfin_adv7393_fb_ops = { + .owner = THIS_MODULE, + .fb_open = bfin_adv7393_fb_open, + .fb_release = bfin_adv7393_fb_release, + .fb_check_var = bfin_adv7393_fb_check_var, + .fb_pan_display = bfin_adv7393_fb_pan_display, + .fb_blank = bfin_adv7393_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = bfin_adv7393_fb_cursor, + .fb_setcolreg = bfin_adv7393_fb_setcolreg, +}; + +static int dma_desc_list(struct adv7393fb_device *fbdev, u16 arg) +{ + if (arg == BUILD) { /* Build */ + fbdev->vb1 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->vb1 == NULL) + goto error; + + fbdev->av1 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->av1 == NULL) + goto error; + + fbdev->vb2 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->vb2 == NULL) + goto error; + + fbdev->av2 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->av2 == NULL) + goto error; + + /* Build linked DMA descriptor list */ + fbdev->vb1->next_desc_addr = fbdev->av1; + fbdev->av1->next_desc_addr = fbdev->vb2; + fbdev->vb2->next_desc_addr = fbdev->av2; + fbdev->av2->next_desc_addr = fbdev->vb1; + + /* Save list head */ + fbdev->descriptor_list_head = fbdev->av2; + + /* Vertical Blanking Field 1 */ + fbdev->vb1->start_addr = VB_DUMMY_MEMORY_SOURCE; + fbdev->vb1->cfg = DMA_CFG_VAL; + + fbdev->vb1->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->vb1->x_modify = 0; + fbdev->vb1->y_count = fbdev->modes[mode].vb1_lines; + fbdev->vb1->y_modify = 0; + + /* Active Video Field 1 */ + + fbdev->av1->start_addr = (unsigned long)fbdev->fb_mem; + fbdev->av1->cfg = DMA_CFG_VAL; + fbdev->av1->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + fbdev->av1->x_modify = fbdev->modes[mode].bpp / 8; + fbdev->av1->y_count = fbdev->modes[mode].a_lines; + fbdev->av1->y_modify = + (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + + 1) * (fbdev->modes[mode].bpp / 8); + + /* Vertical Blanking Field 2 */ + + fbdev->vb2->start_addr = VB_DUMMY_MEMORY_SOURCE; + fbdev->vb2->cfg = DMA_CFG_VAL; + fbdev->vb2->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->vb2->x_modify = 0; + fbdev->vb2->y_count = fbdev->modes[mode].vb2_lines; + fbdev->vb2->y_modify = 0; + + /* Active Video Field 2 */ + + fbdev->av2->start_addr = + (unsigned long)fbdev->fb_mem + fbdev->line_len; + + fbdev->av2->cfg = DMA_CFG_VAL; + + fbdev->av2->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->av2->x_modify = (fbdev->modes[mode].bpp / 8); + fbdev->av2->y_count = fbdev->modes[mode].a_lines; + + fbdev->av2->y_modify = + (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + + 1) * (fbdev->modes[mode].bpp / 8); + + return 1; + } + +error: + l1_data_sram_free(fbdev->vb1); + l1_data_sram_free(fbdev->av1); + l1_data_sram_free(fbdev->vb2); + l1_data_sram_free(fbdev->av2); + + return 0; +} + +static int bfin_config_dma(struct adv7393fb_device *fbdev) +{ + BUG_ON(!(fbdev->fb_mem)); + + set_dma_x_count(CH_PPI, fbdev->descriptor_list_head->x_count); + set_dma_x_modify(CH_PPI, fbdev->descriptor_list_head->x_modify); + set_dma_y_count(CH_PPI, fbdev->descriptor_list_head->y_count); + set_dma_y_modify(CH_PPI, fbdev->descriptor_list_head->y_modify); + set_dma_start_addr(CH_PPI, fbdev->descriptor_list_head->start_addr); + set_dma_next_desc_addr(CH_PPI, + fbdev->descriptor_list_head->next_desc_addr); + set_dma_config(CH_PPI, fbdev->descriptor_list_head->cfg); + + return 1; +} + +static void bfin_disable_dma(void) +{ + bfin_write_DMA0_CONFIG(bfin_read_DMA0_CONFIG() & ~DMAEN); +} + +static void bfin_config_ppi(struct adv7393fb_device *fbdev) +{ + if (ANOMALY_05000183) { + bfin_write_TIMER2_CONFIG(WDTH_CAP); + bfin_write_TIMER_ENABLE(TIMEN2); + } + + bfin_write_PPI_CONTROL(0x381E); + bfin_write_PPI_FRAME(fbdev->modes[mode].tot_lines); + bfin_write_PPI_COUNT(fbdev->modes[mode].xres + + fbdev->modes[mode].boeft_blank - 1); + bfin_write_PPI_DELAY(fbdev->modes[mode].aoeft_blank - 1); +} + +static void bfin_enable_ppi(void) +{ + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); +} + +static void bfin_disable_ppi(void) +{ + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN); +} + +static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7393_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +adv7393_write_block(struct i2c_client *client, + const u8 *data, unsigned int len) +{ + int ret = -1; + u8 reg; + + while (len >= 2) { + reg = *data++; + ret = adv7393_write(client, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + + return ret; +} + +static int adv7393_mode(struct i2c_client *client, u16 mode) +{ + switch (mode) { + case POWER_ON: /* ADV7393 Sleep mode OFF */ + adv7393_write(client, 0x00, 0x1E); + break; + case POWER_DOWN: /* ADV7393 Sleep mode ON */ + adv7393_write(client, 0x00, 0x1F); + break; + case BLANK_OFF: /* Pixel Data Valid */ + adv7393_write(client, 0x82, 0xCB); + break; + case BLANK_ON: /* Pixel Data Invalid */ + adv7393_write(client, 0x82, 0x8B); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static irqreturn_t ppi_irq_error(int irq, void *dev_id) +{ + + struct adv7393fb_device *fbdev = (struct adv7393fb_device *)dev_id; + + u16 status = bfin_read_PPI_STATUS(); + + pr_debug("%s: PPI Status = 0x%X\n", __func__, status); + + if (status) { + bfin_disable_dma(); /* TODO: Check Sequence */ + bfin_disable_ppi(); + bfin_clear_PPI_STATUS(); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + } + + return IRQ_HANDLED; + +} + +static int proc_output(char *buf) +{ + char *p = buf; + + p += sprintf(p, + "Usage:\n" + "echo 0x[REG][Value] > adv7393\n" + "example: echo 0x1234 >adv7393\n" + "writes 0x34 into Register 0x12\n"); + + return p - buf; +} + +static int +adv7393_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = proc_output(page); + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int +adv7393_write_proc(struct file *file, const char __user * buffer, + unsigned long count, void *data) +{ + struct adv7393fb_device *fbdev = data; + char line[8]; + unsigned int val; + int ret; + + ret = copy_from_user(line, buffer, count); + if (ret) + return -EFAULT; + + val = simple_strtoul(line, NULL, 0); + adv7393_write(fbdev->client, val >> 8, val & 0xff); + + return count; +} + +static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct proc_dir_entry *entry; + int num_modes = ARRAY_SIZE(known_modes); + + struct adv7393fb_device *fbdev = NULL; + + if (mem > 2) { + dev_err(&client->dev, "mem out of allowed range [1;2]\n"); + return -EINVAL; + } + + if (mode > num_modes) { + dev_err(&client->dev, "mode %d: not supported", mode); + return -EFAULT; + } + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(&client->dev, "failed to allocate device private record"); + return -ENOMEM; + } + + i2c_set_clientdata(client, fbdev); + + fbdev->modes = known_modes; + fbdev->client = client; + + fbdev->fb_len = + mem * fbdev->modes[mode].xres * fbdev->modes[mode].xres * + (fbdev->modes[mode].bpp / 8); + + fbdev->line_len = + fbdev->modes[mode].xres * (fbdev->modes[mode].bpp / 8); + + /* Workaround "PPI Does Not Start Properly In Specific Mode" */ + if (ANOMALY_05000400) { + if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) { + dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); + ret = -EBUSY; + goto out_8; + } + gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); + } + + if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { + dev_err(&client->dev, "requesting PPI peripheral failed\n"); + ret = -EFAULT; + goto out_8; + } + + fbdev->fb_mem = + dma_alloc_coherent(NULL, fbdev->fb_len, &fbdev->dma_handle, + GFP_KERNEL); + + if (NULL == fbdev->fb_mem) { + dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", + (u32) fbdev->fb_len); + ret = -ENOMEM; + goto out_7; + } + + fbdev->info.screen_base = (void *)fbdev->fb_mem; + bfin_adv7393_fb_fix.smem_start = (int)fbdev->fb_mem; + + bfin_adv7393_fb_fix.smem_len = fbdev->fb_len; + bfin_adv7393_fb_fix.line_length = fbdev->line_len; + + if (mem > 1) + bfin_adv7393_fb_fix.ypanstep = 1; + + bfin_adv7393_fb_defined.red.length = 5; + bfin_adv7393_fb_defined.green.length = 6; + bfin_adv7393_fb_defined.blue.length = 5; + + bfin_adv7393_fb_defined.xres = fbdev->modes[mode].xres; + bfin_adv7393_fb_defined.yres = fbdev->modes[mode].yres; + bfin_adv7393_fb_defined.xres_virtual = fbdev->modes[mode].xres; + bfin_adv7393_fb_defined.yres_virtual = mem * fbdev->modes[mode].yres; + bfin_adv7393_fb_defined.bits_per_pixel = fbdev->modes[mode].bpp; + + fbdev->info.fbops = &bfin_adv7393_fb_ops; + fbdev->info.var = bfin_adv7393_fb_defined; + fbdev->info.fix = bfin_adv7393_fb_fix; + fbdev->info.par = &bfin_par; + fbdev->info.flags = FBINFO_DEFAULT; + + fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); + if (!fbdev->info.pseudo_palette) { + dev_err(&client->dev, "failed to allocate pseudo_palette\n"); + ret = -ENOMEM; + goto out_6; + } + + if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", + BFIN_LCD_NBR_PALETTE_ENTRIES); + ret = -EFAULT; + goto out_5; + } + + if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { + dev_err(&client->dev, "unable to request PPI DMA\n"); + ret = -EFAULT; + goto out_4; + } + + if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, IRQF_DISABLED, + "PPI ERROR", fbdev) < 0) { + dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); + ret = -EFAULT; + goto out_3; + } + + fbdev->open = 0; + + ret = adv7393_write_block(client, fbdev->modes[mode].adv7393_i2c_initd, + fbdev->modes[mode].adv7393_i2c_initd_len); + + if (ret) { + dev_err(&client->dev, "i2c attach: init error\n"); + goto out_1; + } + + + if (register_framebuffer(&fbdev->info) < 0) { + dev_err(&client->dev, "unable to register framebuffer\n"); + ret = -EFAULT; + goto out_1; + } + + dev_info(&client->dev, "fb%d: %s frame buffer device\n", + fbdev->info.node, fbdev->info.fix.id); + dev_info(&client->dev, "fb memory address : 0x%p\n", fbdev->fb_mem); + + entry = create_proc_entry("driver/adv7393", 0, NULL); + if (!entry) { + dev_err(&client->dev, "unable to create /proc entry\n"); + ret = -EFAULT; + goto out_0; + } + + entry->read_proc = adv7393_read_proc; + entry->write_proc = adv7393_write_proc; + entry->data = fbdev; + + return 0; + + out_0: + unregister_framebuffer(&fbdev->info); + out_1: + free_irq(IRQ_PPI_ERROR, fbdev); + out_3: + free_dma(CH_PPI); + out_4: + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, + fbdev->dma_handle); + out_5: + fb_dealloc_cmap(&fbdev->info.cmap); + out_6: + kfree(fbdev->info.pseudo_palette); + out_7: + peripheral_free_list(ppi_pins); + out_8: + kfree(fbdev); + + return ret; +} + +static int bfin_adv7393_fb_open(struct fb_info *info, int user) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + fbdev->info.screen_base = (void *)fbdev->fb_mem; + if (!fbdev->info.screen_base) { + dev_err(&fbdev->client->dev, "unable to map device\n"); + return -ENOMEM; + } + + fbdev->open = 1; + dma_desc_list(fbdev, BUILD); + adv7393_mode(fbdev->client, BLANK_OFF); + bfin_config_ppi(fbdev); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + + return 0; +} + +static int bfin_adv7393_fb_release(struct fb_info *info, int user) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + adv7393_mode(fbdev->client, BLANK_ON); + bfin_disable_dma(); + bfin_disable_ppi(); + dma_desc_list(fbdev, DESTRUCT); + fbdev->open = 0; + return 0; +} + +static int +bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + + switch (var->bits_per_pixel) { + case 16:/* DIRECTCOLOUR, 64k */ + var->red.offset = info->var.red.offset; + var->green.offset = info->var.green.offset; + var->blue.offset = info->var.blue.offset; + var->red.length = info->var.red.length; + var->green.length = info->var.green.length; + var->blue.length = info->var.blue.length; + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + break; + default: + pr_debug("%s: depth not supported: %u BPP\n", __func__, + var->bits_per_pixel); + return -EINVAL; + } + + if (info->var.xres != var->xres || + info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_debug("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +static int +bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + int dy; + u32 dmaaddr; + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + if (!var || !info) + return -EINVAL; + + if (var->xoffset - info->var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + dy = var->yoffset - info->var.yoffset; + + if (dy) { + pr_debug("%s: Panning screen of %d lines\n", __func__, dy); + + dmaaddr = fbdev->av1->start_addr; + dmaaddr += (info->fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + + fbdev->av1->start_addr = (unsigned long)dmaaddr; + fbdev->av2->start_addr = (unsigned long)dmaaddr + fbdev->line_len; + } + + return 0; + +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ +static int bfin_adv7393_fb_blank(int blank, struct fb_info *info) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + switch (blank) { + + case VESA_NO_BLANKING: + /* Turn on panel */ + adv7393_mode(fbdev->client, BLANK_OFF); + break; + + case VESA_VSYNC_SUSPEND: + case VESA_HSYNC_SUSPEND: + case VESA_POWERDOWN: + /* Turn off panel */ + adv7393_mode(fbdev->client, BLANK_ON); + break; + + default: + return -EINVAL; + break; + } + return 0; +} + +int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + if (nocursor) + return 0; + else + return -EINVAL; /* just to force soft_cursor() call */ +} + +static int bfin_adv7393_fb_setcolreg(u_int regno, u_int red, u_int green, + u_int blue, u_int transp, + struct fb_info *info) +{ + if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES) + return -EINVAL; + + if (info->var.grayscale) + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 value; + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + + value = (red << info->var.red.offset) | + (green << info->var.green.offset)| + (blue << info->var.blue.offset); + value &= 0xFFFF; + + ((u32 *) (info->pseudo_palette))[regno] = value; + } + + return 0; +} + +static int __devexit bfin_adv7393_fb_remove(struct i2c_client *client) +{ + struct adv7393fb_device *fbdev = i2c_get_clientdata(client); + + adv7393_mode(client, POWER_DOWN); + + if (fbdev->fb_mem) + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, fbdev->dma_handle); + free_dma(CH_PPI); + free_irq(IRQ_PPI_ERROR, fbdev); + unregister_framebuffer(&fbdev->info); + remove_proc_entry("driver/adv7393", NULL); + fb_dealloc_cmap(&fbdev->info.cmap); + kfree(fbdev->info.pseudo_palette); + + if (ANOMALY_05000400) + gpio_free(P_IDENT(P_PPI0_FS3)); /* FS3 */ + peripheral_free_list(ppi_pins); + kfree(fbdev); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_adv7393_fb_suspend(struct device *dev) +{ + struct adv7393fb_device *fbdev = dev_get_drvdata(dev); + + if (fbdev->open) { + bfin_disable_dma(); + bfin_disable_ppi(); + dma_desc_list(fbdev, DESTRUCT); + } + adv7393_mode(fbdev->client, POWER_DOWN); + + return 0; +} + +static int bfin_adv7393_fb_resume(struct device *dev) +{ + struct adv7393fb_device *fbdev = dev_get_drvdata(dev); + + adv7393_mode(fbdev->client, POWER_ON); + + if (fbdev->open) { + dma_desc_list(fbdev, BUILD); + bfin_config_ppi(fbdev); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + } + + return 0; +} + +static const struct dev_pm_ops bfin_adv7393_dev_pm_ops = { + .suspend = bfin_adv7393_fb_suspend, + .resume = bfin_adv7393_fb_resume, +}; +#endif + +static const struct i2c_device_id bfin_adv7393_id[] = { + {DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bfin_adv7393_id); + +static struct i2c_driver bfin_adv7393_fb_driver = { + .driver = { + .name = DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &bfin_adv7393_dev_pm_ops, +#endif + }, + .probe = bfin_adv7393_fb_probe, + .remove = __devexit_p(bfin_adv7393_fb_remove), + .id_table = bfin_adv7393_id, +}; + +static int __init bfin_adv7393_fb_driver_init(void) +{ +#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) + request_module("i2c-bfin-twi"); +#else + request_module("i2c-gpio"); +#endif + + return i2c_add_driver(&bfin_adv7393_fb_driver); +} +module_init(bfin_adv7393_fb_driver_init); + +static void __exit bfin_adv7393_fb_driver_cleanup(void) +{ + i2c_del_driver(&bfin_adv7393_fb_driver); +} +module_exit(bfin_adv7393_fb_driver_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Frame buffer driver for ADV7393/2 Video Encoder"); + +module_param(mode, int, 0); +MODULE_PARM_DESC(mode, + "Video Mode (0=NTSC,1=PAL,2=NTSC 640x480,3=PAL 640x480,4=NTSC YCbCr input,5=PAL YCbCr input)"); + +module_param(mem, int, 0); +MODULE_PARM_DESC(mem, + "Size of frame buffer memory 1=Single 2=Double Size (allows y-panning / frame stacking)"); + +module_param(nocursor, int, 0644); +MODULE_PARM_DESC(nocursor, "cursor enable/disable"); diff --git a/drivers/video/bfin_adv7393fb.h b/drivers/video/bfin_adv7393fb.h new file mode 100644 index 00000000000..8c7f9e4fc6e --- /dev/null +++ b/drivers/video/bfin_adv7393fb.h @@ -0,0 +1,321 @@ +/* + * Frame buffer driver for ADV7393/2 video encoder + * + * Copyright 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or late. + */ + +#ifndef __BFIN_ADV7393FB_H__ +#define __BFIN_ADV7393FB_H__ + +#define BFIN_LCD_NBR_PALETTE_ENTRIES 256 + +#ifdef CONFIG_NTSC +# define VMODE 0 +#endif +#ifdef CONFIG_PAL +# define VMODE 1 +#endif +#ifdef CONFIG_NTSC_640x480 +# define VMODE 2 +#endif +#ifdef CONFIG_PAL_640x480 +# define VMODE 3 +#endif +#ifdef CONFIG_NTSC_YCBCR +# define VMODE 4 +#endif +#ifdef CONFIG_PAL_YCBCR +# define VMODE 5 +#endif + +#ifndef VMODE +# define VMODE 1 +#endif + +#ifdef CONFIG_ADV7393_2XMEM +# define VMEM 2 +#else +# define VMEM 1 +#endif + +#if defined(CONFIG_BF537) || defined(CONFIG_BF536) || defined(CONFIG_BF534) +# define DMA_CFG_VAL 0x7935 /* Set Sync Bit */ +# define VB_DUMMY_MEMORY_SOURCE L1_DATA_B_START +#else +# define DMA_CFG_VAL 0x7915 +# define VB_DUMMY_MEMORY_SOURCE BOOT_ROM_START +#endif + +enum { + DESTRUCT, + BUILD, +}; + +enum { + POWER_ON, + POWER_DOWN, + BLANK_ON, + BLANK_OFF, +}; + +#define DRIVER_NAME "bfin-adv7393" + +struct adv7393fb_modes { + const s8 name[25]; /* Full name */ + u16 xres; /* Active Horizonzal Pixels */ + u16 yres; /* Active Vertical Pixels */ + u16 bpp; + u16 vmode; + u16 a_lines; /* Active Lines per Field */ + u16 vb1_lines; /* Vertical Blanking Field 1 Lines */ + u16 vb2_lines; /* Vertical Blanking Field 2 Lines */ + u16 tot_lines; /* Total Lines per Frame */ + u16 boeft_blank; /* Before Odd/Even Field Transition No. of Blank Pixels */ + u16 aoeft_blank; /* After Odd/Even Field Transition No. of Blank Pixels */ + const s8 *adv7393_i2c_initd; + u16 adv7393_i2c_initd_len; +}; + +static const u8 init_NTSC_TESTPATTERN[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x10, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0xCB, /* Step control on, pixel data valid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x84, 0x40, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ +}; + +static const u8 init_NTSC[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ + 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ + 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ + 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ + 0x8F, 0x21, /* NTSC Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x20, + 0x8A, 0x0d, +}; + +static const u8 init_PAL[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ + 0x8C, 0xCB, /* PAL Subcarrier Frequency */ + 0x8D, 0x8A, /* PAL Subcarrier Frequency */ + 0x8E, 0x09, /* PAL Subcarrier Frequency */ + 0x8F, 0x2A, /* PAL Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x20, + 0x8A, 0x0d, +}; + +static const u8 init_NTSC_YCbCr[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ + 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ + 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ + 0x8F, 0x21, /* NTSC Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x08, + 0x8A, 0x0d, +}; + +static const u8 init_PAL_YCbCr[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x8C, 0xCB, /* PAL Subcarrier Frequency */ + 0x8D, 0x8A, /* PAL Subcarrier Frequency */ + 0x8E, 0x09, /* PAL Subcarrier Frequency */ + 0x8F, 0x2A, /* PAL Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x08, + 0x8A, 0x0d, +}; + +static struct adv7393fb_modes known_modes[] = { + /* NTSC 720x480 CRT */ + { + .name = "NTSC 720x480", + .xres = 720, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16, + .aoeft_blank = 122, + .adv7393_i2c_initd = init_NTSC, + .adv7393_i2c_initd_len = sizeof(init_NTSC) + }, + /* PAL 720x480 CRT */ + { + .name = "PAL 720x576", + .xres = 720, + .yres = 576, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288, + .vb1_lines = 24, + .vb2_lines = 25, + .tot_lines = 625, + .boeft_blank = 12, + .aoeft_blank = 132, + .adv7393_i2c_initd = init_PAL, + .adv7393_i2c_initd_len = sizeof(init_PAL) + }, + /* NTSC 640x480 CRT Experimental */ + { + .name = "NTSC 640x480", + .xres = 640, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16 + 40, + .aoeft_blank = 122 + 40, + .adv7393_i2c_initd = init_NTSC, + .adv7393_i2c_initd_len = sizeof(init_NTSC) + }, + /* PAL 640x480 CRT Experimental */ + { + .name = "PAL 640x480", + .xres = 640, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288 - 20, + .vb1_lines = 24 + 20, + .vb2_lines = 25 + 20, + .tot_lines = 625, + .boeft_blank = 12 + 40, + .aoeft_blank = 132 + 40, + .adv7393_i2c_initd = init_PAL, + .adv7393_i2c_initd_len = sizeof(init_PAL) + }, + /* NTSC 720x480 YCbCR */ + { + .name = "NTSC 720x480 YCbCR", + .xres = 720, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16, + .aoeft_blank = 122, + .adv7393_i2c_initd = init_NTSC_YCbCr, + .adv7393_i2c_initd_len = sizeof(init_NTSC_YCbCr) + }, + /* PAL 720x480 CRT */ + { + .name = "PAL 720x576 YCbCR", + .xres = 720, + .yres = 576, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288, + .vb1_lines = 24, + .vb2_lines = 25, + .tot_lines = 625, + .boeft_blank = 12, + .aoeft_blank = 132, + .adv7393_i2c_initd = init_PAL_YCbCr, + .adv7393_i2c_initd_len = sizeof(init_PAL_YCbCr) + } +}; + +struct adv7393fb_regs { + +}; + +struct adv7393fb_device { + struct fb_info info; /* FB driver info record */ + + struct i2c_client *client; + + struct dmasg *descriptor_list_head; + struct dmasg *vb1; + struct dmasg *av1; + struct dmasg *vb2; + struct dmasg *av2; + + dma_addr_t dma_handle; + + struct fb_info bfin_adv7393_fb; + + struct adv7393fb_modes *modes; + + struct adv7393fb_regs *regs; /* Registers memory map */ + size_t regs_len; + size_t fb_len; + size_t line_len; + u16 open; + u16 *fb_mem; /* RGB Buffer */ + +}; + +#define to_adv7393fb_device(_info) \ + (_info ? container_of(_info, struct adv7393fb_device, info) : NULL); + +static int bfin_adv7393_fb_open(struct fb_info *info, int user); +static int bfin_adv7393_fb_release(struct fb_info *info, int user); +static int bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int bfin_adv7393_fb_blank(int blank, struct fb_info *info); + +static void bfin_config_ppi(struct adv7393fb_device *fbdev); +static int bfin_config_dma(struct adv7393fb_device *fbdev); +static void bfin_disable_dma(void); +static void bfin_enable_ppi(void); +static void bfin_disable_ppi(void); + +static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value); +static inline int adv7393_read(struct i2c_client *client, u8 reg); +static int adv7393_write_block(struct i2c_client *client, const u8 *data, + unsigned int len); + +int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor); +static int bfin_adv7393_fb_setcolreg(u_int, u_int, u_int, u_int, + u_int, struct fb_info *info); + +#endif diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c index 6b19136aa18..caaa27d4a46 100644 --- a/drivers/video/carminefb.c +++ b/drivers/video/carminefb.c @@ -654,7 +654,7 @@ static int __devinit carminefb_probe(struct pci_dev *dev, printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " "are required.", carminefb_fix.smem_len, CARMINE_TOTAL_DIPLAY_MEM); - goto err_free_reg_mmio; + goto err_unmap_vregs; } if (!request_mem_region(carminefb_fix.smem_start, @@ -667,8 +667,6 @@ static int __devinit carminefb_probe(struct pci_dev *dev, carminefb_fix.smem_len); if (!hw->screen_mem) { printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); - release_mem_region(carminefb_fix.smem_start, - carminefb_fix.smem_len); goto err_reg_smem; } @@ -710,7 +708,7 @@ err_deinit_hw: err_unmap_screen: iounmap(hw->screen_mem); err_reg_smem: - release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); + release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len); err_unmap_vregs: iounmap(hw->v_regs); err_free_reg_mmio: diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 915448ec75b..c97491b8b39 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -375,7 +375,8 @@ static const char *vgacon_startup(void) u16 saved1, saved2; volatile u16 *p; - if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB) { + if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB || + screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) { no_vga: #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 6b93ef93cb1..804000183c5 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -75,7 +75,7 @@ int fb_deferred_io_fsync(struct file *file, int datasync) return 0; /* Kill off the delayed work */ - cancel_rearming_delayed_work(&info->deferred_work); + cancel_delayed_work_sync(&info->deferred_work); /* Run it immediately */ return schedule_delayed_work(&info->deferred_work, 0); diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 563a98b88e9..4f57485f8c5 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -973,6 +973,90 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK("========================================\n"); } +/** + * fb_edid_add_monspecs() - add monitor video modes from E-EDID data + * @edid: 128 byte array with an E-EDID block + * @spacs: monitor specs to be extended + */ +void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) +{ + unsigned char *block; + struct fb_videomode *m; + int num = 0, i; + u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; + u8 pos = 4, svd_n = 0; + + if (!edid) + return; + + if (!edid_checksum(edid)) + return; + + if (edid[0] != 0x2 || + edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) + return; + + DPRINTK(" Short Video Descriptors\n"); + + while (pos < edid[2]) { + u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; + pr_debug("Data block %u of %u bytes\n", type, len); + if (type == 2) + for (i = pos; i < pos + len; i++) { + u8 idx = edid[pos + i] & 0x7f; + svd[svd_n++] = idx; + pr_debug("N%sative mode #%d\n", + edid[pos + i] & 0x80 ? "" : "on-n", idx); + } + pos += len + 1; + } + + block = edid + edid[2]; + + DPRINTK(" Extended Detailed Timings\n"); + + for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE; + i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) + if (PIXEL_CLOCK) + edt[num++] = block - edid; + + /* Yikes, EDID data is totally useless */ + if (!(num + svd_n)) + return; + + m = kzalloc((specs->modedb_len + num + svd_n) * + sizeof(struct fb_videomode), GFP_KERNEL); + + if (!m) + return; + + memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); + + for (i = specs->modedb_len; i < specs->modedb_len + num; i++) { + get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]); + if (i == specs->modedb_len) + m[i].flag |= FB_MODE_IS_FIRST; + pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); + } + + for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { + int idx = svd[i - specs->modedb_len - num]; + if (!idx || idx > 63) { + pr_warning("Reserved SVD code %d\n", idx); + } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) { + pr_warning("Unimplemented SVD code %d\n", idx); + } else { + memcpy(&m[i], cea_modes + idx, sizeof(m[i])); + pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, + m[i].xres, m[i].yres, m[i].refresh); + } + } + + kfree(specs->modedb); + specs->modedb = m; + specs->modedb_len = specs->modedb_len + num + svd_n; +} + /* * VESA Generalized Timing Formula (GTF) */ @@ -1289,6 +1373,9 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) { specs = NULL; } +void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) +{ +} void fb_destroy_modedb(struct fb_videomode *modedb) { } @@ -1396,6 +1483,7 @@ EXPORT_SYMBOL(fb_firmware_edid); EXPORT_SYMBOL(fb_parse_edid); EXPORT_SYMBOL(fb_edid_to_monspecs); +EXPORT_SYMBOL(fb_edid_add_monspecs); EXPORT_SYMBOL(fb_get_mode); EXPORT_SYMBOL(fb_validate_mode); EXPORT_SYMBOL(fb_destroy_modedb); diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index af8f0f2cc78..4052718eefa 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -454,7 +454,6 @@ static int hgafb_blank(int blank_mode, struct fb_info *info) /* * Accel functions */ -#ifdef CONFIG_FB_HGA_ACCEL static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { u_int rows, y; @@ -466,7 +465,7 @@ static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) dest = rowaddr(info, y) + (rect->dx >> 3); switch (rect->rop) { case ROP_COPY: - //fb_memset(dest, rect->color, (rect->width >> 3)); + memset_io(dest, rect->color, (rect->width >> 3)); break; case ROP_XOR: fb_writeb(~(fb_readb(dest)), dest); @@ -488,7 +487,7 @@ static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) for (rows = area->height; rows--; ) { src = rowaddr(info, y1) + (area->sx >> 3); dest = rowaddr(info, y2) + (area->dx >> 3); - //fb_memmove(dest, src, (area->width >> 3)); + memmove(dest, src, (area->width >> 3)); y1++; y2++; } @@ -499,7 +498,7 @@ static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) for (rows = area->height; rows--;) { src = rowaddr(info, y1) + (area->sx >> 3); dest = rowaddr(info, y2) + (area->dx >> 3); - //fb_memmove(dest, src, (area->width >> 3)); + memmove(dest, src, (area->width >> 3)); y1--; y2--; } @@ -511,20 +510,17 @@ static void hgafb_imageblit(struct fb_info *info, const struct fb_image *image) u8 __iomem *dest; u8 *cdat = (u8 *) image->data; u_int rows, y = image->dy; + u_int x; u8 d; for (rows = image->height; rows--; y++) { - d = *cdat++; - dest = rowaddr(info, y) + (image->dx >> 3); - fb_writeb(d, dest); + for (x = 0; x < image->width; x+= 8) { + d = *cdat++; + dest = rowaddr(info, y) + ((image->dx + x)>> 3); + fb_writeb(d, dest); + } } } -#else /* !CONFIG_FB_HGA_ACCEL */ -#define hgafb_fillrect cfb_fillrect -#define hgafb_copyarea cfb_copyarea -#define hgafb_imageblit cfb_imageblit -#endif /* CONFIG_FB_HGA_ACCEL */ - static struct fb_ops hgafb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c index cd2c728a809..7db17d0d8a8 100644 --- a/drivers/video/i810/i810-i2c.c +++ b/drivers/video/i810/i810-i2c.c @@ -45,8 +45,10 @@ static void i810i2c_setscl(void *data, int state) struct i810fb_par *par = chan->par; u8 __iomem *mmio = par->mmio_start_virtual; - i810_writel(mmio, chan->ddc_base, (state ? SCL_VAL_OUT : 0) | SCL_DIR | - SCL_DIR_MASK | SCL_VAL_MASK); + if (state) + i810_writel(mmio, chan->ddc_base, SCL_DIR_MASK | SCL_VAL_MASK); + else + i810_writel(mmio, chan->ddc_base, SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); i810_readl(mmio, chan->ddc_base); /* flush posted write */ } @@ -56,8 +58,10 @@ static void i810i2c_setsda(void *data, int state) struct i810fb_par *par = chan->par; u8 __iomem *mmio = par->mmio_start_virtual; - i810_writel(mmio, chan->ddc_base, (state ? SDA_VAL_OUT : 0) | SDA_DIR | - SDA_DIR_MASK | SDA_VAL_MASK); + if (state) + i810_writel(mmio, chan->ddc_base, SDA_DIR_MASK | SDA_VAL_MASK); + else + i810_writel(mmio, chan->ddc_base, SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); i810_readl(mmio, chan->ddc_base); /* flush posted write */ } diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 1ab2c258867..69bd4a581d4 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -974,6 +974,6 @@ static void __exit imxfb_cleanup(void) module_init(imxfb_init); module_exit(imxfb_cleanup); -MODULE_DESCRIPTION("Motorola i.MX framebuffer driver"); +MODULE_DESCRIPTION("Freescale i.MX framebuffer driver"); MODULE_AUTHOR("Sascha Hauer, Pengutronix"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 052dd9f0b76..a082debe824 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -1247,46 +1247,46 @@ static struct { struct fb_bitfield red, green, blue, transp; int bits_per_pixel; }; /* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */ -static unsigned int mem; /* "matrox:mem:xxxxxM" */ +static unsigned int mem; /* "matroxfb:mem:xxxxxM" */ static int option_precise_width = 1; /* cannot be changed, option_precise_width==0 must imply noaccel */ -static int inv24; /* "matrox:inv24" */ -static int cross4MB = -1; /* "matrox:cross4MB" */ -static int disabled; /* "matrox:disabled" */ -static int noaccel; /* "matrox:noaccel" */ -static int nopan; /* "matrox:nopan" */ -static int no_pci_retry; /* "matrox:nopciretry" */ -static int novga; /* "matrox:novga" */ -static int nobios; /* "matrox:nobios" */ -static int noinit = 1; /* "matrox:init" */ -static int inverse; /* "matrox:inverse" */ -static int sgram; /* "matrox:sgram" */ +static int inv24; /* "matroxfb:inv24" */ +static int cross4MB = -1; /* "matroxfb:cross4MB" */ +static int disabled; /* "matroxfb:disabled" */ +static int noaccel; /* "matroxfb:noaccel" */ +static int nopan; /* "matroxfb:nopan" */ +static int no_pci_retry; /* "matroxfb:nopciretry" */ +static int novga; /* "matroxfb:novga" */ +static int nobios; /* "matroxfb:nobios" */ +static int noinit = 1; /* "matroxfb:init" */ +static int inverse; /* "matroxfb:inverse" */ +static int sgram; /* "matroxfb:sgram" */ #ifdef CONFIG_MTRR -static int mtrr = 1; /* "matrox:nomtrr" */ +static int mtrr = 1; /* "matroxfb:nomtrr" */ #endif -static int grayscale; /* "matrox:grayscale" */ -static int dev = -1; /* "matrox:dev:xxxxx" */ -static unsigned int vesa = ~0; /* "matrox:vesa:xxxxx" */ -static int depth = -1; /* "matrox:depth:xxxxx" */ -static unsigned int xres; /* "matrox:xres:xxxxx" */ -static unsigned int yres; /* "matrox:yres:xxxxx" */ -static unsigned int upper = ~0; /* "matrox:upper:xxxxx" */ -static unsigned int lower = ~0; /* "matrox:lower:xxxxx" */ -static unsigned int vslen; /* "matrox:vslen:xxxxx" */ -static unsigned int left = ~0; /* "matrox:left:xxxxx" */ -static unsigned int right = ~0; /* "matrox:right:xxxxx" */ -static unsigned int hslen; /* "matrox:hslen:xxxxx" */ -static unsigned int pixclock; /* "matrox:pixclock:xxxxx" */ -static int sync = -1; /* "matrox:sync:xxxxx" */ -static unsigned int fv; /* "matrox:fv:xxxxx" */ -static unsigned int fh; /* "matrox:fh:xxxxxk" */ -static unsigned int maxclk; /* "matrox:maxclk:xxxxM" */ -static int dfp; /* "matrox:dfp */ -static int dfp_type = -1; /* "matrox:dfp:xxx */ -static int memtype = -1; /* "matrox:memtype:xxx" */ -static char outputs[8]; /* "matrox:outputs:xxx" */ +static int grayscale; /* "matroxfb:grayscale" */ +static int dev = -1; /* "matroxfb:dev:xxxxx" */ +static unsigned int vesa = ~0; /* "matroxfb:vesa:xxxxx" */ +static int depth = -1; /* "matroxfb:depth:xxxxx" */ +static unsigned int xres; /* "matroxfb:xres:xxxxx" */ +static unsigned int yres; /* "matroxfb:yres:xxxxx" */ +static unsigned int upper = ~0; /* "matroxfb:upper:xxxxx" */ +static unsigned int lower = ~0; /* "matroxfb:lower:xxxxx" */ +static unsigned int vslen; /* "matroxfb:vslen:xxxxx" */ +static unsigned int left = ~0; /* "matroxfb:left:xxxxx" */ +static unsigned int right = ~0; /* "matroxfb:right:xxxxx" */ +static unsigned int hslen; /* "matroxfb:hslen:xxxxx" */ +static unsigned int pixclock; /* "matroxfb:pixclock:xxxxx" */ +static int sync = -1; /* "matroxfb:sync:xxxxx" */ +static unsigned int fv; /* "matroxfb:fv:xxxxx" */ +static unsigned int fh; /* "matroxfb:fh:xxxxxk" */ +static unsigned int maxclk; /* "matroxfb:maxclk:xxxxM" */ +static int dfp; /* "matroxfb:dfp */ +static int dfp_type = -1; /* "matroxfb:dfp:xxx */ +static int memtype = -1; /* "matroxfb:memtype:xxx" */ +static char outputs[8]; /* "matroxfb:outputs:xxx" */ #ifndef MODULE -static char videomode[64]; /* "matrox:mode:xxxxx" or "matrox:xxxxx" */ +static char videomode[64]; /* "matroxfb:mode:xxxxx" or "matroxfb:xxxxx" */ #endif static int matroxfb_getmemory(struct matrox_fb_info *minfo, diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index de450c1fb86..48c3ea8652b 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -32,252 +32,323 @@ const char *fb_mode_option; EXPORT_SYMBOL_GPL(fb_mode_option); - /* - * Standard video mode definitions (taken from XFree86) - */ +/* + * Standard video mode definitions (taken from XFree86) + */ static const struct fb_videomode modedb[] = { - { + /* 640x400 @ 70 Hz, 31.5 kHz hsync */ - NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, 0, + FB_VMODE_NONINTERLACED }, + /* 640x480 @ 60 Hz, 31.5 kHz hsync */ - NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, + FB_VMODE_NONINTERLACED }, + /* 800x600 @ 56 Hz, 35.15 kHz hsync */ - NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, 0, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */ - NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8, - 0, FB_VMODE_INTERLACED - }, { + { NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8, 0, + FB_VMODE_INTERLACED }, + /* 640x400 @ 85 Hz, 37.86 kHz hsync */ - NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3, - FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 640x480 @ 72 Hz, 36.5 kHz hsync */ - NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 640x480 @ 75 Hz, 37.50 kHz hsync */ - NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 800x600 @ 60 Hz, 37.8 kHz hsync */ - NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 640x480 @ 85 Hz, 43.27 kHz hsync */ - NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */ - NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10, - 0, FB_VMODE_INTERLACED - }, { + { NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10, 0, + FB_VMODE_INTERLACED }, /* 800x600 @ 72 Hz, 48.0 kHz hsync */ - NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 60 Hz, 48.4 kHz hsync */ - NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6, 0, + FB_VMODE_NONINTERLACED }, + /* 640x480 @ 100 Hz, 53.01 kHz hsync */ - NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6, 0, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 60 Hz, 53.5 kHz hsync */ - NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8, 0, + FB_VMODE_NONINTERLACED }, + /* 800x600 @ 85 Hz, 55.84 kHz hsync */ - NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5, 0, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 70 Hz, 56.5 kHz hsync */ - NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */ - NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12, - 0, FB_VMODE_INTERLACED - }, { + { NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12, 0, + FB_VMODE_INTERLACED }, + /* 800x600 @ 100 Hz, 64.02 kHz hsync */ - NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6, 0, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 76 Hz, 62.5 kHz hsync */ - NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 70 Hz, 62.4 kHz hsync */ - NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */ - NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1400x1050 @ 60Hz, 63.9 kHz hsync */ - NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/ - NULL, 75, 1400, 1050, 7190, 120, 56, 23, 10, 112, 13, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 75, 1400, 1050, 7190, 120, 56, 23, 10, 112, 13, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/ - NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 85 Hz, 70.24 kHz hsync */ - NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6, 0, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 78 Hz, 70.8 kHz hsync */ - NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */ - NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8, 0, + FB_VMODE_NONINTERLACED }, + /* 1600x1200 @ 60Hz, 75.00 kHz hsync */ - NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 84 Hz, 76.0 kHz hsync */ - NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */ - NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1024x768 @ 100Hz, 80.21 kHz hsync */ - NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */ - NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */ - NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 1152x864 @ 100 Hz, 89.62 kHz hsync */ - NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */ - NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */ - NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */ - NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */ - NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */ - NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15, 0, + FB_VMODE_NONINTERLACED }, + /* 1800x1440 @ 64Hz, 96.15 kHz hsync */ - NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1800x1440 @ 70Hz, 104.52 kHz hsync */ - NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 512x384 @ 78 Hz, 31.50 kHz hsync */ - NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 512x384 @ 85 Hz, 34.38 kHz hsync */ - NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3, 0, + FB_VMODE_NONINTERLACED }, + /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */ - NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0, + FB_VMODE_DOUBLE }, + /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */ - NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1, 0, + FB_VMODE_DOUBLE }, + /* 320x240 @ 72 Hz, 36.5 kHz hsync */ - NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0, + FB_VMODE_DOUBLE }, + /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */ - NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1, 0, + FB_VMODE_DOUBLE }, + /* 400x300 @ 60 Hz, 37.8 kHz hsync */ - NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2, 0, + FB_VMODE_DOUBLE }, + /* 400x300 @ 72 Hz, 48.0 kHz hsync */ - NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3, 0, + FB_VMODE_DOUBLE }, + /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */ - NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1, 0, + FB_VMODE_DOUBLE }, + /* 480x300 @ 60 Hz, 37.8 kHz hsync */ - NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2, 0, + FB_VMODE_DOUBLE }, + /* 480x300 @ 63 Hz, 39.6 kHz hsync */ - NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2, 0, + FB_VMODE_DOUBLE }, + /* 480x300 @ 72 Hz, 48.0 kHz hsync */ - NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, - 0, FB_VMODE_DOUBLE - }, { + { NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, 0, + FB_VMODE_DOUBLE }, + /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */ - NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1152x768, 60 Hz, PowerBook G4 Titanium I and II */ - NULL, 60, 1152, 768, 14047, 158, 26, 29, 3, 136, 6, - FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1152, 768, 14047, 158, 26, 29, 3, 136, 6, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1366x768, 60 Hz, 47.403 kHz hsync, WXGA 16:9 aspect ratio */ - NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5, - 0, FB_VMODE_NONINTERLACED - }, { + { NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5, 0, + FB_VMODE_NONINTERLACED }, + /* 1280x800, 60 Hz, 47.403 kHz hsync, WXGA 16:10 aspect ratio */ - NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3, - 0, FB_VMODE_NONINTERLACED - }, { - /* 720x576i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */ - NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5, - 0, FB_VMODE_INTERLACED - }, { - /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */ - NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, - 0, FB_VMODE_INTERLACED - }, + { NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3, 0, + FB_VMODE_NONINTERLACED }, + + /* 720x576i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */ + { NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5, 0, + FB_VMODE_INTERLACED }, + + /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */ + { NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, 0, + FB_VMODE_INTERLACED }, + + /* 864x480 @ 60 Hz, 35.15 kHz hsync */ + { NULL, 60, 864, 480, 27777, 1, 1, 1, 1, 0, 0, + 0, FB_VMODE_NONINTERLACED }, }; #ifdef CONFIG_FB_MODE_HELPERS +const struct fb_videomode cea_modes[64] = { + /* #1: 640x480p@59.94/60Hz */ + [1] = { + NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #3: 720x480p@59.94/60Hz */ + [3] = { + NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #5: 1920x1080i@59.94/60Hz */ + [5] = { + NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, 0, + }, + /* #7: 720(1440)x480iH@59.94/60Hz */ + [7] = { + NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, + FB_VMODE_INTERLACED, 0, + }, + /* #9: 720(1440)x240pH@59.94/60Hz */ + [9] = { + NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #18: 720x576pH@50Hz */ + [18] = { + NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #19: 1280x720p@50Hz */ + [19] = { + NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, 0, + }, + /* #20: 1920x1080i@50Hz */ + [20] = { + NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, 0, + }, + /* #32: 1920x1080p@23.98/24Hz */ + [32] = { + NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, 0, + }, + /* #35: (2880)x480p4x@59.94/60Hz */ + [35] = { + NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0, + FB_VMODE_NONINTERLACED, 0, + }, +}; + const struct fb_videomode vesa_modes[] = { /* 0 640x350-85 VESA */ { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, @@ -289,10 +360,10 @@ const struct fb_videomode vesa_modes[] = { { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3, FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, /* 3 640x480-60 VESA */ - { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, + { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, /* 4 640x480-72 VESA */ - { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2, + { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2, 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, /* 5 640x480-75 VESA */ { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3, @@ -375,7 +446,7 @@ const struct fb_videomode vesa_modes[] = { FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, /* 26 1600x1200-75 VESA */ - { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, + { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, /* 27 1600x1200-85 VESA */ diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index ca0f6be9d12..cb013919e9c 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1474,8 +1474,7 @@ static int mx3fb_probe(struct platform_device *pdev) goto eremap; } - pr_debug("Remapped %x to %x at %p\n", sdc_reg->start, sdc_reg->end, - mx3fb->reg_base); + pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); /* IDMAC interface */ dmaengine_get(); diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c index 81687ed26ba..62498bd662f 100644 --- a/drivers/video/nuc900fb.c +++ b/drivers/video/nuc900fb.c @@ -15,6 +15,7 @@ */ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> @@ -597,9 +598,9 @@ static int __devinit nuc900fb_probe(struct platform_device *pdev) } fbi->clk = clk_get(&pdev->dev, NULL); - if (!fbi->clk || IS_ERR(fbi->clk)) { + if (IS_ERR(fbi->clk)) { printk(KERN_ERR "nuc900-lcd:failed to get lcd clock source\n"); - ret = -ENOENT; + ret = PTR_ERR(fbi->clk); goto release_irq; } diff --git a/drivers/video/nvidia/nv_backlight.c b/drivers/video/nvidia/nv_backlight.c index 2fb552a6f32..6aac6d1b937 100644 --- a/drivers/video/nvidia/nv_backlight.c +++ b/drivers/video/nvidia/nv_backlight.c @@ -87,7 +87,7 @@ static int nvidia_bl_get_brightness(struct backlight_device *bd) return bd->props.brightness; } -static struct backlight_ops nvidia_bl_ops = { +static const struct backlight_ops nvidia_bl_ops = { .get_brightness = nvidia_bl_get_brightness, .update_status = nvidia_bl_update_status, }; diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c index 64dcc7439c9..90e3bdd1b7a 100644 --- a/drivers/video/omap/lcd_mipid.c +++ b/drivers/video/omap/lcd_mipid.c @@ -396,7 +396,7 @@ static void mipid_esd_start_check(struct mipid_device *md) static void mipid_esd_stop_check(struct mipid_device *md) { if (md->esd_check != NULL) - cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work); + cancel_delayed_work_sync(&md->esd_work); } static void mipid_esd_work(struct work_struct *work) diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 12327bbfdbb..940cab394c2 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -1,11 +1,13 @@ menu "OMAP2/3 Display Device Drivers" depends on OMAP2_DSS -config PANEL_GENERIC - tristate "Generic Panel" +config PANEL_GENERIC_DPI + tristate "Generic DPI Panel" help - Generic panel driver. - Used for DVI output for Beagle and OMAP3 SDP. + Generic DPI panel driver. + Supports DVI output for Beagle and OMAP3 SDP. + Supports LCD Panel used in TI SDP3430 and EVM boards, + OMAP3517 EVM boards and CM-T35. config PANEL_SHARP_LS037V7DW01 tristate "Sharp LS037V7DW01 LCD Panel" @@ -14,11 +16,12 @@ config PANEL_SHARP_LS037V7DW01 help LCD Panel used in TI's SDP3430 and EVM boards -config PANEL_SHARP_LQ043T1DG01 - tristate "Sharp LQ043T1DG01 LCD Panel" - depends on OMAP2_DSS - help - LCD Panel used in TI's OMAP3517 EVM boards +config PANEL_NEC_NL8048HL11_01B + tristate "NEC NL8048HL11-01B Panel" + depends on OMAP2_DSS + help + This NEC NL8048HL11-01B panel is TFT LCD + used in the Zoom2/3/3630 sdp boards. config PANEL_TAAL tristate "Taal DSI Panel" @@ -26,12 +29,6 @@ config PANEL_TAAL help Taal DSI command mode panel from TPO. -config PANEL_TOPPOLY_TDO35S - tristate "Toppoly TDO35S LCD Panel support" - depends on OMAP2_DSS - help - LCD Panel used in CM-T35 - config PANEL_TPO_TD043MTEA1 tristate "TPO TD043MTEA1 LCD Panel" depends on OMAP2_DSS && SPI diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index aa386095d7c..861f0255ec6 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -1,8 +1,7 @@ -obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o +obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o -obj-$(CONFIG_PANEL_SHARP_LQ043T1DG01) += panel-sharp-lq043t1dg01.o +obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o obj-$(CONFIG_PANEL_TAAL) += panel-taal.o -obj-$(CONFIG_PANEL_TOPPOLY_TDO35S) += panel-toppoly-tdo35s.o obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c new file mode 100644 index 00000000000..07eb30ee59c --- /dev/null +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -0,0 +1,365 @@ +/* + * Generic DPI Panels support + * + * Copyright (C) 2010 Canonical Ltd. + * Author: Bryan Wu <bryan.wu@canonical.com> + * + * LCD panel driver for Sharp LQ043T1DG01 + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Vaibhav Hiremath <hvaibhav@ti.com> + * + * LCD panel driver for Toppoly TDO35S + * + * Copyright (C) 2009 CompuLab, Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <plat/panel-generic-dpi.h> + +struct panel_config { + struct omap_video_timings timings; + + int acbi; /* ac-bias pin transitions per interrupt */ + /* Unit: line clocks */ + int acb; /* ac-bias pin frequency */ + + enum omap_panel_config config; + + int power_on_delay; + int power_off_delay; + + /* + * Used to match device to panel configuration + * when use generic panel driver + */ + const char *name; +}; + +/* Panel configurations */ +static struct panel_config generic_dpi_panels[] = { + /* Generic Panel */ + { + { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 23500, + + .hfp = 48, + .hsw = 32, + .hbp = 80, + + .vfp = 3, + .vsw = 4, + .vbp = 7, + }, + .acbi = 0x0, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT, + .power_on_delay = 0, + .power_off_delay = 0, + .name = "generic", + }, + + /* Sharp LQ043T1DG01 */ + { + { + .x_res = 480, + .y_res = 272, + + .pixel_clock = 9000, + + .hsw = 42, + .hfp = 3, + .hbp = 2, + + .vsw = 11, + .vfp = 3, + .vbp = 2, + }, + .acbi = 0x0, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .power_on_delay = 50, + .power_off_delay = 100, + .name = "sharp_lq", + }, + + /* Sharp LS037V7DW01 */ + { + { + .x_res = 480, + .y_res = 640, + + .pixel_clock = 19200, + + .hsw = 2, + .hfp = 1, + .hbp = 28, + + .vsw = 1, + .vfp = 1, + .vbp = 1, + }, + .acbi = 0x0, + .acb = 0x28, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS, + .power_on_delay = 50, + .power_off_delay = 100, + .name = "sharp_ls", + }, + + /* Toppoly TDO35S */ + { + { + .x_res = 480, + .y_res = 640, + + .pixel_clock = 26000, + + .hfp = 104, + .hsw = 8, + .hbp = 8, + + .vfp = 4, + .vsw = 2, + .vbp = 2, + }, + .acbi = 0x0, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC | + OMAP_DSS_LCD_ONOFF, + .power_on_delay = 0, + .power_off_delay = 0, + .name = "toppoly_tdo35s", + }, +}; + +struct panel_drv_data { + + struct omap_dss_device *dssdev; + + struct panel_config *panel_config; +}; + +static inline struct panel_generic_dpi_data +*get_panel_data(const struct omap_dss_device *dssdev) +{ + return (struct panel_generic_dpi_data *) dssdev->data; +} + +static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev) +{ + int r; + struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); + struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_config *panel_config = drv_data->panel_config; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + r = omapdss_dpi_display_enable(dssdev); + if (r) + goto err0; + + /* wait couple of vsyncs until enabling the LCD */ + if (panel_config->power_on_delay) + msleep(panel_config->power_on_delay); + + if (panel_data->platform_enable) { + r = panel_data->platform_enable(dssdev); + if (r) + goto err1; + } + + return 0; +err1: + omapdss_dpi_display_disable(dssdev); +err0: + return r; +} + +static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev) +{ + struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); + struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_config *panel_config = drv_data->panel_config; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + + if (panel_data->platform_disable) + panel_data->platform_disable(dssdev); + + /* wait couple of vsyncs after disabling the LCD */ + if (panel_config->power_off_delay) + msleep(panel_config->power_off_delay); + + omapdss_dpi_display_disable(dssdev); +} + +static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) +{ + struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); + struct panel_config *panel_config = NULL; + struct panel_drv_data *drv_data = NULL; + int i; + + dev_dbg(&dssdev->dev, "probe\n"); + + if (!panel_data || !panel_data->name) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(generic_dpi_panels); i++) { + if (strcmp(panel_data->name, generic_dpi_panels[i].name) == 0) { + panel_config = &generic_dpi_panels[i]; + break; + } + } + + if (!panel_config) + return -EINVAL; + + dssdev->panel.config = panel_config->config; + dssdev->panel.timings = panel_config->timings; + dssdev->panel.acb = panel_config->acb; + dssdev->panel.acbi = panel_config->acbi; + + drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + drv_data->dssdev = dssdev; + drv_data->panel_config = panel_config; + + dev_set_drvdata(&dssdev->dev, drv_data); + + return 0; +} + +static void generic_dpi_panel_remove(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + + dev_dbg(&dssdev->dev, "remove\n"); + + kfree(drv_data); + + dev_set_drvdata(&dssdev->dev, NULL); +} + +static int generic_dpi_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + r = generic_dpi_panel_power_on(dssdev); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void generic_dpi_panel_disable(struct omap_dss_device *dssdev) +{ + generic_dpi_panel_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static int generic_dpi_panel_suspend(struct omap_dss_device *dssdev) +{ + generic_dpi_panel_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int generic_dpi_panel_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + r = generic_dpi_panel_power_on(dssdev); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + dpi_set_timings(dssdev, timings); +} + +static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return dpi_check_timings(dssdev, timings); +} + +static struct omap_dss_driver dpi_driver = { + .probe = generic_dpi_panel_probe, + .remove = generic_dpi_panel_remove, + + .enable = generic_dpi_panel_enable, + .disable = generic_dpi_panel_disable, + .suspend = generic_dpi_panel_suspend, + .resume = generic_dpi_panel_resume, + + .set_timings = generic_dpi_panel_set_timings, + .get_timings = generic_dpi_panel_get_timings, + .check_timings = generic_dpi_panel_check_timings, + + .driver = { + .name = "generic_dpi_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init generic_dpi_panel_drv_init(void) +{ + return omap_dss_register_driver(&dpi_driver); +} + +static void __exit generic_dpi_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&dpi_driver); +} + +module_init(generic_dpi_panel_drv_init); +module_exit(generic_dpi_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c deleted file mode 100644 index 395a68de399..00000000000 --- a/drivers/video/omap2/displays/panel-generic.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Generic panel support - * - * Copyright (C) 2008 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/module.h> -#include <linux/delay.h> - -#include <plat/display.h> - -static struct omap_video_timings generic_panel_timings = { - /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ - .x_res = 640, - .y_res = 480, - .pixel_clock = 23500, - .hfp = 48, - .hsw = 32, - .hbp = 80, - .vfp = 3, - .vsw = 4, - .vbp = 7, -}; - -static int generic_panel_power_on(struct omap_dss_device *dssdev) -{ - int r; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - return 0; - - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - - return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; -} - -static void generic_panel_power_off(struct omap_dss_device *dssdev) -{ - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return; - - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - - omapdss_dpi_display_disable(dssdev); -} - -static int generic_panel_probe(struct omap_dss_device *dssdev) -{ - dssdev->panel.config = OMAP_DSS_LCD_TFT; - dssdev->panel.timings = generic_panel_timings; - - return 0; -} - -static void generic_panel_remove(struct omap_dss_device *dssdev) -{ -} - -static int generic_panel_enable(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = generic_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void generic_panel_disable(struct omap_dss_device *dssdev) -{ - generic_panel_power_off(dssdev); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static int generic_panel_suspend(struct omap_dss_device *dssdev) -{ - generic_panel_power_off(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; - return 0; -} - -static int generic_panel_resume(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = generic_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void generic_panel_set_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - dpi_set_timings(dssdev, timings); -} - -static void generic_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - -static int generic_panel_check_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - return dpi_check_timings(dssdev, timings); -} - -static struct omap_dss_driver generic_driver = { - .probe = generic_panel_probe, - .remove = generic_panel_remove, - - .enable = generic_panel_enable, - .disable = generic_panel_disable, - .suspend = generic_panel_suspend, - .resume = generic_panel_resume, - - .set_timings = generic_panel_set_timings, - .get_timings = generic_panel_get_timings, - .check_timings = generic_panel_check_timings, - - .driver = { - .name = "generic_panel", - .owner = THIS_MODULE, - }, -}; - -static int __init generic_panel_drv_init(void) -{ - return omap_dss_register_driver(&generic_driver); -} - -static void __exit generic_panel_drv_exit(void) -{ - omap_dss_unregister_driver(&generic_driver); -} - -module_init(generic_panel_drv_init); -module_exit(generic_panel_drv_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c new file mode 100644 index 00000000000..925e0fadff5 --- /dev/null +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -0,0 +1,325 @@ +/* + * Support for NEC-nl8048hl11-01b panel driver + * + * Copyright (C) 2010 Texas Instruments Inc. + * Author: Erik Gilling <konkers@android.com> + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <plat/display.h> + +#define LCD_XRES 800 +#define LCD_YRES 480 +/* + * NEC PIX Clock Ratings + * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz + */ +#define LCD_PIXEL_CLOCK 23800 + +struct nec_8048_data { + struct backlight_device *bl; +}; + +static const struct { + unsigned char addr; + unsigned char dat; +} nec_8048_init_seq[] = { + { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 }, + { 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 }, + { 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 }, + { 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F }, + { 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F }, + { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F }, + { 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F }, + { 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 }, + { 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 }, + { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C }, + { 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 }, + { 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 }, + { 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 }, + { 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 }, + { 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC }, + { 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, + { 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 }, + { 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 }, +}; + +/* + * NEC NL8048HL11-01B Manual + * defines HFB, HSW, HBP, VFP, VSW, VBP as shown below + */ + +static struct omap_video_timings nec_8048_panel_timings = { + /* 800 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = LCD_XRES, + .y_res = LCD_YRES, + .pixel_clock = LCD_PIXEL_CLOCK, + .hfp = 6, + .hsw = 1, + .hbp = 4, + .vfp = 3, + .vsw = 1, + .vbp = 4, +}; + +static int nec_8048_bl_update_status(struct backlight_device *bl) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bl->dev); + int level; + + if (!dssdev->set_backlight) + return -EINVAL; + + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + level = bl->props.brightness; + else + level = 0; + + return dssdev->set_backlight(dssdev, level); +} + +static int nec_8048_bl_get_brightness(struct backlight_device *bl) +{ + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + return bl->props.brightness; + + return 0; +} + +static const struct backlight_ops nec_8048_bl_ops = { + .get_brightness = nec_8048_bl_get_brightness, + .update_status = nec_8048_bl_update_status, +}; + +static int nec_8048_panel_probe(struct omap_dss_device *dssdev) +{ + struct backlight_device *bl; + struct nec_8048_data *necd; + struct backlight_properties props; + int r; + + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF | + OMAP_DSS_LCD_ONOFF; + dssdev->panel.timings = nec_8048_panel_timings; + + necd = kzalloc(sizeof(*necd), GFP_KERNEL); + if (!necd) + return -ENOMEM; + + dev_set_drvdata(&dssdev->dev, necd); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 255; + + bl = backlight_device_register("nec-8048", &dssdev->dev, dssdev, + &nec_8048_bl_ops, &props); + if (IS_ERR(bl)) { + r = PTR_ERR(bl); + kfree(necd); + return r; + } + necd->bl = bl; + + bl->props.fb_blank = FB_BLANK_UNBLANK; + bl->props.power = FB_BLANK_UNBLANK; + bl->props.max_brightness = dssdev->max_backlight_level; + bl->props.brightness = dssdev->max_backlight_level; + + r = nec_8048_bl_update_status(bl); + if (r < 0) + dev_err(&dssdev->dev, "failed to set lcd brightness\n"); + + return 0; +} + +static void nec_8048_panel_remove(struct omap_dss_device *dssdev) +{ + struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bl = necd->bl; + + bl->props.power = FB_BLANK_POWERDOWN; + nec_8048_bl_update_status(bl); + backlight_device_unregister(bl); + + kfree(necd); +} + +static int nec_8048_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bl = necd->bl; + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + + r = nec_8048_bl_update_status(bl); + if (r < 0) + dev_err(&dssdev->dev, "failed to set lcd brightness\n"); + + r = omapdss_dpi_display_enable(dssdev); + + return r; +} + +static void nec_8048_panel_disable(struct omap_dss_device *dssdev) +{ + struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bl = necd->bl; + + omapdss_dpi_display_disable(dssdev); + + bl->props.brightness = 0; + nec_8048_bl_update_status(bl); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); +} + +static int nec_8048_panel_suspend(struct omap_dss_device *dssdev) +{ + nec_8048_panel_disable(dssdev); + return 0; +} + +static int nec_8048_panel_resume(struct omap_dss_device *dssdev) +{ + return nec_8048_panel_enable(dssdev); +} + +static int nec_8048_recommended_bpp(struct omap_dss_device *dssdev) +{ + return 16; +} + +static struct omap_dss_driver nec_8048_driver = { + .probe = nec_8048_panel_probe, + .remove = nec_8048_panel_remove, + .enable = nec_8048_panel_enable, + .disable = nec_8048_panel_disable, + .suspend = nec_8048_panel_suspend, + .resume = nec_8048_panel_resume, + .get_recommended_bpp = nec_8048_recommended_bpp, + + .driver = { + .name = "NEC_8048_panel", + .owner = THIS_MODULE, + }, +}; + +static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr, + unsigned char reg_data) +{ + int ret = 0; + unsigned int cmd = 0, data = 0; + + cmd = 0x0000 | reg_addr; /* register address write */ + data = 0x0100 | reg_data ; /* register data write */ + data = (cmd << 16) | data; + + ret = spi_write(spi, (unsigned char *)&data, 4); + if (ret) + pr_err("error in spi_write %x\n", data); + + return ret; +} + +static int init_nec_8048_wvga_lcd(struct spi_device *spi) +{ + unsigned int i; + /* Initialization Sequence */ + /* nec_8048_spi_send(spi, REG, VAL) */ + for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++) + nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, + nec_8048_init_seq[i].dat); + udelay(20); + nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, + nec_8048_init_seq[i].dat); + return 0; +} + +static int nec_8048_spi_probe(struct spi_device *spi) +{ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 32; + spi_setup(spi); + + init_nec_8048_wvga_lcd(spi); + + return omap_dss_register_driver(&nec_8048_driver); +} + +static int nec_8048_spi_remove(struct spi_device *spi) +{ + omap_dss_unregister_driver(&nec_8048_driver); + + return 0; +} + +static int nec_8048_spi_suspend(struct spi_device *spi, pm_message_t mesg) +{ + nec_8048_spi_send(spi, 2, 0x01); + mdelay(40); + + return 0; +} + +static int nec_8048_spi_resume(struct spi_device *spi) +{ + /* reinitialize the panel */ + spi_setup(spi); + nec_8048_spi_send(spi, 2, 0x00); + init_nec_8048_wvga_lcd(spi); + + return 0; +} + +static struct spi_driver nec_8048_spi_driver = { + .probe = nec_8048_spi_probe, + .remove = __devexit_p(nec_8048_spi_remove), + .suspend = nec_8048_spi_suspend, + .resume = nec_8048_spi_resume, + .driver = { + .name = "nec_8048_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, +}; + +static int __init nec_8048_lcd_init(void) +{ + return spi_register_driver(&nec_8048_spi_driver); +} + +static void __exit nec_8048_lcd_exit(void) +{ + return spi_unregister_driver(&nec_8048_spi_driver); +} + +module_init(nec_8048_lcd_init); +module_exit(nec_8048_lcd_exit); +MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); +MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c deleted file mode 100644 index 0c6896cea2d..00000000000 --- a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * LCD panel driver for Sharp LQ043T1DG01 - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Vaibhav Hiremath <hvaibhav@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/err.h> - -#include <plat/display.h> - -static struct omap_video_timings sharp_lq_timings = { - .x_res = 480, - .y_res = 272, - - .pixel_clock = 9000, - - .hsw = 42, - .hfp = 3, - .hbp = 2, - - .vsw = 11, - .vfp = 3, - .vbp = 2, -}; - -static int sharp_lq_panel_power_on(struct omap_dss_device *dssdev) -{ - int r; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - return 0; - - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - /* wait couple of vsyncs until enabling the LCD */ - msleep(50); - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - - return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; -} - -static void sharp_lq_panel_power_off(struct omap_dss_device *dssdev) -{ - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return; - - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - - /* wait at least 5 vsyncs after disabling the LCD */ - msleep(100); - - omapdss_dpi_display_disable(dssdev); -} - -static int sharp_lq_panel_probe(struct omap_dss_device *dssdev) -{ - - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO; - dssdev->panel.acb = 0x0; - dssdev->panel.timings = sharp_lq_timings; - - return 0; -} - -static void sharp_lq_panel_remove(struct omap_dss_device *dssdev) -{ -} - -static int sharp_lq_panel_enable(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = sharp_lq_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void sharp_lq_panel_disable(struct omap_dss_device *dssdev) -{ - sharp_lq_panel_power_off(dssdev); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static int sharp_lq_panel_suspend(struct omap_dss_device *dssdev) -{ - sharp_lq_panel_power_off(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; - return 0; -} - -static int sharp_lq_panel_resume(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = sharp_lq_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static struct omap_dss_driver sharp_lq_driver = { - .probe = sharp_lq_panel_probe, - .remove = sharp_lq_panel_remove, - - .enable = sharp_lq_panel_enable, - .disable = sharp_lq_panel_disable, - .suspend = sharp_lq_panel_suspend, - .resume = sharp_lq_panel_resume, - - .driver = { - .name = "sharp_lq_panel", - .owner = THIS_MODULE, - }, -}; - -static int __init sharp_lq_panel_drv_init(void) -{ - return omap_dss_register_driver(&sharp_lq_driver); -} - -static void __exit sharp_lq_panel_drv_exit(void) -{ - omap_dss_unregister_driver(&sharp_lq_driver); -} - -module_init(sharp_lq_panel_drv_init); -module_exit(sharp_lq_panel_drv_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index e1c765d1141..61026f96ad2 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -465,7 +465,7 @@ static int taal_bl_get_intensity(struct backlight_device *dev) return 0; } -static struct backlight_ops taal_bl_ops = { +static const struct backlight_ops taal_bl_ops = { .get_brightness = taal_bl_get_intensity, .update_status = taal_bl_update_status, }; diff --git a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c deleted file mode 100644 index 526e906c8a6..00000000000 --- a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * LCD panel driver for Toppoly TDO35S - * - * Copyright (C) 2009 CompuLab, Ltd. - * Author: Mike Rapoport <mike@compulab.co.il> - * - * Based on generic panel support - * Copyright (C) 2008 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/module.h> -#include <linux/delay.h> - -#include <plat/display.h> - -static struct omap_video_timings toppoly_tdo_panel_timings = { - /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ - .x_res = 480, - .y_res = 640, - - .pixel_clock = 26000, - - .hfp = 104, - .hsw = 8, - .hbp = 8, - - .vfp = 4, - .vsw = 2, - .vbp = 2, -}; - -static int toppoly_tdo_panel_power_on(struct omap_dss_device *dssdev) -{ - int r; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - return 0; - - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - - return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; -} - -static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev) -{ - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return; - - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - - omapdss_dpi_display_disable(dssdev); -} - -static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev) -{ - dssdev->panel.config = OMAP_DSS_LCD_TFT | - OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC | - OMAP_DSS_LCD_ONOFF; - - dssdev->panel.timings = toppoly_tdo_panel_timings; - - return 0; -} - -static void toppoly_tdo_panel_remove(struct omap_dss_device *dssdev) -{ -} - -static int toppoly_tdo_panel_enable(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = toppoly_tdo_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void toppoly_tdo_panel_disable(struct omap_dss_device *dssdev) -{ - toppoly_tdo_panel_power_off(dssdev); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static int toppoly_tdo_panel_suspend(struct omap_dss_device *dssdev) -{ - toppoly_tdo_panel_power_off(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; - return 0; -} - -static int toppoly_tdo_panel_resume(struct omap_dss_device *dssdev) -{ - int r = 0; - - r = toppoly_tdo_panel_power_on(dssdev); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static struct omap_dss_driver generic_driver = { - .probe = toppoly_tdo_panel_probe, - .remove = toppoly_tdo_panel_remove, - - .enable = toppoly_tdo_panel_enable, - .disable = toppoly_tdo_panel_disable, - .suspend = toppoly_tdo_panel_suspend, - .resume = toppoly_tdo_panel_resume, - - .driver = { - .name = "toppoly_tdo35s_panel", - .owner = THIS_MODULE, - }, -}; - -static int __init toppoly_tdo_panel_drv_init(void) -{ - return omap_dss_register_driver(&generic_driver); -} - -static void __exit toppoly_tdo_panel_drv_exit(void) -{ - omap_dss_unregister_driver(&generic_driver); -} - -module_init(toppoly_tdo_panel_drv_init); -module_exit(toppoly_tdo_panel_drv_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index fa40fa59a9a..9f8c69f16e6 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -44,34 +44,40 @@ /* DISPC */ #define DISPC_BASE 0x48050400 -#define DISPC_SZ_REGS SZ_1K +#define DISPC_SZ_REGS SZ_4K struct dispc_reg { u16 idx; }; #define DISPC_REG(idx) ((const struct dispc_reg) { idx }) -/* DISPC common */ +/* + * DISPC common registers and + * DISPC channel registers , ch = 0 for LCD, ch = 1 for + * DIGIT, and ch = 2 for LCD2 + */ #define DISPC_REVISION DISPC_REG(0x0000) #define DISPC_SYSCONFIG DISPC_REG(0x0010) #define DISPC_SYSSTATUS DISPC_REG(0x0014) #define DISPC_IRQSTATUS DISPC_REG(0x0018) #define DISPC_IRQENABLE DISPC_REG(0x001C) #define DISPC_CONTROL DISPC_REG(0x0040) +#define DISPC_CONTROL2 DISPC_REG(0x0238) #define DISPC_CONFIG DISPC_REG(0x0044) +#define DISPC_CONFIG2 DISPC_REG(0x0620) #define DISPC_CAPABLE DISPC_REG(0x0048) -#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) -#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) -#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) -#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) +#define DISPC_DEFAULT_COLOR(ch) DISPC_REG(ch == 0 ? 0x004C : \ + (ch == 1 ? 0x0050 : 0x03AC)) +#define DISPC_TRANS_COLOR(ch) DISPC_REG(ch == 0 ? 0x0054 : \ + (ch == 1 ? 0x0058 : 0x03B0)) #define DISPC_LINE_STATUS DISPC_REG(0x005C) #define DISPC_LINE_NUMBER DISPC_REG(0x0060) -#define DISPC_TIMING_H DISPC_REG(0x0064) -#define DISPC_TIMING_V DISPC_REG(0x0068) -#define DISPC_POL_FREQ DISPC_REG(0x006C) -#define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_TIMING_H(ch) DISPC_REG(ch != 2 ? 0x0064 : 0x0400) +#define DISPC_TIMING_V(ch) DISPC_REG(ch != 2 ? 0x0068 : 0x0404) +#define DISPC_POL_FREQ(ch) DISPC_REG(ch != 2 ? 0x006C : 0x0408) +#define DISPC_DIVISOR(ch) DISPC_REG(ch != 2 ? 0x0070 : 0x040C) #define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) #define DISPC_SIZE_DIG DISPC_REG(0x0078) -#define DISPC_SIZE_LCD DISPC_REG(0x007C) +#define DISPC_SIZE_LCD(ch) DISPC_REG(ch != 2 ? 0x007C : 0x03CC) /* DISPC GFX plane */ #define DISPC_GFX_BA0 DISPC_REG(0x0080) @@ -86,13 +92,12 @@ struct dispc_reg { u16 idx; }; #define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) #define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) -#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) -#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) -#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) - -#define DISPC_CPR_COEF_R DISPC_REG(0x0220) -#define DISPC_CPR_COEF_G DISPC_REG(0x0224) -#define DISPC_CPR_COEF_B DISPC_REG(0x0228) +#define DISPC_DATA_CYCLE1(ch) DISPC_REG(ch != 2 ? 0x01D4 : 0x03C0) +#define DISPC_DATA_CYCLE2(ch) DISPC_REG(ch != 2 ? 0x01D8 : 0x03C4) +#define DISPC_DATA_CYCLE3(ch) DISPC_REG(ch != 2 ? 0x01DC : 0x03C8) +#define DISPC_CPR_COEF_R(ch) DISPC_REG(ch != 2 ? 0x0220 : 0x03BC) +#define DISPC_CPR_COEF_G(ch) DISPC_REG(ch != 2 ? 0x0224 : 0x03B8) +#define DISPC_CPR_COEF_B(ch) DISPC_REG(ch != 2 ? 0x0228 : 0x03B4) #define DISPC_GFX_PRELOAD DISPC_REG(0x022C) @@ -217,18 +222,29 @@ void dispc_save_context(void) SR(IRQENABLE); SR(CONTROL); SR(CONFIG); - SR(DEFAULT_COLOR0); - SR(DEFAULT_COLOR1); - SR(TRANS_COLOR0); - SR(TRANS_COLOR1); + SR(DEFAULT_COLOR(0)); + SR(DEFAULT_COLOR(1)); + SR(TRANS_COLOR(0)); + SR(TRANS_COLOR(1)); SR(LINE_NUMBER); - SR(TIMING_H); - SR(TIMING_V); - SR(POL_FREQ); - SR(DIVISOR); + SR(TIMING_H(0)); + SR(TIMING_V(0)); + SR(POL_FREQ(0)); + SR(DIVISOR(0)); SR(GLOBAL_ALPHA); SR(SIZE_DIG); - SR(SIZE_LCD); + SR(SIZE_LCD(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + SR(CONTROL2); + SR(DEFAULT_COLOR(2)); + SR(TRANS_COLOR(2)); + SR(SIZE_LCD(2)); + SR(TIMING_H(2)); + SR(TIMING_V(2)); + SR(POL_FREQ(2)); + SR(DIVISOR(2)); + SR(CONFIG2); + } SR(GFX_BA0); SR(GFX_BA1); @@ -241,13 +257,22 @@ void dispc_save_context(void) SR(GFX_WINDOW_SKIP); SR(GFX_TABLE_BA); - SR(DATA_CYCLE1); - SR(DATA_CYCLE2); - SR(DATA_CYCLE3); - - SR(CPR_COEF_R); - SR(CPR_COEF_G); - SR(CPR_COEF_B); + SR(DATA_CYCLE1(0)); + SR(DATA_CYCLE2(0)); + SR(DATA_CYCLE3(0)); + + SR(CPR_COEF_R(0)); + SR(CPR_COEF_G(0)); + SR(CPR_COEF_B(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + SR(CPR_COEF_B(2)); + SR(CPR_COEF_G(2)); + SR(CPR_COEF_R(2)); + + SR(DATA_CYCLE1(2)); + SR(DATA_CYCLE2(2)); + SR(DATA_CYCLE3(2)); + } SR(GFX_PRELOAD); @@ -356,18 +381,28 @@ void dispc_restore_context(void) /*RR(IRQENABLE);*/ /*RR(CONTROL);*/ RR(CONFIG); - RR(DEFAULT_COLOR0); - RR(DEFAULT_COLOR1); - RR(TRANS_COLOR0); - RR(TRANS_COLOR1); + RR(DEFAULT_COLOR(0)); + RR(DEFAULT_COLOR(1)); + RR(TRANS_COLOR(0)); + RR(TRANS_COLOR(1)); RR(LINE_NUMBER); - RR(TIMING_H); - RR(TIMING_V); - RR(POL_FREQ); - RR(DIVISOR); + RR(TIMING_H(0)); + RR(TIMING_V(0)); + RR(POL_FREQ(0)); + RR(DIVISOR(0)); RR(GLOBAL_ALPHA); RR(SIZE_DIG); - RR(SIZE_LCD); + RR(SIZE_LCD(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + RR(DEFAULT_COLOR(2)); + RR(TRANS_COLOR(2)); + RR(SIZE_LCD(2)); + RR(TIMING_H(2)); + RR(TIMING_V(2)); + RR(POL_FREQ(2)); + RR(DIVISOR(2)); + RR(CONFIG2); + } RR(GFX_BA0); RR(GFX_BA1); @@ -380,13 +415,22 @@ void dispc_restore_context(void) RR(GFX_WINDOW_SKIP); RR(GFX_TABLE_BA); - RR(DATA_CYCLE1); - RR(DATA_CYCLE2); - RR(DATA_CYCLE3); - - RR(CPR_COEF_R); - RR(CPR_COEF_G); - RR(CPR_COEF_B); + RR(DATA_CYCLE1(0)); + RR(DATA_CYCLE2(0)); + RR(DATA_CYCLE3(0)); + + RR(CPR_COEF_R(0)); + RR(CPR_COEF_G(0)); + RR(CPR_COEF_B(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + RR(DATA_CYCLE1(2)); + RR(DATA_CYCLE2(2)); + RR(DATA_CYCLE3(2)); + + RR(CPR_COEF_B(2)); + RR(CPR_COEF_G(2)); + RR(CPR_COEF_R(2)); + } RR(GFX_PRELOAD); @@ -490,7 +534,8 @@ void dispc_restore_context(void) /* enable last, because LCD & DIGIT enable are here */ RR(CONTROL); - + if (dss_has_feature(FEAT_MGR_LCD2)) + RR(CONTROL2); /* clear spurious SYNC_LOST_DIGIT interrupts */ dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); @@ -516,42 +561,63 @@ bool dispc_go_busy(enum omap_channel channel) { int bit; - if (channel == OMAP_DSS_CHANNEL_LCD) + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ - return REG_GET(DISPC_CONTROL, bit, bit) == 1; + if (channel == OMAP_DSS_CHANNEL_LCD2) + return REG_GET(DISPC_CONTROL2, bit, bit) == 1; + else + return REG_GET(DISPC_CONTROL, bit, bit) == 1; } void dispc_go(enum omap_channel channel) { int bit; + bool enable_bit, go_bit; enable_clocks(1); - if (channel == OMAP_DSS_CHANNEL_LCD) + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) bit = 0; /* LCDENABLE */ else bit = 1; /* DIGITALENABLE */ /* if the channel is not enabled, we don't need GO */ - if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + if (channel == OMAP_DSS_CHANNEL_LCD2) + enable_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1; + else + enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1; + + if (!enable_bit) goto end; - if (channel == OMAP_DSS_CHANNEL_LCD) + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ - if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + if (channel == OMAP_DSS_CHANNEL_LCD2) + go_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1; + else + go_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1; + + if (go_bit) { DSSERR("GO bit not down for channel %d\n", channel); goto end; } - DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : + (channel == OMAP_DSS_CHANNEL_LCD2 ? "LCD2" : "DIGIT")); - REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit); + else + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); end: enable_clocks(0); } @@ -773,13 +839,26 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) dispc_write_reg(vsi_reg[plane-1], val); } +static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) +{ + if (!dss_has_feature(FEAT_PRE_MULT_ALPHA)) + return; + + if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + plane == OMAP_DSS_VIDEO1) + return; + + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 28, 28); +} + static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) { if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return; - BUG_ON(!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - plane == OMAP_DSS_VIDEO1); + if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + plane == OMAP_DSS_VIDEO1) + return; if (plane == OMAP_DSS_GFX) REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); @@ -851,6 +930,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, { int shift; u32 val; + int chan = 0, chan2 = 0; switch (plane) { case OMAP_DSS_GFX: @@ -866,7 +946,29 @@ static void _dispc_set_channel_out(enum omap_plane plane, } val = dispc_read_reg(dispc_reg_att[plane]); - val = FLD_MOD(val, channel, shift, shift); + if (dss_has_feature(FEAT_MGR_LCD2)) { + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + chan = 0; + chan2 = 0; + break; + case OMAP_DSS_CHANNEL_DIGIT: + chan = 1; + chan2 = 0; + break; + case OMAP_DSS_CHANNEL_LCD2: + chan = 0; + chan2 = 1; + break; + default: + BUG(); + } + + val = FLD_MOD(val, chan, shift, shift); + val = FLD_MOD(val, chan2, 31, 30); + } else { + val = FLD_MOD(val, channel, shift, shift); + } dispc_write_reg(dispc_reg_att[plane], val); } @@ -923,13 +1025,13 @@ void dispc_enable_replication(enum omap_plane plane, bool enable) enable_clocks(0); } -void dispc_set_lcd_size(u16 width, u16 height) +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) { u32 val; BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); enable_clocks(1); - dispc_write_reg(DISPC_SIZE_LCD, val); + dispc_write_reg(DISPC_SIZE_LCD(channel), val); enable_clocks(0); } @@ -1426,12 +1528,13 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, } } -static unsigned long calc_fclk_five_taps(u16 width, u16 height, - u16 out_width, u16 out_height, enum omap_color_mode color_mode) +static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, + u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode) { u32 fclk = 0; /* FIXME venc pclk? */ - u64 tmp, pclk = dispc_pclk_rate(); + u64 tmp, pclk = dispc_pclk_rate(channel); if (height > out_height) { /* FIXME get real display PPL */ @@ -1463,8 +1566,8 @@ static unsigned long calc_fclk_five_taps(u16 width, u16 height, return fclk; } -static unsigned long calc_fclk(u16 width, u16 height, - u16 out_width, u16 out_height) +static unsigned long calc_fclk(enum omap_channel channel, u16 width, + u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; @@ -1488,7 +1591,7 @@ static unsigned long calc_fclk(u16 width, u16 height, vf = 1; /* FIXME venc pclk? */ - return dispc_pclk_rate() * vf * hf; + return dispc_pclk_rate(channel) * vf * hf; } void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) @@ -1507,7 +1610,8 @@ static int _dispc_setup_plane(enum omap_plane plane, bool ilace, enum omap_dss_rotation_type rotation_type, u8 rotation, int mirror, - u8 global_alpha) + u8 global_alpha, u8 pre_mult_alpha, + enum omap_channel channel) { const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; bool five_taps = 0; @@ -1536,29 +1640,12 @@ static int _dispc_setup_plane(enum omap_plane plane, height, pos_y, out_height); } + if (!dss_feat_color_mode_supported(plane, color_mode)) + return -EINVAL; + if (plane == OMAP_DSS_GFX) { if (width != out_width || height != out_height) return -EINVAL; - - switch (color_mode) { - case OMAP_DSS_COLOR_ARGB16: - case OMAP_DSS_COLOR_ARGB32: - case OMAP_DSS_COLOR_RGBA32: - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) - return -EINVAL; - case OMAP_DSS_COLOR_RGBX32: - if (cpu_is_omap24xx()) - return -EINVAL; - /* fall through */ - case OMAP_DSS_COLOR_RGB12U: - case OMAP_DSS_COLOR_RGB16: - case OMAP_DSS_COLOR_RGB24P: - case OMAP_DSS_COLOR_RGB24U: - break; - - default: - return -EINVAL; - } } else { /* video plane */ @@ -1572,42 +1659,16 @@ static int _dispc_setup_plane(enum omap_plane plane, out_height > height * 8) return -EINVAL; - switch (color_mode) { - case OMAP_DSS_COLOR_RGBX32: - case OMAP_DSS_COLOR_RGB12U: - if (cpu_is_omap24xx()) - return -EINVAL; - /* fall through */ - case OMAP_DSS_COLOR_RGB16: - case OMAP_DSS_COLOR_RGB24P: - case OMAP_DSS_COLOR_RGB24U: - break; - - case OMAP_DSS_COLOR_ARGB16: - case OMAP_DSS_COLOR_ARGB32: - case OMAP_DSS_COLOR_RGBA32: - if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) - return -EINVAL; - if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && - plane == OMAP_DSS_VIDEO1) - return -EINVAL; - break; - - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) cconv = 1; - break; - - default: - return -EINVAL; - } /* Must use 5-tap filter? */ five_taps = height > out_height * 2; if (!five_taps) { - fclk = calc_fclk(width, height, - out_width, out_height); + fclk = calc_fclk(channel, width, height, out_width, + out_height); /* Try 5-tap filter if 3-tap fclk is too high */ if (cpu_is_omap34xx() && height > out_height && @@ -1621,7 +1682,7 @@ static int _dispc_setup_plane(enum omap_plane plane, } if (five_taps) - fclk = calc_fclk_five_taps(width, height, + fclk = calc_fclk_five_taps(channel, width, height, out_width, out_height, color_mode); DSSDBG("required fclk rate = %lu Hz\n", fclk); @@ -1693,8 +1754,8 @@ static int _dispc_setup_plane(enum omap_plane plane, _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); - if (plane != OMAP_DSS_VIDEO1) - _dispc_setup_global_alpha(plane, global_alpha); + _dispc_set_pre_mult_alpha(plane, pre_mult_alpha); + _dispc_setup_global_alpha(plane, global_alpha); return 0; } @@ -1710,36 +1771,44 @@ static void dispc_disable_isr(void *data, u32 mask) complete(compl); } -static void _enable_lcd_out(bool enable) +static void _enable_lcd_out(enum omap_channel channel, bool enable) { - REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0); + else + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); } -static void dispc_enable_lcd_out(bool enable) +static void dispc_enable_lcd_out(enum omap_channel channel, bool enable) { struct completion frame_done_completion; bool is_on; int r; + u32 irq; enable_clocks(1); /* When we disable LCD output, we need to wait until frame is done. * Otherwise the DSS is still working, and turning off the clocks * prevents DSS from going to OFF mode */ - is_on = REG_GET(DISPC_CONTROL, 0, 0); + is_on = channel == OMAP_DSS_CHANNEL_LCD2 ? + REG_GET(DISPC_CONTROL2, 0, 0) : + REG_GET(DISPC_CONTROL, 0, 0); + + irq = channel == OMAP_DSS_CHANNEL_LCD2 ? DISPC_IRQ_FRAMEDONE2 : + DISPC_IRQ_FRAMEDONE; if (!enable && is_on) { init_completion(&frame_done_completion); r = omap_dispc_register_isr(dispc_disable_isr, - &frame_done_completion, - DISPC_IRQ_FRAMEDONE); + &frame_done_completion, irq); if (r) DSSERR("failed to register FRAMEDONE isr\n"); } - _enable_lcd_out(enable); + _enable_lcd_out(channel, enable); if (!enable && is_on) { if (!wait_for_completion_timeout(&frame_done_completion, @@ -1747,8 +1816,7 @@ static void dispc_enable_lcd_out(bool enable) DSSERR("timeout waiting for FRAME DONE\n"); r = omap_dispc_unregister_isr(dispc_disable_isr, - &frame_done_completion, - DISPC_IRQ_FRAMEDONE); + &frame_done_completion, irq); if (r) DSSERR("failed to unregister FRAMEDONE isr\n"); @@ -1818,6 +1886,8 @@ static void dispc_enable_digit_out(bool enable) unsigned long flags; spin_lock_irqsave(&dispc.irq_lock, flags); dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + if (dss_has_feature(FEAT_MGR_LCD2)) + dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); _omap_dispc_set_irqs(); spin_unlock_irqrestore(&dispc.irq_lock, flags); @@ -1832,14 +1902,17 @@ bool dispc_is_channel_enabled(enum omap_channel channel) return !!REG_GET(DISPC_CONTROL, 0, 0); else if (channel == OMAP_DSS_CHANNEL_DIGIT) return !!REG_GET(DISPC_CONTROL, 1, 1); + else if (channel == OMAP_DSS_CHANNEL_LCD2) + return !!REG_GET(DISPC_CONTROL2, 0, 0); else BUG(); } void dispc_enable_channel(enum omap_channel channel, bool enable) { - if (channel == OMAP_DSS_CHANNEL_LCD) - dispc_enable_lcd_out(enable); + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) + dispc_enable_lcd_out(channel, enable); else if (channel == OMAP_DSS_CHANNEL_DIGIT) dispc_enable_digit_out(enable); else @@ -1848,6 +1921,9 @@ void dispc_enable_channel(enum omap_channel channel, bool enable) void dispc_lcd_enable_signal_polarity(bool act_high) { + if (!dss_has_feature(FEAT_LCDENABLEPOL)) + return; + enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); enable_clocks(0); @@ -1855,6 +1931,9 @@ void dispc_lcd_enable_signal_polarity(bool act_high) void dispc_lcd_enable_signal(bool enable) { + if (!dss_has_feature(FEAT_LCDENABLESIGNAL)) + return; + enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); enable_clocks(0); @@ -1862,20 +1941,27 @@ void dispc_lcd_enable_signal(bool enable) void dispc_pck_free_enable(bool enable) { + if (!dss_has_feature(FEAT_PCKFREEENABLE)) + return; + enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); enable_clocks(0); } -void dispc_enable_fifohandcheck(bool enable) +void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable) { enable_clocks(1); - REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16); + else + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); enable_clocks(0); } -void dispc_set_lcd_display_type(enum omap_lcd_display_type type) +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type) { int mode; @@ -1894,7 +1980,10 @@ void dispc_set_lcd_display_type(enum omap_lcd_display_type type) } enable_clocks(1); - REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3); + else + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); enable_clocks(0); } @@ -1908,25 +1997,21 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode) void dispc_set_default_color(enum omap_channel channel, u32 color) { - const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, - DISPC_DEFAULT_COLOR1 }; - enable_clocks(1); - dispc_write_reg(def_reg[channel], color); + dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); enable_clocks(0); } u32 dispc_get_default_color(enum omap_channel channel) { - const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, - DISPC_DEFAULT_COLOR1 }; u32 l; BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && - channel != OMAP_DSS_CHANNEL_LCD); + channel != OMAP_DSS_CHANNEL_LCD && + channel != OMAP_DSS_CHANNEL_LCD2); enable_clocks(1); - l = dispc_read_reg(def_reg[channel]); + l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel)); enable_clocks(0); return l; @@ -1936,16 +2021,15 @@ void dispc_set_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type type, u32 trans_key) { - const struct dispc_reg tr_reg[] = { - DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); - else /* OMAP_DSS_CHANNEL_DIGIT */ + else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); + else /* OMAP_DSS_CHANNEL_LCD2 */ + REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11); - dispc_write_reg(tr_reg[ch], trans_key); + dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); enable_clocks(0); } @@ -1953,21 +2037,20 @@ void dispc_get_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type *type, u32 *trans_key) { - const struct dispc_reg tr_reg[] = { - DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; - enable_clocks(1); if (type) { if (ch == OMAP_DSS_CHANNEL_LCD) *type = REG_GET(DISPC_CONFIG, 11, 11); else if (ch == OMAP_DSS_CHANNEL_DIGIT) *type = REG_GET(DISPC_CONFIG, 13, 13); + else if (ch == OMAP_DSS_CHANNEL_LCD2) + *type = REG_GET(DISPC_CONFIG2, 11, 11); else BUG(); } if (trans_key) - *trans_key = dispc_read_reg(tr_reg[ch]); + *trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch)); enable_clocks(0); } @@ -1976,8 +2059,10 @@ void dispc_enable_trans_key(enum omap_channel ch, bool enable) enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); - else /* OMAP_DSS_CHANNEL_DIGIT */ + else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); + else /* OMAP_DSS_CHANNEL_LCD2 */ + REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); enable_clocks(0); } void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) @@ -1988,8 +2073,10 @@ void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); - else /* OMAP_DSS_CHANNEL_DIGIT */ + else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); + else /* OMAP_DSS_CHANNEL_LCD2 */ + REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18); enable_clocks(0); } bool dispc_alpha_blending_enabled(enum omap_channel ch) @@ -2003,13 +2090,14 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch) if (ch == OMAP_DSS_CHANNEL_LCD) enabled = REG_GET(DISPC_CONFIG, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) - enabled = REG_GET(DISPC_CONFIG, 18, 18); + enabled = REG_GET(DISPC_CONFIG, 19, 19); + else if (ch == OMAP_DSS_CHANNEL_LCD2) + enabled = REG_GET(DISPC_CONFIG2, 18, 18); else BUG(); enable_clocks(0); return enabled; - } @@ -2022,6 +2110,8 @@ bool dispc_trans_key_enabled(enum omap_channel ch) enabled = REG_GET(DISPC_CONFIG, 10, 10); else if (ch == OMAP_DSS_CHANNEL_DIGIT) enabled = REG_GET(DISPC_CONFIG, 12, 12); + else if (ch == OMAP_DSS_CHANNEL_LCD2) + enabled = REG_GET(DISPC_CONFIG2, 10, 10); else BUG(); enable_clocks(0); @@ -2030,7 +2120,7 @@ bool dispc_trans_key_enabled(enum omap_channel ch) } -void dispc_set_tft_data_lines(u8 data_lines) +void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) { int code; @@ -2053,11 +2143,15 @@ void dispc_set_tft_data_lines(u8 data_lines) } enable_clocks(1); - REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8); + else + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); enable_clocks(0); } -void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode) { u32 l; int stallmode; @@ -2087,13 +2181,17 @@ void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) enable_clocks(1); - l = dispc_read_reg(DISPC_CONTROL); - - l = FLD_MOD(l, stallmode, 11, 11); - l = FLD_MOD(l, gpout0, 15, 15); - l = FLD_MOD(l, gpout1, 16, 16); - - dispc_write_reg(DISPC_CONTROL, l); + if (channel == OMAP_DSS_CHANNEL_LCD2) { + l = dispc_read_reg(DISPC_CONTROL2); + l = FLD_MOD(l, stallmode, 11, 11); + dispc_write_reg(DISPC_CONTROL2, l); + } else { + l = dispc_read_reg(DISPC_CONTROL); + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + dispc_write_reg(DISPC_CONTROL, l); + } enable_clocks(0); } @@ -2129,8 +2227,8 @@ bool dispc_lcd_timings_ok(struct omap_video_timings *timings) timings->vfp, timings->vbp); } -static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, - int vsw, int vfp, int vbp) +static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, + int hfp, int hbp, int vsw, int vfp, int vbp) { u32 timing_h, timing_v; @@ -2149,13 +2247,14 @@ static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, } enable_clocks(1); - dispc_write_reg(DISPC_TIMING_H, timing_h); - dispc_write_reg(DISPC_TIMING_V, timing_v); + dispc_write_reg(DISPC_TIMING_H(channel), timing_h); + dispc_write_reg(DISPC_TIMING_V(channel), timing_v); enable_clocks(0); } /* change name to mode? */ -void dispc_set_lcd_timings(struct omap_video_timings *timings) +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings) { unsigned xtot, ytot; unsigned long ht, vt; @@ -2165,10 +2264,11 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) timings->vfp, timings->vbp)) BUG(); - _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, - timings->vsw, timings->vfp, timings->vbp); + _dispc_set_lcd_timings(channel, timings->hsw, timings->hfp, + timings->hbp, timings->vsw, timings->vfp, + timings->vbp); - dispc_set_lcd_size(timings->x_res, timings->y_res); + dispc_set_lcd_size(channel, timings->x_res, timings->y_res); xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; @@ -2176,7 +2276,8 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) ht = (timings->pixel_clock * 1000) / xtot; vt = (timings->pixel_clock * 1000) / xtot / ytot; - DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res); + DSSDBG("channel %d xres %u yres %u\n", channel, timings->x_res, + timings->y_res); DSSDBG("pck %u\n", timings->pixel_clock); DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", timings->hsw, timings->hfp, timings->hbp, @@ -2185,21 +2286,23 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); } -static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) +static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, + u16 pck_div) { BUG_ON(lck_div < 1); BUG_ON(pck_div < 2); enable_clocks(1); - dispc_write_reg(DISPC_DIVISOR, + dispc_write_reg(DISPC_DIVISOR(channel), FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); enable_clocks(0); } -static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div, + int *pck_div) { u32 l; - l = dispc_read_reg(DISPC_DIVISOR); + l = dispc_read_reg(DISPC_DIVISOR(channel)); *lck_div = FLD_GET(l, 23, 16); *pck_div = FLD_GET(l, 7, 0); } @@ -2219,13 +2322,13 @@ unsigned long dispc_fclk_rate(void) return r; } -unsigned long dispc_lclk_rate(void) +unsigned long dispc_lclk_rate(enum omap_channel channel) { int lcd; unsigned long r; u32 l; - l = dispc_read_reg(DISPC_DIVISOR); + l = dispc_read_reg(DISPC_DIVISOR(channel)); lcd = FLD_GET(l, 23, 16); @@ -2234,13 +2337,13 @@ unsigned long dispc_lclk_rate(void) return r / lcd; } -unsigned long dispc_pclk_rate(void) +unsigned long dispc_pclk_rate(enum omap_channel channel) { int lcd, pcd; unsigned long r; u32 l; - l = dispc_read_reg(DISPC_DIVISOR); + l = dispc_read_reg(DISPC_DIVISOR(channel)); lcd = FLD_GET(l, 23, 16); pcd = FLD_GET(l, 7, 0); @@ -2256,8 +2359,6 @@ void dispc_dump_clocks(struct seq_file *s) enable_clocks(1); - dispc_get_lcd_divisor(&lcd, &pcd); - seq_printf(s, "- DISPC -\n"); seq_printf(s, "dispc fclk source = %s\n", @@ -2265,9 +2366,25 @@ void dispc_dump_clocks(struct seq_file *s) "dss1_alwon_fclk" : "dsi1_pll_fclk"); seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); - seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd); - seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd); + seq_printf(s, "- LCD1 -\n"); + + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); + + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd); + if (dss_has_feature(FEAT_MGR_LCD2)) { + seq_printf(s, "- LCD2 -\n"); + + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); + + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd); + } enable_clocks(0); } @@ -2309,6 +2426,12 @@ void dispc_dump_irqs(struct seq_file *s) PIS(SYNC_LOST); PIS(SYNC_LOST_DIGIT); PIS(WAKEUP); + if (dss_has_feature(FEAT_MGR_LCD2)) { + PIS(FRAMEDONE2); + PIS(VSYNC2); + PIS(ACBIAS_COUNT_STAT2); + PIS(SYNC_LOST2); + } #undef PIS } #endif @@ -2327,19 +2450,30 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_CONTROL); DUMPREG(DISPC_CONFIG); DUMPREG(DISPC_CAPABLE); - DUMPREG(DISPC_DEFAULT_COLOR0); - DUMPREG(DISPC_DEFAULT_COLOR1); - DUMPREG(DISPC_TRANS_COLOR0); - DUMPREG(DISPC_TRANS_COLOR1); + DUMPREG(DISPC_DEFAULT_COLOR(0)); + DUMPREG(DISPC_DEFAULT_COLOR(1)); + DUMPREG(DISPC_TRANS_COLOR(0)); + DUMPREG(DISPC_TRANS_COLOR(1)); DUMPREG(DISPC_LINE_STATUS); DUMPREG(DISPC_LINE_NUMBER); - DUMPREG(DISPC_TIMING_H); - DUMPREG(DISPC_TIMING_V); - DUMPREG(DISPC_POL_FREQ); - DUMPREG(DISPC_DIVISOR); + DUMPREG(DISPC_TIMING_H(0)); + DUMPREG(DISPC_TIMING_V(0)); + DUMPREG(DISPC_POL_FREQ(0)); + DUMPREG(DISPC_DIVISOR(0)); DUMPREG(DISPC_GLOBAL_ALPHA); DUMPREG(DISPC_SIZE_DIG); - DUMPREG(DISPC_SIZE_LCD); + DUMPREG(DISPC_SIZE_LCD(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + DUMPREG(DISPC_CONTROL2); + DUMPREG(DISPC_CONFIG2); + DUMPREG(DISPC_DEFAULT_COLOR(2)); + DUMPREG(DISPC_TRANS_COLOR(2)); + DUMPREG(DISPC_TIMING_H(2)); + DUMPREG(DISPC_TIMING_V(2)); + DUMPREG(DISPC_POL_FREQ(2)); + DUMPREG(DISPC_DIVISOR(2)); + DUMPREG(DISPC_SIZE_LCD(2)); + } DUMPREG(DISPC_GFX_BA0); DUMPREG(DISPC_GFX_BA1); @@ -2353,13 +2487,22 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_GFX_WINDOW_SKIP); DUMPREG(DISPC_GFX_TABLE_BA); - DUMPREG(DISPC_DATA_CYCLE1); - DUMPREG(DISPC_DATA_CYCLE2); - DUMPREG(DISPC_DATA_CYCLE3); - - DUMPREG(DISPC_CPR_COEF_R); - DUMPREG(DISPC_CPR_COEF_G); - DUMPREG(DISPC_CPR_COEF_B); + DUMPREG(DISPC_DATA_CYCLE1(0)); + DUMPREG(DISPC_DATA_CYCLE2(0)); + DUMPREG(DISPC_DATA_CYCLE3(0)); + + DUMPREG(DISPC_CPR_COEF_R(0)); + DUMPREG(DISPC_CPR_COEF_G(0)); + DUMPREG(DISPC_CPR_COEF_B(0)); + if (dss_has_feature(FEAT_MGR_LCD2)) { + DUMPREG(DISPC_DATA_CYCLE1(2)); + DUMPREG(DISPC_DATA_CYCLE2(2)); + DUMPREG(DISPC_DATA_CYCLE3(2)); + + DUMPREG(DISPC_CPR_COEF_R(2)); + DUMPREG(DISPC_CPR_COEF_G(2)); + DUMPREG(DISPC_CPR_COEF_B(2)); + } DUMPREG(DISPC_GFX_PRELOAD); @@ -2458,8 +2601,8 @@ void dispc_dump_regs(struct seq_file *s) #undef DUMPREG } -static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, - bool ihs, bool ivs, u8 acbi, u8 acb) +static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf, + bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi, u8 acb) { u32 l = 0; @@ -2476,13 +2619,14 @@ static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, l |= FLD_VAL(acb, 7, 0); enable_clocks(1); - dispc_write_reg(DISPC_POL_FREQ, l); + dispc_write_reg(DISPC_POL_FREQ(channel), l); enable_clocks(0); } -void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) +void dispc_set_pol_freq(enum omap_channel channel, + enum omap_panel_config config, u8 acbi, u8 acb) { - _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0, + _dispc_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0, (config & OMAP_DSS_LCD_RF) != 0, (config & OMAP_DSS_LCD_IEO) != 0, (config & OMAP_DSS_LCD_IPC) != 0, @@ -2551,24 +2695,26 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, return 0; } -int dispc_set_clock_div(struct dispc_clock_info *cinfo) +int dispc_set_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo) { DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); - dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + dispc_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); return 0; } -int dispc_get_clock_div(struct dispc_clock_info *cinfo) +int dispc_get_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo) { unsigned long fck; fck = dispc_fclk_rate(); - cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); - cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); + cinfo->lck_div = REG_GET(DISPC_DIVISOR(channel), 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR(channel), 7, 0); cinfo->lck = fck / cinfo->lck_div; cinfo->pck = cinfo->lck / cinfo->pck_div; @@ -2708,6 +2854,8 @@ static void print_irq_status(u32 status) PIS(VID2_FIFO_UNDERFLOW); PIS(SYNC_LOST); PIS(SYNC_LOST_DIGIT); + if (dss_has_feature(FEAT_MGR_LCD2)) + PIS(SYNC_LOST2); #undef PIS printk("\n"); @@ -2926,6 +3074,45 @@ static void dispc_error_worker(struct work_struct *work) } } + if (errors & DISPC_IRQ_SYNC_LOST2) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST for LCD2, disabling LCD2\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_LCD2) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->driver->disable(mgr->device); + break; + } + } + + if (manager) { + struct omap_dss_device *dssdev = manager->device; + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + dssdev->driver->enable(dssdev); + } + } + if (errors & DISPC_IRQ_OCP_ERR) { DSSERR("OCP_ERR\n"); for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { @@ -3033,6 +3220,8 @@ static void _omap_dispc_initialize_irq(void) memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr)); dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + if (dss_has_feature(FEAT_MGR_LCD2)) + dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, * so clear it */ @@ -3065,7 +3254,8 @@ static void _omap_dispc_initial_config(void) dispc_write_reg(DISPC_SYSCONFIG, l); /* FUNCGATED */ - REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + if (dss_has_feature(FEAT_FUNCGATED)) + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); /* L3 firewall setting: enable access to OCM RAM */ /* XXX this should be somewhere in plat-omap */ @@ -3139,17 +3329,18 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_color_mode color_mode, bool ilace, enum omap_dss_rotation_type rotation_type, - u8 rotation, bool mirror, u8 global_alpha) + u8 rotation, bool mirror, u8 global_alpha, + u8 pre_mult_alpha, enum omap_channel channel) { int r = 0; DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " - "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n", + "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", plane, paddr, screen_width, pos_x, pos_y, width, height, out_width, out_height, ilace, color_mode, - rotation, mirror); + rotation, mirror, channel); enable_clocks(1); @@ -3161,7 +3352,8 @@ int dispc_setup_plane(enum omap_plane plane, color_mode, ilace, rotation_type, rotation, mirror, - global_alpha); + global_alpha, + pre_mult_alpha, channel); enable_clocks(0); diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 960e977a8bf..75fb0a51543 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -40,8 +40,9 @@ static struct { } dpi; #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL -static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, - unsigned long *fck, int *lck_div, int *pck_div) +static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, + unsigned long pck_req, unsigned long *fck, int *lck_div, + int *pck_div) { struct dsi_clock_info dsi_cinfo; struct dispc_clock_info dispc_cinfo; @@ -58,7 +59,7 @@ static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) return r; @@ -69,8 +70,9 @@ static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, return 0; } #else -static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, - unsigned long *fck, int *lck_div, int *pck_div) +static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft, + unsigned long pck_req, unsigned long *fck, int *lck_div, + int *pck_div) { struct dss_clock_info dss_cinfo; struct dispc_clock_info dispc_cinfo; @@ -84,7 +86,7 @@ static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, if (r) return r; - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) return r; @@ -107,17 +109,17 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, - dssdev->panel.acb); + dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, - &fck, &lck_div, &pck_div); + r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, + &lck_div, &pck_div); #else - r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, - &fck, &lck_div, &pck_div); + r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, + &lck_div, &pck_div); #endif if (r) goto err0; @@ -132,7 +134,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_set_lcd_timings(t); + dispc_set_lcd_timings(dssdev->manager->id, t); err0: dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); @@ -145,10 +147,12 @@ static int dpi_basic_init(struct omap_dss_device *dssdev) is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); - dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : - OMAP_DSS_LCD_DISPLAY_STN); - dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); + dispc_set_parallel_interface_mode(dssdev->manager->id, + OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(dssdev->manager->id, is_tft ? + OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(dssdev->manager->id, + dssdev->phy.dpi.data_lines); return 0; } @@ -234,7 +238,7 @@ void dpi_set_timings(struct omap_dss_device *dssdev, dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { dpi_set_mode(dssdev); - dispc_go(OMAP_DSS_CHANNEL_LCD); + dispc_go(dssdev->manager->id); } } EXPORT_SYMBOL(dpi_set_timings); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index aa4f7a5fae2..ddf3a056082 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -792,7 +792,8 @@ static int dsi_pll_power(enum dsi_pll_power_state state) } /* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) +static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, + struct dsi_clock_info *cinfo) { if (cinfo->regn == 0 || cinfo->regn > REGN_MAX) return -EINVAL; @@ -812,7 +813,7 @@ static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) * with DSS2_FCK source also */ cinfo->highfreq = 0; } else { - cinfo->clkin = dispc_pclk_rate(); + cinfo->clkin = dispc_pclk_rate(dssdev->manager->id); if (cinfo->clkin < 32000000) cinfo->highfreq = 0; @@ -1206,8 +1207,8 @@ void dsi_dump_clocks(struct seq_file *s) seq_printf(s, "VP_CLK\t\t%lu\n" "VP_PCLK\t\t%lu\n", - dispc_lclk_rate(), - dispc_pclk_rate()); + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD)); enable_clocks(0); } @@ -2888,7 +2889,7 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { dss_setup_partial_planes(dssdev, x, y, w, h, enlarge_update_area); - dispc_set_lcd_size(*w, *h); + dispc_set_lcd_size(dssdev->manager->id, *w, *h); } return 0; @@ -2947,12 +2948,14 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) return r; } - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI); - dispc_enable_fifohandcheck(1); + dispc_set_parallel_interface_mode(dssdev->manager->id, + OMAP_DSS_PARALLELMODE_DSI); + dispc_enable_fifohandcheck(dssdev->manager->id, 1); - dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size); { struct omap_video_timings timings = { @@ -2964,7 +2967,7 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) .vbp = 0, }; - dispc_set_lcd_timings(&timings); + dispc_set_lcd_timings(dssdev->manager->id, &timings); } return 0; @@ -2987,7 +2990,7 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) cinfo.regm = dssdev->phy.dsi.div.regm; cinfo.regm3 = dssdev->phy.dsi.div.regm3; cinfo.regm4 = dssdev->phy.dsi.div.regm4; - r = dsi_calc_clock_rates(&cinfo); + r = dsi_calc_clock_rates(dssdev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; @@ -3019,7 +3022,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) return r; } - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) { DSSERR("Failed to set dispc clocks\n"); return r; diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 5c7940d5f28..b394951120a 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -333,9 +333,9 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_enable_fifohandcheck(bool enable); +void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable); -void dispc_set_lcd_size(u16 width, u16 height); +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height); void dispc_set_digit_size(u16 width, u16 height); u32 dispc_get_plane_fifo_size(enum omap_plane plane); void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); @@ -359,7 +359,8 @@ int dispc_setup_plane(enum omap_plane plane, bool ilace, enum omap_dss_rotation_type rotation_type, u8 rotation, bool mirror, - u8 global_alpha); + u8 global_alpha, u8 pre_mult_alpha, + enum omap_channel channel); bool dispc_go_busy(enum omap_channel channel); void dispc_go(enum omap_channel channel); @@ -368,9 +369,11 @@ bool dispc_is_channel_enabled(enum omap_channel channel); int dispc_enable_plane(enum omap_plane plane, bool enable); void dispc_enable_replication(enum omap_plane plane, bool enable); -void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode); -void dispc_set_tft_data_lines(u8 data_lines); -void dispc_set_lcd_display_type(enum omap_lcd_display_type type); +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode); +void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines); +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type); void dispc_set_loadmode(enum omap_dss_load_mode mode); void dispc_set_default_color(enum omap_channel channel, u32 color); @@ -387,17 +390,21 @@ bool dispc_trans_key_enabled(enum omap_channel ch); bool dispc_alpha_blending_enabled(enum omap_channel ch); bool dispc_lcd_timings_ok(struct omap_video_timings *timings); -void dispc_set_lcd_timings(struct omap_video_timings *timings); +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); -unsigned long dispc_lclk_rate(void); -unsigned long dispc_pclk_rate(void); -void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); +unsigned long dispc_lclk_rate(enum omap_channel channel); +unsigned long dispc_pclk_rate(enum omap_channel channel); +void dispc_set_pol_freq(enum omap_channel channel, + enum omap_panel_config config, u8 acbi, u8 acb); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, struct dispc_clock_info *cinfo); -int dispc_set_clock_div(struct dispc_clock_info *cinfo); -int dispc_get_clock_div(struct dispc_clock_info *cinfo); +int dispc_set_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo); +int dispc_get_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo); /* VENC */ @@ -424,8 +431,8 @@ void rfbi_dump_regs(struct seq_file *s); int rfbi_configure(int rfbi_module, int bpp, int lines); void rfbi_enable_rfbi(bool enable); -void rfbi_transfer_area(u16 width, u16 height, - void (callback)(void *data), void *data); +void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, + u16 height, void (callback)(void *data), void *data); void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); unsigned long rfbi_get_max_tx_rate(void); int rfbi_init_display(struct omap_dss_device *display); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 867f68de125..cf3ef696e14 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -82,6 +82,18 @@ static const enum omap_display_type omap3_dss_supported_displays[] = { OMAP_DISPLAY_TYPE_VENC, }; +static const enum omap_display_type omap4_dss_supported_displays[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DISPLAY_TYPE_VENC, + + /* OMAP_DSS_CHANNEL_LCD2 */ + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_DSI, +}; + static const enum omap_color_mode omap2_dss_supported_color_modes[] = { /* OMAP_DSS_GFX */ OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | @@ -127,6 +139,10 @@ static struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), + .has_feature = + FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | + FEAT_PCKFREEENABLE | FEAT_FUNCGATED, + .num_mgrs = 2, .num_ovls = 3, .supported_displays = omap2_dss_supported_displays, @@ -134,11 +150,29 @@ static struct omap_dss_features omap2_dss_features = { }; /* OMAP3 DSS Features */ -static struct omap_dss_features omap3_dss_features = { +static struct omap_dss_features omap3430_dss_features = { + .reg_fields = omap3_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), + + .has_feature = + FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | + FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | + FEAT_FUNCGATED, + + .num_mgrs = 2, + .num_ovls = 3, + .supported_displays = omap3_dss_supported_displays, + .supported_color_modes = omap3_dss_supported_color_modes, +}; + +static struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = FEAT_GLOBAL_ALPHA, + .has_feature = + FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | + FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | + FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED, .num_mgrs = 2, .num_ovls = 3, @@ -146,6 +180,21 @@ static struct omap_dss_features omap3_dss_features = { .supported_color_modes = omap3_dss_supported_color_modes, }; +/* OMAP4 DSS Features */ +static struct omap_dss_features omap4_dss_features = { + .reg_fields = omap3_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), + + .has_feature = + FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | + FEAT_MGR_LCD2, + + .num_mgrs = 3, + .num_ovls = 3, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap3_dss_supported_color_modes, +}; + /* Functions returning values related to a DSS feature */ int dss_feat_get_num_mgrs(void) { @@ -167,6 +216,13 @@ enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane) return omap_current_dss_features->supported_color_modes[plane]; } +bool dss_feat_color_mode_supported(enum omap_plane plane, + enum omap_color_mode color_mode) +{ + return omap_current_dss_features->supported_color_modes[plane] & + color_mode; +} + /* DSS has_feature check */ bool dss_has_feature(enum dss_feat_id id) { @@ -186,6 +242,10 @@ void dss_features_init(void) { if (cpu_is_omap24xx()) omap_current_dss_features = &omap2_dss_features; + else if (cpu_is_omap3630()) + omap_current_dss_features = &omap3630_dss_features; + else if (cpu_is_omap34xx()) + omap_current_dss_features = &omap3430_dss_features; else - omap_current_dss_features = &omap3_dss_features; + omap_current_dss_features = &omap4_dss_features; } diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index cb231eaa9b3..b9c70be9258 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -20,13 +20,19 @@ #ifndef __OMAP2_DSS_FEATURES_H #define __OMAP2_DSS_FEATURES_H -#define MAX_DSS_MANAGERS 2 +#define MAX_DSS_MANAGERS 3 #define MAX_DSS_OVERLAYS 3 /* DSS has feature id */ enum dss_feat_id { FEAT_GLOBAL_ALPHA = 1 << 0, FEAT_GLOBAL_ALPHA_VID1 = 1 << 1, + FEAT_PRE_MULT_ALPHA = 1 << 2, + FEAT_LCDENABLEPOL = 1 << 3, + FEAT_LCDENABLESIGNAL = 1 << 4, + FEAT_PCKFREEENABLE = 1 << 5, + FEAT_FUNCGATED = 1 << 6, + FEAT_MGR_LCD2 = 1 << 7, }; /* DSS register field id */ @@ -43,6 +49,8 @@ int dss_feat_get_num_mgrs(void); int dss_feat_get_num_ovls(void); enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel); enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane); +bool dss_feat_color_mode_supported(enum omap_plane plane, + enum omap_color_mode color_mode); bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 545e9b9a4d9..172d4e69730 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -406,6 +406,7 @@ struct overlay_cache_data { u16 out_width; /* if 0, out_width == width */ u16 out_height; /* if 0, out_height == height */ u8 global_alpha; + u8 pre_mult_alpha; enum omap_channel channel; bool replication; @@ -512,11 +513,14 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) unsigned long timeout = msecs_to_jiffies(500); u32 irq; - if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD; - else - irq = DISPC_IRQ_VSYNC; - + } else { + if (mgr->id == OMAP_DSS_CHANNEL_LCD) + irq = DISPC_IRQ_VSYNC; + else + irq = DISPC_IRQ_VSYNC2; + } return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); } @@ -524,7 +528,6 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) { unsigned long timeout = msecs_to_jiffies(500); struct manager_cache_data *mc; - enum omap_channel channel; u32 irq; int r; int i; @@ -535,7 +538,6 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; - channel = OMAP_DSS_CHANNEL_DIGIT; } else { if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { enum omap_dss_update_mode mode; @@ -543,11 +545,14 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (mode != OMAP_DSS_UPDATE_AUTO) return 0; - irq = DISPC_IRQ_FRAMEDONE; + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; } else { - irq = DISPC_IRQ_VSYNC; + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; } - channel = OMAP_DSS_CHANNEL_LCD; } mc = &dss_cache.manager_cache[mgr->id]; @@ -594,7 +599,6 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) { unsigned long timeout = msecs_to_jiffies(500); - enum omap_channel channel; struct overlay_cache_data *oc; struct omap_dss_device *dssdev; u32 irq; @@ -611,7 +615,6 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; - channel = OMAP_DSS_CHANNEL_DIGIT; } else { if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { enum omap_dss_update_mode mode; @@ -619,11 +622,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (mode != OMAP_DSS_UPDATE_AUTO) return 0; - irq = DISPC_IRQ_FRAMEDONE; + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; } else { - irq = DISPC_IRQ_VSYNC; + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; } - channel = OMAP_DSS_CHANNEL_LCD; } oc = &dss_cache.overlay_cache[ovl->id]; @@ -842,7 +848,9 @@ static int configure_overlay(enum omap_plane plane) c->rotation_type, c->rotation, c->mirror, - c->global_alpha); + c->global_alpha, + c->pre_mult_alpha, + c->channel); if (r) { /* this shouldn't happen */ @@ -894,10 +902,10 @@ static int configure_dispc(void) r = 0; busy = false; - mgr_busy[0] = dispc_go_busy(0); - mgr_busy[1] = dispc_go_busy(1); - mgr_go[0] = false; - mgr_go[1] = false; + for (i = 0; i < num_mgrs; i++) { + mgr_busy[i] = dispc_go_busy(i); + mgr_go[i] = false; + } /* Commit overlay settings */ for (i = 0; i < num_ovls; ++i) { @@ -1156,9 +1164,10 @@ static void dss_apply_irq_handler(void *data, u32 mask) const int num_mgrs = dss_feat_get_num_mgrs(); int i, r; bool mgr_busy[MAX_DSS_MANAGERS]; + u32 irq_mask; - mgr_busy[0] = dispc_go_busy(0); - mgr_busy[1] = dispc_go_busy(1); + for (i = 0; i < num_mgrs; i++) + mgr_busy[i] = dispc_go_busy(i); spin_lock(&dss_cache.lock); @@ -1179,8 +1188,8 @@ static void dss_apply_irq_handler(void *data, u32 mask) goto end; /* re-read busy flags */ - mgr_busy[0] = dispc_go_busy(0); - mgr_busy[1] = dispc_go_busy(1); + for (i = 0; i < num_mgrs; i++) + mgr_busy[i] = dispc_go_busy(i); /* keep running as long as there are busy managers, so that * we can collect overlay-applied information */ @@ -1189,9 +1198,12 @@ static void dss_apply_irq_handler(void *data, u32 mask) goto end; } - omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, - DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | - DISPC_IRQ_EVSYNC_EVEN); + irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN; + if (dss_has_feature(FEAT_MGR_LCD2)) + irq_mask |= DISPC_IRQ_VSYNC2; + + omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask); dss_cache.irq_enabled = false; end: @@ -1265,6 +1277,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) oc->out_width = ovl->info.out_width; oc->out_height = ovl->info.out_height; oc->global_alpha = ovl->info.global_alpha; + oc->pre_mult_alpha = ovl->info.pre_mult_alpha; oc->replication = dss_use_replication(dssdev, ovl->info.color_mode); @@ -1383,9 +1396,14 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) r = 0; dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); if (!dss_cache.irq_enabled) { - r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, - DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | - DISPC_IRQ_EVSYNC_EVEN); + u32 mask; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN; + if (dss_has_feature(FEAT_MGR_LCD2)) + mask |= DISPC_IRQ_VSYNC2; + + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask); dss_cache.irq_enabled = true; } configure_dispc(); @@ -1477,6 +1495,10 @@ int dss_init_overlay_managers(struct platform_device *pdev) mgr->name = "tv"; mgr->id = OMAP_DSS_CHANNEL_DIGIT; break; + case 2: + mgr->name = "lcd2"; + mgr->id = OMAP_DSS_CHANNEL_LCD2; + break; } mgr->set_device = &omap_dss_set_device; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 75642c22cac..456efef03c2 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -257,6 +257,43 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, return size; } +static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.pre_mult_alpha); +} + +static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + /* only GFX and Video2 plane support pre alpha multiplied + * set zero for Video1 plane + */ + if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + ovl->id == OMAP_DSS_VIDEO1) + info.pre_mult_alpha = 0; + else + info.pre_mult_alpha = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + struct overlay_attribute { struct attribute attr; ssize_t (*show)(struct omap_overlay *, char *); @@ -280,6 +317,9 @@ static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, overlay_enabled_show, overlay_enabled_store); static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, overlay_global_alpha_show, overlay_global_alpha_store); +static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR, + overlay_pre_mult_alpha_show, + overlay_pre_mult_alpha_store); static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_name.attr, @@ -290,6 +330,7 @@ static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_output_size.attr, &overlay_attr_enabled.attr, &overlay_attr_global_alpha.attr, + &overlay_attr_pre_mult_alpha.attr, NULL }; @@ -623,12 +664,22 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) int i; struct omap_overlay_manager *lcd_mgr; struct omap_overlay_manager *tv_mgr; + struct omap_overlay_manager *lcd2_mgr = NULL; struct omap_overlay_manager *mgr = NULL; lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); - - if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { + if (dss_has_feature(FEAT_MGR_LCD2)) + lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + if (!lcd2_mgr->device || force) { + if (lcd2_mgr->device) + lcd2_mgr->unset_device(lcd2_mgr); + lcd2_mgr->set_device(lcd2_mgr, dssdev); + mgr = lcd2_mgr; + } + } else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { if (!lcd_mgr->device || force) { if (lcd_mgr->device) lcd_mgr->unset_device(lcd_mgr); diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index bbe62464e92..10a2ffe0288 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -301,8 +301,8 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, } EXPORT_SYMBOL(omap_rfbi_write_pixels); -void rfbi_transfer_area(u16 width, u16 height, - void (callback)(void *data), void *data) +void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, + u16 height, void (*callback)(void *data), void *data) { u32 l; @@ -311,9 +311,9 @@ void rfbi_transfer_area(u16 width, u16 height, DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_set_lcd_size(width, height); + dispc_set_lcd_size(dssdev->manager->id, width, height); - dispc_enable_channel(OMAP_DSS_CHANNEL_LCD, true); + dispc_enable_channel(dssdev->manager->id, true); rfbi.framedone_callback = callback; rfbi.framedone_callback_data = data; @@ -887,7 +887,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { dss_setup_partial_planes(dssdev, x, y, w, h, true); - dispc_set_lcd_size(*w, *h); + dispc_set_lcd_size(dssdev->manager->id, *w, *h); } return 0; @@ -899,7 +899,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev, void (*callback)(void *), void *data) { if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - rfbi_transfer_area(w, h, callback, data); + rfbi_transfer_area(dssdev, w, h, callback, data); } else { struct omap_overlay *ovl; void __iomem *addr; @@ -1018,11 +1018,13 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) goto err1; } - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); + dispc_set_parallel_interface_mode(dssdev->manager->id, + OMAP_DSS_PARALLELMODE_RFBI); - dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size); rfbi_configure(dssdev->phy.rfbi.channel, dssdev->ctrl.pixel_size, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index ee07a3cc22e..b64adf7dfc8 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -35,12 +35,16 @@ static struct { struct regulator *vdds_sdi_reg; } sdi; -static void sdi_basic_init(void) +static void sdi_basic_init(struct omap_dss_device *dssdev) + { - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_parallel_interface_mode(dssdev->manager->id, + OMAP_DSS_PARALLELMODE_BYPASS); + + dispc_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_tft_data_lines(24); + dispc_set_tft_data_lines(dssdev->manager->id, 24); dispc_lcd_enable_signal_polarity(1); } @@ -68,20 +72,20 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) if (!sdi.skip_init) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - sdi_basic_init(); + sdi_basic_init(dssdev); /* 15.5.9.1.2 */ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; - dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, - dssdev->panel.acb); + dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); if (!sdi.skip_init) { r = dss_calc_clock_div(1, t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); } else { r = dss_get_clock_div(&dss_cinfo); - r = dispc_get_clock_div(&dispc_cinfo); + r = dispc_get_clock_div(dssdev->manager->id, &dispc_cinfo); } if (r) @@ -102,13 +106,13 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_set_lcd_timings(t); + dispc_set_lcd_timings(dssdev->manager->id, t); r = dss_set_clock_div(&dss_cinfo); if (r) goto err2; - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) goto err2; diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 6a704f176c2..4fdab8e9c49 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -2132,8 +2132,9 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) char *str, *options, *this_opt; int r = 0; - str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL); - strcpy(str, def_mode); + str = kstrdup(def_mode, GFP_KERNEL); + if (!str) + return -ENOMEM; options = str; while (!r && (this_opt = strsep(&options, ",")) != NULL) { diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c new file mode 100644 index 00000000000..b81168df253 --- /dev/null +++ b/drivers/video/pxa3xx-gcu.c @@ -0,0 +1,772 @@ +/* + * pxa3xx-gc.c - Linux kernel module for PXA3xx graphics controllers + * + * This driver needs a DirectFB counterpart in user space, communication + * is handled via mmap()ed memory areas and an ioctl. + * + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (c) 2009 Janine Kropp <nin@directfb.org> + * Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * WARNING: This controller is attached to System Bus 2 of the PXA which + * needs its arbiter to be enabled explictly (CKENB & 1<<9). + * There is currently no way to do this from Linux, so you need to teach + * your bootloader for now. + */ + +#include <linux/module.h> +#include <linux/version.h> + +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> +#include <linux/ioctl.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/fs.h> +#include <linux/io.h> + +#include "pxa3xx-gcu.h" + +#define DRV_NAME "pxa3xx-gcu" +#define MISCDEV_MINOR 197 + +#define REG_GCCR 0x00 +#define GCCR_SYNC_CLR (1 << 9) +#define GCCR_BP_RST (1 << 8) +#define GCCR_ABORT (1 << 6) +#define GCCR_STOP (1 << 4) + +#define REG_GCISCR 0x04 +#define REG_GCIECR 0x08 +#define REG_GCRBBR 0x20 +#define REG_GCRBLR 0x24 +#define REG_GCRBHR 0x28 +#define REG_GCRBTR 0x2C +#define REG_GCRBEXHR 0x30 + +#define IE_EOB (1 << 0) +#define IE_EEOB (1 << 5) +#define IE_ALL 0xff + +#define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared)) + +/* #define PXA3XX_GCU_DEBUG */ +/* #define PXA3XX_GCU_DEBUG_TIMER */ + +#ifdef PXA3XX_GCU_DEBUG +#define QDUMP(msg) \ + do { \ + QPRINT(priv, KERN_DEBUG, msg); \ + } while (0) +#else +#define QDUMP(msg) do {} while (0) +#endif + +#define QERROR(msg) \ + do { \ + QPRINT(priv, KERN_ERR, msg); \ + } while (0) + +struct pxa3xx_gcu_batch { + struct pxa3xx_gcu_batch *next; + u32 *ptr; + dma_addr_t phys; + unsigned long length; +}; + +struct pxa3xx_gcu_priv { + void __iomem *mmio_base; + struct clk *clk; + struct pxa3xx_gcu_shared *shared; + dma_addr_t shared_phys; + struct resource *resource_mem; + struct miscdevice misc_dev; + struct file_operations misc_fops; + wait_queue_head_t wait_idle; + wait_queue_head_t wait_free; + spinlock_t spinlock; + struct timeval base_time; + + struct pxa3xx_gcu_batch *free; + + struct pxa3xx_gcu_batch *ready; + struct pxa3xx_gcu_batch *ready_last; + struct pxa3xx_gcu_batch *running; +}; + +static inline unsigned long +gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off) +{ + return __raw_readl(priv->mmio_base + off); +} + +static inline void +gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val) +{ + __raw_writel(val, priv->mmio_base + off); +} + +#define QPRINT(priv, level, msg) \ + do { \ + struct timeval tv; \ + struct pxa3xx_gcu_shared *shared = priv->shared; \ + u32 base = gc_readl(priv, REG_GCRBBR); \ + \ + do_gettimeofday(&tv); \ + \ + printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \ + "STATUS " \ + "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \ + "T %5ld)\n", \ + tv.tv_sec - priv->base_time.tv_sec, \ + tv.tv_usec / 1000, tv.tv_usec % 1000, \ + __func__, msg, \ + shared->hw_running ? "running" : " idle", \ + gc_readl(priv, REG_GCISCR), \ + gc_readl(priv, REG_GCRBBR), \ + gc_readl(priv, REG_GCRBLR), \ + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBTR) - base) / 4); \ + } while (0) + +static void +pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv) +{ + QDUMP("RESET"); + + /* disable interrupts */ + gc_writel(priv, REG_GCIECR, 0); + + /* reset hardware */ + gc_writel(priv, REG_GCCR, GCCR_ABORT); + gc_writel(priv, REG_GCCR, 0); + + memset(priv->shared, 0, SHARED_SIZE); + priv->shared->buffer_phys = priv->shared_phys; + priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC; + + do_gettimeofday(&priv->base_time); + + /* set up the ring buffer pointers */ + gc_writel(priv, REG_GCRBLR, 0); + gc_writel(priv, REG_GCRBBR, priv->shared_phys); + gc_writel(priv, REG_GCRBTR, priv->shared_phys); + + /* enable all IRQs except EOB */ + gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB); +} + +static void +dump_whole_state(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_shared *sh = priv->shared; + u32 base = gc_readl(priv, REG_GCRBBR); + + QDUMP("DUMP"); + + printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n" + "%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n", + sh->hw_running ? "running" : "idle ", + gc_readl(priv, REG_GCISCR), + gc_readl(priv, REG_GCRBBR), + gc_readl(priv, REG_GCRBLR), + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, + (gc_readl(priv, REG_GCRBHR) - base) / 4, + (gc_readl(priv, REG_GCRBTR) - base) / 4); +} + +static void +flush_running(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *running = priv->running; + struct pxa3xx_gcu_batch *next; + + while (running) { + next = running->next; + running->next = priv->free; + priv->free = running; + running = next; + } + + priv->running = NULL; +} + +static void +run_ready(struct pxa3xx_gcu_priv *priv) +{ + unsigned int num = 0; + struct pxa3xx_gcu_shared *shared = priv->shared; + struct pxa3xx_gcu_batch *ready = priv->ready; + + QDUMP("Start"); + + BUG_ON(!ready); + + shared->buffer[num++] = 0x05000000; + + while (ready) { + shared->buffer[num++] = 0x00000001; + shared->buffer[num++] = ready->phys; + ready = ready->next; + } + + shared->buffer[num++] = 0x05000000; + priv->running = priv->ready; + priv->ready = priv->ready_last = NULL; + gc_writel(priv, REG_GCRBLR, 0); + shared->hw_running = 1; + + /* ring base address */ + gc_writel(priv, REG_GCRBBR, shared->buffer_phys); + + /* ring tail address */ + gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4); + + /* ring length */ + gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4); +} + +static irqreturn_t +pxa3xx_gcu_handle_irq(int irq, void *ctx) +{ + struct pxa3xx_gcu_priv *priv = ctx; + struct pxa3xx_gcu_shared *shared = priv->shared; + u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL; + + QDUMP("-Interrupt"); + + if (!status) + return IRQ_NONE; + + spin_lock(&priv->spinlock); + shared->num_interrupts++; + + if (status & IE_EEOB) { + QDUMP(" [EEOB]"); + + flush_running(priv); + wake_up_all(&priv->wait_free); + + if (priv->ready) { + run_ready(priv); + } else { + /* There is no more data prepared by the userspace. + * Set hw_running = 0 and wait for the next userspace + * kick-off */ + shared->num_idle++; + shared->hw_running = 0; + + QDUMP(" '-> Idle."); + + /* set ring buffer length to zero */ + gc_writel(priv, REG_GCRBLR, 0); + + wake_up_all(&priv->wait_idle); + } + + shared->num_done++; + } else { + QERROR(" [???]"); + dump_whole_state(priv); + } + + /* Clear the interrupt */ + gc_writel(priv, REG_GCISCR, status); + spin_unlock(&priv->spinlock); + + return IRQ_HANDLED; +} + +static int +pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for idle..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_idle++; + + while (priv->shared->hw_running) { + int num = priv->shared->num_interrupts; + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_idle, + !priv->shared->hw_running, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && + priv->shared->num_interrupts == num) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +static int +pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for free..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_free++; + + while (!priv->free) { + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_free, + priv->free, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +/* Misc device layer */ + +static ssize_t +pxa3xx_gcu_misc_write(struct file *filp, const char *buff, + size_t count, loff_t *offp) +{ + int ret; + unsigned long flags; + struct pxa3xx_gcu_batch *buffer; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + int words = count / 4; + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_writes++; + + priv->shared->num_words += words; + + /* Last word reserved for batch buffer end command */ + if (words >= PXA3XX_GCU_BATCH_WORDS) + return -E2BIG; + + /* Wait for a free buffer */ + if (!priv->free) { + ret = pxa3xx_gcu_wait_free(priv); + if (ret < 0) + return ret; + } + + /* + * Get buffer from free list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer = priv->free; + priv->free = buffer->next; + + spin_unlock_irqrestore(&priv->spinlock, flags); + + + /* Copy data from user into buffer */ + ret = copy_from_user(buffer->ptr, buff, words * 4); + if (ret) { + spin_lock_irqsave(&priv->spinlock, flags); + buffer->next = priv->free; + priv->free = buffer; + spin_unlock_irqrestore(&priv->spinlock, flags); + return ret; + } + + buffer->length = words; + + /* Append batch buffer end command */ + buffer->ptr[words] = 0x01000000; + + /* + * Add buffer to ready list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer->next = NULL; + + if (priv->ready) { + BUG_ON(priv->ready_last == NULL); + + priv->ready_last->next = buffer; + } else + priv->ready = buffer; + + priv->ready_last = buffer; + + if (!priv->shared->hw_running) + run_ready(priv); + + spin_unlock_irqrestore(&priv->spinlock, flags); + + return words * 4; +} + + +static long +pxa3xx_gcu_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (cmd) { + case PXA3XX_GCU_IOCTL_RESET: + spin_lock_irqsave(&priv->spinlock, flags); + pxa3xx_gcu_reset(priv); + spin_unlock_irqrestore(&priv->spinlock, flags); + return 0; + + case PXA3XX_GCU_IOCTL_WAIT_IDLE: + return pxa3xx_gcu_wait_idle(priv); + } + + return -ENOSYS; +} + +static int +pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned int size = vma->vm_end - vma->vm_start; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (vma->vm_pgoff) { + case 0: + /* hand out the shared data area */ + if (size != SHARED_SIZE) + return -EINVAL; + + return dma_mmap_coherent(NULL, vma, + priv->shared, priv->shared_phys, size); + + case SHARED_SIZE >> PAGE_SHIFT: + /* hand out the MMIO base for direct register access + * from userspace */ + if (size != resource_size(priv->resource_mem)) + return -EINVAL; + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return io_remap_pfn_range(vma, vma->vm_start, + priv->resource_mem->start >> PAGE_SHIFT, + size, vma->vm_page_prot); + } + + return -EINVAL; +} + + +#ifdef PXA3XX_GCU_DEBUG_TIMER +static struct timer_list pxa3xx_gcu_debug_timer; + +static void pxa3xx_gcu_debug_timedout(unsigned long ptr) +{ + struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; + + QERROR("Timer DUMP"); + + /* init the timer structure */ + init_timer(&pxa3xx_gcu_debug_timer); + pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; + pxa3xx_gcu_debug_timer.data = ptr; + pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ + + add_timer(&pxa3xx_gcu_debug_timer); +} + +static void pxa3xx_gcu_init_debug_timer(void) +{ + pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); +} +#else +static inline void pxa3xx_gcu_init_debug_timer(void) {} +#endif + +static int +add_buffer(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *buffer; + + buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + &buffer->phys, GFP_KERNEL); + if (!buffer->ptr) { + kfree(buffer); + return -ENOMEM; + } + + buffer->next = priv->free; + + priv->free = buffer; + + return 0; +} + +static void +free_buffers(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *next, *buffer = priv->free; + + while (buffer) { + next = buffer->next; + + dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + buffer->ptr, buffer->phys); + + kfree(buffer); + + buffer = next; + } + + priv->free = NULL; +} + +static int __devinit +pxa3xx_gcu_probe(struct platform_device *dev) +{ + int i, ret, irq; + struct resource *r; + struct pxa3xx_gcu_priv *priv; + + priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (i = 0; i < 8; i++) { + ret = add_buffer(dev, priv); + if (ret) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + goto err_free_priv; + } + } + + init_waitqueue_head(&priv->wait_idle); + init_waitqueue_head(&priv->wait_free); + spin_lock_init(&priv->spinlock); + + /* we allocate the misc device structure as part of our own allocation, + * so we can get a pointer to our priv structure later on with + * container_of(). This isn't really necessary as we have a fixed minor + * number anyway, but this is to avoid statics. */ + + priv->misc_fops.owner = THIS_MODULE; + priv->misc_fops.write = pxa3xx_gcu_misc_write; + priv->misc_fops.unlocked_ioctl = pxa3xx_gcu_misc_ioctl; + priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap; + + priv->misc_dev.minor = MISCDEV_MINOR, + priv->misc_dev.name = DRV_NAME, + priv->misc_dev.fops = &priv->misc_fops, + + /* register misc device */ + ret = misc_register(&priv->misc_dev); + if (ret < 0) { + dev_err(&dev->dev, "misc_register() for minor %d failed\n", + MISCDEV_MINOR); + goto err_free_priv; + } + + /* handle IO resources */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto err_misc_deregister; + } + + if (!request_mem_region(r->start, resource_size(r), dev->name)) { + dev_err(&dev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto err_misc_deregister; + } + + priv->mmio_base = ioremap_nocache(r->start, resource_size(r)); + if (!priv->mmio_base) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto err_free_mem_region; + } + + /* allocate dma memory */ + priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE, + &priv->shared_phys, GFP_KERNEL); + + if (!priv->shared) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + ret = -ENOMEM; + goto err_free_io; + } + + /* enable the clock */ + priv->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&dev->dev, "failed to get clock\n"); + ret = -ENODEV; + goto err_free_dma; + } + + ret = clk_enable(priv->clk); + if (ret < 0) { + dev_err(&dev->dev, "failed to enable clock\n"); + goto err_put_clk; + } + + /* request the IRQ */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto err_put_clk; + } + + ret = request_irq(irq, pxa3xx_gcu_handle_irq, + IRQF_DISABLED, DRV_NAME, priv); + if (ret) { + dev_err(&dev->dev, "request_irq failed\n"); + ret = -EBUSY; + goto err_put_clk; + } + + platform_set_drvdata(dev, priv); + priv->resource_mem = r; + pxa3xx_gcu_reset(priv); + pxa3xx_gcu_init_debug_timer(); + + dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", + (void *) r->start, (void *) priv->shared_phys, + SHARED_SIZE, irq); + return 0; + +err_put_clk: + clk_disable(priv->clk); + clk_put(priv->clk); + +err_free_dma: + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + +err_free_io: + iounmap(priv->mmio_base); + +err_free_mem_region: + release_mem_region(r->start, resource_size(r)); + +err_misc_deregister: + misc_deregister(&priv->misc_dev); + +err_free_priv: + platform_set_drvdata(dev, NULL); + free_buffers(dev, priv); + kfree(priv); + return ret; +} + +static int __devexit +pxa3xx_gcu_remove(struct platform_device *dev) +{ + struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev); + struct resource *r = priv->resource_mem; + + pxa3xx_gcu_wait_idle(priv); + + misc_deregister(&priv->misc_dev); + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + iounmap(priv->mmio_base); + release_mem_region(r->start, resource_size(r)); + platform_set_drvdata(dev, NULL); + clk_disable(priv->clk); + free_buffers(dev, priv); + kfree(priv); + + return 0; +} + +static struct platform_driver pxa3xx_gcu_driver = { + .probe = pxa3xx_gcu_probe, + .remove = __devexit_p(pxa3xx_gcu_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init +pxa3xx_gcu_init(void) +{ + return platform_driver_register(&pxa3xx_gcu_driver); +} + +static void __exit +pxa3xx_gcu_exit(void) +{ + platform_driver_unregister(&pxa3xx_gcu_driver); +} + +module_init(pxa3xx_gcu_init); +module_exit(pxa3xx_gcu_exit); + +MODULE_DESCRIPTION("PXA3xx graphics controller unit driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(MISCDEV_MINOR); +MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, " + "Denis Oliver Kropp <dok@directfb.org>, " + "Daniel Mack <daniel@caiaq.de>"); diff --git a/drivers/video/pxa3xx-gcu.h b/drivers/video/pxa3xx-gcu.h new file mode 100644 index 00000000000..0428ed03dc4 --- /dev/null +++ b/drivers/video/pxa3xx-gcu.h @@ -0,0 +1,38 @@ +#ifndef __PXA3XX_GCU_H__ +#define __PXA3XX_GCU_H__ + +#include <linux/types.h> + +/* Number of 32bit words in display list (ring buffer). */ +#define PXA3XX_GCU_BUFFER_WORDS ((256 * 1024 - 256) / 4) + +/* To be increased when breaking the ABI */ +#define PXA3XX_GCU_SHARED_MAGIC 0x30000001 + +#define PXA3XX_GCU_BATCH_WORDS 8192 + +struct pxa3xx_gcu_shared { + u32 buffer[PXA3XX_GCU_BUFFER_WORDS]; + + bool hw_running; + + unsigned long buffer_phys; + + unsigned int num_words; + unsigned int num_writes; + unsigned int num_done; + unsigned int num_interrupts; + unsigned int num_wait_idle; + unsigned int num_wait_free; + unsigned int num_idle; + + u32 magic; +}; + +/* Initialization and synchronization. + * Hardware is started upon write(). */ +#define PXA3XX_GCU_IOCTL_RESET _IO('G', 0) +#define PXA3XX_GCU_IOCTL_WAIT_IDLE _IO('G', 2) + +#endif /* __PXA3XX_GCU_H__ */ + diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 618f36bec10..da388186d61 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -331,7 +331,7 @@ static int riva_bl_get_brightness(struct backlight_device *bd) return bd->props.brightness; } -static struct backlight_ops riva_bl_ops = { +static const struct backlight_ops riva_bl_ops = { .get_brightness = riva_bl_get_brightness, .update_status = riva_bl_update_status, }; diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c index a6247fc081a..28b1c6c3d8a 100644 --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c @@ -410,28 +410,6 @@ s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ************************************************************/ /** - * bltbit_wait_bitset - waits for change in register value - * @info : framebuffer structure - * @bit : value expected in register - * @timeout : ... - * - * waits until value changes INTO bit - */ -static u8 -bltbit_wait_bitset(struct fb_info *info, u8 bit, int timeout) -{ - while (!(s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit)) { - udelay(10); - if (!--timeout) { - dbg_blit("wait_bitset timeout\n"); - break; - } - } - - return timeout; -} - -/** * bltbit_wait_bitclear - waits for change in register value * @info : frambuffer structure * @bit : value currently in register @@ -454,34 +432,6 @@ bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout) return timeout; } -/** - * bltbit_fifo_status - checks the current status of the fifo - * @info : framebuffer structure - * - * returns number of free words in buffer - */ -static u8 -bltbit_fifo_status(struct fb_info *info) -{ - u8 status; - - status = s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0); - - /* its empty so room for 16 words */ - if (status & BBLT_FIFO_EMPTY) - return 16; - - /* its full so we dont want to add */ - if (status & BBLT_FIFO_FULL) - return 0; - - /* its atleast half full but we can add one atleast */ - if (status & BBLT_FIFO_NOT_FULL) - return 1; - - return 0; -} - /* * s1d13xxxfb_bitblt_copyarea - accelerated copyarea function * @info : framebuffer structure diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f9aca9d13d1..83ce9a04d87 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <mach/map.h> #include <plat/regs-fb-v4.h> @@ -1013,8 +1014,30 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, return ret; } +static int s3c_fb_open(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + pm_runtime_get_sync(sfb->dev); + + return 0; +} + +static int s3c_fb_release(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + pm_runtime_put_sync(sfb->dev); + + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, + .fb_open = s3c_fb_open, + .fb_release = s3c_fb_release, .fb_check_var = s3c_fb_check_var, .fb_set_par = s3c_fb_set_par, .fb_blank = s3c_fb_blank, @@ -1322,6 +1345,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) clk_enable(sfb->bus_clk); + pm_runtime_enable(sfb->dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "failed to find registers\n"); @@ -1360,6 +1385,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); + platform_set_drvdata(pdev, sfb); + pm_runtime_get_sync(sfb->dev); + /* setup gpio and output polarity controls */ pd->setup_gpio(); @@ -1400,6 +1428,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, sfb); + pm_runtime_put_sync(sfb->dev); return 0; @@ -1434,6 +1463,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) struct s3c_fb *sfb = platform_get_drvdata(pdev); int win; + pm_runtime_get_sync(sfb->dev); + for (win = 0; win < S3C_FB_MAX_WIN; win++) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); @@ -1450,12 +1481,74 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) kfree(sfb); + pm_runtime_put_sync(sfb->dev); + pm_runtime_disable(sfb->dev); + return 0; } #ifdef CONFIG_PM -static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) +static int s3c_fb_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_win *win; + int win_no; + + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { + win = sfb->windows[win_no]; + if (!win) + continue; + + /* use the blank function to push into power-down */ + s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); + } + + clk_disable(sfb->bus_clk); + return 0; +} + +static int s3c_fb_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_platdata *pd = sfb->pdata; + struct s3c_fb_win *win; + int win_no; + + clk_enable(sfb->bus_clk); + + /* setup registers */ + writel(pd->vidcon1, sfb->regs + VIDCON1); + + /* zero all windows before we do anything */ + for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) + s3c_fb_clear_win(sfb, win_no); + + for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win_no * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + + /* restore framebuffers */ + for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { + win = sfb->windows[win_no]; + if (!win) + continue; + + dev_dbg(&pdev->dev, "resuming window %d\n", win_no); + s3c_fb_set_par(win->fbinfo); + } + + return 0; +} + +int s3c_fb_runtime_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); struct s3c_fb_win *win; int win_no; @@ -1473,8 +1566,9 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int s3c_fb_resume(struct platform_device *pdev) +int s3c_fb_runtime_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; @@ -1509,9 +1603,12 @@ static int s3c_fb_resume(struct platform_device *pdev) return 0; } + #else #define s3c_fb_suspend NULL #define s3c_fb_resume NULL +#define s3c_fb_runtime_suspend NULL +#define s3c_fb_runtime_resume NULL #endif @@ -1710,15 +1807,21 @@ static struct platform_device_id s3c_fb_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); +static const struct dev_pm_ops s3cfb_pm_ops = { + .suspend = s3c_fb_suspend, + .resume = s3c_fb_resume, + .runtime_suspend = s3c_fb_runtime_suspend, + .runtime_resume = s3c_fb_runtime_resume, +}; + static struct platform_driver s3c_fb_driver = { .probe = s3c_fb_probe, .remove = __devexit_p(s3c_fb_remove), - .suspend = s3c_fb_suspend, - .resume = s3c_fb_resume, .id_table = s3c_fb_driver_ids, .driver = { .name = "s3c-fb", .owner = THIS_MODULE, + .pm = &s3cfb_pm_ops, }, }; diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 46b430978bc..61c819e35f7 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> @@ -918,9 +919,9 @@ static int __devinit s3c24xxfb_probe(struct platform_device *pdev, } info->clk = clk_get(NULL, "lcd"); - if (!info->clk || IS_ERR(info->clk)) { + if (IS_ERR(info->clk)) { printk(KERN_ERR "failed to get lcd clock source\n"); - ret = -ENOENT; + ret = PTR_ERR(info->clk); goto release_irq; } diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 3f3d431033c..24640c8458a 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -21,18 +22,40 @@ #include <video/sh_mipi_dsi.h> #include <video/sh_mobile_lcdc.h> -#define CMTSRTCTR 0x80d0 -#define CMTSRTREQ 0x8070 - +#define SYSCTRL 0x0000 +#define SYSCONF 0x0004 +#define TIMSET 0x0008 +#define RESREQSET0 0x0018 +#define RESREQSET1 0x001c +#define HSTTOVSET 0x0020 +#define LPRTOVSET 0x0024 +#define TATOVSET 0x0028 +#define PRTOVSET 0x002c +#define DSICTRL 0x0030 #define DSIINTE 0x0060 +#define PHYCTRL 0x0070 + +/* relative to linkbase */ +#define DTCTR 0x0000 +#define VMCTR1 0x0020 +#define VMCTR2 0x0024 +#define VMLEN1 0x0028 +#define CMTSRTREQ 0x0070 +#define CMTSRTCTR 0x00d0 /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ #define MAX_SH_MIPI_DSI 2 struct sh_mipi { void __iomem *base; + void __iomem *linkbase; struct clk *dsit_clk; struct clk *dsip_clk; + struct device *dev; + + void *next_board_data; + void (*next_display_on)(void *board_data, struct fb_info *info); + void (*next_display_off)(void *board_data); }; static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; @@ -55,10 +78,10 @@ static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, int cnt = 100; /* transmit a short packet to LCD panel */ - iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ - iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ + iowrite32(1 | data, mipi->linkbase + CMTSRTCTR); + iowrite32(1, mipi->linkbase + CMTSRTREQ); - while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) + while ((ioread32(mipi->linkbase + CMTSRTREQ) & 1) && --cnt) udelay(1); return cnt ? 0 : -ETIMEDOUT; @@ -90,7 +113,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) * enable LCDC data tx, transition to LPS after completion of each HS * packet */ - iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ + iowrite32(0x00000002 | enable, mipi->linkbase + DTCTR); } static void sh_mipi_shutdown(struct platform_device *pdev) @@ -104,14 +127,22 @@ static void mipi_display_on(void *arg, struct fb_info *info) { struct sh_mipi *mipi = arg; + pm_runtime_get_sync(mipi->dev); sh_mipi_dsi_enable(mipi, true); + + if (mipi->next_display_on) + mipi->next_display_on(mipi->next_board_data, info); } static void mipi_display_off(void *arg) { struct sh_mipi *mipi = arg; + if (mipi->next_display_off) + mipi->next_display_off(mipi->next_board_data); + sh_mipi_dsi_enable(mipi, false); + pm_runtime_put(mipi->dev); } static int __init sh_mipi_setup(struct sh_mipi *mipi, @@ -119,8 +150,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, { void __iomem *base = mipi->base; struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; - u32 pctype, datatype, pixfmt; - u32 linelength; + u32 pctype, datatype, pixfmt, linelength, vmctr2 = 0x00e00000; bool yuv; /* @@ -223,10 +253,10 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, return -EINVAL; /* reset DSI link */ - iowrite32(0x00000001, base); /* SYSCTRL */ + iowrite32(0x00000001, base + SYSCTRL); /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ udelay(50); - iowrite32(0x00000000, base); /* SYSCTRL */ + iowrite32(0x00000000, base + SYSCTRL); /* setup DSI link */ @@ -238,7 +268,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * ECC check enable * additionally enable first two lanes */ - iowrite32(0x00003703, base + 0x04); /* SYSCONF */ + iowrite32(0x00003703, base + SYSCONF); /* * T_wakeup = 0x7000 * T_hs-trail = 3 @@ -246,28 +276,28 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * T_clk-trail = 3 * T_clk-prepare = 2 */ - iowrite32(0x70003332, base + 0x08); /* TIMSET */ + iowrite32(0x70003332, base + TIMSET); /* no responses requested */ - iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ + iowrite32(0x00000000, base + RESREQSET0); /* request response to packets of type 0x28 */ - iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ + iowrite32(0x00000100, base + RESREQSET1); /* High-speed transmission timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ + iowrite32(0x0fffffff, base + HSTTOVSET); /* LP reception timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ + iowrite32(0x0fffffff, base + LPRTOVSET); /* Turn-around timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ + iowrite32(0x0fffffff, base + TATOVSET); /* Peripheral reset timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ + iowrite32(0x0fffffff, base + PRTOVSET); /* Enable timeout counters */ - iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ + iowrite32(0x00000f00, base + DSICTRL); /* Interrupts not used, disable all */ iowrite32(0, base + DSIINTE); /* DSI-Tx bias on */ - iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ + iowrite32(0x00000001, base + PHYCTRL); udelay(200); /* Deassert resets, power on, set multiplier */ - iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ + iowrite32(0x03070b01, base + PHYCTRL); /* setup l-bridge */ @@ -275,20 +305,28 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * Enable transmission of all packets, * transmit LPS after each HS packet completion */ - iowrite32(0x00000006, base + 0x8000); /* DTCTR */ + iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ + iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | + (pdata->clksrc << 16) | (pctype << 12) | datatype, + mipi->linkbase + VMCTR1); + /* * Non-burst mode with sync pulses: VSE and HSE are output, * HSA period allowed, no commands in LP */ - iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ + if (pdata->flags & SH_MIPI_DSI_HSABM) + vmctr2 |= 0x20; + if (pdata->flags & SH_MIPI_DSI_HSPBM) + vmctr2 |= 0x10; + iowrite32(vmctr2, mipi->linkbase + VMCTR2); + /* * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default - * (unused, since VMCTR2[HSABM] = 0) + * (unused if VMCTR2[HSABM] = 0) */ - iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ + iowrite32(1 | (linelength << 16), mipi->linkbase + VMLEN1); msleep(5); @@ -321,11 +359,12 @@ static int __init sh_mipi_probe(struct platform_device *pdev) struct sh_mipi *mipi; struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); unsigned long rate, f_current; int idx = pdev->id, ret; char dsip_clk[] = "dsi.p_clk"; - if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) + if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) return -ENODEV; mutex_lock(&array_lock); @@ -356,6 +395,20 @@ static int __init sh_mipi_probe(struct platform_device *pdev) goto emap; } + if (!request_mem_region(res2->start, resource_size(res2), pdev->name)) { + dev_err(&pdev->dev, "MIPI register region 2 already claimed\n"); + ret = -EBUSY; + goto ereqreg2; + } + + mipi->linkbase = ioremap(res2->start, resource_size(res2)); + if (!mipi->linkbase) { + ret = -ENOMEM; + goto emap2; + } + + mipi->dev = &pdev->dev; + mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); if (IS_ERR(mipi->dsit_clk)) { ret = PTR_ERR(mipi->dsit_clk); @@ -405,6 +458,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev) mipi_dsi[idx] = mipi; + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + ret = sh_mipi_setup(mipi, pdata); if (ret < 0) goto emipisetup; @@ -412,15 +468,22 @@ static int __init sh_mipi_probe(struct platform_device *pdev) mutex_unlock(&array_lock); platform_set_drvdata(pdev, mipi); + /* Save original LCDC callbacks */ + mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; + mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; + mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; + /* Set up LCDC callbacks */ pdata->lcd_chan->board_cfg.board_data = mipi; pdata->lcd_chan->board_cfg.display_on = mipi_display_on; pdata->lcd_chan->board_cfg.display_off = mipi_display_off; + pdata->lcd_chan->board_cfg.owner = THIS_MODULE; return 0; emipisetup: mipi_dsi[idx] = NULL; + pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsip_clk); eclkpon: clk_disable(mipi->dsit_clk); @@ -431,6 +494,10 @@ eclkpget: esettrate: clk_put(mipi->dsit_clk); eclktget: + iounmap(mipi->linkbase); +emap2: + release_mem_region(res2->start, resource_size(res2)); +ereqreg2: iounmap(mipi->base); emap: release_mem_region(res->start, resource_size(res)); @@ -447,6 +514,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) { struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); struct sh_mipi *mipi = platform_get_drvdata(pdev); int i, ret; @@ -467,14 +535,19 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) if (ret < 0) return ret; + pdata->lcd_chan->board_cfg.owner = NULL; pdata->lcd_chan->board_cfg.display_on = NULL; pdata->lcd_chan->board_cfg.display_off = NULL; pdata->lcd_chan->board_cfg.board_data = NULL; + pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsip_clk); clk_disable(mipi->dsit_clk); clk_put(mipi->dsit_clk); clk_put(mipi->dsip_clk); + iounmap(mipi->linkbase); + if (res2) + release_mem_region(res2->start, resource_size(res2)); iounmap(mipi->base); if (res) release_mem_region(res->start, resource_size(res)); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index fcda0e97011..74d9f546a2e 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> @@ -209,7 +210,11 @@ enum hotplug_state { struct sh_hdmi { void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ - bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ + u8 preprogrammed_vic; /* use a pre-programmed VIC or + the external mode */ + u8 edid_block_addr; + u8 edid_segment_nr; + u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; struct fb_info *info; @@ -217,6 +222,7 @@ struct sh_hdmi { struct delayed_work edid_work; struct fb_var_screeninfo var; struct fb_monspecs monspec; + struct notifier_block notifier; }; static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) @@ -342,7 +348,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ - if (!hdmi->preprogrammed_mode) + if (!hdmi->preprogrammed_vic) hdmi_write(hdmi, sync | 1 | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); } @@ -466,7 +472,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) { - if (hdmi->var.yres > 480) { + if (hdmi->var.pixclock < 10000) { + /* for 1080p8bit 148MHz */ + hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); + hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); + hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); + hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); + hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); + hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); + hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); + } else if (hdmi->var.pixclock < 30000) { /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ /* * [1:0] Speed_A @@ -565,13 +582,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); /* - * VIC = 1280 x 720p: ignored if external config is used - * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode + * VIC should be ignored if external config is used, so, we could just use 0, + * but play safe and use a valid value in any case just in case */ - if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920) - vic = 16; - else if (hdmi->var.yres == 480 && hdmi->var.xres == 720) - vic = 2; + if (hdmi->preprogrammed_vic) + vic = hdmi->preprogrammed_vic; else vic = 4; hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); @@ -685,11 +700,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, - const struct fb_videomode *mode) + const struct fb_videomode *mode, + unsigned long *hdmi_rate, unsigned long *parent_rate) { - long target = PICOS2KHZ(mode->pixclock) * 1000, - rate = clk_round_rate(hdmi->hdmi_clk, target); - unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; + unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); + if ((long)*hdmi_rate < 0) + *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); + + rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; + if (rate_error && pdata->clk_optimize_parent) + rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); + else if (clk_get_parent(hdmi->hdmi_clk)) + *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", mode->left_margin, mode->xres, @@ -697,14 +722,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, mode->upper_margin, mode->yres, mode->lower_margin, mode->vsync_len); - dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, - rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, - mode->refresh); + dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, + rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, + mode->refresh, *parent_rate); return rate_error; } -static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) +static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, + unsigned long *parent_rate) { struct fb_var_screeninfo tmpvar; struct fb_var_screeninfo *var = &tmpvar; @@ -713,7 +739,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) struct fb_modelist *modelist = NULL; unsigned int f_width = 0, f_height = 0, f_refresh = 0; unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ - bool exact_match = false; + bool scanning = false, preferred_bad = false; u8 edid[128]; char *forced; int i; @@ -735,7 +761,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) printk(KERN_CONT "\n"); #endif - fb_edid_to_monspecs(edid, &hdmi->monspec); + if (!hdmi->edid_blocks) { + fb_edid_to_monspecs(edid, &hdmi->monspec); + hdmi->edid_blocks = edid[126] + 1; + + dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", + hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); + } else { + dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", + edid[0], edid[2]); + fb_edid_add_monspecs(edid, &hdmi->monspec); + } + + if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + + (hdmi->edid_block_addr >> 7) + 1) { + /* More blocks to read */ + if (hdmi->edid_block_addr) { + hdmi->edid_block_addr = 0; + hdmi->edid_segment_nr++; + } else { + hdmi->edid_block_addr = 0x80; + } + /* Set EDID word address */ + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); + /* Enable EDID interrupt */ + hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); + return -EAGAIN; + } + + /* All E-EDID blocks ready */ + dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); fb_get_options("sh_mobile_lcdc", &forced); if (forced && *forced) { @@ -745,6 +802,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (i < 2) { f_width = 0; f_height = 0; + } else { + /* The user wants us to use the EDID data */ + scanning = true; } dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", f_width, f_height, f_refresh); @@ -752,34 +812,56 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) /* Walk monitor modes to find the best or the exact match */ for (i = 0, mode = hdmi->monspec.modedb; - f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; + i < hdmi->monspec.modedb_len && scanning; i++, mode++) { - unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); + unsigned long rate_error; - /* No interest in unmatching modes */ - if (f_width != mode->xres || f_height != mode->yres) - continue; - if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) - /* - * Exact match if either the refresh rate matches or it - * hasn't been specified and we've found a mode, for - * which we can configure the clock precisely - */ - exact_match = true; - else if (found && found_rate_error <= rate_error) + if (!f_width && !f_height) { /* - * We otherwise search for the closest matching clock - * rate - either if no refresh rate has been specified - * or we cannot find an exactly matching one + * A parameter string "video=sh_mobile_lcdc:0x0" means + * use the preferred EDID mode. If it is rejected by + * .fb_check_var(), keep looking, until an acceptable + * one is found. */ + if ((mode->flag & FB_MODE_IS_FIRST) || preferred_bad) + scanning = false; + else + continue; + } else if (f_width != mode->xres || f_height != mode->yres) { + /* No interest in unmatching modes */ continue; + } + + rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); + + if (scanning) { + if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) + /* + * Exact match if either the refresh rate + * matches or it hasn't been specified and we've + * found a mode, for which we can configure the + * clock precisely + */ + scanning = false; + else if (found && found_rate_error <= rate_error) + /* + * We otherwise search for the closest matching + * clock rate - either if no refresh rate has + * been specified or we cannot find an exactly + * matching one + */ + continue; + } /* Check if supported: sufficient fb memory, supported clock-rate */ fb_videomode_to_var(var, mode); + var->bits_per_pixel = info->var.bits_per_pixel; + if (info && info->fbops->fb_check_var && info->fbops->fb_check_var(var, info)) { - exact_match = false; + scanning = true; + preferred_bad = true; continue; } @@ -797,15 +879,15 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) * driver, and passing ->info with HDMI platform data. */ if (info && !found) { - modelist = hdmi->info->modelist.next && - !list_empty(&hdmi->info->modelist) ? - list_entry(hdmi->info->modelist.next, + modelist = info->modelist.next && + !list_empty(&info->modelist) ? + list_entry(info->modelist.next, struct fb_modelist, list) : NULL; if (modelist) { found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found); + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); } } @@ -813,16 +895,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (!found) return -ENXIO; - dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", found->xres, found->yres, - found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); - - if ((found->xres == 720 && found->yres == 480) || - (found->xres == 1280 && found->yres == 720) || - (found->xres == 1920 && found->yres == 1080)) - hdmi->preprogrammed_mode = true; + if (found->xres == 640 && found->yres == 480 && found->refresh == 60) + hdmi->preprogrammed_vic = 1; + else if (found->xres == 720 && found->yres == 480 && found->refresh == 60) + hdmi->preprogrammed_vic = 2; + else if (found->xres == 720 && found->yres == 576 && found->refresh == 50) + hdmi->preprogrammed_vic = 17; + else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60) + hdmi->preprogrammed_vic = 4; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24) + hdmi->preprogrammed_vic = 32; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50) + hdmi->preprogrammed_vic = 31; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60) + hdmi->preprogrammed_vic = 16; else - hdmi->preprogrammed_mode = false; + hdmi->preprogrammed_vic = 0; + + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", + modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", + found->xres, found->yres, found->refresh, + PICOS2KHZ(found->pixclock) * 1000, found_rate_error); fb_videomode_to_var(&hdmi->var, found); sh_hdmi_external_video_param(hdmi); @@ -871,32 +964,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) /* Check, if hot plug & MSENS pin status are both high */ if ((msens & 0xC0) == 0xC0) { /* Display plug in */ + hdmi->edid_segment_nr = 0; + hdmi->edid_block_addr = 0; + hdmi->edid_blocks = 0; hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; /* Set EDID word address */ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); - /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); /* Enable EDID interrupt */ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); } else if (!(status1 & 0x80)) { /* Display unplug, beware multiple interrupts */ - if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) + if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; schedule_delayed_work(&hdmi->edid_work, 0); - - hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + } /* display_off will switch back to mode_a */ } } else if (status1 & 2) { /* EDID error interrupt: retry */ /* Set EDID word address */ - hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); } else if (status1 & 4) { /* Disable EDID interrupt */ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); - hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); } @@ -979,39 +1074,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock - * @hdmi: driver context - * @pixclock: pixel clock period in picoseconds - * return: configured positive rate if successful - * 0 if couldn't set the rate, but managed to enable the clock - * negative error, if couldn't enable the clock + * @hdmi: driver context + * @hdmi_rate: HDMI clock frequency in Hz + * @parent_rate: if != 0 - set parent clock rate for optimal precision + * return: configured positive rate if successful + * 0 if couldn't set the rate, but managed to enable the + * clock, negative error, if couldn't enable the clock */ -static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) +static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, + unsigned long parent_rate) { - long rate; int ret; - rate = PICOS2KHZ(pixclock) * 1000; - rate = clk_round_rate(hdmi->hdmi_clk, rate); - if (rate > 0) { - ret = clk_set_rate(hdmi->hdmi_clk, rate); + if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { + ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); if (ret < 0) { - dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); - rate = 0; + dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); + hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); } else { - dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); + dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); } - } else { - rate = 0; - dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); } - ret = clk_enable(hdmi->hdmi_clk); + ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); if (ret < 0) { - dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); - return ret; + dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); + hdmi_rate = 0; + } else { + dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); } - return rate; + return hdmi_rate; } /* Hotplug interrupt occurred, read EDID */ @@ -1030,17 +1123,21 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) mutex_lock(&hdmi->mutex); - if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { + struct fb_info *info = hdmi->info; + unsigned long parent_rate = 0, hdmi_rate; + /* A device has been plugged in */ pm_runtime_get_sync(hdmi->dev); - ret = sh_hdmi_read_edid(hdmi); + ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); if (ret < 0) goto out; + hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; + /* Reconfigure the clock */ - clk_disable(hdmi->hdmi_clk); - ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); + ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); if (ret < 0) goto out; @@ -1049,22 +1146,21 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) /* Switched to another (d) power-save mode */ msleep(10); - if (!hdmi->info) + if (!info) goto out; - ch = hdmi->info->par; + ch = info->par; acquire_console_sem(); /* HDMI plug in */ if (!sh_hdmi_must_reconfigure(hdmi) && - hdmi->info->state == FBINFO_STATE_RUNNING) { + info->state == FBINFO_STATE_RUNNING) { /* * First activation with the default monitor - just turn * on, if we run a resume here, the logo disappears */ - if (lock_fb_info(hdmi->info)) { - struct fb_info *info = hdmi->info; + if (lock_fb_info(info)) { info->var.width = hdmi->var.width; info->var.height = hdmi->var.height; sh_hdmi_display_on(hdmi, info); @@ -1072,7 +1168,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) } } else { /* New monitor or have to wake up */ - fb_set_suspend(hdmi->info, 0); + fb_set_suspend(info, 0); } release_console_sem(); @@ -1095,7 +1191,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) } out: - if (ret < 0) + if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; mutex_unlock(&hdmi->mutex); @@ -1103,13 +1199,6 @@ out: } static int sh_hdmi_notify(struct notifier_block *nb, - unsigned long action, void *data); - -static struct notifier_block sh_hdmi_notifier = { - .notifier_call = sh_hdmi_notify, -}; - -static int sh_hdmi_notify(struct notifier_block *nb, unsigned long action, void *data) { struct fb_event *event = data; @@ -1118,7 +1207,7 @@ static int sh_hdmi_notify(struct notifier_block *nb, struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; struct sh_hdmi *hdmi = board_cfg->board_data; - if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info) + if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) return NOTIFY_DONE; switch(action) { @@ -1137,11 +1226,11 @@ static int sh_hdmi_notify(struct notifier_block *nb, * temporarily, synchronise with the work queue and re-acquire * the info->lock. */ - unlock_fb_info(hdmi->info); + unlock_fb_info(info); mutex_lock(&hdmi->mutex); hdmi->info = NULL; mutex_unlock(&hdmi->mutex); - lock_fb_info(hdmi->info); + lock_fb_info(info); return NOTIFY_OK; } return NOTIFY_DONE; @@ -1176,13 +1265,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } - /* Some arbitrary relaxed pixclock just to get things started */ - rate = sh_hdmi_clk_configure(hdmi, 37037); + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ + rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); + if (rate > 0) + rate = sh_hdmi_clk_configure(hdmi, rate, 0); + if (rate < 0) { ret = rate; goto erate; } + ret = clk_enable(hdmi->hdmi_clk); + if (ret < 0) { + dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); + goto erate; + } + dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { @@ -1200,10 +1298,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hdmi); - /* Product and revision IDs are 0 in sh-mobile version */ - dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", - hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); - /* Set up LCDC callbacks */ board_cfg = &pdata->lcd_chan->board_cfg; board_cfg->owner = THIS_MODULE; @@ -1216,6 +1310,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); + /* Product and revision IDs are 0 in sh-mobile version */ + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); + ret = request_irq(irq, sh_hdmi_hotplug, 0, dev_name(&pdev->dev), hdmi); if (ret < 0) { @@ -1230,6 +1328,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto ecodec; } + hdmi->notifier.notifier_call = sh_hdmi_notify; + fb_register_client(&hdmi->notifier); + return 0; ecodec: @@ -1260,6 +1361,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) snd_soc_unregister_codec(&pdev->dev); + fb_unregister_client(&hdmi->notifier); + board_cfg->display_on = NULL; board_cfg->display_off = NULL; board_cfg->board_data = NULL; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index c05326b6123..bd4840a8a6b 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -139,6 +139,7 @@ struct sh_mobile_lcdc_priv { struct notifier_block notifier; unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; + int forced_bpp; /* 2 channel LCDC must share bpp setting */ }; static bool banked(int reg_nr) @@ -461,13 +462,18 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_board_cfg *board_cfg; unsigned long tmp; + int bpp = 0; int k, m; int ret = 0; /* enable clocks before accessing the hardware */ - for (k = 0; k < ARRAY_SIZE(priv->ch); k++) - if (priv->ch[k].enabled) + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + if (priv->ch[k].enabled) { sh_mobile_lcdc_clk_on(priv); + if (!bpp) + bpp = priv->ch[k].info->var.bits_per_pixel; + } + } /* reset */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); @@ -535,7 +541,17 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } /* word and long word swap */ - lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); + switch (bpp) { + case 16: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); + break; + case 24: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7); + break; + case 32: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4); + break; + } for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; @@ -546,7 +562,16 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* set bpp format in PKF[4:0] */ tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~0x0001001f; - tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; + switch (ch->info->var.bits_per_pixel) { + case 16: + tmp |= 0x03; + break; + case 24: + tmp |= 0x0b; + break; + case 32: + break; + } lcdc_write_chan(ch, LDDFR, tmp); /* point out our frame buffer */ @@ -913,15 +938,30 @@ static int sh_mobile_open(struct fb_info *info, int user) static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_mobile_lcdc_priv *p = ch->lcdc; if (var->xres > MAX_XRES || var->yres > MAX_YRES || var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { - dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %ukHz!\n", + dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", var->left_margin, var->xres, var->right_margin, var->hsync_len, var->upper_margin, var->yres, var->lower_margin, var->vsync_len, PICOS2KHZ(var->pixclock)); return -EINVAL; } + + /* only accept the forced_bpp for dual channel configurations */ + if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: /* PKF[4:0] = 00011 - RGB 565 */ + case 24: /* PKF[4:0] = 01011 - RGB 888 */ + case 32: /* PKF[4:0] = 00000 - RGBA 888 */ + break; + default: + return -EINVAL; + } + return 0; } @@ -954,19 +994,27 @@ static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) var->transp.length = 0; break; - case 32: /* PKF[4:0] = 00000 - RGB 888 - * sh7722 pdf says 00RRGGBB but reality is GGBB00RR - * this may be because LDDDSR has word swap enabled.. - */ - var->red.offset = 0; + case 24: /* PKF[4:0] = 01011 - RGB 888 */ + var->red.offset = 16; var->red.length = 8; - var->green.offset = 24; + var->green.offset = 8; var->green.length = 8; - var->blue.offset = 16; + var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break; + + case 32: /* PKF[4:0] = 00000 - RGBA 888 */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + break; default: return -EINVAL; } @@ -1170,6 +1218,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + /* for dual channel LCDC (MAIN + SUB) force shared bpp setting */ + if (j == 2) + priv->forced_bpp = pdata->ch[0].bpp; + priv->base = ioremap_nocache(res->start, resource_size(res)); if (!priv->base) goto err1; diff --git a/drivers/video/sis/init.c b/drivers/video/sis/init.c index 31137adc8fb..66de832361c 100644 --- a/drivers/video/sis/init.c +++ b/drivers/video/sis/init.c @@ -56,10 +56,6 @@ * Used by permission. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include "init.h" #ifdef CONFIG_FB_SIS_300 @@ -880,59 +876,59 @@ SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDispl /*********************************************/ void -SiS_SetReg(SISIOADDRESS port, unsigned short index, unsigned short data) +SiS_SetReg(SISIOADDRESS port, u8 index, u8 data) { - outb((u8)index, port); - outb((u8)data, port + 1); + outb(index, port); + outb(data, port + 1); } void -SiS_SetRegByte(SISIOADDRESS port, unsigned short data) +SiS_SetRegByte(SISIOADDRESS port, u8 data) { - outb((u8)data, port); + outb(data, port); } void -SiS_SetRegShort(SISIOADDRESS port, unsigned short data) +SiS_SetRegShort(SISIOADDRESS port, u16 data) { - outw((u16)data, port); + outw(data, port); } void -SiS_SetRegLong(SISIOADDRESS port, unsigned int data) +SiS_SetRegLong(SISIOADDRESS port, u32 data) { - outl((u32)data, port); + outl(data, port); } -unsigned char -SiS_GetReg(SISIOADDRESS port, unsigned short index) +u8 +SiS_GetReg(SISIOADDRESS port, u8 index) { - outb((u8)index, port); + outb(index, port); return inb(port + 1); } -unsigned char +u8 SiS_GetRegByte(SISIOADDRESS port) { return inb(port); } -unsigned short +u16 SiS_GetRegShort(SISIOADDRESS port) { return inw(port); } -unsigned int +u32 SiS_GetRegLong(SISIOADDRESS port) { return inl(port); } void -SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, unsigned short DataOR) +SiS_SetRegANDOR(SISIOADDRESS Port, u8 Index, u8 DataAND, u8 DataOR) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp = (temp & (DataAND)) | DataOR; @@ -940,9 +936,9 @@ SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, } void -SiS_SetRegAND(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND) +SiS_SetRegAND(SISIOADDRESS Port, u8 Index, u8 DataAND) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp &= DataAND; @@ -950,9 +946,9 @@ SiS_SetRegAND(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND) } void -SiS_SetRegOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataOR) +SiS_SetRegOR(SISIOADDRESS Port, u8 Index, u8 DataOR) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp |= DataOR; diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index ee8ed3c203d..aff73842d87 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -1516,19 +1516,6 @@ unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDispla unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, unsigned int VBFlags2); -void SiS_SetReg(SISIOADDRESS port, unsigned short index, unsigned short data); -void SiS_SetRegByte(SISIOADDRESS port, unsigned short data); -void SiS_SetRegShort(SISIOADDRESS port, unsigned short data); -void SiS_SetRegLong(SISIOADDRESS port, unsigned int data); -unsigned char SiS_GetReg(SISIOADDRESS port, unsigned short index); -unsigned char SiS_GetRegByte(SISIOADDRESS port); -unsigned short SiS_GetRegShort(SISIOADDRESS port); -unsigned int SiS_GetRegLong(SISIOADDRESS port); -void SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, - unsigned short DataOR); -void SiS_SetRegAND(SISIOADDRESS Port,unsigned short Index, unsigned short DataAND); -void SiS_SetRegOR(SISIOADDRESS Port,unsigned short Index, unsigned short DataOR); - void SiS_DisplayOn(struct SiS_Private *SiS_Pr); void SiS_DisplayOff(struct SiS_Private *SiS_Pr); void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr); diff --git a/drivers/video/sis/init301.c b/drivers/video/sis/init301.c index 9fa66fd4052..a89e3cafd5a 100644 --- a/drivers/video/sis/init301.c +++ b/drivers/video/sis/init301.c @@ -57,10 +57,6 @@ * */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #if 1 #define SET_EMI /* 302LV/ELV: Set EMI values */ #endif @@ -5856,7 +5852,7 @@ SiS_SetGroup1_LVDS(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned s temp = tempax & 0x00FF; SiS_SetReg(SiS_Pr->SiS_Part1Port,0x43,temp); temp = ((tempax & 0xFF00) >> 8) << 3; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x44,~0x0F8,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port, 0x44, 0x07, temp); tempax = SiS_Pr->SiS_VDE; /* BDxWadrst1 = BDxWadrst0 + BDxWadroff * VDE */ if(SiS_Pr->SiS_LCDResInfo == Panel_320x240_1 || @@ -5870,7 +5866,7 @@ SiS_SetGroup1_LVDS(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned s temp = ((tempeax & 0xFF0000) >> 16) | 0x10; SiS_SetReg(SiS_Pr->SiS_Part1Port,0x40,temp); temp = ((tempeax & 0x01000000) >> 24) << 7; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x3C,~0x080,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port, 0x3C, 0x7F, temp); SiS_SetReg(SiS_Pr->SiS_Part1Port,0x2F,0x03); SiS_SetReg(SiS_Pr->SiS_Part1Port,0x03,0x50); diff --git a/drivers/video/sis/init301.h b/drivers/video/sis/init301.h index e1fd31d0fdd..2112d6d7fed 100644 --- a/drivers/video/sis/init301.h +++ b/drivers/video/sis/init301.h @@ -428,17 +428,6 @@ static void SiS_OEM661Setting(struct SiS_Private *SiS_Pr, static void SiS_FinalizeLCD(struct SiS_Private *, unsigned short, unsigned short); #endif -extern void SiS_SetReg(SISIOADDRESS, unsigned short, unsigned short); -extern void SiS_SetRegByte(SISIOADDRESS, unsigned short); -extern void SiS_SetRegShort(SISIOADDRESS, unsigned short); -extern void SiS_SetRegLong(SISIOADDRESS, unsigned int); -extern unsigned char SiS_GetReg(SISIOADDRESS, unsigned short); -extern unsigned char SiS_GetRegByte(SISIOADDRESS); -extern unsigned short SiS_GetRegShort(SISIOADDRESS); -extern unsigned int SiS_GetRegLong(SISIOADDRESS); -extern void SiS_SetRegANDOR(SISIOADDRESS, unsigned short, unsigned short, unsigned short); -extern void SiS_SetRegOR(SISIOADDRESS, unsigned short, unsigned short); -extern void SiS_SetRegAND(SISIOADDRESS, unsigned short, unsigned short); extern void SiS_DisplayOff(struct SiS_Private *SiS_Pr); extern void SiS_DisplayOn(struct SiS_Private *SiS_Pr); extern bool SiS_SearchModeID(struct SiS_Private *, unsigned short *, unsigned short *); diff --git a/drivers/video/sis/sis.h b/drivers/video/sis/sis.h index 80d89d37c41..eac7a01925f 100644 --- a/drivers/video/sis/sis.h +++ b/drivers/video/sis/sis.h @@ -307,58 +307,19 @@ #define VB2_LCDOVER1600BRIDGE (VB2_307T | VB2_307LV) #define VB2_RAMDAC202MHZBRIDGE (VB2_301C | VB2_307T) -/* I/O port access macros */ -#define inSISREG(base) inb(base) - -#define outSISREG(base,val) outb(val,base) - -#define orSISREG(base,val) \ - do { \ - u8 __Temp = inSISREG(base); \ - outSISREG(base, __Temp | (val));\ - } while (0) - -#define andSISREG(base,val) \ - do { \ - u8 __Temp = inSISREG(base); \ - outSISREG(base, __Temp & (val));\ - } while (0) - -#define inSISIDXREG(base,idx,var) \ - do { \ - outSISREG(base, idx); \ - var = inSISREG((base)+1); \ - } while (0) - -#define outSISIDXREG(base,idx,val) \ - do { \ - outSISREG(base, idx); \ - outSISREG((base)+1, val); \ - } while (0) - -#define orSISIDXREG(base,idx,val) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = inSISREG((base)+1) | (val); \ - outSISREG((base)+1, __Temp); \ - } while (0) - -#define andSISIDXREG(base,idx,and) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = inSISREG((base)+1) & (and); \ - outSISREG((base)+1, __Temp); \ - } while (0) - -#define setSISIDXREG(base,idx,and,or) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = (inSISREG((base)+1) & (and)) | (or); \ - outSISREG((base)+1, __Temp); \ - } while (0) +/* I/O port access functions */ + +void SiS_SetReg(SISIOADDRESS, u8, u8); +void SiS_SetRegByte(SISIOADDRESS, u8); +void SiS_SetRegShort(SISIOADDRESS, u16); +void SiS_SetRegLong(SISIOADDRESS, u32); +void SiS_SetRegANDOR(SISIOADDRESS, u8, u8, u8); +void SiS_SetRegAND(SISIOADDRESS, u8, u8); +void SiS_SetRegOR(SISIOADDRESS, u8, u8); +u8 SiS_GetReg(SISIOADDRESS, u8); +u8 SiS_GetRegByte(SISIOADDRESS); +u16 SiS_GetRegShort(SISIOADDRESS); +u32 SiS_GetRegLong(SISIOADDRESS); /* MMIO access macros */ #define MMIO_IN8(base, offset) readb((base+offset)) diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 7e3370f115b..2fb8c5a660f 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -737,7 +737,7 @@ sisfb_bridgeisslave(struct sis_video_info *ivideo) if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) return false; - inSISIDXREG(SISPART1,0x00,P1_00); + P1_00 = SiS_GetReg(SISPART1, 0x00); if( ((ivideo->sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) || ((ivideo->sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) { return true; @@ -751,11 +751,11 @@ sisfballowretracecrt1(struct sis_video_info *ivideo) { u8 temp; - inSISIDXREG(SISCR,0x17,temp); + temp = SiS_GetReg(SISCR, 0x17); if(!(temp & 0x80)) return false; - inSISIDXREG(SISSR,0x1f,temp); + temp = SiS_GetReg(SISSR, 0x1f); if(temp & 0xc0) return false; @@ -768,7 +768,7 @@ sisfbcheckvretracecrt1(struct sis_video_info *ivideo) if(!sisfballowretracecrt1(ivideo)) return false; - if(inSISREG(SISINPSTAT) & 0x08) + if (SiS_GetRegByte(SISINPSTAT) & 0x08) return true; else return false; @@ -783,9 +783,9 @@ sisfbwaitretracecrt1(struct sis_video_info *ivideo) return; watchdog = 65536; - while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog); + while ((!(SiS_GetRegByte(SISINPSTAT) & 0x08)) && --watchdog); watchdog = 65536; - while((inSISREG(SISINPSTAT) & 0x08) && --watchdog); + while ((SiS_GetRegByte(SISINPSTAT) & 0x08) && --watchdog); } static bool @@ -799,7 +799,7 @@ sisfbcheckvretracecrt2(struct sis_video_info *ivideo) default: return false; } - inSISIDXREG(SISPART1, reg, temp); + temp = SiS_GetReg(SISPART1, reg); if(temp & 0x02) return true; else @@ -837,10 +837,10 @@ sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount) default: case SIS_315_VGA: idx = 0x30; break; } - inSISIDXREG(SISPART1,(idx+0),reg1); /* 30 */ - inSISIDXREG(SISPART1,(idx+1),reg2); /* 31 */ - inSISIDXREG(SISPART1,(idx+2),reg3); /* 32 */ - inSISIDXREG(SISPART1,(idx+3),reg4); /* 33 */ + reg1 = SiS_GetReg(SISPART1, (idx+0)); /* 30 */ + reg2 = SiS_GetReg(SISPART1, (idx+1)); /* 31 */ + reg3 = SiS_GetReg(SISPART1, (idx+2)); /* 32 */ + reg4 = SiS_GetReg(SISPART1, (idx+3)); /* 33 */ if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; if(reg1 & 0x02) ret |= FB_VBLANK_VSYNCING; if(reg4 & 0x80) ret |= FB_VBLANK_HBLANKING; @@ -853,13 +853,13 @@ sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount) FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_HCOUNT); - reg1 = inSISREG(SISINPSTAT); + reg1 = SiS_GetRegByte(SISINPSTAT); if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING; if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; - inSISIDXREG(SISCR,0x20,reg1); - inSISIDXREG(SISCR,0x1b,reg1); - inSISIDXREG(SISCR,0x1c,reg2); - inSISIDXREG(SISCR,0x1d,reg3); + reg1 = SiS_GetReg(SISCR, 0x20); + reg1 = SiS_GetReg(SISCR, 0x1b); + reg2 = SiS_GetReg(SISCR, 0x1c); + reg3 = SiS_GetReg(SISCR, 0x1d); (*vcount) = reg2 | ((reg3 & 0x07) << 8); (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3; } @@ -930,12 +930,12 @@ sisfb_myblank(struct sis_video_info *ivideo, int blank) (ivideo->sisfb_thismonitor.feature & 0xe0))) { if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63); } if(!(sisfb_bridgeisslave(ivideo))) { - setSISIDXREG(SISSR, 0x01, ~0x20, sr01); - setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f); + SiS_SetRegANDOR(SISSR, 0x01, ~0x20, sr01); + SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, sr1f); } } @@ -965,25 +965,25 @@ sisfb_myblank(struct sis_video_info *ivideo, int blank) (ivideo->vbflags2 & (VB2_301|VB2_30xBDH|VB2_LVDS))) || ((ivideo->sisvga_engine == SIS_315_VGA) && ((ivideo->vbflags2 & (VB2_LVDS | VB2_CHRONTEL)) == VB2_LVDS))) { - setSISIDXREG(SISSR, 0x11, ~0x0c, sr11); + SiS_SetRegANDOR(SISSR, 0x11, ~0x0c, sr11); } if(ivideo->sisvga_engine == SIS_300_VGA) { if((ivideo->vbflags2 & VB2_30xB) && (!(ivideo->vbflags2 & VB2_30xBDH))) { - setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13); + SiS_SetRegANDOR(SISPART1, 0x13, 0x3f, p1_13); } } else if(ivideo->sisvga_engine == SIS_315_VGA) { if((ivideo->vbflags2 & VB2_30xB) && (!(ivideo->vbflags2 & VB2_30xBDH))) { - setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0); + SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); } } } else if(ivideo->currentvbflags & CRT2_VGA) { if(ivideo->vbflags2 & VB2_30xB) { - setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0); + SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); } } @@ -1114,15 +1114,15 @@ sisfb_set_pitch(struct sis_video_info *ivideo) /* We need to set pitch for CRT1 if bridge is in slave mode, too */ if((ivideo->currentvbflags & VB_DISPTYPE_DISP1) || (isslavemode)) { - outSISIDXREG(SISCR,0x13,(HDisplay1 & 0xFF)); - setSISIDXREG(SISSR,0x0E,0xF0,(HDisplay1 >> 8)); + SiS_SetReg(SISCR, 0x13, (HDisplay1 & 0xFF)); + SiS_SetRegANDOR(SISSR, 0x0E, 0xF0, (HDisplay1 >> 8)); } /* We must not set the pitch for CRT2 if bridge is in slave mode */ if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!isslavemode)) { - orSISIDXREG(SISPART1,ivideo->CRT2_write_enable,0x01); - outSISIDXREG(SISPART1,0x07,(HDisplay2 & 0xFF)); - setSISIDXREG(SISPART1,0x09,0xF0,(HDisplay2 >> 8)); + SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); + SiS_SetReg(SISPART1, 0x07, (HDisplay2 & 0xFF)); + SiS_SetRegANDOR(SISPART1, 0x09, 0xF0, (HDisplay2 >> 8)); } } @@ -1167,7 +1167,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) /* >=2.6.12's fbcon clears the screen anyway */ modeno |= 0x80; - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); sisfb_pre_setmode(ivideo); @@ -1176,7 +1176,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) return -EINVAL; } - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); sisfb_post_setmode(ivideo); @@ -1308,13 +1308,13 @@ sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *in static void sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base) { - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); - outSISIDXREG(SISCR, 0x0D, base & 0xFF); - outSISIDXREG(SISCR, 0x0C, (base >> 8) & 0xFF); - outSISIDXREG(SISSR, 0x0D, (base >> 16) & 0xFF); + SiS_SetReg(SISCR, 0x0D, base & 0xFF); + SiS_SetReg(SISCR, 0x0C, (base >> 8) & 0xFF); + SiS_SetReg(SISSR, 0x0D, (base >> 16) & 0xFF); if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISSR, 0x37, 0xFE, (base >> 24) & 0x01); + SiS_SetRegANDOR(SISSR, 0x37, 0xFE, (base >> 24) & 0x01); } } @@ -1322,12 +1322,12 @@ static void sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base) { if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { - orSISIDXREG(SISPART1, ivideo->CRT2_write_enable, 0x01); - outSISIDXREG(SISPART1, 0x06, (base & 0xFF)); - outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF)); - outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF)); + SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); + SiS_SetReg(SISPART1, 0x06, (base & 0xFF)); + SiS_SetReg(SISPART1, 0x05, ((base >> 8) & 0xFF)); + SiS_SetReg(SISPART1, 0x04, ((base >> 16) & 0xFF)); if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7); + SiS_SetRegANDOR(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7); } } } @@ -1388,15 +1388,15 @@ sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, switch(info->var.bits_per_pixel) { case 8: - outSISREG(SISDACA, regno); - outSISREG(SISDACD, (red >> 10)); - outSISREG(SISDACD, (green >> 10)); - outSISREG(SISDACD, (blue >> 10)); + SiS_SetRegByte(SISDACA, regno); + SiS_SetRegByte(SISDACD, (red >> 10)); + SiS_SetRegByte(SISDACD, (green >> 10)); + SiS_SetRegByte(SISDACD, (blue >> 10)); if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { - outSISREG(SISDAC2A, regno); - outSISREG(SISDAC2D, (red >> 8)); - outSISREG(SISDAC2D, (green >> 8)); - outSISREG(SISDAC2D, (blue >> 8)); + SiS_SetRegByte(SISDAC2A, regno); + SiS_SetRegByte(SISDAC2D, (red >> 8)); + SiS_SetRegByte(SISDAC2D, (green >> 8)); + SiS_SetRegByte(SISDAC2D, (blue >> 8)); } break; case 16: @@ -1961,7 +1961,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) switch(ivideo->chip) { #ifdef CONFIG_FB_SIS_300 case SIS_300: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = ((reg & 0x3F) + 1) << 20; break; case SIS_540: @@ -1977,7 +1977,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) case SIS_315H: case SIS_315PRO: case SIS_315: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; switch((reg >> 2) & 0x03) { case 0x01: @@ -1989,31 +1989,31 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) } break; case SIS_330: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; if(reg & 0x0c) ivideo->video_size <<= 1; break; case SIS_550: case SIS_650: case SIS_740: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20; break; case SIS_661: case SIS_741: - inSISIDXREG(SISCR, 0x79, reg); + reg = SiS_GetReg(SISCR, 0x79); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; break; case SIS_660: case SIS_760: case SIS_761: - inSISIDXREG(SISCR, 0x79, reg); + reg = SiS_GetReg(SISCR, 0x79); reg = (reg & 0xf0) >> 4; if(reg) { ivideo->video_size = (1 << reg) << 20; ivideo->UMAsize = ivideo->video_size; } - inSISIDXREG(SISCR, 0x78, reg); + reg = SiS_GetReg(SISCR, 0x78); reg &= 0x30; if(reg) { if(reg == 0x10) { @@ -2027,7 +2027,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) case SIS_340: case XGI_20: case XGI_40: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; if(ivideo->chip != XGI_20) { reg = (reg & 0x0c) >> 2; @@ -2061,11 +2061,11 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_300 if(ivideo->sisvga_engine == SIS_300_VGA) { - inSISIDXREG(SISSR, 0x17, temp); + temp = SiS_GetReg(SISSR, 0x17); if((temp & 0x0F) && (ivideo->chip != SIS_300)) { /* PAL/NTSC is stored on SR16 on such machines */ if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN))) { - inSISIDXREG(SISSR, 0x16, temp); + temp = SiS_GetReg(SISSR, 0x16); if(temp & 0x20) ivideo->vbflags |= TV_PAL; else @@ -2075,7 +2075,7 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) } #endif - inSISIDXREG(SISCR, 0x32, cr32); + cr32 = SiS_GetReg(SISCR, 0x32); if(cr32 & SIS_CRT1) { ivideo->sisfb_crt1off = 0; @@ -2151,15 +2151,15 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) } if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ))) { if(ivideo->sisvga_engine == SIS_300_VGA) { - inSISIDXREG(SISSR, 0x38, temp); + temp = SiS_GetReg(SISSR, 0x38); if(temp & 0x01) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) { - inSISIDXREG(SISSR, 0x38, temp); + temp = SiS_GetReg(SISSR, 0x38); if(temp & 0x01) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } else { - inSISIDXREG(SISCR, 0x79, temp); + temp = SiS_GetReg(SISCR, 0x79); if(temp & 0x20) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } @@ -2198,26 +2198,26 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) u16 temp = 0xffff; int i; - inSISIDXREG(SISSR,0x1F,sr1F); - orSISIDXREG(SISSR,0x1F,0x04); - andSISIDXREG(SISSR,0x1F,0x3F); + sr1F = SiS_GetReg(SISSR, 0x1F); + SiS_SetRegOR(SISSR, 0x1F, 0x04); + SiS_SetRegAND(SISSR, 0x1F, 0x3F); if(sr1F & 0xc0) mustwait = true; #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { - inSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,cr63); + cr63 = SiS_GetReg(SISCR, ivideo->SiS_Pr.SiS_MyCR63); cr63 &= 0x40; - andSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF); + SiS_SetRegAND(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF); } #endif - inSISIDXREG(SISCR,0x17,cr17); + cr17 = SiS_GetReg(SISCR, 0x17); cr17 &= 0x80; if(!cr17) { - orSISIDXREG(SISCR,0x17,0x80); + SiS_SetRegOR(SISCR, 0x17, 0x80); mustwait = true; - outSISIDXREG(SISSR, 0x00, 0x01); - outSISIDXREG(SISSR, 0x00, 0x03); + SiS_SetReg(SISSR, 0x00, 0x01); + SiS_SetReg(SISSR, 0x00, 0x03); } if(mustwait) { @@ -2226,18 +2226,18 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_315 if(ivideo->chip >= SIS_330) { - andSISIDXREG(SISCR,0x32,~0x20); + SiS_SetRegAND(SISCR, 0x32, ~0x20); if(ivideo->chip >= SIS_340) { - outSISIDXREG(SISCR, 0x57, 0x4a); + SiS_SetReg(SISCR, 0x57, 0x4a); } else { - outSISIDXREG(SISCR, 0x57, 0x5f); + SiS_SetReg(SISCR, 0x57, 0x5f); } - orSISIDXREG(SISCR, 0x53, 0x02); - while((inSISREG(SISINPSTAT)) & 0x01) break; - while(!((inSISREG(SISINPSTAT)) & 0x01)) break; - if((inSISREG(SISMISCW)) & 0x10) temp = 1; - andSISIDXREG(SISCR, 0x53, 0xfd); - andSISIDXREG(SISCR, 0x57, 0x00); + SiS_SetRegOR(SISCR, 0x53, 0x02); + while ((SiS_GetRegByte(SISINPSTAT)) & 0x01) break; + while (!((SiS_GetRegByte(SISINPSTAT)) & 0x01)) break; + if ((SiS_GetRegByte(SISMISCW)) & 0x10) temp = 1; + SiS_SetRegAND(SISCR, 0x53, 0xfd); + SiS_SetRegAND(SISCR, 0x57, 0x00); } #endif @@ -2254,18 +2254,18 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) } if((temp) && (temp != 0xffff)) { - orSISIDXREG(SISCR,0x32,0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF,cr63); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF, cr63); } #endif - setSISIDXREG(SISCR,0x17,0x7F,cr17); + SiS_SetRegANDOR(SISCR, 0x17, 0x7F, cr17); - outSISIDXREG(SISSR,0x1F,sr1F); + SiS_SetReg(SISSR, 0x1F, sr1F); } /* Determine and detect attached devices on SiS30x */ @@ -2286,7 +2286,7 @@ SiS_SenseLCD(struct sis_video_info *ivideo) return; /* If LCD already set up by BIOS, skip it */ - inSISIDXREG(SISCR, 0x32, reg); + reg = SiS_GetReg(SISCR, 0x32); if(reg & 0x08) return; @@ -2349,10 +2349,10 @@ SiS_SenseLCD(struct sis_video_info *ivideo) else cr37 |= 0xc0; - outSISIDXREG(SISCR, 0x36, paneltype); + SiS_SetReg(SISCR, 0x36, paneltype); cr37 &= 0xf1; - setSISIDXREG(SISCR, 0x37, 0x0c, cr37); - orSISIDXREG(SISCR, 0x32, 0x08); + SiS_SetRegANDOR(SISCR, 0x37, 0x0c, cr37); + SiS_SetRegOR(SISCR, 0x32, 0x08); ivideo->SiS_Pr.PanelSelfDetected = true; } @@ -2366,19 +2366,19 @@ SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test) result = 0; for(i = 0; i < 3; i++) { mytest = test; - outSISIDXREG(SISPART4,0x11,(type & 0x00ff)); + SiS_SetReg(SISPART4, 0x11, (type & 0x00ff)); temp = (type >> 8) | (mytest & 0x00ff); - setSISIDXREG(SISPART4,0x10,0xe0,temp); + SiS_SetRegANDOR(SISPART4, 0x10, 0xe0, temp); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500); mytest >>= 8; mytest &= 0x7f; - inSISIDXREG(SISPART4,0x03,temp); + temp = SiS_GetReg(SISPART4, 0x03); temp ^= 0x0e; temp &= mytest; if(temp == mytest) result++; #if 1 - outSISIDXREG(SISPART4,0x11,0x00); - andSISIDXREG(SISPART4,0x10,0xe0); + SiS_SetReg(SISPART4, 0x11, 0x00); + SiS_SetRegAND(SISPART4, 0x10, 0xe0); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000); #endif } @@ -2400,7 +2400,7 @@ SiS_Sense30x(struct sis_video_info *ivideo) if(ivideo->vbflags2 & VB2_301) { svhs = 0x00b9; cvbs = 0x00b3; vga2 = 0x00d1; - inSISIDXREG(SISPART4,0x01,myflag); + myflag = SiS_GetReg(SISPART4, 0x01); if(myflag & 0x04) { svhs = 0x00dd; cvbs = 0x00ee; vga2 = 0x00fd; } @@ -2430,7 +2430,7 @@ SiS_Sense30x(struct sis_video_info *ivideo) } if(ivideo->chip == SIS_300) { - inSISIDXREG(SISSR,0x3b,myflag); + myflag = SiS_GetReg(SISSR, 0x3b); if(!(myflag & 0x01)) vga2 = vga2_c = 0; } @@ -2438,93 +2438,93 @@ SiS_Sense30x(struct sis_video_info *ivideo) vga2 = vga2_c = 0; } - inSISIDXREG(SISSR,0x1e,backupSR_1e); - orSISIDXREG(SISSR,0x1e,0x20); + backupSR_1e = SiS_GetReg(SISSR, 0x1e); + SiS_SetRegOR(SISSR, 0x1e, 0x20); - inSISIDXREG(SISPART4,0x0d,backupP4_0d); + backupP4_0d = SiS_GetReg(SISPART4, 0x0d); if(ivideo->vbflags2 & VB2_30xC) { - setSISIDXREG(SISPART4,0x0d,~0x07,0x01); + SiS_SetRegANDOR(SISPART4, 0x0d, ~0x07, 0x01); } else { - orSISIDXREG(SISPART4,0x0d,0x04); + SiS_SetRegOR(SISPART4, 0x0d, 0x04); } SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); - inSISIDXREG(SISPART2,0x00,backupP2_00); - outSISIDXREG(SISPART2,0x00,((backupP2_00 | 0x1c) & 0xfc)); + backupP2_00 = SiS_GetReg(SISPART2, 0x00); + SiS_SetReg(SISPART2, 0x00, ((backupP2_00 | 0x1c) & 0xfc)); - inSISIDXREG(SISPART2,0x4d,backupP2_4d); + backupP2_4d = SiS_GetReg(SISPART2, 0x4d); if(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE) { - outSISIDXREG(SISPART2,0x4d,(backupP2_4d & ~0x10)); + SiS_SetReg(SISPART2, 0x4d, (backupP2_4d & ~0x10)); } if(!(ivideo->vbflags2 & VB2_30xCLV)) { SISDoSense(ivideo, 0, 0); } - andSISIDXREG(SISCR, 0x32, ~0x14); + SiS_SetRegAND(SISCR, 0x32, ~0x14); if(vga2_c || vga2) { if(SISDoSense(ivideo, vga2, vga2_c)) { if(biosflag & 0x01) { printk(KERN_INFO "%s %s SCART output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x04); + SiS_SetRegOR(SISCR, 0x32, 0x04); } else { printk(KERN_INFO "%s secondary VGA connection\n", stdstr); - orSISIDXREG(SISCR, 0x32, 0x10); + SiS_SetRegOR(SISCR, 0x32, 0x10); } } } - andSISIDXREG(SISCR, 0x32, 0x3f); + SiS_SetRegAND(SISCR, 0x32, 0x3f); if(ivideo->vbflags2 & VB2_30xCLV) { - orSISIDXREG(SISPART4,0x0d,0x04); + SiS_SetRegOR(SISPART4, 0x0d, 0x04); } if((ivideo->sisvga_engine == SIS_315_VGA) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) { - outSISIDXREG(SISPART2,0x4d,(backupP2_4d | 0x10)); + SiS_SetReg(SISPART2, 0x4d, (backupP2_4d | 0x10)); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); if((result = SISDoSense(ivideo, svhs, 0x0604))) { if((result = SISDoSense(ivideo, cvbs, 0x0804))) { printk(KERN_INFO "%s %s YPbPr component output\n", stdstr, tvstr); - orSISIDXREG(SISCR,0x32,0x80); + SiS_SetRegOR(SISCR, 0x32, 0x80); } } - outSISIDXREG(SISPART2,0x4d,backupP2_4d); + SiS_SetReg(SISPART2, 0x4d, backupP2_4d); } - andSISIDXREG(SISCR, 0x32, ~0x03); + SiS_SetRegAND(SISCR, 0x32, ~0x03); if(!(ivideo->vbflags & TV_YPBPR)) { if((result = SISDoSense(ivideo, svhs, svhs_c))) { printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x02); + SiS_SetRegOR(SISCR, 0x32, 0x02); } if((biosflag & 0x02) || (!result)) { if(SISDoSense(ivideo, cvbs, cvbs_c)) { printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x01); + SiS_SetRegOR(SISCR, 0x32, 0x01); } } } SISDoSense(ivideo, 0, 0); - outSISIDXREG(SISPART2,0x00,backupP2_00); - outSISIDXREG(SISPART4,0x0d,backupP4_0d); - outSISIDXREG(SISSR,0x1e,backupSR_1e); + SiS_SetReg(SISPART2, 0x00, backupP2_00); + SiS_SetReg(SISPART4, 0x0d, backupP4_0d); + SiS_SetReg(SISSR, 0x1e, backupSR_1e); if(ivideo->vbflags2 & VB2_30xCLV) { - inSISIDXREG(SISPART2,0x00,biosflag); + biosflag = SiS_GetReg(SISPART2, 0x00); if(biosflag & 0x20) { for(myflag = 2; myflag > 0; myflag--) { biosflag ^= 0x20; - outSISIDXREG(SISPART2,0x00,biosflag); + SiS_SetReg(SISPART2, 0x00, biosflag); } } } - outSISIDXREG(SISPART2,0x00,backupP2_00); + SiS_SetReg(SISPART2, 0x00, backupP2_00); } /* Determine and detect attached TV's on Chrontel */ @@ -2588,20 +2588,20 @@ SiS_SenseCh(struct sis_video_info *ivideo) if(temp1 == 0x02) { printk(KERN_INFO "%s SVIDEO output\n", stdstr); ivideo->vbflags |= TV_SVIDEO; - orSISIDXREG(SISCR, 0x32, 0x02); - andSISIDXREG(SISCR, 0x32, ~0x05); + SiS_SetRegOR(SISCR, 0x32, 0x02); + SiS_SetRegAND(SISCR, 0x32, ~0x05); } else if (temp1 == 0x01) { printk(KERN_INFO "%s CVBS output\n", stdstr); ivideo->vbflags |= TV_AVIDEO; - orSISIDXREG(SISCR, 0x32, 0x01); - andSISIDXREG(SISCR, 0x32, ~0x06); + SiS_SetRegOR(SISCR, 0x32, 0x01); + SiS_SetRegAND(SISCR, 0x32, ~0x06); } else { SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } } else if(temp1 == 0) { SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } /* Set general purpose IO for Chrontel communication */ SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x00); @@ -2632,22 +2632,22 @@ SiS_SenseCh(struct sis_video_info *ivideo) case 0x01: printk(KERN_INFO "%s CVBS output\n", stdstr); ivideo->vbflags |= TV_AVIDEO; - orSISIDXREG(SISCR, 0x32, 0x01); - andSISIDXREG(SISCR, 0x32, ~0x06); + SiS_SetRegOR(SISCR, 0x32, 0x01); + SiS_SetRegAND(SISCR, 0x32, ~0x06); break; case 0x02: printk(KERN_INFO "%s SVIDEO output\n", stdstr); ivideo->vbflags |= TV_SVIDEO; - orSISIDXREG(SISCR, 0x32, 0x02); - andSISIDXREG(SISCR, 0x32, ~0x05); + SiS_SetRegOR(SISCR, 0x32, 0x02); + SiS_SetRegAND(SISCR, 0x32, ~0x05); break; case 0x04: printk(KERN_INFO "%s SCART output\n", stdstr); - orSISIDXREG(SISCR, 0x32, 0x04); - andSISIDXREG(SISCR, 0x32, ~0x03); + SiS_SetRegOR(SISCR, 0x32, 0x04); + SiS_SetRegAND(SISCR, 0x32, ~0x03); break; default: - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } #endif } @@ -2665,10 +2665,10 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) if(ivideo->chip == XGI_20) return; - inSISIDXREG(SISPART4, 0x00, vb_chipid); + vb_chipid = SiS_GetReg(SISPART4, 0x00); switch(vb_chipid) { case 0x01: - inSISIDXREG(SISPART4, 0x01, reg); + reg = SiS_GetReg(SISPART4, 0x01); if(reg < 0xb0) { ivideo->vbflags |= VB_301; /* Deprecated */ ivideo->vbflags2 |= VB2_301; @@ -2676,7 +2676,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) } else if(reg < 0xc0) { ivideo->vbflags |= VB_301B; /* Deprecated */ ivideo->vbflags2 |= VB2_301B; - inSISIDXREG(SISPART4,0x23,reg); + reg = SiS_GetReg(SISPART4, 0x23); if(!(reg & 0x02)) { ivideo->vbflags |= VB_30xBDH; /* Deprecated */ ivideo->vbflags2 |= VB2_30xBDH; @@ -2693,7 +2693,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) ivideo->vbflags2 |= VB2_301LV; printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr); } else if(reg <= 0xe1) { - inSISIDXREG(SISPART4,0x39,reg); + reg = SiS_GetReg(SISPART4, 0x39); if(reg == 0xff) { ivideo->vbflags |= VB_302LV; /* Deprecated */ ivideo->vbflags2 |= VB2_302LV; @@ -2718,7 +2718,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) } if((!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) && (ivideo->chip != SIS_300)) { - inSISIDXREG(SISCR, 0x37, reg); + reg = SiS_GetReg(SISCR, 0x37); reg &= SIS_EXTERNAL_CHIP_MASK; reg >>= 1; if(ivideo->sisvga_engine == SIS_300_VGA) { @@ -2759,7 +2759,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) #endif } else if(ivideo->chip >= SIS_661) { #ifdef CONFIG_FB_SIS_315 - inSISIDXREG(SISCR, 0x38, reg); + reg = SiS_GetReg(SISCR, 0x38); reg >>= 5; switch(reg) { case 0x02: @@ -2822,13 +2822,13 @@ sisfb_engine_init(struct sis_video_info *ivideo) tqueue_pos = (ivideo->video_size - ivideo->cmdQueueSize) / (64 * 1024); - inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); + tq_state = SiS_GetReg(SISSR, IND_SIS_TURBOQUEUE_SET); tq_state |= 0xf0; tq_state &= 0xfc; tq_state |= (u8)(tqueue_pos >> 8); - outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); + SiS_SetReg(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); - outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff)); + SiS_SetReg(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff)); ivideo->caps |= TURBO_QUEUE_CAP; } @@ -2865,8 +2865,8 @@ sisfb_engine_init(struct sis_video_info *ivideo) } } - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); if((ivideo->chip >= XGI_40) && ivideo->modechanged) { /* Must disable dual pipe on XGI_40. Can't do @@ -2878,7 +2878,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, 0); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE)); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE)); tempq = MMIO_IN32(ivideo->mmio_vbase, Q_READ_PTR); MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, tempq); @@ -2895,7 +2895,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) sisfb_syncaccel(ivideo); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); } } @@ -2904,7 +2904,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_WRITEPORT, tempq); temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, temp); tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize); MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_PHYBASE, tempq); @@ -2922,7 +2922,7 @@ sisfb_detect_lcd_type(struct sis_video_info *ivideo) u8 reg; int i; - inSISIDXREG(SISCR, 0x36, reg); + reg = SiS_GetReg(SISCR, 0x36); reg &= 0x0f; if(ivideo->sisvga_engine == SIS_300_VGA) { ivideo->CRT2LCDType = sis300paneltype[reg]; @@ -2941,8 +2941,8 @@ sisfb_detect_lcd_type(struct sis_video_info *ivideo) if(ivideo->CRT2LCDType == LCD_UNKNOWN) { /* For broken BIOSes: Assume 1024x768, RGB18 */ ivideo->CRT2LCDType = LCD_1024x768; - setSISIDXREG(SISCR,0x36,0xf0,0x02); - setSISIDXREG(SISCR,0x37,0xee,0x01); + SiS_SetRegANDOR(SISCR, 0x36, 0xf0, 0x02); + SiS_SetRegANDOR(SISCR, 0x37, 0xee, 0x01); printk(KERN_DEBUG "sisfb: Invalid panel ID (%02x), assuming 1024x768, RGB18\n", reg); } @@ -2980,10 +2980,10 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) if(ivideo->sisvga_engine == SIS_300_VGA) { if(ivideo->vbflags2 & (VB2_LVDS | VB2_30xBDH)) { int tmp; - inSISIDXREG(SISCR,0x30,tmp); + tmp = SiS_GetReg(SISCR, 0x30); if(tmp & 0x20) { /* Currently on LCD? If yes, read current pdc */ - inSISIDXREG(SISPART1,0x13,ivideo->detectedpdc); + ivideo->detectedpdc = SiS_GetReg(SISPART1, 0x13); ivideo->detectedpdc &= 0x3c; if(ivideo->SiS_Pr.PDC == -1) { /* Let option override detection */ @@ -3007,7 +3007,7 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Try to find about LCDA */ if(ivideo->vbflags2 & VB2_SISLCDABRIDGE) { int tmp; - inSISIDXREG(SISPART1,0x13,tmp); + tmp = SiS_GetReg(SISPART1, 0x13); if(tmp & 0x04) { ivideo->SiS_Pr.SiS_UseLCDA = true; ivideo->detectedlcda = 0x03; @@ -3017,16 +3017,16 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Save PDC */ if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) { int tmp; - inSISIDXREG(SISCR,0x30,tmp); + tmp = SiS_GetReg(SISCR, 0x30); if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) { /* Currently on LCD? If yes, read current pdc */ u8 pdc; - inSISIDXREG(SISPART1,0x2D,pdc); + pdc = SiS_GetReg(SISPART1, 0x2D); ivideo->detectedpdc = (pdc & 0x0f) << 1; ivideo->detectedpdca = (pdc & 0xf0) >> 3; - inSISIDXREG(SISPART1,0x35,pdc); + pdc = SiS_GetReg(SISPART1, 0x35); ivideo->detectedpdc |= ((pdc >> 7) & 0x01); - inSISIDXREG(SISPART1,0x20,pdc); + pdc = SiS_GetReg(SISPART1, 0x20); ivideo->detectedpdca |= ((pdc >> 6) & 0x01); if(ivideo->newrom) { /* New ROM invalidates other PDC resp. */ @@ -3060,10 +3060,10 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Save EMI */ if(ivideo->vbflags2 & VB2_SISEMIBRIDGE) { - inSISIDXREG(SISPART4,0x30,ivideo->SiS_Pr.EMI_30); - inSISIDXREG(SISPART4,0x31,ivideo->SiS_Pr.EMI_31); - inSISIDXREG(SISPART4,0x32,ivideo->SiS_Pr.EMI_32); - inSISIDXREG(SISPART4,0x33,ivideo->SiS_Pr.EMI_33); + ivideo->SiS_Pr.EMI_30 = SiS_GetReg(SISPART4, 0x30); + ivideo->SiS_Pr.EMI_31 = SiS_GetReg(SISPART4, 0x31); + ivideo->SiS_Pr.EMI_32 = SiS_GetReg(SISPART4, 0x32); + ivideo->SiS_Pr.EMI_33 = SiS_GetReg(SISPART4, 0x33); ivideo->SiS_Pr.HaveEMI = true; if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) { ivideo->SiS_Pr.HaveEMILCD = true; @@ -3488,8 +3488,8 @@ sisfb_check_engine_and_sync(struct sis_video_info *ivideo) * ivideo->accel here, as this might have * been changed before this is called. */ - inSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, cr30); - inSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, cr31); + cr30 = SiS_GetReg(SISSR, IND_SIS_PCI_ADDRESS_SET); + cr31 = SiS_GetReg(SISSR, IND_SIS_MODULE_ENABLE); /* MMIO and 2D/3D engine enabled? */ if((cr30 & SIS_MEM_MAP_IO_ENABLE) && (cr31 & 0x42)) { #ifdef CONFIG_FB_SIS_300 @@ -3507,7 +3507,7 @@ sisfb_check_engine_and_sync(struct sis_video_info *ivideo) * enabled, and that the queue * is not in the state of "reset" */ - inSISIDXREG(SISSR, 0x26, cr30); + cr30 = SiS_GetReg(SISSR, 0x26); if((cr30 & 0xe0) && (!(cr30 & 0x01))) { sisfb_syncaccel(ivideo); } @@ -3524,9 +3524,9 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) ivideo->currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); - inSISIDXREG(SISCR, 0x31, cr31); + cr31 = SiS_GetReg(SISCR, 0x31); cr31 &= ~0x60; cr31 |= 0x04; @@ -3535,11 +3535,11 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { if(ivideo->chip >= SIS_661) { - inSISIDXREG(SISCR, 0x38, cr38); + cr38 = SiS_GetReg(SISCR, 0x38); cr38 &= ~0x07; /* Clear LCDA/DualEdge and YPbPr bits */ } else { tvregnum = 0x38; - inSISIDXREG(SISCR, tvregnum, cr38); + cr38 = SiS_GetReg(SISCR, tvregnum); cr38 &= ~0x3b; /* Clear LCDA/DualEdge and YPbPr bits */ } } @@ -3547,7 +3547,7 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_300 if(ivideo->sisvga_engine == SIS_300_VGA) { tvregnum = 0x35; - inSISIDXREG(SISCR, tvregnum, cr38); + cr38 = SiS_GetReg(SISCR, tvregnum); } #endif @@ -3654,20 +3654,20 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE); } - outSISIDXREG(SISCR, 0x30, cr30); - outSISIDXREG(SISCR, 0x33, cr33); + SiS_SetReg(SISCR, 0x30, cr30); + SiS_SetReg(SISCR, 0x33, cr33); if(ivideo->chip >= SIS_661) { #ifdef CONFIG_FB_SIS_315 cr31 &= ~0x01; /* Clear PAL flag (now in CR35) */ - setSISIDXREG(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */ + SiS_SetRegANDOR(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */ cr38 &= 0x07; /* Use only LCDA and HiVision/YPbPr bits */ - setSISIDXREG(SISCR, 0x38, 0xf8, cr38); + SiS_SetRegANDOR(SISCR, 0x38, 0xf8, cr38); #endif } else if(ivideo->chip != SIS_300) { - outSISIDXREG(SISCR, tvregnum, cr38); + SiS_SetReg(SISCR, tvregnum, cr38); } - outSISIDXREG(SISCR, 0x31, cr31); + SiS_SetReg(SISCR, 0x31, cr31); ivideo->SiS_Pr.SiS_UseOEM = ivideo->sisfb_useoem; @@ -3682,15 +3682,15 @@ sisfb_fixup_SR11(struct sis_video_info *ivideo) u8 tmpreg; if(ivideo->chip >= SIS_661) { - inSISIDXREG(SISSR,0x11,tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x11); if(tmpreg & 0x20) { - inSISIDXREG(SISSR,0x3e,tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x3e); tmpreg = (tmpreg + 1) & 0xff; - outSISIDXREG(SISSR,0x3e,tmpreg); - inSISIDXREG(SISSR,0x11,tmpreg); + SiS_SetReg(SISSR, 0x3e, tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x11); } if(tmpreg & 0xf0) { - andSISIDXREG(SISSR,0x11,0x0f); + SiS_SetRegAND(SISSR, 0x11, 0x0f); } } } @@ -3716,7 +3716,7 @@ sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val) case 1: x += val; if(x < 0) x = 0; - outSISIDXREG(SISSR,0x05,0x86); + SiS_SetReg(SISSR, 0x05, 0x86); SiS_SetCH700x(&ivideo->SiS_Pr, 0x0a, (x & 0xff)); SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((x & 0x0100) >> 7), 0xFD); break; @@ -3745,11 +3745,11 @@ sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val) temp += (val * 2); p2_43 = temp & 0xff; p2_42 = (temp & 0xf00) >> 4; - outSISIDXREG(SISPART2,0x1f,p2_1f); - setSISIDXREG(SISPART2,0x20,0x0F,p2_20); - setSISIDXREG(SISPART2,0x2b,0xF0,p2_2b); - setSISIDXREG(SISPART2,0x42,0x0F,p2_42); - outSISIDXREG(SISPART2,0x43,p2_43); + SiS_SetReg(SISPART2, 0x1f, p2_1f); + SiS_SetRegANDOR(SISPART2, 0x20, 0x0F, p2_20); + SiS_SetRegANDOR(SISPART2, 0x2b, 0xF0, p2_2b); + SiS_SetRegANDOR(SISPART2, 0x42, 0x0F, p2_42); + SiS_SetReg(SISPART2, 0x43, p2_43); } } } @@ -3774,7 +3774,7 @@ sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val) case 1: y -= val; if(y < 0) y = 0; - outSISIDXREG(SISSR,0x05,0x86); + SiS_SetReg(SISSR, 0x05, 0x86); SiS_SetCH700x(&ivideo->SiS_Pr, 0x0b, (y & 0xff)); SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((y & 0x0100) >> 8), 0xFE); break; @@ -3798,8 +3798,8 @@ sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val) p2_02 += 2; } } - outSISIDXREG(SISPART2,0x01,p2_01); - outSISIDXREG(SISPART2,0x02,p2_02); + SiS_SetReg(SISPART2, 0x01, p2_01); + SiS_SetReg(SISPART2, 0x02, p2_02); } } } @@ -3816,7 +3816,7 @@ sisfb_post_setmode(struct sis_video_info *ivideo) u8 reg1; #endif - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); #ifdef CONFIG_FB_SIS_315 sisfb_fixup_SR11(ivideo); @@ -3840,7 +3840,7 @@ sisfb_post_setmode(struct sis_video_info *ivideo) crt1isoff = false; reg = 0x80; } - setSISIDXREG(SISCR, 0x17, 0x7f, reg); + SiS_SetRegANDOR(SISCR, 0x17, 0x7f, reg); } #endif #ifdef CONFIG_FB_SIS_315 @@ -3854,8 +3854,8 @@ sisfb_post_setmode(struct sis_video_info *ivideo) reg = 0x00; reg1 = 0x00; } - setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg); - setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg); + SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, reg1); } #endif @@ -3871,17 +3871,17 @@ sisfb_post_setmode(struct sis_video_info *ivideo) } } - andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04); + SiS_SetRegAND(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04); if(ivideo->currentvbflags & CRT2_TV) { if(ivideo->vbflags2 & VB2_SISBRIDGE) { - inSISIDXREG(SISPART2,0x1f,ivideo->p2_1f); - inSISIDXREG(SISPART2,0x20,ivideo->p2_20); - inSISIDXREG(SISPART2,0x2b,ivideo->p2_2b); - inSISIDXREG(SISPART2,0x42,ivideo->p2_42); - inSISIDXREG(SISPART2,0x43,ivideo->p2_43); - inSISIDXREG(SISPART2,0x01,ivideo->p2_01); - inSISIDXREG(SISPART2,0x02,ivideo->p2_02); + ivideo->p2_1f = SiS_GetReg(SISPART2, 0x1f); + ivideo->p2_20 = SiS_GetReg(SISPART2, 0x20); + ivideo->p2_2b = SiS_GetReg(SISPART2, 0x2b); + ivideo->p2_42 = SiS_GetReg(SISPART2, 0x42); + ivideo->p2_43 = SiS_GetReg(SISPART2, 0x43); + ivideo->p2_01 = SiS_GetReg(SISPART2, 0x01); + ivideo->p2_02 = SiS_GetReg(SISPART2, 0x02); } else if(ivideo->vbflags2 & VB2_CHRONTEL) { if(ivideo->chronteltype == 1) { ivideo->tvx = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0a); @@ -4105,7 +4105,6 @@ sisfb_find_rom(struct pci_dev *pdev) struct sis_video_info *ivideo = pci_get_drvdata(pdev); void __iomem *rom_base; unsigned char *myrombase = NULL; - u32 temp; size_t romsize; /* First, try the official pci ROM functions (except @@ -4132,26 +4131,29 @@ sisfb_find_rom(struct pci_dev *pdev) /* Otherwise do it the conventional way. */ #if defined(__i386__) || defined(__x86_64__) + { + u32 temp; - for(temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) { + for (temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) { - rom_base = ioremap(temp, 65536); - if(!rom_base) - continue; + rom_base = ioremap(temp, 65536); + if (!rom_base) + continue; - if(!sisfb_check_rom(rom_base, ivideo)) { - iounmap(rom_base); - continue; - } + if (!sisfb_check_rom(rom_base, ivideo)) { + iounmap(rom_base); + continue; + } - if((myrombase = vmalloc(65536))) - memcpy_fromio(myrombase, rom_base, 65536); + if ((myrombase = vmalloc(65536))) + memcpy_fromio(myrombase, rom_base, 65536); - iounmap(rom_base); - break; + iounmap(rom_base); + break; - } + } + } #endif return myrombase; @@ -4192,10 +4194,10 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) unsigned char reg; int i, j; - andSISIDXREG(SISSR, 0x15, 0xFB); - orSISIDXREG(SISSR, 0x15, 0x04); - outSISIDXREG(SISSR, 0x13, 0x00); - outSISIDXREG(SISSR, 0x14, 0xBF); + SiS_SetRegAND(SISSR, 0x15, 0xFB); + SiS_SetRegOR(SISSR, 0x15, 0x04); + SiS_SetReg(SISSR, 0x13, 0x00); + SiS_SetReg(SISSR, 0x14, 0xBF); for(i = 0; i < 2; i++) { temp = 0x1234; @@ -4203,12 +4205,12 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) writew(temp, FBAddress); if(readw(FBAddress) == temp) break; - orSISIDXREG(SISSR, 0x3c, 0x01); - inSISIDXREG(SISSR, 0x05, reg); - inSISIDXREG(SISSR, 0x05, reg); - andSISIDXREG(SISSR, 0x3c, 0xfe); - inSISIDXREG(SISSR, 0x05, reg); - inSISIDXREG(SISSR, 0x05, reg); + SiS_SetRegOR(SISSR, 0x3c, 0x01); + reg = SiS_GetReg(SISSR, 0x05); + reg = SiS_GetReg(SISSR, 0x05); + SiS_SetRegAND(SISSR, 0x3c, 0xfe); + reg = SiS_GetReg(SISSR, 0x05); + reg = SiS_GetReg(SISSR, 0x05); temp++; } } @@ -4218,7 +4220,7 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) writel(0x89ABCDEFL, (FBAddress + 8)); writel(0xCDEF0123L, (FBAddress + 12)); - inSISIDXREG(SISSR, 0x3b, reg); + reg = SiS_GetReg(SISSR, 0x3b); if(reg & 0x01) { if(readl((FBAddress + 12)) == 0xCDEF0123L) return 4; /* Channel A 128bit */ @@ -4281,13 +4283,13 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth PhysicalAdrHalfPage = (PageCapacity / 2 + PhysicalAdrHigh) % PageCapacity; PhysicalAdrOtherPage = PageCapacity * SiS_DRAMType[k][2] + PhysicalAdrHigh; - andSISIDXREG(SISSR, 0x15, 0xFB); /* Test */ - orSISIDXREG(SISSR, 0x15, 0x04); /* Test */ + SiS_SetRegAND(SISSR, 0x15, 0xFB); /* Test */ + SiS_SetRegOR(SISSR, 0x15, 0x04); /* Test */ sr14 = (SiS_DRAMType[k][3] * buswidth) - 1; if(buswidth == 4) sr14 |= 0x80; else if(buswidth == 2) sr14 |= 0x40; - outSISIDXREG(SISSR, 0x13, SiS_DRAMType[k][4]); - outSISIDXREG(SISSR, 0x14, sr14); + SiS_SetReg(SISSR, 0x13, SiS_DRAMType[k][4]); + SiS_SetReg(SISSR, 0x14, sr14); BankNumHigh <<= 16; BankNumMid <<= 16; @@ -4354,13 +4356,13 @@ sisfb_post_sis300(struct pci_dev *pdev) if(!ivideo->SiS_Pr.UseROM) bios = NULL; - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); if(bios) { if(bios[0x52] & 0x80) { memtype = bios[0x52]; } else { - inSISIDXREG(SISSR, 0x3a, memtype); + memtype = SiS_GetReg(SISSR, 0x3a); } memtype &= 0x07; } @@ -4384,19 +4386,19 @@ sisfb_post_sis300(struct pci_dev *pdev) v6 = bios[rindex++]; } } - outSISIDXREG(SISSR, 0x28, v1); - outSISIDXREG(SISSR, 0x29, v2); - outSISIDXREG(SISSR, 0x2a, v3); - outSISIDXREG(SISSR, 0x2e, v4); - outSISIDXREG(SISSR, 0x2f, v5); - outSISIDXREG(SISSR, 0x30, v6); + SiS_SetReg(SISSR, 0x28, v1); + SiS_SetReg(SISSR, 0x29, v2); + SiS_SetReg(SISSR, 0x2a, v3); + SiS_SetReg(SISSR, 0x2e, v4); + SiS_SetReg(SISSR, 0x2f, v5); + SiS_SetReg(SISSR, 0x30, v6); v1 = 0x10; if(bios) v1 = bios[0xa4]; - outSISIDXREG(SISSR, 0x07, v1); /* DAC speed */ + SiS_SetReg(SISSR, 0x07, v1); /* DAC speed */ - outSISIDXREG(SISSR, 0x11, 0x0f); /* DDC, power save */ + SiS_SetReg(SISSR, 0x11, 0x0f); /* DDC, power save */ v1 = 0x01; v2 = 0x43; v3 = 0x1e; v4 = 0x2a; v5 = 0x06; v6 = 0x00; v7 = 0x00; v8 = 0x00; @@ -4413,87 +4415,87 @@ sisfb_post_sis300(struct pci_dev *pdev) } if(ivideo->revision_id >= 0x80) v3 &= 0xfd; - outSISIDXREG(SISSR, 0x15, v1); /* Ram type (assuming 0, BIOS 0xa5 step 8) */ - outSISIDXREG(SISSR, 0x16, v2); - outSISIDXREG(SISSR, 0x17, v3); - outSISIDXREG(SISSR, 0x18, v4); - outSISIDXREG(SISSR, 0x19, v5); - outSISIDXREG(SISSR, 0x1a, v6); - outSISIDXREG(SISSR, 0x1b, v7); - outSISIDXREG(SISSR, 0x1c, v8); /* ---- */ - andSISIDXREG(SISSR, 0x15 ,0xfb); - orSISIDXREG(SISSR, 0x15, 0x04); + SiS_SetReg(SISSR, 0x15, v1); /* Ram type (assuming 0, BIOS 0xa5 step 8) */ + SiS_SetReg(SISSR, 0x16, v2); + SiS_SetReg(SISSR, 0x17, v3); + SiS_SetReg(SISSR, 0x18, v4); + SiS_SetReg(SISSR, 0x19, v5); + SiS_SetReg(SISSR, 0x1a, v6); + SiS_SetReg(SISSR, 0x1b, v7); + SiS_SetReg(SISSR, 0x1c, v8); /* ---- */ + SiS_SetRegAND(SISSR, 0x15, 0xfb); + SiS_SetRegOR(SISSR, 0x15, 0x04); if(bios) { if(bios[0x53] & 0x02) { - orSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetRegOR(SISSR, 0x19, 0x20); } } v1 = 0x04; /* DAC pedestal (BIOS 0xe5) */ if(ivideo->revision_id >= 0x80) v1 |= 0x01; - outSISIDXREG(SISSR, 0x1f, v1); - outSISIDXREG(SISSR, 0x20, 0xa4); /* linear & relocated io & disable a0000 */ + SiS_SetReg(SISSR, 0x1f, v1); + SiS_SetReg(SISSR, 0x20, 0xa4); /* linear & relocated io & disable a0000 */ v1 = 0xf6; v2 = 0x0d; v3 = 0x00; if(bios) { v1 = bios[0xe8]; v2 = bios[0xe9]; v3 = bios[0xea]; } - outSISIDXREG(SISSR, 0x23, v1); - outSISIDXREG(SISSR, 0x24, v2); - outSISIDXREG(SISSR, 0x25, v3); - outSISIDXREG(SISSR, 0x21, 0x84); - outSISIDXREG(SISSR, 0x22, 0x00); - outSISIDXREG(SISCR, 0x37, 0x00); - orSISIDXREG(SISPART1, 0x24, 0x01); /* unlock crt2 */ - outSISIDXREG(SISPART1, 0x00, 0x00); + SiS_SetReg(SISSR, 0x23, v1); + SiS_SetReg(SISSR, 0x24, v2); + SiS_SetReg(SISSR, 0x25, v3); + SiS_SetReg(SISSR, 0x21, 0x84); + SiS_SetReg(SISSR, 0x22, 0x00); + SiS_SetReg(SISCR, 0x37, 0x00); + SiS_SetRegOR(SISPART1, 0x24, 0x01); /* unlock crt2 */ + SiS_SetReg(SISPART1, 0x00, 0x00); v1 = 0x40; v2 = 0x11; if(bios) { v1 = bios[0xec]; v2 = bios[0xeb]; } - outSISIDXREG(SISPART1, 0x02, v1); + SiS_SetReg(SISPART1, 0x02, v1); if(ivideo->revision_id >= 0x80) v2 &= ~0x01; - inSISIDXREG(SISPART4, 0x00, reg); + reg = SiS_GetReg(SISPART4, 0x00); if((reg == 1) || (reg == 2)) { - outSISIDXREG(SISCR, 0x37, 0x02); - outSISIDXREG(SISPART2, 0x00, 0x1c); + SiS_SetReg(SISCR, 0x37, 0x02); + SiS_SetReg(SISPART2, 0x00, 0x1c); v4 = 0x00; v5 = 0x00; v6 = 0x10; if(ivideo->SiS_Pr.UseROM) { v4 = bios[0xf5]; v5 = bios[0xf6]; v6 = bios[0xf7]; } - outSISIDXREG(SISPART4, 0x0d, v4); - outSISIDXREG(SISPART4, 0x0e, v5); - outSISIDXREG(SISPART4, 0x10, v6); - outSISIDXREG(SISPART4, 0x0f, 0x3f); - inSISIDXREG(SISPART4, 0x01, reg); + SiS_SetReg(SISPART4, 0x0d, v4); + SiS_SetReg(SISPART4, 0x0e, v5); + SiS_SetReg(SISPART4, 0x10, v6); + SiS_SetReg(SISPART4, 0x0f, 0x3f); + reg = SiS_GetReg(SISPART4, 0x01); if(reg >= 0xb0) { - inSISIDXREG(SISPART4, 0x23, reg); + reg = SiS_GetReg(SISPART4, 0x23); reg &= 0x20; reg <<= 1; - outSISIDXREG(SISPART4, 0x23, reg); + SiS_SetReg(SISPART4, 0x23, reg); } } else { v2 &= ~0x10; } - outSISIDXREG(SISSR, 0x32, v2); + SiS_SetReg(SISSR, 0x32, v2); - andSISIDXREG(SISPART1, 0x24, 0xfe); /* Lock CRT2 */ + SiS_SetRegAND(SISPART1, 0x24, 0xfe); /* Lock CRT2 */ - inSISIDXREG(SISSR, 0x16, reg); + reg = SiS_GetReg(SISSR, 0x16); reg &= 0xc3; - outSISIDXREG(SISCR, 0x35, reg); - outSISIDXREG(SISCR, 0x83, 0x00); + SiS_SetReg(SISCR, 0x35, reg); + SiS_SetReg(SISCR, 0x83, 0x00); #if !defined(__i386__) && !defined(__x86_64__) if(sisfb_videoram) { - outSISIDXREG(SISSR, 0x13, 0x28); /* ? */ + SiS_SetReg(SISSR, 0x13, 0x28); /* ? */ reg = ((sisfb_videoram >> 10) - 1) | 0x40; - outSISIDXREG(SISSR, 0x14, reg); + SiS_SetReg(SISSR, 0x14, reg); } else { #endif /* Need to map max FB size for finding out about RAM size */ @@ -4506,8 +4508,8 @@ sisfb_post_sis300(struct pci_dev *pdev) } else { printk(KERN_DEBUG "sisfb: Failed to map memory for size detection, assuming 8MB\n"); - outSISIDXREG(SISSR, 0x13, 0x28); /* ? */ - outSISIDXREG(SISSR, 0x14, 0x47); /* 8MB, 64bit default */ + SiS_SetReg(SISSR, 0x13, 0x28); /* ? */ + SiS_SetReg(SISSR, 0x14, 0x47); /* 8MB, 64bit default */ } #if !defined(__i386__) && !defined(__x86_64__) } @@ -4516,7 +4518,7 @@ sisfb_post_sis300(struct pci_dev *pdev) v1 = bios[0xe6]; v2 = bios[0xe7]; } else { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); if((reg & 0x30) == 0x30) { v1 = 0x04; /* PCI */ v2 = 0x92; @@ -4525,8 +4527,8 @@ sisfb_post_sis300(struct pci_dev *pdev) v2 = 0xb2; } } - outSISIDXREG(SISSR, 0x21, v1); - outSISIDXREG(SISSR, 0x22, v2); + SiS_SetReg(SISSR, 0x21, v1); + SiS_SetReg(SISSR, 0x22, v2); /* Sense CRT1 */ sisfb_sense_crt1(ivideo); @@ -4539,13 +4541,13 @@ sisfb_post_sis300(struct pci_dev *pdev) ivideo->SiS_Pr.VideoMemorySize = 8 << 20; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Display off */ - orSISIDXREG(SISSR, 0x01, 0x20); + SiS_SetRegOR(SISSR, 0x01, 0x20); /* Save mode number in CR34 */ - outSISIDXREG(SISCR, 0x34, 0x2e); + SiS_SetReg(SISCR, 0x34, 0x2e); /* Let everyone know what the current mode is */ ivideo->modeprechange = 0x2e; @@ -4568,7 +4570,7 @@ sisfb_post_xgi_delay(struct sis_video_info *ivideo, int delay) u8 reg; for(i = 0; i <= (delay * 10 * 36); i++) { - inSISIDXREG(SISSR, 0x05, reg); + reg = SiS_GetReg(SISSR, 0x05); reg++; } } @@ -4660,7 +4662,7 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) * - if running on non-x86, there usually is no VGA window * at a0000. */ - orSISIDXREG(SISSR, 0x20, (0x80 | 0x04)); + SiS_SetRegOR(SISSR, 0x20, (0x80 | 0x04)); /* Need to map max FB size for finding out about RAM size */ mapsize = ivideo->video_size; @@ -4668,76 +4670,76 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) if(!ivideo->video_vbase) { printk(KERN_ERR "sisfb: Unable to detect RAM size. Setting default.\n"); - outSISIDXREG(SISSR, 0x13, 0x35); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0x35); + SiS_SetReg(SISSR, 0x14, 0x41); /* TODO */ return; } /* Non-interleaving */ - outSISIDXREG(SISSR, 0x15, 0x00); + SiS_SetReg(SISSR, 0x15, 0x00); /* No tiling */ - outSISIDXREG(SISSR, 0x1c, 0x00); + SiS_SetReg(SISSR, 0x1c, 0x00); if(ivideo->chip == XGI_20) { channelab = 1; - inSISIDXREG(SISCR, 0x97, reg); + reg = SiS_GetReg(SISCR, 0x97); if(!(reg & 0x01)) { /* Single 32/16 */ buswidth = 32; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x52); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x52); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x02; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x31); - outSISIDXREG(SISSR, 0x14, 0x42); + SiS_SetReg(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x14, 0x42); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 23, mapsize)) goto bail_out; buswidth = 16; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x41); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x01; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; else - outSISIDXREG(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); } else { /* Dual 16/8 */ buswidth = 16; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x41); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x01; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x31); - outSISIDXREG(SISSR, 0x14, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x14, 0x31); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 22, 22, mapsize)) goto bail_out; buswidth = 8; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x30); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x30); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x00; if(sisfb_post_xgi_rwtest(ivideo, 21, 22, mapsize)) goto bail_out; else - outSISIDXREG(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); } } else { /* XGI_40 */ - inSISIDXREG(SISCR, 0x97, reg); + reg = SiS_GetReg(SISCR, 0x97); if(!(reg & 0x10)) { - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); reg >>= 1; } @@ -4745,52 +4747,52 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) buswidth = 32; if(ivideo->revision_id == 2) { channelab = 2; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x44); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x44); sr14 = 0x04; sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x34); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x34); if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; channelab = 1; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x40); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x40); sr14 = 0x00; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x30); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x30); } else { channelab = 3; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x4c); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x4c); sr14 = 0x0c; sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 25, mapsize)) goto bail_out; channelab = 2; - outSISIDXREG(SISSR, 0x14, 0x48); + SiS_SetReg(SISSR, 0x14, 0x48); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x08; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x3c); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x3c); sr14 = 0x0c; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) { channelab = 3; } else { channelab = 2; - outSISIDXREG(SISSR, 0x14, 0x38); + SiS_SetReg(SISSR, 0x14, 0x38); sr14 = 0x08; } } @@ -4801,26 +4803,26 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) buswidth = 64; if(ivideo->revision_id == 2) { channelab = 1; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x52); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x52); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x02; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x42); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x42); } else { channelab = 2; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x5a); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x5a); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x0a; if(sisfb_post_xgi_rwtest(ivideo, 24, 25, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x4a); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x4a); } sisfb_post_xgi_delay(ivideo, 1); @@ -4828,7 +4830,7 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) } bail_out: - setSISIDXREG(SISSR, 0x14, 0xf0, sr14); + SiS_SetRegANDOR(SISSR, 0x14, 0xf0, sr14); sisfb_post_xgi_delay(ivideo, 1); j = (ivideo->chip == XGI_20) ? 5 : 9; @@ -4838,13 +4840,13 @@ bail_out: reg = (ivideo->chip == XGI_20) ? dramsr13[(i * 5) + 4] : dramsr13_4[(i * 5) + 4]; - setSISIDXREG(SISSR, 0x13, 0x80, reg); + SiS_SetRegANDOR(SISSR, 0x13, 0x80, reg); sisfb_post_xgi_delay(ivideo, 50); ranksize = (ivideo->chip == XGI_20) ? dramsr13[(i * 5) + 3] : dramsr13_4[(i * 5) + 3]; - inSISIDXREG(SISSR, 0x13, reg); + reg = SiS_GetReg(SISSR, 0x13); if(reg & 0x80) ranksize <<= 1; if(ivideo->chip == XGI_20) { @@ -4863,7 +4865,7 @@ bail_out: if(!reg) continue; - setSISIDXREG(SISSR, 0x14, 0x0f, (reg & 0xf0)); + SiS_SetRegANDOR(SISSR, 0x14, 0x0f, (reg & 0xf0)); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, j, ((reg >> 4) + channelab - 2 + 20), mapsize)) @@ -4908,9 +4910,9 @@ sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb) v2 = ivideo->bios_abase[0x90 + index + 1]; v3 = ivideo->bios_abase[0x90 + index + 2]; } - outSISIDXREG(SISSR, 0x28, v1); - outSISIDXREG(SISSR, 0x29, v2); - outSISIDXREG(SISSR, 0x2a, v3); + SiS_SetReg(SISSR, 0x28, v1); + SiS_SetReg(SISSR, 0x29, v2); + SiS_SetReg(SISSR, 0x2a, v3); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); @@ -4921,9 +4923,9 @@ sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb) v2 = ivideo->bios_abase[0xb8 + index + 1]; v3 = ivideo->bios_abase[0xb8 + index + 2]; } - outSISIDXREG(SISSR, 0x2e, v1); - outSISIDXREG(SISSR, 0x2f, v2); - outSISIDXREG(SISSR, 0x30, v3); + SiS_SetReg(SISSR, 0x2e, v1); + SiS_SetReg(SISSR, 0x2f, v2); + SiS_SetReg(SISSR, 0x30, v3); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); @@ -4996,29 +4998,29 @@ sisfb_post_xgi(struct pci_dev *pdev) }; /* VGA enable */ - reg = inSISREG(SISVGAENABLE) | 0x01; - outSISREG(SISVGAENABLE, reg); + reg = SiS_GetRegByte(SISVGAENABLE) | 0x01; + SiS_SetRegByte(SISVGAENABLE, reg); /* Misc */ - reg = inSISREG(SISMISCR) | 0x01; - outSISREG(SISMISCW, reg); + reg = SiS_GetRegByte(SISMISCR) | 0x01; + SiS_SetRegByte(SISMISCW, reg); /* Unlock SR */ - outSISIDXREG(SISSR, 0x05, 0x86); - inSISIDXREG(SISSR, 0x05, reg); + SiS_SetReg(SISSR, 0x05, 0x86); + reg = SiS_GetReg(SISSR, 0x05); if(reg != 0xa1) return 0; /* Clear some regs */ for(i = 0; i < 0x22; i++) { if(0x06 + i == 0x20) continue; - outSISIDXREG(SISSR, 0x06 + i, 0x00); + SiS_SetReg(SISSR, 0x06 + i, 0x00); } for(i = 0; i < 0x0b; i++) { - outSISIDXREG(SISSR, 0x31 + i, 0x00); + SiS_SetReg(SISSR, 0x31 + i, 0x00); } for(i = 0; i < 0x10; i++) { - outSISIDXREG(SISCR, 0x30 + i, 0x00); + SiS_SetReg(SISCR, 0x30 + i, 0x00); } ptr = cs78; @@ -5026,7 +5028,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x78]; } for(i = 0; i < 3; i++) { - outSISIDXREG(SISSR, 0x23 + i, ptr[i]); + SiS_SetReg(SISSR, 0x23 + i, ptr[i]); } ptr = cs76; @@ -5034,7 +5036,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x76]; } for(i = 0; i < 2; i++) { - outSISIDXREG(SISSR, 0x21 + i, ptr[i]); + SiS_SetReg(SISSR, 0x21 + i, ptr[i]); } v1 = 0x18; v2 = 0x00; @@ -5042,83 +5044,83 @@ sisfb_post_xgi(struct pci_dev *pdev) v1 = bios[0x74]; v2 = bios[0x75]; } - outSISIDXREG(SISSR, 0x07, v1); - outSISIDXREG(SISSR, 0x11, 0x0f); - outSISIDXREG(SISSR, 0x1f, v2); + SiS_SetReg(SISSR, 0x07, v1); + SiS_SetReg(SISSR, 0x11, 0x0f); + SiS_SetReg(SISSR, 0x1f, v2); /* PCI linear mode, RelIO enabled, A0000 decoding disabled */ - outSISIDXREG(SISSR, 0x20, 0x80 | 0x20 | 0x04); - outSISIDXREG(SISSR, 0x27, 0x74); + SiS_SetReg(SISSR, 0x20, 0x80 | 0x20 | 0x04); + SiS_SetReg(SISSR, 0x27, 0x74); ptr = cs7b; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x7b]; } for(i = 0; i < 3; i++) { - outSISIDXREG(SISSR, 0x31 + i, ptr[i]); + SiS_SetReg(SISSR, 0x31 + i, ptr[i]); } if(ivideo->chip == XGI_40) { if(ivideo->revision_id == 2) { - setSISIDXREG(SISSR, 0x3b, 0x3f, 0xc0); + SiS_SetRegANDOR(SISSR, 0x3b, 0x3f, 0xc0); } - outSISIDXREG(SISCR, 0x7d, 0xfe); - outSISIDXREG(SISCR, 0x7e, 0x0f); + SiS_SetReg(SISCR, 0x7d, 0xfe); + SiS_SetReg(SISCR, 0x7e, 0x0f); } if(ivideo->revision_id == 0) { /* 40 *and* 20? */ - andSISIDXREG(SISCR, 0x58, 0xd7); - inSISIDXREG(SISCR, 0xcb, reg); + SiS_SetRegAND(SISCR, 0x58, 0xd7); + reg = SiS_GetReg(SISCR, 0xcb); if(reg & 0x20) { - setSISIDXREG(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */ + SiS_SetRegANDOR(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */ } } reg = (ivideo->chip == XGI_40) ? 0x20 : 0x00; - setSISIDXREG(SISCR, 0x38, 0x1f, reg); + SiS_SetRegANDOR(SISCR, 0x38, 0x1f, reg); if(ivideo->chip == XGI_20) { - outSISIDXREG(SISSR, 0x36, 0x70); + SiS_SetReg(SISSR, 0x36, 0x70); } else { - outSISIDXREG(SISVID, 0x00, 0x86); - outSISIDXREG(SISVID, 0x32, 0x00); - outSISIDXREG(SISVID, 0x30, 0x00); - outSISIDXREG(SISVID, 0x32, 0x01); - outSISIDXREG(SISVID, 0x30, 0x00); - andSISIDXREG(SISVID, 0x2f, 0xdf); - andSISIDXREG(SISCAP, 0x00, 0x3f); - - outSISIDXREG(SISPART1, 0x2f, 0x01); - outSISIDXREG(SISPART1, 0x00, 0x00); - outSISIDXREG(SISPART1, 0x02, bios[0x7e]); - outSISIDXREG(SISPART1, 0x2e, 0x08); - andSISIDXREG(SISPART1, 0x35, 0x7f); - andSISIDXREG(SISPART1, 0x50, 0xfe); - - inSISIDXREG(SISPART4, 0x00, reg); + SiS_SetReg(SISVID, 0x00, 0x86); + SiS_SetReg(SISVID, 0x32, 0x00); + SiS_SetReg(SISVID, 0x30, 0x00); + SiS_SetReg(SISVID, 0x32, 0x01); + SiS_SetReg(SISVID, 0x30, 0x00); + SiS_SetRegAND(SISVID, 0x2f, 0xdf); + SiS_SetRegAND(SISCAP, 0x00, 0x3f); + + SiS_SetReg(SISPART1, 0x2f, 0x01); + SiS_SetReg(SISPART1, 0x00, 0x00); + SiS_SetReg(SISPART1, 0x02, bios[0x7e]); + SiS_SetReg(SISPART1, 0x2e, 0x08); + SiS_SetRegAND(SISPART1, 0x35, 0x7f); + SiS_SetRegAND(SISPART1, 0x50, 0xfe); + + reg = SiS_GetReg(SISPART4, 0x00); if(reg == 1 || reg == 2) { - outSISIDXREG(SISPART2, 0x00, 0x1c); - outSISIDXREG(SISPART4, 0x0d, bios[0x7f]); - outSISIDXREG(SISPART4, 0x0e, bios[0x80]); - outSISIDXREG(SISPART4, 0x10, bios[0x81]); - andSISIDXREG(SISPART4, 0x0f, 0x3f); + SiS_SetReg(SISPART2, 0x00, 0x1c); + SiS_SetReg(SISPART4, 0x0d, bios[0x7f]); + SiS_SetReg(SISPART4, 0x0e, bios[0x80]); + SiS_SetReg(SISPART4, 0x10, bios[0x81]); + SiS_SetRegAND(SISPART4, 0x0f, 0x3f); - inSISIDXREG(SISPART4, 0x01, reg); + reg = SiS_GetReg(SISPART4, 0x01); if((reg & 0xf0) >= 0xb0) { - inSISIDXREG(SISPART4, 0x23, reg); + reg = SiS_GetReg(SISPART4, 0x23); if(reg & 0x20) reg |= 0x40; - outSISIDXREG(SISPART4, 0x23, reg); + SiS_SetReg(SISPART4, 0x23, reg); reg = (reg & 0x20) ? 0x02 : 0x00; - setSISIDXREG(SISPART1, 0x1e, 0xfd, reg); + SiS_SetRegANDOR(SISPART1, 0x1e, 0xfd, reg); } } v1 = bios[0x77]; - inSISIDXREG(SISSR, 0x3b, reg); + reg = SiS_GetReg(SISSR, 0x3b); if(reg & 0x02) { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); v2 = (reg & 0x30) >> 3; if(!(v2 & 0x04)) v2 ^= 0x02; - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); if(reg & 0x80) v2 |= 0x80; v2 |= 0x01; @@ -5151,36 +5153,36 @@ sisfb_post_xgi(struct pci_dev *pdev) v2 |= 0x08; } } - setSISIDXREG(SISCR, 0x5f, 0xf0, v2); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf0, v2); } - outSISIDXREG(SISSR, 0x22, v1); + SiS_SetReg(SISSR, 0x22, v1); if(ivideo->revision_id == 2) { - inSISIDXREG(SISSR, 0x3b, v1); - inSISIDXREG(SISSR, 0x3a, v2); + v1 = SiS_GetReg(SISSR, 0x3b); + v2 = SiS_GetReg(SISSR, 0x3a); regd = bios[0x90 + 3] | (bios[0x90 + 4] << 8); if( (!(v1 & 0x02)) && (v2 & 0x30) && (regd < 0xcf) ) - setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf1, 0x01); if((mypdev = pci_get_device(0x10de, 0x01e0, NULL))) { /* TODO: set CR5f &0xf1 | 0x01 for version 6570 * of nforce 2 ROM */ if(0) - setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf1, 0x01); pci_dev_put(mypdev); } } v1 = 0x30; - inSISIDXREG(SISSR, 0x3b, reg); - inSISIDXREG(SISCR, 0x5f, v2); + reg = SiS_GetReg(SISSR, 0x3b); + v2 = SiS_GetReg(SISCR, 0x5f); if((!(reg & 0x02)) && (v2 & 0x0e)) v1 |= 0x08; - outSISIDXREG(SISSR, 0x27, v1); + SiS_SetReg(SISSR, 0x27, v1); if(bios[0x64] & 0x01) { - setSISIDXREG(SISCR, 0x5f, 0xf0, bios[0x64]); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf0, bios[0x64]); } v1 = bios[0x4f7]; @@ -5188,27 +5190,27 @@ sisfb_post_xgi(struct pci_dev *pdev) regd = (regd >> 20) & 0x0f; if(regd == 1) { v1 &= 0xfc; - orSISIDXREG(SISCR, 0x5f, 0x08); - } - outSISIDXREG(SISCR, 0x48, v1); - - setSISIDXREG(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb); - setSISIDXREG(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f); - setSISIDXREG(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f); - setSISIDXREG(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7); - setSISIDXREG(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f); - outSISIDXREG(SISCR, 0x70, bios[0x4fc]); - setSISIDXREG(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f); - outSISIDXREG(SISCR, 0x74, 0xd0); - setSISIDXREG(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30); - setSISIDXREG(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f); - setSISIDXREG(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f); + SiS_SetRegOR(SISCR, 0x5f, 0x08); + } + SiS_SetReg(SISCR, 0x48, v1); + + SiS_SetRegANDOR(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb); + SiS_SetRegANDOR(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f); + SiS_SetRegANDOR(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f); + SiS_SetRegANDOR(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7); + SiS_SetRegANDOR(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f); + SiS_SetReg(SISCR, 0x70, bios[0x4fc]); + SiS_SetRegANDOR(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f); + SiS_SetReg(SISCR, 0x74, 0xd0); + SiS_SetRegANDOR(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30); + SiS_SetRegANDOR(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f); + SiS_SetRegANDOR(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f); v1 = bios[0x501]; if((mypdev = pci_get_device(0x8086, 0x2530, NULL))) { v1 = 0xf0; pci_dev_put(mypdev); } - outSISIDXREG(SISCR, 0x77, v1); + SiS_SetReg(SISCR, 0x77, v1); } /* RAM type */ @@ -5219,14 +5221,14 @@ sisfb_post_xgi(struct pci_dev *pdev) if(ivideo->haveXGIROM) { v1 = bios[0x140 + regb]; } - outSISIDXREG(SISCR, 0x6d, v1); + SiS_SetReg(SISCR, 0x6d, v1); ptr = cs128; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x128]; } for(i = 0, j = 0; i < 3; i++, j += 8) { - outSISIDXREG(SISCR, 0x68 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x68 + i, ptr[j + regb]); } ptr = cs31a; @@ -5250,14 +5252,14 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x04; if(regd & 0x02) reg |= 0x08; regd >>= 2; - outSISIDXREG(SISCR, rega, reg); - inSISIDXREG(SISCR, rega, reg); - inSISIDXREG(SISCR, rega, reg); + SiS_SetReg(SISCR, rega, reg); + reg = SiS_GetReg(SISCR, rega); + reg = SiS_GetReg(SISCR, rega); reg += 0x10; } } - andSISIDXREG(SISCR, 0x6e, 0xfc); + SiS_SetRegAND(SISCR, 0x6e, 0xfc); ptr = NULL; if(ivideo->haveXGIROM) { @@ -5265,7 +5267,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[index]; } for(i = 0; i < 4; i++) { - setSISIDXREG(SISCR, 0x6e, 0xfc, i); + SiS_SetRegANDOR(SISCR, 0x6e, 0xfc, i); reg = 0x00; for(j = 0; j < 2; j++) { regd = 0; @@ -5279,9 +5281,9 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x01; if(regd & 0x02) reg |= 0x02; regd >>= 2; - outSISIDXREG(SISCR, 0x6f, reg); - inSISIDXREG(SISCR, 0x6f, reg); - inSISIDXREG(SISCR, 0x6f, reg); + SiS_SetReg(SISCR, 0x6f, reg); + reg = SiS_GetReg(SISCR, 0x6f); + reg = SiS_GetReg(SISCR, 0x6f); reg += 0x08; } } @@ -5292,10 +5294,10 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x148]; } for(i = 0, j = 0; i < 2; i++, j += 8) { - outSISIDXREG(SISCR, 0x80 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x80 + i, ptr[j + regb]); } - andSISIDXREG(SISCR, 0x89, 0x8f); + SiS_SetRegAND(SISCR, 0x89, 0x8f); ptr = cs45a; if(ivideo->haveXGIROM) { @@ -5309,9 +5311,9 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x01; if(regd & 0x02) reg |= 0x02; regd >>= 2; - outSISIDXREG(SISCR, 0x89, reg); - inSISIDXREG(SISCR, 0x89, reg); - inSISIDXREG(SISCR, 0x89, reg); + SiS_SetReg(SISCR, 0x89, reg); + reg = SiS_GetReg(SISCR, 0x89); + reg = SiS_GetReg(SISCR, 0x89); reg += 0x10; } @@ -5322,27 +5324,27 @@ sisfb_post_xgi(struct pci_dev *pdev) v3 = bios[0x120 + regb]; v4 = bios[0x1ca]; } - outSISIDXREG(SISCR, 0x45, v1 & 0x0f); - outSISIDXREG(SISCR, 0x99, (v1 >> 4) & 0x07); - orSISIDXREG(SISCR, 0x40, v1 & 0x80); - outSISIDXREG(SISCR, 0x41, v2); + SiS_SetReg(SISCR, 0x45, v1 & 0x0f); + SiS_SetReg(SISCR, 0x99, (v1 >> 4) & 0x07); + SiS_SetRegOR(SISCR, 0x40, v1 & 0x80); + SiS_SetReg(SISCR, 0x41, v2); ptr = cs170; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x170]; } for(i = 0, j = 0; i < 7; i++, j += 8) { - outSISIDXREG(SISCR, 0x90 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x90 + i, ptr[j + regb]); } - outSISIDXREG(SISCR, 0x59, v3); + SiS_SetReg(SISCR, 0x59, v3); ptr = cs1a8; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x1a8]; } for(i = 0, j = 0; i < 3; i++, j += 8) { - outSISIDXREG(SISCR, 0xc3 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0xc3 + i, ptr[j + regb]); } ptr = cs100; @@ -5350,27 +5352,27 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x100]; } for(i = 0, j = 0; i < 2; i++, j += 8) { - outSISIDXREG(SISCR, 0x8a + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x8a + i, ptr[j + regb]); } - outSISIDXREG(SISCR, 0xcf, v4); + SiS_SetReg(SISCR, 0xcf, v4); - outSISIDXREG(SISCR, 0x83, 0x09); - outSISIDXREG(SISCR, 0x87, 0x00); + SiS_SetReg(SISCR, 0x83, 0x09); + SiS_SetReg(SISCR, 0x87, 0x00); if(ivideo->chip == XGI_40) { if( (ivideo->revision_id == 1) || (ivideo->revision_id == 2) ) { - outSISIDXREG(SISCR, 0x8c, 0x87); + SiS_SetReg(SISCR, 0x8c, 0x87); } } - outSISIDXREG(SISSR, 0x17, 0x00); - outSISIDXREG(SISSR, 0x1a, 0x87); + SiS_SetReg(SISSR, 0x17, 0x00); + SiS_SetReg(SISSR, 0x1a, 0x87); if(ivideo->chip == XGI_20) { - outSISIDXREG(SISSR, 0x15, 0x00); - outSISIDXREG(SISSR, 0x1c, 0x00); + SiS_SetReg(SISSR, 0x15, 0x00); + SiS_SetReg(SISSR, 0x1c, 0x00); } ramtype = 0x00; v1 = 0x10; @@ -5380,16 +5382,16 @@ sisfb_post_xgi(struct pci_dev *pdev) } if(!(ramtype & 0x80)) { if(ivideo->chip == XGI_20) { - outSISIDXREG(SISCR, 0x97, v1); - inSISIDXREG(SISCR, 0x97, reg); + SiS_SetReg(SISCR, 0x97, v1); + reg = SiS_GetReg(SISCR, 0x97); if(reg & 0x10) { ramtype = (reg & 0x01) << 1; } } else { - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); ramtype = reg & 0x02; if(!(ramtype)) { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); ramtype = (reg >> 1) & 0x01; } } @@ -5410,55 +5412,55 @@ sisfb_post_xgi(struct pci_dev *pdev) v2 = bios[regb + 0x160]; v3 = bios[regb + 0x168]; } - outSISIDXREG(SISCR, 0x82, v1); - outSISIDXREG(SISCR, 0x85, v2); - outSISIDXREG(SISCR, 0x86, v3); + SiS_SetReg(SISCR, 0x82, v1); + SiS_SetReg(SISCR, 0x85, v2); + SiS_SetReg(SISCR, 0x86, v3); } else { - outSISIDXREG(SISCR, 0x82, 0x88); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]); - outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]); + SiS_SetReg(SISCR, 0x82, 0x88); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, bios[regb + 0x168]); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, bios[regb + 0x160]); + SiS_SetReg(SISCR, 0x82, bios[regb + 0x158]); } if(ivideo->chip == XGI_40) { - outSISIDXREG(SISCR, 0x97, 0x00); + SiS_SetReg(SISCR, 0x97, 0x00); } - outSISIDXREG(SISCR, 0x98, 0x01); - outSISIDXREG(SISCR, 0x9a, 0x02); + SiS_SetReg(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x9a, 0x02); - outSISIDXREG(SISSR, 0x18, 0x01); + SiS_SetReg(SISSR, 0x18, 0x01); if((ivideo->chip == XGI_20) || (ivideo->revision_id == 2)) { - outSISIDXREG(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x19, 0x40); } else { - outSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x19, 0x20); } - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); if((ivideo->chip == XGI_20) || (bios[0x1cb] != 0x0c)) { sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x18, 0x00); if((ivideo->chip == XGI_20) || (ivideo->revision_id == 2)) { - outSISIDXREG(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x19, 0x40); } else { - outSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x19, 0x20); } } else if((ivideo->chip == XGI_40) && (bios[0x1cb] == 0x0c)) { - /* outSISIDXREG(SISSR, 0x16, 0x0c); */ /* ? */ + /* SiS_SetReg(SISSR, 0x16, 0x0c); */ /* ? */ } - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 4); v1 = 0x31; v2 = 0x03; v3 = 0x83; v4 = 0x03; v5 = 0x83; if(ivideo->haveXGIROM) { @@ -5469,74 +5471,74 @@ sisfb_post_xgi(struct pci_dev *pdev) v4 = bios[index + 2]; v5 = bios[index + 3]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01)); - outSISIDXREG(SISSR, 0x16, v2); - outSISIDXREG(SISSR, 0x16, v3); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01)); + SiS_SetReg(SISSR, 0x16, v2); + SiS_SetReg(SISSR, 0x16, v3); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x1b, 0x03); + SiS_SetReg(SISSR, 0x1b, 0x03); sisfb_post_xgi_delay(ivideo, 0x22); - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x00); - outSISIDXREG(SISSR, 0x16, v4); - outSISIDXREG(SISSR, 0x16, v5); - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x00); + SiS_SetReg(SISSR, 0x16, v4); + SiS_SetReg(SISSR, 0x16, v5); + SiS_SetReg(SISSR, 0x1b, 0x00); break; case 1: - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - inSISIDXREG(SISCR, 0x86, reg); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + reg = SiS_GetReg(SISCR, 0x86); v1 = cs168[regb]; v2 = cs160[regb]; v3 = cs158[regb]; if(ivideo->haveXGIROM) { v1 = bios[regb + 0x168]; v2 = bios[regb + 0x160]; v3 = bios[regb + 0x158]; } - outSISIDXREG(SISCR, 0x86, v1); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, v2); - outSISIDXREG(SISCR, 0x82, v3); - outSISIDXREG(SISCR, 0x98, 0x01); - outSISIDXREG(SISCR, 0x9a, 0x02); - - outSISIDXREG(SISSR, 0x28, 0x64); - outSISIDXREG(SISSR, 0x29, 0x63); + SiS_SetReg(SISCR, 0x86, v1); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, v2); + SiS_SetReg(SISCR, 0x82, v3); + SiS_SetReg(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x9a, 0x02); + + SiS_SetReg(SISSR, 0x28, 0x64); + SiS_SetReg(SISSR, 0x29, 0x63); sisfb_post_xgi_delay(ivideo, 15); - outSISIDXREG(SISSR, 0x18, 0x00); - outSISIDXREG(SISSR, 0x19, 0x20); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); - outSISIDXREG(SISSR, 0x18, 0xc5); - outSISIDXREG(SISSR, 0x19, 0x23); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0xc5); + SiS_SetReg(SISSR, 0x19, 0x23); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISCR, 0x97,0x11); + SiS_SetReg(SISCR, 0x97, 0x11); sisfb_post_xgi_setclocks(ivideo, regb); sisfb_post_xgi_delay(ivideo, 0x46); - outSISIDXREG(SISSR, 0x18, 0xc5); - outSISIDXREG(SISSR, 0x19, 0x23); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0xc5); + SiS_SetReg(SISSR, 0x19, 0x23); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISSR, 0x1b, 0x04); + SiS_SetReg(SISSR, 0x1b, 0x04); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x1b, 0x00); sisfb_post_xgi_delay(ivideo, 1); v1 = 0x31; if(ivideo->haveXGIROM) { v1 = bios[0xf0]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x06); - outSISIDXREG(SISSR, 0x16, 0x04); - outSISIDXREG(SISSR, 0x16, 0x84); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x06); + SiS_SetReg(SISSR, 0x16, 0x04); + SiS_SetReg(SISSR, 0x16, 0x84); sisfb_post_xgi_delay(ivideo, 1); break; default: @@ -5544,85 +5546,85 @@ sisfb_post_xgi(struct pci_dev *pdev) if((ivideo->chip == XGI_40) && ((ivideo->revision_id == 1) || (ivideo->revision_id == 2))) { - outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]); - outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]); - outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]); + SiS_SetReg(SISCR, 0x82, bios[regb + 0x158]); + SiS_SetReg(SISCR, 0x85, bios[regb + 0x160]); + SiS_SetReg(SISCR, 0x86, bios[regb + 0x168]); } else { - outSISIDXREG(SISCR, 0x82, 0x88); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); + SiS_SetReg(SISCR, 0x82, 0x88); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); v1 = cs160[regb]; v2 = cs158[regb]; if(ivideo->haveXGIROM) { v1 = bios[regb + 0x160]; v2 = bios[regb + 0x158]; } - outSISIDXREG(SISCR, 0x85, v1); - outSISIDXREG(SISCR, 0x82, v2); + SiS_SetReg(SISCR, 0x85, v1); + SiS_SetReg(SISCR, 0x82, v2); } if(ivideo->chip == XGI_40) { - outSISIDXREG(SISCR, 0x97, 0x11); + SiS_SetReg(SISCR, 0x97, 0x11); } if((ivideo->chip == XGI_40) && (ivideo->revision_id == 2)) { - outSISIDXREG(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x98, 0x01); } else { - outSISIDXREG(SISCR, 0x98, 0x03); + SiS_SetReg(SISCR, 0x98, 0x03); } - outSISIDXREG(SISCR, 0x9a, 0x02); + SiS_SetReg(SISCR, 0x9a, 0x02); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x18, 0x01); + SiS_SetReg(SISSR, 0x18, 0x01); } else { - outSISIDXREG(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x18, 0x00); } - outSISIDXREG(SISSR, 0x19, 0x40); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); if((ivideo->chip == XGI_40) && (bios[0x1cb] != 0x0c)) { sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x18, 0x00); - outSISIDXREG(SISSR, 0x19, 0x40); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); } sisfb_post_xgi_delay(ivideo, 4); v1 = 0x31; if(ivideo->haveXGIROM) { v1 = bios[0xf0]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x01); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x01); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x16, bios[0x53e]); - outSISIDXREG(SISSR, 0x16, bios[0x53f]); + SiS_SetReg(SISSR, 0x16, bios[0x53e]); + SiS_SetReg(SISSR, 0x16, bios[0x53f]); } else { - outSISIDXREG(SISSR, 0x16, 0x05); - outSISIDXREG(SISSR, 0x16, 0x85); + SiS_SetReg(SISSR, 0x16, 0x05); + SiS_SetReg(SISSR, 0x16, 0x85); } sisfb_post_xgi_delay(ivideo, 0x43); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x1b, 0x01); + SiS_SetReg(SISSR, 0x1b, 0x01); } else { - outSISIDXREG(SISSR, 0x1b, 0x03); + SiS_SetReg(SISSR, 0x1b, 0x03); } sisfb_post_xgi_delay(ivideo, 0x22); - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x00); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x00); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x16, bios[0x540]); - outSISIDXREG(SISSR, 0x16, bios[0x541]); + SiS_SetReg(SISSR, 0x16, bios[0x540]); + SiS_SetReg(SISSR, 0x16, bios[0x541]); } else { - outSISIDXREG(SISSR, 0x16, 0x05); - outSISIDXREG(SISSR, 0x16, 0x85); + SiS_SetReg(SISSR, 0x16, 0x05); + SiS_SetReg(SISSR, 0x16, 0x85); } - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x1b, 0x00); } regb = 0; /* ! */ @@ -5630,7 +5632,7 @@ sisfb_post_xgi(struct pci_dev *pdev) if(ivideo->haveXGIROM) { v1 = bios[0x110 + regb]; } - outSISIDXREG(SISSR, 0x1b, v1); + SiS_SetReg(SISSR, 0x1b, v1); /* RAM size */ v1 = 0x00; v2 = 0x00; @@ -5642,8 +5644,8 @@ sisfb_post_xgi(struct pci_dev *pdev) regd = 1 << regb; if((v1 & 0x40) && (v2 & regd) && ivideo->haveXGIROM) { - outSISIDXREG(SISSR, 0x13, bios[regb + 0xe0]); - outSISIDXREG(SISSR, 0x14, bios[regb + 0xe0 + 8]); + SiS_SetReg(SISSR, 0x13, bios[regb + 0xe0]); + SiS_SetReg(SISSR, 0x14, bios[regb + 0xe0 + 8]); } else { @@ -5655,24 +5657,24 @@ sisfb_post_xgi(struct pci_dev *pdev) ivideo->SiS_Pr.VideoMemorySize = 8 << 20; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Disable read-cache */ - andSISIDXREG(SISSR, 0x21, 0xdf); + SiS_SetRegAND(SISSR, 0x21, 0xdf); sisfb_post_xgi_ramsize(ivideo); /* Enable read-cache */ - orSISIDXREG(SISSR, 0x21, 0x20); + SiS_SetRegOR(SISSR, 0x21, 0x20); } #if 0 printk(KERN_DEBUG "-----------------\n"); for(i = 0; i < 0xff; i++) { - inSISIDXREG(SISCR, i, reg); + reg = SiS_GetReg(SISCR, i); printk(KERN_DEBUG "CR%02x(%x) = 0x%02x\n", i, SISCR, reg); } for(i = 0; i < 0x40; i++) { - inSISIDXREG(SISSR, i, reg); + reg = SiS_GetReg(SISSR, i); printk(KERN_DEBUG "SR%02x(%x) = 0x%02x\n", i, SISSR, reg); } printk(KERN_DEBUG "-----------------\n"); @@ -5680,13 +5682,13 @@ sisfb_post_xgi(struct pci_dev *pdev) /* Sense CRT1 */ if(ivideo->chip == XGI_20) { - orSISIDXREG(SISCR, 0x32, 0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } else { - inSISIDXREG(SISPART4, 0x00, reg); + reg = SiS_GetReg(SISPART4, 0x00); if((reg == 1) || (reg == 2)) { sisfb_sense_crt1(ivideo); } else { - orSISIDXREG(SISCR, 0x32, 0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } } @@ -5697,20 +5699,20 @@ sisfb_post_xgi(struct pci_dev *pdev) ivideo->curFSTN = ivideo->curDSTN = 0; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Display off */ - orSISIDXREG(SISSR, 0x01, 0x20); + SiS_SetRegOR(SISSR, 0x01, 0x20); /* Save mode number in CR34 */ - outSISIDXREG(SISCR, 0x34, 0x2e); + SiS_SetReg(SISCR, 0x34, 0x2e); /* Let everyone know what the current mode is */ ivideo->modeprechange = 0x2e; if(ivideo->chip == XGI_40) { - inSISIDXREG(SISCR, 0xca, reg); - inSISIDXREG(SISCR, 0xcc, v1); + reg = SiS_GetReg(SISCR, 0xca); + v1 = SiS_GetReg(SISCR, 0xcc); if((reg & 0x10) && (!(v1 & 0x04))) { printk(KERN_ERR "sisfb: Please connect power to the card.\n"); @@ -5953,7 +5955,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } #endif - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); if( (!ivideo->sisvga_enabled) #if !defined(__i386__) && !defined(__x86_64__) @@ -5961,13 +5963,13 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #endif ) { for(i = 0x30; i <= 0x3f; i++) { - outSISIDXREG(SISCR, i, 0x00); + SiS_SetReg(SISCR, i, 0x00); } } /* Find out about current video mode */ ivideo->modeprechange = 0x03; - inSISIDXREG(SISCR, 0x34, reg); + reg = SiS_GetReg(SISCR, 0x34); if(reg & 0x7f) { ivideo->modeprechange = reg & 0x7f; } else if(ivideo->sisvga_enabled) { @@ -6064,9 +6066,9 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if((ivideo->sisfb_mode_idx < 0) || ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) { /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */ - orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE)); + SiS_SetRegOR(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE)); /* Enable 2D accelerator engine */ - orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D); + SiS_SetRegOR(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D); } if(sisfb_pdc != 0xff) { diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index dee64c3b1e6..2ab704118c4 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -536,7 +536,7 @@ static int sstfb_set_par(struct fb_info *info) fbiinit2 = sst_read(FBIINIT2); fbiinit3 = sst_read(FBIINIT3); - /* everything is reset. we enable fbiinit2/3 remap : dac acces ok */ + /* everything is reset. we enable fbiinit2/3 remap : dac access ok */ pci_write_config_dword(sst_dev, PCI_INIT_ENABLE, PCI_EN_INIT_WR | PCI_REMAP_DAC ); diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c new file mode 100644 index 00000000000..2c8364e9b63 --- /dev/null +++ b/drivers/video/udlfb.c @@ -0,0 +1,1878 @@ +/* + * udlfb.c -- Framebuffer driver for DisplayLink USB controller + * + * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> + * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + * + * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, + * usb-skeleton by GregKH. + * + * Device-specific portions based on information from Displaylink, with work + * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/uaccess.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <video/udlfb.h> +#include "edid.h" + +static struct fb_fix_screeninfo dlfb_fix = { + .id = "udlfb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | + FBINFO_VIRTFB | + FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; + +/* + * There are many DisplayLink-based products, all with unique PIDs. We are able + * to support all volume ones (circa 2009) with a single driver, so we match + * globally on VID. TODO: Probe() needs to detect when we might be running + * "future" chips, and bail on those, so a compatible driver can match. + */ +static struct usb_device_id id_table[] = { + {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,}, + {}, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* module options */ +static int console; /* Optionally allow fbcon to consume first framebuffer */ +static int fb_defio; /* Optionally enable experimental fb_defio mmap support */ + +/* dlfb keeps a list of urbs for efficient bulk transfers */ +static void dlfb_urb_completion(struct urb *urb); +static struct urb *dlfb_get_urb(struct dlfb_data *dev); +static int dlfb_submit_urb(struct dlfb_data *dev, struct urb * urb, size_t len); +static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size); +static void dlfb_free_urb_list(struct dlfb_data *dev); + +/* + * All DisplayLink bulk operations start with 0xAF, followed by specific code + * All operations are written to buffers which then later get sent to device + */ +static char *dlfb_set_register(char *buf, u8 reg, u8 val) +{ + *buf++ = 0xAF; + *buf++ = 0x20; + *buf++ = reg; + *buf++ = val; + return buf; +} + +static char *dlfb_vidreg_lock(char *buf) +{ + return dlfb_set_register(buf, 0xFF, 0x00); +} + +static char *dlfb_vidreg_unlock(char *buf) +{ + return dlfb_set_register(buf, 0xFF, 0xFF); +} + +/* + * On/Off for driving the DisplayLink framebuffer to the display + * 0x00 H and V sync on + * 0x01 H and V sync off (screen blank but powered) + * 0x07 DPMS powerdown (requires modeset to come back) + */ +static char *dlfb_enable_hvsync(char *buf, bool enable) +{ + if (enable) + return dlfb_set_register(buf, 0x1F, 0x00); + else + return dlfb_set_register(buf, 0x1F, 0x07); +} + +static char *dlfb_set_color_depth(char *buf, u8 selection) +{ + return dlfb_set_register(buf, 0x00, selection); +} + +static char *dlfb_set_base16bpp(char *wrptr, u32 base) +{ + /* the base pointer is 16 bits wide, 0x20 is hi byte. */ + wrptr = dlfb_set_register(wrptr, 0x20, base >> 16); + wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); + return dlfb_set_register(wrptr, 0x22, base); +} + +/* + * DisplayLink HW has separate 16bpp and 8bpp framebuffers. + * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer + */ +static char *dlfb_set_base8bpp(char *wrptr, u32 base) +{ + wrptr = dlfb_set_register(wrptr, 0x26, base >> 16); + wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); + return dlfb_set_register(wrptr, 0x28, base); +} + +static char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value) +{ + wrptr = dlfb_set_register(wrptr, reg, value >> 8); + return dlfb_set_register(wrptr, reg+1, value); +} + +/* + * This is kind of weird because the controller takes some + * register values in a different byte order than other registers. + */ +static char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value) +{ + wrptr = dlfb_set_register(wrptr, reg, value); + return dlfb_set_register(wrptr, reg+1, value >> 8); +} + +/* + * LFSR is linear feedback shift register. The reason we have this is + * because the display controller needs to minimize the clock depth of + * various counters used in the display path. So this code reverses the + * provided value into the lfsr16 value by counting backwards to get + * the value that needs to be set in the hardware comparator to get the + * same actual count. This makes sense once you read above a couple of + * times and think about it from a hardware perspective. + */ +static u16 dlfb_lfsr16(u16 actual_count) +{ + u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ + + while (actual_count--) { + lv = ((lv << 1) | + (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) + & 0xFFFF; + } + + return (u16) lv; +} + +/* + * This does LFSR conversion on the value that is to be written. + * See LFSR explanation above for more detail. + */ +static char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value) +{ + return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value)); +} + +/* + * This takes a standard fbdev screeninfo struct and all of its monitor mode + * details and converts them into the DisplayLink equivalent register commands. + */ +static char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var) +{ + u16 xds, yds; + u16 xde, yde; + u16 yec; + + /* x display start */ + xds = var->left_margin + var->hsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); + /* x display end */ + xde = xds + var->xres; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde); + + /* y display start */ + yds = var->upper_margin + var->vsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); + /* y display end */ + yde = yds + var->yres; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde); + + /* x end count is active + blanking - 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x09, + xde + var->right_margin - 1); + + /* libdlo hardcodes hsync start to 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1); + + /* hsync end is width of sync pulse + 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1); + + /* hpixels is active pixels */ + wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres); + + /* yendcount is vertical active + vertical blanking */ + yec = var->yres + var->upper_margin + var->lower_margin + + var->vsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec); + + /* libdlo hardcodes vsync start to 0 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0); + + /* vsync end is width of vsync pulse */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len); + + /* vpixels is active pixels */ + wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres); + + /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */ + wrptr = dlfb_set_register_16be(wrptr, 0x1B, + 200*1000*1000/var->pixclock); + + return wrptr; +} + +/* + * This takes a standard fbdev screeninfo struct that was fetched or prepared + * and then generates the appropriate command sequence that then drives the + * display controller. + */ +static int dlfb_set_video_mode(struct dlfb_data *dev, + struct fb_var_screeninfo *var) +{ + char *buf; + char *wrptr; + int retval = 0; + int writesize; + struct urb *urb; + + if (!atomic_read(&dev->usb_active)) + return -EPERM; + + urb = dlfb_get_urb(dev); + if (!urb) + return -ENOMEM; + + buf = (char *) urb->transfer_buffer; + + /* + * This first section has to do with setting the base address on the + * controller * associated with the display. There are 2 base + * pointers, currently, we only * use the 16 bpp segment. + */ + wrptr = dlfb_vidreg_lock(buf); + wrptr = dlfb_set_color_depth(wrptr, 0x00); + /* set base for 16bpp segment to 0 */ + wrptr = dlfb_set_base16bpp(wrptr, 0); + /* set base for 8bpp segment to end of fb */ + wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len); + + wrptr = dlfb_set_vid_cmds(wrptr, var); + wrptr = dlfb_enable_hvsync(wrptr, true); + wrptr = dlfb_vidreg_unlock(wrptr); + + writesize = wrptr - buf; + + retval = dlfb_submit_urb(dev, urb, writesize); + + return retval; +} + +static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + if (offset + size > info->fix.smem_len) + return -EINVAL; + + pos = (unsigned long)info->fix.smem_start + offset; + + pr_notice("mmap() framebuffer addr:%lu size:%lu\n", + pos, size); + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + return 0; +} + +/* + * Trims identical data from front and back of line + * Sets new front buffer address and width + * And returns byte count of identical pixels + * Assumes CPU natural alignment (unsigned long) + * for back and front buffer ptrs and width + */ +static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) +{ + int j, k; + const unsigned long *back = (const unsigned long *) bback; + const unsigned long *front = (const unsigned long *) *bfront; + const int width = *width_bytes / sizeof(unsigned long); + int identical = width; + int start = width; + int end = width; + + prefetch((void *) front); + prefetch((void *) back); + + for (j = 0; j < width; j++) { + if (back[j] != front[j]) { + start = j; + break; + } + } + + for (k = width - 1; k > j; k--) { + if (back[k] != front[k]) { + end = k+1; + break; + } + } + + identical = start + (width - end); + *bfront = (u8 *) &front[start]; + *width_bytes = (end - start) * sizeof(unsigned long); + + return identical * sizeof(unsigned long); +} + +/* + * Render a command stream for an encoded horizontal line segment of pixels. + * + * A command buffer holds several commands. + * It always begins with a fresh command header + * (the protocol doesn't require this, but we enforce it to allow + * multiple buffers to be potentially encoded and sent in parallel). + * A single command encodes one contiguous horizontal line of pixels + * + * The function relies on the client to do all allocation, so that + * rendering can be done directly to output buffers (e.g. USB URBs). + * The function fills the supplied command buffer, providing information + * on where it left off, so the client may call in again with additional + * buffers if the line will take several buffers to complete. + * + * A single command can transmit a maximum of 256 pixels, + * regardless of the compression ratio (protocol design limit). + * To the hardware, 0 for a size byte means 256 + * + * Rather than 256 pixel commands which are either rl or raw encoded, + * the rlx command simply assumes alternating raw and rl spans within one cmd. + * This has a slightly larger header overhead, but produces more even results. + * It also processes all data (read and write) in a single pass. + * Performance benchmarks of common cases show it having just slightly better + * compression than 256 pixel raw or rle commands, with similar CPU consumpion. + * But for very rl friendly data, will compress not quite as well. + */ +static void dlfb_compress_hline( + const uint16_t **pixel_start_ptr, + const uint16_t *const pixel_end, + uint32_t *device_address_ptr, + uint8_t **command_buffer_ptr, + const uint8_t *const cmd_buffer_end) +{ + const uint16_t *pixel = *pixel_start_ptr; + uint32_t dev_addr = *device_address_ptr; + uint8_t *cmd = *command_buffer_ptr; + const int bpp = 2; + + while ((pixel_end > pixel) && + (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { + uint8_t *raw_pixels_count_byte = 0; + uint8_t *cmd_pixels_count_byte = 0; + const uint16_t *raw_pixel_start = 0; + const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0; + + prefetchw((void *) cmd); /* pull in one cache line at least */ + + *cmd++ = 0xAF; + *cmd++ = 0x6B; + *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr) & 0xFF); + + cmd_pixels_count_byte = cmd++; /* we'll know this later */ + cmd_pixel_start = pixel; + + raw_pixels_count_byte = cmd++; /* we'll know this later */ + raw_pixel_start = pixel; + + cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1, + min((int)(pixel_end - pixel), + (int)(cmd_buffer_end - cmd) / bpp)); + + prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); + + while (pixel < cmd_pixel_end) { + const uint16_t * const repeating_pixel = pixel; + + *(uint16_t *)cmd = cpu_to_be16p(pixel); + cmd += 2; + pixel++; + + if (unlikely((pixel < cmd_pixel_end) && + (*pixel == *repeating_pixel))) { + /* go back and fill in raw pixel count */ + *raw_pixels_count_byte = ((repeating_pixel - + raw_pixel_start) + 1) & 0xFF; + + while ((pixel < cmd_pixel_end) + && (*pixel == *repeating_pixel)) { + pixel++; + } + + /* immediately after raw data is repeat byte */ + *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; + + /* Then start another raw pixel span */ + raw_pixel_start = pixel; + raw_pixels_count_byte = cmd++; + } + } + + if (pixel > raw_pixel_start) { + /* finalize last RAW span */ + *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; + } + + *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; + dev_addr += (pixel - cmd_pixel_start) * bpp; + } + + if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) { + /* Fill leftover bytes with no-ops */ + if (cmd_buffer_end > cmd) + memset(cmd, 0xAF, cmd_buffer_end - cmd); + cmd = (uint8_t *) cmd_buffer_end; + } + + *command_buffer_ptr = cmd; + *pixel_start_ptr = pixel; + *device_address_ptr = dev_addr; + + return; +} + +/* + * There are 3 copies of every pixel: The front buffer that the fbdev + * client renders to, the actual framebuffer across the USB bus in hardware + * (that we can only write to, slowly, and can never read), and (optionally) + * our shadow copy that tracks what's been sent to that hardware buffer. + */ +static int dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, + const char *front, char **urb_buf_ptr, + u32 byte_offset, u32 byte_width, + int *ident_ptr, int *sent_ptr) +{ + const u8 *line_start, *line_end, *next_pixel; + u32 dev_addr = dev->base16 + byte_offset; + struct urb *urb = *urb_ptr; + u8 *cmd = *urb_buf_ptr; + u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + + line_start = (u8 *) (front + byte_offset); + next_pixel = line_start; + line_end = next_pixel + byte_width; + + if (dev->backing_buffer) { + int offset; + const u8 *back_start = (u8 *) (dev->backing_buffer + + byte_offset); + + *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, + &byte_width); + + offset = next_pixel - line_start; + line_end = next_pixel + byte_width; + dev_addr += offset; + back_start += offset; + line_start += offset; + + memcpy((char *)back_start, (char *) line_start, + byte_width); + } + + while (next_pixel < line_end) { + + dlfb_compress_hline((const uint16_t **) &next_pixel, + (const uint16_t *) line_end, &dev_addr, + (u8 **) &cmd, (u8 *) cmd_end); + + if (cmd >= cmd_end) { + int len = cmd - (u8 *) urb->transfer_buffer; + if (dlfb_submit_urb(dev, urb, len)) + return 1; /* lost pixels is set */ + *sent_ptr += len; + urb = dlfb_get_urb(dev); + if (!urb) + return 1; /* lost_pixels is set */ + *urb_ptr = urb; + cmd = urb->transfer_buffer; + cmd_end = &cmd[urb->transfer_buffer_length]; + } + } + + *urb_buf_ptr = cmd; + + return 0; +} + +int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, + int width, int height, char *data) +{ + int i, ret; + char *cmd; + cycles_t start_cycles, end_cycles; + int bytes_sent = 0; + int bytes_identical = 0; + struct urb *urb; + int aligned_x; + + start_cycles = get_cycles(); + + aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); + width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); + x = aligned_x; + + if ((width <= 0) || + (x + width > dev->info->var.xres) || + (y + height > dev->info->var.yres)) + return -EINVAL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + urb = dlfb_get_urb(dev); + if (!urb) + return 0; + cmd = urb->transfer_buffer; + + for (i = y; i < y + height ; i++) { + const int line_offset = dev->info->fix.line_length * i; + const int byte_offset = line_offset + (x * BPP); + + if (dlfb_render_hline(dev, &urb, + (char *) dev->info->fix.smem_start, + &cmd, byte_offset, width * BPP, + &bytes_identical, &bytes_sent)) + goto error; + } + + if (cmd > (char *) urb->transfer_buffer) { + /* Send partial buffer remaining before exiting */ + int len = cmd - (char *) urb->transfer_buffer; + ret = dlfb_submit_urb(dev, urb, len); + bytes_sent += len; + } else + dlfb_urb_completion(urb); + +error: + atomic_add(bytes_sent, &dev->bytes_sent); + atomic_add(bytes_identical, &dev->bytes_identical); + atomic_add(width*height*2, &dev->bytes_rendered); + end_cycles = get_cycles(); + atomic_add(((unsigned int) ((end_cycles - start_cycles) + >> 10)), /* Kcycles */ + &dev->cpu_kcycles_used); + + return 0; +} + +/* + * Path triggered by usermode clients who write to filesystem + * e.g. cat filename > /dev/fb1 + * Not used by X Windows or text-mode console. But useful for testing. + * Slow because of extra copy and we must assume all pixels dirty. + */ +static ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t result; + struct dlfb_data *dev = info->par; + u32 offset = (u32) *ppos; + + result = fb_sys_write(info, buf, count, ppos); + + if (result > 0) { + int start = max((int)(offset / info->fix.line_length) - 1, 0); + int lines = min((u32)((result / info->fix.line_length) + 1), + (u32)info->var.yres); + + dlfb_handle_damage(dev, 0, start, info->var.xres, + lines, info->screen_base); + } + + return result; +} + +/* hardware has native COPY command (see libdlo), but not worth it for fbcon */ +static void dlfb_ops_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + + struct dlfb_data *dev = info->par; + + sys_copyarea(info, area); + + dlfb_handle_damage(dev, area->dx, area->dy, + area->width, area->height, info->screen_base); +} + +static void dlfb_ops_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct dlfb_data *dev = info->par; + + sys_imageblit(info, image); + + dlfb_handle_damage(dev, image->dx, image->dy, + image->width, image->height, info->screen_base); +} + +static void dlfb_ops_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct dlfb_data *dev = info->par; + + sys_fillrect(info, rect); + + dlfb_handle_damage(dev, rect->dx, rect->dy, rect->width, + rect->height, info->screen_base); +} + +/* + * NOTE: fb_defio.c is holding info->fbdefio.mutex + * Touching ANY framebuffer memory that triggers a page fault + * in fb_defio will cause a deadlock, when it also tries to + * grab the same mutex. + */ +static void dlfb_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct page *cur; + struct fb_deferred_io *fbdefio = info->fbdefio; + struct dlfb_data *dev = info->par; + struct urb *urb; + char *cmd; + cycles_t start_cycles, end_cycles; + int bytes_sent = 0; + int bytes_identical = 0; + int bytes_rendered = 0; + + if (!fb_defio) + return; + + if (!atomic_read(&dev->usb_active)) + return; + + start_cycles = get_cycles(); + + urb = dlfb_get_urb(dev); + if (!urb) + return; + + cmd = urb->transfer_buffer; + + /* walk the written page list and render each to device */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + + if (dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start, + &cmd, cur->index << PAGE_SHIFT, + PAGE_SIZE, &bytes_identical, &bytes_sent)) + goto error; + bytes_rendered += PAGE_SIZE; + } + + if (cmd > (char *) urb->transfer_buffer) { + /* Send partial buffer remaining before exiting */ + int len = cmd - (char *) urb->transfer_buffer; + dlfb_submit_urb(dev, urb, len); + bytes_sent += len; + } else + dlfb_urb_completion(urb); + +error: + atomic_add(bytes_sent, &dev->bytes_sent); + atomic_add(bytes_identical, &dev->bytes_identical); + atomic_add(bytes_rendered, &dev->bytes_rendered); + end_cycles = get_cycles(); + atomic_add(((unsigned int) ((end_cycles - start_cycles) + >> 10)), /* Kcycles */ + &dev->cpu_kcycles_used); +} + +static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) +{ + int i; + int ret; + char *rbuf; + + rbuf = kmalloc(2, GFP_KERNEL); + if (!rbuf) + return 0; + + for (i = 0; i < len; i++) { + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), (0x02), + (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, + HZ); + if (ret < 1) { + pr_err("Read EDID byte %d failed err %x\n", i, ret); + i--; + break; + } + edid[i] = rbuf[1]; + } + + kfree(rbuf); + + return i; +} + +static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + + struct dlfb_data *dev = info->par; + struct dloarea *area = NULL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + /* TODO: Update X server to get this from sysfs instead */ + if (cmd == DLFB_IOCTL_RETURN_EDID) { + char *edid = (char *)arg; + if (copy_to_user(edid, dev->edid, dev->edid_size)) + return -EFAULT; + return 0; + } + + /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ + if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { + + /* + * If we have a damage-aware client, turn fb_defio "off" + * To avoid perf imact of unecessary page fault handling. + * Done by resetting the delay for this fb_info to a very + * long period. Pages will become writable and stay that way. + * Reset to normal value when all clients have closed this fb. + */ + if (info->fbdefio) + info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; + + area = (struct dloarea *)arg; + + if (area->x < 0) + area->x = 0; + + if (area->x > info->var.xres) + area->x = info->var.xres; + + if (area->y < 0) + area->y = 0; + + if (area->y > info->var.yres) + area->y = info->var.yres; + + dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, + info->screen_base); + } + + return 0; +} + +/* taken from vesafb */ +static int +dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + int err = 0; + + if (regno >= info->cmap.len) + return 1; + + if (regno < 16) { + if (info->var.red.offset == 10) { + /* 1:5:5:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); + } else { + /* 0:5:6:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800)) | + ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); + } + } + + return err; +} + +/* + * It's common for several clients to have framebuffer open simultaneously. + * e.g. both fbcon and X. Makes things interesting. + * Assumes caller is holding info->lock (for open and release at least) + */ +static int dlfb_ops_open(struct fb_info *info, int user) +{ + struct dlfb_data *dev = info->par; + + /* + * fbcon aggressively connects to first framebuffer it finds, + * preventing other clients (X) from working properly. Usually + * not what the user wants. Fail by default with option to enable. + */ + if ((user == 0) & (!console)) + return -EBUSY; + + /* If the USB device is gone, we don't accept new opens */ + if (dev->virtualized) + return -ENODEV; + + dev->fb_count++; + + kref_get(&dev->kref); + + if (fb_defio && (info->fbdefio == NULL)) { + /* enable defio at last moment if not disabled by client */ + + struct fb_deferred_io *fbdefio; + + fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + + if (fbdefio) { + fbdefio->delay = DL_DEFIO_WRITE_DELAY; + fbdefio->deferred_io = dlfb_dpy_deferred_io; + } + + info->fbdefio = fbdefio; + fb_deferred_io_init(info); + } + + pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n", + info->node, user, info, dev->fb_count); + + return 0; +} + +/* + * Called when all client interfaces to start transactions have been disabled, + * and all references to our device instance (dlfb_data) are released. + * Every transaction must have a reference, so we know are fully spun down + */ +static void dlfb_free(struct kref *kref) +{ + struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); + + /* this function will wait for all in-flight urbs to complete */ + if (dev->urbs.count > 0) + dlfb_free_urb_list(dev); + + if (dev->backing_buffer) + vfree(dev->backing_buffer); + + kfree(dev->edid); + + pr_warn("freeing dlfb_data %p\n", dev); + + kfree(dev); +} + +static void dlfb_release_urb_work(struct work_struct *work) +{ + struct urb_node *unode = container_of(work, struct urb_node, + release_urb_work.work); + + up(&unode->dev->urbs.limit_sem); +} + +static void dlfb_free_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + free_framebuffer_work.work); + struct fb_info *info = dev->info; + int node = info->node; + + unregister_framebuffer(info); + + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + dev->info = 0; + + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_warn("fb_info for /dev/fb%d has been freed\n", node); + + /* ref taken in probe() as part of registering framebfufer */ + kref_put(&dev->kref, dlfb_free); +} + +/* + * Assumes caller is holding info->lock mutex (for open and release at least) + */ +static int dlfb_ops_release(struct fb_info *info, int user) +{ + struct dlfb_data *dev = info->par; + + dev->fb_count--; + + /* We can't free fb_info here - fbmem will touch it when we return */ + if (dev->virtualized && (dev->fb_count == 0)) + schedule_delayed_work(&dev->free_framebuffer_work, HZ); + + if ((dev->fb_count == 0) && (info->fbdefio)) { + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + info->fbdefio = NULL; + info->fbops->fb_mmap = dlfb_ops_mmap; + } + + pr_warn("released /dev/fb%d user=%d count=%d\n", + info->node, user, dev->fb_count); + + kref_put(&dev->kref, dlfb_free); + + return 0; +} + +/* + * Check whether a video mode is supported by the DisplayLink chip + * We start from monitor's modes, so don't need to filter that here + */ +static int dlfb_is_valid_mode(struct fb_videomode *mode, + struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + + if (mode->xres * mode->yres > dev->sku_pixel_limit) { + pr_warn("%dx%d beyond chip capabilities\n", + mode->xres, mode->yres); + return 0; + } + + pr_info("%dx%d valid mode\n", mode->xres, mode->yres); + + return 1; +} + +static void dlfb_var_color_format(struct fb_var_screeninfo *var) +{ + const struct fb_bitfield red = { 11, 5, 0 }; + const struct fb_bitfield green = { 5, 6, 0 }; + const struct fb_bitfield blue = { 0, 5, 0 }; + + var->bits_per_pixel = 16; + var->red = red; + var->green = green; + var->blue = blue; +} + +static int dlfb_ops_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_videomode mode; + + /* TODO: support dynamically changing framebuffer size */ + if ((var->xres * var->yres * 2) > info->fix.smem_len) + return -EINVAL; + + /* set device-specific elements of var unrelated to mode */ + dlfb_var_color_format(var); + + fb_var_to_videomode(&mode, var); + + if (!dlfb_is_valid_mode(&mode, info)) + return -EINVAL; + + return 0; +} + +static int dlfb_ops_set_par(struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + int result; + u16 *pix_framebuffer; + int i; + + pr_notice("set_par mode %dx%d\n", info->var.xres, info->var.yres); + + result = dlfb_set_video_mode(dev, &info->var); + + if ((result == 0) && (dev->fb_count == 0)) { + + /* paint greenscreen */ + + pix_framebuffer = (u16 *) info->screen_base; + for (i = 0; i < info->fix.smem_len / 2; i++) + pix_framebuffer[i] = 0x37e6; + + dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres, + info->screen_base); + } + + return result; +} + +/* + * In order to come back from full DPMS off, we need to set the mode again + */ +static int dlfb_ops_blank(int blank_mode, struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + + if (blank_mode != FB_BLANK_UNBLANK) { + char *bufptr; + struct urb *urb; + + urb = dlfb_get_urb(dev); + if (!urb) + return 0; + + bufptr = (char *) urb->transfer_buffer; + bufptr = dlfb_vidreg_lock(bufptr); + bufptr = dlfb_enable_hvsync(bufptr, false); + bufptr = dlfb_vidreg_unlock(bufptr); + + dlfb_submit_urb(dev, urb, bufptr - + (char *) urb->transfer_buffer); + } else { + dlfb_set_video_mode(dev, &info->var); + } + + return 0; +} + +static struct fb_ops dlfb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = dlfb_ops_write, + .fb_setcolreg = dlfb_ops_setcolreg, + .fb_fillrect = dlfb_ops_fillrect, + .fb_copyarea = dlfb_ops_copyarea, + .fb_imageblit = dlfb_ops_imageblit, + .fb_mmap = dlfb_ops_mmap, + .fb_ioctl = dlfb_ops_ioctl, + .fb_open = dlfb_ops_open, + .fb_release = dlfb_ops_release, + .fb_blank = dlfb_ops_blank, + .fb_check_var = dlfb_ops_check_var, + .fb_set_par = dlfb_ops_set_par, +}; + + +/* + * Assumes &info->lock held by caller + * Assumes no active clients have framebuffer open + */ +static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) +{ + int retval = -ENOMEM; + int old_len = info->fix.smem_len; + int new_len; + unsigned char *old_fb = info->screen_base; + unsigned char *new_fb; + unsigned char *new_back; + + pr_warn("Reallocating framebuffer. Addresses will change!\n"); + + new_len = info->fix.line_length * info->var.yres; + + if (PAGE_ALIGN(new_len) > old_len) { + /* + * Alloc system memory for virtual framebuffer + */ + new_fb = vmalloc(new_len); + if (!new_fb) { + pr_err("Virtual framebuffer alloc failed\n"); + goto error; + } + + if (info->screen_base) { + memcpy(new_fb, old_fb, old_len); + vfree(info->screen_base); + } + + info->screen_base = new_fb; + info->fix.smem_len = PAGE_ALIGN(new_len); + info->fix.smem_start = (unsigned long) new_fb; + info->flags = udlfb_info_flags; + + /* + * Second framebuffer copy to mirror the framebuffer state + * on the physical USB device. We can function without this. + * But with imperfect damage info we may send pixels over USB + * that were, in fact, unchanged - wasting limited USB bandwidth + */ + new_back = vzalloc(new_len); + if (!new_back) + pr_info("No shadow/backing buffer allocated\n"); + else { + if (dev->backing_buffer) + vfree(dev->backing_buffer); + dev->backing_buffer = new_back; + } + } + + retval = 0; + +error: + return retval; +} + +/* + * 1) Get EDID from hw, or use sw default + * 2) Parse into various fb_info structs + * 3) Allocate virtual framebuffer memory to back highest res mode + * + * Parses EDID into three places used by various parts of fbdev: + * fb_var_screeninfo contains the timing of the monitor's preferred mode + * fb_info.monspecs is full parsed EDID info, including monspecs.modedb + * fb_info.modelist is a linked list of all monitor & VESA modes which work + * + * If EDID is not readable/valid, then modelist is all VESA modes, + * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode + * Returns 0 if successful + */ +static int dlfb_setup_modes(struct dlfb_data *dev, + struct fb_info *info, + char *default_edid, size_t default_edid_size) +{ + int i; + const struct fb_videomode *default_vmode = NULL; + int result = 0; + char *edid; + int tries = 3; + + if (info->dev) /* only use mutex if info has been registered */ + mutex_lock(&info->lock); + + edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) { + result = -ENOMEM; + goto error; + } + + fb_destroy_modelist(&info->modelist); + memset(&info->monspecs, 0, sizeof(info->monspecs)); + + /* + * Try to (re)read EDID from hardware first + * EDID data may return, but not parse as valid + * Try again a few times, in case of e.g. analog cable noise + */ + while (tries--) { + + i = dlfb_get_edid(dev, edid, EDID_LENGTH); + + if (i >= EDID_LENGTH) + fb_edid_to_monspecs(edid, &info->monspecs); + + if (info->monspecs.modedb_len > 0) { + dev->edid = edid; + dev->edid_size = i; + break; + } + } + + /* If that fails, use a previously returned EDID if available */ + if (info->monspecs.modedb_len == 0) { + + pr_err("Unable to get valid EDID from device/display\n"); + + if (dev->edid) { + fb_edid_to_monspecs(dev->edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) + pr_err("Using previously queried EDID\n"); + } + } + + /* If that fails, use the default EDID we were handed */ + if (info->monspecs.modedb_len == 0) { + if (default_edid_size >= EDID_LENGTH) { + fb_edid_to_monspecs(default_edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) { + memcpy(edid, default_edid, default_edid_size); + dev->edid = edid; + dev->edid_size = default_edid_size; + pr_err("Using default/backup EDID\n"); + } + } + } + + /* If we've got modes, let's pick a best default mode */ + if (info->monspecs.modedb_len > 0) { + + for (i = 0; i < info->monspecs.modedb_len; i++) { + if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info)) + fb_add_videomode(&info->monspecs.modedb[i], + &info->modelist); + else /* if we've removed top/best mode */ + info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; + } + + default_vmode = fb_find_best_display(&info->monspecs, + &info->modelist); + } + + /* If everything else has failed, fall back to safe default mode */ + if (default_vmode == NULL) { + + struct fb_videomode fb_vmode = {0}; + + /* + * Add the standard VESA modes to our modelist + * Since we don't have EDID, there may be modes that + * overspec monitor and/or are incorrect aspect ratio, etc. + * But at least the user has a chance to choose + */ + for (i = 0; i < VESA_MODEDB_SIZE; i++) { + if (dlfb_is_valid_mode((struct fb_videomode *) + &vesa_modes[i], info)) + fb_add_videomode(&vesa_modes[i], + &info->modelist); + } + + /* + * default to resolution safe for projectors + * (since they are most common case without EDID) + */ + fb_vmode.xres = 800; + fb_vmode.yres = 600; + fb_vmode.refresh = 60; + default_vmode = fb_find_nearest_mode(&fb_vmode, + &info->modelist); + } + + /* If we have good mode and no active clients*/ + if ((default_vmode != NULL) && (dev->fb_count == 0)) { + + fb_videomode_to_var(&info->var, default_vmode); + dlfb_var_color_format(&info->var); + + /* + * with mode size info, we can now alloc our framebuffer. + */ + memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); + info->fix.line_length = info->var.xres * + (info->var.bits_per_pixel / 8); + + result = dlfb_realloc_framebuffer(dev, info); + + } else + result = -EINVAL; + +error: + if (edid && (dev->edid != edid)) + kfree(edid); + + if (info->dev) + mutex_unlock(&info->lock); + + return result; +} + +static ssize_t metrics_bytes_rendered_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_rendered)); +} + +static ssize_t metrics_bytes_identical_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_identical)); +} + +static ssize_t metrics_bytes_sent_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_sent)); +} + +static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->cpu_kcycles_used)); +} + +static ssize_t edid_show( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, + char *buf, loff_t off, size_t count) { + struct device *fbdev = container_of(kobj, struct device, kobj); + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + if (dev->edid == NULL) + return 0; + + if ((off >= dev->edid_size) || (count > dev->edid_size)) + return 0; + + if (off + count > dev->edid_size) + count = dev->edid_size - off; + + pr_info("sysfs edid copy %p to %p, %d bytes\n", + dev->edid, buf, (int) count); + + memcpy(buf, dev->edid, count); + + return count; +} + +static ssize_t edid_store( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, + char *src, loff_t src_off, size_t src_size) { + struct device *fbdev = container_of(kobj, struct device, kobj); + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + /* We only support write of entire EDID at once, no offset*/ + if ((src_size != EDID_LENGTH) || (src_off != 0)) + return 0; + + dlfb_setup_modes(dev, fb_info, src, src_size); + + if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { + pr_info("sysfs written EDID is new default\n"); + dlfb_ops_set_par(fb_info); + return src_size; + } else + return 0; +} + +static ssize_t metrics_reset_store(struct device *fbdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + atomic_set(&dev->bytes_rendered, 0); + atomic_set(&dev->bytes_identical, 0); + atomic_set(&dev->bytes_sent, 0); + atomic_set(&dev->cpu_kcycles_used, 0); + + return count; +} + +static struct bin_attribute edid_attr = { + .attr.name = "edid", + .attr.mode = 0666, + .size = EDID_LENGTH, + .read = edid_show, + .write = edid_store +}; + +static struct device_attribute fb_device_attrs[] = { + __ATTR_RO(metrics_bytes_rendered), + __ATTR_RO(metrics_bytes_identical), + __ATTR_RO(metrics_bytes_sent), + __ATTR_RO(metrics_cpu_kcycles_used), + __ATTR(metrics_reset, S_IWUSR, NULL, metrics_reset_store), +}; + +/* + * This is necessary before we can communicate with the display controller. + */ +static int dlfb_select_std_channel(struct dlfb_data *dev) +{ + int ret; + u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7, + 0x1C, 0x88, 0x5E, 0x15, + 0x60, 0xFE, 0xC6, 0x97, + 0x16, 0x3D, 0x47, 0xF2 }; + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + NR_USB_REQUEST_CHANNEL, + (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, + set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); + return ret; +} + +static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, + struct usb_device *usbdev) +{ + char *desc; + char *buf; + char *desc_end; + + u8 total_len = 0; + + buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); + if (!buf) + return false; + desc = buf; + + total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ + 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); + if (total_len > 5) { + pr_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \ + "%02x %02x %02x %02x %02x %02x %02x\n", + total_len, desc[0], + desc[1], desc[2], desc[3], desc[4], desc[5], desc[6], + desc[7], desc[8], desc[9], desc[10]); + + if ((desc[0] != total_len) || /* descriptor length */ + (desc[1] != 0x5f) || /* vendor descriptor type */ + (desc[2] != 0x01) || /* version (2 bytes) */ + (desc[3] != 0x00) || + (desc[4] != total_len - 2)) /* length after type */ + goto unrecognized; + + desc_end = desc + total_len; + desc += 5; /* the fixed header we've already parsed */ + + while (desc < desc_end) { + u8 length; + u16 key; + + key = *((u16 *) desc); + desc += sizeof(u16); + length = *desc; + desc++; + + switch (key) { + case 0x0200: { /* max_area */ + u32 max_area; + max_area = le32_to_cpu(*((u32 *)desc)); + pr_warn("DL chip limited to %d pixel modes\n", + max_area); + dev->sku_pixel_limit = max_area; + break; + } + default: + break; + } + desc += length; + } + } + + goto success; + +unrecognized: + /* allow udlfb to load for now even if firmware unrecognized */ + pr_err("Unrecognized vendor firmware descriptor\n"); + +success: + kfree(buf); + return true; +} +static int dlfb_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct dlfb_data *dev = 0; + struct fb_info *info = 0; + int retval = -ENOMEM; + int i; + + /* usb initialization */ + + usbdev = interface_to_usbdev(interface); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + err("dlfb_usb_probe: failed alloc of dev struct\n"); + goto error; + } + + /* we need to wait for both usb and fbdev to spin down on disconnect */ + kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ + + dev->udev = usbdev; + dev->gdev = &usbdev->dev; /* our generic struct device * */ + usb_set_intfdata(interface, dev); + + pr_info("%s %s - serial #%s\n", + usbdev->manufacturer, usbdev->product, usbdev->serial); + pr_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->descriptor.bcdDevice, dev); + pr_info("console enable=%d\n", console); + pr_info("fb_defio enable=%d\n", fb_defio); + + dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ + + if (!dlfb_parse_vendor_descriptor(dev, usbdev)) { + pr_err("firmware not recognized. Assume incompatible device\n"); + goto error; + } + + if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { + retval = -ENOMEM; + pr_err("dlfb_alloc_urb_list failed\n"); + goto error; + } + + /* We don't register a new USB class. Our client interface is fbdev */ + + /* allocates framebuffer driver structure, not framebuffer memory */ + info = framebuffer_alloc(0, &usbdev->dev); + if (!info) { + retval = -ENOMEM; + pr_err("framebuffer_alloc failed\n"); + goto error; + } + + dev->info = info; + info->par = dev; + info->pseudo_palette = dev->pseudo_palette; + info->fbops = &dlfb_ops; + + retval = fb_alloc_cmap(&info->cmap, 256, 0); + if (retval < 0) { + pr_err("fb_alloc_cmap failed %x\n", retval); + goto error; + } + + INIT_DELAYED_WORK(&dev->free_framebuffer_work, + dlfb_free_framebuffer_work); + + INIT_LIST_HEAD(&info->modelist); + + retval = dlfb_setup_modes(dev, info, NULL, 0); + if (retval != 0) { + pr_err("unable to find common mode for display and adapter\n"); + goto error; + } + + /* ready to begin using device */ + + atomic_set(&dev->usb_active, 1); + dlfb_select_std_channel(dev); + + dlfb_ops_check_var(&info->var, info); + dlfb_ops_set_par(info); + + retval = register_framebuffer(info); + if (retval < 0) { + pr_err("register_framebuffer failed %d\n", retval); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_create_file(info->dev, &fb_device_attrs[i]); + + device_create_bin_file(info->dev, &edid_attr); + + pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." + " Using %dK framebuffer memory\n", info->node, + info->var.xres, info->var.yres, + ((dev->backing_buffer) ? + info->fix.smem_len * 2 : info->fix.smem_len) >> 10); + return 0; + +error: + if (dev) { + + if (info) { + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + framebuffer_release(info); + } + + if (dev->backing_buffer) + vfree(dev->backing_buffer); + + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ + + /* dev has been deallocated. Do not dereference */ + } + + return retval; +} + +static void dlfb_usb_disconnect(struct usb_interface *interface) +{ + struct dlfb_data *dev; + struct fb_info *info; + int i; + + dev = usb_get_intfdata(interface); + info = dev->info; + + pr_info("USB disconnect starting\n"); + + /* we virtualize until all fb clients release. Then we free */ + dev->virtualized = true; + + /* When non-active we'll update virtual framebuffer, but no new urbs */ + atomic_set(&dev->usb_active, 0); + + /* remove udlfb's sysfs interfaces */ + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_remove_file(info->dev, &fb_device_attrs[i]); + device_remove_bin_file(info->dev, &edid_attr); + + usb_set_intfdata(interface, NULL); + + /* if clients still have us open, will be freed on last close */ + if (dev->fb_count == 0) + schedule_delayed_work(&dev->free_framebuffer_work, 0); + + /* release reference taken by kref_init in probe() */ + kref_put(&dev->kref, dlfb_free); + + /* consider dlfb_data freed */ + + return; +} + +static struct usb_driver dlfb_driver = { + .name = "udlfb", + .probe = dlfb_usb_probe, + .disconnect = dlfb_usb_disconnect, + .id_table = id_table, +}; + +static int __init dlfb_module_init(void) +{ + int res; + + res = usb_register(&dlfb_driver); + if (res) + err("usb_register failed. Error number %d", res); + + return res; +} + +static void __exit dlfb_module_exit(void) +{ + usb_deregister(&dlfb_driver); +} + +module_init(dlfb_module_init); +module_exit(dlfb_module_exit); + +static void dlfb_urb_completion(struct urb *urb) +{ + struct urb_node *unode = urb->context; + struct dlfb_data *dev = unode->dev; + unsigned long flags; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + pr_err("%s - nonzero write bulk status received: %d\n", + __func__, urb->status); + atomic_set(&dev->lost_pixels, 1); + } + } + + urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */ + + spin_lock_irqsave(&dev->urbs.lock, flags); + list_add_tail(&unode->entry, &dev->urbs.list); + dev->urbs.available++; + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + /* + * When using fb_defio, we deadlock if up() is called + * while another is waiting. So queue to another process. + */ + if (fb_defio) + schedule_delayed_work(&unode->release_urb_work, 0); + else + up(&dev->urbs.limit_sem); +} + +static void dlfb_free_urb_list(struct dlfb_data *dev) +{ + int count = dev->urbs.count; + struct list_head *node; + struct urb_node *unode; + struct urb *urb; + int ret; + unsigned long flags; + + pr_notice("Waiting for completes and freeing all render urbs\n"); + + /* keep waiting and freeing, until we've got 'em all */ + while (count--) { + + /* Getting interrupted means a leak, but ok at shutdown*/ + ret = down_interruptible(&dev->urbs.limit_sem); + if (ret) + break; + + spin_lock_irqsave(&dev->urbs.lock, flags); + + node = dev->urbs.list.next; /* have reserved one with sem */ + list_del_init(node); + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(node, struct urb_node, entry); + urb = unode->urb; + + /* Free each separately allocated piece */ + usb_free_coherent(urb->dev, dev->urbs.size, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + kfree(node); + } + +} + +static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) +{ + int i = 0; + struct urb *urb; + struct urb_node *unode; + char *buf; + + spin_lock_init(&dev->urbs.lock); + + dev->urbs.size = size; + INIT_LIST_HEAD(&dev->urbs.list); + + while (i < count) { + unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); + if (!unode) + break; + unode->dev = dev; + + INIT_DELAYED_WORK(&unode->release_urb_work, + dlfb_release_urb_work); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(unode); + break; + } + unode->urb = urb; + + buf = usb_alloc_coherent(dev->udev, MAX_TRANSFER, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + kfree(unode); + usb_free_urb(urb); + break; + } + + /* urb->transfer_buffer_length set to actual before submit */ + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1), + buf, size, dlfb_urb_completion, unode); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + list_add_tail(&unode->entry, &dev->urbs.list); + + i++; + } + + sema_init(&dev->urbs.limit_sem, i); + dev->urbs.count = i; + dev->urbs.available = i; + + pr_notice("allocated %d %d byte urbs\n", i, (int) size); + + return i; +} + +static struct urb *dlfb_get_urb(struct dlfb_data *dev) +{ + int ret = 0; + struct list_head *entry; + struct urb_node *unode; + struct urb *urb = NULL; + unsigned long flags; + + /* Wait for an in-flight buffer to complete and get re-queued */ + ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT); + if (ret) { + atomic_set(&dev->lost_pixels, 1); + pr_warn("wait for urb interrupted: %x available: %d\n", + ret, dev->urbs.available); + goto error; + } + + spin_lock_irqsave(&dev->urbs.lock, flags); + + BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */ + entry = dev->urbs.list.next; + list_del_init(entry); + dev->urbs.available--; + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(entry, struct urb_node, entry); + urb = unode->urb; + +error: + return urb; +} + +static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len) +{ + int ret; + + BUG_ON(len > dev->urbs.size); + + urb->transfer_buffer_length = len; /* set to actual payload len */ + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dlfb_urb_completion(urb); /* because no one else will */ + atomic_set(&dev->lost_pixels, 1); + pr_err("usb_submit_urb error %x\n", ret); + } + return ret; +} + +module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found"); + +module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*"); + +MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " + "Jaya Kumar <jayakumar.lkml@gmail.com>, " + "Bernie Thompson <bernie@plugable.com>"); +MODULE_DESCRIPTION("DisplayLink kernel framebuffer driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index a3aa9170950..6723d6910cd 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -15,6 +15,9 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/pm.h> +#include <asm/olpc.h> /* * The default port config. @@ -29,6 +32,19 @@ static struct via_port_cfg adap_configs[] = { }; /* + * The OLPC XO-1.5 puts the camera power and reset lines onto + * GPIO 2C. + */ +static const struct via_port_cfg olpc_adap_configs[] = { + [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 }, + [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, + [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, + [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c }, + [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, + { 0, 0, 0, 0 } +}; + +/* * We currently only support one viafb device (will there ever be * more than one?), so just declare it globally here. */ @@ -575,6 +591,78 @@ static void via_teardown_subdevs(void) } } +/* + * Power management functions + */ +#ifdef CONFIG_PM +static LIST_HEAD(viafb_pm_hooks); +static DEFINE_MUTEX(viafb_pm_hooks_lock); + +void viafb_pm_register(struct viafb_pm_hooks *hooks) +{ + INIT_LIST_HEAD(&hooks->list); + + mutex_lock(&viafb_pm_hooks_lock); + list_add_tail(&hooks->list, &viafb_pm_hooks); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_register); + +void viafb_pm_unregister(struct viafb_pm_hooks *hooks) +{ + mutex_lock(&viafb_pm_hooks_lock); + list_del(&hooks->list); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_unregister); + +static int via_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct viafb_pm_hooks *hooks; + + if (state.event != PM_EVENT_SUSPEND) + return 0; + /* + * "I've occasionally hit a few drivers that caused suspend + * failures, and each and every time it was a driver bug, and + * the right thing to do was to just ignore the error and suspend + * anyway - returning an error code and trying to undo the suspend + * is not what anybody ever really wants, even if our model + *_allows_ for it." + * -- Linus Torvalds, Dec. 7, 2009 + */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list) + hooks->suspend(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int via_resume(struct pci_dev *pdev) +{ + struct viafb_pm_hooks *hooks; + + /* Get the bus side powered up */ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + if (pci_enable_device(pdev)) + return 0; + + pci_set_master(pdev); + + /* Now bring back any subdevs */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry(hooks, &viafb_pm_hooks, list) + hooks->resume(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + return 0; +} +#endif /* CONFIG_PM */ static int __devinit via_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -584,6 +672,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) return ret; + /* * Global device initialization. */ @@ -591,6 +680,9 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, global_dev.pdev = pdev; global_dev.chip_type = ent->driver_data; global_dev.port_cfg = adap_configs; + if (machine_is_olpc()) + global_dev.port_cfg = olpc_adap_configs; + spin_lock_init(&global_dev.reg_lock); ret = via_pci_setup_mmio(&global_dev); if (ret) @@ -663,8 +755,8 @@ static struct pci_driver via_driver = { .probe = via_pci_probe, .remove = __devexit_p(via_pci_remove), #ifdef CONFIG_PM - .suspend = viafb_suspend, - .resume = viafb_resume, + .suspend = via_suspend, + .resume = via_resume, #endif }; diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c index 39acb37e7a1..c2a0a1cfd3b 100644 --- a/drivers/video/via/via-gpio.c +++ b/drivers/video/via/via-gpio.c @@ -172,6 +172,28 @@ static void viafb_gpio_disable(struct viafb_gpio *gpio) via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); } +#ifdef CONFIG_PM + +static int viafb_gpio_suspend(void *private) +{ + return 0; +} + +static int viafb_gpio_resume(void *private) +{ + int i; + + for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) + viafb_gpio_enable(gpio_config.active_gpios[i]); + return 0; +} + +static struct viafb_pm_hooks viafb_gpio_pm_hooks = { + .suspend = viafb_gpio_suspend, + .resume = viafb_gpio_resume +}; +#endif /* CONFIG_PM */ + /* * Look up a specific gpio and return the number it was assigned. */ @@ -236,6 +258,9 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev) printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); gpio_config.gpio_chip.ngpio = 0; } +#ifdef CONFIG_PM + viafb_pm_register(&viafb_gpio_pm_hooks); +#endif return ret; } @@ -245,6 +270,10 @@ static int viafb_gpio_remove(struct platform_device *platdev) unsigned long flags; int ret = 0, i; +#ifdef CONFIG_PM + viafb_pm_unregister(&viafb_gpio_pm_hooks); +#endif + /* * Get unregistered. */ diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index d298cfccd6f..289edd51952 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) #ifdef CONFIG_PM -int viafb_suspend(struct pci_dev *pdev, pm_message_t state) +static int viafb_suspend(void *unused) { - if (state.event == PM_EVENT_SUSPEND) { - acquire_console_sem(); - fb_set_suspend(viafbinfo, 1); - - viafb_sync(viafbinfo); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - release_console_sem(); - } + acquire_console_sem(); + fb_set_suspend(viafbinfo, 1); + viafb_sync(viafbinfo); + release_console_sem(); return 0; } -int viafb_resume(struct pci_dev *pdev) +static int viafb_resume(void *unused) { acquire_console_sem(); - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - if (pci_enable_device(pdev)) - goto fail; - pci_set_master(pdev); if (viaparinfo->shared->vdev->engine_mmio) viafb_reset_engine(viaparinfo); viafb_set_par(viafbinfo); @@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev) viafb_set_par(viafbinfo1); fb_set_suspend(viafbinfo, 0); -fail: release_console_sem(); return 0; } +static struct viafb_pm_hooks viafb_fb_pm_hooks = { + .suspend = viafb_suspend, + .resume = viafb_resume +}; + #endif @@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viafb_init_proc(viaparinfo->shared); viafb_init_dac(IGA2); + +#ifdef CONFIG_PM + viafb_pm_register(&viafb_fb_pm_hooks); +#endif return 0; out_fb_unreg: diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 4960e3da664..d66f963e930 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev); /* Temporary */ int viafb_init(void); void viafb_exit(void); -int viafb_suspend(struct pci_dev *pdev, pm_message_t state); -int viafb_resume(struct pci_dev *pdev); #endif /* __VIAFBDEV_H__ */ diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c new file mode 100644 index 00000000000..0e120d67eb6 --- /dev/null +++ b/drivers/video/vt8500lcdfb.c @@ -0,0 +1,475 @@ +/* + * linux/drivers/video/vt8500lcdfb.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * Based on skeletonfb.c and pxafb.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include <mach/vt8500fb.h> + +#include "vt8500lcdfb.h" +#include "wmt_ge_rops.h" + +#define to_vt8500lcd_info(__info) container_of(__info, \ + struct vt8500lcd_info, fb) + +static int vt8500lcd_set_par(struct fb_info *info) +{ + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + int reg_bpp = 5; /* 16bpp */ + int i; + unsigned long control0; + + if (!fbi) + return -EINVAL; + + if (info->var.bits_per_pixel <= 8) { + /* palettized */ + info->var.red.offset = 0; + info->var.red.length = info->var.bits_per_pixel; + info->var.red.msb_right = 0; + + info->var.green.offset = 0; + info->var.green.length = info->var.bits_per_pixel; + info->var.green.msb_right = 0; + + info->var.blue.offset = 0; + info->var.blue.length = info->var.bits_per_pixel; + info->var.blue.msb_right = 0; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + info->fix.line_length = info->var.xres_virtual / + (8/info->var.bits_per_pixel); + } else { + /* non-palettized */ + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + if (info->var.bits_per_pixel == 16) { + /* RGB565 */ + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.red.msb_right = 0; + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->var.blue.msb_right = 0; + } else { + /* Equal depths per channel */ + info->var.red.offset = info->var.bits_per_pixel + * 2 / 3; + info->var.red.length = info->var.bits_per_pixel / 3; + info->var.red.msb_right = 0; + info->var.green.offset = info->var.bits_per_pixel / 3; + info->var.green.length = info->var.bits_per_pixel / 3; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = info->var.bits_per_pixel / 3; + info->var.blue.msb_right = 0; + } + + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.bits_per_pixel > 16 ? + info->var.xres_virtual << 2 : + info->var.xres_virtual << 1; + } + + for (i = 0; i < 8; i++) { + if (bpp_values[i] == info->var.bits_per_pixel) { + reg_bpp = i; + continue; + } + } + + control0 = readl(fbi->regbase) & ~0xf; + writel(0, fbi->regbase); + while (readl(fbi->regbase + 0x38) & 0x10) + /* wait */; + writel((((info->var.hsync_len - 1) & 0x3f) << 26) + | ((info->var.left_margin & 0xff) << 18) + | (((info->var.xres - 1) & 0x3ff) << 8) + | (info->var.right_margin & 0xff), fbi->regbase + 0x4); + writel((((info->var.vsync_len - 1) & 0x3f) << 26) + | ((info->var.upper_margin & 0xff) << 18) + | (((info->var.yres - 1) & 0x3ff) << 8) + | (info->var.lower_margin & 0xff), fbi->regbase + 0x8); + writel((((info->var.yres - 1) & 0x400) << 2) + | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10); + writel(0x80000000, fbi->regbase + 0x20); + writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase); + + return 0; +} + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) { + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + int ret = 1; + unsigned int val; + if (regno >= 256) + return -EINVAL; + + if (info->var.grayscale) + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + + switch (fbi->fb.fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + u32 *pal = fbi->fb.pseudo_palette; + + val = chan_to_field(red, &fbi->fb.var.red); + val |= chan_to_field(green, &fbi->fb.var.green); + val |= chan_to_field(blue, &fbi->fb.var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_STATIC_PSEUDOCOLOR: + case FB_VISUAL_PSEUDOCOLOR: + writew((red & 0xf800) + | ((green >> 5) & 0x7e0) + | ((blue >> 11) & 0x1f), + fbi->palette_cpu + sizeof(u16) * regno); + break; + } + + return ret; +} + +static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + + if (cmd == FBIO_WAITFORVSYNC) { + /* Unmask End of Frame interrupt */ + writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c); + ret = wait_event_interruptible_timeout(fbi->wait, + readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10); + /* Mask back to reduce unwanted interrupt traffic */ + writel(0xffffffff, fbi->regbase + 0x3c); + if (ret < 0) + return ret; + if (ret == 0) + return -ETIMEDOUT; + } + + return ret; +} + +static int vt8500lcd_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned pixlen = info->fix.line_length / info->var.xres_virtual; + unsigned off = pixlen * var->xoffset + + info->fix.line_length * var->yoffset; + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + + writel((1 << 31) + | (((var->xres_virtual - var->xres) * pixlen / 4) << 20) + | (off >> 2), fbi->regbase + 0x20); + return 0; +} + +/* + * vt8500lcd_blank(): + * Blank the display by setting all palette values to zero. Note, + * True Color modes do not really use the palette, so this will not + * blank the display in all modes. + */ +static int vt8500lcd_blank(int blank, struct fb_info *info) +{ + int i; + + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || + info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) + for (i = 0; i < 256; i++) + vt8500lcd_setcolreg(i, 0, 0, 0, 0, info); + case FB_BLANK_UNBLANK: + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || + info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) + fb_set_cmap(&info->cmap, info); + } + return 0; +} + +static struct fb_ops vt8500lcd_ops = { + .owner = THIS_MODULE, + .fb_set_par = vt8500lcd_set_par, + .fb_setcolreg = vt8500lcd_setcolreg, + .fb_fillrect = wmt_ge_fillrect, + .fb_copyarea = wmt_ge_copyarea, + .fb_imageblit = sys_imageblit, + .fb_sync = wmt_ge_sync, + .fb_ioctl = vt8500lcd_ioctl, + .fb_pan_display = vt8500lcd_pan_display, + .fb_blank = vt8500lcd_blank, +}; + +static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id) +{ + struct vt8500lcd_info *fbi = dev_id; + + if (readl(fbi->regbase + 0x38) & (1 << 3)) + wake_up_interruptible(&fbi->wait); + + writel(0xffffffff, fbi->regbase + 0x38); + return IRQ_HANDLED; +} + +static int __devinit vt8500lcd_probe(struct platform_device *pdev) +{ + struct vt8500lcd_info *fbi; + struct resource *res; + struct vt8500fb_platform_data *pdata = pdev->dev.platform_data; + void *addr; + int irq, ret; + + ret = -ENOMEM; + fbi = NULL; + + fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16, + GFP_KERNEL); + if (!fbi) { + dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); + ret = -ENOMEM; + goto failed; + } + + strcpy(fbi->fb.fix.id, "VT8500 LCD"); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.xpanstep = 0; + fbi->fb.fix.ypanstep = 1; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; + + fbi->fb.fbops = &vt8500lcd_ops; + fbi->fb.flags = FBINFO_DEFAULT + | FBINFO_HWACCEL_COPYAREA + | FBINFO_HWACCEL_FILLRECT + | FBINFO_HWACCEL_YPAN + | FBINFO_VIRTFB + | FBINFO_PARTIAL_PAN_OK; + fbi->fb.node = -1; + + addr = fbi; + addr = addr + sizeof(struct vt8500lcd_info); + fbi->fb.pseudo_palette = addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto failed_fbi; + } + + res = request_mem_region(res->start, resource_size(res), "vt8500lcd"); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto failed_fbi; + } + + fbi->regbase = ioremap(res->start, resource_size(res)); + if (fbi->regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto failed_free_res; + } + + fbi->fb.fix.smem_start = pdata->video_mem_phys; + fbi->fb.fix.smem_len = pdata->video_mem_len; + fbi->fb.screen_base = pdata->video_mem_virt; + + fbi->palette_size = PAGE_ALIGN(512); + fbi->palette_cpu = dma_alloc_coherent(&pdev->dev, + fbi->palette_size, + &fbi->palette_phys, + GFP_KERNEL); + if (fbi->palette_cpu == NULL) { + dev_err(&pdev->dev, "Failed to allocate palette buffer\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto failed_free_palette; + } + + ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi); + if (ret) { + dev_err(&pdev->dev, "request_irq failed: %d\n", ret); + ret = -EBUSY; + goto failed_free_palette; + } + + init_waitqueue_head(&fbi->wait); + + if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { + dev_err(&pdev->dev, "Failed to allocate color map\n"); + ret = -ENOMEM; + goto failed_free_irq; + } + + fb_videomode_to_var(&fbi->fb.var, &pdata->mode); + fbi->fb.var.bits_per_pixel = pdata->bpp; + fbi->fb.var.xres_virtual = pdata->xres_virtual; + fbi->fb.var.yres_virtual = pdata->yres_virtual; + + ret = vt8500lcd_set_par(&fbi->fb); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); + goto failed_free_cmap; + } + + writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c); + writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18); + + platform_set_drvdata(pdev, fbi); + + ret = register_framebuffer(&fbi->fb); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to register framebuffer device: %d\n", ret); + goto failed_free_cmap; + } + + /* + * Ok, now enable the LCD controller + */ + writel(readl(fbi->regbase) | 1, fbi->regbase); + + return 0; + +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); +failed_free_irq: + free_irq(irq, fbi); +failed_free_palette: + dma_free_coherent(&pdev->dev, fbi->palette_size, + fbi->palette_cpu, fbi->palette_phys); +failed_free_io: + iounmap(fbi->regbase); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_fbi: + platform_set_drvdata(pdev, NULL); + kfree(fbi); +failed: + return ret; +} + +static int __devexit vt8500lcd_remove(struct platform_device *pdev) +{ + struct vt8500lcd_info *fbi = platform_get_drvdata(pdev); + struct resource *res; + int irq; + + unregister_framebuffer(&fbi->fb); + + writel(0, fbi->regbase); + + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); + + irq = platform_get_irq(pdev, 0); + free_irq(irq, fbi); + + dma_free_coherent(&pdev->dev, fbi->palette_size, + fbi->palette_cpu, fbi->palette_phys); + + iounmap(fbi->regbase); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(fbi); + + return 0; +} + +static struct platform_driver vt8500lcd_driver = { + .probe = vt8500lcd_probe, + .remove = __devexit_p(vt8500lcd_remove), + .driver = { + .owner = THIS_MODULE, + .name = "vt8500-lcd", + }, +}; + +static int __init vt8500lcd_init(void) +{ + return platform_driver_register(&vt8500lcd_driver); +} + +static void __exit vt8500lcd_exit(void) +{ + platform_driver_unregister(&vt8500lcd_driver); +} + +module_init(vt8500lcd_init); +module_exit(vt8500lcd_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); +MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h new file mode 100644 index 00000000000..36ca3ca09d8 --- /dev/null +++ b/drivers/video/vt8500lcdfb.h @@ -0,0 +1,34 @@ +/* + * linux/drivers/video/vt8500lcdfb.h + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +struct vt8500lcd_info { + struct fb_info fb; + void __iomem *regbase; + void __iomem *palette_cpu; + dma_addr_t palette_phys; + size_t palette_size; + wait_queue_head_t wait; +}; + +static int bpp_values[] = { + 1, + 2, + 4, + 8, + 12, + 16, + 18, + 24, +}; diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c new file mode 100644 index 00000000000..96e34a56916 --- /dev/null +++ b/drivers/video/wm8505fb.c @@ -0,0 +1,422 @@ +/* + * WonderMedia WM8505 Frame Buffer device driver + * + * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> + * Based on vt8500lcdfb.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include <mach/vt8500fb.h> + +#include "wm8505fb_regs.h" +#include "wmt_ge_rops.h" + +#define DRIVER_NAME "wm8505-fb" + +#define to_wm8505fb_info(__info) container_of(__info, \ + struct wm8505fb_info, fb) +struct wm8505fb_info { + struct fb_info fb; + void __iomem *regbase; + unsigned int contrast; +}; + + +static int wm8505fb_init_hw(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + int i; + + /* I know the purpose only of few registers, so clear unknown */ + for (i = 0; i < 0x200; i += 4) + writel(0, fbi->regbase + i); + + /* Set frame buffer address */ + writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR); + writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1); + + /* Set in-memory picture format to RGB 32bpp */ + writel(0x1c, fbi->regbase + WMT_GOVR_COLORSPACE); + writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1); + + /* Virtual buffer size */ + writel(info->var.xres, fbi->regbase + WMT_GOVR_XRES); + writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL); + + /* black magic ;) */ + writel(0xf, fbi->regbase + WMT_GOVR_FHI); + writel(4, fbi->regbase + WMT_GOVR_DVO_SET); + writel(1, fbi->regbase + WMT_GOVR_MIF_ENABLE); + writel(1, fbi->regbase + WMT_GOVR_REG_UPDATE); + + return 0; +} + +static int wm8505fb_set_timing(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + int h_start = info->var.left_margin; + int h_end = h_start + info->var.xres; + int h_all = h_end + info->var.right_margin; + int h_sync = info->var.hsync_len; + + int v_start = info->var.upper_margin; + int v_end = v_start + info->var.yres; + int v_all = v_end + info->var.lower_margin; + int v_sync = info->var.vsync_len; + + writel(0, fbi->regbase + WMT_GOVR_TG); + + writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START); + writel(h_end, fbi->regbase + WMT_GOVR_TIMING_H_END); + writel(h_all, fbi->regbase + WMT_GOVR_TIMING_H_ALL); + writel(h_sync, fbi->regbase + WMT_GOVR_TIMING_H_SYNC); + + writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START); + writel(v_end, fbi->regbase + WMT_GOVR_TIMING_V_END); + writel(v_all, fbi->regbase + WMT_GOVR_TIMING_V_ALL); + writel(v_sync, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); + + writel(1, fbi->regbase + WMT_GOVR_TG); + + return 0; +} + + +static int wm8505fb_set_par(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + if (!fbi) + return -EINVAL; + + if (info->var.bits_per_pixel == 32) { + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.red.msb_right = 0; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->var.blue.msb_right = 0; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.xres_virtual << 2; + } + + wm8505fb_set_timing(info); + + writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast, + fbi->regbase + WMT_GOVR_CONTRAST); + + return 0; +} + +static ssize_t contrast_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + return sprintf(buf, "%d\n", fbi->contrast); +} + +static ssize_t contrast_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + unsigned long tmp; + + if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff)) + return -EINVAL; + fbi->contrast = tmp; + + wm8505fb_set_par(info); + + return count; +} + +static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store); + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) { + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + int ret = 1; + unsigned int val; + if (regno >= 256) + return -EINVAL; + + if (info->var.grayscale) + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + + switch (fbi->fb.fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + val = chan_to_field(red, &fbi->fb.var.red); + val |= chan_to_field(green, &fbi->fb.var.green); + val |= chan_to_field(blue, &fbi->fb.var.blue); + + pal[regno] = val; + ret = 0; + } + break; + } + + return ret; +} + +static int wm8505fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN); + writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN); + return 0; +} + +static int wm8505fb_blank(int blank, struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + switch (blank) { + case FB_BLANK_UNBLANK: + wm8505fb_set_timing(info); + break; + default: + writel(0, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); + break; + } + + return 0; +} + +static struct fb_ops wm8505fb_ops = { + .owner = THIS_MODULE, + .fb_set_par = wm8505fb_set_par, + .fb_setcolreg = wm8505fb_setcolreg, + .fb_fillrect = wmt_ge_fillrect, + .fb_copyarea = wmt_ge_copyarea, + .fb_imageblit = sys_imageblit, + .fb_sync = wmt_ge_sync, + .fb_pan_display = wm8505fb_pan_display, + .fb_blank = wm8505fb_blank, +}; + +static int __devinit wm8505fb_probe(struct platform_device *pdev) +{ + struct wm8505fb_info *fbi; + struct resource *res; + void *addr; + struct vt8500fb_platform_data *pdata; + int ret; + + pdata = pdev->dev.platform_data; + + ret = -ENOMEM; + fbi = NULL; + + fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16, + GFP_KERNEL); + if (!fbi) { + dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); + ret = -ENOMEM; + goto failed; + } + + strcpy(fbi->fb.fix.id, DRIVER_NAME); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.xpanstep = 1; + fbi->fb.fix.ypanstep = 1; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.fbops = &wm8505fb_ops; + fbi->fb.flags = FBINFO_DEFAULT + | FBINFO_HWACCEL_COPYAREA + | FBINFO_HWACCEL_FILLRECT + | FBINFO_HWACCEL_XPAN + | FBINFO_HWACCEL_YPAN + | FBINFO_VIRTFB + | FBINFO_PARTIAL_PAN_OK; + fbi->fb.node = -1; + + addr = fbi; + addr = addr + sizeof(struct wm8505fb_info); + fbi->fb.pseudo_palette = addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto failed_fbi; + } + + res = request_mem_region(res->start, resource_size(res), DRIVER_NAME); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto failed_fbi; + } + + fbi->regbase = ioremap(res->start, resource_size(res)); + if (fbi->regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto failed_free_res; + } + + fb_videomode_to_var(&fbi->fb.var, &pdata->mode); + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.xres_virtual = pdata->xres_virtual; + fbi->fb.var.yres_virtual = pdata->yres_virtual; + fbi->fb.var.bits_per_pixel = pdata->bpp; + + fbi->fb.fix.smem_start = pdata->video_mem_phys; + fbi->fb.fix.smem_len = pdata->video_mem_len; + fbi->fb.screen_base = pdata->video_mem_virt; + fbi->fb.screen_size = pdata->video_mem_len; + + if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { + dev_err(&pdev->dev, "Failed to allocate color map\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + wm8505fb_init_hw(&fbi->fb); + + fbi->contrast = 0x80; + ret = wm8505fb_set_par(&fbi->fb); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); + goto failed_free_cmap; + } + + platform_set_drvdata(pdev, fbi); + + ret = register_framebuffer(&fbi->fb); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to register framebuffer device: %d\n", ret); + goto failed_free_cmap; + } + + ret = device_create_file(&pdev->dev, &dev_attr_contrast); + if (ret < 0) { + printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n", + fbi->fb.node, ret); + } + + printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n", + fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start, + fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1); + + return 0; + +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); +failed_free_io: + iounmap(fbi->regbase); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_fbi: + platform_set_drvdata(pdev, NULL); + kfree(fbi); +failed: + return ret; +} + +static int __devexit wm8505fb_remove(struct platform_device *pdev) +{ + struct wm8505fb_info *fbi = platform_get_drvdata(pdev); + struct resource *res; + + device_remove_file(&pdev->dev, &dev_attr_contrast); + + unregister_framebuffer(&fbi->fb); + + writel(0, fbi->regbase); + + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); + + iounmap(fbi->regbase); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(fbi); + + return 0; +} + +static struct platform_driver wm8505fb_driver = { + .probe = wm8505fb_probe, + .remove = __devexit_p(wm8505fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init wm8505fb_init(void) +{ + return platform_driver_register(&wm8505fb_driver); +} + +static void __exit wm8505fb_exit(void) +{ + platform_driver_unregister(&wm8505fb_driver); +} + +module_init(wm8505fb_init); +module_exit(wm8505fb_exit); + +MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>"); +MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h new file mode 100644 index 00000000000..4dd41668c6d --- /dev/null +++ b/drivers/video/wm8505fb_regs.h @@ -0,0 +1,76 @@ +/* + * GOVR registers list for WM8505 chips + * + * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> + * Based on VIA/WonderMedia wm8510-govrh-reg.h + * http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/ + * drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef _WM8505FB_REGS_H +#define _WM8505FB_REGS_H + +/* + * Color space select register, default value 0x1c + * BIT0 GOVRH_DVO_YUV2RGB_ENABLE + * BIT1 GOVRH_VGA_YUV2RGB_ENABLE + * BIT2 GOVRH_RGB_MODE + * BIT3 GOVRH_DAC_CLKINV + * BIT4 GOVRH_BLANK_ZERO + */ +#define WMT_GOVR_COLORSPACE 0x1e4 +/* + * Another colorspace select register, default value 1 + * BIT0 GOVRH_DVO_RGB + * BIT1 GOVRH_DVO_YUV422 + */ +#define WMT_GOVR_COLORSPACE1 0x30 + +#define WMT_GOVR_CONTRAST 0x1b8 +#define WMT_GOVR_BRGHTNESS 0x1bc /* incompatible with RGB? */ + +/* Framubeffer address */ +#define WMT_GOVR_FBADDR 0x90 +#define WMT_GOVR_FBADDR1 0x94 /* UV offset in YUV mode */ + +/* Offset of visible window */ +#define WMT_GOVR_XPAN 0xa4 +#define WMT_GOVR_YPAN 0xa0 + +#define WMT_GOVR_XRES 0x98 +#define WMT_GOVR_XRES_VIRTUAL 0x9c + +#define WMT_GOVR_MIF_ENABLE 0x80 +#define WMT_GOVR_FHI 0xa8 +#define WMT_GOVR_REG_UPDATE 0xe4 + +/* + * BIT0 GOVRH_DVO_OUTWIDTH + * BIT1 GOVRH_DVO_SYNC_POLAR + * BIT2 GOVRH_DVO_ENABLE + */ +#define WMT_GOVR_DVO_SET 0x148 + +/* Timing generator? */ +#define WMT_GOVR_TG 0x100 + +/* Timings */ +#define WMT_GOVR_TIMING_H_ALL 0x108 +#define WMT_GOVR_TIMING_V_ALL 0x10c +#define WMT_GOVR_TIMING_V_START 0x110 +#define WMT_GOVR_TIMING_V_END 0x114 +#define WMT_GOVR_TIMING_H_START 0x118 +#define WMT_GOVR_TIMING_H_END 0x11c +#define WMT_GOVR_TIMING_V_SYNC 0x128 +#define WMT_GOVR_TIMING_H_SYNC 0x12c + +#endif /* _WM8505FB_REGS_H */ diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c new file mode 100644 index 00000000000..45832b7ef7d --- /dev/null +++ b/drivers/video/wmt_ge_rops.c @@ -0,0 +1,186 @@ +/* + * linux/drivers/video/wmt_ge_rops.c + * + * Accelerators for raster operations using WonderMedia Graphics Engine + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/fb.h> +#include <linux/platform_device.h> +#include "fb_draw.h" + +#define GE_COMMAND_OFF 0x00 +#define GE_DEPTH_OFF 0x04 +#define GE_HIGHCOLOR_OFF 0x08 +#define GE_ROPCODE_OFF 0x14 +#define GE_FIRE_OFF 0x18 +#define GE_SRCBASE_OFF 0x20 +#define GE_SRCDISPW_OFF 0x24 +#define GE_SRCDISPH_OFF 0x28 +#define GE_SRCAREAX_OFF 0x2c +#define GE_SRCAREAY_OFF 0x30 +#define GE_SRCAREAW_OFF 0x34 +#define GE_SRCAREAH_OFF 0x38 +#define GE_DESTBASE_OFF 0x3c +#define GE_DESTDISPW_OFF 0x40 +#define GE_DESTDISPH_OFF 0x44 +#define GE_DESTAREAX_OFF 0x48 +#define GE_DESTAREAY_OFF 0x4c +#define GE_DESTAREAW_OFF 0x50 +#define GE_DESTAREAH_OFF 0x54 +#define GE_PAT0C_OFF 0x88 /* Pattern 0 color */ +#define GE_ENABLE_OFF 0xec +#define GE_INTEN_OFF 0xf0 +#define GE_STATUS_OFF 0xf8 + +static void __iomem *regbase; + +void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + unsigned long fg, pat; + + if (p->state != FBINFO_STATE_RUNNING) + return; + + if (p->fix.visual == FB_VISUAL_TRUECOLOR || + p->fix.visual == FB_VISUAL_DIRECTCOLOR) + fg = ((u32 *) (p->pseudo_palette))[rect->color]; + else + fg = rect->color; + + pat = pixel_to_pat(p->var.bits_per_pixel, fg); + + if (p->fbops->fb_sync) + p->fbops->fb_sync(p); + + writel(p->var.bits_per_pixel == 32 ? 3 : + (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF); + writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF); + writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); + writel(rect->dx, regbase + GE_DESTAREAX_OFF); + writel(rect->dy, regbase + GE_DESTAREAY_OFF); + writel(rect->width - 1, regbase + GE_DESTAREAW_OFF); + writel(rect->height - 1, regbase + GE_DESTAREAH_OFF); + + writel(pat, regbase + GE_PAT0C_OFF); + writel(1, regbase + GE_COMMAND_OFF); + writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF); + writel(1, regbase + GE_FIRE_OFF); +} +EXPORT_SYMBOL_GPL(wmt_ge_fillrect); + +void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + if (p->state != FBINFO_STATE_RUNNING) + return; + + if (p->fbops->fb_sync) + p->fbops->fb_sync(p); + + writel(p->var.bits_per_pixel > 16 ? 3 : + (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF); + + writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF); + writel(area->sx, regbase + GE_SRCAREAX_OFF); + writel(area->sy, regbase + GE_SRCAREAY_OFF); + writel(area->width - 1, regbase + GE_SRCAREAW_OFF); + writel(area->height - 1, regbase + GE_SRCAREAH_OFF); + + writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); + writel(area->dx, regbase + GE_DESTAREAX_OFF); + writel(area->dy, regbase + GE_DESTAREAY_OFF); + writel(area->width - 1, regbase + GE_DESTAREAW_OFF); + writel(area->height - 1, regbase + GE_DESTAREAH_OFF); + + writel(0xcc, regbase + GE_ROPCODE_OFF); + writel(1, regbase + GE_COMMAND_OFF); + writel(1, regbase + GE_FIRE_OFF); +} +EXPORT_SYMBOL_GPL(wmt_ge_copyarea); + +int wmt_ge_sync(struct fb_info *p) +{ + int loops = 5000000; + while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops) + cpu_relax(); + return loops > 0 ? 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(wmt_ge_sync); + +static int __devinit wmt_ge_rops_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + return -ENODEV; + } + + /* Only one ROP engine is presently supported. */ + if (unlikely(regbase)) { + WARN_ON(1); + return -EBUSY; + } + + regbase = ioremap(res->start, resource_size(res)); + if (regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + return -EBUSY; + } + + writel(1, regbase + GE_ENABLE_OFF); + printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n"); + + return 0; +} + +static int __devexit wmt_ge_rops_remove(struct platform_device *pdev) +{ + iounmap(regbase); + return 0; +} + +static struct platform_driver wmt_ge_rops_driver = { + .probe = wmt_ge_rops_probe, + .remove = __devexit_p(wmt_ge_rops_remove), + .driver = { + .owner = THIS_MODULE, + .name = "wmt_ge_rops", + }, +}; + +static int __init wmt_ge_rops_init(void) +{ + return platform_driver_register(&wmt_ge_rops_driver); +} + +static void __exit wmt_ge_rops_exit(void) +{ + platform_driver_unregister(&wmt_ge_rops_driver); +} + +module_init(wmt_ge_rops_init); +module_exit(wmt_ge_rops_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com"); +MODULE_DESCRIPTION("Accelerators for raster operations using " + "WonderMedia Graphics Engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h new file mode 100644 index 00000000000..87380751a44 --- /dev/null +++ b/drivers/video/wmt_ge_rops.h @@ -0,0 +1,5 @@ +extern void wmt_ge_fillrect(struct fb_info *info, + const struct fb_fillrect *rect); +extern void wmt_ge_copyarea(struct fb_info *info, + const struct fb_copyarea *area); +extern int wmt_ge_sync(struct fb_info *info); diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 428d273be72..3e6934d4bea 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -492,7 +492,7 @@ xenfb_make_preferred_console(void) return; acquire_console_sem(); - for (c = console_drivers; c; c = c->next) { + for_each_console(c) { if (!strcmp(c->name, "tty") && c->index == 0) break; } @@ -562,26 +562,24 @@ static void xenfb_init_shared_page(struct xenfb_info *info, static int xenfb_connect_backend(struct xenbus_device *dev, struct xenfb_info *info) { - int ret, evtchn; + int ret, evtchn, irq; struct xenbus_transaction xbt; ret = xenbus_alloc_evtchn(dev, &evtchn); if (ret) return ret; - ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, + irq = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, 0, dev->devicetype, info); - if (ret < 0) { + if (irq < 0) { xenbus_free_evtchn(dev, evtchn); xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); - return ret; + return irq; } - info->irq = ret; - again: ret = xenbus_transaction_start(&xbt); if (ret) { xenbus_dev_fatal(dev, ret, "starting transaction"); - return ret; + goto unbind_irq; } ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", virt_to_mfn(info->page)); @@ -603,20 +601,25 @@ static int xenfb_connect_backend(struct xenbus_device *dev, if (ret == -EAGAIN) goto again; xenbus_dev_fatal(dev, ret, "completing transaction"); - return ret; + goto unbind_irq; } xenbus_switch_state(dev, XenbusStateInitialised); + info->irq = irq; return 0; error_xenbus: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, ret, "writing xenstore"); + unbind_irq: + unbind_from_irqhandler(irq, info); return ret; } static void xenfb_disconnect_backend(struct xenfb_info *info) { + /* Prevent xenfb refresh */ + info->update_wanted = 0; if (info->irq >= 0) unbind_from_irqhandler(info->irq, info); info->irq = -1; |