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 #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 
255 static 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 	 * Init sequence was supplied by the panel vendor without much
262 	 * documentation.
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 
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,
337 };
338 
339 static const struct ltk050h3146w_desc ltk050h3146w_data = {
340 	.mode = &ltk050h3146w_mode,
341 	.init = ltk050h3146w_init_sequence,
342 };
343 
344 static 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 
352 static 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 
377 static 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 	 * Init sequence was supplied by the panel vendor without much
384 	 * documentation.
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 	/* 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;
412 	}
413 
414 	msleep(60);
415 
416 	return 0;
417 }
418 
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,
431 };
432 
433 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
434 	.mode = &ltk050h3146w_a2_mode,
435 	.init = ltk050h3146w_a2_init_sequence,
436 };
437 
438 static 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 
467 static 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 	/* T9: 120ms */
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 
520 disable_iovcc:
521 	regulator_disable(ctx->iovcc);
522 disable_vci:
523 	regulator_disable(ctx->vci);
524 	return ret;
525 }
526 
527 static 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 
547 static const struct drm_panel_funcs ltk050h3146w_funcs = {
548 	.unprepare	= ltk050h3146w_unprepare,
549 	.prepare	= ltk050h3146w_prepare,
550 	.get_modes	= ltk050h3146w_get_modes,
551 };
552 
553 static 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_EOT_PACKET;
597 
598 	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_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 
617 static 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 
631 static 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 
647 static const struct of_device_id ltk050h3146w_of_match[] = {
648 	{
649 		.compatible = "leadtek,ltk050h3146w",
650 		.data = &ltk050h3146w_data,
651 	},
652 	{
653 		.compatible = "leadtek,ltk050h3146w-a2",
654 		.data = &ltk050h3146w_a2_data,
655 	},
656 	{ /* sentinel */ }
657 };
658 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
659 
660 static 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 };
669 module_mipi_dsi_driver(ltk050h3146w_driver);
670 
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");
674