Merge tag 'v4.11-rc7' into drm-next
[sfrench/cifs-2.6.git] / drivers / gpu / drm / sun4i / sun4i_drv.c
1 /*
2  * Copyright (C) 2015 Free Electrons
3  * Copyright (C) 2015 NextThing Co
4  *
5  * Maxime Ripard <maxime.ripard@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  */
12
13 #include <linux/component.h>
14 #include <linux/of_graph.h>
15
16 #include <drm/drmP.h>
17 #include <drm/drm_crtc_helper.h>
18 #include <drm/drm_fb_cma_helper.h>
19 #include <drm/drm_gem_cma_helper.h>
20 #include <drm/drm_fb_helper.h>
21 #include <drm/drm_of.h>
22
23 #include "sun4i_crtc.h"
24 #include "sun4i_drv.h"
25 #include "sun4i_framebuffer.h"
26 #include "sun4i_layer.h"
27
28 DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops);
29
30 static struct drm_driver sun4i_drv_driver = {
31         .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
32
33         /* Generic Operations */
34         .fops                   = &sun4i_drv_fops,
35         .name                   = "sun4i-drm",
36         .desc                   = "Allwinner sun4i Display Engine",
37         .date                   = "20150629",
38         .major                  = 1,
39         .minor                  = 0,
40
41         /* GEM Operations */
42         .dumb_create            = drm_gem_cma_dumb_create,
43         .dumb_destroy           = drm_gem_dumb_destroy,
44         .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
45         .gem_free_object_unlocked = drm_gem_cma_free_object,
46         .gem_vm_ops             = &drm_gem_cma_vm_ops,
47
48         /* PRIME Operations */
49         .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
50         .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
51         .gem_prime_import       = drm_gem_prime_import,
52         .gem_prime_export       = drm_gem_prime_export,
53         .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
54         .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
55         .gem_prime_vmap         = drm_gem_cma_prime_vmap,
56         .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
57         .gem_prime_mmap         = drm_gem_cma_prime_mmap,
58
59         /* Frame Buffer Operations */
60 };
61
62 static void sun4i_remove_framebuffers(void)
63 {
64         struct apertures_struct *ap;
65
66         ap = alloc_apertures(1);
67         if (!ap)
68                 return;
69
70         /* The framebuffer can be located anywhere in RAM */
71         ap->ranges[0].base = 0;
72         ap->ranges[0].size = ~0;
73
74         drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
75         kfree(ap);
76 }
77
78 static int sun4i_drv_bind(struct device *dev)
79 {
80         struct drm_device *drm;
81         struct sun4i_drv *drv;
82         int ret;
83
84         drm = drm_dev_alloc(&sun4i_drv_driver, dev);
85         if (IS_ERR(drm))
86                 return PTR_ERR(drm);
87
88         drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
89         if (!drv) {
90                 ret = -ENOMEM;
91                 goto free_drm;
92         }
93         drm->dev_private = drv;
94
95         drm_vblank_init(drm, 1);
96         drm_mode_config_init(drm);
97
98         ret = component_bind_all(drm->dev, drm);
99         if (ret) {
100                 dev_err(drm->dev, "Couldn't bind all pipelines components\n");
101                 goto free_drm;
102         }
103
104         /* Create our layers */
105         drv->layers = sun4i_layers_init(drm);
106         if (IS_ERR(drv->layers)) {
107                 dev_err(drm->dev, "Couldn't create the planes\n");
108                 ret = PTR_ERR(drv->layers);
109                 goto free_drm;
110         }
111
112         /* Create our CRTC */
113         drv->crtc = sun4i_crtc_init(drm);
114         if (!drv->crtc) {
115                 dev_err(drm->dev, "Couldn't create the CRTC\n");
116                 ret = -EINVAL;
117                 goto free_drm;
118         }
119         drm->irq_enabled = true;
120
121         /* Remove early framebuffers (ie. simplefb) */
122         sun4i_remove_framebuffers();
123
124         /* Create our framebuffer */
125         drv->fbdev = sun4i_framebuffer_init(drm);
126         if (IS_ERR(drv->fbdev)) {
127                 dev_err(drm->dev, "Couldn't create our framebuffer\n");
128                 ret = PTR_ERR(drv->fbdev);
129                 goto free_drm;
130         }
131
132         /* Enable connectors polling */
133         drm_kms_helper_poll_init(drm);
134
135         ret = drm_dev_register(drm, 0);
136         if (ret)
137                 goto free_drm;
138
139         return 0;
140
141 free_drm:
142         drm_dev_unref(drm);
143         return ret;
144 }
145
146 static void sun4i_drv_unbind(struct device *dev)
147 {
148         struct drm_device *drm = dev_get_drvdata(dev);
149
150         drm_dev_unregister(drm);
151         drm_kms_helper_poll_fini(drm);
152         sun4i_framebuffer_free(drm);
153         drm_vblank_cleanup(drm);
154         drm_dev_unref(drm);
155 }
156
157 static const struct component_master_ops sun4i_drv_master_ops = {
158         .bind   = sun4i_drv_bind,
159         .unbind = sun4i_drv_unbind,
160 };
161
162 static bool sun4i_drv_node_is_frontend(struct device_node *node)
163 {
164         return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
165                 of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") ||
166                 of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
167 }
168
169 static bool sun4i_drv_node_is_tcon(struct device_node *node)
170 {
171         return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
172                 of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
173                 of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
174                 of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
175 }
176
177 static int compare_of(struct device *dev, void *data)
178 {
179         DRM_DEBUG_DRIVER("Comparing of node %s with %s\n",
180                          of_node_full_name(dev->of_node),
181                          of_node_full_name(data));
182
183         return dev->of_node == data;
184 }
185
186 static int sun4i_drv_add_endpoints(struct device *dev,
187                                    struct component_match **match,
188                                    struct device_node *node)
189 {
190         struct device_node *port, *ep, *remote;
191         int count = 0;
192
193         /*
194          * We don't support the frontend for now, so we will never
195          * have a device bound. Just skip over it, but we still want
196          * the rest our pipeline to be added.
197          */
198         if (!sun4i_drv_node_is_frontend(node) &&
199             !of_device_is_available(node))
200                 return 0;
201
202         if (!sun4i_drv_node_is_frontend(node)) {
203                 /* Add current component */
204                 DRM_DEBUG_DRIVER("Adding component %s\n",
205                                  of_node_full_name(node));
206                 drm_of_component_match_add(dev, match, compare_of, node);
207                 count++;
208         }
209
210         /* Inputs are listed first, then outputs */
211         port = of_graph_get_port_by_id(node, 1);
212         if (!port) {
213                 DRM_DEBUG_DRIVER("No output to bind\n");
214                 return count;
215         }
216
217         for_each_available_child_of_node(port, ep) {
218                 remote = of_graph_get_remote_port_parent(ep);
219                 if (!remote) {
220                         DRM_DEBUG_DRIVER("Error retrieving the output node\n");
221                         of_node_put(remote);
222                         continue;
223                 }
224
225                 /*
226                  * If the node is our TCON, the first port is used for
227                  * panel or bridges, and will not be part of the
228                  * component framework.
229                  */
230                 if (sun4i_drv_node_is_tcon(node)) {
231                         struct of_endpoint endpoint;
232
233                         if (of_graph_parse_endpoint(ep, &endpoint)) {
234                                 DRM_DEBUG_DRIVER("Couldn't parse endpoint\n");
235                                 continue;
236                         }
237
238                         if (!endpoint.id) {
239                                 DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n");
240                                 continue;
241                         }
242                 }
243
244                 /* Walk down our tree */
245                 count += sun4i_drv_add_endpoints(dev, match, remote);
246
247                 of_node_put(remote);
248         }
249
250         return count;
251 }
252
253 static int sun4i_drv_probe(struct platform_device *pdev)
254 {
255         struct component_match *match = NULL;
256         struct device_node *np = pdev->dev.of_node;
257         int i, count = 0;
258
259         for (i = 0;; i++) {
260                 struct device_node *pipeline = of_parse_phandle(np,
261                                                                 "allwinner,pipelines",
262                                                                 i);
263                 if (!pipeline)
264                         break;
265
266                 count += sun4i_drv_add_endpoints(&pdev->dev, &match,
267                                                 pipeline);
268                 of_node_put(pipeline);
269
270                 DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
271                                  count, i);
272         }
273
274         if (count)
275                 return component_master_add_with_match(&pdev->dev,
276                                                        &sun4i_drv_master_ops,
277                                                        match);
278         else
279                 return 0;
280 }
281
282 static int sun4i_drv_remove(struct platform_device *pdev)
283 {
284         return 0;
285 }
286
287 static const struct of_device_id sun4i_drv_of_table[] = {
288         { .compatible = "allwinner,sun5i-a13-display-engine" },
289         { .compatible = "allwinner,sun6i-a31-display-engine" },
290         { .compatible = "allwinner,sun6i-a31s-display-engine" },
291         { .compatible = "allwinner,sun8i-a33-display-engine" },
292         { }
293 };
294 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
295
296 static struct platform_driver sun4i_drv_platform_driver = {
297         .probe          = sun4i_drv_probe,
298         .remove         = sun4i_drv_remove,
299         .driver         = {
300                 .name           = "sun4i-drm",
301                 .of_match_table = sun4i_drv_of_table,
302         },
303 };
304 module_platform_driver(sun4i_drv_platform_driver);
305
306 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
307 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
308 MODULE_DESCRIPTION("Allwinner A10 Display Engine DRM/KMS Driver");
309 MODULE_LICENSE("GPL");