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