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 #include <drm/drm_print.h>
21 
22 struct ltk050h3146w_cmd {
23 	char cmd;
24 	char data;
25 };
26 
27 struct ltk050h3146w;
28 struct ltk050h3146w_desc {
29 	const struct drm_display_mode *mode;
30 	int (*init)(struct ltk050h3146w *ctx);
31 };
32 
33 struct ltk050h3146w {
34 	struct device *dev;
35 	struct drm_panel panel;
36 	struct gpio_desc *reset_gpio;
37 	struct regulator *vci;
38 	struct regulator *iovcc;
39 	const struct ltk050h3146w_desc *panel_desc;
40 	bool prepared;
41 };
42 
43 static const struct ltk050h3146w_cmd page1_cmds[] = {
44 	{ 0x22, 0x0A }, /* BGR SS GS */
45 	{ 0x31, 0x00 }, /* column inversion */
46 	{ 0x53, 0xA2 }, /* VCOM1 */
47 	{ 0x55, 0xA2 }, /* VCOM2 */
48 	{ 0x50, 0x81 }, /* VREG1OUT=5V */
49 	{ 0x51, 0x85 }, /* VREG2OUT=-5V */
50 	{ 0x62, 0x0D }, /* EQT Time setting */
51 /*
52  * The vendor init selected page 1 here _again_
53  * Is this supposed to be page 2?
54  */
55 	{ 0xA0, 0x00 },
56 	{ 0xA1, 0x1A },
57 	{ 0xA2, 0x28 },
58 	{ 0xA3, 0x13 },
59 	{ 0xA4, 0x16 },
60 	{ 0xA5, 0x29 },
61 	{ 0xA6, 0x1D },
62 	{ 0xA7, 0x1E },
63 	{ 0xA8, 0x84 },
64 	{ 0xA9, 0x1C },
65 	{ 0xAA, 0x28 },
66 	{ 0xAB, 0x75 },
67 	{ 0xAC, 0x1A },
68 	{ 0xAD, 0x19 },
69 	{ 0xAE, 0x4D },
70 	{ 0xAF, 0x22 },
71 	{ 0xB0, 0x28 },
72 	{ 0xB1, 0x54 },
73 	{ 0xB2, 0x66 },
74 	{ 0xB3, 0x39 },
75 	{ 0xC0, 0x00 },
76 	{ 0xC1, 0x1A },
77 	{ 0xC2, 0x28 },
78 	{ 0xC3, 0x13 },
79 	{ 0xC4, 0x16 },
80 	{ 0xC5, 0x29 },
81 	{ 0xC6, 0x1D },
82 	{ 0xC7, 0x1E },
83 	{ 0xC8, 0x84 },
84 	{ 0xC9, 0x1C },
85 	{ 0xCA, 0x28 },
86 	{ 0xCB, 0x75 },
87 	{ 0xCC, 0x1A },
88 	{ 0xCD, 0x19 },
89 	{ 0xCE, 0x4D },
90 	{ 0xCF, 0x22 },
91 	{ 0xD0, 0x28 },
92 	{ 0xD1, 0x54 },
93 	{ 0xD2, 0x66 },
94 	{ 0xD3, 0x39 },
95 };
96 
97 static const struct ltk050h3146w_cmd page3_cmds[] = {
98 	{ 0x01, 0x00 },
99 	{ 0x02, 0x00 },
100 	{ 0x03, 0x73 },
101 	{ 0x04, 0x00 },
102 	{ 0x05, 0x00 },
103 	{ 0x06, 0x0a },
104 	{ 0x07, 0x00 },
105 	{ 0x08, 0x00 },
106 	{ 0x09, 0x01 },
107 	{ 0x0a, 0x00 },
108 	{ 0x0b, 0x00 },
109 	{ 0x0c, 0x01 },
110 	{ 0x0d, 0x00 },
111 	{ 0x0e, 0x00 },
112 	{ 0x0f, 0x1d },
113 	{ 0x10, 0x1d },
114 	{ 0x11, 0x00 },
115 	{ 0x12, 0x00 },
116 	{ 0x13, 0x00 },
117 	{ 0x14, 0x00 },
118 	{ 0x15, 0x00 },
119 	{ 0x16, 0x00 },
120 	{ 0x17, 0x00 },
121 	{ 0x18, 0x00 },
122 	{ 0x19, 0x00 },
123 	{ 0x1a, 0x00 },
124 	{ 0x1b, 0x00 },
125 	{ 0x1c, 0x00 },
126 	{ 0x1d, 0x00 },
127 	{ 0x1e, 0x40 },
128 	{ 0x1f, 0x80 },
129 	{ 0x20, 0x06 },
130 	{ 0x21, 0x02 },
131 	{ 0x22, 0x00 },
132 	{ 0x23, 0x00 },
133 	{ 0x24, 0x00 },
134 	{ 0x25, 0x00 },
135 	{ 0x26, 0x00 },
136 	{ 0x27, 0x00 },
137 	{ 0x28, 0x33 },
138 	{ 0x29, 0x03 },
139 	{ 0x2a, 0x00 },
140 	{ 0x2b, 0x00 },
141 	{ 0x2c, 0x00 },
142 	{ 0x2d, 0x00 },
143 	{ 0x2e, 0x00 },
144 	{ 0x2f, 0x00 },
145 	{ 0x30, 0x00 },
146 	{ 0x31, 0x00 },
147 	{ 0x32, 0x00 },
148 	{ 0x33, 0x00 },
149 	{ 0x34, 0x04 },
150 	{ 0x35, 0x00 },
151 	{ 0x36, 0x00 },
152 	{ 0x37, 0x00 },
153 	{ 0x38, 0x3C },
154 	{ 0x39, 0x35 },
155 	{ 0x3A, 0x01 },
156 	{ 0x3B, 0x40 },
157 	{ 0x3C, 0x00 },
158 	{ 0x3D, 0x01 },
159 	{ 0x3E, 0x00 },
160 	{ 0x3F, 0x00 },
161 	{ 0x40, 0x00 },
162 	{ 0x41, 0x88 },
163 	{ 0x42, 0x00 },
164 	{ 0x43, 0x00 },
165 	{ 0x44, 0x1F },
166 	{ 0x50, 0x01 },
167 	{ 0x51, 0x23 },
168 	{ 0x52, 0x45 },
169 	{ 0x53, 0x67 },
170 	{ 0x54, 0x89 },
171 	{ 0x55, 0xab },
172 	{ 0x56, 0x01 },
173 	{ 0x57, 0x23 },
174 	{ 0x58, 0x45 },
175 	{ 0x59, 0x67 },
176 	{ 0x5a, 0x89 },
177 	{ 0x5b, 0xab },
178 	{ 0x5c, 0xcd },
179 	{ 0x5d, 0xef },
180 	{ 0x5e, 0x11 },
181 	{ 0x5f, 0x01 },
182 	{ 0x60, 0x00 },
183 	{ 0x61, 0x15 },
184 	{ 0x62, 0x14 },
185 	{ 0x63, 0x0E },
186 	{ 0x64, 0x0F },
187 	{ 0x65, 0x0C },
188 	{ 0x66, 0x0D },
189 	{ 0x67, 0x06 },
190 	{ 0x68, 0x02 },
191 	{ 0x69, 0x07 },
192 	{ 0x6a, 0x02 },
193 	{ 0x6b, 0x02 },
194 	{ 0x6c, 0x02 },
195 	{ 0x6d, 0x02 },
196 	{ 0x6e, 0x02 },
197 	{ 0x6f, 0x02 },
198 	{ 0x70, 0x02 },
199 	{ 0x71, 0x02 },
200 	{ 0x72, 0x02 },
201 	{ 0x73, 0x02 },
202 	{ 0x74, 0x02 },
203 	{ 0x75, 0x01 },
204 	{ 0x76, 0x00 },
205 	{ 0x77, 0x14 },
206 	{ 0x78, 0x15 },
207 	{ 0x79, 0x0E },
208 	{ 0x7a, 0x0F },
209 	{ 0x7b, 0x0C },
210 	{ 0x7c, 0x0D },
211 	{ 0x7d, 0x06 },
212 	{ 0x7e, 0x02 },
213 	{ 0x7f, 0x07 },
214 	{ 0x80, 0x02 },
215 	{ 0x81, 0x02 },
216 	{ 0x82, 0x02 },
217 	{ 0x83, 0x02 },
218 	{ 0x84, 0x02 },
219 	{ 0x85, 0x02 },
220 	{ 0x86, 0x02 },
221 	{ 0x87, 0x02 },
222 	{ 0x88, 0x02 },
223 	{ 0x89, 0x02 },
224 	{ 0x8A, 0x02 },
225 };
226 
227 static const struct ltk050h3146w_cmd page4_cmds[] = {
228 	{ 0x70, 0x00 },
229 	{ 0x71, 0x00 },
230 	{ 0x82, 0x0F }, /* VGH_MOD clamp level=15v */
231 	{ 0x84, 0x0F }, /* VGH clamp level 15V */
232 	{ 0x85, 0x0D }, /* VGL clamp level (-10V) */
233 	{ 0x32, 0xAC },
234 	{ 0x8C, 0x80 },
235 	{ 0x3C, 0xF5 },
236 	{ 0xB5, 0x07 }, /* GAMMA OP */
237 	{ 0x31, 0x45 }, /* SOURCE OP */
238 	{ 0x3A, 0x24 }, /* PS_EN OFF */
239 	{ 0x88, 0x33 }, /* LVD */
240 };
241 
242 static inline
243 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
244 {
245 	return container_of(panel, struct ltk050h3146w, panel);
246 }
247 
248 #define dsi_dcs_write_seq(dsi, cmd, seq...) do {			\
249 		static const u8 d[] = { seq };				\
250 		int ret;						\
251 		ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d));	\
252 		if (ret < 0)						\
253 			return ret;					\
254 	} while (0)
255 
256 static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
257 {
258 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
259 	int ret;
260 
261 	/*
262 	 * Init sequence was supplied by the panel vendor without much
263 	 * documentation.
264 	 */
265 	dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
266 	dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
267 			  0x01);
268 	dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
269 	dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
270 	dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
271 
272 	dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
273 	dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
274 			  0x28, 0x04, 0xcc, 0xcc, 0xcc);
275 	dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
276 	dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
277 	dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
278 	dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
279 	dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
280 			  0x80);
281 	dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
282 			  0x16, 0x00, 0x00);
283 	dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
284 			  0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
285 			  0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
286 			  0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
287 			  0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
288 	dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
289 			  0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
290 			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
291 	dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
292 			  0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
293 			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
294 	dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
295 			  0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
296 			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
297 	dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
298 			  0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
299 			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
300 	dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
301 			  0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
302 			  0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
303 	dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
304 			  0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
305 			  0x21, 0x00, 0x60);
306 	dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
307 	dsi_dcs_write_seq(dsi, 0xde, 0x02);
308 	dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
309 	dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
310 	dsi_dcs_write_seq(dsi, 0xc1, 0x11);
311 	dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
312 	dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
313 	dsi_dcs_write_seq(dsi, 0xde, 0x00);
314 
315 	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
316 	if (ret < 0) {
317 		DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
318 			      ret);
319 		return ret;
320 	}
321 
322 	msleep(60);
323 
324 	return 0;
325 }
326 
327 static const struct drm_display_mode ltk050h3146w_mode = {
328 	.hdisplay	= 720,
329 	.hsync_start	= 720 + 42,
330 	.hsync_end	= 720 + 42 + 8,
331 	.htotal		= 720 + 42 + 8 + 42,
332 	.vdisplay	= 1280,
333 	.vsync_start	= 1280 + 12,
334 	.vsync_end	= 1280 + 12 + 4,
335 	.vtotal		= 1280 + 12 + 4 + 18,
336 	.clock		= 64018,
337 	.width_mm	= 62,
338 	.height_mm	= 110,
339 };
340 
341 static const struct ltk050h3146w_desc ltk050h3146w_data = {
342 	.mode = &ltk050h3146w_mode,
343 	.init = ltk050h3146w_init_sequence,
344 };
345 
346 static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
347 {
348 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
349 	u8 d[3] = { 0x98, 0x81, page };
350 
351 	return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
352 }
353 
354 static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
355 				      const struct ltk050h3146w_cmd *cmds,
356 				      int num)
357 {
358 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
359 	int i, ret;
360 
361 	ret = ltk050h3146w_a2_select_page(ctx, page);
362 	if (ret < 0) {
363 		DRM_DEV_ERROR(ctx->dev, "failed to select page %d: %d\n",
364 			      page, ret);
365 		return ret;
366 	}
367 
368 	for (i = 0; i < num; i++) {
369 		ret = mipi_dsi_generic_write(dsi, &cmds[i],
370 					     sizeof(struct ltk050h3146w_cmd));
371 		if (ret < 0) {
372 			DRM_DEV_ERROR(ctx->dev,
373 				      "failed to write page %d init cmds: %d\n",
374 				       page, ret);
375 			return ret;
376 		}
377 	}
378 
379 	return 0;
380 }
381 
382 static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
383 {
384 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
385 	int ret;
386 
387 	/*
388 	 * Init sequence was supplied by the panel vendor without much
389 	 * documentation.
390 	 */
391 	ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
392 					 ARRAY_SIZE(page3_cmds));
393 	if (ret < 0)
394 		return ret;
395 
396 	ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
397 					 ARRAY_SIZE(page4_cmds));
398 	if (ret < 0)
399 		return ret;
400 
401 	ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
402 					 ARRAY_SIZE(page1_cmds));
403 	if (ret < 0)
404 		return ret;
405 
406 	ret = ltk050h3146w_a2_select_page(ctx, 0);
407 	if (ret < 0) {
408 		DRM_DEV_ERROR(ctx->dev, "failed to select page 0: %d\n", ret);
409 		return ret;
410 	}
411 
412 	/* vendor code called this without param, where there should be one */
413 	ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
414 	if (ret < 0) {
415 		DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
416 			      ret);
417 		return ret;
418 	}
419 
420 	msleep(60);
421 
422 	return 0;
423 }
424 
425 static const struct drm_display_mode ltk050h3146w_a2_mode = {
426 	.hdisplay	= 720,
427 	.hsync_start	= 720 + 42,
428 	.hsync_end	= 720 + 42 + 10,
429 	.htotal		= 720 + 42 + 10 + 60,
430 	.vdisplay	= 1280,
431 	.vsync_start	= 1280 + 18,
432 	.vsync_end	= 1280 + 18 + 4,
433 	.vtotal		= 1280 + 18 + 4 + 12,
434 	.clock		= 65595,
435 	.width_mm	= 62,
436 	.height_mm	= 110,
437 };
438 
439 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
440 	.mode = &ltk050h3146w_a2_mode,
441 	.init = ltk050h3146w_a2_init_sequence,
442 };
443 
444 static int ltk050h3146w_unprepare(struct drm_panel *panel)
445 {
446 	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
447 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
448 	int ret;
449 
450 	if (!ctx->prepared)
451 		return 0;
452 
453 	ret = mipi_dsi_dcs_set_display_off(dsi);
454 	if (ret < 0) {
455 		DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n",
456 			      ret);
457 		return ret;
458 	}
459 
460 	mipi_dsi_dcs_enter_sleep_mode(dsi);
461 	if (ret < 0) {
462 		DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n",
463 			      ret);
464 		return ret;
465 	}
466 
467 	regulator_disable(ctx->iovcc);
468 	regulator_disable(ctx->vci);
469 
470 	ctx->prepared = false;
471 
472 	return 0;
473 }
474 
475 static int ltk050h3146w_prepare(struct drm_panel *panel)
476 {
477 	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
478 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
479 	int ret;
480 
481 	if (ctx->prepared)
482 		return 0;
483 
484 	DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
485 	ret = regulator_enable(ctx->vci);
486 	if (ret < 0) {
487 		DRM_DEV_ERROR(ctx->dev,
488 			      "Failed to enable vci supply: %d\n", ret);
489 		return ret;
490 	}
491 	ret = regulator_enable(ctx->iovcc);
492 	if (ret < 0) {
493 		DRM_DEV_ERROR(ctx->dev,
494 			      "Failed to enable iovcc supply: %d\n", ret);
495 		goto disable_vci;
496 	}
497 
498 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
499 	usleep_range(5000, 6000);
500 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
501 	msleep(20);
502 
503 	ret = ctx->panel_desc->init(ctx);
504 	if (ret < 0) {
505 		DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
506 			      ret);
507 		goto disable_iovcc;
508 	}
509 
510 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
511 	if (ret < 0) {
512 		DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
513 		goto disable_iovcc;
514 	}
515 
516 	/* T9: 120ms */
517 	msleep(120);
518 
519 	ret = mipi_dsi_dcs_set_display_on(dsi);
520 	if (ret < 0) {
521 		DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret);
522 		goto disable_iovcc;
523 	}
524 
525 	msleep(50);
526 
527 	ctx->prepared = true;
528 
529 	return 0;
530 
531 disable_iovcc:
532 	regulator_disable(ctx->iovcc);
533 disable_vci:
534 	regulator_disable(ctx->vci);
535 	return ret;
536 }
537 
538 static int ltk050h3146w_get_modes(struct drm_panel *panel,
539 				  struct drm_connector *connector)
540 {
541 	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
542 	struct drm_display_mode *mode;
543 
544 	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
545 	if (!mode)
546 		return -ENOMEM;
547 
548 	drm_mode_set_name(mode);
549 
550 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
551 	connector->display_info.width_mm = mode->width_mm;
552 	connector->display_info.height_mm = mode->height_mm;
553 	drm_mode_probed_add(connector, mode);
554 
555 	return 1;
556 }
557 
558 static const struct drm_panel_funcs ltk050h3146w_funcs = {
559 	.unprepare	= ltk050h3146w_unprepare,
560 	.prepare	= ltk050h3146w_prepare,
561 	.get_modes	= ltk050h3146w_get_modes,
562 };
563 
564 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
565 {
566 	struct device *dev = &dsi->dev;
567 	struct ltk050h3146w *ctx;
568 	int ret;
569 
570 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
571 	if (!ctx)
572 		return -ENOMEM;
573 
574 	ctx->panel_desc = of_device_get_match_data(dev);
575 	if (!ctx->panel_desc)
576 		return -EINVAL;
577 
578 	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
579 	if (IS_ERR(ctx->reset_gpio)) {
580 		DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
581 		return PTR_ERR(ctx->reset_gpio);
582 	}
583 
584 	ctx->vci = devm_regulator_get(dev, "vci");
585 	if (IS_ERR(ctx->vci)) {
586 		ret = PTR_ERR(ctx->vci);
587 		if (ret != -EPROBE_DEFER)
588 			DRM_DEV_ERROR(dev,
589 				      "Failed to request vci regulator: %d\n",
590 				      ret);
591 		return ret;
592 	}
593 
594 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
595 	if (IS_ERR(ctx->iovcc)) {
596 		ret = PTR_ERR(ctx->iovcc);
597 		if (ret != -EPROBE_DEFER)
598 			DRM_DEV_ERROR(dev,
599 				      "Failed to request iovcc regulator: %d\n",
600 				      ret);
601 		return ret;
602 	}
603 
604 	mipi_dsi_set_drvdata(dsi, ctx);
605 
606 	ctx->dev = dev;
607 
608 	dsi->lanes = 4;
609 	dsi->format = MIPI_DSI_FMT_RGB888;
610 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
611 			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
612 
613 	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
614 		       DRM_MODE_CONNECTOR_DSI);
615 
616 	ret = drm_panel_of_backlight(&ctx->panel);
617 	if (ret)
618 		return ret;
619 
620 	drm_panel_add(&ctx->panel);
621 
622 	ret = mipi_dsi_attach(dsi);
623 	if (ret < 0) {
624 		DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret);
625 		drm_panel_remove(&ctx->panel);
626 		return ret;
627 	}
628 
629 	return 0;
630 }
631 
632 static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
633 {
634 	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
635 	int ret;
636 
637 	ret = drm_panel_unprepare(&ctx->panel);
638 	if (ret < 0)
639 		DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
640 			      ret);
641 
642 	ret = drm_panel_disable(&ctx->panel);
643 	if (ret < 0)
644 		DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
645 			      ret);
646 }
647 
648 static int ltk050h3146w_remove(struct mipi_dsi_device *dsi)
649 {
650 	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
651 	int ret;
652 
653 	ltk050h3146w_shutdown(dsi);
654 
655 	ret = mipi_dsi_detach(dsi);
656 	if (ret < 0)
657 		DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
658 			      ret);
659 
660 	drm_panel_remove(&ctx->panel);
661 
662 	return 0;
663 }
664 
665 static const struct of_device_id ltk050h3146w_of_match[] = {
666 	{
667 		.compatible = "leadtek,ltk050h3146w",
668 		.data = &ltk050h3146w_data,
669 	},
670 	{
671 		.compatible = "leadtek,ltk050h3146w-a2",
672 		.data = &ltk050h3146w_a2_data,
673 	},
674 	{ /* sentinel */ }
675 };
676 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
677 
678 static struct mipi_dsi_driver ltk050h3146w_driver = {
679 	.driver = {
680 		.name = "panel-leadtek-ltk050h3146w",
681 		.of_match_table = ltk050h3146w_of_match,
682 	},
683 	.probe	= ltk050h3146w_probe,
684 	.remove = ltk050h3146w_remove,
685 	.shutdown = ltk050h3146w_shutdown,
686 };
687 module_mipi_dsi_driver(ltk050h3146w_driver);
688 
689 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
690 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
691 MODULE_LICENSE("GPL v2");
692