Loading...
Note: File does not exist in v4.6.
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
4 */
5
6#include <linux/delay.h>
7#include <linux/gpio/consumer.h>
8#include <linux/media-bus-format.h>
9#include <linux/module.h>
10#include <linux/of.h>
11#include <linux/of_device.h>
12#include <linux/regulator/consumer.h>
13
14#include <video/display_timing.h>
15#include <video/mipi_display.h>
16
17#include <drm/drm_mipi_dsi.h>
18#include <drm/drm_modes.h>
19#include <drm/drm_panel.h>
20#include <drm/drm_print.h>
21
22struct ltk050h3146w_cmd {
23 char cmd;
24 char data;
25};
26
27struct ltk050h3146w;
28struct ltk050h3146w_desc {
29 const struct drm_display_mode *mode;
30 int (*init)(struct ltk050h3146w *ctx);
31};
32
33struct ltk050h3146w {
34 struct device *dev;
35 struct drm_panel panel;
36 struct gpio_desc *reset_gpio;
37 struct regulator *vci;
38 struct regulator *iovcc;
39 const struct ltk050h3146w_desc *panel_desc;
40 bool prepared;
41};
42
43static const struct ltk050h3146w_cmd page1_cmds[] = {
44 { 0x22, 0x0A }, /* BGR SS GS */
45 { 0x31, 0x00 }, /* column inversion */
46 { 0x53, 0xA2 }, /* VCOM1 */
47 { 0x55, 0xA2 }, /* VCOM2 */
48 { 0x50, 0x81 }, /* VREG1OUT=5V */
49 { 0x51, 0x85 }, /* VREG2OUT=-5V */
50 { 0x62, 0x0D }, /* EQT Time setting */
51/*
52 * The vendor init selected page 1 here _again_
53 * Is this supposed to be page 2?
54 */
55 { 0xA0, 0x00 },
56 { 0xA1, 0x1A },
57 { 0xA2, 0x28 },
58 { 0xA3, 0x13 },
59 { 0xA4, 0x16 },
60 { 0xA5, 0x29 },
61 { 0xA6, 0x1D },
62 { 0xA7, 0x1E },
63 { 0xA8, 0x84 },
64 { 0xA9, 0x1C },
65 { 0xAA, 0x28 },
66 { 0xAB, 0x75 },
67 { 0xAC, 0x1A },
68 { 0xAD, 0x19 },
69 { 0xAE, 0x4D },
70 { 0xAF, 0x22 },
71 { 0xB0, 0x28 },
72 { 0xB1, 0x54 },
73 { 0xB2, 0x66 },
74 { 0xB3, 0x39 },
75 { 0xC0, 0x00 },
76 { 0xC1, 0x1A },
77 { 0xC2, 0x28 },
78 { 0xC3, 0x13 },
79 { 0xC4, 0x16 },
80 { 0xC5, 0x29 },
81 { 0xC6, 0x1D },
82 { 0xC7, 0x1E },
83 { 0xC8, 0x84 },
84 { 0xC9, 0x1C },
85 { 0xCA, 0x28 },
86 { 0xCB, 0x75 },
87 { 0xCC, 0x1A },
88 { 0xCD, 0x19 },
89 { 0xCE, 0x4D },
90 { 0xCF, 0x22 },
91 { 0xD0, 0x28 },
92 { 0xD1, 0x54 },
93 { 0xD2, 0x66 },
94 { 0xD3, 0x39 },
95};
96
97static const struct ltk050h3146w_cmd page3_cmds[] = {
98 { 0x01, 0x00 },
99 { 0x02, 0x00 },
100 { 0x03, 0x73 },
101 { 0x04, 0x00 },
102 { 0x05, 0x00 },
103 { 0x06, 0x0a },
104 { 0x07, 0x00 },
105 { 0x08, 0x00 },
106 { 0x09, 0x01 },
107 { 0x0a, 0x00 },
108 { 0x0b, 0x00 },
109 { 0x0c, 0x01 },
110 { 0x0d, 0x00 },
111 { 0x0e, 0x00 },
112 { 0x0f, 0x1d },
113 { 0x10, 0x1d },
114 { 0x11, 0x00 },
115 { 0x12, 0x00 },
116 { 0x13, 0x00 },
117 { 0x14, 0x00 },
118 { 0x15, 0x00 },
119 { 0x16, 0x00 },
120 { 0x17, 0x00 },
121 { 0x18, 0x00 },
122 { 0x19, 0x00 },
123 { 0x1a, 0x00 },
124 { 0x1b, 0x00 },
125 { 0x1c, 0x00 },
126 { 0x1d, 0x00 },
127 { 0x1e, 0x40 },
128 { 0x1f, 0x80 },
129 { 0x20, 0x06 },
130 { 0x21, 0x02 },
131 { 0x22, 0x00 },
132 { 0x23, 0x00 },
133 { 0x24, 0x00 },
134 { 0x25, 0x00 },
135 { 0x26, 0x00 },
136 { 0x27, 0x00 },
137 { 0x28, 0x33 },
138 { 0x29, 0x03 },
139 { 0x2a, 0x00 },
140 { 0x2b, 0x00 },
141 { 0x2c, 0x00 },
142 { 0x2d, 0x00 },
143 { 0x2e, 0x00 },
144 { 0x2f, 0x00 },
145 { 0x30, 0x00 },
146 { 0x31, 0x00 },
147 { 0x32, 0x00 },
148 { 0x33, 0x00 },
149 { 0x34, 0x04 },
150 { 0x35, 0x00 },
151 { 0x36, 0x00 },
152 { 0x37, 0x00 },
153 { 0x38, 0x3C },
154 { 0x39, 0x35 },
155 { 0x3A, 0x01 },
156 { 0x3B, 0x40 },
157 { 0x3C, 0x00 },
158 { 0x3D, 0x01 },
159 { 0x3E, 0x00 },
160 { 0x3F, 0x00 },
161 { 0x40, 0x00 },
162 { 0x41, 0x88 },
163 { 0x42, 0x00 },
164 { 0x43, 0x00 },
165 { 0x44, 0x1F },
166 { 0x50, 0x01 },
167 { 0x51, 0x23 },
168 { 0x52, 0x45 },
169 { 0x53, 0x67 },
170 { 0x54, 0x89 },
171 { 0x55, 0xab },
172 { 0x56, 0x01 },
173 { 0x57, 0x23 },
174 { 0x58, 0x45 },
175 { 0x59, 0x67 },
176 { 0x5a, 0x89 },
177 { 0x5b, 0xab },
178 { 0x5c, 0xcd },
179 { 0x5d, 0xef },
180 { 0x5e, 0x11 },
181 { 0x5f, 0x01 },
182 { 0x60, 0x00 },
183 { 0x61, 0x15 },
184 { 0x62, 0x14 },
185 { 0x63, 0x0E },
186 { 0x64, 0x0F },
187 { 0x65, 0x0C },
188 { 0x66, 0x0D },
189 { 0x67, 0x06 },
190 { 0x68, 0x02 },
191 { 0x69, 0x07 },
192 { 0x6a, 0x02 },
193 { 0x6b, 0x02 },
194 { 0x6c, 0x02 },
195 { 0x6d, 0x02 },
196 { 0x6e, 0x02 },
197 { 0x6f, 0x02 },
198 { 0x70, 0x02 },
199 { 0x71, 0x02 },
200 { 0x72, 0x02 },
201 { 0x73, 0x02 },
202 { 0x74, 0x02 },
203 { 0x75, 0x01 },
204 { 0x76, 0x00 },
205 { 0x77, 0x14 },
206 { 0x78, 0x15 },
207 { 0x79, 0x0E },
208 { 0x7a, 0x0F },
209 { 0x7b, 0x0C },
210 { 0x7c, 0x0D },
211 { 0x7d, 0x06 },
212 { 0x7e, 0x02 },
213 { 0x7f, 0x07 },
214 { 0x80, 0x02 },
215 { 0x81, 0x02 },
216 { 0x82, 0x02 },
217 { 0x83, 0x02 },
218 { 0x84, 0x02 },
219 { 0x85, 0x02 },
220 { 0x86, 0x02 },
221 { 0x87, 0x02 },
222 { 0x88, 0x02 },
223 { 0x89, 0x02 },
224 { 0x8A, 0x02 },
225};
226
227static const struct ltk050h3146w_cmd page4_cmds[] = {
228 { 0x70, 0x00 },
229 { 0x71, 0x00 },
230 { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */
231 { 0x84, 0x0F }, /* VGH clamp level 15V */
232 { 0x85, 0x0D }, /* VGL clamp level (-10V) */
233 { 0x32, 0xAC },
234 { 0x8C, 0x80 },
235 { 0x3C, 0xF5 },
236 { 0xB5, 0x07 }, /* GAMMA OP */
237 { 0x31, 0x45 }, /* SOURCE OP */
238 { 0x3A, 0x24 }, /* PS_EN OFF */
239 { 0x88, 0x33 }, /* LVD */
240};
241
242static inline
243struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
244{
245 return container_of(panel, struct ltk050h3146w, panel);
246}
247
248#define dsi_dcs_write_seq(dsi, cmd, seq...) do { \
249 static const u8 b[] = { cmd, seq }; \
250 int ret; \
251 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
252 if (ret < 0) \
253 return ret; \
254 } while (0)
255
256static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
257{
258 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
259 int ret;
260
261 /*
262 * Init sequence was supplied by the panel vendor without much
263 * documentation.
264 */
265 dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
266 dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
267 0x01);
268 dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
269 dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
270 dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
271
272 dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
273 dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
274 0x28, 0x04, 0xcc, 0xcc, 0xcc);
275 dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
276 dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
277 dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
278 dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
279 dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
280 0x80);
281 dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
282 0x16, 0x00, 0x00);
283 dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
284 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
285 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
286 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
287 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
288 dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
289 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
290 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
291 dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
292 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
293 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
294 dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
295 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
296 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
297 dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
298 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
299 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
300 dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
301 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
302 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
303 dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
304 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
305 0x21, 0x00, 0x60);
306 dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
307 dsi_dcs_write_seq(dsi, 0xde, 0x02);
308 dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
309 dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
310 dsi_dcs_write_seq(dsi, 0xc1, 0x11);
311 dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
312 dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
313 dsi_dcs_write_seq(dsi, 0xde, 0x00);
314
315 ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
316 if (ret < 0) {
317 DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
318 ret);
319 return ret;
320 }
321
322 msleep(60);
323
324 return 0;
325}
326
327static const struct drm_display_mode ltk050h3146w_mode = {
328 .hdisplay = 720,
329 .hsync_start = 720 + 42,
330 .hsync_end = 720 + 42 + 8,
331 .htotal = 720 + 42 + 8 + 42,
332 .vdisplay = 1280,
333 .vsync_start = 1280 + 12,
334 .vsync_end = 1280 + 12 + 4,
335 .vtotal = 1280 + 12 + 4 + 18,
336 .clock = 64018,
337 .width_mm = 62,
338 .height_mm = 110,
339};
340
341static const struct ltk050h3146w_desc ltk050h3146w_data = {
342 .mode = <k050h3146w_mode,
343 .init = ltk050h3146w_init_sequence,
344};
345
346static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
347{
348 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
349 u8 d[3] = { 0x98, 0x81, page };
350
351 return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
352}
353
354static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
355 const struct ltk050h3146w_cmd *cmds,
356 int num)
357{
358 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
359 int i, ret;
360
361 ret = ltk050h3146w_a2_select_page(ctx, page);
362 if (ret < 0) {
363 DRM_DEV_ERROR(ctx->dev, "failed to select page %d: %d\n",
364 page, ret);
365 return ret;
366 }
367
368 for (i = 0; i < num; i++) {
369 ret = mipi_dsi_generic_write(dsi, &cmds[i],
370 sizeof(struct ltk050h3146w_cmd));
371 if (ret < 0) {
372 DRM_DEV_ERROR(ctx->dev,
373 "failed to write page %d init cmds: %d\n",
374 page, ret);
375 return ret;
376 }
377 }
378
379 return 0;
380}
381
382static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
383{
384 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
385 int ret;
386
387 /*
388 * Init sequence was supplied by the panel vendor without much
389 * documentation.
390 */
391 ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
392 ARRAY_SIZE(page3_cmds));
393 if (ret < 0)
394 return ret;
395
396 ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
397 ARRAY_SIZE(page4_cmds));
398 if (ret < 0)
399 return ret;
400
401 ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
402 ARRAY_SIZE(page1_cmds));
403 if (ret < 0)
404 return ret;
405
406 ret = ltk050h3146w_a2_select_page(ctx, 0);
407 if (ret < 0) {
408 DRM_DEV_ERROR(ctx->dev, "failed to select page 0: %d\n", ret);
409 return ret;
410 }
411
412 /* vendor code called this without param, where there should be one */
413 ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
414 if (ret < 0) {
415 DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
416 ret);
417 return ret;
418 }
419
420 msleep(60);
421
422 return 0;
423}
424
425static const struct drm_display_mode ltk050h3146w_a2_mode = {
426 .hdisplay = 720,
427 .hsync_start = 720 + 42,
428 .hsync_end = 720 + 42 + 10,
429 .htotal = 720 + 42 + 10 + 60,
430 .vdisplay = 1280,
431 .vsync_start = 1280 + 18,
432 .vsync_end = 1280 + 18 + 4,
433 .vtotal = 1280 + 18 + 4 + 12,
434 .clock = 65595,
435 .width_mm = 62,
436 .height_mm = 110,
437};
438
439static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
440 .mode = <k050h3146w_a2_mode,
441 .init = ltk050h3146w_a2_init_sequence,
442};
443
444static int ltk050h3146w_unprepare(struct drm_panel *panel)
445{
446 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
447 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
448 int ret;
449
450 if (!ctx->prepared)
451 return 0;
452
453 ret = mipi_dsi_dcs_set_display_off(dsi);
454 if (ret < 0) {
455 DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n",
456 ret);
457 return ret;
458 }
459
460 mipi_dsi_dcs_enter_sleep_mode(dsi);
461 if (ret < 0) {
462 DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n",
463 ret);
464 return ret;
465 }
466
467 regulator_disable(ctx->iovcc);
468 regulator_disable(ctx->vci);
469
470 ctx->prepared = false;
471
472 return 0;
473}
474
475static int ltk050h3146w_prepare(struct drm_panel *panel)
476{
477 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
478 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
479 int ret;
480
481 if (ctx->prepared)
482 return 0;
483
484 DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
485 ret = regulator_enable(ctx->vci);
486 if (ret < 0) {
487 DRM_DEV_ERROR(ctx->dev,
488 "Failed to enable vci supply: %d\n", ret);
489 return ret;
490 }
491 ret = regulator_enable(ctx->iovcc);
492 if (ret < 0) {
493 DRM_DEV_ERROR(ctx->dev,
494 "Failed to enable iovcc supply: %d\n", ret);
495 goto disable_vci;
496 }
497
498 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
499 usleep_range(5000, 6000);
500 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
501 msleep(20);
502
503 ret = ctx->panel_desc->init(ctx);
504 if (ret < 0) {
505 DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
506 ret);
507 goto disable_iovcc;
508 }
509
510 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
511 if (ret < 0) {
512 DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
513 goto disable_iovcc;
514 }
515
516 /* T9: 120ms */
517 msleep(120);
518
519 ret = mipi_dsi_dcs_set_display_on(dsi);
520 if (ret < 0) {
521 DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret);
522 goto disable_iovcc;
523 }
524
525 msleep(50);
526
527 ctx->prepared = true;
528
529 return 0;
530
531disable_iovcc:
532 regulator_disable(ctx->iovcc);
533disable_vci:
534 regulator_disable(ctx->vci);
535 return ret;
536}
537
538static int ltk050h3146w_get_modes(struct drm_panel *panel,
539 struct drm_connector *connector)
540{
541 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
542 struct drm_display_mode *mode;
543
544 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
545 if (!mode)
546 return -ENOMEM;
547
548 drm_mode_set_name(mode);
549
550 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
551 connector->display_info.width_mm = mode->width_mm;
552 connector->display_info.height_mm = mode->height_mm;
553 drm_mode_probed_add(connector, mode);
554
555 return 1;
556}
557
558static const struct drm_panel_funcs ltk050h3146w_funcs = {
559 .unprepare = ltk050h3146w_unprepare,
560 .prepare = ltk050h3146w_prepare,
561 .get_modes = ltk050h3146w_get_modes,
562};
563
564static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
565{
566 struct device *dev = &dsi->dev;
567 struct ltk050h3146w *ctx;
568 int ret;
569
570 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
571 if (!ctx)
572 return -ENOMEM;
573
574 ctx->panel_desc = of_device_get_match_data(dev);
575 if (!ctx->panel_desc)
576 return -EINVAL;
577
578 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
579 if (IS_ERR(ctx->reset_gpio)) {
580 DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
581 return PTR_ERR(ctx->reset_gpio);
582 }
583
584 ctx->vci = devm_regulator_get(dev, "vci");
585 if (IS_ERR(ctx->vci)) {
586 ret = PTR_ERR(ctx->vci);
587 if (ret != -EPROBE_DEFER)
588 DRM_DEV_ERROR(dev,
589 "Failed to request vci regulator: %d\n",
590 ret);
591 return ret;
592 }
593
594 ctx->iovcc = devm_regulator_get(dev, "iovcc");
595 if (IS_ERR(ctx->iovcc)) {
596 ret = PTR_ERR(ctx->iovcc);
597 if (ret != -EPROBE_DEFER)
598 DRM_DEV_ERROR(dev,
599 "Failed to request iovcc regulator: %d\n",
600 ret);
601 return ret;
602 }
603
604 mipi_dsi_set_drvdata(dsi, ctx);
605
606 ctx->dev = dev;
607
608 dsi->lanes = 4;
609 dsi->format = MIPI_DSI_FMT_RGB888;
610 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
611 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
612
613 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs,
614 DRM_MODE_CONNECTOR_DSI);
615
616 ret = drm_panel_of_backlight(&ctx->panel);
617 if (ret)
618 return ret;
619
620 drm_panel_add(&ctx->panel);
621
622 ret = mipi_dsi_attach(dsi);
623 if (ret < 0) {
624 DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret);
625 drm_panel_remove(&ctx->panel);
626 return ret;
627 }
628
629 return 0;
630}
631
632static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
633{
634 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
635 int ret;
636
637 ret = drm_panel_unprepare(&ctx->panel);
638 if (ret < 0)
639 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
640 ret);
641
642 ret = drm_panel_disable(&ctx->panel);
643 if (ret < 0)
644 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
645 ret);
646}
647
648static int ltk050h3146w_remove(struct mipi_dsi_device *dsi)
649{
650 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
651 int ret;
652
653 ltk050h3146w_shutdown(dsi);
654
655 ret = mipi_dsi_detach(dsi);
656 if (ret < 0)
657 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
658 ret);
659
660 drm_panel_remove(&ctx->panel);
661
662 return 0;
663}
664
665static const struct of_device_id ltk050h3146w_of_match[] = {
666 {
667 .compatible = "leadtek,ltk050h3146w",
668 .data = <k050h3146w_data,
669 },
670 {
671 .compatible = "leadtek,ltk050h3146w-a2",
672 .data = <k050h3146w_a2_data,
673 },
674 { /* sentinel */ }
675};
676MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
677
678static struct mipi_dsi_driver ltk050h3146w_driver = {
679 .driver = {
680 .name = "panel-leadtek-ltk050h3146w",
681 .of_match_table = ltk050h3146w_of_match,
682 },
683 .probe = ltk050h3146w_probe,
684 .remove = ltk050h3146w_remove,
685 .shutdown = ltk050h3146w_shutdown,
686};
687module_mipi_dsi_driver(ltk050h3146w_driver);
688
689MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
690MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
691MODULE_LICENSE("GPL v2");