]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/rcar-du/rcar_du_drv.c
Merge branch 'for-3.11-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[karo-tx-linux.git] / drivers / gpu / drm / rcar-du / rcar_du_drv.c
1 /*
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  *
38  * Acquiring a reference enables the device clock and setup core registers. A
39  * reference must be held before accessing any hardware registers.
40  *
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  */
45 int rcar_du_get(struct rcar_du_device *rcdu)
46 {
47         int ret;
48
49         if (rcdu->use_count)
50                 goto done;
51
52         /* Enable clocks before accessing the hardware. */
53         ret = clk_prepare_enable(rcdu->clock);
54         if (ret < 0)
55                 return ret;
56
57         /* Enable extended features */
58         rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
59         rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
60         rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
61         rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
62         rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
63
64         /* Use DS1PR and DS2PR to configure planes priorities and connects the
65          * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
66          */
67         rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
68
69 done:
70         rcdu->use_count++;
71         return 0;
72 }
73
74 /*
75  * rcar_du_put - Release a reference to the DU
76  *
77  * Releasing the last reference disables the device clock.
78  *
79  * This function must be called with the DRM mode_config lock held.
80  */
81 void rcar_du_put(struct rcar_du_device *rcdu)
82 {
83         if (--rcdu->use_count)
84                 return;
85
86         clk_disable_unprepare(rcdu->clock);
87 }
88
89 /* -----------------------------------------------------------------------------
90  * DRM operations
91  */
92
93 static int rcar_du_unload(struct drm_device *dev)
94 {
95         drm_kms_helper_poll_fini(dev);
96         drm_mode_config_cleanup(dev);
97         drm_vblank_cleanup(dev);
98         drm_irq_uninstall(dev);
99
100         dev->dev_private = NULL;
101
102         return 0;
103 }
104
105 static int rcar_du_load(struct drm_device *dev, unsigned long flags)
106 {
107         struct platform_device *pdev = dev->platformdev;
108         struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
109         struct rcar_du_device *rcdu;
110         struct resource *ioarea;
111         struct resource *mem;
112         int ret;
113
114         if (pdata == NULL) {
115                 dev_err(dev->dev, "no platform data\n");
116                 return -ENODEV;
117         }
118
119         rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
120         if (rcdu == NULL) {
121                 dev_err(dev->dev, "failed to allocate private data\n");
122                 return -ENOMEM;
123         }
124
125         rcdu->dev = &pdev->dev;
126         rcdu->pdata = pdata;
127         rcdu->ddev = dev;
128         dev->dev_private = rcdu;
129
130         /* I/O resources and clocks */
131         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
132         if (mem == NULL) {
133                 dev_err(&pdev->dev, "failed to get memory resource\n");
134                 return -EINVAL;
135         }
136
137         ioarea = devm_request_mem_region(&pdev->dev, mem->start,
138                                          resource_size(mem), pdev->name);
139         if (ioarea == NULL) {
140                 dev_err(&pdev->dev, "failed to request memory region\n");
141                 return -EBUSY;
142         }
143
144         rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
145                                           resource_size(ioarea));
146         if (rcdu->mmio == NULL) {
147                 dev_err(&pdev->dev, "failed to remap memory resource\n");
148                 return -ENOMEM;
149         }
150
151         rcdu->clock = devm_clk_get(&pdev->dev, NULL);
152         if (IS_ERR(rcdu->clock)) {
153                 dev_err(&pdev->dev, "failed to get clock\n");
154                 return -ENOENT;
155         }
156
157         /* DRM/KMS objects */
158         ret = rcar_du_modeset_init(rcdu);
159         if (ret < 0) {
160                 dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
161                 goto done;
162         }
163
164         /* IRQ and vblank handling */
165         ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
166         if (ret < 0) {
167                 dev_err(&pdev->dev, "failed to initialize vblank\n");
168                 goto done;
169         }
170
171         ret = drm_irq_install(dev);
172         if (ret < 0) {
173                 dev_err(&pdev->dev, "failed to install IRQ handler\n");
174                 goto done;
175         }
176
177         platform_set_drvdata(pdev, rcdu);
178
179 done:
180         if (ret)
181                 rcar_du_unload(dev);
182
183         return ret;
184 }
185
186 static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
187 {
188         struct rcar_du_device *rcdu = dev->dev_private;
189         unsigned int i;
190
191         for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
192                 rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
193 }
194
195 static irqreturn_t rcar_du_irq(int irq, void *arg)
196 {
197         struct drm_device *dev = arg;
198         struct rcar_du_device *rcdu = dev->dev_private;
199         unsigned int i;
200
201         for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
202                 rcar_du_crtc_irq(&rcdu->crtcs[i]);
203
204         return IRQ_HANDLED;
205 }
206
207 static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
208 {
209         struct rcar_du_device *rcdu = dev->dev_private;
210
211         rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
212
213         return 0;
214 }
215
216 static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
217 {
218         struct rcar_du_device *rcdu = dev->dev_private;
219
220         rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
221 }
222
223 static const struct file_operations rcar_du_fops = {
224         .owner          = THIS_MODULE,
225         .open           = drm_open,
226         .release        = drm_release,
227         .unlocked_ioctl = drm_ioctl,
228 #ifdef CONFIG_COMPAT
229         .compat_ioctl   = drm_compat_ioctl,
230 #endif
231         .poll           = drm_poll,
232         .read           = drm_read,
233         .fasync         = drm_fasync,
234         .llseek         = no_llseek,
235         .mmap           = drm_gem_cma_mmap,
236 };
237
238 static struct drm_driver rcar_du_driver = {
239         .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
240                                 | DRIVER_PRIME,
241         .load                   = rcar_du_load,
242         .unload                 = rcar_du_unload,
243         .preclose               = rcar_du_preclose,
244         .irq_handler            = rcar_du_irq,
245         .get_vblank_counter     = drm_vblank_count,
246         .enable_vblank          = rcar_du_enable_vblank,
247         .disable_vblank         = rcar_du_disable_vblank,
248         .gem_free_object        = drm_gem_cma_free_object,
249         .gem_vm_ops             = &drm_gem_cma_vm_ops,
250         .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
251         .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
252         .gem_prime_import       = drm_gem_prime_import,
253         .gem_prime_export       = drm_gem_prime_export,
254         .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
255         .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
256         .gem_prime_vmap         = drm_gem_cma_prime_vmap,
257         .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
258         .gem_prime_mmap         = drm_gem_cma_prime_mmap,
259         .dumb_create            = rcar_du_dumb_create,
260         .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
261         .dumb_destroy           = drm_gem_cma_dumb_destroy,
262         .fops                   = &rcar_du_fops,
263         .name                   = "rcar-du",
264         .desc                   = "Renesas R-Car Display Unit",
265         .date                   = "20130110",
266         .major                  = 1,
267         .minor                  = 0,
268 };
269
270 /* -----------------------------------------------------------------------------
271  * Power management
272  */
273
274 #if CONFIG_PM_SLEEP
275 static int rcar_du_pm_suspend(struct device *dev)
276 {
277         struct rcar_du_device *rcdu = dev_get_drvdata(dev);
278
279         drm_kms_helper_poll_disable(rcdu->ddev);
280         /* TODO Suspend the CRTC */
281
282         return 0;
283 }
284
285 static int rcar_du_pm_resume(struct device *dev)
286 {
287         struct rcar_du_device *rcdu = dev_get_drvdata(dev);
288
289         /* TODO Resume the CRTC */
290
291         drm_kms_helper_poll_enable(rcdu->ddev);
292         return 0;
293 }
294 #endif
295
296 static const struct dev_pm_ops rcar_du_pm_ops = {
297         SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
298 };
299
300 /* -----------------------------------------------------------------------------
301  * Platform driver
302  */
303
304 static int rcar_du_probe(struct platform_device *pdev)
305 {
306         return drm_platform_init(&rcar_du_driver, pdev);
307 }
308
309 static int rcar_du_remove(struct platform_device *pdev)
310 {
311         drm_platform_exit(&rcar_du_driver, pdev);
312
313         return 0;
314 }
315
316 static struct platform_driver rcar_du_platform_driver = {
317         .probe          = rcar_du_probe,
318         .remove         = rcar_du_remove,
319         .driver         = {
320                 .owner  = THIS_MODULE,
321                 .name   = "rcar-du",
322                 .pm     = &rcar_du_pm_ops,
323         },
324 };
325
326 module_platform_driver(rcar_du_platform_driver);
327
328 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
329 MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
330 MODULE_LICENSE("GPL");