2 * rcar_du_drv.c -- R-Car Display Unit DRM driver
4 * Copyright (C) 2013-2015 Renesas Electronics Corporation
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
14 #include <linux/clk.h>
17 #include <linux/module.h>
18 #include <linux/of_device.h>
19 #include <linux/platform_device.h>
21 #include <linux/slab.h>
22 #include <linux/wait.h>
25 #include <drm/drm_crtc_helper.h>
26 #include <drm/drm_fb_cma_helper.h>
27 #include <drm/drm_gem_cma_helper.h>
29 #include "rcar_du_drv.h"
30 #include "rcar_du_kms.h"
31 #include "rcar_du_regs.h"
33 /* -----------------------------------------------------------------------------
37 static const struct rcar_du_device_info rcar_du_r8a7779_info
= {
42 /* R8A7779 has two RGB outputs and one (currently unsupported)
45 [RCAR_DU_OUTPUT_DPAD0
] = {
46 .possible_crtcs
= BIT(0),
49 [RCAR_DU_OUTPUT_DPAD1
] = {
50 .possible_crtcs
= BIT(1) | BIT(0),
57 static const struct rcar_du_device_info rcar_du_r8a7790_info
= {
59 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
60 | RCAR_DU_FEATURE_EXT_CTRL_REGS
,
61 .quirks
= RCAR_DU_QUIRK_ALIGN_128B
| RCAR_DU_QUIRK_LVDS_LANES
,
64 /* R8A7790 has one RGB output, two LVDS outputs and one
65 * (currently unsupported) TCON output.
67 [RCAR_DU_OUTPUT_DPAD0
] = {
68 .possible_crtcs
= BIT(2) | BIT(1) | BIT(0),
71 [RCAR_DU_OUTPUT_LVDS0
] = {
72 .possible_crtcs
= BIT(0),
75 [RCAR_DU_OUTPUT_LVDS1
] = {
76 .possible_crtcs
= BIT(2) | BIT(1),
83 /* M2-W (r8a7791) and M2-N (r8a7793) are identical */
84 static const struct rcar_du_device_info rcar_du_r8a7791_info
= {
86 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
87 | RCAR_DU_FEATURE_EXT_CTRL_REGS
,
90 /* R8A779[13] has one RGB output, one LVDS output and one
91 * (currently unsupported) TCON output.
93 [RCAR_DU_OUTPUT_DPAD0
] = {
94 .possible_crtcs
= BIT(1) | BIT(0),
97 [RCAR_DU_OUTPUT_LVDS0
] = {
98 .possible_crtcs
= BIT(0),
105 static const struct rcar_du_device_info rcar_du_r8a7792_info
= {
107 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
108 | RCAR_DU_FEATURE_EXT_CTRL_REGS
,
111 /* R8A7792 has two RGB outputs. */
112 [RCAR_DU_OUTPUT_DPAD0
] = {
113 .possible_crtcs
= BIT(0),
116 [RCAR_DU_OUTPUT_DPAD1
] = {
117 .possible_crtcs
= BIT(1),
124 static const struct rcar_du_device_info rcar_du_r8a7794_info
= {
126 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
127 | RCAR_DU_FEATURE_EXT_CTRL_REGS
,
130 /* R8A7794 has two RGB outputs and one (currently unsupported)
133 [RCAR_DU_OUTPUT_DPAD0
] = {
134 .possible_crtcs
= BIT(0),
137 [RCAR_DU_OUTPUT_DPAD1
] = {
138 .possible_crtcs
= BIT(1),
145 static const struct rcar_du_device_info rcar_du_r8a7795_info
= {
147 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
148 | RCAR_DU_FEATURE_EXT_CTRL_REGS
149 | RCAR_DU_FEATURE_VSP1_SOURCE
,
152 /* R8A7795 has one RGB output, two HDMI outputs and one
155 [RCAR_DU_OUTPUT_DPAD0
] = {
156 .possible_crtcs
= BIT(3),
159 [RCAR_DU_OUTPUT_HDMI0
] = {
160 .possible_crtcs
= BIT(1),
163 [RCAR_DU_OUTPUT_HDMI1
] = {
164 .possible_crtcs
= BIT(2),
167 [RCAR_DU_OUTPUT_LVDS0
] = {
168 .possible_crtcs
= BIT(0),
173 .dpll_ch
= BIT(1) | BIT(2),
176 static const struct rcar_du_device_info rcar_du_r8a7796_info
= {
178 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
179 | RCAR_DU_FEATURE_EXT_CTRL_REGS
180 | RCAR_DU_FEATURE_VSP1_SOURCE
,
183 /* R8A7796 has one RGB output, one LVDS output and one
184 * (currently unsupported) HDMI output.
186 [RCAR_DU_OUTPUT_DPAD0
] = {
187 .possible_crtcs
= BIT(2),
190 [RCAR_DU_OUTPUT_LVDS0
] = {
191 .possible_crtcs
= BIT(0),
198 static const struct of_device_id rcar_du_of_table
[] = {
199 { .compatible
= "renesas,du-r8a7779", .data
= &rcar_du_r8a7779_info
},
200 { .compatible
= "renesas,du-r8a7790", .data
= &rcar_du_r8a7790_info
},
201 { .compatible
= "renesas,du-r8a7791", .data
= &rcar_du_r8a7791_info
},
202 { .compatible
= "renesas,du-r8a7792", .data
= &rcar_du_r8a7792_info
},
203 { .compatible
= "renesas,du-r8a7793", .data
= &rcar_du_r8a7791_info
},
204 { .compatible
= "renesas,du-r8a7794", .data
= &rcar_du_r8a7794_info
},
205 { .compatible
= "renesas,du-r8a7795", .data
= &rcar_du_r8a7795_info
},
206 { .compatible
= "renesas,du-r8a7796", .data
= &rcar_du_r8a7796_info
},
210 MODULE_DEVICE_TABLE(of
, rcar_du_of_table
);
212 /* -----------------------------------------------------------------------------
216 static void rcar_du_lastclose(struct drm_device
*dev
)
218 struct rcar_du_device
*rcdu
= dev
->dev_private
;
220 drm_fbdev_cma_restore_mode(rcdu
->fbdev
);
223 DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops
);
225 static struct drm_driver rcar_du_driver
= {
226 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_PRIME
228 .lastclose
= rcar_du_lastclose
,
229 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
230 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
231 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
232 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
233 .gem_prime_import
= drm_gem_prime_import
,
234 .gem_prime_export
= drm_gem_prime_export
,
235 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
236 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
237 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
238 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
239 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
240 .dumb_create
= rcar_du_dumb_create
,
241 .dumb_map_offset
= drm_gem_cma_dumb_map_offset
,
242 .dumb_destroy
= drm_gem_dumb_destroy
,
243 .fops
= &rcar_du_fops
,
245 .desc
= "Renesas R-Car Display Unit",
251 /* -----------------------------------------------------------------------------
255 #ifdef CONFIG_PM_SLEEP
256 static int rcar_du_pm_suspend(struct device
*dev
)
258 struct rcar_du_device
*rcdu
= dev_get_drvdata(dev
);
260 drm_kms_helper_poll_disable(rcdu
->ddev
);
261 /* TODO Suspend the CRTC */
266 static int rcar_du_pm_resume(struct device
*dev
)
268 struct rcar_du_device
*rcdu
= dev_get_drvdata(dev
);
270 /* TODO Resume the CRTC */
272 drm_kms_helper_poll_enable(rcdu
->ddev
);
277 static const struct dev_pm_ops rcar_du_pm_ops
= {
278 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend
, rcar_du_pm_resume
)
281 /* -----------------------------------------------------------------------------
285 static int rcar_du_remove(struct platform_device
*pdev
)
287 struct rcar_du_device
*rcdu
= platform_get_drvdata(pdev
);
288 struct drm_device
*ddev
= rcdu
->ddev
;
290 drm_dev_unregister(ddev
);
293 drm_fbdev_cma_fini(rcdu
->fbdev
);
295 drm_kms_helper_poll_fini(ddev
);
296 drm_mode_config_cleanup(ddev
);
303 static int rcar_du_probe(struct platform_device
*pdev
)
305 struct rcar_du_device
*rcdu
;
306 struct drm_device
*ddev
;
307 struct resource
*mem
;
310 /* Allocate and initialize the R-Car device structure. */
311 rcdu
= devm_kzalloc(&pdev
->dev
, sizeof(*rcdu
), GFP_KERNEL
);
315 rcdu
->dev
= &pdev
->dev
;
316 rcdu
->info
= of_device_get_match_data(rcdu
->dev
);
318 platform_set_drvdata(pdev
, rcdu
);
321 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
322 rcdu
->mmio
= devm_ioremap_resource(&pdev
->dev
, mem
);
323 if (IS_ERR(rcdu
->mmio
))
324 return PTR_ERR(rcdu
->mmio
);
326 /* DRM/KMS objects */
327 ddev
= drm_dev_alloc(&rcar_du_driver
, &pdev
->dev
);
329 return PTR_ERR(ddev
);
332 ddev
->dev_private
= rcdu
;
334 ret
= rcar_du_modeset_init(rcdu
);
336 if (ret
!= -EPROBE_DEFER
)
338 "failed to initialize DRM/KMS (%d)\n", ret
);
342 ddev
->irq_enabled
= 1;
344 /* Register the DRM device with the core and the connectors with
347 ret
= drm_dev_register(ddev
, 0);
351 DRM_INFO("Device %s probed\n", dev_name(&pdev
->dev
));
356 rcar_du_remove(pdev
);
361 static struct platform_driver rcar_du_platform_driver
= {
362 .probe
= rcar_du_probe
,
363 .remove
= rcar_du_remove
,
366 .pm
= &rcar_du_pm_ops
,
367 .of_match_table
= rcar_du_of_table
,
371 module_platform_driver(rcar_du_platform_driver
);
373 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
374 MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
375 MODULE_LICENSE("GPL");