Linux Audio

Check our new training course

Embedded Linux training

Mar 31-Apr 8, 2025
Register
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.8
  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	mipi_dsi_dcs_write_seq(dsi, 0x11);
299	mipi_dsi_dcs_write_seq(dsi, 0x29);
300
301	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
302	if (ret < 0) {
303		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
304		return ret;
305	}
306
307	msleep(60);
308
309	return 0;
 
310}
311
312static const struct drm_display_mode ltk050h3148w_mode = {
313	.hdisplay	= 720,
314	.hsync_start	= 720 + 12,
315	.hsync_end	= 720 + 12 + 6,
316	.htotal		= 720 + 12 + 6 + 24,
317	.vdisplay	= 1280,
318	.vsync_start	= 1280 + 9,
319	.vsync_end	= 1280 + 9 + 2,
320	.vtotal		= 1280 + 9 + 2 + 16,
321	.clock		= 59756,
322	.width_mm	= 62,
323	.height_mm	= 110,
324};
325
326static const struct ltk050h3146w_desc ltk050h3148w_data = {
327	.mode = &ltk050h3148w_mode,
328	.init = ltk050h3148w_init_sequence,
329	.mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO_BURST,
 
330};
331
332static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
333{
334	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
335	int ret;
336
337	/*
338	 * Init sequence was supplied by the panel vendor without much
339	 * documentation.
340	 */
341	mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
342	mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
343			       0x01);
344	mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
345	mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
346	mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
347
348	mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
349	mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
350			       0x28, 0x04, 0xcc, 0xcc, 0xcc);
351	mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
352	mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
353	mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
354	mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
355	mipi_dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
356			       0x80);
357	mipi_dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
358			       0x16, 0x00, 0x00);
359	mipi_dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
360			       0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
361			       0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
362			       0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
363			       0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
364	mipi_dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
365			       0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
366			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
367	mipi_dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
368			       0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
369			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
370	mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
371			       0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
372			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
373	mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
374			       0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
375			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
376	mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
377			       0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
378			       0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
379	mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
380			       0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
381			       0x21, 0x00, 0x60);
382	mipi_dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
383	mipi_dsi_dcs_write_seq(dsi, 0xde, 0x02);
384	mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
385	mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
386	mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x11);
387	mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
388	mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
389	mipi_dsi_dcs_write_seq(dsi, 0xde, 0x00);
390
391	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
392	if (ret < 0) {
393		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
394		return ret;
395	}
396
397	msleep(60);
398
399	return 0;
400}
401
402static const struct drm_display_mode ltk050h3146w_mode = {
403	.hdisplay	= 720,
404	.hsync_start	= 720 + 42,
405	.hsync_end	= 720 + 42 + 8,
406	.htotal		= 720 + 42 + 8 + 42,
407	.vdisplay	= 1280,
408	.vsync_start	= 1280 + 12,
409	.vsync_end	= 1280 + 12 + 4,
410	.vtotal		= 1280 + 12 + 4 + 18,
411	.clock		= 64018,
412	.width_mm	= 62,
413	.height_mm	= 110,
414};
415
416static const struct ltk050h3146w_desc ltk050h3146w_data = {
417	.mode = &ltk050h3146w_mode,
418	.init = ltk050h3146w_init_sequence,
419	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
420		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
421};
422
423static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
424{
425	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
426	u8 d[3] = { 0x98, 0x81, page };
427
428	return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
429}
430
431static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
432				      const struct ltk050h3146w_cmd *cmds,
433				      int num)
434{
435	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
436	int i, ret;
437
438	ret = ltk050h3146w_a2_select_page(ctx, page);
439	if (ret < 0) {
440		dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
441		return ret;
442	}
443
444	for (i = 0; i < num; i++) {
445		ret = mipi_dsi_generic_write(dsi, &cmds[i],
446					     sizeof(struct ltk050h3146w_cmd));
447		if (ret < 0) {
448			dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
449			return ret;
450		}
451	}
452
453	return 0;
454}
455
456static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
457{
458	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
459	int ret;
460
461	/*
462	 * Init sequence was supplied by the panel vendor without much
463	 * documentation.
464	 */
465	ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
466					 ARRAY_SIZE(page3_cmds));
467	if (ret < 0)
468		return ret;
469
470	ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
471					 ARRAY_SIZE(page4_cmds));
472	if (ret < 0)
473		return ret;
474
475	ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
476					 ARRAY_SIZE(page1_cmds));
477	if (ret < 0)
478		return ret;
479
480	ret = ltk050h3146w_a2_select_page(ctx, 0);
481	if (ret < 0) {
482		dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
483		return ret;
484	}
485
486	/* vendor code called this without param, where there should be one */
487	ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
488	if (ret < 0) {
489		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
490		return ret;
491	}
492
493	msleep(60);
494
495	return 0;
496}
497
498static const struct drm_display_mode ltk050h3146w_a2_mode = {
499	.hdisplay	= 720,
500	.hsync_start	= 720 + 42,
501	.hsync_end	= 720 + 42 + 10,
502	.htotal		= 720 + 42 + 10 + 60,
503	.vdisplay	= 1280,
504	.vsync_start	= 1280 + 18,
505	.vsync_end	= 1280 + 18 + 4,
506	.vtotal		= 1280 + 18 + 4 + 12,
507	.clock		= 65595,
508	.width_mm	= 62,
509	.height_mm	= 110,
510};
511
512static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
513	.mode = &ltk050h3146w_a2_mode,
514	.init = ltk050h3146w_a2_init_sequence,
515	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
516		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
517};
518
519static int ltk050h3146w_unprepare(struct drm_panel *panel)
520{
521	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
522	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
523	int ret;
524
525	if (!ctx->prepared)
526		return 0;
527
528	ret = mipi_dsi_dcs_set_display_off(dsi);
529	if (ret < 0) {
530		dev_err(ctx->dev, "failed to set display off: %d\n", ret);
531		return ret;
532	}
533
534	mipi_dsi_dcs_enter_sleep_mode(dsi);
535	if (ret < 0) {
536		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
537		return ret;
538	}
539
540	regulator_disable(ctx->iovcc);
541	regulator_disable(ctx->vci);
542
543	ctx->prepared = false;
544
545	return 0;
546}
547
548static int ltk050h3146w_prepare(struct drm_panel *panel)
549{
550	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
551	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
552	int ret;
553
554	if (ctx->prepared)
555		return 0;
556
557	dev_dbg(ctx->dev, "Resetting the panel\n");
558	ret = regulator_enable(ctx->vci);
559	if (ret < 0) {
560		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
561		return ret;
562	}
563	ret = regulator_enable(ctx->iovcc);
564	if (ret < 0) {
565		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
566		goto disable_vci;
567	}
568
569	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
570	usleep_range(5000, 6000);
571	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
572	msleep(20);
573
574	ret = ctx->panel_desc->init(ctx);
575	if (ret < 0) {
576		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
577		goto disable_iovcc;
578	}
579
580	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
581	if (ret < 0) {
582		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
583		goto disable_iovcc;
584	}
585
586	/* T9: 120ms */
587	msleep(120);
 
 
588
589	ret = mipi_dsi_dcs_set_display_on(dsi);
590	if (ret < 0) {
591		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
592		goto disable_iovcc;
593	}
594
595	msleep(50);
596
597	ctx->prepared = true;
598
599	return 0;
600
601disable_iovcc:
602	regulator_disable(ctx->iovcc);
603disable_vci:
604	regulator_disable(ctx->vci);
605	return ret;
606}
607
608static int ltk050h3146w_get_modes(struct drm_panel *panel,
609				  struct drm_connector *connector)
610{
611	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
612	struct drm_display_mode *mode;
613
614	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
615	if (!mode)
616		return -ENOMEM;
617
618	drm_mode_set_name(mode);
619
620	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
621	connector->display_info.width_mm = mode->width_mm;
622	connector->display_info.height_mm = mode->height_mm;
623	drm_mode_probed_add(connector, mode);
624
625	return 1;
626}
627
628static const struct drm_panel_funcs ltk050h3146w_funcs = {
629	.unprepare	= ltk050h3146w_unprepare,
630	.prepare	= ltk050h3146w_prepare,
631	.get_modes	= ltk050h3146w_get_modes,
632};
633
634static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
635{
636	struct device *dev = &dsi->dev;
637	struct ltk050h3146w *ctx;
638	int ret;
639
640	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
641	if (!ctx)
642		return -ENOMEM;
643
644	ctx->panel_desc = of_device_get_match_data(dev);
645	if (!ctx->panel_desc)
646		return -EINVAL;
647
648	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
649	if (IS_ERR(ctx->reset_gpio)) {
650		dev_err(dev, "cannot get reset gpio\n");
651		return PTR_ERR(ctx->reset_gpio);
652	}
653
654	ctx->vci = devm_regulator_get(dev, "vci");
655	if (IS_ERR(ctx->vci)) {
656		ret = PTR_ERR(ctx->vci);
657		if (ret != -EPROBE_DEFER)
658			dev_err(dev, "Failed to request vci regulator: %d\n", ret);
659		return ret;
660	}
661
662	ctx->iovcc = devm_regulator_get(dev, "iovcc");
663	if (IS_ERR(ctx->iovcc)) {
664		ret = PTR_ERR(ctx->iovcc);
665		if (ret != -EPROBE_DEFER)
666			dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
667		return ret;
668	}
669
670	mipi_dsi_set_drvdata(dsi, ctx);
671
672	ctx->dev = dev;
673
674	dsi->lanes = 4;
675	dsi->format = MIPI_DSI_FMT_RGB888;
676	dsi->mode_flags = ctx->panel_desc->mode_flags;
677
678	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,
679		       DRM_MODE_CONNECTOR_DSI);
680
681	ret = drm_panel_of_backlight(&ctx->panel);
682	if (ret)
683		return ret;
684
685	drm_panel_add(&ctx->panel);
686
687	ret = mipi_dsi_attach(dsi);
688	if (ret < 0) {
689		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
690		drm_panel_remove(&ctx->panel);
691		return ret;
692	}
693
694	return 0;
695}
696
697static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
698{
699	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
700	int ret;
701
702	ret = drm_panel_unprepare(&ctx->panel);
703	if (ret < 0)
704		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
705
706	ret = drm_panel_disable(&ctx->panel);
707	if (ret < 0)
708		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
709}
710
711static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
712{
713	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
714	int ret;
715
716	ltk050h3146w_shutdown(dsi);
717
718	ret = mipi_dsi_detach(dsi);
719	if (ret < 0)
720		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
721
722	drm_panel_remove(&ctx->panel);
723}
724
725static const struct of_device_id ltk050h3146w_of_match[] = {
726	{
727		.compatible = "leadtek,ltk050h3146w",
728		.data = &ltk050h3146w_data,
729	},
730	{
731		.compatible = "leadtek,ltk050h3146w-a2",
732		.data = &ltk050h3146w_a2_data,
733	},
734	{
735		.compatible = "leadtek,ltk050h3148w",
736		.data = &ltk050h3148w_data,
737	},
738	{ /* sentinel */ }
739};
740MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
741
742static struct mipi_dsi_driver ltk050h3146w_driver = {
743	.driver = {
744		.name = "panel-leadtek-ltk050h3146w",
745		.of_match_table = ltk050h3146w_of_match,
746	},
747	.probe	= ltk050h3146w_probe,
748	.remove = ltk050h3146w_remove,
749	.shutdown = ltk050h3146w_shutdown,
750};
751module_mipi_dsi_driver(ltk050h3146w_driver);
752
753MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
754MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
755MODULE_LICENSE("GPL v2");