blob: 5f82e046f83e905c67bd6bac7453a8474739e1b4 [file] [log] [blame]
Laurent Pinchart4bf8e192013-06-19 13:54:11 +02001/*
2 * rcar_du_drv.c -- R-Car Display Unit DRM driver
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
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.
12 */
13
14#include <linux/clk.h>
15#include <linux/io.h>
16#include <linux/mm.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/pm.h>
20#include <linux/slab.h>
21
22#include <drm/drmP.h>
23#include <drm/drm_crtc_helper.h>
24#include <drm/drm_gem_cma_helper.h>
25
26#include "rcar_du_crtc.h"
27#include "rcar_du_drv.h"
28#include "rcar_du_kms.h"
29#include "rcar_du_regs.h"
30
31/* -----------------------------------------------------------------------------
32 * Core device operations
33 */
34
35/*
36 * rcar_du_get - Acquire a reference to the DU
37 *
Laurent Pinchartf66ee302013-06-14 14:15:01 +020038 * Acquiring the first reference setups core registers. A reference must be
39 * held before accessing any hardware registers.
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020040 *
41 * This function must be called with the DRM mode_config lock held.
42 *
43 * Return 0 in case of success or a negative error code otherwise.
44 */
45int rcar_du_get(struct rcar_du_device *rcdu)
46{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020047 if (rcdu->use_count)
48 goto done;
49
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020050 /* Enable extended features */
51 rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
52 rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
53 rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
54 rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
55 rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
56
57 /* Use DS1PR and DS2PR to configure planes priorities and connects the
58 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
59 */
60 rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
61
62done:
63 rcdu->use_count++;
64 return 0;
65}
66
67/*
68 * rcar_du_put - Release a reference to the DU
69 *
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020070 * This function must be called with the DRM mode_config lock held.
71 */
72void rcar_du_put(struct rcar_du_device *rcdu)
73{
Laurent Pinchartf66ee302013-06-14 14:15:01 +020074 --rcdu->use_count;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020075}
76
77/* -----------------------------------------------------------------------------
78 * DRM operations
79 */
80
81static int rcar_du_unload(struct drm_device *dev)
82{
83 drm_kms_helper_poll_fini(dev);
84 drm_mode_config_cleanup(dev);
85 drm_vblank_cleanup(dev);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020086
Laurent Pinchartf66ee302013-06-14 14:15:01 +020087 dev->irq_enabled = 0;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020088 dev->dev_private = NULL;
89
90 return 0;
91}
92
93static int rcar_du_load(struct drm_device *dev, unsigned long flags)
94{
95 struct platform_device *pdev = dev->platformdev;
96 struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
97 struct rcar_du_device *rcdu;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020098 struct resource *mem;
99 int ret;
100
101 if (pdata == NULL) {
102 dev_err(dev->dev, "no platform data\n");
103 return -ENODEV;
104 }
105
106 rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
107 if (rcdu == NULL) {
108 dev_err(dev->dev, "failed to allocate private data\n");
109 return -ENOMEM;
110 }
111
112 rcdu->dev = &pdev->dev;
113 rcdu->pdata = pdata;
Laurent Pinchart481d3422013-06-14 13:38:33 +0200114 rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200115 rcdu->ddev = dev;
116 dev->dev_private = rcdu;
117
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200118 /* I/O resources */
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200119 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Laurent Pinchartd5b6dcc2013-06-17 02:29:07 +0200120 rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
121 if (IS_ERR(rcdu->mmio))
122 return PTR_ERR(rcdu->mmio);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200123
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200124 /* DRM/KMS objects */
125 ret = rcar_du_modeset_init(rcdu);
126 if (ret < 0) {
127 dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
128 goto done;
129 }
130
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200131 /* vblank handling */
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200132 ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
133 if (ret < 0) {
134 dev_err(&pdev->dev, "failed to initialize vblank\n");
135 goto done;
136 }
137
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200138 dev->irq_enabled = 1;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200139
140 platform_set_drvdata(pdev, rcdu);
141
142done:
143 if (ret)
144 rcar_du_unload(dev);
145
146 return ret;
147}
148
149static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
150{
151 struct rcar_du_device *rcdu = dev->dev_private;
152 unsigned int i;
153
154 for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
155 rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
156}
157
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200158static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
159{
160 struct rcar_du_device *rcdu = dev->dev_private;
161
162 rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
163
164 return 0;
165}
166
167static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
168{
169 struct rcar_du_device *rcdu = dev->dev_private;
170
171 rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
172}
173
174static const struct file_operations rcar_du_fops = {
175 .owner = THIS_MODULE,
176 .open = drm_open,
177 .release = drm_release,
178 .unlocked_ioctl = drm_ioctl,
179#ifdef CONFIG_COMPAT
180 .compat_ioctl = drm_compat_ioctl,
181#endif
182 .poll = drm_poll,
183 .read = drm_read,
184 .fasync = drm_fasync,
185 .llseek = no_llseek,
186 .mmap = drm_gem_cma_mmap,
187};
188
189static struct drm_driver rcar_du_driver = {
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200190 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME,
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200191 .load = rcar_du_load,
192 .unload = rcar_du_unload,
193 .preclose = rcar_du_preclose,
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200194 .get_vblank_counter = drm_vblank_count,
195 .enable_vblank = rcar_du_enable_vblank,
196 .disable_vblank = rcar_du_disable_vblank,
197 .gem_free_object = drm_gem_cma_free_object,
198 .gem_vm_ops = &drm_gem_cma_vm_ops,
199 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
200 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
Laurent Pinchartffb40402013-07-10 15:23:35 +0200201 .gem_prime_import = drm_gem_prime_import,
202 .gem_prime_export = drm_gem_prime_export,
203 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
204 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
205 .gem_prime_vmap = drm_gem_cma_prime_vmap,
206 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
207 .gem_prime_mmap = drm_gem_cma_prime_mmap,
Laurent Pinchart59e32642013-07-04 20:05:51 +0200208 .dumb_create = rcar_du_dumb_create,
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200209 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
210 .dumb_destroy = drm_gem_cma_dumb_destroy,
211 .fops = &rcar_du_fops,
212 .name = "rcar-du",
213 .desc = "Renesas R-Car Display Unit",
214 .date = "20130110",
215 .major = 1,
216 .minor = 0,
217};
218
219/* -----------------------------------------------------------------------------
220 * Power management
221 */
222
223#if CONFIG_PM_SLEEP
224static int rcar_du_pm_suspend(struct device *dev)
225{
226 struct rcar_du_device *rcdu = dev_get_drvdata(dev);
227
228 drm_kms_helper_poll_disable(rcdu->ddev);
229 /* TODO Suspend the CRTC */
230
231 return 0;
232}
233
234static int rcar_du_pm_resume(struct device *dev)
235{
236 struct rcar_du_device *rcdu = dev_get_drvdata(dev);
237
238 /* TODO Resume the CRTC */
239
240 drm_kms_helper_poll_enable(rcdu->ddev);
241 return 0;
242}
243#endif
244
245static const struct dev_pm_ops rcar_du_pm_ops = {
246 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
247};
248
249/* -----------------------------------------------------------------------------
250 * Platform driver
251 */
252
253static int rcar_du_probe(struct platform_device *pdev)
254{
255 return drm_platform_init(&rcar_du_driver, pdev);
256}
257
258static int rcar_du_remove(struct platform_device *pdev)
259{
260 drm_platform_exit(&rcar_du_driver, pdev);
261
262 return 0;
263}
264
Laurent Pinchart481d3422013-06-14 13:38:33 +0200265static const struct rcar_du_device_info rcar_du_r8a7779_info = {
266 .features = 0,
267};
268
269static const struct platform_device_id rcar_du_id_table[] = {
270 { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info },
271 { }
272};
273
274MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
275
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200276static struct platform_driver rcar_du_platform_driver = {
277 .probe = rcar_du_probe,
278 .remove = rcar_du_remove,
279 .driver = {
280 .owner = THIS_MODULE,
281 .name = "rcar-du",
282 .pm = &rcar_du_pm_ops,
283 },
Laurent Pinchart481d3422013-06-14 13:38:33 +0200284 .id_table = rcar_du_id_table,
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200285};
286
287module_platform_driver(rcar_du_platform_driver);
288
289MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
290MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
291MODULE_LICENSE("GPL");