1
2
3
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
21struct ltk050h3146w_cmd {
22 char cmd;
23 char data;
24};
25
26struct ltk050h3146w;
27struct ltk050h3146w_desc {
28 const struct drm_display_mode *mode;
29 int (*init)(struct ltk050h3146w *ctx);
30};
31
32struct 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
42static const struct ltk050h3146w_cmd page1_cmds[] = {
43 { 0x22, 0x0A },
44 { 0x31, 0x00 },
45 { 0x53, 0xA2 },
46 { 0x55, 0xA2 },
47 { 0x50, 0x81 },
48 { 0x51, 0x85 },
49 { 0x62, 0x0D },
50
51
52
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
96static 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
226static const struct ltk050h3146w_cmd page4_cmds[] = {
227 { 0x70, 0x00 },
228 { 0x71, 0x00 },
229 { 0x82, 0x0F },
230 { 0x84, 0x0F },
231 { 0x85, 0x0D },
232 { 0x32, 0xAC },
233 { 0x8C, 0x80 },
234 { 0x3C, 0xF5 },
235 { 0xB5, 0x07 },
236 { 0x31, 0x45 },
237 { 0x3A, 0x24 },
238 { 0x88, 0x33 },
239};
240
241static inline
242struct 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
255static 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
262
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
325static 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
339static const struct ltk050h3146w_desc ltk050h3146w_data = {
340 .mode = <k050h3146w_mode,
341 .init = ltk050h3146w_init_sequence,
342};
343
344static 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
352static 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
377static 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
384
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
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
419static 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
433static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
434 .mode = <k050h3146w_a2_mode,
435 .init = ltk050h3146w_a2_init_sequence,
436};
437
438static 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
467static 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
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
520disable_iovcc:
521 regulator_disable(ctx->iovcc);
522disable_vci:
523 regulator_disable(ctx->vci);
524 return ret;
525}
526
527static 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
547static const struct drm_panel_funcs ltk050h3146w_funcs = {
548 .unprepare = ltk050h3146w_unprepare,
549 .prepare = ltk050h3146w_prepare,
550 .get_modes = ltk050h3146w_get_modes,
551};
552
553static 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_NO_EOT_PACKET;
597
598 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_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
617static 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
631static 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
647static const struct of_device_id ltk050h3146w_of_match[] = {
648 {
649 .compatible = "leadtek,ltk050h3146w",
650 .data = <k050h3146w_data,
651 },
652 {
653 .compatible = "leadtek,ltk050h3146w-a2",
654 .data = <k050h3146w_a2_data,
655 },
656 { }
657};
658MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
659
660static 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};
669module_mipi_dsi_driver(ltk050h3146w_driver);
670
671MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
672MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
673MODULE_LICENSE("GPL v2");
674