2 * rcar_du_drv.c -- R-Car Display Unit DRM driver
4 * Copyright (C) 2013 Renesas 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/platform_device.h>
20 #include <linux/slab.h>
23 #include <drm/drm_crtc_helper.h>
24 #include <drm/drm_fb_cma_helper.h>
25 #include <drm/drm_gem_cma_helper.h>
27 #include "rcar_du_crtc.h"
28 #include "rcar_du_drv.h"
29 #include "rcar_du_kms.h"
30 #include "rcar_du_regs.h"
32 /* -----------------------------------------------------------------------------
36 static int rcar_du_unload(struct drm_device
*dev
)
38 struct rcar_du_device
*rcdu
= dev
->dev_private
;
41 drm_fbdev_cma_fini(rcdu
->fbdev
);
43 drm_kms_helper_poll_fini(dev
);
44 drm_mode_config_cleanup(dev
);
45 drm_vblank_cleanup(dev
);
48 dev
->dev_private
= NULL
;
53 static int rcar_du_load(struct drm_device
*dev
, unsigned long flags
)
55 struct platform_device
*pdev
= dev
->platformdev
;
56 struct rcar_du_platform_data
*pdata
= pdev
->dev
.platform_data
;
57 struct rcar_du_device
*rcdu
;
62 dev_err(dev
->dev
, "no platform data\n");
66 rcdu
= devm_kzalloc(&pdev
->dev
, sizeof(*rcdu
), GFP_KERNEL
);
68 dev_err(dev
->dev
, "failed to allocate private data\n");
72 rcdu
->dev
= &pdev
->dev
;
74 rcdu
->info
= (struct rcar_du_device_info
*)pdev
->id_entry
->driver_data
;
76 dev
->dev_private
= rcdu
;
79 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
80 rcdu
->mmio
= devm_ioremap_resource(&pdev
->dev
, mem
);
81 if (IS_ERR(rcdu
->mmio
))
82 return PTR_ERR(rcdu
->mmio
);
85 ret
= rcar_du_modeset_init(rcdu
);
87 dev_err(&pdev
->dev
, "failed to initialize DRM/KMS\n");
92 ret
= drm_vblank_init(dev
, (1 << rcdu
->num_crtcs
) - 1);
94 dev_err(&pdev
->dev
, "failed to initialize vblank\n");
100 platform_set_drvdata(pdev
, rcdu
);
109 static void rcar_du_preclose(struct drm_device
*dev
, struct drm_file
*file
)
111 struct rcar_du_device
*rcdu
= dev
->dev_private
;
114 for (i
= 0; i
< rcdu
->num_crtcs
; ++i
)
115 rcar_du_crtc_cancel_page_flip(&rcdu
->crtcs
[i
], file
);
118 static void rcar_du_lastclose(struct drm_device
*dev
)
120 struct rcar_du_device
*rcdu
= dev
->dev_private
;
122 drm_fbdev_cma_restore_mode(rcdu
->fbdev
);
125 static int rcar_du_enable_vblank(struct drm_device
*dev
, int crtc
)
127 struct rcar_du_device
*rcdu
= dev
->dev_private
;
129 rcar_du_crtc_enable_vblank(&rcdu
->crtcs
[crtc
], true);
134 static void rcar_du_disable_vblank(struct drm_device
*dev
, int crtc
)
136 struct rcar_du_device
*rcdu
= dev
->dev_private
;
138 rcar_du_crtc_enable_vblank(&rcdu
->crtcs
[crtc
], false);
141 static const struct file_operations rcar_du_fops
= {
142 .owner
= THIS_MODULE
,
144 .release
= drm_release
,
145 .unlocked_ioctl
= drm_ioctl
,
147 .compat_ioctl
= drm_compat_ioctl
,
152 .mmap
= drm_gem_cma_mmap
,
155 static struct drm_driver rcar_du_driver
= {
156 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_PRIME
,
157 .load
= rcar_du_load
,
158 .unload
= rcar_du_unload
,
159 .preclose
= rcar_du_preclose
,
160 .lastclose
= rcar_du_lastclose
,
161 .set_busid
= drm_platform_set_busid
,
162 .get_vblank_counter
= drm_vblank_count
,
163 .enable_vblank
= rcar_du_enable_vblank
,
164 .disable_vblank
= rcar_du_disable_vblank
,
165 .gem_free_object
= drm_gem_cma_free_object
,
166 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
167 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
168 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
169 .gem_prime_import
= drm_gem_prime_import
,
170 .gem_prime_export
= drm_gem_prime_export
,
171 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
172 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
173 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
174 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
175 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
176 .dumb_create
= rcar_du_dumb_create
,
177 .dumb_map_offset
= drm_gem_cma_dumb_map_offset
,
178 .dumb_destroy
= drm_gem_dumb_destroy
,
179 .fops
= &rcar_du_fops
,
181 .desc
= "Renesas R-Car Display Unit",
187 /* -----------------------------------------------------------------------------
191 #ifdef CONFIG_PM_SLEEP
192 static int rcar_du_pm_suspend(struct device
*dev
)
194 struct rcar_du_device
*rcdu
= dev_get_drvdata(dev
);
196 drm_kms_helper_poll_disable(rcdu
->ddev
);
197 /* TODO Suspend the CRTC */
202 static int rcar_du_pm_resume(struct device
*dev
)
204 struct rcar_du_device
*rcdu
= dev_get_drvdata(dev
);
206 /* TODO Resume the CRTC */
208 drm_kms_helper_poll_enable(rcdu
->ddev
);
213 static const struct dev_pm_ops rcar_du_pm_ops
= {
214 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend
, rcar_du_pm_resume
)
217 /* -----------------------------------------------------------------------------
221 static int rcar_du_probe(struct platform_device
*pdev
)
223 return drm_platform_init(&rcar_du_driver
, pdev
);
226 static int rcar_du_remove(struct platform_device
*pdev
)
228 struct rcar_du_device
*rcdu
= platform_get_drvdata(pdev
);
230 drm_put_dev(rcdu
->ddev
);
235 static const struct rcar_du_device_info rcar_du_r8a7779_info
= {
239 /* R8A7779 has two RGB outputs and one (currently unsupported)
242 [RCAR_DU_OUTPUT_DPAD0
] = {
243 .possible_crtcs
= BIT(0),
244 .encoder_type
= DRM_MODE_ENCODER_NONE
,
246 [RCAR_DU_OUTPUT_DPAD1
] = {
247 .possible_crtcs
= BIT(1) | BIT(0),
248 .encoder_type
= DRM_MODE_ENCODER_NONE
,
254 static const struct rcar_du_device_info rcar_du_r8a7790_info
= {
255 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_DEFR8
,
256 .quirks
= RCAR_DU_QUIRK_ALIGN_128B
| RCAR_DU_QUIRK_LVDS_LANES
,
259 /* R8A7790 has one RGB output, two LVDS outputs and one
260 * (currently unsupported) TCON output.
262 [RCAR_DU_OUTPUT_DPAD0
] = {
263 .possible_crtcs
= BIT(2) | BIT(1) | BIT(0),
264 .encoder_type
= DRM_MODE_ENCODER_NONE
,
266 [RCAR_DU_OUTPUT_LVDS0
] = {
267 .possible_crtcs
= BIT(0),
268 .encoder_type
= DRM_MODE_ENCODER_LVDS
,
270 [RCAR_DU_OUTPUT_LVDS1
] = {
271 .possible_crtcs
= BIT(2) | BIT(1),
272 .encoder_type
= DRM_MODE_ENCODER_LVDS
,
278 static const struct rcar_du_device_info rcar_du_r8a7791_info
= {
279 .features
= RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_DEFR8
,
282 /* R8A7791 has one RGB output, one LVDS output and one
283 * (currently unsupported) TCON output.
285 [RCAR_DU_OUTPUT_DPAD0
] = {
286 .possible_crtcs
= BIT(1),
287 .encoder_type
= DRM_MODE_ENCODER_NONE
,
289 [RCAR_DU_OUTPUT_LVDS0
] = {
290 .possible_crtcs
= BIT(0),
291 .encoder_type
= DRM_MODE_ENCODER_LVDS
,
297 static const struct platform_device_id rcar_du_id_table
[] = {
298 { "rcar-du-r8a7779", (kernel_ulong_t
)&rcar_du_r8a7779_info
},
299 { "rcar-du-r8a7790", (kernel_ulong_t
)&rcar_du_r8a7790_info
},
300 { "rcar-du-r8a7791", (kernel_ulong_t
)&rcar_du_r8a7791_info
},
304 MODULE_DEVICE_TABLE(platform
, rcar_du_id_table
);
306 static struct platform_driver rcar_du_platform_driver
= {
307 .probe
= rcar_du_probe
,
308 .remove
= rcar_du_remove
,
310 .owner
= THIS_MODULE
,
312 .pm
= &rcar_du_pm_ops
,
314 .id_table
= rcar_du_id_table
,
317 module_platform_driver(rcar_du_platform_driver
);
319 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
320 MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
321 MODULE_LICENSE("GPL");