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
20struct ltk050h3146w_cmd {
21	char cmd;
22	char data;
23};
24
25struct ltk050h3146w;
26struct ltk050h3146w_desc {
27	const unsigned long mode_flags;
28	const struct drm_display_mode *mode;
29	int (*init)(struct ltk050h3146w *ctx);
30};
31
32struct 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
42static 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
96static 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
226static 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
241static inline
242struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
243{
244	return container_of(panel, struct ltk050h3146w, panel);
245}
246
247static 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
312static 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
326static 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
332static 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
402static 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
416static 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
423static 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
431static 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
456static 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
498static 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
512static 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
519static 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
548static 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
601disable_iovcc:
602	regulator_disable(ctx->iovcc);
603disable_vci:
604	regulator_disable(ctx->vci);
605	return ret;
606}
607
608static 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
628static const struct drm_panel_funcs ltk050h3146w_funcs = {
629	.unprepare	= ltk050h3146w_unprepare,
630	.prepare	= ltk050h3146w_prepare,
631	.get_modes	= ltk050h3146w_get_modes,
632};
633
634static 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
688static 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
702static 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
716static 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};
731MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
732
733static 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};
742module_mipi_dsi_driver(ltk050h3146w_driver);
743
744MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
745MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
746MODULE_LICENSE("GPL v2");
747