From 639f6571458948b5112be2cf00c0c2c04db2897d Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 27 Aug 2008 10:51:02 +0800 Subject: Blackfin arch: move include/asm-blackfin header files to arch/blackfin Signed-off-by: Bryan Wu --- drivers/video/bf54x-lq043fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 6d5aa806777e..7644ed249564 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -58,7 +58,7 @@ #include #include -#include +#include #define NO_BL_SUPPORT -- cgit v1.2.3 From 09a2910e54646f7a334702fbafa7a6129dc072e6 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 2 Sep 2008 14:35:51 -0700 Subject: cirrusfb: check_par fixes 1. Check if virtual resolution fits into memory. Otherwise, Linux hangs during panning. 2. When selected use all available memory to maximize yres_virtual to speed up panning (previously also xres_virtual was increased). 3. Simplify memory restriction calculations. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/cirrusfb.c | 59 +++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 41 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index c14b2435d23e..e729fb279645 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -628,27 +628,18 @@ static long cirrusfb_get_mclk(long freq, int bpp, long *div) static int cirrusfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - int nom, den; /* translyting from pixels->bytes */ - int yres, i; - static struct { int xres, yres; } modes[] = - { { 1600, 1280 }, - { 1280, 1024 }, - { 1024, 768 }, - { 800, 600 }, - { 640, 480 }, - { -1, -1 } }; + int yres; + /* memory size in pixels */ + unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; switch (var->bits_per_pixel) { case 1: - nom = 4; - den = 8; + pixels /= 4; break; /* 8 pixel per byte, only 1/4th of mem usable */ case 8: case 16: case 24: case 32: - nom = var->bits_per_pixel / 8; - den = 1; break; /* 1 pixel == 1 byte */ default: printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..." @@ -658,43 +649,29 @@ static int cirrusfb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } - if (var->xres * nom / den * var->yres > info->screen_size) { - printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..." - "resolution too high to fit into video memory!\n", - var->xres, var->yres, var->bits_per_pixel); - DPRINTK("EXIT - EINVAL error\n"); - return -EINVAL; - } - + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; /* use highest possible virtual resolution */ - if (var->xres_virtual == -1 && - var->yres_virtual == -1) { - printk(KERN_INFO - "cirrusfb: using maximum available virtual resolution\n"); - for (i = 0; modes[i].xres != -1; i++) { - int size = modes[i].xres * nom / den * modes[i].yres; - if (size < info->screen_size / 2) - break; - } - if (modes[i].xres == -1) { - printk(KERN_ERR "cirrusfb: could not find a virtual " - "resolution that fits into video memory!!\n"); - DPRINTK("EXIT - EINVAL error\n"); - return -EINVAL; - } - var->xres_virtual = modes[i].xres; - var->yres_virtual = modes[i].yres; + if (var->yres_virtual == -1) { + var->yres_virtual = pixels / var->xres_virtual; printk(KERN_INFO "cirrusfb: virtual resolution set to " "maximum of %dx%d\n", var->xres_virtual, var->yres_virtual); } - - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; + if (var->xres_virtual * var->yres_virtual > pixels) { + printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... " + "virtual resolution too high to fit into video memory!\n", + var->xres_virtual, var->yres_virtual, + var->bits_per_pixel); + DPRINTK("EXIT - EINVAL error\n"); + return -EINVAL; + } + + if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) -- cgit v1.2.3 From bf6910c0afb1e416a99ad5b2296e088449fbe481 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 2 Sep 2008 14:36:03 -0700 Subject: tdfxfb: fix SDRAM memory size detection Fix memory detection on Voodoo3 cards with SDRAM memory. Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tdfxfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 77aafcfae037..fa7cbecbe820 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -426,7 +426,7 @@ static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short dev_id) if (dev_id < PCI_DEVICE_ID_3DFX_VOODOO5) { /* Banshee/Voodoo3 */ chip_size = 2; - if (has_sgram && (draminit0 & DRAMINIT0_SGRAM_TYPE)) + if (has_sgram && !(draminit0 & DRAMINIT0_SGRAM_TYPE)) chip_size = 1; } else { /* Voodoo4/5 */ -- cgit v1.2.3 From b4a49b12e879ce79f495cc318c4b33caec6922e8 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 2 Sep 2008 14:36:04 -0700 Subject: tdfxfb: fix frame buffer name overrun If there are more then one graphics card handled by the tdfxfb driver the name of the frame buffer overruns reserved size. Signed-off-by: Krzysztof Helt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tdfxfb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index fa7cbecbe820..4599a4385bc9 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -95,7 +95,6 @@ static inline int mtrr_del(int reg, unsigned long base, #define VOODOO5_MAX_PIXCLOCK 350000 static struct fb_fix_screeninfo tdfx_fix __devinitdata = { - .id = "3Dfx", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, .ypanstep = 1, @@ -1200,15 +1199,15 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, /* Configure the default fb_fix_screeninfo first */ switch (pdev->device) { case PCI_DEVICE_ID_3DFX_BANSHEE: - strcat(tdfx_fix.id, " Banshee"); + strcpy(tdfx_fix.id, "3Dfx Banshee"); default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK; break; case PCI_DEVICE_ID_3DFX_VOODOO3: - strcat(tdfx_fix.id, " Voodoo3"); + strcpy(tdfx_fix.id, "3Dfx Voodoo3"); default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK; break; case PCI_DEVICE_ID_3DFX_VOODOO5: - strcat(tdfx_fix.id, " Voodoo5"); + strcpy(tdfx_fix.id, "3Dfx Voodoo5"); default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK; break; } -- cgit v1.2.3 From 34a35bddb9382fc2663e3137875ee58928f7d704 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 5 Sep 2008 14:00:22 -0700 Subject: atmel_lcdfb: fix oops in rmmod when framebuffer fails to register If framebuffer registration failed in platform driver ->probe() callback, dev_get_drvdata() points to freed memory region, but ->remove() function try to use it and the following oops occurs: Unable to handle kernel NULL pointer dereference at virtual address 00000228 pgd = c3a20000 [00000228] *pgd=23a2b031, *pte=00000000, *ppte=00000000 Internal error: Oops: 17 [#1] Modules linked in: atmel_lcdfb(-) cfbcopyarea cfbimgblt cfbfillrect [last unloaded: atmel_lcdfb] CPU: 0 Not tainted (2.6.27-rc2 #116) PC is at atmel_lcdfb_remove+0x14/0xf8 [atmel_lcdfb] LR is at platform_drv_remove+0x20/0x24 pc : [] lr : [] psr: a0000013 sp : c3a45e84 ip : c3a45ea0 fp : c3a45e9c r10: 00000002 r9 : c3a44000 r8 : c0026c04 r7 : 00000880 r6 : c02bb228 r5 : 00000000 r4 : c02bb230 r3 : bf007e3c r2 : c02bb230 r1 : 00000004 r0 : c02bb228 Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 0005317f Table: 23a20000 DAC: 00000015 Process rmmod (pid: 6799, stack limit = 0xc3a44260) Stack: (0xc3a45e84 to 0xc3a46000) 5e80: c02bb230 bf007e3c bf007e3c c3a45eac c3a45ea0 c0157d28 bf006bc0 5ea0: c3a45ec4 c3a45eb0 c0156d20 c0157d18 c02bb230 c02bb2d8 c3a45ee0 c3a45ec8 5ec0: c0156da8 c0156cb8 bf007e3c bf007ee0 c02c8e14 c3a45efc c3a45ee4 c0156018 5ee0: c0156d50 bf007e3c bf007ee0 00000000 c3a45f18 c3a45f00 c0157220 c0155f9c 5f00: 00000000 bf007ee0 bf008000 c3a45f28 c3a45f1c c0157e34 c01571ec c3a45f38 5f20: c3a45f2c bf006ba8 c0157e30 c3a45fa4 c3a45f3c c005772c bf006ba4 656d7461 5f40: 636c5f6c 00626664 c004c988 c3a45f80 c3a45f5c 00000000 c3a45fb0 00000000 5f60: ffffffff becaccd8 00000880 00000000 000a5e80 00000001 bf007ee0 00000880 5f80: c3a45f84 00000000 becaccd4 00000002 000003df 00000081 00000000 c3a45fa8 5fa0: c0026a60 c0057584 00000002 000003df 00900081 000a5e80 00000880 00000000 5fc0: becaccd4 00000002 000003df 00000000 000a5e80 00000001 00000002 0000005f 5fe0: 4004f5ec becacbe8 0001a158 4004f5fc 20000010 00900081 f9ffbadf 7bbfb2bb Backtrace: [] (atmel_lcdfb_remove+0x0/0xf8 [atmel_lcdfb]) from [] (platform_drv_remove+0x20/0x24) r6:bf007e3c r5:bf007e3c r4:c02bb230 [] (platform_drv_remove+0x0/0x24) from [] (__device_release_driver+0x78/0x98) [] (__device_release_driver+0x0/0x98) from [] (driver_detach+0x68/0x90) r5:c02bb2d8 r4:c02bb230 [] (driver_detach+0x0/0x90) from [] (bus_remove_driver+0x8c/0xb4) r6:c02c8e14 r5:bf007ee0 r4:bf007e3c [] (bus_remove_driver+0x0/0xb4) from [] (driver_unregister+0x44/0x48) r6:00000000 r5:bf007ee0 r4:bf007e3c [] (driver_unregister+0x0/0x48) from [] (platform_driver_unregister+0x14/0x18) r6:bf008000 r5:bf007ee0 r4:00000000 [] (platform_driver_unregister+0x0/0x18) from [] (atmel_lcdfb_exit+0x14/0x1c [atmel_lcdfb]) [] (atmel_lcdfb_exit+0x0/0x1c [atmel_lcdfb]) from [] (sys_delete_module+0x1b8/0x22c) [] (sys_delete_module+0x0/0x22c) from [] (ret_fast_syscall+0x0/0x2c) r7:00000081 r6:000003df r5:00000002 r4:becaccd4 Code: e92dd870 e24cb004 e59050c4 e1a06000 (e5954228) ---[ end trace 85476b184d9e68d8 ]--- This patch fixes the oops. Signed-off-by: Stanislaw Gruszka Acked-by: Nicolas Ferre Acked-by: Krzysztof Helt Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/atmel_lcdfb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 9c5925927ece..5a24c6411d34 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -939,7 +939,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) ret = register_framebuffer(info); if (ret < 0) { dev_err(dev, "failed to register framebuffer device: %d\n", ret); - goto free_cmap; + goto reset_drvdata; } /* add selected videomode to modelist */ @@ -955,7 +955,8 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) return 0; - +reset_drvdata: + dev_set_drvdata(dev, NULL); free_cmap: fb_dealloc_cmap(&info->cmap); unregister_irqs: @@ -992,10 +993,11 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fb_info *info = dev_get_drvdata(dev); - struct atmel_lcdfb_info *sinfo = info->par; + struct atmel_lcdfb_info *sinfo; - if (!sinfo) + if (!info || !info->par) return 0; + sinfo = info->par; cancel_work_sync(&sinfo->task); exit_backlight(sinfo); -- cgit v1.2.3 From faa312da9cd0b044bdc84483162c6ee10b9c83c0 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 04:18:43 +0800 Subject: lcd: allow lcd device to handle mode change events Some LCD panels are capable of different resolutions, and is allowed to change at run-time, so to make "struct lcd_device" to be able to handle mode change events here. Signed-off-by: Eric Miao Acked-by: Krzysztof Helt Signed-off-by: Russell King --- drivers/video/backlight/lcd.c | 18 +++++++++++++++--- drivers/video/fbmem.c | 1 + include/linux/lcd.h | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b15b2b84a6f7..8e1731d3b228 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -27,14 +27,26 @@ static int fb_notifier_callback(struct notifier_block *self, struct fb_event *evdata = data; /* If we aren't interested in this event, skip it immediately ... */ - if (event != FB_EVENT_BLANK) + switch (event) { + case FB_EVENT_BLANK: + case FB_EVENT_MODE_CHANGE: + case FB_EVENT_MODE_CHANGE_ALL: + break; + default: return 0; + } ld = container_of(self, struct lcd_device, fb_notif); + if (!ld->ops) + return 0; + mutex_lock(&ld->ops_lock); - if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { + if (event == FB_EVENT_BLANK) ld->ops->set_power(ld, *(int *)evdata->data); + else + ld->ops->set_mode(ld, evdata->data); + } mutex_unlock(&ld->ops_lock); return 0; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 98843c2ecf73..0737570030f5 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -979,6 +979,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; + event.data = &mode; fb_notifier_call_chain(evnt, &event); } } diff --git a/include/linux/lcd.h b/include/linux/lcd.h index 173febac6656..c67fecafff90 100644 --- a/include/linux/lcd.h +++ b/include/linux/lcd.h @@ -11,6 +11,7 @@ #include #include #include +#include /* Notes on locking: * @@ -45,6 +46,8 @@ struct lcd_ops { int (*get_contrast)(struct lcd_device *); /* Set LCD panel contrast */ int (*set_contrast)(struct lcd_device *, int contrast); + /* Set LCD panel mode (resolutions ...) */ + int (*set_mode)(struct lcd_device *, struct fb_videomode *); /* Check if given framebuffer device is the one LCD is bound to; return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */ int (*check_fb)(struct lcd_device *, struct fb_info *); -- cgit v1.2.3 From b18250a8f66050bd2a52287cd543fb93100e8ee0 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 04:21:44 +0800 Subject: lcd: add SPI-based LCD and backlight driver for SHARP corgi/spitz The driver is based on different source files including corgi_ssp.c, corgi_lcd.c and corgi_bl.c, previously authored by Richard Purdie and many others. The LCD and Backlight device actually share the same SPI device, so they are made into this single driver. Signed-off-by: Eric Miao Cc: Richard Purdie Signed-off-by: Russell King --- drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/corgi_lcd.c | 541 ++++++++++++++++++++++++++++++++++++ include/linux/spi/corgi_lcd.h | 16 ++ 4 files changed, 565 insertions(+) create mode 100644 drivers/video/backlight/corgi_lcd.c create mode 100644 include/linux/spi/corgi_lcd.h (limited to 'drivers/video') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 452b770d8cc9..f5406cd78e28 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -24,6 +24,13 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config LCD_CORGI + tristate "LCD Panel support for SHARP corgi/spitz model" + depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL + help + Say y here to support the LCD panels usually found on SHARP + corgi (C7x0) and spitz (Cxx00) models. + config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" depends on LCD_CLASS_DEVICE && SPI_MASTER diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b405aace803f..cf12d58392e0 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,6 +1,7 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c new file mode 100644 index 000000000000..bf69e50d262e --- /dev/null +++ b/drivers/video/backlight/corgi_lcd.c @@ -0,0 +1,541 @@ +/* + * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models) + * + * Copyright (c) 2004-2006 Richard Purdie + * + * Based on Sharp's 2.4 Backlight Driver + * + * Copyright (c) 2008 Marvell International Ltd. + * Converted to SPI device based LCD/Backlight device driver + * by Eric Miao + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +/* Register Addresses */ +#define RESCTL_ADRS 0x00 +#define PHACTRL_ADRS 0x01 +#define DUTYCTRL_ADRS 0x02 +#define POWERREG0_ADRS 0x03 +#define POWERREG1_ADRS 0x04 +#define GPOR3_ADRS 0x05 +#define PICTRL_ADRS 0x06 +#define POLCTRL_ADRS 0x07 + +/* Register Bit Definitions */ +#define RESCTL_QVGA 0x01 +#define RESCTL_VGA 0x00 + +#define POWER1_VW_ON 0x01 /* VW Supply FET ON */ +#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */ +#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */ + +#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */ +#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */ +#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */ + +#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */ +#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */ +#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */ +#define POWER0_COM_ON 0x08 /* COM Power Supply ON */ +#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */ + +#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */ +#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */ +#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */ + +#define PICTRL_INIT_STATE 0x01 +#define PICTRL_INIOFF 0x02 +#define PICTRL_POWER_DOWN 0x04 +#define PICTRL_COM_SIGNAL_OFF 0x08 +#define PICTRL_DAC_SIGNAL_OFF 0x10 + +#define POLCTRL_SYNC_POL_FALL 0x01 +#define POLCTRL_EN_POL_FALL 0x02 +#define POLCTRL_DATA_POL_FALL 0x04 +#define POLCTRL_SYNC_ACT_H 0x08 +#define POLCTRL_EN_ACT_L 0x10 + +#define POLCTRL_SYNC_POL_RISE 0x00 +#define POLCTRL_EN_POL_RISE 0x00 +#define POLCTRL_DATA_POL_RISE 0x00 +#define POLCTRL_SYNC_ACT_L 0x00 +#define POLCTRL_EN_ACT_H 0x00 + +#define PHACTRL_PHASE_MANUAL 0x01 +#define DEFAULT_PHAD_QVGA (9) +#define DEFAULT_COMADJ (125) + +struct corgi_lcd { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + struct backlight_device *bl_dev; + + int intensity; + int power; + int mode; + char buf[2]; + + void (*notify)(int intensity); + void (*kick_battery)(void); +}; + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); + +/* + * This is only a psuedo I2C interface. We can't use the standard kernel + * routines as the interface is write only. We just assume the data is acked... + */ +static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data) +{ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data); + udelay(10); +} + +static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data) +{ + lcdtg_ssp_i2c_send(lcd, data); + lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, data); +} + +static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base); +} + +static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); +} + +static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd, + uint8_t base, uint8_t data) +{ + int i; + for (i = 0; i < 8; i++) { + if (data & 0x80) + lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT); + else + lcdtg_i2c_send_bit(lcd, base); + data <<= 1; + } +} + +static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_i2c_send_bit(lcd, base); +} + +static void lcdtg_set_common_voltage(struct corgi_lcd *lcd, + uint8_t base_data, uint8_t data) +{ + /* Set Common Voltage to M62332FP via I2C */ + lcdtg_i2c_send_start(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x9c); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x00); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, data); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_stop(lcd, base_data); +} + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) +{ + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .cs_change = 1, + .tx_buf = lcd->buf, + }; + + lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi_dev, &msg); +} + +/* Set Phase Adjust */ +static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode) +{ + int adj; + + switch(mode) { + case CORGI_LCD_MODE_VGA: + /* Setting for VGA */ + adj = sharpsl_param.phadadj; + adj = (adj < 0) ? PHACTRL_PHASE_MANUAL : + PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1); + break; + case CORGI_LCD_MODE_QVGA: + default: + /* Setting for QVGA */ + adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL; + break; + } + + corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj); +} + +static void corgi_lcd_power_on(struct corgi_lcd *lcd) +{ + int comadj; + + /* Initialize Internal Logic & Port */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_POWER_DOWN | PICTRL_INIOFF | + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF | + PICTRL_DAC_SIGNAL_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); + + /* VDD(+8V), SVSS(-4V) ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + mdelay(3); + + /* DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* INIB = H, INI = L */ + /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF); + + /* Set Common Voltage */ + comadj = sharpsl_param.comadj; + if (comadj < 0) + comadj = DEFAULT_COMADJ; + + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_OFF, comadj); + + /* VCC5 ON, DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_ON); + + /* GVSS(-8V) ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + mdelay(2); + + /* COM SIGNAL ON (PICTL[3] = L) */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE); + + /* COM ON, DAC ON, VCC5_ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_ON | POWER0_VCC5_ON); + + /* VW ON, GVSS ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* Signals output enable */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0); + + /* Set Phase Adjust */ + lcdtg_set_phadadj(lcd, lcd->mode); + + /* Initialize for Input Signals from ATI */ + corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS, + POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | + POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L | + POLCTRL_EN_ACT_H); + udelay(1000); + + switch (lcd->mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } +} + +static void corgi_lcd_power_off(struct corgi_lcd *lcd) +{ + /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */ + msleep(34); + + /* (1)VW OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* (2)COM OFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF); + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON); + + /* (3)Set Common Voltage Bias 0V */ + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_ON, 0); + + /* (4)GVSS OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + + /* (5)VCC5 OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (6)Set PDWN, INIOFF, DACOFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF | + PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF); + + /* (7)DAC OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (8)VDD OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); +} + +static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + int mode = CORGI_LCD_MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = CORGI_LCD_MODE_VGA; + + if (lcd->mode == mode) + return 0; + + lcdtg_set_phadadj(lcd, mode); + + switch (mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } + + lcd->mode = mode; + return 0; +} + +static int corgi_lcd_set_power(struct lcd_device *ld, int power) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + corgi_lcd_power_on(lcd); + + if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + corgi_lcd_power_off(lcd); + + lcd->power = power; + return 0; +} + +static int corgi_lcd_get_power(struct lcd_device *ld) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + return lcd->power; +} + +static struct lcd_ops corgi_lcd_ops = { + .get_power = corgi_lcd_get_power, + .set_power = corgi_lcd_set_power, + .set_mode = corgi_lcd_set_mode, +}; + +static int corgi_bl_get_intensity(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + + return lcd->intensity; +} + +static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) +{ + if (intensity > 0x10) + intensity += 0x10; + + corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); + lcd->intensity = intensity; + + if (lcd->notify) + lcd->notify(intensity); + + if (lcd->kick_battery) + lcd->kick_battery(); + + return 0; +} + +static int corgi_bl_update_status(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + return corgi_bl_set_intensity(lcd, intensity); +} + +static struct backlight_ops corgi_bl_ops = { + .get_brightness = corgi_bl_get_intensity, + .update_status = corgi_bl_update_status, +}; + +#ifdef CONFIG_PM +static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgi_bl_set_intensity(lcd, 0); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + return 0; +} + +static int corgi_lcd_resume(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; +} +#else +#define corgi_lcd_suspend NULL +#define corgi_lcd_resume NULL +#endif + +static int __devinit corgi_lcd_probe(struct spi_device *spi) +{ + struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; + struct corgi_lcd *lcd; + int ret = 0; + + if (pdata == NULL) { + dev_err(&spi->dev, "platform data not available\n"); + return -EINVAL; + } + + lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + lcd->spi_dev = spi; + + lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev, + lcd, &corgi_lcd_ops); + if (IS_ERR(lcd->lcd_dev)) { + ret = PTR_ERR(lcd->lcd_dev); + goto err_free_lcd; + } + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA; + + lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev, + lcd, &corgi_bl_ops); + if (IS_ERR(lcd->bl_dev)) { + ret = PTR_ERR(lcd->bl_dev); + goto err_unregister_lcd; + } + lcd->bl_dev->props.max_brightness = pdata->max_intensity; + lcd->bl_dev->props.brightness = pdata->default_intensity; + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + + lcd->notify = pdata->notify; + lcd->kick_battery = pdata->kick_battery; + + dev_set_drvdata(&spi->dev, lcd); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; + +err_unregister_lcd: + lcd_device_unregister(lcd->lcd_dev); +err_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit corgi_lcd_remove(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + lcd->bl_dev->props.brightness = 0; + backlight_update_status(lcd->bl_dev); + backlight_device_unregister(lcd->bl_dev); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd); + + return 0; +} + +static struct spi_driver corgi_lcd_driver = { + .driver = { + .name = "corgi-lcd", + .owner = THIS_MODULE, + }, + .probe = corgi_lcd_probe, + .remove = __devexit_p(corgi_lcd_remove), + .suspend = corgi_lcd_suspend, + .resume = corgi_lcd_resume, +}; + +static int __init corgi_lcd_init(void) +{ + return spi_register_driver(&corgi_lcd_driver); +} +module_init(corgi_lcd_init); + +static void __exit corgi_lcd_exit(void) +{ + spi_unregister_driver(&corgi_lcd_driver); +} +module_exit(corgi_lcd_exit); + +MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); +MODULE_AUTHOR("Eric Miao "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h new file mode 100644 index 000000000000..3c53ac26c8d1 --- /dev/null +++ b/include/linux/spi/corgi_lcd.h @@ -0,0 +1,16 @@ +#ifndef __LINUX_SPI_CORGI_LCD_H +#define __LINUX_SPI_CORGI_LCD_H + +#define CORGI_LCD_MODE_QVGA 1 +#define CORGI_LCD_MODE_VGA 2 + +struct corgi_lcd_platform_data { + int init_mode; + int max_intensity; + int default_intensity; + + void (*notify)(int intensity); + void (*kick_battery)(void); +}; + +#endif /* __LINUX_SPI_CORGI_LCD_H */ -- cgit v1.2.3 From bfdcaa3b6899bbfc6ba633aff3f5f2422486c8c1 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 05:57:20 +0800 Subject: lcd: add corgibl_limit_intensity() to corgi_lcd This is not generic enough, added here for backward compatibility. And make this an individual commit so future revert will be a bit easier. Signed-off-by: Eric Miao Cc: Richard Purdie Signed-off-by: Russell King --- drivers/video/backlight/corgi_lcd.c | 27 +++++++++++++++++++++++++++ include/linux/spi/corgi_lcd.h | 1 + 2 files changed, 28 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index bf69e50d262e..068f14864099 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -86,6 +86,7 @@ struct corgi_lcd { struct lcd_device *lcd_dev; struct backlight_device *bl_dev; + int limit_mask; int intensity; int power; int mode; @@ -97,6 +98,11 @@ struct corgi_lcd { static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); +static struct corgi_lcd *the_corgi_lcd; +static unsigned long corgibl_flags; +#define CORGIBL_SUSPENDED 0x01 +#define CORGIBL_BATTLOW 0x02 + /* * This is only a psuedo I2C interface. We can't use the standard kernel * routines as the interface is write only. We just assume the data is acked... @@ -413,9 +419,25 @@ static int corgi_bl_update_status(struct backlight_device *bd) if (bd->props.fb_blank != FB_BLANK_UNBLANK) intensity = 0; + if (corgibl_flags & CORGIBL_SUSPENDED) + intensity = 0; + if (corgibl_flags & CORGIBL_BATTLOW) + intensity &= lcd->limit_mask; + return corgi_bl_set_intensity(lcd, intensity); } +void corgibl_limit_intensity(int limit) +{ + if (limit) + corgibl_flags |= CORGIBL_BATTLOW; + else + corgibl_flags &= ~CORGIBL_BATTLOW; + + backlight_update_status(the_corgi_lcd->bl_dev); +} +EXPORT_SYMBOL(corgibl_limit_intensity); + static struct backlight_ops corgi_bl_ops = { .get_brightness = corgi_bl_get_intensity, .update_status = corgi_bl_update_status, @@ -426,6 +448,7 @@ static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) { struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + corgibl_flags |= CORGIBL_SUSPENDED; corgi_bl_set_intensity(lcd, 0); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); return 0; @@ -435,6 +458,7 @@ static int corgi_lcd_resume(struct spi_device *spi) { struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + corgibl_flags &= ~CORGIBL_SUSPENDED; corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); return 0; @@ -488,6 +512,9 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, lcd); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); + + lcd->limit_mask = pdata->limit_mask; + the_corgi_lcd = lcd; return 0; err_unregister_lcd: diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h index 3c53ac26c8d1..b6161aae2752 100644 --- a/include/linux/spi/corgi_lcd.h +++ b/include/linux/spi/corgi_lcd.h @@ -8,6 +8,7 @@ struct corgi_lcd_platform_data { int init_mode; int max_intensity; int default_intensity; + int limit_mask; void (*notify)(int intensity); void (*kick_battery)(void); -- cgit v1.2.3 From dd89ccb23a718a25dd989a27b04bf52871c9fb23 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Mon, 1 Sep 2008 06:50:23 +0800 Subject: lcd: add support for Toppoly TDO24M series LCD panels This type of LCD panel can be found on PXA3xx/Littleton platforms. Add LCD device and SPI-based driver for this. Signed-off-by: Eric Miao Signed-off-by: Russell King --- drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/tdo24m.c | 396 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 drivers/video/backlight/tdo24m.c (limited to 'drivers/video') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index f5406cd78e28..c72a13562954 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -51,6 +51,14 @@ config LCD_ILI9320 If you have a panel based on the ILI9320 controller chip then say y to include a power driver for it. +config LCD_TDO24M + tristate "Toppoly TDO24M LCD Panels support" + depends on LCD_CLASS_DEVICE && SPI_MASTER + default n + help + If you have a Toppoly TDO24M series LCD panel, say y here to + include the support for it. + config LCD_VGG2432A4 tristate "VGG2432A4 LCM device support" depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index cf12d58392e0..3ec551eb472c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o +obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c new file mode 100644 index 000000000000..8427669162ea --- /dev/null +++ b/drivers/video/backlight/tdo24m.c @@ -0,0 +1,396 @@ +/* + * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels + * + * Copyright (C) 2008 Marvell International Ltd. + * Eric Miao + * + * 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 + * publishhed by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +#define TDO24M_SPI_BUFF_SIZE (4) +#define MODE_QVGA 0 +#define MODE_VGA 1 + +struct tdo24m { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + + struct spi_message msg; + struct spi_transfer xfer; + uint8_t *buf; + + int power; + int mode; +}; + +/* use bit 30, 31 as the indicator of command parameter number */ +#define CMD0(x) ((0 << 30) | (x)) +#define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1)) +#define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\ + ((x1) << 9) | 0x100 | (x2)) +#define CMD_NULL (-1) + +static uint32_t lcd_panel_reset[] = { + CMD0(0x1), /* reset */ + CMD0(0x0), /* nop */ + CMD0(0x0), /* nop */ + CMD0(0x0), /* nop */ + CMD_NULL, +}; + +static uint32_t lcd_panel_on[] = { + CMD0(0x29), /* Display ON */ + CMD2(0xB8, 0xFF, 0xF9), /* Output Control */ + CMD0(0x11), /* Sleep out */ + CMD1(0xB0, 0x16), /* Wake */ + CMD_NULL, +}; + +static uint32_t lcd_panel_off[] = { + CMD0(0x28), /* Display OFF */ + CMD2(0xB8, 0x80, 0x02), /* Output Control */ + CMD0(0x10), /* Sleep in */ + CMD1(0xB0, 0x00), /* Deep stand by in */ + CMD_NULL, +}; + +static uint32_t lcd_vga_pass_through[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x80), + CMD1(0xE1, 0x00), + CMD1(0x36, 0x50), + CMD1(0x3B, 0x00), + CMD_NULL, +}; + +static uint32_t lcd_qvga_pass_through[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x81), + CMD1(0xE1, 0x00), + CMD1(0x36, 0x50), + CMD1(0x3B, 0x22), + CMD_NULL, +}; + +static uint32_t lcd_vga_transfer[] = { + CMD1(0xcf, 0x02), /* Blanking period control (1) */ + CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ + CMD1(0xd1, 0x01), /* CKV timing control on/off */ + CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */ + CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */ + CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */ + CMD1(0xd5, 0x14), /* ASW timing control (2) */ + CMD0(0x21), /* Invert for normally black display */ + CMD0(0x29), /* Display on */ + CMD_NULL, +}; + +static uint32_t lcd_qvga_transfer[] = { + CMD1(0xd6, 0x02), /* Blanking period control (1) */ + CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */ + CMD1(0xd8, 0x01), /* CKV timing control on/off */ + CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */ + CMD2(0xde, 0x05, 0x0a), /* OEV timing control */ + CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */ + CMD1(0xe0, 0x0a), /* ASW timing control (2) */ + CMD0(0x21), /* Invert for normally black display */ + CMD0(0x29), /* Display on */ + CMD_NULL, +}; + +static uint32_t lcd_panel_config[] = { + CMD2(0xb8, 0xff, 0xf9), /* Output control */ + CMD0(0x11), /* sleep out */ + CMD1(0xba, 0x01), /* Display mode (1) */ + CMD1(0xbb, 0x00), /* Display mode (2) */ + CMD1(0x3a, 0x60), /* Display mode 18-bit RGB */ + CMD1(0xbf, 0x10), /* Drive system change control */ + CMD1(0xb1, 0x56), /* Booster operation setup */ + CMD1(0xb2, 0x33), /* Booster mode setup */ + CMD1(0xb3, 0x11), /* Booster frequency setup */ + CMD1(0xb4, 0x02), /* Op amp/system clock */ + CMD1(0xb5, 0x35), /* VCS voltage */ + CMD1(0xb6, 0x40), /* VCOM voltage */ + CMD1(0xb7, 0x03), /* External display signal */ + CMD1(0xbd, 0x00), /* ASW slew rate */ + CMD1(0xbe, 0x00), /* Dummy data for QuadData operation */ + CMD1(0xc0, 0x11), /* Sleep out FR count (A) */ + CMD1(0xc1, 0x11), /* Sleep out FR count (B) */ + CMD1(0xc2, 0x11), /* Sleep out FR count (C) */ + CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */ + CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */ + CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */ + CMD1(0xc6, 0xc0), /* Sleep out FR count (G) */ + CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */ + CMD1(0xc8, 0x44), /* Gamma 1 fine tuning (2) */ + CMD1(0xc9, 0x33), /* Gamma 1 inclination adjustment */ + CMD1(0xca, 0x00), /* Gamma 1 blue offset adjustment */ + CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */ + CMD_NULL, +}; + +static int tdo24m_writes(struct tdo24m *lcd, uint32_t *array) +{ + struct spi_transfer *x = &lcd->xfer; + uint32_t data, *p = array; + int nparams, err = 0; + + for (; *p != CMD_NULL; p++) { + + nparams = (*p >> 30) & 0x3; + + data = *p << (7 - nparams); + switch (nparams) { + case 0: + lcd->buf[0] = (data >> 8) & 0xff; + lcd->buf[1] = data & 0xff; + break; + case 1: + lcd->buf[0] = (data >> 16) & 0xff; + lcd->buf[1] = (data >> 8) & 0xff; + lcd->buf[2] = data & 0xff; + break; + case 2: + lcd->buf[0] = (data >> 24) & 0xff; + lcd->buf[1] = (data >> 16) & 0xff; + lcd->buf[2] = (data >> 8) & 0xff; + lcd->buf[3] = data & 0xff; + break; + default: + continue; + } + x->len = nparams + 2; + err = spi_sync(lcd->spi_dev, &lcd->msg); + if (err) + break; + } + + return err; +} + +static int tdo24m_adj_mode(struct tdo24m *lcd, int mode) +{ + switch (mode) { + case MODE_VGA: + tdo24m_writes(lcd, lcd_vga_pass_through); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_vga_transfer); + break; + case MODE_QVGA: + tdo24m_writes(lcd, lcd_qvga_pass_through); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_qvga_transfer); + break; + default: + return -EINVAL; + } + + lcd->mode = mode; + return 0; +} + +static int tdo24m_power_on(struct tdo24m *lcd) +{ + int err; + + err = tdo24m_writes(lcd, lcd_panel_on); + if (err) + goto out; + + err = tdo24m_writes(lcd, lcd_panel_reset); + if (err) + goto out; + + err = tdo24m_adj_mode(lcd, lcd->mode); +out: + return err; +} + +static int tdo24m_power_off(struct tdo24m *lcd) +{ + return tdo24m_writes(lcd, lcd_panel_off); +} + +static int tdo24m_power(struct tdo24m *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = tdo24m_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = tdo24m_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + + +static int tdo24m_set_power(struct lcd_device *ld, int power) +{ + struct tdo24m *lcd = lcd_get_data(ld); + return tdo24m_power(lcd, power); +} + +static int tdo24m_get_power(struct lcd_device *ld) +{ + struct tdo24m *lcd = lcd_get_data(ld); + return lcd->power; +} + +static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct tdo24m *lcd = lcd_get_data(ld); + int mode = MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = MODE_VGA; + + if (lcd->mode == mode) + return 0; + + return tdo24m_adj_mode(lcd, mode); +} + +static struct lcd_ops tdo24m_ops = { + .get_power = tdo24m_get_power, + .set_power = tdo24m_set_power, + .set_mode = tdo24m_set_mode, +}; + +static int __devinit tdo24m_probe(struct spi_device *spi) +{ + struct tdo24m *lcd; + struct spi_message *m; + struct spi_transfer *x; + int err; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + err = spi_setup(spi); + if (err) + return err; + + lcd = kzalloc(sizeof(struct tdo24m), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + lcd->spi_dev = spi; + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = MODE_VGA; /* default to VGA */ + + lcd->buf = kmalloc(TDO24M_SPI_BUFF_SIZE, sizeof(GFP_KERNEL)); + if (lcd->buf == NULL) { + kfree(lcd); + return -ENOMEM; + } + + m = &lcd->msg; + x = &lcd->xfer; + + spi_message_init(m); + + x->tx_buf = &lcd->buf[0]; + spi_message_add_tail(x, m); + + lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev, + lcd, &tdo24m_ops); + if (IS_ERR(lcd->lcd_dev)) { + err = PTR_ERR(lcd->lcd_dev); + goto out_free; + } + + dev_set_drvdata(&spi->dev, lcd); + err = tdo24m_power(lcd, FB_BLANK_UNBLANK); + if (err) + goto out_unregister; + + return 0; + +out_unregister: + lcd_device_unregister(lcd->lcd_dev); +out_free: + kfree(lcd->buf); + kfree(lcd); + return err; +} + +static int __devexit tdo24m_remove(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + tdo24m_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd->buf); + kfree(lcd); + + return 0; +} + +#ifdef CONFIG_PM +static int tdo24m_suspend(struct spi_device *spi, pm_message_t state) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + return tdo24m_power(lcd, FB_BLANK_POWERDOWN); +} + +static int tdo24m_resume(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + return tdo24m_power(lcd, FB_BLANK_UNBLANK); +} +#else +#define tdo24m_suspend NULL +#define tdo24m_resume NULL +#endif + +/* Power down all displays on reboot, poweroff or halt */ +static void tdo24m_shutdown(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + tdo24m_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver tdo24m_driver = { + .driver = { + .name = "tdo24m", + .owner = THIS_MODULE, + }, + .probe = tdo24m_probe, + .remove = __devexit_p(tdo24m_remove), + .shutdown = tdo24m_shutdown, + .suspend = tdo24m_suspend, + .resume = tdo24m_resume, +}; + +static int __init tdo24m_init(void) +{ + return spi_register_driver(&tdo24m_driver); +} +module_init(tdo24m_init); + +static void __exit tdo24m_exit(void) +{ + spi_unregister_driver(&tdo24m_driver); +} +module_exit(tdo24m_exit); + +MODULE_AUTHOR("Eric Miao "); +MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ff7a4c7130c0ad97d55f7ab3f0a35fbc1f41b376 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Sun, 7 Sep 2008 11:30:06 +0800 Subject: [ARM] corgi_lcd: use GPIO API for BACKLIGHT_ON and BACKLIGHT_CONT Signed-off-by: Eric Miao Signed-off-by: Russell King --- arch/arm/mach-pxa/corgi.c | 9 +--- arch/arm/mach-pxa/spitz.c | 23 ++++------ drivers/video/backlight/corgi_lcd.c | 83 ++++++++++++++++++++++++++++++++++--- include/linux/spi/corgi_lcd.h | 3 ++ 4 files changed, 90 insertions(+), 28 deletions(-) (limited to 'drivers/video') diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index f8fd1d872157..5c08c4e9cd22 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c @@ -444,12 +444,6 @@ static struct pxa2xx_spi_chip corgi_ads7846_chip = { .cs_control = corgi_ads7846_cs, }; -static void corgi_notify_intensity(int intensity) -{ - /* Bit 5 is via SCOOP */ - gpio_set_value(CORGI_GPIO_BACKLIGHT_CONT, !!(intensity & 0x0020)); -} - static void corgi_bl_kick_battery(void) { void (*kick_batt)(void); @@ -466,7 +460,8 @@ static struct corgi_lcd_platform_data corgi_lcdcon_info = { .max_intensity = 0x2f, .default_intensity = 0x1f, .limit_mask = 0x0b, - .notify = corgi_notify_intensity, + .gpio_backlight_cont = CORGI_GPIO_BACKLIGHT_CONT, + .gpio_backlight_on = -1, .kick_battery = corgi_bl_kick_battery, }; diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 1d8654d2fb96..245890d2b6b5 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -305,21 +305,6 @@ static struct pxa2xx_spi_chip spitz_ads7846_chip = { .cs_control = spitz_ads7846_cs, }; -static void spitz_notify_intensity(int intensity) -{ - if (machine_is_spitz() || machine_is_borzoi()) { - gpio_set_value(SPITZ_GPIO_BACKLIGHT_CONT, !(intensity & 0x20)); - gpio_set_value(SPITZ_GPIO_BACKLIGHT_ON, intensity); - return; - } - - if (machine_is_akita()) { - gpio_set_value(AKITA_GPIO_BACKLIGHT_CONT, !(intensity & 0x20)); - gpio_set_value(AKITA_GPIO_BACKLIGHT_ON, intensity); - return; - } -} - static void spitz_bl_kick_battery(void) { void (*kick_batt)(void); @@ -336,7 +321,8 @@ static struct corgi_lcd_platform_data spitz_lcdcon_info = { .max_intensity = 0x2f, .default_intensity = 0x1f, .limit_mask = 0x0b, - .notify = spitz_notify_intensity, + .gpio_backlight_cont = SPITZ_GPIO_BACKLIGHT_CONT, + .gpio_backlight_on = SPITZ_GPIO_BACKLIGHT_ON, .kick_battery = spitz_bl_kick_battery, }; @@ -399,6 +385,11 @@ static void __init spitz_init_spi(void) if (err) goto err_free_2; + if (machine_is_akita()) { + spitz_lcdcon_info.gpio_backlight_cont = AKITA_GPIO_BACKLIGHT_CONT; + spitz_lcdcon_info.gpio_backlight_on = AKITA_GPIO_BACKLIGHT_ON; + } + pxa2xx_set_spi_info(2, &spitz_spi_info); spi_register_board_info(ARRAY_AND_SIZE(spitz_spi_devices)); return; diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index 068f14864099..2afd47eefe74 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +93,10 @@ struct corgi_lcd { int mode; char buf[2]; - void (*notify)(int intensity); + int gpio_backlight_on; + int gpio_backlight_cont; + int gpio_backlight_cont_inverted; + void (*kick_battery)(void); }; @@ -393,18 +397,26 @@ static int corgi_bl_get_intensity(struct backlight_device *bd) static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) { + int cont; + if (intensity > 0x10) intensity += 0x10; corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); - lcd->intensity = intensity; - if (lcd->notify) - lcd->notify(intensity); + /* Bit 5 via GPIO_BACKLIGHT_CONT */ + cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted; + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_set_value(lcd->gpio_backlight_cont, cont); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_set_value(lcd->gpio_backlight_on, intensity); if (lcd->kick_battery) lcd->kick_battery(); + lcd->intensity = intensity; return 0; } @@ -468,6 +480,56 @@ static int corgi_lcd_resume(struct spi_device *spi) #define corgi_lcd_resume NULL #endif +static int setup_gpio_backlight(struct corgi_lcd *lcd, + struct corgi_lcd_platform_data *pdata) +{ + struct spi_device *spi = lcd->spi_dev; + int err; + + lcd->gpio_backlight_on = -1; + lcd->gpio_backlight_cont = -1; + + if (gpio_is_valid(pdata->gpio_backlight_on)) { + err = gpio_request(pdata->gpio_backlight_on, "BL_ON"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_on\n", pdata->gpio_backlight_on); + return err; + } + + lcd->gpio_backlight_on = pdata->gpio_backlight_on; + gpio_direction_output(lcd->gpio_backlight_on, 0); + } + + if (gpio_is_valid(pdata->gpio_backlight_cont)) { + err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_cont\n", pdata->gpio_backlight_cont); + goto err_free_backlight_on; + } + + lcd->gpio_backlight_cont = pdata->gpio_backlight_cont; + + /* spitz and akita use both GPIOs for backlight, and + * have inverted polarity of GPIO_BACKLIGHT_CONT + */ + if (gpio_is_valid(lcd->gpio_backlight_on)) { + lcd->gpio_backlight_cont_inverted = 1; + gpio_direction_output(lcd->gpio_backlight_cont, 1); + } else { + lcd->gpio_backlight_cont_inverted = 0; + gpio_direction_output(lcd->gpio_backlight_cont, 0); + } + } + return 0; + +err_free_backlight_on: + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + return err; +} + static int __devinit corgi_lcd_probe(struct spi_device *spi) { struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; @@ -506,7 +568,10 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) lcd->bl_dev->props.brightness = pdata->default_intensity; lcd->bl_dev->props.power = FB_BLANK_UNBLANK; - lcd->notify = pdata->notify; + ret = setup_gpio_backlight(lcd, pdata); + if (ret) + goto err_unregister_bl; + lcd->kick_battery = pdata->kick_battery; dev_set_drvdata(&spi->dev, lcd); @@ -517,6 +582,8 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi) the_corgi_lcd = lcd; return 0; +err_unregister_bl: + backlight_device_unregister(lcd->bl_dev); err_unregister_lcd: lcd_device_unregister(lcd->lcd_dev); err_free_lcd: @@ -533,6 +600,12 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi) backlight_update_status(lcd->bl_dev); backlight_device_unregister(lcd->bl_dev); + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_free(lcd->gpio_backlight_cont); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->lcd_dev); kfree(lcd); diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h index b6161aae2752..6692b3418ccf 100644 --- a/include/linux/spi/corgi_lcd.h +++ b/include/linux/spi/corgi_lcd.h @@ -10,6 +10,9 @@ struct corgi_lcd_platform_data { int default_intensity; int limit_mask; + int gpio_backlight_on; /* -1 if n/a */ + int gpio_backlight_cont; /* -1 if n/a */ + void (*notify)(int intensity); void (*kick_battery)(void); }; -- cgit v1.2.3