diff options
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 17 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 4 | ||||
-rw-r--r-- | drivers/video/backlight/ili9320.c | 330 | ||||
-rw-r--r-- | drivers/video/backlight/ili9320.h | 80 | ||||
-rw-r--r-- | drivers/video/backlight/vgg2432a4.c | 284 |
5 files changed, 714 insertions, 1 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 30bf7f2f163..a5b3a92ffdc 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -36,6 +36,23 @@ config LCD_LTV350QV The LTV350QV panel is present on all ATSTK1000 boards. +config LCD_ILI9320 + tristate + depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT + default n + help + If you have a panel based on the ILI9320 controller chip + then say y to include a power driver for it. + +config LCD_VGG2432A4 + tristate "VGG2432A4 LCM device support" + depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER + select LCD_ILI9320 + default n + help + If you have a VGG2432A4 panel based on the ILI9320 controller chip + then say y to include a power driver for it. + # # Backlight # diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b51a7cd1250..366d84e380c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,7 +1,9 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o -obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_ILI9320) += ili9320.o +obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c new file mode 100644 index 00000000000..ba89b41b639 --- /dev/null +++ b/drivers/video/backlight/ili9320.c @@ -0,0 +1,330 @@ +/* drivers/video/backlight/ili9320.c + * + * ILI9320 LCD controller driver core. + * + * Copyright 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * 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/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> + +#include <linux/spi/spi.h> + +#include <video/ili9320.h> + +#include "ili9320.h" + + +static inline int ili9320_write_spi(struct ili9320 *ili, + unsigned int reg, + unsigned int value) +{ + struct ili9320_spi *spi = &ili->access.spi; + unsigned char *addr = spi->buffer_addr; + unsigned char *data = spi->buffer_data; + + /* spi message consits of: + * first byte: ID and operation + */ + + addr[0] = spi->id | ILI9320_SPI_INDEX | ILI9320_SPI_WRITE; + addr[1] = reg >> 8; + addr[2] = reg; + + /* second message is the data to transfer */ + + data[0] = spi->id | ILI9320_SPI_DATA | ILI9320_SPI_WRITE; + data[1] = value >> 8; + data[2] = value; + + return spi_sync(spi->dev, &spi->message); +} + +int ili9320_write(struct ili9320 *ili, unsigned int reg, unsigned int value) +{ + dev_dbg(ili->dev, "write: reg=%02x, val=%04x\n", reg, value); + return ili->write(ili, reg, value); +} + +EXPORT_SYMBOL_GPL(ili9320_write); + +int ili9320_write_regs(struct ili9320 *ili, + struct ili9320_reg *values, + int nr_values) +{ + int index; + int ret; + + for (index = 0; index < nr_values; index++, values++) { + ret = ili9320_write(ili, values->address, values->value); + if (ret != 0) + return ret; + } + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_write_regs); + +static void ili9320_reset(struct ili9320 *lcd) +{ + struct ili9320_platdata *cfg = lcd->platdata; + + cfg->reset(1); + mdelay(50); + + cfg->reset(0); + mdelay(50); + + cfg->reset(1); + mdelay(100); +} + +static inline int ili9320_init_chip(struct ili9320 *lcd) +{ + int ret; + + ili9320_reset(lcd); + + ret = lcd->client->init(lcd, lcd->platdata); + if (ret != 0) { + dev_err(lcd->dev, "failed to initialise display\n"); + return ret; + } + + lcd->initialised = 1; + return 0; +} + +static inline int ili9320_power_on(struct ili9320 *lcd) +{ + if (!lcd->initialised) + ili9320_init_chip(lcd); + + lcd->display1 |= (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE); + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; +} + +static inline int ili9320_power_off(struct ili9320 *lcd) +{ + lcd->display1 &= ~(ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE); + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; +} + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +static int ili9320_power(struct ili9320 *lcd, int power) +{ + int ret = 0; + + dev_dbg(lcd->dev, "power %d => %d\n", lcd->power, power); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = ili9320_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = ili9320_power_off(lcd); + + if (ret == 0) + lcd->power = power; + else + dev_warn(lcd->dev, "failed to set power mode %d\n", power); + + return ret; +} + +static inline struct ili9320 *to_our_lcd(struct lcd_device *lcd) +{ + return lcd_get_data(lcd); +} + +static int ili9320_set_power(struct lcd_device *ld, int power) +{ + struct ili9320 *lcd = to_our_lcd(ld); + + return ili9320_power(lcd, power); +} + +static int ili9320_get_power(struct lcd_device *ld) +{ + struct ili9320 *lcd = to_our_lcd(ld); + + return lcd->power; +} + +static struct lcd_ops ili9320_ops = { + .get_power = ili9320_get_power, + .set_power = ili9320_set_power, +}; + +static void __devinit ili9320_setup_spi(struct ili9320 *ili, + struct spi_device *dev) +{ + struct ili9320_spi *spi = &ili->access.spi; + + ili->write = ili9320_write_spi; + spi->dev = dev; + + /* fill the two messages we are going to use to send the data + * with, the first the address followed by the data. The datasheet + * says they should be done as two distinct cycles of the SPI CS line. + */ + + spi->xfer[0].tx_buf = spi->buffer_addr; + spi->xfer[1].tx_buf = spi->buffer_data; + spi->xfer[0].len = 3; + spi->xfer[1].len = 3; + spi->xfer[0].bits_per_word = 8; + spi->xfer[1].bits_per_word = 8; + spi->xfer[0].cs_change = 1; + + spi_message_init(&spi->message); + spi_message_add_tail(&spi->xfer[0], &spi->message); + spi_message_add_tail(&spi->xfer[1], &spi->message); +} + +int __devinit ili9320_probe_spi(struct spi_device *spi, + struct ili9320_client *client) +{ + struct ili9320_platdata *cfg = spi->dev.platform_data; + struct device *dev = &spi->dev; + struct ili9320 *ili; + struct lcd_device *lcd; + int ret = 0; + + /* verify we where given some information */ + + if (cfg == NULL) { + dev_err(dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (cfg->hsize <= 0 || cfg->vsize <= 0 || cfg->reset == NULL) { + dev_err(dev, "invalid platform data supplied\n"); + return -EINVAL; + } + + /* allocate and initialse our state */ + + ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL); + if (ili == NULL) { + dev_err(dev, "no memory for device\n"); + return -ENOMEM; + } + + ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1); + + ili->dev = dev; + ili->client = client; + ili->power = FB_BLANK_POWERDOWN; + ili->platdata = cfg; + + dev_set_drvdata(&spi->dev, ili); + + ili9320_setup_spi(ili, spi); + + lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops); + if (IS_ERR(lcd)) { + dev_err(dev, "failed to register lcd device\n"); + ret = PTR_ERR(lcd); + goto err_free; + } + + ili->lcd = lcd; + + dev_info(dev, "initialising %s\n", client->name); + + ret = ili9320_power(ili, FB_BLANK_UNBLANK); + if (ret != 0) { + dev_err(dev, "failed to set lcd power state\n"); + goto err_unregister; + } + + return 0; + + err_unregister: + lcd_device_unregister(lcd); + + err_free: + kfree(ili); + + return ret; +} + +EXPORT_SYMBOL_GPL(ili9320_probe_spi); + +int __devexit ili9320_remove(struct ili9320 *ili) +{ + ili9320_power(ili, FB_BLANK_POWERDOWN); + + lcd_device_unregister(ili->lcd); + kfree(ili); + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_remove); + +#ifdef CONFIG_PM +int ili9320_suspend(struct ili9320 *lcd, pm_message_t state) +{ + int ret; + + dev_dbg(lcd->dev, "%s: event %d\n", __func__, state.event); + + if (state.event == PM_EVENT_SUSPEND) { + ret = ili9320_power(lcd, FB_BLANK_POWERDOWN); + + if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) { + ili9320_write(lcd, ILI9320_POWER1, lcd->power1 | + ILI9320_POWER1_SLP | + ILI9320_POWER1_DSTB); + lcd->initialised = 0; + } + + return ret; + } + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_suspend); + +int ili9320_resume(struct ili9320 *lcd) +{ + dev_info(lcd->dev, "resuming from power state %d\n", lcd->power); + + if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) { + ili9320_write(lcd, ILI9320_POWER1, 0x00); + } + + return ili9320_power(lcd, FB_BLANK_UNBLANK); +} + +EXPORT_SYMBOL_GPL(ili9320_resume); +#endif + +/* Power down all displays on reboot, poweroff or halt */ +void ili9320_shutdown(struct ili9320 *lcd) +{ + ili9320_power(lcd, FB_BLANK_POWERDOWN); +} + +EXPORT_SYMBOL_GPL(ili9320_shutdown); + +MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); +MODULE_DESCRIPTION("ILI9320 LCD Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/ili9320.h b/drivers/video/backlight/ili9320.h new file mode 100644 index 00000000000..e388eca7cac --- /dev/null +++ b/drivers/video/backlight/ili9320.h @@ -0,0 +1,80 @@ +/* drivers/video/backlight/ili9320.h + * + * ILI9320 LCD controller driver core. + * + * Copyright 2007 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://armlinux.simtec.co.uk/ + * + * 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. +*/ + +/* Holder for register and value pairs. */ +struct ili9320_reg { + unsigned short address; + unsigned short value; +}; + +struct ili9320; + +struct ili9320_client { + const char *name; + int (*init)(struct ili9320 *ili, struct ili9320_platdata *cfg); + +}; +/* Device attached via an SPI bus. */ +struct ili9320_spi { + struct spi_device *dev; + struct spi_message message; + struct spi_transfer xfer[2]; + + unsigned char id; + unsigned char buffer_addr[4]; + unsigned char buffer_data[4]; +}; + +/* ILI9320 device state. */ +struct ili9320 { + union { + struct ili9320_spi spi; /* SPI attachged device. */ + } access; /* Register access method. */ + + struct device *dev; + struct lcd_device *lcd; /* LCD device we created. */ + struct ili9320_client *client; + struct ili9320_platdata *platdata; + + int power; /* current power state. */ + int initialised; + + unsigned short display1; + unsigned short power1; + + int (*write)(struct ili9320 *ili, unsigned int reg, unsigned int val); +}; + + +/* ILI9320 register access routines */ + +extern int ili9320_write(struct ili9320 *ili, + unsigned int reg, unsigned int value); + +extern int ili9320_write_regs(struct ili9320 *ili, + struct ili9320_reg *values, + int nr_values); + +/* Device probe */ + +extern int ili9320_probe_spi(struct spi_device *spi, + struct ili9320_client *cli); + +extern int ili9320_remove(struct ili9320 *lcd); +extern void ili9320_shutdown(struct ili9320 *lcd); + +/* PM */ + +extern int ili9320_suspend(struct ili9320 *lcd, pm_message_t state); +extern int ili9320_resume(struct ili9320 *lcd); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c new file mode 100644 index 00000000000..593c7687d54 --- /dev/null +++ b/drivers/video/backlight/vgg2432a4.c @@ -0,0 +1,284 @@ +/* drivers/video/backlight/vgg2432a4.c + * + * VGG2432A4 (ILI9320) LCD controller driver. + * + * Copyright 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * 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/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> + +#include <linux/spi/spi.h> + +#include <video/ili9320.h> + +#include "ili9320.h" + +/* Device initialisation sequences */ + +static struct ili9320_reg vgg_init1[] = { + { + .address = ILI9320_POWER1, + .value = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0), + }, { + .address = ILI9320_POWER2, + .value = (ILI9320_POWER2_VC(7) | + ILI9320_POWER2_DC0(0) | ILI9320_POWER2_DC1(0)), + }, { + .address = ILI9320_POWER3, + .value = ILI9320_POWER3_VRH(0), + }, { + .address = ILI9320_POWER4, + .value = ILI9320_POWER4_VREOUT(0), + }, +}; + +static struct ili9320_reg vgg_init2[] = { + { + .address = ILI9320_POWER1, + .value = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE | + ILI9320_POWER1_BT(7) | ILI9320_POWER1_SAP), + }, { + .address = ILI9320_POWER2, + .value = ILI9320_POWER2_VC(7) | ILI9320_POWER2_DC0(3), + } +}; + +static struct ili9320_reg vgg_gamma[] = { + { + .address = ILI9320_GAMMA1, + .value = 0x0000, + }, { + .address = ILI9320_GAMMA2, + .value = 0x0505, + }, { + .address = ILI9320_GAMMA3, + .value = 0x0004, + }, { + .address = ILI9320_GAMMA4, + .value = 0x0006, + }, { + .address = ILI9320_GAMMA5, + .value = 0x0707, + }, { + .address = ILI9320_GAMMA6, + .value = 0x0105, + }, { + .address = ILI9320_GAMMA7, + .value = 0x0002, + }, { + .address = ILI9320_GAMMA8, + .value = 0x0707, + }, { + .address = ILI9320_GAMMA9, + .value = 0x0704, + }, { + .address = ILI9320_GAMMA10, + .value = 0x807, + } + +}; + +static struct ili9320_reg vgg_init0[] = { + [0] = { + /* set direction and scan mode gate */ + .address = ILI9320_DRIVER, + .value = ILI9320_DRIVER_SS, + }, { + .address = ILI9320_DRIVEWAVE, + .value = (ILI9320_DRIVEWAVE_MUSTSET | + ILI9320_DRIVEWAVE_EOR | ILI9320_DRIVEWAVE_BC), + }, { + .address = ILI9320_ENTRYMODE, + .value = ILI9320_ENTRYMODE_ID(3) | ILI9320_ENTRYMODE_BGR, + }, { + .address = ILI9320_RESIZING, + .value = 0x0, + }, +}; + + +static int vgg2432a4_lcd_init(struct ili9320 *lcd, + struct ili9320_platdata *cfg) +{ + unsigned int addr; + int ret; + + /* Set VCore before anything else (VGG243237-6UFLWA) */ + ret = ili9320_write(lcd, 0x00e5, 0x8000); + if (ret) + goto err_initial; + + /* Start the oscillator up before we can do anything else. */ + ret = ili9320_write(lcd, ILI9320_OSCILATION, ILI9320_OSCILATION_OSC); + if (ret) + goto err_initial; + + /* must wait at-lesat 10ms after starting */ + mdelay(15); + + ret = ili9320_write_regs(lcd, vgg_init0, ARRAY_SIZE(vgg_init0)); + if (ret != 0) + goto err_initial; + + ili9320_write(lcd, ILI9320_DISPLAY2, cfg->display2); + ili9320_write(lcd, ILI9320_DISPLAY3, cfg->display3); + ili9320_write(lcd, ILI9320_DISPLAY4, cfg->display4); + + ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1); + ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0); + ili9320_write(lcd, ILI9320_RGB_IF2, ILI9320_RGBIF2_DPL); + + ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1)); + if (ret != 0) + goto err_vgg; + + mdelay(300); + + ret = ili9320_write_regs(lcd, vgg_init2, ARRAY_SIZE(vgg_init2)); + if (ret != 0) + goto err_vgg2; + + mdelay(100); + + ili9320_write(lcd, ILI9320_POWER3, 0x13c); + + mdelay(100); + + ili9320_write(lcd, ILI9320_POWER4, 0x1c00); + ili9320_write(lcd, ILI9320_POWER7, 0x000e); + + mdelay(100); + + ili9320_write(lcd, ILI9320_GRAM_HORIZ_ADDR, 0x00); + ili9320_write(lcd, ILI9320_GRAM_VERT_ADD, 0x00); + + ret = ili9320_write_regs(lcd, vgg_gamma, ARRAY_SIZE(vgg_gamma)); + if (ret != 0) + goto err_vgg3; + + ili9320_write(lcd, ILI9320_HORIZ_START, 0x0); + ili9320_write(lcd, ILI9320_HORIZ_END, cfg->hsize - 1); + ili9320_write(lcd, ILI9320_VERT_START, 0x0); + ili9320_write(lcd, ILI9320_VERT_END, cfg->vsize - 1); + + ili9320_write(lcd, ILI9320_DRIVER2, + ILI9320_DRIVER2_NL(((cfg->vsize - 240) / 8) + 0x1D)); + + ili9320_write(lcd, ILI9320_BASE_IMAGE, 0x1); + ili9320_write(lcd, ILI9320_VERT_SCROLL, 0x00); + + for (addr = ILI9320_PARTIAL1_POSITION; addr <= ILI9320_PARTIAL2_END; + addr++) { + ili9320_write(lcd, addr, 0x0); + } + + ili9320_write(lcd, ILI9320_INTERFACE1, 0x10); + ili9320_write(lcd, ILI9320_INTERFACE2, cfg->interface2); + ili9320_write(lcd, ILI9320_INTERFACE3, cfg->interface3); + ili9320_write(lcd, ILI9320_INTERFACE4, cfg->interface4); + ili9320_write(lcd, ILI9320_INTERFACE5, cfg->interface5); + ili9320_write(lcd, ILI9320_INTERFACE6, cfg->interface6); + + lcd->display1 = (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_DTE | + ILI9320_DISPLAY1_GON | ILI9320_DISPLAY1_BASEE | + 0x40); + + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; + + err_vgg3: + err_vgg2: + err_vgg: + err_initial: + return ret; +} + +#ifdef CONFIG_PM +static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state) +{ + return ili9320_suspend(dev_get_drvdata(&spi->dev), state); +} + +static int vgg2432a4_resume(struct spi_device *spi) +{ + return ili9320_resume(dev_get_drvdata(&spi->dev)); +} +#else +#define vgg2432a4_suspend NULL +#define vgg2432a4_resume NULL +#endif + +static struct ili9320_client vgg2432a4_client = { + .name = "VGG2432A4", + .init = vgg2432a4_lcd_init, +}; + +/* Device probe */ + +static int __devinit vgg2432a4_probe(struct spi_device *spi) +{ + int ret; + + ret = ili9320_probe_spi(spi, &vgg2432a4_client); + if (ret != 0) { + dev_err(&spi->dev, "failed to initialise ili9320\n"); + return ret; + } + + return 0; +} + +static int __devexit vgg2432a4_remove(struct spi_device *spi) +{ + return ili9320_remove(dev_get_drvdata(&spi->dev)); +} + +static void vgg2432a4_shutdown(struct spi_device *spi) +{ + ili9320_shutdown(dev_get_drvdata(&spi->dev)); +} + +static struct spi_driver vgg2432a4_driver = { + .driver = { + .name = "VGG2432A4", + .owner = THIS_MODULE, + }, + .probe = vgg2432a4_probe, + .remove = __devexit_p(vgg2432a4_remove), + .shutdown = vgg2432a4_shutdown, + .suspend = vgg2432a4_suspend, + .resume = vgg2432a4_resume, +}; + +/* Device driver initialisation */ + +static int __init vgg2432a4_init(void) +{ + return spi_register_driver(&vgg2432a4_driver); +} + +static void __exit vgg2432a4_exit(void) +{ + spi_unregister_driver(&vgg2432a4_driver); +} + +module_init(vgg2432a4_init); +module_exit(vgg2432a4_exit); + +MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); +MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); +MODULE_LICENSE("GPL v2"); + + |