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