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/aperture.h>
12 #include <linux/component.h>
13 #include <linux/module.h>
14 #include <linux/of_graph.h>
15 #include <linux/sys_soc.h>
16 #include <linux/platform_device.h>
17 #include <linux/soc/amlogic/meson-canvas.h>
19 #include <drm/drm_atomic_helper.h>
20 #include <drm/drm_client_setup.h>
21 #include <drm/drm_drv.h>
22 #include <drm/drm_fbdev_dma.h>
23 #include <drm/drm_gem_dma_helper.h>
24 #include <drm/drm_gem_framebuffer_helper.h>
25 #include <drm/drm_modeset_helper_vtables.h>
26 #include <drm/drm_module.h>
27 #include <drm/drm_probe_helper.h>
28 #include <drm/drm_vblank.h>
30 #include "meson_crtc.h"
31 #include "meson_drv.h"
32 #include "meson_overlay.h"
33 #include "meson_plane.h"
34 #include "meson_osd_afbcd.h"
35 #include "meson_registers.h"
36 #include "meson_encoder_cvbs.h"
37 #include "meson_encoder_hdmi.h"
38 #include "meson_encoder_dsi.h"
39 #include "meson_viu.h"
40 #include "meson_vpp.h"
41 #include "meson_rdma.h"
43 #define DRIVER_NAME "meson"
44 #define DRIVER_DESC "Amlogic Meson DRM driver"
47 * DOC: Video Processing Unit
49 * VPU Handles the Global Video Processing, it includes management of the
50 * clocks gates, blocks reset lines and power domains.
54 * - Full reset of entire video processing HW blocks
55 * - Scaling and setup of the VPU clock
57 * - Powering up video processing HW blocks
58 * - Powering Up HDMI controller and PHY
61 static const struct drm_mode_config_funcs meson_mode_config_funcs
= {
62 .atomic_check
= drm_atomic_helper_check
,
63 .atomic_commit
= drm_atomic_helper_commit
,
64 .fb_create
= drm_gem_fb_create
,
67 static const struct drm_mode_config_helper_funcs meson_mode_config_helpers
= {
68 .atomic_commit_tail
= drm_atomic_helper_commit_tail_rpm
,
71 static irqreturn_t
meson_irq(int irq
, void *arg
)
73 struct drm_device
*dev
= arg
;
74 struct meson_drm
*priv
= dev
->dev_private
;
76 (void)readl_relaxed(priv
->io_base
+ _REG(VENC_INTFLAG
));
83 static int meson_dumb_create(struct drm_file
*file
, struct drm_device
*dev
,
84 struct drm_mode_create_dumb
*args
)
87 * We need 64bytes aligned stride, and PAGE aligned size
89 args
->pitch
= ALIGN(DIV_ROUND_UP(args
->width
* args
->bpp
, 8), SZ_64
);
90 args
->size
= PAGE_ALIGN(args
->pitch
* args
->height
);
92 return drm_gem_dma_dumb_create_internal(file
, dev
, args
);
95 DEFINE_DRM_GEM_DMA_FOPS(fops
);
97 static const struct drm_driver meson_driver
= {
98 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_ATOMIC
,
101 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create
),
102 DRM_FBDEV_DMA_DRIVER_OPS
,
113 static bool meson_vpu_has_available_connectors(struct device
*dev
)
115 struct device_node
*ep
, *remote
;
117 /* Parses each endpoint and check if remote exists */
118 for_each_endpoint_of_node(dev
->of_node
, ep
) {
119 /* If the endpoint node exists, consider it enabled */
120 remote
= of_graph_get_remote_port(ep
);
131 static const struct regmap_config meson_regmap_config
= {
135 .max_register
= 0x1000,
138 static void meson_vpu_init(struct meson_drm
*priv
)
143 * Slave dc0 and dc5 connected to master port 1.
144 * By default other slaves are connected to master port 0.
146 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
147 VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
148 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C1
));
150 /* Slave dc0 connected to master port 1 */
151 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
152 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L1C2
));
154 /* Slave dc4 and dc7 connected to master port 1 */
155 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
156 VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
157 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_RDARB_MODE_L2C1
));
159 /* Slave dc1 connected to master port 1 */
160 value
= VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
161 writel_relaxed(value
, priv
->io_base
+ _REG(VPU_WRARB_MODE_L2C1
));
164 struct meson_drm_soc_attr
{
165 struct meson_drm_soc_limits limits
;
166 const struct soc_device_attribute
*attrs
;
169 static const struct meson_drm_soc_attr meson_drm_soc_attrs
[] = {
170 /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
173 .max_hdmi_phy_freq
= 1650000,
175 .attrs
= (const struct soc_device_attribute
[]) {
176 { .soc_id
= "GXL (S805*)", },
182 static int meson_drv_bind_master(struct device
*dev
, bool has_components
)
184 struct platform_device
*pdev
= to_platform_device(dev
);
185 const struct meson_drm_match_data
*match
;
186 struct meson_drm
*priv
;
187 struct drm_device
*drm
;
188 struct resource
*res
;
192 /* Checks if an output connector is available */
193 if (!meson_vpu_has_available_connectors(dev
)) {
194 dev_err(dev
, "No output connector available\n");
198 match
= of_device_get_match_data(dev
);
202 drm
= drm_dev_alloc(&meson_driver
, dev
);
206 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
211 drm
->dev_private
= priv
;
214 priv
->compat
= match
->compat
;
215 priv
->afbcd
.ops
= match
->afbcd_ops
;
217 regs
= devm_platform_ioremap_resource_byname(pdev
, "vpu");
223 priv
->io_base
= regs
;
225 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "hhi");
230 /* Simply ioremap since it may be a shared register zone */
231 regs
= devm_ioremap(dev
, res
->start
, resource_size(res
));
233 ret
= -EADDRNOTAVAIL
;
237 priv
->hhi
= devm_regmap_init_mmio(dev
, regs
,
238 &meson_regmap_config
);
239 if (IS_ERR(priv
->hhi
)) {
240 dev_err(&pdev
->dev
, "Couldn't create the HHI regmap\n");
241 ret
= PTR_ERR(priv
->hhi
);
245 priv
->canvas
= meson_canvas_get(dev
);
246 if (IS_ERR(priv
->canvas
)) {
247 ret
= PTR_ERR(priv
->canvas
);
251 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_osd1
);
254 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_0
);
256 goto free_canvas_osd1
;
257 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_1
);
259 goto free_canvas_vd1_0
;
260 ret
= meson_canvas_alloc(priv
->canvas
, &priv
->canvas_id_vd1_2
);
262 goto free_canvas_vd1_1
;
264 priv
->vsync_irq
= platform_get_irq(pdev
, 0);
266 ret
= drm_vblank_init(drm
, 1);
268 goto free_canvas_vd1_2
;
270 /* Assign limits per soc revision/package */
271 for (i
= 0 ; i
< ARRAY_SIZE(meson_drm_soc_attrs
) ; ++i
) {
272 if (soc_device_match(meson_drm_soc_attrs
[i
].attrs
)) {
273 priv
->limits
= &meson_drm_soc_attrs
[i
].limits
;
279 * Remove early framebuffers (ie. simplefb). The framebuffer can be
280 * located anywhere in RAM
282 ret
= aperture_remove_all_conflicting_devices(meson_driver
.name
);
284 goto free_canvas_vd1_2
;
286 ret
= drmm_mode_config_init(drm
);
288 goto free_canvas_vd1_2
;
289 drm
->mode_config
.max_width
= 3840;
290 drm
->mode_config
.max_height
= 2160;
291 drm
->mode_config
.funcs
= &meson_mode_config_funcs
;
292 drm
->mode_config
.helper_private
= &meson_mode_config_helpers
;
294 /* Hardware Initialization */
296 meson_vpu_init(priv
);
297 meson_venc_init(priv
);
298 meson_vpp_init(priv
);
299 meson_viu_init(priv
);
300 if (priv
->afbcd
.ops
) {
301 ret
= priv
->afbcd
.ops
->init(priv
);
303 goto free_canvas_vd1_2
;
306 /* Encoder Initialization */
308 ret
= meson_encoder_cvbs_probe(priv
);
312 if (has_components
) {
313 ret
= component_bind_all(dev
, drm
);
315 dev_err(drm
->dev
, "Couldn't bind all components\n");
316 /* Do not try to unbind */
317 has_components
= false;
322 ret
= meson_encoder_hdmi_probe(priv
);
326 if (meson_vpu_is_compatible(priv
, VPU_COMPATIBLE_G12A
)) {
327 ret
= meson_encoder_dsi_probe(priv
);
332 ret
= meson_plane_create(priv
);
336 ret
= meson_overlay_create(priv
);
340 ret
= meson_crtc_create(priv
);
344 ret
= request_irq(priv
->vsync_irq
, meson_irq
, 0, drm
->driver
->name
, drm
);
348 drm_mode_config_reset(drm
);
350 drm_kms_helper_poll_init(drm
);
352 platform_set_drvdata(pdev
, priv
);
354 ret
= drm_dev_register(drm
, 0);
358 drm_client_setup(drm
, NULL
);
363 free_irq(priv
->vsync_irq
, drm
);
366 priv
->afbcd
.ops
->exit(priv
);
368 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_2
);
370 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_1
);
372 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_0
);
374 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
378 meson_encoder_dsi_remove(priv
);
379 meson_encoder_hdmi_remove(priv
);
380 meson_encoder_cvbs_remove(priv
);
383 component_unbind_all(dev
, drm
);
388 static int meson_drv_bind(struct device
*dev
)
390 return meson_drv_bind_master(dev
, true);
393 static void meson_drv_unbind(struct device
*dev
)
395 struct meson_drm
*priv
= dev_get_drvdata(dev
);
396 struct drm_device
*drm
= priv
->drm
;
399 meson_canvas_free(priv
->canvas
, priv
->canvas_id_osd1
);
400 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_0
);
401 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_1
);
402 meson_canvas_free(priv
->canvas
, priv
->canvas_id_vd1_2
);
405 drm_dev_unregister(drm
);
406 drm_kms_helper_poll_fini(drm
);
407 drm_atomic_helper_shutdown(drm
);
408 free_irq(priv
->vsync_irq
, drm
);
411 meson_encoder_dsi_remove(priv
);
412 meson_encoder_hdmi_remove(priv
);
413 meson_encoder_cvbs_remove(priv
);
415 component_unbind_all(dev
, drm
);
418 priv
->afbcd
.ops
->exit(priv
);
421 static const struct component_master_ops meson_drv_master_ops
= {
422 .bind
= meson_drv_bind
,
423 .unbind
= meson_drv_unbind
,
426 static int __maybe_unused
meson_drv_pm_suspend(struct device
*dev
)
428 struct meson_drm
*priv
= dev_get_drvdata(dev
);
433 return drm_mode_config_helper_suspend(priv
->drm
);
436 static int __maybe_unused
meson_drv_pm_resume(struct device
*dev
)
438 struct meson_drm
*priv
= dev_get_drvdata(dev
);
443 meson_vpu_init(priv
);
444 meson_venc_init(priv
);
445 meson_vpp_init(priv
);
446 meson_viu_init(priv
);
448 priv
->afbcd
.ops
->init(priv
);
450 return drm_mode_config_helper_resume(priv
->drm
);
453 static void meson_drv_shutdown(struct platform_device
*pdev
)
455 struct meson_drm
*priv
= dev_get_drvdata(&pdev
->dev
);
460 drm_kms_helper_poll_fini(priv
->drm
);
461 drm_atomic_helper_shutdown(priv
->drm
);
465 * Only devices to use as components
466 * TOFIX: get rid of components when we can finally
467 * get meson_dx_hdmi to stop using the meson_drm
468 * private structure for HHI registers.
470 static const struct of_device_id components_dev_match
[] = {
471 { .compatible
= "amlogic,meson-gxbb-dw-hdmi" },
472 { .compatible
= "amlogic,meson-gxl-dw-hdmi" },
473 { .compatible
= "amlogic,meson-gxm-dw-hdmi" },
474 { .compatible
= "amlogic,meson-g12a-dw-hdmi" },
478 static int meson_drv_probe(struct platform_device
*pdev
)
480 struct component_match
*match
= NULL
;
481 struct device_node
*np
= pdev
->dev
.of_node
;
482 struct device_node
*ep
, *remote
;
485 for_each_endpoint_of_node(np
, ep
) {
486 remote
= of_graph_get_remote_port_parent(ep
);
487 if (!remote
|| !of_device_is_available(remote
)) {
492 if (of_match_node(components_dev_match
, remote
)) {
493 component_match_add(&pdev
->dev
, &match
, component_compare_of
, remote
);
495 dev_dbg(&pdev
->dev
, "parent %pOF remote match add %pOF parent %s\n",
496 np
, remote
, dev_name(&pdev
->dev
));
505 return meson_drv_bind_master(&pdev
->dev
, false);
507 /* If some endpoints were found, initialize the nodes */
509 dev_info(&pdev
->dev
, "Queued %d outputs on vpu\n", count
);
511 return component_master_add_with_match(&pdev
->dev
,
512 &meson_drv_master_ops
,
516 /* If no output endpoints were available, simply bail out */
520 static void meson_drv_remove(struct platform_device
*pdev
)
522 component_master_del(&pdev
->dev
, &meson_drv_master_ops
);
525 static struct meson_drm_match_data meson_drm_gxbb_data
= {
526 .compat
= VPU_COMPATIBLE_GXBB
,
529 static struct meson_drm_match_data meson_drm_gxl_data
= {
530 .compat
= VPU_COMPATIBLE_GXL
,
533 static struct meson_drm_match_data meson_drm_gxm_data
= {
534 .compat
= VPU_COMPATIBLE_GXM
,
535 .afbcd_ops
= &meson_afbcd_gxm_ops
,
538 static struct meson_drm_match_data meson_drm_g12a_data
= {
539 .compat
= VPU_COMPATIBLE_G12A
,
540 .afbcd_ops
= &meson_afbcd_g12a_ops
,
543 static const struct of_device_id dt_match
[] = {
544 { .compatible
= "amlogic,meson-gxbb-vpu",
545 .data
= (void *)&meson_drm_gxbb_data
},
546 { .compatible
= "amlogic,meson-gxl-vpu",
547 .data
= (void *)&meson_drm_gxl_data
},
548 { .compatible
= "amlogic,meson-gxm-vpu",
549 .data
= (void *)&meson_drm_gxm_data
},
550 { .compatible
= "amlogic,meson-g12a-vpu",
551 .data
= (void *)&meson_drm_g12a_data
},
554 MODULE_DEVICE_TABLE(of
, dt_match
);
556 static const struct dev_pm_ops meson_drv_pm_ops
= {
557 SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend
, meson_drv_pm_resume
)
560 static struct platform_driver meson_drm_platform_driver
= {
561 .probe
= meson_drv_probe
,
562 .remove
= meson_drv_remove
,
563 .shutdown
= meson_drv_shutdown
,
566 .of_match_table
= dt_match
,
567 .pm
= &meson_drv_pm_ops
,
571 drm_module_platform_driver(meson_drm_platform_driver
);
573 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
574 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
575 MODULE_DESCRIPTION(DRIVER_DESC
);
576 MODULE_LICENSE("GPL");