1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Novatek NT35950 DriverIC panels driver
4 *
5 * Copyright (c) 2021 AngeloGioacchino Del Regno
6 *                    <angelogioacchino.delregno@somainline.org>
7 */
8#include <linux/delay.h>
9#include <linux/gpio/consumer.h>
10#include <linux/module.h>
11#include <linux/of.h>
12#include <linux/of_graph.h>
13#include <linux/regulator/consumer.h>
14
15#include <drm/drm_connector.h>
16#include <drm/drm_crtc.h>
17#include <drm/drm_mipi_dsi.h>
18#include <drm/drm_modes.h>
19#include <drm/drm_panel.h>
20
21#define MCS_CMD_MAUCCTR			0xf0 /* Manufacturer command enable */
22#define MCS_PARAM_SCALER_FUNCTION	0x58 /* Scale-up function */
23#define MCS_PARAM_SCALEUP_MODE		0xc9
24 #define MCS_SCALEUP_SIMPLE		0x0
25 #define MCS_SCALEUP_BILINEAR		BIT(0)
26 #define MCS_SCALEUP_DUPLICATE		(BIT(0) | BIT(4))
27
28/* VESA Display Stream Compression param */
29#define MCS_PARAM_VESA_DSC_ON		0x03
30
31/* Data Compression mode */
32#define MCS_PARAM_DATA_COMPRESSION	0x90
33 #define MCS_DATA_COMPRESSION_NONE	0x00
34 #define MCS_DATA_COMPRESSION_FBC	0x02
35 #define MCS_DATA_COMPRESSION_DSC	0x03
36
37/* Display Output control */
38#define MCS_PARAM_DISP_OUTPUT_CTRL	0xb4
39 #define MCS_DISP_OUT_SRAM_EN		BIT(0)
40 #define MCS_DISP_OUT_VIDEO_MODE	BIT(4)
41
42/* VESA Display Stream Compression setting */
43#define MCS_PARAM_VESA_DSC_SETTING	0xc0
44
45/* SubPixel Rendering (SPR) */
46#define MCS_PARAM_SPR_EN		0xe3
47#define MCS_PARAM_SPR_MODE		0xef
48 #define MCS_SPR_MODE_YYG_RAINBOW_RGB	0x01
49
50#define NT35950_VREG_MAX		4
51
52struct nt35950 {
53	struct drm_panel panel;
54	struct drm_connector *connector;
55	struct mipi_dsi_device *dsi[2];
56	struct regulator_bulk_data vregs[NT35950_VREG_MAX];
57	struct gpio_desc *reset_gpio;
58	const struct nt35950_panel_desc *desc;
59
60	int cur_mode;
61	u8 last_page;
62	bool prepared;
63};
64
65struct nt35950_panel_mode {
66	const struct drm_display_mode mode;
67
68	bool enable_sram;
69	bool is_video_mode;
70	u8 scaler_on;
71	u8 scaler_mode;
72	u8 compression;
73	u8 spr_en;
74	u8 spr_mode;
75};
76
77struct nt35950_panel_desc {
78	const char *model_name;
79	const struct mipi_dsi_device_info dsi_info;
80	const struct nt35950_panel_mode *mode_data;
81
82	bool is_dual_dsi;
83	u8 num_lanes;
84	u8 num_modes;
85};
86
87static inline struct nt35950 *to_nt35950(struct drm_panel *panel)
88{
89	return container_of(panel, struct nt35950, panel);
90}
91
92static void nt35950_reset(struct nt35950 *nt)
93{
94	gpiod_set_value_cansleep(nt->reset_gpio, 1);
95	usleep_range(12000, 13000);
96	gpiod_set_value_cansleep(nt->reset_gpio, 0);
97	usleep_range(300, 400);
98	gpiod_set_value_cansleep(nt->reset_gpio, 1);
99	usleep_range(12000, 13000);
100}
101
102/*
103 * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page
104 * @nt:   Main driver structure
105 * @page: Page number (0-7)
106 *
107 * Return: Number of transferred bytes or negative number on error
108 */
109static int nt35950_set_cmd2_page(struct nt35950 *nt, u8 page)
110{
111	const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52,
112				      0x08, page };
113	int ret;
114
115	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], mauc_cmd2_page,
116					ARRAY_SIZE(mauc_cmd2_page));
117	if (ret < 0)
118		return ret;
119
120	nt->last_page = page;
121	return 0;
122}
123
124/*
125 * nt35950_set_data_compression - Set data compression mode
126 * @nt:        Main driver structure
127 * @comp_mode: Compression mode
128 *
129 * Return: Number of transferred bytes or negative number on error
130 */
131static int nt35950_set_data_compression(struct nt35950 *nt, u8 comp_mode)
132{
133	u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode };
134	u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode };
135	u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 };
136	u8 last_page = nt->last_page;
137	int ret;
138
139	/* Set CMD2 Page 0 if we're not there yet */
140	if (last_page != 0) {
141		ret = nt35950_set_cmd2_page(nt, 0);
142		if (ret < 0)
143			return ret;
144	}
145
146	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_data_compression,
147					ARRAY_SIZE(cmd_data_compression));
148	if (ret < 0)
149		return ret;
150
151	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_on,
152					ARRAY_SIZE(cmd_vesa_dsc_on));
153	if (ret < 0)
154		return ret;
155
156	/* Set the vesa dsc setting on Page 4 */
157	ret = nt35950_set_cmd2_page(nt, 4);
158	if (ret < 0)
159		return ret;
160
161	/* Display Stream Compression setting, always 0x03 */
162	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_setting,
163					ARRAY_SIZE(cmd_vesa_dsc_setting));
164	if (ret < 0)
165		return ret;
166
167	/* Get back to the previously set page */
168	return nt35950_set_cmd2_page(nt, last_page);
169}
170
171/*
172 * nt35950_set_scaler - Enable/disable resolution upscaling
173 * @nt:        Main driver structure
174 * @scale_up:  Scale up function control
175 *
176 * Return: Number of transferred bytes or negative number on error
177 */
178static int nt35950_set_scaler(struct nt35950 *nt, u8 scale_up)
179{
180	u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up };
181
182	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
183					 ARRAY_SIZE(cmd_scaler));
184}
185
186/*
187 * nt35950_set_scale_mode - Resolution upscaling mode
188 * @nt:   Main driver structure
189 * @mode: Scaler mode (MCS_DATA_COMPRESSION_*)
190 *
191 * Return: Number of transferred bytes or negative number on error
192 */
193static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode)
194{
195	u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode };
196
197	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler,
198					 ARRAY_SIZE(cmd_scaler));
199}
200
201/*
202 * nt35950_inject_black_image - Display a completely black image
203 * @nt:   Main driver structure
204 *
205 * After IC setup, the attached panel may show random data
206 * due to driveric behavior changes (resolution, compression,
207 * scaling, etc). This function, called after parameters setup,
208 * makes the driver ic to output a completely black image to
209 * the display.
210 * It makes sense to push a black image before sending the sleep-out
211 * and display-on commands.
212 *
213 * Return: Number of transferred bytes or negative number on error
214 */
215static int nt35950_inject_black_image(struct nt35950 *nt)
216{
217	const u8 cmd0_black_img[] = { 0x6f, 0x01 };
218	const u8 cmd1_black_img[] = { 0xf3, 0x10 };
219	u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 };
220	int ret;
221
222	/* Enable test command */
223	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
224	if (ret < 0)
225		return ret;
226
227	/* Send a black image */
228	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd0_black_img,
229					ARRAY_SIZE(cmd0_black_img));
230	if (ret < 0)
231		return ret;
232	ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd1_black_img,
233					ARRAY_SIZE(cmd1_black_img));
234	if (ret < 0)
235		return ret;
236
237	/* Disable test command */
238	cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00;
239	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test));
240}
241
242/*
243 * nt35950_set_dispout - Set Display Output register parameters
244 * @nt:    Main driver structure
245 *
246 * Return: Number of transferred bytes or negative number on error
247 */
248static int nt35950_set_dispout(struct nt35950 *nt)
249{
250	u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };
251	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
252
253	if (mode_data[nt->cur_mode].is_video_mode)
254		cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE;
255	if (mode_data[nt->cur_mode].enable_sram)
256		cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;
257
258	return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_dispout,
259					 ARRAY_SIZE(cmd_dispout));
260}
261
262static int nt35950_get_current_mode(struct nt35950 *nt)
263{
264	struct drm_connector *connector = nt->connector;
265	struct drm_crtc_state *crtc_state;
266	int i;
267
268	/* Return the default (first) mode if no info available yet */
269	if (!connector->state || !connector->state->crtc)
270		return 0;
271
272	crtc_state = connector->state->crtc->state;
273
274	for (i = 0; i < nt->desc->num_modes; i++) {
275		if (drm_mode_match(&crtc_state->mode,
276				   &nt->desc->mode_data[i].mode,
277				   DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK))
278			return i;
279	}
280
281	return 0;
282}
283
284static int nt35950_on(struct nt35950 *nt)
285{
286	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
287	struct mipi_dsi_device *dsi = nt->dsi[0];
288	struct device *dev = &dsi->dev;
289	int ret;
290
291	nt->cur_mode = nt35950_get_current_mode(nt);
292	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
293	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
294
295	ret = nt35950_set_cmd2_page(nt, 0);
296	if (ret < 0)
297		return ret;
298
299	ret = nt35950_set_data_compression(nt, mode_data[nt->cur_mode].compression);
300	if (ret < 0)
301		return ret;
302
303	ret = nt35950_set_scale_mode(nt, mode_data[nt->cur_mode].scaler_mode);
304	if (ret < 0)
305		return ret;
306
307	ret = nt35950_set_scaler(nt, mode_data[nt->cur_mode].scaler_on);
308	if (ret < 0)
309		return ret;
310
311	ret = nt35950_set_dispout(nt);
312	if (ret < 0)
313		return ret;
314
315	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
316	if (ret < 0) {
317		dev_err(dev, "Failed to set tear on: %d\n", ret);
318		return ret;
319	}
320
321	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
322	if (ret < 0) {
323		dev_err(dev, "Failed to set tear scanline: %d\n", ret);
324		return ret;
325	}
326
327	/* CMD2 Page 1 */
328	ret = nt35950_set_cmd2_page(nt, 1);
329	if (ret < 0)
330		return ret;
331
332	/* Unknown command */
333	mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x88, 0x88);
334
335	/* CMD2 Page 7 */
336	ret = nt35950_set_cmd2_page(nt, 7);
337	if (ret < 0)
338		return ret;
339
340	/* Enable SubPixel Rendering */
341	mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_EN, 0x01);
342
343	/* SPR Mode: YYG Rainbow-RGB */
344	mipi_dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_MODE, MCS_SPR_MODE_YYG_RAINBOW_RGB);
345
346	/* CMD3 */
347	ret = nt35950_inject_black_image(nt);
348	if (ret < 0)
349		return ret;
350
351	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
352	if (ret < 0)
353		return ret;
354	msleep(120);
355
356	ret = mipi_dsi_dcs_set_display_on(dsi);
357	if (ret < 0)
358		return ret;
359	msleep(120);
360
361	nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM;
362	nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;
363
364	return 0;
365}
366
367static int nt35950_off(struct nt35950 *nt)
368{
369	struct device *dev = &nt->dsi[0]->dev;
370	int ret;
371
372	ret = mipi_dsi_dcs_set_display_off(nt->dsi[0]);
373	if (ret < 0) {
374		dev_err(dev, "Failed to set display off: %d\n", ret);
375		goto set_lpm;
376	}
377	usleep_range(10000, 11000);
378
379	ret = mipi_dsi_dcs_enter_sleep_mode(nt->dsi[0]);
380	if (ret < 0) {
381		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
382		goto set_lpm;
383	}
384	msleep(150);
385
386set_lpm:
387	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
388	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
389
390	return 0;
391}
392
393static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)
394{
395	int ret;
396
397	nt->vregs[0].supply = "vddio";
398	nt->vregs[1].supply = "avdd";
399	nt->vregs[2].supply = "avee";
400	nt->vregs[3].supply = "dvdd";
401	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->vregs),
402				      nt->vregs);
403	if (ret < 0)
404		return ret;
405
406	ret = regulator_is_supported_voltage(nt->vregs[0].consumer,
407					     1750000, 1950000);
408	if (!ret)
409		return -EINVAL;
410	ret = regulator_is_supported_voltage(nt->vregs[1].consumer,
411					     5200000, 5900000);
412	if (!ret)
413		return -EINVAL;
414	/* AVEE is negative: -5.90V to -5.20V */
415	ret = regulator_is_supported_voltage(nt->vregs[2].consumer,
416					     5200000, 5900000);
417	if (!ret)
418		return -EINVAL;
419
420	ret = regulator_is_supported_voltage(nt->vregs[3].consumer,
421					     1300000, 1400000);
422	if (!ret)
423		return -EINVAL;
424
425	return 0;
426}
427
428static int nt35950_prepare(struct drm_panel *panel)
429{
430	struct nt35950 *nt = to_nt35950(panel);
431	struct device *dev = &nt->dsi[0]->dev;
432	int ret;
433
434	if (nt->prepared)
435		return 0;
436
437	ret = regulator_enable(nt->vregs[0].consumer);
438	if (ret)
439		return ret;
440	usleep_range(2000, 5000);
441
442	ret = regulator_enable(nt->vregs[3].consumer);
443	if (ret)
444		goto end;
445	usleep_range(15000, 18000);
446
447	ret = regulator_enable(nt->vregs[1].consumer);
448	if (ret)
449		goto end;
450
451	ret = regulator_enable(nt->vregs[2].consumer);
452	if (ret)
453		goto end;
454	usleep_range(12000, 13000);
455
456	nt35950_reset(nt);
457
458	ret = nt35950_on(nt);
459	if (ret < 0) {
460		dev_err(dev, "Failed to initialize panel: %d\n", ret);
461		goto end;
462	}
463	nt->prepared = true;
464
465end:
466	if (ret < 0) {
467		regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);
468		return ret;
469	}
470
471	return 0;
472}
473
474static int nt35950_unprepare(struct drm_panel *panel)
475{
476	struct nt35950 *nt = to_nt35950(panel);
477	struct device *dev = &nt->dsi[0]->dev;
478	int ret;
479
480	if (!nt->prepared)
481		return 0;
482
483	ret = nt35950_off(nt);
484	if (ret < 0)
485		dev_err(dev, "Failed to deinitialize panel: %d\n", ret);
486
487	gpiod_set_value_cansleep(nt->reset_gpio, 0);
488	regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);
489
490	nt->prepared = false;
491	return 0;
492}
493
494static int nt35950_get_modes(struct drm_panel *panel,
495			     struct drm_connector *connector)
496{
497	struct nt35950 *nt = to_nt35950(panel);
498	int i;
499
500	for (i = 0; i < nt->desc->num_modes; i++) {
501		struct drm_display_mode *mode;
502
503		mode = drm_mode_duplicate(connector->dev,
504					  &nt->desc->mode_data[i].mode);
505		if (!mode)
506			return -ENOMEM;
507
508		drm_mode_set_name(mode);
509
510		mode->type |= DRM_MODE_TYPE_DRIVER;
511		if (nt->desc->num_modes == 1)
512			mode->type |= DRM_MODE_TYPE_PREFERRED;
513
514		drm_mode_probed_add(connector, mode);
515	}
516
517	connector->display_info.bpc = 8;
518	connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm;
519	connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm;
520	nt->connector = connector;
521
522	return nt->desc->num_modes;
523}
524
525static const struct drm_panel_funcs nt35950_panel_funcs = {
526	.prepare = nt35950_prepare,
527	.unprepare = nt35950_unprepare,
528	.get_modes = nt35950_get_modes,
529};
530
531static int nt35950_probe(struct mipi_dsi_device *dsi)
532{
533	struct device *dev = &dsi->dev;
534	struct device_node *dsi_r;
535	struct mipi_dsi_host *dsi_r_host;
536	struct nt35950 *nt;
537	const struct mipi_dsi_device_info *info;
538	int i, num_dsis = 1, ret;
539
540	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
541	if (!nt)
542		return -ENOMEM;
543
544	ret = nt35950_sharp_init_vregs(nt, dev);
545	if (ret)
546		return dev_err_probe(dev, ret, "Regulator init failure.\n");
547
548	nt->desc = of_device_get_match_data(dev);
549	if (!nt->desc)
550		return -ENODEV;
551
552	nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
553	if (IS_ERR(nt->reset_gpio)) {
554		return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),
555				     "Failed to get reset gpio\n");
556	}
557
558	/* If the panel is connected on two DSIs then DSI0 left, DSI1 right */
559	if (nt->desc->is_dual_dsi) {
560		info = &nt->desc->dsi_info;
561		dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
562		if (!dsi_r) {
563			dev_err(dev, "Cannot get secondary DSI node.\n");
564			return -ENODEV;
565		}
566		dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);
567		of_node_put(dsi_r);
568		if (!dsi_r_host) {
569			dev_err(dev, "Cannot get secondary DSI host\n");
570			return -EPROBE_DEFER;
571		}
572
573		nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);
574		if (!nt->dsi[1]) {
575			dev_err(dev, "Cannot get secondary DSI node\n");
576			return -ENODEV;
577		}
578		num_dsis++;
579	}
580
581	nt->dsi[0] = dsi;
582	mipi_dsi_set_drvdata(dsi, nt);
583
584	drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs,
585		       DRM_MODE_CONNECTOR_DSI);
586
587	ret = drm_panel_of_backlight(&nt->panel);
588	if (ret) {
589		if (num_dsis == 2)
590			mipi_dsi_device_unregister(nt->dsi[1]);
591
592		return dev_err_probe(dev, ret, "Failed to get backlight\n");
593	}
594
595	drm_panel_add(&nt->panel);
596
597	for (i = 0; i < num_dsis; i++) {
598		nt->dsi[i]->lanes = nt->desc->num_lanes;
599		nt->dsi[i]->format = MIPI_DSI_FMT_RGB888;
600
601		nt->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
602					 MIPI_DSI_MODE_LPM;
603
604		if (nt->desc->mode_data[0].is_video_mode)
605			nt->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO;
606
607		ret = mipi_dsi_attach(nt->dsi[i]);
608		if (ret < 0) {
609			/* If we fail to attach to either host, we're done */
610			if (num_dsis == 2)
611				mipi_dsi_device_unregister(nt->dsi[1]);
612
613			return dev_err_probe(dev, ret,
614					     "Cannot attach to DSI%d host.\n", i);
615		}
616	}
617
618	/* Make sure to set RESX LOW before starting the power-on sequence */
619	gpiod_set_value_cansleep(nt->reset_gpio, 0);
620	return 0;
621}
622
623static void nt35950_remove(struct mipi_dsi_device *dsi)
624{
625	struct nt35950 *nt = mipi_dsi_get_drvdata(dsi);
626	int ret;
627
628	ret = mipi_dsi_detach(nt->dsi[0]);
629	if (ret < 0)
630		dev_err(&dsi->dev,
631			"Failed to detach from DSI0 host: %d\n", ret);
632
633	if (nt->dsi[1]) {
634		ret = mipi_dsi_detach(nt->dsi[1]);
635		if (ret < 0)
636			dev_err(&dsi->dev,
637				"Failed to detach from DSI1 host: %d\n", ret);
638		mipi_dsi_device_unregister(nt->dsi[1]);
639	}
640
641	drm_panel_remove(&nt->panel);
642}
643
644static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = {
645	{
646		/* 1920x1080 60Hz no compression */
647		.mode = {
648			.clock = 214537,
649			.hdisplay = 1080,
650			.hsync_start = 1080 + 400,
651			.hsync_end = 1080 + 400 + 40,
652			.htotal = 1080 + 400 + 40 + 300,
653			.vdisplay = 1920,
654			.vsync_start = 1920 + 12,
655			.vsync_end = 1920 + 12 + 2,
656			.vtotal = 1920 + 12 + 2 + 10,
657			.width_mm = 68,
658			.height_mm = 121,
659		},
660		.compression = MCS_DATA_COMPRESSION_NONE,
661		.enable_sram = true,
662		.is_video_mode = false,
663		.scaler_on = 1,
664		.scaler_mode = MCS_SCALEUP_DUPLICATE,
665	},
666	/* TODO: Add 2160x3840 60Hz when DSC is supported */
667};
668
669static const struct nt35950_panel_desc sharp_ls055d1sx04 = {
670	.model_name = "Sharp LS055D1SX04",
671	.dsi_info = {
672		.type = "LS055D1SX04",
673		.channel = 0,
674		.node = NULL,
675	},
676	.mode_data = sharp_ls055d1sx04_modes,
677	.num_modes = ARRAY_SIZE(sharp_ls055d1sx04_modes),
678	.is_dual_dsi = true,
679	.num_lanes = 4,
680};
681
682static const struct of_device_id nt35950_of_match[] = {
683	{ .compatible = "sharp,ls055d1sx04", .data = &sharp_ls055d1sx04 },
684	{  }
685};
686MODULE_DEVICE_TABLE(of, nt35950_of_match);
687
688static struct mipi_dsi_driver nt35950_driver = {
689	.probe = nt35950_probe,
690	.remove = nt35950_remove,
691	.driver = {
692		.name = "panel-novatek-nt35950",
693		.of_match_table = nt35950_of_match,
694	},
695};
696module_mipi_dsi_driver(nt35950_driver);
697
698MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");
699MODULE_DESCRIPTION("Novatek NT35950 DriverIC panels driver");
700MODULE_LICENSE("GPL v2");
701