Loading...
Note: File does not exist in v3.1.
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");