[PATCH v10 10/12] drm: renesas: rz-du: Support several DU instances in one DRM device
From: Tommaso Merciai
Date: Fri Nov 13 2026 - 08:41:42 EST
RZ/G3E has two DU instances that share a single DRM device.
Add an RZG2L_DU_FEATURE_COMPONENT flag and use the component framework
for it. The first available DU instance is the master and creates the
DRM device, the other one binds to it as a component.
If only one DU instance is available, it creates the DRM device directly
at probe time, as on SoCs without the flag.
Set the index of each instance to the number of CRTCs already created.
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@xxxxxxxxxxxxxx>
---
v9->v10:
- New patch.
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 118 ++++++++++++++++++-
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 2 +
2 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
index 93d59e2cf8a4..873586f2ef28 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -7,6 +7,7 @@
* Based on rcar_du_drv.c
*/
+#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -14,10 +15,12 @@
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -170,6 +173,7 @@ static int rzg2l_du_instance_init(struct rzg2l_du_device *du,
struct drm_device *ddev)
{
struct rzg2l_du_device *rcdu;
+ int ret;
rcdu = drmm_kzalloc(ddev, sizeof(*rcdu), GFP_KERNEL);
if (!rcdu)
@@ -180,9 +184,28 @@ static int rzg2l_du_instance_init(struct rzg2l_du_device *du,
rcdu->mmio = du->mmio;
rcdu->ddev = ddev;
- return rzg2l_du_modeset_init_du(rcdu);
+ rcdu->index = ddev->mode_config.num_crtc;
+
+ ret = rzg2l_du_modeset_init_du(rcdu);
+ if (ret < 0)
+ return ret;
+
+ if (WARN_ON(drm_crtc_index(&rcdu->crtcs[0].crtc) != rcdu->index))
+ return -EINVAL;
+
+ return 0;
}
+static int rzg2l_du_instance_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ return rzg2l_du_instance_init(dev_get_drvdata(dev), data);
+}
+
+static const struct component_ops rzg2l_du_instance_ops = {
+ .bind = rzg2l_du_instance_bind,
+};
+
/* -----------------------------------------------------------------------------
* DRM device
*/
@@ -191,7 +214,7 @@ struct rzg2l_du_drm {
struct drm_device ddev;
};
-static int rzg2l_du_drm_init(struct device *dev)
+static int rzg2l_du_drm_init(struct device *dev, bool has_components)
{
struct rzg2l_du_device *du = dev_get_drvdata(dev);
struct rzg2l_du_drm *rzgdrm;
@@ -213,6 +236,12 @@ static int rzg2l_du_drm_init(struct device *dev)
if (ret < 0)
goto error;
+ if (has_components) {
+ ret = component_bind_all(dev, ddev);
+ if (ret < 0)
+ goto error;
+ }
+
ret = rzg2l_du_modeset_init_finish(ddev);
if (ret < 0)
goto error_fini;
@@ -235,6 +264,8 @@ static int rzg2l_du_drm_init(struct device *dev)
error_fini:
drm_kms_helper_poll_fini(ddev);
+ if (has_components)
+ component_unbind_all(dev, ddev);
error:
/*
* Don't use dev_err_probe(), as it would overwrite the probe deferral
@@ -246,7 +277,7 @@ static int rzg2l_du_drm_init(struct device *dev)
return ret;
}
-static void rzg2l_du_drm_fini(struct device *dev)
+static void rzg2l_du_drm_fini(struct device *dev, bool has_components)
{
struct rzg2l_du_device *du = dev_get_drvdata(dev);
struct drm_device *ddev = du->ddev;
@@ -254,6 +285,83 @@ static void rzg2l_du_drm_fini(struct device *dev)
drm_dev_unregister(ddev);
drm_atomic_helper_shutdown(ddev);
drm_kms_helper_poll_fini(ddev);
+
+ if (has_components)
+ component_unbind_all(dev, ddev);
+
+ du->ddev = NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Component master
+ */
+
+static int rzg2l_du_master_bind(struct device *dev)
+{
+ return rzg2l_du_drm_init(dev, true);
+}
+
+static void rzg2l_du_master_unbind(struct device *dev)
+{
+ rzg2l_du_drm_fini(dev, true);
+}
+
+static const struct component_master_ops rzg2l_du_master_ops = {
+ .bind = rzg2l_du_master_bind,
+ .unbind = rzg2l_du_master_unbind,
+};
+
+static int rzg2l_du_instance_probe(struct rzg2l_du_device *du)
+{
+ struct component_match *match = NULL;
+ const struct of_device_id *of_id;
+ struct device *dev = du->dev;
+
+ if (!rzg2l_du_has(du, RZG2L_DU_FEATURE_COMPONENT))
+ return rzg2l_du_drm_init(dev, false);
+
+ for_each_available_child_of_node_scoped(dev->of_node->parent, np) {
+ of_id = of_match_node(dev->driver->of_match_table, np);
+ if (!of_id || of_id->data != du->info)
+ continue;
+
+ /* The first available DU instance is the component master. */
+ if (!du->is_master) {
+ if (np != dev->of_node)
+ return component_add(dev, &rzg2l_du_instance_ops);
+
+ du->is_master = true;
+ continue;
+ }
+
+ drm_of_component_match_add(dev, &match, component_compare_of, np);
+ }
+
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+
+ if (!match)
+ return rzg2l_du_drm_init(dev, false);
+
+ return component_master_add_with_match(dev, &rzg2l_du_master_ops, match);
+}
+
+static void rzg2l_du_instance_remove(struct rzg2l_du_device *du)
+{
+ if (!rzg2l_du_has(du, RZG2L_DU_FEATURE_COMPONENT)) {
+ rzg2l_du_drm_fini(du->dev, false);
+ return;
+ }
+
+ if (!du->is_master) {
+ component_del(du->dev, &rzg2l_du_instance_ops);
+ return;
+ }
+
+ component_master_del(du->dev, &rzg2l_du_master_ops);
+
+ if (du->ddev)
+ rzg2l_du_drm_fini(du->dev, false);
}
/* -----------------------------------------------------------------------------
@@ -262,7 +370,7 @@ static void rzg2l_du_drm_fini(struct device *dev)
static void rzg2l_du_remove(struct platform_device *pdev)
{
- rzg2l_du_drm_fini(&pdev->dev);
+ rzg2l_du_instance_remove(platform_get_drvdata(pdev));
}
static void rzg2l_du_shutdown(struct platform_device *pdev)
@@ -304,7 +412,7 @@ static int rzg2l_du_probe(struct platform_device *pdev)
rcdu->info = info;
rcdu->mmio = mmio;
- return rzg2l_du_drm_init(&pdev->dev);
+ return rzg2l_du_instance_probe(rcdu);
}
static struct platform_driver rzg2l_du_platform_driver = {
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
index 2952c9d7348b..4c2e12206d83 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
@@ -21,6 +21,7 @@ struct device;
struct drm_property;
#define RZG2L_DU_FEATURE_DPIO_OE BIT(0) /* Has DPIO output enable control */
+#define RZG2L_DU_FEATURE_COMPONENT BIT(1) /* Has several DU instances in one DRM device */
enum rzg2l_du_output {
RZG2L_DU_OUTPUT_DSI0,
@@ -71,6 +72,7 @@ struct rzg2l_du_device {
struct drm_device *ddev;
unsigned int index;
+ bool is_master;
struct rzg2l_du_crtc crtcs[RZG2L_DU_MAX_CRTCS];
unsigned int num_crtcs;
--
2.54.0