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