xref: /linux/drivers/video/backlight/lms283gf05.c (revision 04e961fb1da31258f7bf4480a59518c3de3caa60)
15036cc41SMarek Vasut /*
25036cc41SMarek Vasut  * lms283gf05.c -- support for Samsung LMS283GF05 LCD
35036cc41SMarek Vasut  *
45036cc41SMarek Vasut  * Copyright (c) 2009 Marek Vasut <marek.vasut@gmail.com>
55036cc41SMarek Vasut  *
65036cc41SMarek Vasut  * This program is free software; you can redistribute it and/or modify
75036cc41SMarek Vasut  * it under the terms of the GNU General Public License version 2 as
85036cc41SMarek Vasut  * published by the Free Software Foundation.
95036cc41SMarek Vasut  */
105036cc41SMarek Vasut 
115036cc41SMarek Vasut #include <linux/device.h>
125036cc41SMarek Vasut #include <linux/kernel.h>
135036cc41SMarek Vasut #include <linux/delay.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
155036cc41SMarek Vasut #include <linux/gpio.h>
165036cc41SMarek Vasut #include <linux/lcd.h>
175036cc41SMarek Vasut 
185036cc41SMarek Vasut #include <linux/spi/spi.h>
195036cc41SMarek Vasut #include <linux/spi/lms283gf05.h>
20355b200bSPaul Gortmaker #include <linux/module.h>
215036cc41SMarek Vasut 
225036cc41SMarek Vasut struct lms283gf05_state {
235036cc41SMarek Vasut 	struct spi_device	*spi;
245036cc41SMarek Vasut 	struct lcd_device	*ld;
255036cc41SMarek Vasut };
265036cc41SMarek Vasut 
275036cc41SMarek Vasut struct lms283gf05_seq {
285036cc41SMarek Vasut 	unsigned char		reg;
295036cc41SMarek Vasut 	unsigned short		value;
305036cc41SMarek Vasut 	unsigned char		delay;
315036cc41SMarek Vasut };
325036cc41SMarek Vasut 
335036cc41SMarek Vasut /* Magic sequences supplied by manufacturer, for details refer to datasheet */
345036cc41SMarek Vasut static struct lms283gf05_seq disp_initseq[] = {
355036cc41SMarek Vasut 	/* REG, VALUE, DELAY */
365036cc41SMarek Vasut 	{ 0x07, 0x0000, 0 },
375036cc41SMarek Vasut 	{ 0x13, 0x0000, 10 },
385036cc41SMarek Vasut 
395036cc41SMarek Vasut 	{ 0x11, 0x3004, 0 },
405036cc41SMarek Vasut 	{ 0x14, 0x200F, 0 },
415036cc41SMarek Vasut 	{ 0x10, 0x1a20, 0 },
425036cc41SMarek Vasut 	{ 0x13, 0x0040, 50 },
435036cc41SMarek Vasut 
445036cc41SMarek Vasut 	{ 0x13, 0x0060, 0 },
455036cc41SMarek Vasut 	{ 0x13, 0x0070, 200 },
465036cc41SMarek Vasut 
475036cc41SMarek Vasut 	{ 0x01, 0x0127, 0 },
485036cc41SMarek Vasut 	{ 0x02,	0x0700, 0 },
495036cc41SMarek Vasut 	{ 0x03, 0x1030, 0 },
505036cc41SMarek Vasut 	{ 0x08, 0x0208, 0 },
515036cc41SMarek Vasut 	{ 0x0B, 0x0620, 0 },
525036cc41SMarek Vasut 	{ 0x0C, 0x0110, 0 },
535036cc41SMarek Vasut 	{ 0x30, 0x0120, 0 },
545036cc41SMarek Vasut 	{ 0x31, 0x0127, 0 },
555036cc41SMarek Vasut 	{ 0x32, 0x0000, 0 },
565036cc41SMarek Vasut 	{ 0x33, 0x0503, 0 },
575036cc41SMarek Vasut 	{ 0x34, 0x0727, 0 },
585036cc41SMarek Vasut 	{ 0x35, 0x0124, 0 },
595036cc41SMarek Vasut 	{ 0x36, 0x0706, 0 },
605036cc41SMarek Vasut 	{ 0x37, 0x0701, 0 },
615036cc41SMarek Vasut 	{ 0x38, 0x0F00, 0 },
625036cc41SMarek Vasut 	{ 0x39, 0x0F00, 0 },
635036cc41SMarek Vasut 	{ 0x40, 0x0000, 0 },
645036cc41SMarek Vasut 	{ 0x41, 0x0000, 0 },
655036cc41SMarek Vasut 	{ 0x42, 0x013f, 0 },
665036cc41SMarek Vasut 	{ 0x43, 0x0000, 0 },
675036cc41SMarek Vasut 	{ 0x44, 0x013f, 0 },
685036cc41SMarek Vasut 	{ 0x45, 0x0000, 0 },
695036cc41SMarek Vasut 	{ 0x46, 0xef00, 0 },
705036cc41SMarek Vasut 	{ 0x47, 0x013f, 0 },
715036cc41SMarek Vasut 	{ 0x48, 0x0000, 0 },
725036cc41SMarek Vasut 	{ 0x07, 0x0015, 30 },
735036cc41SMarek Vasut 
745036cc41SMarek Vasut 	{ 0x07, 0x0017, 0 },
755036cc41SMarek Vasut 
765036cc41SMarek Vasut 	{ 0x20, 0x0000, 0 },
775036cc41SMarek Vasut 	{ 0x21, 0x0000, 0 },
785036cc41SMarek Vasut 	{ 0x22, 0x0000, 0 }
795036cc41SMarek Vasut };
805036cc41SMarek Vasut 
815036cc41SMarek Vasut static struct lms283gf05_seq disp_pdwnseq[] = {
825036cc41SMarek Vasut 	{ 0x07, 0x0016, 30 },
835036cc41SMarek Vasut 
845036cc41SMarek Vasut 	{ 0x07, 0x0004, 0 },
855036cc41SMarek Vasut 	{ 0x10, 0x0220, 20 },
865036cc41SMarek Vasut 
875036cc41SMarek Vasut 	{ 0x13, 0x0060, 50 },
885036cc41SMarek Vasut 
895036cc41SMarek Vasut 	{ 0x13, 0x0040, 50 },
905036cc41SMarek Vasut 
915036cc41SMarek Vasut 	{ 0x13, 0x0000, 0 },
925036cc41SMarek Vasut 	{ 0x10, 0x0000, 0 }
935036cc41SMarek Vasut };
945036cc41SMarek Vasut 
955036cc41SMarek Vasut 
965036cc41SMarek Vasut static void lms283gf05_reset(unsigned long gpio, bool inverted)
975036cc41SMarek Vasut {
985036cc41SMarek Vasut 	gpio_set_value(gpio, !inverted);
995036cc41SMarek Vasut 	mdelay(100);
1005036cc41SMarek Vasut 	gpio_set_value(gpio, inverted);
1015036cc41SMarek Vasut 	mdelay(20);
1025036cc41SMarek Vasut 	gpio_set_value(gpio, !inverted);
1035036cc41SMarek Vasut 	mdelay(20);
1045036cc41SMarek Vasut }
1055036cc41SMarek Vasut 
1065036cc41SMarek Vasut static void lms283gf05_toggle(struct spi_device *spi,
1075036cc41SMarek Vasut 			struct lms283gf05_seq *seq, int sz)
1085036cc41SMarek Vasut {
1095036cc41SMarek Vasut 	char buf[3];
1105036cc41SMarek Vasut 	int i;
1115036cc41SMarek Vasut 
1125036cc41SMarek Vasut 	for (i = 0; i < sz; i++) {
1135036cc41SMarek Vasut 		buf[0] = 0x74;
1145036cc41SMarek Vasut 		buf[1] = 0x00;
1155036cc41SMarek Vasut 		buf[2] = seq[i].reg;
1165036cc41SMarek Vasut 		spi_write(spi, buf, 3);
1175036cc41SMarek Vasut 
1185036cc41SMarek Vasut 		buf[0] = 0x76;
1195036cc41SMarek Vasut 		buf[1] = seq[i].value >> 8;
1205036cc41SMarek Vasut 		buf[2] = seq[i].value & 0xff;
1215036cc41SMarek Vasut 		spi_write(spi, buf, 3);
1225036cc41SMarek Vasut 
1235036cc41SMarek Vasut 		mdelay(seq[i].delay);
1245036cc41SMarek Vasut 	}
1255036cc41SMarek Vasut }
1265036cc41SMarek Vasut 
1275036cc41SMarek Vasut static int lms283gf05_power_set(struct lcd_device *ld, int power)
1285036cc41SMarek Vasut {
1295036cc41SMarek Vasut 	struct lms283gf05_state *st = lcd_get_data(ld);
1305036cc41SMarek Vasut 	struct spi_device *spi = st->spi;
1315036cc41SMarek Vasut 	struct lms283gf05_pdata *pdata = spi->dev.platform_data;
1325036cc41SMarek Vasut 
1336bde9082SMarek Vasut 	if (power <= FB_BLANK_NORMAL) {
1345036cc41SMarek Vasut 		if (pdata)
1355036cc41SMarek Vasut 			lms283gf05_reset(pdata->reset_gpio,
1365036cc41SMarek Vasut 					pdata->reset_inverted);
1375036cc41SMarek Vasut 		lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
1385036cc41SMarek Vasut 	} else {
1395036cc41SMarek Vasut 		lms283gf05_toggle(spi, disp_pdwnseq, ARRAY_SIZE(disp_pdwnseq));
1405036cc41SMarek Vasut 		if (pdata)
1415036cc41SMarek Vasut 			gpio_set_value(pdata->reset_gpio,
1425036cc41SMarek Vasut 					pdata->reset_inverted);
1435036cc41SMarek Vasut 	}
1445036cc41SMarek Vasut 
1455036cc41SMarek Vasut 	return 0;
1465036cc41SMarek Vasut }
1475036cc41SMarek Vasut 
1485036cc41SMarek Vasut static struct lcd_ops lms_ops = {
1495036cc41SMarek Vasut 	.set_power	= lms283gf05_power_set,
1505036cc41SMarek Vasut 	.get_power	= NULL,
1515036cc41SMarek Vasut };
1525036cc41SMarek Vasut 
1535036cc41SMarek Vasut static int __devinit lms283gf05_probe(struct spi_device *spi)
1545036cc41SMarek Vasut {
1555036cc41SMarek Vasut 	struct lms283gf05_state *st;
1565036cc41SMarek Vasut 	struct lms283gf05_pdata *pdata = spi->dev.platform_data;
1575036cc41SMarek Vasut 	struct lcd_device *ld;
1585036cc41SMarek Vasut 	int ret = 0;
1595036cc41SMarek Vasut 
1605036cc41SMarek Vasut 	if (pdata != NULL) {
161*04e961fbSJingoo Han 		ret = devm_gpio_request(&spi->dev, pdata->reset_gpio,
162*04e961fbSJingoo Han 					"LMS285GF05 RESET");
1635036cc41SMarek Vasut 		if (ret)
1645036cc41SMarek Vasut 			return ret;
1655036cc41SMarek Vasut 
1665036cc41SMarek Vasut 		ret = gpio_direction_output(pdata->reset_gpio,
1675036cc41SMarek Vasut 						!pdata->reset_inverted);
1685036cc41SMarek Vasut 		if (ret)
169*04e961fbSJingoo Han 			return ret;
1705036cc41SMarek Vasut 	}
1715036cc41SMarek Vasut 
17226f2b35cSJingoo Han 	st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
17326f2b35cSJingoo Han 				GFP_KERNEL);
1745036cc41SMarek Vasut 	if (st == NULL) {
1755036cc41SMarek Vasut 		dev_err(&spi->dev, "No memory for device state\n");
176*04e961fbSJingoo Han 		return -ENOMEM;
1775036cc41SMarek Vasut 	}
1785036cc41SMarek Vasut 
1795036cc41SMarek Vasut 	ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops);
180*04e961fbSJingoo Han 	if (IS_ERR(ld))
181*04e961fbSJingoo Han 		return PTR_ERR(ld);
1825036cc41SMarek Vasut 
1835036cc41SMarek Vasut 	st->spi = spi;
1845036cc41SMarek Vasut 	st->ld = ld;
1855036cc41SMarek Vasut 
1865036cc41SMarek Vasut 	dev_set_drvdata(&spi->dev, st);
1875036cc41SMarek Vasut 
1885036cc41SMarek Vasut 	/* kick in the LCD */
1895036cc41SMarek Vasut 	if (pdata)
1905036cc41SMarek Vasut 		lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted);
1915036cc41SMarek Vasut 	lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
1925036cc41SMarek Vasut 
1935036cc41SMarek Vasut 	return 0;
1945036cc41SMarek Vasut }
1955036cc41SMarek Vasut 
1965036cc41SMarek Vasut static int __devexit lms283gf05_remove(struct spi_device *spi)
1975036cc41SMarek Vasut {
1985036cc41SMarek Vasut 	struct lms283gf05_state *st = dev_get_drvdata(&spi->dev);
1995036cc41SMarek Vasut 
2005036cc41SMarek Vasut 	lcd_device_unregister(st->ld);
2015036cc41SMarek Vasut 
2025036cc41SMarek Vasut 	return 0;
2035036cc41SMarek Vasut }
2045036cc41SMarek Vasut 
2055036cc41SMarek Vasut static struct spi_driver lms283gf05_driver = {
2065036cc41SMarek Vasut 	.driver = {
2075036cc41SMarek Vasut 		.name	= "lms283gf05",
2085036cc41SMarek Vasut 		.owner	= THIS_MODULE,
2095036cc41SMarek Vasut 	},
2105036cc41SMarek Vasut 	.probe		= lms283gf05_probe,
2115036cc41SMarek Vasut 	.remove		= __devexit_p(lms283gf05_remove),
2125036cc41SMarek Vasut };
2135036cc41SMarek Vasut 
214462dd838SAxel Lin module_spi_driver(lms283gf05_driver);
2155036cc41SMarek Vasut 
2165036cc41SMarek Vasut MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
2175036cc41SMarek Vasut MODULE_DESCRIPTION("LCD283GF05 LCD");
2185036cc41SMarek Vasut MODULE_LICENSE("GPL v2");
219