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	void (*init)(struct mipi_dsi_multi_context *dsi_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};
 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 void ltk050h3148w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
247{
248	/*
249	 * Init sequence was supplied by the panel vendor without much
250	 * documentation.
251	 */
252	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0xff, 0x83, 0x94);
253	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
254				     0x71, 0x31, 0x55, 0x2f);
255	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
256	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x88);
257	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
258	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
259				     0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
260				     0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
261	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
262				     0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
263				     0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
264				     0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
265				     0x17, 0x11, 0x40);
266	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
267				     0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
268				     0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
269				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
270				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
271				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
272	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
273				     0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
274				     0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
275				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
276				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
277				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
278	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
279				     0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
280				     0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
281				     0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
282				     0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
283				     0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
284				     0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
285				     0x5d, 0x61, 0x65, 0x7f);
286	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcc, 0x0b);
287	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x1f, 0x31);
288	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb6, 0xc4, 0xc4);
289	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x01);
290	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x00);
291	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x00);
292	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0xef);
293	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x02);
294
295	mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
296	mipi_dsi_msleep(dsi_ctx, 60);
297}
298
299static const struct drm_display_mode ltk050h3148w_mode = {
300	.hdisplay	= 720,
301	.hsync_start	= 720 + 12,
302	.hsync_end	= 720 + 12 + 6,
303	.htotal		= 720 + 12 + 6 + 24,
304	.vdisplay	= 1280,
305	.vsync_start	= 1280 + 9,
306	.vsync_end	= 1280 + 9 + 2,
307	.vtotal		= 1280 + 9 + 2 + 16,
308	.clock		= 59756,
309	.width_mm	= 62,
310	.height_mm	= 110,
311};
312
313static const struct ltk050h3146w_desc ltk050h3148w_data = {
314	.mode = &ltk050h3148w_mode,
315	.init = ltk050h3148w_init_sequence,
316	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
317		      MIPI_DSI_MODE_VIDEO_BURST,
318};
319
320static void ltk050h3146w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
321{
322	/*
323	 * Init sequence was supplied by the panel vendor without much
324	 * documentation.
325	 */
326	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdf, 0x93, 0x65, 0xf8);
327	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
328				     0x01);
329	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0xb5);
330	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x00, 0xb5);
331	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
332
333	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x00, 0xc4, 0x23, 0x07);
334	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
335				     0x28, 0x04, 0xcc, 0xcc, 0xcc);
336	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f, 0x04);
337	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1e, 0xf2);
338	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x26, 0x03);
339	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x00, 0x12);
340	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
341				     0x80);
342	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
343				     0x16, 0x00, 0x00);
344	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
345				     0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
346				     0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
347				     0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
348				     0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
349	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
350				     0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
351				     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
352	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
353				     0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
354				     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
355	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
356				     0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
357				     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
358	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
359				     0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
360				     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
361	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
362				     0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
363				     0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
364	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
365				     0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
366				     0x21, 0x00, 0x60);
367	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x2c, 0xa3, 0x00);
368	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x02);
369	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x32, 0x1c);
370	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x3b, 0x70, 0x00, 0x04);
371	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x11);
372	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
373	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x20, 0x38, 0x1e, 0x84);
374	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x00);
375
376	mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
377	mipi_dsi_msleep(dsi_ctx, 60);
378}
379
380static const struct drm_display_mode ltk050h3146w_mode = {
381	.hdisplay	= 720,
382	.hsync_start	= 720 + 42,
383	.hsync_end	= 720 + 42 + 8,
384	.htotal		= 720 + 42 + 8 + 42,
385	.vdisplay	= 1280,
386	.vsync_start	= 1280 + 12,
387	.vsync_end	= 1280 + 12 + 4,
388	.vtotal		= 1280 + 12 + 4 + 18,
389	.clock		= 64018,
390	.width_mm	= 62,
391	.height_mm	= 110,
392};
393
394static const struct ltk050h3146w_desc ltk050h3146w_data = {
395	.mode = &ltk050h3146w_mode,
396	.init = ltk050h3146w_init_sequence,
397	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
398		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
399};
400
401static void ltk050h3146w_a2_select_page(struct mipi_dsi_multi_context *dsi_ctx, int page)
402{
403	u8 d[4] = { 0xff, 0x98, 0x81, page };
404
405	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, d, ARRAY_SIZE(d));
406}
407
408static void ltk050h3146w_a2_write_page(struct mipi_dsi_multi_context *dsi_ctx, int page,
409				      const struct ltk050h3146w_cmd *cmds,
410				      int num)
411{
412	ltk050h3146w_a2_select_page(dsi_ctx, page);
413
414	for (int i = 0; i < num; i++)
415		mipi_dsi_generic_write_multi(dsi_ctx, &cmds[i],
416					     sizeof(struct ltk050h3146w_cmd));
417}
418
419static void ltk050h3146w_a2_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
420{
421	/*
422	 * Init sequence was supplied by the panel vendor without much
423	 * documentation.
424	 */
425	ltk050h3146w_a2_write_page(dsi_ctx, 3, page3_cmds,
426					 ARRAY_SIZE(page3_cmds));
427	ltk050h3146w_a2_write_page(dsi_ctx, 4, page4_cmds,
428					 ARRAY_SIZE(page4_cmds));
429	ltk050h3146w_a2_write_page(dsi_ctx, 1, page1_cmds,
430					 ARRAY_SIZE(page1_cmds));
431	ltk050h3146w_a2_select_page(dsi_ctx, 0);
432
433	/* vendor code called this without param, where there should be one */
434	mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 0);
435
436	mipi_dsi_msleep(dsi_ctx, 60);
437}
438
439static const struct drm_display_mode ltk050h3146w_a2_mode = {
440	.hdisplay	= 720,
441	.hsync_start	= 720 + 42,
442	.hsync_end	= 720 + 42 + 10,
443	.htotal		= 720 + 42 + 10 + 60,
444	.vdisplay	= 1280,
445	.vsync_start	= 1280 + 18,
446	.vsync_end	= 1280 + 18 + 4,
447	.vtotal		= 1280 + 18 + 4 + 12,
448	.clock		= 65595,
449	.width_mm	= 62,
450	.height_mm	= 110,
451};
452
453static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
454	.mode = &ltk050h3146w_a2_mode,
455	.init = ltk050h3146w_a2_init_sequence,
456	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
457		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
458};
459
460static int ltk050h3146w_unprepare(struct drm_panel *panel)
461{
462	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
463	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
464	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
465
466	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
467	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
468	if (dsi_ctx.accum_err)
469		return dsi_ctx.accum_err;
470
471	regulator_disable(ctx->iovcc);
472	regulator_disable(ctx->vci);
473
474	return 0;
475}
476
477static int ltk050h3146w_prepare(struct drm_panel *panel)
478{
479	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
480	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
481	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
482
483	dev_dbg(ctx->dev, "Resetting the panel\n");
484	dsi_ctx.accum_err = regulator_enable(ctx->vci);
485	if (dsi_ctx.accum_err) {
486		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", dsi_ctx.accum_err);
487		return dsi_ctx.accum_err;
488	}
489	dsi_ctx.accum_err = regulator_enable(ctx->iovcc);
490	if (dsi_ctx.accum_err) {
491		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", dsi_ctx.accum_err);
492		goto disable_vci;
493	}
494
495	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
496	usleep_range(5000, 6000);
497	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
498	msleep(20);
499
500	ctx->panel_desc->init(&dsi_ctx);
501	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
502	/* T9: 120ms */
503	mipi_dsi_msleep(&dsi_ctx, 120);
504	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
505	mipi_dsi_msleep(&dsi_ctx, 50);
506
507	if (dsi_ctx.accum_err)
508		goto disable_iovcc;
509
510	return 0;
511
512disable_iovcc:
513	regulator_disable(ctx->iovcc);
514disable_vci:
515	regulator_disable(ctx->vci);
516	return dsi_ctx.accum_err;
517}
518
519static int ltk050h3146w_get_modes(struct drm_panel *panel,
520				  struct drm_connector *connector)
521{
522	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
523	struct drm_display_mode *mode;
524
525	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
526	if (!mode)
527		return -ENOMEM;
528
529	drm_mode_set_name(mode);
530
531	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
532	connector->display_info.width_mm = mode->width_mm;
533	connector->display_info.height_mm = mode->height_mm;
534	drm_mode_probed_add(connector, mode);
535
536	return 1;
537}
538
539static const struct drm_panel_funcs ltk050h3146w_funcs = {
540	.unprepare	= ltk050h3146w_unprepare,
541	.prepare	= ltk050h3146w_prepare,
542	.get_modes	= ltk050h3146w_get_modes,
543};
544
545static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
546{
547	struct device *dev = &dsi->dev;
548	struct ltk050h3146w *ctx;
549	int ret;
550
551	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
552	if (!ctx)
553		return -ENOMEM;
554
555	ctx->panel_desc = of_device_get_match_data(dev);
556	if (!ctx->panel_desc)
557		return -EINVAL;
558
559	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
560	if (IS_ERR(ctx->reset_gpio))
561		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");
562
563	ctx->vci = devm_regulator_get(dev, "vci");
564	if (IS_ERR(ctx->vci))
565		return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");
566
567	ctx->iovcc = devm_regulator_get(dev, "iovcc");
568	if (IS_ERR(ctx->iovcc))
569		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
570				     "Failed to request iovcc regulator\n");
571
572	mipi_dsi_set_drvdata(dsi, ctx);
573
574	ctx->dev = dev;
575
576	dsi->lanes = 4;
577	dsi->format = MIPI_DSI_FMT_RGB888;
578	dsi->mode_flags = ctx->panel_desc->mode_flags;
579
580	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
581		       DRM_MODE_CONNECTOR_DSI);
582
583	ret = drm_panel_of_backlight(&ctx->panel);
584	if (ret)
585		return ret;
586
587	drm_panel_add(&ctx->panel);
588
589	ret = mipi_dsi_attach(dsi);
590	if (ret < 0) {
591		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
592		drm_panel_remove(&ctx->panel);
593		return ret;
594	}
595
596	return 0;
597}
598
599static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
600{
601	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
602	int ret;
603
604	ret = mipi_dsi_detach(dsi);
605	if (ret < 0)
606		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
607
608	drm_panel_remove(&ctx->panel);
609}
610
611static const struct of_device_id ltk050h3146w_of_match[] = {
612	{
613		.compatible = "leadtek,ltk050h3146w",
614		.data = &ltk050h3146w_data,
615	},
616	{
617		.compatible = "leadtek,ltk050h3146w-a2",
618		.data = &ltk050h3146w_a2_data,
619	},
620	{
621		.compatible = "leadtek,ltk050h3148w",
622		.data = &ltk050h3148w_data,
623	},
624	{ /* sentinel */ }
625};
626MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
627
628static struct mipi_dsi_driver ltk050h3146w_driver = {
629	.driver = {
630		.name = "panel-leadtek-ltk050h3146w",
631		.of_match_table = ltk050h3146w_of_match,
632	},
633	.probe	= ltk050h3146w_probe,
634	.remove = ltk050h3146w_remove,
635};
636module_mipi_dsi_driver(ltk050h3146w_driver);
637
638MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
639MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
640MODULE_LICENSE("GPL v2");