diff options
Diffstat (limited to 'drivers/video')
104 files changed, 8548 insertions, 3292 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e6a8d8c0101d..549b960667c8 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -8,9 +8,6 @@ menu "Graphics support" config HAVE_FB_ATMEL bool -config HAVE_FB_IMX - bool - config SH_MIPI_DSI tristate depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK @@ -359,7 +356,7 @@ config FB_SA1100 config FB_IMX tristate "Freescale i.MX LCD support" - depends on FB && (HAVE_FB_IMX || ARCH_MX1 || ARCH_MX2) + depends on FB && IMX_HAVE_PLATFORM_IMX_FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1463,6 +1460,14 @@ config FB_S3 ---help--- Driver for graphics boards with S3 Trio / S3 Virge chip. +config FB_S3_DDC + bool "DDC for S3 support" + depends on FB_S3 + select FB_DDC + default y + help + Say Y here if you want DDC support for your S3 graphics card. + config FB_SAVAGE tristate "S3 Savage support" depends on FB && PCI && EXPERIMENTAL @@ -1562,6 +1567,17 @@ config FB_VIA_DIRECT_PROCFS correct output device configuration. Its use is strongly discouraged. +config FB_VIA_X_COMPATIBILITY + bool "X server compatibility" + depends on FB_VIA + default n + help + This option reduces the functionality (power saving, ...) of the + framebuffer to avoid negative impact on the OpenChrome X server. + If you use any X server other than fbdev you should enable this + otherwise it should be safe to disable it and allow using all + features. + endif config FB_NEOMAGIC @@ -1975,6 +1991,18 @@ config FB_SH_MOBILE_HDMI ---help--- Driver for the on-chip SH-Mobile HDMI controller. +config FB_SH_MOBILE_MERAM + tristate "SuperH Mobile MERAM read ahead support for LCDC" + depends on FB_SH_MOBILE_LCDC + default y + ---help--- + Enable MERAM support for the SH-Mobile LCD controller. + + This will allow for caching of the framebuffer to provide more + reliable access under heavy main memory bus traffic situations. + Up to 4 memory channels can be configured, allowing 4 RGB or + 2 YCbCr framebuffers to be configured. + config FB_TMIO tristate "Toshiba Mobile IO FrameBuffer support" depends on FB && MFD_CORE @@ -2238,29 +2266,43 @@ config FB_METRONOME config FB_MB862XX tristate "Fujitsu MB862xx GDC support" depends on FB + depends on PCI || (OF && PPC) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT ---help--- Frame buffer driver for Fujitsu Carmine/Coral-P(A)/Lime controllers. +choice + prompt "GDC variant" + depends on FB_MB862XX + config FB_MB862XX_PCI_GDC bool "Carmine/Coral-P(A) GDC" - depends on PCI && FB_MB862XX + depends on PCI ---help--- This enables framebuffer support for Fujitsu Carmine/Coral-P(A) PCI graphics controller devices. config FB_MB862XX_LIME bool "Lime GDC" - depends on FB_MB862XX - depends on OF && !FB_MB862XX_PCI_GDC - depends on PPC + depends on OF && PPC select FB_FOREIGN_ENDIAN select FB_LITTLE_ENDIAN ---help--- Framebuffer support for Fujitsu Lime GDC on host CPU bus. +endchoice + +config FB_MB862XX_I2C + bool "Support I2C bus on MB862XX GDC" + depends on FB_MB862XX && I2C + default y + help + Selecting this option adds Coral-P(A)/Lime GDC I2C bus adapter + driver to support accessing I2C devices on controller's I2C bus. + These are usually some video decoder chips. + config FB_EP93XX tristate "EP93XX frame buffer support" depends on FB && ARCH_EP93XX diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 2ea44b6625fe..8b83129e209c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -130,6 +130,7 @@ 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 +obj-$(CONFIG_FB_SH_MOBILE_MERAM) += sh_mobile_meram.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ obj-y += omap2/ diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index 82acb8dc4aa1..6183a57eb69d 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -66,7 +66,7 @@ * have. Allow 1% either way on the nominal for TVs. */ #define NR_MONTYPES 6 -static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = { +static struct fb_monspecs monspecs[NR_MONTYPES] __devinitdata = { { /* TV */ .hfmin = 15469, .hfmax = 15781, @@ -873,7 +873,7 @@ static struct fb_ops acornfb_ops = { /* * Everything after here is initialisation!!! */ -static struct fb_videomode modedb[] __initdata = { +static struct fb_videomode modedb[] __devinitdata = { { /* 320x256 @ 50Hz */ NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2, FB_SYNC_COMP_HIGH_ACT, @@ -925,8 +925,7 @@ static struct fb_videomode modedb[] __initdata = { } }; -static struct fb_videomode __initdata -acornfb_default_mode = { +static struct fb_videomode acornfb_default_mode __devinitdata = { .name = NULL, .refresh = 60, .xres = 640, @@ -942,7 +941,7 @@ acornfb_default_mode = { .vmode = FB_VMODE_NONINTERLACED }; -static void __init acornfb_init_fbinfo(void) +static void __devinit acornfb_init_fbinfo(void) { static int first = 1; @@ -1018,8 +1017,7 @@ static void __init acornfb_init_fbinfo(void) * size can optionally be followed by 'M' or 'K' for * MB or KB respectively. */ -static void __init -acornfb_parse_mon(char *opt) +static void __devinit acornfb_parse_mon(char *opt) { char *p = opt; @@ -1066,8 +1064,7 @@ bad: current_par.montype = -1; } -static void __init -acornfb_parse_montype(char *opt) +static void __devinit acornfb_parse_montype(char *opt) { current_par.montype = -2; @@ -1108,8 +1105,7 @@ acornfb_parse_montype(char *opt) } } -static void __init -acornfb_parse_dram(char *opt) +static void __devinit acornfb_parse_dram(char *opt) { unsigned int size; @@ -1134,15 +1130,14 @@ acornfb_parse_dram(char *opt) static struct options { char *name; void (*parse)(char *opt); -} opt_table[] __initdata = { +} opt_table[] __devinitdata = { { "mon", acornfb_parse_mon }, { "montype", acornfb_parse_montype }, { "dram", acornfb_parse_dram }, { NULL, NULL } }; -int __init -acornfb_setup(char *options) +static int __devinit acornfb_setup(char *options) { struct options *optp; char *opt; @@ -1179,8 +1174,7 @@ acornfb_setup(char *options) * Detect type of monitor connected * For now, we just assume SVGA */ -static int __init -acornfb_detect_monitortype(void) +static int __devinit acornfb_detect_monitortype(void) { return 4; } diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index e5d6b56d4447..5ea6596dd824 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -2224,22 +2224,23 @@ static int amifb_ioctl(struct fb_info *info, * Allocate, Clear and Align a Block of Chip Memory */ -static u_long unaligned_chipptr = 0; +static void *aligned_chipptr; static inline u_long __init chipalloc(u_long size) { - size += PAGE_SIZE-1; - if (!(unaligned_chipptr = (u_long)amiga_chip_alloc(size, - "amifb [RAM]"))) - panic("No Chip RAM for frame buffer"); - memset((void *)unaligned_chipptr, 0, size); - return PAGE_ALIGN(unaligned_chipptr); + aligned_chipptr = amiga_chip_alloc(size, "amifb [RAM]"); + if (!aligned_chipptr) { + pr_err("amifb: No Chip RAM for frame buffer"); + return 0; + } + memset(aligned_chipptr, 0, size); + return (u_long)aligned_chipptr; } static inline void chipfree(void) { - if (unaligned_chipptr) - amiga_chip_free((void *)unaligned_chipptr); + if (aligned_chipptr) + amiga_chip_free(aligned_chipptr); } @@ -2295,7 +2296,7 @@ default_chipset: defmode = amiga_vblank == 50 ? DEFMODE_PAL : DEFMODE_NTSC; if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT > - VIDEOMEMSIZE_ECS_1M) + VIDEOMEMSIZE_ECS_2M) fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_2M; else fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_1M; @@ -2312,7 +2313,7 @@ default_chipset: maxfmode = TAG_FMODE_4; defmode = DEFMODE_AGA; if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT > - VIDEOMEMSIZE_AGA_1M) + VIDEOMEMSIZE_AGA_2M) fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_2M; else fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_1M; @@ -2385,6 +2386,10 @@ default_chipset: DUMMYSPRITEMEMSIZE+ COPINITSIZE+ 4*COPLISTSIZE); + if (!chipptr) { + err = -ENOMEM; + goto amifb_error; + } assignchunk(videomemory, u_long, chipptr, fb_info.fix.smem_len); assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE); diff --git a/drivers/video/arcfb.c b/drivers/video/arcfb.c index 3ec4923c2d84..c22e8d39a2cb 100644 --- a/drivers/video/arcfb.c +++ b/drivers/video/arcfb.c @@ -515,11 +515,10 @@ static int __devinit arcfb_probe(struct platform_device *dev) /* We need a flat backing store for the Arc's less-flat actual paged framebuffer */ - if (!(videomemory = vmalloc(videomemorysize))) + videomemory = vzalloc(videomemorysize); + if (!videomemory) return retval; - memset(videomemory, 0, videomemorysize); - info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev); if (!info) goto err; diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 5b2b5ef4edba..64e41f5448c4 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -3117,7 +3117,7 @@ int __init atafb_init(void) atafb_ops.fb_setcolreg = &falcon_setcolreg; error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO, - "framebuffer/modeswitch", + "framebuffer:modeswitch", falcon_vbl_switcher); if (error) return error; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index ebb893c49e90..d7aaec5667bf 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -248,10 +248,6 @@ static int atyfb_sync(struct fb_info *info); static int aty_init(struct fb_info *info); -#ifdef CONFIG_ATARI -static int store_video_par(char *videopar, unsigned char m64_num); -#endif - static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); @@ -2268,11 +2264,13 @@ error: return; } +#ifdef CONFIG_PCI static void aty_bl_exit(struct backlight_device *bd) { backlight_device_unregister(bd); printk("aty: Backlight unloaded\n"); } +#endif /* CONFIG_PCI */ #endif /* CONFIG_FB_ATY_BACKLIGHT */ @@ -2789,7 +2787,7 @@ aty_init_exit: return ret; } -#ifdef CONFIG_ATARI +#if defined(CONFIG_ATARI) && !defined(MODULE) static int __devinit store_video_par(char *video_str, unsigned char m64_num) { char *p; @@ -2818,7 +2816,7 @@ static int __devinit store_video_par(char *video_str, unsigned char m64_num) phys_vmembase[m64_num] = 0; return -1; } -#endif /* CONFIG_ATARI */ +#endif /* CONFIG_ATARI && !MODULE */ /* * Blank the display. diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index c8b520e9a11a..c04b94da81f7 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -16,7 +16,6 @@ #include <linux/fb.h> #include <linux/i2c.h> #include <linux/backlight.h> -#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> #define MAX_BRIGHTNESS (0xFF) @@ -168,7 +167,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) struct pm860x_backlight_pdata *pdata = NULL; struct pm860x_backlight_data *data; struct backlight_device *bl; - struct mfd_cell *cell; struct resource *res; struct backlight_properties props; unsigned char value; @@ -181,10 +179,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return -EINVAL; } - cell = pdev->dev.platform_data; - if (cell == NULL) - return -ENODEV; - pdata = cell->mfd_data; + pdata = pdev->dev.platform_data; if (pdata == NULL) { dev_err(&pdev->dev, "platform data isn't assigned to " "backlight\n"); diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 0c9373bedd1f..2d93c8d61ad5 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -302,6 +302,18 @@ config BACKLIGHT_ADP8860 To compile this driver as a module, choose M here: the module will be called adp8860_bl. +config BACKLIGHT_ADP8870 + tristate "Backlight Driver for ADP8870 using WLED" + depends on BACKLIGHT_CLASS_DEVICE && I2C + select NEW_LEDS + select LEDS_CLASS + help + If you have a LCD backlight connected to the ADP8870, + say Y here to enable this driver. + + To compile this driver as a module, choose M here: the module will + be called adp8870_bl. + config BACKLIGHT_88PM860X tristate "Backlight Driver for 88PM8606 using WLED" depends on MFD_88PM860X diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b9ca8490df87..ee72adb8786e 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c index af3119707dbf..d1aee730d7d8 100644 --- a/drivers/video/backlight/adp5520_bl.c +++ b/drivers/video/backlight/adp5520_bl.c @@ -211,8 +211,12 @@ static ssize_t adp5520_bl_daylight_max_store(struct device *dev, const char *buf, size_t count) { struct adp5520_bl *data = dev_get_drvdata(dev); + int ret; + + ret = strict_strtoul(buf, 10, &data->cached_daylight_max); + if (ret < 0) + return ret; - strict_strtoul(buf, 10, &data->cached_daylight_max); return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX); } static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show, diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c new file mode 100644 index 000000000000..05a8832bb3eb --- /dev/null +++ b/drivers/video/backlight/adp8870_bl.c @@ -0,0 +1,1012 @@ +/* + * Backlight driver for Analog Devices ADP8870 Backlight Devices + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#include <linux/i2c/adp8870.h> +#define ADP8870_EXT_FEATURES +#define ADP8870_USE_LEDS + + +#define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */ +#define ADP8870_MDCR 0x01 /* Device mode and status */ +#define ADP8870_INT_STAT 0x02 /* Interrupts status */ +#define ADP8870_INT_EN 0x03 /* Interrupts enable */ +#define ADP8870_CFGR 0x04 /* Configuration register */ +#define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */ +#define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */ +#define ADP8870_BLOFF 0x07 /* Backlight off timeout */ +#define ADP8870_BLDIM 0x08 /* Backlight dim timeout */ +#define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */ +#define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */ +#define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */ +#define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */ +#define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */ +#define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */ +#define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */ +#define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */ +#define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */ +#define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */ +#define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */ +#define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */ +#define ADP8870_ISCC 0x1B /* Independent sink current control register */ +#define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */ +#define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */ +#define ADP8870_ISCF 0x1E /* Independent sink current fade register */ +#define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */ +#define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */ +#define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */ +#define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */ +#define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */ +#define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */ +#define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ +#define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */ +#define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */ +#define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ +#define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */ +#define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */ +#define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */ +#define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */ +#define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */ +#define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */ +#define ADP8870_L2TRP 0x32 /* L2 comparator reference */ +#define ADP8870_L2HYS 0x33 /* L2 hysteresis */ +#define ADP8870_L3TRP 0x34 /* L3 comparator reference */ +#define ADP8870_L3HYS 0x35 /* L3 hysteresis */ +#define ADP8870_L4TRP 0x36 /* L4 comparator reference */ +#define ADP8870_L4HYS 0x37 /* L4 hysteresis */ +#define ADP8870_L5TRP 0x38 /* L5 comparator reference */ +#define ADP8870_L5HYS 0x39 /* L5 hysteresis */ +#define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */ +#define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */ +#define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */ +#define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */ + +#define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */ +#define ADP8870_DEVID(x) ((x) & 0xF) +#define ADP8870_MANID(x) ((x) >> 4) + +/* MDCR Device mode and status */ +#define D7ALSEN (1 << 7) +#define INT_CFG (1 << 6) +#define NSTBY (1 << 5) +#define DIM_EN (1 << 4) +#define GDWN_DIS (1 << 3) +#define SIS_EN (1 << 2) +#define CMP_AUTOEN (1 << 1) +#define BLEN (1 << 0) + +/* ADP8870_ALS1_EN Main ALS comparator level enable */ +#define L5_EN (1 << 3) +#define L4_EN (1 << 2) +#define L3_EN (1 << 1) +#define L2_EN (1 << 0) + +#define CFGR_BLV_SHIFT 3 +#define CFGR_BLV_MASK 0x7 +#define ADP8870_FLAG_LED_MASK 0xFF + +#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) +#define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) +#define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1) + +struct adp8870_bl { + struct i2c_client *client; + struct backlight_device *bl; + struct adp8870_led *led; + struct adp8870_backlight_platform_data *pdata; + struct mutex lock; + unsigned long cached_daylight_max; + int id; + int revid; + int current_brightness; +}; + +struct adp8870_led { + struct led_classdev cdev; + struct work_struct work; + struct i2c_client *client; + enum led_brightness new_brightness; + int id; + int flags; +}; + +static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "failed reading at 0x%02x\n", reg); + return ret; + } + + *val = ret; + return 0; +} + + +static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret) + dev_err(&client->dev, "failed to write\n"); + + return ret; +} + +static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8870_read(client, reg, ®_val); + + if (!ret && ((reg_val & bit_mask) == 0)) { + reg_val |= bit_mask; + ret = adp8870_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + uint8_t reg_val; + int ret; + + mutex_lock(&data->lock); + + ret = adp8870_read(client, reg, ®_val); + + if (!ret && (reg_val & bit_mask)) { + reg_val &= ~bit_mask; + ret = adp8870_write(client, reg, reg_val); + } + + mutex_unlock(&data->lock); + return ret; +} + +/* + * Independent sink / LED + */ +#if defined(ADP8870_USE_LEDS) +static void adp8870_led_work(struct work_struct *work) +{ + struct adp8870_led *led = container_of(work, struct adp8870_led, work); + adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, + led->new_brightness >> 1); +} + +static void adp8870_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct adp8870_led *led; + + led = container_of(led_cdev, struct adp8870_led, cdev); + led->new_brightness = value; + /* + * Use workqueue for IO since I2C operations can sleep. + */ + schedule_work(&led->work); +} + +static int adp8870_led_setup(struct adp8870_led *led) +{ + struct i2c_client *client = led->client; + int ret = 0; + + ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); + if (ret) + return ret; + + ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); + if (ret) + return ret; + + if (led->id > 4) + ret = adp8870_set_bits(client, ADP8870_ISCT1, + (led->flags & 0x3) << ((led->id - 5) * 2)); + else + ret = adp8870_set_bits(client, ADP8870_ISCT2, + (led->flags & 0x3) << ((led->id - 1) * 2)); + + return ret; +} + +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8870_bl *data = i2c_get_clientdata(client); + struct adp8870_led *led, *led_dat; + struct led_info *cur_led; + int ret, i; + + + led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL); + if (led == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); + if (ret) + goto err_free; + + ret = adp8870_write(client, ADP8870_ISCT1, + (pdata->led_on_time & 0x3) << 6); + if (ret) + goto err_free; + + ret = adp8870_write(client, ADP8870_ISCF, + FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); + if (ret) + goto err_free; + + for (i = 0; i < pdata->num_leds; ++i) { + cur_led = &pdata->leds[i]; + led_dat = &led[i]; + + led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; + + if (led_dat->id > 7 || led_dat->id < 1) { + dev_err(&client->dev, "Invalid LED ID %d\n", + led_dat->id); + goto err; + } + + if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { + dev_err(&client->dev, "LED %d used by Backlight\n", + led_dat->id); + goto err; + } + + led_dat->cdev.name = cur_led->name; + led_dat->cdev.default_trigger = cur_led->default_trigger; + led_dat->cdev.brightness_set = adp8870_led_set; + led_dat->cdev.brightness = LED_OFF; + led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; + led_dat->client = client; + led_dat->new_brightness = LED_OFF; + INIT_WORK(&led_dat->work, adp8870_led_work); + + ret = led_classdev_register(&client->dev, &led_dat->cdev); + if (ret) { + dev_err(&client->dev, "failed to register LED %d\n", + led_dat->id); + goto err; + } + + ret = adp8870_led_setup(led_dat); + if (ret) { + dev_err(&client->dev, "failed to write\n"); + i++; + goto err; + } + } + + data->led = led; + + return 0; + + err: + for (i = i - 1; i >= 0; --i) { + led_classdev_unregister(&led[i].cdev); + cancel_work_sync(&led[i].work); + } + + err_free: + kfree(led); + + return ret; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + struct adp8870_bl *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&data->led[i].cdev); + cancel_work_sync(&data->led[i].work); + } + + kfree(data->led); + return 0; +} +#else +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ + return 0; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ + return 0; +} +#endif + +static int adp8870_bl_set(struct backlight_device *bl, int brightness) +{ + struct adp8870_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + int ret = 0; + + if (data->pdata->en_ambl_sens) { + if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { + /* Disable Ambient Light auto adjust */ + ret = adp8870_clr_bits(client, ADP8870_MDCR, + CMP_AUTOEN); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLMX1, brightness); + if (ret) + return ret; + } else { + /* + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust + * restore daylight l1 sysfs brightness + */ + ret = adp8870_write(client, ADP8870_BLMX1, + data->cached_daylight_max); + if (ret) + return ret; + + ret = adp8870_set_bits(client, ADP8870_MDCR, + CMP_AUTOEN); + if (ret) + return ret; + } + } else { + ret = adp8870_write(client, ADP8870_BLMX1, brightness); + if (ret) + return ret; + } + + if (data->current_brightness && brightness == 0) + ret = adp8870_set_bits(client, + ADP8870_MDCR, DIM_EN); + else if (data->current_brightness == 0 && brightness) + ret = adp8870_clr_bits(client, + ADP8870_MDCR, DIM_EN); + + if (!ret) + data->current_brightness = brightness; + + return ret; +} + +static int adp8870_bl_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return adp8870_bl_set(bl, brightness); +} + +static int adp8870_bl_get_brightness(struct backlight_device *bl) +{ + struct adp8870_bl *data = bl_get_data(bl); + + return data->current_brightness; +} + +static const struct backlight_ops adp8870_bl_ops = { + .update_status = adp8870_bl_update_status, + .get_brightness = adp8870_bl_get_brightness, +}; + +static int adp8870_bl_setup(struct backlight_device *bl) +{ + struct adp8870_bl *data = bl_get_data(bl); + struct i2c_client *client = data->client; + struct adp8870_backlight_platform_data *pdata = data->pdata; + int ret = 0; + + ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); + if (ret) + return ret; + + if (pdata->en_ambl_sens) { + data->cached_daylight_max = pdata->l1_daylight_max; + ret = adp8870_write(client, ADP8870_BLMX2, + pdata->l2_bright_max); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLDM2, + pdata->l2_bright_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX3, + pdata->l3_office_max); + if (ret) + return ret; + ret = adp8870_write(client, ADP8870_BLDM3, + pdata->l3_office_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX4, + pdata->l4_indoor_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM4, + pdata->l4_indor_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLMX5, + pdata->l5_dark_max); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLDM5, + pdata->l5_dark_dim); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | + L3_EN | L2_EN); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_CMP_CTL, + ALS_CMPR_CFG_VAL(pdata->abml_filt)); + if (ret) + return ret; + } + + ret = adp8870_write(client, ADP8870_CFGR, + BL_CFGR_VAL(pdata->bl_fade_law, 0)); + if (ret) + return ret; + + ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, + pdata->bl_fade_out)); + if (ret) + return ret; + /* + * ADP8870 Rev0 requires GDWN_DIS bit set + */ + + ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | + (data->revid == 0 ? GDWN_DIS : 0)); + + return ret; +} + +static ssize_t adp8870_show(struct device *dev, char *buf, int reg) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, reg, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", reg_val); +} + +static ssize_t adp8870_store(struct device *dev, const char *buf, + size_t count, int reg) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&data->lock); + adp8870_write(data->client, reg, val); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX5); +} + +static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX5); +} +static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, + adp8870_bl_l5_dark_max_store); + + +static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX4); +} + +static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX4); +} +static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, + adp8870_bl_l4_indoor_max_store); + + +static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX3); +} + +static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX3); +} + +static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, + adp8870_bl_l3_office_max_store); + +static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX2); +} + +static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLMX2); +} +static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, + adp8870_bl_l2_bright_max_store); + +static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLMX1); +} + +static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); + if (ret) + return ret; + + return adp8870_store(dev, buf, count, ADP8870_BLMX1); +} +static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, + adp8870_bl_l1_daylight_max_store); + +static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM5); +} + +static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM5); +} +static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, + adp8870_bl_l5_dark_dim_store); + +static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM4); +} + +static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM4); +} +static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, + adp8870_bl_l4_indoor_dim_store); + + +static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM3); +} + +static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM3); +} +static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, + adp8870_bl_l3_office_dim_store); + +static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM2); +} + +static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM2); +} +static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, + adp8870_bl_l2_bright_dim_store); + +static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp8870_show(dev, buf, ADP8870_BLDM1); +} + +static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp8870_store(dev, buf, count, ADP8870_BLDM1); +} +static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, + adp8870_bl_l1_daylight_dim_store); + +#ifdef ADP8870_EXT_FEATURES +static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + uint16_t ret_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); + if (error < 0) { + mutex_unlock(&data->lock); + return error; + } + ret_val = reg_val; + error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + /* Return 13-bit conversion value for the first light sensor */ + ret_val += (reg_val & 0x1F) << 8; + + return sprintf(buf, "%u\n", ret_val); +} +static DEVICE_ATTR(ambient_light_level, 0444, + adp8870_bl_ambient_light_level_show, NULL); + +static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp8870_read(data->client, ADP8870_CFGR, ®_val); + mutex_unlock(&data->lock); + + if (error < 0) + return error; + + return sprintf(buf, "%u\n", + ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); +} + +static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adp8870_bl *data = dev_get_drvdata(dev); + unsigned long val; + uint8_t reg_val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + if (val == 0) { + /* Enable automatic ambient light sensing */ + adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); + } else if ((val > 0) && (val < 6)) { + /* Disable automatic ambient light sensing */ + adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); + + /* Set user supplied ambient light zone */ + mutex_lock(&data->lock); + adp8870_read(data->client, ADP8870_CFGR, ®_val); + reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); + reg_val |= (val - 1) << CFGR_BLV_SHIFT; + adp8870_write(data->client, ADP8870_CFGR, reg_val); + mutex_unlock(&data->lock); + } + + return count; +} +static DEVICE_ATTR(ambient_light_zone, 0664, + adp8870_bl_ambient_light_zone_show, + adp8870_bl_ambient_light_zone_store); +#endif + +static struct attribute *adp8870_bl_attributes[] = { + &dev_attr_l5_dark_max.attr, + &dev_attr_l5_dark_dim.attr, + &dev_attr_l4_indoor_max.attr, + &dev_attr_l4_indoor_dim.attr, + &dev_attr_l3_office_max.attr, + &dev_attr_l3_office_dim.attr, + &dev_attr_l2_bright_max.attr, + &dev_attr_l2_bright_dim.attr, + &dev_attr_l1_daylight_max.attr, + &dev_attr_l1_daylight_dim.attr, +#ifdef ADP8870_EXT_FEATURES + &dev_attr_ambient_light_level.attr, + &dev_attr_ambient_light_zone.attr, +#endif + NULL +}; + +static const struct attribute_group adp8870_bl_attr_group = { + .attrs = adp8870_bl_attributes, +}; + +static int __devinit adp8870_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct backlight_properties props; + struct backlight_device *bl; + struct adp8870_bl *data; + struct adp8870_backlight_platform_data *pdata = + client->dev.platform_data; + uint8_t reg_val; + 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; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + ret = adp8870_read(client, ADP8870_MFDVID, ®_val); + if (ret < 0) + return -EIO; + + if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { + dev_err(&client->dev, "failed to probe\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->revid = ADP8870_DEVID(reg_val); + data->client = client; + data->pdata = pdata; + data->id = id->driver_data; + data->current_brightness = 0; + i2c_set_clientdata(client, data); + + mutex_init(&data->lock); + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; + props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; + bl = backlight_device_register(dev_driver_string(&client->dev), + &client->dev, data, &adp8870_bl_ops, &props); + if (IS_ERR(bl)) { + dev_err(&client->dev, "failed to register backlight\n"); + ret = PTR_ERR(bl); + goto out2; + } + + data->bl = bl; + + if (pdata->en_ambl_sens) + ret = sysfs_create_group(&bl->dev.kobj, + &adp8870_bl_attr_group); + + if (ret) { + dev_err(&client->dev, "failed to register sysfs\n"); + goto out1; + } + + ret = adp8870_bl_setup(bl); + if (ret) { + ret = -EIO; + goto out; + } + + backlight_update_status(bl); + + dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); + + if (pdata->num_leds) + adp8870_led_probe(client); + + return 0; + +out: + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8870_bl_attr_group); +out1: + backlight_device_unregister(bl); +out2: + i2c_set_clientdata(client, NULL); + kfree(data); + + return ret; +} + +static int __devexit adp8870_remove(struct i2c_client *client) +{ + struct adp8870_bl *data = i2c_get_clientdata(client); + + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + + if (data->led) + adp8870_led_remove(client); + + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&data->bl->dev.kobj, + &adp8870_bl_attr_group); + + backlight_device_unregister(data->bl); + i2c_set_clientdata(client, NULL); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + + return 0; +} + +static int adp8870_i2c_resume(struct i2c_client *client) +{ + adp8870_set_bits(client, ADP8870_MDCR, NSTBY); + + return 0; +} +#else +#define adp8870_i2c_suspend NULL +#define adp8870_i2c_resume NULL +#endif + +static const struct i2c_device_id adp8870_id[] = { + { "adp8870", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp8870_id); + +static struct i2c_driver adp8870_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = adp8870_probe, + .remove = __devexit_p(adp8870_remove), + .suspend = adp8870_i2c_suspend, + .resume = adp8870_i2c_resume, + .id_table = adp8870_id, +}; + +static int __init adp8870_init(void) +{ + return i2c_add_driver(&adp8870_driver); +} +module_init(adp8870_init); + +static void __exit adp8870_exit(void) +{ + i2c_del_driver(&adp8870_driver); +} +module_exit(adp8870_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP8870 Backlight driver"); +MODULE_ALIAS("platform:adp8870-backlight"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index dd0e84a9bd2f..cca43c06d3c8 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -333,7 +333,7 @@ static void __exit ltv350qv_exit(void) module_init(ltv350qv_init); module_exit(ltv350qv_exit); -MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:ltv350qv"); diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c index 47c21fb2c82f..bea53c1a4950 100644 --- a/drivers/video/bf537-lq035.c +++ b/drivers/video/bf537-lq035.c @@ -789,6 +789,7 @@ static int __devinit bfin_lq035_probe(struct platform_device *pdev) i2c_add_driver(&ad5280_driver); memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; props.max_brightness = MAX_BRIGHENESS; bl_dev = backlight_device_register("bf537-bl", NULL, NULL, &bfin_lq035fb_bl_ops, &props); diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c index ebda6876d3a9..377dde3d5bfc 100644 --- a/drivers/video/broadsheetfb.c +++ b/drivers/video/broadsheetfb.c @@ -1101,12 +1101,10 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE); - videomemory = vmalloc(videomemorysize); + videomemory = vzalloc(videomemorysize); if (!videomemory) goto err_fb_rel; - memset(videomemory, 0, videomemorysize); - info->screen_base = (char *)videomemory; info->fbops = &broadsheetfb_ops; diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 3772433c49d1..93317b5b8740 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -6,7 +6,7 @@ * * This driver is based on sgicons.c and cons_newport. * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1996 David S. Miller (davem@davemloft.net) * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ #include <linux/init.h> diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 8b7d47386f39..fcdac872522d 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -899,7 +899,7 @@ static struct fb_ops da8xx_fb_ops = { .fb_blank = cfb_blank, }; -static int __init fb_probe(struct platform_device *device) +static int __devinit fb_probe(struct platform_device *device) { struct da8xx_lcdc_platform_data *fb_pdata = device->dev.platform_data; @@ -1165,7 +1165,7 @@ static int fb_resume(struct platform_device *dev) static struct platform_driver da8xx_fb_driver = { .probe = fb_probe, - .remove = fb_remove, + .remove = __devexit_p(fb_remove), .suspend = fb_suspend, .resume = fb_resume, .driver = { diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index 4eb38db36e4b..784139aed079 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c @@ -16,6 +16,8 @@ #include <linux/pci.h> #include <video/vga.h> +static bool request_mem_succeeded = false; + static struct fb_var_screeninfo efifb_defined __devinitdata = { .activate = FB_ACTIVATE_NOW, .height = -1, @@ -242,9 +244,9 @@ static int set_system(const struct dmi_system_id *id) return 0; } - printk(KERN_INFO "efifb: dmi detected %s - framebuffer at %p " + printk(KERN_INFO "efifb: dmi detected %s - framebuffer at 0x%08x " "(%dx%d, stride %d)\n", id->ident, - (void *)screen_info.lfb_base, screen_info.lfb_width, + screen_info.lfb_base, screen_info.lfb_width, screen_info.lfb_height, screen_info.lfb_linelength); @@ -281,7 +283,9 @@ static void efifb_destroy(struct fb_info *info) { if (info->screen_base) iounmap(info->screen_base); - release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); + if (request_mem_succeeded) + release_mem_region(info->apertures->ranges[0].base, + info->apertures->ranges[0].size); framebuffer_release(info); } @@ -326,14 +330,13 @@ static int __init efifb_setup(char *options) return 0; } -static int __devinit efifb_probe(struct platform_device *dev) +static int __init efifb_probe(struct platform_device *dev) { struct fb_info *info; int err; unsigned int size_vmode; unsigned int size_remap; unsigned int size_total; - int request_succeeded = 0; if (!screen_info.lfb_depth) screen_info.lfb_depth = 32; @@ -387,7 +390,7 @@ static int __devinit efifb_probe(struct platform_device *dev) efifb_fix.smem_len = size_remap; if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) { - request_succeeded = 1; + request_mem_succeeded = true; } else { /* We cannot make this fatal. Sometimes this comes from magic spaces our resource handlers simply don't know about */ @@ -413,7 +416,7 @@ static int __devinit efifb_probe(struct platform_device *dev) info->apertures->ranges[0].base = efifb_fix.smem_start; info->apertures->ranges[0].size = size_remap; - info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len); + info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len); if (!info->screen_base) { printk(KERN_ERR "efifb: abort, cannot ioremap video memory " "0x%x @ 0x%lx\n", @@ -491,13 +494,12 @@ err_unmap: err_release_fb: framebuffer_release(info); err_release_mem: - if (request_succeeded) + if (request_mem_succeeded) release_mem_region(efifb_fix.smem_start, size_total); return err; } static struct platform_driver efifb_driver = { - .probe = efifb_probe, .driver = { .name = "efifb", }, @@ -528,13 +530,21 @@ static int __init efifb_init(void) if (!screen_info.lfb_linelength) return -ENODEV; - ret = platform_driver_register(&efifb_driver); + ret = platform_device_register(&efifb_device); + if (ret) + return ret; - if (!ret) { - ret = platform_device_register(&efifb_device); - if (ret) - platform_driver_unregister(&efifb_driver); + /* + * This is not just an optimization. We will interfere + * with a real driver if we get reprobed, so don't allow + * it. + */ + ret = platform_driver_probe(&efifb_driver, efifb_probe); + if (ret) { + platform_device_unregister(&efifb_device); + return ret; } + return ret; } module_init(efifb_init); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index e0c2284924b6..5aac00eb1830 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -42,9 +42,34 @@ #define FBPIXMAPSIZE (1024 * 8) +static DEFINE_MUTEX(registration_lock); struct fb_info *registered_fb[FB_MAX] __read_mostly; int num_registered_fb __read_mostly; +static struct fb_info *get_fb_info(unsigned int idx) +{ + struct fb_info *fb_info; + + if (idx >= FB_MAX) + return ERR_PTR(-ENODEV); + + mutex_lock(®istration_lock); + fb_info = registered_fb[idx]; + if (fb_info) + atomic_inc(&fb_info->count); + mutex_unlock(®istration_lock); + + return fb_info; +} + +static void put_fb_info(struct fb_info *fb_info) +{ + if (!atomic_dec_and_test(&fb_info->count)) + return; + if (fb_info->fbops->fb_destroy) + fb_info->fbops->fb_destroy(fb_info); +} + int lock_fb_info(struct fb_info *info) { mutex_lock(&info->lock); @@ -647,6 +672,7 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; } static void *fb_seq_start(struct seq_file *m, loff_t *pos) { + mutex_lock(®istration_lock); return (*pos < FB_MAX) ? pos : NULL; } @@ -658,6 +684,7 @@ static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) static void fb_seq_stop(struct seq_file *m, void *v) { + mutex_unlock(®istration_lock); } static int fb_seq_show(struct seq_file *m, void *v) @@ -690,13 +717,30 @@ static const struct file_operations fb_proc_fops = { .release = seq_release, }; -static ssize_t -fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +/* + * We hold a reference to the fb_info in file->private_data, + * but if the current registered fb has changed, we don't + * actually want to use it. + * + * So look up the fb_info using the inode minor number, + * and just verify it against the reference we have. + */ +static struct fb_info *file_fb_info(struct file *file) { - unsigned long p = *ppos; struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; + + if (info != file->private_data) + info = NULL; + return info; +} + +static ssize_t +fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + struct fb_info *info = file_fb_info(file); u8 *buffer, *dst; u8 __iomem *src; int c, cnt = 0, err = 0; @@ -761,9 +805,7 @@ static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; - struct inode *inode = file->f_path.dentry->d_inode; - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; + struct fb_info *info = file_fb_info(file); u8 *buffer, *src; u8 __iomem *dst; int c, cnt = 0, err = 0; @@ -1141,10 +1183,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; + struct fb_info *info = file_fb_info(file); + if (!info) + return -ENODEV; return do_fb_ioctl(info, cmd, arg); } @@ -1265,12 +1307,13 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, static long fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; + struct fb_info *info = file_fb_info(file); + struct fb_ops *fb; long ret = -ENOIOCTLCMD; + if (!info) + return -ENODEV; + fb = info->fbops; switch(cmd) { case FBIOGET_VSCREENINFO: case FBIOPUT_VSCREENINFO: @@ -1303,16 +1346,18 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, static int fb_mmap(struct file *file, struct vm_area_struct * vma) { - int fbidx = iminor(file->f_path.dentry->d_inode); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; + struct fb_info *info = file_fb_info(file); + struct fb_ops *fb; unsigned long off; unsigned long start; u32 len; + if (!info) + return -ENODEV; if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; off = vma->vm_pgoff << PAGE_SHIFT; + fb = info->fbops; if (!fb) return -ENODEV; mutex_lock(&info->mm_lock); @@ -1361,14 +1406,16 @@ __releases(&info->lock) struct fb_info *info; int res = 0; - if (fbidx >= FB_MAX) - return -ENODEV; - info = registered_fb[fbidx]; - if (!info) + info = get_fb_info(fbidx); + if (!info) { request_module("fb%d", fbidx); - info = registered_fb[fbidx]; - if (!info) - return -ENODEV; + info = get_fb_info(fbidx); + if (!info) + return -ENODEV; + } + if (IS_ERR(info)) + return PTR_ERR(info); + mutex_lock(&info->lock); if (!try_module_get(info->fbops->owner)) { res = -ENODEV; @@ -1386,6 +1433,8 @@ __releases(&info->lock) #endif out: mutex_unlock(&info->lock); + if (res) + put_fb_info(info); return res; } @@ -1401,6 +1450,7 @@ __releases(&info->lock) info->fbops->fb_release(info,1); module_put(info->fbops->owner); mutex_unlock(&info->lock); + put_fb_info(info); return 0; } @@ -1487,8 +1537,10 @@ static bool fb_do_apertures_overlap(struct apertures_struct *gena, return false; } +static int do_unregister_framebuffer(struct fb_info *fb_info); + #define VGA_FB_PHYS 0xA0000 -void remove_conflicting_framebuffers(struct apertures_struct *a, +static void do_remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { int i; @@ -1510,43 +1562,32 @@ void remove_conflicting_framebuffers(struct apertures_struct *a, printk(KERN_INFO "fb: conflicting fb hw usage " "%s vs %s - removing generic driver\n", name, registered_fb[i]->fix.id); - unregister_framebuffer(registered_fb[i]); + do_unregister_framebuffer(registered_fb[i]); } } } -EXPORT_SYMBOL(remove_conflicting_framebuffers); -/** - * register_framebuffer - registers a frame buffer device - * @fb_info: frame buffer info structure - * - * Registers a frame buffer device @fb_info. - * - * Returns negative errno on error, or zero for success. - * - */ - -int -register_framebuffer(struct fb_info *fb_info) +static int do_register_framebuffer(struct fb_info *fb_info) { int i; struct fb_event event; struct fb_videomode mode; - if (num_registered_fb == FB_MAX) - return -ENXIO; - if (fb_check_foreignness(fb_info)) return -ENOSYS; - remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, + do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, fb_is_primary_device(fb_info)); + if (num_registered_fb == FB_MAX) + return -ENXIO; + num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; fb_info->node = i; + atomic_set(&fb_info->count, 1); mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); @@ -1592,36 +1633,14 @@ register_framebuffer(struct fb_info *fb_info) return 0; } - -/** - * unregister_framebuffer - releases a frame buffer device - * @fb_info: frame buffer info structure - * - * Unregisters a frame buffer device @fb_info. - * - * Returns negative errno on error, or zero for success. - * - * This function will also notify the framebuffer console - * to release the driver. - * - * This is meant to be called within a driver's module_exit() - * function. If this is called outside module_exit(), ensure - * that the driver implements fb_open() and fb_release() to - * check that no processes are using the device. - */ - -int -unregister_framebuffer(struct fb_info *fb_info) +static int do_unregister_framebuffer(struct fb_info *fb_info) { struct fb_event event; int i, ret = 0; i = fb_info->node; - if (!registered_fb[i]) { - ret = -EINVAL; - goto done; - } - + if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) + return -EINVAL; if (!lock_fb_info(fb_info)) return -ENODEV; @@ -1629,16 +1648,14 @@ unregister_framebuffer(struct fb_info *fb_info) ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); unlock_fb_info(fb_info); - if (ret) { - ret = -EINVAL; - goto done; - } + if (ret) + return -EINVAL; if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); - registered_fb[i]=NULL; + registered_fb[i] = NULL; num_registered_fb--; fb_cleanup_device(fb_info); device_destroy(fb_class, MKDEV(FB_MAJOR, i)); @@ -1646,9 +1663,65 @@ unregister_framebuffer(struct fb_info *fb_info) fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); /* this may free fb info */ - if (fb_info->fbops->fb_destroy) - fb_info->fbops->fb_destroy(fb_info); -done: + put_fb_info(fb_info); + return 0; +} + +void remove_conflicting_framebuffers(struct apertures_struct *a, + const char *name, bool primary) +{ + mutex_lock(®istration_lock); + do_remove_conflicting_framebuffers(a, name, primary); + mutex_unlock(®istration_lock); +} +EXPORT_SYMBOL(remove_conflicting_framebuffers); + +/** + * register_framebuffer - registers a frame buffer device + * @fb_info: frame buffer info structure + * + * Registers a frame buffer device @fb_info. + * + * Returns negative errno on error, or zero for success. + * + */ +int +register_framebuffer(struct fb_info *fb_info) +{ + int ret; + + mutex_lock(®istration_lock); + ret = do_register_framebuffer(fb_info); + mutex_unlock(®istration_lock); + + return ret; +} + +/** + * unregister_framebuffer - releases a frame buffer device + * @fb_info: frame buffer info structure + * + * Unregisters a frame buffer device @fb_info. + * + * Returns negative errno on error, or zero for success. + * + * This function will also notify the framebuffer console + * to release the driver. + * + * This is meant to be called within a driver's module_exit() + * function. If this is called outside module_exit(), ensure + * that the driver implements fb_open() and fb_release() to + * check that no processes are using the device. + */ +int +unregister_framebuffer(struct fb_info *fb_info) +{ + int ret; + + mutex_lock(®istration_lock); + ret = do_unregister_framebuffer(fb_info); + mutex_unlock(®istration_lock); + return ret; } diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c index 1b94643ecbcf..fbef15f7a218 100644 --- a/drivers/video/hecubafb.c +++ b/drivers/video/hecubafb.c @@ -231,11 +231,10 @@ static int __devinit hecubafb_probe(struct platform_device *dev) videomemorysize = (DPY_W*DPY_H)/8; - if (!(videomemory = vmalloc(videomemorysize))) + videomemory = vzalloc(videomemorysize); + if (!videomemory) return retval; - memset(videomemory, 0, videomemorysize); - info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); if (!info) goto err_fballoc; diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index ef72cb483834..f135dbead07d 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -65,12 +65,6 @@ #define CPOS_OP (1<<28) #define CPOS_CXP(x) (((x) & 3ff) << 16) -#ifdef CONFIG_ARCH_MX1 -#define CPOS_CYP(y) ((y) & 0x1ff) -#else -#define CPOS_CYP(y) ((y) & 0x3ff) -#endif - #define LCDC_LCWHB 0x10 #define LCWHB_BK_EN (1<<31) #define LCWHB_CW(w) (((w) & 0x1f) << 24) @@ -79,16 +73,6 @@ #define LCDC_LCHCC 0x14 -#ifdef CONFIG_ARCH_MX1 -#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11) -#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5) -#define LCHCC_CUR_COL_B(b) ((b) & 0x1f) -#else -#define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12) -#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6) -#define LCHCC_CUR_COL_B(b) ((b) & 0x3f) -#endif - #define LCDC_PCR 0x18 #define LCDC_HCR 0x1C @@ -115,11 +99,7 @@ #define LCDC_RMCR 0x34 -#ifdef CONFIG_ARCH_MX1 -#define RMCR_LCDC_EN (1<<1) -#else -#define RMCR_LCDC_EN 0 -#endif +#define RMCR_LCDC_EN_MX1 (1<<1) #define RMCR_SELF_REF (1<<0) @@ -536,7 +516,11 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1), fbi->regs + LCDC_CPOS); - writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR); + /* + * RMCR_LCDC_EN_MX1 is present on i.MX1 only, but doesn't hurt + * on other SoCs + */ + writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR); clk_enable(fbi->clk); @@ -872,10 +856,10 @@ failed_platform_init: dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu, fbi->map_dma); failed_map: - clk_put(fbi->clk); -failed_getclock: iounmap(fbi->regs); failed_ioremap: + clk_put(fbi->clk); +failed_getclock: release_mem_region(res->start, resource_size(res)); failed_req: kfree(info->pseudo_palette); diff --git a/drivers/video/mb862xx/Makefile b/drivers/video/mb862xx/Makefile index d7777714166b..5707ed0e31a7 100644 --- a/drivers/video/mb862xx/Makefile +++ b/drivers/video/mb862xx/Makefile @@ -2,4 +2,7 @@ # Makefile for the MB862xx framebuffer driver # -obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o mb862xxfb_accel.o +obj-$(CONFIG_FB_MB862XX) += mb862xxfb.o + +mb862xxfb-y := mb862xxfbdrv.o mb862xxfb_accel.o +mb862xxfb-$(CONFIG_FB_MB862XX_I2C) += mb862xx-i2c.o diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c new file mode 100644 index 000000000000..b953099edd8e --- /dev/null +++ b/drivers/video/mb862xx/mb862xx-i2c.c @@ -0,0 +1,178 @@ +/* + * Coral-P(A)/Lime I2C adapter driver + * + * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> + * + * 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. + * + */ + +#include <linux/fb.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include "mb862xxfb.h" +#include "mb862xx_reg.h" + +static int mb862xx_i2c_wait_event(struct i2c_adapter *adap) +{ + struct mb862xxfb_par *par = adap->algo_data; + u32 reg; + + do { + udelay(1); + reg = inreg(i2c, GC_I2C_BCR); + if (reg & (I2C_INT | I2C_BER)) + break; + } while (1); + + return (reg & I2C_BER) ? 0 : 1; +} + +static int mb862xx_i2c_do_address(struct i2c_adapter *adap, int addr) +{ + struct mb862xxfb_par *par = adap->algo_data; + + outreg(i2c, GC_I2C_DAR, addr); + outreg(i2c, GC_I2C_CCR, I2C_CLOCK_AND_ENABLE); + outreg(i2c, GC_I2C_BCR, par->i2c_rs ? I2C_REPEATED_START : I2C_START); + if (!mb862xx_i2c_wait_event(adap)) + return -EIO; + par->i2c_rs = !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); + return par->i2c_rs; +} + +static int mb862xx_i2c_write_byte(struct i2c_adapter *adap, u8 byte) +{ + struct mb862xxfb_par *par = adap->algo_data; + + outreg(i2c, GC_I2C_DAR, byte); + outreg(i2c, GC_I2C_BCR, I2C_START); + if (!mb862xx_i2c_wait_event(adap)) + return -EIO; + return !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); +} + +static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) +{ + struct mb862xxfb_par *par = adap->algo_data; + + outreg(i2c, GC_I2C_BCR, I2C_START | (last ? 0 : I2C_ACK)); + if (!mb862xx_i2c_wait_event(adap)) + return 0; + *byte = inreg(i2c, GC_I2C_DAR); + return 1; +} + +void mb862xx_i2c_stop(struct i2c_adapter *adap) +{ + struct mb862xxfb_par *par = adap->algo_data; + + outreg(i2c, GC_I2C_BCR, I2C_STOP); + outreg(i2c, GC_I2C_CCR, I2C_DISABLE); + par->i2c_rs = 0; +} + +static int mb862xx_i2c_read(struct i2c_adapter *adap, struct i2c_msg *m) +{ + int i, ret = 0; + int last = m->len - 1; + + for (i = 0; i < m->len; i++) { + if (!mb862xx_i2c_read_byte(adap, &m->buf[i], i == last)) { + ret = -EIO; + break; + } + } + return ret; +} + +static int mb862xx_i2c_write(struct i2c_adapter *adap, struct i2c_msg *m) +{ + int i, ret = 0; + + for (i = 0; i < m->len; i++) { + if (!mb862xx_i2c_write_byte(adap, m->buf[i])) { + ret = -EIO; + break; + } + } + return ret; +} + +static int mb862xx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct mb862xxfb_par *par = adap->algo_data; + struct i2c_msg *m; + int addr; + int i = 0, err = 0; + + dev_dbg(par->dev, "%s: %d msgs\n", __func__, num); + + for (i = 0; i < num; i++) { + m = &msgs[i]; + if (!m->len) { + dev_dbg(par->dev, "%s: null msgs\n", __func__); + continue; + } + addr = m->addr; + if (m->flags & I2C_M_RD) + addr |= 1; + + err = mb862xx_i2c_do_address(adap, addr); + if (err < 0) + break; + if (m->flags & I2C_M_RD) + err = mb862xx_i2c_read(adap, m); + else + err = mb862xx_i2c_write(adap, m); + } + + if (i) + mb862xx_i2c_stop(adap); + + return (err < 0) ? err : i; +} + +static u32 mb862xx_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static const struct i2c_algorithm mb862xx_algo = { + .master_xfer = mb862xx_xfer, + .functionality = mb862xx_func, +}; + +static struct i2c_adapter mb862xx_i2c_adapter = { + .name = "MB862xx I2C adapter", + .algo = &mb862xx_algo, + .owner = THIS_MODULE, +}; + +int mb862xx_i2c_init(struct mb862xxfb_par *par) +{ + int ret; + + mb862xx_i2c_adapter.algo_data = par; + par->adap = &mb862xx_i2c_adapter; + + ret = i2c_add_adapter(par->adap); + if (ret < 0) { + dev_err(par->dev, "failed to add %s\n", + mb862xx_i2c_adapter.name); + } + return ret; +} + +void mb862xx_i2c_exit(struct mb862xxfb_par *par) +{ + if (par->adap) { + i2c_del_adapter(par->adap); + par->adap = NULL; + } +} diff --git a/drivers/video/mb862xx/mb862xx_reg.h b/drivers/video/mb862xx/mb862xx_reg.h index 2ba65e118500..9df48b8edc94 100644 --- a/drivers/video/mb862xx/mb862xx_reg.h +++ b/drivers/video/mb862xx/mb862xx_reg.h @@ -5,11 +5,8 @@ #ifndef _MB862XX_REG_H #define _MB862XX_REG_H -#ifdef MB862XX_MMIO_BOTTOM -#define MB862XX_MMIO_BASE 0x03fc0000 -#else #define MB862XX_MMIO_BASE 0x01fc0000 -#endif +#define MB862XX_MMIO_HIGH_BASE 0x03fc0000 #define MB862XX_I2C_BASE 0x0000c000 #define MB862XX_DISP_BASE 0x00010000 #define MB862XX_CAP_BASE 0x00018000 @@ -23,6 +20,7 @@ #define GC_IMASK 0x00000024 #define GC_SRST 0x0000002c #define GC_CCF 0x00000038 +#define GC_RSW 0x0000005c #define GC_CID 0x000000f0 #define GC_REVISION 0x00000084 @@ -53,10 +51,16 @@ #define GC_L0OA0 0x00000024 #define GC_L0DA0 0x00000028 #define GC_L0DY_L0DX 0x0000002c +#define GC_L1M 0x00000030 +#define GC_L1DA 0x00000034 #define GC_DCM1 0x00000100 #define GC_L0EM 0x00000110 #define GC_L0WY_L0WX 0x00000114 #define GC_L0WH_L0WW 0x00000118 +#define GC_L1EM 0x00000120 +#define GC_L1WY_L1WX 0x00000124 +#define GC_L1WH_L1WW 0x00000128 +#define GC_DLS 0x00000180 #define GC_DCM2 0x00000104 #define GC_DCM3 0x00000108 #define GC_CPM_CUTC 0x000000a0 @@ -68,6 +72,11 @@ #define GC_CPM_CEN0 0x00100000 #define GC_CPM_CEN1 0x00200000 +#define GC_DCM1_DEN 0x80000000 +#define GC_DCM1_L1E 0x00020000 +#define GC_L1M_16 0x80000000 +#define GC_L1M_YC 0x40000000 +#define GC_L1M_CS 0x20000000 #define GC_DCM01_ESY 0x00000004 #define GC_DCM01_SC 0x00003f00 @@ -79,9 +88,50 @@ #define GC_L0M_L0C_16 0x80000000 #define GC_L0EM_L0EC_24 0x40000000 #define GC_L0M_L0W_UNIT 64 +#define GC_L1EM_DM 0x02000000 #define GC_DISP_REFCLK_400 400 +/* I2C */ +#define GC_I2C_BSR 0x00000000 /* BSR */ +#define GC_I2C_BCR 0x00000004 /* BCR */ +#define GC_I2C_CCR 0x00000008 /* CCR */ +#define GC_I2C_ADR 0x0000000C /* ADR */ +#define GC_I2C_DAR 0x00000010 /* DAR */ + +#define I2C_DISABLE 0x00000000 +#define I2C_STOP 0x00000000 +#define I2C_START 0x00000010 +#define I2C_REPEATED_START 0x00000030 +#define I2C_CLOCK_AND_ENABLE 0x0000003f +#define I2C_READY 0x01 +#define I2C_INT 0x01 +#define I2C_INTE 0x02 +#define I2C_ACK 0x08 +#define I2C_BER 0x80 +#define I2C_BEIE 0x40 +#define I2C_TRX 0x80 +#define I2C_LRB 0x10 + +/* Capture registers and bits */ +#define GC_CAP_VCM 0x00000000 +#define GC_CAP_CSC 0x00000004 +#define GC_CAP_VCS 0x00000008 +#define GC_CAP_CBM 0x00000010 +#define GC_CAP_CBOA 0x00000014 +#define GC_CAP_CBLA 0x00000018 +#define GC_CAP_IMG_START 0x0000001C +#define GC_CAP_IMG_END 0x00000020 +#define GC_CAP_CMSS 0x00000048 +#define GC_CAP_CMDS 0x0000004C + +#define GC_VCM_VIE 0x80000000 +#define GC_VCM_CM 0x03000000 +#define GC_VCM_VS_PAL 0x00000002 +#define GC_CBM_OO 0x80000000 +#define GC_CBM_HRV 0x00000010 +#define GC_CBM_CBST 0x00000001 + /* Carmine specific */ #define MB86297_DRAW_BASE 0x00020000 #define MB86297_DISP0_BASE 0x00100000 diff --git a/drivers/video/mb862xx/mb862xxfb.h b/drivers/video/mb862xx/mb862xxfb.h index d7e7cb76bbf2..8550630c1e01 100644 --- a/drivers/video/mb862xx/mb862xxfb.h +++ b/drivers/video/mb862xx/mb862xxfb.h @@ -1,6 +1,26 @@ #ifndef __MB862XX_H__ #define __MB862XX_H__ +struct mb862xx_l1_cfg { + unsigned short sx; + unsigned short sy; + unsigned short sw; + unsigned short sh; + unsigned short dx; + unsigned short dy; + unsigned short dw; + unsigned short dh; + int mirror; +}; + +#define MB862XX_BASE 'M' +#define MB862XX_L1_GET_CFG _IOR(MB862XX_BASE, 0, struct mb862xx_l1_cfg*) +#define MB862XX_L1_SET_CFG _IOW(MB862XX_BASE, 1, struct mb862xx_l1_cfg*) +#define MB862XX_L1_ENABLE _IOW(MB862XX_BASE, 2, int) +#define MB862XX_L1_CAP_CTL _IOW(MB862XX_BASE, 3, int) + +#ifdef __KERNEL__ + #define PCI_VENDOR_ID_FUJITSU_LIMITED 0x10cf #define PCI_DEVICE_ID_FUJITSU_CORALP 0x2019 #define PCI_DEVICE_ID_FUJITSU_CORALPA 0x201e @@ -38,6 +58,8 @@ struct mb862xxfb_par { void __iomem *mmio_base; /* remapped registers */ size_t mapped_vram; /* length of remapped vram */ size_t mmio_len; /* length of register region */ + unsigned long cap_buf; /* capture buffers offset */ + size_t cap_len; /* length of capture buffers */ void __iomem *host; /* relocatable reg. bases */ void __iomem *i2c; @@ -57,11 +79,23 @@ struct mb862xxfb_par { unsigned int refclk; /* disp. reference clock */ struct mb862xx_gc_mode *gc_mode; /* GDC mode init data */ int pre_init; /* don't init display if 1 */ + struct i2c_adapter *adap; /* GDC I2C bus adapter */ + int i2c_rs; + + struct mb862xx_l1_cfg l1_cfg; + int l1_stride; u32 pseudo_palette[16]; }; extern void mb862xxfb_init_accel(struct fb_info *info, int xres); +#ifdef CONFIG_FB_MB862XX_I2C +extern int mb862xx_i2c_init(struct mb862xxfb_par *par); +extern void mb862xx_i2c_exit(struct mb862xxfb_par *par); +#else +static inline int mb862xx_i2c_init(struct mb862xxfb_par *par) { return 0; } +static inline void mb862xx_i2c_exit(struct mb862xxfb_par *par) { } +#endif #if defined(CONFIG_FB_MB862XX_LIME) && defined(CONFIG_FB_MB862XX_PCI_GDC) #error "Select Lime GDC or CoralP/Carmine support, but not both together" @@ -82,4 +116,6 @@ extern void mb862xxfb_init_accel(struct fb_info *info, int xres); #define pack(a, b) (((a) << 16) | (b)) +#endif /* __KERNEL__ */ + #endif diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfbdrv.c index c76e663a6cd4..f70bd63b0187 100644 --- a/drivers/video/mb862xx/mb862xxfb.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -16,6 +16,7 @@ #include <linux/fb.h> #include <linux/delay.h> +#include <linux/uaccess.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -27,7 +28,7 @@ #define NR_PALETTE 256 #define MB862XX_MEM_SIZE 0x1000000 -#define CORALP_MEM_SIZE 0x4000000 +#define CORALP_MEM_SIZE 0x2000000 #define CARMINE_MEM_SIZE 0x8000000 #define DRV_NAME "mb862xxfb" @@ -309,6 +310,97 @@ static int mb862xxfb_blank(int mode, struct fb_info *fbi) return 0; } +static int mb862xxfb_ioctl(struct fb_info *fbi, unsigned int cmd, + unsigned long arg) +{ + struct mb862xxfb_par *par = fbi->par; + struct mb862xx_l1_cfg *l1_cfg = &par->l1_cfg; + void __user *argp = (void __user *)arg; + int *enable; + u32 l1em = 0; + + switch (cmd) { + case MB862XX_L1_GET_CFG: + if (copy_to_user(argp, l1_cfg, sizeof(*l1_cfg))) + return -EFAULT; + break; + case MB862XX_L1_SET_CFG: + if (copy_from_user(l1_cfg, argp, sizeof(*l1_cfg))) + return -EFAULT; + if ((l1_cfg->sw >= l1_cfg->dw) && (l1_cfg->sh >= l1_cfg->dh)) { + /* downscaling */ + outreg(cap, GC_CAP_CSC, + pack((l1_cfg->sh << 11) / l1_cfg->dh, + (l1_cfg->sw << 11) / l1_cfg->dw)); + l1em = inreg(disp, GC_L1EM); + l1em &= ~GC_L1EM_DM; + } else if ((l1_cfg->sw <= l1_cfg->dw) && + (l1_cfg->sh <= l1_cfg->dh)) { + /* upscaling */ + outreg(cap, GC_CAP_CSC, + pack((l1_cfg->sh << 11) / l1_cfg->dh, + (l1_cfg->sw << 11) / l1_cfg->dw)); + outreg(cap, GC_CAP_CMSS, + pack(l1_cfg->sw >> 1, l1_cfg->sh)); + outreg(cap, GC_CAP_CMDS, + pack(l1_cfg->dw >> 1, l1_cfg->dh)); + l1em = inreg(disp, GC_L1EM); + l1em |= GC_L1EM_DM; + } + + if (l1_cfg->mirror) { + outreg(cap, GC_CAP_CBM, + inreg(cap, GC_CAP_CBM) | GC_CBM_HRV); + l1em |= l1_cfg->dw * 2 - 8; + } else { + outreg(cap, GC_CAP_CBM, + inreg(cap, GC_CAP_CBM) & ~GC_CBM_HRV); + l1em &= 0xffff0000; + } + outreg(disp, GC_L1EM, l1em); + break; + case MB862XX_L1_ENABLE: + enable = (int *)arg; + if (*enable) { + outreg(disp, GC_L1DA, par->cap_buf); + outreg(cap, GC_CAP_IMG_START, + pack(l1_cfg->sy >> 1, l1_cfg->sx)); + outreg(cap, GC_CAP_IMG_END, + pack(l1_cfg->sh, l1_cfg->sw)); + outreg(disp, GC_L1M, GC_L1M_16 | GC_L1M_YC | GC_L1M_CS | + (par->l1_stride << 16)); + outreg(disp, GC_L1WY_L1WX, + pack(l1_cfg->dy, l1_cfg->dx)); + outreg(disp, GC_L1WH_L1WW, + pack(l1_cfg->dh - 1, l1_cfg->dw)); + outreg(disp, GC_DLS, 1); + outreg(cap, GC_CAP_VCM, + GC_VCM_VIE | GC_VCM_CM | GC_VCM_VS_PAL); + outreg(disp, GC_DCM1, inreg(disp, GC_DCM1) | + GC_DCM1_DEN | GC_DCM1_L1E); + } else { + outreg(cap, GC_CAP_VCM, + inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); + outreg(disp, GC_DCM1, + inreg(disp, GC_DCM1) & ~GC_DCM1_L1E); + } + break; + case MB862XX_L1_CAP_CTL: + enable = (int *)arg; + if (*enable) { + outreg(cap, GC_CAP_VCM, + inreg(cap, GC_CAP_VCM) | GC_VCM_VIE); + } else { + outreg(cap, GC_CAP_VCM, + inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); + } + break; + default: + return -EINVAL; + } + return 0; +} + /* framebuffer ops */ static struct fb_ops mb862xxfb_ops = { .owner = THIS_MODULE, @@ -320,6 +412,7 @@ static struct fb_ops mb862xxfb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_ioctl = mb862xxfb_ioctl, }; /* initialize fb_info data */ @@ -328,6 +421,7 @@ static int mb862xxfb_init_fbinfo(struct fb_info *fbi) struct mb862xxfb_par *par = fbi->par; struct mb862xx_gc_mode *mode = par->gc_mode; unsigned long reg; + int stride; fbi->fbops = &mb862xxfb_ops; fbi->pseudo_palette = par->pseudo_palette; @@ -336,7 +430,6 @@ static int mb862xxfb_init_fbinfo(struct fb_info *fbi) strcpy(fbi->fix.id, DRV_NAME); fbi->fix.smem_start = (unsigned long)par->fb_base_phys; - fbi->fix.smem_len = par->mapped_vram; fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; fbi->fix.mmio_len = par->mmio_len; fbi->fix.accel = FB_ACCEL_NONE; @@ -420,6 +513,28 @@ static int mb862xxfb_init_fbinfo(struct fb_info *fbi) FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; fbi->fix.line_length = (fbi->var.xres_virtual * fbi->var.bits_per_pixel) / 8; + fbi->fix.smem_len = fbi->fix.line_length * fbi->var.yres_virtual; + + /* + * reserve space for capture buffers and two cursors + * at the end of vram: 720x576 * 2 * 2.2 + 64x64 * 16. + */ + par->cap_buf = par->mapped_vram - 0x1bd800 - 0x10000; + par->cap_len = 0x1bd800; + par->l1_cfg.sx = 0; + par->l1_cfg.sy = 0; + par->l1_cfg.sw = 720; + par->l1_cfg.sh = 576; + par->l1_cfg.dx = 0; + par->l1_cfg.dy = 0; + par->l1_cfg.dw = 720; + par->l1_cfg.dh = 576; + stride = par->l1_cfg.sw * (fbi->var.bits_per_pixel / 8); + par->l1_stride = stride / 64 + ((stride % 64) ? 1 : 0); + outreg(cap, GC_CAP_CBM, GC_CBM_OO | GC_CBM_CBST | + (par->l1_stride << 16)); + outreg(cap, GC_CAP_CBOA, par->cap_buf); + outreg(cap, GC_CAP_CBLA, par->cap_buf + par->cap_len); return 0; } @@ -742,22 +857,38 @@ static int coralp_init(struct mb862xxfb_par *par) par->refclk = GC_DISP_REFCLK_400; + if (par->mapped_vram >= 0x2000000) { + /* relocate gdc registers space */ + writel(1, par->fb_base + MB862XX_MMIO_BASE + GC_RSW); + udelay(1); /* wait at least 20 bus cycles */ + } + ver = inreg(host, GC_CID); cn = (ver & GC_CID_CNAME_MSK) >> 8; ver = ver & GC_CID_VERSION_MSK; if (cn == 3) { + unsigned long reg; + dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n",\ (ver == 6) ? "P" : (ver == 8) ? "PA" : "?", par->pdev->revision); - outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); - udelay(200); - outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); - udelay(10); + reg = inreg(disp, GC_DCM1); + if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) + par->pre_init = 1; + + if (!par->pre_init) { + outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); + udelay(200); + outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); + udelay(10); + } /* Clear interrupt status */ outreg(host, GC_IST, 0); } else { return -ENODEV; } + + mb862xx_i2c_init(par); return 0; } @@ -899,7 +1030,13 @@ static int __devinit mb862xx_pci_probe(struct pci_dev *pdev, case PCI_DEVICE_ID_FUJITSU_CORALPA: par->fb_base_phys = pci_resource_start(par->pdev, 0); par->mapped_vram = CORALP_MEM_SIZE; - par->mmio_base_phys = par->fb_base_phys + MB862XX_MMIO_BASE; + if (par->mapped_vram >= 0x2000000) { + par->mmio_base_phys = par->fb_base_phys + + MB862XX_MMIO_HIGH_BASE; + } else { + par->mmio_base_phys = par->fb_base_phys + + MB862XX_MMIO_BASE; + } par->mmio_len = MB862XX_MMIO_SIZE; par->type = BT_CORALP; break; @@ -1009,6 +1146,8 @@ static void __devexit mb862xx_pci_remove(struct pci_dev *pdev) outreg(host, GC_IMASK, 0); } + mb862xx_i2c_exit(par); + device_remove_file(&pdev->dev, &dev_attr_dispregs); pci_set_drvdata(pdev, NULL); diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index ed64edfd2c43..97d45e5115e2 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -628,12 +628,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev) /* we need to add a spare page because our csum caching scheme walks * to the end of the page */ videomemorysize = PAGE_SIZE + (fw * fh); - videomemory = vmalloc(videomemorysize); + videomemory = vzalloc(videomemorysize); if (!videomemory) goto err_fb_rel; - memset(videomemory, 0, videomemorysize); - info->screen_base = (char __force __iomem *)videomemory; info->fbops = &metronomefb_ops; diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 48c3ea8652b6..cb175fe7abc0 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -1128,3 +1128,4 @@ EXPORT_SYMBOL(fb_find_best_mode); EXPORT_SYMBOL(fb_find_nearest_mode); EXPORT_SYMBOL(fb_videomode_to_modelist); EXPORT_SYMBOL(fb_find_mode); +EXPORT_SYMBOL(fb_find_mode_cvt); diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index b66d86ac7cea..178b0720bd79 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -679,7 +679,7 @@ static int __devinit mddi_probe(struct platform_device *pdev) printk(KERN_ERR "mddi: no associated mem resource!\n"); return -ENOMEM; } - mddi->base = ioremap(resource->start, resource->end - resource->start); + mddi->base = ioremap(resource->start, resource_size(resource)); if (!mddi->base) { printk(KERN_ERR "mddi: failed to remap base!\n"); ret = -EINVAL; diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 7d0284882984..0b2f2dd41416 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -401,7 +401,7 @@ static int mxsfb_set_par(struct fb_info *fb_info) writel(CTRL1_FIFO_CLEAR, host->base + LCDC_CTRL1 + REG_SET); ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER | - CTRL_SET_BUS_WIDTH(host->ld_intf_width);; + CTRL_SET_BUS_WIDTH(host->ld_intf_width); switch (fb_info->var.bits_per_pixel) { case 16: diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 49226a1b909e..25db55696e14 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -30,7 +30,6 @@ objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o -objs-y$(CONFIG_MACH_OMAP2EVM) += lcd_omap2evm.o objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 529483467abf..0ccd7adf47bb 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -922,14 +922,14 @@ static int get_dss_clocks(void) return PTR_ERR(dispc.dss_ick); } - dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "dss1_fck"); + dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "fck"); if (IS_ERR(dispc.dss1_fck)) { dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); clk_put(dispc.dss_ick); return PTR_ERR(dispc.dss1_fck); } - dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_fck"); + dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_clk"); if (IS_ERR(dispc.dss_54m_fck)) { dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); clk_put(dispc.dss_ick); diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c deleted file mode 100644 index 7e7a65c08452..000000000000 --- a/drivers/video/omap/lcd_omap2evm.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * LCD panel support for the MISTRAL OMAP2EVM board - * - * Author: Arun C <arunedarath@mistralsolutions.com> - * - * Derived from drivers/video/omap/lcd_omap3evm.c - * Derived from drivers/video/omap/lcd-apollon.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/i2c/twl.h> - -#include <plat/mux.h> -#include <asm/mach-types.h> - -#include "omapfb.h" - -#define LCD_PANEL_ENABLE_GPIO 154 -#define LCD_PANEL_LR 128 -#define LCD_PANEL_UD 129 -#define LCD_PANEL_INI 152 -#define LCD_PANEL_QVGA 148 -#define LCD_PANEL_RESB 153 - -#define TWL_LED_LEDEN 0x00 -#define TWL_PWMA_PWMAON 0x00 -#define TWL_PWMA_PWMAOFF 0x01 - -static unsigned int bklight_level; - -static int omap2evm_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); - gpio_request(LCD_PANEL_LR, "LCD lr"); - gpio_request(LCD_PANEL_UD, "LCD ud"); - gpio_request(LCD_PANEL_INI, "LCD ini"); - gpio_request(LCD_PANEL_QVGA, "LCD qvga"); - gpio_request(LCD_PANEL_RESB, "LCD resb"); - - gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); - gpio_direction_output(LCD_PANEL_RESB, 1); - gpio_direction_output(LCD_PANEL_INI, 1); - gpio_direction_output(LCD_PANEL_QVGA, 0); - gpio_direction_output(LCD_PANEL_LR, 1); - gpio_direction_output(LCD_PANEL_UD, 1); - - twl_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); - twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); - twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); - bklight_level = 100; - - return 0; -} - -static void omap2evm_panel_cleanup(struct lcd_panel *panel) -{ - gpio_free(LCD_PANEL_RESB); - gpio_free(LCD_PANEL_QVGA); - gpio_free(LCD_PANEL_INI); - gpio_free(LCD_PANEL_UD); - gpio_free(LCD_PANEL_LR); - gpio_free(LCD_PANEL_ENABLE_GPIO); -} - -static int omap2evm_panel_enable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); - return 0; -} - -static void omap2evm_panel_disable(struct lcd_panel *panel) -{ - gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); -} - -static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -static int omap2evm_bklight_setlevel(struct lcd_panel *panel, - unsigned int level) -{ - u8 c; - if ((level >= 0) && (level <= 100)) { - c = (125 * (100 - level)) / 100 + 2; - twl_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); - bklight_level = level; - } - return 0; -} - -static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel) -{ - return bklight_level; -} - -static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel) -{ - return 100; -} - -struct lcd_panel omap2evm_panel = { - .name = "omap2evm", - .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | - OMAP_LCDC_INV_HSYNC, - - .bpp = 16, - .data_lines = 18, - .x_res = 480, - .y_res = 640, - .hsw = 3, - .hfp = 0, - .hbp = 28, - .vsw = 2, - .vfp = 1, - .vbp = 0, - - .pixel_clock = 20000, - - .init = omap2evm_panel_init, - .cleanup = omap2evm_panel_cleanup, - .enable = omap2evm_panel_enable, - .disable = omap2evm_panel_disable, - .get_caps = omap2evm_panel_get_caps, - .set_bklight_level = omap2evm_bklight_setlevel, - .get_bklight_level = omap2evm_bklight_getlevel, - .get_bklight_max = omap2evm_bklight_getmaxlevel, -}; - -static int omap2evm_panel_probe(struct platform_device *pdev) -{ - omapfb_register_panel(&omap2evm_panel); - return 0; -} - -static int omap2evm_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int omap2evm_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int omap2evm_panel_resume(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver omap2evm_panel_driver = { - .probe = omap2evm_panel_probe, - .remove = omap2evm_panel_remove, - .suspend = omap2evm_panel_suspend, - .resume = omap2evm_panel_resume, - .driver = { - .name = "omap2evm_lcd", - .owner = THIS_MODULE, - }, -}; - -static int __init omap2evm_panel_drv_init(void) -{ - return platform_driver_register(&omap2evm_panel_driver); -} - -static void __exit omap2evm_panel_drv_exit(void) -{ - platform_driver_unregister(&omap2evm_panel_driver); -} - -module_init(omap2evm_panel_drv_init); -module_exit(omap2evm_panel_drv_exit); diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index e264efd0278f..b3ddd743d8a6 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -90,7 +90,7 @@ static void omapdss_release(struct device *dev) /* dummy device for clocks */ static struct platform_device omapdss_device = { - .name = "omapdss", + .name = "omapdss_dss", .id = -1, .dev = { .release = omapdss_release, diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index eada9f12efc7..0c6981f1a4a3 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -90,7 +90,7 @@ static int rfbi_get_clocks(void) return PTR_ERR(rfbi.dss_ick); } - rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "dss1_fck"); + rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "fck"); if (IS_ERR(rfbi.dss1_fck)) { dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n"); clk_put(rfbi.dss_ick); diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index d853d05dad31..5ddef129f798 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_OMAP2_VRAM) += vram.o obj-$(CONFIG_OMAP2_VRFB) += vrfb.o -obj-y += dss/ -obj-y += omapfb/ +obj-$(CONFIG_OMAP2_DSS) += dss/ +obj-$(CONFIG_FB_OMAP2) += omapfb/ obj-y += displays/ diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index d18ad6b2372a..609a28073178 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -3,6 +3,7 @@ menu "OMAP2/3 Display Device Drivers" config PANEL_GENERIC_DPI tristate "Generic DPI Panel" + depends on OMAP2_DSS_DPI help Generic DPI panel driver. Supports DVI output for Beagle and OMAP3 SDP. @@ -11,20 +12,20 @@ config PANEL_GENERIC_DPI config PANEL_LGPHILIPS_LB035Q02 tristate "LG.Philips LB035Q02 LCD Panel" - depends on OMAP2_DSS && SPI + depends on OMAP2_DSS_DPI && SPI help LCD Panel used on the Gumstix Overo Palo35 config PANEL_SHARP_LS037V7DW01 tristate "Sharp LS037V7DW01 LCD Panel" - depends on OMAP2_DSS + depends on OMAP2_DSS_DPI select BACKLIGHT_CLASS_DEVICE help LCD Panel used in TI's SDP3430 and EVM boards config PANEL_NEC_NL8048HL11_01B tristate "NEC NL8048HL11-01B Panel" - depends on OMAP2_DSS + depends on OMAP2_DSS_DPI help This NEC NL8048HL11-01B panel is TFT LCD used in the Zoom2/3/3630 sdp boards. @@ -37,7 +38,7 @@ config PANEL_TAAL config PANEL_TPO_TD043MTEA1 tristate "TPO TD043MTEA1 LCD Panel" - depends on OMAP2_DSS && SPI + depends on OMAP2_DSS_DPI && SPI help LCD Panel used in OMAP3 Pandora diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 7e04c921aa2a..dbd59b8e5b36 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -30,7 +30,7 @@ #include <linux/backlight.h> #include <linux/fb.h> -#include <plat/display.h> +#include <video/omapdss.h> #define MIPID_CMD_READ_DISP_ID 0x04 #define MIPID_CMD_READ_RED 0x06 diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 4a9b9ff59467..9c90f75653fb 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -33,8 +33,9 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> +#include <video/omapdss.h> -#include <plat/panel-generic-dpi.h> +#include <video/omap-panel-generic-dpi.h> struct panel_config { struct omap_video_timings timings; @@ -181,6 +182,56 @@ static struct panel_config generic_dpi_panels[] = { .power_off_delay = 0, .name = "samsung_lte430wq_f0c", }, + + /* Seiko 70WVW1TZ3Z3 */ + { + { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 33000, + + .hsw = 128, + .hfp = 10, + .hbp = 10, + + .vsw = 2, + .vfp = 4, + .vbp = 11, + }, + .acbi = 0x0, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS, + .power_on_delay = 0, + .power_off_delay = 0, + .name = "seiko_70wvw1tz3", + }, + + /* Powertip PH480272T */ + { + { + .x_res = 480, + .y_res = 272, + + .pixel_clock = 9000, + + .hsw = 40, + .hfp = 2, + .hbp = 2, + + .vsw = 10, + .vfp = 2, + .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 = 0, + .power_off_delay = 0, + .name = "powertip_ph480272t", + }, }; struct panel_drv_data { @@ -285,7 +336,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) return 0; } -static void generic_dpi_panel_remove(struct omap_dss_device *dssdev) +static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev) { struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); @@ -358,7 +409,7 @@ static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, static struct omap_dss_driver dpi_driver = { .probe = generic_dpi_panel_probe, - .remove = generic_dpi_panel_remove, + .remove = __exit_p(generic_dpi_panel_remove), .enable = generic_dpi_panel_enable, .disable = generic_dpi_panel_disable, diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 271324db2436..e0eb35be303e 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -21,7 +21,7 @@ #include <linux/spi/spi.h> #include <linux/mutex.h> -#include <plat/display.h> +#include <video/omapdss.h> struct lb035q02_data { struct mutex lock; diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 925e0fadff54..2ba9d0ca187c 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -22,7 +22,7 @@ #include <linux/backlight.h> #include <linux/fb.h> -#include <plat/display.h> +#include <video/omapdss.h> #define LCD_XRES 800 #define LCD_YRES 480 diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index d2b35d2df2a6..ba38b3ad17d6 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -25,7 +25,7 @@ #include <linux/err.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> struct sharp_data { struct backlight_device *bl; @@ -120,7 +120,7 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) return 0; } -static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) +static void __exit sharp_ls_panel_remove(struct omap_dss_device *dssdev) { struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); struct backlight_device *bl = sd->bl; @@ -205,7 +205,7 @@ static int sharp_ls_panel_resume(struct omap_dss_device *dssdev) static struct omap_dss_driver sharp_ls_driver = { .probe = sharp_ls_panel_probe, - .remove = sharp_ls_panel_remove, + .remove = __exit_p(sharp_ls_panel_remove), .enable = sharp_ls_panel_enable, .disable = sharp_ls_panel_disable, diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index adc9900458e1..fdd5d4ae437d 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -33,8 +33,8 @@ #include <linux/regulator/consumer.h> #include <linux/mutex.h> -#include <plat/display.h> -#include <plat/nokia-dsi-panel.h> +#include <video/omapdss.h> +#include <video/omap-panel-nokia-dsi.h> /* DSI Virtual channel. Hardcoded for now. */ #define TCH 0 @@ -63,12 +63,12 @@ #define DCS_GET_ID2 0xdb #define DCS_GET_ID3 0xdc -#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) - static irqreturn_t taal_te_isr(int irq, void *data); static void taal_te_timeout_work_callback(struct work_struct *work); static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); +static int taal_panel_reset(struct omap_dss_device *dssdev); + struct panel_regulator { struct regulator *regulator; const char *name; @@ -229,8 +229,14 @@ struct taal_data { bool intro_printed; - struct workqueue_struct *esd_wq; + struct workqueue_struct *workqueue; + struct delayed_work esd_work; + unsigned esd_interval; + + bool ulps_enabled; + unsigned ulps_timeout; + struct delayed_work ulps_work; struct panel_config *panel_config; }; @@ -242,6 +248,7 @@ static inline struct nokia_dsi_panel_data } static void taal_esd_work(struct work_struct *work); +static void taal_ulps_work(struct work_struct *work); static void hw_guard_start(struct taal_data *td, int guard_msec) { @@ -264,7 +271,7 @@ static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data) int r; u8 buf[1]; - r = dsi_vc_dcs_read(td->channel, dcs_cmd, buf, 1); + r = dsi_vc_dcs_read(td->dssdev, td->channel, dcs_cmd, buf, 1); if (r < 0) return r; @@ -276,7 +283,7 @@ static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data) static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd) { - return dsi_vc_dcs_write(td->channel, &dcs_cmd, 1); + return dsi_vc_dcs_write(td->dssdev, td->channel, &dcs_cmd, 1); } static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param) @@ -284,7 +291,7 @@ static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param) u8 buf[2]; buf[0] = dcs_cmd; buf[1] = param; - return dsi_vc_dcs_write(td->channel, buf, 2); + return dsi_vc_dcs_write(td->dssdev, td->channel, buf, 2); } static int taal_sleep_in(struct taal_data *td) @@ -296,7 +303,7 @@ static int taal_sleep_in(struct taal_data *td) hw_guard_wait(td); cmd = DCS_SLEEP_IN; - r = dsi_vc_dcs_write_nosync(td->channel, &cmd, 1); + r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1); if (r) return r; @@ -402,7 +409,7 @@ static int taal_set_update_window(struct taal_data *td, buf[3] = (x2 >> 8) & 0xff; buf[4] = (x2 >> 0) & 0xff; - r = dsi_vc_dcs_write_nosync(td->channel, buf, sizeof(buf)); + r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf)); if (r) return r; @@ -412,15 +419,132 @@ static int taal_set_update_window(struct taal_data *td, buf[3] = (y2 >> 8) & 0xff; buf[4] = (y2 >> 0) & 0xff; - r = dsi_vc_dcs_write_nosync(td->channel, buf, sizeof(buf)); + r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf)); if (r) return r; - dsi_vc_send_bta_sync(td->channel); + dsi_vc_send_bta_sync(td->dssdev, td->channel); return r; } +static void taal_queue_esd_work(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + if (td->esd_interval > 0) + queue_delayed_work(td->workqueue, &td->esd_work, + msecs_to_jiffies(td->esd_interval)); +} + +static void taal_cancel_esd_work(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + cancel_delayed_work(&td->esd_work); +} + +static void taal_queue_ulps_work(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + if (td->ulps_timeout > 0) + queue_delayed_work(td->workqueue, &td->ulps_work, + msecs_to_jiffies(td->ulps_timeout)); +} + +static void taal_cancel_ulps_work(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + cancel_delayed_work(&td->ulps_work); +} + +static int taal_enter_ulps(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); + int r; + + if (td->ulps_enabled) + return 0; + + taal_cancel_ulps_work(dssdev); + + r = _taal_enable_te(dssdev, false); + if (r) + goto err; + + disable_irq(gpio_to_irq(panel_data->ext_te_gpio)); + + omapdss_dsi_display_disable(dssdev, false, true); + + td->ulps_enabled = true; + + return 0; + +err: + dev_err(&dssdev->dev, "enter ULPS failed"); + taal_panel_reset(dssdev); + + td->ulps_enabled = false; + + taal_queue_ulps_work(dssdev); + + return r; +} + +static int taal_exit_ulps(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); + int r; + + if (!td->ulps_enabled) + return 0; + + r = omapdss_dsi_display_enable(dssdev); + if (r) + goto err; + + omapdss_dsi_vc_enable_hs(dssdev, td->channel, true); + + r = _taal_enable_te(dssdev, true); + if (r) + goto err; + + enable_irq(gpio_to_irq(panel_data->ext_te_gpio)); + + taal_queue_ulps_work(dssdev); + + td->ulps_enabled = false; + + return 0; + +err: + dev_err(&dssdev->dev, "exit ULPS failed"); + r = taal_panel_reset(dssdev); + + enable_irq(gpio_to_irq(panel_data->ext_te_gpio)); + td->ulps_enabled = false; + + taal_queue_ulps_work(dssdev); + + return r; +} + +static int taal_wake_up(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + if (td->ulps_enabled) + return taal_exit_ulps(dssdev); + + taal_cancel_ulps_work(dssdev); + taal_queue_ulps_work(dssdev); + return 0; +} + static int taal_bl_update_status(struct backlight_device *dev) { struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); @@ -441,9 +565,13 @@ static int taal_bl_update_status(struct backlight_device *dev) if (td->use_dsi_bl) { if (td->enabled) { - dsi_bus_lock(); - r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); - dsi_bus_unlock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (!r) + r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); + + dsi_bus_unlock(dssdev); } else { r = 0; } @@ -504,9 +632,13 @@ static ssize_t taal_num_errors_show(struct device *dev, mutex_lock(&td->lock); if (td->enabled) { - dsi_bus_lock(); - r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors); - dsi_bus_unlock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (!r) + r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors); + + dsi_bus_unlock(dssdev); } else { r = -ENODEV; } @@ -530,9 +662,13 @@ static ssize_t taal_hw_revision_show(struct device *dev, mutex_lock(&td->lock); if (td->enabled) { - dsi_bus_lock(); - r = taal_get_id(td, &id1, &id2, &id3); - dsi_bus_unlock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (!r) + r = taal_get_id(td, &id1, &id2, &id3); + + dsi_bus_unlock(dssdev); } else { r = -ENODEV; } @@ -579,6 +715,7 @@ static ssize_t store_cabc_mode(struct device *dev, struct omap_dss_device *dssdev = to_dss_device(dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev); int i; + int r; for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { if (sysfs_streq(cabc_modes[i], buf)) @@ -591,10 +728,19 @@ static ssize_t store_cabc_mode(struct device *dev, mutex_lock(&td->lock); if (td->enabled) { - dsi_bus_lock(); - if (!td->cabc_broken) - taal_dcs_write_1(td, DCS_WRITE_CABC, i); - dsi_bus_unlock(); + dsi_bus_lock(dssdev); + + if (!td->cabc_broken) { + r = taal_wake_up(dssdev); + if (r) + goto err; + + r = taal_dcs_write_1(td, DCS_WRITE_CABC, i); + if (r) + goto err; + } + + dsi_bus_unlock(dssdev); } td->cabc_mode = i; @@ -602,6 +748,10 @@ static ssize_t store_cabc_mode(struct device *dev, mutex_unlock(&td->lock); return count; +err: + dsi_bus_unlock(dssdev); + mutex_unlock(&td->lock); + return r; } static ssize_t show_cabc_available_modes(struct device *dev, @@ -620,18 +770,161 @@ static ssize_t show_cabc_available_modes(struct device *dev, return len < PAGE_SIZE ? len : PAGE_SIZE - 1; } +static ssize_t taal_store_esd_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + unsigned long t; + int r; + + r = strict_strtoul(buf, 10, &t); + if (r) + return r; + + mutex_lock(&td->lock); + taal_cancel_esd_work(dssdev); + td->esd_interval = t; + if (td->enabled) + taal_queue_esd_work(dssdev); + mutex_unlock(&td->lock); + + return count; +} + +static ssize_t taal_show_esd_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + unsigned t; + + mutex_lock(&td->lock); + t = td->esd_interval; + mutex_unlock(&td->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t taal_store_ulps(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + unsigned long t; + int r; + + r = strict_strtoul(buf, 10, &t); + if (r) + return r; + + mutex_lock(&td->lock); + + if (td->enabled) { + dsi_bus_lock(dssdev); + + if (t) + r = taal_enter_ulps(dssdev); + else + r = taal_wake_up(dssdev); + + dsi_bus_unlock(dssdev); + } + + mutex_unlock(&td->lock); + + if (r) + return r; + + return count; +} + +static ssize_t taal_show_ulps(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + unsigned t; + + mutex_lock(&td->lock); + t = td->ulps_enabled; + mutex_unlock(&td->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t taal_store_ulps_timeout(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + unsigned long t; + int r; + + r = strict_strtoul(buf, 10, &t); + if (r) + return r; + + mutex_lock(&td->lock); + td->ulps_timeout = t; + + if (td->enabled) { + /* taal_wake_up will restart the timer */ + dsi_bus_lock(dssdev); + r = taal_wake_up(dssdev); + dsi_bus_unlock(dssdev); + } + + mutex_unlock(&td->lock); + + if (r) + return r; + + return count; +} + +static ssize_t taal_show_ulps_timeout(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + unsigned t; + + mutex_lock(&td->lock); + t = td->ulps_timeout; + mutex_unlock(&td->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL); static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL); static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, show_cabc_mode, store_cabc_mode); static DEVICE_ATTR(cabc_available_modes, S_IRUGO, show_cabc_available_modes, NULL); +static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR, + taal_show_esd_interval, taal_store_esd_interval); +static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR, + taal_show_ulps, taal_store_ulps); +static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR, + taal_show_ulps_timeout, taal_store_ulps_timeout); static struct attribute *taal_attrs[] = { &dev_attr_num_dsi_errors.attr, &dev_attr_hw_revision.attr, &dev_attr_cabc_mode.attr, &dev_attr_cabc_available_modes.attr, + &dev_attr_esd_interval.attr, + &dev_attr_ulps.attr, + &dev_attr_ulps_timeout.attr, NULL, }; @@ -700,6 +993,9 @@ static int taal_probe(struct omap_dss_device *dssdev) } td->dssdev = dssdev; td->panel_config = panel_config; + td->esd_interval = panel_data->esd_interval; + td->ulps_enabled = false; + td->ulps_timeout = panel_data->ulps_timeout; mutex_init(&td->lock); @@ -710,13 +1006,14 @@ static int taal_probe(struct omap_dss_device *dssdev) if (r) goto err_reg; - td->esd_wq = create_singlethread_workqueue("taal_esd"); - if (td->esd_wq == NULL) { + td->workqueue = create_singlethread_workqueue("taal_esd"); + if (td->workqueue == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); r = -ENOMEM; goto err_wq; } INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); + INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work); dev_set_drvdata(&dssdev->dev, td); @@ -734,8 +1031,8 @@ static int taal_probe(struct omap_dss_device *dssdev) props.max_brightness = 127; props.type = BACKLIGHT_RAW; - bldev = backlight_device_register("taal", &dssdev->dev, dssdev, - &taal_bl_ops, &props); + bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev, + dssdev, &taal_bl_ops, &props); if (IS_ERR(bldev)) { r = PTR_ERR(bldev); goto err_bl; @@ -810,7 +1107,7 @@ err_irq: err_gpio: backlight_device_unregister(bldev); err_bl: - destroy_workqueue(td->esd_wq); + destroy_workqueue(td->workqueue); err_wq: free_regulators(panel_config->regulators, panel_config->num_regulators); err_reg: @@ -819,7 +1116,7 @@ err: return r; } -static void taal_remove(struct omap_dss_device *dssdev) +static void __exit taal_remove(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); @@ -841,8 +1138,9 @@ static void taal_remove(struct omap_dss_device *dssdev) taal_bl_update_status(bldev); backlight_device_unregister(bldev); - cancel_delayed_work(&td->esd_work); - destroy_workqueue(td->esd_wq); + taal_cancel_ulps_work(dssdev); + taal_cancel_esd_work(dssdev); + destroy_workqueue(td->workqueue); /* reset, to be sure that the panel is in a valid state */ taal_hw_reset(dssdev); @@ -867,7 +1165,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) taal_hw_reset(dssdev); - omapdss_dsi_vc_enable_hs(td->channel, false); + omapdss_dsi_vc_enable_hs(dssdev, td->channel, false); r = taal_sleep_out(td); if (r) @@ -924,7 +1222,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) td->intro_printed = true; } - omapdss_dsi_vc_enable_hs(td->channel, true); + omapdss_dsi_vc_enable_hs(dssdev, td->channel, true); return 0; err: @@ -932,7 +1230,7 @@ err: taal_hw_reset(dssdev); - omapdss_dsi_display_disable(dssdev); + omapdss_dsi_display_disable(dssdev, true, false); err0: return r; } @@ -955,15 +1253,23 @@ static void taal_power_off(struct omap_dss_device *dssdev) taal_hw_reset(dssdev); } - omapdss_dsi_display_disable(dssdev); + omapdss_dsi_display_disable(dssdev, true, false); td->enabled = 0; } +static int taal_panel_reset(struct omap_dss_device *dssdev) +{ + dev_err(&dssdev->dev, "performing LCD reset\n"); + + taal_power_off(dssdev); + taal_hw_reset(dssdev); + return taal_power_on(dssdev); +} + static int taal_enable(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; dev_dbg(&dssdev->dev, "enable\n"); @@ -975,18 +1281,16 @@ static int taal_enable(struct omap_dss_device *dssdev) goto err; } - dsi_bus_lock(); + dsi_bus_lock(dssdev); r = taal_power_on(dssdev); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); if (r) goto err; - if (panel_data->use_esd_check) - queue_delayed_work(td->esd_wq, &td->esd_work, - TAAL_ESD_CHECK_PERIOD); + taal_queue_esd_work(dssdev); dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; @@ -1007,14 +1311,17 @@ static void taal_disable(struct omap_dss_device *dssdev) mutex_lock(&td->lock); - cancel_delayed_work(&td->esd_work); + taal_cancel_ulps_work(dssdev); + taal_cancel_esd_work(dssdev); - dsi_bus_lock(); + dsi_bus_lock(dssdev); - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + taal_wake_up(dssdev); taal_power_off(dssdev); + } - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; @@ -1035,13 +1342,16 @@ static int taal_suspend(struct omap_dss_device *dssdev) goto err; } - cancel_delayed_work(&td->esd_work); + taal_cancel_ulps_work(dssdev); + taal_cancel_esd_work(dssdev); - dsi_bus_lock(); + dsi_bus_lock(dssdev); - taal_power_off(dssdev); + r = taal_wake_up(dssdev); + if (!r) + taal_power_off(dssdev); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; @@ -1056,7 +1366,6 @@ err: static int taal_resume(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); int r; dev_dbg(&dssdev->dev, "resume\n"); @@ -1068,19 +1377,17 @@ static int taal_resume(struct omap_dss_device *dssdev) goto err; } - dsi_bus_lock(); + dsi_bus_lock(dssdev); r = taal_power_on(dssdev); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); if (r) { dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } else { dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - if (panel_data->use_esd_check) - queue_delayed_work(td->esd_wq, &td->esd_work, - TAAL_ESD_CHECK_PERIOD); + taal_queue_esd_work(dssdev); } mutex_unlock(&td->lock); @@ -1095,7 +1402,7 @@ static void taal_framedone_cb(int err, void *data) { struct omap_dss_device *dssdev = data; dev_dbg(&dssdev->dev, "framedone, err %d\n", err); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); } static irqreturn_t taal_te_isr(int irq, void *data) @@ -1123,7 +1430,7 @@ static irqreturn_t taal_te_isr(int irq, void *data) return IRQ_HANDLED; err: dev_err(&dssdev->dev, "start update failed\n"); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); return IRQ_HANDLED; } @@ -1136,7 +1443,7 @@ static void taal_te_timeout_work_callback(struct work_struct *work) dev_err(&dssdev->dev, "TE not received for 250ms!\n"); atomic_set(&td->do_update, 0); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); } static int taal_update(struct omap_dss_device *dssdev, @@ -1149,7 +1456,11 @@ static int taal_update(struct omap_dss_device *dssdev, dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); mutex_lock(&td->lock); - dsi_bus_lock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (r) + goto err; if (!td->enabled) { r = 0; @@ -1184,7 +1495,7 @@ static int taal_update(struct omap_dss_device *dssdev, mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); return r; } @@ -1196,8 +1507,8 @@ static int taal_sync(struct omap_dss_device *dssdev) dev_dbg(&dssdev->dev, "sync\n"); mutex_lock(&td->lock); - dsi_bus_lock(); - dsi_bus_unlock(); + dsi_bus_lock(dssdev); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); dev_dbg(&dssdev->dev, "sync done\n"); @@ -1235,9 +1546,13 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) if (td->te_enabled == enable) goto end; - dsi_bus_lock(); + dsi_bus_lock(dssdev); if (td->enabled) { + r = taal_wake_up(dssdev); + if (r) + goto err; + r = _taal_enable_te(dssdev, enable); if (r) goto err; @@ -1245,13 +1560,13 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) td->te_enabled = enable; - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); end: mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); return r; @@ -1281,9 +1596,13 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) if (td->rotate == rotate) goto end; - dsi_bus_lock(); + dsi_bus_lock(dssdev); if (td->enabled) { + r = taal_wake_up(dssdev); + if (r) + goto err; + r = taal_set_addr_mode(td, rotate, td->mirror); if (r) goto err; @@ -1291,12 +1610,12 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) td->rotate = rotate; - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); end: mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); return r; } @@ -1325,8 +1644,12 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) if (td->mirror == enable) goto end; - dsi_bus_lock(); + dsi_bus_lock(dssdev); if (td->enabled) { + r = taal_wake_up(dssdev); + if (r) + goto err; + r = taal_set_addr_mode(td, td->rotate, enable); if (r) goto err; @@ -1334,12 +1657,12 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) td->mirror = enable; - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); end: mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); return r; } @@ -1369,7 +1692,11 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num) goto err1; } - dsi_bus_lock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (r) + goto err2; r = taal_dcs_read_1(td, DCS_GET_ID1, &id1); if (r) @@ -1381,11 +1708,11 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num) if (r) goto err2; - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); return 0; err2: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); err1: mutex_unlock(&td->lock); return r; @@ -1415,7 +1742,11 @@ static int taal_memory_read(struct omap_dss_device *dssdev, dssdev->panel.timings.x_res * dssdev->panel.timings.y_res * 3); - dsi_bus_lock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (r) + goto err2; /* plen 1 or 2 goes into short packet. until checksum error is fixed, * use short packets. plen 32 works, but bigger packets seem to cause @@ -1427,7 +1758,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev, taal_set_update_window(td, x, y, w, h); - r = dsi_vc_set_max_rx_packet_size(td->channel, plen); + r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen); if (r) goto err2; @@ -1435,7 +1766,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev, u8 dcs_cmd = first ? 0x2e : 0x3e; first = 0; - r = dsi_vc_dcs_read(td->channel, dcs_cmd, + r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd, buf + buf_used, size - buf_used); if (r < 0) { @@ -1461,14 +1792,35 @@ static int taal_memory_read(struct omap_dss_device *dssdev, r = buf_used; err3: - dsi_vc_set_max_rx_packet_size(td->channel, 1); + dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1); err2: - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); err1: mutex_unlock(&td->lock); return r; } +static void taal_ulps_work(struct work_struct *work) +{ + struct taal_data *td = container_of(work, struct taal_data, + ulps_work.work); + struct omap_dss_device *dssdev = td->dssdev; + + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) { + mutex_unlock(&td->lock); + return; + } + + dsi_bus_lock(dssdev); + + taal_enter_ulps(dssdev); + + dsi_bus_unlock(dssdev); + mutex_unlock(&td->lock); +} + static void taal_esd_work(struct work_struct *work) { struct taal_data *td = container_of(work, struct taal_data, @@ -1485,7 +1837,13 @@ static void taal_esd_work(struct work_struct *work) return; } - dsi_bus_lock(); + dsi_bus_lock(dssdev); + + r = taal_wake_up(dssdev); + if (r) { + dev_err(&dssdev->dev, "failed to exit ULPS\n"); + goto err; + } r = taal_dcs_read_1(td, DCS_RDDSDR, &state1); if (r) { @@ -1521,22 +1879,20 @@ static void taal_esd_work(struct work_struct *work) goto err; } - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); - queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + taal_queue_esd_work(dssdev); mutex_unlock(&td->lock); return; err: dev_err(&dssdev->dev, "performing LCD reset\n"); - taal_power_off(dssdev); - taal_hw_reset(dssdev); - taal_power_on(dssdev); + taal_panel_reset(dssdev); - dsi_bus_unlock(); + dsi_bus_unlock(dssdev); - queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + taal_queue_esd_work(dssdev); mutex_unlock(&td->lock); } @@ -1557,7 +1913,7 @@ static enum omap_dss_update_mode taal_get_update_mode( static struct omap_dss_driver taal_driver = { .probe = taal_probe, - .remove = taal_remove, + .remove = __exit_p(taal_remove), .enable = taal_enable, .disable = taal_disable, diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index dbe9d43b4850..2462b9ec6662 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -17,7 +17,7 @@ #include <linux/err.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> #define TPO_R02_MODE(x) ((x) & 7) #define TPO_R02_MODE_800x480 7 @@ -144,13 +144,15 @@ static ssize_t tpo_td043_vmirror_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); - long val; + int val; int ret; - ret = strict_strtol(buf, 0, &val); + ret = kstrtoint(buf, 0, &val); if (ret < 0) return ret; + val = !!val; + ret = tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror, val); if (ret < 0) return ret; @@ -175,7 +177,7 @@ static ssize_t tpo_td043_mode_store(struct device *dev, long val; int ret; - ret = strict_strtol(buf, 0, &val); + ret = kstrtol(buf, 0, &val); if (ret != 0 || val & ~7) return -EINVAL; diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index bfc5da0e9700..6b3e2da11419 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -80,7 +80,7 @@ config OMAP2_DSS_SDI config OMAP2_DSS_DSI bool "DSI support" - depends on ARCH_OMAP3 + depends on ARCH_OMAP3 || ARCH_OMAP4 default n help MIPI DSI (Display Serial Interface) support. @@ -90,14 +90,6 @@ config OMAP2_DSS_DSI See http://www.mipi.org/ for DSI spesifications. -config OMAP2_DSS_USE_DSI_PLL - bool "Use DSI PLL for PCLK (EXPERIMENTAL)" - default n - depends on OMAP2_DSS_DSI - help - Use DSI PLL to generate pixel clock. Currently only for DPI output. - DSI PLL can be used to generate higher and more precise pixel clocks. - config OMAP2_DSS_FAKE_VSYNC bool "Fake VSYNC irq from manual update displays" default n @@ -125,4 +117,27 @@ config OMAP2_DSS_MIN_FCK_PER_PCK Max FCK is 173MHz, so this doesn't work if your PCK is very high. +config OMAP2_DSS_SLEEP_BEFORE_RESET + bool "Sleep 50ms before DSS reset" + default y + help + For some unknown reason we may get SYNC_LOST errors from the display + subsystem at initialization time if we don't sleep before resetting + the DSS. See the source (dss.c) for more comments. + + However, 50ms is quite long time to sleep, and with some + configurations the SYNC_LOST may never happen, so the sleep can + be disabled here. + +config OMAP2_DSS_SLEEP_AFTER_VENC_RESET + bool "Sleep 20ms after VENC reset" + default y + help + There is a 20ms sleep after VENC reset which seemed to fix the + reset. The reason for the bug is unclear, and it's also unclear + on what platforms this happens. + + This option enables the sleep, and is enabled by default. You can + disable the sleep if it doesn't cause problems on your platform. + endif diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 1aa2ed1e786e..3da426719dd6 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -33,7 +33,7 @@ #include <linux/device.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" #include "dss_features.h" @@ -54,6 +54,9 @@ unsigned int dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif +static int omap_dss_register_device(struct omap_dss_device *); +static void omap_dss_unregister_device(struct omap_dss_device *); + /* REGULATORS */ struct regulator *dss_get_vdds_dsi(void) @@ -124,8 +127,7 @@ static int dss_initialize_debugfs(void) #endif #if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS) - debugfs_create_file("dsi_irq", S_IRUGO, dss_debugfs_dir, - &dsi_dump_irqs, &dss_debug_fops); + dsi_create_debugfs_files_irq(dss_debugfs_dir, &dss_debug_fops); #endif debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, @@ -137,8 +139,7 @@ static int dss_initialize_debugfs(void) &rfbi_dump_regs, &dss_debug_fops); #endif #ifdef CONFIG_OMAP2_DSS_DSI - debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, - &dsi_dump_regs, &dss_debug_fops); + dsi_create_debugfs_files_reg(dss_debugfs_dir, &dss_debug_fops); #endif #ifdef CONFIG_OMAP2_DSS_VENC debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, @@ -480,7 +481,7 @@ static void omap_dss_dev_release(struct device *dev) reset_device(dev, 0); } -int omap_dss_register_device(struct omap_dss_device *dssdev) +static int omap_dss_register_device(struct omap_dss_device *dssdev) { static int dev_num; @@ -494,7 +495,7 @@ int omap_dss_register_device(struct omap_dss_device *dssdev) return device_register(&dssdev->dev); } -void omap_dss_unregister_device(struct omap_dss_device *dssdev) +static void omap_dss_unregister_device(struct omap_dss_device *dssdev) { device_unregister(&dssdev->dev); } diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 7804779c9da1..7a9a2e7d9685 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -37,99 +37,15 @@ #include <plat/sram.h> #include <plat/clock.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" #include "dss_features.h" +#include "dispc.h" /* DISPC */ #define DISPC_SZ_REGS SZ_4K -struct dispc_reg { u16 idx; }; - -#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) - -/* - * 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_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(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_DIVISORo(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(ch) DISPC_REG(ch != 2 ? 0x007C : 0x03CC) - -/* DISPC GFX plane */ -#define DISPC_GFX_BA0 DISPC_REG(0x0080) -#define DISPC_GFX_BA1 DISPC_REG(0x0084) -#define DISPC_GFX_POSITION DISPC_REG(0x0088) -#define DISPC_GFX_SIZE DISPC_REG(0x008C) -#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) -#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) -#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) -#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) -#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) -#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) -#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) - -#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) - -/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ -#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) - -#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) -#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) -#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) -#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) -#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) -#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) -#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) -#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) -#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) -#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) -#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) -#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) -#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) - -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) -/* coef index i = {0, 1, 2, 3, 4} */ -#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) - -#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) - -#define DISPC_DIVISOR DISPC_REG(0x0804) - #define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ DISPC_IRQ_OCP_ERR | \ DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ @@ -167,10 +83,6 @@ struct dispc_v_coef { #define REG_FLD_MOD(idx, val, start, end) \ dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) -static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID_ATTRIBUTES(0), - DISPC_VID_ATTRIBUTES(1) }; - struct dispc_irq_stats { unsigned long last_reset; unsigned irq_count; @@ -198,25 +110,38 @@ static struct { #endif } dispc; +enum omap_color_component { + /* used for all color formats for OMAP3 and earlier + * and for RGB and Y color component on OMAP4 + */ + DISPC_COLOR_COMPONENT_RGB_Y = 1 << 0, + /* used for UV component for + * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12 + * color formats on OMAP4 + */ + DISPC_COLOR_COMPONENT_UV = 1 << 1, +}; + static void _omap_dispc_set_irqs(void); -static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +static inline void dispc_write_reg(const u16 idx, u32 val) { - __raw_writel(val, dispc.base + idx.idx); + __raw_writel(val, dispc.base + idx); } -static inline u32 dispc_read_reg(const struct dispc_reg idx) +static inline u32 dispc_read_reg(const u16 idx) { - return __raw_readl(dispc.base + idx.idx); + return __raw_readl(dispc.base + idx); } #define SR(reg) \ - dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) + dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ - dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) + dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)]) void dispc_save_context(void) { + int i; if (cpu_is_omap24xx()) return; @@ -224,157 +149,153 @@ void dispc_save_context(void) SR(IRQENABLE); SR(CONTROL); SR(CONFIG); - SR(DEFAULT_COLOR(0)); - SR(DEFAULT_COLOR(1)); - SR(TRANS_COLOR(0)); - SR(TRANS_COLOR(1)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); SR(LINE_NUMBER); - SR(TIMING_H(0)); - SR(TIMING_V(0)); - SR(POL_FREQ(0)); - SR(DIVISORo(0)); + SR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); + SR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); + SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + SR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); SR(GLOBAL_ALPHA); - SR(SIZE_DIG); - SR(SIZE_LCD(0)); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); 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(DIVISORo(2)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + SR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + SR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + SR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); SR(CONFIG2); } - SR(GFX_BA0); - SR(GFX_BA1); - SR(GFX_POSITION); - SR(GFX_SIZE); - SR(GFX_ATTRIBUTES); - SR(GFX_FIFO_THRESHOLD); - SR(GFX_ROW_INC); - SR(GFX_PIXEL_INC); - SR(GFX_WINDOW_SKIP); - SR(GFX_TABLE_BA); - - 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)); + SR(OVL_BA0(OMAP_DSS_GFX)); + SR(OVL_BA1(OMAP_DSS_GFX)); + SR(OVL_POSITION(OMAP_DSS_GFX)); + SR(OVL_SIZE(OMAP_DSS_GFX)); + SR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + SR(OVL_ROW_INC(OMAP_DSS_GFX)); + SR(OVL_PIXEL_INC(OMAP_DSS_GFX)); + SR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + SR(OVL_TABLE_BA(OMAP_DSS_GFX)); + + SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { - SR(CPR_COEF_B(2)); - SR(CPR_COEF_G(2)); - SR(CPR_COEF_R(2)); + SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); - SR(DATA_CYCLE1(2)); - SR(DATA_CYCLE2(2)); - SR(DATA_CYCLE3(2)); + SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); } - SR(GFX_PRELOAD); + SR(OVL_PRELOAD(OMAP_DSS_GFX)); /* VID1 */ - SR(VID_BA0(0)); - SR(VID_BA1(0)); - SR(VID_POSITION(0)); - SR(VID_SIZE(0)); - SR(VID_ATTRIBUTES(0)); - SR(VID_FIFO_THRESHOLD(0)); - SR(VID_ROW_INC(0)); - SR(VID_PIXEL_INC(0)); - SR(VID_FIR(0)); - SR(VID_PICTURE_SIZE(0)); - SR(VID_ACCU0(0)); - SR(VID_ACCU1(0)); - - SR(VID_FIR_COEF_H(0, 0)); - SR(VID_FIR_COEF_H(0, 1)); - SR(VID_FIR_COEF_H(0, 2)); - SR(VID_FIR_COEF_H(0, 3)); - SR(VID_FIR_COEF_H(0, 4)); - SR(VID_FIR_COEF_H(0, 5)); - SR(VID_FIR_COEF_H(0, 6)); - SR(VID_FIR_COEF_H(0, 7)); - - SR(VID_FIR_COEF_HV(0, 0)); - SR(VID_FIR_COEF_HV(0, 1)); - SR(VID_FIR_COEF_HV(0, 2)); - SR(VID_FIR_COEF_HV(0, 3)); - SR(VID_FIR_COEF_HV(0, 4)); - SR(VID_FIR_COEF_HV(0, 5)); - SR(VID_FIR_COEF_HV(0, 6)); - SR(VID_FIR_COEF_HV(0, 7)); - - SR(VID_CONV_COEF(0, 0)); - SR(VID_CONV_COEF(0, 1)); - SR(VID_CONV_COEF(0, 2)); - SR(VID_CONV_COEF(0, 3)); - SR(VID_CONV_COEF(0, 4)); - - SR(VID_FIR_COEF_V(0, 0)); - SR(VID_FIR_COEF_V(0, 1)); - SR(VID_FIR_COEF_V(0, 2)); - SR(VID_FIR_COEF_V(0, 3)); - SR(VID_FIR_COEF_V(0, 4)); - SR(VID_FIR_COEF_V(0, 5)); - SR(VID_FIR_COEF_V(0, 6)); - SR(VID_FIR_COEF_V(0, 7)); - - SR(VID_PRELOAD(0)); + SR(OVL_BA0(OMAP_DSS_VIDEO1)); + SR(OVL_BA1(OMAP_DSS_VIDEO1)); + SR(OVL_POSITION(OMAP_DSS_VIDEO1)); + SR(OVL_SIZE(OMAP_DSS_VIDEO1)); + SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + SR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); + SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + SR(OVL_FIR(OMAP_DSS_VIDEO1)); + SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU0(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 5; i++) + SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + SR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); + SR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); + SR(OVL_FIR2(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + SR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); /* VID2 */ - SR(VID_BA0(1)); - SR(VID_BA1(1)); - SR(VID_POSITION(1)); - SR(VID_SIZE(1)); - SR(VID_ATTRIBUTES(1)); - SR(VID_FIFO_THRESHOLD(1)); - SR(VID_ROW_INC(1)); - SR(VID_PIXEL_INC(1)); - SR(VID_FIR(1)); - SR(VID_PICTURE_SIZE(1)); - SR(VID_ACCU0(1)); - SR(VID_ACCU1(1)); - - SR(VID_FIR_COEF_H(1, 0)); - SR(VID_FIR_COEF_H(1, 1)); - SR(VID_FIR_COEF_H(1, 2)); - SR(VID_FIR_COEF_H(1, 3)); - SR(VID_FIR_COEF_H(1, 4)); - SR(VID_FIR_COEF_H(1, 5)); - SR(VID_FIR_COEF_H(1, 6)); - SR(VID_FIR_COEF_H(1, 7)); - - SR(VID_FIR_COEF_HV(1, 0)); - SR(VID_FIR_COEF_HV(1, 1)); - SR(VID_FIR_COEF_HV(1, 2)); - SR(VID_FIR_COEF_HV(1, 3)); - SR(VID_FIR_COEF_HV(1, 4)); - SR(VID_FIR_COEF_HV(1, 5)); - SR(VID_FIR_COEF_HV(1, 6)); - SR(VID_FIR_COEF_HV(1, 7)); - - SR(VID_CONV_COEF(1, 0)); - SR(VID_CONV_COEF(1, 1)); - SR(VID_CONV_COEF(1, 2)); - SR(VID_CONV_COEF(1, 3)); - SR(VID_CONV_COEF(1, 4)); - - SR(VID_FIR_COEF_V(1, 0)); - SR(VID_FIR_COEF_V(1, 1)); - SR(VID_FIR_COEF_V(1, 2)); - SR(VID_FIR_COEF_V(1, 3)); - SR(VID_FIR_COEF_V(1, 4)); - SR(VID_FIR_COEF_V(1, 5)); - SR(VID_FIR_COEF_V(1, 6)); - SR(VID_FIR_COEF_V(1, 7)); - - SR(VID_PRELOAD(1)); + SR(OVL_BA0(OMAP_DSS_VIDEO2)); + SR(OVL_BA1(OMAP_DSS_VIDEO2)); + SR(OVL_POSITION(OMAP_DSS_VIDEO2)); + SR(OVL_SIZE(OMAP_DSS_VIDEO2)); + SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + SR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); + SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + SR(OVL_FIR(OMAP_DSS_VIDEO2)); + SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU0(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 5; i++) + SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + SR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); + SR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); + SR(OVL_FIR2(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + SR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); if (dss_has_feature(FEAT_CORE_CLK_DIV)) SR(DIVISOR); @@ -382,160 +303,158 @@ void dispc_save_context(void) void dispc_restore_context(void) { + int i; RR(SYSCONFIG); /*RR(IRQENABLE);*/ /*RR(CONTROL);*/ RR(CONFIG); - RR(DEFAULT_COLOR(0)); - RR(DEFAULT_COLOR(1)); - RR(TRANS_COLOR(0)); - RR(TRANS_COLOR(1)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); RR(LINE_NUMBER); - RR(TIMING_H(0)); - RR(TIMING_V(0)); - RR(POL_FREQ(0)); - RR(DIVISORo(0)); + RR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); + RR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); + RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + RR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); RR(GLOBAL_ALPHA); - RR(SIZE_DIG); - RR(SIZE_LCD(0)); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); 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(DIVISORo(2)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + RR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + RR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + RR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); RR(CONFIG2); } - RR(GFX_BA0); - RR(GFX_BA1); - RR(GFX_POSITION); - RR(GFX_SIZE); - RR(GFX_ATTRIBUTES); - RR(GFX_FIFO_THRESHOLD); - RR(GFX_ROW_INC); - RR(GFX_PIXEL_INC); - RR(GFX_WINDOW_SKIP); - RR(GFX_TABLE_BA); - - 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)); + RR(OVL_BA0(OMAP_DSS_GFX)); + RR(OVL_BA1(OMAP_DSS_GFX)); + RR(OVL_POSITION(OMAP_DSS_GFX)); + RR(OVL_SIZE(OMAP_DSS_GFX)); + RR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + RR(OVL_ROW_INC(OMAP_DSS_GFX)); + RR(OVL_PIXEL_INC(OMAP_DSS_GFX)); + RR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + RR(OVL_TABLE_BA(OMAP_DSS_GFX)); + + + RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { - RR(DATA_CYCLE1(2)); - RR(DATA_CYCLE2(2)); - RR(DATA_CYCLE3(2)); + RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); - RR(CPR_COEF_B(2)); - RR(CPR_COEF_G(2)); - RR(CPR_COEF_R(2)); + RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); } - RR(GFX_PRELOAD); + RR(OVL_PRELOAD(OMAP_DSS_GFX)); /* VID1 */ - RR(VID_BA0(0)); - RR(VID_BA1(0)); - RR(VID_POSITION(0)); - RR(VID_SIZE(0)); - RR(VID_ATTRIBUTES(0)); - RR(VID_FIFO_THRESHOLD(0)); - RR(VID_ROW_INC(0)); - RR(VID_PIXEL_INC(0)); - RR(VID_FIR(0)); - RR(VID_PICTURE_SIZE(0)); - RR(VID_ACCU0(0)); - RR(VID_ACCU1(0)); - - RR(VID_FIR_COEF_H(0, 0)); - RR(VID_FIR_COEF_H(0, 1)); - RR(VID_FIR_COEF_H(0, 2)); - RR(VID_FIR_COEF_H(0, 3)); - RR(VID_FIR_COEF_H(0, 4)); - RR(VID_FIR_COEF_H(0, 5)); - RR(VID_FIR_COEF_H(0, 6)); - RR(VID_FIR_COEF_H(0, 7)); - - RR(VID_FIR_COEF_HV(0, 0)); - RR(VID_FIR_COEF_HV(0, 1)); - RR(VID_FIR_COEF_HV(0, 2)); - RR(VID_FIR_COEF_HV(0, 3)); - RR(VID_FIR_COEF_HV(0, 4)); - RR(VID_FIR_COEF_HV(0, 5)); - RR(VID_FIR_COEF_HV(0, 6)); - RR(VID_FIR_COEF_HV(0, 7)); - - RR(VID_CONV_COEF(0, 0)); - RR(VID_CONV_COEF(0, 1)); - RR(VID_CONV_COEF(0, 2)); - RR(VID_CONV_COEF(0, 3)); - RR(VID_CONV_COEF(0, 4)); - - RR(VID_FIR_COEF_V(0, 0)); - RR(VID_FIR_COEF_V(0, 1)); - RR(VID_FIR_COEF_V(0, 2)); - RR(VID_FIR_COEF_V(0, 3)); - RR(VID_FIR_COEF_V(0, 4)); - RR(VID_FIR_COEF_V(0, 5)); - RR(VID_FIR_COEF_V(0, 6)); - RR(VID_FIR_COEF_V(0, 7)); - - RR(VID_PRELOAD(0)); + RR(OVL_BA0(OMAP_DSS_VIDEO1)); + RR(OVL_BA1(OMAP_DSS_VIDEO1)); + RR(OVL_POSITION(OMAP_DSS_VIDEO1)); + RR(OVL_SIZE(OMAP_DSS_VIDEO1)); + RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + RR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); + RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + RR(OVL_FIR(OMAP_DSS_VIDEO1)); + RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU0(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 5; i++) + RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + RR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); + RR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); + RR(OVL_FIR2(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + RR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); /* VID2 */ - RR(VID_BA0(1)); - RR(VID_BA1(1)); - RR(VID_POSITION(1)); - RR(VID_SIZE(1)); - RR(VID_ATTRIBUTES(1)); - RR(VID_FIFO_THRESHOLD(1)); - RR(VID_ROW_INC(1)); - RR(VID_PIXEL_INC(1)); - RR(VID_FIR(1)); - RR(VID_PICTURE_SIZE(1)); - RR(VID_ACCU0(1)); - RR(VID_ACCU1(1)); - - RR(VID_FIR_COEF_H(1, 0)); - RR(VID_FIR_COEF_H(1, 1)); - RR(VID_FIR_COEF_H(1, 2)); - RR(VID_FIR_COEF_H(1, 3)); - RR(VID_FIR_COEF_H(1, 4)); - RR(VID_FIR_COEF_H(1, 5)); - RR(VID_FIR_COEF_H(1, 6)); - RR(VID_FIR_COEF_H(1, 7)); - - RR(VID_FIR_COEF_HV(1, 0)); - RR(VID_FIR_COEF_HV(1, 1)); - RR(VID_FIR_COEF_HV(1, 2)); - RR(VID_FIR_COEF_HV(1, 3)); - RR(VID_FIR_COEF_HV(1, 4)); - RR(VID_FIR_COEF_HV(1, 5)); - RR(VID_FIR_COEF_HV(1, 6)); - RR(VID_FIR_COEF_HV(1, 7)); - - RR(VID_CONV_COEF(1, 0)); - RR(VID_CONV_COEF(1, 1)); - RR(VID_CONV_COEF(1, 2)); - RR(VID_CONV_COEF(1, 3)); - RR(VID_CONV_COEF(1, 4)); - - RR(VID_FIR_COEF_V(1, 0)); - RR(VID_FIR_COEF_V(1, 1)); - RR(VID_FIR_COEF_V(1, 2)); - RR(VID_FIR_COEF_V(1, 3)); - RR(VID_FIR_COEF_V(1, 4)); - RR(VID_FIR_COEF_V(1, 5)); - RR(VID_FIR_COEF_V(1, 6)); - RR(VID_FIR_COEF_V(1, 7)); - - RR(VID_PRELOAD(1)); + RR(OVL_BA0(OMAP_DSS_VIDEO2)); + RR(OVL_BA1(OMAP_DSS_VIDEO2)); + RR(OVL_POSITION(OMAP_DSS_VIDEO2)); + RR(OVL_SIZE(OMAP_DSS_VIDEO2)); + RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + RR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); + RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + RR(OVL_FIR(OMAP_DSS_VIDEO2)); + RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU0(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 5; i++) + RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + RR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); + RR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); + RR(OVL_FIR2(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + RR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); if (dss_has_feature(FEAT_CORE_CLK_DIV)) RR(DIVISOR); @@ -632,27 +551,43 @@ end: static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) { + dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value); +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value); +} + +static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value) +{ BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value); } -static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value); } -static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value); } static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, - int vscaleup, int five_taps) + int vscaleup, int five_taps, + enum omap_color_component color_comp) { /* Coefficients for horizontal up-sampling */ static const struct dispc_h_coef coef_hup[8] = { @@ -750,8 +685,14 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, | FLD_VAL(v_coef[i].vc1, 23, 16) | FLD_VAL(v_coef[i].vc2, 31, 24); - _dispc_write_firh_reg(plane, i, h); - _dispc_write_firhv_reg(plane, i, hv); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } else { + _dispc_write_firh2_reg(plane, i, h); + _dispc_write_firhv2_reg(plane, i, hv); + } + } if (five_taps) { @@ -759,7 +700,10 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, u32 v; v = FLD_VAL(v_coef[i].vc00, 7, 0) | FLD_VAL(v_coef[i].vc22, 15, 8); - _dispc_write_firv_reg(plane, i, v); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) + _dispc_write_firv_reg(plane, i, v); + else + _dispc_write_firv2_reg(plane, i, v); } } } @@ -779,72 +723,83 @@ static void _dispc_setup_color_conv_coef(void) ct = &ctbl_bt601_5; - dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); - - dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4), + CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4), + CVAL(0, ct->bcb)); #undef CVAL - REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); - REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1), + ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2), + ct->full_range, 11, 11); } static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, - DISPC_VID_BA0(0), - DISPC_VID_BA0(1) }; - - dispc_write_reg(ba0_reg[plane], paddr); + dispc_write_reg(DISPC_OVL_BA0(plane), paddr); } static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, - DISPC_VID_BA1(0), - DISPC_VID_BA1(1) }; + dispc_write_reg(DISPC_OVL_BA1(plane), paddr); +} - dispc_write_reg(ba1_reg[plane], paddr); +static void _dispc_set_plane_ba0_uv(enum omap_plane plane, u32 paddr) +{ + dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr); } -static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +static void _dispc_set_plane_ba1_uv(enum omap_plane plane, u32 paddr) { - const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, - DISPC_VID_POSITION(0), - DISPC_VID_POSITION(1) }; + dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); +} +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); - dispc_write_reg(pos_reg[plane], val); + + dispc_write_reg(DISPC_OVL_POSITION(plane), val); } static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) { - const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, - DISPC_VID_PICTURE_SIZE(0), - DISPC_VID_PICTURE_SIZE(1) }; u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(siz_reg[plane], val); + + if (plane == OMAP_DSS_GFX) + dispc_write_reg(DISPC_OVL_SIZE(plane), val); + else + dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); } static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) { u32 val; - const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), - DISPC_VID_SIZE(1) }; BUG_ON(plane == OMAP_DSS_GFX); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(vsi_reg[plane-1], val); + + dispc_write_reg(DISPC_OVL_SIZE(plane), val); } static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) @@ -856,7 +811,7 @@ static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) plane == OMAP_DSS_VIDEO1) return; - REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 28, 28); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); } static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) @@ -876,61 +831,93 @@ static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, - DISPC_VID_PIXEL_INC(0), - DISPC_VID_PIXEL_INC(1) }; - - dispc_write_reg(ri_reg[plane], inc); + dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc); } static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, - DISPC_VID_ROW_INC(0), - DISPC_VID_ROW_INC(1) }; - - dispc_write_reg(ri_reg[plane], inc); + dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); } static void _dispc_set_color_mode(enum omap_plane plane, enum omap_color_mode color_mode) { u32 m = 0; - - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - m = 0x0; break; - case OMAP_DSS_COLOR_CLUT2: - m = 0x1; break; - case OMAP_DSS_COLOR_CLUT4: - m = 0x2; break; - case OMAP_DSS_COLOR_CLUT8: - m = 0x3; break; - case OMAP_DSS_COLOR_RGB12U: - m = 0x4; break; - case OMAP_DSS_COLOR_ARGB16: - m = 0x5; break; - case OMAP_DSS_COLOR_RGB16: - m = 0x6; break; - case OMAP_DSS_COLOR_RGB24U: - m = 0x8; break; - case OMAP_DSS_COLOR_RGB24P: - m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: - m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: - m = 0xb; break; - case OMAP_DSS_COLOR_ARGB32: - m = 0xc; break; - case OMAP_DSS_COLOR_RGBA32: - m = 0xd; break; - case OMAP_DSS_COLOR_RGBX32: - m = 0xe; break; - default: - BUG(); break; + if (plane != OMAP_DSS_GFX) { + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + m = 0x0; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x1; break; + case OMAP_DSS_COLOR_RGBA16: + m = 0x2; break; + case OMAP_DSS_COLOR_RGBX16: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + case OMAP_DSS_COLOR_XRGB16_1555: + m = 0xf; break; + default: + BUG(); break; + } + } else { + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + case OMAP_DSS_COLOR_XRGB16_1555: + m = 0xf; break; + default: + BUG(); break; + } } - REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } static void _dispc_set_channel_out(enum omap_plane plane, @@ -953,7 +940,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, return; } - val = dispc_read_reg(dispc_reg_att[plane]); + val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); if (dss_has_feature(FEAT_MGR_LCD2)) { switch (channel) { case OMAP_DSS_CHANNEL_LCD: @@ -977,7 +964,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, } else { val = FLD_MOD(val, channel, shift, shift); } - dispc_write_reg(dispc_reg_att[plane], val); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } void dispc_set_burst_size(enum omap_plane plane, @@ -1001,9 +988,9 @@ void dispc_set_burst_size(enum omap_plane plane, return; } - val = dispc_read_reg(dispc_reg_att[plane]); + val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); val = FLD_MOD(val, burst_size, shift+1, shift); - dispc_write_reg(dispc_reg_att[plane], val); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); enable_clocks(0); } @@ -1028,9 +1015,9 @@ static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) BUG_ON(plane == OMAP_DSS_GFX); - val = dispc_read_reg(dispc_reg_att[plane]); + val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); val = FLD_MOD(val, enable, 9, 9); - dispc_write_reg(dispc_reg_att[plane], val); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } void dispc_enable_replication(enum omap_plane plane, bool enable) @@ -1043,7 +1030,7 @@ void dispc_enable_replication(enum omap_plane plane, bool enable) bit = 10; enable_clocks(1); - REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); enable_clocks(0); } @@ -1053,7 +1040,7 @@ void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) 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(channel), val); + dispc_write_reg(DISPC_SIZE_MGR(channel), val); enable_clocks(0); } @@ -1063,15 +1050,12 @@ void dispc_set_digit_size(u16 width, u16 height) 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_DIG, val); + dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); enable_clocks(0); } static void dispc_read_plane_fifo_sizes(void) { - const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, - DISPC_VID_FIFO_SIZE_STATUS(0), - DISPC_VID_FIFO_SIZE_STATUS(1) }; u32 size; int plane; u8 start, end; @@ -1081,7 +1065,8 @@ static void dispc_read_plane_fifo_sizes(void) dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { - size = FLD_GET(dispc_read_reg(fsz_reg[plane]), start, end); + size = FLD_GET(dispc_read_reg(DISPC_OVL_FIFO_SIZE_STATUS(plane)), + start, end); dispc.fifo_size[plane] = size; } @@ -1095,23 +1080,22 @@ u32 dispc_get_plane_fifo_size(enum omap_plane plane) void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) { - const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, - DISPC_VID_FIFO_THRESHOLD(0), - DISPC_VID_FIFO_THRESHOLD(1) }; u8 hi_start, hi_end, lo_start, lo_end; + dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); + dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); + enable_clocks(1); DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", plane, - REG_GET(ftrs_reg[plane], 11, 0), - REG_GET(ftrs_reg[plane], 27, 16), + REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + lo_start, lo_end), + REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + hi_start, hi_end), low, high); - dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); - dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); - - dispc_write_reg(ftrs_reg[plane], + dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | FLD_VAL(low, lo_start, lo_end)); @@ -1128,106 +1112,120 @@ void dispc_enable_fifomerge(bool enable) enable_clocks(0); } -static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +static void _dispc_set_fir(enum omap_plane plane, + int hinc, int vinc, + enum omap_color_component color_comp) { u32 val; - const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), - DISPC_VID_FIR(1) }; - u8 hinc_start, hinc_end, vinc_start, vinc_end; - - BUG_ON(plane == OMAP_DSS_GFX); - dss_feat_get_reg_field(FEAT_REG_FIRHINC, &hinc_start, &hinc_end); - dss_feat_get_reg_field(FEAT_REG_FIRVINC, &vinc_start, &vinc_end); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { + u8 hinc_start, hinc_end, vinc_start, vinc_end; - val = FLD_VAL(vinc, vinc_start, vinc_end) | - FLD_VAL(hinc, hinc_start, hinc_end); + dss_feat_get_reg_field(FEAT_REG_FIRHINC, + &hinc_start, &hinc_end); + dss_feat_get_reg_field(FEAT_REG_FIRVINC, + &vinc_start, &vinc_end); + val = FLD_VAL(vinc, vinc_start, vinc_end) | + FLD_VAL(hinc, hinc_start, hinc_end); - dispc_write_reg(fir_reg[plane-1], val); + dispc_write_reg(DISPC_OVL_FIR(plane), val); + } else { + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dispc_write_reg(DISPC_OVL_FIR2(plane), val); + } } static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), - DISPC_VID_ACCU0(1) }; u8 hor_start, hor_end, vert_start, vert_end; - BUG_ON(plane == OMAP_DSS_GFX); - dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); val = FLD_VAL(vaccu, vert_start, vert_end) | FLD_VAL(haccu, hor_start, hor_end); - dispc_write_reg(ac0_reg[plane-1], val); + dispc_write_reg(DISPC_OVL_ACCU0(plane), val); } static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), - DISPC_VID_ACCU1(1) }; u8 hor_start, hor_end, vert_start, vert_end; - BUG_ON(plane == OMAP_DSS_GFX); - dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); val = FLD_VAL(vaccu, vert_start, vert_end) | FLD_VAL(haccu, hor_start, hor_end); - dispc_write_reg(ac1_reg[plane-1], val); + dispc_write_reg(DISPC_OVL_ACCU1(plane), val); +} + +static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val); } +static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; -static void _dispc_set_scaling(enum omap_plane plane, + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val); +} + +static void _dispc_set_scale_param(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode) + bool five_taps, u8 rotation, + enum omap_color_component color_comp) { - int fir_hinc; - int fir_vinc; + int fir_hinc, fir_vinc; int hscaleup, vscaleup; - int accu0 = 0; - int accu1 = 0; - u32 l; - - BUG_ON(plane == OMAP_DSS_GFX); hscaleup = orig_width <= out_width; vscaleup = orig_height <= out_height; - _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps, color_comp); - if (!orig_width || orig_width == out_width) - fir_hinc = 0; - else - fir_hinc = 1024 * orig_width / out_width; + fir_hinc = 1024 * orig_width / out_width; + fir_vinc = 1024 * orig_height / out_height; - if (!orig_height || orig_height == out_height) - fir_vinc = 0; - else - fir_vinc = 1024 * orig_height / out_height; + _dispc_set_fir(plane, fir_hinc, fir_vinc, color_comp); +} - _dispc_set_fir(plane, fir_hinc, fir_vinc); +static void _dispc_set_scaling_common(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + int accu0 = 0; + int accu1 = 0; + u32 l; - l = dispc_read_reg(dispc_reg_att[plane]); + _dispc_set_scale_param(plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_RGB_Y); + l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); /* RESIZEENABLE and VERTICALTAPS */ l &= ~((0x3 << 5) | (0x1 << 21)); - l |= fir_hinc ? (1 << 5) : 0; - l |= fir_vinc ? (1 << 6) : 0; + l |= (orig_width != out_width) ? (1 << 5) : 0; + l |= (orig_height != out_height) ? (1 << 6) : 0; l |= five_taps ? (1 << 21) : 0; /* VRESIZECONF and HRESIZECONF */ if (dss_has_feature(FEAT_RESIZECONF)) { l &= ~(0x3 << 7); - l |= hscaleup ? 0 : (1 << 7); - l |= vscaleup ? 0 : (1 << 8); + l |= (orig_width <= out_width) ? 0 : (1 << 7); + l |= (orig_height <= out_height) ? 0 : (1 << 8); } /* LINEBUFFERSPLIT */ @@ -1236,7 +1234,7 @@ static void _dispc_set_scaling(enum omap_plane plane, l |= five_taps ? (1 << 22) : 0; } - dispc_write_reg(dispc_reg_att[plane], l); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); /* * field 0 = even field = bottom field @@ -1244,7 +1242,7 @@ static void _dispc_set_scaling(enum omap_plane plane, */ if (ilace && !fieldmode) { accu1 = 0; - accu0 = (fir_vinc / 2) & 0x3ff; + accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff; if (accu0 >= 1024/2) { accu1 = 1024/2; accu0 -= accu1; @@ -1255,6 +1253,93 @@ static void _dispc_set_scaling(enum omap_plane plane, _dispc_set_vid_accu1(plane, 0, accu1); } +static void _dispc_set_scaling_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + int scale_x = out_width != orig_width; + int scale_y = out_height != orig_height; + + if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) + return; + if ((color_mode != OMAP_DSS_COLOR_YUV2 && + color_mode != OMAP_DSS_COLOR_UYVY && + color_mode != OMAP_DSS_COLOR_NV12)) { + /* reset chroma resampling for RGB formats */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); + return; + } + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + /* UV is subsampled by 2 vertically*/ + orig_height >>= 1; + /* UV is subsampled by 2 horz.*/ + orig_width >>= 1; + break; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + /*For YUV422 with 90/270 rotation, + *we don't upsample chroma + */ + if (rotation == OMAP_DSS_ROT_0 || + rotation == OMAP_DSS_ROT_180) + /* UV is subsampled by 2 hrz*/ + orig_width >>= 1; + /* must use FIR for YUV422 if rotated */ + if (rotation != OMAP_DSS_ROT_0) + scale_x = scale_y = true; + break; + default: + BUG(); + } + + if (out_width != orig_width) + scale_x = true; + if (out_height != orig_height) + scale_y = true; + + _dispc_set_scale_param(plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_UV); + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), + (scale_x || scale_y) ? 1 : 0, 8, 8); + /* set H scaling */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); + /* set V scaling */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); + + _dispc_set_vid_accu2_0(plane, 0x80, 0); + _dispc_set_vid_accu2_1(plane, 0x80, 0); +} + +static void _dispc_set_scaling(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + _dispc_set_scaling_common(plane, + orig_width, orig_height, + out_width, out_height, + ilace, five_taps, + fieldmode, color_mode, + rotation); + + _dispc_set_scaling_uv(plane, + orig_width, orig_height, + out_width, out_height, + ilace, five_taps, + fieldmode, color_mode, + rotation); +} + static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, bool mirroring, enum omap_color_mode color_mode) { @@ -1302,9 +1387,10 @@ static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, row_repeat = false; } - REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); if (dss_has_feature(FEAT_ROWREPEATENABLE)) - REG_FLD_MOD(dispc_reg_att[plane], row_repeat ? 1 : 0, 18, 18); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), + row_repeat ? 1 : 0, 18, 18); } static int color_mode_to_bpp(enum omap_color_mode color_mode) @@ -1317,12 +1403,17 @@ static int color_mode_to_bpp(enum omap_color_mode color_mode) case OMAP_DSS_COLOR_CLUT4: return 4; case OMAP_DSS_COLOR_CLUT8: + case OMAP_DSS_COLOR_NV12: return 8; case OMAP_DSS_COLOR_RGB12U: case OMAP_DSS_COLOR_RGB16: case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: + case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_XRGB16_1555: return 16; case OMAP_DSS_COLOR_RGB24P: return 24; @@ -1655,7 +1746,7 @@ static int _dispc_setup_plane(enum omap_plane plane, enum omap_dss_rotation_type rotation_type, u8 rotation, int mirror, u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel) + enum omap_channel channel, u32 puv_addr) { const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; bool five_taps = 0; @@ -1704,7 +1795,8 @@ static int _dispc_setup_plane(enum omap_plane plane, return -EINVAL; if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) + color_mode == OMAP_DSS_COLOR_UYVY || + color_mode == OMAP_DSS_COLOR_NV12) cconv = 1; /* Must use 5-tap filter? */ @@ -1778,6 +1870,12 @@ static int _dispc_setup_plane(enum omap_plane plane, _dispc_set_plane_ba0(plane, paddr + offset0); _dispc_set_plane_ba1(plane, paddr + offset1); + if (OMAP_DSS_COLOR_NV12 == color_mode) { + _dispc_set_plane_ba0_uv(plane, puv_addr + offset0); + _dispc_set_plane_ba1_uv(plane, puv_addr + offset1); + } + + _dispc_set_row_inc(plane, row_inc); _dispc_set_pix_inc(plane, pix_inc); @@ -1791,7 +1889,8 @@ static int _dispc_setup_plane(enum omap_plane plane, if (plane != OMAP_DSS_GFX) { _dispc_set_scaling(plane, width, height, out_width, out_height, - ilace, five_taps, fieldmode); + ilace, five_taps, fieldmode, + color_mode, rotation); _dispc_set_vid_size(plane, out_width, out_height); _dispc_set_vid_color_conv(plane, cconv); } @@ -1806,7 +1905,7 @@ static int _dispc_setup_plane(enum omap_plane plane, static void _dispc_enable_plane(enum omap_plane plane, bool enable) { - REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); } static void dispc_disable_isr(void *data, u32 mask) @@ -2353,14 +2452,20 @@ static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div, unsigned long dispc_fclk_rate(void) { + struct platform_device *dsidev; unsigned long r = 0; switch (dss_get_dispc_clk_source()) { - case DSS_CLK_SRC_FCK: + case OMAP_DSS_CLK_SRC_FCK: r = dss_clk_get_rate(DSS_CLK_FCK); break; - case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: - r = dsi_get_pll_hsdiv_dispc_rate(); + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(0); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(1); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); break; default: BUG(); @@ -2371,6 +2476,7 @@ unsigned long dispc_fclk_rate(void) unsigned long dispc_lclk_rate(enum omap_channel channel) { + struct platform_device *dsidev; int lcd; unsigned long r; u32 l; @@ -2380,11 +2486,16 @@ unsigned long dispc_lclk_rate(enum omap_channel channel) lcd = FLD_GET(l, 23, 16); switch (dss_get_lcd_clk_source(channel)) { - case DSS_CLK_SRC_FCK: + case OMAP_DSS_CLK_SRC_FCK: r = dss_clk_get_rate(DSS_CLK_FCK); break; - case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: - r = dsi_get_pll_hsdiv_dispc_rate(); + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(0); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(1); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); break; default: BUG(); @@ -2412,8 +2523,8 @@ void dispc_dump_clocks(struct seq_file *s) { int lcd, pcd; u32 l; - enum dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); - enum dss_clk_source lcd_clk_src; + enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); + enum omap_dss_clk_source lcd_clk_src; enable_clocks(1); @@ -2516,7 +2627,7 @@ void dispc_dump_irqs(struct seq_file *s) void dispc_dump_regs(struct seq_file *s) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) +#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); @@ -2528,152 +2639,227 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_CONTROL); DUMPREG(DISPC_CONFIG); DUMPREG(DISPC_CAPABLE); - DUMPREG(DISPC_DEFAULT_COLOR(0)); - DUMPREG(DISPC_DEFAULT_COLOR(1)); - DUMPREG(DISPC_TRANS_COLOR(0)); - DUMPREG(DISPC_TRANS_COLOR(1)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); DUMPREG(DISPC_LINE_STATUS); DUMPREG(DISPC_LINE_NUMBER); - DUMPREG(DISPC_TIMING_H(0)); - DUMPREG(DISPC_TIMING_V(0)); - DUMPREG(DISPC_POL_FREQ(0)); - DUMPREG(DISPC_DIVISORo(0)); + DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD)); DUMPREG(DISPC_GLOBAL_ALPHA); - DUMPREG(DISPC_SIZE_DIG); - DUMPREG(DISPC_SIZE_LCD(0)); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); 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_DIVISORo(2)); - DUMPREG(DISPC_SIZE_LCD(2)); - } - - DUMPREG(DISPC_GFX_BA0); - DUMPREG(DISPC_GFX_BA1); - DUMPREG(DISPC_GFX_POSITION); - DUMPREG(DISPC_GFX_SIZE); - DUMPREG(DISPC_GFX_ATTRIBUTES); - DUMPREG(DISPC_GFX_FIFO_THRESHOLD); - DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); - DUMPREG(DISPC_GFX_ROW_INC); - DUMPREG(DISPC_GFX_PIXEL_INC); - DUMPREG(DISPC_GFX_WINDOW_SKIP); - DUMPREG(DISPC_GFX_TABLE_BA); - - 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)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + } + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_TABLE_BA(OMAP_DSS_GFX)); + + DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); 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); - - DUMPREG(DISPC_VID_BA0(0)); - DUMPREG(DISPC_VID_BA1(0)); - DUMPREG(DISPC_VID_POSITION(0)); - DUMPREG(DISPC_VID_SIZE(0)); - DUMPREG(DISPC_VID_ATTRIBUTES(0)); - DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); - DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); - DUMPREG(DISPC_VID_ROW_INC(0)); - DUMPREG(DISPC_VID_PIXEL_INC(0)); - DUMPREG(DISPC_VID_FIR(0)); - DUMPREG(DISPC_VID_PICTURE_SIZE(0)); - DUMPREG(DISPC_VID_ACCU0(0)); - DUMPREG(DISPC_VID_ACCU1(0)); - - DUMPREG(DISPC_VID_BA0(1)); - DUMPREG(DISPC_VID_BA1(1)); - DUMPREG(DISPC_VID_POSITION(1)); - DUMPREG(DISPC_VID_SIZE(1)); - DUMPREG(DISPC_VID_ATTRIBUTES(1)); - DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); - DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); - DUMPREG(DISPC_VID_ROW_INC(1)); - DUMPREG(DISPC_VID_PIXEL_INC(1)); - DUMPREG(DISPC_VID_FIR(1)); - DUMPREG(DISPC_VID_PICTURE_SIZE(1)); - DUMPREG(DISPC_VID_ACCU0(1)); - DUMPREG(DISPC_VID_ACCU1(1)); - - DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); - DUMPREG(DISPC_VID_CONV_COEF(0, 0)); - DUMPREG(DISPC_VID_CONV_COEF(0, 1)); - DUMPREG(DISPC_VID_CONV_COEF(0, 2)); - DUMPREG(DISPC_VID_CONV_COEF(0, 3)); - DUMPREG(DISPC_VID_CONV_COEF(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); - - DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); - DUMPREG(DISPC_VID_CONV_COEF(1, 0)); - DUMPREG(DISPC_VID_CONV_COEF(1, 1)); - DUMPREG(DISPC_VID_CONV_COEF(1, 2)); - DUMPREG(DISPC_VID_CONV_COEF(1, 3)); - DUMPREG(DISPC_VID_CONV_COEF(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); - - DUMPREG(DISPC_VID_PRELOAD(0)); - DUMPREG(DISPC_VID_PRELOAD(1)); + DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); + + DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + } + + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX)); + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO1)); + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO2)); + + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 7)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 7)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 7)); + } + if (dss_has_feature(FEAT_ATTR2)) + DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 7)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 7)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7)); + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 7)); + } + if (dss_has_feature(FEAT_ATTR2)) + DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2)); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); #undef DUMPREG @@ -3388,11 +3574,12 @@ 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 pre_mult_alpha, enum omap_channel channel) + u8 pre_mult_alpha, enum omap_channel channel, + u32 puv_addr) { int r = 0; - DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d, %d, %dx%d -> " "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", plane, paddr, screen_width, pos_x, pos_y, width, height, @@ -3411,7 +3598,8 @@ int dispc_setup_plane(enum omap_plane plane, rotation_type, rotation, mirror, global_alpha, - pre_mult_alpha, channel); + pre_mult_alpha, + channel, puv_addr); enable_clocks(0); diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h new file mode 100644 index 000000000000..6c9ee0a0efb3 --- /dev/null +++ b/drivers/video/omap2/dss/dispc.h @@ -0,0 +1,691 @@ +/* + * linux/drivers/video/omap2/dss/dispc.h + * + * Copyright (C) 2011 Texas Instruments + * Author: Archit Taneja <archit@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/>. + */ + +#ifndef __OMAP2_DISPC_REG_H +#define __OMAP2_DISPC_REG_H + +/* DISPC common registers */ +#define DISPC_REVISION 0x0000 +#define DISPC_SYSCONFIG 0x0010 +#define DISPC_SYSSTATUS 0x0014 +#define DISPC_IRQSTATUS 0x0018 +#define DISPC_IRQENABLE 0x001C +#define DISPC_CONTROL 0x0040 +#define DISPC_CONFIG 0x0044 +#define DISPC_CAPABLE 0x0048 +#define DISPC_LINE_STATUS 0x005C +#define DISPC_LINE_NUMBER 0x0060 +#define DISPC_GLOBAL_ALPHA 0x0074 +#define DISPC_CONTROL2 0x0238 +#define DISPC_CONFIG2 0x0620 +#define DISPC_DIVISOR 0x0804 + +/* DISPC overlay registers */ +#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA0_OFFSET(n)) +#define DISPC_OVL_BA1(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA1_OFFSET(n)) +#define DISPC_OVL_BA0_UV(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA0_UV_OFFSET(n)) +#define DISPC_OVL_BA1_UV(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA1_UV_OFFSET(n)) +#define DISPC_OVL_POSITION(n) (DISPC_OVL_BASE(n) + \ + DISPC_POS_OFFSET(n)) +#define DISPC_OVL_SIZE(n) (DISPC_OVL_BASE(n) + \ + DISPC_SIZE_OFFSET(n)) +#define DISPC_OVL_ATTRIBUTES(n) (DISPC_OVL_BASE(n) + \ + DISPC_ATTR_OFFSET(n)) +#define DISPC_OVL_ATTRIBUTES2(n) (DISPC_OVL_BASE(n) + \ + DISPC_ATTR2_OFFSET(n)) +#define DISPC_OVL_FIFO_THRESHOLD(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIFO_THRESH_OFFSET(n)) +#define DISPC_OVL_FIFO_SIZE_STATUS(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIFO_SIZE_STATUS_OFFSET(n)) +#define DISPC_OVL_ROW_INC(n) (DISPC_OVL_BASE(n) + \ + DISPC_ROW_INC_OFFSET(n)) +#define DISPC_OVL_PIXEL_INC(n) (DISPC_OVL_BASE(n) + \ + DISPC_PIX_INC_OFFSET(n)) +#define DISPC_OVL_WINDOW_SKIP(n) (DISPC_OVL_BASE(n) + \ + DISPC_WINDOW_SKIP_OFFSET(n)) +#define DISPC_OVL_TABLE_BA(n) (DISPC_OVL_BASE(n) + \ + DISPC_TABLE_BA_OFFSET(n)) +#define DISPC_OVL_FIR(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_OFFSET(n)) +#define DISPC_OVL_FIR2(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIR2_OFFSET(n)) +#define DISPC_OVL_PICTURE_SIZE(n) (DISPC_OVL_BASE(n) + \ + DISPC_PIC_SIZE_OFFSET(n)) +#define DISPC_OVL_ACCU0(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU0_OFFSET(n)) +#define DISPC_OVL_ACCU1(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU1_OFFSET(n)) +#define DISPC_OVL_ACCU2_0(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU2_0_OFFSET(n)) +#define DISPC_OVL_ACCU2_1(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU2_1_OFFSET(n)) +#define DISPC_OVL_FIR_COEF_H(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_H_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_HV(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_HV_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_H2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_H2_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_HV2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_HV2_OFFSET(n, i)) +#define DISPC_OVL_CONV_COEF(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_CONV_COEF_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_V(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_V_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_V2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_V2_OFFSET(n, i)) +#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \ + DISPC_PRELOAD_OFFSET(n)) + +/* DISPC manager/channel specific registers */ +static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x004C; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0050; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03AC; + default: + BUG(); + } +} + +static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0054; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0058; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B0; + default: + BUG(); + } +} + +static inline u16 DISPC_TIMING_H(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0064; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0400; + default: + BUG(); + } +} + +static inline u16 DISPC_TIMING_V(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0068; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0404; + default: + BUG(); + } +} + +static inline u16 DISPC_POL_FREQ(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x006C; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0408; + default: + BUG(); + } +} + +static inline u16 DISPC_DIVISORo(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0070; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x040C; + default: + BUG(); + } +} + +/* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */ +static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x007C; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0078; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03CC; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01D4; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C0; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01D8; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C4; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01DC; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C8; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0220; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03BC; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0224; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B8; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0228; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B4; + default: + BUG(); + } +} + +/* DISPC overlay register base addresses */ +static inline u16 DISPC_OVL_BASE(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0080; + case OMAP_DSS_VIDEO1: + return 0x00BC; + case OMAP_DSS_VIDEO2: + return 0x014C; + default: + BUG(); + } +} + +/* DISPC overlay register offsets */ +static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0000; + default: + BUG(); + } +} + +static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0004; + default: + BUG(); + } +} + +static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0544; + case OMAP_DSS_VIDEO2: + return 0x04BC; + default: + BUG(); + } +} + +static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0548; + case OMAP_DSS_VIDEO2: + return 0x04C0; + default: + BUG(); + } +} + +static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0008; + default: + BUG(); + } +} + +static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x000C; + default: + BUG(); + } +} + +static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0020; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0010; + default: + BUG(); + } +} + +static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0568; + case OMAP_DSS_VIDEO2: + return 0x04DC; + default: + BUG(); + } +} + +static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0024; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0014; + default: + BUG(); + } +} + +static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0028; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0018; + default: + BUG(); + } +} + +static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x002C; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x001C; + default: + BUG(); + } +} + +static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0030; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0020; + default: + BUG(); + } +} + +static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0034; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + BUG(); + default: + BUG(); + } +} + +static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0038; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + BUG(); + default: + BUG(); + } +} + +static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0024; + default: + BUG(); + } +} + +static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0580; + case OMAP_DSS_VIDEO2: + return 0x055C; + default: + BUG(); + } +} + +static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0028; + default: + BUG(); + } +} + + +static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x002C; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0584; + case OMAP_DSS_VIDEO2: + return 0x0560; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0030; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0588; + case OMAP_DSS_VIDEO2: + return 0x0564; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0034 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x058C + i * 0x8; + case OMAP_DSS_VIDEO2: + return 0x0568 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0038 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0590 + i * 8; + case OMAP_DSS_VIDEO2: + return 0x056C + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4,} */ +static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0074 + i * 0x4; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0124 + i * 0x4; + case OMAP_DSS_VIDEO2: + return 0x00B4 + i * 0x4; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x05CC + i * 0x4; + case OMAP_DSS_VIDEO2: + return 0x05A8 + i * 0x4; + default: + BUG(); + } +} + +static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x01AC; + case OMAP_DSS_VIDEO1: + return 0x0174; + case OMAP_DSS_VIDEO2: + return 0x00E8; + default: + BUG(); + } +} +#endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index a85a6f38b40c..c2dfc8c50057 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -27,7 +27,7 @@ #include <linux/jiffies.h> #include <linux/platform_device.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" static ssize_t display_enabled_show(struct device *dev, @@ -44,9 +44,13 @@ static ssize_t display_enabled_store(struct device *dev, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - bool enabled, r; + int r, enabled; - enabled = simple_strtoul(buf, NULL, 10); + r = kstrtoint(buf, 0, &enabled); + if (r) + return r; + + enabled = !!enabled; if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { if (enabled) { @@ -82,7 +86,9 @@ static ssize_t display_upd_mode_store(struct device *dev, if (!dssdev->driver->set_update_mode) return -EINVAL; - val = simple_strtoul(buf, NULL, 10); + r = kstrtoint(buf, 0, &val); + if (r) + return r; switch (val) { case OMAP_DSS_UPDATE_DISABLED: @@ -114,13 +120,16 @@ static ssize_t display_tear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long te; - int r; + int te, r; if (!dssdev->driver->enable_te || !dssdev->driver->get_te) return -ENOENT; - te = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &te); + if (r) + return r; + + te = !!te; r = dssdev->driver->enable_te(dssdev, te); if (r) @@ -196,13 +205,14 @@ static ssize_t display_rotate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long rot; - int r; + int rot, r; if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) return -ENOENT; - rot = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &rot); + if (r) + return r; r = dssdev->driver->set_rotate(dssdev, rot); if (r) @@ -226,13 +236,16 @@ static ssize_t display_mirror_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long mirror; - int r; + int mirror, r; if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) return -ENOENT; - mirror = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &mirror); + if (r) + return r; + + mirror = !!mirror; r = dssdev->driver->set_mirror(dssdev, mirror); if (r) @@ -259,14 +272,15 @@ static ssize_t display_wss_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long wss; + u32 wss; int r; if (!dssdev->driver->get_wss || !dssdev->driver->set_wss) return -ENOENT; - if (strict_strtoul(buf, 0, &wss)) - return -EINVAL; + r = kstrtou32(buf, 0, &wss); + if (r) + return r; if (wss > 0xfffff) return -EINVAL; diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 2d3ca4ca4a05..ff6bd30132df 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -30,16 +30,40 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" static struct { struct regulator *vdds_dsi_reg; + struct platform_device *dsidev; } dpi; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk) +{ + int dsi_module; + + dsi_module = clk == OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ? 0 : 1; + + return dsi_get_dsidev_from_id(dsi_module); +} + +static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev) +{ + if (dssdev->clocks.dispc.dispc_fclk_src == + OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.dispc_fclk_src == + OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.channel.lcd_clk_src == + OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.channel.lcd_clk_src == + OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC) + return true; + else + return false; +} + 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) @@ -48,16 +72,16 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, struct dispc_clock_info dispc_cinfo; int r; - r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo, - &dispc_cinfo); + r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, pck_req, + &dsi_cinfo, &dispc_cinfo); if (r) return r; - r = dsi_pll_set_clock_div(&dsi_cinfo); + r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo); if (r) return r; - dss_select_dispc_clk_source(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC); + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) @@ -69,7 +93,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, return 0; } -#else + 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) @@ -96,13 +120,12 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft, return 0; } -#endif static int dpi_set_mode(struct omap_dss_device *dssdev) { struct omap_video_timings *t = &dssdev->panel.timings; - int lck_div, pck_div; - unsigned long fck; + int lck_div = 0, pck_div = 0; + unsigned long fck = 0; unsigned long pck; bool is_tft; int r = 0; @@ -114,13 +137,12 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, - &lck_div, &pck_div); -#else - r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, - &lck_div, &pck_div); -#endif + if (dpi_use_dsi_pll(dssdev)) + r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); + else + r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); if (r) goto err0; @@ -179,12 +201,13 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) if (r) goto err2; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dss_clk_enable(DSS_CLK_SYSCK); - r = dsi_pll_init(dssdev, 0, 1); - if (r) - goto err3; -#endif + if (dpi_use_dsi_pll(dssdev)) { + dss_clk_enable(DSS_CLK_SYSCK); + r = dsi_pll_init(dpi.dsidev, 0, 1); + if (r) + goto err3; + } + r = dpi_set_mode(dssdev); if (r) goto err4; @@ -196,11 +219,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) return 0; err4: -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dsi_pll_uninit(); + if (dpi_use_dsi_pll(dssdev)) + dsi_pll_uninit(dpi.dsidev, true); err3: - dss_clk_disable(DSS_CLK_SYSCK); -#endif + if (dpi_use_dsi_pll(dssdev)) + dss_clk_disable(DSS_CLK_SYSCK); err2: dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); if (cpu_is_omap34xx()) @@ -216,11 +239,11 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) { dssdev->manager->disable(dssdev->manager); -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dss_select_dispc_clk_source(DSS_CLK_SRC_FCK); - dsi_pll_uninit(); - dss_clk_disable(DSS_CLK_SYSCK); -#endif + if (dpi_use_dsi_pll(dssdev)) { + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dsi_pll_uninit(dpi.dsidev, true); + dss_clk_disable(DSS_CLK_SYSCK); + } dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); @@ -251,6 +274,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, int lck_div, pck_div; unsigned long fck; unsigned long pck; + struct dispc_clock_info dispc_cinfo; if (!dispc_lcd_timings_ok(timings)) return -EINVAL; @@ -260,11 +284,9 @@ int dpi_check_timings(struct omap_dss_device *dssdev, is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - { + if (dpi_use_dsi_pll(dssdev)) { struct dsi_clock_info dsi_cinfo; - struct dispc_clock_info dispc_cinfo; - r = dsi_pll_calc_clock_div_pck(is_tft, + r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, timings->pixel_clock * 1000, &dsi_cinfo, &dispc_cinfo); @@ -272,13 +294,8 @@ int dpi_check_timings(struct omap_dss_device *dssdev, return r; fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; - lck_div = dispc_cinfo.lck_div; - pck_div = dispc_cinfo.pck_div; - } -#else - { + } else { struct dss_clock_info dss_cinfo; - struct dispc_clock_info dispc_cinfo; r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); @@ -286,10 +303,10 @@ int dpi_check_timings(struct omap_dss_device *dssdev, return r; fck = dss_cinfo.fck; - lck_div = dispc_cinfo.lck_div; - pck_div = dispc_cinfo.pck_div; } -#endif + + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; pck = fck / lck_div / pck_div / 1000; @@ -316,6 +333,12 @@ int dpi_init_display(struct omap_dss_device *dssdev) dpi.vdds_dsi_reg = vdds_dsi; } + if (dpi_use_dsi_pll(dssdev)) { + enum omap_dss_clk_source dispc_fclk_src = + dssdev->clocks.dispc.dispc_fclk_src; + dpi.dsidev = dpi_get_dsidev(dispc_fclk_src); + } + return 0; } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 0a7f1a47f8e3..345757cfcbee 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -33,8 +33,11 @@ #include <linux/regulator/consumer.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/debugfs.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/clock.h> #include "dss.h" @@ -56,6 +59,7 @@ struct dsi_reg { u16 idx; }; #define DSI_IRQSTATUS DSI_REG(0x0018) #define DSI_IRQENABLE DSI_REG(0x001C) #define DSI_CTRL DSI_REG(0x0040) +#define DSI_GNQ DSI_REG(0x0044) #define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) #define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) #define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) @@ -90,6 +94,7 @@ struct dsi_reg { u16 idx; }; #define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) #define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) #define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) +#define DSI_DSIPHY_CFG10 DSI_REG(0x200 + 0x0028) /* DSI_PLL_CTRL_SCP */ @@ -99,11 +104,11 @@ struct dsi_reg { u16 idx; }; #define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) #define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) -#define REG_GET(idx, start, end) \ - FLD_GET(dsi_read_reg(idx), start, end) +#define REG_GET(dsidev, idx, start, end) \ + FLD_GET(dsi_read_reg(dsidev, idx), start, end) -#define REG_FLD_MOD(idx, val, start, end) \ - dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) +#define REG_FLD_MOD(dsidev, idx, val, start, end) \ + dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end)) /* Global interrupts */ #define DSI_IRQ_VC0 (1 << 0) @@ -147,31 +152,50 @@ struct dsi_reg { u16 idx; }; #define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) #define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) #define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3) +#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4) #define DSI_CIO_IRQ_ERRESC1 (1 << 5) #define DSI_CIO_IRQ_ERRESC2 (1 << 6) #define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRESC4 (1 << 8) +#define DSI_CIO_IRQ_ERRESC5 (1 << 9) #define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) #define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) #define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13) +#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14) #define DSI_CIO_IRQ_STATEULPS1 (1 << 15) #define DSI_CIO_IRQ_STATEULPS2 (1 << 16) #define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_STATEULPS4 (1 << 18) +#define DSI_CIO_IRQ_STATEULPS5 (1 << 19) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) #define DSI_CIO_IRQ_ERROR_MASK \ (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ - DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ - DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRCONTROL1 | \ - DSI_CIO_IRQ_ERRCONTROL2 | DSI_CIO_IRQ_ERRCONTROL3 | \ + DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \ + DSI_CIO_IRQ_ERRSYNCESC5 | \ + DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ + DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \ + DSI_CIO_IRQ_ERRESC5 | \ + DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \ + DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \ + DSI_CIO_IRQ_ERRCONTROL5 | \ DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3) + DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) #define DSI_DT_DCS_SHORT_WRITE_0 0x05 #define DSI_DT_DCS_SHORT_WRITE_1 0x15 @@ -208,6 +232,19 @@ enum dsi_vc_mode { DSI_VC_MODE_VP, }; +enum dsi_lane { + DSI_CLK_P = 1 << 0, + DSI_CLK_N = 1 << 1, + DSI_DATA1_P = 1 << 2, + DSI_DATA1_N = 1 << 3, + DSI_DATA2_P = 1 << 4, + DSI_DATA2_N = 1 << 5, + DSI_DATA3_P = 1 << 6, + DSI_DATA3_N = 1 << 7, + DSI_DATA4_P = 1 << 8, + DSI_DATA4_N = 1 << 9, +}; + struct dsi_update_region { u16 x, y, w, h; struct omap_dss_device *device; @@ -227,14 +264,16 @@ struct dsi_isr_tables { struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS]; }; -static struct -{ +struct dsi_data { struct platform_device *pdev; void __iomem *base; int irq; + void (*dsi_mux_pads)(bool enable); + struct dsi_clock_info current_cinfo; + bool vdds_dsi_enabled; struct regulator *vdds_dsi_reg; struct { @@ -258,8 +297,7 @@ static struct struct dsi_update_region update_region; bool te_enabled; - - struct workqueue_struct *workqueue; + bool ulps_enabled; void (*framedone_callback)(int, void *); void *framedone_data; @@ -292,21 +330,63 @@ static struct unsigned long regm_dispc_max, regm_dsi_max; unsigned long fint_min, fint_max; unsigned long lpdiv_max; -} dsi; + + int num_data_lanes; + + unsigned scp_clk_refcount; +}; + +struct dsi_packet_sent_handler_data { + struct platform_device *dsidev; + struct completion *completion; +}; + +static struct platform_device *dsi_pdev_map[MAX_NUM_DSI]; #ifdef DEBUG static unsigned int dsi_perf; module_param_named(dsi_perf, dsi_perf, bool, 0644); #endif -static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev) { - __raw_writel(val, dsi.base + idx.idx); + return dev_get_drvdata(&dsidev->dev); } -static inline u32 dsi_read_reg(const struct dsi_reg idx) +static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) { - return __raw_readl(dsi.base + idx.idx); + return dsi_pdev_map[dssdev->phy.dsi.module]; +} + +struct platform_device *dsi_get_dsidev_from_id(int module) +{ + return dsi_pdev_map[module]; +} + +static int dsi_get_dsidev_id(struct platform_device *dsidev) +{ + /* TEMP: Pass 0 as the dsi module index till the time the dsi platform + * device names aren't changed to the form "omapdss_dsi.0", + * "omapdss_dsi.1" and so on */ + BUG_ON(dsidev->id != -1); + + return 0; +} + +static inline void dsi_write_reg(struct platform_device *dsidev, + const struct dsi_reg idx, u32 val) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + __raw_writel(val, dsi->base + idx.idx); +} + +static inline u32 dsi_read_reg(struct platform_device *dsidev, + const struct dsi_reg idx) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return __raw_readl(dsi->base + idx.idx); } @@ -318,21 +398,29 @@ void dsi_restore_context(void) { } -void dsi_bus_lock(void) +void dsi_bus_lock(struct omap_dss_device *dssdev) { - down(&dsi.bus_lock); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + down(&dsi->bus_lock); } EXPORT_SYMBOL(dsi_bus_lock); -void dsi_bus_unlock(void) +void dsi_bus_unlock(struct omap_dss_device *dssdev) { - up(&dsi.bus_lock); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + up(&dsi->bus_lock); } EXPORT_SYMBOL(dsi_bus_unlock); -static bool dsi_bus_is_locked(void) +static bool dsi_bus_is_locked(struct platform_device *dsidev) { - return dsi.bus_lock.count == 0; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->bus_lock.count == 0; } static void dsi_completion_handler(void *data, u32 mask) @@ -340,12 +428,12 @@ static void dsi_completion_handler(void *data, u32 mask) complete((struct completion *)data); } -static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, - int value) +static inline int wait_for_bit_change(struct platform_device *dsidev, + const struct dsi_reg idx, int bitnum, int value) { int t = 100000; - while (REG_GET(idx, bitnum, bitnum) != value) { + while (REG_GET(dsidev, idx, bitnum, bitnum) != value) { if (--t == 0) return !value; } @@ -354,18 +442,21 @@ static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, } #ifdef DEBUG -static void dsi_perf_mark_setup(void) +static void dsi_perf_mark_setup(struct platform_device *dsidev) { - dsi.perf_setup_time = ktime_get(); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + dsi->perf_setup_time = ktime_get(); } -static void dsi_perf_mark_start(void) +static void dsi_perf_mark_start(struct platform_device *dsidev) { - dsi.perf_start_time = ktime_get(); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + dsi->perf_start_time = ktime_get(); } -static void dsi_perf_show(const char *name) +static void dsi_perf_show(struct platform_device *dsidev, const char *name) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); ktime_t t, setup_time, trans_time; u32 total_bytes; u32 setup_us, trans_us, total_us; @@ -375,21 +466,21 @@ static void dsi_perf_show(const char *name) t = ktime_get(); - setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); + setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time); setup_us = (u32)ktime_to_us(setup_time); if (setup_us == 0) setup_us = 1; - trans_time = ktime_sub(t, dsi.perf_start_time); + trans_time = ktime_sub(t, dsi->perf_start_time); trans_us = (u32)ktime_to_us(trans_time); if (trans_us == 0) trans_us = 1; total_us = setup_us + trans_us; - total_bytes = dsi.update_region.w * - dsi.update_region.h * - dsi.update_region.device->ctrl.pixel_size / 8; + total_bytes = dsi->update_region.w * + dsi->update_region.h * + dsi->update_region.device->ctrl.pixel_size / 8; printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " "%u bytes, %u kbytes/sec\n", @@ -402,9 +493,9 @@ static void dsi_perf_show(const char *name) total_bytes * 1000 / total_us); } #else -#define dsi_perf_mark_setup() -#define dsi_perf_mark_start() -#define dsi_perf_show(x) +#define dsi_perf_mark_setup(x) +#define dsi_perf_mark_start(x) +#define dsi_perf_show(x, y) #endif static void print_irq_status(u32 status) @@ -510,38 +601,42 @@ static void print_irq_status_cio(u32 status) } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -static void dsi_collect_irq_stats(u32 irqstatus, u32 *vcstatus, u32 ciostatus) +static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; - spin_lock(&dsi.irq_stats_lock); + spin_lock(&dsi->irq_stats_lock); - dsi.irq_stats.irq_count++; - dss_collect_irq_stats(irqstatus, dsi.irq_stats.dsi_irqs); + dsi->irq_stats.irq_count++; + dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs); for (i = 0; i < 4; ++i) - dss_collect_irq_stats(vcstatus[i], dsi.irq_stats.vc_irqs[i]); + dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]); - dss_collect_irq_stats(ciostatus, dsi.irq_stats.cio_irqs); + dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs); - spin_unlock(&dsi.irq_stats_lock); + spin_unlock(&dsi->irq_stats_lock); } #else -#define dsi_collect_irq_stats(irqstatus, vcstatus, ciostatus) +#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus) #endif static int debug_irq; -static void dsi_handle_irq_errors(u32 irqstatus, u32 *vcstatus, u32 ciostatus) +static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; if (irqstatus & DSI_IRQ_ERROR_MASK) { DSSERR("DSI error, irqstatus %x\n", irqstatus); print_irq_status(irqstatus); - spin_lock(&dsi.errors_lock); - dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; - spin_unlock(&dsi.errors_lock); + spin_lock(&dsi->errors_lock); + dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK; + spin_unlock(&dsi->errors_lock); } else if (debug_irq) { print_irq_status(irqstatus); } @@ -602,22 +697,27 @@ static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables, static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) { + struct platform_device *dsidev; + struct dsi_data *dsi; u32 irqstatus, vcstatus[4], ciostatus; int i; - spin_lock(&dsi.irq_lock); + dsidev = (struct platform_device *) arg; + dsi = dsi_get_dsidrv_data(dsidev); + + spin_lock(&dsi->irq_lock); - irqstatus = dsi_read_reg(DSI_IRQSTATUS); + irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); /* IRQ is not for us */ if (!irqstatus) { - spin_unlock(&dsi.irq_lock); + spin_unlock(&dsi->irq_lock); return IRQ_NONE; } - dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); + dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); /* flush posted write */ - dsi_read_reg(DSI_IRQSTATUS); + dsi_read_reg(dsidev, DSI_IRQSTATUS); for (i = 0; i < 4; ++i) { if ((irqstatus & (1 << i)) == 0) { @@ -625,45 +725,47 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) continue; } - vcstatus[i] = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); - dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus[i]); + dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]); /* flush posted write */ - dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); } if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { - ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); - dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); /* flush posted write */ - dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); } else { ciostatus = 0; } #ifdef DSI_CATCH_MISSING_TE if (irqstatus & DSI_IRQ_TE_TRIGGER) - del_timer(&dsi.te_timer); + del_timer(&dsi->te_timer); #endif /* make a copy and unlock, so that isrs can unregister * themselves */ - memcpy(&dsi.isr_tables_copy, &dsi.isr_tables, sizeof(dsi.isr_tables)); + memcpy(&dsi->isr_tables_copy, &dsi->isr_tables, + sizeof(dsi->isr_tables)); - spin_unlock(&dsi.irq_lock); + spin_unlock(&dsi->irq_lock); - dsi_handle_isrs(&dsi.isr_tables_copy, irqstatus, vcstatus, ciostatus); + dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); - dsi_handle_irq_errors(irqstatus, vcstatus, ciostatus); + dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus); - dsi_collect_irq_stats(irqstatus, vcstatus, ciostatus); + dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus); return IRQ_HANDLED; } -/* dsi.irq_lock has to be locked by the caller */ -static void _omap_dsi_configure_irqs(struct dsi_isr_data *isr_array, +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_configure_irqs(struct platform_device *dsidev, + struct dsi_isr_data *isr_array, unsigned isr_array_size, u32 default_mask, const struct dsi_reg enable_reg, const struct dsi_reg status_reg) @@ -684,61 +786,67 @@ static void _omap_dsi_configure_irqs(struct dsi_isr_data *isr_array, mask |= isr_data->mask; } - old_mask = dsi_read_reg(enable_reg); + old_mask = dsi_read_reg(dsidev, enable_reg); /* clear the irqstatus for newly enabled irqs */ - dsi_write_reg(status_reg, (mask ^ old_mask) & mask); - dsi_write_reg(enable_reg, mask); + dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask); + dsi_write_reg(dsidev, enable_reg, mask); /* flush posted writes */ - dsi_read_reg(enable_reg); - dsi_read_reg(status_reg); + dsi_read_reg(dsidev, enable_reg); + dsi_read_reg(dsidev, status_reg); } -/* dsi.irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs(void) +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs(struct platform_device *dsidev) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 mask = DSI_IRQ_ERROR_MASK; #ifdef DSI_CATCH_MISSING_TE mask |= DSI_IRQ_TE_TRIGGER; #endif - _omap_dsi_configure_irqs(dsi.isr_tables.isr_table, - ARRAY_SIZE(dsi.isr_tables.isr_table), mask, + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table), mask, DSI_IRQENABLE, DSI_IRQSTATUS); } -/* dsi.irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs_vc(int vc) +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc) { - _omap_dsi_configure_irqs(dsi.isr_tables.isr_table_vc[vc], - ARRAY_SIZE(dsi.isr_tables.isr_table_vc[vc]), + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]), DSI_VC_IRQ_ERROR_MASK, DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc)); } -/* dsi.irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs_cio(void) +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev) { - _omap_dsi_configure_irqs(dsi.isr_tables.isr_table_cio, - ARRAY_SIZE(dsi.isr_tables.isr_table_cio), + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio), DSI_CIO_IRQ_ERROR_MASK, DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS); } -static void _dsi_initialize_irq(void) +static void _dsi_initialize_irq(struct platform_device *dsidev) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int vc; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); - memset(&dsi.isr_tables, 0, sizeof(dsi.isr_tables)); + memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables)); - _omap_dsi_set_irqs(); + _omap_dsi_set_irqs(dsidev); for (vc = 0; vc < 4; ++vc) - _omap_dsi_set_irqs_vc(vc); - _omap_dsi_set_irqs_cio(); + _omap_dsi_set_irqs_vc(dsidev, vc); + _omap_dsi_set_irqs_cio(dsidev); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); } static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask, @@ -797,126 +905,137 @@ static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask, return -EINVAL; } -static int dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr, + void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); - r = _dsi_register_isr(isr, arg, mask, dsi.isr_tables.isr_table, - ARRAY_SIZE(dsi.isr_tables.isr_table)); + r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table)); if (r == 0) - _omap_dsi_set_irqs(); + _omap_dsi_set_irqs(dsidev); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_unregister_isr(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); - r = _dsi_unregister_isr(isr, arg, mask, dsi.isr_tables.isr_table, - ARRAY_SIZE(dsi.isr_tables.isr_table)); + r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table)); if (r == 0) - _omap_dsi_set_irqs(); + _omap_dsi_set_irqs(dsidev); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_register_isr_vc(int channel, omap_dsi_isr_t isr, void *arg, - u32 mask) +static int dsi_register_isr_vc(struct platform_device *dsidev, int channel, + omap_dsi_isr_t isr, void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); r = _dsi_register_isr(isr, arg, mask, - dsi.isr_tables.isr_table_vc[channel], - ARRAY_SIZE(dsi.isr_tables.isr_table_vc[channel])); + dsi->isr_tables.isr_table_vc[channel], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); if (r == 0) - _omap_dsi_set_irqs_vc(channel); + _omap_dsi_set_irqs_vc(dsidev, channel); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr_vc(int channel, omap_dsi_isr_t isr, void *arg, - u32 mask) +static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel, + omap_dsi_isr_t isr, void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); r = _dsi_unregister_isr(isr, arg, mask, - dsi.isr_tables.isr_table_vc[channel], - ARRAY_SIZE(dsi.isr_tables.isr_table_vc[channel])); + dsi->isr_tables.isr_table_vc[channel], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); if (r == 0) - _omap_dsi_set_irqs_vc(channel); + _omap_dsi_set_irqs_vc(dsidev, channel); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_register_isr_cio(omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_register_isr_cio(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); - r = _dsi_register_isr(isr, arg, mask, dsi.isr_tables.isr_table_cio, - ARRAY_SIZE(dsi.isr_tables.isr_table_cio)); + r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); if (r == 0) - _omap_dsi_set_irqs_cio(); + _omap_dsi_set_irqs_cio(dsidev); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr_cio(omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_unregister_isr_cio(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; - spin_lock_irqsave(&dsi.irq_lock, flags); + spin_lock_irqsave(&dsi->irq_lock, flags); - r = _dsi_unregister_isr(isr, arg, mask, dsi.isr_tables.isr_table_cio, - ARRAY_SIZE(dsi.isr_tables.isr_table_cio)); + r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); if (r == 0) - _omap_dsi_set_irqs_cio(); + _omap_dsi_set_irqs_cio(dsidev); - spin_unlock_irqrestore(&dsi.irq_lock, flags); + spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static u32 dsi_get_errors(void) +static u32 dsi_get_errors(struct platform_device *dsidev) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; u32 e; - spin_lock_irqsave(&dsi.errors_lock, flags); - e = dsi.errors; - dsi.errors = 0; - spin_unlock_irqrestore(&dsi.errors_lock, flags); + spin_lock_irqsave(&dsi->errors_lock, flags); + e = dsi->errors; + dsi->errors = 0; + spin_unlock_irqrestore(&dsi->errors_lock, flags); return e; } @@ -930,23 +1049,27 @@ static inline void enable_clocks(bool enable) } /* source clock for DSI PLL. this could also be PCLKFREE */ -static inline void dsi_enable_pll_clock(bool enable) +static inline void dsi_enable_pll_clock(struct platform_device *dsidev, + bool enable) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + if (enable) dss_clk_enable(DSS_CLK_SYSCK); else dss_clk_disable(DSS_CLK_SYSCK); - if (enable && dsi.pll_locked) { - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) + if (enable && dsi->pll_locked) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) DSSERR("cannot lock PLL when enabling clocks\n"); } } #ifdef DEBUG -static void _dsi_print_reset_status(void) +static void _dsi_print_reset_status(struct platform_device *dsidev) { u32 l; + int b0, b1, b2; if (!dss_debug) return; @@ -954,35 +1077,47 @@ static void _dsi_print_reset_status(void) /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - l = dsi_read_reg(DSI_DSIPHY_CFG5); + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); printk(KERN_DEBUG "DSI resets: "); - l = dsi_read_reg(DSI_PLL_STATUS); + l = dsi_read_reg(dsidev, DSI_PLL_STATUS); printk("PLL (%d) ", FLD_GET(l, 0, 0)); - l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + l = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); printk("CIO (%d) ", FLD_GET(l, 29, 29)); - l = dsi_read_reg(DSI_DSIPHY_CFG5); - printk("PHY (%x, %d, %d, %d)\n", - FLD_GET(l, 28, 26), + if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { + b0 = 28; + b1 = 27; + b2 = 26; + } else { + b0 = 24; + b1 = 25; + b2 = 26; + } + + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + printk("PHY (%x%x%x, %d, %d, %d)\n", + FLD_GET(l, b0, b0), + FLD_GET(l, b1, b1), + FLD_GET(l, b2, b2), FLD_GET(l, 29, 29), FLD_GET(l, 30, 30), FLD_GET(l, 31, 31)); } #else -#define _dsi_print_reset_status() +#define _dsi_print_reset_status(x) #endif -static inline int dsi_if_enable(bool enable) +static inline int dsi_if_enable(struct platform_device *dsidev, bool enable) { DSSDBG("dsi_if_enable(%d)\n", enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */ - if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) { DSSERR("Failed to set dsi_if_enable to %d\n", enable); return -EIO; } @@ -990,31 +1125,38 @@ static inline int dsi_if_enable(bool enable) return 0; } -unsigned long dsi_get_pll_hsdiv_dispc_rate(void) +unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) { - return dsi.current_cinfo.dsi_pll_hsdiv_dispc_clk; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk; } -static unsigned long dsi_get_pll_hsdiv_dsi_rate(void) +static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev) { - return dsi.current_cinfo.dsi_pll_hsdiv_dsi_clk; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk; } -static unsigned long dsi_get_txbyteclkhs(void) +static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) { - return dsi.current_cinfo.clkin4ddr / 16; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.clkin4ddr / 16; } -static unsigned long dsi_fclk_rate(void) +static unsigned long dsi_fclk_rate(struct platform_device *dsidev) { unsigned long r; + int dsi_module = dsi_get_dsidev_id(dsidev); - if (dss_get_dsi_clk_source() == DSS_CLK_SRC_FCK) { + if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) { /* DSI FCLK source is DSS_CLK_FCK */ r = dss_clk_get_rate(DSS_CLK_FCK); } else { /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */ - r = dsi_get_pll_hsdiv_dsi_rate(); + r = dsi_get_pll_hsdiv_dsi_rate(dsidev); } return r; @@ -1022,31 +1164,50 @@ static unsigned long dsi_fclk_rate(void) static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long dsi_fclk; unsigned lp_clk_div; unsigned long lp_clk; - lp_clk_div = dssdev->phy.dsi.div.lp_clk_div; + lp_clk_div = dssdev->clocks.dsi.lp_clk_div; - if (lp_clk_div == 0 || lp_clk_div > dsi.lpdiv_max) + if (lp_clk_div == 0 || lp_clk_div > dsi->lpdiv_max) return -EINVAL; - dsi_fclk = dsi_fclk_rate(); + dsi_fclk = dsi_fclk_rate(dsidev); lp_clk = dsi_fclk / 2 / lp_clk_div; DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); - dsi.current_cinfo.lp_clk = lp_clk; - dsi.current_cinfo.lp_clk_div = lp_clk_div; + dsi->current_cinfo.lp_clk = lp_clk; + dsi->current_cinfo.lp_clk_div = lp_clk_div; - REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ + /* LP_CLK_DIVISOR */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0); - REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, - 21, 21); /* LP_RX_SYNCHRO_ENABLE */ + /* LP_RX_SYNCHRO_ENABLE */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); return 0; } +static void dsi_enable_scp_clk(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->scp_clk_refcount++ == 0) + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */ +} + +static void dsi_disable_scp_clk(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + WARN_ON(dsi->scp_clk_refcount == 0); + if (--dsi->scp_clk_refcount == 0) + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */ +} enum dsi_pll_power_state { DSI_PLL_POWER_OFF = 0x0, @@ -1055,14 +1216,21 @@ enum dsi_pll_power_state { DSI_PLL_POWER_ON_DIV = 0x3, }; -static int dsi_pll_power(enum dsi_pll_power_state state) +static int dsi_pll_power(struct platform_device *dsidev, + enum dsi_pll_power_state state) { int t = 0; - REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + /* DSI-PLL power command 0x3 is not working */ + if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) && + state == DSI_PLL_POWER_ON_DIV) + state = DSI_PLL_POWER_ON_ALL; + + /* PLL_PWR_CMD */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) { if (++t > 1000) { DSSERR("Failed to set DSI PLL power mode to %d\n", state); @@ -1078,16 +1246,19 @@ static int dsi_pll_power(enum dsi_pll_power_state state) static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, struct dsi_clock_info *cinfo) { - if (cinfo->regn == 0 || cinfo->regn > dsi.regn_max) + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) return -EINVAL; - if (cinfo->regm == 0 || cinfo->regm > dsi.regm_max) + if (cinfo->regm == 0 || cinfo->regm > dsi->regm_max) return -EINVAL; - if (cinfo->regm_dispc > dsi.regm_dispc_max) + if (cinfo->regm_dispc > dsi->regm_dispc_max) return -EINVAL; - if (cinfo->regm_dsi > dsi.regm_dsi_max) + if (cinfo->regm_dsi > dsi->regm_dsi_max) return -EINVAL; if (cinfo->use_sys_clk) { @@ -1106,7 +1277,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); - if (cinfo->fint > dsi.fint_max || cinfo->fint < dsi.fint_min) + if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) return -EINVAL; cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint; @@ -1129,10 +1300,11 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, return 0; } -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, - struct dsi_clock_info *dsi_cinfo, +int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, + unsigned long req_pck, struct dsi_clock_info *dsi_cinfo, struct dispc_clock_info *dispc_cinfo) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dsi_clock_info cur, best; struct dispc_clock_info best_dispc; int min_fck_per_pck; @@ -1143,10 +1315,10 @@ int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); - if (req_pck == dsi.cache_req_pck && - dsi.cache_cinfo.clkin == dss_sys_clk) { + if (req_pck == dsi->cache_req_pck && + dsi->cache_cinfo.clkin == dss_sys_clk) { DSSDBG("DSI clock info found from cache\n"); - *dsi_cinfo = dsi.cache_cinfo; + *dsi_cinfo = dsi->cache_cinfo; dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo); return 0; @@ -1176,17 +1348,17 @@ retry: /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ - for (cur.regn = 1; cur.regn < dsi.regn_max; ++cur.regn) { + for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) { if (cur.highfreq == 0) cur.fint = cur.clkin / cur.regn; else cur.fint = cur.clkin / (2 * cur.regn); - if (cur.fint > dsi.fint_max || cur.fint < dsi.fint_min) + if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min) continue; /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ - for (cur.regm = 1; cur.regm < dsi.regm_max; ++cur.regm) { + for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) { unsigned long a, b; a = 2 * cur.regm * (cur.clkin/1000); @@ -1198,8 +1370,8 @@ retry: /* dsi_pll_hsdiv_dispc_clk(MHz) = * DSIPHY(MHz) / regm_dispc < 173MHz/186Mhz */ - for (cur.regm_dispc = 1; cur.regm_dispc < dsi.regm_dispc_max; - ++cur.regm_dispc) { + for (cur.regm_dispc = 1; cur.regm_dispc < + dsi->regm_dispc_max; ++cur.regm_dispc) { struct dispc_clock_info cur_dispc; cur.dsi_pll_hsdiv_dispc_clk = cur.clkin4ddr / cur.regm_dispc; @@ -1259,34 +1431,39 @@ found: if (dispc_cinfo) *dispc_cinfo = best_dispc; - dsi.cache_req_pck = req_pck; - dsi.cache_clk_freq = 0; - dsi.cache_cinfo = best; + dsi->cache_req_pck = req_pck; + dsi->cache_clk_freq = 0; + dsi->cache_cinfo = best; return 0; } -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) +int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; u32 l; - int f; + int f = 0; u8 regn_start, regn_end, regm_start, regm_end; u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end; DSSDBGF(); - dsi.current_cinfo.fint = cinfo->fint; - dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr; - dsi.current_cinfo.dsi_pll_hsdiv_dispc_clk = + dsi->current_cinfo.use_sys_clk = cinfo->use_sys_clk; + dsi->current_cinfo.highfreq = cinfo->highfreq; + + dsi->current_cinfo.fint = cinfo->fint; + dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; + dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = cinfo->dsi_pll_hsdiv_dispc_clk; - dsi.current_cinfo.dsi_pll_hsdiv_dsi_clk = + dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk = cinfo->dsi_pll_hsdiv_dsi_clk; - dsi.current_cinfo.regn = cinfo->regn; - dsi.current_cinfo.regm = cinfo->regm; - dsi.current_cinfo.regm_dispc = cinfo->regm_dispc; - dsi.current_cinfo.regm_dsi = cinfo->regm_dsi; + dsi->current_cinfo.regn = cinfo->regn; + dsi->current_cinfo.regm = cinfo->regm; + dsi->current_cinfo.regm_dispc = cinfo->regm_dispc; + dsi->current_cinfo.regm_dsi = cinfo->regm_dsi; DSSDBG("DSI Fint %ld\n", cinfo->fint); @@ -1309,12 +1486,12 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4); DSSDBG("regm_dispc = %d, %s (%s) = %lu\n", cinfo->regm_dispc, - dss_get_generic_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), - dss_feat_get_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), cinfo->dsi_pll_hsdiv_dispc_clk); DSSDBG("regm_dsi = %d, %s (%s) = %lu\n", cinfo->regm_dsi, - dss_get_generic_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), - dss_feat_get_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), cinfo->dsi_pll_hsdiv_dsi_clk); dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGN, ®n_start, ®n_end); @@ -1324,9 +1501,10 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DSI, ®m_dsi_start, ®m_dsi_end); - REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ + /* DSI_PLL_AUTOMODE = manual */ + REG_FLD_MOD(dsidev, DSI_PLL_CONTROL, 0, 0, 0); - l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION1); l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ /* DSI_PLL_REGN */ l = FLD_MOD(l, cinfo->regn - 1, regn_start, regn_end); @@ -1338,22 +1516,22 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) /* DSIPROTO_CLOCK_DIV */ l = FLD_MOD(l, cinfo->regm_dsi > 0 ? cinfo->regm_dsi - 1 : 0, regm_dsi_start, regm_dsi_end); - dsi_write_reg(DSI_PLL_CONFIGURATION1, l); - - BUG_ON(cinfo->fint < dsi.fint_min || cinfo->fint > dsi.fint_max); - if (cinfo->fint < 1000000) - f = 0x3; - else if (cinfo->fint < 1250000) - f = 0x4; - else if (cinfo->fint < 1500000) - f = 0x5; - else if (cinfo->fint < 1750000) - f = 0x6; - else - f = 0x7; + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION1, l); + + BUG_ON(cinfo->fint < dsi->fint_min || cinfo->fint > dsi->fint_max); + + if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) { + f = cinfo->fint < 1000000 ? 0x3 : + cinfo->fint < 1250000 ? 0x4 : + cinfo->fint < 1500000 ? 0x5 : + cinfo->fint < 1750000 ? 0x6 : + 0x7; + } + + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); - l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ + if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) + l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ l = FLD_MOD(l, cinfo->use_sys_clk ? 0 : 1, 11, 11); /* DSI_PLL_CLKSEL */ l = FLD_MOD(l, cinfo->highfreq, @@ -1361,25 +1539,25 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); - REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ - if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_PLL_GO, 0, 0) != 0) { DSSERR("dsi pll go bit not going down.\n"); r = -EIO; goto err; } - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) { DSSERR("cannot lock PLL\n"); r = -EIO; goto err; } - dsi.pll_locked = 1; + dsi->pll_locked = 1; - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ @@ -1394,52 +1572,53 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); DSSDBG("PLL config done\n"); err: return r; } -int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, +int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; enum dsi_pll_power_state pwstate; DSSDBG("PLL init\n"); -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - /* - * HACK: this is just a quick hack to get the USE_DSI_PLL - * option working. USE_DSI_PLL is itself a big hack, and - * should be removed. - */ - if (dsi.vdds_dsi_reg == NULL) { + if (dsi->vdds_dsi_reg == NULL) { struct regulator *vdds_dsi; - vdds_dsi = regulator_get(&dsi.pdev->dev, "vdds_dsi"); + vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); if (IS_ERR(vdds_dsi)) { DSSERR("can't get VDDS_DSI regulator\n"); return PTR_ERR(vdds_dsi); } - dsi.vdds_dsi_reg = vdds_dsi; + dsi->vdds_dsi_reg = vdds_dsi; } -#endif enable_clocks(1); - dsi_enable_pll_clock(1); + dsi_enable_pll_clock(dsidev, 1); + /* + * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. + */ + dsi_enable_scp_clk(dsidev); - r = regulator_enable(dsi.vdds_dsi_reg); - if (r) - goto err0; + if (!dsi->vdds_dsi_enabled) { + r = regulator_enable(dsi->vdds_dsi_reg); + if (r) + goto err0; + dsi->vdds_dsi_enabled = true; + } /* XXX PLL does not come out of reset without this... */ dispc_pck_free_enable(1); - if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) { DSSERR("PLL not coming out of reset.\n"); r = -ENODEV; dispc_pck_free_enable(0); @@ -1459,7 +1638,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, else pwstate = DSI_PLL_POWER_OFF; - r = dsi_pll_power(pwstate); + r = dsi_pll_power(dsidev, pwstate); if (r) goto err1; @@ -1468,42 +1647,53 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, return 0; err1: - regulator_disable(dsi.vdds_dsi_reg); + if (dsi->vdds_dsi_enabled) { + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } err0: + dsi_disable_scp_clk(dsidev); enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(dsidev, 0); return r; } -void dsi_pll_uninit(void) +void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->pll_locked = 0; + dsi_pll_power(dsidev, DSI_PLL_POWER_OFF); + if (disconnect_lanes) { + WARN_ON(!dsi->vdds_dsi_enabled); + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } + + dsi_disable_scp_clk(dsidev); enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(dsidev, 0); - dsi.pll_locked = 0; - dsi_pll_power(DSI_PLL_POWER_OFF); - regulator_disable(dsi.vdds_dsi_reg); DSSDBG("PLL uninit done\n"); } -void dsi_dump_clocks(struct seq_file *s) +static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, + struct seq_file *s) { - int clksel; - struct dsi_clock_info *cinfo = &dsi.current_cinfo; - enum dss_clk_source dispc_clk_src, dsi_clk_src; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_clock_info *cinfo = &dsi->current_cinfo; + enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; + int dsi_module = dsi_get_dsidev_id(dsidev); dispc_clk_src = dss_get_dispc_clk_source(); - dsi_clk_src = dss_get_dsi_clk_source(); + dsi_clk_src = dss_get_dsi_clk_source(dsi_module); enable_clocks(1); - clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); - - seq_printf(s, "- DSI PLL -\n"); + seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); seq_printf(s, "dsi pll source = %s\n", - clksel == 0 ? - "dss_sys_clk" : "pclkfree"); + cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree"); seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); @@ -1515,7 +1705,7 @@ void dsi_dump_clocks(struct seq_file *s) dss_feat_get_clk_source_name(dispc_clk_src), cinfo->dsi_pll_hsdiv_dispc_clk, cinfo->regm_dispc, - dispc_clk_src == DSS_CLK_SRC_FCK ? + dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); seq_printf(s, "%s (%s)\t%-16luregm_dsi %u\t(%s)\n", @@ -1523,45 +1713,55 @@ void dsi_dump_clocks(struct seq_file *s) dss_feat_get_clk_source_name(dsi_clk_src), cinfo->dsi_pll_hsdiv_dsi_clk, cinfo->regm_dsi, - dsi_clk_src == DSS_CLK_SRC_FCK ? + dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); - seq_printf(s, "- DSI -\n"); + seq_printf(s, "- DSI%d -\n", dsi_module + 1); seq_printf(s, "dsi fclk source = %s (%s)\n", dss_get_generic_clk_source_name(dsi_clk_src), dss_feat_get_clk_source_name(dsi_clk_src)); - seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate()); + seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev)); seq_printf(s, "DDR_CLK\t\t%lu\n", cinfo->clkin4ddr / 4); - seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs()); + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev)); seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); - seq_printf(s, "VP_CLK\t\t%lu\n" - "VP_PCLK\t\t%lu\n", - dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), - dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD)); - enable_clocks(0); } +void dsi_dump_clocks(struct seq_file *s) +{ + struct platform_device *dsidev; + int i; + + for (i = 0; i < MAX_NUM_DSI; i++) { + dsidev = dsi_get_dsidev_from_id(i); + if (dsidev) + dsi_dump_dsidev_clocks(dsidev, s); + } +} + #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -void dsi_dump_irqs(struct seq_file *s) +static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, + struct seq_file *s) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; struct dsi_irq_stats stats; + int dsi_module = dsi_get_dsidev_id(dsidev); - spin_lock_irqsave(&dsi.irq_stats_lock, flags); + spin_lock_irqsave(&dsi->irq_stats_lock, flags); - stats = dsi.irq_stats; - memset(&dsi.irq_stats, 0, sizeof(dsi.irq_stats)); - dsi.irq_stats.last_reset = jiffies; + stats = dsi->irq_stats; + memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats)); + dsi->irq_stats.last_reset = jiffies; - spin_unlock_irqrestore(&dsi.irq_stats_lock, flags); + spin_unlock_irqrestore(&dsi->irq_stats_lock, flags); seq_printf(s, "period %u ms\n", jiffies_to_msecs(jiffies - stats.last_reset)); @@ -1570,7 +1770,7 @@ void dsi_dump_irqs(struct seq_file *s) #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); - seq_printf(s, "-- DSI interrupts --\n"); + seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1); PIS(VC0); PIS(VC1); PIS(VC2); @@ -1636,13 +1836,45 @@ void dsi_dump_irqs(struct seq_file *s) PIS(ULPSACTIVENOT_ALL1); #undef PIS } + +static void dsi1_dump_irqs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(0); + + dsi_dump_dsidev_irqs(dsidev, s); +} + +static void dsi2_dump_irqs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(1); + + dsi_dump_dsidev_irqs(dsidev, s); +} + +void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, + const struct file_operations *debug_fops) +{ + struct platform_device *dsidev; + + dsidev = dsi_get_dsidev_from_id(0); + if (dsidev) + debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir, + &dsi1_dump_irqs, debug_fops); + + dsidev = dsi_get_dsidev_from_id(1); + if (dsidev) + debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir, + &dsi2_dump_irqs, debug_fops); +} #endif -void dsi_dump_regs(struct seq_file *s) +static void dsi_dump_dsidev_regs(struct platform_device *dsidev, + struct seq_file *s) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r)) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); + dsi_enable_scp_clk(dsidev); DUMPREG(DSI_REVISION); DUMPREG(DSI_SYSCONFIG); @@ -1714,25 +1946,57 @@ void dsi_dump_regs(struct seq_file *s) DUMPREG(DSI_PLL_CONFIGURATION1); DUMPREG(DSI_PLL_CONFIGURATION2); + dsi_disable_scp_clk(dsidev); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); #undef DUMPREG } -enum dsi_complexio_power_state { +static void dsi1_dump_regs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(0); + + dsi_dump_dsidev_regs(dsidev, s); +} + +static void dsi2_dump_regs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(1); + + dsi_dump_dsidev_regs(dsidev, s); +} + +void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, + const struct file_operations *debug_fops) +{ + struct platform_device *dsidev; + + dsidev = dsi_get_dsidev_from_id(0); + if (dsidev) + debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir, + &dsi1_dump_regs, debug_fops); + + dsidev = dsi_get_dsidev_from_id(1); + if (dsidev) + debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir, + &dsi2_dump_regs, debug_fops); +} +enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_OFF = 0x0, DSI_COMPLEXIO_POWER_ON = 0x1, DSI_COMPLEXIO_POWER_ULPS = 0x2, }; -static int dsi_complexio_power(enum dsi_complexio_power_state state) +static int dsi_cio_power(struct platform_device *dsidev, + enum dsi_cio_power_state state) { int t = 0; /* PWR_CMD */ - REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27); /* PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1), + 26, 25) != state) { if (++t > 1000) { DSSERR("failed to set complexio power state to " "%d\n", state); @@ -1744,9 +2008,70 @@ static int dsi_complexio_power(enum dsi_complexio_power_state state) return 0; } -static void dsi_complexio_config(struct omap_dss_device *dssdev) +/* Number of data lanes present on DSI interface */ +static inline int dsi_get_num_data_lanes(struct platform_device *dsidev) { + /* DSI on OMAP3 doesn't have register DSI_GNQ, set number + * of data lanes as 2 by default */ + if (dss_has_feature(FEAT_DSI_GNQ)) + return REG_GET(dsidev, DSI_GNQ, 11, 9); /* NB_DATA_LANES */ + else + return 2; +} + +/* Number of data lanes used by the dss device */ +static inline int dsi_get_num_data_lanes_dssdev(struct omap_dss_device *dssdev) +{ + int num_data_lanes = 0; + + if (dssdev->phy.dsi.data1_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data2_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data3_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data4_lane != 0) + num_data_lanes++; + + return num_data_lanes; +} + +static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) +{ + int val; + + /* line buffer on OMAP3 is 1024 x 24bits */ + /* XXX: for some reason using full buffer size causes + * considerable TX slowdown with update sizes that fill the + * whole buffer */ + if (!dss_has_feature(FEAT_DSI_GNQ)) + return 1023 * 3; + + val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */ + + switch (val) { + case 1: + return 512 * 3; /* 512x24 bits */ + case 2: + return 682 * 3; /* 682x24 bits */ + case 3: + return 853 * 3; /* 853x24 bits */ + case 4: + return 1024 * 3; /* 1024x24 bits */ + case 5: + return 1194 * 3; /* 1194x24 bits */ + case 6: + return 1365 * 3; /* 1365x24 bits */ + default: + BUG(); + } +} + +static void dsi_set_lane_config(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u32 r; + int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev); int clk_lane = dssdev->phy.dsi.clk_lane; int data1_lane = dssdev->phy.dsi.data1_lane; @@ -1755,14 +2080,28 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) int data1_pol = dssdev->phy.dsi.data1_pol; int data2_pol = dssdev->phy.dsi.data2_pol; - r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); r = FLD_MOD(r, clk_lane, 2, 0); r = FLD_MOD(r, clk_pol, 3, 3); r = FLD_MOD(r, data1_lane, 6, 4); r = FLD_MOD(r, data1_pol, 7, 7); r = FLD_MOD(r, data2_lane, 10, 8); r = FLD_MOD(r, data2_pol, 11, 11); - dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + if (num_data_lanes_dssdev > 2) { + int data3_lane = dssdev->phy.dsi.data3_lane; + int data3_pol = dssdev->phy.dsi.data3_pol; + + r = FLD_MOD(r, data3_lane, 14, 12); + r = FLD_MOD(r, data3_pol, 15, 15); + } + if (num_data_lanes_dssdev > 3) { + int data4_lane = dssdev->phy.dsi.data4_lane; + int data4_pol = dssdev->phy.dsi.data4_pol; + + r = FLD_MOD(r, data4_lane, 18, 16); + r = FLD_MOD(r, data4_pol, 19, 19); + } + dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r); /* The configuration of the DSI complex I/O (number of data lanes, position, differential order) should not be changed while @@ -1776,27 +2115,31 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) DSI complex I/O configuration is unknown. */ /* - REG_FLD_MOD(DSI_CTRL, 1, 0, 0); - REG_FLD_MOD(DSI_CTRL, 0, 0, 0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); - REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(dsidev, DSI_CTRL, 0, 0, 0); + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); + REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0); */ } -static inline unsigned ns2ddr(unsigned ns) +static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + /* convert time in ns to ddr ticks, rounding up */ - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; } -static inline unsigned ddr2ns(unsigned ddr) +static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr) { - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; return ddr * 1000 * 1000 / (ddr_clk / 1000); } -static void dsi_complexio_timings(void) +static void dsi_cio_timings(struct platform_device *dsidev) { u32 r; u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; @@ -1808,139 +2151,323 @@ static void dsi_complexio_timings(void) /* 1 * DDR_CLK = 2 * UI */ /* min 40ns + 4*UI max 85ns + 6*UI */ - ths_prepare = ns2ddr(70) + 2; + ths_prepare = ns2ddr(dsidev, 70) + 2; /* min 145ns + 10*UI */ - ths_prepare_ths_zero = ns2ddr(175) + 2; + ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2; /* min max(8*UI, 60ns+4*UI) */ - ths_trail = ns2ddr(60) + 5; + ths_trail = ns2ddr(dsidev, 60) + 5; /* min 100ns */ - ths_exit = ns2ddr(145); + ths_exit = ns2ddr(dsidev, 145); /* tlpx min 50n */ - tlpx_half = ns2ddr(25); + tlpx_half = ns2ddr(dsidev, 25); /* min 60ns */ - tclk_trail = ns2ddr(60) + 2; + tclk_trail = ns2ddr(dsidev, 60) + 2; /* min 38ns, max 95ns */ - tclk_prepare = ns2ddr(65); + tclk_prepare = ns2ddr(dsidev, 65); /* min tclk-prepare + tclk-zero = 300ns */ - tclk_zero = ns2ddr(260); + tclk_zero = ns2ddr(dsidev, 260); DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", - ths_prepare, ddr2ns(ths_prepare), - ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); + ths_prepare, ddr2ns(dsidev, ths_prepare), + ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero)); DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", - ths_trail, ddr2ns(ths_trail), - ths_exit, ddr2ns(ths_exit)); + ths_trail, ddr2ns(dsidev, ths_trail), + ths_exit, ddr2ns(dsidev, ths_exit)); DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " "tclk_zero %u (%uns)\n", - tlpx_half, ddr2ns(tlpx_half), - tclk_trail, ddr2ns(tclk_trail), - tclk_zero, ddr2ns(tclk_zero)); + tlpx_half, ddr2ns(dsidev, tlpx_half), + tclk_trail, ddr2ns(dsidev, tclk_trail), + tclk_zero, ddr2ns(dsidev, tclk_zero)); DSSDBG("tclk_prepare %u (%uns)\n", - tclk_prepare, ddr2ns(tclk_prepare)); + tclk_prepare, ddr2ns(dsidev, tclk_prepare)); /* program timings */ - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); r = FLD_MOD(r, ths_prepare, 31, 24); r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); r = FLD_MOD(r, ths_trail, 15, 8); r = FLD_MOD(r, ths_exit, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG0, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); r = FLD_MOD(r, tlpx_half, 22, 16); r = FLD_MOD(r, tclk_trail, 15, 8); r = FLD_MOD(r, tclk_zero, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG1, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); r = FLD_MOD(r, tclk_prepare, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG2, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r); } +static void dsi_cio_enable_lane_override(struct omap_dss_device *dssdev, + enum dsi_lane lanes) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int clk_lane = dssdev->phy.dsi.clk_lane; + int data1_lane = dssdev->phy.dsi.data1_lane; + int data2_lane = dssdev->phy.dsi.data2_lane; + int data3_lane = dssdev->phy.dsi.data3_lane; + int data4_lane = dssdev->phy.dsi.data4_lane; + int clk_pol = dssdev->phy.dsi.clk_pol; + int data1_pol = dssdev->phy.dsi.data1_pol; + int data2_pol = dssdev->phy.dsi.data2_pol; + int data3_pol = dssdev->phy.dsi.data3_pol; + int data4_pol = dssdev->phy.dsi.data4_pol; + + u32 l = 0; + u8 lptxscp_start = dsi->num_data_lanes == 2 ? 22 : 26; + + if (lanes & DSI_CLK_P) + l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 0 : 1)); + if (lanes & DSI_CLK_N) + l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 1 : 0)); + + if (lanes & DSI_DATA1_P) + l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 0 : 1)); + if (lanes & DSI_DATA1_N) + l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 1 : 0)); + + if (lanes & DSI_DATA2_P) + l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 0 : 1)); + if (lanes & DSI_DATA2_N) + l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 1 : 0)); + + if (lanes & DSI_DATA3_P) + l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 0 : 1)); + if (lanes & DSI_DATA3_N) + l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 1 : 0)); + + if (lanes & DSI_DATA4_P) + l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 0 : 1)); + if (lanes & DSI_DATA4_N) + l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 1 : 0)); + /* + * Bits in REGLPTXSCPDAT4TO0DXDY: + * 17: DY0 18: DX0 + * 19: DY1 20: DX1 + * 21: DY2 22: DX2 + * 23: DY3 24: DX3 + * 25: DY4 26: DX4 + */ + + /* Set the lane override configuration */ + + /* REGLPTXSCPDAT4TO0DXDY */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17); -static int dsi_complexio_init(struct omap_dss_device *dssdev) + /* Enable lane override */ + + /* ENLPTXSCPDAT */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27); +} + +static void dsi_cio_disable_lane_override(struct platform_device *dsidev) { - int r = 0; + /* Disable lane override */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */ + /* Reset the lane override configuration */ + /* REGLPTXSCPDAT4TO0DXDY */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17); +} + +static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int t; + int bits[3]; + bool in_use[3]; + + if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { + bits[0] = 28; + bits[1] = 27; + bits[2] = 26; + } else { + bits[0] = 24; + bits[1] = 25; + bits[2] = 26; + } + + in_use[0] = false; + in_use[1] = false; + in_use[2] = false; + + if (dssdev->phy.dsi.clk_lane != 0) + in_use[dssdev->phy.dsi.clk_lane - 1] = true; + if (dssdev->phy.dsi.data1_lane != 0) + in_use[dssdev->phy.dsi.data1_lane - 1] = true; + if (dssdev->phy.dsi.data2_lane != 0) + in_use[dssdev->phy.dsi.data2_lane - 1] = true; + + t = 100000; + while (true) { + u32 l; + int i; + int ok; + + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + + ok = 0; + for (i = 0; i < 3; ++i) { + if (!in_use[i] || (l & (1 << bits[i]))) + ok++; + } + + if (ok == 3) + break; + + if (--t == 0) { + for (i = 0; i < 3; ++i) { + if (!in_use[i] || (l & (1 << bits[i]))) + continue; + + DSSERR("CIO TXCLKESC%d domain not coming " \ + "out of reset\n", i); + } + return -EIO; + } + } + + return 0; +} + +static int dsi_cio_init(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int r; + int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev); + u32 l; - DSSDBG("dsi_complexio_init\n"); + DSSDBGF(); - /* CIO_CLK_ICG, enable L3 clk to CIO */ - REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(true); + + dsi_enable_scp_clk(dsidev); /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - dsi_read_reg(DSI_DSIPHY_CFG5); + dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); - if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { - DSSERR("ComplexIO PHY not coming out of reset.\n"); - r = -ENODEV; - goto err; + if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) { + DSSERR("CIO SCP Clock domain not coming out of reset.\n"); + r = -EIO; + goto err_scp_clk_dom; } - dsi_complexio_config(dssdev); + dsi_set_lane_config(dssdev); + + /* set TX STOP MODE timer to maximum for this operation */ + l = dsi_read_reg(dsidev, DSI_TIMING1); + l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */ + l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */ + l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(dsidev, DSI_TIMING1, l); - r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + if (dsi->ulps_enabled) { + u32 lane_mask = DSI_CLK_P | DSI_DATA1_P | DSI_DATA2_P; + DSSDBG("manual ulps exit\n"); + + /* ULPS is exited by Mark-1 state for 1ms, followed by + * stop state. DSS HW cannot do this via the normal + * ULPS exit sequence, as after reset the DSS HW thinks + * that we are not in ULPS mode, and refuses to send the + * sequence. So we need to send the ULPS exit sequence + * manually. + */ + + if (num_data_lanes_dssdev > 2) + lane_mask |= DSI_DATA3_P; + + if (num_data_lanes_dssdev > 3) + lane_mask |= DSI_DATA4_P; + + dsi_cio_enable_lane_override(dssdev, lane_mask); + } + + r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON); if (r) - goto err; + goto err_cio_pwr; - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { - DSSERR("ComplexIO not coming out of reset.\n"); + if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + DSSERR("CIO PWR clock domain not coming out of reset.\n"); r = -ENODEV; - goto err; + goto err_cio_pwr_dom; } - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { - DSSERR("ComplexIO LDO power down.\n"); - r = -ENODEV; - goto err; + dsi_if_enable(dsidev, true); + dsi_if_enable(dsidev, false); + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + + r = dsi_cio_wait_tx_clk_esc_reset(dssdev); + if (r) + goto err_tx_clk_esc_rst; + + if (dsi->ulps_enabled) { + /* Keep Mark-1 state for 1ms (as per DSI spec) */ + ktime_t wait = ns_to_ktime(1000 * 1000); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&wait, HRTIMER_MODE_REL); + + /* Disable the override. The lanes should be set to Mark-11 + * state by the HW */ + dsi_cio_disable_lane_override(dsidev); } - dsi_complexio_timings(); + /* FORCE_TX_STOP_MODE_IO */ + REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15); - /* - The configuration of the DSI complex I/O (number of data lanes, - position, differential order) should not be changed while - DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the - hardware to recognize a new configuration of the complex I/O (done - in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow - this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next - reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] - LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN - bit to 1. If the sequence is not followed, the DSi complex I/O - configuration is undetermined. - */ - dsi_if_enable(1); - dsi_if_enable(0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - dsi_if_enable(1); - dsi_if_enable(0); + dsi_cio_timings(dsidev); + + dsi->ulps_enabled = false; DSSDBG("CIO init done\n"); -err: + + return 0; + +err_tx_clk_esc_rst: + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */ +err_cio_pwr_dom: + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); +err_cio_pwr: + if (dsi->ulps_enabled) + dsi_cio_disable_lane_override(dsidev); +err_scp_clk_dom: + dsi_disable_scp_clk(dsidev); + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(false); return r; } -static void dsi_complexio_uninit(void) +static void dsi_cio_uninit(struct platform_device *dsidev) { - dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); + dsi_disable_scp_clk(dsidev); + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(false); } -static int _dsi_wait_reset(void) +static int _dsi_wait_reset(struct platform_device *dsidev) { int t = 0; - while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { + while (REG_GET(dsidev, DSI_SYSSTATUS, 0, 0) == 0) { if (++t > 5) { DSSERR("soft reset failed\n"); return -ENODEV; @@ -1951,28 +2478,30 @@ static int _dsi_wait_reset(void) return 0; } -static int _dsi_reset(void) +static int _dsi_reset(struct platform_device *dsidev) { /* Soft reset */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); - return _dsi_wait_reset(); + REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 1, 1); + return _dsi_wait_reset(dsidev); } -static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, +static void dsi_config_tx_fifo(struct platform_device *dsidev, + enum fifo_size size1, enum fifo_size size2, enum fifo_size size3, enum fifo_size size4) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + dsi->vc[0].fifo_size = size1; + dsi->vc[1].fifo_size = size2; + dsi->vc[2].fifo_size = size3; + dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -1985,24 +2514,26 @@ static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); + dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r); } -static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, +static void dsi_config_rx_fifo(struct platform_device *dsidev, + enum fifo_size size1, enum fifo_size size2, enum fifo_size size3, enum fifo_size size4) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + dsi->vc[0].fifo_size = size1; + dsi->vc[1].fifo_size = size2; + dsi->vc[2].fifo_size = size3; + dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -2015,18 +2546,18 @@ static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); + dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r); } -static int dsi_force_tx_stop_mode_io(void) +static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev) { u32 r; - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); - if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) { DSSERR("TX_STOP bit not going down\n"); return -EIO; } @@ -2034,16 +2565,135 @@ static int dsi_force_tx_stop_mode_io(void) return 0; } -static int dsi_vc_enable(int channel, bool enable) +static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel) +{ + return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0); +} + +static void dsi_packet_sent_handler_vp(void *data, u32 mask) +{ + struct dsi_packet_sent_handler_data *vp_data = + (struct dsi_packet_sent_handler_data *) data; + struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev); + const int channel = dsi->update_channel; + u8 bit = dsi->te_enabled ? 30 : 31; + + if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0) + complete(vp_data->completion); +} + +static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DECLARE_COMPLETION_ONSTACK(completion); + struct dsi_packet_sent_handler_data vp_data = { dsidev, &completion }; + int r = 0; + u8 bit; + + bit = dsi->te_enabled ? 30 : 31; + + r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); + if (r) + goto err0; + + /* Wait for completion only if TE_EN/TE_START is still set */ + if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) { + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(10)) == 0) { + DSSERR("Failed to complete previous frame transfer\n"); + r = -EIO; + goto err1; + } + } + + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); + + return 0; +err1: + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); +err0: + return r; +} + +static void dsi_packet_sent_handler_l4(void *data, u32 mask) +{ + struct dsi_packet_sent_handler_data *l4_data = + (struct dsi_packet_sent_handler_data *) data; + struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev); + const int channel = dsi->update_channel; + + if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0) + complete(l4_data->completion); +} + +static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel) +{ + DECLARE_COMPLETION_ONSTACK(completion); + struct dsi_packet_sent_handler_data l4_data = { dsidev, &completion }; + int r = 0; + + r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); + if (r) + goto err0; + + /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) { + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(10)) == 0) { + DSSERR("Failed to complete previous l4 transfer\n"); + r = -EIO; + goto err1; + } + } + + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); + + return 0; +err1: + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); +err0: + return r; +} + +static int dsi_sync_vc(struct platform_device *dsidev, int channel) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + WARN_ON(!dsi_bus_is_locked(dsidev)); + + WARN_ON(in_interrupt()); + + if (!dsi_vc_is_enabled(dsidev, channel)) + return 0; + + switch (dsi->vc[channel].mode) { + case DSI_VC_MODE_VP: + return dsi_sync_vc_vp(dsidev, channel); + case DSI_VC_MODE_L4: + return dsi_sync_vc_l4(dsidev, channel); + default: + BUG(); + } +} + +static int dsi_vc_enable(struct platform_device *dsidev, int channel, + bool enable) { DSSDBG("dsi_vc_enable channel %d, enable %d\n", channel, enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0); - if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), + 0, enable) != enable) { DSSERR("Failed to set dsi_vc_enable to %d\n", enable); return -EIO; } @@ -2051,13 +2701,13 @@ static int dsi_vc_enable(int channel, bool enable) return 0; } -static void dsi_vc_initial_config(int channel) +static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) { u32 r; DSSDBGF("%d", channel); - r = dsi_read_reg(DSI_VC_CTRL(channel)); + r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); if (FLD_GET(r, 15, 15)) /* VC_BUSY */ DSSERR("VC(%d) busy when trying to configure it!\n", @@ -2070,85 +2720,107 @@ static void dsi_vc_initial_config(int channel) r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ + if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH)) + r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ - dsi_write_reg(DSI_VC_CTRL(channel), r); + dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r); } -static int dsi_vc_config_l4(int channel) +static int dsi_vc_config_l4(struct platform_device *dsidev, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_L4) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->vc[channel].mode == DSI_VC_MODE_L4) return 0; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_sync_vc(dsidev, channel); + + dsi_vc_enable(dsidev, channel, 0); /* VC_BUSY */ - if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for L4\n", channel); return -EIO; } - REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + + /* DCS_CMD_ENABLE */ + if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 30, 30); - dsi_vc_enable(channel, 1); + dsi_vc_enable(dsidev, channel, 1); - dsi.vc[channel].mode = DSI_VC_MODE_L4; + dsi->vc[channel].mode = DSI_VC_MODE_L4; return 0; } -static int dsi_vc_config_vp(int channel) +static int dsi_vc_config_vp(struct platform_device *dsidev, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_VP) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->vc[channel].mode == DSI_VC_MODE_VP) return 0; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_sync_vc(dsidev, channel); + + dsi_vc_enable(dsidev, channel, 0); /* VC_BUSY */ - if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for VP\n", channel); return -EIO; } - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ + /* SOURCE, 1 = video port */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 1, 1); + + /* DCS_CMD_ENABLE */ + if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 30, 30); - dsi_vc_enable(channel, 1); + dsi_vc_enable(dsidev, channel, 1); - dsi.vc[channel].mode = DSI_VC_MODE_VP; + dsi->vc[channel].mode = DSI_VC_MODE_VP; return 0; } -void omapdss_dsi_vc_enable_hs(int channel, bool enable) +void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, + bool enable) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - dsi_vc_enable(channel, 0); - dsi_if_enable(0); + dsi_vc_enable(dsidev, channel, 0); + dsi_if_enable(dsidev, 0); - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9); - dsi_vc_enable(channel, 1); - dsi_if_enable(1); + dsi_vc_enable(dsidev, channel, 1); + dsi_if_enable(dsidev, 1); - dsi_force_tx_stop_mode_io(); + dsi_force_tx_stop_mode_io(dsidev); } EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs); -static void dsi_vc_flush_long_data(int channel) +static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel) { - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { u32 val; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -2194,13 +2866,14 @@ static void dsi_show_rx_ack_with_err(u16 err) DSSERR("\t\tDSI Protocol Violation\n"); } -static u16 dsi_vc_flush_receive_data(int channel) +static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, + int channel) { /* RX_FIFO_NOT_EMPTY */ - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { u32 val; u8 dt; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSERR("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { @@ -2215,7 +2888,7 @@ static u16 dsi_vc_flush_receive_data(int channel) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { DSSERR("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); - dsi_vc_flush_long_data(channel); + dsi_vc_flush_long_data(dsidev, channel); } else { DSSERR("\tunknown datatype 0x%02x\n", dt); } @@ -2223,40 +2896,44 @@ static u16 dsi_vc_flush_receive_data(int channel) return 0; } -static int dsi_vc_send_bta(int channel) +static int dsi_vc_send_bta(struct platform_device *dsidev, int channel) { - if (dsi.debug_write || dsi.debug_read) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->debug_write || dsi->debug_read) DSSDBG("dsi_vc_send_bta %d\n", channel); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); - dsi_vc_flush_receive_data(channel); + dsi_vc_flush_receive_data(dsidev, channel); } - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ return 0; } -int dsi_vc_send_bta_sync(int channel) +int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); DECLARE_COMPLETION_ONSTACK(completion); int r = 0; u32 err; - r = dsi_register_isr_vc(channel, dsi_completion_handler, + r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler, &completion, DSI_VC_IRQ_BTA); if (r) goto err0; - r = dsi_register_isr(dsi_completion_handler, &completion, + r = dsi_register_isr(dsidev, dsi_completion_handler, &completion, DSI_IRQ_ERROR_MASK); if (r) goto err1; - r = dsi_vc_send_bta(channel); + r = dsi_vc_send_bta(dsidev, channel); if (r) goto err2; @@ -2267,41 +2944,42 @@ int dsi_vc_send_bta_sync(int channel) goto err2; } - err = dsi_get_errors(); + err = dsi_get_errors(dsidev); if (err) { DSSERR("Error while sending BTA: %x\n", err); r = -EIO; goto err2; } err2: - dsi_unregister_isr(dsi_completion_handler, &completion, + dsi_unregister_isr(dsidev, dsi_completion_handler, &completion, DSI_IRQ_ERROR_MASK); err1: - dsi_unregister_isr_vc(channel, dsi_completion_handler, + dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler, &completion, DSI_VC_IRQ_BTA); err0: return r; } EXPORT_SYMBOL(dsi_vc_send_bta_sync); -static inline void dsi_vc_write_long_header(int channel, u8 data_type, - u16 len, u8 ecc) +static inline void dsi_vc_write_long_header(struct platform_device *dsidev, + int channel, u8 data_type, u16 len, u8 ecc) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 data_id; - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - data_id = data_type | dsi.vc[channel].vc_id << 6; + data_id = data_type | dsi->vc[channel].vc_id << 6; val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | FLD_VAL(ecc, 31, 24); - dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val); } -static inline void dsi_vc_write_long_payload(int channel, - u8 b1, u8 b2, u8 b3, u8 b4) +static inline void dsi_vc_write_long_payload(struct platform_device *dsidev, + int channel, u8 b1, u8 b2, u8 b3, u8 b4) { u32 val; @@ -2310,34 +2988,35 @@ static inline void dsi_vc_write_long_payload(int channel, /* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", b1, b2, b3, b4, val); */ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val); } -static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, - u8 ecc) +static int dsi_vc_send_long(struct platform_device *dsidev, int channel, + u8 data_type, u8 *data, u16 len, u8 ecc) { /*u32 val; */ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; u8 *p; int r = 0; u8 b1, b2, b3, b4; - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("dsi_vc_send_long, %d bytes\n", len); /* len + header */ - if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + if (dsi->vc[channel].fifo_size * 32 * 4 < len + 4) { DSSERR("unable to send long packet: packet too long.\n"); return -EINVAL; } - dsi_vc_config_l4(channel); + dsi_vc_config_l4(dsidev, channel); - dsi_vc_write_long_header(channel, data_type, len, ecc); + dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc); p = data; for (i = 0; i < len >> 2; i++) { - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("\tsending full packet %d\n", i); b1 = *p++; @@ -2345,14 +3024,14 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, b3 = *p++; b4 = *p++; - dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4); } i = len % 4; if (i) { b1 = 0; b2 = 0; b3 = 0; - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("\tsending remainder bytes %d\n", i); switch (i) { @@ -2370,62 +3049,69 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, break; } - dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0); } return r; } -static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +static int dsi_vc_send_short(struct platform_device *dsidev, int channel, + u8 data_type, u16 data, u8 ecc) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r; u8 data_id; - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", channel, data_type, data & 0xff, (data >> 8) & 0xff); - dsi_vc_config_l4(channel); + dsi_vc_config_l4(dsidev, channel); - if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) { DSSERR("ERROR FIFO FULL, aborting transfer\n"); return -EINVAL; } - data_id = data_type | dsi.vc[channel].vc_id << 6; + data_id = data_type | dsi->vc[channel].vc_id << 6; r = (data_id << 0) | (data << 8) | (ecc << 24); - dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r); return 0; } -int dsi_vc_send_null(int channel) +int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u8 nullpkg[] = {0, 0, 0, 0}; - return dsi_vc_send_long(channel, DSI_DT_NULL_PACKET, nullpkg, 4, 0); + + return dsi_vc_send_long(dsidev, channel, DSI_DT_NULL_PACKET, nullpkg, + 4, 0); } EXPORT_SYMBOL(dsi_vc_send_null); -int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, + u8 *data, int len) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; BUG_ON(len == 0); if (len == 1) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_0, data[0], 0); } else if (len == 2) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_1, data[0] | (data[1] << 8), 0); } else { /* 0x39 = DCS Long Write */ - r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + r = dsi_vc_send_long(dsidev, channel, DSI_DT_DCS_LONG_WRITE, data, len, 0); } @@ -2433,21 +3119,24 @@ int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) } EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); -int dsi_vc_dcs_write(int channel, u8 *data, int len) +int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, + int len) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; - r = dsi_vc_dcs_write_nosync(channel, data, len); + r = dsi_vc_dcs_write_nosync(dssdev, channel, data, len); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(dssdev, channel); if (r) goto err; - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { DSSERR("rx fifo not empty after write, dumping data:\n"); - dsi_vc_flush_receive_data(channel); + dsi_vc_flush_receive_data(dsidev, channel); r = -EIO; goto err; } @@ -2460,47 +3149,51 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_write); -int dsi_vc_dcs_write_0(int channel, u8 dcs_cmd) +int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd) { - return dsi_vc_dcs_write(channel, &dcs_cmd, 1); + return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1); } EXPORT_SYMBOL(dsi_vc_dcs_write_0); -int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param) +int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 param) { u8 buf[2]; buf[0] = dcs_cmd; buf[1] = param; - return dsi_vc_dcs_write(channel, buf, 2); + return dsi_vc_dcs_write(dssdev, channel, buf, 2); } EXPORT_SYMBOL(dsi_vc_dcs_write_1); -int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *buf, int buflen) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 dt; int r; - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd); - r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_READ, dcs_cmd, 0); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(dssdev, channel); if (r) goto err; /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) { DSSERR("RX fifo empty when trying to read.\n"); r = -EIO; goto err; } - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi->debug_read) DSSDBG("\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { @@ -2511,7 +3204,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_SHORT_READ_1) { u8 data = FLD_GET(val, 15, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS short response, 1 byte: %02x\n", data); if (buflen < 1) { @@ -2524,7 +3217,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) return 1; } else if (dt == DSI_DT_RX_SHORT_READ_2) { u16 data = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS short response, 2 byte: %04x\n", data); if (buflen < 2) { @@ -2539,7 +3232,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { int w; int len = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS long response, len %d\n", len); if (len > buflen) { @@ -2550,8 +3243,9 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) /* two byte checksum ends the packet, not included in len */ for (w = 0; w < len + 2;) { int b; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(dsidev, + DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi->debug_read) DSSDBG("\t\t%02x %02x %02x %02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -2582,11 +3276,12 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_read); -int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) +int dsi_vc_dcs_read_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *data) { int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, data, 1); + r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, data, 1); if (r < 0) return r; @@ -2598,12 +3293,13 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) } EXPORT_SYMBOL(dsi_vc_dcs_read_1); -int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2) +int dsi_vc_dcs_read_2(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *data1, u8 *data2) { u8 buf[2]; int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, buf, 2); + r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, buf, 2); if (r < 0) return r; @@ -2618,14 +3314,94 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2) } EXPORT_SYMBOL(dsi_vc_dcs_read_2); -int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel, + u16 len) { - return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + + return dsi_vc_send_short(dsidev, channel, DSI_DT_SET_MAX_RET_PKG_SIZE, len, 0); } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); -static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) +static int dsi_enter_ulps(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DECLARE_COMPLETION_ONSTACK(completion); + int r; + + DSSDBGF(); + + WARN_ON(!dsi_bus_is_locked(dsidev)); + + WARN_ON(dsi->ulps_enabled); + + if (dsi->ulps_enabled) + return 0; + + if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) { + DSSERR("DDR_CLK_ALWAYS_ON enabled when entering ULPS\n"); + return -EIO; + } + + dsi_sync_vc(dsidev, 0); + dsi_sync_vc(dsidev, 1); + dsi_sync_vc(dsidev, 2); + dsi_sync_vc(dsidev, 3); + + dsi_force_tx_stop_mode_io(dsidev); + + dsi_vc_enable(dsidev, 0, false); + dsi_vc_enable(dsidev, 1, false); + dsi_vc_enable(dsidev, 2, false); + dsi_vc_enable(dsidev, 3, false); + + if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */ + DSSERR("HS busy when enabling ULPS\n"); + return -EIO; + } + + if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */ + DSSERR("LP busy when enabling ULPS\n"); + return -EIO; + } + + r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + if (r) + return r; + + /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */ + /* LANEx_ULPS_SIG2 */ + REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (1 << 0) | (1 << 1) | (1 << 2), + 7, 5); + + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(1000)) == 0) { + DSSERR("ULPS enable timeout\n"); + r = -EIO; + goto err; + } + + dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS); + + dsi_if_enable(dsidev, false); + + dsi->ulps_enabled = true; + + return 0; + +err: + dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + return r; +} + +static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2634,14 +3410,14 @@ static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(dsidev, DSI_TIMING2); r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(dsidev, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2651,7 +3427,8 @@ static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) +static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, + bool x8, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2660,14 +3437,14 @@ static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */ r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); @@ -2677,7 +3454,8 @@ static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) +static void dsi_set_stop_state_counter(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2686,14 +3464,14 @@ static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2703,7 +3481,8 @@ static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) +static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2712,14 +3491,14 @@ static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in TxByteClkHS */ - fck = dsi_get_txbyteclkhs(); + fck = dsi_get_txbyteclkhs(dsidev); - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(dsidev, DSI_TIMING2); r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(dsidev, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2730,24 +3509,25 @@ static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) } static int dsi_proto_config(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u32 r; int buswidth = 0; - dsi_config_tx_fifo(DSI_FIFO_SIZE_32, + dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); - dsi_config_rx_fifo(DSI_FIFO_SIZE_32, + dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); /* XXX what values for the timeouts? */ - dsi_set_stop_state_counter(0x1000, false, false); - dsi_set_ta_timeout(0x1fff, true, true); - dsi_set_lp_rx_timeout(0x1fff, true, true); - dsi_set_hs_tx_timeout(0x1fff, true, true); + dsi_set_stop_state_counter(dsidev, 0x1000, false, false); + dsi_set_ta_timeout(dsidev, 0x1fff, true, true); + dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true); + dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true); switch (dssdev->ctrl.pixel_size) { case 16: @@ -2763,7 +3543,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) BUG(); } - r = dsi_read_reg(DSI_CTRL); + r = dsi_read_reg(dsidev, DSI_CTRL); r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ @@ -2773,21 +3553,25 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ - r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ - r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ + if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + /* DCS_CMD_CODE, 1=start, 0=continue */ + r = FLD_MOD(r, 0, 25, 25); + } - dsi_write_reg(DSI_CTRL, r); + dsi_write_reg(dsidev, DSI_CTRL, r); - dsi_vc_initial_config(0); - dsi_vc_initial_config(1); - dsi_vc_initial_config(2); - dsi_vc_initial_config(3); + dsi_vc_initial_config(dsidev, 0); + dsi_vc_initial_config(dsidev, 1); + dsi_vc_initial_config(dsidev, 2); + dsi_vc_initial_config(dsidev, 3); return 0; } static void dsi_proto_timings(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; unsigned tclk_pre, tclk_post; unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; @@ -2797,32 +3581,27 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) unsigned ths_eot; u32 r; - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); ths_prepare = FLD_GET(r, 31, 24); ths_prepare_ths_zero = FLD_GET(r, 23, 16); ths_zero = ths_prepare_ths_zero - ths_prepare; ths_trail = FLD_GET(r, 15, 8); ths_exit = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); tlpx = FLD_GET(r, 22, 16) * 2; tclk_trail = FLD_GET(r, 15, 8); tclk_zero = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); tclk_prepare = FLD_GET(r, 7, 0); /* min 8*UI */ tclk_pre = 20; /* min 60ns + 52*UI */ - tclk_post = ns2ddr(60) + 26; + tclk_post = ns2ddr(dsidev, 60) + 26; - /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ - if (dssdev->phy.dsi.data1_lane != 0 && - dssdev->phy.dsi.data2_lane != 0) - ths_eot = 2; - else - ths_eot = 4; + ths_eot = DIV_ROUND_UP(4, dsi_get_num_data_lanes_dssdev(dssdev)); ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, 4); @@ -2831,10 +3610,10 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); - r = dsi_read_reg(DSI_CLK_TIMING); + r = dsi_read_reg(dsidev, DSI_CLK_TIMING); r = FLD_MOD(r, ddr_clk_pre, 15, 8); r = FLD_MOD(r, ddr_clk_post, 7, 0); - dsi_write_reg(DSI_CLK_TIMING, r); + dsi_write_reg(dsidev, DSI_CLK_TIMING, r); DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", ddr_clk_pre, @@ -2848,7 +3627,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) r = FLD_VAL(enter_hs_mode_lat, 31, 16) | FLD_VAL(exit_hs_mode_lat, 15, 0); - dsi_write_reg(DSI_VM_TIMING7, r); + dsi_write_reg(dsidev, DSI_VM_TIMING7, r); DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", enter_hs_mode_lat, exit_hs_mode_lat); @@ -2858,25 +3637,27 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) #define DSI_DECL_VARS \ int __dsi_cb = 0; u32 __dsi_cv = 0; -#define DSI_FLUSH(ch) \ +#define DSI_FLUSH(dsidev, ch) \ if (__dsi_cb > 0) { \ /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ __dsi_cb = __dsi_cv = 0; \ } -#define DSI_PUSH(ch, data) \ +#define DSI_PUSH(dsidev, ch, data) \ do { \ __dsi_cv |= (data) << (__dsi_cb * 8); \ /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ if (++__dsi_cb > 3) \ - DSI_FLUSH(ch); \ + DSI_FLUSH(dsidev, ch); \ } while (0) static int dsi_update_screen_l4(struct omap_dss_device *dssdev, int x, int y, int w, int h) { /* Note: supports only 24bit colors in 32bit container */ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int first = 1; int fifo_stalls = 0; int max_dsi_packet_size; @@ -2915,7 +3696,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, * in fifo */ /* When using CPU, max long packet size is TX buffer size */ - max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + max_dsi_packet_size = dsi->vc[0].fifo_size * 32 * 4; /* we seem to get better perf if we divide the tx fifo to half, and while the other half is being sent, we fill the other half @@ -2944,35 +3725,36 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, #if 1 /* using fifo not empty */ /* TX_FIFO_NOT_EMPTY */ - while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(0)), 5, 5)) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } udelay(1); } #elif 1 /* using fifo emptiness */ - while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < + while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < max_dsi_packet_size) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } } #else - while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { + while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, + 7, 0) + 1) * 4 == 0) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } } @@ -2981,17 +3763,17 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, pixels_left -= pixels; - dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + dsi_vc_write_long_header(dsidev, 0, DSI_DT_DCS_LONG_WRITE, 1 + pixels * bytespp, 0); - DSI_PUSH(0, dcs_cmd); + DSI_PUSH(dsidev, 0, dcs_cmd); while (pixels-- > 0) { u32 pix = __raw_readl(data++); - DSI_PUSH(0, (pix >> 16) & 0xff); - DSI_PUSH(0, (pix >> 8) & 0xff); - DSI_PUSH(0, (pix >> 0) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 16) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 8) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 0) & 0xff); current_x++; if (current_x == x+w) { @@ -3000,7 +3782,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, } } - DSI_FLUSH(0); + DSI_FLUSH(dsidev, 0); } return 0; @@ -3009,6 +3791,8 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned bytespp; unsigned bytespl; unsigned bytespf; @@ -3017,16 +3801,13 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, unsigned packet_len; u32 l; int r; - const unsigned channel = dsi.update_channel; - /* line buffer is 1024 x 24bits */ - /* XXX: for some reason using full buffer size causes considerable TX - * slowdown with update sizes that fill the whole buffer */ - const unsigned line_buf_size = 1023 * 3; + const unsigned channel = dsi->update_channel; + const unsigned line_buf_size = dsi_get_line_buf_size(dsidev); DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", x, y, w, h); - dsi_vc_config_vp(channel); + dsi_vc_config_vp(dsidev, channel); bytespp = dssdev->ctrl.pixel_size / 8; bytespl = w * bytespp; @@ -3047,15 +3828,16 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, total_len += (bytespf % packet_payload) + 1; l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(dsidev, DSI_VC_TE(channel), l); - dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); + dsi_vc_write_long_header(dsidev, channel, DSI_DT_DCS_LONG_WRITE, + packet_len, 0); - if (dsi.te_enabled) + if (dsi->te_enabled) l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ else l = FLD_MOD(l, 1, 31, 31); /* TE_START */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(dsidev, DSI_VC_TE(channel), l); /* We put SIDLEMODE to no-idle for the duration of the transfer, * because DSS interrupts are not capable of waking up the CPU and the @@ -3065,23 +3847,23 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, */ dispc_disable_sidle(); - dsi_perf_mark_start(); + dsi_perf_mark_start(dsidev); - r = queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work, - msecs_to_jiffies(250)); + r = schedule_delayed_work(&dsi->framedone_timeout_work, + msecs_to_jiffies(250)); BUG_ON(r == 0); dss_start_update(dssdev); - if (dsi.te_enabled) { + if (dsi->te_enabled) { /* disable LP_RX_TO, so that we can receive TE. Time to wait * for TE is longer than the timer allows */ - REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ - dsi_vc_send_bta(channel); + dsi_vc_send_bta(dsidev, channel); #ifdef DSI_CATCH_MISSING_TE - mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250)); + mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250)); #endif } } @@ -3093,41 +3875,28 @@ static void dsi_te_timeout(unsigned long arg) } #endif -static void dsi_framedone_bta_callback(void *data, u32 mask); - -static void dsi_handle_framedone(int error) +static void dsi_handle_framedone(struct platform_device *dsidev, int error) { - const int channel = dsi.update_channel; - - dsi_unregister_isr_vc(channel, dsi_framedone_bta_callback, - NULL, DSI_VC_IRQ_BTA); - - cancel_delayed_work(&dsi.framedone_timeout_work); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); /* SIDLEMODE back to smart-idle */ dispc_enable_sidle(); - if (dsi.te_enabled) { + if (dsi->te_enabled) { /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } - /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { - DSSERR("Received error during frame transfer:\n"); - dsi_vc_flush_receive_data(channel); - if (!error) - error = -EIO; - } - - dsi.framedone_callback(error, dsi.framedone_data); + dsi->framedone_callback(error, dsi->framedone_data); if (!error) - dsi_perf_show("DISPC"); + dsi_perf_show(dsidev, "DISPC"); } static void dsi_framedone_timeout_work_callback(struct work_struct *work) { + struct dsi_data *dsi = container_of(work, struct dsi_data, + framedone_timeout_work.work); /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after * 250ms which would conflict with this timeout work. What should be * done is first cancel the transfer on the HW, and then cancel the @@ -3137,70 +3906,34 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work) DSSERR("Framedone not received for 250ms!\n"); - dsi_handle_framedone(-ETIMEDOUT); -} - -static void dsi_framedone_bta_callback(void *data, u32 mask) -{ - dsi_handle_framedone(0); - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif + dsi_handle_framedone(dsi->pdev, -ETIMEDOUT); } static void dsi_framedone_irq_callback(void *data, u32 mask) { - const int channel = dsi.update_channel; - int r; + struct omap_dss_device *dssdev = (struct omap_dss_device *) data; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); /* Note: We get FRAMEDONE when DISPC has finished sending pixels and * turns itself off. However, DSI still has the pixels in its buffers, * and is sending the data. */ - if (dsi.te_enabled) { - /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ - } - - /* Send BTA after the frame. We need this for the TE to work, as TE - * trigger is only sent for BTAs without preceding packet. Thus we need - * to BTA after the pixel packets so that next BTA will cause TE - * trigger. - * - * This is not needed when TE is not in use, but we do it anyway to - * make sure that the transfer has been completed. It would be more - * optimal, but more complex, to wait only just before starting next - * transfer. - * - * Also, as there's no interrupt telling when the transfer has been - * done and the channel could be reconfigured, the only way is to - * busyloop until TE_SIZE is zero. With BTA we can do this - * asynchronously. - * */ - - r = dsi_register_isr_vc(channel, dsi_framedone_bta_callback, - NULL, DSI_VC_IRQ_BTA); - if (r) { - DSSERR("Failed to register BTA ISR\n"); - dsi_handle_framedone(-EIO); - return; - } + __cancel_delayed_work(&dsi->framedone_timeout_work); - r = dsi_vc_send_bta(channel); - if (r) { - DSSERR("BTA after framedone failed\n"); - dsi_unregister_isr_vc(channel, dsi_framedone_bta_callback, - NULL, DSI_VC_IRQ_BTA); - dsi_handle_framedone(-EIO); - } + dsi_handle_framedone(dsidev, 0); + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif } int omap_dsi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h, bool enlarge_update_area) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u16 dw, dh; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -3220,7 +3953,7 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dsi_perf_mark_setup(); + dsi_perf_mark_setup(dsidev); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { dss_setup_partial_planes(dssdev, x, y, w, h, @@ -3237,7 +3970,10 @@ int omap_dsi_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h, void (*callback)(int, void *), void *data) { - dsi.update_channel = channel; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->update_channel = channel; /* OMAP DSS cannot send updates of odd widths. * omap_dsi_prepare_update() makes the widths even, but add a BUG_ON @@ -3246,14 +3982,14 @@ int omap_dsi_update(struct omap_dss_device *dssdev, BUG_ON(x % 2 == 1); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dsi.framedone_callback = callback; - dsi.framedone_data = data; + dsi->framedone_callback = callback; + dsi->framedone_data = data; - dsi.update_region.x = x; - dsi.update_region.y = y; - dsi.update_region.w = w; - dsi.update_region.h = h; - dsi.update_region.device = dssdev; + dsi->update_region.x = x; + dsi->update_region.y = y; + dsi->update_region.w = w; + dsi->update_region.h = h; + dsi->update_region.device = dssdev; dsi_update_screen_dispc(dssdev, x, y, w, h); } else { @@ -3263,7 +3999,7 @@ int omap_dsi_update(struct omap_dss_device *dssdev, if (r) return r; - dsi_perf_show("L4"); + dsi_perf_show(dsidev, "L4"); callback(0, data); } @@ -3276,9 +4012,13 @@ EXPORT_SYMBOL(omap_dsi_update); static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; + u32 irq; + + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; - r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev, + irq); if (r) { DSSERR("can't get FRAMEDONE irq\n"); return r; @@ -3311,28 +4051,34 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) { - omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + u32 irq; + + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; + + omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev, + irq); } static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_clock_info cinfo; int r; /* we always use DSS_CLK_SYSCK as input clock */ cinfo.use_sys_clk = true; - cinfo.regn = dssdev->phy.dsi.div.regn; - cinfo.regm = dssdev->phy.dsi.div.regm; - cinfo.regm_dispc = dssdev->phy.dsi.div.regm_dispc; - cinfo.regm_dsi = dssdev->phy.dsi.div.regm_dsi; + cinfo.regn = dssdev->clocks.dsi.regn; + cinfo.regm = dssdev->clocks.dsi.regm; + cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc; + cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi; r = dsi_calc_clock_rates(dssdev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; } - r = dsi_pll_set_clock_div(&cinfo); + r = dsi_pll_set_clock_div(dsidev, &cinfo); if (r) { DSSERR("Failed to set dsi clocks\n"); return r; @@ -3343,14 +4089,15 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dispc_clock_info dispc_cinfo; int r; unsigned long long fck; - fck = dsi_get_pll_hsdiv_dispc_rate(); + fck = dsi_get_pll_hsdiv_dispc_rate(dsidev); - dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div; - dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div; + dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div; + dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div; r = dispc_calc_clock_rates(fck, &dispc_cinfo); if (r) { @@ -3369,11 +4116,11 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int dsi_module = dsi_get_dsidev_id(dsidev); int r; - _dsi_print_reset_status(); - - r = dsi_pll_init(dssdev, true, true); + r = dsi_pll_init(dsidev, true, true); if (r) goto err0; @@ -3381,8 +4128,10 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err1; - dss_select_dispc_clk_source(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC); - dss_select_dsi_clk_source(DSS_CLK_SRC_DSI_PLL_HSDIV_DSI); + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); + dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src); + dss_select_lcd_clk_source(dssdev->manager->id, + dssdev->clocks.dispc.channel.lcd_clk_src); DSSDBG("PLL OK\n"); @@ -3390,82 +4139,92 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err2; - r = dsi_complexio_init(dssdev); + r = dsi_cio_init(dssdev); if (r) goto err2; - _dsi_print_reset_status(); + _dsi_print_reset_status(dsidev); dsi_proto_timings(dssdev); dsi_set_lp_clk_divisor(dssdev); if (1) - _dsi_print_reset_status(); + _dsi_print_reset_status(dsidev); r = dsi_proto_config(dssdev); if (r) goto err3; /* enable interface */ - dsi_vc_enable(0, 1); - dsi_vc_enable(1, 1); - dsi_vc_enable(2, 1); - dsi_vc_enable(3, 1); - dsi_if_enable(1); - dsi_force_tx_stop_mode_io(); + dsi_vc_enable(dsidev, 0, 1); + dsi_vc_enable(dsidev, 1, 1); + dsi_vc_enable(dsidev, 2, 1); + dsi_vc_enable(dsidev, 3, 1); + dsi_if_enable(dsidev, 1); + dsi_force_tx_stop_mode_io(dsidev); return 0; err3: - dsi_complexio_uninit(); + dsi_cio_uninit(dsidev); err2: - dss_select_dispc_clk_source(DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(DSS_CLK_SRC_FCK); + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); err1: - dsi_pll_uninit(); + dsi_pll_uninit(dsidev, true); err0: return r; } -static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) +static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, + bool disconnect_lanes, bool enter_ulps) { - /* disable interface */ - dsi_if_enable(0); - dsi_vc_enable(0, 0); - dsi_vc_enable(1, 0); - dsi_vc_enable(2, 0); - dsi_vc_enable(3, 0); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int dsi_module = dsi_get_dsidev_id(dsidev); - dss_select_dispc_clk_source(DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(DSS_CLK_SRC_FCK); - dsi_complexio_uninit(); - dsi_pll_uninit(); + if (enter_ulps && !dsi->ulps_enabled) + dsi_enter_ulps(dsidev); + + /* disable interface */ + dsi_if_enable(dsidev, 0); + dsi_vc_enable(dsidev, 0, 0); + dsi_vc_enable(dsidev, 1, 0); + dsi_vc_enable(dsidev, 2, 0); + dsi_vc_enable(dsidev, 3, 0); + + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dsi_cio_uninit(dsidev); + dsi_pll_uninit(dsidev, disconnect_lanes); } -static int dsi_core_init(void) +static int dsi_core_init(struct platform_device *dsidev) { /* Autoidle */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); + REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 0, 0); /* ENWAKEUP */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); + REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 2, 2); /* SIDLEMODE smart-idle */ - REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); + REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 2, 4, 3); - _dsi_initialize_irq(); + _dsi_initialize_irq(dsidev); return 0; } int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; DSSDBG("dsi_display_enable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - mutex_lock(&dsi.lock); + mutex_lock(&dsi->lock); r = omap_dss_start_device(dssdev); if (r) { @@ -3474,13 +4233,13 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) } enable_clocks(1); - dsi_enable_pll_clock(1); + dsi_enable_pll_clock(dsidev, 1); - r = _dsi_reset(); + r = _dsi_reset(dsidev); if (r) goto err1; - dsi_core_init(); + dsi_core_init(dsidev); r = dsi_display_init_dispc(dssdev); if (r) @@ -3490,7 +4249,7 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) if (r) goto err2; - mutex_unlock(&dsi.lock); + mutex_unlock(&dsi->lock); return 0; @@ -3498,39 +4257,46 @@ err2: dsi_display_uninit_dispc(dssdev); err1: enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(dsidev, 0); omap_dss_stop_device(dssdev); err0: - mutex_unlock(&dsi.lock); + mutex_unlock(&dsi->lock); DSSDBG("dsi_display_enable FAILED\n"); return r; } EXPORT_SYMBOL(omapdss_dsi_display_enable); -void omapdss_dsi_display_disable(struct omap_dss_device *dssdev) +void omapdss_dsi_display_disable(struct omap_dss_device *dssdev, + bool disconnect_lanes, bool enter_ulps) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DSSDBG("dsi_display_disable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - mutex_lock(&dsi.lock); + mutex_lock(&dsi->lock); dsi_display_uninit_dispc(dssdev); - dsi_display_uninit_dsi(dssdev); + dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps); enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(dsidev, 0); omap_dss_stop_device(dssdev); - mutex_unlock(&dsi.lock); + mutex_unlock(&dsi->lock); } EXPORT_SYMBOL(omapdss_dsi_display_disable); int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) { - dsi.te_enabled = enable; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->te_enabled = enable; return 0; } EXPORT_SYMBOL(omapdss_dsi_enable_te); @@ -3550,23 +4316,33 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, int dsi_init_display(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int dsi_module = dsi_get_dsidev_id(dsidev); + DSSDBG("DSI init\n"); /* XXX these should be figured out dynamically */ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; - if (dsi.vdds_dsi_reg == NULL) { + if (dsi->vdds_dsi_reg == NULL) { struct regulator *vdds_dsi; - vdds_dsi = regulator_get(&dsi.pdev->dev, "vdds_dsi"); + vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); if (IS_ERR(vdds_dsi)) { DSSERR("can't get VDDS_DSI regulator\n"); return PTR_ERR(vdds_dsi); } - dsi.vdds_dsi_reg = vdds_dsi; + dsi->vdds_dsi_reg = vdds_dsi; + } + + if (dsi_get_num_data_lanes_dssdev(dssdev) > dsi->num_data_lanes) { + DSSERR("DSI%d can't support more than %d data lanes\n", + dsi_module + 1, dsi->num_data_lanes); + return -EINVAL; } return 0; @@ -3574,11 +4350,13 @@ int dsi_init_display(struct omap_dss_device *dssdev) int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; - for (i = 0; i < ARRAY_SIZE(dsi.vc); i++) { - if (!dsi.vc[i].dssdev) { - dsi.vc[i].dssdev = dssdev; + for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { + if (!dsi->vc[i].dssdev) { + dsi->vc[i].dssdev = dssdev; *channel = i; return 0; } @@ -3591,6 +4369,9 @@ EXPORT_SYMBOL(omap_dsi_request_vc); int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + if (vc_id < 0 || vc_id > 3) { DSSERR("VC ID out of range\n"); return -EINVAL; @@ -3601,13 +4382,13 @@ int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id) return -EINVAL; } - if (dsi.vc[channel].dssdev != dssdev) { + if (dsi->vc[channel].dssdev != dssdev) { DSSERR("Virtual Channel not allocated to display %s\n", dssdev->name); return -EINVAL; } - dsi.vc[channel].vc_id = vc_id; + dsi->vc[channel].vc_id = vc_id; return 0; } @@ -3615,143 +4396,172 @@ EXPORT_SYMBOL(omap_dsi_set_vc_id); void omap_dsi_release_vc(struct omap_dss_device *dssdev, int channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + if ((channel >= 0 && channel <= 3) && - dsi.vc[channel].dssdev == dssdev) { - dsi.vc[channel].dssdev = NULL; - dsi.vc[channel].vc_id = 0; + dsi->vc[channel].dssdev == dssdev) { + dsi->vc[channel].dssdev = NULL; + dsi->vc[channel].vc_id = 0; } } EXPORT_SYMBOL(omap_dsi_release_vc); -void dsi_wait_pll_hsdiv_dispc_active(void) +void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) { - if (wait_for_bit_change(DSI_PLL_STATUS, 7, 1) != 1) + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 7, 1) != 1) DSSERR("%s (%s) not active\n", - dss_get_generic_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), - dss_feat_get_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC)); + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC)); } -void dsi_wait_pll_hsdiv_dsi_active(void) +void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) { - if (wait_for_bit_change(DSI_PLL_STATUS, 8, 1) != 1) + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 8, 1) != 1) DSSERR("%s (%s) not active\n", - dss_get_generic_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), - dss_feat_get_clk_source_name(DSS_CLK_SRC_DSI_PLL_HSDIV_DSI)); + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI)); } -static void dsi_calc_clock_param_ranges(void) +static void dsi_calc_clock_param_ranges(struct platform_device *dsidev) { - dsi.regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN); - dsi.regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM); - dsi.regm_dispc_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC); - dsi.regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI); - dsi.fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT); - dsi.fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT); - dsi.lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN); + dsi->regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM); + dsi->regm_dispc_max = + dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC); + dsi->regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI); + dsi->fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT); + dsi->fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT); + dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); } -static int dsi_init(struct platform_device *pdev) +static int dsi_init(struct platform_device *dsidev) { + struct omap_display_platform_data *dss_plat_data; + struct omap_dss_board_info *board_info; u32 rev; - int r, i; + int r, i, dsi_module = dsi_get_dsidev_id(dsidev); struct resource *dsi_mem; + struct dsi_data *dsi; + + dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); + if (!dsi) { + r = -ENOMEM; + goto err0; + } + + dsi->pdev = dsidev; + dsi_pdev_map[dsi_module] = dsidev; + dev_set_drvdata(&dsidev->dev, dsi); + + dss_plat_data = dsidev->dev.platform_data; + board_info = dss_plat_data->board_data; + dsi->dsi_mux_pads = board_info->dsi_mux_pads; - spin_lock_init(&dsi.irq_lock); - spin_lock_init(&dsi.errors_lock); - dsi.errors = 0; + spin_lock_init(&dsi->irq_lock); + spin_lock_init(&dsi->errors_lock); + dsi->errors = 0; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock_init(&dsi.irq_stats_lock); - dsi.irq_stats.last_reset = jiffies; + spin_lock_init(&dsi->irq_stats_lock); + dsi->irq_stats.last_reset = jiffies; #endif - mutex_init(&dsi.lock); - sema_init(&dsi.bus_lock, 1); + mutex_init(&dsi->lock); + sema_init(&dsi->bus_lock, 1); - dsi.workqueue = create_singlethread_workqueue("dsi"); - if (dsi.workqueue == NULL) - return -ENOMEM; - - INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work, + INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); #ifdef DSI_CATCH_MISSING_TE - init_timer(&dsi.te_timer); - dsi.te_timer.function = dsi_te_timeout; - dsi.te_timer.data = 0; + init_timer(&dsi->te_timer); + dsi->te_timer.function = dsi_te_timeout; + dsi->te_timer.data = 0; #endif - dsi_mem = platform_get_resource(dsi.pdev, IORESOURCE_MEM, 0); + dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0); if (!dsi_mem) { DSSERR("can't get IORESOURCE_MEM DSI\n"); r = -EINVAL; goto err1; } - dsi.base = ioremap(dsi_mem->start, resource_size(dsi_mem)); - if (!dsi.base) { + dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem)); + if (!dsi->base) { DSSERR("can't ioremap DSI\n"); r = -ENOMEM; goto err1; } - dsi.irq = platform_get_irq(dsi.pdev, 0); - if (dsi.irq < 0) { + dsi->irq = platform_get_irq(dsi->pdev, 0); + if (dsi->irq < 0) { DSSERR("platform_get_irq failed\n"); r = -ENODEV; goto err2; } - r = request_irq(dsi.irq, omap_dsi_irq_handler, IRQF_SHARED, - "OMAP DSI1", dsi.pdev); + r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, + dev_name(&dsidev->dev), dsi->pdev); if (r < 0) { DSSERR("request_irq failed\n"); goto err2; } /* DSI VCs initialization */ - for (i = 0; i < ARRAY_SIZE(dsi.vc); i++) { - dsi.vc[i].mode = DSI_VC_MODE_L4; - dsi.vc[i].dssdev = NULL; - dsi.vc[i].vc_id = 0; + for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { + dsi->vc[i].mode = DSI_VC_MODE_L4; + dsi->vc[i].dssdev = NULL; + dsi->vc[i].vc_id = 0; } - dsi_calc_clock_param_ranges(); + dsi_calc_clock_param_ranges(dsidev); enable_clocks(1); - rev = dsi_read_reg(DSI_REVISION); - dev_dbg(&pdev->dev, "OMAP DSI rev %d.%d\n", + rev = dsi_read_reg(dsidev, DSI_REVISION); + dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev); + enable_clocks(0); return 0; err2: - iounmap(dsi.base); + iounmap(dsi->base); err1: - destroy_workqueue(dsi.workqueue); + kfree(dsi); +err0: return r; } -static void dsi_exit(void) +static void dsi_exit(struct platform_device *dsidev) { - if (dsi.vdds_dsi_reg != NULL) { - regulator_put(dsi.vdds_dsi_reg); - dsi.vdds_dsi_reg = NULL; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->vdds_dsi_reg != NULL) { + if (dsi->vdds_dsi_enabled) { + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } + + regulator_put(dsi->vdds_dsi_reg); + dsi->vdds_dsi_reg = NULL; } - free_irq(dsi.irq, dsi.pdev); - iounmap(dsi.base); + free_irq(dsi->irq, dsi->pdev); + iounmap(dsi->base); - destroy_workqueue(dsi.workqueue); + kfree(dsi); DSSDBG("omap_dsi_exit\n"); } /* DSI1 HW IP initialisation */ -static int omap_dsi1hw_probe(struct platform_device *pdev) +static int omap_dsi1hw_probe(struct platform_device *dsidev) { int r; - dsi.pdev = pdev; - r = dsi_init(pdev); + + r = dsi_init(dsidev); if (r) { DSSERR("Failed to initialize DSI\n"); goto err_dsi; @@ -3760,9 +4570,12 @@ err_dsi: return r; } -static int omap_dsi1hw_remove(struct platform_device *pdev) +static int omap_dsi1hw_remove(struct platform_device *dsidev) { - dsi_exit(); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi_exit(dsidev); + WARN_ON(dsi->scp_clk_refcount > 0); return 0; } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 3f1fee63c678..d9489d5c4f08 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -29,7 +29,7 @@ #include <linux/seq_file.h> #include <linux/clk.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/clock.h> #include "dss.h" #include "dss_features.h" @@ -45,7 +45,6 @@ struct dss_reg { #define DSS_REVISION DSS_REG(0x0000) #define DSS_SYSCONFIG DSS_REG(0x0010) #define DSS_SYSSTATUS DSS_REG(0x0014) -#define DSS_IRQSTATUS DSS_REG(0x0018) #define DSS_CONTROL DSS_REG(0x0040) #define DSS_SDI_CONTROL DSS_REG(0x0044) #define DSS_PLL_CONTROL DSS_REG(0x0048) @@ -75,17 +74,17 @@ static struct { struct dss_clock_info cache_dss_cinfo; struct dispc_clock_info cache_dispc_cinfo; - enum dss_clk_source dsi_clk_source; - enum dss_clk_source dispc_clk_source; - enum dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; + enum omap_dss_clk_source dsi_clk_source[MAX_NUM_DSI]; + enum omap_dss_clk_source dispc_clk_source; + enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; u32 ctx[DSS_SZ_REGS / sizeof(u32)]; } dss; static const char * const dss_generic_clk_source_names[] = { - [DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI_PLL_HSDIV_DISPC", - [DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI_PLL_HSDIV_DSI", - [DSS_CLK_SRC_FCK] = "DSS_FCK", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI_PLL_HSDIV_DISPC", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI_PLL_HSDIV_DSI", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK", }; static void dss_clk_enable_all_no_ctx(void); @@ -230,7 +229,7 @@ void dss_sdi_disable(void) REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ } -const char *dss_get_generic_clk_source_name(enum dss_clk_source clk_src) +const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src) { return dss_generic_clk_source_names[clk_src]; } @@ -246,8 +245,8 @@ void dss_dump_clocks(struct seq_file *s) seq_printf(s, "- DSS -\n"); - fclk_name = dss_get_generic_clk_source_name(DSS_CLK_SRC_FCK); - fclk_real_name = dss_feat_get_clk_source_name(DSS_CLK_SRC_FCK); + fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK); + fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); fclk_rate = dss_clk_get_rate(DSS_CLK_FCK); if (dss.dpll4_m4_ck) { @@ -286,7 +285,6 @@ void dss_dump_regs(struct seq_file *s) DUMPREG(DSS_REVISION); DUMPREG(DSS_SYSCONFIG); DUMPREG(DSS_SYSSTATUS); - DUMPREG(DSS_IRQSTATUS); DUMPREG(DSS_CONTROL); if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & @@ -300,18 +298,25 @@ void dss_dump_regs(struct seq_file *s) #undef DUMPREG } -void dss_select_dispc_clk_source(enum dss_clk_source clk_src) +void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) { + struct platform_device *dsidev; int b; u8 start, end; switch (clk_src) { - case DSS_CLK_SRC_FCK: + case OMAP_DSS_CLK_SRC_FCK: b = 0; break; - case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: b = 1; - dsi_wait_pll_hsdiv_dispc_active(); + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + b = 2; + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dispc_active(dsidev); break; default: BUG(); @@ -324,17 +329,27 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src) dss.dispc_clk_source = clk_src; } -void dss_select_dsi_clk_source(enum dss_clk_source clk_src) +void dss_select_dsi_clk_source(int dsi_module, + enum omap_dss_clk_source clk_src) { + struct platform_device *dsidev; int b; switch (clk_src) { - case DSS_CLK_SRC_FCK: + case OMAP_DSS_CLK_SRC_FCK: b = 0; break; - case DSS_CLK_SRC_DSI_PLL_HSDIV_DSI: + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI: + BUG_ON(dsi_module != 0); + b = 1; + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dsi_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI: + BUG_ON(dsi_module != 1); b = 1; - dsi_wait_pll_hsdiv_dsi_active(); + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dsi_active(dsidev); break; default: BUG(); @@ -342,25 +357,33 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src) REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ - dss.dsi_clk_source = clk_src; + dss.dsi_clk_source[dsi_module] = clk_src; } void dss_select_lcd_clk_source(enum omap_channel channel, - enum dss_clk_source clk_src) + enum omap_dss_clk_source clk_src) { + struct platform_device *dsidev; int b, ix, pos; if (!dss_has_feature(FEAT_LCD_CLK_SRC)) return; switch (clk_src) { - case DSS_CLK_SRC_FCK: + case OMAP_DSS_CLK_SRC_FCK: b = 0; break; - case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: BUG_ON(channel != OMAP_DSS_CHANNEL_LCD); b = 1; - dsi_wait_pll_hsdiv_dispc_active(); + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2); + b = 1; + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dispc_active(dsidev); break; default: BUG(); @@ -373,20 +396,26 @@ void dss_select_lcd_clk_source(enum omap_channel channel, dss.lcd_clk_source[ix] = clk_src; } -enum dss_clk_source dss_get_dispc_clk_source(void) +enum omap_dss_clk_source dss_get_dispc_clk_source(void) { return dss.dispc_clk_source; } -enum dss_clk_source dss_get_dsi_clk_source(void) +enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module) { - return dss.dsi_clk_source; + return dss.dsi_clk_source[dsi_module]; } -enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) +enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) { - int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; - return dss.lcd_clk_source[ix]; + if (dss_has_feature(FEAT_LCD_CLK_SRC)) { + int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + return dss.lcd_clk_source[ix]; + } else { + /* LCD_CLK source is the same as DISPC_FCLK source for + * OMAP2 and OMAP3 */ + return dss.dispc_clk_source; + } } /* calculate clock rates using dividers in cinfo */ @@ -659,13 +688,18 @@ static int dss_init(void) * the kernel resets it */ omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); +#ifdef CONFIG_OMAP2_DSS_SLEEP_BEFORE_RESET /* We need to wait here a bit, otherwise we sometimes start to * get synclost errors, and after that only power cycle will * restore DSS functionality. I have no idea why this happens. * And we have to wait _before_ resetting the DSS, but after * enabling clocks. + * + * This bug was at least present on OMAP3430. It's unknown + * if it happens on OMAP2 or OMAP3630. */ msleep(50); +#endif _omap_dss_reset(); @@ -700,10 +734,11 @@ static int dss_init(void) dss.dpll4_m4_ck = dpll4_m4_ck; - dss.dsi_clk_source = DSS_CLK_SRC_FCK; - dss.dispc_clk_source = DSS_CLK_SRC_FCK; - dss.lcd_clk_source[0] = DSS_CLK_SRC_FCK; - dss.lcd_clk_source[1] = DSS_CLK_SRC_FCK; + dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; + dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; + dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; + dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; + dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; dss_save_context(); @@ -1015,6 +1050,14 @@ static void core_dump_clocks(struct seq_file *s) dss.dss_video_fck }; + const char *names[5] = { + "ick", + "fck", + "sys_clk", + "tv_fck", + "video_fck" + }; + seq_printf(s, "- CORE -\n"); seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled); @@ -1022,8 +1065,11 @@ static void core_dump_clocks(struct seq_file *s) for (i = 0; i < 5; i++) { if (!clocks[i]) continue; - seq_printf(s, "%-15s\t%lu\t%d\n", + seq_printf(s, "%s (%s)%*s\t%lu\t%d\n", + names[i], clocks[i]->name, + 24 - strlen(names[i]) - strlen(clocks[i]->name), + "", clk_get_rate(clocks[i]), clocks[i]->usecount); } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index c2f582bb19c0..8ab6d43329bb 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -117,15 +117,6 @@ enum dss_clock { DSS_CLK_VIDFCK = 1 << 4, /* DSS_96M_FCLK*/ }; -enum dss_clk_source { - DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC, /* OMAP3: DSI1_PLL_FCLK - * OMAP4: PLL1_CLK1 */ - DSS_CLK_SRC_DSI_PLL_HSDIV_DSI, /* OMAP3: DSI2_PLL_FCLK - * OMAP4: PLL1_CLK2 */ - DSS_CLK_SRC_FCK, /* OMAP2/3: DSS1_ALWON_FCLK - * OMAP4: DSS_FCLK */ -}; - enum dss_hdmi_venc_clk_source_select { DSS_VENC_TV_CLK = 0, DSS_HDMI_M_PCLK = 1, @@ -236,7 +227,7 @@ void dss_clk_enable(enum dss_clock clks); void dss_clk_disable(enum dss_clock clks); unsigned long dss_clk_get_rate(enum dss_clock clk); int dss_need_ctx_restore(void); -const char *dss_get_generic_clk_source_name(enum dss_clk_source clk_src); +const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); void dss_dump_regs(struct seq_file *s); @@ -248,13 +239,14 @@ void dss_sdi_init(u8 datapairs); int dss_sdi_enable(void); void dss_sdi_disable(void); -void dss_select_dispc_clk_source(enum dss_clk_source clk_src); -void dss_select_dsi_clk_source(enum dss_clk_source clk_src); +void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src); +void dss_select_dsi_clk_source(int dsi_module, + enum omap_dss_clk_source clk_src); void dss_select_lcd_clk_source(enum omap_channel channel, - enum dss_clk_source clk_src); -enum dss_clk_source dss_get_dispc_clk_source(void); -enum dss_clk_source dss_get_dsi_clk_source(void); -enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel); + enum omap_dss_clk_source clk_src); +enum omap_dss_clk_source dss_get_dispc_clk_source(void); +enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module); +enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel); void dss_set_venc_output(enum omap_dss_venc_type type); void dss_set_dac_pwrdn_bgz(bool enable); @@ -284,31 +276,39 @@ static inline void sdi_exit(void) /* DSI */ #ifdef CONFIG_OMAP2_DSS_DSI + +struct dentry; +struct file_operations; + int dsi_init_platform_driver(void); void dsi_uninit_platform_driver(void); void dsi_dump_clocks(struct seq_file *s); -void dsi_dump_irqs(struct seq_file *s); -void dsi_dump_regs(struct seq_file *s); +void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, + const struct file_operations *debug_fops); +void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, + const struct file_operations *debug_fops); void dsi_save_context(void); void dsi_restore_context(void); int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); -unsigned long dsi_get_pll_hsdiv_dispc_rate(void); -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo); -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, - struct dsi_clock_info *cinfo, +unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); +int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo); +int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, + unsigned long req_pck, struct dsi_clock_info *cinfo, struct dispc_clock_info *dispc_cinfo); -int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, +int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv); -void dsi_pll_uninit(void); +void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, u32 fifo_size, enum omap_burst_size *burst_size, u32 *fifo_low, u32 *fifo_high); -void dsi_wait_pll_hsdiv_dispc_active(void); -void dsi_wait_pll_hsdiv_dsi_active(void); +void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); +void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); +struct platform_device *dsi_get_dsidev_from_id(int module); #else static inline int dsi_init_platform_driver(void) { @@ -317,17 +317,47 @@ static inline int dsi_init_platform_driver(void) static inline void dsi_uninit_platform_driver(void) { } -static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(void) +static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) { WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); return 0; } -static inline void dsi_wait_pll_hsdiv_dispc_active(void) +static inline int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo) +{ + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; +} +static inline int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, + bool is_tft, unsigned long req_pck, + struct dsi_clock_info *dsi_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; +} +static inline int dsi_pll_init(struct platform_device *dsidev, + bool enable_hsclk, bool enable_hsdiv) { + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; } -static inline void dsi_wait_pll_hsdiv_dsi_active(void) +static inline void dsi_pll_uninit(struct platform_device *dsidev, + bool disconnect_lanes) { } +static inline void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) +{ +} +static inline void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) +{ +} +static inline struct platform_device *dsi_get_dsidev_from_id(int module) +{ + WARN("%s: DSI not compiled in, returning platform device as NULL\n", + __func__); + return NULL; +} #endif /* DPI */ @@ -391,7 +421,8 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_dss_rotation_type rotation_type, u8 rotation, bool mirror, u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel); + enum omap_channel channel, + u32 puv_addr); bool dispc_go_busy(enum omap_channel channel); void dispc_go(enum omap_channel channel); @@ -485,13 +516,6 @@ void hdmi_panel_exit(void); int rfbi_init_platform_driver(void); void rfbi_uninit_platform_driver(void); 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(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); #else static inline int rfbi_init_platform_driver(void) diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index aa1622241d0d..1c18888e5df3 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -22,7 +22,7 @@ #include <linux/err.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -52,7 +52,7 @@ struct omap_dss_features { }; /* This struct is assigned to one of the below during initialization */ -static struct omap_dss_features *omap_current_dss_features; +static const struct omap_dss_features *omap_current_dss_features; static const struct dss_reg_field omap2_dss_reg_fields[] = { [FEAT_REG_FIRHINC] = { 11, 0 }, @@ -177,22 +177,55 @@ static const enum omap_color_mode omap3_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, }; +static const enum omap_color_mode omap4_dss_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | + OMAP_DSS_COLOR_ARGB16_1555, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBX32, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBX32, +}; + static const char * const omap2_dss_clk_source_names[] = { - [DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "N/A", - [DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "N/A", - [DSS_CLK_SRC_FCK] = "DSS_FCLK1", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "N/A", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "N/A", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK1", }; static const char * const omap3_dss_clk_source_names[] = { - [DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI1_PLL_FCLK", - [DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI2_PLL_FCLK", - [DSS_CLK_SRC_FCK] = "DSS1_ALWON_FCLK", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI1_PLL_FCLK", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI2_PLL_FCLK", + [OMAP_DSS_CLK_SRC_FCK] = "DSS1_ALWON_FCLK", }; static const char * const omap4_dss_clk_source_names[] = { - [DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "PLL1_CLK1", - [DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "PLL1_CLK2", - [DSS_CLK_SRC_FCK] = "DSS_FCLK", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "PLL1_CLK1", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "PLL1_CLK2", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK", + [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "PLL2_CLK1", + [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "PLL2_CLK2", }; static const struct dss_param_range omap2_dss_param_range[] = { @@ -226,7 +259,7 @@ static const struct dss_param_range omap4_dss_param_range[] = { }; /* OMAP2 DSS Features */ -static struct omap_dss_features omap2_dss_features = { +static const struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), @@ -244,7 +277,7 @@ static struct omap_dss_features omap2_dss_features = { }; /* OMAP3 DSS Features */ -static struct omap_dss_features omap3430_dss_features = { +static const struct omap_dss_features omap3430_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), @@ -252,7 +285,8 @@ static struct omap_dss_features omap3430_dss_features = { FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | - FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF, + FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | + FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC, .num_mgrs = 2, .num_ovls = 3, @@ -262,7 +296,7 @@ static struct omap_dss_features omap3430_dss_features = { .dss_params = omap3_dss_param_range, }; -static struct omap_dss_features omap3630_dss_features = { +static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), @@ -271,7 +305,8 @@ static struct omap_dss_features omap3630_dss_features = { FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | - FEAT_RESIZECONF, + FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | + FEAT_DSI_PLL_FREQSEL, .num_mgrs = 2, .num_ovls = 3, @@ -282,19 +317,43 @@ static struct omap_dss_features omap3630_dss_features = { }; /* OMAP4 DSS Features */ -static struct omap_dss_features omap4_dss_features = { +/* For OMAP4430 ES 1.0 revision */ +static const struct omap_dss_features omap4430_es1_0_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), .has_feature = FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC, + FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | + FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | + FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2, .num_mgrs = 3, .num_ovls = 3, .supported_displays = omap4_dss_supported_displays, - .supported_color_modes = omap3_dss_supported_color_modes, + .supported_color_modes = omap4_dss_supported_color_modes, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, +}; + +/* For all the other OMAP4 versions */ +static const struct omap_dss_features omap4_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), + + .has_feature = + FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | + FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | + FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | + FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | + FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | + FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2, + + .num_mgrs = 3, + .num_ovls = 3, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap4_dss_supported_color_modes, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, }; @@ -337,7 +396,7 @@ bool dss_feat_color_mode_supported(enum omap_plane plane, color_mode; } -const char *dss_feat_get_clk_source_name(enum dss_clk_source id) +const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id) { return omap_current_dss_features->clksrc_names[id]; } @@ -365,6 +424,10 @@ void dss_features_init(void) omap_current_dss_features = &omap3630_dss_features; else if (cpu_is_omap34xx()) omap_current_dss_features = &omap3430_dss_features; - else + else if (omap_rev() == OMAP4430_REV_ES1_0) + omap_current_dss_features = &omap4430_es1_0_dss_features; + else if (cpu_is_omap44xx()) omap_current_dss_features = &omap4_dss_features; + else + DSSWARN("Unsupported OMAP version"); } diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index 12e9c4ef0dec..07b346f7d916 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -23,23 +23,34 @@ #define MAX_DSS_MANAGERS 3 #define MAX_DSS_OVERLAYS 3 #define MAX_DSS_LCD_MANAGERS 2 +#define MAX_NUM_DSI 2 /* 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, - FEAT_LINEBUFFERSPLIT = 1 << 8, - FEAT_ROWREPEATENABLE = 1 << 9, - FEAT_RESIZECONF = 1 << 10, + 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, + FEAT_LINEBUFFERSPLIT = 1 << 8, + FEAT_ROWREPEATENABLE = 1 << 9, + FEAT_RESIZECONF = 1 << 10, /* Independent core clk divider */ - FEAT_CORE_CLK_DIV = 1 << 11, - FEAT_LCD_CLK_SRC = 1 << 12, + FEAT_CORE_CLK_DIV = 1 << 11, + FEAT_LCD_CLK_SRC = 1 << 12, + /* DSI-PLL power command 0x3 is not working */ + FEAT_DSI_PLL_PWR_BUG = 1 << 13, + FEAT_DSI_PLL_FREQSEL = 1 << 14, + FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15, + FEAT_DSI_VC_OCP_WIDTH = 1 << 16, + FEAT_DSI_REVERSE_TXCLKESC = 1 << 17, + FEAT_DSI_GNQ = 1 << 18, + FEAT_HDMI_CTS_SWMODE = 1 << 19, + FEAT_HANDLE_UV_SEPARATE = 1 << 20, + FEAT_ATTR2 = 1 << 21, }; /* DSS register field id */ @@ -77,7 +88,7 @@ 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); -const char *dss_feat_get_clk_source_name(enum dss_clk_source id); +const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); 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/hdmi.c b/drivers/video/omap2/dss/hdmi.c index a981def8099a..b0555f4f0a78 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -29,10 +29,16 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/string.h> -#include <plat/display.h> +#include <video/omapdss.h> +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#include <sound/soc.h> +#include <sound/pcm_params.h> +#endif #include "dss.h" #include "hdmi.h" +#include "dss_features.h" static struct { struct mutex lock; @@ -1052,25 +1058,26 @@ static void update_hdmi_timings(struct hdmi_config *cfg, cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; } -static void hdmi_compute_pll(unsigned long clkin, int phy, - int n, struct hdmi_pll_info *pi) +static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, + struct hdmi_pll_info *pi) { - unsigned long refclk; + unsigned long clkin, refclk; u32 mf; + clkin = dss_clk_get_rate(DSS_CLK_SYSCK) / 10000; /* * Input clock is predivided by N + 1 * out put of which is reference clk */ - refclk = clkin / (n + 1); - pi->regn = n; + pi->regn = dssdev->clocks.hdmi.regn; + refclk = clkin / (pi->regn + 1); /* * multiplier is pixel_clk/ref_clk * Multiplying by 100 to avoid fractional part removal */ - pi->regm = (phy * 100/(refclk))/100; - pi->regm2 = 1; + pi->regm = (phy * 100 / (refclk)) / 100; + pi->regm2 = dssdev->clocks.hdmi.regm2; /* * fractional multiplier is remainder of the difference between @@ -1078,14 +1085,14 @@ static void hdmi_compute_pll(unsigned long clkin, int phy, * multiplied by 2^18(262144) divided by the reference clock */ mf = (phy - pi->regm * refclk) * 262144; - pi->regmf = mf/(refclk); + pi->regmf = mf / (refclk); /* * Dcofreq should be set to 1 if required pixel clock * is greater than 1000MHz */ pi->dcofreq = phy > 1000 * 100; - pi->regsd = ((pi->regm * clkin / 10) / ((n + 1) * 250) + 5) / 10; + pi->regsd = ((pi->regm * clkin / 10) / ((pi->regn + 1) * 250) + 5) / 10; DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); @@ -1106,7 +1113,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) int r, code = 0; struct hdmi_pll_info pll_data; struct omap_video_timings *p; - int clkin, n, phy; + unsigned long phy; hdmi_enable_clocks(1); @@ -1126,11 +1133,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dssdev->panel.timings = cea_vesa_timings[code].timings; update_hdmi_timings(&hdmi.cfg, p, code); - clkin = 3840; /* 38.4 MHz */ - n = 15; /* this is a constant for our math */ phy = p->pixel_clock; - hdmi_compute_pll(clkin, phy, n, &pll_data); + hdmi_compute_pll(dssdev, phy, &pll_data); hdmi_wp_video_start(0); @@ -1160,7 +1165,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) * dynamically by user. This can be moved to single location , say * Boardfile. */ - dss_select_dispc_clk_source(DSS_CLK_SRC_FCK); + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); /* bypass TV gamma table */ dispc_enable_gamma_table(0); @@ -1275,10 +1280,420 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +static void hdmi_wp_audio_config_format( + struct hdmi_audio_format *aud_fmt) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_format\n"); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CFG); + r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); + r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); + r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); + r = FLD_MOD(r, aud_fmt->type, 4, 4); + r = FLD_MOD(r, aud_fmt->justification, 3, 3); + r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); + r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); + r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CFG, r); +} + +static void hdmi_wp_audio_config_dma(struct hdmi_audio_dma *aud_dma) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_dma\n"); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CFG2); + r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); + r = FLD_MOD(r, aud_dma->block_size, 7, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CFG2, r); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CTRL); + r = FLD_MOD(r, aud_dma->mode, 9, 9); + r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CTRL, r); +} + +static void hdmi_core_audio_config(struct hdmi_core_audio_config *cfg) +{ + u32 r; + + /* audio clock recovery parameters */ + r = hdmi_read_reg(HDMI_CORE_AV_ACR_CTRL); + r = FLD_MOD(r, cfg->use_mclk, 2, 2); + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_ACR_CTRL, r); + + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); + + if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) { + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); + } else { + /* + * HDMI IP uses this configuration to divide the MCLK to + * update CTS value. + */ + REG_FLD_MOD(HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + /* Configure clock for audio packets */ + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_1, + cfg->aud_par_busclk, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_2, + (cfg->aud_par_busclk >> 8), 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_3, + (cfg->aud_par_busclk >> 16), 7, 0); + } + + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ + REG_FLD_MOD(HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); + + /* I2S parameters */ + REG_FLD_MOD(HDMI_CORE_AV_I2S_CHST4, cfg->freq_sample, 3, 0); + + r = hdmi_read_reg(HDMI_CORE_AV_I2S_IN_CTRL); + r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); + r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); + r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); + r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); + r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); + r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); + r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); + r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_I2S_IN_CTRL, r); + + r = hdmi_read_reg(HDMI_CORE_AV_I2S_CHST5); + r = FLD_MOD(r, cfg->freq_sample, 7, 4); + r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); + r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_I2S_CHST5, r); + + REG_FLD_MOD(HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); + + /* Audio channels and mode parameters */ + REG_FLD_MOD(HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1); + r = hdmi_read_reg(HDMI_CORE_AV_AUD_MODE); + r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4); + r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3); + r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); + r = FLD_MOD(r, cfg->en_spdif, 1, 1); + hdmi_write_reg(HDMI_CORE_AV_AUD_MODE, r); +} + +static void hdmi_core_audio_infoframe_config( + struct hdmi_core_infoframe_audio *info_aud) +{ + u8 val; + u8 sum = 0, checksum = 0; + + /* + * Set audio info frame type, version and length as + * described in HDMI 1.4a Section 8.2.2 specification. + * Checksum calculation is defined in Section 5.3.5. + */ + hdmi_write_reg(HDMI_CORE_AV_AUDIO_TYPE, 0x84); + hdmi_write_reg(HDMI_CORE_AV_AUDIO_VERS, 0x01); + hdmi_write_reg(HDMI_CORE_AV_AUDIO_LEN, 0x0a); + sum += 0x84 + 0x001 + 0x00a; + + val = (info_aud->db1_coding_type << 4) + | (info_aud->db1_channel_count - 1); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(0), val); + sum += val; + + val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(1), val); + sum += val; + + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + + val = info_aud->db4_channel_alloc; + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(3), val); + sum += val; + + val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(4), val); + sum += val; + + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(5), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(6), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(7), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(8), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(9), 0x00); + + checksum = 0x100 - sum; + hdmi_write_reg(HDMI_CORE_AV_AUDIO_CHSUM, checksum); + + /* + * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing + * is available. + */ +} + +static int hdmi_config_audio_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 r; + u32 deep_color = 0; + u32 pclk = hdmi.cfg.timings.timings.pixel_clock; + + if (n == NULL || cts == NULL) + return -EINVAL; + /* + * Obtain current deep color configuration. This needed + * to calculate the TMDS clock based on the pixel clock. + */ + r = REG_GET(HDMI_WP_VIDEO_CFG, 1, 0); + switch (r) { + case 1: /* No deep color selected */ + deep_color = 100; + break; + case 2: /* 10-bit deep color selected */ + deep_color = 125; + break; + case 3: /* 12-bit deep color selected */ + deep_color = 150; + break; + default: + return -EINVAL; + } + + switch (sample_freq) { + case 32000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 6144; + break; + default: + *n = 0; + return -EINVAL; + } + + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + + return 0; +} + +static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core_cfg; + struct hdmi_core_infoframe_audio aud_if_cfg; + int err, n, cts; + enum hdmi_core_audio_sample_freq sample_freq; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + core_cfg.i2s_cfg.word_max_length = + HDMI_AUDIO_I2S_MAX_WORD_20BITS; + core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; + core_cfg.i2s_cfg.in_length_bits = + HDMI_AUDIO_I2S_INPUT_LENGTH_16; + core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + audio_dma.transfer_size = 0x10; + break; + case SNDRV_PCM_FORMAT_S24_LE: + core_cfg.i2s_cfg.word_max_length = + HDMI_AUDIO_I2S_MAX_WORD_24BITS; + core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; + core_cfg.i2s_cfg.in_length_bits = + HDMI_AUDIO_I2S_INPUT_LENGTH_24; + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + audio_dma.transfer_size = 0x20; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 32000: + sample_freq = HDMI_AUDIO_FS_32000; + break; + case 44100: + sample_freq = HDMI_AUDIO_FS_44100; + break; + case 48000: + sample_freq = HDMI_AUDIO_FS_48000; + break; + default: + return -EINVAL; + } + + err = hdmi_config_audio_acr(params_rate(params), &n, &cts); + if (err < 0) + return err; + + /* Audio wrapper config */ + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + audio_format.active_chnnls_msk = 0x03; + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* Disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; + + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + hdmi_wp_audio_config_dma(&audio_dma); + hdmi_wp_audio_config_format(&audio_format); + + /* + * I2S config + */ + core_cfg.i2s_cfg.en_high_bitrate_aud = false; + /* Only used with high bitrate audio */ + core_cfg.i2s_cfg.cbit_order = false; + /* Serial data and word select should change on sck rising edge */ + core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + /* Set I2S word select polarity */ + core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; + core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + /* Set serial data to word select shift. See Phillips spec. */ + core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + /* Enable one of the four available serial data channels */ + core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + + /* Core audio config */ + core_cfg.freq_sample = sample_freq; + core_cfg.n = n; + core_cfg.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core_cfg.aud_par_busclk = 0; + core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core_cfg.use_mclk = false; + } else { + core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); + core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core_cfg.use_mclk = true; + core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; + } + core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; + core_cfg.en_spdif = false; + /* Use sample frequency from channel status word */ + core_cfg.fs_override = true; + /* Enable ACR packets */ + core_cfg.en_acr_pkt = true; + /* Disable direct streaming digital audio */ + core_cfg.en_dsd_audio = false; + /* Use parallel audio interface */ + core_cfg.en_parallel_aud_input = true; + + hdmi_core_audio_config(&core_cfg); + + /* + * Configure packet + * info frame audio see doc CEA861-D page 74 + */ + aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; + aud_if_cfg.db1_channel_count = 2; + aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; + aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; + aud_if_cfg.db4_channel_alloc = 0x00; + aud_if_cfg.db5_downmix_inh = false; + aud_if_cfg.db5_lsv = 0; + + hdmi_core_audio_infoframe_config(&aud_if_cfg); + return 0; +} + +static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int err = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 1, 0, 0); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 31, 31); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 30, 30); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 0, 0, 0); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 30, 30); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 31, 31); + break; + default: + err = -EINVAL; + } + return err; +} + +static int hdmi_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!hdmi.mode) { + pr_err("Current video settings do not support audio.\n"); + return -EIO; + } + return 0; +} + +static struct snd_soc_codec_driver hdmi_audio_codec_drv = { +}; + +static struct snd_soc_dai_ops hdmi_audio_codec_ops = { + .hw_params = hdmi_audio_hw_params, + .trigger = hdmi_audio_trigger, + .startup = hdmi_audio_startup, +}; + +static struct snd_soc_dai_driver hdmi_codec_dai_drv = { + .name = "hdmi-audio-codec", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &hdmi_audio_codec_ops, +}; +#endif + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { struct resource *hdmi_mem; +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + int ret; +#endif hdmi.pdata = pdev->dev.platform_data; hdmi.pdev = pdev; @@ -1300,6 +1715,17 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_panel_init(); +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + + /* Register ASoC codec DAI */ + ret = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, + &hdmi_codec_dai_drv, 1); + if (ret) { + DSSERR("can't register ASoC HDMI audio codec\n"); + return ret; + } +#endif return 0; } @@ -1307,6 +1733,11 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev) { hdmi_panel_exit(); +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + snd_soc_unregister_codec(&pdev->dev); +#endif + iounmap(hdmi.base_wp); return 0; diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/hdmi.h index 9887ab96da3c..c885f9cb0659 100644 --- a/drivers/video/omap2/dss/hdmi.h +++ b/drivers/video/omap2/dss/hdmi.h @@ -22,7 +22,7 @@ #define _OMAP4_DSS_HDMI_H_ #include <linux/string.h> -#include <plat/display.h> +#include <video/omapdss.h> #define HDMI_WP 0x0 #define HDMI_CORE_SYS 0x400 @@ -48,6 +48,10 @@ struct hdmi_reg { u16 idx; }; #define HDMI_WP_VIDEO_TIMING_H HDMI_WP_REG(0x68) #define HDMI_WP_VIDEO_TIMING_V HDMI_WP_REG(0x6C) #define HDMI_WP_WP_CLK HDMI_WP_REG(0x70) +#define HDMI_WP_AUDIO_CFG HDMI_WP_REG(0x80) +#define HDMI_WP_AUDIO_CFG2 HDMI_WP_REG(0x84) +#define HDMI_WP_AUDIO_CTRL HDMI_WP_REG(0x88) +#define HDMI_WP_AUDIO_DATA HDMI_WP_REG(0x8C) /* HDMI IP Core System */ #define HDMI_CORE_SYS_REG(idx) HDMI_REG(HDMI_CORE_SYS + idx) @@ -105,6 +109,8 @@ struct hdmi_reg { u16 idx; }; #define HDMI_CORE_AV_AVI_DBYTE_NELEMS HDMI_CORE_AV_REG(15) #define HDMI_CORE_AV_SPD_DBYTE HDMI_CORE_AV_REG(0x190) #define HDMI_CORE_AV_SPD_DBYTE_NELEMS HDMI_CORE_AV_REG(27) +#define HDMI_CORE_AV_AUD_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x210) +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS HDMI_CORE_AV_REG(10) #define HDMI_CORE_AV_MPEG_DBYTE HDMI_CORE_AV_REG(0x290) #define HDMI_CORE_AV_MPEG_DBYTE_NELEMS HDMI_CORE_AV_REG(27) #define HDMI_CORE_AV_GEN_DBYTE HDMI_CORE_AV_REG(0x300) @@ -153,6 +159,10 @@ struct hdmi_reg { u16 idx; }; #define HDMI_CORE_AV_SPD_VERS HDMI_CORE_AV_REG(0x184) #define HDMI_CORE_AV_SPD_LEN HDMI_CORE_AV_REG(0x188) #define HDMI_CORE_AV_SPD_CHSUM HDMI_CORE_AV_REG(0x18C) +#define HDMI_CORE_AV_AUDIO_TYPE HDMI_CORE_AV_REG(0x200) +#define HDMI_CORE_AV_AUDIO_VERS HDMI_CORE_AV_REG(0x204) +#define HDMI_CORE_AV_AUDIO_LEN HDMI_CORE_AV_REG(0x208) +#define HDMI_CORE_AV_AUDIO_CHSUM HDMI_CORE_AV_REG(0x20C) #define HDMI_CORE_AV_MPEG_TYPE HDMI_CORE_AV_REG(0x280) #define HDMI_CORE_AV_MPEG_VERS HDMI_CORE_AV_REG(0x284) #define HDMI_CORE_AV_MPEG_LEN HDMI_CORE_AV_REG(0x288) @@ -272,7 +282,7 @@ enum hdmi_core_packet_ctrl { HDMI_PACKETREPEATOFF = 0 }; -/* INFOFRAME_AVI_ definitions */ +/* INFOFRAME_AVI_ and INFOFRAME_AUDIO_ definitions */ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB1Y_RGB = 0, HDMI_INFOFRAME_AVI_DB1Y_YUV422 = 1, @@ -317,7 +327,36 @@ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB5PR_7 = 6, HDMI_INFOFRAME_AVI_DB5PR_8 = 7, HDMI_INFOFRAME_AVI_DB5PR_9 = 8, - HDMI_INFOFRAME_AVI_DB5PR_10 = 9 + HDMI_INFOFRAME_AVI_DB5PR_10 = 9, + HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, + HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, + HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, + HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, + HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, + HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, + HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, + HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, + HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, + HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, + HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, + HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, + HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, + HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, + HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, + HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, + HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, + HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, + HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, + HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, + HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, + HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, + HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, + HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, + HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, + HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 }; enum hdmi_packing_mode { @@ -327,6 +366,121 @@ enum hdmi_packing_mode { HDMI_PACK_ALREADYPACKED = 7 }; +enum hdmi_core_audio_sample_freq { + HDMI_AUDIO_FS_32000 = 0x3, + HDMI_AUDIO_FS_44100 = 0x0, + HDMI_AUDIO_FS_48000 = 0x2, + HDMI_AUDIO_FS_88200 = 0x8, + HDMI_AUDIO_FS_96000 = 0xA, + HDMI_AUDIO_FS_176400 = 0xC, + HDMI_AUDIO_FS_192000 = 0xE, + HDMI_AUDIO_FS_NOT_INDICATED = 0x1 +}; + +enum hdmi_core_audio_layout { + HDMI_AUDIO_LAYOUT_2CH = 0, + HDMI_AUDIO_LAYOUT_8CH = 1 +}; + +enum hdmi_core_cts_mode { + HDMI_AUDIO_CTS_MODE_HW = 0, + HDMI_AUDIO_CTS_MODE_SW = 1 +}; + +enum hdmi_stereo_channels { + HDMI_AUDIO_STEREO_NOCHANNELS = 0, + HDMI_AUDIO_STEREO_ONECHANNEL = 1, + HDMI_AUDIO_STEREO_TWOCHANNELS = 2, + HDMI_AUDIO_STEREO_THREECHANNELS = 3, + HDMI_AUDIO_STEREO_FOURCHANNELS = 4 +}; + +enum hdmi_audio_type { + HDMI_AUDIO_TYPE_LPCM = 0, + HDMI_AUDIO_TYPE_IEC = 1 +}; + +enum hdmi_audio_justify { + HDMI_AUDIO_JUSTIFY_LEFT = 0, + HDMI_AUDIO_JUSTIFY_RIGHT = 1 +}; + +enum hdmi_audio_sample_order { + HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0, + HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1 +}; + +enum hdmi_audio_samples_perword { + HDMI_AUDIO_ONEWORD_ONESAMPLE = 0, + HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1 +}; + +enum hdmi_audio_sample_size { + HDMI_AUDIO_SAMPLE_16BITS = 0, + HDMI_AUDIO_SAMPLE_24BITS = 1 +}; + +enum hdmi_audio_transf_mode { + HDMI_AUDIO_TRANSF_DMA = 0, + HDMI_AUDIO_TRANSF_IRQ = 1 +}; + +enum hdmi_audio_blk_strt_end_sig { + HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0, + HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1 +}; + +enum hdmi_audio_i2s_config { + HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, + HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, + HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, + HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, + HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, + HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, + HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, + HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, + HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, + HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, + HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, + HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, + HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, + HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, + HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, + HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, + HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, + HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, + HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, + HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, + HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, + HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, + HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, + HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, + HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, + HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, + HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, + HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, + HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, + HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, + HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, + HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, + HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, + HDMI_AUDIO_I2S_SD0_EN = 1, + HDMI_AUDIO_I2S_SD1_EN = 1 << 1, + HDMI_AUDIO_I2S_SD2_EN = 1 << 2, + HDMI_AUDIO_I2S_SD3_EN = 1 << 3, +}; + +enum hdmi_audio_mclk_mode { + HDMI_AUDIO_MCLK_128FS = 0, + HDMI_AUDIO_MCLK_256FS = 1, + HDMI_AUDIO_MCLK_384FS = 2, + HDMI_AUDIO_MCLK_512FS = 3, + HDMI_AUDIO_MCLK_768FS = 4, + HDMI_AUDIO_MCLK_1024FS = 5, + HDMI_AUDIO_MCLK_1152FS = 6, + HDMI_AUDIO_MCLK_192FS = 7 +}; + struct hdmi_core_video_config { enum hdmi_core_inputbus_width ip_bus_width; enum hdmi_core_dither_trunc op_dither_truc; @@ -376,6 +530,19 @@ struct hdmi_core_infoframe_avi { u16 db12_13_pixel_sofright; /* Pixel number start of right bar */ }; +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_audio { + u8 db1_coding_type; + u8 db1_channel_count; + u8 db2_sample_freq; + u8 db2_sample_size; + u8 db4_channel_alloc; + bool db5_downmix_inh; + u8 db5_lsv; /* Level shift values for downmix */ +}; struct hdmi_core_packet_enable_repeat { u32 audio_pkt; @@ -412,4 +579,53 @@ struct hdmi_config { struct hdmi_cm cm; }; +struct hdmi_audio_format { + enum hdmi_stereo_channels stereo_channels; + u8 active_chnnls_msk; + enum hdmi_audio_type type; + enum hdmi_audio_justify justification; + enum hdmi_audio_sample_order sample_order; + enum hdmi_audio_samples_perword samples_per_word; + enum hdmi_audio_sample_size sample_size; + enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end; +}; + +struct hdmi_audio_dma { + u8 transfer_size; + u8 block_size; + enum hdmi_audio_transf_mode mode; + u16 fifo_threshold; +}; + +struct hdmi_core_audio_i2s_config { + u8 word_max_length; + u8 word_length; + u8 in_length_bits; + u8 justification; + u8 en_high_bitrate_aud; + u8 sck_edge_mode; + u8 cbit_order; + u8 vbit; + u8 ws_polarity; + u8 direction; + u8 shift; + u8 active_sds; +}; + +struct hdmi_core_audio_config { + struct hdmi_core_audio_i2s_config i2s_cfg; + enum hdmi_core_audio_sample_freq freq_sample; + bool fs_override; + u32 n; + u32 cts; + u32 aud_par_busclk; + enum hdmi_core_audio_layout layout; + enum hdmi_core_cts_mode cts_mode; + bool use_mclk; + enum hdmi_audio_mclk_mode mclk_mode; + bool en_acr_pkt; + bool en_dsd_audio; + bool en_parallel_aud_input; + bool en_spdif; +}; #endif diff --git a/drivers/video/omap2/dss/hdmi_omap4_panel.c b/drivers/video/omap2/dss/hdmi_omap4_panel.c index ffb5de94131f..7d4f2bd7c506 100644 --- a/drivers/video/omap2/dss/hdmi_omap4_panel.c +++ b/drivers/video/omap2/dss/hdmi_omap4_panel.c @@ -24,7 +24,7 @@ #include <linux/io.h> #include <linux/mutex.h> #include <linux/module.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index bcd37ec86952..9aeea50e33ff 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -29,7 +29,7 @@ #include <linux/spinlock.h> #include <linux/jiffies.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -393,6 +393,7 @@ struct overlay_cache_data { u32 paddr; void __iomem *vaddr; + u32 p_uv_addr; /* relevant for NV12 format only */ u16 screen_width; u16 width; u16 height; @@ -775,10 +776,17 @@ static int configure_overlay(enum omap_plane plane) } switch (c->color_mode) { + case OMAP_DSS_COLOR_NV12: + bpp = 8; + break; case OMAP_DSS_COLOR_RGB16: case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: + case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_XRGB16_1555: bpp = 16; break; @@ -854,7 +862,8 @@ static int configure_overlay(enum omap_plane plane) c->mirror, c->global_alpha, c->pre_mult_alpha, - c->channel); + c->channel, + c->p_uv_addr); if (r) { /* this shouldn't happen */ @@ -1269,6 +1278,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) oc->paddr = ovl->info.paddr; oc->vaddr = ovl->info.vaddr; + oc->p_uv_addr = ovl->info.p_uv_addr; oc->screen_width = ovl->info.screen_width; oc->width = ovl->info.width; oc->height = ovl->info.height; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index f1aca6d04011..0f08025b1f0e 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -31,7 +31,7 @@ #include <linux/delay.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -201,12 +201,16 @@ static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, size_t size) { - int r; + int r, enable; struct omap_overlay_info info; ovl->get_overlay_info(ovl, &info); - info.enabled = simple_strtoul(buf, NULL, 10); + r = kstrtoint(buf, 0, &enable); + if (r) + return r; + + info.enabled = !!enable; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -231,8 +235,13 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, const char *buf, size_t size) { int r; + u8 alpha; struct omap_overlay_info info; + r = kstrtou8(buf, 0, &alpha); + if (r) + return r; + ovl->get_overlay_info(ovl, &info); /* Video1 plane does not support global alpha @@ -242,7 +251,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, ovl->id == OMAP_DSS_VIDEO1) info.global_alpha = 255; else - info.global_alpha = simple_strtoul(buf, NULL, 10); + info.global_alpha = alpha; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -268,8 +277,13 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, const char *buf, size_t size) { int r; + u8 alpha; struct omap_overlay_info info; + r = kstrtou8(buf, 0, &alpha); + if (r) + return r; + ovl->get_overlay_info(ovl, &info); /* only GFX and Video2 plane support pre alpha multiplied @@ -279,7 +293,7 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, ovl->id == OMAP_DSS_VIDEO1) info.pre_mult_alpha = 0; else - info.pre_mult_alpha = simple_strtoul(buf, NULL, 10); + info.pre_mult_alpha = alpha; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -491,13 +505,18 @@ static int omap_dss_set_manager(struct omap_overlay *ovl, ovl->manager = mgr; dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); - /* XXX: on manual update display, in auto update mode, a bug happens - * here. When an overlay is first enabled on LCD, then it's disabled, - * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT - * errors. Waiting before changing the channel_out fixes it. I'm - * guessing that the overlay is still somehow being used for the LCD, - * but I don't understand how or why. */ - msleep(40); + /* XXX: When there is an overlay on a DSI manual update display, and + * the overlay is first disabled, then moved to tv, and enabled, we + * seem to get SYNC_LOST_DIGIT error. + * + * Waiting doesn't seem to help, but updating the manual update display + * after disabling the overlay seems to fix this. This hints that the + * overlay is perhaps somehow tied to the LCD output until the output + * is updated. + * + * Userspace workaround for this is to update the LCD after disabling + * the overlay, but before moving the overlay to TV. + */ dispc_set_channel_out(ovl->id, mgr->id); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 5ea17f49c611..c06fbe0bc678 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -32,8 +32,9 @@ #include <linux/ktime.h> #include <linux/hrtimer.h> #include <linux/seq_file.h> +#include <linux/semaphore.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" struct rfbi_reg { u16 idx; }; @@ -65,9 +66,6 @@ struct rfbi_reg { u16 idx; }; #define REG_FLD_MOD(idx, val, start, end) \ rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) -/* To work around an RFBI transfer rate limitation */ -#define OMAP_RFBI_RATE_LIMIT 1 - enum omap_rfbi_cycleformat { OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, @@ -89,11 +87,6 @@ enum omap_rfbi_parallelmode { OMAP_DSS_RFBI_PARALLELMODE_16 = 3, }; -enum update_cmd { - RFBI_CMD_UPDATE = 0, - RFBI_CMD_SYNC = 1, -}; - static int rfbi_convert_timings(struct rfbi_timings *t); static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); @@ -114,20 +107,9 @@ static struct { struct omap_dss_device *dssdev[2]; - struct kfifo cmd_fifo; - spinlock_t cmd_lock; - struct completion cmd_done; - atomic_t cmd_fifo_full; - atomic_t cmd_pending; + struct semaphore bus_lock; } rfbi; -struct update_region { - u16 x; - u16 y; - u16 w; - u16 h; -}; - static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) { __raw_writel(val, rfbi.base + idx.idx); @@ -146,9 +128,20 @@ static void rfbi_enable_clocks(bool enable) dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); } +void rfbi_bus_lock(void) +{ + down(&rfbi.bus_lock); +} +EXPORT_SYMBOL(rfbi_bus_lock); + +void rfbi_bus_unlock(void) +{ + up(&rfbi.bus_lock); +} +EXPORT_SYMBOL(rfbi_bus_unlock); + void omap_rfbi_write_command(const void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -172,13 +165,11 @@ void omap_rfbi_write_command(const void *buf, u32 len) default: BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_command); void omap_rfbi_read_data(void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -206,13 +197,11 @@ void omap_rfbi_read_data(void *buf, u32 len) default: BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_read_data); void omap_rfbi_write_data(const void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -237,7 +226,6 @@ void omap_rfbi_write_data(const void *buf, u32 len) BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_data); @@ -249,8 +237,6 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, int horiz_offset = scr_width - w; int i; - rfbi_enable_clocks(1); - if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { const u16 __iomem *pd = buf; @@ -295,12 +281,10 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, } else { BUG(); } - - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_pixels); -void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, +static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, u16 height, void (*callback)(void *data), void *data) { u32 l; @@ -317,8 +301,6 @@ void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, rfbi.framedone_callback = callback; rfbi.framedone_callback_data = data; - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_PIXEL_CNT, width * height); l = rfbi_read_reg(RFBI_CONTROL); @@ -337,15 +319,11 @@ static void framedone_callback(void *data, u32 mask) REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); - rfbi_enable_clocks(0); - callback = rfbi.framedone_callback; rfbi.framedone_callback = NULL; if (callback != NULL) callback(rfbi.framedone_callback_data); - - atomic_set(&rfbi.cmd_pending, 0); } #if 1 /* VERBOSE */ @@ -435,7 +413,7 @@ static int calc_extif_timings(struct rfbi_timings *t) } -void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) +static void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) { int r; @@ -447,7 +425,6 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) BUG_ON(!t->converted); - rfbi_enable_clocks(1); rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); @@ -456,7 +433,6 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) (t->tim[2] ? 1 : 0), 4, 4); rfbi_print_timings(); - rfbi_enable_clocks(0); } static int ps_to_rfbi_ticks(int time, int div) @@ -472,59 +448,6 @@ static int ps_to_rfbi_ticks(int time, int div) return ret; } -#ifdef OMAP_RFBI_RATE_LIMIT -unsigned long rfbi_get_max_tx_rate(void) -{ - unsigned long l4_rate, dss1_rate; - int min_l4_ticks = 0; - int i; - - /* According to TI this can't be calculated so make the - * adjustments for a couple of known frequencies and warn for - * others. - */ - static const struct { - unsigned long l4_clk; /* HZ */ - unsigned long dss1_clk; /* HZ */ - unsigned long min_l4_ticks; - } ftab[] = { - { 55, 132, 7, }, /* 7.86 MPix/s */ - { 110, 110, 12, }, /* 9.16 MPix/s */ - { 110, 132, 10, }, /* 11 Mpix/s */ - { 120, 120, 10, }, /* 12 Mpix/s */ - { 133, 133, 10, }, /* 13.3 Mpix/s */ - }; - - l4_rate = rfbi.l4_khz / 1000; - dss1_rate = dss_clk_get_rate(DSS_CLK_FCK) / 1000000; - - for (i = 0; i < ARRAY_SIZE(ftab); i++) { - /* Use a window instead of an exact match, to account - * for different DPLL multiplier / divider pairs. - */ - if (abs(ftab[i].l4_clk - l4_rate) < 3 && - abs(ftab[i].dss1_clk - dss1_rate) < 3) { - min_l4_ticks = ftab[i].min_l4_ticks; - break; - } - } - if (i == ARRAY_SIZE(ftab)) { - /* Can't be sure, return anyway the maximum not - * rate-limited. This might cause a problem only for the - * tearing synchronisation. - */ - DSSERR("can't determine maximum RFBI transfer rate\n"); - return rfbi.l4_khz * 1000; - } - return rfbi.l4_khz * 1000 / min_l4_ticks; -} -#else -int rfbi_get_max_tx_rate(void) -{ - return rfbi.l4_khz * 1000; -} -#endif - static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) { *clk_period = 1000000000 / rfbi.l4_khz; @@ -644,7 +567,6 @@ int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", mode, hs, vs, hs_pol_inv, vs_pol_inv); - rfbi_enable_clocks(1); rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); @@ -657,7 +579,6 @@ int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, l &= ~(1 << 20); else l |= 1 << 20; - rfbi_enable_clocks(0); return 0; } @@ -672,7 +593,6 @@ int omap_rfbi_enable_te(bool enable, unsigned line) if (line > (1 << 11) - 1) return -EINVAL; - rfbi_enable_clocks(1); l = rfbi_read_reg(RFBI_CONFIG(0)); l &= ~(0x3 << 2); if (enable) { @@ -682,50 +602,12 @@ int omap_rfbi_enable_te(bool enable, unsigned line) rfbi.te_enabled = 0; rfbi_write_reg(RFBI_CONFIG(0), l); rfbi_write_reg(RFBI_LINE_NUMBER, line); - rfbi_enable_clocks(0); return 0; } EXPORT_SYMBOL(omap_rfbi_enable_te); -#if 0 -static void rfbi_enable_config(int enable1, int enable2) -{ - u32 l; - int cs = 0; - - if (enable1) - cs |= 1<<0; - if (enable2) - cs |= 1<<1; - - rfbi_enable_clocks(1); - - l = rfbi_read_reg(RFBI_CONTROL); - - l = FLD_MOD(l, cs, 3, 2); - l = FLD_MOD(l, 0, 1, 1); - - rfbi_write_reg(RFBI_CONTROL, l); - - - l = rfbi_read_reg(RFBI_CONFIG(0)); - l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ - /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ - /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ - - l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ - l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ - l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ - - l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); - rfbi_write_reg(RFBI_CONFIG(0), l); - - rfbi_enable_clocks(0); -} -#endif - -int rfbi_configure(int rfbi_module, int bpp, int lines) +static int rfbi_configure(int rfbi_module, int bpp, int lines) { u32 l; int cycle1 = 0, cycle2 = 0, cycle3 = 0; @@ -821,8 +703,6 @@ int rfbi_configure(int rfbi_module, int bpp, int lines) break; } - rfbi_enable_clocks(1); - REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ l = 0; @@ -856,11 +736,15 @@ int rfbi_configure(int rfbi_module, int bpp, int lines) DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", bpp, lines, cycle1, cycle2, cycle3); - rfbi_enable_clocks(0); - return 0; } -EXPORT_SYMBOL(rfbi_configure); + +int omap_rfbi_configure(struct omap_dss_device *dssdev, int pixel_size, + int data_lines) +{ + return rfbi_configure(dssdev->phy.rfbi.channel, pixel_size, data_lines); +} +EXPORT_SYMBOL(omap_rfbi_configure); int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) @@ -960,6 +844,8 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) { int r; + rfbi_enable_clocks(1); + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -1002,6 +888,8 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) omap_dispc_unregister_isr(framedone_callback, NULL, DISPC_IRQ_FRAMEDONE); omap_dss_stop_device(dssdev); + + rfbi_enable_clocks(0); } EXPORT_SYMBOL(omapdss_rfbi_display_disable); @@ -1021,11 +909,7 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi.pdev = pdev; - spin_lock_init(&rfbi.cmd_lock); - - init_completion(&rfbi.cmd_done); - atomic_set(&rfbi.cmd_fifo_full, 0); - atomic_set(&rfbi.cmd_pending, 0); + sema_init(&rfbi.bus_lock, 1); rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); if (!rfbi_mem) { diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 54a53e648180..0bd4b0350f80 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -25,7 +25,7 @@ #include <linux/err.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 8e35a5bae429..980f919ed987 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -34,7 +34,7 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -373,8 +373,11 @@ static void venc_reset(void) } } +#ifdef CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET /* the magical sleep that makes things work */ + /* XXX more info? What bug this circumvents? */ msleep(20); +#endif } static void venc_enable_clocks(int enable) @@ -473,6 +476,12 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) mutex_lock(&venc.venc_lock); + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; goto err1; @@ -484,10 +493,11 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - /* wait couple of vsyncs until enabling the LCD */ - msleep(50); - + mutex_unlock(&venc.venc_lock); + return 0; err1: + omap_dss_stop_device(dssdev); +err0: mutex_unlock(&venc.venc_lock); return r; @@ -510,10 +520,9 @@ static void venc_panel_disable(struct omap_dss_device *dssdev) venc_power_off(dssdev); - /* wait at least 5 vsyncs after disabling the LCD */ - msleep(100); - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); end: mutex_unlock(&venc.venc_lock); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 6f435450987e..cff450392b79 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -28,7 +28,7 @@ #include <linux/omapfb.h> #include <linux/vmalloc.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/vrfb.h> #include <plat/vram.h> @@ -895,8 +895,16 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) p.display_info.xres = xres; p.display_info.yres = yres; - p.display_info.width = 0; - p.display_info.height = 0; + + if (display->driver->get_dimensions) { + u32 w, h; + display->driver->get_dimensions(display, &w, &h); + p.display_info.width = w; + p.display_info.height = h; + } else { + p.display_info.width = 0; + p.display_info.height = 0; + } if (copy_to_user((void __user *)arg, &p.display_info, sizeof(p.display_info))) diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 505ec6672049..505bc12a3031 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -30,7 +30,7 @@ #include <linux/platform_device.h> #include <linux/omapfb.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/vram.h> #include <plat/vrfb.h> @@ -702,8 +702,16 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) var->xres, var->yres, var->xres_virtual, var->yres_virtual); - var->height = -1; - var->width = -1; + if (display && display->driver->get_dimensions) { + u32 w, h; + display->driver->get_dimensions(display, &w, &h); + var->width = DIV_ROUND_CLOSEST(w, 1000); + var->height = DIV_ROUND_CLOSEST(h, 1000); + } else { + var->height = -1; + var->width = -1; + } + var->grayscale = 0; if (display && display->driver->get_timings) { @@ -749,35 +757,6 @@ static int omapfb_open(struct fb_info *fbi, int user) static int omapfb_release(struct fb_info *fbi, int user) { -#if 0 - struct omapfb_info *ofbi = FB2OFB(fbi); - struct omapfb2_device *fbdev = ofbi->fbdev; - struct omap_dss_device *display = fb2display(fbi); - - DBG("Closing fb with plane index %d\n", ofbi->id); - - omapfb_lock(fbdev); - - if (display && display->get_update_mode && display->update) { - /* XXX this update should be removed, I think. But it's - * good for debugging */ - if (display->get_update_mode(display) == - OMAP_DSS_UPDATE_MANUAL) { - u16 w, h; - - if (display->sync) - display->sync(display); - - display->get_resolution(display, &w, &h); - display->update(display, 0, 0, w, h); - } - } - - if (display && display->sync) - display->sync(display); - - omapfb_unlock(fbdev); -#endif return 0; } @@ -1263,7 +1242,6 @@ static int omapfb_blank(int blank, struct fb_info *fbi) struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_device *fbdev = ofbi->fbdev; struct omap_dss_device *display = fb2display(fbi); - int do_update = 0; int r = 0; if (!display) @@ -1279,11 +1257,6 @@ static int omapfb_blank(int blank, struct fb_info *fbi) if (display->driver->resume) r = display->driver->resume(display); - if (r == 0 && display->driver->get_update_mode && - display->driver->get_update_mode(display) == - OMAP_DSS_UPDATE_MANUAL) - do_update = 1; - break; case FB_BLANK_NORMAL: @@ -1307,13 +1280,6 @@ static int omapfb_blank(int blank, struct fb_info *fbi) exit: omapfb_unlock(fbdev); - if (r == 0 && do_update && display->driver->update) { - u16 w, h; - display->driver->get_resolution(display, &w, &h); - - r = display->driver->update(display, 0, 0, w, h); - } - return r; } @@ -2030,9 +1996,9 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) static int omapfb_mode_to_timings(const char *mode_str, struct omap_video_timings *timings, u8 *bpp) { - struct fb_info fbi; - struct fb_var_screeninfo var; - struct fb_ops fbops; + struct fb_info *fbi; + struct fb_var_screeninfo *var; + struct fb_ops *fbops; int r; #ifdef CONFIG_OMAP2_DSS_VENC @@ -2050,39 +2016,66 @@ static int omapfb_mode_to_timings(const char *mode_str, /* this is quite a hack, but I wanted to use the modedb and for * that we need fb_info and var, so we create dummy ones */ - memset(&fbi, 0, sizeof(fbi)); - memset(&var, 0, sizeof(var)); - memset(&fbops, 0, sizeof(fbops)); - fbi.fbops = &fbops; - - r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24); - - if (r != 0) { - timings->pixel_clock = PICOS2KHZ(var.pixclock); - timings->hbp = var.left_margin; - timings->hfp = var.right_margin; - timings->vbp = var.upper_margin; - timings->vfp = var.lower_margin; - timings->hsw = var.hsync_len; - timings->vsw = var.vsync_len; - timings->x_res = var.xres; - timings->y_res = var.yres; - - switch (var.bits_per_pixel) { - case 16: - *bpp = 16; - break; - case 24: - case 32: - default: - *bpp = 24; - break; - } + *bpp = 0; + fbi = NULL; + var = NULL; + fbops = NULL; - return 0; - } else { - return -EINVAL; + fbi = kzalloc(sizeof(*fbi), GFP_KERNEL); + if (fbi == NULL) { + r = -ENOMEM; + goto err; + } + + var = kzalloc(sizeof(*var), GFP_KERNEL); + if (var == NULL) { + r = -ENOMEM; + goto err; + } + + fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); + if (fbops == NULL) { + r = -ENOMEM; + goto err; + } + + fbi->fbops = fbops; + + r = fb_find_mode(var, fbi, mode_str, NULL, 0, NULL, 24); + if (r == 0) { + r = -EINVAL; + goto err; + } + + timings->pixel_clock = PICOS2KHZ(var->pixclock); + timings->hbp = var->left_margin; + timings->hfp = var->right_margin; + timings->vbp = var->upper_margin; + timings->vfp = var->lower_margin; + timings->hsw = var->hsync_len; + timings->vsw = var->vsync_len; + timings->x_res = var->xres; + timings->y_res = var->yres; + + switch (var->bits_per_pixel) { + case 16: + *bpp = 16; + break; + case 24: + case 32: + default: + *bpp = 24; + break; } + + r = 0; + +err: + kfree(fbi); + kfree(var); + kfree(fbops); + + return r; } static int omapfb_set_def_mode(struct omapfb2_device *fbdev, @@ -2185,6 +2178,61 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) return r; } +static int omapfb_init_display(struct omapfb2_device *fbdev, + struct omap_dss_device *dssdev) +{ + struct omap_dss_driver *dssdrv = dssdev->driver; + int r; + + r = dssdrv->enable(dssdev); + if (r) { + dev_warn(fbdev->dev, "Failed to enable display '%s'\n", + dssdev->name); + return r; + } + + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + u16 w, h; + if (dssdrv->enable_te) { + r = dssdrv->enable_te(dssdev, 1); + if (r) { + dev_err(fbdev->dev, "Failed to set TE\n"); + return r; + } + } + + if (dssdrv->set_update_mode) { + r = dssdrv->set_update_mode(dssdev, + OMAP_DSS_UPDATE_MANUAL); + if (r) { + dev_err(fbdev->dev, + "Failed to set update mode\n"); + return r; + } + } + + dssdrv->get_resolution(dssdev, &w, &h); + r = dssdrv->update(dssdev, 0, 0, w, h); + if (r) { + dev_err(fbdev->dev, + "Failed to update display\n"); + return r; + } + } else { + if (dssdrv->set_update_mode) { + r = dssdrv->set_update_mode(dssdev, + OMAP_DSS_UPDATE_AUTO); + if (r) { + dev_err(fbdev->dev, + "Failed to set update mode\n"); + return r; + } + } + } + + return 0; +} + static int omapfb_probe(struct platform_device *pdev) { struct omapfb2_device *fbdev = NULL; @@ -2284,30 +2332,13 @@ static int omapfb_probe(struct platform_device *pdev) } if (def_display) { - struct omap_dss_driver *dssdrv = def_display->driver; - - r = def_display->driver->enable(def_display); + r = omapfb_init_display(fbdev, def_display); if (r) { - dev_warn(fbdev->dev, "Failed to enable display '%s'\n", - def_display->name); + dev_err(fbdev->dev, + "failed to initialize default " + "display\n"); goto cleanup; } - - if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { - u16 w, h; - if (dssdrv->enable_te) - dssdrv->enable_te(def_display, 1); - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_MANUAL); - - dssdrv->get_resolution(def_display, &w, &h); - def_display->driver->update(def_display, 0, 0, w, h); - } else { - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_AUTO); - } } DBG("create sysfs for fbs\n"); diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 6f9c72cd6bb0..2f5e817b2a9a 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -29,7 +29,7 @@ #include <linux/mm.h> #include <linux/omapfb.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/vrfb.h> #include "omapfb.h" @@ -50,10 +50,12 @@ static ssize_t store_rotate_type(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb2_mem_region *rg; - enum omap_dss_rotation_type rot_type; + int rot_type; int r; - rot_type = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &rot_type); + if (r) + return r; if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) return -EINVAL; @@ -102,14 +104,15 @@ static ssize_t store_mirror(struct device *dev, { struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - unsigned long mirror; + int mirror; int r; struct fb_var_screeninfo new_var; - mirror = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &mirror); + if (r) + return r; - if (mirror != 0 && mirror != 1) - return -EINVAL; + mirror = !!mirror; if (!lock_fb_info(fbi)) return -ENODEV; @@ -445,7 +448,11 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, int r; int i; - size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); + r = kstrtoul(buf, 0, &size); + if (r) + return r; + + size = PAGE_ALIGN(size); if (!lock_fb_info(fbi)) return -ENODEV; diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index 1305fc9880ba..aa1b1d974276 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -29,13 +29,15 @@ #include <linux/rwsem.h> -#include <plat/display.h> +#include <video/omapdss.h> #ifdef DEBUG extern unsigned int omapfb_debug; #define DBG(format, ...) \ - if (omapfb_debug) \ - printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__) + do { \ + if (omapfb_debug) \ + printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__); \ + } while (0) #else #define DBG(format, ...) #endif diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 35f61dd0cb3a..bb95ec56d25d 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -623,19 +623,21 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no IO memory defined\n"); - return -ENOENT; + ret = -ENOENT; + goto failed_put_clk; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no IRQ defined\n"); - return -ENOENT; + ret = -ENOENT; + goto failed_put_clk; } info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev); if (info == NULL) { - clk_put(clk); - return -ENOMEM; + ret = -ENOMEM; + goto failed_put_clk; } /* Initialize private data */ @@ -671,7 +673,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) fbi->reg_base = ioremap_nocache(res->start, resource_size(res)); if (fbi->reg_base == NULL) { ret = -ENOMEM; - goto failed; + goto failed_free_info; } /* @@ -683,7 +685,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) &fbi->fb_start_dma, GFP_KERNEL); if (info->screen_base == NULL) { ret = -ENOMEM; - goto failed; + goto failed_free_info; } info->fix.smem_start = (unsigned long)fbi->fb_start_dma; @@ -772,8 +774,9 @@ failed_free_clk: failed_free_fbmem: dma_free_coherent(fbi->dev, info->fix.smem_len, info->screen_base, fbi->fb_start_dma); -failed: +failed_free_info: kfree(info); +failed_put_clk: clk_put(clk); dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 3b6cdcac8f1a..4aecf213c9be 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -182,6 +182,7 @@ struct s3c_fb_vsync { /** * struct s3c_fb - overall hardware state of the hardware + * @slock: The spinlock protection for this data sturcture. * @dev: The device that we bound to, for printing, etc. * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. @@ -195,6 +196,7 @@ struct s3c_fb_vsync { * @vsync_info: VSYNC-related information (count, queues...) */ struct s3c_fb { + spinlock_t slock; struct device *dev; struct resource *regs_res; struct clk *bus_clk; @@ -233,13 +235,12 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct s3c_fb_win *win = info->par; - struct s3c_fb_pd_win *windata = win->windata; struct s3c_fb *sfb = win->parent; dev_dbg(sfb->dev, "checking parameters\n"); - var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); - var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); + var->xres_virtual = max(var->xres_virtual, var->xres); + var->yres_virtual = max(var->yres_virtual, var->yres); if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) { dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", @@ -300,6 +301,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 5; break; + case 32: case 28: case 25: var->transp.length = var->bits_per_pixel - 24; @@ -308,7 +310,6 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, case 24: /* our 24bpp is unpacked, so 32bpp */ var->bits_per_pixel = 32; - case 32: var->red.offset = 16; var->red.length = 8; var->green.offset = 8; @@ -556,6 +557,13 @@ static int s3c_fb_set_par(struct fb_info *info) vidosd_set_alpha(win, alpha); vidosd_set_size(win, data); + /* Enable DMA channel for this window */ + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data |= SHADOWCON_CHx_ENABLE(win_no); + writel(data, sfb->regs + SHADOWCON); + } + data = WINCONx_ENWIN; /* note, since we have to round up the bits-per-pixel, we end up @@ -635,13 +643,6 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.wincon + (win_no * 4)); writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); - /* Enable DMA channel for this window */ - if (sfb->variant.has_shadowcon) { - data = readl(sfb->regs + SHADOWCON); - data |= SHADOWCON_CHx_ENABLE(win_no); - writel(data, sfb->regs + SHADOWCON); - } - shadow_protect_win(win, 0); return 0; @@ -947,6 +948,8 @@ static irqreturn_t s3c_fb_irq(int irq, void *dev_id) void __iomem *regs = sfb->regs; u32 irq_sts_reg; + spin_lock(&sfb->slock); + irq_sts_reg = readl(regs + VIDINTCON1); if (irq_sts_reg & VIDINTCON1_INT_FRAME) { @@ -963,6 +966,7 @@ static irqreturn_t s3c_fb_irq(int irq, void *dev_id) */ s3c_fb_disable_irq(sfb); + spin_unlock(&sfb->slock); return IRQ_HANDLED; } @@ -1339,6 +1343,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) sfb->pdata = pd; sfb->variant = fbdrv->variant; + spin_lock_init(&sfb->slock); + sfb->bus_clk = clk_get(dev, "lcd"); if (IS_ERR(sfb->bus_clk)) { dev_err(dev, "failed to get bus clock\n"); @@ -1442,8 +1448,7 @@ err_ioremap: iounmap(sfb->regs); err_req_region: - release_resource(sfb->regs_res); - kfree(sfb->regs_res); + release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); err_clk: clk_disable(sfb->bus_clk); @@ -1479,14 +1484,12 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) clk_disable(sfb->bus_clk); clk_put(sfb->bus_clk); - release_resource(sfb->regs_res); - kfree(sfb->regs_res); - - kfree(sfb); + release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); pm_runtime_put_sync(sfb->dev); pm_runtime_disable(sfb->dev); + kfree(sfb); return 0; } @@ -1521,7 +1524,8 @@ static int s3c_fb_resume(struct device *dev) clk_enable(sfb->bus_clk); - /* setup registers */ + /* setup gpio and output polarity controls */ + pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); /* zero all windows before we do anything */ @@ -1549,7 +1553,7 @@ static int s3c_fb_resume(struct device *dev) return 0; } -int s3c_fb_runtime_suspend(struct device *dev) +static int s3c_fb_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); @@ -1569,7 +1573,7 @@ int s3c_fb_runtime_suspend(struct device *dev) return 0; } -int s3c_fb_runtime_resume(struct device *dev) +static int s3c_fb_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); @@ -1579,7 +1583,8 @@ int s3c_fb_runtime_resume(struct device *dev) clk_enable(sfb->bus_clk); - /* setup registers */ + /* setup gpio and output polarity controls */ + pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); /* zero all windows before we do anything */ @@ -1623,28 +1628,31 @@ static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { .has_osd_c = 1, .osd_size_off = 0x8, .palette_sz = 256, - .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(24)), }, [1] = { .has_osd_c = 1, .has_osd_d = 1, - .osd_size_off = 0x12, + .osd_size_off = 0xc, .has_osd_alpha = 1, .palette_sz = 256, .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(18) | VALID_BPP(19) | - VALID_BPP(24) | VALID_BPP(25)), + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), }, [2] = { .has_osd_c = 1, .has_osd_d = 1, - .osd_size_off = 0x12, + .osd_size_off = 0xc, .has_osd_alpha = 1, .palette_sz = 16, .palette_16bpp = 1, .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(18) | VALID_BPP(19) | - VALID_BPP(24) | VALID_BPP(25)), + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), }, [3] = { .has_osd_c = 1, @@ -1653,7 +1661,8 @@ static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { .palette_16bpp = 1, .valid_bpp = (VALID_BPP124 | VALID_BPP(16) | VALID_BPP(18) | VALID_BPP(19) | - VALID_BPP(24) | VALID_BPP(25)), + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), }, [4] = { .has_osd_c = 1, @@ -1662,7 +1671,65 @@ static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { .palette_16bpp = 1, .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(16) | VALID_BPP(18) | - VALID_BPP(24) | VALID_BPP(25)), + VALID_BPP(19) | VALID_BPP(24) | + VALID_BPP(25) | VALID_BPP(28)), + }, +}; + +static struct s3c_fb_win_variant s3c_fb_data_s5p_wins[] = { + [0] = { + .has_osd_c = 1, + .osd_size_off = 0x8, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) | + VALID_BPP(15) | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(32)), + }, + [1] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0xc, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) | + VALID_BPP(15) | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(32)), + }, + [2] = { + .has_osd_c = 1, + .has_osd_d = 1, + .osd_size_off = 0xc, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) | + VALID_BPP(15) | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(32)), + }, + [3] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) | + VALID_BPP(15) | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(32)), + }, + [4] = { + .has_osd_c = 1, + .has_osd_alpha = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) | + VALID_BPP(15) | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(32)), }, }; @@ -1719,11 +1786,11 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { .has_prtcon = 1, }, - .win[0] = &s3c_fb_data_64xx_wins[0], - .win[1] = &s3c_fb_data_64xx_wins[1], - .win[2] = &s3c_fb_data_64xx_wins[2], - .win[3] = &s3c_fb_data_64xx_wins[3], - .win[4] = &s3c_fb_data_64xx_wins[4], + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], + .win[3] = &s3c_fb_data_s5p_wins[3], + .win[4] = &s3c_fb_data_s5p_wins[4], }; static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { @@ -1749,11 +1816,11 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { .has_shadowcon = 1, }, - .win[0] = &s3c_fb_data_64xx_wins[0], - .win[1] = &s3c_fb_data_64xx_wins[1], - .win[2] = &s3c_fb_data_64xx_wins[2], - .win[3] = &s3c_fb_data_64xx_wins[3], - .win[4] = &s3c_fb_data_64xx_wins[4], + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], + .win[3] = &s3c_fb_data_s5p_wins[3], + .win[4] = &s3c_fb_data_s5p_wins[4], }; /* S3C2443/S3C2416 style hardware */ diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 61c819e35f7f..0aa13761de6e 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -867,7 +867,7 @@ static int __devinit s3c24xxfb_probe(struct platform_device *pdev, goto dealloc_fb; } - size = (res->end - res->start) + 1; + size = resource_size(res); info->mem = request_mem_region(res->start, size, pdev->name); if (info->mem == NULL) { dev_err(&pdev->dev, "failed to get memory region\n"); @@ -997,8 +997,7 @@ release_irq: release_regs: iounmap(info->io); release_mem: - release_resource(info->mem); - kfree(info->mem); + release_mem_region(res->start, size); dealloc_fb: platform_set_drvdata(pdev, NULL); framebuffer_release(fbinfo); @@ -1044,8 +1043,7 @@ static int __devexit s3c2410fb_remove(struct platform_device *pdev) iounmap(info->io); - release_resource(info->mem); - kfree(info->mem); + release_mem_region(info->mem->start, resource_size(info->mem)); platform_set_drvdata(pdev, NULL); framebuffer_release(fbinfo); diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index c4482f2e5799..4ca5d0c8fe84 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -25,6 +25,9 @@ #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ #include <video/vga.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + #ifdef CONFIG_MTRR #include <asm/mtrr.h> #endif @@ -36,6 +39,12 @@ struct s3fb_info { struct mutex open_lock; unsigned int ref_count; u32 pseudo_palette[16]; +#ifdef CONFIG_FB_S3_DDC + u8 __iomem *mmio; + bool ddc_registered; + struct i2c_adapter ddc_adapter; + struct i2c_algo_bit_data ddc_algo; +#endif }; @@ -105,6 +114,9 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", #define CHIP_UNDECIDED_FLAG 0x80 #define CHIP_MASK 0xFF +#define MMIO_OFFSET 0x1000000 +#define MMIO_SIZE 0x10000 + /* CRT timing register sets */ static const struct vga_regset s3_h_total_regs[] = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END}; @@ -140,7 +152,7 @@ static const struct svga_timing_regs s3_timing_regs = { /* Module parameters */ -static char *mode_option __devinitdata = "640x480-8@60"; +static char *mode_option __devinitdata; #ifdef CONFIG_MTRR static int mtrr __devinitdata = 1; @@ -169,6 +181,119 @@ MODULE_PARM_DESC(fasttext, "Enable S3 fast text mode (1=enable, 0=disable, defau /* ------------------------------------------------------------------------- */ +#ifdef CONFIG_FB_S3_DDC + +#define DDC_REG 0xaa /* Trio 3D/1X/2X */ +#define DDC_MMIO_REG 0xff20 /* all other chips */ +#define DDC_SCL_OUT (1 << 0) +#define DDC_SDA_OUT (1 << 1) +#define DDC_SCL_IN (1 << 2) +#define DDC_SDA_IN (1 << 3) +#define DDC_DRIVE_EN (1 << 4) + +static bool s3fb_ddc_needs_mmio(int chip) +{ + return !(chip == CHIP_360_TRIO3D_1X || + chip == CHIP_362_TRIO3D_2X || + chip == CHIP_368_TRIO3D_2X); +} + +static u8 s3fb_ddc_read(struct s3fb_info *par) +{ + if (s3fb_ddc_needs_mmio(par->chip)) + return readb(par->mmio + DDC_MMIO_REG); + else + return vga_rcrt(par->state.vgabase, DDC_REG); +} + +static void s3fb_ddc_write(struct s3fb_info *par, u8 val) +{ + if (s3fb_ddc_needs_mmio(par->chip)) + writeb(val, par->mmio + DDC_MMIO_REG); + else + vga_wcrt(par->state.vgabase, DDC_REG, val); +} + +static void s3fb_ddc_setscl(void *data, int val) +{ + struct s3fb_info *par = data; + unsigned char reg; + + reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; + if (val) + reg |= DDC_SCL_OUT; + else + reg &= ~DDC_SCL_OUT; + s3fb_ddc_write(par, reg); +} + +static void s3fb_ddc_setsda(void *data, int val) +{ + struct s3fb_info *par = data; + unsigned char reg; + + reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; + if (val) + reg |= DDC_SDA_OUT; + else + reg &= ~DDC_SDA_OUT; + s3fb_ddc_write(par, reg); +} + +static int s3fb_ddc_getscl(void *data) +{ + struct s3fb_info *par = data; + + return !!(s3fb_ddc_read(par) & DDC_SCL_IN); +} + +static int s3fb_ddc_getsda(void *data) +{ + struct s3fb_info *par = data; + + return !!(s3fb_ddc_read(par) & DDC_SDA_IN); +} + +static int __devinit s3fb_setup_ddc_bus(struct fb_info *info) +{ + struct s3fb_info *par = info->par; + + strlcpy(par->ddc_adapter.name, info->fix.id, + sizeof(par->ddc_adapter.name)); + par->ddc_adapter.owner = THIS_MODULE; + par->ddc_adapter.class = I2C_CLASS_DDC; + par->ddc_adapter.algo_data = &par->ddc_algo; + par->ddc_adapter.dev.parent = info->device; + par->ddc_algo.setsda = s3fb_ddc_setsda; + par->ddc_algo.setscl = s3fb_ddc_setscl; + par->ddc_algo.getsda = s3fb_ddc_getsda; + par->ddc_algo.getscl = s3fb_ddc_getscl; + par->ddc_algo.udelay = 10; + par->ddc_algo.timeout = 20; + par->ddc_algo.data = par; + + i2c_set_adapdata(&par->ddc_adapter, par); + + /* + * some Virge cards have external MUX to switch chip I2C bus between + * DDC and extension pins - switch it do DDC + */ +/* vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */ + if (par->chip == CHIP_357_VIRGE_GX2 || + par->chip == CHIP_359_VIRGE_GX2P) + svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03); + else + svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03); + /* some Virge need this or the DDC is ignored */ + svga_wcrt_mask(par->state.vgabase, 0x5c, 0x03, 0x03); + + return i2c_bit_add_bus(&par->ddc_adapter); +} +#endif /* CONFIG_FB_S3_DDC */ + + +/* ------------------------------------------------------------------------- */ + /* Set font in S3 fast text mode */ static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map) @@ -994,6 +1119,7 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i struct s3fb_info *par; int rc; u8 regval, cr38, cr39; + bool found = false; /* Ignore secondary VGA device because there is no VGA arbitration */ if (! svga_primary_device(dev)) { @@ -1110,12 +1236,69 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i info->fix.ypanstep = 0; info->fix.accel = FB_ACCEL_NONE; info->pseudo_palette = (void*) (par->pseudo_palette); + info->var.bits_per_pixel = 8; + +#ifdef CONFIG_FB_S3_DDC + /* Enable MMIO if needed */ + if (s3fb_ddc_needs_mmio(par->chip)) { + par->mmio = ioremap(info->fix.smem_start + MMIO_OFFSET, MMIO_SIZE); + if (par->mmio) + svga_wcrt_mask(par->state.vgabase, 0x53, 0x08, 0x08); /* enable MMIO */ + else + dev_err(info->device, "unable to map MMIO at 0x%lx, disabling DDC", + info->fix.smem_start + MMIO_OFFSET); + } + if (!s3fb_ddc_needs_mmio(par->chip) || par->mmio) + if (s3fb_setup_ddc_bus(info) == 0) { + u8 *edid = fb_ddc_read(&par->ddc_adapter); + par->ddc_registered = true; + if (edid) { + fb_edid_to_monspecs(edid, &info->monspecs); + kfree(edid); + if (!info->monspecs.modedb) + dev_err(info->device, "error getting mode database\n"); + else { + const struct fb_videomode *m; + + fb_videomode_to_modelist(info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + m = fb_find_best_display(&info->monspecs, &info->modelist); + if (m) { + fb_videomode_to_var(&info->var, m); + /* fill all other info->var's fields */ + if (s3fb_check_var(&info->var, info) == 0) + found = true; + } + } + } + } +#endif + if (!mode_option && !found) + mode_option = "640x480-8@60"; /* Prepare startup mode */ - rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); - if (! ((rc == 1) || (rc == 2))) { - rc = -EINVAL; - dev_err(info->device, "mode %s not found\n", mode_option); + if (mode_option) { + rc = fb_find_mode(&info->var, info, mode_option, + info->monspecs.modedb, info->monspecs.modedb_len, + NULL, info->var.bits_per_pixel); + if (!rc || rc == 4) { + rc = -EINVAL; + dev_err(info->device, "mode %s not found\n", mode_option); + fb_destroy_modedb(info->monspecs.modedb); + info->monspecs.modedb = NULL; + goto err_find_mode; + } + } + + fb_destroy_modedb(info->monspecs.modedb); + info->monspecs.modedb = NULL; + + /* maximize virtual vertical size for fast scrolling */ + info->var.yres_virtual = info->fix.smem_len * 8 / + (info->var.bits_per_pixel * info->var.xres_virtual); + if (info->var.yres_virtual < info->var.yres) { + dev_err(info->device, "virtual vertical size smaller than real\n"); goto err_find_mode; } @@ -1164,6 +1347,12 @@ err_reg_fb: fb_dealloc_cmap(&info->cmap); err_alloc_cmap: err_find_mode: +#ifdef CONFIG_FB_S3_DDC + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + if (par->mmio) + iounmap(par->mmio); +#endif pci_iounmap(dev, info->screen_base); err_iomap: pci_release_regions(dev); @@ -1180,12 +1369,11 @@ err_enable_device: static void __devexit s3_pci_remove(struct pci_dev *dev) { struct fb_info *info = pci_get_drvdata(dev); + struct s3fb_info __maybe_unused *par = info->par; if (info) { #ifdef CONFIG_MTRR - struct s3fb_info *par = info->par; - if (par->mtrr_reg >= 0) { mtrr_del(par->mtrr_reg, 0, 0); par->mtrr_reg = -1; @@ -1195,6 +1383,13 @@ static void __devexit s3_pci_remove(struct pci_dev *dev) unregister_framebuffer(info); fb_dealloc_cmap(&info->cmap); +#ifdef CONFIG_FB_S3_DDC + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + if (par->mmio) + iounmap(par->mmio); +#endif + pci_iounmap(dev, info->screen_base); pci_release_regions(dev); /* pci_disable_device(dev); */ diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c index bb71fea07284..80fa87e2ae2f 100644 --- a/drivers/video/savage/savagefb-i2c.c +++ b/drivers/video/savage/savagefb-i2c.c @@ -171,6 +171,8 @@ void savagefb_create_i2c_busses(struct fb_info *info) switch (par->chip) { case S3_PROSAVAGE: + case S3_PROSAVAGEDDR: + case S3_TWISTER: par->chan.reg = CR_SERIAL2; par->chan.ioaddr = par->mmio.vbase; par->chan.algo.setsda = prosavage_gpio_setsda; diff --git a/drivers/video/savage/savagefb.h b/drivers/video/savage/savagefb.h index 4e9490c19d7d..32549d177b19 100644 --- a/drivers/video/savage/savagefb.h +++ b/drivers/video/savage/savagefb.h @@ -36,7 +36,6 @@ #define PCI_CHIP_SAVAGE_IX 0x8c13 #define PCI_CHIP_PROSAVAGE_PM 0x8a25 #define PCI_CHIP_PROSAVAGE_KM 0x8a26 - /* Twister is a code name; hope I get the real name soon. */ #define PCI_CHIP_S3TWISTER_P 0x8d01 #define PCI_CHIP_S3TWISTER_K 0x8d02 #define PCI_CHIP_PROSAVAGE_DDR 0x8d03 @@ -52,14 +51,15 @@ #define PCI_CHIP_SUPSAV_IXCDDR 0x8c2f +#define S3_SAVAGE_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000)) #define S3_SAVAGE3D_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX)) -#define S3_SAVAGE4_SERIES(chip) ((chip==S3_SAVAGE4) || (chip==S3_PROSAVAGE)) +#define S3_SAVAGE4_SERIES(chip) ((chip>=S3_SAVAGE4) || (chip<=S3_PROSAVAGEDDR)) #define S3_SAVAGE_MOBILE_SERIES(chip) ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE)) -#define S3_SAVAGE_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000)) +#define S3_MOBILE_TWISTER_SERIES(chip) ((chip==S3_TWISTER) || (chip==S3_PROSAVAGEDDR)) /* Chip tags. These are used to group the adapters into * related families. @@ -71,6 +71,8 @@ typedef enum { S3_SAVAGE_MX, S3_SAVAGE4, S3_PROSAVAGE, + S3_TWISTER, + S3_PROSAVAGEDDR, S3_SUPERSAVAGE, S3_SAVAGE2000, S3_LAST diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index a2dc1a7ec758..4de541ca9c52 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -328,7 +328,9 @@ SavageSetup2DEngine(struct savagefb_par *par) savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par); break; case S3_SAVAGE4: + case S3_TWISTER: case S3_PROSAVAGE: + case S3_PROSAVAGEDDR: case S3_SUPERSAVAGE: /* Disable BCI */ savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par); @@ -1886,6 +1888,8 @@ static int savage_init_hw(struct savagefb_par *par) break; case S3_PROSAVAGE: + case S3_PROSAVAGEDDR: + case S3_TWISTER: videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024; break; @@ -1963,7 +1967,8 @@ static int savage_init_hw(struct savagefb_par *par) } } - if (S3_SAVAGE_MOBILE_SERIES(par->chip) && !par->crtonly) + if ((S3_SAVAGE_MOBILE_SERIES(par->chip) || + S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly) par->display_type = DISP_LCD; else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi)) par->display_type = DISP_DFP; @@ -2111,19 +2116,19 @@ static int __devinit savage_init_fb_info(struct fb_info *info, snprintf(info->fix.id, 16, "ProSavageKM"); break; case FB_ACCEL_S3TWISTER_P: - par->chip = S3_PROSAVAGE; + par->chip = S3_TWISTER; snprintf(info->fix.id, 16, "TwisterP"); break; case FB_ACCEL_S3TWISTER_K: - par->chip = S3_PROSAVAGE; + par->chip = S3_TWISTER; snprintf(info->fix.id, 16, "TwisterK"); break; case FB_ACCEL_PROSAVAGE_DDR: - par->chip = S3_PROSAVAGE; + par->chip = S3_PROSAVAGEDDR; snprintf(info->fix.id, 16, "ProSavageDDR"); break; case FB_ACCEL_PROSAVAGE_DDRK: - par->chip = S3_PROSAVAGE; + par->chip = S3_PROSAVAGEDDR; snprintf(info->fix.id, 16, "ProSavage8"); break; } @@ -2232,6 +2237,22 @@ static int __devinit savagefb_probe(struct pci_dev* dev, &info->modelist); #endif info->var = savagefb_var800x600x8; + /* if a panel was detected, default to a CVT mode instead */ + if (par->SavagePanelWidth) { + struct fb_videomode cvt_mode; + + memset(&cvt_mode, 0, sizeof(cvt_mode)); + cvt_mode.xres = par->SavagePanelWidth; + cvt_mode.yres = par->SavagePanelHeight; + cvt_mode.refresh = 60; + /* FIXME: if we know there is only the panel + * we can enable reduced blanking as well */ + if (fb_find_mode_cvt(&cvt_mode, 0, 0)) + printk(KERN_WARNING "No CVT mode found for panel\n"); + else if (fb_find_mode(&info->var, info, NULL, NULL, 0, + &cvt_mode, 0) != 3) + info->var = savagefb_var800x600x8; + } if (mode_option) { fb_find_mode(&info->var, info, mode_option, diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c index 8fe19582c460..45e47d847163 100644 --- a/drivers/video/sh7760fb.c +++ b/drivers/video/sh7760fb.c @@ -551,8 +551,7 @@ out_unmap: free_irq(par->irq, &par->vsync); iounmap(par->base); out_res: - release_resource(par->ioarea); - kfree(par->ioarea); + release_mem_region(res->start, resource_size(res)); out_fb: framebuffer_release(info); return ret; @@ -570,8 +569,7 @@ static int __devexit sh7760fb_remove(struct platform_device *dev) if (par->irq >= 0) free_irq(par->irq, par); iounmap(par->base); - release_resource(par->ioarea); - kfree(par->ioarea); + release_mem_region(par->ioarea->start, resource_size(par->ioarea)); framebuffer_release(info); platform_set_drvdata(dev, NULL); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 2b9e56a6bde4..7d54e2c612f7 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -1127,9 +1127,6 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) 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, &hdmi_rate, &parent_rate); if (ret < 0) goto out; @@ -1187,7 +1184,6 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) fb_set_suspend(hdmi->info, 1); console_unlock(); - pm_runtime_put(hdmi->dev); } out: @@ -1308,7 +1304,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); /* Product and revision IDs are 0 in sh-mobile version */ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", @@ -1336,6 +1332,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) ecodec: free_irq(irq, hdmi); ereqirq: + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); iounmap(hdmi->base); emap: @@ -1372,6 +1369,7 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) free_irq(irq, hdmi); /* Wait for already scheduled work */ cancel_delayed_work_sync(&hdmi->edid_work); + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); clk_disable(hdmi->hdmi_clk); clk_put(hdmi->hdmi_clk); diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 9bcc61b4ef14..019dbd3f12b2 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -27,6 +27,7 @@ #include <asm/atomic.h> #include "sh_mobile_lcdcfb.h" +#include "sh_mobile_meram.h" #define SIDE_B_OFFSET 0x1000 #define MIRROR_OFFSET 0x2000 @@ -143,6 +144,7 @@ struct sh_mobile_lcdc_priv { unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; int forced_bpp; /* 2 channel LCDC must share bpp setting */ + struct sh_mobile_meram_info *meram_dev; }; static bool banked(int reg_nr) @@ -468,8 +470,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) unsigned long tmp; int bpp = 0; unsigned long ldddsr; - int k, m; - int ret = 0; + int k, m, ret; /* enable clocks before accessing the hardware */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { @@ -538,11 +539,12 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) lcdc_write_chan(ch, LDPMR, 0); board_cfg = &ch->cfg.board_cfg; - if (board_cfg->setup_sys) - ret = board_cfg->setup_sys(board_cfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); - if (ret) - return ret; + if (board_cfg->setup_sys) { + ret = board_cfg->setup_sys(board_cfg->board_data, + ch, &sh_mobile_lcdc_sys_bus_ops); + if (ret) + return ret; + } } /* word and long word swap */ @@ -564,6 +566,9 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + unsigned long base_addr_y; + unsigned long base_addr_c = 0; + int pitch; ch = &priv->ch[k]; if (!priv->ch[k].enabled) @@ -598,16 +603,68 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } lcdc_write_chan(ch, LDDFR, tmp); + base_addr_y = ch->info->fix.smem_start; + base_addr_c = base_addr_y + + ch->info->var.xres * + ch->info->var.yres_virtual; + pitch = ch->info->fix.line_length; + + /* test if we can enable meram */ + if (ch->cfg.meram_cfg && priv->meram_dev && + priv->meram_dev->ops) { + struct sh_mobile_meram_cfg *cfg; + struct sh_mobile_meram_info *mdev; + unsigned long icb_addr_y, icb_addr_c; + int icb_pitch; + int pf; + + cfg = ch->cfg.meram_cfg; + mdev = priv->meram_dev; + /* we need to de-init configured ICBs before we + * we can re-initialize them. + */ + if (ch->meram_enabled) + mdev->ops->meram_unregister(mdev, cfg); + + ch->meram_enabled = 0; + + if (ch->info->var.nonstd) { + if (ch->info->var.bits_per_pixel == 24) + pf = SH_MOBILE_MERAM_PF_NV24; + else + pf = SH_MOBILE_MERAM_PF_NV; + } else { + pf = SH_MOBILE_MERAM_PF_RGB; + } + + ret = mdev->ops->meram_register(mdev, cfg, pitch, + ch->info->var.yres, + pf, + base_addr_y, + base_addr_c, + &icb_addr_y, + &icb_addr_c, + &icb_pitch); + if (!ret) { + /* set LDSA1R value */ + base_addr_y = icb_addr_y; + pitch = icb_pitch; + + /* set LDSA2R value if required */ + if (base_addr_c) + base_addr_c = icb_addr_c; + + ch->meram_enabled = 1; + } + } + /* point out our frame buffer */ - lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); + lcdc_write_chan(ch, LDSA1R, base_addr_y); if (ch->info->var.nonstd) - lcdc_write_chan(ch, LDSA2R, - ch->info->fix.smem_start + - ch->info->var.xres * - ch->info->var.yres_virtual); + lcdc_write_chan(ch, LDSA2R, base_addr_c); /* set line size */ - lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); + lcdc_write_chan(ch, LDMLSR, pitch); /* setup deferred io if SYS bus */ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; @@ -692,6 +749,17 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) board_cfg->display_off(board_cfg->board_data); module_put(board_cfg->owner); } + + /* disable the meram */ + if (ch->meram_enabled) { + struct sh_mobile_meram_cfg *cfg; + struct sh_mobile_meram_info *mdev; + cfg = ch->cfg.meram_cfg; + mdev = priv->meram_dev; + mdev->ops->meram_unregister(mdev, cfg); + ch->meram_enabled = 0; + } + } /* stop the lcdc */ @@ -875,9 +943,29 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, } else base_addr_c = 0; - lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); - if (base_addr_c) - lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); + if (!ch->meram_enabled) { + lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); + if (base_addr_c) + lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); + } else { + struct sh_mobile_meram_cfg *cfg; + struct sh_mobile_meram_info *mdev; + unsigned long icb_addr_y, icb_addr_c; + int ret; + + cfg = ch->cfg.meram_cfg; + mdev = priv->meram_dev; + ret = mdev->ops->meram_update(mdev, cfg, + base_addr_y, base_addr_c, + &icb_addr_y, &icb_addr_c); + if (ret) + return ret; + + lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y); + if (icb_addr_c) + lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c); + + } if (lcdc_chan_is_sublcd(ch)) lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); @@ -1288,7 +1376,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, struct fb_info *info = event->info; struct sh_mobile_lcdc_chan *ch = info->par; struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; - int ret; if (&ch->lcdc->notifier != nb) return NOTIFY_DONE; @@ -1302,7 +1389,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, board_cfg->display_off(board_cfg->board_data); module_put(board_cfg->owner); } - pm_runtime_put(info->device); sh_mobile_lcdc_stop(ch->lcdc); break; case FB_EVENT_RESUME: @@ -1316,9 +1402,7 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, module_put(board_cfg->owner); } - ret = sh_mobile_lcdc_start(ch->lcdc); - if (!ret) - pm_runtime_get_sync(info->device); + sh_mobile_lcdc_start(ch->lcdc); } return NOTIFY_OK; @@ -1420,6 +1504,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + priv->meram_dev = pdata->meram_dev; + for (i = 0; i < j; i++) { struct fb_var_screeninfo *var; const struct fb_videomode *lcd_cfg, *max_cfg = NULL; diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h index f16cb5645a13..aeed6687e6a7 100644 --- a/drivers/video/sh_mobile_lcdcfb.h +++ b/drivers/video/sh_mobile_lcdcfb.h @@ -39,6 +39,7 @@ struct sh_mobile_lcdc_chan { int use_count; int blank_status; struct mutex open_lock; /* protects the use counter */ + int meram_enabled; }; #endif diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c new file mode 100644 index 000000000000..9170c82b495c --- /dev/null +++ b/drivers/video/sh_mobile_meram.c @@ -0,0 +1,567 @@ +/* + * SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver + * + * Copyright (c) 2011 Damian Hobson-Garcia <dhobsong@igel.co.jp> + * Takanari Hayama <taki@igel.co.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include "sh_mobile_meram.h" + +/* meram registers */ +#define MExxCTL 0x0 +#define MExxBSIZE 0x4 +#define MExxMNCF 0x8 +#define MExxSARA 0x10 +#define MExxSARB 0x14 +#define MExxSBSIZE 0x18 + +#define MERAM_MExxCTL_VAL(ctl, next_icb, addr) \ + ((ctl) | (((next_icb) & 0x1f) << 11) | (((addr) & 0x7ff) << 16)) +#define MERAM_MExxBSIZE_VAL(a, b, c) \ + (((a) << 28) | ((b) << 16) | (c)) + +#define MEVCR1 0x4 +#define MEACTS 0x10 +#define MEQSEL1 0x40 +#define MEQSEL2 0x44 + +/* settings */ +#define MERAM_SEC_LINE 15 +#define MERAM_LINE_WIDTH 2048 + +/* + * MERAM/ICB access functions + */ + +#define MERAM_ICB_OFFSET(base, idx, off) \ + ((base) + (0x400 + ((idx) * 0x20) + (off))) + +static inline void meram_write_icb(void __iomem *base, int idx, int off, + unsigned long val) +{ + iowrite32(val, MERAM_ICB_OFFSET(base, idx, off)); +} + +static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) +{ + return ioread32(MERAM_ICB_OFFSET(base, idx, off)); +} + +static inline void meram_write_reg(void __iomem *base, int off, + unsigned long val) +{ + iowrite32(val, base + off); +} + +static inline unsigned long meram_read_reg(void __iomem *base, int off) +{ + return ioread32(base + off); +} + +/* + * register ICB + */ + +#define MERAM_CACHE_START(p) ((p) >> 16) +#define MERAM_CACHE_END(p) ((p) & 0xffff) +#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \ + (((o) + (s) - 1) & 0xffff)) + +/* + * check if there's no overlaps in MERAM allocation. + */ + +static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_icb *new) +{ + int i; + int used_start, used_end, meram_start, meram_end; + + /* valid ICB? */ + if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) + return 1; + + if (test_bit(new->marker_icb, &priv->used_icb) || + test_bit(new->cache_icb, &priv->used_icb)) + return 1; + + for (i = 0; i < priv->used_meram_cache_regions; i++) { + used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); + used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); + meram_start = new->meram_offset; + meram_end = new->meram_offset + new->meram_size; + + if ((meram_start >= used_start && meram_start < used_end) || + (meram_end > used_start && meram_end < used_end)) + return 1; + } + + return 0; +} + +/* + * mark the specified ICB as used + */ + +static inline void meram_mark(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_icb *new) +{ + int n; + + if (new->marker_icb < 0 || new->cache_icb < 0) + return; + + __set_bit(new->marker_icb, &priv->used_icb); + __set_bit(new->cache_icb, &priv->used_icb); + + n = priv->used_meram_cache_regions; + + priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, + new->meram_size); + + priv->used_meram_cache_regions++; +} + +/* + * unmark the specified ICB as used + */ + +static inline void meram_unmark(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_icb *icb) +{ + int i; + unsigned long pattern; + + if (icb->marker_icb < 0 || icb->cache_icb < 0) + return; + + __clear_bit(icb->marker_icb, &priv->used_icb); + __clear_bit(icb->cache_icb, &priv->used_icb); + + pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size); + for (i = 0; i < priv->used_meram_cache_regions; i++) { + if (priv->used_meram_cache[i] == pattern) { + while (i < priv->used_meram_cache_regions - 1) { + priv->used_meram_cache[i] = + priv->used_meram_cache[i + 1] ; + i++; + } + priv->used_meram_cache[i] = 0; + priv->used_meram_cache_regions--; + break; + } + } +} + +/* + * is this a YCbCr(NV12, NV16 or NV24) colorspace + */ +static inline int is_nvcolor(int cspace) +{ + if (cspace == SH_MOBILE_MERAM_PF_NV || + cspace == SH_MOBILE_MERAM_PF_NV24) + return 1; + return 0; +} + +/* + * set the next address to fetch + */ +static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_cfg *cfg, + unsigned long base_addr_y, + unsigned long base_addr_c) +{ + unsigned long target; + + target = (cfg->current_reg) ? MExxSARA : MExxSARB; + cfg->current_reg ^= 1; + + /* set the next address to fetch */ + meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, + base_addr_y); + meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, + base_addr_y + cfg->icb[0].cache_unit); + + if (is_nvcolor(cfg->pixelformat)) { + meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, + base_addr_c); + meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, + base_addr_c + cfg->icb[1].cache_unit); + } +} + +/* + * get the next ICB address + */ +static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_cfg *cfg, + unsigned long *icb_addr_y, + unsigned long *icb_addr_c) +{ + unsigned long icb_offset; + + if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) + icb_offset = 0x80000000 | (cfg->current_reg << 29); + else + icb_offset = 0xc0000000 | (cfg->current_reg << 23); + + *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); + if ((*icb_addr_c) && is_nvcolor(cfg->pixelformat)) + *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); +} + +#define MERAM_CALC_BYTECOUNT(x, y) \ + (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1)) + +/* + * initialize MERAM + */ + +static int meram_init(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_icb *icb, + int xres, int yres, int *out_pitch) +{ + unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); + unsigned long bnm; + int lcdc_pitch, xpitch, line_cnt; + int save_lines; + + /* adjust pitch to 1024, 2048, 4096 or 8192 */ + lcdc_pitch = (xres - 1) | 1023; + lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1); + lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2); + lcdc_pitch += 1; + + /* derive settings */ + if (lcdc_pitch == 8192 && yres >= 1024) { + lcdc_pitch = xpitch = MERAM_LINE_WIDTH; + line_cnt = total_byte_count >> 11; + *out_pitch = xres; + save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); + save_lines *= MERAM_SEC_LINE; + } else { + xpitch = xres; + line_cnt = yres; + *out_pitch = lcdc_pitch; + save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; + save_lines &= 0xff; + } + bnm = (save_lines - 1) << 16; + + /* TODO: we better to check if we have enough MERAM buffer size */ + + /* set up ICB */ + meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, + MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); + meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, + MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); + + meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); + meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); + + meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); + meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); + + /* save a cache unit size */ + icb->cache_unit = xres * save_lines; + + /* + * Set MERAM for framebuffer + * + * 0x70f: WD = 0x3, WS=0x1, CM=0x1, MD=FB mode + * we also chain the cache_icb and the marker_icb. + * we also split the allocated MERAM buffer between two ICBs. + */ + meram_write_icb(priv->base, icb->cache_icb, MExxCTL, + MERAM_MExxCTL_VAL(0x70f, icb->marker_icb, + icb->meram_offset)); + meram_write_icb(priv->base, icb->marker_icb, MExxCTL, + MERAM_MExxCTL_VAL(0x70f, icb->cache_icb, + icb->meram_offset + + icb->meram_size / 2)); + + return 0; +} + +static void meram_deinit(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_icb *icb) +{ + /* disable ICB */ + meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 0); + meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 0); + icb->cache_unit = 0; +} + +/* + * register the ICB + */ + +static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_cfg *cfg, + int xres, int yres, int pixelformat, + unsigned long base_addr_y, + unsigned long base_addr_c, + unsigned long *icb_addr_y, + unsigned long *icb_addr_c, + int *pitch) +{ + struct platform_device *pdev; + struct sh_mobile_meram_priv *priv; + int n, out_pitch; + int error = 0; + + if (!pdata || !pdata->priv || !pdata->pdev || !cfg) + return -EINVAL; + + if (pixelformat != SH_MOBILE_MERAM_PF_NV && + pixelformat != SH_MOBILE_MERAM_PF_NV24 && + pixelformat != SH_MOBILE_MERAM_PF_RGB) + return -EINVAL; + + priv = pdata->priv; + pdev = pdata->pdev; + + dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)", + xres, yres, (!pixelformat) ? "yuv" : "rgb", + base_addr_y, base_addr_c); + + mutex_lock(&priv->lock); + + /* we can't handle wider than 8192px */ + if (xres > 8192) { + dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); + error = -EINVAL; + goto err; + } + + if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { + dev_err(&pdev->dev, "no more ICB available."); + error = -EINVAL; + goto err; + } + + /* do we have at least one ICB config? */ + if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) { + dev_err(&pdev->dev, "at least one ICB is required."); + error = -EINVAL; + goto err; + } + + /* make sure that there's no overlaps */ + if (meram_check_overlap(priv, &cfg->icb[0])) { + dev_err(&pdev->dev, "conflicting config detected."); + error = -EINVAL; + goto err; + } + n = 1; + + /* do the same if we have the second ICB set */ + if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) { + if (meram_check_overlap(priv, &cfg->icb[1])) { + dev_err(&pdev->dev, "conflicting config detected."); + error = -EINVAL; + goto err; + } + n = 2; + } + + if (is_nvcolor(pixelformat) && n != 2) { + dev_err(&pdev->dev, "requires two ICB sets for planar Y/C."); + error = -EINVAL; + goto err; + } + + /* we now register the ICB */ + cfg->pixelformat = pixelformat; + meram_mark(priv, &cfg->icb[0]); + if (is_nvcolor(pixelformat)) + meram_mark(priv, &cfg->icb[1]); + + /* initialize MERAM */ + meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); + *pitch = out_pitch; + if (pixelformat == SH_MOBILE_MERAM_PF_NV) + meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, + &out_pitch); + else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) + meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, + &out_pitch); + + cfg->current_reg = 1; + meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); + meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); + + dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx", + *icb_addr_y, *icb_addr_c); + +err: + mutex_unlock(&priv->lock); + return error; +} + +static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_cfg *cfg) +{ + struct sh_mobile_meram_priv *priv; + + if (!pdata || !pdata->priv || !cfg) + return -EINVAL; + + priv = pdata->priv; + + mutex_lock(&priv->lock); + + /* deinit & unmark */ + if (is_nvcolor(cfg->pixelformat)) { + meram_deinit(priv, &cfg->icb[1]); + meram_unmark(priv, &cfg->icb[1]); + } + meram_deinit(priv, &cfg->icb[0]); + meram_unmark(priv, &cfg->icb[0]); + + mutex_unlock(&priv->lock); + + return 0; +} + +static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_cfg *cfg, + unsigned long base_addr_y, + unsigned long base_addr_c, + unsigned long *icb_addr_y, + unsigned long *icb_addr_c) +{ + struct sh_mobile_meram_priv *priv; + + if (!pdata || !pdata->priv || !cfg) + return -EINVAL; + + priv = pdata->priv; + + mutex_lock(&priv->lock); + + meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); + meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); + + mutex_unlock(&priv->lock); + + return 0; +} + +static struct sh_mobile_meram_ops sh_mobile_meram_ops = { + .module = THIS_MODULE, + .meram_register = sh_mobile_meram_register, + .meram_unregister = sh_mobile_meram_unregister, + .meram_update = sh_mobile_meram_update, +}; + +/* + * initialize MERAM + */ + +static int sh_mobile_meram_remove(struct platform_device *pdev); + +static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) +{ + struct sh_mobile_meram_priv *priv; + struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; + struct resource *res; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot get platform resources\n"); + return -ENOENT; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, priv); + + /* initialize private data */ + mutex_init(&priv->lock); + priv->base = ioremap_nocache(res->start, resource_size(res)); + if (!priv->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + error = -EFAULT; + goto err; + } + pdata->ops = &sh_mobile_meram_ops; + pdata->priv = priv; + pdata->pdev = pdev; + + /* initialize ICB addressing mode */ + if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) + meram_write_reg(priv->base, MEVCR1, 1 << 29); + + dev_info(&pdev->dev, "sh_mobile_meram initialized."); + + return 0; + +err: + sh_mobile_meram_remove(pdev); + + return error; +} + + +static int sh_mobile_meram_remove(struct platform_device *pdev) +{ + struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + + if (priv->base) + iounmap(priv->base); + + mutex_destroy(&priv->lock); + + kfree(priv); + + return 0; +} + +static struct platform_driver sh_mobile_meram_driver = { + .driver = { + .name = "sh_mobile_meram", + .owner = THIS_MODULE, + }, + .probe = sh_mobile_meram_probe, + .remove = sh_mobile_meram_remove, +}; + +static int __init sh_mobile_meram_init(void) +{ + return platform_driver_register(&sh_mobile_meram_driver); +} + +static void __exit sh_mobile_meram_exit(void) +{ + platform_driver_unregister(&sh_mobile_meram_driver); +} + +module_init(sh_mobile_meram_init); +module_exit(sh_mobile_meram_exit); + +MODULE_DESCRIPTION("SuperH Mobile MERAM driver"); +MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sh_mobile_meram.h b/drivers/video/sh_mobile_meram.h new file mode 100644 index 000000000000..82c54fbce8bd --- /dev/null +++ b/drivers/video/sh_mobile_meram.h @@ -0,0 +1,41 @@ +#ifndef __sh_mobile_meram_h__ +#define __sh_mobile_meram_h__ + +#include <linux/mutex.h> +#include <video/sh_mobile_meram.h> + +/* + * MERAM private + */ + +#define MERAM_ICB_Y 0x1 +#define MERAM_ICB_C 0x2 + +/* MERAM cache size */ +#define SH_MOBILE_MERAM_ICB_NUM 32 + +#define SH_MOBILE_MERAM_CACHE_OFFSET(p) ((p) >> 16) +#define SH_MOBILE_MERAM_CACHE_SIZE(p) ((p) & 0xffff) + +struct sh_mobile_meram_priv { + void __iomem *base; + struct mutex lock; + unsigned long used_icb; + int used_meram_cache_regions; + unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; +}; + +int sh_mobile_meram_alloc_icb(const struct sh_mobile_meram_cfg *cfg, + int xres, + int yres, + unsigned int base_addr, + int yuv_mode, + int *marker_icb, + int *out_pitch); + +void sh_mobile_meram_free_icb(int marker_icb); + +#define SH_MOBILE_MERAM_START(ind, ab) \ + (0xC0000000 | ((ab & 0x1) << 23) | ((ind & 0x1F) << 24)) + +#endif /* !__sh_mobile_meram_h__ */ diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 56ef6b3a9851..87f0be1e78b5 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -1625,22 +1625,22 @@ static int sm501fb_start(struct sm501fb_info *info, return 0; /* everything is setup */ err_mem_res: - release_resource(info->fbmem_res); - kfree(info->fbmem_res); + release_mem_region(info->fbmem_res->start, + resource_size(info->fbmem_res)); err_regs2d_map: iounmap(info->regs2d); err_regs2d_res: - release_resource(info->regs2d_res); - kfree(info->regs2d_res); + release_mem_region(info->regs2d_res->start, + resource_size(info->regs2d_res)); err_regs_map: iounmap(info->regs); err_regs_res: - release_resource(info->regs_res); - kfree(info->regs_res); + release_mem_region(info->regs_res->start, + resource_size(info->regs_res)); err_release: return ret; @@ -1652,16 +1652,16 @@ static void sm501fb_stop(struct sm501fb_info *info) sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); iounmap(info->fbmem); - release_resource(info->fbmem_res); - kfree(info->fbmem_res); + release_mem_region(info->fbmem_res->start, + resource_size(info->fbmem_res)); iounmap(info->regs2d); - release_resource(info->regs2d_res); - kfree(info->regs2d_res); + release_mem_region(info->regs2d_res->start, + resource_size(info->regs2d_res)); iounmap(info->regs); - release_resource(info->regs_res); - kfree(info->regs_res); + release_mem_region(info->regs_res->start, + resource_size(info->regs_res)); } static int sm501fb_init_fb(struct fb_info *fb, diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index 0c341d739604..cd1c4dcef8fd 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -250,7 +250,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) */ static int tmiofb_hw_stop(struct platform_device *dev) { - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct fb_info *info = platform_get_drvdata(dev); struct tmiofb_par *par = info->par; @@ -311,7 +311,7 @@ static int tmiofb_hw_init(struct platform_device *dev) */ static void tmiofb_hw_mode(struct platform_device *dev) { - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct fb_info *info = platform_get_drvdata(dev); struct fb_videomode *mode = info->mode; struct tmiofb_par *par = info->par; @@ -557,8 +557,7 @@ static int tmiofb_ioctl(struct fb_info *fbi, static struct fb_videomode * tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) { - struct tmio_fb_data *data = - mfd_get_data(to_platform_device(info->device)); + struct tmio_fb_data *data = info->device->platform_data; struct fb_videomode *best = NULL; int i; @@ -578,8 +577,7 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct fb_videomode *mode; - struct tmio_fb_data *data = - mfd_get_data(to_platform_device(info->device)); + struct tmio_fb_data *data = info->device->platform_data; mode = tmiofb_find_mode(info, var); if (!mode || var->bits_per_pixel > 16) @@ -680,7 +678,7 @@ static struct fb_ops tmiofb_ops = { static int __devinit tmiofb_probe(struct platform_device *dev) { const struct mfd_cell *cell = mfd_get_cell(dev); - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1); struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2); diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 68041d9dc260..52b0f3e8ccac 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -27,7 +27,9 @@ #include <linux/fb.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/prefetch.h> #include <linux/delay.h> +#include <linux/prefetch.h> #include <video/udlfb.h> #include "edid.h" @@ -1586,10 +1588,19 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_create_file(info->dev, &fb_device_attrs[i]); + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { + retval = device_create_file(info->dev, &fb_device_attrs[i]); + if (retval) { + pr_err("device_create_file failed %d\n", retval); + goto err_del_attrs; + } + } - device_create_bin_file(info->dev, &edid_attr); + retval = device_create_bin_file(info->dev, &edid_attr); + if (retval) { + pr_err("device_create_bin_file failed %d\n", retval); + goto err_del_attrs; + } pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." " Using %dK framebuffer memory\n", info->node, @@ -1598,6 +1609,10 @@ static int dlfb_usb_probe(struct usb_interface *interface, info->fix.smem_len * 2 : info->fix.smem_len) >> 10); return 0; +err_del_attrs: + for (i -= 1; i >= 0; i--) + device_remove_file(info->dev, &fb_device_attrs[i]); + error: if (dev) { diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 53b2c5aae067..305c975b1787 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -1265,9 +1265,11 @@ static void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image static void vga16fb_destroy(struct fb_info *info) { + struct platform_device *dev = container_of(info->device, struct platform_device, dev); iounmap(info->screen_base); fb_dealloc_cmap(&info->cmap); /* XXX unshare VGA regions */ + platform_set_drvdata(dev, NULL); framebuffer_release(info); } diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile index 96f01ee2a412..5108136e8776 100644 --- a/drivers/video/via/Makefile +++ b/drivers/video/via/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_FB_VIA) += viafb.o viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \ via_utility.o vt1636.o global.o tblDPASetting.o viamode.o \ - via-core.o via-gpio.o via_modesetting.o + via-core.o via-gpio.o via_modesetting.o via_clock.o diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index 29d70244a21f..3ebf20c06eef 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -137,17 +137,11 @@ struct chip_information { struct lvds_chip_information lvds_chip_info2; }; -struct crt_setting_information { - int iga_path; -}; - struct tmds_setting_information { int iga_path; int h_active; int v_active; int max_pixel_clock; - int max_hres; - int max_vres; }; struct lvds_setting_information { diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index 41ca198b5098..b1f364745ca0 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -28,17 +28,11 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len); static void __devinit dvi_get_panel_size_from_DDCv1( struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting); -static void __devinit dvi_get_panel_size_from_DDCv2( - struct tmds_chip_information *tmds_chip, - struct tmds_setting_information *tmds_setting); static int viafb_dvi_query_EDID(void); -static int check_tmds_chip(int device_id_subaddr, int device_id) +static inline bool check_tmds_chip(int device_id_subaddr, int device_id) { - if (tmds_register_read(device_id_subaddr) == device_id) - return OK; - else - return FAIL; + return tmds_register_read(device_id_subaddr) == device_id; } void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, @@ -47,22 +41,13 @@ void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, DEBUG_MSG(KERN_INFO "viafb_init_dvi_size()\n"); viafb_dvi_sense(); - switch (viafb_dvi_query_EDID()) { - case 1: + if (viafb_dvi_query_EDID() == 1) dvi_get_panel_size_from_DDCv1(tmds_chip, tmds_setting); - break; - case 2: - dvi_get_panel_size_from_DDCv2(tmds_chip, tmds_setting); - break; - default: - printk(KERN_WARNING "viafb_init_dvi_size: DVI panel size undetected!\n"); - break; - } return; } -int __devinit viafb_tmds_trasmitter_identify(void) +bool __devinit viafb_tmds_trasmitter_identify(void) { unsigned char sr2a = 0, sr1e = 0, sr3e = 0; @@ -101,7 +86,7 @@ int __devinit viafb_tmds_trasmitter_identify(void) viaparinfo->chip_info-> tmds_chip_info.tmds_chip_slave_addr = VT1632_TMDS_I2C_ADDR; viaparinfo->chip_info->tmds_chip_info.i2c_port = VIA_PORT_31; - if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) != FAIL) { + if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID)) { /* * Currently only support 12bits,dual edge,add 24bits mode later */ @@ -112,11 +97,10 @@ int __devinit viafb_tmds_trasmitter_identify(void) viaparinfo->chip_info->tmds_chip_info.tmds_chip_name); DEBUG_MSG(KERN_INFO "\n %2d", viaparinfo->chip_info->tmds_chip_info.i2c_port); - return OK; + return true; } else { viaparinfo->chip_info->tmds_chip_info.i2c_port = VIA_PORT_2C; - if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) - != FAIL) { + if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID)) { tmds_register_write(0x08, 0x3b); DEBUG_MSG(KERN_INFO "\n VT1632 TMDS ! \n"); DEBUG_MSG(KERN_INFO "\n %2d", @@ -125,7 +109,7 @@ int __devinit viafb_tmds_trasmitter_identify(void) DEBUG_MSG(KERN_INFO "\n %2d", viaparinfo->chip_info-> tmds_chip_info.i2c_port); - return OK; + return true; } } @@ -135,7 +119,7 @@ int __devinit viafb_tmds_trasmitter_identify(void) ((viafb_display_hardware_layout == HW_LAYOUT_DVI_ONLY) || (viafb_display_hardware_layout == HW_LAYOUT_LCD_DVI))) { DEBUG_MSG(KERN_INFO "\n Integrated TMDS ! \n"); - return OK; + return true; } switch (viaparinfo->chip_info->gfx_chip_name) { @@ -159,7 +143,7 @@ int __devinit viafb_tmds_trasmitter_identify(void) tmds_chip_info.tmds_chip_name = NON_TMDS_TRANSMITTER; viaparinfo->chip_info->tmds_chip_info. tmds_chip_slave_addr = VT1632_TMDS_I2C_ADDR; - return FAIL; + return false; } static void tmds_register_write(int index, u8 data) @@ -306,12 +290,7 @@ static int viafb_dvi_query_EDID(void) return EDID_VERSION_1; /* Found EDID1 Table */ } - data0 = (u8) tmds_register_read(0x00); - viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = restore; - if (data0 == 0x20) - return EDID_VERSION_2; /* Found EDID2 Table */ - else - return false; + return false; } /* Get Panel Size Using EDID1 Table */ @@ -319,50 +298,15 @@ static void __devinit dvi_get_panel_size_from_DDCv1( struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting) { - int i, max_h = 0, tmp, restore; - unsigned char rData; + int i, restore; unsigned char EDID_DATA[18]; DEBUG_MSG(KERN_INFO "\n dvi_get_panel_size_from_DDCv1 \n"); restore = tmds_chip->tmds_chip_slave_addr; tmds_chip->tmds_chip_slave_addr = 0xA0; - - rData = tmds_register_read(0x23); - if (rData & 0x3C) - max_h = 640; - if (rData & 0xC0) - max_h = 720; - if (rData & 0x03) - max_h = 800; - - rData = tmds_register_read(0x24); - if (rData & 0xC0) - max_h = 800; - if (rData & 0x1E) - max_h = 1024; - if (rData & 0x01) - max_h = 1280; - for (i = 0x25; i < 0x6D; i++) { switch (i) { - case 0x26: - case 0x28: - case 0x2A: - case 0x2C: - case 0x2E: - case 0x30: - case 0x32: - case 0x34: - rData = tmds_register_read(i); - if (rData == 1) - break; - /* data = (data + 31) * 8 */ - tmp = (rData + 31) << 3; - if (tmp > max_h) - max_h = tmp; - break; - case 0x36: case 0x48: case 0x5A: @@ -383,91 +327,11 @@ static void __devinit dvi_get_panel_size_from_DDCv1( } } - tmds_setting->max_hres = max_h; - switch (max_h) { - case 640: - tmds_setting->max_vres = 480; - break; - case 800: - tmds_setting->max_vres = 600; - break; - case 1024: - tmds_setting->max_vres = 768; - break; - case 1280: - tmds_setting->max_vres = 1024; - break; - case 1400: - tmds_setting->max_vres = 1050; - break; - case 1440: - tmds_setting->max_vres = 1050; - break; - case 1600: - tmds_setting->max_vres = 1200; - break; - case 1920: - tmds_setting->max_vres = 1080; - break; - default: - DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d ! " - "set default panel size.\n", max_h); - break; - } - DEBUG_MSG(KERN_INFO "DVI max pixelclock = %d\n", tmds_setting->max_pixel_clock); tmds_chip->tmds_chip_slave_addr = restore; } -/* Get Panel Size Using EDID2 Table */ -static void __devinit dvi_get_panel_size_from_DDCv2( - struct tmds_chip_information *tmds_chip, - struct tmds_setting_information *tmds_setting) -{ - int restore; - unsigned char R_Buffer[2]; - - DEBUG_MSG(KERN_INFO "\n dvi_get_panel_size_from_DDCv2 \n"); - - restore = tmds_chip->tmds_chip_slave_addr; - tmds_chip->tmds_chip_slave_addr = 0xA2; - - /* Horizontal: 0x76, 0x77 */ - tmds_register_read_bytes(0x76, R_Buffer, 2); - tmds_setting->max_hres = R_Buffer[0] + (R_Buffer[1] << 8); - - switch (tmds_setting->max_hres) { - case 640: - tmds_setting->max_vres = 480; - break; - case 800: - tmds_setting->max_vres = 600; - break; - case 1024: - tmds_setting->max_vres = 768; - break; - case 1280: - tmds_setting->max_vres = 1024; - break; - case 1400: - tmds_setting->max_vres = 1050; - break; - case 1440: - tmds_setting->max_vres = 1050; - break; - case 1600: - tmds_setting->max_vres = 1200; - break; - default: - DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d! " - "set default panel size.\n", tmds_setting->max_hres); - break; - } - - tmds_chip->tmds_chip_slave_addr = restore; -} - /* If Disable DVI, turn off pad */ void viafb_dvi_disable(void) { diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h index 2c525c0c1adb..f473dd010977 100644 --- a/drivers/video/via/dvi.h +++ b/drivers/video/via/dvi.h @@ -56,7 +56,7 @@ int viafb_dvi_sense(void); void viafb_dvi_disable(void); void viafb_dvi_enable(void); -int __devinit viafb_tmds_trasmitter_identify(void); +bool __devinit viafb_tmds_trasmitter_identify(void); void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting); void viafb_dvi_set_mode(struct VideoModeTable *videoMode, int mode_bpp, diff --git a/drivers/video/via/global.c b/drivers/video/via/global.c index 1ee511b73307..e10d8249534c 100644 --- a/drivers/video/via/global.c +++ b/drivers/video/via/global.c @@ -40,10 +40,6 @@ int viafb_hotplug_Yres = 480; int viafb_hotplug_bpp = 32; int viafb_hotplug_refresh = 60; int viafb_primary_dev = None_Device; -unsigned int viafb_second_xres = 640; -unsigned int viafb_second_yres = 480; -unsigned int viafb_second_virtual_xres; -unsigned int viafb_second_virtual_yres; int viafb_lcd_panel_id = LCD_PANEL_ID_MAXIMUM + 1; struct fb_info *viafbinfo; struct fb_info *viafbinfo1; diff --git a/drivers/video/via/global.h b/drivers/video/via/global.h index 38ef5ac66953..ff969dc34593 100644 --- a/drivers/video/via/global.h +++ b/drivers/video/via/global.h @@ -73,8 +73,6 @@ extern int viafb_hotplug_bpp; extern int viafb_hotplug_refresh; extern int viafb_primary_dev; -extern unsigned int viafb_second_xres; -extern unsigned int viafb_second_yres; extern int viafb_lcd_panel_id; #endif /* __GLOBAL_H__ */ diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index dc4c778877ce..47b13535ed2b 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -20,274 +20,84 @@ */ #include <linux/via-core.h> +#include <asm/olpc.h> #include "global.h" - -static struct pll_config cle266_pll_config[] = { - {19, 4, 0}, - {26, 5, 0}, - {28, 5, 0}, - {31, 5, 0}, - {33, 5, 0}, - {55, 5, 0}, - {102, 5, 0}, - {53, 6, 0}, - {92, 6, 0}, - {98, 6, 0}, - {112, 6, 0}, - {41, 7, 0}, - {60, 7, 0}, - {99, 7, 0}, - {100, 7, 0}, - {83, 8, 0}, - {86, 8, 0}, - {108, 8, 0}, - {87, 9, 0}, - {118, 9, 0}, - {95, 12, 0}, - {115, 12, 0}, - {108, 13, 0}, - {83, 17, 0}, - {67, 20, 0}, - {86, 20, 0}, - {98, 20, 0}, - {121, 24, 0}, - {99, 29, 0}, - {33, 3, 1}, - {15, 4, 1}, - {23, 4, 1}, - {37, 5, 1}, - {83, 5, 1}, - {85, 5, 1}, - {94, 5, 1}, - {103, 5, 1}, - {109, 5, 1}, - {113, 5, 1}, - {121, 5, 1}, - {82, 6, 1}, - {31, 7, 1}, - {55, 7, 1}, - {84, 7, 1}, - {83, 8, 1}, - {76, 9, 1}, - {127, 9, 1}, - {33, 4, 2}, - {75, 4, 2}, - {119, 4, 2}, - {121, 4, 2}, - {91, 5, 2}, - {118, 5, 2}, - {83, 6, 2}, - {109, 6, 2}, - {90, 7, 2}, - {93, 2, 3}, - {53, 3, 3}, - {73, 4, 3}, - {89, 4, 3}, - {105, 4, 3}, - {117, 4, 3}, - {101, 5, 3}, - {121, 5, 3}, - {127, 5, 3}, - {99, 7, 3} +#include "via_clock.h" + +static struct pll_limit cle266_pll_limits[] = { + {19, 19, 4, 0}, + {26, 102, 5, 0}, + {53, 112, 6, 0}, + {41, 100, 7, 0}, + {83, 108, 8, 0}, + {87, 118, 9, 0}, + {95, 115, 12, 0}, + {108, 108, 13, 0}, + {83, 83, 17, 0}, + {67, 98, 20, 0}, + {121, 121, 24, 0}, + {99, 99, 29, 0}, + {33, 33, 3, 1}, + {15, 23, 4, 1}, + {37, 121, 5, 1}, + {82, 82, 6, 1}, + {31, 84, 7, 1}, + {83, 83, 8, 1}, + {76, 127, 9, 1}, + {33, 121, 4, 2}, + {91, 118, 5, 2}, + {83, 109, 6, 2}, + {90, 90, 7, 2}, + {93, 93, 2, 3}, + {53, 53, 3, 3}, + {73, 117, 4, 3}, + {101, 127, 5, 3}, + {99, 99, 7, 3} }; -static struct pll_config k800_pll_config[] = { - {22, 2, 0}, - {28, 3, 0}, - {81, 3, 1}, - {85, 3, 1}, - {98, 3, 1}, - {112, 3, 1}, - {86, 4, 1}, - {166, 4, 1}, - {109, 5, 1}, - {113, 5, 1}, - {121, 5, 1}, - {131, 5, 1}, - {143, 5, 1}, - {153, 5, 1}, - {66, 3, 2}, - {68, 3, 2}, - {95, 3, 2}, - {106, 3, 2}, - {116, 3, 2}, - {93, 4, 2}, - {119, 4, 2}, - {121, 4, 2}, - {133, 4, 2}, - {137, 4, 2}, - {117, 5, 2}, - {118, 5, 2}, - {120, 5, 2}, - {124, 5, 2}, - {132, 5, 2}, - {137, 5, 2}, - {141, 5, 2}, - {166, 5, 2}, - {170, 5, 2}, - {191, 5, 2}, - {206, 5, 2}, - {208, 5, 2}, - {30, 2, 3}, - {69, 3, 3}, - {82, 3, 3}, - {83, 3, 3}, - {109, 3, 3}, - {114, 3, 3}, - {125, 3, 3}, - {89, 4, 3}, - {103, 4, 3}, - {117, 4, 3}, - {126, 4, 3}, - {150, 4, 3}, - {161, 4, 3}, - {121, 5, 3}, - {127, 5, 3}, - {131, 5, 3}, - {134, 5, 3}, - {148, 5, 3}, - {169, 5, 3}, - {172, 5, 3}, - {182, 5, 3}, - {195, 5, 3}, - {196, 5, 3}, - {208, 5, 3}, - {66, 2, 4}, - {85, 3, 4}, - {141, 4, 4}, - {146, 4, 4}, - {161, 4, 4}, - {177, 5, 4} +static struct pll_limit k800_pll_limits[] = { + {22, 22, 2, 0}, + {28, 28, 3, 0}, + {81, 112, 3, 1}, + {86, 166, 4, 1}, + {109, 153, 5, 1}, + {66, 116, 3, 2}, + {93, 137, 4, 2}, + {117, 208, 5, 2}, + {30, 30, 2, 3}, + {69, 125, 3, 3}, + {89, 161, 4, 3}, + {121, 208, 5, 3}, + {66, 66, 2, 4}, + {85, 85, 3, 4}, + {141, 161, 4, 4}, + {177, 177, 5, 4} }; -static struct pll_config cx700_pll_config[] = { - {98, 3, 1}, - {86, 4, 1}, - {109, 5, 1}, - {110, 5, 1}, - {113, 5, 1}, - {121, 5, 1}, - {131, 5, 1}, - {135, 5, 1}, - {142, 5, 1}, - {143, 5, 1}, - {153, 5, 1}, - {187, 5, 1}, - {208, 5, 1}, - {68, 2, 2}, - {95, 3, 2}, - {116, 3, 2}, - {93, 4, 2}, - {119, 4, 2}, - {133, 4, 2}, - {137, 4, 2}, - {151, 4, 2}, - {166, 4, 2}, - {110, 5, 2}, - {112, 5, 2}, - {117, 5, 2}, - {118, 5, 2}, - {120, 5, 2}, - {132, 5, 2}, - {137, 5, 2}, - {141, 5, 2}, - {151, 5, 2}, - {166, 5, 2}, - {175, 5, 2}, - {191, 5, 2}, - {206, 5, 2}, - {174, 7, 2}, - {82, 3, 3}, - {109, 3, 3}, - {117, 4, 3}, - {150, 4, 3}, - {161, 4, 3}, - {112, 5, 3}, - {115, 5, 3}, - {121, 5, 3}, - {127, 5, 3}, - {129, 5, 3}, - {131, 5, 3}, - {134, 5, 3}, - {138, 5, 3}, - {148, 5, 3}, - {157, 5, 3}, - {169, 5, 3}, - {172, 5, 3}, - {190, 5, 3}, - {195, 5, 3}, - {196, 5, 3}, - {208, 5, 3}, - {141, 5, 4}, - {150, 5, 4}, - {166, 5, 4}, - {176, 5, 4}, - {177, 5, 4}, - {183, 5, 4}, - {202, 5, 4} +static struct pll_limit cx700_pll_limits[] = { + {98, 98, 3, 1}, + {86, 86, 4, 1}, + {109, 208, 5, 1}, + {68, 68, 2, 2}, + {95, 116, 3, 2}, + {93, 166, 4, 2}, + {110, 206, 5, 2}, + {174, 174, 7, 2}, + {82, 109, 3, 3}, + {117, 161, 4, 3}, + {112, 208, 5, 3}, + {141, 202, 5, 4} }; -static struct pll_config vx855_pll_config[] = { - {86, 4, 1}, - {108, 5, 1}, - {110, 5, 1}, - {113, 5, 1}, - {121, 5, 1}, - {131, 5, 1}, - {135, 5, 1}, - {142, 5, 1}, - {143, 5, 1}, - {153, 5, 1}, - {164, 5, 1}, - {187, 5, 1}, - {208, 5, 1}, - {110, 5, 2}, - {112, 5, 2}, - {117, 5, 2}, - {118, 5, 2}, - {124, 5, 2}, - {132, 5, 2}, - {137, 5, 2}, - {141, 5, 2}, - {149, 5, 2}, - {151, 5, 2}, - {159, 5, 2}, - {166, 5, 2}, - {167, 5, 2}, - {172, 5, 2}, - {189, 5, 2}, - {191, 5, 2}, - {194, 5, 2}, - {206, 5, 2}, - {208, 5, 2}, - {83, 3, 3}, - {88, 3, 3}, - {109, 3, 3}, - {112, 3, 3}, - {103, 4, 3}, - {105, 4, 3}, - {161, 4, 3}, - {112, 5, 3}, - {115, 5, 3}, - {121, 5, 3}, - {127, 5, 3}, - {134, 5, 3}, - {137, 5, 3}, - {148, 5, 3}, - {157, 5, 3}, - {169, 5, 3}, - {172, 5, 3}, - {182, 5, 3}, - {191, 5, 3}, - {195, 5, 3}, - {209, 5, 3}, - {142, 4, 4}, - {146, 4, 4}, - {161, 4, 4}, - {141, 5, 4}, - {150, 5, 4}, - {165, 5, 4}, - {176, 5, 4} +static struct pll_limit vx855_pll_limits[] = { + {86, 86, 4, 1}, + {108, 208, 5, 1}, + {110, 208, 5, 2}, + {83, 112, 3, 3}, + {103, 161, 4, 3}, + {112, 209, 5, 3}, + {142, 161, 4, 4}, + {141, 176, 5, 4} }; /* according to VIA Technologies these values are based on experiment */ @@ -308,6 +118,42 @@ static struct io_reg scaling_parameters[] = { {VIACR, CR87, 0xFF, 0x1F}, /* LCD Scaling Parameter 14 */ }; +static struct io_reg common_vga[] = { + {VIACR, CR07, 0x10, 0x10}, /* [0] vertical total (bit 8) + [1] vertical display end (bit 8) + [2] vertical retrace start (bit 8) + [3] start vertical blanking (bit 8) + [4] line compare (bit 8) + [5] vertical total (bit 9) + [6] vertical display end (bit 9) + [7] vertical retrace start (bit 9) */ + {VIACR, CR08, 0xFF, 0x00}, /* [0-4] preset row scan + [5-6] byte panning */ + {VIACR, CR09, 0xDF, 0x40}, /* [0-4] max scan line + [5] start vertical blanking (bit 9) + [6] line compare (bit 9) + [7] scan doubling */ + {VIACR, CR0A, 0xFF, 0x1E}, /* [0-4] cursor start + [5] cursor disable */ + {VIACR, CR0B, 0xFF, 0x00}, /* [0-4] cursor end + [5-6] cursor skew */ + {VIACR, CR0E, 0xFF, 0x00}, /* [0-7] cursor location (high) */ + {VIACR, CR0F, 0xFF, 0x00}, /* [0-7] cursor location (low) */ + {VIACR, CR11, 0xF0, 0x80}, /* [0-3] vertical retrace end + [6] memory refresh bandwidth + [7] CRTC register protect enable */ + {VIACR, CR14, 0xFF, 0x00}, /* [0-4] underline location + [5] divide memory address clock by 4 + [6] double word addressing */ + {VIACR, CR17, 0xFF, 0x63}, /* [0-1] mapping of display address 13-14 + [2] divide scan line clock by 2 + [3] divide memory address clock by 2 + [5] address wrap + [6] byte mode select + [7] sync enable */ + {VIACR, CR18, 0xFF, 0xFF}, /* [0-7] line compare */ +}; + static struct fifo_depth_select display_fifo_depth_reg = { /* IGA1 FIFO Depth_Select */ {IGA1_FIFO_DEPTH_SELECT_REG_NUM, {{SR17, 0, 7} } }, @@ -676,6 +522,9 @@ static struct via_device_mapping device_mapping[] = { {VIA_LVDS2, "LVDS2"} }; +/* structure with function pointers to support clock control */ +static struct via_clock clock; + static void load_fix_bit_crtc_reg(void); static void __devinit init_gfx_chip_info(int chip_type); static void __devinit init_tmds_chip_info(void); @@ -770,13 +619,14 @@ static u32 get_lcd_devices(int output_interface) /*Set IGA path for each device*/ void viafb_set_iga_path(void) { + int crt_iga_path = 0; if (viafb_SAMM_ON == 1) { if (viafb_CRT_ON) { if (viafb_primary_dev == CRT_Device) - viaparinfo->crt_setting_info->iga_path = IGA1; + crt_iga_path = IGA1; else - viaparinfo->crt_setting_info->iga_path = IGA2; + crt_iga_path = IGA2; } if (viafb_DVI_ON) { @@ -793,8 +643,7 @@ void viafb_set_iga_path(void) UNICHROME_CLE266)) { viaparinfo-> lvds_setting_info->iga_path = IGA2; - viaparinfo-> - crt_setting_info->iga_path = IGA1; + crt_iga_path = IGA1; viaparinfo-> tmds_setting_info->iga_path = IGA1; } else @@ -814,10 +663,10 @@ void viafb_set_iga_path(void) viafb_SAMM_ON = 0; if (viafb_CRT_ON && viafb_LCD_ON) { - viaparinfo->crt_setting_info->iga_path = IGA1; + crt_iga_path = IGA1; viaparinfo->lvds_setting_info->iga_path = IGA2; } else if (viafb_CRT_ON && viafb_DVI_ON) { - viaparinfo->crt_setting_info->iga_path = IGA1; + crt_iga_path = IGA1; viaparinfo->tmds_setting_info->iga_path = IGA2; } else if (viafb_LCD_ON && viafb_DVI_ON) { viaparinfo->tmds_setting_info->iga_path = IGA1; @@ -826,7 +675,7 @@ void viafb_set_iga_path(void) viaparinfo->lvds_setting_info->iga_path = IGA2; viaparinfo->lvds_setting_info2->iga_path = IGA2; } else if (viafb_CRT_ON) { - viaparinfo->crt_setting_info->iga_path = IGA1; + crt_iga_path = IGA1; } else if (viafb_LCD_ON) { viaparinfo->lvds_setting_info->iga_path = IGA2; } else if (viafb_DVI_ON) { @@ -837,7 +686,7 @@ void viafb_set_iga_path(void) viaparinfo->shared->iga1_devices = 0; viaparinfo->shared->iga2_devices = 0; if (viafb_CRT_ON) { - if (viaparinfo->crt_setting_info->iga_path == IGA1) + if (crt_iga_path == IGA1) viaparinfo->shared->iga1_devices |= VIA_CRT; else viaparinfo->shared->iga2_devices |= VIA_CRT; @@ -875,6 +724,10 @@ void viafb_set_iga_path(void) viaparinfo->chip_info-> lvds_chip_info2.output_interface); } + + /* looks like the OLPC has its display wired to DVP1 and LVDS2 */ + if (machine_is_olpc()) + viaparinfo->shared->iga2_devices = VIA_DVP1 | VIA_LVDS2; } static void set_color_register(u8 index, u8 red, u8 green, u8 blue) @@ -1162,25 +1015,17 @@ void via_odev_to_seq(struct seq_file *m, u32 odev) static void load_fix_bit_crtc_reg(void) { + viafb_unlock_crt(); + /* always set to 1 */ viafb_write_reg_mask(CR03, VIACR, 0x80, BIT7); /* line compare should set all bits = 1 (extend modes) */ - viafb_write_reg(CR18, VIACR, 0xff); - /* line compare should set all bits = 1 (extend modes) */ - viafb_write_reg_mask(CR07, VIACR, 0x10, BIT4); - /* line compare should set all bits = 1 (extend modes) */ - viafb_write_reg_mask(CR09, VIACR, 0x40, BIT6); - /* line compare should set all bits = 1 (extend modes) */ viafb_write_reg_mask(CR35, VIACR, 0x10, BIT4); /* line compare should set all bits = 1 (extend modes) */ viafb_write_reg_mask(CR33, VIACR, 0x06, BIT0 + BIT1 + BIT2); /*viafb_write_reg_mask(CR32, VIACR, 0x01, BIT0); */ - /* extend mode always set to e3h */ - viafb_write_reg(CR17, VIACR, 0xe3); - /* extend mode always set to 0h */ - viafb_write_reg(CR08, VIACR, 0x00); - /* extend mode always set to 0h */ - viafb_write_reg(CR14, VIACR, 0x00); + + viafb_lock_crt(); /* If K8M800, enable Prefetch Mode. */ if ((viaparinfo->chip_info->gfx_chip_name == UNICHROME_K800) @@ -1601,69 +1446,54 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active) } -static u32 cle266_encode_pll(struct pll_config pll) -{ - return (pll.multiplier << 8) - | (pll.rshift << 6) - | pll.divisor; -} - -static u32 k800_encode_pll(struct pll_config pll) -{ - return ((pll.divisor - 2) << 16) - | (pll.rshift << 10) - | (pll.multiplier - 2); -} - -static u32 vx855_encode_pll(struct pll_config pll) -{ - return (pll.divisor << 16) - | (pll.rshift << 10) - | pll.multiplier; -} - -static inline u32 get_pll_internal_frequency(u32 ref_freq, - struct pll_config pll) -{ - return ref_freq / pll.divisor * pll.multiplier; -} - -static inline u32 get_pll_output_frequency(u32 ref_freq, struct pll_config pll) -{ - return get_pll_internal_frequency(ref_freq, pll)>>pll.rshift; -} - -static struct pll_config get_pll_config(struct pll_config *config, int size, +static struct via_pll_config get_pll_config(struct pll_limit *limits, int size, int clk) { - struct pll_config best = config[0]; + struct via_pll_config cur, up, down, best = {0, 1, 0}; const u32 f0 = 14318180; /* X1 frequency */ - int i; - - for (i = 1; i < size; i++) { - if (abs(get_pll_output_frequency(f0, config[i]) - clk) - < abs(get_pll_output_frequency(f0, best) - clk)) - best = config[i]; + int i, f; + + for (i = 0; i < size; i++) { + cur.rshift = limits[i].rshift; + cur.divisor = limits[i].divisor; + cur.multiplier = clk / ((f0 / cur.divisor)>>cur.rshift); + f = abs(get_pll_output_frequency(f0, cur) - clk); + up = down = cur; + up.multiplier++; + down.multiplier--; + if (abs(get_pll_output_frequency(f0, up) - clk) < f) + cur = up; + else if (abs(get_pll_output_frequency(f0, down) - clk) < f) + cur = down; + + if (cur.multiplier < limits[i].multiplier_min) + cur.multiplier = limits[i].multiplier_min; + else if (cur.multiplier > limits[i].multiplier_max) + cur.multiplier = limits[i].multiplier_max; + + f = abs(get_pll_output_frequency(f0, cur) - clk); + if (f < abs(get_pll_output_frequency(f0, best) - clk)) + best = cur; } return best; } -u32 viafb_get_clk_value(int clk) +static struct via_pll_config get_best_pll_config(int clk) { - u32 value = 0; + struct via_pll_config config; switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: case UNICHROME_K400: - value = cle266_encode_pll(get_pll_config(cle266_pll_config, - ARRAY_SIZE(cle266_pll_config), clk)); + config = get_pll_config(cle266_pll_limits, + ARRAY_SIZE(cle266_pll_limits), clk); break; case UNICHROME_K800: case UNICHROME_PM800: case UNICHROME_CN700: - value = k800_encode_pll(get_pll_config(k800_pll_config, - ARRAY_SIZE(k800_pll_config), clk)); + config = get_pll_config(k800_pll_limits, + ARRAY_SIZE(k800_pll_limits), clk); break; case UNICHROME_CX700: case UNICHROME_CN750: @@ -1671,92 +1501,28 @@ u32 viafb_get_clk_value(int clk) case UNICHROME_P4M890: case UNICHROME_P4M900: case UNICHROME_VX800: - value = k800_encode_pll(get_pll_config(cx700_pll_config, - ARRAY_SIZE(cx700_pll_config), clk)); + config = get_pll_config(cx700_pll_limits, + ARRAY_SIZE(cx700_pll_limits), clk); break; case UNICHROME_VX855: case UNICHROME_VX900: - value = vx855_encode_pll(get_pll_config(vx855_pll_config, - ARRAY_SIZE(vx855_pll_config), clk)); + config = get_pll_config(vx855_pll_limits, + ARRAY_SIZE(vx855_pll_limits), clk); break; } - return value; + return config; } /* Set VCLK*/ void viafb_set_vclock(u32 clk, int set_iga) { - /* H.W. Reset : ON */ - viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7); + struct via_pll_config config = get_best_pll_config(clk); - if (set_iga == IGA1) { - /* Change D,N FOR VCLK */ - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_CLE266: - case UNICHROME_K400: - via_write_reg(VIASR, SR46, (clk & 0x00FF)); - via_write_reg(VIASR, SR47, (clk & 0xFF00) >> 8); - break; - - case UNICHROME_K800: - case UNICHROME_PM800: - case UNICHROME_CN700: - case UNICHROME_CX700: - case UNICHROME_CN750: - case UNICHROME_K8M890: - case UNICHROME_P4M890: - case UNICHROME_P4M900: - case UNICHROME_VX800: - case UNICHROME_VX855: - case UNICHROME_VX900: - via_write_reg(VIASR, SR44, (clk & 0x0000FF)); - via_write_reg(VIASR, SR45, (clk & 0x00FF00) >> 8); - via_write_reg(VIASR, SR46, (clk & 0xFF0000) >> 16); - break; - } - } - - if (set_iga == IGA2) { - /* Change D,N FOR LCK */ - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_CLE266: - case UNICHROME_K400: - via_write_reg(VIASR, SR44, (clk & 0x00FF)); - via_write_reg(VIASR, SR45, (clk & 0xFF00) >> 8); - break; - - case UNICHROME_K800: - case UNICHROME_PM800: - case UNICHROME_CN700: - case UNICHROME_CX700: - case UNICHROME_CN750: - case UNICHROME_K8M890: - case UNICHROME_P4M890: - case UNICHROME_P4M900: - case UNICHROME_VX800: - case UNICHROME_VX855: - case UNICHROME_VX900: - via_write_reg(VIASR, SR4A, (clk & 0x0000FF)); - via_write_reg(VIASR, SR4B, (clk & 0x00FF00) >> 8); - via_write_reg(VIASR, SR4C, (clk & 0xFF0000) >> 16); - break; - } - } - - /* H.W. Reset : OFF */ - viafb_write_reg_mask(CR17, VIACR, 0x80, BIT7); - - /* Reset PLL */ - if (set_iga == IGA1) { - viafb_write_reg_mask(SR40, VIASR, 0x02, BIT1); - viafb_write_reg_mask(SR40, VIASR, 0x00, BIT1); - } - - if (set_iga == IGA2) { - viafb_write_reg_mask(SR40, VIASR, 0x04, BIT2); - viafb_write_reg_mask(SR40, VIASR, 0x00, BIT2); - } + if (set_iga == IGA1) + clock.set_primary_pll(config); + if (set_iga == IGA2) + clock.set_secondary_pll(config); /* Fire! */ via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ @@ -2002,7 +1768,7 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, int i; int index = 0; int h_addr, v_addr; - u32 pll_D_N, clock, refresh = viafb_refresh; + u32 clock, refresh = viafb_refresh; if (viafb_SAMM_ON && set_iga == IGA2) refresh = viafb_refresh1; @@ -2033,8 +1799,6 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, v_addr = crt_reg.ver_addr; if (set_iga == IGA1) { viafb_unlock_crt(); - viafb_write_reg(CR09, VIACR, 0x00); /*initial CR09=0 */ - viafb_write_reg_mask(CR11, VIACR, 0x00, BIT4 + BIT5 + BIT6); viafb_write_reg_mask(CR17, VIACR, 0x00, BIT7); } @@ -2047,7 +1811,6 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, break; } - load_fix_bit_crtc_reg(); viafb_lock_crt(); viafb_write_reg_mask(CR17, VIACR, 0x80, BIT7); viafb_load_fetch_count_reg(h_addr, bpp_byte, set_iga); @@ -2059,20 +1822,17 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, clock = crt_reg.hor_total * crt_reg.ver_total * crt_table[index].refresh_rate; - pll_D_N = viafb_get_clk_value(clock); - DEBUG_MSG(KERN_INFO "PLL=%x", pll_D_N); - viafb_set_vclock(pll_D_N, set_iga); + viafb_set_vclock(clock, set_iga); } void __devinit viafb_init_chip_info(int chip_type) { + via_clock_init(&clock, chip_type); init_gfx_chip_info(chip_type); init_tmds_chip_info(); init_lvds_chip_info(); - viaparinfo->crt_setting_info->iga_path = IGA1; - /*Set IGA path for each device */ viafb_set_iga_path(); @@ -2354,6 +2114,7 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, outb(0x00, VIAAR); /* Write Common Setting for Video Mode */ + viafb_write_regx(common_vga, ARRAY_SIZE(common_vga)); switch (viaparinfo->chip_info->gfx_chip_name) { case UNICHROME_CLE266: viafb_write_regx(CLE266_ModeXregs, NUM_TOTAL_CLE266_ModeXregs); @@ -2400,9 +2161,6 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, viafb_write_reg_mask(0x15, VIASR, 0xA2, 0xA2); - /* Write CRTC */ - viafb_fill_crtc_timing(crt_timing, vmode_tbl, video_bpp / 8, IGA1); - /* Write Graphic Controller */ for (i = 0; i < StdGR; i++) via_write_reg(VIAGR, i, VPIT.GR[i]); @@ -2432,6 +2190,7 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, } } + load_fix_bit_crtc_reg(); via_set_primary_pitch(viafbinfo->fix.line_length); via_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length : viafbinfo->fix.line_length); @@ -2451,15 +2210,15 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, /* CRT set mode */ if (viafb_CRT_ON) { - if (viafb_SAMM_ON && (viaparinfo->crt_setting_info->iga_path == - IGA2)) { + if (viafb_SAMM_ON && + viaparinfo->shared->iga2_devices & VIA_CRT) { viafb_fill_crtc_timing(crt_timing1, vmode_tbl1, - video_bpp1 / 8, - viaparinfo->crt_setting_info->iga_path); + video_bpp1 / 8, IGA2); } else { viafb_fill_crtc_timing(crt_timing, vmode_tbl, video_bpp / 8, - viaparinfo->crt_setting_info->iga_path); + (viaparinfo->shared->iga1_devices & VIA_CRT) + ? IGA1 : IGA2); } /* Patch if set_hres is not 8 alignment (1366) to viafb_setmode @@ -2557,6 +2316,33 @@ int viafb_setmode(struct VideoModeTable *vmode_tbl, int video_bpp, get_sync(viafbinfo1)); } + clock.set_engine_pll_state(VIA_STATE_ON); + clock.set_primary_clock_source(VIA_CLKSRC_X1, true); + clock.set_secondary_clock_source(VIA_CLKSRC_X1, true); + +#ifdef CONFIG_FB_VIA_X_COMPATIBILITY + clock.set_primary_pll_state(VIA_STATE_ON); + clock.set_primary_clock_state(VIA_STATE_ON); + clock.set_secondary_pll_state(VIA_STATE_ON); + clock.set_secondary_clock_state(VIA_STATE_ON); +#else + if (viaparinfo->shared->iga1_devices) { + clock.set_primary_pll_state(VIA_STATE_ON); + clock.set_primary_clock_state(VIA_STATE_ON); + } else { + clock.set_primary_pll_state(VIA_STATE_OFF); + clock.set_primary_clock_state(VIA_STATE_OFF); + } + + if (viaparinfo->shared->iga2_devices) { + clock.set_secondary_pll_state(VIA_STATE_ON); + clock.set_secondary_clock_state(VIA_STATE_ON); + } else { + clock.set_secondary_pll_state(VIA_STATE_OFF); + clock.set_secondary_clock_state(VIA_STATE_OFF); + } +#endif /*CONFIG_FB_VIA_X_COMPATIBILITY*/ + via_set_state(devices, VIA_STATE_ON); device_screen_on(); return 1; @@ -2598,8 +2384,12 @@ int viafb_get_refresh(int hres, int vres, u32 long_refresh) best = &vmode->crtc[i]; } - if (abs(best->refresh_rate - long_refresh) > 3) - return 60; + if (abs(best->refresh_rate - long_refresh) > 3) { + if (hres == 1200 && vres == 900) + return 49; /* OLPC DCON only supports 50 Hz */ + else + return 60; + } return best->refresh_rate; } diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index 8858593405aa..c7239eb83bae 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -732,20 +732,13 @@ struct _lcd_scaling_factor { struct _lcd_ver_scaling_factor lcd_ver_scaling_factor; }; -struct pll_config { - u16 multiplier; +struct pll_limit { + u16 multiplier_min; + u16 multiplier_max; u8 divisor; u8 rshift; }; -struct pll_map { - u32 clk; - struct pll_config cle266_pll; - struct pll_config k800_pll; - struct pll_config cx700_pll; - struct pll_config vx855_pll; -}; - struct rgbLUT { u8 red; u8 green; @@ -910,7 +903,6 @@ struct via_device_mapping { const char *name; }; -extern unsigned int viafb_second_virtual_xres; extern int viafb_SAMM_ON; extern int viafb_dual_fb; extern int viafb_LCD2_ON; @@ -936,7 +928,6 @@ void viafb_lock_crt(void); void viafb_unlock_crt(void); void viafb_load_fetch_count_reg(int h_addr, int bpp_byte, int set_iga); void viafb_write_regx(struct io_reg RegTable[], int ItemNum); -u32 viafb_get_clk_value(int clk); void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active); void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ *p_gfx_dpa_setting); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 64bc7e763103..6e06981d638b 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -48,7 +48,6 @@ static struct _lcd_scaling_factor lcd_scaling_factor_CLE = { {LCD_VER_SCALING_FACTOR_REG_NUM_CLE, {{CR78, 0, 7}, {CR79, 6, 7} } } }; -static int check_lvds_chip(int device_id_subaddr, int device_id); static bool lvds_identify_integratedlvds(void); static void __devinit fp_id_to_vindex(int panel_id); static int lvds_register_read(int index); @@ -84,12 +83,9 @@ static struct display_timing lcd_centering_timging(struct display_timing mode_crt_reg, struct display_timing panel_crt_reg); -static int check_lvds_chip(int device_id_subaddr, int device_id) +static inline bool check_lvds_chip(int device_id_subaddr, int device_id) { - if (lvds_register_read(device_id_subaddr) == device_id) - return OK; - else - return FAIL; + return lvds_register_read(device_id_subaddr) == device_id; } void __devinit viafb_init_lcd_size(void) @@ -150,7 +146,7 @@ static bool lvds_identify_integratedlvds(void) return true; } -int __devinit viafb_lvds_trasmitter_identify(void) +bool __devinit viafb_lvds_trasmitter_identify(void) { if (viafb_lvds_identify_vt1636(VIA_PORT_31)) { viaparinfo->chip_info->lvds_chip_info.i2c_port = VIA_PORT_31; @@ -175,20 +171,20 @@ int __devinit viafb_lvds_trasmitter_identify(void) viaparinfo->chip_info->lvds_chip_info.lvds_chip_slave_addr = VT1631_LVDS_I2C_ADDR; - if (check_lvds_chip(VT1631_DEVICE_ID_REG, VT1631_DEVICE_ID) != FAIL) { + if (check_lvds_chip(VT1631_DEVICE_ID_REG, VT1631_DEVICE_ID)) { DEBUG_MSG(KERN_INFO "\n VT1631 LVDS ! \n"); DEBUG_MSG(KERN_INFO "\n %2d", viaparinfo->chip_info->lvds_chip_info.lvds_chip_name); DEBUG_MSG(KERN_INFO "\n %2d", viaparinfo->chip_info->lvds_chip_info.lvds_chip_name); - return OK; + return true; } viaparinfo->chip_info->lvds_chip_info.lvds_chip_name = NON_LVDS_TRANSMITTER; viaparinfo->chip_info->lvds_chip_info.lvds_chip_slave_addr = VT1631_LVDS_I2C_ADDR; - return FAIL; + return false; } static void __devinit fp_id_to_vindex(int panel_id) @@ -562,7 +558,7 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, int set_vres = plvds_setting_info->v_active; int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; - u32 pll_D_N, clock; + u32 clock; struct display_timing mode_crt_reg, panel_crt_reg; struct crt_mode_table *panel_crt_table = NULL; struct VideoModeTable *vmode_tbl = viafb_get_mode(panel_hres, @@ -613,10 +609,7 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, viafb_load_FIFO_reg(set_iga, set_hres, set_vres); fill_lcd_format(); - - pll_D_N = viafb_get_clk_value(clock); - DEBUG_MSG(KERN_INFO "PLL=0x%x", pll_D_N); - viafb_set_vclock(pll_D_N, set_iga); + viafb_set_vclock(clock, set_iga); lcd_patch_skew(plvds_setting_info, plvds_chip_info); /* If K8M800, enable LCD Prefetch Mode. */ diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index c7909fe29550..75f60a655b0e 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -79,7 +79,7 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); -int __devinit viafb_lvds_trasmitter_identify(void); +bool __devinit viafb_lvds_trasmitter_identify(void); void viafb_init_lvds_output_interface(struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 4b7831f0d012..61b0bd596b85 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -22,14 +22,6 @@ #ifndef __SHARE_H__ #define __SHARE_H__ -/* Define Return Value */ -#define FAIL -1 -#define OK 1 - -#ifndef NULL -#define NULL 0 -#endif - /* Define Bit Field */ #define BIT0 0x01 #define BIT1 0x02 @@ -290,6 +282,7 @@ #define HW_LAYOUT_LCD_EXTERNAL_LCD2 0x10 /* Definition Refresh Rate */ +#define REFRESH_49 49 #define REFRESH_50 50 #define REFRESH_60 60 #define REFRESH_75 75 @@ -575,10 +568,6 @@ #define M1280X720_R50_HSP NEGATIVE #define M1280X720_R50_VSP POSITIVE -/* 1280x720@60 Sync Polarity (CEA Mode) */ -#define M1280X720_CEA_R60_HSP POSITIVE -#define M1280X720_CEA_R60_VSP POSITIVE - /* 1440x900@60 Sync Polarity (CVT Mode) */ #define M1440X900_R60_HSP NEGATIVE #define M1440X900_R60_VSP POSITIVE @@ -619,10 +608,6 @@ #define M1920X1200_RB_R60_HSP POSITIVE #define M1920X1200_RB_R60_VSP NEGATIVE -/* 1920x1080@60 Sync Polarity (CEA Mode) */ -#define M1920X1080_CEA_R60_HSP POSITIVE -#define M1920X1080_CEA_R60_VSP POSITIVE - /* 2048x1536@60 Sync Polarity (CVT Mode) */ #define M2048x1536_R60_HSP NEGATIVE #define M2048x1536_R60_VSP POSITIVE diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index 6723d6910cde..eb112b621735 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -505,7 +505,14 @@ static int __devinit via_pci_setup_mmio(struct viafb_dev *vdev) ret = vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type); if (ret < 0) goto out_unmap; - vdev->fbmem = ioremap_nocache(vdev->fbmem_start, vdev->fbmem_len); + + /* try to map less memory on failure, 8 MB should be still enough */ + for (; vdev->fbmem_len >= 8 << 20; vdev->fbmem_len /= 2) { + vdev->fbmem = ioremap_wc(vdev->fbmem_start, vdev->fbmem_len); + if (vdev->fbmem) + break; + } + if (vdev->fbmem == NULL) { ret = -ENOMEM; goto out_unmap; diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c index c2a0a1cfd3b3..ab5341814c74 100644 --- a/drivers/video/via/via-gpio.c +++ b/drivers/video/via/via-gpio.c @@ -145,7 +145,7 @@ static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) } -static struct viafb_gpio_cfg gpio_config = { +static struct viafb_gpio_cfg viafb_gpio_config = { .gpio_chip = { .label = "VIAFB onboard GPIO", .owner = THIS_MODULE, @@ -183,8 +183,8 @@ 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]); + for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) + viafb_gpio_enable(viafb_gpio_config.active_gpios[i]); return 0; } @@ -201,9 +201,9 @@ int viafb_gpio_lookup(const char *name) { int i; - for (i = 0; i < gpio_config.gpio_chip.ngpio; i++) - if (!strcmp(name, gpio_config.active_gpios[i]->vg_name)) - return gpio_config.gpio_chip.base + i; + for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i++) + if (!strcmp(name, viafb_gpio_config.active_gpios[i]->vg_name)) + return viafb_gpio_config.gpio_chip.base + i; return -1; } EXPORT_SYMBOL_GPL(viafb_gpio_lookup); @@ -229,14 +229,15 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev) for (gpio = viafb_all_gpios; gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) if (gpio->vg_port_index == port_cfg[i].ioport_index) { - gpio_config.active_gpios[ngpio] = gpio; - gpio_config.gpio_names[ngpio] = gpio->vg_name; + viafb_gpio_config.active_gpios[ngpio] = gpio; + viafb_gpio_config.gpio_names[ngpio] = + gpio->vg_name; ngpio++; } } - gpio_config.gpio_chip.ngpio = ngpio; - gpio_config.gpio_chip.names = gpio_config.gpio_names; - gpio_config.vdev = vdev; + viafb_gpio_config.gpio_chip.ngpio = ngpio; + viafb_gpio_config.gpio_chip.names = viafb_gpio_config.gpio_names; + viafb_gpio_config.vdev = vdev; if (ngpio == 0) { printk(KERN_INFO "viafb: no GPIOs configured\n"); return 0; @@ -245,18 +246,18 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev) * Enable the ports. They come in pairs, with a single * enable bit for both. */ - spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); + spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); for (i = 0; i < ngpio; i += 2) - viafb_gpio_enable(gpio_config.active_gpios[i]); - spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); + viafb_gpio_enable(viafb_gpio_config.active_gpios[i]); + spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags); /* * Get registered. */ - gpio_config.gpio_chip.base = -1; /* Dynamic */ - ret = gpiochip_add(&gpio_config.gpio_chip); + viafb_gpio_config.gpio_chip.base = -1; /* Dynamic */ + ret = gpiochip_add(&viafb_gpio_config.gpio_chip); if (ret) { printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); - gpio_config.gpio_chip.ngpio = 0; + viafb_gpio_config.gpio_chip.ngpio = 0; } #ifdef CONFIG_PM viafb_pm_register(&viafb_gpio_pm_hooks); @@ -277,8 +278,8 @@ static int viafb_gpio_remove(struct platform_device *platdev) /* * Get unregistered. */ - if (gpio_config.gpio_chip.ngpio > 0) { - ret = gpiochip_remove(&gpio_config.gpio_chip); + if (viafb_gpio_config.gpio_chip.ngpio > 0) { + ret = gpiochip_remove(&viafb_gpio_config.gpio_chip); if (ret) { /* Somebody still using it? */ printk(KERN_ERR "Viafb: GPIO remove failed\n"); return ret; @@ -287,11 +288,11 @@ static int viafb_gpio_remove(struct platform_device *platdev) /* * Disable the ports. */ - spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); - for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) - viafb_gpio_disable(gpio_config.active_gpios[i]); - gpio_config.gpio_chip.ngpio = 0; - spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); + spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); + for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) + viafb_gpio_disable(viafb_gpio_config.active_gpios[i]); + viafb_gpio_config.gpio_chip.ngpio = 0; + spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags); return ret; } diff --git a/drivers/video/via/via_clock.c b/drivers/video/via/via_clock.c new file mode 100644 index 000000000000..af8f26b643c1 --- /dev/null +++ b/drivers/video/via/via_clock.c @@ -0,0 +1,349 @@ +/* + * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * 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, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * clock and PLL management functions + */ + +#include <linux/kernel.h> +#include <linux/via-core.h> +#include "via_clock.h" +#include "global.h" +#include "debug.h" + +const char *via_slap = "Please slap VIA Technologies to motivate them " + "releasing full documentation for your platform!\n"; + +static inline u32 cle266_encode_pll(struct via_pll_config pll) +{ + return (pll.multiplier << 8) + | (pll.rshift << 6) + | pll.divisor; +} + +static inline u32 k800_encode_pll(struct via_pll_config pll) +{ + return ((pll.divisor - 2) << 16) + | (pll.rshift << 10) + | (pll.multiplier - 2); +} + +static inline u32 vx855_encode_pll(struct via_pll_config pll) +{ + return (pll.divisor << 16) + | (pll.rshift << 10) + | pll.multiplier; +} + +static inline void cle266_set_primary_pll_encoded(u32 data) +{ + via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ + via_write_reg(VIASR, 0x46, data & 0xFF); + via_write_reg(VIASR, 0x47, (data >> 8) & 0xFF); + via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ +} + +static inline void k800_set_primary_pll_encoded(u32 data) +{ + via_write_reg_mask(VIASR, 0x40, 0x02, 0x02); /* enable reset */ + via_write_reg(VIASR, 0x44, data & 0xFF); + via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); + via_write_reg(VIASR, 0x46, (data >> 16) & 0xFF); + via_write_reg_mask(VIASR, 0x40, 0x00, 0x02); /* disable reset */ +} + +static inline void cle266_set_secondary_pll_encoded(u32 data) +{ + via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ + via_write_reg(VIASR, 0x44, data & 0xFF); + via_write_reg(VIASR, 0x45, (data >> 8) & 0xFF); + via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ +} + +static inline void k800_set_secondary_pll_encoded(u32 data) +{ + via_write_reg_mask(VIASR, 0x40, 0x04, 0x04); /* enable reset */ + via_write_reg(VIASR, 0x4A, data & 0xFF); + via_write_reg(VIASR, 0x4B, (data >> 8) & 0xFF); + via_write_reg(VIASR, 0x4C, (data >> 16) & 0xFF); + via_write_reg_mask(VIASR, 0x40, 0x00, 0x04); /* disable reset */ +} + +static inline void set_engine_pll_encoded(u32 data) +{ + via_write_reg_mask(VIASR, 0x40, 0x01, 0x01); /* enable reset */ + via_write_reg(VIASR, 0x47, data & 0xFF); + via_write_reg(VIASR, 0x48, (data >> 8) & 0xFF); + via_write_reg(VIASR, 0x49, (data >> 16) & 0xFF); + via_write_reg_mask(VIASR, 0x40, 0x00, 0x01); /* disable reset */ +} + +static void cle266_set_primary_pll(struct via_pll_config config) +{ + cle266_set_primary_pll_encoded(cle266_encode_pll(config)); +} + +static void k800_set_primary_pll(struct via_pll_config config) +{ + k800_set_primary_pll_encoded(k800_encode_pll(config)); +} + +static void vx855_set_primary_pll(struct via_pll_config config) +{ + k800_set_primary_pll_encoded(vx855_encode_pll(config)); +} + +static void cle266_set_secondary_pll(struct via_pll_config config) +{ + cle266_set_secondary_pll_encoded(cle266_encode_pll(config)); +} + +static void k800_set_secondary_pll(struct via_pll_config config) +{ + k800_set_secondary_pll_encoded(k800_encode_pll(config)); +} + +static void vx855_set_secondary_pll(struct via_pll_config config) +{ + k800_set_secondary_pll_encoded(vx855_encode_pll(config)); +} + +static void k800_set_engine_pll(struct via_pll_config config) +{ + set_engine_pll_encoded(k800_encode_pll(config)); +} + +static void vx855_set_engine_pll(struct via_pll_config config) +{ + set_engine_pll_encoded(vx855_encode_pll(config)); +} + +static void set_primary_pll_state(u8 state) +{ + u8 value; + + switch (state) { + case VIA_STATE_ON: + value = 0x20; + break; + case VIA_STATE_OFF: + value = 0x00; + break; + default: + return; + } + + via_write_reg_mask(VIASR, 0x2D, value, 0x30); +} + +static void set_secondary_pll_state(u8 state) +{ + u8 value; + + switch (state) { + case VIA_STATE_ON: + value = 0x08; + break; + case VIA_STATE_OFF: + value = 0x00; + break; + default: + return; + } + + via_write_reg_mask(VIASR, 0x2D, value, 0x0C); +} + +static void set_engine_pll_state(u8 state) +{ + u8 value; + + switch (state) { + case VIA_STATE_ON: + value = 0x02; + break; + case VIA_STATE_OFF: + value = 0x00; + break; + default: + return; + } + + via_write_reg_mask(VIASR, 0x2D, value, 0x03); +} + +static void set_primary_clock_state(u8 state) +{ + u8 value; + + switch (state) { + case VIA_STATE_ON: + value = 0x20; + break; + case VIA_STATE_OFF: + value = 0x00; + break; + default: + return; + } + + via_write_reg_mask(VIASR, 0x1B, value, 0x30); +} + +static void set_secondary_clock_state(u8 state) +{ + u8 value; + + switch (state) { + case VIA_STATE_ON: + value = 0x80; + break; + case VIA_STATE_OFF: + value = 0x00; + break; + default: + return; + } + + via_write_reg_mask(VIASR, 0x1B, value, 0xC0); +} + +static inline u8 set_clock_source_common(enum via_clksrc source, bool use_pll) +{ + u8 data = 0; + + switch (source) { + case VIA_CLKSRC_X1: + data = 0x00; + break; + case VIA_CLKSRC_TVX1: + data = 0x02; + break; + case VIA_CLKSRC_TVPLL: + data = 0x04; /* 0x06 should be the same */ + break; + case VIA_CLKSRC_DVP1TVCLKR: + data = 0x0A; + break; + case VIA_CLKSRC_CAP0: + data = 0xC; + break; + case VIA_CLKSRC_CAP1: + data = 0x0E; + break; + } + + if (!use_pll) + data |= 1; + + return data; +} + +static void set_primary_clock_source(enum via_clksrc source, bool use_pll) +{ + u8 data = set_clock_source_common(source, use_pll) << 4; + via_write_reg_mask(VIACR, 0x6C, data, 0xF0); +} + +static void set_secondary_clock_source(enum via_clksrc source, bool use_pll) +{ + u8 data = set_clock_source_common(source, use_pll); + via_write_reg_mask(VIACR, 0x6C, data, 0x0F); +} + +static void dummy_set_clock_state(u8 state) +{ + printk(KERN_INFO "Using undocumented set clock state.\n%s", via_slap); +} + +static void dummy_set_clock_source(enum via_clksrc source, bool use_pll) +{ + printk(KERN_INFO "Using undocumented set clock source.\n%s", via_slap); +} + +static void dummy_set_pll_state(u8 state) +{ + printk(KERN_INFO "Using undocumented set PLL state.\n%s", via_slap); +} + +static void dummy_set_pll(struct via_pll_config config) +{ + printk(KERN_INFO "Using undocumented set PLL.\n%s", via_slap); +} + +void via_clock_init(struct via_clock *clock, int gfx_chip) +{ + switch (gfx_chip) { + case UNICHROME_CLE266: + case UNICHROME_K400: + clock->set_primary_clock_state = dummy_set_clock_state; + clock->set_primary_clock_source = dummy_set_clock_source; + clock->set_primary_pll_state = dummy_set_pll_state; + clock->set_primary_pll = cle266_set_primary_pll; + + clock->set_secondary_clock_state = dummy_set_clock_state; + clock->set_secondary_clock_source = dummy_set_clock_source; + clock->set_secondary_pll_state = dummy_set_pll_state; + clock->set_secondary_pll = cle266_set_secondary_pll; + + clock->set_engine_pll_state = dummy_set_pll_state; + clock->set_engine_pll = dummy_set_pll; + break; + case UNICHROME_K800: + case UNICHROME_PM800: + case UNICHROME_CN700: + case UNICHROME_CX700: + case UNICHROME_CN750: + case UNICHROME_K8M890: + case UNICHROME_P4M890: + case UNICHROME_P4M900: + case UNICHROME_VX800: + clock->set_primary_clock_state = set_primary_clock_state; + clock->set_primary_clock_source = set_primary_clock_source; + clock->set_primary_pll_state = set_primary_pll_state; + clock->set_primary_pll = k800_set_primary_pll; + + clock->set_secondary_clock_state = set_secondary_clock_state; + clock->set_secondary_clock_source = set_secondary_clock_source; + clock->set_secondary_pll_state = set_secondary_pll_state; + clock->set_secondary_pll = k800_set_secondary_pll; + + clock->set_engine_pll_state = set_engine_pll_state; + clock->set_engine_pll = k800_set_engine_pll; + break; + case UNICHROME_VX855: + case UNICHROME_VX900: + clock->set_primary_clock_state = set_primary_clock_state; + clock->set_primary_clock_source = set_primary_clock_source; + clock->set_primary_pll_state = set_primary_pll_state; + clock->set_primary_pll = vx855_set_primary_pll; + + clock->set_secondary_clock_state = set_secondary_clock_state; + clock->set_secondary_clock_source = set_secondary_clock_source; + clock->set_secondary_pll_state = set_secondary_pll_state; + clock->set_secondary_pll = vx855_set_secondary_pll; + + clock->set_engine_pll_state = set_engine_pll_state; + clock->set_engine_pll = vx855_set_engine_pll; + break; + + } +} diff --git a/drivers/video/via/via_clock.h b/drivers/video/via/via_clock.h new file mode 100644 index 000000000000..88714ae0d157 --- /dev/null +++ b/drivers/video/via/via_clock.h @@ -0,0 +1,76 @@ +/* + * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * 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, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * clock and PLL management functions + */ + +#ifndef __VIA_CLOCK_H__ +#define __VIA_CLOCK_H__ + +#include <linux/types.h> + +enum via_clksrc { + VIA_CLKSRC_X1 = 0, + VIA_CLKSRC_TVX1, + VIA_CLKSRC_TVPLL, + VIA_CLKSRC_DVP1TVCLKR, + VIA_CLKSRC_CAP0, + VIA_CLKSRC_CAP1, +}; + +struct via_pll_config { + u16 multiplier; + u8 divisor; + u8 rshift; +}; + +struct via_clock { + void (*set_primary_clock_state)(u8 state); + void (*set_primary_clock_source)(enum via_clksrc src, bool use_pll); + void (*set_primary_pll_state)(u8 state); + void (*set_primary_pll)(struct via_pll_config config); + + void (*set_secondary_clock_state)(u8 state); + void (*set_secondary_clock_source)(enum via_clksrc src, bool use_pll); + void (*set_secondary_pll_state)(u8 state); + void (*set_secondary_pll)(struct via_pll_config config); + + void (*set_engine_pll_state)(u8 state); + void (*set_engine_pll)(struct via_pll_config config); +}; + + +static inline u32 get_pll_internal_frequency(u32 ref_freq, + struct via_pll_config pll) +{ + return ref_freq / pll.divisor * pll.multiplier; +} + +static inline u32 get_pll_output_frequency(u32 ref_freq, + struct via_pll_config pll) +{ + return get_pll_internal_frequency(ref_freq, pll) >> pll.rshift; +} + +void via_clock_init(struct via_clock *clock, int gfx_chip); + +#endif /* __VIA_CLOCK_H__ */ diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index a542bed086e2..cf43c80d27f6 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/via-core.h> +#include <asm/olpc.h> #define _MASTER_FILE #include "global.h" @@ -37,6 +38,8 @@ static char *viafb_mode1; static int viafb_bpp = 32; static int viafb_bpp1 = 32; +static unsigned int viafb_second_xres = 640; +static unsigned int viafb_second_yres = 480; static unsigned int viafb_second_offset; static int viafb_second_size; @@ -440,8 +443,8 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) if (viafb_SAMM_ON == 1) { u.viamode.xres_sec = viafb_second_xres; u.viamode.yres_sec = viafb_second_yres; - u.viamode.virtual_xres_sec = viafb_second_virtual_xres; - u.viamode.virtual_yres_sec = viafb_second_virtual_yres; + u.viamode.virtual_xres_sec = viafb_dual_fb ? viafbinfo1->var.xres_virtual : viafbinfo->var.xres_virtual; + u.viamode.virtual_yres_sec = viafb_dual_fb ? viafbinfo1->var.yres_virtual : viafbinfo->var.yres_virtual; u.viamode.refresh_sec = viafb_refresh1; u.viamode.bpp_sec = viafb_bpp1; } else { @@ -930,10 +933,8 @@ static int get_primary_device(void) /* Rule: device on iga1 path are the primary device. */ if (viafb_SAMM_ON) { if (viafb_CRT_ON) { - if (viaparinfo->crt_setting_info->iga_path == IGA1) { - DEBUG_MSG(KERN_INFO "CRT IGA Path:%d\n", - viaparinfo-> - crt_setting_info->iga_path); + if (viaparinfo->shared->iga1_devices & VIA_CRT) { + DEBUG_MSG(KERN_INFO "CRT IGA Path:%d\n", IGA1); primary_device = CRT_Device; } } @@ -1011,8 +1012,13 @@ static int __init parse_active_dev(void) /* Note: The previous of active_dev is primary device, and the following is secondary device. */ if (!viafb_active_dev) { - viafb_CRT_ON = STATE_ON; - viafb_SAMM_ON = STATE_OFF; + if (machine_is_olpc()) { /* LCD only */ + viafb_LCD_ON = STATE_ON; + viafb_SAMM_ON = STATE_OFF; + } else { + viafb_CRT_ON = STATE_ON; + viafb_SAMM_ON = STATE_OFF; + } } else if (!strcmp(viafb_active_dev, "CRT+DVI")) { /* CRT+DVI */ viafb_CRT_ON = STATE_ON; @@ -1665,8 +1671,13 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) char *ptr; if (!str) { - *xres = 640; - *yres = 480; + if (machine_is_olpc()) { + *xres = 1200; + *yres = 900; + } else { + *xres = 640; + *yres = 480; + } return 0; } @@ -1746,7 +1757,6 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viaparinfo->lvds_setting_info = &viaparinfo->shared->lvds_setting_info; viaparinfo->lvds_setting_info2 = &viaparinfo->shared->lvds_setting_info2; - viaparinfo->crt_setting_info = &viaparinfo->shared->crt_setting_info; viaparinfo->chip_info = &viaparinfo->shared->chip_info; if (viafb_dual_fb) @@ -1793,14 +1803,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) parse_mode(viafb_mode, &default_xres, &default_yres); vmode_entry = viafb_get_mode(default_xres, default_yres); - if (viafb_SAMM_ON == 1) { + if (viafb_SAMM_ON == 1) parse_mode(viafb_mode1, &viafb_second_xres, &viafb_second_yres); - viafb_second_virtual_xres = viafb_second_xres; - viafb_second_virtual_yres = viafb_second_yres; - } - default_var.xres = default_xres; default_var.yres = default_yres; default_var.xres_virtual = default_xres; @@ -1844,8 +1850,8 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) default_var.xres = viafb_second_xres; default_var.yres = viafb_second_yres; - default_var.xres_virtual = viafb_second_virtual_xres; - default_var.yres_virtual = viafb_second_virtual_yres; + default_var.xres_virtual = viafb_second_xres; + default_var.yres_virtual = viafb_second_yres; default_var.bits_per_pixel = viafb_bpp1; viafb_fill_var_timing_info(&default_var, viafb_get_refresh( default_var.xres, default_var.yres, viafb_refresh1), @@ -1927,11 +1933,16 @@ void __devexit via_fb_pci_remove(struct pci_dev *pdev) } #ifndef MODULE -static int __init viafb_setup(char *options) +static int __init viafb_setup(void) { char *this_opt; + char *options; + DEBUG_MSG(KERN_INFO "viafb_setup!\n"); + if (fb_get_options("viafb", &options)) + return -ENODEV; + if (!options || !*options) return 0; @@ -2005,11 +2016,16 @@ static int __init viafb_setup(char *options) int __init viafb_init(void) { u32 dummy_x, dummy_y; + int r; + + if (machine_is_olpc()) + /* Apply XO-1.5-specific configuration. */ + viafb_lcd_panel_id = 23; + #ifndef MODULE - char *option = NULL; - if (fb_get_options("viafb", &option)) - return -ENODEV; - viafb_setup(option); + r = viafb_setup(); + if (r < 0) + return r; #endif if (parse_mode(viafb_mode, &dummy_x, &dummy_y) || !viafb_get_mode(dummy_x, dummy_y) diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 137996dc547e..d9440635d1d4 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -50,7 +50,6 @@ struct viafb_shared { /* All the information will be needed to set engine */ struct tmds_setting_information tmds_setting_info; - struct crt_setting_information crt_setting_info; struct lvds_setting_information lvds_setting_info; struct lvds_setting_information lvds_setting_info2; struct chip_information chip_info; @@ -79,14 +78,11 @@ struct viafb_par { /* All the information will be needed to set engine */ /* depreciated, use the ones in shared directly */ struct tmds_setting_information *tmds_setting_info; - struct crt_setting_information *crt_setting_info; struct lvds_setting_information *lvds_setting_info; struct lvds_setting_information *lvds_setting_info2; struct chip_information *chip_info; }; -extern unsigned int viafb_second_virtual_yres; -extern unsigned int viafb_second_virtual_xres; extern int viafb_SAMM_ON; extern int viafb_dual_fb; extern int viafb_LCD2_ON; diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index 8c5bc41ff6a4..58df74e1417e 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -30,10 +30,6 @@ struct io_reg CN400_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIASR, SR1A, 0xFB, 0x08}, {VIASR, SR1E, 0x0F, 0x01}, {VIASR, SR2A, 0xFF, 0x00}, -{VIACR, CR0A, 0xFF, 0x1E}, /* Cursor Start */ -{VIACR, CR0B, 0xFF, 0x00}, /* Cursor End */ -{VIACR, CR0E, 0xFF, 0x00}, /* Cursor Location High */ -{VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, @@ -41,7 +37,6 @@ struct io_reg CN400_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFF, 0x40}, {VIACR, CR6B, 0xFF, 0x00}, -{VIACR, CR6C, 0xFF, 0x00}, {VIACR, CR88, 0xFF, 0x40}, /* LCD Panel Type */ {VIACR, CR89, 0xFF, 0x00}, /* LCD Timing Control 0 */ {VIACR, CR8A, 0xFF, 0x88}, /* LCD Timing Control 1 */ @@ -87,7 +82,6 @@ struct io_reg CN700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFD, 0x40}, {VIACR, CR6B, 0xFF, 0x00}, -{VIACR, CR6C, 0xFF, 0x00}, {VIACR, CR77, 0xFF, 0x00}, /* LCD scaling Factor */ {VIACR, CR78, 0xFF, 0x00}, /* LCD scaling Factor */ {VIACR, CR79, 0xFF, 0x00}, /* LCD scaling Factor */ @@ -125,10 +119,6 @@ struct io_reg KM400_ModeXregs[] = { {VIASR, SR2A, 0xFF, 0x00}, /* Power Management Control 5 */ {VIASR, SR2D, 0xFF, 0xFF}, /* Power Management Control 1 */ {VIASR, SR2E, 0xFF, 0xFF}, /* Power Management Control 2 */ - {VIACR, CR0A, 0xFF, 0x1E}, /* Cursor Start */ - {VIACR, CR0B, 0xFF, 0x00}, /* Cursor End */ - {VIACR, CR0E, 0xFF, 0x00}, /* Cursor Location High */ - {VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ {VIACR, CR33, 0xFF, 0x00}, {VIACR, CR55, 0x80, 0x00}, {VIACR, CR5D, 0x80, 0x00}, @@ -161,11 +151,7 @@ struct io_reg CX700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIASR, SR1B, 0xFF, 0xF0}, {VIASR, SR1E, 0xFF, 0x01}, {VIASR, SR2A, 0xFF, 0x00}, -{VIASR, SR2D, 0xFF, 0xFF}, /* VCK and LCK PLL power on. */ -{VIACR, CR0A, 0xFF, 0x1E}, /* Cursor Start */ -{VIACR, CR0B, 0xFF, 0x00}, /* Cursor End */ -{VIACR, CR0E, 0xFF, 0x00}, /* Cursor Location High */ -{VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ +{VIASR, SR2D, 0xC0, 0xC0}, /* delayed E3_ECK */ {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, @@ -174,7 +160,6 @@ struct io_reg CX700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFF, 0x40}, {VIACR, CR6B, 0xFF, 0x00}, -{VIACR, CR6C, 0xFF, 0x00}, {VIACR, CR88, 0xFF, 0x40}, /* LCD Panel Type */ {VIACR, CR89, 0xFF, 0x00}, /* LCD Timing Control 0 */ {VIACR, CR8A, 0xFF, 0x88}, /* LCD Timing Control 1 */ @@ -204,14 +189,7 @@ struct io_reg VX855_ModeXregs[] = { {VIASR, SR2A, 0xF0, 0x00}, {VIASR, SR58, 0xFF, 0x00}, {VIASR, SR59, 0xFF, 0x00}, -{VIASR, SR2D, 0xFF, 0xFF}, /* VCK and LCK PLL power on. */ -{VIACR, CR09, 0xFF, 0x00}, /* Initial CR09=0*/ -{VIACR, CR11, 0x8F, 0x00}, /* IGA1 initial Vertical end */ -{VIACR, CR17, 0x7F, 0x00}, /* IGA1 CRT Mode control init */ -{VIACR, CR0A, 0xFF, 0x1E}, /* Cursor Start */ -{VIACR, CR0B, 0xFF, 0x00}, /* Cursor End */ -{VIACR, CR0E, 0xFF, 0x00}, /* Cursor Location High */ -{VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ +{VIASR, SR2D, 0xC0, 0xC0}, /* delayed E3_ECK */ {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0x7F, 0x00}, {VIACR, CR35, 0xFF, 0x00}, @@ -219,7 +197,6 @@ struct io_reg VX855_ModeXregs[] = { {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFD, 0x60}, {VIACR, CR6B, 0xFF, 0x00}, -{VIACR, CR6C, 0xFF, 0x00}, {VIACR, CR88, 0xFF, 0x40}, /* LCD Panel Type */ {VIACR, CR89, 0xFF, 0x00}, /* LCD Timing Control 0 */ {VIACR, CR8A, 0xFF, 0x88}, /* LCD Timing Control 1 */ @@ -606,7 +583,7 @@ static struct crt_mode_table CRTM1200x720[] = { /* 1200x900 (DCON) */ static struct crt_mode_table DCON1200x900[] = { /* r_rate, hsp, vsp */ - {REFRESH_60, M1200X900_R60_HSP, M1200X900_R60_VSP, + {REFRESH_49, M1200X900_R60_HSP, M1200X900_R60_VSP, /* The correct htotal is 1240, but this doesn't raster on VX855. */ /* Via suggested changing to a multiple of 16, hence 1264. */ /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ @@ -877,23 +854,6 @@ static struct VideoModeTable viafb_rb_modes[] = { {CRTM1920x1200_RB, ARRAY_SIZE(CRTM1920x1200_RB)} }; -struct crt_mode_table CEAM1280x720[] = { - {REFRESH_60, M1280X720_CEA_R60_HSP, M1280X720_CEA_R60_VSP, - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {1650, 1280, 1280, 370, 1390, 40, 750, 720, 720, 30, 725, 5} } -}; -struct crt_mode_table CEAM1920x1080[] = { - {REFRESH_60, M1920X1080_CEA_R60_HSP, M1920X1080_CEA_R60_VSP, - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {2200, 1920, 1920, 300, 2008, 44, 1125, 1080, 1080, 45, 1084, 5} } -}; -struct VideoModeTable CEA_HDMI_Modes[] = { - /* Display : 1280x720 */ - {CEAM1280x720, ARRAY_SIZE(CEAM1280x720)}, - {CEAM1920x1080, ARRAY_SIZE(CEAM1920x1080)} -}; - -int NUM_TOTAL_CEA_MODES = ARRAY_SIZE(CEA_HDMI_Modes); int NUM_TOTAL_CN400_ModeXregs = ARRAY_SIZE(CN400_ModeXregs); int NUM_TOTAL_CN700_ModeXregs = ARRAY_SIZE(CN700_ModeXregs); int NUM_TOTAL_KM400_ModeXregs = ARRAY_SIZE(KM400_ModeXregs); diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h index 8a67ea1b5ef0..3751289eb450 100644 --- a/drivers/video/via/viamode.h +++ b/drivers/video/via/viamode.h @@ -41,7 +41,6 @@ struct patch_table { struct io_reg *io_reg_table; }; -extern int NUM_TOTAL_CEA_MODES; extern int NUM_TOTAL_CN400_ModeXregs; extern int NUM_TOTAL_CN700_ModeXregs; extern int NUM_TOTAL_KM400_ModeXregs; @@ -50,14 +49,6 @@ extern int NUM_TOTAL_VX855_ModeXregs; extern int NUM_TOTAL_CLE266_ModeXregs; extern int NUM_TOTAL_PATCH_MODE; -/********************/ -/* Mode Table */ -/********************/ - -extern struct crt_mode_table CEAM1280x720[]; -extern struct crt_mode_table CEAM1920x1080[]; -extern struct VideoModeTable CEA_HDMI_Modes[]; - extern struct io_reg CN400_ModeXregs[]; extern struct io_reg CN700_ModeXregs[]; extern struct io_reg KM400_ModeXregs[]; diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index a20218c2fda8..beac52fc1c0e 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -395,10 +395,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, spin_lock_init(&info->dirty_lock); spin_lock_init(&info->resize_lock); - info->fb = vmalloc(fb_size); + info->fb = vzalloc(fb_size); if (info->fb == NULL) goto error_nomem; - memset(info->fb, 0, fb_size); info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; |