1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * NewVision NV3052C IPS LCD panel driver |
4 | * |
5 | * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> |
6 | * Copyright (C) 2022, Christophe Branchereau <cbranchereau@gmail.com> |
7 | */ |
8 | |
9 | #include <linux/delay.h> |
10 | #include <linux/device.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/media-bus-format.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regulator/consumer.h> |
17 | #include <linux/spi/spi.h> |
18 | #include <video/mipi_display.h> |
19 | #include <drm/drm_mipi_dbi.h> |
20 | #include <drm/drm_modes.h> |
21 | #include <drm/drm_panel.h> |
22 | |
23 | struct nv3052c_reg { |
24 | u8 cmd; |
25 | u8 val; |
26 | }; |
27 | |
28 | struct nv3052c_panel_info { |
29 | const struct drm_display_mode *display_modes; |
30 | unsigned int num_modes; |
31 | u16 width_mm, height_mm; |
32 | u32 bus_format, bus_flags; |
33 | const struct nv3052c_reg *panel_regs; |
34 | unsigned int panel_regs_len; |
35 | }; |
36 | |
37 | struct nv3052c { |
38 | struct device *dev; |
39 | struct drm_panel panel; |
40 | struct mipi_dbi dbi; |
41 | const struct nv3052c_panel_info *panel_info; |
42 | struct regulator *supply; |
43 | struct gpio_desc *reset_gpio; |
44 | }; |
45 | |
46 | static const struct nv3052c_reg ltk035c5444t_panel_regs[] = { |
47 | // EXTC Command set enable, select page 1 |
48 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, |
49 | // Mostly unknown registers |
50 | { 0xe3, 0x00 }, |
51 | { 0x40, 0x00 }, |
52 | { 0x03, 0x40 }, |
53 | { 0x04, 0x00 }, |
54 | { 0x05, 0x03 }, |
55 | { 0x08, 0x00 }, |
56 | { 0x09, 0x07 }, |
57 | { 0x0a, 0x01 }, |
58 | { 0x0b, 0x32 }, |
59 | { 0x0c, 0x32 }, |
60 | { 0x0d, 0x0b }, |
61 | { 0x0e, 0x00 }, |
62 | { 0x23, 0xa0 }, |
63 | { 0x24, 0x0c }, |
64 | { 0x25, 0x06 }, |
65 | { 0x26, 0x14 }, |
66 | { 0x27, 0x14 }, |
67 | { 0x38, 0xcc }, // VCOM_ADJ1 |
68 | { 0x39, 0xd7 }, // VCOM_ADJ2 |
69 | { 0x3a, 0x4a }, // VCOM_ADJ3 |
70 | { 0x28, 0x40 }, |
71 | { 0x29, 0x01 }, |
72 | { 0x2a, 0xdf }, |
73 | { 0x49, 0x3c }, |
74 | { 0x91, 0x77 }, // EXTPW_CTRL2 |
75 | { 0x92, 0x77 }, // EXTPW_CTRL3 |
76 | { 0xa0, 0x55 }, |
77 | { 0xa1, 0x50 }, |
78 | { 0xa4, 0x9c }, |
79 | { 0xa7, 0x02 }, |
80 | { 0xa8, 0x01 }, |
81 | { 0xa9, 0x01 }, |
82 | { 0xaa, 0xfc }, |
83 | { 0xab, 0x28 }, |
84 | { 0xac, 0x06 }, |
85 | { 0xad, 0x06 }, |
86 | { 0xae, 0x06 }, |
87 | { 0xaf, 0x03 }, |
88 | { 0xb0, 0x08 }, |
89 | { 0xb1, 0x26 }, |
90 | { 0xb2, 0x28 }, |
91 | { 0xb3, 0x28 }, |
92 | { 0xb4, 0x33 }, |
93 | { 0xb5, 0x08 }, |
94 | { 0xb6, 0x26 }, |
95 | { 0xb7, 0x08 }, |
96 | { 0xb8, 0x26 }, |
97 | { 0xf0, 0x00 }, |
98 | { 0xf6, 0xc0 }, |
99 | // EXTC Command set enable, select page 2 |
100 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, |
101 | // Set gray scale voltage to adjust gamma |
102 | { 0xb0, 0x0b }, // PGAMVR0 |
103 | { 0xb1, 0x16 }, // PGAMVR1 |
104 | { 0xb2, 0x17 }, // PGAMVR2 |
105 | { 0xb3, 0x2c }, // PGAMVR3 |
106 | { 0xb4, 0x32 }, // PGAMVR4 |
107 | { 0xb5, 0x3b }, // PGAMVR5 |
108 | { 0xb6, 0x29 }, // PGAMPR0 |
109 | { 0xb7, 0x40 }, // PGAMPR1 |
110 | { 0xb8, 0x0d }, // PGAMPK0 |
111 | { 0xb9, 0x05 }, // PGAMPK1 |
112 | { 0xba, 0x12 }, // PGAMPK2 |
113 | { 0xbb, 0x10 }, // PGAMPK3 |
114 | { 0xbc, 0x12 }, // PGAMPK4 |
115 | { 0xbd, 0x15 }, // PGAMPK5 |
116 | { 0xbe, 0x19 }, // PGAMPK6 |
117 | { 0xbf, 0x0e }, // PGAMPK7 |
118 | { 0xc0, 0x16 }, // PGAMPK8 |
119 | { 0xc1, 0x0a }, // PGAMPK9 |
120 | // Set gray scale voltage to adjust gamma |
121 | { 0xd0, 0x0c }, // NGAMVR0 |
122 | { 0xd1, 0x17 }, // NGAMVR0 |
123 | { 0xd2, 0x14 }, // NGAMVR1 |
124 | { 0xd3, 0x2e }, // NGAMVR2 |
125 | { 0xd4, 0x32 }, // NGAMVR3 |
126 | { 0xd5, 0x3c }, // NGAMVR4 |
127 | { 0xd6, 0x22 }, // NGAMPR0 |
128 | { 0xd7, 0x3d }, // NGAMPR1 |
129 | { 0xd8, 0x0d }, // NGAMPK0 |
130 | { 0xd9, 0x07 }, // NGAMPK1 |
131 | { 0xda, 0x13 }, // NGAMPK2 |
132 | { 0xdb, 0x13 }, // NGAMPK3 |
133 | { 0xdc, 0x11 }, // NGAMPK4 |
134 | { 0xdd, 0x15 }, // NGAMPK5 |
135 | { 0xde, 0x19 }, // NGAMPK6 |
136 | { 0xdf, 0x10 }, // NGAMPK7 |
137 | { 0xe0, 0x17 }, // NGAMPK8 |
138 | { 0xe1, 0x0a }, // NGAMPK9 |
139 | // EXTC Command set enable, select page 3 |
140 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, |
141 | // Set various timing settings |
142 | { 0x00, 0x2a }, // GIP_VST_1 |
143 | { 0x01, 0x2a }, // GIP_VST_2 |
144 | { 0x02, 0x2a }, // GIP_VST_3 |
145 | { 0x03, 0x2a }, // GIP_VST_4 |
146 | { 0x04, 0x61 }, // GIP_VST_5 |
147 | { 0x05, 0x80 }, // GIP_VST_6 |
148 | { 0x06, 0xc7 }, // GIP_VST_7 |
149 | { 0x07, 0x01 }, // GIP_VST_8 |
150 | { 0x08, 0x03 }, // GIP_VST_9 |
151 | { 0x09, 0x04 }, // GIP_VST_10 |
152 | { 0x70, 0x22 }, // GIP_ECLK1 |
153 | { 0x71, 0x80 }, // GIP_ECLK2 |
154 | { 0x30, 0x2a }, // GIP_CLK_1 |
155 | { 0x31, 0x2a }, // GIP_CLK_2 |
156 | { 0x32, 0x2a }, // GIP_CLK_3 |
157 | { 0x33, 0x2a }, // GIP_CLK_4 |
158 | { 0x34, 0x61 }, // GIP_CLK_5 |
159 | { 0x35, 0xc5 }, // GIP_CLK_6 |
160 | { 0x36, 0x80 }, // GIP_CLK_7 |
161 | { 0x37, 0x23 }, // GIP_CLK_8 |
162 | { 0x40, 0x03 }, // GIP_CLKA_1 |
163 | { 0x41, 0x04 }, // GIP_CLKA_2 |
164 | { 0x42, 0x05 }, // GIP_CLKA_3 |
165 | { 0x43, 0x06 }, // GIP_CLKA_4 |
166 | { 0x44, 0x11 }, // GIP_CLKA_5 |
167 | { 0x45, 0xe8 }, // GIP_CLKA_6 |
168 | { 0x46, 0xe9 }, // GIP_CLKA_7 |
169 | { 0x47, 0x11 }, // GIP_CLKA_8 |
170 | { 0x48, 0xea }, // GIP_CLKA_9 |
171 | { 0x49, 0xeb }, // GIP_CLKA_10 |
172 | { 0x50, 0x07 }, // GIP_CLKB_1 |
173 | { 0x51, 0x08 }, // GIP_CLKB_2 |
174 | { 0x52, 0x09 }, // GIP_CLKB_3 |
175 | { 0x53, 0x0a }, // GIP_CLKB_4 |
176 | { 0x54, 0x11 }, // GIP_CLKB_5 |
177 | { 0x55, 0xec }, // GIP_CLKB_6 |
178 | { 0x56, 0xed }, // GIP_CLKB_7 |
179 | { 0x57, 0x11 }, // GIP_CLKB_8 |
180 | { 0x58, 0xef }, // GIP_CLKB_9 |
181 | { 0x59, 0xf0 }, // GIP_CLKB_10 |
182 | // Map internal GOA signals to GOA output pad |
183 | { 0xb1, 0x01 }, // PANELD2U2 |
184 | { 0xb4, 0x15 }, // PANELD2U5 |
185 | { 0xb5, 0x16 }, // PANELD2U6 |
186 | { 0xb6, 0x09 }, // PANELD2U7 |
187 | { 0xb7, 0x0f }, // PANELD2U8 |
188 | { 0xb8, 0x0d }, // PANELD2U9 |
189 | { 0xb9, 0x0b }, // PANELD2U10 |
190 | { 0xba, 0x00 }, // PANELD2U11 |
191 | { 0xc7, 0x02 }, // PANELD2U24 |
192 | { 0xca, 0x17 }, // PANELD2U27 |
193 | { 0xcb, 0x18 }, // PANELD2U28 |
194 | { 0xcc, 0x0a }, // PANELD2U29 |
195 | { 0xcd, 0x10 }, // PANELD2U30 |
196 | { 0xce, 0x0e }, // PANELD2U31 |
197 | { 0xcf, 0x0c }, // PANELD2U32 |
198 | { 0xd0, 0x00 }, // PANELD2U33 |
199 | // Map internal GOA signals to GOA output pad |
200 | { 0x81, 0x00 }, // PANELU2D2 |
201 | { 0x84, 0x15 }, // PANELU2D5 |
202 | { 0x85, 0x16 }, // PANELU2D6 |
203 | { 0x86, 0x10 }, // PANELU2D7 |
204 | { 0x87, 0x0a }, // PANELU2D8 |
205 | { 0x88, 0x0c }, // PANELU2D9 |
206 | { 0x89, 0x0e }, // PANELU2D10 |
207 | { 0x8a, 0x02 }, // PANELU2D11 |
208 | { 0x97, 0x00 }, // PANELU2D24 |
209 | { 0x9a, 0x17 }, // PANELU2D27 |
210 | { 0x9b, 0x18 }, // PANELU2D28 |
211 | { 0x9c, 0x0f }, // PANELU2D29 |
212 | { 0x9d, 0x09 }, // PANELU2D30 |
213 | { 0x9e, 0x0b }, // PANELU2D31 |
214 | { 0x9f, 0x0d }, // PANELU2D32 |
215 | { 0xa0, 0x01 }, // PANELU2D33 |
216 | // EXTC Command set enable, select page 2 |
217 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, |
218 | // Unknown registers |
219 | { 0x01, 0x01 }, |
220 | { 0x02, 0xda }, |
221 | { 0x03, 0xba }, |
222 | { 0x04, 0xa8 }, |
223 | { 0x05, 0x9a }, |
224 | { 0x06, 0x70 }, |
225 | { 0x07, 0xff }, |
226 | { 0x08, 0x91 }, |
227 | { 0x09, 0x90 }, |
228 | { 0x0a, 0xff }, |
229 | { 0x0b, 0x8f }, |
230 | { 0x0c, 0x60 }, |
231 | { 0x0d, 0x58 }, |
232 | { 0x0e, 0x48 }, |
233 | { 0x0f, 0x38 }, |
234 | { 0x10, 0x2b }, |
235 | // EXTC Command set enable, select page 0 |
236 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, |
237 | // Display Access Control |
238 | { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 |
239 | }; |
240 | |
241 | static const struct nv3052c_reg fs035vg158_panel_regs[] = { |
242 | // EXTC Command set enable, select page 1 |
243 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, |
244 | // Mostly unknown registers |
245 | { 0xe3, 0x00 }, |
246 | { 0x40, 0x00 }, |
247 | { 0x03, 0x40 }, |
248 | { 0x04, 0x00 }, |
249 | { 0x05, 0x03 }, |
250 | { 0x08, 0x00 }, |
251 | { 0x09, 0x07 }, |
252 | { 0x0a, 0x01 }, |
253 | { 0x0b, 0x32 }, |
254 | { 0x0c, 0x32 }, |
255 | { 0x0d, 0x0b }, |
256 | { 0x0e, 0x00 }, |
257 | { 0x23, 0x20 }, // RGB interface control: DE MODE PCLK-N |
258 | { 0x24, 0x0c }, |
259 | { 0x25, 0x06 }, |
260 | { 0x26, 0x14 }, |
261 | { 0x27, 0x14 }, |
262 | { 0x38, 0x9c }, //VCOM_ADJ1, different to ltk035c5444t |
263 | { 0x39, 0xa7 }, //VCOM_ADJ2, different to ltk035c5444t |
264 | { 0x3a, 0x50 }, //VCOM_ADJ3, different to ltk035c5444t |
265 | { 0x28, 0x40 }, |
266 | { 0x29, 0x01 }, |
267 | { 0x2a, 0xdf }, |
268 | { 0x49, 0x3c }, |
269 | { 0x91, 0x57 }, //EXTPW_CTRL2, different to ltk035c5444t |
270 | { 0x92, 0x57 }, //EXTPW_CTRL3, different to ltk035c5444t |
271 | { 0xa0, 0x55 }, |
272 | { 0xa1, 0x50 }, |
273 | { 0xa4, 0x9c }, |
274 | { 0xa7, 0x02 }, |
275 | { 0xa8, 0x01 }, |
276 | { 0xa9, 0x01 }, |
277 | { 0xaa, 0xfc }, |
278 | { 0xab, 0x28 }, |
279 | { 0xac, 0x06 }, |
280 | { 0xad, 0x06 }, |
281 | { 0xae, 0x06 }, |
282 | { 0xaf, 0x03 }, |
283 | { 0xb0, 0x08 }, |
284 | { 0xb1, 0x26 }, |
285 | { 0xb2, 0x28 }, |
286 | { 0xb3, 0x28 }, |
287 | { 0xb4, 0x03 }, // Unknown, different to ltk035c5444 |
288 | { 0xb5, 0x08 }, |
289 | { 0xb6, 0x26 }, |
290 | { 0xb7, 0x08 }, |
291 | { 0xb8, 0x26 }, |
292 | { 0xf0, 0x00 }, |
293 | { 0xf6, 0xc0 }, |
294 | // EXTC Command set enable, select page 0 |
295 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, |
296 | // Set gray scale voltage to adjust gamma |
297 | { 0xb0, 0x0b }, // PGAMVR0 |
298 | { 0xb1, 0x16 }, // PGAMVR1 |
299 | { 0xb2, 0x17 }, // PGAMVR2 |
300 | { 0xb3, 0x2c }, // PGAMVR3 |
301 | { 0xb4, 0x32 }, // PGAMVR4 |
302 | { 0xb5, 0x3b }, // PGAMVR5 |
303 | { 0xb6, 0x29 }, // PGAMPR0 |
304 | { 0xb7, 0x40 }, // PGAMPR1 |
305 | { 0xb8, 0x0d }, // PGAMPK0 |
306 | { 0xb9, 0x05 }, // PGAMPK1 |
307 | { 0xba, 0x12 }, // PGAMPK2 |
308 | { 0xbb, 0x10 }, // PGAMPK3 |
309 | { 0xbc, 0x12 }, // PGAMPK4 |
310 | { 0xbd, 0x15 }, // PGAMPK5 |
311 | { 0xbe, 0x19 }, // PGAMPK6 |
312 | { 0xbf, 0x0e }, // PGAMPK7 |
313 | { 0xc0, 0x16 }, // PGAMPK8 |
314 | { 0xc1, 0x0a }, // PGAMPK9 |
315 | // Set gray scale voltage to adjust gamma |
316 | { 0xd0, 0x0c }, // NGAMVR0 |
317 | { 0xd1, 0x17 }, // NGAMVR0 |
318 | { 0xd2, 0x14 }, // NGAMVR1 |
319 | { 0xd3, 0x2e }, // NGAMVR2 |
320 | { 0xd4, 0x32 }, // NGAMVR3 |
321 | { 0xd5, 0x3c }, // NGAMVR4 |
322 | { 0xd6, 0x22 }, // NGAMPR0 |
323 | { 0xd7, 0x3d }, // NGAMPR1 |
324 | { 0xd8, 0x0d }, // NGAMPK0 |
325 | { 0xd9, 0x07 }, // NGAMPK1 |
326 | { 0xda, 0x13 }, // NGAMPK2 |
327 | { 0xdb, 0x13 }, // NGAMPK3 |
328 | { 0xdc, 0x11 }, // NGAMPK4 |
329 | { 0xdd, 0x15 }, // NGAMPK5 |
330 | { 0xde, 0x19 }, // NGAMPK6 |
331 | { 0xdf, 0x10 }, // NGAMPK7 |
332 | { 0xe0, 0x17 }, // NGAMPK8 |
333 | { 0xe1, 0x0a }, // NGAMPK9 |
334 | // EXTC Command set enable, select page 3 |
335 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, |
336 | // Set various timing settings |
337 | { 0x00, 0x2a }, // GIP_VST_1 |
338 | { 0x01, 0x2a }, // GIP_VST_2 |
339 | { 0x02, 0x2a }, // GIP_VST_3 |
340 | { 0x03, 0x2a }, // GIP_VST_4 |
341 | { 0x04, 0x61 }, // GIP_VST_5 |
342 | { 0x05, 0x80 }, // GIP_VST_6 |
343 | { 0x06, 0xc7 }, // GIP_VST_7 |
344 | { 0x07, 0x01 }, // GIP_VST_8 |
345 | { 0x08, 0x03 }, // GIP_VST_9 |
346 | { 0x09, 0x04 }, // GIP_VST_10 |
347 | { 0x70, 0x22 }, // GIP_ECLK1 |
348 | { 0x71, 0x80 }, // GIP_ECLK2 |
349 | { 0x30, 0x2a }, // GIP_CLK_1 |
350 | { 0x31, 0x2a }, // GIP_CLK_2 |
351 | { 0x32, 0x2a }, // GIP_CLK_3 |
352 | { 0x33, 0x2a }, // GIP_CLK_4 |
353 | { 0x34, 0x61 }, // GIP_CLK_5 |
354 | { 0x35, 0xc5 }, // GIP_CLK_6 |
355 | { 0x36, 0x80 }, // GIP_CLK_7 |
356 | { 0x37, 0x23 }, // GIP_CLK_8 |
357 | { 0x40, 0x03 }, // GIP_CLKA_1 |
358 | { 0x41, 0x04 }, // GIP_CLKA_2 |
359 | { 0x42, 0x05 }, // GIP_CLKA_3 |
360 | { 0x43, 0x06 }, // GIP_CLKA_4 |
361 | { 0x44, 0x11 }, // GIP_CLKA_5 |
362 | { 0x45, 0xe8 }, // GIP_CLKA_6 |
363 | { 0x46, 0xe9 }, // GIP_CLKA_7 |
364 | { 0x47, 0x11 }, // GIP_CLKA_8 |
365 | { 0x48, 0xea }, // GIP_CLKA_9 |
366 | { 0x49, 0xeb }, // GIP_CLKA_10 |
367 | { 0x50, 0x07 }, // GIP_CLKB_1 |
368 | { 0x51, 0x08 }, // GIP_CLKB_2 |
369 | { 0x52, 0x09 }, // GIP_CLKB_3 |
370 | { 0x53, 0x0a }, // GIP_CLKB_4 |
371 | { 0x54, 0x11 }, // GIP_CLKB_5 |
372 | { 0x55, 0xec }, // GIP_CLKB_6 |
373 | { 0x56, 0xed }, // GIP_CLKB_7 |
374 | { 0x57, 0x11 }, // GIP_CLKB_8 |
375 | { 0x58, 0xef }, // GIP_CLKB_9 |
376 | { 0x59, 0xf0 }, // GIP_CLKB_10 |
377 | // Map internal GOA signals to GOA output pad |
378 | { 0xb1, 0x01 }, // PANELD2U2 |
379 | { 0xb4, 0x15 }, // PANELD2U5 |
380 | { 0xb5, 0x16 }, // PANELD2U6 |
381 | { 0xb6, 0x09 }, // PANELD2U7 |
382 | { 0xb7, 0x0f }, // PANELD2U8 |
383 | { 0xb8, 0x0d }, // PANELD2U9 |
384 | { 0xb9, 0x0b }, // PANELD2U10 |
385 | { 0xba, 0x00 }, // PANELD2U11 |
386 | { 0xc7, 0x02 }, // PANELD2U24 |
387 | { 0xca, 0x17 }, // PANELD2U27 |
388 | { 0xcb, 0x18 }, // PANELD2U28 |
389 | { 0xcc, 0x0a }, // PANELD2U29 |
390 | { 0xcd, 0x10 }, // PANELD2U30 |
391 | { 0xce, 0x0e }, // PANELD2U31 |
392 | { 0xcf, 0x0c }, // PANELD2U32 |
393 | { 0xd0, 0x00 }, // PANELD2U33 |
394 | // Map internal GOA signals to GOA output pad |
395 | { 0x81, 0x00 }, // PANELU2D2 |
396 | { 0x84, 0x15 }, // PANELU2D5 |
397 | { 0x85, 0x16 }, // PANELU2D6 |
398 | { 0x86, 0x10 }, // PANELU2D7 |
399 | { 0x87, 0x0a }, // PANELU2D8 |
400 | { 0x88, 0x0c }, // PANELU2D9 |
401 | { 0x89, 0x0e }, // PANELU2D10 |
402 | { 0x8a, 0x02 }, // PANELU2D11 |
403 | { 0x97, 0x00 }, // PANELU2D24 |
404 | { 0x9a, 0x17 }, // PANELU2D27 |
405 | { 0x9b, 0x18 }, // PANELU2D28 |
406 | { 0x9c, 0x0f }, // PANELU2D29 |
407 | { 0x9d, 0x09 }, // PANELU2D30 |
408 | { 0x9e, 0x0b }, // PANELU2D31 |
409 | { 0x9f, 0x0d }, // PANELU2D32 |
410 | { 0xa0, 0x01 }, // PANELU2D33 |
411 | // EXTC Command set enable, select page 2 |
412 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, |
413 | // Unknown registers |
414 | { 0x01, 0x01 }, |
415 | { 0x02, 0xda }, |
416 | { 0x03, 0xba }, |
417 | { 0x04, 0xa8 }, |
418 | { 0x05, 0x9a }, |
419 | { 0x06, 0x70 }, |
420 | { 0x07, 0xff }, |
421 | { 0x08, 0x91 }, |
422 | { 0x09, 0x90 }, |
423 | { 0x0a, 0xff }, |
424 | { 0x0b, 0x8f }, |
425 | { 0x0c, 0x60 }, |
426 | { 0x0d, 0x58 }, |
427 | { 0x0e, 0x48 }, |
428 | { 0x0f, 0x38 }, |
429 | { 0x10, 0x2b }, |
430 | // EXTC Command set enable, select page 0 |
431 | { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, |
432 | // Display Access Control |
433 | { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 |
434 | }; |
435 | |
436 | static inline struct nv3052c *to_nv3052c(struct drm_panel *panel) |
437 | { |
438 | return container_of(panel, struct nv3052c, panel); |
439 | } |
440 | |
441 | static int nv3052c_prepare(struct drm_panel *panel) |
442 | { |
443 | struct nv3052c *priv = to_nv3052c(panel); |
444 | const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs; |
445 | unsigned int panel_regs_len = priv->panel_info->panel_regs_len; |
446 | struct mipi_dbi *dbi = &priv->dbi; |
447 | unsigned int i; |
448 | int err; |
449 | |
450 | err = regulator_enable(regulator: priv->supply); |
451 | if (err) { |
452 | dev_err(priv->dev, "Failed to enable power supply: %d\n", err); |
453 | return err; |
454 | } |
455 | |
456 | /* Reset the chip */ |
457 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
458 | usleep_range(min: 10, max: 1000); |
459 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 0); |
460 | usleep_range(min: 5000, max: 20000); |
461 | |
462 | for (i = 0; i < panel_regs_len; i++) { |
463 | err = mipi_dbi_command(dbi, panel_regs[i].cmd, |
464 | panel_regs[i].val); |
465 | |
466 | if (err) { |
467 | dev_err(priv->dev, "Unable to set register: %d\n", err); |
468 | goto err_disable_regulator; |
469 | } |
470 | } |
471 | |
472 | err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); |
473 | if (err) { |
474 | dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err); |
475 | goto err_disable_regulator; |
476 | } |
477 | |
478 | return 0; |
479 | |
480 | err_disable_regulator: |
481 | regulator_disable(regulator: priv->supply); |
482 | return err; |
483 | } |
484 | |
485 | static int nv3052c_unprepare(struct drm_panel *panel) |
486 | { |
487 | struct nv3052c *priv = to_nv3052c(panel); |
488 | struct mipi_dbi *dbi = &priv->dbi; |
489 | int err; |
490 | |
491 | err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); |
492 | if (err) |
493 | dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err); |
494 | |
495 | gpiod_set_value_cansleep(desc: priv->reset_gpio, value: 1); |
496 | regulator_disable(regulator: priv->supply); |
497 | |
498 | return 0; |
499 | } |
500 | |
501 | static int nv3052c_enable(struct drm_panel *panel) |
502 | { |
503 | struct nv3052c *priv = to_nv3052c(panel); |
504 | struct mipi_dbi *dbi = &priv->dbi; |
505 | int err; |
506 | |
507 | err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); |
508 | if (err) { |
509 | dev_err(priv->dev, "Unable to enable display: %d\n", err); |
510 | return err; |
511 | } |
512 | |
513 | if (panel->backlight) { |
514 | /* Wait for the picture to be ready before enabling backlight */ |
515 | msleep(msecs: 120); |
516 | } |
517 | |
518 | return 0; |
519 | } |
520 | |
521 | static int nv3052c_disable(struct drm_panel *panel) |
522 | { |
523 | struct nv3052c *priv = to_nv3052c(panel); |
524 | struct mipi_dbi *dbi = &priv->dbi; |
525 | int err; |
526 | |
527 | err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); |
528 | if (err) { |
529 | dev_err(priv->dev, "Unable to disable display: %d\n", err); |
530 | return err; |
531 | } |
532 | |
533 | return 0; |
534 | } |
535 | |
536 | static int nv3052c_get_modes(struct drm_panel *panel, |
537 | struct drm_connector *connector) |
538 | { |
539 | struct nv3052c *priv = to_nv3052c(panel); |
540 | const struct nv3052c_panel_info *panel_info = priv->panel_info; |
541 | struct drm_display_mode *mode; |
542 | unsigned int i; |
543 | |
544 | for (i = 0; i < panel_info->num_modes; i++) { |
545 | mode = drm_mode_duplicate(dev: connector->dev, |
546 | mode: &panel_info->display_modes[i]); |
547 | if (!mode) |
548 | return -ENOMEM; |
549 | |
550 | drm_mode_set_name(mode); |
551 | |
552 | mode->type = DRM_MODE_TYPE_DRIVER; |
553 | if (panel_info->num_modes == 1) |
554 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
555 | |
556 | drm_mode_probed_add(connector, mode); |
557 | } |
558 | |
559 | connector->display_info.bpc = 8; |
560 | connector->display_info.width_mm = panel_info->width_mm; |
561 | connector->display_info.height_mm = panel_info->height_mm; |
562 | |
563 | drm_display_info_set_bus_formats(info: &connector->display_info, |
564 | formats: &panel_info->bus_format, num_formats: 1); |
565 | connector->display_info.bus_flags = panel_info->bus_flags; |
566 | |
567 | return panel_info->num_modes; |
568 | } |
569 | |
570 | static const struct drm_panel_funcs nv3052c_funcs = { |
571 | .prepare = nv3052c_prepare, |
572 | .unprepare = nv3052c_unprepare, |
573 | .enable = nv3052c_enable, |
574 | .disable = nv3052c_disable, |
575 | .get_modes = nv3052c_get_modes, |
576 | }; |
577 | |
578 | static int nv3052c_probe(struct spi_device *spi) |
579 | { |
580 | struct device *dev = &spi->dev; |
581 | struct nv3052c *priv; |
582 | int err; |
583 | |
584 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
585 | if (!priv) |
586 | return -ENOMEM; |
587 | |
588 | priv->dev = dev; |
589 | |
590 | priv->panel_info = of_device_get_match_data(dev); |
591 | if (!priv->panel_info) |
592 | return -EINVAL; |
593 | |
594 | priv->supply = devm_regulator_get(dev, id: "power"); |
595 | if (IS_ERR(ptr: priv->supply)) |
596 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->supply), fmt: "Failed to get power supply\n"); |
597 | |
598 | priv->reset_gpio = devm_gpiod_get(dev, con_id: "reset", flags: GPIOD_OUT_HIGH); |
599 | if (IS_ERR(ptr: priv->reset_gpio)) |
600 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->reset_gpio), fmt: "Failed to get reset GPIO\n"); |
601 | |
602 | err = mipi_dbi_spi_init(spi, dbi: &priv->dbi, NULL); |
603 | if (err) |
604 | return dev_err_probe(dev, err, fmt: "MIPI DBI init failed\n"); |
605 | |
606 | priv->dbi.read_commands = NULL; |
607 | |
608 | spi_set_drvdata(spi, data: priv); |
609 | |
610 | drm_panel_init(panel: &priv->panel, dev, funcs: &nv3052c_funcs, |
611 | DRM_MODE_CONNECTOR_DPI); |
612 | |
613 | err = drm_panel_of_backlight(panel: &priv->panel); |
614 | if (err) |
615 | return dev_err_probe(dev, err, fmt: "Failed to attach backlight\n"); |
616 | |
617 | drm_panel_add(panel: &priv->panel); |
618 | |
619 | return 0; |
620 | } |
621 | |
622 | static void nv3052c_remove(struct spi_device *spi) |
623 | { |
624 | struct nv3052c *priv = spi_get_drvdata(spi); |
625 | |
626 | drm_panel_remove(panel: &priv->panel); |
627 | drm_panel_disable(panel: &priv->panel); |
628 | drm_panel_unprepare(panel: &priv->panel); |
629 | } |
630 | |
631 | static const struct drm_display_mode ltk035c5444t_modes[] = { |
632 | { /* 60 Hz */ |
633 | .clock = 24000, |
634 | .hdisplay = 640, |
635 | .hsync_start = 640 + 96, |
636 | .hsync_end = 640 + 96 + 16, |
637 | .htotal = 640 + 96 + 16 + 48, |
638 | .vdisplay = 480, |
639 | .vsync_start = 480 + 5, |
640 | .vsync_end = 480 + 5 + 2, |
641 | .vtotal = 480 + 5 + 2 + 13, |
642 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
643 | }, |
644 | { /* 50 Hz */ |
645 | .clock = 18000, |
646 | .hdisplay = 640, |
647 | .hsync_start = 640 + 39, |
648 | .hsync_end = 640 + 39 + 2, |
649 | .htotal = 640 + 39 + 2 + 39, |
650 | .vdisplay = 480, |
651 | .vsync_start = 480 + 5, |
652 | .vsync_end = 480 + 5 + 2, |
653 | .vtotal = 480 + 5 + 2 + 13, |
654 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
655 | }, |
656 | }; |
657 | |
658 | static const struct drm_display_mode fs035vg158_modes[] = { |
659 | { /* 60 Hz */ |
660 | .clock = 21000, |
661 | .hdisplay = 640, |
662 | .hsync_start = 640 + 34, |
663 | .hsync_end = 640 + 34 + 4, |
664 | .htotal = 640 + 34 + 4 + 20, |
665 | .vdisplay = 480, |
666 | .vsync_start = 480 + 12, |
667 | .vsync_end = 480 + 12 + 4, |
668 | .vtotal = 480 + 12 + 4 + 6, |
669 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
670 | }, |
671 | }; |
672 | |
673 | static const struct nv3052c_panel_info ltk035c5444t_panel_info = { |
674 | .display_modes = ltk035c5444t_modes, |
675 | .num_modes = ARRAY_SIZE(ltk035c5444t_modes), |
676 | .width_mm = 77, |
677 | .height_mm = 64, |
678 | .bus_format = MEDIA_BUS_FMT_RGB888_1X24, |
679 | .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, |
680 | .panel_regs = ltk035c5444t_panel_regs, |
681 | .panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs), |
682 | }; |
683 | |
684 | static const struct nv3052c_panel_info fs035vg158_panel_info = { |
685 | .display_modes = fs035vg158_modes, |
686 | .num_modes = ARRAY_SIZE(fs035vg158_modes), |
687 | .width_mm = 70, |
688 | .height_mm = 53, |
689 | .bus_format = MEDIA_BUS_FMT_RGB888_1X24, |
690 | .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, |
691 | .panel_regs = fs035vg158_panel_regs, |
692 | .panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs), |
693 | }; |
694 | |
695 | static const struct spi_device_id nv3052c_ids[] = { |
696 | { "ltk035c5444t", }, |
697 | { "fs035vg158", }, |
698 | { /* sentinel */ } |
699 | }; |
700 | MODULE_DEVICE_TABLE(spi, nv3052c_ids); |
701 | |
702 | static const struct of_device_id nv3052c_of_match[] = { |
703 | { .compatible = "leadtek,ltk035c5444t", .data = <k035c5444t_panel_info }, |
704 | { .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info }, |
705 | { /* sentinel */ } |
706 | }; |
707 | MODULE_DEVICE_TABLE(of, nv3052c_of_match); |
708 | |
709 | static struct spi_driver nv3052c_driver = { |
710 | .driver = { |
711 | .name = "nv3052c", |
712 | .of_match_table = nv3052c_of_match, |
713 | }, |
714 | .id_table = nv3052c_ids, |
715 | .probe = nv3052c_probe, |
716 | .remove = nv3052c_remove, |
717 | }; |
718 | module_spi_driver(nv3052c_driver); |
719 | |
720 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); |
721 | MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); |
722 | MODULE_LICENSE("GPL v2"); |
723 |
Definitions
- nv3052c_reg
- nv3052c_panel_info
- nv3052c
- ltk035c5444t_panel_regs
- fs035vg158_panel_regs
- to_nv3052c
- nv3052c_prepare
- nv3052c_unprepare
- nv3052c_enable
- nv3052c_disable
- nv3052c_get_modes
- nv3052c_funcs
- nv3052c_probe
- nv3052c_remove
- ltk035c5444t_modes
- fs035vg158_modes
- ltk035c5444t_panel_info
- fs035vg158_panel_info
- nv3052c_ids
- nv3052c_of_match
Improve your Profiling and Debugging skills
Find out more