]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/shmobile/shmob_drm_drv.c
Merge tag 'drm-intel-next-2017-03-20' of git://anongit.freedesktop.org/git/drm-intel...
[karo-tx-linux.git] / drivers / gpu / drm / shmobile / shmob_drm_drv.c
1 /*
2  * shmob_drm_drv.c  --  SH Mobile DRM driver
3  *
4  * Copyright (C) 2012 Renesas Electronics Corporation
5  *
6  * 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 "shmob_drm_drv.h"
27 #include "shmob_drm_kms.h"
28 #include "shmob_drm_plane.h"
29 #include "shmob_drm_regs.h"
30
31 /* -----------------------------------------------------------------------------
32  * Hardware initialization
33  */
34
35 static int shmob_drm_init_interface(struct shmob_drm_device *sdev)
36 {
37         static const u32 ldmt1r[] = {
38                 [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8,
39                 [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9,
40                 [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A,
41                 [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B,
42                 [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16,
43                 [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18,
44                 [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24,
45                 [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR,
46                 [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A,
47                 [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B,
48                 [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C,
49                 [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D,
50                 [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9,
51                 [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12,
52                 [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A,
53                 [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B,
54                 [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C,
55                 [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18,
56                 [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24,
57         };
58
59         if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) {
60                 dev_err(sdev->dev, "invalid interface type %u\n",
61                         sdev->pdata->iface.interface);
62                 return -EINVAL;
63         }
64
65         sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface];
66         return 0;
67 }
68
69 static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
70                                             enum shmob_drm_clk_source clksrc)
71 {
72         struct clk *clk;
73         char *clkname;
74
75         switch (clksrc) {
76         case SHMOB_DRM_CLK_BUS:
77                 clkname = "bus_clk";
78                 sdev->lddckr = LDDCKR_ICKSEL_BUS;
79                 break;
80         case SHMOB_DRM_CLK_PERIPHERAL:
81                 clkname = "peripheral_clk";
82                 sdev->lddckr = LDDCKR_ICKSEL_MIPI;
83                 break;
84         case SHMOB_DRM_CLK_EXTERNAL:
85                 clkname = NULL;
86                 sdev->lddckr = LDDCKR_ICKSEL_HDMI;
87                 break;
88         default:
89                 return -EINVAL;
90         }
91
92         clk = devm_clk_get(sdev->dev, clkname);
93         if (IS_ERR(clk)) {
94                 dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
95                 return PTR_ERR(clk);
96         }
97
98         sdev->clock = clk;
99         return 0;
100 }
101
102 /* -----------------------------------------------------------------------------
103  * DRM operations
104  */
105
106 static irqreturn_t shmob_drm_irq(int irq, void *arg)
107 {
108         struct drm_device *dev = arg;
109         struct shmob_drm_device *sdev = dev->dev_private;
110         unsigned long flags;
111         u32 status;
112
113         /* Acknowledge interrupts. Putting interrupt enable and interrupt flag
114          * bits in the same register is really brain-dead design and requires
115          * taking a spinlock.
116          */
117         spin_lock_irqsave(&sdev->irq_lock, flags);
118         status = lcdc_read(sdev, LDINTR);
119         lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK);
120         spin_unlock_irqrestore(&sdev->irq_lock, flags);
121
122         if (status & LDINTR_VES) {
123                 drm_handle_vblank(dev, 0);
124                 shmob_drm_crtc_finish_page_flip(&sdev->crtc);
125         }
126
127         return IRQ_HANDLED;
128 }
129
130 static const struct file_operations shmob_drm_fops = {
131         .owner          = THIS_MODULE,
132         .open           = drm_open,
133         .release        = drm_release,
134         .unlocked_ioctl = drm_ioctl,
135         .compat_ioctl   = drm_compat_ioctl,
136         .poll           = drm_poll,
137         .read           = drm_read,
138         .llseek         = no_llseek,
139         .mmap           = drm_gem_cma_mmap,
140 };
141
142 static struct drm_driver shmob_drm_driver = {
143         .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
144                                 | DRIVER_PRIME,
145         .irq_handler            = shmob_drm_irq,
146         .gem_free_object_unlocked = drm_gem_cma_free_object,
147         .gem_vm_ops             = &drm_gem_cma_vm_ops,
148         .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
149         .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
150         .gem_prime_import       = drm_gem_prime_import,
151         .gem_prime_export       = drm_gem_prime_export,
152         .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
153         .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
154         .gem_prime_vmap         = drm_gem_cma_prime_vmap,
155         .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
156         .gem_prime_mmap         = drm_gem_cma_prime_mmap,
157         .dumb_create            = drm_gem_cma_dumb_create,
158         .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
159         .dumb_destroy           = drm_gem_dumb_destroy,
160         .fops                   = &shmob_drm_fops,
161         .name                   = "shmob-drm",
162         .desc                   = "Renesas SH Mobile DRM",
163         .date                   = "20120424",
164         .major                  = 1,
165         .minor                  = 0,
166 };
167
168 /* -----------------------------------------------------------------------------
169  * Power management
170  */
171
172 #ifdef CONFIG_PM_SLEEP
173 static int shmob_drm_pm_suspend(struct device *dev)
174 {
175         struct shmob_drm_device *sdev = dev_get_drvdata(dev);
176
177         drm_kms_helper_poll_disable(sdev->ddev);
178         shmob_drm_crtc_suspend(&sdev->crtc);
179
180         return 0;
181 }
182
183 static int shmob_drm_pm_resume(struct device *dev)
184 {
185         struct shmob_drm_device *sdev = dev_get_drvdata(dev);
186
187         drm_modeset_lock_all(sdev->ddev);
188         shmob_drm_crtc_resume(&sdev->crtc);
189         drm_modeset_unlock_all(sdev->ddev);
190
191         drm_kms_helper_poll_enable(sdev->ddev);
192         return 0;
193 }
194 #endif
195
196 static const struct dev_pm_ops shmob_drm_pm_ops = {
197         SET_SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume)
198 };
199
200 /* -----------------------------------------------------------------------------
201  * Platform driver
202  */
203
204 static int shmob_drm_remove(struct platform_device *pdev)
205 {
206         struct shmob_drm_device *sdev = platform_get_drvdata(pdev);
207         struct drm_device *ddev = sdev->ddev;
208
209         drm_dev_unregister(ddev);
210         drm_kms_helper_poll_fini(ddev);
211         drm_mode_config_cleanup(ddev);
212         drm_irq_uninstall(ddev);
213         drm_dev_unref(ddev);
214
215         return 0;
216 }
217
218 static int shmob_drm_probe(struct platform_device *pdev)
219 {
220         struct shmob_drm_platform_data *pdata = pdev->dev.platform_data;
221         struct shmob_drm_device *sdev;
222         struct drm_device *ddev;
223         struct resource *res;
224         unsigned int i;
225         int ret;
226
227         if (pdata == NULL) {
228                 dev_err(&pdev->dev, "no platform data\n");
229                 return -EINVAL;
230         }
231
232         /*
233          * Allocate and initialize the driver private data, I/O resources and
234          * clocks.
235          */
236         sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
237         if (sdev == NULL)
238                 return -ENOMEM;
239
240         sdev->dev = &pdev->dev;
241         sdev->pdata = pdata;
242         spin_lock_init(&sdev->irq_lock);
243
244         platform_set_drvdata(pdev, sdev);
245
246         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
247         sdev->mmio = devm_ioremap_resource(&pdev->dev, res);
248         if (sdev->mmio == NULL)
249                 return -ENOMEM;
250
251         ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
252         if (ret < 0)
253                 return ret;
254
255         ret = shmob_drm_init_interface(sdev);
256         if (ret < 0)
257                 return ret;
258
259         /* Allocate and initialize the DRM device. */
260         ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev);
261         if (IS_ERR(ddev))
262                 return PTR_ERR(ddev);
263
264         sdev->ddev = ddev;
265         ddev->dev_private = sdev;
266
267         ret = shmob_drm_modeset_init(sdev);
268         if (ret < 0) {
269                 dev_err(&pdev->dev, "failed to initialize mode setting\n");
270                 goto err_free_drm_dev;
271         }
272
273         for (i = 0; i < 4; ++i) {
274                 ret = shmob_drm_plane_create(sdev, i);
275                 if (ret < 0) {
276                         dev_err(&pdev->dev, "failed to create plane %u\n", i);
277                         goto err_modeset_cleanup;
278                 }
279         }
280
281         ret = drm_vblank_init(ddev, 1);
282         if (ret < 0) {
283                 dev_err(&pdev->dev, "failed to initialize vblank\n");
284                 goto err_modeset_cleanup;
285         }
286
287         ret = drm_irq_install(ddev, platform_get_irq(pdev, 0));
288         if (ret < 0) {
289                 dev_err(&pdev->dev, "failed to install IRQ handler\n");
290                 goto err_vblank_cleanup;
291         }
292
293         /*
294          * Register the DRM device with the core and the connectors with
295          * sysfs.
296          */
297         ret = drm_dev_register(ddev, 0);
298         if (ret < 0)
299                 goto err_irq_uninstall;
300
301         return 0;
302
303 err_irq_uninstall:
304         drm_irq_uninstall(ddev);
305 err_vblank_cleanup:
306         drm_vblank_cleanup(ddev);
307 err_modeset_cleanup:
308         drm_kms_helper_poll_fini(ddev);
309         drm_mode_config_cleanup(ddev);
310 err_free_drm_dev:
311         drm_dev_unref(ddev);
312
313         return ret;
314 }
315
316 static struct platform_driver shmob_drm_platform_driver = {
317         .probe          = shmob_drm_probe,
318         .remove         = shmob_drm_remove,
319         .driver         = {
320                 .name   = "shmob-drm",
321                 .pm     = &shmob_drm_pm_ops,
322         },
323 };
324
325 module_platform_driver(shmob_drm_platform_driver);
326
327 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
328 MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");
329 MODULE_LICENSE("GPL");