panel-simple.c revision 0a2288c06aab73c966e82045c8f20b0e713baf2a
1/*
2 * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include <linux/backlight.h>
25#include <linux/gpio/consumer.h>
26#include <linux/module.h>
27#include <linux/of_platform.h>
28#include <linux/platform_device.h>
29#include <linux/regulator/consumer.h>
30
31#include <drm/drmP.h>
32#include <drm/drm_crtc.h>
33#include <drm/drm_mipi_dsi.h>
34#include <drm/drm_panel.h>
35
36struct panel_desc {
37	const struct drm_display_mode *modes;
38	unsigned int num_modes;
39
40	unsigned int bpc;
41
42	struct {
43		unsigned int width;
44		unsigned int height;
45	} size;
46};
47
48struct panel_simple {
49	struct drm_panel base;
50	bool enabled;
51
52	const struct panel_desc *desc;
53
54	struct backlight_device *backlight;
55	struct regulator *supply;
56	struct i2c_adapter *ddc;
57
58	struct gpio_desc *enable_gpio;
59};
60
61static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
62{
63	return container_of(panel, struct panel_simple, base);
64}
65
66static int panel_simple_get_fixed_modes(struct panel_simple *panel)
67{
68	struct drm_connector *connector = panel->base.connector;
69	struct drm_device *drm = panel->base.drm;
70	struct drm_display_mode *mode;
71	unsigned int i, num = 0;
72
73	if (!panel->desc)
74		return 0;
75
76	for (i = 0; i < panel->desc->num_modes; i++) {
77		const struct drm_display_mode *m = &panel->desc->modes[i];
78
79		mode = drm_mode_duplicate(drm, m);
80		if (!mode) {
81			dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
82				m->hdisplay, m->vdisplay, m->vrefresh);
83			continue;
84		}
85
86		drm_mode_set_name(mode);
87
88		drm_mode_probed_add(connector, mode);
89		num++;
90	}
91
92	connector->display_info.bpc = panel->desc->bpc;
93	connector->display_info.width_mm = panel->desc->size.width;
94	connector->display_info.height_mm = panel->desc->size.height;
95
96	return num;
97}
98
99static int panel_simple_disable(struct drm_panel *panel)
100{
101	struct panel_simple *p = to_panel_simple(panel);
102
103	if (!p->enabled)
104		return 0;
105
106	if (p->backlight) {
107		p->backlight->props.power = FB_BLANK_POWERDOWN;
108		backlight_update_status(p->backlight);
109	}
110
111	if (p->enable_gpio)
112		gpiod_set_value_cansleep(p->enable_gpio, 0);
113
114	regulator_disable(p->supply);
115	p->enabled = false;
116
117	return 0;
118}
119
120static int panel_simple_enable(struct drm_panel *panel)
121{
122	struct panel_simple *p = to_panel_simple(panel);
123	int err;
124
125	if (p->enabled)
126		return 0;
127
128	err = regulator_enable(p->supply);
129	if (err < 0) {
130		dev_err(panel->dev, "failed to enable supply: %d\n", err);
131		return err;
132	}
133
134	if (p->enable_gpio)
135		gpiod_set_value_cansleep(p->enable_gpio, 1);
136
137	if (p->backlight) {
138		p->backlight->props.power = FB_BLANK_UNBLANK;
139		backlight_update_status(p->backlight);
140	}
141
142	p->enabled = true;
143
144	return 0;
145}
146
147static int panel_simple_get_modes(struct drm_panel *panel)
148{
149	struct panel_simple *p = to_panel_simple(panel);
150	int num = 0;
151
152	/* probe EDID if a DDC bus is available */
153	if (p->ddc) {
154		struct edid *edid = drm_get_edid(panel->connector, p->ddc);
155		drm_mode_connector_update_edid_property(panel->connector, edid);
156		if (edid) {
157			num += drm_add_edid_modes(panel->connector, edid);
158			kfree(edid);
159		}
160	}
161
162	/* add hard-coded panel modes */
163	num += panel_simple_get_fixed_modes(p);
164
165	return num;
166}
167
168static const struct drm_panel_funcs panel_simple_funcs = {
169	.disable = panel_simple_disable,
170	.enable = panel_simple_enable,
171	.get_modes = panel_simple_get_modes,
172};
173
174static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
175{
176	struct device_node *backlight, *ddc;
177	struct panel_simple *panel;
178	int err;
179
180	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
181	if (!panel)
182		return -ENOMEM;
183
184	panel->enabled = false;
185	panel->desc = desc;
186
187	panel->supply = devm_regulator_get(dev, "power");
188	if (IS_ERR(panel->supply))
189		return PTR_ERR(panel->supply);
190
191	panel->enable_gpio = devm_gpiod_get(dev, "enable");
192	if (IS_ERR(panel->enable_gpio)) {
193		err = PTR_ERR(panel->enable_gpio);
194		if (err != -ENOENT) {
195			dev_err(dev, "failed to request GPIO: %d\n", err);
196			return err;
197		}
198
199		panel->enable_gpio = NULL;
200	} else {
201		err = gpiod_direction_output(panel->enable_gpio, 0);
202		if (err < 0) {
203			dev_err(dev, "failed to setup GPIO: %d\n", err);
204			return err;
205		}
206	}
207
208	backlight = of_parse_phandle(dev->of_node, "backlight", 0);
209	if (backlight) {
210		panel->backlight = of_find_backlight_by_node(backlight);
211		of_node_put(backlight);
212
213		if (!panel->backlight)
214			return -EPROBE_DEFER;
215	}
216
217	ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
218	if (ddc) {
219		panel->ddc = of_find_i2c_adapter_by_node(ddc);
220		of_node_put(ddc);
221
222		if (!panel->ddc) {
223			err = -EPROBE_DEFER;
224			goto free_backlight;
225		}
226	}
227
228	drm_panel_init(&panel->base);
229	panel->base.dev = dev;
230	panel->base.funcs = &panel_simple_funcs;
231
232	err = drm_panel_add(&panel->base);
233	if (err < 0)
234		goto free_ddc;
235
236	dev_set_drvdata(dev, panel);
237
238	return 0;
239
240free_ddc:
241	if (panel->ddc)
242		put_device(&panel->ddc->dev);
243free_backlight:
244	if (panel->backlight)
245		put_device(&panel->backlight->dev);
246
247	return err;
248}
249
250static int panel_simple_remove(struct device *dev)
251{
252	struct panel_simple *panel = dev_get_drvdata(dev);
253
254	drm_panel_detach(&panel->base);
255	drm_panel_remove(&panel->base);
256
257	panel_simple_disable(&panel->base);
258
259	if (panel->ddc)
260		put_device(&panel->ddc->dev);
261
262	if (panel->backlight)
263		put_device(&panel->backlight->dev);
264
265	return 0;
266}
267
268static void panel_simple_shutdown(struct device *dev)
269{
270	struct panel_simple *panel = dev_get_drvdata(dev);
271
272	panel_simple_disable(&panel->base);
273}
274
275static const struct drm_display_mode auo_b101aw03_mode = {
276	.clock = 51450,
277	.hdisplay = 1024,
278	.hsync_start = 1024 + 156,
279	.hsync_end = 1024 + 156 + 8,
280	.htotal = 1024 + 156 + 8 + 156,
281	.vdisplay = 600,
282	.vsync_start = 600 + 16,
283	.vsync_end = 600 + 16 + 6,
284	.vtotal = 600 + 16 + 6 + 16,
285	.vrefresh = 60,
286};
287
288static const struct panel_desc auo_b101aw03 = {
289	.modes = &auo_b101aw03_mode,
290	.num_modes = 1,
291	.bpc = 6,
292	.size = {
293		.width = 223,
294		.height = 125,
295	},
296};
297
298static const struct drm_display_mode auo_b133xtn01_mode = {
299	.clock = 69500,
300	.hdisplay = 1366,
301	.hsync_start = 1366 + 48,
302	.hsync_end = 1366 + 48 + 32,
303	.htotal = 1366 + 48 + 32 + 20,
304	.vdisplay = 768,
305	.vsync_start = 768 + 3,
306	.vsync_end = 768 + 3 + 6,
307	.vtotal = 768 + 3 + 6 + 13,
308	.vrefresh = 60,
309};
310
311static const struct panel_desc auo_b133xtn01 = {
312	.modes = &auo_b133xtn01_mode,
313	.num_modes = 1,
314	.bpc = 6,
315	.size = {
316		.width = 293,
317		.height = 165,
318	},
319};
320
321static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
322	.clock = 72070,
323	.hdisplay = 1366,
324	.hsync_start = 1366 + 58,
325	.hsync_end = 1366 + 58 + 58,
326	.htotal = 1366 + 58 + 58 + 58,
327	.vdisplay = 768,
328	.vsync_start = 768 + 4,
329	.vsync_end = 768 + 4 + 4,
330	.vtotal = 768 + 4 + 4 + 4,
331	.vrefresh = 60,
332};
333
334static const struct panel_desc chunghwa_claa101wa01a = {
335	.modes = &chunghwa_claa101wa01a_mode,
336	.num_modes = 1,
337	.bpc = 6,
338	.size = {
339		.width = 220,
340		.height = 120,
341	},
342};
343
344static const struct drm_display_mode chunghwa_claa101wb01_mode = {
345	.clock = 69300,
346	.hdisplay = 1366,
347	.hsync_start = 1366 + 48,
348	.hsync_end = 1366 + 48 + 32,
349	.htotal = 1366 + 48 + 32 + 20,
350	.vdisplay = 768,
351	.vsync_start = 768 + 16,
352	.vsync_end = 768 + 16 + 8,
353	.vtotal = 768 + 16 + 8 + 16,
354	.vrefresh = 60,
355};
356
357static const struct panel_desc chunghwa_claa101wb01 = {
358	.modes = &chunghwa_claa101wb01_mode,
359	.num_modes = 1,
360	.bpc = 6,
361	.size = {
362		.width = 223,
363		.height = 125,
364	},
365};
366
367static const struct drm_display_mode edt_et057090dhu_mode = {
368	.clock = 25175,
369	.hdisplay = 640,
370	.hsync_start = 640 + 16,
371	.hsync_end = 640 + 16 + 30,
372	.htotal = 640 + 16 + 30 + 114,
373	.vdisplay = 480,
374	.vsync_start = 480 + 10,
375	.vsync_end = 480 + 10 + 3,
376	.vtotal = 480 + 10 + 3 + 32,
377	.vrefresh = 60,
378	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
379};
380
381static const struct panel_desc edt_et057090dhu = {
382	.modes = &edt_et057090dhu_mode,
383	.num_modes = 1,
384	.bpc = 6,
385	.size = {
386		.width = 115,
387		.height = 86,
388	},
389};
390
391static const struct drm_display_mode edt_etm0700g0dh6_mode = {
392	.clock = 33260,
393	.hdisplay = 800,
394	.hsync_start = 800 + 40,
395	.hsync_end = 800 + 40 + 128,
396	.htotal = 800 + 40 + 128 + 88,
397	.vdisplay = 480,
398	.vsync_start = 480 + 10,
399	.vsync_end = 480 + 10 + 2,
400	.vtotal = 480 + 10 + 2 + 33,
401	.vrefresh = 60,
402	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
403};
404
405static const struct panel_desc edt_etm0700g0dh6 = {
406	.modes = &edt_etm0700g0dh6_mode,
407	.num_modes = 1,
408	.bpc = 6,
409	.size = {
410		.width = 152,
411		.height = 91,
412	},
413};
414
415static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
416	.clock = 32260,
417	.hdisplay = 800,
418	.hsync_start = 800 + 168,
419	.hsync_end = 800 + 168 + 64,
420	.htotal = 800 + 168 + 64 + 88,
421	.vdisplay = 480,
422	.vsync_start = 480 + 37,
423	.vsync_end = 480 + 37 + 2,
424	.vtotal = 480 + 37 + 2 + 8,
425	.vrefresh = 60,
426};
427
428static const struct panel_desc foxlink_fl500wvr00_a0t = {
429	.modes = &foxlink_fl500wvr00_a0t_mode,
430	.num_modes = 1,
431	.size = {
432		.width = 108,
433		.height = 65,
434	},
435};
436
437static const struct drm_display_mode innolux_n116bge_mode = {
438	.clock = 71000,
439	.hdisplay = 1366,
440	.hsync_start = 1366 + 64,
441	.hsync_end = 1366 + 64 + 6,
442	.htotal = 1366 + 64 + 6 + 64,
443	.vdisplay = 768,
444	.vsync_start = 768 + 8,
445	.vsync_end = 768 + 8 + 4,
446	.vtotal = 768 + 8 + 4 + 8,
447	.vrefresh = 60,
448	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
449};
450
451static const struct panel_desc innolux_n116bge = {
452	.modes = &innolux_n116bge_mode,
453	.num_modes = 1,
454	.bpc = 6,
455	.size = {
456		.width = 256,
457		.height = 144,
458	},
459};
460
461static const struct drm_display_mode innolux_n156bge_l21_mode = {
462	.clock = 69300,
463	.hdisplay = 1366,
464	.hsync_start = 1366 + 16,
465	.hsync_end = 1366 + 16 + 34,
466	.htotal = 1366 + 16 + 34 + 50,
467	.vdisplay = 768,
468	.vsync_start = 768 + 2,
469	.vsync_end = 768 + 2 + 6,
470	.vtotal = 768 + 2 + 6 + 12,
471	.vrefresh = 60,
472};
473
474static const struct panel_desc innolux_n156bge_l21 = {
475	.modes = &innolux_n156bge_l21_mode,
476	.num_modes = 1,
477	.bpc = 6,
478	.size = {
479		.width = 344,
480		.height = 193,
481	},
482};
483
484static const struct drm_display_mode lg_lp129qe_mode = {
485	.clock = 285250,
486	.hdisplay = 2560,
487	.hsync_start = 2560 + 48,
488	.hsync_end = 2560 + 48 + 32,
489	.htotal = 2560 + 48 + 32 + 80,
490	.vdisplay = 1700,
491	.vsync_start = 1700 + 3,
492	.vsync_end = 1700 + 3 + 10,
493	.vtotal = 1700 + 3 + 10 + 36,
494	.vrefresh = 60,
495};
496
497static const struct panel_desc lg_lp129qe = {
498	.modes = &lg_lp129qe_mode,
499	.num_modes = 1,
500	.bpc = 8,
501	.size = {
502		.width = 272,
503		.height = 181,
504	},
505};
506
507static const struct drm_display_mode samsung_ltn101nt05_mode = {
508	.clock = 54030,
509	.hdisplay = 1024,
510	.hsync_start = 1024 + 24,
511	.hsync_end = 1024 + 24 + 136,
512	.htotal = 1024 + 24 + 136 + 160,
513	.vdisplay = 600,
514	.vsync_start = 600 + 3,
515	.vsync_end = 600 + 3 + 6,
516	.vtotal = 600 + 3 + 6 + 61,
517	.vrefresh = 60,
518};
519
520static const struct panel_desc samsung_ltn101nt05 = {
521	.modes = &samsung_ltn101nt05_mode,
522	.num_modes = 1,
523	.bpc = 6,
524	.size = {
525		.width = 1024,
526		.height = 600,
527	},
528};
529
530static const struct of_device_id platform_of_match[] = {
531	{
532		.compatible = "auo,b101aw03",
533		.data = &auo_b101aw03,
534	}, {
535		.compatible = "auo,b133xtn01",
536		.data = &auo_b133xtn01,
537	}, {
538		.compatible = "chunghwa,claa101wa01a",
539		.data = &chunghwa_claa101wa01a
540	}, {
541		.compatible = "chunghwa,claa101wb01",
542		.data = &chunghwa_claa101wb01
543	}, {
544		.compatible = "edt,et057090dhu",
545		.data = &edt_et057090dhu,
546	}, {
547		.compatible = "edt,et070080dh6",
548		.data = &edt_etm0700g0dh6,
549	}, {
550		.compatible = "edt,etm0700g0dh6",
551		.data = &edt_etm0700g0dh6,
552	}, {
553		.compatible = "foxlink,fl500wvr00-a0t",
554		.data = &foxlink_fl500wvr00_a0t,
555	}, {
556		.compatible = "innolux,n116bge",
557		.data = &innolux_n116bge,
558	}, {
559		.compatible = "innolux,n156bge-l21",
560		.data = &innolux_n156bge_l21,
561	}, {
562		.compatible = "lg,lp129qe",
563		.data = &lg_lp129qe,
564	}, {
565		.compatible = "samsung,ltn101nt05",
566		.data = &samsung_ltn101nt05,
567	}, {
568		.compatible = "simple-panel",
569	}, {
570		/* sentinel */
571	}
572};
573MODULE_DEVICE_TABLE(of, platform_of_match);
574
575static int panel_simple_platform_probe(struct platform_device *pdev)
576{
577	const struct of_device_id *id;
578
579	id = of_match_node(platform_of_match, pdev->dev.of_node);
580	if (!id)
581		return -ENODEV;
582
583	return panel_simple_probe(&pdev->dev, id->data);
584}
585
586static int panel_simple_platform_remove(struct platform_device *pdev)
587{
588	return panel_simple_remove(&pdev->dev);
589}
590
591static void panel_simple_platform_shutdown(struct platform_device *pdev)
592{
593	panel_simple_shutdown(&pdev->dev);
594}
595
596static struct platform_driver panel_simple_platform_driver = {
597	.driver = {
598		.name = "panel-simple",
599		.owner = THIS_MODULE,
600		.of_match_table = platform_of_match,
601	},
602	.probe = panel_simple_platform_probe,
603	.remove = panel_simple_platform_remove,
604	.shutdown = panel_simple_platform_shutdown,
605};
606
607struct panel_desc_dsi {
608	struct panel_desc desc;
609
610	unsigned long flags;
611	enum mipi_dsi_pixel_format format;
612	unsigned int lanes;
613};
614
615static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
616	.clock = 71000,
617	.hdisplay = 800,
618	.hsync_start = 800 + 32,
619	.hsync_end = 800 + 32 + 1,
620	.htotal = 800 + 32 + 1 + 57,
621	.vdisplay = 1280,
622	.vsync_start = 1280 + 28,
623	.vsync_end = 1280 + 28 + 1,
624	.vtotal = 1280 + 28 + 1 + 14,
625	.vrefresh = 60,
626};
627
628static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
629	.desc = {
630		.modes = &lg_ld070wx3_sl01_mode,
631		.num_modes = 1,
632		.size = {
633			.width = 94,
634			.height = 151,
635		},
636	},
637	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
638	.format = MIPI_DSI_FMT_RGB888,
639	.lanes = 4,
640};
641
642static const struct drm_display_mode lg_lh500wx1_sd03_mode = {
643	.clock = 67000,
644	.hdisplay = 720,
645	.hsync_start = 720 + 12,
646	.hsync_end = 720 + 12 + 4,
647	.htotal = 720 + 12 + 4 + 112,
648	.vdisplay = 1280,
649	.vsync_start = 1280 + 8,
650	.vsync_end = 1280 + 8 + 4,
651	.vtotal = 1280 + 8 + 4 + 12,
652	.vrefresh = 60,
653};
654
655static const struct panel_desc_dsi lg_lh500wx1_sd03 = {
656	.desc = {
657		.modes = &lg_lh500wx1_sd03_mode,
658		.num_modes = 1,
659		.size = {
660			.width = 62,
661			.height = 110,
662		},
663	},
664	.flags = MIPI_DSI_MODE_VIDEO,
665	.format = MIPI_DSI_FMT_RGB888,
666	.lanes = 4,
667};
668
669static const struct drm_display_mode panasonic_vvx10f004b00_mode = {
670	.clock = 157200,
671	.hdisplay = 1920,
672	.hsync_start = 1920 + 154,
673	.hsync_end = 1920 + 154 + 16,
674	.htotal = 1920 + 154 + 16 + 32,
675	.vdisplay = 1200,
676	.vsync_start = 1200 + 17,
677	.vsync_end = 1200 + 17 + 2,
678	.vtotal = 1200 + 17 + 2 + 16,
679	.vrefresh = 60,
680};
681
682static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
683	.desc = {
684		.modes = &panasonic_vvx10f004b00_mode,
685		.num_modes = 1,
686		.size = {
687			.width = 217,
688			.height = 136,
689		},
690	},
691	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
692		 MIPI_DSI_CLOCK_NON_CONTINUOUS,
693	.format = MIPI_DSI_FMT_RGB888,
694	.lanes = 4,
695};
696
697static const struct of_device_id dsi_of_match[] = {
698	{
699		.compatible = "lg,ld070wx3-sl01",
700		.data = &lg_ld070wx3_sl01
701	}, {
702		.compatible = "lg,lh500wx1-sd03",
703		.data = &lg_lh500wx1_sd03
704	}, {
705		.compatible = "panasonic,vvx10f004b00",
706		.data = &panasonic_vvx10f004b00
707	}, {
708		/* sentinel */
709	}
710};
711MODULE_DEVICE_TABLE(of, dsi_of_match);
712
713static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
714{
715	const struct panel_desc_dsi *desc;
716	const struct of_device_id *id;
717	int err;
718
719	id = of_match_node(dsi_of_match, dsi->dev.of_node);
720	if (!id)
721		return -ENODEV;
722
723	desc = id->data;
724
725	err = panel_simple_probe(&dsi->dev, &desc->desc);
726	if (err < 0)
727		return err;
728
729	dsi->mode_flags = desc->flags;
730	dsi->format = desc->format;
731	dsi->lanes = desc->lanes;
732
733	return mipi_dsi_attach(dsi);
734}
735
736static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi)
737{
738	int err;
739
740	err = mipi_dsi_detach(dsi);
741	if (err < 0)
742		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
743
744	return panel_simple_remove(&dsi->dev);
745}
746
747static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi)
748{
749	panel_simple_shutdown(&dsi->dev);
750}
751
752static struct mipi_dsi_driver panel_simple_dsi_driver = {
753	.driver = {
754		.name = "panel-simple-dsi",
755		.owner = THIS_MODULE,
756		.of_match_table = dsi_of_match,
757	},
758	.probe = panel_simple_dsi_probe,
759	.remove = panel_simple_dsi_remove,
760	.shutdown = panel_simple_dsi_shutdown,
761};
762
763static int __init panel_simple_init(void)
764{
765	int err;
766
767	err = platform_driver_register(&panel_simple_platform_driver);
768	if (err < 0)
769		return err;
770
771	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
772		err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
773		if (err < 0)
774			return err;
775	}
776
777	return 0;
778}
779module_init(panel_simple_init);
780
781static void __exit panel_simple_exit(void)
782{
783	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
784		mipi_dsi_driver_unregister(&panel_simple_dsi_driver);
785
786	platform_driver_unregister(&panel_simple_platform_driver);
787}
788module_exit(panel_simple_exit);
789
790MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
791MODULE_DESCRIPTION("DRM Driver for Simple Panels");
792MODULE_LICENSE("GPL and additional rights");
793