1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 BayLibre, SAS
4  * Author: Neil Armstrong <narmstrong@baylibre.com>
5  */
6 
7 #include <linux/delay.h>
8 #include <linux/gpio/consumer.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/regulator/consumer.h>
12 
13 #include <video/mipi_display.h>
14 
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_device.h>
17 #include <drm/drm_mipi_dsi.h>
18 #include <drm/drm_modes.h>
19 #include <drm/drm_panel.h>
20 
21 struct khadas_ts050_panel {
22 	struct drm_panel base;
23 	struct mipi_dsi_device *link;
24 
25 	struct regulator *supply;
26 	struct gpio_desc *reset_gpio;
27 	struct gpio_desc *enable_gpio;
28 
29 	bool prepared;
30 	bool enabled;
31 };
32 
33 struct khadas_ts050_panel_cmd {
34 	u8 cmd;
35 	u8 data;
36 };
37 
38 /* Only the CMD1 User Command set is documented */
39 static const struct khadas_ts050_panel_cmd init_code[] = {
40 	/* Select Unknown CMD Page (Undocumented) */
41 	{0xff, 0xee},
42 	/* Reload CMD1: Don't reload default value to register */
43 	{0xfb, 0x01},
44 	{0x1f, 0x45},
45 	{0x24, 0x4f},
46 	{0x38, 0xc8},
47 	{0x39, 0x27},
48 	{0x1e, 0x77},
49 	{0x1d, 0x0f},
50 	{0x7e, 0x71},
51 	{0x7c, 0x03},
52 	{0xff, 0x00},
53 	{0xfb, 0x01},
54 	{0x35, 0x01},
55 	/* Select CMD2 Page0 (Undocumented) */
56 	{0xff, 0x01},
57 	/* Reload CMD1: Don't reload default value to register */
58 	{0xfb, 0x01},
59 	{0x00, 0x01},
60 	{0x01, 0x55},
61 	{0x02, 0x40},
62 	{0x05, 0x40},
63 	{0x06, 0x4a},
64 	{0x07, 0x24},
65 	{0x08, 0x0c},
66 	{0x0b, 0x7d},
67 	{0x0c, 0x7d},
68 	{0x0e, 0xb0},
69 	{0x0f, 0xae},
70 	{0x11, 0x10},
71 	{0x12, 0x10},
72 	{0x13, 0x03},
73 	{0x14, 0x4a},
74 	{0x15, 0x12},
75 	{0x16, 0x12},
76 	{0x18, 0x00},
77 	{0x19, 0x77},
78 	{0x1a, 0x55},
79 	{0x1b, 0x13},
80 	{0x1c, 0x00},
81 	{0x1d, 0x00},
82 	{0x1e, 0x13},
83 	{0x1f, 0x00},
84 	{0x23, 0x00},
85 	{0x24, 0x00},
86 	{0x25, 0x00},
87 	{0x26, 0x00},
88 	{0x27, 0x00},
89 	{0x28, 0x00},
90 	{0x35, 0x00},
91 	{0x66, 0x00},
92 	{0x58, 0x82},
93 	{0x59, 0x02},
94 	{0x5a, 0x02},
95 	{0x5b, 0x02},
96 	{0x5c, 0x82},
97 	{0x5d, 0x82},
98 	{0x5e, 0x02},
99 	{0x5f, 0x02},
100 	{0x72, 0x31},
101 	/* Select CMD2 Page4 (Undocumented) */
102 	{0xff, 0x05},
103 	/* Reload CMD1: Don't reload default value to register */
104 	{0xfb, 0x01},
105 	{0x00, 0x01},
106 	{0x01, 0x0b},
107 	{0x02, 0x0c},
108 	{0x03, 0x09},
109 	{0x04, 0x0a},
110 	{0x05, 0x00},
111 	{0x06, 0x0f},
112 	{0x07, 0x10},
113 	{0x08, 0x00},
114 	{0x09, 0x00},
115 	{0x0a, 0x00},
116 	{0x0b, 0x00},
117 	{0x0c, 0x00},
118 	{0x0d, 0x13},
119 	{0x0e, 0x15},
120 	{0x0f, 0x17},
121 	{0x10, 0x01},
122 	{0x11, 0x0b},
123 	{0x12, 0x0c},
124 	{0x13, 0x09},
125 	{0x14, 0x0a},
126 	{0x15, 0x00},
127 	{0x16, 0x0f},
128 	{0x17, 0x10},
129 	{0x18, 0x00},
130 	{0x19, 0x00},
131 	{0x1a, 0x00},
132 	{0x1b, 0x00},
133 	{0x1c, 0x00},
134 	{0x1d, 0x13},
135 	{0x1e, 0x15},
136 	{0x1f, 0x17},
137 	{0x20, 0x00},
138 	{0x21, 0x03},
139 	{0x22, 0x01},
140 	{0x23, 0x40},
141 	{0x24, 0x40},
142 	{0x25, 0xed},
143 	{0x29, 0x58},
144 	{0x2a, 0x12},
145 	{0x2b, 0x01},
146 	{0x4b, 0x06},
147 	{0x4c, 0x11},
148 	{0x4d, 0x20},
149 	{0x4e, 0x02},
150 	{0x4f, 0x02},
151 	{0x50, 0x20},
152 	{0x51, 0x61},
153 	{0x52, 0x01},
154 	{0x53, 0x63},
155 	{0x54, 0x77},
156 	{0x55, 0xed},
157 	{0x5b, 0x00},
158 	{0x5c, 0x00},
159 	{0x5d, 0x00},
160 	{0x5e, 0x00},
161 	{0x5f, 0x15},
162 	{0x60, 0x75},
163 	{0x61, 0x00},
164 	{0x62, 0x00},
165 	{0x63, 0x00},
166 	{0x64, 0x00},
167 	{0x65, 0x00},
168 	{0x66, 0x00},
169 	{0x67, 0x00},
170 	{0x68, 0x04},
171 	{0x69, 0x00},
172 	{0x6a, 0x00},
173 	{0x6c, 0x40},
174 	{0x75, 0x01},
175 	{0x76, 0x01},
176 	{0x7a, 0x80},
177 	{0x7b, 0xa3},
178 	{0x7c, 0xd8},
179 	{0x7d, 0x60},
180 	{0x7f, 0x15},
181 	{0x80, 0x81},
182 	{0x83, 0x05},
183 	{0x93, 0x08},
184 	{0x94, 0x10},
185 	{0x8a, 0x00},
186 	{0x9b, 0x0f},
187 	{0xea, 0xff},
188 	{0xec, 0x00},
189 	/* Select CMD2 Page0 (Undocumented) */
190 	{0xff, 0x01},
191 	/* Reload CMD1: Don't reload default value to register */
192 	{0xfb, 0x01},
193 	{0x75, 0x00},
194 	{0x76, 0xdf},
195 	{0x77, 0x00},
196 	{0x78, 0xe4},
197 	{0x79, 0x00},
198 	{0x7a, 0xed},
199 	{0x7b, 0x00},
200 	{0x7c, 0xf6},
201 	{0x7d, 0x00},
202 	{0x7e, 0xff},
203 	{0x7f, 0x01},
204 	{0x80, 0x07},
205 	{0x81, 0x01},
206 	{0x82, 0x10},
207 	{0x83, 0x01},
208 	{0x84, 0x18},
209 	{0x85, 0x01},
210 	{0x86, 0x20},
211 	{0x87, 0x01},
212 	{0x88, 0x3d},
213 	{0x89, 0x01},
214 	{0x8a, 0x56},
215 	{0x8b, 0x01},
216 	{0x8c, 0x84},
217 	{0x8d, 0x01},
218 	{0x8e, 0xab},
219 	{0x8f, 0x01},
220 	{0x90, 0xec},
221 	{0x91, 0x02},
222 	{0x92, 0x22},
223 	{0x93, 0x02},
224 	{0x94, 0x23},
225 	{0x95, 0x02},
226 	{0x96, 0x55},
227 	{0x97, 0x02},
228 	{0x98, 0x8b},
229 	{0x99, 0x02},
230 	{0x9a, 0xaf},
231 	{0x9b, 0x02},
232 	{0x9c, 0xdf},
233 	{0x9d, 0x03},
234 	{0x9e, 0x01},
235 	{0x9f, 0x03},
236 	{0xa0, 0x2c},
237 	{0xa2, 0x03},
238 	{0xa3, 0x39},
239 	{0xa4, 0x03},
240 	{0xa5, 0x47},
241 	{0xa6, 0x03},
242 	{0xa7, 0x56},
243 	{0xa9, 0x03},
244 	{0xaa, 0x66},
245 	{0xab, 0x03},
246 	{0xac, 0x76},
247 	{0xad, 0x03},
248 	{0xae, 0x85},
249 	{0xaf, 0x03},
250 	{0xb0, 0x90},
251 	{0xb1, 0x03},
252 	{0xb2, 0xcb},
253 	{0xb3, 0x00},
254 	{0xb4, 0xdf},
255 	{0xb5, 0x00},
256 	{0xb6, 0xe4},
257 	{0xb7, 0x00},
258 	{0xb8, 0xed},
259 	{0xb9, 0x00},
260 	{0xba, 0xf6},
261 	{0xbb, 0x00},
262 	{0xbc, 0xff},
263 	{0xbd, 0x01},
264 	{0xbe, 0x07},
265 	{0xbf, 0x01},
266 	{0xc0, 0x10},
267 	{0xc1, 0x01},
268 	{0xc2, 0x18},
269 	{0xc3, 0x01},
270 	{0xc4, 0x20},
271 	{0xc5, 0x01},
272 	{0xc6, 0x3d},
273 	{0xc7, 0x01},
274 	{0xc8, 0x56},
275 	{0xc9, 0x01},
276 	{0xca, 0x84},
277 	{0xcb, 0x01},
278 	{0xcc, 0xab},
279 	{0xcd, 0x01},
280 	{0xce, 0xec},
281 	{0xcf, 0x02},
282 	{0xd0, 0x22},
283 	{0xd1, 0x02},
284 	{0xd2, 0x23},
285 	{0xd3, 0x02},
286 	{0xd4, 0x55},
287 	{0xd5, 0x02},
288 	{0xd6, 0x8b},
289 	{0xd7, 0x02},
290 	{0xd8, 0xaf},
291 	{0xd9, 0x02},
292 	{0xda, 0xdf},
293 	{0xdb, 0x03},
294 	{0xdc, 0x01},
295 	{0xdd, 0x03},
296 	{0xde, 0x2c},
297 	{0xdf, 0x03},
298 	{0xe0, 0x39},
299 	{0xe1, 0x03},
300 	{0xe2, 0x47},
301 	{0xe3, 0x03},
302 	{0xe4, 0x56},
303 	{0xe5, 0x03},
304 	{0xe6, 0x66},
305 	{0xe7, 0x03},
306 	{0xe8, 0x76},
307 	{0xe9, 0x03},
308 	{0xea, 0x85},
309 	{0xeb, 0x03},
310 	{0xec, 0x90},
311 	{0xed, 0x03},
312 	{0xee, 0xcb},
313 	{0xef, 0x00},
314 	{0xf0, 0xbb},
315 	{0xf1, 0x00},
316 	{0xf2, 0xc0},
317 	{0xf3, 0x00},
318 	{0xf4, 0xcc},
319 	{0xf5, 0x00},
320 	{0xf6, 0xd6},
321 	{0xf7, 0x00},
322 	{0xf8, 0xe1},
323 	{0xf9, 0x00},
324 	{0xfa, 0xea},
325 	/* Select CMD2 Page2 (Undocumented) */
326 	{0xff, 0x02},
327 	/* Reload CMD1: Don't reload default value to register */
328 	{0xfb, 0x01},
329 	{0x00, 0x00},
330 	{0x01, 0xf4},
331 	{0x02, 0x00},
332 	{0x03, 0xef},
333 	{0x04, 0x01},
334 	{0x05, 0x07},
335 	{0x06, 0x01},
336 	{0x07, 0x28},
337 	{0x08, 0x01},
338 	{0x09, 0x44},
339 	{0x0a, 0x01},
340 	{0x0b, 0x76},
341 	{0x0c, 0x01},
342 	{0x0d, 0xa0},
343 	{0x0e, 0x01},
344 	{0x0f, 0xe7},
345 	{0x10, 0x02},
346 	{0x11, 0x1f},
347 	{0x12, 0x02},
348 	{0x13, 0x22},
349 	{0x14, 0x02},
350 	{0x15, 0x54},
351 	{0x16, 0x02},
352 	{0x17, 0x8b},
353 	{0x18, 0x02},
354 	{0x19, 0xaf},
355 	{0x1a, 0x02},
356 	{0x1b, 0xe0},
357 	{0x1c, 0x03},
358 	{0x1d, 0x01},
359 	{0x1e, 0x03},
360 	{0x1f, 0x2d},
361 	{0x20, 0x03},
362 	{0x21, 0x39},
363 	{0x22, 0x03},
364 	{0x23, 0x47},
365 	{0x24, 0x03},
366 	{0x25, 0x57},
367 	{0x26, 0x03},
368 	{0x27, 0x65},
369 	{0x28, 0x03},
370 	{0x29, 0x77},
371 	{0x2a, 0x03},
372 	{0x2b, 0x85},
373 	{0x2d, 0x03},
374 	{0x2f, 0x8f},
375 	{0x30, 0x03},
376 	{0x31, 0xcb},
377 	{0x32, 0x00},
378 	{0x33, 0xbb},
379 	{0x34, 0x00},
380 	{0x35, 0xc0},
381 	{0x36, 0x00},
382 	{0x37, 0xcc},
383 	{0x38, 0x00},
384 	{0x39, 0xd6},
385 	{0x3a, 0x00},
386 	{0x3b, 0xe1},
387 	{0x3d, 0x00},
388 	{0x3f, 0xea},
389 	{0x40, 0x00},
390 	{0x41, 0xf4},
391 	{0x42, 0x00},
392 	{0x43, 0xfe},
393 	{0x44, 0x01},
394 	{0x45, 0x07},
395 	{0x46, 0x01},
396 	{0x47, 0x28},
397 	{0x48, 0x01},
398 	{0x49, 0x44},
399 	{0x4a, 0x01},
400 	{0x4b, 0x76},
401 	{0x4c, 0x01},
402 	{0x4d, 0xa0},
403 	{0x4e, 0x01},
404 	{0x4f, 0xe7},
405 	{0x50, 0x02},
406 	{0x51, 0x1f},
407 	{0x52, 0x02},
408 	{0x53, 0x22},
409 	{0x54, 0x02},
410 	{0x55, 0x54},
411 	{0x56, 0x02},
412 	{0x58, 0x8b},
413 	{0x59, 0x02},
414 	{0x5a, 0xaf},
415 	{0x5b, 0x02},
416 	{0x5c, 0xe0},
417 	{0x5d, 0x03},
418 	{0x5e, 0x01},
419 	{0x5f, 0x03},
420 	{0x60, 0x2d},
421 	{0x61, 0x03},
422 	{0x62, 0x39},
423 	{0x63, 0x03},
424 	{0x64, 0x47},
425 	{0x65, 0x03},
426 	{0x66, 0x57},
427 	{0x67, 0x03},
428 	{0x68, 0x65},
429 	{0x69, 0x03},
430 	{0x6a, 0x77},
431 	{0x6b, 0x03},
432 	{0x6c, 0x85},
433 	{0x6d, 0x03},
434 	{0x6e, 0x8f},
435 	{0x6f, 0x03},
436 	{0x70, 0xcb},
437 	{0x71, 0x00},
438 	{0x72, 0x00},
439 	{0x73, 0x00},
440 	{0x74, 0x21},
441 	{0x75, 0x00},
442 	{0x76, 0x4c},
443 	{0x77, 0x00},
444 	{0x78, 0x6b},
445 	{0x79, 0x00},
446 	{0x7a, 0x85},
447 	{0x7b, 0x00},
448 	{0x7c, 0x9a},
449 	{0x7d, 0x00},
450 	{0x7e, 0xad},
451 	{0x7f, 0x00},
452 	{0x80, 0xbe},
453 	{0x81, 0x00},
454 	{0x82, 0xcd},
455 	{0x83, 0x01},
456 	{0x84, 0x01},
457 	{0x85, 0x01},
458 	{0x86, 0x29},
459 	{0x87, 0x01},
460 	{0x88, 0x68},
461 	{0x89, 0x01},
462 	{0x8a, 0x98},
463 	{0x8b, 0x01},
464 	{0x8c, 0xe5},
465 	{0x8d, 0x02},
466 	{0x8e, 0x1e},
467 	{0x8f, 0x02},
468 	{0x90, 0x30},
469 	{0x91, 0x02},
470 	{0x92, 0x52},
471 	{0x93, 0x02},
472 	{0x94, 0x88},
473 	{0x95, 0x02},
474 	{0x96, 0xaa},
475 	{0x97, 0x02},
476 	{0x98, 0xd7},
477 	{0x99, 0x02},
478 	{0x9a, 0xf7},
479 	{0x9b, 0x03},
480 	{0x9c, 0x21},
481 	{0x9d, 0x03},
482 	{0x9e, 0x2e},
483 	{0x9f, 0x03},
484 	{0xa0, 0x3d},
485 	{0xa2, 0x03},
486 	{0xa3, 0x4c},
487 	{0xa4, 0x03},
488 	{0xa5, 0x5e},
489 	{0xa6, 0x03},
490 	{0xa7, 0x71},
491 	{0xa9, 0x03},
492 	{0xaa, 0x86},
493 	{0xab, 0x03},
494 	{0xac, 0x94},
495 	{0xad, 0x03},
496 	{0xae, 0xfa},
497 	{0xaf, 0x00},
498 	{0xb0, 0x00},
499 	{0xb1, 0x00},
500 	{0xb2, 0x21},
501 	{0xb3, 0x00},
502 	{0xb4, 0x4c},
503 	{0xb5, 0x00},
504 	{0xb6, 0x6b},
505 	{0xb7, 0x00},
506 	{0xb8, 0x85},
507 	{0xb9, 0x00},
508 	{0xba, 0x9a},
509 	{0xbb, 0x00},
510 	{0xbc, 0xad},
511 	{0xbd, 0x00},
512 	{0xbe, 0xbe},
513 	{0xbf, 0x00},
514 	{0xc0, 0xcd},
515 	{0xc1, 0x01},
516 	{0xc2, 0x01},
517 	{0xc3, 0x01},
518 	{0xc4, 0x29},
519 	{0xc5, 0x01},
520 	{0xc6, 0x68},
521 	{0xc7, 0x01},
522 	{0xc8, 0x98},
523 	{0xc9, 0x01},
524 	{0xca, 0xe5},
525 	{0xcb, 0x02},
526 	{0xcc, 0x1e},
527 	{0xcd, 0x02},
528 	{0xce, 0x20},
529 	{0xcf, 0x02},
530 	{0xd0, 0x52},
531 	{0xd1, 0x02},
532 	{0xd2, 0x88},
533 	{0xd3, 0x02},
534 	{0xd4, 0xaa},
535 	{0xd5, 0x02},
536 	{0xd6, 0xd7},
537 	{0xd7, 0x02},
538 	{0xd8, 0xf7},
539 	{0xd9, 0x03},
540 	{0xda, 0x21},
541 	{0xdb, 0x03},
542 	{0xdc, 0x2e},
543 	{0xdd, 0x03},
544 	{0xde, 0x3d},
545 	{0xdf, 0x03},
546 	{0xe0, 0x4c},
547 	{0xe1, 0x03},
548 	{0xe2, 0x5e},
549 	{0xe3, 0x03},
550 	{0xe4, 0x71},
551 	{0xe5, 0x03},
552 	{0xe6, 0x86},
553 	{0xe7, 0x03},
554 	{0xe8, 0x94},
555 	{0xe9, 0x03},
556 	{0xea, 0xfa},
557 	/* Select CMD2 Page0 (Undocumented) */
558 	{0xff, 0x01},
559 	/* Reload CMD1: Don't reload default value to register */
560 	{0xfb, 0x01},
561 	/* Select CMD2 Page1 (Undocumented) */
562 	{0xff, 0x02},
563 	/* Reload CMD1: Don't reload default value to register */
564 	{0xfb, 0x01},
565 	/* Select CMD2 Page3 (Undocumented) */
566 	{0xff, 0x04},
567 	/* Reload CMD1: Don't reload default value to register */
568 	{0xfb, 0x01},
569 	/* Select CMD1 */
570 	{0xff, 0x00},
571 	{0xd3, 0x05}, /* RGBMIPICTRL: VSYNC back porch = 5 */
572 	{0xd4, 0x04}, /* RGBMIPICTRL: VSYNC front porch = 4 */
573 };
574 
575 static inline
576 struct khadas_ts050_panel *to_khadas_ts050_panel(struct drm_panel *panel)
577 {
578 	return container_of(panel, struct khadas_ts050_panel, base);
579 }
580 
581 static int khadas_ts050_panel_prepare(struct drm_panel *panel)
582 {
583 	struct khadas_ts050_panel *khadas_ts050 = to_khadas_ts050_panel(panel);
584 	unsigned int i;
585 	int err;
586 
587 	if (khadas_ts050->prepared)
588 		return 0;
589 
590 	gpiod_set_value_cansleep(khadas_ts050->enable_gpio, 0);
591 
592 	err = regulator_enable(khadas_ts050->supply);
593 	if (err < 0)
594 		return err;
595 
596 	gpiod_set_value_cansleep(khadas_ts050->enable_gpio, 1);
597 
598 	msleep(60);
599 
600 	gpiod_set_value_cansleep(khadas_ts050->reset_gpio, 1);
601 
602 	usleep_range(10000, 11000);
603 
604 	gpiod_set_value_cansleep(khadas_ts050->reset_gpio, 0);
605 
606 	/* Select CMD2 page 4 (Undocumented) */
607 	mipi_dsi_dcs_write(khadas_ts050->link, 0xff, (u8[]){ 0x05 }, 1);
608 
609 	/* Reload CMD1: Don't reload default value to register */
610 	mipi_dsi_dcs_write(khadas_ts050->link, 0xfb, (u8[]){ 0x01 }, 1);
611 
612 	mipi_dsi_dcs_write(khadas_ts050->link, 0xc5, (u8[]){ 0x01 }, 1);
613 
614 	msleep(100);
615 
616 	for (i = 0; i < ARRAY_SIZE(init_code); i++) {
617 		err = mipi_dsi_dcs_write(khadas_ts050->link,
618 					 init_code[i].cmd,
619 					 &init_code[i].data, 1);
620 		if (err < 0) {
621 			dev_err(panel->dev, "failed write cmds: %d\n", err);
622 			goto poweroff;
623 		}
624 	}
625 
626 	err = mipi_dsi_dcs_exit_sleep_mode(khadas_ts050->link);
627 	if (err < 0) {
628 		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
629 		goto poweroff;
630 	}
631 
632 	msleep(120);
633 
634 	/* Select CMD1 */
635 	mipi_dsi_dcs_write(khadas_ts050->link, 0xff, (u8[]){ 0x00 }, 1);
636 
637 	err = mipi_dsi_dcs_set_tear_on(khadas_ts050->link,
638 				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
639 	if (err < 0) {
640 		dev_err(panel->dev, "failed to set tear on: %d\n", err);
641 		goto poweroff;
642 	}
643 
644 	err = mipi_dsi_dcs_set_display_on(khadas_ts050->link);
645 	if (err < 0) {
646 		dev_err(panel->dev, "failed to set display on: %d\n", err);
647 		goto poweroff;
648 	}
649 
650 	usleep_range(10000, 11000);
651 
652 	khadas_ts050->prepared = true;
653 
654 	return 0;
655 
656 poweroff:
657 	gpiod_set_value_cansleep(khadas_ts050->enable_gpio, 0);
658 	gpiod_set_value_cansleep(khadas_ts050->reset_gpio, 1);
659 
660 	regulator_disable(khadas_ts050->supply);
661 
662 	return err;
663 }
664 
665 static int khadas_ts050_panel_unprepare(struct drm_panel *panel)
666 {
667 	struct khadas_ts050_panel *khadas_ts050 = to_khadas_ts050_panel(panel);
668 	int err;
669 
670 	if (!khadas_ts050->prepared)
671 		return 0;
672 
673 	khadas_ts050->prepared = false;
674 
675 	err = mipi_dsi_dcs_enter_sleep_mode(khadas_ts050->link);
676 	if (err < 0)
677 		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
678 
679 	msleep(150);
680 
681 	gpiod_set_value_cansleep(khadas_ts050->enable_gpio, 0);
682 	gpiod_set_value_cansleep(khadas_ts050->reset_gpio, 1);
683 
684 	err = regulator_disable(khadas_ts050->supply);
685 	if (err < 0)
686 		return err;
687 
688 	return 0;
689 }
690 
691 static int khadas_ts050_panel_enable(struct drm_panel *panel)
692 {
693 	struct khadas_ts050_panel *khadas_ts050 = to_khadas_ts050_panel(panel);
694 
695 	khadas_ts050->enabled = true;
696 
697 	return 0;
698 }
699 
700 static int khadas_ts050_panel_disable(struct drm_panel *panel)
701 {
702 	struct khadas_ts050_panel *khadas_ts050 = to_khadas_ts050_panel(panel);
703 	int err;
704 
705 	if (!khadas_ts050->enabled)
706 		return 0;
707 
708 	err = mipi_dsi_dcs_set_display_off(khadas_ts050->link);
709 	if (err < 0)
710 		dev_err(panel->dev, "failed to set display off: %d\n", err);
711 
712 	usleep_range(10000, 11000);
713 
714 	khadas_ts050->enabled = false;
715 
716 	return 0;
717 }
718 
719 static const struct drm_display_mode default_mode = {
720 	.clock = 120000,
721 	.hdisplay = 1088,
722 	.hsync_start = 1088 + 104,
723 	.hsync_end = 1088 + 104 + 4,
724 	.htotal = 1088 + 104 + 4 + 127,
725 	.vdisplay = 1920,
726 	.vsync_start = 1920 + 4,
727 	.vsync_end = 1920 + 4 + 2,
728 	.vtotal = 1920 + 4 + 2 + 3,
729 	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
730 };
731 
732 static int khadas_ts050_panel_get_modes(struct drm_panel *panel,
733 					struct drm_connector *connector)
734 {
735 	struct drm_display_mode *mode;
736 
737 	mode = drm_mode_duplicate(connector->dev, &default_mode);
738 	if (!mode) {
739 		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
740 			default_mode.hdisplay, default_mode.vdisplay,
741 			drm_mode_vrefresh(&default_mode));
742 		return -ENOMEM;
743 	}
744 
745 	drm_mode_set_name(mode);
746 
747 	drm_mode_probed_add(connector, mode);
748 
749 	connector->display_info.width_mm = 64;
750 	connector->display_info.height_mm = 118;
751 	connector->display_info.bpc = 8;
752 
753 	return 1;
754 }
755 
756 static const struct drm_panel_funcs khadas_ts050_panel_funcs = {
757 	.prepare = khadas_ts050_panel_prepare,
758 	.unprepare = khadas_ts050_panel_unprepare,
759 	.enable = khadas_ts050_panel_enable,
760 	.disable = khadas_ts050_panel_disable,
761 	.get_modes = khadas_ts050_panel_get_modes,
762 };
763 
764 static const struct of_device_id khadas_ts050_of_match[] = {
765 	{ .compatible = "khadas,ts050", },
766 	{ /* sentinel */ }
767 };
768 MODULE_DEVICE_TABLE(of, khadas_ts050_of_match);
769 
770 static int khadas_ts050_panel_add(struct khadas_ts050_panel *khadas_ts050)
771 {
772 	struct device *dev = &khadas_ts050->link->dev;
773 	int err;
774 
775 	khadas_ts050->supply = devm_regulator_get(dev, "power");
776 	if (IS_ERR(khadas_ts050->supply))
777 		return dev_err_probe(dev, PTR_ERR(khadas_ts050->supply),
778 				     "failed to get power supply");
779 
780 	khadas_ts050->reset_gpio = devm_gpiod_get(dev, "reset",
781 						  GPIOD_OUT_LOW);
782 	if (IS_ERR(khadas_ts050->reset_gpio))
783 		return dev_err_probe(dev, PTR_ERR(khadas_ts050->reset_gpio),
784 				     "failed to get reset gpio");
785 
786 	khadas_ts050->enable_gpio = devm_gpiod_get(dev, "enable",
787 						   GPIOD_OUT_HIGH);
788 	if (IS_ERR(khadas_ts050->enable_gpio))
789 		return dev_err_probe(dev, PTR_ERR(khadas_ts050->enable_gpio),
790 				     "failed to get enable gpio");
791 
792 	drm_panel_init(&khadas_ts050->base, &khadas_ts050->link->dev,
793 		       &khadas_ts050_panel_funcs, DRM_MODE_CONNECTOR_DSI);
794 
795 	err = drm_panel_of_backlight(&khadas_ts050->base);
796 	if (err)
797 		return err;
798 
799 	drm_panel_add(&khadas_ts050->base);
800 
801 	return 0;
802 }
803 
804 static int khadas_ts050_panel_probe(struct mipi_dsi_device *dsi)
805 {
806 	struct khadas_ts050_panel *khadas_ts050;
807 	int err;
808 
809 	dsi->lanes = 4;
810 	dsi->format = MIPI_DSI_FMT_RGB888;
811 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
812 			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
813 
814 	khadas_ts050 = devm_kzalloc(&dsi->dev, sizeof(*khadas_ts050),
815 				    GFP_KERNEL);
816 	if (!khadas_ts050)
817 		return -ENOMEM;
818 
819 	mipi_dsi_set_drvdata(dsi, khadas_ts050);
820 	khadas_ts050->link = dsi;
821 
822 	err = khadas_ts050_panel_add(khadas_ts050);
823 	if (err < 0)
824 		return err;
825 
826 	err = mipi_dsi_attach(dsi);
827 	if (err)
828 		drm_panel_remove(&khadas_ts050->base);
829 
830 	return err;
831 }
832 
833 static void khadas_ts050_panel_remove(struct mipi_dsi_device *dsi)
834 {
835 	struct khadas_ts050_panel *khadas_ts050 = mipi_dsi_get_drvdata(dsi);
836 	int err;
837 
838 	err = mipi_dsi_detach(dsi);
839 	if (err < 0)
840 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
841 
842 	drm_panel_remove(&khadas_ts050->base);
843 	drm_panel_disable(&khadas_ts050->base);
844 	drm_panel_unprepare(&khadas_ts050->base);
845 }
846 
847 static void khadas_ts050_panel_shutdown(struct mipi_dsi_device *dsi)
848 {
849 	struct khadas_ts050_panel *khadas_ts050 = mipi_dsi_get_drvdata(dsi);
850 
851 	drm_panel_disable(&khadas_ts050->base);
852 	drm_panel_unprepare(&khadas_ts050->base);
853 }
854 
855 static struct mipi_dsi_driver khadas_ts050_panel_driver = {
856 	.driver = {
857 		.name = "panel-khadas-ts050",
858 		.of_match_table = khadas_ts050_of_match,
859 	},
860 	.probe = khadas_ts050_panel_probe,
861 	.remove = khadas_ts050_panel_remove,
862 	.shutdown = khadas_ts050_panel_shutdown,
863 };
864 module_mipi_dsi_driver(khadas_ts050_panel_driver);
865 
866 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
867 MODULE_DESCRIPTION("Khadas TS050 panel driver");
868 MODULE_LICENSE("GPL v2");
869