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