Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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
 23struct nv3052c_reg {
 24	u8 cmd;
 25	u8 val;
 26};
 27
 28struct 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
 37struct 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
 46static 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
241static 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
437static const struct nv3052c_reg wl_355608_a8_panel_regs[] = {
438	// EXTC Command set enable, select page 1
439	{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },
440	// Mostly unknown registers
441	{ 0xe3, 0x00 },
442	{ 0x40, 0x00 },
443	{ 0x03, 0x40 },
444	{ 0x04, 0x00 },
445	{ 0x05, 0x03 },
446	{ 0x08, 0x00 },
447	{ 0x09, 0x07 },
448	{ 0x0a, 0x01 },
449	{ 0x0b, 0x32 },
450	{ 0x0c, 0x32 },
451	{ 0x0d, 0x0b },
452	{ 0x0e, 0x00 },
453	{ 0x23, 0xa0 },
454	{ 0x24, 0x0c },
455	{ 0x25, 0x06 },
456	{ 0x26, 0x14 },
457	{ 0x27, 0x14 },
458	{ 0x38, 0xcc }, // VCOM_ADJ1
459	{ 0x39, 0xd7 }, // VCOM_ADJ2
460	{ 0x3a, 0x44 }, // VCOM_ADJ3
461	{ 0x28, 0x40 },
462	{ 0x29, 0x01 },
463	{ 0x2a, 0xdf },
464	{ 0x49, 0x3c },
465	{ 0x91, 0x77 }, // EXTPW_CTRL2
466	{ 0x92, 0x77 }, // EXTPW_CTRL3
467	{ 0xa0, 0x55 },
468	{ 0xa1, 0x50 },
469	{ 0xa4, 0x9c },
470	{ 0xa7, 0x02 },
471	{ 0xa8, 0x01 },
472	{ 0xa9, 0x01 },
473	{ 0xaa, 0xfc },
474	{ 0xab, 0x28 },
475	{ 0xac, 0x06 },
476	{ 0xad, 0x06 },
477	{ 0xae, 0x06 },
478	{ 0xaf, 0x03 },
479	{ 0xb0, 0x08 },
480	{ 0xb1, 0x26 },
481	{ 0xb2, 0x28 },
482	{ 0xb3, 0x28 },
483	{ 0xb4, 0x33 },
484	{ 0xb5, 0x08 },
485	{ 0xb6, 0x26 },
486	{ 0xb7, 0x08 },
487	{ 0xb8, 0x26 },
488	{ 0xf0, 0x00 },
489	{ 0xf6, 0xc0 },
490	// EXTC Command set enable, select page 2
491	{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
492	// Set gray scale voltage to adjust gamma
493	{ 0xb0, 0x0b }, // PGAMVR0
494	{ 0xb1, 0x16 }, // PGAMVR1
495	{ 0xb2, 0x17 }, // PGAMVR2
496	{ 0xb3, 0x2c }, // PGAMVR3
497	{ 0xb4, 0x32 }, // PGAMVR4
498	{ 0xb5, 0x3b }, // PGAMVR5
499	{ 0xb6, 0x29 }, // PGAMPR0
500	{ 0xb7, 0x40 }, // PGAMPR1
501	{ 0xb8, 0x0d }, // PGAMPK0
502	{ 0xb9, 0x05 }, // PGAMPK1
503	{ 0xba, 0x12 }, // PGAMPK2
504	{ 0xbb, 0x10 }, // PGAMPK3
505	{ 0xbc, 0x12 }, // PGAMPK4
506	{ 0xbd, 0x15 }, // PGAMPK5
507	{ 0xbe, 0x19 }, // PGAMPK6
508	{ 0xbf, 0x0e }, // PGAMPK7
509	{ 0xc0, 0x16 }, // PGAMPK8
510	{ 0xc1, 0x0a }, // PGAMPK9
511	// Set gray scale voltage to adjust gamma
512	{ 0xd0, 0x0c }, // NGAMVR0
513	{ 0xd1, 0x17 }, // NGAMVR0
514	{ 0xd2, 0x14 }, // NGAMVR1
515	{ 0xd3, 0x2e }, // NGAMVR2
516	{ 0xd4, 0x32 }, // NGAMVR3
517	{ 0xd5, 0x3c }, // NGAMVR4
518	{ 0xd6, 0x22 }, // NGAMPR0
519	{ 0xd7, 0x3d }, // NGAMPR1
520	{ 0xd8, 0x0d }, // NGAMPK0
521	{ 0xd9, 0x07 }, // NGAMPK1
522	{ 0xda, 0x13 }, // NGAMPK2
523	{ 0xdb, 0x13 }, // NGAMPK3
524	{ 0xdc, 0x11 }, // NGAMPK4
525	{ 0xdd, 0x15 }, // NGAMPK5
526	{ 0xde, 0x19 }, // NGAMPK6
527	{ 0xdf, 0x10 }, // NGAMPK7
528	{ 0xe0, 0x17 }, // NGAMPK8
529	{ 0xe1, 0x0a }, // NGAMPK9
530	// EXTC Command set enable, select page 3
531	{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },
532	// Set various timing settings
533	{ 0x00, 0x2a }, // GIP_VST_1
534	{ 0x01, 0x2a }, // GIP_VST_2
535	{ 0x02, 0x2a }, // GIP_VST_3
536	{ 0x03, 0x2a }, // GIP_VST_4
537	{ 0x04, 0x61 }, // GIP_VST_5
538	{ 0x05, 0x80 }, // GIP_VST_6
539	{ 0x06, 0xc7 }, // GIP_VST_7
540	{ 0x07, 0x01 }, // GIP_VST_8
541	{ 0x08, 0x03 }, // GIP_VST_9
542	{ 0x09, 0x04 }, // GIP_VST_10
543	{ 0x70, 0x22 }, // GIP_ECLK1
544	{ 0x71, 0x80 }, // GIP_ECLK2
545	{ 0x30, 0x2a }, // GIP_CLK_1
546	{ 0x31, 0x2a }, // GIP_CLK_2
547	{ 0x32, 0x2a }, // GIP_CLK_3
548	{ 0x33, 0x2a }, // GIP_CLK_4
549	{ 0x34, 0x61 }, // GIP_CLK_5
550	{ 0x35, 0xc5 }, // GIP_CLK_6
551	{ 0x36, 0x80 }, // GIP_CLK_7
552	{ 0x37, 0x23 }, // GIP_CLK_8
553	{ 0x40, 0x03 }, // GIP_CLKA_1
554	{ 0x41, 0x04 }, // GIP_CLKA_2
555	{ 0x42, 0x05 }, // GIP_CLKA_3
556	{ 0x43, 0x06 }, // GIP_CLKA_4
557	{ 0x44, 0x11 }, // GIP_CLKA_5
558	{ 0x45, 0xe8 }, // GIP_CLKA_6
559	{ 0x46, 0xe9 }, // GIP_CLKA_7
560	{ 0x47, 0x11 }, // GIP_CLKA_8
561	{ 0x48, 0xea }, // GIP_CLKA_9
562	{ 0x49, 0xeb }, // GIP_CLKA_10
563	{ 0x50, 0x07 }, // GIP_CLKB_1
564	{ 0x51, 0x08 }, // GIP_CLKB_2
565	{ 0x52, 0x09 }, // GIP_CLKB_3
566	{ 0x53, 0x0a }, // GIP_CLKB_4
567	{ 0x54, 0x11 }, // GIP_CLKB_5
568	{ 0x55, 0xec }, // GIP_CLKB_6
569	{ 0x56, 0xed }, // GIP_CLKB_7
570	{ 0x57, 0x11 }, // GIP_CLKB_8
571	{ 0x58, 0xef }, // GIP_CLKB_9
572	{ 0x59, 0xf0 }, // GIP_CLKB_10
573	// Map internal GOA signals to GOA output pad
574	{ 0xb1, 0x01 }, // PANELD2U2
575	{ 0xb4, 0x15 }, // PANELD2U5
576	{ 0xb5, 0x16 }, // PANELD2U6
577	{ 0xb6, 0x09 }, // PANELD2U7
578	{ 0xb7, 0x0f }, // PANELD2U8
579	{ 0xb8, 0x0d }, // PANELD2U9
580	{ 0xb9, 0x0b }, // PANELD2U10
581	{ 0xba, 0x00 }, // PANELD2U11
582	{ 0xc7, 0x02 }, // PANELD2U24
583	{ 0xca, 0x17 }, // PANELD2U27
584	{ 0xcb, 0x18 }, // PANELD2U28
585	{ 0xcc, 0x0a }, // PANELD2U29
586	{ 0xcd, 0x10 }, // PANELD2U30
587	{ 0xce, 0x0e }, // PANELD2U31
588	{ 0xcf, 0x0c }, // PANELD2U32
589	{ 0xd0, 0x00 }, // PANELD2U33
590	// Map internal GOA signals to GOA output pad
591	{ 0x81, 0x00 }, // PANELU2D2
592	{ 0x84, 0x15 }, // PANELU2D5
593	{ 0x85, 0x16 }, // PANELU2D6
594	{ 0x86, 0x10 }, // PANELU2D7
595	{ 0x87, 0x0a }, // PANELU2D8
596	{ 0x88, 0x0c }, // PANELU2D9
597	{ 0x89, 0x0e }, // PANELU2D10
598	{ 0x8a, 0x02 }, // PANELU2D11
599	{ 0x97, 0x00 }, // PANELU2D24
600	{ 0x9a, 0x17 }, // PANELU2D27
601	{ 0x9b, 0x18 }, // PANELU2D28
602	{ 0x9c, 0x0f }, // PANELU2D29
603	{ 0x9d, 0x09 }, // PANELU2D30
604	{ 0x9e, 0x0b }, // PANELU2D31
605	{ 0x9f, 0x0d }, // PANELU2D32
606	{ 0xa0, 0x01 }, // PANELU2D33
607	// EXTC Command set enable, select page 2
608	{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
609	// Unknown registers
610	{ 0x01, 0x01 },
611	{ 0x02, 0xda },
612	{ 0x03, 0xba },
613	{ 0x04, 0xa8 },
614	{ 0x05, 0x9a },
615	{ 0x06, 0x70 },
616	{ 0x07, 0xff },
617	{ 0x08, 0x91 },
618	{ 0x09, 0x90 },
619	{ 0x0a, 0xff },
620	{ 0x0b, 0x8f },
621	{ 0x0c, 0x60 },
622	{ 0x0d, 0x58 },
623	{ 0x0e, 0x48 },
624	{ 0x0f, 0x38 },
625	{ 0x10, 0x2b },
626	// EXTC Command set enable, select page 0
627	{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },
628	// Display Access Control
629	{ 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0
630};
631
632static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
633{
634	return container_of(panel, struct nv3052c, panel);
635}
636
637static int nv3052c_prepare(struct drm_panel *panel)
638{
639	struct nv3052c *priv = to_nv3052c(panel);
640	const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs;
641	unsigned int panel_regs_len = priv->panel_info->panel_regs_len;
642	struct mipi_dbi *dbi = &priv->dbi;
643	unsigned int i;
644	int err;
645
646	err = regulator_enable(priv->supply);
647	if (err) {
648		dev_err(priv->dev, "Failed to enable power supply: %d\n", err);
649		return err;
650	}
651
652	/* Reset the chip */
653	gpiod_set_value_cansleep(priv->reset_gpio, 1);
654	usleep_range(10, 1000);
655	gpiod_set_value_cansleep(priv->reset_gpio, 0);
656	usleep_range(5000, 20000);
657
658	for (i = 0; i < panel_regs_len; i++) {
659		err = mipi_dbi_command(dbi, panel_regs[i].cmd,
660				       panel_regs[i].val);
661
662		if (err) {
663			dev_err(priv->dev, "Unable to set register: %d\n", err);
664			goto err_disable_regulator;
665		}
666	}
667
668	err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
669	if (err) {
670		dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err);
671		goto err_disable_regulator;
672	}
673
674	return 0;
675
676err_disable_regulator:
677	regulator_disable(priv->supply);
678	return err;
679}
680
681static int nv3052c_unprepare(struct drm_panel *panel)
682{
683	struct nv3052c *priv = to_nv3052c(panel);
684	struct mipi_dbi *dbi = &priv->dbi;
685	int err;
686
687	err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
688	if (err)
689		dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err);
690
691	gpiod_set_value_cansleep(priv->reset_gpio, 1);
692	regulator_disable(priv->supply);
693
694	return 0;
695}
696
697static int nv3052c_enable(struct drm_panel *panel)
698{
699	struct nv3052c *priv = to_nv3052c(panel);
700	struct mipi_dbi *dbi = &priv->dbi;
701	int err;
702
703	err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
704	if (err) {
705		dev_err(priv->dev, "Unable to enable display: %d\n", err);
706		return err;
707	}
708
709	if (panel->backlight) {
710		/* Wait for the picture to be ready before enabling backlight */
711		msleep(120);
712	}
713
714	return 0;
715}
716
717static int nv3052c_disable(struct drm_panel *panel)
718{
719	struct nv3052c *priv = to_nv3052c(panel);
720	struct mipi_dbi *dbi = &priv->dbi;
721	int err;
722
723	err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
724	if (err) {
725		dev_err(priv->dev, "Unable to disable display: %d\n", err);
726		return err;
727	}
728
729	return 0;
730}
731
732static int nv3052c_get_modes(struct drm_panel *panel,
733			     struct drm_connector *connector)
734{
735	struct nv3052c *priv = to_nv3052c(panel);
736	const struct nv3052c_panel_info *panel_info = priv->panel_info;
737	struct drm_display_mode *mode;
738	unsigned int i;
739
740	for (i = 0; i < panel_info->num_modes; i++) {
741		mode = drm_mode_duplicate(connector->dev,
742					  &panel_info->display_modes[i]);
743		if (!mode)
744			return -ENOMEM;
745
746		drm_mode_set_name(mode);
747
748		mode->type = DRM_MODE_TYPE_DRIVER;
749		if (panel_info->num_modes == 1)
750			mode->type |= DRM_MODE_TYPE_PREFERRED;
751
752		drm_mode_probed_add(connector, mode);
753	}
754
755	connector->display_info.bpc = 8;
756	connector->display_info.width_mm = panel_info->width_mm;
757	connector->display_info.height_mm = panel_info->height_mm;
758
759	drm_display_info_set_bus_formats(&connector->display_info,
760					 &panel_info->bus_format, 1);
761	connector->display_info.bus_flags = panel_info->bus_flags;
762
763	return panel_info->num_modes;
764}
765
766static const struct drm_panel_funcs nv3052c_funcs = {
767	.prepare	= nv3052c_prepare,
768	.unprepare	= nv3052c_unprepare,
769	.enable		= nv3052c_enable,
770	.disable	= nv3052c_disable,
771	.get_modes	= nv3052c_get_modes,
772};
773
774static int nv3052c_probe(struct spi_device *spi)
775{
776	struct device *dev = &spi->dev;
777	struct nv3052c *priv;
778	int err;
779
780	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
781	if (!priv)
782		return -ENOMEM;
783
784	priv->dev = dev;
785
786	priv->panel_info = of_device_get_match_data(dev);
787	if (!priv->panel_info)
788		return -EINVAL;
789
790	priv->supply = devm_regulator_get(dev, "power");
791	if (IS_ERR(priv->supply))
792		return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n");
793
794	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
795	if (IS_ERR(priv->reset_gpio))
796		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
797
798	err = mipi_dbi_spi_init(spi, &priv->dbi, NULL);
799	if (err)
800		return dev_err_probe(dev, err, "MIPI DBI init failed\n");
801
802	priv->dbi.read_commands = NULL;
803
804	spi_set_drvdata(spi, priv);
805
806	drm_panel_init(&priv->panel, dev, &nv3052c_funcs,
807		       DRM_MODE_CONNECTOR_DPI);
808
809	err = drm_panel_of_backlight(&priv->panel);
810	if (err)
811		return dev_err_probe(dev, err, "Failed to attach backlight\n");
812
813	drm_panel_add(&priv->panel);
814
815	return 0;
816}
817
818static void nv3052c_remove(struct spi_device *spi)
819{
820	struct nv3052c *priv = spi_get_drvdata(spi);
821
822	drm_panel_remove(&priv->panel);
823	drm_panel_disable(&priv->panel);
824	drm_panel_unprepare(&priv->panel);
825}
826
827static const struct drm_display_mode ltk035c5444t_modes[] = {
828	{ /* 60 Hz */
829		.clock = 24000,
830		.hdisplay = 640,
831		.hsync_start = 640 + 96,
832		.hsync_end = 640 + 96 + 16,
833		.htotal = 640 + 96 + 16 + 48,
834		.vdisplay = 480,
835		.vsync_start = 480 + 5,
836		.vsync_end = 480 + 5 + 2,
837		.vtotal = 480 + 5 + 2 + 13,
838		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
839	},
840	{ /* 50 Hz */
841		.clock = 18000,
842		.hdisplay = 640,
843		.hsync_start = 640 + 39,
844		.hsync_end = 640 + 39 + 2,
845		.htotal = 640 + 39 + 2 + 39,
846		.vdisplay = 480,
847		.vsync_start = 480 + 5,
848		.vsync_end = 480 + 5 + 2,
849		.vtotal = 480 + 5 + 2 + 13,
850		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
851	},
852};
853
854static const struct drm_display_mode fs035vg158_modes[] = {
855	{ /* 60 Hz */
856		.clock = 21000,
857		.hdisplay = 640,
858		.hsync_start = 640 + 34,
859		.hsync_end = 640 + 34 + 4,
860		.htotal = 640 + 34 + 4 + 20,
861		.vdisplay = 480,
862		.vsync_start = 480 + 12,
863		.vsync_end = 480 + 12 + 4,
864		.vtotal = 480 + 12 + 4 + 6,
865		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
866	},
867};
868
869static const struct drm_display_mode wl_355608_a8_mode[] = {
870	{
871		.clock = 24000,
872		.hdisplay = 640,
873		.hsync_start = 640 + 64,
874		.hsync_end = 640 + 64 + 20,
875		.htotal = 640 + 64 + 20 + 46,
876		.vdisplay = 480,
877		.vsync_start = 480 + 21,
878		.vsync_end = 480 + 21 + 4,
879		.vtotal = 480 + 21 + 4 + 15,
880		.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
881	},
882};
883
884static const struct nv3052c_panel_info ltk035c5444t_panel_info = {
885	.display_modes = ltk035c5444t_modes,
886	.num_modes = ARRAY_SIZE(ltk035c5444t_modes),
887	.width_mm = 77,
888	.height_mm = 64,
889	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
890	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
891	.panel_regs = ltk035c5444t_panel_regs,
892	.panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs),
893};
894
895static const struct nv3052c_panel_info fs035vg158_panel_info = {
896	.display_modes = fs035vg158_modes,
897	.num_modes = ARRAY_SIZE(fs035vg158_modes),
898	.width_mm = 70,
899	.height_mm = 53,
900	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
901	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
902	.panel_regs = fs035vg158_panel_regs,
903	.panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs),
904};
905
906static const struct nv3052c_panel_info wl_355608_a8_panel_info = {
907	.display_modes = wl_355608_a8_mode,
908	.num_modes = ARRAY_SIZE(wl_355608_a8_mode),
909	.width_mm = 150,
910	.height_mm = 94,
911	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
912	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
913	.panel_regs = wl_355608_a8_panel_regs,
914	.panel_regs_len = ARRAY_SIZE(wl_355608_a8_panel_regs),
915};
916
917static const struct spi_device_id nv3052c_ids[] = {
918	{ "ltk035c5444t", },
919	{ "fs035vg158", },
920	{ "rg35xx-plus-panel", },
921	{ /* sentinel */ }
922};
923MODULE_DEVICE_TABLE(spi, nv3052c_ids);
924
925static const struct of_device_id nv3052c_of_match[] = {
926	{ .compatible = "leadtek,ltk035c5444t", .data = &ltk035c5444t_panel_info },
927	{ .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info },
928	{ .compatible = "anbernic,rg35xx-plus-panel", .data = &wl_355608_a8_panel_info },
929	{ /* sentinel */ }
930};
931MODULE_DEVICE_TABLE(of, nv3052c_of_match);
932
933static struct spi_driver nv3052c_driver = {
934	.driver = {
935		.name = "nv3052c",
936		.of_match_table = nv3052c_of_match,
937	},
938	.id_table = nv3052c_ids,
939	.probe = nv3052c_probe,
940	.remove = nv3052c_remove,
941};
942module_spi_driver(nv3052c_driver);
943
944MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
945MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
946MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com");
947MODULE_DESCRIPTION("NewVision NV3052C IPS LCD panel driver");
948MODULE_LICENSE("GPL v2");