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 const uint32_t lcd_panel_reset[] = {
51 CMD0(0x1),
52 CMD0(0x0),
53 CMD0(0x0),
54 CMD0(0x0),
55 CMD_NULL,
56};
57
58static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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, const uint32_t *array)
179{
180 struct spi_transfer *x = &lcd->xfer;
181 const uint32_t *p = array;
182 uint32_t data;
183 int nparams, err = 0;
184
185 for (; *p != CMD_NULL; p++) {
186 if (!lcd->color_invert && *p == CMD0(0x21))
187 continue;
188
189 nparams = (*p >> 30) & 0x3;
190
191 data = *p << (7 - nparams);
192 switch (nparams) {
193 case 0:
194 lcd->buf[0] = (data >> 8) & 0xff;
195 lcd->buf[1] = data & 0xff;
196 break;
197 case 1:
198 lcd->buf[0] = (data >> 16) & 0xff;
199 lcd->buf[1] = (data >> 8) & 0xff;
200 lcd->buf[2] = data & 0xff;
201 break;
202 case 2:
203 lcd->buf[0] = (data >> 24) & 0xff;
204 lcd->buf[1] = (data >> 16) & 0xff;
205 lcd->buf[2] = (data >> 8) & 0xff;
206 lcd->buf[3] = data & 0xff;
207 break;
208 default:
209 continue;
210 }
211 x->len = nparams + 2;
212 err = spi_sync(lcd->spi_dev, &lcd->msg);
213 if (err)
214 break;
215 }
216
217 return err;
218}
219
220static int tdo24m_adj_mode(struct tdo24m *lcd, int mode)
221{
222 switch (mode) {
223 case MODE_VGA:
224 tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m);
225 tdo24m_writes(lcd, lcd_panel_config);
226 tdo24m_writes(lcd, lcd_vga_transfer_tdo24m);
227 break;
228 case MODE_QVGA:
229 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m);
230 tdo24m_writes(lcd, lcd_panel_config);
231 tdo24m_writes(lcd, lcd_qvga_transfer);
232 break;
233 default:
234 return -EINVAL;
235 }
236
237 lcd->mode = mode;
238 return 0;
239}
240
241static int tdo35s_adj_mode(struct tdo24m *lcd, int mode)
242{
243 switch (mode) {
244 case MODE_VGA:
245 tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s);
246 tdo24m_writes(lcd, lcd_panel_config);
247 tdo24m_writes(lcd, lcd_vga_transfer_tdo35s);
248 break;
249 case MODE_QVGA:
250 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s);
251 tdo24m_writes(lcd, lcd_panel_config);
252 tdo24m_writes(lcd, lcd_qvga_transfer);
253 break;
254 default:
255 return -EINVAL;
256 }
257
258 lcd->mode = mode;
259 return 0;
260}
261
262static int tdo24m_power_on(struct tdo24m *lcd)
263{
264 int err;
265
266 err = tdo24m_writes(lcd, lcd_panel_on);
267 if (err)
268 goto out;
269
270 err = tdo24m_writes(lcd, lcd_panel_reset);
271 if (err)
272 goto out;
273
274 err = lcd->adj_mode(lcd, lcd->mode);
275out:
276 return err;
277}
278
279static int tdo24m_power_off(struct tdo24m *lcd)
280{
281 return tdo24m_writes(lcd, lcd_panel_off);
282}
283
284static int tdo24m_power(struct tdo24m *lcd, int power)
285{
286 int ret = 0;
287
288 if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
289 ret = tdo24m_power_on(lcd);
290 else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
291 ret = tdo24m_power_off(lcd);
292
293 if (!ret)
294 lcd->power = power;
295
296 return ret;
297}
298
299
300static int tdo24m_set_power(struct lcd_device *ld, int power)
301{
302 struct tdo24m *lcd = lcd_get_data(ld);
303
304 return tdo24m_power(lcd, power);
305}
306
307static int tdo24m_get_power(struct lcd_device *ld)
308{
309 struct tdo24m *lcd = lcd_get_data(ld);
310
311 return lcd->power;
312}
313
314static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
315{
316 struct tdo24m *lcd = lcd_get_data(ld);
317 int mode = MODE_QVGA;
318
319 if (m->xres == 640 || m->xres == 480)
320 mode = MODE_VGA;
321
322 if (lcd->mode == mode)
323 return 0;
324
325 return lcd->adj_mode(lcd, mode);
326}
327
328static struct lcd_ops tdo24m_ops = {
329 .get_power = tdo24m_get_power,
330 .set_power = tdo24m_set_power,
331 .set_mode = tdo24m_set_mode,
332};
333
334static int tdo24m_probe(struct spi_device *spi)
335{
336 struct tdo24m *lcd;
337 struct spi_message *m;
338 struct spi_transfer *x;
339 struct tdo24m_platform_data *pdata;
340 enum tdo24m_model model;
341 int err;
342
343 pdata = dev_get_platdata(&spi->dev);
344 if (pdata)
345 model = pdata->model;
346 else
347 model = TDO24M;
348
349 spi->bits_per_word = 8;
350 spi->mode = SPI_MODE_3;
351 err = spi_setup(spi);
352 if (err)
353 return err;
354
355 lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
356 if (!lcd)
357 return -ENOMEM;
358
359 lcd->spi_dev = spi;
360 lcd->power = FB_BLANK_POWERDOWN;
361 lcd->mode = MODE_VGA;
362
363 lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
364 if (lcd->buf == NULL)
365 return -ENOMEM;
366
367 m = &lcd->msg;
368 x = &lcd->xfer;
369
370 spi_message_init(m);
371
372 x->cs_change = 1;
373 x->tx_buf = &lcd->buf[0];
374 spi_message_add_tail(x, m);
375
376 switch (model) {
377 case TDO24M:
378 lcd->color_invert = 1;
379 lcd->adj_mode = tdo24m_adj_mode;
380 break;
381 case TDO35S:
382 lcd->adj_mode = tdo35s_adj_mode;
383 lcd->color_invert = 0;
384 break;
385 default:
386 dev_err(&spi->dev, "Unsupported model");
387 return -EINVAL;
388 }
389
390 lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "tdo24m", &spi->dev,
391 lcd, &tdo24m_ops);
392 if (IS_ERR(lcd->lcd_dev))
393 return PTR_ERR(lcd->lcd_dev);
394
395 spi_set_drvdata(spi, lcd);
396 err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
397 if (err)
398 return err;
399
400 return 0;
401}
402
403static int tdo24m_remove(struct spi_device *spi)
404{
405 struct tdo24m *lcd = spi_get_drvdata(spi);
406
407 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
408 return 0;
409}
410
411#ifdef CONFIG_PM_SLEEP
412static int tdo24m_suspend(struct device *dev)
413{
414 struct tdo24m *lcd = dev_get_drvdata(dev);
415
416 return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
417}
418
419static int tdo24m_resume(struct device *dev)
420{
421 struct tdo24m *lcd = dev_get_drvdata(dev);
422
423 return tdo24m_power(lcd, FB_BLANK_UNBLANK);
424}
425#endif
426
427static SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume);
428
429
430static void tdo24m_shutdown(struct spi_device *spi)
431{
432 struct tdo24m *lcd = spi_get_drvdata(spi);
433
434 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
435}
436
437static struct spi_driver tdo24m_driver = {
438 .driver = {
439 .name = "tdo24m",
440 .owner = THIS_MODULE,
441 .pm = &tdo24m_pm_ops,
442 },
443 .probe = tdo24m_probe,
444 .remove = tdo24m_remove,
445 .shutdown = tdo24m_shutdown,
446};
447
448module_spi_driver(tdo24m_driver);
449
450MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
451MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
452MODULE_LICENSE("GPL");
453MODULE_ALIAS("spi:tdo24m");
454