1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * rcar_du_drv.c  --  R-Car Display Unit DRM driver
4 *
5 * Copyright (C) 2013-2015 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10#include <linux/clk.h>
11#include <linux/io.h>
12#include <linux/mm.h>
13#include <linux/module.h>
14#include <linux/of_device.h>
15#include <linux/platform_device.h>
16#include <linux/pm.h>
17#include <linux/slab.h>
18#include <linux/wait.h>
19
20#include <drm/drm_atomic_helper.h>
21#include <drm/drm_fb_cma_helper.h>
22#include <drm/drm_fb_helper.h>
23#include <drm/drm_drv.h>
24#include <drm/drm_gem_cma_helper.h>
25#include <drm/drm_probe_helper.h>
26
27#include "rcar_du_drv.h"
28#include "rcar_du_kms.h"
29#include "rcar_du_of.h"
30#include "rcar_du_regs.h"
31
32/* -----------------------------------------------------------------------------
33 * Device Information
34 */
35
36static const struct rcar_du_device_info rzg1_du_r8a7743_info = {
37	.gen = 2,
38	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
39		  | RCAR_DU_FEATURE_INTERLACED
40		  | RCAR_DU_FEATURE_TVM_SYNC,
41	.channels_mask = BIT(1) | BIT(0),
42	.routes = {
43		/*
44		 * R8A774[34] has one RGB output and one LVDS output
45		 */
46		[RCAR_DU_OUTPUT_DPAD0] = {
47			.possible_crtcs = BIT(1) | BIT(0),
48			.port = 0,
49		},
50		[RCAR_DU_OUTPUT_LVDS0] = {
51			.possible_crtcs = BIT(0),
52			.port = 1,
53		},
54	},
55	.num_lvds = 1,
56};
57
58static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
59	.gen = 2,
60	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
61		  | RCAR_DU_FEATURE_INTERLACED
62		  | RCAR_DU_FEATURE_TVM_SYNC,
63	.channels_mask = BIT(1) | BIT(0),
64	.routes = {
65		/*
66		 * R8A7745 has two RGB outputs
67		 */
68		[RCAR_DU_OUTPUT_DPAD0] = {
69			.possible_crtcs = BIT(0),
70			.port = 0,
71		},
72		[RCAR_DU_OUTPUT_DPAD1] = {
73			.possible_crtcs = BIT(1),
74			.port = 1,
75		},
76	},
77};
78
79static const struct rcar_du_device_info rzg1_du_r8a77470_info = {
80	.gen = 2,
81	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
82		  | RCAR_DU_FEATURE_INTERLACED
83		  | RCAR_DU_FEATURE_TVM_SYNC,
84	.channels_mask = BIT(1) | BIT(0),
85	.routes = {
86		/*
87		 * R8A77470 has two RGB outputs, one LVDS output, and
88		 * one (currently unsupported) analog video output
89		 */
90		[RCAR_DU_OUTPUT_DPAD0] = {
91			.possible_crtcs = BIT(0),
92			.port = 0,
93		},
94		[RCAR_DU_OUTPUT_DPAD1] = {
95			.possible_crtcs = BIT(1),
96			.port = 1,
97		},
98		[RCAR_DU_OUTPUT_LVDS0] = {
99			.possible_crtcs = BIT(0) | BIT(1),
100			.port = 2,
101		},
102	},
103};
104
105static const struct rcar_du_device_info rcar_du_r8a774a1_info = {
106	.gen = 3,
107	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
108		  | RCAR_DU_FEATURE_VSP1_SOURCE
109		  | RCAR_DU_FEATURE_INTERLACED
110		  | RCAR_DU_FEATURE_TVM_SYNC,
111	.channels_mask = BIT(2) | BIT(1) | BIT(0),
112	.routes = {
113		/*
114		 * R8A774A1 has one RGB output, one LVDS output and one HDMI
115		 * output.
116		 */
117		[RCAR_DU_OUTPUT_DPAD0] = {
118			.possible_crtcs = BIT(2),
119			.port = 0,
120		},
121		[RCAR_DU_OUTPUT_HDMI0] = {
122			.possible_crtcs = BIT(1),
123			.port = 1,
124		},
125		[RCAR_DU_OUTPUT_LVDS0] = {
126			.possible_crtcs = BIT(0),
127			.port = 2,
128		},
129	},
130	.num_lvds = 1,
131	.dpll_mask =  BIT(1),
132};
133
134static const struct rcar_du_device_info rcar_du_r8a774b1_info = {
135	.gen = 3,
136	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
137		  | RCAR_DU_FEATURE_VSP1_SOURCE
138		  | RCAR_DU_FEATURE_INTERLACED
139		  | RCAR_DU_FEATURE_TVM_SYNC,
140	.channels_mask = BIT(3) | BIT(1) | BIT(0),
141	.routes = {
142		/*
143		 * R8A774B1 has one RGB output, one LVDS output and one HDMI
144		 * output.
145		 */
146		[RCAR_DU_OUTPUT_DPAD0] = {
147			.possible_crtcs = BIT(2),
148			.port = 0,
149		},
150		[RCAR_DU_OUTPUT_HDMI0] = {
151			.possible_crtcs = BIT(1),
152			.port = 1,
153		},
154		[RCAR_DU_OUTPUT_LVDS0] = {
155			.possible_crtcs = BIT(0),
156			.port = 2,
157		},
158	},
159	.num_lvds = 1,
160	.dpll_mask =  BIT(1),
161};
162
163static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
164	.gen = 3,
165	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
166		  | RCAR_DU_FEATURE_VSP1_SOURCE,
167	.channels_mask = BIT(1) | BIT(0),
168	.routes = {
169		/*
170		 * R8A774C0 has one RGB output and two LVDS outputs
171		 */
172		[RCAR_DU_OUTPUT_DPAD0] = {
173			.possible_crtcs = BIT(0) | BIT(1),
174			.port = 0,
175		},
176		[RCAR_DU_OUTPUT_LVDS0] = {
177			.possible_crtcs = BIT(0),
178			.port = 1,
179		},
180		[RCAR_DU_OUTPUT_LVDS1] = {
181			.possible_crtcs = BIT(1),
182			.port = 2,
183		},
184	},
185	.num_lvds = 2,
186	.lvds_clk_mask =  BIT(1) | BIT(0),
187};
188
189static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
190	.gen = 3,
191	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
192		  | RCAR_DU_FEATURE_VSP1_SOURCE
193		  | RCAR_DU_FEATURE_INTERLACED
194		  | RCAR_DU_FEATURE_TVM_SYNC,
195	.channels_mask = BIT(3) | BIT(1) | BIT(0),
196	.routes = {
197		/*
198		 * R8A774E1 has one RGB output, one LVDS output and one HDMI
199		 * output.
200		 */
201		[RCAR_DU_OUTPUT_DPAD0] = {
202			.possible_crtcs = BIT(2),
203			.port = 0,
204		},
205		[RCAR_DU_OUTPUT_HDMI0] = {
206			.possible_crtcs = BIT(1),
207			.port = 1,
208		},
209		[RCAR_DU_OUTPUT_LVDS0] = {
210			.possible_crtcs = BIT(0),
211			.port = 2,
212		},
213	},
214	.num_lvds = 1,
215	.dpll_mask =  BIT(1),
216};
217
218static const struct rcar_du_device_info rcar_du_r8a7779_info = {
219	.gen = 1,
220	.features = RCAR_DU_FEATURE_INTERLACED
221		  | RCAR_DU_FEATURE_TVM_SYNC,
222	.channels_mask = BIT(1) | BIT(0),
223	.routes = {
224		/*
225		 * R8A7779 has two RGB outputs and one (currently unsupported)
226		 * TCON output.
227		 */
228		[RCAR_DU_OUTPUT_DPAD0] = {
229			.possible_crtcs = BIT(0),
230			.port = 0,
231		},
232		[RCAR_DU_OUTPUT_DPAD1] = {
233			.possible_crtcs = BIT(1) | BIT(0),
234			.port = 1,
235		},
236	},
237};
238
239static const struct rcar_du_device_info rcar_du_r8a7790_info = {
240	.gen = 2,
241	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
242		  | RCAR_DU_FEATURE_INTERLACED
243		  | RCAR_DU_FEATURE_TVM_SYNC,
244	.quirks = RCAR_DU_QUIRK_ALIGN_128B,
245	.channels_mask = BIT(2) | BIT(1) | BIT(0),
246	.routes = {
247		/*
248		 * R8A7742 and R8A7790 each have one RGB output and two LVDS
249		 * outputs. Additionally R8A7790 supports one TCON output
250		 * (currently unsupported by the driver).
251		 */
252		[RCAR_DU_OUTPUT_DPAD0] = {
253			.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
254			.port = 0,
255		},
256		[RCAR_DU_OUTPUT_LVDS0] = {
257			.possible_crtcs = BIT(0),
258			.port = 1,
259		},
260		[RCAR_DU_OUTPUT_LVDS1] = {
261			.possible_crtcs = BIT(2) | BIT(1),
262			.port = 2,
263		},
264	},
265	.num_lvds = 2,
266};
267
268/* M2-W (r8a7791) and M2-N (r8a7793) are identical */
269static const struct rcar_du_device_info rcar_du_r8a7791_info = {
270	.gen = 2,
271	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
272		  | RCAR_DU_FEATURE_INTERLACED
273		  | RCAR_DU_FEATURE_TVM_SYNC,
274	.channels_mask = BIT(1) | BIT(0),
275	.routes = {
276		/*
277		 * R8A779[13] has one RGB output, one LVDS output and one
278		 * (currently unsupported) TCON output.
279		 */
280		[RCAR_DU_OUTPUT_DPAD0] = {
281			.possible_crtcs = BIT(1) | BIT(0),
282			.port = 0,
283		},
284		[RCAR_DU_OUTPUT_LVDS0] = {
285			.possible_crtcs = BIT(0),
286			.port = 1,
287		},
288	},
289	.num_lvds = 1,
290};
291
292static const struct rcar_du_device_info rcar_du_r8a7792_info = {
293	.gen = 2,
294	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
295		  | RCAR_DU_FEATURE_INTERLACED
296		  | RCAR_DU_FEATURE_TVM_SYNC,
297	.channels_mask = BIT(1) | BIT(0),
298	.routes = {
299		/* R8A7792 has two RGB outputs. */
300		[RCAR_DU_OUTPUT_DPAD0] = {
301			.possible_crtcs = BIT(0),
302			.port = 0,
303		},
304		[RCAR_DU_OUTPUT_DPAD1] = {
305			.possible_crtcs = BIT(1),
306			.port = 1,
307		},
308	},
309};
310
311static const struct rcar_du_device_info rcar_du_r8a7794_info = {
312	.gen = 2,
313	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
314		  | RCAR_DU_FEATURE_INTERLACED
315		  | RCAR_DU_FEATURE_TVM_SYNC,
316	.channels_mask = BIT(1) | BIT(0),
317	.routes = {
318		/*
319		 * R8A7794 has two RGB outputs and one (currently unsupported)
320		 * TCON output.
321		 */
322		[RCAR_DU_OUTPUT_DPAD0] = {
323			.possible_crtcs = BIT(0),
324			.port = 0,
325		},
326		[RCAR_DU_OUTPUT_DPAD1] = {
327			.possible_crtcs = BIT(1),
328			.port = 1,
329		},
330	},
331};
332
333static const struct rcar_du_device_info rcar_du_r8a7795_info = {
334	.gen = 3,
335	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
336		  | RCAR_DU_FEATURE_VSP1_SOURCE
337		  | RCAR_DU_FEATURE_INTERLACED
338		  | RCAR_DU_FEATURE_TVM_SYNC,
339	.channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
340	.routes = {
341		/*
342		 * R8A7795 has one RGB output, two HDMI outputs and one
343		 * LVDS output.
344		 */
345		[RCAR_DU_OUTPUT_DPAD0] = {
346			.possible_crtcs = BIT(3),
347			.port = 0,
348		},
349		[RCAR_DU_OUTPUT_HDMI0] = {
350			.possible_crtcs = BIT(1),
351			.port = 1,
352		},
353		[RCAR_DU_OUTPUT_HDMI1] = {
354			.possible_crtcs = BIT(2),
355			.port = 2,
356		},
357		[RCAR_DU_OUTPUT_LVDS0] = {
358			.possible_crtcs = BIT(0),
359			.port = 3,
360		},
361	},
362	.num_lvds = 1,
363	.dpll_mask =  BIT(2) | BIT(1),
364};
365
366static const struct rcar_du_device_info rcar_du_r8a7796_info = {
367	.gen = 3,
368	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
369		  | RCAR_DU_FEATURE_VSP1_SOURCE
370		  | RCAR_DU_FEATURE_INTERLACED
371		  | RCAR_DU_FEATURE_TVM_SYNC,
372	.channels_mask = BIT(2) | BIT(1) | BIT(0),
373	.routes = {
374		/*
375		 * R8A7796 has one RGB output, one LVDS output and one HDMI
376		 * output.
377		 */
378		[RCAR_DU_OUTPUT_DPAD0] = {
379			.possible_crtcs = BIT(2),
380			.port = 0,
381		},
382		[RCAR_DU_OUTPUT_HDMI0] = {
383			.possible_crtcs = BIT(1),
384			.port = 1,
385		},
386		[RCAR_DU_OUTPUT_LVDS0] = {
387			.possible_crtcs = BIT(0),
388			.port = 2,
389		},
390	},
391	.num_lvds = 1,
392	.dpll_mask =  BIT(1),
393};
394
395static const struct rcar_du_device_info rcar_du_r8a77965_info = {
396	.gen = 3,
397	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
398		  | RCAR_DU_FEATURE_VSP1_SOURCE
399		  | RCAR_DU_FEATURE_INTERLACED
400		  | RCAR_DU_FEATURE_TVM_SYNC,
401	.channels_mask = BIT(3) | BIT(1) | BIT(0),
402	.routes = {
403		/*
404		 * R8A77965 has one RGB output, one LVDS output and one HDMI
405		 * output.
406		 */
407		[RCAR_DU_OUTPUT_DPAD0] = {
408			.possible_crtcs = BIT(2),
409			.port = 0,
410		},
411		[RCAR_DU_OUTPUT_HDMI0] = {
412			.possible_crtcs = BIT(1),
413			.port = 1,
414		},
415		[RCAR_DU_OUTPUT_LVDS0] = {
416			.possible_crtcs = BIT(0),
417			.port = 2,
418		},
419	},
420	.num_lvds = 1,
421	.dpll_mask =  BIT(1),
422};
423
424static const struct rcar_du_device_info rcar_du_r8a77970_info = {
425	.gen = 3,
426	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
427		  | RCAR_DU_FEATURE_VSP1_SOURCE
428		  | RCAR_DU_FEATURE_INTERLACED
429		  | RCAR_DU_FEATURE_TVM_SYNC,
430	.channels_mask = BIT(0),
431	.routes = {
432		/*
433		 * R8A77970 and R8A77980 have one RGB output and one LVDS
434		 * output.
435		 */
436		[RCAR_DU_OUTPUT_DPAD0] = {
437			.possible_crtcs = BIT(0),
438			.port = 0,
439		},
440		[RCAR_DU_OUTPUT_LVDS0] = {
441			.possible_crtcs = BIT(0),
442			.port = 1,
443		},
444	},
445	.num_lvds = 1,
446};
447
448static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
449	.gen = 3,
450	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
451		  | RCAR_DU_FEATURE_VSP1_SOURCE,
452	.channels_mask = BIT(1) | BIT(0),
453	.routes = {
454		/*
455		 * R8A77990 and R8A77995 have one RGB output and two LVDS
456		 * outputs.
457		 */
458		[RCAR_DU_OUTPUT_DPAD0] = {
459			.possible_crtcs = BIT(0) | BIT(1),
460			.port = 0,
461		},
462		[RCAR_DU_OUTPUT_LVDS0] = {
463			.possible_crtcs = BIT(0),
464			.port = 1,
465		},
466		[RCAR_DU_OUTPUT_LVDS1] = {
467			.possible_crtcs = BIT(1),
468			.port = 2,
469		},
470	},
471	.num_lvds = 2,
472	.lvds_clk_mask =  BIT(1) | BIT(0),
473};
474
475static const struct of_device_id rcar_du_of_table[] = {
476	{ .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info },
477	{ .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
478	{ .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info },
479	{ .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
480	{ .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info },
481	{ .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info },
482	{ .compatible = "renesas,du-r8a774b1", .data = &rcar_du_r8a774b1_info },
483	{ .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info },
484	{ .compatible = "renesas,du-r8a774e1", .data = &rcar_du_r8a774e1_info },
485	{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
486	{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
487	{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
488	{ .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info },
489	{ .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
490	{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
491	{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
492	{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
493	{ .compatible = "renesas,du-r8a77961", .data = &rcar_du_r8a7796_info },
494	{ .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info },
495	{ .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
496	{ .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info },
497	{ .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info },
498	{ .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info },
499	{ }
500};
501
502MODULE_DEVICE_TABLE(of, rcar_du_of_table);
503
504/* -----------------------------------------------------------------------------
505 * DRM operations
506 */
507
508DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops);
509
510static struct drm_driver rcar_du_driver = {
511	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
512	DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(rcar_du_dumb_create),
513	.fops			= &rcar_du_fops,
514	.name			= "rcar-du",
515	.desc			= "Renesas R-Car Display Unit",
516	.date			= "20130110",
517	.major			= 1,
518	.minor			= 0,
519};
520
521/* -----------------------------------------------------------------------------
522 * Power management
523 */
524
525#ifdef CONFIG_PM_SLEEP
526static int rcar_du_pm_suspend(struct device *dev)
527{
528	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
529
530	return drm_mode_config_helper_suspend(rcdu->ddev);
531}
532
533static int rcar_du_pm_resume(struct device *dev)
534{
535	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
536
537	return drm_mode_config_helper_resume(rcdu->ddev);
538}
539#endif
540
541static const struct dev_pm_ops rcar_du_pm_ops = {
542	SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
543};
544
545/* -----------------------------------------------------------------------------
546 * Platform driver
547 */
548
549static int rcar_du_remove(struct platform_device *pdev)
550{
551	struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
552	struct drm_device *ddev = rcdu->ddev;
553
554	drm_dev_unregister(ddev);
555
556	drm_kms_helper_poll_fini(ddev);
557
558	drm_dev_put(ddev);
559
560	return 0;
561}
562
563static int rcar_du_probe(struct platform_device *pdev)
564{
565	struct rcar_du_device *rcdu;
566	struct drm_device *ddev;
567	struct resource *mem;
568	int ret;
569
570	/* Allocate and initialize the R-Car device structure. */
571	rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
572	if (rcdu == NULL)
573		return -ENOMEM;
574
575	rcdu->dev = &pdev->dev;
576	rcdu->info = of_device_get_match_data(rcdu->dev);
577
578	platform_set_drvdata(pdev, rcdu);
579
580	/* I/O resources */
581	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
582	rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
583	if (IS_ERR(rcdu->mmio))
584		return PTR_ERR(rcdu->mmio);
585
586	/* DRM/KMS objects */
587	ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
588	if (IS_ERR(ddev))
589		return PTR_ERR(ddev);
590
591	rcdu->ddev = ddev;
592	ddev->dev_private = rcdu;
593
594	ret = rcar_du_modeset_init(rcdu);
595	if (ret < 0) {
596		if (ret != -EPROBE_DEFER)
597			dev_err(&pdev->dev,
598				"failed to initialize DRM/KMS (%d)\n", ret);
599		goto error;
600	}
601
602	ddev->irq_enabled = 1;
603
604	/*
605	 * Register the DRM device with the core and the connectors with
606	 * sysfs.
607	 */
608	ret = drm_dev_register(ddev, 0);
609	if (ret)
610		goto error;
611
612	DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
613
614	drm_fbdev_generic_setup(ddev, 32);
615
616	return 0;
617
618error:
619	rcar_du_remove(pdev);
620
621	return ret;
622}
623
624static struct platform_driver rcar_du_platform_driver = {
625	.probe		= rcar_du_probe,
626	.remove		= rcar_du_remove,
627	.driver		= {
628		.name	= "rcar-du",
629		.pm	= &rcar_du_pm_ops,
630		.of_match_table = rcar_du_of_table,
631	},
632};
633
634static int __init rcar_du_init(void)
635{
636	rcar_du_of_init(rcar_du_of_table);
637
638	return platform_driver_register(&rcar_du_platform_driver);
639}
640module_init(rcar_du_init);
641
642static void __exit rcar_du_exit(void)
643{
644	platform_driver_unregister(&rcar_du_platform_driver);
645}
646module_exit(rcar_du_exit);
647
648MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
649MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
650MODULE_LICENSE("GPL");
651