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