1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH |
4 | * |
5 | * base on panel-kingdisplay-kd097d04.c |
6 | * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd |
7 | */ |
8 | |
9 | #include <linux/backlight.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_device.h> |
15 | #include <linux/regulator/consumer.h> |
16 | |
17 | #include <video/mipi_display.h> |
18 | |
19 | #include <drm/drm_crtc.h> |
20 | #include <drm/drm_device.h> |
21 | #include <drm/drm_mipi_dsi.h> |
22 | #include <drm/drm_modes.h> |
23 | #include <drm/drm_panel.h> |
24 | |
25 | struct ltk500hd1829_cmd { |
26 | char cmd; |
27 | char data; |
28 | }; |
29 | |
30 | struct ltk500hd1829_desc { |
31 | const struct drm_display_mode *mode; |
32 | const struct ltk500hd1829_cmd *init; |
33 | unsigned int num_init; |
34 | }; |
35 | |
36 | struct ltk500hd1829 { |
37 | struct device *dev; |
38 | struct drm_panel panel; |
39 | struct gpio_desc *reset_gpio; |
40 | struct regulator *vcc; |
41 | struct regulator *iovcc; |
42 | const struct ltk500hd1829_desc *panel_desc; |
43 | bool prepared; |
44 | }; |
45 | |
46 | static const struct ltk500hd1829_cmd ltk101b4029w_init[] = { |
47 | /* Page0 */ |
48 | { 0xE0, 0x00 }, |
49 | /* PASSWORD */ |
50 | { 0xE1, 0x93 }, |
51 | { 0xE2, 0x65 }, |
52 | { 0xE3, 0xF8 }, |
53 | { 0x80, 0x03 }, /* 0X03:4-LANE; 0X02:3-LANE; 0X01:2-LANE */ |
54 | /* Page1 */ |
55 | { 0xE0, 0x01 }, |
56 | /* Set VCOM */ |
57 | { 0x00, 0x00 }, |
58 | { 0x01, 0x6F }, |
59 | /* Set Gamma Power, VGMP,VGMN,VGSP,VGSN */ |
60 | { 0x17, 0x00 }, |
61 | { 0x18, 0xAF }, /* 4.3V */ |
62 | { 0x19, 0x01 }, /* 0.3V */ |
63 | { 0x1A, 0x00 }, |
64 | { 0x1B, 0xAF }, /* 4.3V */ |
65 | { 0x1C, 0x01 }, /* 0.3V */ |
66 | /* Set Gate Power */ |
67 | { 0x1F, 0x3E }, /* VGH_R = 15V */ |
68 | { 0x20, 0x28 }, /* VGL_R = -12V */ |
69 | { 0x21, 0x28 }, /* VGL_R2 = -12V */ |
70 | { 0x22, 0x7E }, |
71 | /* SETPANEL */ |
72 | { 0x35, 0x26 }, |
73 | { 0x37, 0x09 }, |
74 | /* SET RGBCYC */ |
75 | { 0x38, 0x04 }, |
76 | { 0x39, 0x00 }, |
77 | { 0x3A, 0x01 }, |
78 | { 0x3C, 0x7C }, |
79 | { 0x3D, 0xFF }, |
80 | { 0x3E, 0xFF }, |
81 | { 0x3F, 0x7F }, |
82 | /* Set TCON */ |
83 | { 0x40, 0x06 }, /* RSO = 800 RGB */ |
84 | { 0x41, 0xA0 }, /* LN = 640->1280 line */ |
85 | { 0x42, 0x81 }, |
86 | { 0x43, 0x08 }, /* VFP = 8 */ |
87 | { 0x44, 0x0B }, /* VBP = 12 */ |
88 | { 0x45, 0x28 }, /* HBP = 40 */ |
89 | /* power voltage */ |
90 | { 0x55, 0x0F }, /* DCDCM = 0001, JD PWR_IC */ |
91 | { 0x57, 0x69 }, |
92 | { 0x59, 0x0A }, /* VCL = -2.9V */ |
93 | { 0x5A, 0x28 }, /* VGH = 15V */ |
94 | { 0x5B, 0x14 }, /* VGL = -11V */ |
95 | /* Gamma */ |
96 | { 0x5D, 0x7C }, |
97 | { 0x5E, 0x65 }, |
98 | { 0x5F, 0x55 }, |
99 | { 0x60, 0x47 }, |
100 | { 0x61, 0x43 }, |
101 | { 0x62, 0x32 }, |
102 | { 0x63, 0x34 }, |
103 | { 0x64, 0x1C }, |
104 | { 0x65, 0x33 }, |
105 | { 0x66, 0x31 }, |
106 | { 0x67, 0x30 }, |
107 | { 0x68, 0x4E }, |
108 | { 0x69, 0x3C }, |
109 | { 0x6A, 0x44 }, |
110 | { 0x6B, 0x35 }, |
111 | { 0x6C, 0x31 }, |
112 | { 0x6D, 0x23 }, |
113 | { 0x6E, 0x11 }, |
114 | { 0x6F, 0x00 }, |
115 | { 0x70, 0x7C }, |
116 | { 0x71, 0x65 }, |
117 | { 0x72, 0x55 }, |
118 | { 0x73, 0x47 }, |
119 | { 0x74, 0x43 }, |
120 | { 0x75, 0x32 }, |
121 | { 0x76, 0x34 }, |
122 | { 0x77, 0x1C }, |
123 | { 0x78, 0x33 }, |
124 | { 0x79, 0x31 }, |
125 | { 0x7A, 0x30 }, |
126 | { 0x7B, 0x4E }, |
127 | { 0x7C, 0x3C }, |
128 | { 0x7D, 0x44 }, |
129 | { 0x7E, 0x35 }, |
130 | { 0x7F, 0x31 }, |
131 | { 0x80, 0x23 }, |
132 | { 0x81, 0x11 }, |
133 | { 0x82, 0x00 }, |
134 | /* Page2, for GIP */ |
135 | { 0xE0, 0x02 }, |
136 | /* GIP_L Pin mapping */ |
137 | { 0x00, 0x1E }, |
138 | { 0x01, 0x1E }, |
139 | { 0x02, 0x41 }, |
140 | { 0x03, 0x41 }, |
141 | { 0x04, 0x43 }, |
142 | { 0x05, 0x43 }, |
143 | { 0x06, 0x1F }, |
144 | { 0x07, 0x1F }, |
145 | { 0x08, 0x35 }, |
146 | { 0x09, 0x1F }, |
147 | { 0x0A, 0x15 }, |
148 | { 0x0B, 0x15 }, |
149 | { 0x0C, 0x1F }, |
150 | { 0x0D, 0x47 }, |
151 | { 0x0E, 0x47 }, |
152 | { 0x0F, 0x45 }, |
153 | { 0x10, 0x45 }, |
154 | { 0x11, 0x4B }, |
155 | { 0x12, 0x4B }, |
156 | { 0x13, 0x49 }, |
157 | { 0x14, 0x49 }, |
158 | { 0x15, 0x1F }, |
159 | /* GIP_R Pin mapping */ |
160 | { 0x16, 0x1E }, |
161 | { 0x17, 0x1E }, |
162 | { 0x18, 0x40 }, |
163 | { 0x19, 0x40 }, |
164 | { 0x1A, 0x42 }, |
165 | { 0x1B, 0x42 }, |
166 | { 0x1C, 0x1F }, |
167 | { 0x1D, 0x1F }, |
168 | { 0x1E, 0x35 }, |
169 | { 0x1F, 0x1F }, |
170 | { 0x20, 0x15 }, |
171 | { 0x21, 0x15 }, |
172 | { 0x22, 0x1f }, |
173 | { 0x23, 0x46 }, |
174 | { 0x24, 0x46 }, |
175 | { 0x25, 0x44 }, |
176 | { 0x26, 0x44 }, |
177 | { 0x27, 0x4A }, |
178 | { 0x28, 0x4A }, |
179 | { 0x29, 0x48 }, |
180 | { 0x2A, 0x48 }, |
181 | { 0x2B, 0x1F }, |
182 | /* GIP Timing */ |
183 | { 0x58, 0x40 }, |
184 | { 0x5B, 0x30 }, |
185 | { 0x5C, 0x03 }, |
186 | { 0x5D, 0x30 }, |
187 | { 0x5E, 0x01 }, |
188 | { 0x5F, 0x02 }, |
189 | { 0x63, 0x14 }, |
190 | { 0x64, 0x6A }, |
191 | { 0x67, 0x73 }, |
192 | { 0x68, 0x05 }, |
193 | { 0x69, 0x14 }, |
194 | { 0x6A, 0x6A }, |
195 | { 0x6B, 0x08 }, |
196 | { 0x6C, 0x00 }, |
197 | { 0x6D, 0x00 }, |
198 | { 0x6E, 0x00 }, |
199 | { 0x6F, 0x88 }, |
200 | { 0x77, 0xDD }, |
201 | { 0x79, 0x0E }, |
202 | { 0x7A, 0x03 }, |
203 | { 0x7D, 0x14 }, |
204 | { 0x7E, 0x6A }, |
205 | /* Page4 */ |
206 | { 0xE0, 0x04 }, |
207 | { 0x09, 0x11 }, |
208 | { 0x0E, 0x48 }, |
209 | { 0x2B, 0x2B }, |
210 | { 0x2D, 0x03 }, |
211 | { 0x2E, 0x44 }, |
212 | /* Page0 */ |
213 | { 0xE0, 0x00 }, |
214 | { 0xE6, 0x02 }, |
215 | { 0xE7, 0x0C }, |
216 | }; |
217 | |
218 | static const struct drm_display_mode ltk101b4029w_mode = { |
219 | .hdisplay = 800, |
220 | .hsync_start = 800 + 18, |
221 | .hsync_end = 800 + 18 + 18, |
222 | .htotal = 800 + 18 + 18 + 18, |
223 | .vdisplay = 1280, |
224 | .vsync_start = 1280 + 24, |
225 | .vsync_end = 1280 + 24 + 4, |
226 | .vtotal = 1280 + 24 + 4 + 8, |
227 | .clock = 67330, |
228 | .width_mm = 136, |
229 | .height_mm = 218, |
230 | }; |
231 | |
232 | static const struct ltk500hd1829_desc ltk101b4029w_data = { |
233 | .mode = <k101b4029w_mode, |
234 | .init = ltk101b4029w_init, |
235 | .num_init = ARRAY_SIZE(ltk101b4029w_init), |
236 | }; |
237 | |
238 | /* |
239 | * There is no description in the Reference Manual about these commands. |
240 | * We received them from the vendor, so just use them as is. |
241 | */ |
242 | static const struct ltk500hd1829_cmd ltk500hd1829_init[] = { |
243 | { 0xE0, 0x00 }, |
244 | { 0xE1, 0x93 }, |
245 | { 0xE2, 0x65 }, |
246 | { 0xE3, 0xF8 }, |
247 | { 0x80, 0x03 }, |
248 | { 0xE0, 0x04 }, |
249 | { 0x2D, 0x03 }, |
250 | { 0xE0, 0x01 }, |
251 | { 0x00, 0x00 }, |
252 | { 0x01, 0xB6 }, |
253 | { 0x03, 0x00 }, |
254 | { 0x04, 0xC5 }, |
255 | { 0x17, 0x00 }, |
256 | { 0x18, 0xBF }, |
257 | { 0x19, 0x01 }, |
258 | { 0x1A, 0x00 }, |
259 | { 0x1B, 0xBF }, |
260 | { 0x1C, 0x01 }, |
261 | { 0x1F, 0x7C }, |
262 | { 0x20, 0x26 }, |
263 | { 0x21, 0x26 }, |
264 | { 0x22, 0x4E }, |
265 | { 0x37, 0x09 }, |
266 | { 0x38, 0x04 }, |
267 | { 0x39, 0x08 }, |
268 | { 0x3A, 0x1F }, |
269 | { 0x3B, 0x1F }, |
270 | { 0x3C, 0x78 }, |
271 | { 0x3D, 0xFF }, |
272 | { 0x3E, 0xFF }, |
273 | { 0x3F, 0x00 }, |
274 | { 0x40, 0x04 }, |
275 | { 0x41, 0xA0 }, |
276 | { 0x43, 0x0F }, |
277 | { 0x44, 0x0A }, |
278 | { 0x45, 0x24 }, |
279 | { 0x55, 0x01 }, |
280 | { 0x56, 0x01 }, |
281 | { 0x57, 0xA5 }, |
282 | { 0x58, 0x0A }, |
283 | { 0x59, 0x4A }, |
284 | { 0x5A, 0x38 }, |
285 | { 0x5B, 0x10 }, |
286 | { 0x5C, 0x19 }, |
287 | { 0x5D, 0x7C }, |
288 | { 0x5E, 0x64 }, |
289 | { 0x5F, 0x54 }, |
290 | { 0x60, 0x48 }, |
291 | { 0x61, 0x44 }, |
292 | { 0x62, 0x35 }, |
293 | { 0x63, 0x3A }, |
294 | { 0x64, 0x24 }, |
295 | { 0x65, 0x3B }, |
296 | { 0x66, 0x39 }, |
297 | { 0x67, 0x37 }, |
298 | { 0x68, 0x56 }, |
299 | { 0x69, 0x41 }, |
300 | { 0x6A, 0x47 }, |
301 | { 0x6B, 0x2F }, |
302 | { 0x6C, 0x23 }, |
303 | { 0x6D, 0x13 }, |
304 | { 0x6E, 0x02 }, |
305 | { 0x6F, 0x08 }, |
306 | { 0x70, 0x7C }, |
307 | { 0x71, 0x64 }, |
308 | { 0x72, 0x54 }, |
309 | { 0x73, 0x48 }, |
310 | { 0x74, 0x44 }, |
311 | { 0x75, 0x35 }, |
312 | { 0x76, 0x3A }, |
313 | { 0x77, 0x22 }, |
314 | { 0x78, 0x3B }, |
315 | { 0x79, 0x39 }, |
316 | { 0x7A, 0x38 }, |
317 | { 0x7B, 0x52 }, |
318 | { 0x7C, 0x41 }, |
319 | { 0x7D, 0x47 }, |
320 | { 0x7E, 0x2F }, |
321 | { 0x7F, 0x23 }, |
322 | { 0x80, 0x13 }, |
323 | { 0x81, 0x02 }, |
324 | { 0x82, 0x08 }, |
325 | { 0xE0, 0x02 }, |
326 | { 0x00, 0x57 }, |
327 | { 0x01, 0x77 }, |
328 | { 0x02, 0x44 }, |
329 | { 0x03, 0x46 }, |
330 | { 0x04, 0x48 }, |
331 | { 0x05, 0x4A }, |
332 | { 0x06, 0x4C }, |
333 | { 0x07, 0x4E }, |
334 | { 0x08, 0x50 }, |
335 | { 0x09, 0x55 }, |
336 | { 0x0A, 0x52 }, |
337 | { 0x0B, 0x55 }, |
338 | { 0x0C, 0x55 }, |
339 | { 0x0D, 0x55 }, |
340 | { 0x0E, 0x55 }, |
341 | { 0x0F, 0x55 }, |
342 | { 0x10, 0x55 }, |
343 | { 0x11, 0x55 }, |
344 | { 0x12, 0x55 }, |
345 | { 0x13, 0x40 }, |
346 | { 0x14, 0x55 }, |
347 | { 0x15, 0x55 }, |
348 | { 0x16, 0x57 }, |
349 | { 0x17, 0x77 }, |
350 | { 0x18, 0x45 }, |
351 | { 0x19, 0x47 }, |
352 | { 0x1A, 0x49 }, |
353 | { 0x1B, 0x4B }, |
354 | { 0x1C, 0x4D }, |
355 | { 0x1D, 0x4F }, |
356 | { 0x1E, 0x51 }, |
357 | { 0x1F, 0x55 }, |
358 | { 0x20, 0x53 }, |
359 | { 0x21, 0x55 }, |
360 | { 0x22, 0x55 }, |
361 | { 0x23, 0x55 }, |
362 | { 0x24, 0x55 }, |
363 | { 0x25, 0x55 }, |
364 | { 0x26, 0x55 }, |
365 | { 0x27, 0x55 }, |
366 | { 0x28, 0x55 }, |
367 | { 0x29, 0x41 }, |
368 | { 0x2A, 0x55 }, |
369 | { 0x2B, 0x55 }, |
370 | { 0x2C, 0x57 }, |
371 | { 0x2D, 0x77 }, |
372 | { 0x2E, 0x4F }, |
373 | { 0x2F, 0x4D }, |
374 | { 0x30, 0x4B }, |
375 | { 0x31, 0x49 }, |
376 | { 0x32, 0x47 }, |
377 | { 0x33, 0x45 }, |
378 | { 0x34, 0x41 }, |
379 | { 0x35, 0x55 }, |
380 | { 0x36, 0x53 }, |
381 | { 0x37, 0x55 }, |
382 | { 0x38, 0x55 }, |
383 | { 0x39, 0x55 }, |
384 | { 0x3A, 0x55 }, |
385 | { 0x3B, 0x55 }, |
386 | { 0x3C, 0x55 }, |
387 | { 0x3D, 0x55 }, |
388 | { 0x3E, 0x55 }, |
389 | { 0x3F, 0x51 }, |
390 | { 0x40, 0x55 }, |
391 | { 0x41, 0x55 }, |
392 | { 0x42, 0x57 }, |
393 | { 0x43, 0x77 }, |
394 | { 0x44, 0x4E }, |
395 | { 0x45, 0x4C }, |
396 | { 0x46, 0x4A }, |
397 | { 0x47, 0x48 }, |
398 | { 0x48, 0x46 }, |
399 | { 0x49, 0x44 }, |
400 | { 0x4A, 0x40 }, |
401 | { 0x4B, 0x55 }, |
402 | { 0x4C, 0x52 }, |
403 | { 0x4D, 0x55 }, |
404 | { 0x4E, 0x55 }, |
405 | { 0x4F, 0x55 }, |
406 | { 0x50, 0x55 }, |
407 | { 0x51, 0x55 }, |
408 | { 0x52, 0x55 }, |
409 | { 0x53, 0x55 }, |
410 | { 0x54, 0x55 }, |
411 | { 0x55, 0x50 }, |
412 | { 0x56, 0x55 }, |
413 | { 0x57, 0x55 }, |
414 | { 0x58, 0x40 }, |
415 | { 0x59, 0x00 }, |
416 | { 0x5A, 0x00 }, |
417 | { 0x5B, 0x10 }, |
418 | { 0x5C, 0x09 }, |
419 | { 0x5D, 0x30 }, |
420 | { 0x5E, 0x01 }, |
421 | { 0x5F, 0x02 }, |
422 | { 0x60, 0x30 }, |
423 | { 0x61, 0x03 }, |
424 | { 0x62, 0x04 }, |
425 | { 0x63, 0x06 }, |
426 | { 0x64, 0x6A }, |
427 | { 0x65, 0x75 }, |
428 | { 0x66, 0x0F }, |
429 | { 0x67, 0xB3 }, |
430 | { 0x68, 0x0B }, |
431 | { 0x69, 0x06 }, |
432 | { 0x6A, 0x6A }, |
433 | { 0x6B, 0x10 }, |
434 | { 0x6C, 0x00 }, |
435 | { 0x6D, 0x04 }, |
436 | { 0x6E, 0x04 }, |
437 | { 0x6F, 0x88 }, |
438 | { 0x70, 0x00 }, |
439 | { 0x71, 0x00 }, |
440 | { 0x72, 0x06 }, |
441 | { 0x73, 0x7B }, |
442 | { 0x74, 0x00 }, |
443 | { 0x75, 0xBC }, |
444 | { 0x76, 0x00 }, |
445 | { 0x77, 0x05 }, |
446 | { 0x78, 0x2E }, |
447 | { 0x79, 0x00 }, |
448 | { 0x7A, 0x00 }, |
449 | { 0x7B, 0x00 }, |
450 | { 0x7C, 0x00 }, |
451 | { 0x7D, 0x03 }, |
452 | { 0x7E, 0x7B }, |
453 | { 0xE0, 0x04 }, |
454 | { 0x09, 0x10 }, |
455 | { 0x2B, 0x2B }, |
456 | { 0x2E, 0x44 }, |
457 | { 0xE0, 0x00 }, |
458 | { 0xE6, 0x02 }, |
459 | { 0xE7, 0x02 }, |
460 | { 0x35, 0x00 }, |
461 | }; |
462 | |
463 | static const struct drm_display_mode ltk500hd1829_mode = { |
464 | .hdisplay = 720, |
465 | .hsync_start = 720 + 50, |
466 | .hsync_end = 720 + 50 + 50, |
467 | .htotal = 720 + 50 + 50 + 50, |
468 | .vdisplay = 1280, |
469 | .vsync_start = 1280 + 30, |
470 | .vsync_end = 1280 + 30 + 4, |
471 | .vtotal = 1280 + 30 + 4 + 12, |
472 | .clock = 69217, |
473 | .width_mm = 62, |
474 | .height_mm = 110, |
475 | }; |
476 | |
477 | static const struct ltk500hd1829_desc ltk500hd1829_data = { |
478 | .mode = <k500hd1829_mode, |
479 | .init = ltk500hd1829_init, |
480 | .num_init = ARRAY_SIZE(ltk500hd1829_init), |
481 | }; |
482 | |
483 | static inline |
484 | struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel) |
485 | { |
486 | return container_of(panel, struct ltk500hd1829, panel); |
487 | } |
488 | |
489 | static int ltk500hd1829_unprepare(struct drm_panel *panel) |
490 | { |
491 | struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); |
492 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
493 | int ret; |
494 | |
495 | if (!ctx->prepared) |
496 | return 0; |
497 | |
498 | ret = mipi_dsi_dcs_set_display_off(dsi); |
499 | if (ret < 0) |
500 | dev_err(panel->dev, "failed to set display off: %d\n" , ret); |
501 | |
502 | ret = mipi_dsi_dcs_enter_sleep_mode(dsi); |
503 | if (ret < 0) { |
504 | dev_err(panel->dev, "failed to enter sleep mode: %d\n" , ret); |
505 | } |
506 | |
507 | /* 120ms to enter sleep mode */ |
508 | msleep(msecs: 120); |
509 | |
510 | regulator_disable(regulator: ctx->iovcc); |
511 | regulator_disable(regulator: ctx->vcc); |
512 | |
513 | ctx->prepared = false; |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | static int ltk500hd1829_prepare(struct drm_panel *panel) |
519 | { |
520 | struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); |
521 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
522 | unsigned int i; |
523 | int ret; |
524 | |
525 | if (ctx->prepared) |
526 | return 0; |
527 | |
528 | ret = regulator_enable(regulator: ctx->vcc); |
529 | if (ret < 0) { |
530 | dev_err(ctx->dev, "Failed to enable vci supply: %d\n" , ret); |
531 | return ret; |
532 | } |
533 | ret = regulator_enable(regulator: ctx->iovcc); |
534 | if (ret < 0) { |
535 | dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n" , ret); |
536 | goto disable_vcc; |
537 | } |
538 | |
539 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
540 | /* tRW: 10us */ |
541 | usleep_range(min: 10, max: 20); |
542 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
543 | |
544 | /* tRT: >= 5ms */ |
545 | usleep_range(min: 5000, max: 6000); |
546 | |
547 | for (i = 0; i < ctx->panel_desc->num_init; i++) { |
548 | ret = mipi_dsi_generic_write(dsi, payload: &ctx->panel_desc->init[i], |
549 | size: sizeof(struct ltk500hd1829_cmd)); |
550 | if (ret < 0) { |
551 | dev_err(panel->dev, "failed to write init cmds: %d\n" , ret); |
552 | goto disable_iovcc; |
553 | } |
554 | } |
555 | |
556 | ret = mipi_dsi_dcs_exit_sleep_mode(dsi); |
557 | if (ret < 0) { |
558 | dev_err(panel->dev, "failed to exit sleep mode: %d\n" , ret); |
559 | goto disable_iovcc; |
560 | } |
561 | |
562 | /* 120ms to exit sleep mode */ |
563 | msleep(msecs: 120); |
564 | |
565 | ret = mipi_dsi_dcs_set_display_on(dsi); |
566 | if (ret < 0) { |
567 | dev_err(panel->dev, "failed to set display on: %d\n" , ret); |
568 | goto disable_iovcc; |
569 | } |
570 | |
571 | ctx->prepared = true; |
572 | |
573 | return 0; |
574 | |
575 | disable_iovcc: |
576 | regulator_disable(regulator: ctx->iovcc); |
577 | disable_vcc: |
578 | regulator_disable(regulator: ctx->vcc); |
579 | return ret; |
580 | } |
581 | |
582 | static int ltk500hd1829_get_modes(struct drm_panel *panel, |
583 | struct drm_connector *connector) |
584 | { |
585 | struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); |
586 | struct drm_display_mode *mode; |
587 | |
588 | mode = drm_mode_duplicate(dev: connector->dev, mode: ctx->panel_desc->mode); |
589 | if (!mode) { |
590 | dev_err(ctx->dev, "failed to add mode %ux%u@%u\n" , |
591 | ctx->panel_desc->mode->hdisplay, ctx->panel_desc->mode->vdisplay, |
592 | drm_mode_vrefresh(ctx->panel_desc->mode)); |
593 | return -ENOMEM; |
594 | } |
595 | |
596 | drm_mode_set_name(mode); |
597 | |
598 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
599 | connector->display_info.width_mm = mode->width_mm; |
600 | connector->display_info.height_mm = mode->height_mm; |
601 | drm_mode_probed_add(connector, mode); |
602 | |
603 | return 1; |
604 | } |
605 | |
606 | static const struct drm_panel_funcs ltk500hd1829_funcs = { |
607 | .unprepare = ltk500hd1829_unprepare, |
608 | .prepare = ltk500hd1829_prepare, |
609 | .get_modes = ltk500hd1829_get_modes, |
610 | }; |
611 | |
612 | static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) |
613 | { |
614 | struct ltk500hd1829 *ctx; |
615 | struct device *dev = &dsi->dev; |
616 | int ret; |
617 | |
618 | ctx = devm_kzalloc(dev: &dsi->dev, size: sizeof(*ctx), GFP_KERNEL); |
619 | if (!ctx) |
620 | return -ENOMEM; |
621 | |
622 | ctx->panel_desc = of_device_get_match_data(dev); |
623 | if (!ctx->panel_desc) |
624 | return -EINVAL; |
625 | |
626 | ctx->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
627 | if (IS_ERR(ptr: ctx->reset_gpio)) { |
628 | dev_err(dev, "cannot get reset gpio\n" ); |
629 | return PTR_ERR(ptr: ctx->reset_gpio); |
630 | } |
631 | |
632 | ctx->vcc = devm_regulator_get(dev, id: "vcc" ); |
633 | if (IS_ERR(ptr: ctx->vcc)) { |
634 | ret = PTR_ERR(ptr: ctx->vcc); |
635 | if (ret != -EPROBE_DEFER) |
636 | dev_err(dev, "Failed to request vcc regulator: %d\n" , ret); |
637 | return ret; |
638 | } |
639 | |
640 | ctx->iovcc = devm_regulator_get(dev, id: "iovcc" ); |
641 | if (IS_ERR(ptr: ctx->iovcc)) { |
642 | ret = PTR_ERR(ptr: ctx->iovcc); |
643 | if (ret != -EPROBE_DEFER) |
644 | dev_err(dev, "Failed to request iovcc regulator: %d\n" , ret); |
645 | return ret; |
646 | } |
647 | |
648 | mipi_dsi_set_drvdata(dsi, data: ctx); |
649 | |
650 | ctx->dev = dev; |
651 | |
652 | dsi->lanes = 4; |
653 | dsi->format = MIPI_DSI_FMT_RGB888; |
654 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | |
655 | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; |
656 | |
657 | drm_panel_init(panel: &ctx->panel, dev: &dsi->dev, funcs: <k500hd1829_funcs, |
658 | DRM_MODE_CONNECTOR_DSI); |
659 | |
660 | ret = drm_panel_of_backlight(panel: &ctx->panel); |
661 | if (ret) |
662 | return ret; |
663 | |
664 | drm_panel_add(panel: &ctx->panel); |
665 | |
666 | ret = mipi_dsi_attach(dsi); |
667 | if (ret < 0) { |
668 | dev_err(dev, "mipi_dsi_attach failed: %d\n" , ret); |
669 | drm_panel_remove(panel: &ctx->panel); |
670 | return ret; |
671 | } |
672 | |
673 | return 0; |
674 | } |
675 | |
676 | static void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi) |
677 | { |
678 | struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); |
679 | int ret; |
680 | |
681 | ret = drm_panel_unprepare(panel: &ctx->panel); |
682 | if (ret < 0) |
683 | dev_err(&dsi->dev, "Failed to unprepare panel: %d\n" , ret); |
684 | |
685 | ret = drm_panel_disable(panel: &ctx->panel); |
686 | if (ret < 0) |
687 | dev_err(&dsi->dev, "Failed to disable panel: %d\n" , ret); |
688 | } |
689 | |
690 | static void ltk500hd1829_remove(struct mipi_dsi_device *dsi) |
691 | { |
692 | struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); |
693 | int ret; |
694 | |
695 | ltk500hd1829_shutdown(dsi); |
696 | |
697 | ret = mipi_dsi_detach(dsi); |
698 | if (ret < 0) |
699 | dev_err(&dsi->dev, "failed to detach from DSI host: %d\n" , ret); |
700 | |
701 | drm_panel_remove(panel: &ctx->panel); |
702 | } |
703 | |
704 | static const struct of_device_id ltk500hd1829_of_match[] = { |
705 | { |
706 | .compatible = "leadtek,ltk101b4029w" , |
707 | .data = <k101b4029w_data, |
708 | }, |
709 | { |
710 | .compatible = "leadtek,ltk500hd1829" , |
711 | .data = <k500hd1829_data, |
712 | }, |
713 | { /* sentinel */ } |
714 | }; |
715 | MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match); |
716 | |
717 | static struct mipi_dsi_driver ltk500hd1829_driver = { |
718 | .driver = { |
719 | .name = "panel-leadtek-ltk500hd1829" , |
720 | .of_match_table = ltk500hd1829_of_match, |
721 | }, |
722 | .probe = ltk500hd1829_probe, |
723 | .remove = ltk500hd1829_remove, |
724 | .shutdown = ltk500hd1829_shutdown, |
725 | }; |
726 | module_mipi_dsi_driver(ltk500hd1829_driver); |
727 | |
728 | MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>" ); |
729 | MODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver" ); |
730 | MODULE_LICENSE("GPL v2" ); |
731 | |