backlight: spi driver for LMS283GF05 LCD
authorMarek Vasut <marek.vasut@gmail.com>
Thu, 6 Aug 2009 23:07:10 +0000 (16:07 -0700)
committerRichard Purdie <rpurdie@linux.intel.com>
Mon, 7 Sep 2009 14:08:06 +0000 (15:08 +0100)
ADd support for the SPI part of LMS283GF05 LCD.  The LCD uses SPI for
initialization and powerdown sequences.  No further defails are specified
in the datasheet about the initialization/powerdown sequence, just the
magic numbers that have to be sent over SPI bus.  This LCD can be found in
the Aeronix Zipit Z2 handheld.

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/lms283gf05.c [new file with mode: 0644]
include/linux/spi/lms283gf05.h [new file with mode: 0644]

index f86dbfd209ad8f34d4bbd506049d3980889a8b09..c0d4a536ea877a1f39426a5274ac6f5af423c94f 100644 (file)
@@ -31,6 +31,13 @@ config LCD_CORGI
          Say y here to support the LCD panels usually found on SHARP
          corgi (C7x0) and spitz (Cxx00) models.
 
+config LCD_LMS283GF05
+       tristate "Samsung LMS283GF05 LCD"
+       depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO
+       help
+         SPI driver for Samsung LMS283GF05. This provides basic support
+         for powering the LCD up/down through a sysfs interface.
+
 config LCD_LTV350QV
        tristate "Samsung LTV350QV LCD Panel"
        depends on LCD_CLASS_DEVICE && SPI_MASTER
index df0b67ce88b6cfb2f6706a0286af2acf768ae4ef..0abc014215babb2275691bf3f1c0e4ee6b0ff379 100644 (file)
@@ -3,6 +3,7 @@
 obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
 obj-$(CONFIG_LCD_CORGI)                   += corgi_lcd.o
 obj-$(CONFIG_LCD_HP700)                   += jornada720_lcd.o
