Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
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
310static 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
324static 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
331static 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
401static 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
415static 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
422static 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
430static 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
455static 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
497static 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
511static 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
518static 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
547static 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
600disable_iovcc:
601	regulator_disable(ctx->iovcc);
602disable_vci:
603	regulator_disable(ctx->vci);
604	return ret;
605}
606
607static 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
627static const struct drm_panel_funcs ltk050h3146w_funcs = {
628	.unprepare	= ltk050h3146w_unprepare,
629	.prepare	= ltk050h3146w_prepare,
630	.get_modes	= ltk050h3146w_get_modes,
631};
632
633static 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
687static 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
701static 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
715static 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};
730MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
731
732static 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};
741module_mipi_dsi_driver(ltk050h3146w_driver);
742
743MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
744MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
745MODULE_LICENSE("GPL v2");