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 return tdo24m_power(lcd, power);
304}
305
306static int tdo24m_get_power(struct lcd_device *ld)
307{
308 struct tdo24m *lcd = lcd_get_data(ld);
309 return lcd->power;
310}
311
312static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
313{
314 struct tdo24m *lcd = lcd_get_data(ld);
315 int mode = MODE_QVGA;
316
317 if (m->xres == 640 || m->xres == 480)
318 mode = MODE_VGA;
319
320 if (lcd->mode == mode)
321 return 0;
322
323 return lcd->adj_mode(lcd, mode);
324}
325
326static struct lcd_ops tdo24m_ops = {
327 .get_power = tdo24m_get_power,
328 .set_power = tdo24m_set_power,
329 .set_mode = tdo24m_set_mode,
330};
331
332static int tdo24m_probe(struct spi_device *spi)
333{
334 struct tdo24m *lcd;
335 struct spi_message *m;
336 struct spi_transfer *x;
337 struct tdo24m_platform_data *pdata;
338 enum tdo24m_model model;
339 int err;
340
341 pdata = spi->dev.platform_data;
342 if (pdata)
343 model = pdata->model;
344 else
345 model = TDO24M;
346
347 spi->bits_per_word = 8;
348 spi->mode = SPI_MODE_3;
349 err = spi_setup(spi);
350 if (err)
351 return err;
352
353 lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
354 if (!lcd)
355 return -ENOMEM;
356
357 lcd->spi_dev = spi;
358 lcd->power = FB_BLANK_POWERDOWN;
359 lcd->mode = MODE_VGA;
360
361 lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
362 if (lcd->buf == NULL)
363 return -ENOMEM;
364
365 m = &lcd->msg;
366 x = &lcd->xfer;
367
368 spi_message_init(m);
369
370 x->cs_change = 1;
371 x->tx_buf = &lcd->buf[0];
372 spi_message_add_tail(x, m);
373
374 switch (model) {
375 case TDO24M:
376 lcd->color_invert = 1;
377 lcd->adj_mode = tdo24m_adj_mode;
378 break;
379 case TDO35S:
380 lcd->adj_mode = tdo35s_adj_mode;
381 lcd->color_invert = 0;
382 break;
383 default:
384 dev_err(&spi->dev, "Unsupported model");
385 return -EINVAL;
386 }
387
388 lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev,
389 lcd, &tdo24m_ops);
390 if (IS_ERR(lcd->lcd_dev))
391 return PTR_ERR(lcd->lcd_dev);
392
393 spi_set_drvdata(spi, lcd);
394 err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
395 if (err)
396 goto out_unregister;
397
398 return 0;
399
400out_unregister:
401 lcd_device_unregister(lcd->lcd_dev);
402 return err;
403}
404
405static int tdo24m_remove(struct spi_device *spi)
406{
407 struct tdo24m *lcd = spi_get_drvdata(spi);
408
409 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
410 lcd_device_unregister(lcd->lcd_dev);
411
412 return 0;
413}
414
415#ifdef CONFIG_PM_SLEEP
416static int tdo24m_suspend(struct device *dev)
417{
418 struct tdo24m *lcd = dev_get_drvdata(dev);
419
420 return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
421}
422
423static int tdo24m_resume(struct device *dev)
424{
425 struct tdo24m *lcd = dev_get_drvdata(dev);
426
427 return tdo24m_power(lcd, FB_BLANK_UNBLANK);
428}
429#endif
430
431static SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume);
432
433
434static void tdo24m_shutdown(struct spi_device *spi)
435{
436 struct tdo24m *lcd = spi_get_drvdata(spi);
437
438 tdo24m_power(lcd, FB_BLANK_POWERDOWN);
439}
440
441static struct spi_driver tdo24m_driver = {
442 .driver = {
443 .name = "tdo24m",
444 .owner = THIS_MODULE,
445 .pm = &tdo24m_pm_ops,
446 },
447 .probe = tdo24m_probe,
448 .remove = tdo24m_remove,
449 .shutdown = tdo24m_shutdown,
450};
451
452module_spi_driver(tdo24m_driver);
453
454MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
455MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
456MODULE_LICENSE("GPL");
457MODULE_ALIAS("spi:tdo24m");
458