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