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