Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
436static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
437{
438	return container_of(panel, struct nv3052c, panel);
439}
440
441static 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(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(priv->reset_gpio, 1);
458	usleep_range(10, 1000);
459	gpiod_set_value_cansleep(priv->reset_gpio, 0);
460	usleep_range(5000, 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
480err_disable_regulator:
481	regulator_disable(priv->supply);
482	return err;
483}
484
485static 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(priv->reset_gpio, 1);
496	regulator_disable(priv->supply);
497
498	return 0;
499}
500
501static 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(120);
516	}
517
518	return 0;
519}
520
521static 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
536static 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(connector->dev,
546					  &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(&connector->display_info,
564					 &panel_info->bus_format, 1);
565	connector->display_info.bus_flags = panel_info->bus_flags;
566
567	return panel_info->num_modes;
568}
569
570static 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
578static 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, 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, "power");
595	if (IS_ERR(priv->supply))
596		return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n");
597
598	priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
599	if (IS_ERR(priv->reset_gpio))
600		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
601
602	err = mipi_dbi_spi_init(spi, &priv->dbi, NULL);
603	if (err)
604		return dev_err_probe(dev, err, "MIPI DBI init failed\n");
605
606	priv->dbi.read_commands = NULL;
607
608	spi_set_drvdata(spi, priv);
609
610	drm_panel_init(&priv->panel, dev, &nv3052c_funcs,
611		       DRM_MODE_CONNECTOR_DPI);
612
613	err = drm_panel_of_backlight(&priv->panel);
614	if (err)
615		return dev_err_probe(dev, err, "Failed to attach backlight\n");
616
617	drm_panel_add(&priv->panel);
618
619	return 0;
620}
621
622static void nv3052c_remove(struct spi_device *spi)
623{
624	struct nv3052c *priv = spi_get_drvdata(spi);
625
626	drm_panel_remove(&priv->panel);
627	drm_panel_disable(&priv->panel);
628	drm_panel_unprepare(&priv->panel);
629}
630
631static 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
658static 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
673static 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
684static 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
695static const struct spi_device_id nv3052c_ids[] = {
696	{ "ltk035c5444t", },
697	{ "fs035vg158", },
698	{ /* sentinel */ }
699};
700MODULE_DEVICE_TABLE(spi, nv3052c_ids);
701
702static const struct of_device_id nv3052c_of_match[] = {
703	{ .compatible = "leadtek,ltk035c5444t", .data = &ltk035c5444t_panel_info },
704	{ .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info },
705	{ /* sentinel */ }
706};
707MODULE_DEVICE_TABLE(of, nv3052c_of_match);
708
709static 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};
718module_spi_driver(nv3052c_driver);
719
720MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
721MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
722MODULE_LICENSE("GPL v2");