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