+obj-$(CONFIG_LCD_LMS283GF05)      += lms283gf05.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/lms283gf05.c b/drivers/video/backlight/lms283gf05.c
new file mode 100644 (file)
index 0000000..447b542
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * lms283gf05.c -- support for Samsung LMS283GF05 LCD
+ *
+ * Copyright (c) 2009 Marek Vasut <marek.vasut@gmail.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.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/lms283gf05.h>
+
+struct lms283gf05_state {
+       struct spi_device       *spi;
+       struct lcd_device       *ld;
+};
+
+struct lms283gf05_seq {
+       unsigned char           reg;
+       unsigned short          value;
+       unsigned char           delay;
+};
+
+/* Magic sequences supplied by manufacturer, for details refer to datasheet */
+static struct lms283gf05_seq disp_initseq[] = {
+       /* REG, VALUE, DELAY */
+       { 0x07, 0x0000, 0 },
+       { 0x13, 0x0000, 10 },
+
+       { 0x11, 0x3004, 0 },
+       { 0x14, 0x200F, 0 },
+       { 0x10, 0x1a20, 0 },
+       { 0x13, 0x0040, 50 },
+
+       { 0x13, 0x0060, 0 },
+       { 0x13, 0x0070, 200 },
+
+       { 0x01, 0x0127, 0 },
+       { 0x02, 0x0700, 0 },
+       { 0x03, 0x1030, 0 },
+       { 0x08, 0x0208, 0 },
+       { 0x0B, 0x0620, 0 },
+       { 0x0C, 0x0110, 0 },
+       { 0x30, 0x0120, 0 },
+       { 0x31, 0x0127, 0 },
+       { 0x32, 0x0000, 0 },
+       { 0x33, 0x0503, 0 },
+       { 0x34, 0x0727, 0 },
+       { 0x35, 0x0124, 0 },
+       { 0x36, 0x0706, 0 },
+       { 0x37, 0x0701, 0 },
+       { 0x38, 0x0F00, 0 },
+       { 0x39, 0x0F00, 0 },
+       { 0x40, 0x0000, 0 },
+       { 0x41, 0x0000, 0 },
+       { 0x42, 0x013f, 0 },
+       { 0x43, 0x0000, 0 },
+       { 0x44, 0x013f, 0 },
+       { 0x45, 0x0000, 0 },
+       { 0x46, 0xef00, 0 },
+       { 0x47, 0x013f, 0 },
+       { 0x48, 0x0000, 0 },
+       { 0x07, 0x0015, 30 },
+
+       { 0x07, 0x0017, 0 },
+
+       { 0x20, 0x0000, 0 },
+       { 0x21, 0x0000, 0 },
+       { 0x22, 0x0000, 0 }
+};
+
+static struct lms283gf05_seq disp_pdwnseq[] = {
+       { 0x07, 0x0016, 30 },
+
+       { 0x07, 0x0004, 0 },
+       { 0x10, 0x0220, 20 },
+
+       { 0x13, 0x0060, 50 },
+
+       { 0x13, 0x0040, 50 },
+
+       { 0x13, 0x0000, 0 },
+       { 0x10, 0x0000, 0 }
+};
+
+
+static void lms283gf05_reset(unsigned long gpio, bool inverted)
+{
+       gpio_set_value(gpio, !inverted);
+       mdelay(100);
+       gpio_set_value(gpio, inverted);
+       mdelay(20);
+       gpio_set_value(gpio, !inverted);
+       mdelay(20);
+}
+
+static void lms283gf05_toggle(struct spi_device *spi,
+                       struct lms283gf05_seq *seq, int sz)
+{
+       char buf[3];
+       int i;
+
+       for (i = 0; i < sz; i++) {
+               buf[0] = 0x74;
+               buf[1] = 0x00;
+               buf[2] = seq[i].reg;
+               spi_write(spi, buf, 3);
+
+               buf[0] = 0x76;
+               buf[1] = seq[i].value >> 8;
+               buf[2] = seq[i].value & 0xff;
+               spi_write(spi, buf, 3);
+
+               mdelay(seq[i].delay);
+       }
+}
+
+static int lms283gf05_power_set(struct lcd_device *ld, int power)
+{
+       struct lms283gf05_state *st = lcd_get_data(ld);
+       struct spi_device *spi = st->spi;
+       struct lms283gf05_pdata *pdata = spi->dev.platform_data;
+
+       if (power) {
+               if (pdata)
+                       lms283gf05_reset(pdata->reset_gpio,
+                                       pdata->reset_inverted);
+               lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
+       } else {
+               lms283gf05_toggle(spi, disp_pdwnseq, ARRAY_SIZE(disp_pdwnseq));
+               if (pdata)
+                       gpio_set_value(pdata->reset_gpio,
+                                       pdata->reset_inverted);
+       }
+
+       return 0;
+}
+
+static struct lcd_ops lms_ops = {
+       .set_power      = lms283gf05_power_set,
+       .get_power      = NULL,
+};
+
+static int __devinit lms283gf05_probe(struct spi_device *spi)
+{
+       struct lms283gf05_state *st;
+       struct lms283gf05_pdata *pdata = spi->dev.platform_data;
+       struct lcd_device *ld;
+       int ret = 0;
+
+       if (pdata != NULL) {
+               ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET");
+               if (ret)
+                       return ret;
+
+               ret = gpio_direction_output(pdata->reset_gpio,
+                                               !pdata->reset_inverted);
+               if (ret)
+                       goto err;
+       }
+
+       st = kzalloc(sizeof(struct lms283gf05_state), GFP_KERNEL);
+       if (st == NULL) {
+               dev_err(&spi->dev, "No memory for device state\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops);
+       if (IS_ERR(ld)) {
+               ret = PTR_ERR(ld);
+               goto err2;
+       }
+
+       st->spi = spi;
+       st->ld = ld;
+
+       dev_set_drvdata(&spi->dev, st);
+
+       /* kick in the LCD */
+       if (pdata)
+               lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted);
+       lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
+
+       return 0;
+
+err2:
+       kfree(st);
+err:
+       if (pdata != NULL)
+               gpio_free(pdata->reset_gpio);
+
+       return ret;
+}
+
+static int __devexit lms283gf05_remove(struct spi_device *spi)
+{
+       struct lms283gf05_state *st = dev_get_drvdata(&spi->dev);
+       struct lms283gf05_pdata *pdata = st->spi->dev.platform_data;
+
+       lcd_device_unregister(st->ld);
+
+       if (pdata != NULL)
+               gpio_free(pdata->reset_gpio);
+
+       kfree(st);
+
+       return 0;
+}
+
+static struct spi_driver lms283gf05_driver = {
+       .driver = {
+               .name   = "lms283gf05",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = lms283gf05_probe,
+       .remove         = __devexit_p(lms283gf05_remove),
+};
+
+static __init int lms283gf05_init(void)
+{
+       return spi_register_driver(&lms283gf05_driver);
+}
+
+static __exit void lms283gf05_exit(void)
+{
+       spi_unregister_driver(&lms283gf05_driver);
+}
+
+module_init(lms283gf05_init);
+module_exit(lms283gf05_exit);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("LCD283GF05 LCD");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/spi/lms283gf05.h b/include/linux/spi/lms283gf05.h
new file mode 100644 (file)
index 0000000..555d254
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * lms283gf05.h - Platform glue for Samsung LMS283GF05 LCD
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _INCLUDE_LINUX_SPI_LMS283GF05_H_
+#define _INCLUDE_LINUX_SPI_LMS283GF05_H_
+
+struct lms283gf05_pdata {
+       unsigned long   reset_gpio;
+       bool            reset_inverted;
+};
+
+#endif /* _INCLUDE_LINUX_SPI_LMS283GF05_H_ */