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