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 struct drm_display_mode *mode;
28	int (*init)(struct ltk050h3146w *ctx);
29};
30
31struct 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
41static 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
95static 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
225static 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
240static inline
241struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
242{
243	return container_of(panel, struct ltk050h3146w, panel);
244}
245
246static 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
316static 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
330static const struct ltk050h3146w_desc ltk050h3146w_data = {
331	.mode = &ltk050h3146w_mode,
332	.init = ltk050h3146w_init_sequence,
333};
334
335static 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
343static 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
368static 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
410static 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
424static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
425	.mode = &ltk050h3146w_a2_mode,
426	.init = ltk050h3146w_a2_init_sequence,
427};
428
429static 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
458static 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
511disable_iovcc:
512	regulator_disable(ctx->iovcc);
513disable_vci:
514	regulator_disable(ctx->vci);
515	return ret;
516}
517
518static 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
538static const struct drm_panel_funcs ltk050h3146w_funcs = {
539	.unprepare	= ltk050h3146w_unprepare,
540	.prepare	= ltk050h3146w_prepare,
541	.get_modes	= ltk050h3146w_get_modes,
542};
543
544static 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
608static 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
622static 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
636static 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};
647MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
648
649static 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};
658module_mipi_dsi_driver(ltk050h3146w_driver);
659
660MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
661MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
662MODULE_LICENSE("GPL v2");
663