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 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 = <k050h3148w_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 = <k050h3146w_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 = <k050h3146w_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, <k050h3146w_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 = <k050h3146w_data,
729 },
730 {
731 .compatible = "leadtek,ltk050h3146w-a2",
732 .data = <k050h3146w_a2_data,
733 },
734 {
735 .compatible = "leadtek,ltk050h3148w",
736 .data = <k050h3148w_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");