1
2
3
4
5
6
7
8
9
10
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/device.h>
16#include <linux/spi/spi.h>
17#include <linux/spi/tdo24m.h>
18#include <linux/fb.h>
19#include <linux/lcd.h>
20#include <linux/slab.h>
21
22#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
23
24#define TDO24M_SPI_BUFF_SIZE (4)
25#define MODE_QVGA 0
26#define MODE_VGA 1
27
28struct tdo24m {
29 struct spi_device *spi_dev;
30 struct lcd_device *lcd_dev;
31
32 struct spi_message msg;
33 struct spi_transfer xfer;
34 uint8_t *buf;
35
36 int (*adj_mode)(struct tdo24m *lcd, int mode);
37 int color_invert;
38
39 int power;
40 int mode;
41};
42
43
44#define CMD0(x) ((0 << 30) | (x))
45#define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1))
46#define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\
47 ((x1) << 9) | 0x100 | (x2))
48#define CMD_NULL (-1)
49
50static uint32_t lcd_panel_reset[] = {
51 CMD0(0x1),
52 CMD0(0x0),
53 CMD0(0x0),
54 CMD0(0x0),
55 CMD_NULL,
56};
57
58static uint32_t lcd_panel_on[] = {
59 CMD0(0x29),
60 CMD2(0xB8, 0xFF, 0xF9),
61 CMD0(0x11),
62 CMD1(0xB0, 0x16),
63 CMD_NULL,
64};
65
66static uint32_t lcd_panel_off[] = {
67 CMD0(0x28),
68 CMD2(0xB8, 0x80, 0x02),
69 CMD0(0x10),
70 CMD1(0xB0, 0x00),
71 CMD_NULL,
72};
73
74static uint32_t lcd_vga_pass_through_tdo24m[] = {
75 CMD1(0xB0, 0x16),
76 CMD1(0xBC, 0x80),
77 CMD1(0xE1, 0x00),
78 CMD1(0x36, 0x50),
79 CMD1(0x3B, 0x00),
80 CMD_NULL,
81};
82
83static uint32_t lcd_qvga_pass_through_tdo24m[] = {
84 CMD1(0xB0, 0x16),
85 CMD1(0xBC, 0x81),
86 CMD1(0xE1, 0x00),
87 CMD1(0x36, 0x50),
88 CMD1(0x3B, 0x22),
89 CMD_NULL,
90};
91
92static uint32_t lcd_vga_transfer_tdo24m[] = {
93 CMD1(0xcf, 0x02),
94 CMD2(0xd0, 0x08, 0x04),
95 CMD1(0xd1, 0x01),
96 CMD2(0xd2, 0x14, 0x00),
97 CMD2(0xd3, 0x1a, 0x0f),
98 CMD2(0xd4, 0x1f, 0xaf),
99 CMD1(0xd5, 0x14),
100 CMD0(0x21),
101 CMD0(0x29),
102 CMD_NULL,
103};
104
105static uint32_t lcd_qvga_transfer[] = {
106 CMD1(0xd6, 0x02),
107 CMD2(0xd7, 0x08, 0x04),
108 CMD1(0xd8, 0x01),
109 CMD2(0xd9, 0x00, 0x08),
110 CMD2(0xde, 0x05, 0x0a),
111 CMD2(0xdf, 0x0a, 0x19),
112 CMD1(0xe0, 0x0a),
113 CMD0(0x21),
114 CMD0(0x29),
115 CMD_NULL,
116};
117
118static uint32_t lcd_vga_pass_through_tdo35s[] = {
119 CMD1(0xB0, 0x16),
120 CMD1(0xBC, 0x80),
121 CMD1(0xE1, 0x00),
122 CMD1(0x3B, 0x00),
123 CMD_NULL,
124};
125
126static uint32_t lcd_qvga_pass_through_tdo35s[] = {
127 CMD1(0xB0, 0x16),
128 CMD1(0xBC, 0x81),
129 CMD1(0xE1, 0x00),
130 CMD1(0x3B, 0x22),
131 CMD_NULL,
132};
133
134static uint32_t lcd_vga_transfer_tdo35s[] = {
135 CMD1(0xcf, 0x02),
136 CMD2(0xd0, 0x08, 0x04),
137 CMD1(0xd1, 0x01),
138 CMD2(0xd2, 0x00, 0x1e),
139 CMD2(0xd3, 0x14, 0x28),
140 CMD2(0xd4, 0x28, 0x64),
141 CMD1(0xd5, 0x28),
142 CMD0(0x21),
143 CMD0(0x29),
144 CMD_NULL,
145};
146
147static uint32_t lcd_panel_config[] = {
148 CMD2(0xb8, 0xff, 0xf9),
149 CMD0(0x11),
150 CMD1(0xba, 0x01),
151 CMD1(0xbb, 0x00),
152 CMD1(0x3a, 0x60),
153 CMD1(0xbf, 0x10),
154 CMD1(0xb1, 0x56),
155 CMD1(0xb2, 0x33),
156 CMD1(0xb3, 0x11),
157 CMD1(0xb4, 0x02),
158 CMD1(0xb5, 0x35),
159 CMD1(0xb6, 0x40),
160 CMD1(0xb7, 0x03),
161 CMD1(0xbd, 0x00),
162 CMD1(0xbe, 0x00),
163 CMD1(0xc0, 0x11),
164 CMD1(0xc1, 0x11),
165 CMD1(0xc2, 0x11),
166 CMD2(0xc3, 0x20, 0x40),
167 CMD2(0xc4, 0x60, 0xc0),
168 CMD2(0xc5, 0x10, 0x20),
169 CMD1(0xc6, 0xc0),
170 CMD2(0xc7, 0x33, 0x43),
171 CMD1(0xc8, 0x44),
172 CMD1(0xc9, 0x33),
173 CMD1(0xca, 0x00),
174 CMD2(0xec, 0x01, 0xf0),
175 CMD_NULL,
176};
177
178static int tdo24m_writes(struct tdo24m *lcd, uint32_t *array)
179{
180 struct spi_transfer *x = &lcd->xfer;
181 uint32_t data, *p = array;
182 int nparams, err = 0;
183
184 for (; *p != CMD_NULL; p++) {
185 if (!lcd->color_invert && *p == CMD0(0x21))
186 continue;
187
188 nparams = (*p >> 30) & 0x3;
189
190 data = *p << (7 - nparams);
191 switch (nparams) {
192 case 0:
193 lcd->buf[0] = (data >> 8) & 0xff;
194 lcd->buf[1] = data & 0xff;
195 break;
196 case 1:
197 lcd->buf[0] = (data >> 16) & 0xff;
198 lcd->buf[1] = (data >> 8) & 0xff;
199 lcd->buf[2] = data & 0xff;
200 break;
201 case 2:
202 lcd->buf[0] = (data >> 24) & 0xff;
203 lcd->buf[1] = (data >> 16) & 0xff;
204 lcd->buf[2] = (data >> 8) & 0xff;
205 lcd->buf[3] = data & 0xff;
206 break;
207 default:
208 continue;
209 }
210 x->len = nparams + 2;
211 err = spi_sync(lcd->spi_dev, &lcd->msg);
212 if (err)
213 break;
214 }
215
216 return err;
217}
218
219static int tdo24m_adj_mode(struct tdo24m *lcd, int mode)
220{
221 switch (mode) {
222 case MODE_VGA:
223 tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m);
224 tdo24m_writes(lcd, lcd_panel_config);
225 tdo24m_writes(lcd, lcd_vga_transfer_tdo24m);
226 break;
227 case MODE_QVGA:
228 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m);
229 tdo24m_writes(lcd, lcd_panel_config);
230 tdo24m_writes(lcd, lcd_qvga_transfer);
231 break;
232 default:
233 return -EINVAL;
234 }
235
236 lcd->mode = mode;
237 return 0;
238}
239
240static int tdo35s_adj_mode(struct tdo24m *lcd, int mode)
241{
242 switch (mode) {
243 case MODE_VGA:
244 tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s);
245 tdo24m_writes(lcd, lcd_panel_config);
246 tdo24m_writes(lcd, lcd_vga_transfer_tdo35s);
247 break;
248 case MODE_QVGA:
249 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s);
250 tdo24m_writes(lcd, lcd_panel_config);
251 tdo24m_writes(lcd, lcd_qvga_transfer);
252 break;
253 default:
254 return -EINVAL;
255 }
256
257 lcd->mode = mode;
258 return 0;
259}
260
261static int tdo24m_power_on(struct tdo24m *lcd)
262{
263 int err;
264
265 err = tdo24m_writes(lcd, lcd_panel_on);
266 if (err)
267 goto out;
268
269 err = tdo24m_writes(lcd, lcd_panel_reset);
270 if (err)
271 goto out;
272
273 err = lcd->adj_mode(lcd, lcd->mode);
274out:
275 return err;
276}
277
278static int tdo24m_power_off(struct tdo24m *lcd)
279{
280 return tdo24m_writes(lcd, lcd_panel_off);
281}
282
283static int tdo24m_power(struct tdo24m *lcd, int power)
284{
285 int ret = 0;
286
287 if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
288 ret = tdo24m_power_on(lcd);
289 else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
290 ret = tdo24m_power_off(lcd);
291
292 if (!ret)
293 lcd->power = power;
294
295 return ret;
296}
297
298
299static int tdo24m_set_power(struct lcd_device *ld, int power)
300{
301 struct tdo24m *lcd = lcd_get_data(ld);
302 return tdo24m_power(lcd, power);
303}
304
305static int tdo24m_get_power(struct lcd_device *ld)
306{
307 struct tdo24m *lcd = lcd_get_data(ld);
308 return lcd->power;
309}
310
311static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
312{
313 struct tdo24m *lcd = lcd_get_data(ld);
314 int mode = MODE_QVGA;
315
316 if (m->xres == 640 || m->xres == 480)
317 mode = MODE_VGA;
318
319 if (lcd->mode == mode)
320 return 0;
321
322 return lcd->adj_mode(lcd, mode);
323}
324
325static struct lcd_ops tdo24m_ops = {
326 .get_power = tdo24m_get_power,
327 .set_power = tdo24m_set_power,
328 .set_mode = tdo24m_set_mode,
329};
330
331static int __devinit tdo24m_probe(struct spi_device *spi)
332{
333 struct tdo24m *lcd;
334 struct spi_message *m;
335 struct spi_transfer *x;
336 struct tdo24m_platform_data *pdata;
337 enum tdo24m_model model;
338 int err;
339
340 pdata = spi->dev.platform_data;
341 if (pdata)
342 model = pdata->model;
343 else
344 model = TDO24M;
345
346 spi->bits_per_word = 8;
347 spi->mode = SPI_MODE_3;
348 err = spi_setup(spi);
349 if (err)
350 return err;
351
352 lcd = kzalloc(sizeof(struct tdo24m), GFP_KERNEL);
353 if (!lcd)
354 return -ENOMEM;
355
356 lcd->spi_dev = spi;
357 lcd->power = FB_BLANK_POWERDOWN;
358 lcd->mode = MODE_VGA;
359
360 lcd->buf = kmalloc(TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
361 if (lcd->buf == NULL) {
362 kfree(lcd);
363 return -ENOMEM;
364 }
365
366 m = &lcd->msg;
367 x = &lcd->xfer;
368
369 spi_message_init(m);
370
371 x->cs_change = 1;
372 x->tx_buf = &lcd->buf[0];
373 spi_message_add_tail(x, m);
374
375 switch (model) {
376 case TDO24M:
377 lcd->color_invert = 1;
378 lcd->adj_mode = tdo24m_adj_mode;
379 break;
380 case TDO35S:
381 lcd->adj_mode = tdo35s_adj_mode;
382 lcd->color_invert = 0;
383 break;
384 default:
385 dev_err(&spi->dev, "Unsupported model");
386 goto out_free;
387 }
388
389 lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev,
390 lcd, &tdo24m_ops);
391 if (IS_ERR(lcd->lcd_dev)) {
392 err = PTR_ERR(lcd->lcd_dev);
393 goto out_free;
394 }
395
396 dev_set_drvdata(&spi->dev, lcd);
397 err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
398 if (err)
399 goto out_unregister;
400
401 return 0;
402
403out_unregister:
404 lcd_device_unregister(lcd->lcd_dev);
405out_free:
406 kfree(lcd->buf);
407 kfree(lcd);
408 return err;
409}
410
411static int __devexit tdo24m_remove(struct spi_device *spi)
412{
413 struct tdo24m *lcd = dev_get_drvdata(&spi->dev);
414
415 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
416 lcd_device_unregister(lcd->lcd_dev);
417 kfree(lcd->buf);
418 kfree(lcd);
419
420 return 0;
421}
422
423#ifdef CONFIG_PM
424static int tdo24m_suspend(struct spi_device *spi, pm_message_t state)
425{
426 struct tdo24m *lcd = dev_get_drvdata(&spi->dev);
427
428 return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
429}
430
431static int tdo24m_resume(struct spi_device *spi)
432{
433 struct tdo24m *lcd = dev_get_drvdata(&spi->dev);
434
435 return tdo24m_power(lcd, FB_BLANK_UNBLANK);
436}
437#else
438#define tdo24m_suspend NULL
439#define tdo24m_resume NULL
440#endif
441
442
443static void tdo24m_shutdown(struct spi_device *spi)
444{
445 struct tdo24m *lcd = dev_get_drvdata(&spi->dev);
446
447 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
448}
449
450static struct spi_driver tdo24m_driver = {
451 .driver = {
452 .name = "tdo24m",
453 .owner = THIS_MODULE,
454 },
455 .probe = tdo24m_probe,
456 .remove = __devexit_p(tdo24m_remove),
457 .shutdown = tdo24m_shutdown,
458 .suspend = tdo24m_suspend,
459 .resume = tdo24m_resume,
460};
461
462static int __init tdo24m_init(void)
463{
464 return spi_register_driver(&tdo24m_driver);
465}
466module_init(tdo24m_init);
467
468static void __exit tdo24m_exit(void)
469{
470 spi_unregister_driver(&tdo24m_driver);
471}
472module_exit(tdo24m_exit);
473
474MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
475MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
476MODULE_LICENSE("GPL");
477MODULE_ALIAS("spi:tdo24m");
478