1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * LCD driver for MIPI DBI-C / DCS compatible LCDs |
4 | * |
5 | * Copyright (C) 2006 Nokia Corporation |
6 | * Author: Imre Deak <imre.deak@nokia.com> |
7 | */ |
8 | #include <linux/device.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/workqueue.h> |
13 | #include <linux/spi/spi.h> |
14 | #include <linux/module.h> |
15 | |
16 | #include <linux/platform_data/lcd-mipid.h> |
17 | |
18 | #include "omapfb.h" |
19 | |
20 | #define MIPID_MODULE_NAME "lcd_mipid" |
21 | |
22 | #define MIPID_CMD_READ_DISP_ID 0x04 |
23 | #define MIPID_CMD_READ_RED 0x06 |
24 | #define MIPID_CMD_READ_GREEN 0x07 |
25 | #define MIPID_CMD_READ_BLUE 0x08 |
26 | #define MIPID_CMD_READ_DISP_STATUS 0x09 |
27 | #define MIPID_CMD_RDDSDR 0x0F |
28 | #define MIPID_CMD_SLEEP_IN 0x10 |
29 | #define MIPID_CMD_SLEEP_OUT 0x11 |
30 | #define MIPID_CMD_DISP_OFF 0x28 |
31 | #define MIPID_CMD_DISP_ON 0x29 |
32 | |
33 | #define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000) |
34 | |
35 | #define to_mipid_device(p) container_of(p, struct mipid_device, \ |
36 | panel) |
37 | struct mipid_device { |
38 | int enabled; |
39 | int revision; |
40 | unsigned int saved_bklight_level; |
41 | unsigned long hw_guard_end; /* next value of jiffies |
42 | when we can issue the |
43 | next sleep in/out command */ |
44 | unsigned long hw_guard_wait; /* max guard time in jiffies */ |
45 | struct gpio_desc *reset; |
46 | |
47 | struct omapfb_device *fbdev; |
48 | struct spi_device *spi; |
49 | struct mutex mutex; |
50 | struct lcd_panel panel; |
51 | |
52 | struct delayed_work esd_work; |
53 | void (*esd_check)(struct mipid_device *m); |
54 | }; |
55 | |
56 | static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf, |
57 | int wlen, u8 *rbuf, int rlen) |
58 | { |
59 | struct spi_message m; |
60 | struct spi_transfer *x, xfer[4]; |
61 | u16 w; |
62 | int r; |
63 | |
64 | BUG_ON(md->spi == NULL); |
65 | |
66 | spi_message_init(m: &m); |
67 | |
68 | memset(xfer, 0, sizeof(xfer)); |
69 | x = &xfer[0]; |
70 | |
71 | cmd &= 0xff; |
72 | x->tx_buf = &cmd; |
73 | x->bits_per_word = 9; |
74 | x->len = 2; |
75 | spi_message_add_tail(t: x, m: &m); |
76 | |
77 | if (wlen) { |
78 | x++; |
79 | x->tx_buf = wbuf; |
80 | x->len = wlen; |
81 | x->bits_per_word = 9; |
82 | spi_message_add_tail(t: x, m: &m); |
83 | } |
84 | |
85 | if (rlen) { |
86 | x++; |
87 | x->rx_buf = &w; |
88 | x->len = 1; |
89 | spi_message_add_tail(t: x, m: &m); |
90 | |
91 | if (rlen > 1) { |
92 | /* Arrange for the extra clock before the first |
93 | * data bit. |
94 | */ |
95 | x->bits_per_word = 9; |
96 | x->len = 2; |
97 | |
98 | x++; |
99 | x->rx_buf = &rbuf[1]; |
100 | x->len = rlen - 1; |
101 | spi_message_add_tail(t: x, m: &m); |
102 | } |
103 | } |
104 | |
105 | r = spi_sync(spi: md->spi, message: &m); |
106 | if (r < 0) |
107 | dev_dbg(&md->spi->dev, "spi_sync %d\n" , r); |
108 | |
109 | if (rlen) |
110 | rbuf[0] = w & 0xff; |
111 | } |
112 | |
113 | static inline void mipid_cmd(struct mipid_device *md, int cmd) |
114 | { |
115 | mipid_transfer(md, cmd, NULL, wlen: 0, NULL, rlen: 0); |
116 | } |
117 | |
118 | static inline void mipid_write(struct mipid_device *md, |
119 | int reg, const u8 *buf, int len) |
120 | { |
121 | mipid_transfer(md, cmd: reg, wbuf: buf, wlen: len, NULL, rlen: 0); |
122 | } |
123 | |
124 | static inline void mipid_read(struct mipid_device *md, |
125 | int reg, u8 *buf, int len) |
126 | { |
127 | mipid_transfer(md, cmd: reg, NULL, wlen: 0, rbuf: buf, rlen: len); |
128 | } |
129 | |
130 | static void set_data_lines(struct mipid_device *md, int data_lines) |
131 | { |
132 | u16 par; |
133 | |
134 | switch (data_lines) { |
135 | case 16: |
136 | par = 0x150; |
137 | break; |
138 | case 18: |
139 | par = 0x160; |
140 | break; |
141 | case 24: |
142 | par = 0x170; |
143 | break; |
144 | } |
145 | mipid_write(md, reg: 0x3a, buf: (u8 *)&par, len: 2); |
146 | } |
147 | |
148 | static void send_init_string(struct mipid_device *md) |
149 | { |
150 | u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; |
151 | |
152 | mipid_write(md, reg: 0xc2, buf: (u8 *)initpar, len: sizeof(initpar)); |
153 | set_data_lines(md, data_lines: md->panel.data_lines); |
154 | } |
155 | |
156 | static void hw_guard_start(struct mipid_device *md, int guard_msec) |
157 | { |
158 | md->hw_guard_wait = msecs_to_jiffies(m: guard_msec); |
159 | md->hw_guard_end = jiffies + md->hw_guard_wait; |
160 | } |
161 | |
162 | static void hw_guard_wait(struct mipid_device *md) |
163 | { |
164 | unsigned long wait = md->hw_guard_end - jiffies; |
165 | |
166 | if ((long)wait > 0 && time_before_eq(wait, md->hw_guard_wait)) { |
167 | set_current_state(TASK_UNINTERRUPTIBLE); |
168 | schedule_timeout(timeout: wait); |
169 | } |
170 | } |
171 | |
172 | static void set_sleep_mode(struct mipid_device *md, int on) |
173 | { |
174 | int cmd, sleep_time = 50; |
175 | |
176 | if (on) |
177 | cmd = MIPID_CMD_SLEEP_IN; |
178 | else |
179 | cmd = MIPID_CMD_SLEEP_OUT; |
180 | hw_guard_wait(md); |
181 | mipid_cmd(md, cmd); |
182 | hw_guard_start(md, guard_msec: 120); |
183 | /* |
184 | * When we enable the panel, it seems we _have_ to sleep |
185 | * 120 ms before sending the init string. When disabling the |
186 | * panel we'll sleep for the duration of 2 frames, so that the |
187 | * controller can still provide the PCLK,HS,VS signals. |
188 | */ |
189 | if (!on) |
190 | sleep_time = 120; |
191 | msleep(msecs: sleep_time); |
192 | } |
193 | |
194 | static void set_display_state(struct mipid_device *md, int enabled) |
195 | { |
196 | int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; |
197 | |
198 | mipid_cmd(md, cmd); |
199 | } |
200 | |
201 | static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level) |
202 | { |
203 | struct mipid_device *md = to_mipid_device(panel); |
204 | struct mipid_platform_data *pd = md->spi->dev.platform_data; |
205 | |
206 | if (pd->get_bklight_max == NULL || pd->set_bklight_level == NULL) |
207 | return -ENODEV; |
208 | if (level > pd->get_bklight_max(pd)) |
209 | return -EINVAL; |
210 | if (!md->enabled) { |
211 | md->saved_bklight_level = level; |
212 | return 0; |
213 | } |
214 | pd->set_bklight_level(pd, level); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static unsigned int mipid_get_bklight_level(struct lcd_panel *panel) |
220 | { |
221 | struct mipid_device *md = to_mipid_device(panel); |
222 | struct mipid_platform_data *pd = md->spi->dev.platform_data; |
223 | |
224 | if (pd->get_bklight_level == NULL) |
225 | return -ENODEV; |
226 | return pd->get_bklight_level(pd); |
227 | } |
228 | |
229 | static unsigned int mipid_get_bklight_max(struct lcd_panel *panel) |
230 | { |
231 | struct mipid_device *md = to_mipid_device(panel); |
232 | struct mipid_platform_data *pd = md->spi->dev.platform_data; |
233 | |
234 | if (pd->get_bklight_max == NULL) |
235 | return -ENODEV; |
236 | |
237 | return pd->get_bklight_max(pd); |
238 | } |
239 | |
240 | static unsigned long mipid_get_caps(struct lcd_panel *panel) |
241 | { |
242 | return OMAPFB_CAPS_SET_BACKLIGHT; |
243 | } |
244 | |
245 | static u16 read_first_pixel(struct mipid_device *md) |
246 | { |
247 | u16 pixel; |
248 | u8 red, green, blue; |
249 | |
250 | mutex_lock(&md->mutex); |
251 | mipid_read(md, MIPID_CMD_READ_RED, buf: &red, len: 1); |
252 | mipid_read(md, MIPID_CMD_READ_GREEN, buf: &green, len: 1); |
253 | mipid_read(md, MIPID_CMD_READ_BLUE, buf: &blue, len: 1); |
254 | mutex_unlock(lock: &md->mutex); |
255 | |
256 | switch (md->panel.data_lines) { |
257 | case 16: |
258 | pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1); |
259 | break; |
260 | case 24: |
261 | /* 24 bit -> 16 bit */ |
262 | pixel = ((red >> 3) << 11) | ((green >> 2) << 5) | |
263 | (blue >> 3); |
264 | break; |
265 | default: |
266 | pixel = 0; |
267 | BUG(); |
268 | } |
269 | |
270 | return pixel; |
271 | } |
272 | |
273 | static int mipid_run_test(struct lcd_panel *panel, int test_num) |
274 | { |
275 | struct mipid_device *md = to_mipid_device(panel); |
276 | static const u16 test_values[4] = { |
277 | 0x0000, 0xffff, 0xaaaa, 0x5555, |
278 | }; |
279 | int i; |
280 | |
281 | if (test_num != MIPID_TEST_RGB_LINES) |
282 | return MIPID_TEST_INVALID; |
283 | |
284 | for (i = 0; i < ARRAY_SIZE(test_values); i++) { |
285 | int delay; |
286 | unsigned long tmo; |
287 | |
288 | omapfb_write_first_pixel(fbdev: md->fbdev, pixval: test_values[i]); |
289 | tmo = jiffies + msecs_to_jiffies(m: 100); |
290 | delay = 25; |
291 | while (1) { |
292 | u16 pixel; |
293 | |
294 | msleep(msecs: delay); |
295 | pixel = read_first_pixel(md); |
296 | if (pixel == test_values[i]) |
297 | break; |
298 | if (time_after(jiffies, tmo)) { |
299 | dev_err(&md->spi->dev, |
300 | "MIPI LCD RGB I/F test failed: " |
301 | "expecting %04x, got %04x\n" , |
302 | test_values[i], pixel); |
303 | return MIPID_TEST_FAILED; |
304 | } |
305 | delay = 10; |
306 | } |
307 | } |
308 | |
309 | return 0; |
310 | } |
311 | |
312 | static void ls041y3_esd_recover(struct mipid_device *md) |
313 | { |
314 | dev_err(&md->spi->dev, "performing LCD ESD recovery\n" ); |
315 | set_sleep_mode(md, on: 1); |
316 | set_sleep_mode(md, on: 0); |
317 | } |
318 | |
319 | static void ls041y3_esd_check_mode1(struct mipid_device *md) |
320 | { |
321 | u8 state1, state2; |
322 | |
323 | mipid_read(md, MIPID_CMD_RDDSDR, buf: &state1, len: 1); |
324 | set_sleep_mode(md, on: 0); |
325 | mipid_read(md, MIPID_CMD_RDDSDR, buf: &state2, len: 1); |
326 | dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n" , |
327 | state1, state2); |
328 | /* Each sleep out command will trigger a self diagnostic and flip |
329 | * Bit6 if the test passes. |
330 | */ |
331 | if (!((state1 ^ state2) & (1 << 6))) |
332 | ls041y3_esd_recover(md); |
333 | } |
334 | |
335 | static void ls041y3_esd_check_mode2(struct mipid_device *md) |
336 | { |
337 | int i; |
338 | u8 rbuf[2]; |
339 | static const struct { |
340 | int cmd; |
341 | int wlen; |
342 | u16 wbuf[3]; |
343 | } *rd, rd_ctrl[7] = { |
344 | { 0xb0, 4, { 0x0101, 0x01fe, } }, |
345 | { 0xb1, 4, { 0x01de, 0x0121, } }, |
346 | { 0xc2, 4, { 0x0100, 0x0100, } }, |
347 | { 0xbd, 2, { 0x0100, } }, |
348 | { 0xc2, 4, { 0x01fc, 0x0103, } }, |
349 | { 0xb4, 0, }, |
350 | { 0x00, 0, }, |
351 | }; |
352 | |
353 | rd = rd_ctrl; |
354 | for (i = 0; i < 3; i++, rd++) |
355 | mipid_write(md, reg: rd->cmd, buf: (u8 *)rd->wbuf, len: rd->wlen); |
356 | |
357 | udelay(10); |
358 | mipid_read(md, reg: rd->cmd, buf: rbuf, len: 2); |
359 | rd++; |
360 | |
361 | for (i = 0; i < 3; i++, rd++) { |
362 | udelay(10); |
363 | mipid_write(md, reg: rd->cmd, buf: (u8 *)rd->wbuf, len: rd->wlen); |
364 | } |
365 | |
366 | dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n" , rbuf[1]); |
367 | if (rbuf[1] == 0x00) |
368 | ls041y3_esd_recover(md); |
369 | } |
370 | |
371 | static void ls041y3_esd_check(struct mipid_device *md) |
372 | { |
373 | ls041y3_esd_check_mode1(md); |
374 | if (md->revision >= 0x88) |
375 | ls041y3_esd_check_mode2(md); |
376 | } |
377 | |
378 | static void mipid_esd_start_check(struct mipid_device *md) |
379 | { |
380 | if (md->esd_check != NULL) |
381 | schedule_delayed_work(dwork: &md->esd_work, |
382 | MIPID_ESD_CHECK_PERIOD); |
383 | } |
384 | |
385 | static void mipid_esd_stop_check(struct mipid_device *md) |
386 | { |
387 | if (md->esd_check != NULL) |
388 | cancel_delayed_work_sync(dwork: &md->esd_work); |
389 | } |
390 | |
391 | static void mipid_esd_work(struct work_struct *work) |
392 | { |
393 | struct mipid_device *md = container_of(work, struct mipid_device, |
394 | esd_work.work); |
395 | |
396 | mutex_lock(&md->mutex); |
397 | md->esd_check(md); |
398 | mutex_unlock(lock: &md->mutex); |
399 | mipid_esd_start_check(md); |
400 | } |
401 | |
402 | static int mipid_enable(struct lcd_panel *panel) |
403 | { |
404 | struct mipid_device *md = to_mipid_device(panel); |
405 | |
406 | mutex_lock(&md->mutex); |
407 | |
408 | if (md->enabled) { |
409 | mutex_unlock(lock: &md->mutex); |
410 | return 0; |
411 | } |
412 | set_sleep_mode(md, on: 0); |
413 | md->enabled = 1; |
414 | send_init_string(md); |
415 | set_display_state(md, enabled: 1); |
416 | mipid_set_bklight_level(panel, level: md->saved_bklight_level); |
417 | mipid_esd_start_check(md); |
418 | |
419 | mutex_unlock(lock: &md->mutex); |
420 | return 0; |
421 | } |
422 | |
423 | static void mipid_disable(struct lcd_panel *panel) |
424 | { |
425 | struct mipid_device *md = to_mipid_device(panel); |
426 | |
427 | /* |
428 | * A final ESD work might be called before returning, |
429 | * so do this without holding the lock. |
430 | */ |
431 | mipid_esd_stop_check(md); |
432 | mutex_lock(&md->mutex); |
433 | |
434 | if (!md->enabled) { |
435 | mutex_unlock(lock: &md->mutex); |
436 | return; |
437 | } |
438 | md->saved_bklight_level = mipid_get_bklight_level(panel); |
439 | mipid_set_bklight_level(panel, level: 0); |
440 | set_display_state(md, enabled: 0); |
441 | set_sleep_mode(md, on: 1); |
442 | md->enabled = 0; |
443 | |
444 | mutex_unlock(lock: &md->mutex); |
445 | } |
446 | |
447 | static int panel_enabled(struct mipid_device *md) |
448 | { |
449 | u32 disp_status; |
450 | int enabled; |
451 | |
452 | mipid_read(md, MIPID_CMD_READ_DISP_STATUS, buf: (u8 *)&disp_status, len: 4); |
453 | disp_status = __be32_to_cpu(disp_status); |
454 | enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); |
455 | dev_dbg(&md->spi->dev, |
456 | "LCD panel %senabled by bootloader (status 0x%04x)\n" , |
457 | enabled ? "" : "not " , disp_status); |
458 | return enabled; |
459 | } |
460 | |
461 | static int mipid_init(struct lcd_panel *panel, |
462 | struct omapfb_device *fbdev) |
463 | { |
464 | struct mipid_device *md = to_mipid_device(panel); |
465 | |
466 | md->fbdev = fbdev; |
467 | INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work); |
468 | mutex_init(&md->mutex); |
469 | |
470 | md->enabled = panel_enabled(md); |
471 | |
472 | if (md->enabled) |
473 | mipid_esd_start_check(md); |
474 | else |
475 | md->saved_bklight_level = mipid_get_bklight_level(panel); |
476 | |
477 | return 0; |
478 | } |
479 | |
480 | static void mipid_cleanup(struct lcd_panel *panel) |
481 | { |
482 | struct mipid_device *md = to_mipid_device(panel); |
483 | |
484 | if (md->enabled) |
485 | mipid_esd_stop_check(md); |
486 | } |
487 | |
488 | static const struct lcd_panel mipid_panel = { |
489 | .config = OMAP_LCDC_PANEL_TFT, |
490 | |
491 | .bpp = 16, |
492 | .x_res = 800, |
493 | .y_res = 480, |
494 | .pixel_clock = 21940, |
495 | .hsw = 50, |
496 | .hfp = 20, |
497 | .hbp = 15, |
498 | .vsw = 2, |
499 | .vfp = 1, |
500 | .vbp = 3, |
501 | |
502 | .init = mipid_init, |
503 | .cleanup = mipid_cleanup, |
504 | .enable = mipid_enable, |
505 | .disable = mipid_disable, |
506 | .get_caps = mipid_get_caps, |
507 | .set_bklight_level = mipid_set_bklight_level, |
508 | .get_bklight_level = mipid_get_bklight_level, |
509 | .get_bklight_max = mipid_get_bklight_max, |
510 | .run_test = mipid_run_test, |
511 | }; |
512 | |
513 | static int mipid_detect(struct mipid_device *md) |
514 | { |
515 | struct mipid_platform_data *pdata; |
516 | u8 display_id[3]; |
517 | |
518 | pdata = md->spi->dev.platform_data; |
519 | if (pdata == NULL) { |
520 | dev_err(&md->spi->dev, "missing platform data\n" ); |
521 | return -ENOENT; |
522 | } |
523 | |
524 | mipid_read(md, MIPID_CMD_READ_DISP_ID, buf: display_id, len: 3); |
525 | dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n" , |
526 | display_id[0], display_id[1], display_id[2]); |
527 | |
528 | switch (display_id[0]) { |
529 | case 0x45: |
530 | md->panel.name = "lph8923" ; |
531 | break; |
532 | case 0x83: |
533 | md->panel.name = "ls041y3" ; |
534 | md->esd_check = ls041y3_esd_check; |
535 | break; |
536 | default: |
537 | md->panel.name = "unknown" ; |
538 | dev_err(&md->spi->dev, "invalid display ID\n" ); |
539 | return -ENODEV; |
540 | } |
541 | |
542 | md->revision = display_id[1]; |
543 | md->panel.data_lines = pdata->data_lines; |
544 | pr_info("omapfb: %s rev %02x LCD detected, %d data lines\n" , |
545 | md->panel.name, md->revision, md->panel.data_lines); |
546 | |
547 | return 0; |
548 | } |
549 | |
550 | static int mipid_spi_probe(struct spi_device *spi) |
551 | { |
552 | struct mipid_device *md; |
553 | int r; |
554 | |
555 | md = kzalloc(size: sizeof(*md), GFP_KERNEL); |
556 | if (md == NULL) { |
557 | dev_err(&spi->dev, "out of memory\n" ); |
558 | return -ENOMEM; |
559 | } |
560 | |
561 | /* This will de-assert RESET if active */ |
562 | md->reset = gpiod_get(dev: &spi->dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
563 | if (IS_ERR(ptr: md->reset)) |
564 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: md->reset), |
565 | fmt: "no reset GPIO line\n" ); |
566 | |
567 | spi->mode = SPI_MODE_0; |
568 | md->spi = spi; |
569 | dev_set_drvdata(dev: &spi->dev, data: md); |
570 | md->panel = mipid_panel; |
571 | |
572 | r = mipid_detect(md); |
573 | if (r < 0) |
574 | goto free_md; |
575 | |
576 | omapfb_register_panel(panel: &md->panel); |
577 | |
578 | return 0; |
579 | |
580 | free_md: |
581 | kfree(objp: md); |
582 | return r; |
583 | } |
584 | |
585 | static void mipid_spi_remove(struct spi_device *spi) |
586 | { |
587 | struct mipid_device *md = dev_get_drvdata(dev: &spi->dev); |
588 | |
589 | /* Asserts RESET */ |
590 | gpiod_set_value(desc: md->reset, value: 1); |
591 | mipid_disable(panel: &md->panel); |
592 | kfree(objp: md); |
593 | } |
594 | |
595 | static struct spi_driver mipid_spi_driver = { |
596 | .driver = { |
597 | .name = MIPID_MODULE_NAME, |
598 | }, |
599 | .probe = mipid_spi_probe, |
600 | .remove = mipid_spi_remove, |
601 | }; |
602 | |
603 | module_spi_driver(mipid_spi_driver); |
604 | |
605 | MODULE_DESCRIPTION("MIPI display driver" ); |
606 | MODULE_LICENSE("GPL" ); |
607 | |