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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
922 static void 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 
942 static void panel_shutdown(struct mipi_dsi_device *dsi)
943 {
944 	struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
945 
946 	boe_panel_disable(&pinfo->base);
947 	boe_panel_unprepare(&pinfo->base);
948 }
949 
950 static struct mipi_dsi_driver panel_driver = {
951 	.driver = {
952 		.name = "panel-boe-himax8279d",
953 		.of_match_table = panel_of_match,
954 	},
955 	.probe = panel_probe,
956 	.remove = panel_remove,
957 	.shutdown = panel_shutdown,
958 };
959 module_mipi_dsi_driver(panel_driver);
960 
961 MODULE_AUTHOR("Jerry Han <jerry.han.hq@gmail.com>");
962 MODULE_DESCRIPTION("Boe Himax8279d driver");
963 MODULE_LICENSE("GPL v2");
964