Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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/of_device.h>
 12#include <linux/regulator/consumer.h>
 13
 14#include <video/display_timing.h>
 15#include <video/mipi_display.h>
 16
 17#include <drm/drm_mipi_dsi.h>
 18#include <drm/drm_modes.h>
 19#include <drm/drm_panel.h>
 20#include <drm/drm_print.h>
 21
 22struct ltk050h3146w_cmd {
 23	char cmd;
 24	char data;
 25};
 26
 27struct ltk050h3146w;
 28struct ltk050h3146w_desc {
 29	const struct drm_display_mode *mode;
 30	int (*init)(struct ltk050h3146w *ctx);
 31};
 32
 33struct ltk050h3146w {
 34	struct device *dev;
 35	struct drm_panel panel;
 36	struct gpio_desc *reset_gpio;
 37	struct regulator *vci;
 38	struct regulator *iovcc;
 39	const struct ltk050h3146w_desc *panel_desc;
 40	bool prepared;
 41};
 42
 43static const struct ltk050h3146w_cmd page1_cmds[] = {
 44	{ 0x22, 0x0A }, /* BGR SS GS */
 45	{ 0x31, 0x00 }, /* column inversion */
 46	{ 0x53, 0xA2 }, /* VCOM1 */
 47	{ 0x55, 0xA2 }, /* VCOM2 */
 48	{ 0x50, 0x81 }, /* VREG1OUT=5V */
 49	{ 0x51, 0x85 }, /* VREG2OUT=-5V */
 50	{ 0x62, 0x0D }, /* EQT Time setting */
 51/*
 52 * The vendor init selected page 1 here _again_
 53 * Is this supposed to be page 2?
 54 */
 55	{ 0xA0, 0x00 },
 56	{ 0xA1, 0x1A },
 57	{ 0xA2, 0x28 },
 58	{ 0xA3, 0x13 },
 59	{ 0xA4, 0x16 },
 60	{ 0xA5, 0x29 },
 61	{ 0xA6, 0x1D },
 62	{ 0xA7, 0x1E },
 63	{ 0xA8, 0x84 },
 64	{ 0xA9, 0x1C },
 65	{ 0xAA, 0x28 },
 66	{ 0xAB, 0x75 },
 67	{ 0xAC, 0x1A },
 68	{ 0xAD, 0x19 },
 69	{ 0xAE, 0x4D },
 70	{ 0xAF, 0x22 },
 71	{ 0xB0, 0x28 },
 72	{ 0xB1, 0x54 },
 73	{ 0xB2, 0x66 },
 74	{ 0xB3, 0x39 },
 75	{ 0xC0, 0x00 },
 76	{ 0xC1, 0x1A },
 77	{ 0xC2, 0x28 },
 78	{ 0xC3, 0x13 },
 79	{ 0xC4, 0x16 },
 80	{ 0xC5, 0x29 },
 81	{ 0xC6, 0x1D },
 82	{ 0xC7, 0x1E },
 83	{ 0xC8, 0x84 },
 84	{ 0xC9, 0x1C },
 85	{ 0xCA, 0x28 },
 86	{ 0xCB, 0x75 },
 87	{ 0xCC, 0x1A },
 88	{ 0xCD, 0x19 },
 89	{ 0xCE, 0x4D },
 90	{ 0xCF, 0x22 },
 91	{ 0xD0, 0x28 },
 92	{ 0xD1, 0x54 },
 93	{ 0xD2, 0x66 },
 94	{ 0xD3, 0x39 },
 95};
 96
 97static const struct ltk050h3146w_cmd page3_cmds[] = {
 98	{ 0x01, 0x00 },
 99	{ 0x02, 0x00 },
100	{ 0x03, 0x73 },
101	{ 0x04, 0x00 },
102	{ 0x05, 0x00 },
103	{ 0x06, 0x0a },
104	{ 0x07, 0x00 },
105	{ 0x08, 0x00 },
106	{ 0x09, 0x01 },
107	{ 0x0a, 0x00 },
108	{ 0x0b, 0x00 },
109	{ 0x0c, 0x01 },
110	{ 0x0d, 0x00 },
111	{ 0x0e, 0x00 },
112	{ 0x0f, 0x1d },
113	{ 0x10, 0x1d },
114	{ 0x11, 0x00 },
115	{ 0x12, 0x00 },
116	{ 0x13, 0x00 },
117	{ 0x14, 0x00 },
118	{ 0x15, 0x00 },
119	{ 0x16, 0x00 },
120	{ 0x17, 0x00 },
121	{ 0x18, 0x00 },
122	{ 0x19, 0x00 },
123	{ 0x1a, 0x00 },
124	{ 0x1b, 0x00 },
125	{ 0x1c, 0x00 },
126	{ 0x1d, 0x00 },
127	{ 0x1e, 0x40 },
128	{ 0x1f, 0x80 },
129	{ 0x20, 0x06 },
130	{ 0x21, 0x02 },
131	{ 0x22, 0x00 },
132	{ 0x23, 0x00 },
133	{ 0x24, 0x00 },
134	{ 0x25, 0x00 },
135	{ 0x26, 0x00 },
136	{ 0x27, 0x00 },
137	{ 0x28, 0x33 },
138	{ 0x29, 0x03 },
139	{ 0x2a, 0x00 },
140	{ 0x2b, 0x00 },
141	{ 0x2c, 0x00 },
142	{ 0x2d, 0x00 },
143	{ 0x2e, 0x00 },
144	{ 0x2f, 0x00 },
145	{ 0x30, 0x00 },
146	{ 0x31, 0x00 },
147	{ 0x32, 0x00 },
148	{ 0x33, 0x00 },
149	{ 0x34, 0x04 },
150	{ 0x35, 0x00 },
151	{ 0x36, 0x00 },
152	{ 0x37, 0x00 },
153	{ 0x38, 0x3C },
154	{ 0x39, 0x35 },
155	{ 0x3A, 0x01 },
156	{ 0x3B, 0x40 },
157	{ 0x3C, 0x00 },
158	{ 0x3D, 0x01 },
159	{ 0x3E, 0x00 },
160	{ 0x3F, 0x00 },
161	{ 0x40, 0x00 },
162	{ 0x41, 0x88 },
163	{ 0x42, 0x00 },
164	{ 0x43, 0x00 },
165	{ 0x44, 0x1F },
166	{ 0x50, 0x01 },
167	{ 0x51, 0x23 },
168	{ 0x52, 0x45 },
169	{ 0x53, 0x67 },
170	{ 0x54, 0x89 },
171	{ 0x55, 0xab },
172	{ 0x56, 0x01 },
173	{ 0x57, 0x23 },
174	{ 0x58, 0x45 },
175	{ 0x59, 0x67 },
176	{ 0x5a, 0x89 },
177	{ 0x5b, 0xab },
178	{ 0x5c, 0xcd },
179	{ 0x5d, 0xef },
180	{ 0x5e, 0x11 },
181	{ 0x5f, 0x01 },
182	{ 0x60, 0x00 },
183	{ 0x61, 0x15 },
184	{ 0x62, 0x14 },
185	{ 0x63, 0x0E },
186	{ 0x64, 0x0F },
187	{ 0x65, 0x0C },
188	{ 0x66, 0x0D },
189	{ 0x67, 0x06 },
190	{ 0x68, 0x02 },
191	{ 0x69, 0x07 },
192	{ 0x6a, 0x02 },
193	{ 0x6b, 0x02 },
194	{ 0x6c, 0x02 },
195	{ 0x6d, 0x02 },
196	{ 0x6e, 0x02 },
197	{ 0x6f, 0x02 },
198	{ 0x70, 0x02 },
199	{ 0x71, 0x02 },
200	{ 0x72, 0x02 },
201	{ 0x73, 0x02 },
202	{ 0x74, 0x02 },
203	{ 0x75, 0x01 },
204	{ 0x76, 0x00 },
205	{ 0x77, 0x14 },
206	{ 0x78, 0x15 },
207	{ 0x79, 0x0E },
208	{ 0x7a, 0x0F },
209	{ 0x7b, 0x0C },
210	{ 0x7c, 0x0D },
211	{ 0x7d, 0x06 },
212	{ 0x7e, 0x02 },
213	{ 0x7f, 0x07 },
214	{ 0x80, 0x02 },
215	{ 0x81, 0x02 },
216	{ 0x82, 0x02 },
217	{ 0x83, 0x02 },
218	{ 0x84, 0x02 },
219	{ 0x85, 0x02 },
220	{ 0x86, 0x02 },
221	{ 0x87, 0x02 },
222	{ 0x88, 0x02 },
223	{ 0x89, 0x02 },
224	{ 0x8A, 0x02 },
225};
226
227static const struct ltk050h3146w_cmd page4_cmds[] = {
228	{ 0x70, 0x00 },
229	{ 0x71, 0x00 },
230	{ 0x82, 0x0F }, /* VGH_MOD clamp level=15v */
231	{ 0x84, 0x0F }, /* VGH clamp level 15V */
232	{ 0x85, 0x0D }, /* VGL clamp level (-10V) */
233	{ 0x32, 0xAC },
234	{ 0x8C, 0x80 },
235	{ 0x3C, 0xF5 },
236	{ 0xB5, 0x07 }, /* GAMMA OP */
237	{ 0x31, 0x45 }, /* SOURCE OP */
238	{ 0x3A, 0x24 }, /* PS_EN OFF */
239	{ 0x88, 0x33 }, /* LVD */
240};
241
242static inline
243struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
244{
245	return container_of(panel, struct ltk050h3146w, panel);
246}
247
248#define dsi_dcs_write_seq(dsi, cmd, seq...) do {			\
249		static const u8 b[] = { cmd, seq };			\
250		int ret;						\
251		ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b));	\
252		if (ret < 0)						\
253			return ret;					\
254	} while (0)
255
256static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
257{
258	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
259	int ret;
260
261	/*
262	 * Init sequence was supplied by the panel vendor without much
263	 * documentation.
264	 */
265	dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
266	dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
267			  0x01);
268	dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
269	dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
270	dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
271
272	dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
273	dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
274			  0x28, 0x04, 0xcc, 0xcc, 0xcc);
275	dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
276	dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
277	dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
278	dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
279	dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
280			  0x80);
281	dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
282			  0x16, 0x00, 0x00);
283	dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
284			  0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
285			  0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
286			  0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
287			  0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
288	dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
289			  0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
290			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
291	dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
292			  0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
293			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
294	dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
295			  0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
296			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
297	dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
298			  0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
299			  0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
300	dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
301			  0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
302			  0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
303	dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
304			  0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
305			  0x21, 0x00, 0x60);
306	dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
307	dsi_dcs_write_seq(dsi, 0xde, 0x02);
308	dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
309	dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
310	dsi_dcs_write_seq(dsi, 0xc1, 0x11);
311	dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
312	dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
313	dsi_dcs_write_seq(dsi, 0xde, 0x00);
314
315	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
316	if (ret < 0) {
317		DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
318			      ret);
319		return ret;
320	}
321
322	msleep(60);
323
324	return 0;
325}
326
327static const struct drm_display_mode ltk050h3146w_mode = {
328	.hdisplay	= 720,
329	.hsync_start	= 720 + 42,
330	.hsync_end	= 720 + 42 + 8,
331	.htotal		= 720 + 42 + 8 + 42,
332	.vdisplay	= 1280,
333	.vsync_start	= 1280 + 12,
334	.vsync_end	= 1280 + 12 + 4,
335	.vtotal		= 1280 + 12 + 4 + 18,
336	.clock		= 64018,
337	.width_mm	= 62,
338	.height_mm	= 110,
339};
340
341static const struct ltk050h3146w_desc ltk050h3146w_data = {
342	.mode = &ltk050h3146w_mode,
343	.init = ltk050h3146w_init_sequence,
344};
345
346static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
347{
348	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
349	u8 d[3] = { 0x98, 0x81, page };
350
351	return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
352}
353
354static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
355				      const struct ltk050h3146w_cmd *cmds,
356				      int num)
357{
358	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
359	int i, ret;
360
361	ret = ltk050h3146w_a2_select_page(ctx, page);
362	if (ret < 0) {
363		DRM_DEV_ERROR(ctx->dev, "failed to select page %d: %d\n",
364			      page, ret);
365		return ret;
366	}
367
368	for (i = 0; i < num; i++) {
369		ret = mipi_dsi_generic_write(dsi, &cmds[i],
370					     sizeof(struct ltk050h3146w_cmd));
371		if (ret < 0) {
372			DRM_DEV_ERROR(ctx->dev,
373				      "failed to write page %d init cmds: %d\n",
374				       page, ret);
375			return ret;
376		}
377	}
378
379	return 0;
380}
381
382static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
383{
384	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
385	int ret;
386
387	/*
388	 * Init sequence was supplied by the panel vendor without much
389	 * documentation.
390	 */
391	ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
392					 ARRAY_SIZE(page3_cmds));
393	if (ret < 0)
394		return ret;
395
396	ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
397					 ARRAY_SIZE(page4_cmds));
398	if (ret < 0)
399		return ret;
400
401	ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
402					 ARRAY_SIZE(page1_cmds));
403	if (ret < 0)
404		return ret;
405
406	ret = ltk050h3146w_a2_select_page(ctx, 0);
407	if (ret < 0) {
408		DRM_DEV_ERROR(ctx->dev, "failed to select page 0: %d\n", ret);
409		return ret;
410	}
411
412	/* vendor code called this without param, where there should be one */
413	ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
414	if (ret < 0) {
415		DRM_DEV_ERROR(ctx->dev, "failed to set tear on: %d\n",
416			      ret);
417		return ret;
418	}
419
420	msleep(60);
421
422	return 0;
423}
424
425static const struct drm_display_mode ltk050h3146w_a2_mode = {
426	.hdisplay	= 720,
427	.hsync_start	= 720 + 42,
428	.hsync_end	= 720 + 42 + 10,
429	.htotal		= 720 + 42 + 10 + 60,
430	.vdisplay	= 1280,
431	.vsync_start	= 1280 + 18,
432	.vsync_end	= 1280 + 18 + 4,
433	.vtotal		= 1280 + 18 + 4 + 12,
434	.clock		= 65595,
435	.width_mm	= 62,
436	.height_mm	= 110,
437};
438
439static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
440	.mode = &ltk050h3146w_a2_mode,
441	.init = ltk050h3146w_a2_init_sequence,
442};
443
444static int ltk050h3146w_unprepare(struct drm_panel *panel)
445{
446	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
447	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
448	int ret;
449
450	if (!ctx->prepared)
451		return 0;
452
453	ret = mipi_dsi_dcs_set_display_off(dsi);
454	if (ret < 0) {
455		DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n",
456			      ret);
457		return ret;
458	}
459
460	mipi_dsi_dcs_enter_sleep_mode(dsi);
461	if (ret < 0) {
462		DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n",
463			      ret);
464		return ret;
465	}
466
467	regulator_disable(ctx->iovcc);
468	regulator_disable(ctx->vci);
469
470	ctx->prepared = false;
471
472	return 0;
473}
474
475static int ltk050h3146w_prepare(struct drm_panel *panel)
476{
477	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
478	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
479	int ret;
480
481	if (ctx->prepared)
482		return 0;
483
484	DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
485	ret = regulator_enable(ctx->vci);
486	if (ret < 0) {
487		DRM_DEV_ERROR(ctx->dev,
488			      "Failed to enable vci supply: %d\n", ret);
489		return ret;
490	}
491	ret = regulator_enable(ctx->iovcc);
492	if (ret < 0) {
493		DRM_DEV_ERROR(ctx->dev,
494			      "Failed to enable iovcc supply: %d\n", ret);
495		goto disable_vci;
496	}
497
498	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
499	usleep_range(5000, 6000);
500	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
501	msleep(20);
502
503	ret = ctx->panel_desc->init(ctx);
504	if (ret < 0) {
505		DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
506			      ret);
507		goto disable_iovcc;
508	}
509
510	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
511	if (ret < 0) {
512		DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
513		goto disable_iovcc;
514	}
515
516	/* T9: 120ms */
517	msleep(120);
518
519	ret = mipi_dsi_dcs_set_display_on(dsi);
520	if (ret < 0) {
521		DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret);
522		goto disable_iovcc;
523	}
524
525	msleep(50);
526
527	ctx->prepared = true;
528
529	return 0;
530
531disable_iovcc:
532	regulator_disable(ctx->iovcc);
533disable_vci:
534	regulator_disable(ctx->vci);
535	return ret;
536}
537
538static int ltk050h3146w_get_modes(struct drm_panel *panel,
539				  struct drm_connector *connector)
540{
541	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
542	struct drm_display_mode *mode;
543
544	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
545	if (!mode)
546		return -ENOMEM;
547
548	drm_mode_set_name(mode);
549
550	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
551	connector->display_info.width_mm = mode->width_mm;
552	connector->display_info.height_mm = mode->height_mm;
553	drm_mode_probed_add(connector, mode);
554
555	return 1;
556}
557
558static const struct drm_panel_funcs ltk050h3146w_funcs = {
559	.unprepare	= ltk050h3146w_unprepare,
560	.prepare	= ltk050h3146w_prepare,
561	.get_modes	= ltk050h3146w_get_modes,
562};
563
564static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
565{
566	struct device *dev = &dsi->dev;
567	struct ltk050h3146w *ctx;
568	int ret;
569
570	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
571	if (!ctx)
572		return -ENOMEM;
573
574	ctx->panel_desc = of_device_get_match_data(dev);
575	if (!ctx->panel_desc)
576		return -EINVAL;
577
578	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
579	if (IS_ERR(ctx->reset_gpio)) {
580		DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
581		return PTR_ERR(ctx->reset_gpio);
582	}
583
584	ctx->vci = devm_regulator_get(dev, "vci");
585	if (IS_ERR(ctx->vci)) {
586		ret = PTR_ERR(ctx->vci);
587		if (ret != -EPROBE_DEFER)
588			DRM_DEV_ERROR(dev,
589				      "Failed to request vci regulator: %d\n",
590				      ret);
591		return ret;
592	}
593
594	ctx->iovcc = devm_regulator_get(dev, "iovcc");
595	if (IS_ERR(ctx->iovcc)) {
596		ret = PTR_ERR(ctx->iovcc);
597		if (ret != -EPROBE_DEFER)
598			DRM_DEV_ERROR(dev,
599				      "Failed to request iovcc regulator: %d\n",
600				      ret);
601		return ret;
602	}
603
604	mipi_dsi_set_drvdata(dsi, ctx);
605
606	ctx->dev = dev;
607
608	dsi->lanes = 4;
609	dsi->format = MIPI_DSI_FMT_RGB888;
610	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
611			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
612
613	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
614		       DRM_MODE_CONNECTOR_DSI);
615
616	ret = drm_panel_of_backlight(&ctx->panel);
617	if (ret)
618		return ret;
619
620	drm_panel_add(&ctx->panel);
621
622	ret = mipi_dsi_attach(dsi);
623	if (ret < 0) {
624		DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret);
625		drm_panel_remove(&ctx->panel);
626		return ret;
627	}
628
629	return 0;
630}
631
632static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
633{
634	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
635	int ret;
636
637	ret = drm_panel_unprepare(&ctx->panel);
638	if (ret < 0)
639		DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
640			      ret);
641
642	ret = drm_panel_disable(&ctx->panel);
643	if (ret < 0)
644		DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
645			      ret);
646}
647
648static int ltk050h3146w_remove(struct mipi_dsi_device *dsi)
649{
650	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
651	int ret;
652
653	ltk050h3146w_shutdown(dsi);
654
655	ret = mipi_dsi_detach(dsi);
656	if (ret < 0)
657		DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
658			      ret);
659
660	drm_panel_remove(&ctx->panel);
661
662	return 0;
663}
664
665static const struct of_device_id ltk050h3146w_of_match[] = {
666	{
667		.compatible = "leadtek,ltk050h3146w",
668		.data = &ltk050h3146w_data,
669	},
670	{
671		.compatible = "leadtek,ltk050h3146w-a2",
672		.data = &ltk050h3146w_a2_data,
673	},
674	{ /* sentinel */ }
675};
676MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
677
678static struct mipi_dsi_driver ltk050h3146w_driver = {
679	.driver = {
680		.name = "panel-leadtek-ltk050h3146w",
681		.of_match_table = ltk050h3146w_of_match,
682	},
683	.probe	= ltk050h3146w_probe,
684	.remove = ltk050h3146w_remove,
685	.shutdown = ltk050h3146w_shutdown,
686};
687module_mipi_dsi_driver(ltk050h3146w_driver);
688
689MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
690MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
691MODULE_LICENSE("GPL v2");