Merge drm/drm-next into drm-misc-next
[sfrench/cifs-2.6.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 unsigned long mode_flags;
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 static int ltk050h3148w_init_sequence(struct ltk050h3146w *ctx)
248 {
249         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
250         int ret;
251
252         /*
253          * Init sequence was supplied by the panel vendor without much
254          * documentation.
255          */
256         mipi_dsi_dcs_write_seq(dsi, 0xb9, 0xff, 0x83, 0x94);
257         mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
258                                0x71, 0x31, 0x55, 0x2f);
259         mipi_dsi_dcs_write_seq(dsi, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
260         mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x88);
261         mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
262         mipi_dsi_dcs_write_seq(dsi, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
263                                0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
264                                0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
265         mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
266                                0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
267                                0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
268                                0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
269                                0x17, 0x11, 0x40);
270         mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
271                                0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
272                                0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
273                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
274                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
275                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
276         mipi_dsi_dcs_write_seq(dsi, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
277                                0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
278                                0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
279                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
280                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
281                                0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
282         mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
283                                0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
284                                0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
285                                0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
286                                0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
287                                0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
288                                0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
289                                0x5d, 0x61, 0x65, 0x7f);
290         mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x0b);
291         mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x1f, 0x31);
292         mipi_dsi_dcs_write_seq(dsi, 0xb6, 0xc4, 0xc4);
293         mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
294         mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x00);
295         mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
296         mipi_dsi_dcs_write_seq(dsi, 0xc6, 0xef);
297         mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x02);
298         mipi_dsi_dcs_write_seq(dsi, 0x11);
299         mipi_dsi_dcs_write_seq(dsi, 0x29);
300
301         ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
302         if (ret < 0) {
303                 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
304                 return ret;
305         }
306
307         msleep(60);
308
309         return 0;
310 }
311
312 static const struct drm_display_mode ltk050h3148w_mode = {
313         .hdisplay       = 720,
314         .hsync_start    = 720 + 12,
315         .hsync_end      = 720 + 12 + 6,
316         .htotal         = 720 + 12 + 6 + 24,
317         .vdisplay       = 1280,
318         .vsync_start    = 1280 + 9,
319         .vsync_end      = 1280 + 9 + 2,
320         .vtotal         = 1280 + 9 + 2 + 16,
321         .clock          = 59756,
322         .width_mm       = 62,
323         .height_mm      = 110,
324 };
325
326 static const struct ltk050h3146w_desc ltk050h3148w_data = {
327         .mode = &ltk050h3148w_mode,
328         .init = ltk050h3148w_init_sequence,
329         .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO_BURST,
330 };
331
332 static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
333 {
334         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
335         int ret;
336
337         /*
338          * Init sequence was supplied by the panel vendor without much
339          * documentation.
340          */
341         mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
342         mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
343                                0x01);
344         mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
345         mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
346         mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
347
348         mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
349         mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
350                                0x28, 0x04, 0xcc, 0xcc, 0xcc);
351         mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
352         mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
353         mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
354         mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
355         mipi_dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
356                                0x80);
357         mipi_dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
358                                0x16, 0x00, 0x00);
359         mipi_dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
360                                0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
361                                0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
362                                0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
363                                0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
364         mipi_dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
365                                0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
366                                0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
367         mipi_dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
368                                0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
369                                0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
370         mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
371                                0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
372                                0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
373         mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
374                                0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
375                                0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
376         mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
377                                0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
378                                0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
379         mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
380                                0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
381                                0x21, 0x00, 0x60);
382         mipi_dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
383         mipi_dsi_dcs_write_seq(dsi, 0xde, 0x02);
384         mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
385         mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
386         mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x11);
387         mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
388         mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
389         mipi_dsi_dcs_write_seq(dsi, 0xde, 0x00);
390
391         ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
392         if (ret < 0) {
393                 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
394                 return ret;
395         }
396
397         msleep(60);
398
399         return 0;
400 }
401
402 static const struct drm_display_mode ltk050h3146w_mode = {
403         .hdisplay       = 720,
404         .hsync_start    = 720 + 42,
405         .hsync_end      = 720 + 42 + 8,
406         .htotal         = 720 + 42 + 8 + 42,
407         .vdisplay       = 1280,
408         .vsync_start    = 1280 + 12,
409         .vsync_end      = 1280 + 12 + 4,
410         .vtotal         = 1280 + 12 + 4 + 18,
411         .clock          = 64018,
412         .width_mm       = 62,
413         .height_mm      = 110,
414 };
415
416 static const struct ltk050h3146w_desc ltk050h3146w_data = {
417         .mode = &ltk050h3146w_mode,
418         .init = ltk050h3146w_init_sequence,
419         .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
420                 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
421 };
422
423 static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
424 {
425         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
426         u8 d[3] = { 0x98, 0x81, page };
427
428         return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
429 }
430
431 static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
432                                       const struct ltk050h3146w_cmd *cmds,
433                                       int num)
434 {
435         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
436         int i, ret;
437
438         ret = ltk050h3146w_a2_select_page(ctx, page);
439         if (ret < 0) {
440                 dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
441                 return ret;
442         }
443
444         for (i = 0; i < num; i++) {
445                 ret = mipi_dsi_generic_write(dsi, &cmds[i],
446                                              sizeof(struct ltk050h3146w_cmd));
447                 if (ret < 0) {
448                         dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
449                         return ret;
450                 }
451         }
452
453         return 0;
454 }
455
456 static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
457 {
458         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
459         int ret;
460
461         /*
462          * Init sequence was supplied by the panel vendor without much
463          * documentation.
464          */
465         ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
466                                          ARRAY_SIZE(page3_cmds));
467         if (ret < 0)
468                 return ret;
469
470         ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
471                                          ARRAY_SIZE(page4_cmds));
472         if (ret < 0)
473                 return ret;
474
475         ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
476                                          ARRAY_SIZE(page1_cmds));
477         if (ret < 0)
478                 return ret;
479
480         ret = ltk050h3146w_a2_select_page(ctx, 0);
481         if (ret < 0) {
482                 dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
483                 return ret;
484         }
485
486         /* vendor code called this without param, where there should be one */
487         ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
488         if (ret < 0) {
489                 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
490                 return ret;
491         }
492
493         msleep(60);
494
495         return 0;
496 }
497
498 static const struct drm_display_mode ltk050h3146w_a2_mode = {
499         .hdisplay       = 720,
500         .hsync_start    = 720 + 42,
501         .hsync_end      = 720 + 42 + 10,
502         .htotal         = 720 + 42 + 10 + 60,
503         .vdisplay       = 1280,
504         .vsync_start    = 1280 + 18,
505         .vsync_end      = 1280 + 18 + 4,
506         .vtotal         = 1280 + 18 + 4 + 12,
507         .clock          = 65595,
508         .width_mm       = 62,
509         .height_mm      = 110,
510 };
511
512 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
513         .mode = &ltk050h3146w_a2_mode,
514         .init = ltk050h3146w_a2_init_sequence,
515         .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
516                 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
517 };
518
519 static int ltk050h3146w_unprepare(struct drm_panel *panel)
520 {
521         struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
522         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
523         int ret;
524
525         if (!ctx->prepared)
526                 return 0;
527
528         ret = mipi_dsi_dcs_set_display_off(dsi);
529         if (ret < 0) {
530                 dev_err(ctx->dev, "failed to set display off: %d\n", ret);
531                 return ret;
532         }
533
534         mipi_dsi_dcs_enter_sleep_mode(dsi);
535         if (ret < 0) {
536                 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
537                 return ret;
538         }
539
540         regulator_disable(ctx->iovcc);
541         regulator_disable(ctx->vci);
542
543         ctx->prepared = false;
544
545         return 0;
546 }
547
548 static int ltk050h3146w_prepare(struct drm_panel *panel)
549 {
550         struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
551         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
552         int ret;
553
554         if (ctx->prepared)
555                 return 0;
556
557         dev_dbg(ctx->dev, "Resetting the panel\n");
558         ret = regulator_enable(ctx->vci);
559         if (ret < 0) {
560                 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
561                 return ret;
562         }
563         ret = regulator_enable(ctx->iovcc);
564         if (ret < 0) {
565                 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
566                 goto disable_vci;
567         }
568
569         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
570         usleep_range(5000, 6000);
571         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
572         msleep(20);
573
574         ret = ctx->panel_desc->init(ctx);
575         if (ret < 0) {
576                 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
577                 goto disable_iovcc;
578         }
579
580         ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
581         if (ret < 0) {
582                 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
583                 goto disable_iovcc;
584         }
585
586         /* T9: 120ms */
587         msleep(120);
588
589         ret = mipi_dsi_dcs_set_display_on(dsi);
590         if (ret < 0) {
591                 dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
592                 goto disable_iovcc;
593         }
594
595         msleep(50);
596
597         ctx->prepared = true;
598
599         return 0;
600
601 disable_iovcc:
602         regulator_disable(ctx->iovcc);
603 disable_vci:
604         regulator_disable(ctx->vci);
605         return ret;
606 }
607
608 static int ltk050h3146w_get_modes(struct drm_panel *panel,
609                                   struct drm_connector *connector)
610 {
611         struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
612         struct drm_display_mode *mode;
613
614         mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
615         if (!mode)
616                 return -ENOMEM;
617
618         drm_mode_set_name(mode);
619
620         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
621         connector->display_info.width_mm = mode->width_mm;
622         connector->display_info.height_mm = mode->height_mm;
623         drm_mode_probed_add(connector, mode);
624
625         return 1;
626 }
627
628 static const struct drm_panel_funcs ltk050h3146w_funcs = {
629         .unprepare      = ltk050h3146w_unprepare,
630         .prepare        = ltk050h3146w_prepare,
631         .get_modes      = ltk050h3146w_get_modes,
632 };
633
634 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
635 {
636         struct device *dev = &dsi->dev;
637         struct ltk050h3146w *ctx;
638         int ret;
639
640         ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
641         if (!ctx)
642                 return -ENOMEM;
643
644         ctx->panel_desc = of_device_get_match_data(dev);
645         if (!ctx->panel_desc)
646                 return -EINVAL;
647
648         ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
649         if (IS_ERR(ctx->reset_gpio))
650                 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");
651
652         ctx->vci = devm_regulator_get(dev, "vci");
653         if (IS_ERR(ctx->vci))
654                 return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");
655
656         ctx->iovcc = devm_regulator_get(dev, "iovcc");
657         if (IS_ERR(ctx->iovcc))
658                 return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
659                                      "Failed to request iovcc regulator\n");
660
661         mipi_dsi_set_drvdata(dsi, ctx);
662
663         ctx->dev = dev;
664
665         dsi->lanes = 4;
666         dsi->format = MIPI_DSI_FMT_RGB888;
667         dsi->mode_flags = ctx->panel_desc->mode_flags;
668
669         drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
670                        DRM_MODE_CONNECTOR_DSI);
671
672         ret = drm_panel_of_backlight(&ctx->panel);
673         if (ret)
674                 return ret;
675
676         drm_panel_add(&ctx->panel);
677
678         ret = mipi_dsi_attach(dsi);
679         if (ret < 0) {
680                 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
681                 drm_panel_remove(&ctx->panel);
682                 return ret;
683         }
684
685         return 0;
686 }
687
688 static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
689 {
690         struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
691         int ret;
692
693         ret = drm_panel_unprepare(&ctx->panel);
694         if (ret < 0)
695                 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
696
697         ret = drm_panel_disable(&ctx->panel);
698         if (ret < 0)
699                 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
700 }
701
702 static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
703 {
704         struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
705         int ret;
706
707         ltk050h3146w_shutdown(dsi);
708
709         ret = mipi_dsi_detach(dsi);
710         if (ret < 0)
711                 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
712
713         drm_panel_remove(&ctx->panel);
714 }
715
716 static const struct of_device_id ltk050h3146w_of_match[] = {
717         {
718                 .compatible = "leadtek,ltk050h3146w",
719                 .data = &ltk050h3146w_data,
720         },
721         {
722                 .compatible = "leadtek,ltk050h3146w-a2",
723                 .data = &ltk050h3146w_a2_data,
724         },
725         {
726                 .compatible = "leadtek,ltk050h3148w",
727                 .data = &ltk050h3148w_data,
728         },
729         { /* sentinel */ }
730 };
731 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
732
733 static struct mipi_dsi_driver ltk050h3146w_driver = {
734         .driver = {
735                 .name = "panel-leadtek-ltk050h3146w",
736                 .of_match_table = ltk050h3146w_of_match,
737         },
738         .probe  = ltk050h3146w_probe,
739         .remove = ltk050h3146w_remove,
740         .shutdown = ltk050h3146w_shutdown,
741 };
742 module_mipi_dsi_driver(ltk050h3146w_driver);
743
744 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
745 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
746 MODULE_LICENSE("GPL v2");