2 * Copyright (C) 2016 BayLibre, SAS
3 * Author: Neil Armstrong <narmstrong@baylibre.com>
4 * Copyright (C) 2014 Endless Mobile
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.
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.
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/>.
20 * Jasper St. Pierre <jstpierre@mecheye.net>
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/mutex.h>
26 #include <linux/platform_device.h>
27 #include <linux/component.h>
28 #include <linux/of_graph.h>
31 #include <drm/drm_atomic.h>
32 #include <drm/drm_atomic_helper.h>
33 #include <drm/drm_flip_work.h>
34 #include <drm/drm_crtc_helper.h>
35 #include <drm/drm_plane_helper.h>
36 #include <drm/drm_gem_cma_helper.h>
37 #include <drm/drm_gem_framebuffer_helper.h>
38 #include <drm/drm_fb_cma_helper.h>
39 #include <drm/drm_rect.h>
40 #include <drm/drm_fb_helper.h>
42 #include "meson_drv.h"
43 #include "meson_plane.h"
44 #include "meson_crtc.h"
45 #include "meson_venc_cvbs.h"
47 #include "meson_vpp.h"
48 #include "meson_viu.h"
49 #include "meson_venc.h"
50 #include "meson_canvas.h"
51 #include "meson_registers.h"
53 #define DRIVER_NAME "meson"
54 #define DRIVER_DESC "Amlogic Meson DRM driver"
57 * DOC: Video Processing Unit
59 * VPU Handles the Global Video Processing, it includes management of the
60 * clocks gates, blocks reset lines and power domains.
64 * - Full reset of entire video processing HW blocks
65 * - Scaling and setup of the VPU clock
67 * - Powering up video processing HW blocks
68 * - Powering Up HDMI controller and PHY
71 static void meson_fb_output_poll_changed(struct drm_device
*dev
)
73 struct meson_drm
*priv
= dev
->dev_private
;
75 drm_fbdev_cma_hotplug_event(priv
->fbdev
);
78 static const struct drm_mode_config_funcs meson_mode_config_funcs
= {
79 .output_poll_changed
= meson_fb_output_poll_changed
,
80 .atomic_check
= drm_atomic_helper_check
,
81 .atomic_commit
= drm_atomic_helper_commit
,
82 .fb_create
= drm_gem_fb_create
,
85 static irqreturn_t
meson_irq(int irq
, void *arg
)
87 struct drm_device
*dev
= arg
;
88 struct meson_drm
*priv
= dev
->dev_private
;
90 (void)readl_relaxed(priv
->io_base
+ _REG(VENC_INTFLAG
));
97 DEFINE_DRM_GEM_CMA_FOPS(fops
);
99 static struct drm_driver meson_driver
= {
100 .driver_features
= DRIVER_HAVE_IRQ
| DRIVER_GEM
|
101 DRIVER_MODESET
| DRIVER_PRIME
|
105 .irq_handler
= meson_irq
,
108 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
109 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
110 .gem_prime_import
= drm_gem_prime_import
,
111 .gem_prime_export
= drm_gem_prime_export
,
112 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
113 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
114 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
115 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
116 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
119 .dumb_create
= drm_gem_cma_dumb_create
,
120 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
121 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
132 static bool meson_vpu_has_available_connectors(struct device
*dev
)
134 struct device_node
*ep
, *remote
;
136 /* Parses each endpoint and check if remote exists */
137 for_each_endpoint_of_node(dev
->of_node
, ep
) {
138 /* If the endpoint node exists, consider it enabled */
139 remote
= of_graph_get_remote_port(ep
);
147 static struct regmap_config meson_regmap_config
= {
151 .max_register
= 0x1000,
154 static void meson_vpu_init(struct meson_drm
*priv
)
156 writel_relaxed(0x210000, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C1
));
157 writel_relaxed(0x10000, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C2
));
158 writel_relaxed(0x900000, priv
->io_base
+ _REG(VPU_RDARB_MODE_L2C1
));
159 writel_relaxed(0x20000, priv
->io_base
+ _REG(VPU_WRARB_MODE_L2C1
));
162 static int meson_drv_bind_master(struct device
*dev
, bool has_components
)
164 struct platform_device
*pdev
= to_platform_device(dev
);
165 struct meson_drm
*priv
;
166 struct drm_device
*drm
;
167 struct resource
*res
;
171 /* Checks if an output connector is available */
172 if (!meson_vpu_has_available_connectors(dev
)) {
173 dev_err(dev
, "No output connector available\n");
177 drm
= drm_dev_alloc(&meson_driver
, dev
);
181 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
186 drm
->dev_private
= priv
;
190 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "vpu");
191 regs
= devm_ioremap_resource(dev
, res
);
193 return PTR_ERR(regs
);
195 priv
->io_base
= regs
;
197 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "hhi");
198 /* Simply ioremap since it may be a shared register zone */
199 regs
= devm_ioremap(dev
, res
->start
, resource_size(res
));
201 return -EADDRNOTAVAIL
;
203 priv
->hhi
= devm_regmap_init_mmio(dev
, regs
,
204 &meson_regmap_config
);
205 if (IS_ERR(priv
->hhi
)) {
206 dev_err(&pdev
->dev
, "Couldn't create the HHI regmap\n");
207 return PTR_ERR(priv
->hhi
);
210 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "dmc");
211 /* Simply ioremap since it may be a shared register zone */
212 regs
= devm_ioremap(dev
, res
->start
, resource_size(res
));
214 return -EADDRNOTAVAIL
;
216 priv
->dmc
= devm_regmap_init_mmio(dev
, regs
,
217 &meson_regmap_config
);
218 if (IS_ERR(priv
->dmc
)) {
219 dev_err(&pdev
->dev
, "Couldn't create the DMC regmap\n");
220 return PTR_ERR(priv
->dmc
);
223 priv
->vsync_irq
= platform_get_irq(pdev
, 0);
225 drm_vblank_init(drm
, 1);
226 drm_mode_config_init(drm
);
227 drm
->mode_config
.max_width
= 3840;
228 drm
->mode_config
.max_height
= 2160;
229 drm
->mode_config
.funcs
= &meson_mode_config_funcs
;
231 /* Hardware Initialization */
233 meson_vpu_init(priv
);
234 meson_venc_init(priv
);
235 meson_vpp_init(priv
);
236 meson_viu_init(priv
);
238 /* Encoder Initialization */
240 ret
= meson_venc_cvbs_create(priv
);
244 if (has_components
) {
245 ret
= component_bind_all(drm
->dev
, drm
);
247 dev_err(drm
->dev
, "Couldn't bind all components\n");
252 ret
= meson_plane_create(priv
);
256 ret
= meson_crtc_create(priv
);
260 ret
= drm_irq_install(drm
, priv
->vsync_irq
);
264 drm_mode_config_reset(drm
);
266 priv
->fbdev
= drm_fbdev_cma_init(drm
, 32,
267 drm
->mode_config
.num_connector
);
268 if (IS_ERR(priv
->fbdev
)) {
269 ret
= PTR_ERR(priv
->fbdev
);
273 drm_kms_helper_poll_init(drm
);
275 platform_set_drvdata(pdev
, priv
);
277 ret
= drm_dev_register(drm
, 0);
289 static int meson_drv_bind(struct device
*dev
)
291 return meson_drv_bind_master(dev
, true);
294 static void meson_drv_unbind(struct device
*dev
)
296 struct drm_device
*drm
= dev_get_drvdata(dev
);
297 struct meson_drm
*priv
= drm
->dev_private
;
299 drm_dev_unregister(drm
);
300 drm_kms_helper_poll_fini(drm
);
301 drm_fbdev_cma_fini(priv
->fbdev
);
302 drm_mode_config_cleanup(drm
);
307 static const struct component_master_ops meson_drv_master_ops
= {
308 .bind
= meson_drv_bind
,
309 .unbind
= meson_drv_unbind
,
312 static int compare_of(struct device
*dev
, void *data
)
314 DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n",
317 return dev
->of_node
== data
;
320 /* Possible connectors nodes to ignore */
321 static const struct of_device_id connectors_match
[] = {
322 { .compatible
= "composite-video-connector" },
323 { .compatible
= "svideo-connector" },
324 { .compatible
= "hdmi-connector" },
325 { .compatible
= "dvi-connector" },
329 static int meson_probe_remote(struct platform_device
*pdev
,
330 struct component_match
**match
,
331 struct device_node
*parent
,
332 struct device_node
*remote
)
334 struct device_node
*ep
, *remote_node
;
337 /* If node is a connector, return and do not add to match table */
338 if (of_match_node(connectors_match
, remote
))
341 component_match_add(&pdev
->dev
, match
, compare_of
, remote
);
343 for_each_endpoint_of_node(remote
, ep
) {
344 remote_node
= of_graph_get_remote_port_parent(ep
);
346 remote_node
== parent
|| /* Ignore parent endpoint */
347 !of_device_is_available(remote_node
))
350 count
+= meson_probe_remote(pdev
, match
, remote
, remote_node
);
352 of_node_put(remote_node
);
358 static int meson_drv_probe(struct platform_device
*pdev
)
360 struct component_match
*match
= NULL
;
361 struct device_node
*np
= pdev
->dev
.of_node
;
362 struct device_node
*ep
, *remote
;
365 for_each_endpoint_of_node(np
, ep
) {
366 remote
= of_graph_get_remote_port_parent(ep
);
367 if (!remote
|| !of_device_is_available(remote
))
370 count
+= meson_probe_remote(pdev
, &match
, np
, remote
);
374 return meson_drv_bind_master(&pdev
->dev
, false);
376 /* If some endpoints were found, initialize the nodes */
378 dev_info(&pdev
->dev
, "Queued %d outputs on vpu\n", count
);
380 return component_master_add_with_match(&pdev
->dev
,
381 &meson_drv_master_ops
,
385 /* If no output endpoints were available, simply bail out */
389 static const struct of_device_id dt_match
[] = {
390 { .compatible
= "amlogic,meson-gxbb-vpu" },
391 { .compatible
= "amlogic,meson-gxl-vpu" },
392 { .compatible
= "amlogic,meson-gxm-vpu" },
395 MODULE_DEVICE_TABLE(of
, dt_match
);
397 static struct platform_driver meson_drm_platform_driver
= {
398 .probe
= meson_drv_probe
,
401 .of_match_table
= dt_match
,
405 module_platform_driver(meson_drm_platform_driver
);
407 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
408 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
409 MODULE_DESCRIPTION(DRIVER_DESC
);
410 MODULE_LICENSE("GPL");