Linux Audio

Check our new training course

Loading...
v6.13.7
  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");
v6.9.4
  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");