Loading...
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 = <k050h3148w_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 = <k050h3146w_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 = <k050h3146w_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, <k050h3146w_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 = <k050h3146w_data,
615 },
616 {
617 .compatible = "leadtek,ltk050h3146w-a2",
618 .data = <k050h3146w_a2_data,
619 },
620 {
621 .compatible = "leadtek,ltk050h3148w",
622 .data = <k050h3148w_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");
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 = <k050h3148w_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 = <k050h3146w_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 = <k050h3146w_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, <k050h3146w_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 = <k050h3146w_data,
719 },
720 {
721 .compatible = "leadtek,ltk050h3146w-a2",
722 .data = <k050h3146w_a2_data,
723 },
724 {
725 .compatible = "leadtek,ltk050h3148w",
726 .data = <k050h3148w_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");