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
panel_to_ltk050h3146w(struct drm_panel * panel)241 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
242 {
243 return container_of(panel, struct ltk050h3146w, panel);
244 }
245
ltk050h3146w_init_sequence(struct ltk050h3146w * ctx)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 = <k050h3146w_mode,
332 .init = ltk050h3146w_init_sequence,
333 };
334
ltk050h3146w_a2_select_page(struct ltk050h3146w * ctx,int page)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
ltk050h3146w_a2_write_page(struct ltk050h3146w * ctx,int page,const struct ltk050h3146w_cmd * cmds,int num)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
ltk050h3146w_a2_init_sequence(struct ltk050h3146w * ctx)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 = <k050h3146w_a2_mode,
426 .init = ltk050h3146w_a2_init_sequence,
427 };
428
ltk050h3146w_unprepare(struct drm_panel * panel)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
ltk050h3146w_prepare(struct drm_panel * panel)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
ltk050h3146w_get_modes(struct drm_panel * panel,struct drm_connector * connector)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
ltk050h3146w_probe(struct mipi_dsi_device * dsi)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, <k050h3146w_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
ltk050h3146w_shutdown(struct mipi_dsi_device * dsi)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
ltk050h3146w_remove(struct mipi_dsi_device * dsi)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 = <k050h3146w_data,
640 },
641 {
642 .compatible = "leadtek,ltk050h3146w-a2",
643 .data = <k050h3146w_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");
663