1cccb6d3cSBen Dooks /* drivers/video/backlight/vgg2432a4.c
2cccb6d3cSBen Dooks  *
3cccb6d3cSBen Dooks  * VGG2432A4 (ILI9320) LCD controller driver.
4cccb6d3cSBen Dooks  *
5cccb6d3cSBen Dooks  * Copyright 2007 Simtec Electronics
6cccb6d3cSBen Dooks  *	http://armlinux.simtec.co.uk/
7cccb6d3cSBen Dooks  *	Ben Dooks <ben@simtec.co.uk>
8cccb6d3cSBen Dooks  *
9cccb6d3cSBen Dooks  * This program is free software; you can redistribute it and/or modify
10cccb6d3cSBen Dooks  * it under the terms of the GNU General Public License version 2 as
11cccb6d3cSBen Dooks  * published by the Free Software Foundation.
12cccb6d3cSBen Dooks */
13cccb6d3cSBen Dooks 
14cccb6d3cSBen Dooks #include <linux/delay.h>
15cccb6d3cSBen Dooks #include <linux/err.h>
16cccb6d3cSBen Dooks #include <linux/fb.h>
17cccb6d3cSBen Dooks #include <linux/init.h>
18cccb6d3cSBen Dooks #include <linux/lcd.h>
19cccb6d3cSBen Dooks #include <linux/module.h>
20cccb6d3cSBen Dooks 
21cccb6d3cSBen Dooks #include <linux/spi/spi.h>
22cccb6d3cSBen Dooks 
23cccb6d3cSBen Dooks #include <video/ili9320.h>
24cccb6d3cSBen Dooks 
25cccb6d3cSBen Dooks #include "ili9320.h"
26cccb6d3cSBen Dooks 
27cccb6d3cSBen Dooks /* Device initialisation sequences */
28cccb6d3cSBen Dooks 
29cccb6d3cSBen Dooks static struct ili9320_reg vgg_init1[] = {
30cccb6d3cSBen Dooks 	{
31cccb6d3cSBen Dooks 		.address = ILI9320_POWER1,
32cccb6d3cSBen Dooks 		.value	 = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0),
33cccb6d3cSBen Dooks 	}, {
34cccb6d3cSBen Dooks 		.address = ILI9320_POWER2,
35cccb6d3cSBen Dooks 		.value	 = (ILI9320_POWER2_VC(7) |
36cccb6d3cSBen Dooks 			    ILI9320_POWER2_DC0(0) | ILI9320_POWER2_DC1(0)),
37cccb6d3cSBen Dooks 	}, {
38cccb6d3cSBen Dooks 		.address = ILI9320_POWER3,
39cccb6d3cSBen Dooks 		.value	 = ILI9320_POWER3_VRH(0),
40cccb6d3cSBen Dooks 	}, {
41cccb6d3cSBen Dooks 		.address = ILI9320_POWER4,
42cccb6d3cSBen Dooks 		.value	 = ILI9320_POWER4_VREOUT(0),
43cccb6d3cSBen Dooks 	},
44cccb6d3cSBen Dooks };
45cccb6d3cSBen Dooks 
46cccb6d3cSBen Dooks static struct ili9320_reg vgg_init2[] = {
47cccb6d3cSBen Dooks 	{
48cccb6d3cSBen Dooks 		.address = ILI9320_POWER1,
49cccb6d3cSBen Dooks 		.value   = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE |
50cccb6d3cSBen Dooks 			    ILI9320_POWER1_BT(7) | ILI9320_POWER1_SAP),
51cccb6d3cSBen Dooks 	}, {
52cccb6d3cSBen Dooks 		.address = ILI9320_POWER2,
53cccb6d3cSBen Dooks 		.value   = ILI9320_POWER2_VC(7) | ILI9320_POWER2_DC0(3),
54cccb6d3cSBen Dooks 	}
55cccb6d3cSBen Dooks };
56cccb6d3cSBen Dooks 
57cccb6d3cSBen Dooks static struct ili9320_reg vgg_gamma[] = {
58cccb6d3cSBen Dooks 	{
59cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA1,
60cccb6d3cSBen Dooks 		.value	 = 0x0000,
61cccb6d3cSBen Dooks 	}, {
62cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA2,
63cccb6d3cSBen Dooks 		.value   = 0x0505,
64cccb6d3cSBen Dooks 	}, {
65cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA3,
66cccb6d3cSBen Dooks 		.value	 = 0x0004,
67cccb6d3cSBen Dooks 	}, {
68cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA4,
69cccb6d3cSBen Dooks 		.value	 = 0x0006,
70cccb6d3cSBen Dooks 	}, {
71cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA5,
72cccb6d3cSBen Dooks 		.value	 = 0x0707,
73cccb6d3cSBen Dooks 	}, {
74cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA6,
75cccb6d3cSBen Dooks 		.value	 = 0x0105,
76cccb6d3cSBen Dooks 	}, {
77cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA7,
78cccb6d3cSBen Dooks 		.value	 = 0x0002,
79cccb6d3cSBen Dooks 	}, {
80cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA8,
81cccb6d3cSBen Dooks 		.value	 = 0x0707,
82cccb6d3cSBen Dooks 	}, {
83cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA9,
84cccb6d3cSBen Dooks 		.value	 = 0x0704,
85cccb6d3cSBen Dooks 	}, {
86cccb6d3cSBen Dooks 		.address = ILI9320_GAMMA10,
87cccb6d3cSBen Dooks 		.value	 = 0x807,
88cccb6d3cSBen Dooks 	}
89cccb6d3cSBen Dooks 
90cccb6d3cSBen Dooks };
91cccb6d3cSBen Dooks 
92cccb6d3cSBen Dooks static struct ili9320_reg vgg_init0[] = {
93cccb6d3cSBen Dooks 	[0]	= {
94cccb6d3cSBen Dooks 		/* set direction and scan mode gate */
95cccb6d3cSBen Dooks 		.address = ILI9320_DRIVER,
96cccb6d3cSBen Dooks 		.value	 = ILI9320_DRIVER_SS,
97cccb6d3cSBen Dooks 	}, {
98cccb6d3cSBen Dooks 		.address = ILI9320_DRIVEWAVE,
99cccb6d3cSBen Dooks 		.value	 = (ILI9320_DRIVEWAVE_MUSTSET |
100cccb6d3cSBen Dooks 			    ILI9320_DRIVEWAVE_EOR | ILI9320_DRIVEWAVE_BC),
101cccb6d3cSBen Dooks 	}, {
102cccb6d3cSBen Dooks 		.address = ILI9320_ENTRYMODE,
103cccb6d3cSBen Dooks 		.value	 = ILI9320_ENTRYMODE_ID(3) | ILI9320_ENTRYMODE_BGR,
104cccb6d3cSBen Dooks 	}, {
105cccb6d3cSBen Dooks 		.address = ILI9320_RESIZING,
106cccb6d3cSBen Dooks 		.value	 = 0x0,
107cccb6d3cSBen Dooks 	},
108cccb6d3cSBen Dooks };
109cccb6d3cSBen Dooks 
110cccb6d3cSBen Dooks 
111cccb6d3cSBen Dooks static int vgg2432a4_lcd_init(struct ili9320 *lcd,
112cccb6d3cSBen Dooks 			      struct ili9320_platdata *cfg)
113cccb6d3cSBen Dooks {
114cccb6d3cSBen Dooks 	unsigned int addr;
115cccb6d3cSBen Dooks 	int ret;
116cccb6d3cSBen Dooks 
117cccb6d3cSBen Dooks 	/* Set VCore before anything else (VGG243237-6UFLWA) */
118cccb6d3cSBen Dooks 	ret = ili9320_write(lcd, 0x00e5, 0x8000);
119cccb6d3cSBen Dooks 	if (ret)
120cccb6d3cSBen Dooks 		goto err_initial;
121cccb6d3cSBen Dooks 
122cccb6d3cSBen Dooks 	/* Start the oscillator up before we can do anything else. */
123cccb6d3cSBen Dooks 	ret = ili9320_write(lcd, ILI9320_OSCILATION, ILI9320_OSCILATION_OSC);
124cccb6d3cSBen Dooks 	if (ret)
125cccb6d3cSBen Dooks 		goto err_initial;
126cccb6d3cSBen Dooks 
127cccb6d3cSBen Dooks 	/* must wait at-lesat 10ms after starting */
128cccb6d3cSBen Dooks 	mdelay(15);
129cccb6d3cSBen Dooks 
130cccb6d3cSBen Dooks 	ret = ili9320_write_regs(lcd, vgg_init0, ARRAY_SIZE(vgg_init0));
131cccb6d3cSBen Dooks 	if (ret != 0)
132cccb6d3cSBen Dooks 		goto err_initial;
133cccb6d3cSBen Dooks 
134cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_DISPLAY2, cfg->display2);
135cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_DISPLAY3, cfg->display3);
136cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_DISPLAY4, cfg->display4);
137cccb6d3cSBen Dooks 
138cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1);
139cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0);
140866bbdbaSDenis V. Lunev 	ili9320_write(lcd, ILI9320_RGB_IF2, cfg->rgb_if2);
141cccb6d3cSBen Dooks 
142cccb6d3cSBen Dooks 	ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1));
143cccb6d3cSBen Dooks 	if (ret != 0)
144cccb6d3cSBen Dooks 		goto err_vgg;
145cccb6d3cSBen Dooks 
146cccb6d3cSBen Dooks 	mdelay(300);
147cccb6d3cSBen Dooks 
148cccb6d3cSBen Dooks 	ret = ili9320_write_regs(lcd, vgg_init2, ARRAY_SIZE(vgg_init2));
149cccb6d3cSBen Dooks 	if (ret != 0)
150cccb6d3cSBen Dooks 		goto err_vgg2;
151cccb6d3cSBen Dooks 
152cccb6d3cSBen Dooks 	mdelay(100);
153cccb6d3cSBen Dooks 
154cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_POWER3, 0x13c);
155cccb6d3cSBen Dooks 
156cccb6d3cSBen Dooks 	mdelay(100);
157cccb6d3cSBen Dooks 
158cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_POWER4, 0x1c00);
159cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_POWER7, 0x000e);
160cccb6d3cSBen Dooks 
161cccb6d3cSBen Dooks 	mdelay(100);
162cccb6d3cSBen Dooks 
163cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_GRAM_HORIZ_ADDR, 0x00);
164cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_GRAM_VERT_ADD, 0x00);
165cccb6d3cSBen Dooks 
166cccb6d3cSBen Dooks 	ret = ili9320_write_regs(lcd, vgg_gamma, ARRAY_SIZE(vgg_gamma));
167cccb6d3cSBen Dooks 	if (ret != 0)
168cccb6d3cSBen Dooks 		goto err_vgg3;
169cccb6d3cSBen Dooks 
170cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_HORIZ_START, 0x0);
171cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_HORIZ_END, cfg->hsize - 1);
172cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_VERT_START, 0x0);
173cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_VERT_END, cfg->vsize - 1);
174cccb6d3cSBen Dooks 
175cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_DRIVER2,
176cccb6d3cSBen Dooks 		      ILI9320_DRIVER2_NL(((cfg->vsize - 240) / 8) + 0x1D));
177cccb6d3cSBen Dooks 
178cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_BASE_IMAGE, 0x1);
179cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_VERT_SCROLL, 0x00);
180cccb6d3cSBen Dooks 
181cccb6d3cSBen Dooks 	for (addr = ILI9320_PARTIAL1_POSITION; addr <= ILI9320_PARTIAL2_END;
182cccb6d3cSBen Dooks 	     addr++) {
183cccb6d3cSBen Dooks 		ili9320_write(lcd, addr, 0x0);
184cccb6d3cSBen Dooks 	}
185cccb6d3cSBen Dooks 
186cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE1, 0x10);
187cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE2, cfg->interface2);
188cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE3, cfg->interface3);
189cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE4, cfg->interface4);
190cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE5, cfg->interface5);
191cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_INTERFACE6, cfg->interface6);
192cccb6d3cSBen Dooks 
193cccb6d3cSBen Dooks 	lcd->display1 = (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_DTE |
194cccb6d3cSBen Dooks 			 ILI9320_DISPLAY1_GON | ILI9320_DISPLAY1_BASEE |
195cccb6d3cSBen Dooks 			 0x40);
196cccb6d3cSBen Dooks 
197cccb6d3cSBen Dooks 	ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
198cccb6d3cSBen Dooks 
199cccb6d3cSBen Dooks 	return 0;
200cccb6d3cSBen Dooks 
201cccb6d3cSBen Dooks  err_vgg3:
202cccb6d3cSBen Dooks  err_vgg2:
203cccb6d3cSBen Dooks  err_vgg:
204cccb6d3cSBen Dooks  err_initial:
205cccb6d3cSBen Dooks 	return ret;
206cccb6d3cSBen Dooks }
207cccb6d3cSBen Dooks 
208cccb6d3cSBen Dooks #ifdef CONFIG_PM
209cccb6d3cSBen Dooks static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state)
210cccb6d3cSBen Dooks {
211cccb6d3cSBen Dooks 	return ili9320_suspend(dev_get_drvdata(&spi->dev), state);
212cccb6d3cSBen Dooks }
213cccb6d3cSBen Dooks 
214cccb6d3cSBen Dooks static int vgg2432a4_resume(struct spi_device *spi)
215cccb6d3cSBen Dooks {
216cccb6d3cSBen Dooks 	return ili9320_resume(dev_get_drvdata(&spi->dev));
217cccb6d3cSBen Dooks }
218cccb6d3cSBen Dooks #else
219cccb6d3cSBen Dooks #define vgg2432a4_suspend	NULL
220cccb6d3cSBen Dooks #define vgg2432a4_resume 	NULL
221cccb6d3cSBen Dooks #endif
222cccb6d3cSBen Dooks 
223cccb6d3cSBen Dooks static struct ili9320_client vgg2432a4_client = {
224cccb6d3cSBen Dooks 	.name	= "VGG2432A4",
225cccb6d3cSBen Dooks 	.init	= vgg2432a4_lcd_init,
226cccb6d3cSBen Dooks };
227cccb6d3cSBen Dooks 
228cccb6d3cSBen Dooks /* Device probe */
229cccb6d3cSBen Dooks 
230cccb6d3cSBen Dooks static int __devinit vgg2432a4_probe(struct spi_device *spi)
231cccb6d3cSBen Dooks {
232cccb6d3cSBen Dooks 	int ret;
233cccb6d3cSBen Dooks 
234cccb6d3cSBen Dooks 	ret = ili9320_probe_spi(spi, &vgg2432a4_client);
235cccb6d3cSBen Dooks 	if (ret != 0) {
236cccb6d3cSBen Dooks 		dev_err(&spi->dev, "failed to initialise ili9320\n");
237cccb6d3cSBen Dooks 		return ret;
238cccb6d3cSBen Dooks 	}
239cccb6d3cSBen Dooks 
240cccb6d3cSBen Dooks 	return 0;
241cccb6d3cSBen Dooks }
242cccb6d3cSBen Dooks 
243cccb6d3cSBen Dooks static int __devexit vgg2432a4_remove(struct spi_device *spi)
244cccb6d3cSBen Dooks {
245cccb6d3cSBen Dooks 	return ili9320_remove(dev_get_drvdata(&spi->dev));
246cccb6d3cSBen Dooks }
247cccb6d3cSBen Dooks 
248cccb6d3cSBen Dooks static void vgg2432a4_shutdown(struct spi_device *spi)
249cccb6d3cSBen Dooks {
250cccb6d3cSBen Dooks 	ili9320_shutdown(dev_get_drvdata(&spi->dev));
251cccb6d3cSBen Dooks }
252cccb6d3cSBen Dooks 
253cccb6d3cSBen Dooks static struct spi_driver vgg2432a4_driver = {
254cccb6d3cSBen Dooks 	.driver = {
255cccb6d3cSBen Dooks 		.name		= "VGG2432A4",
256cccb6d3cSBen Dooks 		.owner		= THIS_MODULE,
257cccb6d3cSBen Dooks 	},
258cccb6d3cSBen Dooks 	.probe		= vgg2432a4_probe,
259d1723fa2SBill Pemberton 	.remove		= vgg2432a4_remove,
260cccb6d3cSBen Dooks 	.shutdown	= vgg2432a4_shutdown,
261cccb6d3cSBen Dooks 	.suspend	= vgg2432a4_suspend,
262cccb6d3cSBen Dooks 	.resume		= vgg2432a4_resume,
263cccb6d3cSBen Dooks };
264cccb6d3cSBen Dooks 
265462dd838SAxel Lin module_spi_driver(vgg2432a4_driver);
266cccb6d3cSBen Dooks 
267cccb6d3cSBen Dooks MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
268cccb6d3cSBen Dooks MODULE_DESCRIPTION("VGG2432A4 LCD Driver");
269cccb6d3cSBen Dooks MODULE_LICENSE("GPL v2");
270e0626e38SAnton Vorontsov MODULE_ALIAS("spi:VGG2432A4");
271