Merge drm/drm-next into drm-intel-next-queued
[linux-2.6-microblaze.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/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
21 struct ltk050h3146w_cmd {
22         char cmd;
23         char data;
24 };
25
26 struct ltk050h3146w;
27 struct ltk050h3146w_desc {
28         const struct drm_display_mode *mode;
29         int (*init)(struct ltk050h3146w *ctx);
30 };
31
32 struct 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
42 static 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
96 static 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
226 static 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
241 static inline
242 struct 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 {                        \
248                 static const u8 b[] = { cmd, seq };                     \
249                 int ret;                                                \
250                 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
251                 if (ret < 0)                                            \
252                         return ret;                                     \
253         } while (0)
254
255 static 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) {
316                 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
317                 return ret;
318         }
319
320         msleep(60);
321
322         return 0;
323 }
324
325 static 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
339 static const struct ltk050h3146w_desc ltk050h3146w_data = {
340         .mode = &ltk050h3146w_mode,
341         .init = ltk050h3146w_init_sequence,
342 };
343
344 static 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
352 static 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) {
361                 dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
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) {
369                         dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
370                         return ret;
371                 }
372         }
373
374         return 0;
375 }
376
377 static 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) {
403                 dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
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) {
410                 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
411                 return ret;
412         }
413
414         msleep(60);
415
416         return 0;
417 }
418
419 static 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
433 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
434         .mode = &ltk050h3146w_a2_mode,
435         .init = ltk050h3146w_a2_init_sequence,
436 };
437
438 static 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) {
449                 dev_err(ctx->dev, "failed to set display off: %d\n", ret);
450                 return ret;
451         }
452
453         mipi_dsi_dcs_enter_sleep_mode(dsi);
454         if (ret < 0) {
455                 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
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
467 static 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
476         dev_dbg(ctx->dev, "Resetting the panel\n");
477         ret = regulator_enable(ctx->vci);
478         if (ret < 0) {
479                 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
480                 return ret;
481         }
482         ret = regulator_enable(ctx->iovcc);
483         if (ret < 0) {
484                 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
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) {
495                 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
496                 goto disable_iovcc;
497         }
498
499         ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
500         if (ret < 0) {
501                 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
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) {
510                 dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
511                 goto disable_iovcc;
512         }
513
514         msleep(50);
515
516         ctx->prepared = true;
517
518         return 0;
519
520 disable_iovcc:
521         regulator_disable(ctx->iovcc);
522 disable_vci:
523         regulator_disable(ctx->vci);
524         return ret;
525 }
526
527 static 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
547 static const struct drm_panel_funcs ltk050h3146w_funcs = {
548         .unprepare      = ltk050h3146w_unprepare,
549         .prepare        = ltk050h3146w_prepare,
550         .get_modes      = ltk050h3146w_get_modes,
551 };
552
553 static 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)) {
569                 dev_err(dev, "cannot get reset gpio\n");
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)
577                         dev_err(dev, "Failed to request vci regulator: %d\n", ret);
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)
585                         dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
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 |
596                           MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
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) {
609                 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
610                 drm_panel_remove(&ctx->panel);
611                 return ret;
612         }
613
614         return 0;
615 }
616
617 static 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)
624                 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
625
626         ret = drm_panel_disable(&ctx->panel);
627         if (ret < 0)
628                 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
629 }
630
631 static 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)
640                 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
641
642         drm_panel_remove(&ctx->panel);
643
644         return 0;
645 }
646
647 static 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 };
658 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
659
660 static 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 };
669 module_mipi_dsi_driver(ltk050h3146w_driver);
670
671 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
672 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
673 MODULE_LICENSE("GPL v2");