]> Git Repo - linux.git/blob - drivers/gpu/drm/meson/meson_drv.c
Merge tag 'docs-4.11' of git://git.lwn.net/linux
[linux.git] / drivers / gpu / drm / meson / meson_drv.c
1 /*
2  * Copyright (C) 2016 BayLibre, SAS
3  * Author: Neil Armstrong <[email protected]>
4  * Copyright (C) 2014 Endless Mobile
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * Written by:
20  *     Jasper St. Pierre <[email protected]>
21  */
22
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/mutex.h>
26 #include <linux/platform_device.h>
27 #include <linux/of_graph.h>
28
29 #include <drm/drmP.h>
30 #include <drm/drm_atomic.h>
31 #include <drm/drm_atomic_helper.h>
32 #include <drm/drm_flip_work.h>
33 #include <drm/drm_crtc_helper.h>
34 #include <drm/drm_plane_helper.h>
35 #include <drm/drm_gem_cma_helper.h>
36 #include <drm/drm_fb_cma_helper.h>
37 #include <drm/drm_rect.h>
38 #include <drm/drm_fb_helper.h>
39
40 #include "meson_drv.h"
41 #include "meson_plane.h"
42 #include "meson_crtc.h"
43 #include "meson_venc_cvbs.h"
44
45 #include "meson_vpp.h"
46 #include "meson_viu.h"
47 #include "meson_venc.h"
48 #include "meson_canvas.h"
49 #include "meson_registers.h"
50
51 #define DRIVER_NAME "meson"
52 #define DRIVER_DESC "Amlogic Meson DRM driver"
53
54 /*
55  * Video Processing Unit
56  *
57  * VPU Handles the Global Video Processing, it includes management of the
58  * clocks gates, blocks reset lines and power domains.
59  *
60  * What is missing :
61  * - Full reset of entire video processing HW blocks
62  * - Scaling and setup of the VPU clock
63  * - Bus clock gates
64  * - Powering up video processing HW blocks
65  * - Powering Up HDMI controller and PHY
66  */
67
68 static void meson_fb_output_poll_changed(struct drm_device *dev)
69 {
70         struct meson_drm *priv = dev->dev_private;
71
72         drm_fbdev_cma_hotplug_event(priv->fbdev);
73 }
74
75 static const struct drm_mode_config_funcs meson_mode_config_funcs = {
76         .output_poll_changed = meson_fb_output_poll_changed,
77         .atomic_check        = drm_atomic_helper_check,
78         .atomic_commit       = drm_atomic_helper_commit,
79         .fb_create           = drm_fb_cma_create,
80 };
81
82 static int meson_enable_vblank(struct drm_device *dev, unsigned int crtc)
83 {
84         struct meson_drm *priv = dev->dev_private;
85
86         meson_venc_enable_vsync(priv);
87
88         return 0;
89 }
90
91 static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc)
92 {
93         struct meson_drm *priv = dev->dev_private;
94
95         meson_venc_disable_vsync(priv);
96 }
97
98 static irqreturn_t meson_irq(int irq, void *arg)
99 {
100         struct drm_device *dev = arg;
101         struct meson_drm *priv = dev->dev_private;
102
103         (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
104
105         meson_crtc_irq(priv);
106
107         return IRQ_HANDLED;
108 }
109
110 static const struct file_operations fops = {
111         .owner          = THIS_MODULE,
112         .open           = drm_open,
113         .release        = drm_release,
114         .unlocked_ioctl = drm_ioctl,
115 #ifdef CONFIG_COMPAT
116         .compat_ioctl   = drm_compat_ioctl,
117 #endif
118         .poll           = drm_poll,
119         .read           = drm_read,
120         .llseek         = no_llseek,
121         .mmap           = drm_gem_cma_mmap,
122 };
123
124 static struct drm_driver meson_driver = {
125         .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM |
126                                   DRIVER_MODESET | DRIVER_PRIME |
127                                   DRIVER_ATOMIC,
128
129         /* Vblank */
130         .enable_vblank          = meson_enable_vblank,
131         .disable_vblank         = meson_disable_vblank,
132         .get_vblank_counter     = drm_vblank_no_hw_counter,
133
134         /* IRQ */
135         .irq_handler            = meson_irq,
136
137         /* PRIME Ops */
138         .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
139         .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
140         .gem_prime_import       = drm_gem_prime_import,
141         .gem_prime_export       = drm_gem_prime_export,
142         .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
143         .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
144         .gem_prime_vmap         = drm_gem_cma_prime_vmap,
145         .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
146         .gem_prime_mmap         = drm_gem_cma_prime_mmap,
147
148         /* GEM Ops */
149         .dumb_create            = drm_gem_cma_dumb_create,
150         .dumb_destroy           = drm_gem_dumb_destroy,
151         .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
152         .gem_free_object_unlocked = drm_gem_cma_free_object,
153         .gem_vm_ops             = &drm_gem_cma_vm_ops,
154
155         /* Misc */
156         .fops                   = &fops,
157         .name                   = DRIVER_NAME,
158         .desc                   = DRIVER_DESC,
159         .date                   = "20161109",
160         .major                  = 1,
161         .minor                  = 0,
162 };
163
164 static bool meson_vpu_has_available_connectors(struct device *dev)
165 {
166         struct device_node *ep, *remote;
167
168         /* Parses each endpoint and check if remote exists */
169         for_each_endpoint_of_node(dev->of_node, ep) {
170                 /* If the endpoint node exists, consider it enabled */
171                 remote = of_graph_get_remote_port(ep);
172                 if (remote)
173                         return true;
174         }
175
176         return false;
177 }
178
179 static struct regmap_config meson_regmap_config = {
180         .reg_bits       = 32,
181         .val_bits       = 32,
182         .reg_stride     = 4,
183         .max_register   = 0x1000,
184 };
185
186 static int meson_drv_probe(struct platform_device *pdev)
187 {
188         struct device *dev = &pdev->dev;
189         struct meson_drm *priv;
190         struct drm_device *drm;
191         struct resource *res;
192         void __iomem *regs;
193         int ret;
194
195         /* Checks if an output connector is available */
196         if (!meson_vpu_has_available_connectors(dev)) {
197                 dev_err(dev, "No output connector available\n");
198                 return -ENODEV;
199         }
200
201         drm = drm_dev_alloc(&meson_driver, dev);
202         if (IS_ERR(drm))
203                 return PTR_ERR(drm);
204
205         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
206         if (!priv) {
207                 ret = -ENOMEM;
208                 goto free_drm;
209         }
210         drm->dev_private = priv;
211         priv->drm = drm;
212         priv->dev = dev;
213
214         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
215         regs = devm_ioremap_resource(dev, res);
216         if (IS_ERR(regs))
217                 return PTR_ERR(regs);
218
219         priv->io_base = regs;
220
221         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
222         /* Simply ioremap since it may be a shared register zone */
223         regs = devm_ioremap(dev, res->start, resource_size(res));
224         if (!regs)
225                 return -EADDRNOTAVAIL;
226
227         priv->hhi = devm_regmap_init_mmio(dev, regs,
228                                           &meson_regmap_config);
229         if (IS_ERR(priv->hhi)) {
230                 dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
231                 return PTR_ERR(priv->hhi);
232         }
233
234         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
235         /* Simply ioremap since it may be a shared register zone */
236         regs = devm_ioremap(dev, res->start, resource_size(res));
237         if (!regs)
238                 return -EADDRNOTAVAIL;
239
240         priv->dmc = devm_regmap_init_mmio(dev, regs,
241                                           &meson_regmap_config);
242         if (IS_ERR(priv->dmc)) {
243                 dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
244                 return PTR_ERR(priv->dmc);
245         }
246
247         priv->vsync_irq = platform_get_irq(pdev, 0);
248
249         drm_vblank_init(drm, 1);
250         drm_mode_config_init(drm);
251
252         /* Encoder Initialization */
253
254         ret = meson_venc_cvbs_create(priv);
255         if (ret)
256                 goto free_drm;
257
258         /* Hardware Initialization */
259
260         meson_venc_init(priv);
261         meson_vpp_init(priv);
262         meson_viu_init(priv);
263
264         ret = meson_plane_create(priv);
265         if (ret)
266                 goto free_drm;
267
268         ret = meson_crtc_create(priv);
269         if (ret)
270                 goto free_drm;
271
272         ret = drm_irq_install(drm, priv->vsync_irq);
273         if (ret)
274                 goto free_drm;
275
276         drm_mode_config_reset(drm);
277         drm->mode_config.max_width = 8192;
278         drm->mode_config.max_height = 8192;
279         drm->mode_config.funcs = &meson_mode_config_funcs;
280
281         priv->fbdev = drm_fbdev_cma_init(drm, 32,
282                                          drm->mode_config.num_crtc,
283                                          drm->mode_config.num_connector);
284         if (IS_ERR(priv->fbdev)) {
285                 ret = PTR_ERR(priv->fbdev);
286                 goto free_drm;
287         }
288
289         drm_kms_helper_poll_init(drm);
290
291         platform_set_drvdata(pdev, priv);
292
293         ret = drm_dev_register(drm, 0);
294         if (ret)
295                 goto free_drm;
296
297         return 0;
298
299 free_drm:
300         drm_dev_unref(drm);
301
302         return ret;
303 }
304
305 static int meson_drv_remove(struct platform_device *pdev)
306 {
307         struct drm_device *drm = dev_get_drvdata(&pdev->dev);
308         struct meson_drm *priv = drm->dev_private;
309
310         drm_dev_unregister(drm);
311         drm_kms_helper_poll_fini(drm);
312         drm_fbdev_cma_fini(priv->fbdev);
313         drm_mode_config_cleanup(drm);
314         drm_vblank_cleanup(drm);
315         drm_dev_unref(drm);
316
317         return 0;
318 }
319
320 static const struct of_device_id dt_match[] = {
321         { .compatible = "amlogic,meson-gxbb-vpu" },
322         { .compatible = "amlogic,meson-gxl-vpu" },
323         { .compatible = "amlogic,meson-gxm-vpu" },
324         {}
325 };
326 MODULE_DEVICE_TABLE(of, dt_match);
327
328 static struct platform_driver meson_drm_platform_driver = {
329         .probe      = meson_drv_probe,
330         .remove     = meson_drv_remove,
331         .driver     = {
332                 .owner  = THIS_MODULE,
333                 .name   = DRIVER_NAME,
334                 .of_match_table = dt_match,
335         },
336 };
337
338 module_platform_driver(meson_drm_platform_driver);
339
340 MODULE_AUTHOR("Jasper St. Pierre <[email protected]>");
341 MODULE_AUTHOR("Neil Armstrong <[email protected]>");
342 MODULE_DESCRIPTION(DRIVER_DESC);
343 MODULE_LICENSE("GPL");
This page took 0.052508 seconds and 4 git commands to generate.