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