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, mode: 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(msecs: 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, mode: 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(msecs: 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, cmd: 0xff, data: 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, payload: &cmds[i],
446 size: 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, page: 3, cmds: page3_cmds,
466 ARRAY_SIZE(page3_cmds));
467 if (ret < 0)
468 return ret;
469
470 ret = ltk050h3146w_a2_write_page(ctx, page: 4, cmds: page4_cmds,
471 ARRAY_SIZE(page4_cmds));
472 if (ret < 0)
473 return ret;
474
475 ret = ltk050h3146w_a2_write_page(ctx, page: 1, cmds: page1_cmds,
476 ARRAY_SIZE(page1_cmds));
477 if (ret < 0)
478 return ret;
479
480 ret = ltk050h3146w_a2_select_page(ctx, page: 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, mode: 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(msecs: 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(regulator: ctx->iovcc);
541 regulator_disable(regulator: 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(regulator: 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(regulator: 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(desc: ctx->reset_gpio, value: 1);
570 usleep_range(min: 5000, max: 6000);
571 gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0);
572 msleep(msecs: 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(msecs: 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(msecs: 50);
596
597 ctx->prepared = true;
598
599 return 0;
600
601disable_iovcc:
602 regulator_disable(regulator: ctx->iovcc);
603disable_vci:
604 regulator_disable(regulator: 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(dev: connector->dev, mode: 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, size: 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, con_id: "reset", flags: GPIOD_OUT_LOW);
649 if (IS_ERR(ptr: ctx->reset_gpio))
650 return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->reset_gpio), fmt: "cannot get reset gpio\n");
651
652 ctx->vci = devm_regulator_get(dev, id: "vci");
653 if (IS_ERR(ptr: ctx->vci))
654 return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->vci), fmt: "Failed to request vci regulator\n");
655
656 ctx->iovcc = devm_regulator_get(dev, id: "iovcc");
657 if (IS_ERR(ptr: ctx->iovcc))
658 return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->iovcc),
659 fmt: "Failed to request iovcc regulator\n");
660
661 mipi_dsi_set_drvdata(dsi, data: ctx);
662
663 ctx->dev = dev;
664
665 dsi->lanes = 4;
666 dsi->format = MIPI_DSI_FMT_RGB888;
667 dsi->mode_flags = ctx->panel_desc->mode_flags;
668
669 drm_panel_init(panel: &ctx->panel, dev: &dsi->dev, funcs: &ltk050h3146w_funcs,
670 DRM_MODE_CONNECTOR_DSI);
671
672 ret = drm_panel_of_backlight(panel: &ctx->panel);
673 if (ret)
674 return ret;
675
676 drm_panel_add(panel: &ctx->panel);
677
678 ret = mipi_dsi_attach(dsi);
679 if (ret < 0) {
680 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
681 drm_panel_remove(panel: &ctx->panel);
682 return ret;
683 }
684
685 return 0;
686}
687
688static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
689{
690 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
691 int ret;
692
693 ret = drm_panel_unprepare(panel: &ctx->panel);
694 if (ret < 0)
695 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
696
697 ret = drm_panel_disable(panel: &ctx->panel);
698 if (ret < 0)
699 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
700}
701
702static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
703{
704 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
705 int ret;
706
707 ltk050h3146w_shutdown(dsi);
708
709 ret = mipi_dsi_detach(dsi);
710 if (ret < 0)
711 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
712
713 drm_panel_remove(panel: &ctx->panel);
714}
715
716static const struct of_device_id ltk050h3146w_of_match[] = {
717 {
718 .compatible = "leadtek,ltk050h3146w",
719 .data = &ltk050h3146w_data,
720 },
721 {
722 .compatible = "leadtek,ltk050h3146w-a2",
723 .data = &ltk050h3146w_a2_data,
724 },
725 {
726 .compatible = "leadtek,ltk050h3148w",
727 .data = &ltk050h3148w_data,
728 },
729 { /* sentinel */ }
730};
731MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
732
733static struct mipi_dsi_driver ltk050h3146w_driver = {
734 .driver = {
735 .name = "panel-leadtek-ltk050h3146w",
736 .of_match_table = ltk050h3146w_of_match,
737 },
738 .probe = ltk050h3146w_probe,
739 .remove = ltk050h3146w_remove,
740 .shutdown = ltk050h3146w_shutdown,
741};
742module_mipi_dsi_driver(ltk050h3146w_driver);
743
744MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
745MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
746MODULE_LICENSE("GPL v2");
747

source code of linux/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c