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