1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2014 Endless Mobile
8 * Jasper St. Pierre <jstpierre@mecheye.net>
11 #include <linux/component.h>
12 #include <linux/module.h>
13 #include <linux/of_graph.h>
14 #include <linux/platform_device.h>
15 #include <linux/soc/amlogic/meson-canvas.h>
17 #include <drm/drm_atomic_helper.h>
18 #include <drm/drm_drv.h>
19 #include <drm/drm_fb_helper.h>
20 #include <drm/drm_gem_cma_helper.h>
21 #include <drm/drm_gem_framebuffer_helper.h>
22 #include <drm/drm_irq.h>
23 #include <drm/drm_modeset_helper_vtables.h>
24 #include <drm/drm_probe_helper.h>
25 #include <drm/drm_vblank.h>
27 #include "meson_crtc.h"
28 #include "meson_drv.h"
29 #include "meson_overlay.h"
30 #include "meson_plane.h"
31 #include "meson_osd_afbcd.h"
32 #include "meson_registers.h"
33 #include "meson_venc_cvbs.h"
34 #include "meson_viu.h"
35 #include "meson_vpp.h"
36 #include "meson_rdma.h"
38 #define DRIVER_NAME "meson"
39 #define DRIVER_DESC "Amlogic Meson DRM driver"
42 * DOC: Video Processing Unit
44 * VPU Handles the Global Video Processing, it includes management of the
45 * clocks gates, blocks reset lines and power domains.
49 * - Full reset of entire video processing HW blocks
50 * - Scaling and setup of the VPU clock
52 * - Powering up video processing HW blocks
53 * - Powering Up HDMI controller and PHY
56 static const struct drm_mode_config_funcs meson_mode_config_funcs
= {
57 .atomic_check
= drm_atomic_helper_check
,
58 .atomic_commit
= drm_atomic_helper_commit
,
59 .fb_create
= drm_gem_fb_create
,
62 static const struct drm_mode_config_helper_funcs meson_mode_config_helpers
= {
63 .atomic_commit_tail
= drm_atomic_helper_commit_tail_rpm
,
66 static irqreturn_t
meson_irq(int irq
, void *arg
)
68 struct drm_device
*dev
= arg
;
69 struct meson_drm
*priv
= dev
->dev_private
;
71 (void)readl_relaxed(priv
->io_base
+ _REG(VENC_INTFLAG
));
78 static int meson_dumb_create(struct drm_file
*file
, struct drm_device
*dev
,
79 struct drm_mode_create_dumb
*args
)
82 * We need 64bytes aligned stride, and PAGE aligned size
84 args
->pitch
= ALIGN(DIV_ROUND_UP(args
->width
* args
->bpp
, 8), SZ_64
);
85 args
->size
= PAGE_ALIGN(args
->pitch
* args
->height
);
87 return drm_gem_cma_dumb_create_internal(file
, dev
, args
);
90 DEFINE_DRM_GEM_CMA_FOPS(fops
);
92 static struct drm_driver meson_driver
= {
93 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_ATOMIC
,
96 .irq_handler
= meson_irq
,
99 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
100 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
101 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
102 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
103 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
104 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
105 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
108 .dumb_create
= meson_dumb_create
,
109 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
110 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
121 static bool meson_vpu_has_available_connectors(struct device
*dev
)
123 struct device_node
*ep
, *remote
;
125 /* Parses each endpoint and check if remote exists */
126 for_each_endpoint_of_node(dev
->of_node
, ep
) {
127 /* If the endpoint node exists, consider it enabled */
128 remote
= of_graph_get_remote_port(ep
);
136 static struct regmap_config meson_regmap_config
= {
140 .max_register
= 0x1000,
143 static void meson_vpu_init(struct meson_drm
*priv
)
148 * Slave dc0 and dc5 connected to master port 1.
149 * By default other slaves are connected to master port 0.
151 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
152 VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
153 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C1
));
155 /* Slave dc0 connected to master port 1 */
156 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
157 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C2
));
159 /* Slave dc4 and dc7 connected to master port 1 */
160 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
161 VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
162 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L2C1
));
164 /* Slave dc1 connected to master port 1 */
165 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
166 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_WRARB_MODE_L2C1
));
169 static void meson_remove_framebuffers(void)
171 struct apertures_struct
*ap
;
173 ap
= alloc_apertures(1);
177 /* The framebuffer can be located anywhere in RAM */
178 ap
->ranges
[0].base
= 0;
179 ap
->ranges
[0].size
= ~0;
181 drm_fb_helper_remove_conflicting_framebuffers(ap
, "meson-drm-fb",
186 static int meson_drv_bind_master(struct device
*dev
, bool has_components
)
188 struct platform_device
*pdev
= to_platform_device(dev
);
189 const struct meson_drm_match_data
*match
;
190 struct meson_drm
*priv
;
191 struct drm_device
*drm
;
192 struct resource
*res
;
196 /* Checks if an output connector is available */
197 if (!meson_vpu_has_available_connectors(dev
)) {
198 dev_err(dev
, "No output connector available\n");
202 match
= of_device_get_match_data(dev
);
206 drm
= drm_dev_alloc(&meson_driver
, dev
);
210 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
215 drm
->dev_private
= priv
;
218 priv
->compat
= match
->compat
;
219 priv
->afbcd
.ops
= match
->afbcd_ops
;
221 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "vpu");
222 regs
= devm_ioremap_resource(dev
, res
);
228 priv
->io_base
= regs
;
230 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "hhi");
235 /* Simply ioremap since it may be a shared register zone */
236 regs
= devm_ioremap(dev
, res
->start
, resource_size(res
));
238 ret
= -EADDRNOTAVAIL
;
242 priv
->hhi
= devm_regmap_init_mmio(dev
, regs
,
243 &meson_regmap_config
);
244 if (IS_ERR(priv
->hhi
)) {
245 dev_err(&pdev
->dev
, "Couldn't create the HHI regmap\n");
246 ret
= PTR_ERR(priv
->hhi
);
250 priv
->canvas
= meson_canvas_get(dev
);
251 if (IS_ERR(priv
->canvas
)) {
252 ret
= PTR_ERR(priv
->canvas
);
256 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_osd1
);
259 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_0
);
261 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
264 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_1
);
266 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
267 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_0
);
270 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_2
);
272 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
273 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_0
);
274 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_1
);
278 priv
->vsync_irq
= platform_get_irq(pdev
, 0);
280 ret
= drm_vblank_init(drm
, 1);
284 /* Remove early framebuffers (ie. simplefb) */
285 meson_remove_framebuffers();
287 drm_mode_config_init(drm
);
288 drm
->mode_config
.max_width
= 3840;
289 drm
->mode_config
.max_height
= 2160;
290 drm
->mode_config
.funcs
= &meson_mode_config_funcs
;
291 drm
->mode_config
.helper_private
= &meson_mode_config_helpers
;
293 /* Hardware Initialization */
295 meson_vpu_init(priv
);
296 meson_venc_init(priv
);
297 meson_vpp_init(priv
);
298 meson_viu_init(priv
);
299 if (priv
->afbcd
.ops
) {
300 ret
= priv
->afbcd
.ops
->init(priv
);
305 /* Encoder Initialization */
307 ret
= meson_venc_cvbs_create(priv
);
311 if (has_components
) {
312 ret
= component_bind_all(drm
->dev
, drm
);
314 dev_err(drm
->dev
, "Couldn't bind all components\n");
319 ret
= meson_plane_create(priv
);
323 ret
= meson_overlay_create(priv
);
327 ret
= meson_crtc_create(priv
);
331 ret
= drm_irq_install(drm
, priv
->vsync_irq
);
335 drm_mode_config_reset(drm
);
337 drm_kms_helper_poll_init(drm
);
339 platform_set_drvdata(pdev
, priv
);
341 ret
= drm_dev_register(drm
, 0);
345 drm_fbdev_generic_setup(drm
, 32);
350 drm_irq_uninstall(drm
);
357 static int meson_drv_bind(struct device
*dev
)
359 return meson_drv_bind_master(dev
, true);
362 static void meson_drv_unbind(struct device
*dev
)
364 struct meson_drm
*priv
= dev_get_drvdata(dev
);
365 struct drm_device
*drm
= priv
->drm
;
368 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
369 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_0
);
370 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_1
);
371 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_2
);
374 if (priv
->afbcd
.ops
) {
375 priv
->afbcd
.ops
->reset(priv
);
376 meson_rdma_free(priv
);
379 drm_dev_unregister(drm
);
380 drm_irq_uninstall(drm
);
381 drm_kms_helper_poll_fini(drm
);
382 drm_mode_config_cleanup(drm
);
386 static const struct component_master_ops meson_drv_master_ops
= {
387 .bind
= meson_drv_bind
,
388 .unbind
= meson_drv_unbind
,
391 static int __maybe_unused
meson_drv_pm_suspend(struct device
*dev
)
393 struct meson_drm
*priv
= dev_get_drvdata(dev
);
398 return drm_mode_config_helper_suspend(priv
->drm
);
401 static int __maybe_unused
meson_drv_pm_resume(struct device
*dev
)
403 struct meson_drm
*priv
= dev_get_drvdata(dev
);
408 meson_vpu_init(priv
);
409 meson_venc_init(priv
);
410 meson_vpp_init(priv
);
411 meson_viu_init(priv
);
413 priv
->afbcd
.ops
->init(priv
);
415 drm_mode_config_helper_resume(priv
->drm
);
420 static int compare_of(struct device
*dev
, void *data
)
422 DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n",
425 return dev
->of_node
== data
;
428 /* Possible connectors nodes to ignore */
429 static const struct of_device_id connectors_match
[] = {
430 { .compatible
= "composite-video-connector" },
431 { .compatible
= "svideo-connector" },
432 { .compatible
= "hdmi-connector" },
433 { .compatible
= "dvi-connector" },
437 static int meson_probe_remote(struct platform_device
*pdev
,
438 struct component_match
**match
,
439 struct device_node
*parent
,
440 struct device_node
*remote
)
442 struct device_node
*ep
, *remote_node
;
445 /* If node is a connector, return and do not add to match table */
446 if (of_match_node(connectors_match
, remote
))
449 component_match_add(&pdev
->dev
, match
, compare_of
, remote
);
451 for_each_endpoint_of_node(remote
, ep
) {
452 remote_node
= of_graph_get_remote_port_parent(ep
);
454 remote_node
== parent
|| /* Ignore parent endpoint */
455 !of_device_is_available(remote_node
)) {
456 of_node_put(remote_node
);
460 count
+= meson_probe_remote(pdev
, match
, remote
, remote_node
);
462 of_node_put(remote_node
);
468 static int meson_drv_probe(struct platform_device
*pdev
)
470 struct component_match
*match
= NULL
;
471 struct device_node
*np
= pdev
->dev
.of_node
;
472 struct device_node
*ep
, *remote
;
475 for_each_endpoint_of_node(np
, ep
) {
476 remote
= of_graph_get_remote_port_parent(ep
);
477 if (!remote
|| !of_device_is_available(remote
)) {
482 count
+= meson_probe_remote(pdev
, &match
, np
, remote
);
487 return meson_drv_bind_master(&pdev
->dev
, false);
489 /* If some endpoints were found, initialize the nodes */
491 dev_info(&pdev
->dev
, "Queued %d outputs on vpu\n", count
);
493 return component_master_add_with_match(&pdev
->dev
,
494 &meson_drv_master_ops
,
498 /* If no output endpoints were available, simply bail out */
502 static struct meson_drm_match_data meson_drm_gxbb_data
= {
503 .compat
= VPU_COMPATIBLE_GXBB
,
506 static struct meson_drm_match_data meson_drm_gxl_data
= {
507 .compat
= VPU_COMPATIBLE_GXL
,
510 static struct meson_drm_match_data meson_drm_gxm_data
= {
511 .compat
= VPU_COMPATIBLE_GXM
,
512 .afbcd_ops
= &meson_afbcd_gxm_ops
,
515 static struct meson_drm_match_data meson_drm_g12a_data
= {
516 .compat
= VPU_COMPATIBLE_G12A
,
517 .afbcd_ops
= &meson_afbcd_g12a_ops
,
520 static const struct of_device_id dt_match
[] = {
521 { .compatible
= "amlogic,meson-gxbb-vpu",
522 .data
= (void *)&meson_drm_gxbb_data
},
523 { .compatible
= "amlogic,meson-gxl-vpu",
524 .data
= (void *)&meson_drm_gxl_data
},
525 { .compatible
= "amlogic,meson-gxm-vpu",
526 .data
= (void *)&meson_drm_gxm_data
},
527 { .compatible
= "amlogic,meson-g12a-vpu",
528 .data
= (void *)&meson_drm_g12a_data
},
531 MODULE_DEVICE_TABLE(of
, dt_match
);
533 static const struct dev_pm_ops meson_drv_pm_ops
= {
534 SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend
, meson_drv_pm_resume
)
537 static struct platform_driver meson_drm_platform_driver
= {
538 .probe
= meson_drv_probe
,
541 .of_match_table
= dt_match
,
542 .pm
= &meson_drv_pm_ops
,
546 module_platform_driver(meson_drm_platform_driver
);
548 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
549 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
550 MODULE_DESCRIPTION(DRIVER_DESC
);
551 MODULE_LICENSE("GPL");