diff options
author | Stefano Babic <sbabic@denx.de> | 2013-04-29 19:17:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-29 21:28:17 -0400 |
commit | 4cfbfa9714789d4bed3ea93859e36c2e04ac4b30 (patch) | |
tree | dd4d1d11279daea6c19981583cde16e3fd431992 /drivers/video/backlight | |
parent | 359177e0606d9b80d89f877ddd36d82b32a0cb72 (diff) |
video: backlight: add ili922x lcd driver
Add LCD driver for Ilitek ILI9221/ILI9222 controller. The driver uses
SPI interface for controller access and configuration and RGB interface
for graphics data transfer.
Signed-off-by: Stefano Babic <sbabic@denx.de>
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r-- | drivers/video/backlight/Kconfig | 7 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/ili922x.c | 555 |
3 files changed, 563 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a4481dfadfd5..2e166c3fc4c3 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -59,6 +59,13 @@ config LCD_LTV350QV | |||
59 | 59 | ||
60 | The LTV350QV panel is present on all ATSTK1000 boards. | 60 | The LTV350QV panel is present on all ATSTK1000 boards. |
61 | 61 | ||
62 | config LCD_ILI922X | ||
63 | tristate "ILI Technology ILI9221/ILI9222 support" | ||
64 | depends on SPI | ||
65 | help | ||
66 | If you have a panel based on the ILI9221/9222 controller | ||
67 | chip then say y to include a driver for it. | ||
68 | |||
62 | config LCD_ILI9320 | 69 | config LCD_ILI9320 |
63 | tristate "ILI Technology ILI9320 controller support" | 70 | tristate "ILI Technology ILI9320 controller support" |
64 | depends on SPI | 71 | depends on SPI |
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 96c4d620c5ce..92711fe60464 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o | |||
5 | obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o | 5 | obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o |
6 | obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o | 6 | obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o |
7 | obj-$(CONFIG_LCD_HX8357) += hx8357.o | 7 | obj-$(CONFIG_LCD_HX8357) += hx8357.o |
8 | obj-$(CONFIG_LCD_ILI922X) += ili922x.o | ||
8 | obj-$(CONFIG_LCD_ILI9320) += ili9320.o | 9 | obj-$(CONFIG_LCD_ILI9320) += ili9320.o |
9 | obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o | 10 | obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o |
10 | obj-$(CONFIG_LCD_LD9040) += ld9040.o | 11 | obj-$(CONFIG_LCD_LD9040) += ld9040.o |
diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c new file mode 100644 index 000000000000..8b8465ed0014 --- /dev/null +++ b/drivers/video/backlight/ili922x.c | |||
@@ -0,0 +1,555 @@ | |||
1 | /* | ||
2 | * (C) Copyright 2008 | ||
3 | * Stefano Babic, DENX Software Engineering, sbabic@denx.de. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This driver implements a lcd device for the ILITEK 922x display | ||
11 | * controller. The interface to the display is SPI and the display's | ||
12 | * memory is cyclically updated over the RGB interface. | ||
13 | */ | ||
14 | |||
15 | #include <linux/fb.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/lcd.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/string.h> | ||
26 | |||
27 | /* Register offset, see manual section 8.2 */ | ||
28 | #define REG_START_OSCILLATION 0x00 | ||
29 | #define REG_DRIVER_CODE_READ 0x00 | ||
30 | #define REG_DRIVER_OUTPUT_CONTROL 0x01 | ||
31 | #define REG_LCD_AC_DRIVEING_CONTROL 0x02 | ||
32 | #define REG_ENTRY_MODE 0x03 | ||
33 | #define REG_COMPARE_1 0x04 | ||
34 | #define REG_COMPARE_2 0x05 | ||
35 | #define REG_DISPLAY_CONTROL_1 0x07 | ||
36 | #define REG_DISPLAY_CONTROL_2 0x08 | ||
37 | #define REG_DISPLAY_CONTROL_3 0x09 | ||
38 | #define REG_FRAME_CYCLE_CONTROL 0x0B | ||
39 | #define REG_EXT_INTF_CONTROL 0x0C | ||
40 | #define REG_POWER_CONTROL_1 0x10 | ||
41 | #define REG_POWER_CONTROL_2 0x11 | ||
42 | #define REG_POWER_CONTROL_3 0x12 | ||
43 | #define REG_POWER_CONTROL_4 0x13 | ||
44 | #define REG_RAM_ADDRESS_SET 0x21 | ||
45 | #define REG_WRITE_DATA_TO_GRAM 0x22 | ||
46 | #define REG_RAM_WRITE_MASK1 0x23 | ||
47 | #define REG_RAM_WRITE_MASK2 0x24 | ||
48 | #define REG_GAMMA_CONTROL_1 0x30 | ||
49 | #define REG_GAMMA_CONTROL_2 0x31 | ||
50 | #define REG_GAMMA_CONTROL_3 0x32 | ||
51 | #define REG_GAMMA_CONTROL_4 0x33 | ||
52 | #define REG_GAMMA_CONTROL_5 0x34 | ||
53 | #define REG_GAMMA_CONTROL_6 0x35 | ||
54 | #define REG_GAMMA_CONTROL_7 0x36 | ||
55 | #define REG_GAMMA_CONTROL_8 0x37 | ||
56 | #define REG_GAMMA_CONTROL_9 0x38 | ||
57 | #define REG_GAMMA_CONTROL_10 0x39 | ||
58 | #define REG_GATE_SCAN_CONTROL 0x40 | ||
59 | #define REG_VERT_SCROLL_CONTROL 0x41 | ||
60 | #define REG_FIRST_SCREEN_DRIVE_POS 0x42 | ||
61 | #define REG_SECOND_SCREEN_DRIVE_POS 0x43 | ||
62 | #define REG_RAM_ADDR_POS_H 0x44 | ||
63 | #define REG_RAM_ADDR_POS_V 0x45 | ||
64 | #define REG_OSCILLATOR_CONTROL 0x4F | ||
65 | #define REG_GPIO 0x60 | ||
66 | #define REG_OTP_VCM_PROGRAMMING 0x61 | ||
67 | #define REG_OTP_VCM_STATUS_ENABLE 0x62 | ||
68 | #define REG_OTP_PROGRAMMING_ID_KEY 0x65 | ||
69 | |||
70 | /* | ||
71 | * maximum frequency for register access | ||
72 | * (not for the GRAM access) | ||
73 | */ | ||
74 | #define ILITEK_MAX_FREQ_REG 4000000 | ||
75 | |||
76 | /* | ||
77 | * Device ID as found in the datasheet (supports 9221 and 9222) | ||
78 | */ | ||
79 | #define ILITEK_DEVICE_ID 0x9220 | ||
80 | #define ILITEK_DEVICE_ID_MASK 0xFFF0 | ||
81 | |||
82 | /* Last two bits in the START BYTE */ | ||
83 | #define START_RS_INDEX 0 | ||
84 | #define START_RS_REG 1 | ||
85 | #define START_RW_WRITE 0 | ||
86 | #define START_RW_READ 1 | ||
87 | |||
88 | /** | ||
89 | * START_BYTE(id, rs, rw) | ||
90 | * | ||
91 | * Set the start byte according to the required operation. | ||
92 | * The start byte is defined as: | ||
93 | * ---------------------------------- | ||
94 | * | 0 | 1 | 1 | 1 | 0 | ID | RS | RW | | ||
95 | * ---------------------------------- | ||
96 | * @id: display's id as set by the manufacturer | ||
97 | * @rs: operation type bit, one of: | ||
98 | * - START_RS_INDEX set the index register | ||
99 | * - START_RS_REG write/read registers/GRAM | ||
100 | * @rw: read/write operation | ||
101 | * - START_RW_WRITE write | ||
102 | * - START_RW_READ read | ||
103 | */ | ||
104 | #define START_BYTE(id, rs, rw) \ | ||
105 | (0x70 | (((id) & 0x01) << 2) | (((rs) & 0x01) << 1) | ((rw) & 0x01)) | ||
106 | |||
107 | /** | ||
108 | * CHECK_FREQ_REG(spi_device s, spi_transfer x) - Check the frequency | ||
109 | * for the SPI transfer. According to the datasheet, the controller | ||
110 | * accept higher frequency for the GRAM transfer, but it requires | ||
111 | * lower frequency when the registers are read/written. | ||
112 | * The macro sets the frequency in the spi_transfer structure if | ||
113 | * the frequency exceeds the maximum value. | ||
114 | */ | ||
115 | #define CHECK_FREQ_REG(s, x) \ | ||
116 | do { \ | ||
117 | if (s->max_speed_hz > ILITEK_MAX_FREQ_REG) \ | ||
118 | ((struct spi_transfer *)x)->speed_hz = \ | ||
119 | ILITEK_MAX_FREQ_REG; \ | ||
120 | } while (0) | ||
121 | |||
122 | #define CMD_BUFSIZE 16 | ||
123 | |||
124 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) | ||
125 | |||
126 | #define set_tx_byte(b) (tx_invert ? ~(b) : b) | ||
127 | |||
128 | /** | ||
129 | * ili922x_id - id as set by manufacturer | ||
130 | */ | ||
131 | static int ili922x_id = 1; | ||
132 | module_param(ili922x_id, int, 0); | ||
133 | |||
134 | static int tx_invert; | ||
135 | module_param(tx_invert, int, 0); | ||
136 | |||
137 | /** | ||
138 | * driver's private structure | ||
139 | */ | ||
140 | struct ili922x { | ||
141 | struct spi_device *spi; | ||
142 | struct lcd_device *ld; | ||
143 | int power; | ||
144 | }; | ||
145 | |||
146 | /** | ||
147 | * ili922x_read_status - read status register from display | ||
148 | * @spi: spi device | ||
149 | * @rs: output value | ||
150 | */ | ||
151 | static int ili922x_read_status(struct spi_device *spi, u16 *rs) | ||
152 | { | ||
153 | struct spi_message msg; | ||
154 | struct spi_transfer xfer; | ||
155 | unsigned char tbuf[CMD_BUFSIZE]; | ||
156 | unsigned char rbuf[CMD_BUFSIZE]; | ||
157 | int ret, i; | ||
158 | |||
159 | memset(&xfer, 0, sizeof(struct spi_transfer)); | ||
160 | spi_message_init(&msg); | ||
161 | xfer.tx_buf = tbuf; | ||
162 | xfer.rx_buf = rbuf; | ||
163 | xfer.cs_change = 1; | ||
164 | CHECK_FREQ_REG(spi, &xfer); | ||
165 | |||
166 | tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX, | ||
167 | START_RW_READ)); | ||
168 | /* | ||
169 | * we need 4-byte xfer here due to invalid dummy byte | ||
170 | * received after start byte | ||
171 | */ | ||
172 | for (i = 1; i < 4; i++) | ||
173 | tbuf[i] = set_tx_byte(0); /* dummy */ | ||
174 | |||
175 | xfer.bits_per_word = 8; | ||
176 | xfer.len = 4; | ||
177 | spi_message_add_tail(&xfer, &msg); | ||
178 | ret = spi_sync(spi, &msg); | ||
179 | if (ret < 0) { | ||
180 | dev_dbg(&spi->dev, "Error sending SPI message 0x%x", ret); | ||
181 | return ret; | ||
182 | } | ||
183 | |||
184 | *rs = (rbuf[2] << 8) + rbuf[3]; | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * ili922x_read - read register from display | ||
190 | * @spi: spi device | ||
191 | * @reg: offset of the register to be read | ||
192 | * @rx: output value | ||
193 | */ | ||
194 | static int ili922x_read(struct spi_device *spi, u8 reg, u16 *rx) | ||
195 | { | ||
196 | struct spi_message msg; | ||
197 | struct spi_transfer xfer_regindex, xfer_regvalue; | ||
198 | unsigned char tbuf[CMD_BUFSIZE]; | ||
199 | unsigned char rbuf[CMD_BUFSIZE]; | ||
200 | int ret, len = 0, send_bytes; | ||
201 | |||
202 | memset(&xfer_regindex, 0, sizeof(struct spi_transfer)); | ||
203 | memset(&xfer_regvalue, 0, sizeof(struct spi_transfer)); | ||
204 | spi_message_init(&msg); | ||
205 | xfer_regindex.tx_buf = tbuf; | ||
206 | xfer_regindex.rx_buf = rbuf; | ||
207 | xfer_regindex.cs_change = 1; | ||
208 | CHECK_FREQ_REG(spi, &xfer_regindex); | ||
209 | |||
210 | tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX, | ||
211 | START_RW_WRITE)); | ||
212 | tbuf[1] = set_tx_byte(0); | ||
213 | tbuf[2] = set_tx_byte(reg); | ||
214 | xfer_regindex.bits_per_word = 8; | ||
215 | len = xfer_regindex.len = 3; | ||
216 | spi_message_add_tail(&xfer_regindex, &msg); | ||
217 | |||
218 | send_bytes = len; | ||
219 | |||
220 | tbuf[len++] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG, | ||
221 | START_RW_READ)); | ||
222 | tbuf[len++] = set_tx_byte(0); | ||
223 | tbuf[len] = set_tx_byte(0); | ||
224 | |||
225 | xfer_regvalue.cs_change = 1; | ||
226 | xfer_regvalue.len = 3; | ||
227 | xfer_regvalue.tx_buf = &tbuf[send_bytes]; | ||
228 | xfer_regvalue.rx_buf = &rbuf[send_bytes]; | ||
229 | CHECK_FREQ_REG(spi, &xfer_regvalue); | ||
230 | |||
231 | spi_message_add_tail(&xfer_regvalue, &msg); | ||
232 | ret = spi_sync(spi, &msg); | ||
233 | if (ret < 0) { | ||
234 | dev_dbg(&spi->dev, "Error sending SPI message 0x%x", ret); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | *rx = (rbuf[1 + send_bytes] << 8) + rbuf[2 + send_bytes]; | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * ili922x_write - write a controller register | ||
244 | * @spi: struct spi_device * | ||
245 | * @reg: offset of the register to be written | ||
246 | * @value: value to be written | ||
247 | */ | ||
248 | static int ili922x_write(struct spi_device *spi, u8 reg, u16 value) | ||
249 | { | ||
250 | struct spi_message msg; | ||
251 | struct spi_transfer xfer_regindex, xfer_regvalue; | ||
252 | unsigned char tbuf[CMD_BUFSIZE]; | ||
253 | unsigned char rbuf[CMD_BUFSIZE]; | ||
254 | int ret, len = 0; | ||
255 | |||
256 | memset(&xfer_regindex, 0, sizeof(struct spi_transfer)); | ||
257 | memset(&xfer_regvalue, 0, sizeof(struct spi_transfer)); | ||
258 | |||
259 | spi_message_init(&msg); | ||
260 | xfer_regindex.tx_buf = tbuf; | ||
261 | xfer_regindex.rx_buf = rbuf; | ||
262 | xfer_regindex.cs_change = 1; | ||
263 | CHECK_FREQ_REG(spi, &xfer_regindex); | ||
264 | |||
265 | tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_INDEX, | ||
266 | START_RW_WRITE)); | ||
267 | tbuf[1] = set_tx_byte(0); | ||
268 | tbuf[2] = set_tx_byte(reg); | ||
269 | xfer_regindex.bits_per_word = 8; | ||
270 | xfer_regindex.len = 3; | ||
271 | spi_message_add_tail(&xfer_regindex, &msg); | ||
272 | |||
273 | ret = spi_sync(spi, &msg); | ||
274 | |||
275 | spi_message_init(&msg); | ||
276 | len = 0; | ||
277 | tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG, | ||
278 | START_RW_WRITE)); | ||
279 | tbuf[1] = set_tx_byte((value & 0xFF00) >> 8); | ||
280 | tbuf[2] = set_tx_byte(value & 0x00FF); | ||
281 | |||
282 | xfer_regvalue.cs_change = 1; | ||
283 | xfer_regvalue.len = 3; | ||
284 | xfer_regvalue.tx_buf = tbuf; | ||
285 | xfer_regvalue.rx_buf = rbuf; | ||
286 | CHECK_FREQ_REG(spi, &xfer_regvalue); | ||
287 | |||
288 | spi_message_add_tail(&xfer_regvalue, &msg); | ||
289 | |||
290 | ret = spi_sync(spi, &msg); | ||
291 | if (ret < 0) { | ||
292 | dev_err(&spi->dev, "Error sending SPI message 0x%x", ret); | ||
293 | return ret; | ||
294 | } | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | #ifdef DEBUG | ||
299 | /** | ||
300 | * ili922x_reg_dump - dump all registers | ||
301 | */ | ||
302 | static void ili922x_reg_dump(struct spi_device *spi) | ||
303 | { | ||
304 | u8 reg; | ||
305 | u16 rx; | ||
306 | |||
307 | dev_dbg(&spi->dev, "ILI922x configuration registers:\n"); | ||
308 | for (reg = REG_START_OSCILLATION; | ||
309 | reg <= REG_OTP_PROGRAMMING_ID_KEY; reg++) { | ||
310 | ili922x_read(spi, reg, &rx); | ||
311 | dev_dbg(&spi->dev, "reg @ 0x%02X: 0x%04X\n", reg, rx); | ||
312 | } | ||
313 | } | ||
314 | #else | ||
315 | static inline void ili922x_reg_dump(struct spi_device *spi) {} | ||
316 | #endif | ||
317 | |||
318 | /** | ||
319 | * set_write_to_gram_reg - initialize the display to write the GRAM | ||
320 | * @spi: spi device | ||
321 | */ | ||
322 | static void set_write_to_gram_reg(struct spi_device *spi) | ||
323 | { | ||
324 | struct spi_message msg; | ||
325 | struct spi_transfer xfer; | ||
326 | unsigned char tbuf[CMD_BUFSIZE]; | ||
327 | |||
328 | memset(&xfer, 0, sizeof(struct spi_transfer)); | ||
329 | |||
330 | spi_message_init(&msg); | ||
331 | xfer.tx_buf = tbuf; | ||
332 | xfer.rx_buf = NULL; | ||
333 | xfer.cs_change = 1; | ||
334 | |||
335 | tbuf[0] = START_BYTE(ili922x_id, START_RS_INDEX, START_RW_WRITE); | ||
336 | tbuf[1] = 0; | ||
337 | tbuf[2] = REG_WRITE_DATA_TO_GRAM; | ||
338 | |||
339 | xfer.bits_per_word = 8; | ||
340 | xfer.len = 3; | ||
341 | spi_message_add_tail(&xfer, &msg); | ||
342 | spi_sync(spi, &msg); | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * ili922x_poweron - turn the display on | ||
347 | * @spi: spi device | ||
348 | * | ||
349 | * The sequence to turn on the display is taken from | ||
350 | * the datasheet and/or the example code provided by the | ||
351 | * manufacturer. | ||
352 | */ | ||
353 | static int ili922x_poweron(struct spi_device *spi) | ||
354 | { | ||
355 | int ret; | ||
356 | |||
357 | /* Power on */ | ||
358 | ret = ili922x_write(spi, REG_POWER_CONTROL_1, 0x0000); | ||
359 | usleep_range(10000, 10500); | ||
360 | ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000); | ||
361 | ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0000); | ||
362 | msleep(40); | ||
363 | ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x0000); | ||
364 | msleep(40); | ||
365 | /* register 0x56 is not documented in the datasheet */ | ||
366 | ret += ili922x_write(spi, 0x56, 0x080F); | ||
367 | ret += ili922x_write(spi, REG_POWER_CONTROL_1, 0x4240); | ||
368 | usleep_range(10000, 10500); | ||
369 | ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000); | ||
370 | ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0014); | ||
371 | msleep(40); | ||
372 | ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x1319); | ||
373 | msleep(40); | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * ili922x_poweroff - turn the display off | ||
380 | * @spi: spi device | ||
381 | */ | ||
382 | static int ili922x_poweroff(struct spi_device *spi) | ||
383 | { | ||
384 | int ret; | ||
385 | |||
386 | /* Power off */ | ||
387 | ret = ili922x_write(spi, REG_POWER_CONTROL_1, 0x0000); | ||
388 | usleep_range(10000, 10500); | ||
389 | ret += ili922x_write(spi, REG_POWER_CONTROL_2, 0x0000); | ||
390 | ret += ili922x_write(spi, REG_POWER_CONTROL_3, 0x0000); | ||
391 | msleep(40); | ||
392 | ret += ili922x_write(spi, REG_POWER_CONTROL_4, 0x0000); | ||
393 | msleep(40); | ||
394 | |||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | /** | ||
399 | * ili922x_display_init - initialize the display by setting | ||
400 | * the configuration registers | ||
401 | * @spi: spi device | ||
402 | */ | ||
403 | static void ili922x_display_init(struct spi_device *spi) | ||
404 | { | ||
405 | ili922x_write(spi, REG_START_OSCILLATION, 1); | ||
406 | usleep_range(10000, 10500); | ||
407 | ili922x_write(spi, REG_DRIVER_OUTPUT_CONTROL, 0x691B); | ||
408 | ili922x_write(spi, REG_LCD_AC_DRIVEING_CONTROL, 0x0700); | ||
409 | ili922x_write(spi, REG_ENTRY_MODE, 0x1030); | ||
410 | ili922x_write(spi, REG_COMPARE_1, 0x0000); | ||
411 | ili922x_write(spi, REG_COMPARE_2, 0x0000); | ||
412 | ili922x_write(spi, REG_DISPLAY_CONTROL_1, 0x0037); | ||
413 | ili922x_write(spi, REG_DISPLAY_CONTROL_2, 0x0202); | ||
414 | ili922x_write(spi, REG_DISPLAY_CONTROL_3, 0x0000); | ||
415 | ili922x_write(spi, REG_FRAME_CYCLE_CONTROL, 0x0000); | ||
416 | |||
417 | /* Set RGB interface */ | ||
418 | ili922x_write(spi, REG_EXT_INTF_CONTROL, 0x0110); | ||
419 | |||
420 | ili922x_poweron(spi); | ||
421 | |||
422 | ili922x_write(spi, REG_GAMMA_CONTROL_1, 0x0302); | ||
423 | ili922x_write(spi, REG_GAMMA_CONTROL_2, 0x0407); | ||
424 | ili922x_write(spi, REG_GAMMA_CONTROL_3, 0x0304); | ||
425 | ili922x_write(spi, REG_GAMMA_CONTROL_4, 0x0203); | ||
426 | ili922x_write(spi, REG_GAMMA_CONTROL_5, 0x0706); | ||
427 | ili922x_write(spi, REG_GAMMA_CONTROL_6, 0x0407); | ||
428 | ili922x_write(spi, REG_GAMMA_CONTROL_7, 0x0706); | ||
429 | ili922x_write(spi, REG_GAMMA_CONTROL_8, 0x0000); | ||
430 | ili922x_write(spi, REG_GAMMA_CONTROL_9, 0x0C06); | ||
431 | ili922x_write(spi, REG_GAMMA_CONTROL_10, 0x0F00); | ||
432 | ili922x_write(spi, REG_RAM_ADDRESS_SET, 0x0000); | ||
433 | ili922x_write(spi, REG_GATE_SCAN_CONTROL, 0x0000); | ||
434 | ili922x_write(spi, REG_VERT_SCROLL_CONTROL, 0x0000); | ||
435 | ili922x_write(spi, REG_FIRST_SCREEN_DRIVE_POS, 0xDB00); | ||
436 | ili922x_write(spi, REG_SECOND_SCREEN_DRIVE_POS, 0xDB00); | ||
437 | ili922x_write(spi, REG_RAM_ADDR_POS_H, 0xAF00); | ||
438 | ili922x_write(spi, REG_RAM_ADDR_POS_V, 0xDB00); | ||
439 | ili922x_reg_dump(spi); | ||
440 | set_write_to_gram_reg(spi); | ||
441 | } | ||
442 | |||
443 | static int ili922x_lcd_power(struct ili922x *lcd, int power) | ||
444 | { | ||
445 | int ret = 0; | ||
446 | |||
447 | if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) | ||
448 | ret = ili922x_poweron(lcd->spi); | ||
449 | else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) | ||
450 | ret = ili922x_poweroff(lcd->spi); | ||
451 | |||
452 | if (!ret) | ||
453 | lcd->power = power; | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | static int ili922x_set_power(struct lcd_device *ld, int power) | ||
459 | { | ||
460 | struct ili922x *ili = lcd_get_data(ld); | ||
461 | |||
462 | return ili922x_lcd_power(ili, power); | ||
463 | } | ||
464 | |||
465 | static int ili922x_get_power(struct lcd_device *ld) | ||
466 | { | ||
467 | struct ili922x *ili = lcd_get_data(ld); | ||
468 | |||
469 | return ili->power; | ||
470 | } | ||
471 | |||
472 | static struct lcd_ops ili922x_ops = { | ||
473 | .get_power = ili922x_get_power, | ||
474 | .set_power = ili922x_set_power, | ||
475 | }; | ||
476 | |||
477 | static int ili922x_probe(struct spi_device *spi) | ||
478 | { | ||
479 | struct ili922x *ili; | ||
480 | struct lcd_device *lcd; | ||
481 | int ret; | ||
482 | u16 reg = 0; | ||
483 | |||
484 | ili = devm_kzalloc(&spi->dev, sizeof(*ili), GFP_KERNEL); | ||
485 | if (!ili) { | ||
486 | dev_err(&spi->dev, "cannot alloc priv data\n"); | ||
487 | return -ENOMEM; | ||
488 | } | ||
489 | |||
490 | ili->spi = spi; | ||
491 | dev_set_drvdata(&spi->dev, ili); | ||
492 | |||
493 | /* check if the device is connected */ | ||
494 | ret = ili922x_read(spi, REG_DRIVER_CODE_READ, ®); | ||
495 | if (ret || ((reg & ILITEK_DEVICE_ID_MASK) != ILITEK_DEVICE_ID)) { | ||
496 | dev_err(&spi->dev, | ||
497 | "no LCD found: Chip ID 0x%x, ret %d\n", | ||
498 | reg, ret); | ||
499 | return -ENODEV; | ||
500 | } else { | ||
501 | dev_info(&spi->dev, "ILI%x found, SPI freq %d, mode %d\n", | ||
502 | reg, spi->max_speed_hz, spi->mode); | ||
503 | } | ||
504 | |||
505 | ret = ili922x_read_status(spi, ®); | ||
506 | if (ret) { | ||
507 | dev_err(&spi->dev, "reading RS failed...\n"); | ||
508 | return ret; | ||
509 | } else | ||
510 | dev_dbg(&spi->dev, "status: 0x%x\n", reg); | ||
511 | |||
512 | ili922x_display_init(spi); | ||
513 | |||
514 | ili->power = FB_BLANK_POWERDOWN; | ||
515 | |||
516 | lcd = lcd_device_register("ili922xlcd", &spi->dev, ili, | ||
517 | &ili922x_ops); | ||
518 | if (IS_ERR(lcd)) { | ||
519 | dev_err(&spi->dev, "cannot register LCD\n"); | ||
520 | return PTR_ERR(lcd); | ||
521 | } | ||
522 | |||
523 | ili->ld = lcd; | ||
524 | spi_set_drvdata(spi, ili); | ||
525 | |||
526 | ili922x_lcd_power(ili, FB_BLANK_UNBLANK); | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | static int ili922x_remove(struct spi_device *spi) | ||
532 | { | ||
533 | struct ili922x *ili = spi_get_drvdata(spi); | ||
534 | |||
535 | ili922x_poweroff(spi); | ||
536 | lcd_device_unregister(ili->ld); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static struct spi_driver ili922x_driver = { | ||
541 | .driver = { | ||
542 | .name = "ili922x", | ||
543 | .owner = THIS_MODULE, | ||
544 | }, | ||
545 | .probe = ili922x_probe, | ||
546 | .remove = ili922x_remove, | ||
547 | }; | ||
548 | |||
549 | module_spi_driver(ili922x_driver); | ||
550 | |||
551 | MODULE_AUTHOR("Stefano Babic <sbabic@denx.de>"); | ||
552 | MODULE_DESCRIPTION("ILI9221/9222 LCD driver"); | ||
553 | MODULE_LICENSE("GPL"); | ||
554 | MODULE_PARM_DESC(ili922x_id, "set controller identifier (default=1)"); | ||
555 | MODULE_PARM_DESC(tx_invert, "invert bytes before sending"); | ||