WIP FPC-III support
[linux/fpc-iii.git] / drivers / gpu / drm / panel / panel-leadtek-ltk050h3146w.c
blobed0d5f959037da8177c137b07bf89bbfbe02a48b
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
4 */
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>
14 #include <video/display_timing.h>
15 #include <video/mipi_display.h>
17 #include <drm/drm_mipi_dsi.h>
18 #include <drm/drm_modes.h>
19 #include <drm/drm_panel.h>
21 struct ltk050h3146w_cmd {
22 char cmd;
23 char data;
26 struct ltk050h3146w;
27 struct ltk050h3146w_desc {
28 const struct drm_display_mode *mode;
29 int (*init)(struct ltk050h3146w *ctx);
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;
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 */
51 * The vendor init selected page 1 here _again_
52 * Is this supposed to be page 2?
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 },
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 },
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 */
241 static inline
242 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
244 return container_of(panel, struct ltk050h3146w, panel);
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)
255 static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
257 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
258 int ret;
261 * Init sequence was supplied by the panel vendor without much
262 * documentation.
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);
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);
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;
320 msleep(60);
322 return 0;
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,
339 static const struct ltk050h3146w_desc ltk050h3146w_data = {
340 .mode = &ltk050h3146w_mode,
341 .init = ltk050h3146w_init_sequence,
344 static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
346 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
347 u8 d[3] = { 0x98, 0x81, page };
349 return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
352 static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
353 const struct ltk050h3146w_cmd *cmds,
354 int num)
356 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
357 int i, ret;
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;
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;
374 return 0;
377 static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
379 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
380 int ret;
383 * Init sequence was supplied by the panel vendor without much
384 * documentation.
386 ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
387 ARRAY_SIZE(page3_cmds));
388 if (ret < 0)
389 return ret;
391 ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
392 ARRAY_SIZE(page4_cmds));
393 if (ret < 0)
394 return ret;
396 ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
397 ARRAY_SIZE(page1_cmds));
398 if (ret < 0)
399 return ret;
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;
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;
414 msleep(60);
416 return 0;
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,
433 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
434 .mode = &ltk050h3146w_a2_mode,
435 .init = ltk050h3146w_a2_init_sequence,
438 static int ltk050h3146w_unprepare(struct drm_panel *panel)
440 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
441 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
442 int ret;
444 if (!ctx->prepared)
445 return 0;
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;
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;
459 regulator_disable(ctx->iovcc);
460 regulator_disable(ctx->vci);
462 ctx->prepared = false;
464 return 0;
467 static int ltk050h3146w_prepare(struct drm_panel *panel)
469 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
470 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
471 int ret;
473 if (ctx->prepared)
474 return 0;
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;
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;
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);
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;
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;
505 /* T9: 120ms */
506 msleep(120);
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;
514 msleep(50);
516 ctx->prepared = true;
518 return 0;
520 disable_iovcc:
521 regulator_disable(ctx->iovcc);
522 disable_vci:
523 regulator_disable(ctx->vci);
524 return ret;
527 static int ltk050h3146w_get_modes(struct drm_panel *panel,
528 struct drm_connector *connector)
530 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
531 struct drm_display_mode *mode;
533 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
534 if (!mode)
535 return -ENOMEM;
537 drm_mode_set_name(mode);
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);
544 return 1;
547 static const struct drm_panel_funcs ltk050h3146w_funcs = {
548 .unprepare = ltk050h3146w_unprepare,
549 .prepare = ltk050h3146w_prepare,
550 .get_modes = ltk050h3146w_get_modes,
553 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
555 struct device *dev = &dsi->dev;
556 struct ltk050h3146w *ctx;
557 int ret;
559 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
560 if (!ctx)
561 return -ENOMEM;
563 ctx->panel_desc = of_device_get_match_data(dev);
564 if (!ctx->panel_desc)
565 return -EINVAL;
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);
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;
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;
589 mipi_dsi_set_drvdata(dsi, ctx);
591 ctx->dev = dev;
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;
598 drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
599 DRM_MODE_CONNECTOR_DSI);
601 ret = drm_panel_of_backlight(&ctx->panel);
602 if (ret)
603 return ret;
605 drm_panel_add(&ctx->panel);
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;
614 return 0;
617 static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
619 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
620 int ret;
622 ret = drm_panel_unprepare(&ctx->panel);
623 if (ret < 0)
624 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
626 ret = drm_panel_disable(&ctx->panel);
627 if (ret < 0)
628 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
631 static int ltk050h3146w_remove(struct mipi_dsi_device *dsi)
633 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
634 int ret;
636 ltk050h3146w_shutdown(dsi);
638 ret = mipi_dsi_detach(dsi);
639 if (ret < 0)
640 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
642 drm_panel_remove(&ctx->panel);
644 return 0;
647 static const struct of_device_id ltk050h3146w_of_match[] = {
649 .compatible = "leadtek,ltk050h3146w",
650 .data = &ltk050h3146w_data,
653 .compatible = "leadtek,ltk050h3146w-a2",
654 .data = &ltk050h3146w_a2_data,
656 { /* sentinel */ }
658 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
660 static struct mipi_dsi_driver ltk050h3146w_driver = {
661 .driver = {
662 .name = "panel-leadtek-ltk050h3146w",
663 .of_match_table = ltk050h3146w_of_match,
665 .probe = ltk050h3146w_probe,
666 .remove = ltk050h3146w_remove,
667 .shutdown = ltk050h3146w_shutdown,
669 module_mipi_dsi_driver(ltk050h3146w_driver);
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");