1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2019, Huaqin Telecom Technology Co., Ltd
4 *
5 * Author: Jerry Han <jerry.han.hq@gmail.com>
6 *
7 */
8
9 #include <linux/delay.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_device.h>
14
15 #include <linux/gpio/consumer.h>
16 #include <linux/regulator/consumer.h>
17
18 #include <drm/drm_device.h>
19 #include <drm/drm_mipi_dsi.h>
20 #include <drm/drm_modes.h>
21 #include <drm/drm_panel.h>
22
23 #include <video/mipi_display.h>
24
25 struct panel_cmd {
26 char cmd;
27 char data;
28 };
29
30 struct panel_desc {
31 const struct drm_display_mode *display_mode;
32 unsigned int bpc;
33 unsigned int width_mm;
34 unsigned int height_mm;
35
36 unsigned long mode_flags;
37 enum mipi_dsi_pixel_format format;
38 unsigned int lanes;
39 const struct panel_cmd *on_cmds;
40 unsigned int on_cmds_num;
41 };
42
43 struct panel_info {
44 struct drm_panel base;
45 struct mipi_dsi_device *link;
46 const struct panel_desc *desc;
47
48 struct gpio_desc *enable_gpio;
49 struct gpio_desc *pp33_gpio;
50 struct gpio_desc *pp18_gpio;
51
52 bool prepared;
53 bool enabled;
54 };
55
to_panel_info(struct drm_panel * panel)56 static inline struct panel_info *to_panel_info(struct drm_panel *panel)
57 {
58 return container_of(panel, struct panel_info, base);
59 }
60
disable_gpios(struct panel_info * pinfo)61 static void disable_gpios(struct panel_info *pinfo)
62 {
63 gpiod_set_value(pinfo->enable_gpio, 0);
64 gpiod_set_value(pinfo->pp33_gpio, 0);
65 gpiod_set_value(pinfo->pp18_gpio, 0);
66 }
67
send_mipi_cmds(struct drm_panel * panel,const struct panel_cmd * cmds)68 static int send_mipi_cmds(struct drm_panel *panel, const struct panel_cmd *cmds)
69 {
70 struct panel_info *pinfo = to_panel_info(panel);
71 unsigned int i = 0;
72 int err;
73
74 for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
75 err = mipi_dsi_dcs_write_buffer(pinfo->link, &cmds[i],
76 sizeof(struct panel_cmd));
77
78 if (err < 0)
79 return err;
80 }
81
82 return 0;
83 }
84
boe_panel_disable(struct drm_panel * panel)85 static int boe_panel_disable(struct drm_panel *panel)
86 {
87 struct panel_info *pinfo = to_panel_info(panel);
88 int err;
89
90 if (!pinfo->enabled)
91 return 0;
92
93 err = mipi_dsi_dcs_set_display_off(pinfo->link);
94 if (err < 0) {
95 dev_err(panel->dev, "failed to set display off: %d\n", err);
96 return err;
97 }
98
99 pinfo->enabled = false;
100
101 return 0;
102 }
103
boe_panel_unprepare(struct drm_panel * panel)104 static int boe_panel_unprepare(struct drm_panel *panel)
105 {
106 struct panel_info *pinfo = to_panel_info(panel);
107 int err;
108
109 if (!pinfo->prepared)
110 return 0;
111
112 err = mipi_dsi_dcs_set_display_off(pinfo->link);
113 if (err < 0)
114 dev_err(panel->dev, "failed to set display off: %d\n", err);
115
116 err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
117 if (err < 0)
118 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
119
120 /* sleep_mode_delay: 1ms - 2ms */
121 usleep_range(1000, 2000);
122
123 disable_gpios(pinfo);
124
125 pinfo->prepared = false;
126
127 return 0;
128 }
129
boe_panel_prepare(struct drm_panel * panel)130 static int boe_panel_prepare(struct drm_panel *panel)
131 {
132 struct panel_info *pinfo = to_panel_info(panel);
133 int err;
134
135 if (pinfo->prepared)
136 return 0;
137
138 gpiod_set_value(pinfo->pp18_gpio, 1);
139 /* T1: 5ms - 6ms */
140 usleep_range(5000, 6000);
141 gpiod_set_value(pinfo->pp33_gpio, 1);
142
143 /* reset sequence */
144 /* T2: 14ms - 15ms */
145 usleep_range(14000, 15000);
146 gpiod_set_value(pinfo->enable_gpio, 1);
147
148 /* T3: 1ms - 2ms */
149 usleep_range(1000, 2000);
150 gpiod_set_value(pinfo->enable_gpio, 0);
151
152 /* T4: 1ms - 2ms */
153 usleep_range(1000, 2000);
154 gpiod_set_value(pinfo->enable_gpio, 1);
155
156 /* T5: 5ms - 6ms */
157 usleep_range(5000, 6000);
158
159 /* send init code */
160 err = send_mipi_cmds(panel, pinfo->desc->on_cmds);
161 if (err < 0) {
162 dev_err(panel->dev, "failed to send DCS Init Code: %d\n", err);
163 goto poweroff;
164 }
165
166 err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
167 if (err < 0) {
168 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
169 goto poweroff;
170 }
171
172 /* T6: 120ms - 121ms */
173 usleep_range(120000, 121000);
174
175 err = mipi_dsi_dcs_set_display_on(pinfo->link);
176 if (err < 0) {
177 dev_err(panel->dev, "failed to set display on: %d\n", err);
178 goto poweroff;
179 }
180
181 /* T7: 20ms - 21ms */
182 usleep_range(20000, 21000);
183
184 pinfo->prepared = true;
185
186 return 0;
187
188 poweroff:
189 disable_gpios(pinfo);
190 return err;
191 }
192
boe_panel_enable(struct drm_panel * panel)193 static int boe_panel_enable(struct drm_panel *panel)
194 {
195 struct panel_info *pinfo = to_panel_info(panel);
196 int ret;
197
198 if (pinfo->enabled)
199 return 0;
200
201 usleep_range(120000, 121000);
202
203 ret = mipi_dsi_dcs_set_display_on(pinfo->link);
204 if (ret < 0) {
205 dev_err(panel->dev, "failed to set display on: %d\n", ret);
206 return ret;
207 }
208
209 pinfo->enabled = true;
210
211 return 0;
212 }
213
boe_panel_get_modes(struct drm_panel * panel,struct drm_connector * connector)214 static int boe_panel_get_modes(struct drm_panel *panel,
215 struct drm_connector *connector)
216 {
217 struct panel_info *pinfo = to_panel_info(panel);
218 const struct drm_display_mode *m = pinfo->desc->display_mode;
219 struct drm_display_mode *mode;
220
221 mode = drm_mode_duplicate(connector->dev, m);
222 if (!mode) {
223 dev_err(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
224 m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
225 return -ENOMEM;
226 }
227
228 drm_mode_set_name(mode);
229
230 drm_mode_probed_add(connector, mode);
231
232 connector->display_info.width_mm = pinfo->desc->width_mm;
233 connector->display_info.height_mm = pinfo->desc->height_mm;
234 connector->display_info.bpc = pinfo->desc->bpc;
235
236 return 1;
237 }
238
239 static const struct drm_panel_funcs panel_funcs = {
240 .disable = boe_panel_disable,
241 .unprepare = boe_panel_unprepare,
242 .prepare = boe_panel_prepare,
243 .enable = boe_panel_enable,
244 .get_modes = boe_panel_get_modes,
245 };
246
247 static const struct drm_display_mode default_display_mode = {
248 .clock = 159420,
249 .hdisplay = 1200,
250 .hsync_start = 1200 + 80,
251 .hsync_end = 1200 + 80 + 60,
252 .htotal = 1200 + 80 + 60 + 24,
253 .vdisplay = 1920,
254 .vsync_start = 1920 + 10,
255 .vsync_end = 1920 + 10 + 14,
256 .vtotal = 1920 + 10 + 14 + 4,
257 };
258
259 /* 8 inch */
260 static const struct panel_cmd boe_himax8279d8p_on_cmds[] = {
261 { 0xB0, 0x05 },
262 { 0xB1, 0xE5 },
263 { 0xB3, 0x52 },
264 { 0xC0, 0x00 },
265 { 0xC2, 0x57 },
266 { 0xD9, 0x85 },
267 { 0xB0, 0x01 },
268 { 0xC8, 0x00 },
269 { 0xC9, 0x00 },
270 { 0xCC, 0x26 },
271 { 0xCD, 0x26 },
272 { 0xDC, 0x00 },
273 { 0xDD, 0x00 },
274 { 0xE0, 0x26 },
275 { 0xE1, 0x26 },
276 { 0xB0, 0x03 },
277 { 0xC3, 0x2A },
278 { 0xE7, 0x2A },
279 { 0xC5, 0x2A },
280 { 0xDE, 0x2A },
281 { 0xBC, 0x02 },
282 { 0xCB, 0x02 },
283 { 0xB0, 0x00 },
284 { 0xB6, 0x03 },
285 { 0xBA, 0x8B },
286 { 0xBF, 0x15 },
287 { 0xC0, 0x18 },
288 { 0xC2, 0x14 },
289 { 0xC3, 0x02 },
290 { 0xC4, 0x14 },
291 { 0xC5, 0x02 },
292 { 0xCC, 0x0A },
293 { 0xB0, 0x06 },
294 { 0xC0, 0xA5 },
295 { 0xD5, 0x20 },
296 { 0xC0, 0x00 },
297 { 0xB0, 0x02 },
298 { 0xC0, 0x00 },
299 { 0xC1, 0x02 },
300 { 0xC2, 0x06 },
301 { 0xC3, 0x16 },
302 { 0xC4, 0x0E },
303 { 0xC5, 0x18 },
304 { 0xC6, 0x26 },
305 { 0xC7, 0x32 },
306 { 0xC8, 0x3F },
307 { 0xC9, 0x3F },
308 { 0xCA, 0x3F },
309 { 0xCB, 0x3F },
310 { 0xCC, 0x3D },
311 { 0xCD, 0x2F },
312 { 0xCE, 0x2F },
313 { 0xCF, 0x2F },
314 { 0xD0, 0x07 },
315 { 0xD2, 0x00 },
316 { 0xD3, 0x02 },
317 { 0xD4, 0x06 },
318 { 0xD5, 0x12 },
319 { 0xD6, 0x0A },
320 { 0xD7, 0x14 },
321 { 0xD8, 0x22 },
322 { 0xD9, 0x2E },
323 { 0xDA, 0x3D },
324 { 0xDB, 0x3F },
325 { 0xDC, 0x3F },
326 { 0xDD, 0x3F },
327 { 0xDE, 0x3D },
328 { 0xDF, 0x2F },
329 { 0xE0, 0x2F },
330 { 0xE1, 0x2F },
331 { 0xE2, 0x07 },
332 { 0xB0, 0x07 },
333 { 0xB1, 0x18 },
334 { 0xB2, 0x19 },
335 { 0xB3, 0x2E },
336 { 0xB4, 0x52 },
337 { 0xB5, 0x72 },
338 { 0xB6, 0x8C },
339 { 0xB7, 0xBD },
340 { 0xB8, 0xEB },
341 { 0xB9, 0x47 },
342 { 0xBA, 0x96 },
343 { 0xBB, 0x1E },
344 { 0xBC, 0x90 },
345 { 0xBD, 0x93 },
346 { 0xBE, 0xFA },
347 { 0xBF, 0x56 },
348 { 0xC0, 0x8C },
349 { 0xC1, 0xB7 },
350 { 0xC2, 0xCC },
351 { 0xC3, 0xDF },
352 { 0xC4, 0xE8 },
353 { 0xC5, 0xF0 },
354 { 0xC6, 0xF8 },
355 { 0xC7, 0xFA },
356 { 0xC8, 0xFC },
357 { 0xC9, 0x00 },
358 { 0xCA, 0x00 },
359 { 0xCB, 0x5A },
360 { 0xCC, 0xAF },
361 { 0xCD, 0xFF },
362 { 0xCE, 0xFF },
363 { 0xB0, 0x08 },
364 { 0xB1, 0x04 },
365 { 0xB2, 0x15 },
366 { 0xB3, 0x2D },
367 { 0xB4, 0x51 },
368 { 0xB5, 0x72 },
369 { 0xB6, 0x8D },
370 { 0xB7, 0xBE },
371 { 0xB8, 0xED },
372 { 0xB9, 0x4A },
373 { 0xBA, 0x9A },
374 { 0xBB, 0x23 },
375 { 0xBC, 0x95 },
376 { 0xBD, 0x98 },
377 { 0xBE, 0xFF },
378 { 0xBF, 0x59 },
379 { 0xC0, 0x8E },
380 { 0xC1, 0xB9 },
381 { 0xC2, 0xCD },
382 { 0xC3, 0xDF },
383 { 0xC4, 0xE8 },
384 { 0xC5, 0xF0 },
385 { 0xC6, 0xF8 },
386 { 0xC7, 0xFA },
387 { 0xC8, 0xFC },
388 { 0xC9, 0x00 },
389 { 0xCA, 0x00 },
390 { 0xCB, 0x5A },
391 { 0xCC, 0xAF },
392 { 0xCD, 0xFF },
393 { 0xCE, 0xFF },
394 { 0xB0, 0x09 },
395 { 0xB1, 0x04 },
396 { 0xB2, 0x2C },
397 { 0xB3, 0x36 },
398 { 0xB4, 0x53 },
399 { 0xB5, 0x73 },
400 { 0xB6, 0x8E },
401 { 0xB7, 0xC0 },
402 { 0xB8, 0xEF },
403 { 0xB9, 0x4C },
404 { 0xBA, 0x9D },
405 { 0xBB, 0x25 },
406 { 0xBC, 0x96 },
407 { 0xBD, 0x9A },
408 { 0xBE, 0x01 },
409 { 0xBF, 0x59 },
410 { 0xC0, 0x8E },
411 { 0xC1, 0xB9 },
412 { 0xC2, 0xCD },
413 { 0xC3, 0xDF },
414 { 0xC4, 0xE8 },
415 { 0xC5, 0xF0 },
416 { 0xC6, 0xF8 },
417 { 0xC7, 0xFA },
418 { 0xC8, 0xFC },
419 { 0xC9, 0x00 },
420 { 0xCA, 0x00 },
421 { 0xCB, 0x5A },
422 { 0xCC, 0xBF },
423 { 0xCD, 0xFF },
424 { 0xCE, 0xFF },
425 { 0xB0, 0x0A },
426 { 0xB1, 0x18 },
427 { 0xB2, 0x19 },
428 { 0xB3, 0x2E },
429 { 0xB4, 0x52 },
430 { 0xB5, 0x72 },
431 { 0xB6, 0x8C },
432 { 0xB7, 0xBD },
433 { 0xB8, 0xEB },
434 { 0xB9, 0x47 },
435 { 0xBA, 0x96 },
436 { 0xBB, 0x1E },
437 { 0xBC, 0x90 },
438 { 0xBD, 0x93 },
439 { 0xBE, 0xFA },
440 { 0xBF, 0x56 },
441 { 0xC0, 0x8C },
442 { 0xC1, 0xB7 },
443 { 0xC2, 0xCC },
444 { 0xC3, 0xDF },
445 { 0xC4, 0xE8 },
446 { 0xC5, 0xF0 },
447 { 0xC6, 0xF8 },
448 { 0xC7, 0xFA },
449 { 0xC8, 0xFC },
450 { 0xC9, 0x00 },
451 { 0xCA, 0x00 },
452 { 0xCB, 0x5A },
453 { 0xCC, 0xAF },
454 { 0xCD, 0xFF },
455 { 0xCE, 0xFF },
456 { 0xB0, 0x0B },
457 { 0xB1, 0x04 },
458 { 0xB2, 0x15 },
459 { 0xB3, 0x2D },
460 { 0xB4, 0x51 },
461 { 0xB5, 0x72 },
462 { 0xB6, 0x8D },
463 { 0xB7, 0xBE },
464 { 0xB8, 0xED },
465 { 0xB9, 0x4A },
466 { 0xBA, 0x9A },
467 { 0xBB, 0x23 },
468 { 0xBC, 0x95 },
469 { 0xBD, 0x98 },
470 { 0xBE, 0xFF },
471 { 0xBF, 0x59 },
472 { 0xC0, 0x8E },
473 { 0xC1, 0xB9 },
474 { 0xC2, 0xCD },
475 { 0xC3, 0xDF },
476 { 0xC4, 0xE8 },
477 { 0xC5, 0xF0 },
478 { 0xC6, 0xF8 },
479 { 0xC7, 0xFA },
480 { 0xC8, 0xFC },
481 { 0xC9, 0x00 },
482 { 0xCA, 0x00 },
483 { 0xCB, 0x5A },
484 { 0xCC, 0xAF },
485 { 0xCD, 0xFF },
486 { 0xCE, 0xFF },
487 { 0xB0, 0x0C },
488 { 0xB1, 0x04 },
489 { 0xB2, 0x2C },
490 { 0xB3, 0x36 },
491 { 0xB4, 0x53 },
492 { 0xB5, 0x73 },
493 { 0xB6, 0x8E },
494 { 0xB7, 0xC0 },
495 { 0xB8, 0xEF },
496 { 0xB9, 0x4C },
497 { 0xBA, 0x9D },
498 { 0xBB, 0x25 },
499 { 0xBC, 0x96 },
500 { 0xBD, 0x9A },
501 { 0xBE, 0x01 },
502 { 0xBF, 0x59 },
503 { 0xC0, 0x8E },
504 { 0xC1, 0xB9 },
505 { 0xC2, 0xCD },
506 { 0xC3, 0xDF },
507 { 0xC4, 0xE8 },
508 { 0xC5, 0xF0 },
509 { 0xC6, 0xF8 },
510 { 0xC7, 0xFA },
511 { 0xC8, 0xFC },
512 { 0xC9, 0x00 },
513 { 0xCA, 0x00 },
514 { 0xCB, 0x5A },
515 { 0xCC, 0xBF },
516 { 0xCD, 0xFF },
517 { 0xCE, 0xFF },
518 { 0xB0, 0x04 },
519 { 0xB5, 0x02 },
520 { 0xB6, 0x01 },
521 };
522
523 static const struct panel_desc boe_himax8279d8p_panel_desc = {
524 .display_mode = &default_display_mode,
525 .bpc = 8,
526 .width_mm = 107,
527 .height_mm = 172,
528 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
529 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
530 .format = MIPI_DSI_FMT_RGB888,
531 .lanes = 4,
532 .on_cmds = boe_himax8279d8p_on_cmds,
533 .on_cmds_num = 260,
534 };
535
536 /* 10 inch */
537 static const struct panel_cmd boe_himax8279d10p_on_cmds[] = {
538 { 0xB0, 0x05 },
539 { 0xB1, 0xE5 },
540 { 0xB3, 0x52 },
541 { 0xB0, 0x00 },
542 { 0xB6, 0x03 },
543 { 0xBA, 0x8B },
544 { 0xBF, 0x1A },
545 { 0xC0, 0x0F },
546 { 0xC2, 0x0C },
547 { 0xC3, 0x02 },
548 { 0xC4, 0x0C },
549 { 0xC5, 0x02 },
550 { 0xB0, 0x01 },
551 { 0xE0, 0x26 },
552 { 0xE1, 0x26 },
553 { 0xDC, 0x00 },
554 { 0xDD, 0x00 },
555 { 0xCC, 0x26 },
556 { 0xCD, 0x26 },
557 { 0xC8, 0x00 },
558 { 0xC9, 0x00 },
559 { 0xD2, 0x03 },
560 { 0xD3, 0x03 },
561 { 0xE6, 0x04 },
562 { 0xE7, 0x04 },
563 { 0xC4, 0x09 },
564 { 0xC5, 0x09 },
565 { 0xD8, 0x0A },
566 { 0xD9, 0x0A },
567 { 0xC2, 0x0B },
568 { 0xC3, 0x0B },
569 { 0xD6, 0x0C },
570 { 0xD7, 0x0C },
571 { 0xC0, 0x05 },
572 { 0xC1, 0x05 },
573 { 0xD4, 0x06 },
574 { 0xD5, 0x06 },
575 { 0xCA, 0x07 },
576 { 0xCB, 0x07 },
577 { 0xDE, 0x08 },
578 { 0xDF, 0x08 },
579 { 0xB0, 0x02 },
580 { 0xC0, 0x00 },
581 { 0xC1, 0x0D },
582 { 0xC2, 0x17 },
583 { 0xC3, 0x26 },
584 { 0xC4, 0x31 },
585 { 0xC5, 0x1C },
586 { 0xC6, 0x2C },
587 { 0xC7, 0x33 },
588 { 0xC8, 0x31 },
589 { 0xC9, 0x37 },
590 { 0xCA, 0x37 },
591 { 0xCB, 0x37 },
592 { 0xCC, 0x39 },
593 { 0xCD, 0x2E },
594 { 0xCE, 0x2F },
595 { 0xCF, 0x2F },
596 { 0xD0, 0x07 },
597 { 0xD2, 0x00 },
598 { 0xD3, 0x0D },
599 { 0xD4, 0x17 },
600 { 0xD5, 0x26 },
601 { 0xD6, 0x31 },
602 { 0xD7, 0x3F },
603 { 0xD8, 0x3F },
604 { 0xD9, 0x3F },
605 { 0xDA, 0x3F },
606 { 0xDB, 0x37 },
607 { 0xDC, 0x37 },
608 { 0xDD, 0x37 },
609 { 0xDE, 0x39 },
610 { 0xDF, 0x2E },
611 { 0xE0, 0x2F },
612 { 0xE1, 0x2F },
613 { 0xE2, 0x07 },
614 { 0xB0, 0x03 },
615 { 0xC8, 0x0B },
616 { 0xC9, 0x07 },
617 { 0xC3, 0x00 },
618 { 0xE7, 0x00 },
619 { 0xC5, 0x2A },
620 { 0xDE, 0x2A },
621 { 0xCA, 0x43 },
622 { 0xC9, 0x07 },
623 { 0xE4, 0xC0 },
624 { 0xE5, 0x0D },
625 { 0xCB, 0x01 },
626 { 0xBC, 0x01 },
627 { 0xB0, 0x06 },
628 { 0xB8, 0xA5 },
629 { 0xC0, 0xA5 },
630 { 0xC7, 0x0F },
631 { 0xD5, 0x32 },
632 { 0xB8, 0x00 },
633 { 0xC0, 0x00 },
634 { 0xBC, 0x00 },
635 { 0xB0, 0x07 },
636 { 0xB1, 0x00 },
637 { 0xB2, 0x05 },
638 { 0xB3, 0x10 },
639 { 0xB4, 0x22 },
640 { 0xB5, 0x36 },
641 { 0xB6, 0x4A },
642 { 0xB7, 0x6C },
643 { 0xB8, 0x9A },
644 { 0xB9, 0xD7 },
645 { 0xBA, 0x17 },
646 { 0xBB, 0x92 },
647 { 0xBC, 0x15 },
648 { 0xBD, 0x18 },
649 { 0xBE, 0x8C },
650 { 0xBF, 0x00 },
651 { 0xC0, 0x3A },
652 { 0xC1, 0x72 },
653 { 0xC2, 0x8C },
654 { 0xC3, 0xA5 },
655 { 0xC4, 0xB1 },
656 { 0xC5, 0xBE },
657 { 0xC6, 0xCA },
658 { 0xC7, 0xD1 },
659 { 0xC8, 0xD4 },
660 { 0xC9, 0x00 },
661 { 0xCA, 0x00 },
662 { 0xCB, 0x16 },
663 { 0xCC, 0xAF },
664 { 0xCD, 0xFF },
665 { 0xCE, 0xFF },
666 { 0xB0, 0x08 },
667 { 0xB1, 0x04 },
668 { 0xB2, 0x05 },
669 { 0xB3, 0x11 },
670 { 0xB4, 0x24 },
671 { 0xB5, 0x39 },
672 { 0xB6, 0x4E },
673 { 0xB7, 0x72 },
674 { 0xB8, 0xA3 },
675 { 0xB9, 0xE1 },
676 { 0xBA, 0x25 },
677 { 0xBB, 0xA8 },
678 { 0xBC, 0x2E },
679 { 0xBD, 0x32 },
680 { 0xBE, 0xAD },
681 { 0xBF, 0x28 },
682 { 0xC0, 0x63 },
683 { 0xC1, 0x9B },
684 { 0xC2, 0xB5 },
685 { 0xC3, 0xCF },
686 { 0xC4, 0xDB },
687 { 0xC5, 0xE8 },
688 { 0xC6, 0xF5 },
689 { 0xC7, 0xFA },
690 { 0xC8, 0xFC },
691 { 0xC9, 0x00 },
692 { 0xCA, 0x00 },
693 { 0xCB, 0x16 },
694 { 0xCC, 0xAF },
695 { 0xCD, 0xFF },
696 { 0xCE, 0xFF },
697 { 0xB0, 0x09 },
698 { 0xB1, 0x04 },
699 { 0xB2, 0x04 },
700 { 0xB3, 0x0F },
701 { 0xB4, 0x22 },
702 { 0xB5, 0x37 },
703 { 0xB6, 0x4D },
704 { 0xB7, 0x71 },
705 { 0xB8, 0xA2 },
706 { 0xB9, 0xE1 },
707 { 0xBA, 0x26 },
708 { 0xBB, 0xA9 },
709 { 0xBC, 0x2F },
710 { 0xBD, 0x33 },
711 { 0xBE, 0xAC },
712 { 0xBF, 0x24 },
713 { 0xC0, 0x5D },
714 { 0xC1, 0x94 },
715 { 0xC2, 0xAC },
716 { 0xC3, 0xC5 },
717 { 0xC4, 0xD1 },
718 { 0xC5, 0xDC },
719 { 0xC6, 0xE8 },
720 { 0xC7, 0xED },
721 { 0xC8, 0xF0 },
722 { 0xC9, 0x00 },
723 { 0xCA, 0x00 },
724 { 0xCB, 0x16 },
725 { 0xCC, 0xAF },
726 { 0xCD, 0xFF },
727 { 0xCE, 0xFF },
728 { 0xB0, 0x0A },
729 { 0xB1, 0x00 },
730 { 0xB2, 0x05 },
731 { 0xB3, 0x10 },
732 { 0xB4, 0x22 },
733 { 0xB5, 0x36 },
734 { 0xB6, 0x4A },
735 { 0xB7, 0x6C },
736 { 0xB8, 0x9A },
737 { 0xB9, 0xD7 },
738 { 0xBA, 0x17 },
739 { 0xBB, 0x92 },
740 { 0xBC, 0x15 },
741 { 0xBD, 0x18 },
742 { 0xBE, 0x8C },
743 { 0xBF, 0x00 },
744 { 0xC0, 0x3A },
745 { 0xC1, 0x72 },
746 { 0xC2, 0x8C },
747 { 0xC3, 0xA5 },
748 { 0xC4, 0xB1 },
749 { 0xC5, 0xBE },
750 { 0xC6, 0xCA },
751 { 0xC7, 0xD1 },
752 { 0xC8, 0xD4 },
753 { 0xC9, 0x00 },
754 { 0xCA, 0x00 },
755 { 0xCB, 0x16 },
756 { 0xCC, 0xAF },
757 { 0xCD, 0xFF },
758 { 0xCE, 0xFF },
759 { 0xB0, 0x0B },
760 { 0xB1, 0x04 },
761 { 0xB2, 0x05 },
762 { 0xB3, 0x11 },
763 { 0xB4, 0x24 },
764 { 0xB5, 0x39 },
765 { 0xB6, 0x4E },
766 { 0xB7, 0x72 },
767 { 0xB8, 0xA3 },
768 { 0xB9, 0xE1 },
769 { 0xBA, 0x25 },
770 { 0xBB, 0xA8 },
771 { 0xBC, 0x2E },
772 { 0xBD, 0x32 },
773 { 0xBE, 0xAD },
774 { 0xBF, 0x28 },
775 { 0xC0, 0x63 },
776 { 0xC1, 0x9B },
777 { 0xC2, 0xB5 },
778 { 0xC3, 0xCF },
779 { 0xC4, 0xDB },
780 { 0xC5, 0xE8 },
781 { 0xC6, 0xF5 },
782 { 0xC7, 0xFA },
783 { 0xC8, 0xFC },
784 { 0xC9, 0x00 },
785 { 0xCA, 0x00 },
786 { 0xCB, 0x16 },
787 { 0xCC, 0xAF },
788 { 0xCD, 0xFF },
789 { 0xCE, 0xFF },
790 { 0xB0, 0x0C },
791 { 0xB1, 0x04 },
792 { 0xB2, 0x04 },
793 { 0xB3, 0x0F },
794 { 0xB4, 0x22 },
795 { 0xB5, 0x37 },
796 { 0xB6, 0x4D },
797 { 0xB7, 0x71 },
798 { 0xB8, 0xA2 },
799 { 0xB9, 0xE1 },
800 { 0xBA, 0x26 },
801 { 0xBB, 0xA9 },
802 { 0xBC, 0x2F },
803 { 0xBD, 0x33 },
804 { 0xBE, 0xAC },
805 { 0xBF, 0x24 },
806 { 0xC0, 0x5D },
807 { 0xC1, 0x94 },
808 { 0xC2, 0xAC },
809 { 0xC3, 0xC5 },
810 { 0xC4, 0xD1 },
811 { 0xC5, 0xDC },
812 { 0xC6, 0xE8 },
813 { 0xC7, 0xED },
814 { 0xC8, 0xF0 },
815 { 0xC9, 0x00 },
816 { 0xCA, 0x00 },
817 { 0xCB, 0x16 },
818 { 0xCC, 0xAF },
819 { 0xCD, 0xFF },
820 { 0xCE, 0xFF },
821 };
822
823 static const struct panel_desc boe_himax8279d10p_panel_desc = {
824 .display_mode = &default_display_mode,
825 .bpc = 8,
826 .width_mm = 135,
827 .height_mm = 216,
828 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
829 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
830 .format = MIPI_DSI_FMT_RGB888,
831 .lanes = 4,
832 .on_cmds = boe_himax8279d10p_on_cmds,
833 .on_cmds_num = 283,
834 };
835
836 static const struct of_device_id panel_of_match[] = {
837 {
838 .compatible = "boe,himax8279d8p",
839 .data = &boe_himax8279d8p_panel_desc,
840 },
841 {
842 .compatible = "boe,himax8279d10p",
843 .data = &boe_himax8279d10p_panel_desc,
844 },
845 {
846 /* sentinel */
847 }
848 };
849 MODULE_DEVICE_TABLE(of, panel_of_match);
850
panel_add(struct panel_info * pinfo)851 static int panel_add(struct panel_info *pinfo)
852 {
853 struct device *dev = &pinfo->link->dev;
854 int ret;
855
856 pinfo->pp18_gpio = devm_gpiod_get(dev, "pp18", GPIOD_OUT_HIGH);
857 if (IS_ERR(pinfo->pp18_gpio)) {
858 ret = PTR_ERR(pinfo->pp18_gpio);
859 if (ret != -EPROBE_DEFER)
860 dev_err(dev, "failed to get pp18 gpio: %d\n", ret);
861 return ret;
862 }
863
864 pinfo->pp33_gpio = devm_gpiod_get(dev, "pp33", GPIOD_OUT_HIGH);
865 if (IS_ERR(pinfo->pp33_gpio)) {
866 ret = PTR_ERR(pinfo->pp33_gpio);
867 if (ret != -EPROBE_DEFER)
868 dev_err(dev, "failed to get pp33 gpio: %d\n", ret);
869 return ret;
870 }
871
872 pinfo->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
873 if (IS_ERR(pinfo->enable_gpio)) {
874 ret = PTR_ERR(pinfo->enable_gpio);
875 if (ret != -EPROBE_DEFER)
876 dev_err(dev, "failed to get enable gpio: %d\n", ret);
877 return ret;
878 }
879
880 drm_panel_init(&pinfo->base, dev, &panel_funcs,
881 DRM_MODE_CONNECTOR_DSI);
882
883 ret = drm_panel_of_backlight(&pinfo->base);
884 if (ret)
885 return ret;
886
887 drm_panel_add(&pinfo->base);
888
889 return 0;
890 }
891
panel_probe(struct mipi_dsi_device * dsi)892 static int panel_probe(struct mipi_dsi_device *dsi)
893 {
894 struct panel_info *pinfo;
895 const struct panel_desc *desc;
896 int err;
897
898 pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
899 if (!pinfo)
900 return -ENOMEM;
901
902 desc = of_device_get_match_data(&dsi->dev);
903 dsi->mode_flags = desc->mode_flags;
904 dsi->format = desc->format;
905 dsi->lanes = desc->lanes;
906 pinfo->desc = desc;
907
908 pinfo->link = dsi;
909 mipi_dsi_set_drvdata(dsi, pinfo);
910
911 err = panel_add(pinfo);
912 if (err < 0)
913 return err;
914
915 err = mipi_dsi_attach(dsi);
916 if (err < 0)
917 drm_panel_remove(&pinfo->base);
918
919 return err;
920 }
921
panel_remove(struct mipi_dsi_device * dsi)922 static int panel_remove(struct mipi_dsi_device *dsi)
923 {
924 struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
925 int err;
926
927 err = boe_panel_disable(&pinfo->base);
928 if (err < 0)
929 dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
930
931 err = boe_panel_unprepare(&pinfo->base);
932 if (err < 0)
933 dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
934
935 err = mipi_dsi_detach(dsi);
936 if (err < 0)
937 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
938
939 drm_panel_remove(&pinfo->base);
940
941 return 0;
942 }
943
panel_shutdown(struct mipi_dsi_device * dsi)944 static void panel_shutdown(struct mipi_dsi_device *dsi)
945 {
946 struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
947
948 boe_panel_disable(&pinfo->base);
949 boe_panel_unprepare(&pinfo->base);
950 }
951
952 static struct mipi_dsi_driver panel_driver = {
953 .driver = {
954 .name = "panel-boe-himax8279d",
955 .of_match_table = panel_of_match,
956 },
957 .probe = panel_probe,
958 .remove = panel_remove,
959 .shutdown = panel_shutdown,
960 };
961 module_mipi_dsi_driver(panel_driver);
962
963 MODULE_AUTHOR("Jerry Han <jerry.han.hq@gmail.com>");
964 MODULE_DESCRIPTION("Boe Himax8279d driver");
965 MODULE_LICENSE("GPL v2");
966