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