[PATCH v7 09/13] drm: renesas: rz-du: Narrow active channels to DT-connected outputs
From: Tommaso Merciai
Date: Thu May 07 2026 - 05:27:19 EST
Skip CRTC creation for DU channels that have no connected output in DT.
Scan the device endpoint graph to determine which channels have at least
one endpoint with a remote-endpoint phandle, and restrict the active
channels mask to that subset.
On EP-routing platforms (RZG2L_DU_FEATURE_EP_ROUTING), port@N maps
directly to DU channel N. On port-based platforms, any connected
endpoint activates the single hardware channel.
Update rzg2l_du_vsps_init() to compute the renesas,vsps property stride
from the full hardware channel count (hweight8(channels_mask)) rather
than the narrowed connected count, so the correct DT entry is selected
for each active channel.
No functional change intended for existing platforms.
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@xxxxxxxxxxxxxx>
---
v6->v7:
- New patch.
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c | 58 +++++++++++++++++---
1 file changed, 49 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
index 01b037594926..c2cd542a8cf5 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
@@ -364,7 +364,8 @@ static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
return num_encoders;
}
-static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
+static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu,
+ unsigned long channels_mask)
{
const struct device_node *np = rcdu->dev->of_node;
const char *vsps_prop_name = "renesas,vsps";
@@ -374,6 +375,8 @@ static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
unsigned int crtcs_mask;
} vsps[RZG2L_DU_MAX_VSPS] = { { NULL, }, };
unsigned int vsps_count = 0;
+ unsigned int swindex = 0;
+ unsigned int hwindex;
unsigned int cells;
unsigned int i;
int ret;
@@ -384,15 +387,15 @@ static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
* connected DU CRTCs.
*/
ret = of_property_count_u32_elems(np, vsps_prop_name);
- cells = ret / rcdu->num_crtcs - 1;
+ cells = ret / hweight8(rcdu->info->channels_mask) - 1;
if (cells != 1)
return -EINVAL;
- for (i = 0; i < rcdu->num_crtcs; ++i) {
+ for_each_set_bit(hwindex, &channels_mask, RZG2L_DU_MAX_CRTCS) {
unsigned int j;
ret = of_parse_phandle_with_fixed_args(np, vsps_prop_name,
- cells, i, &args);
+ cells, hwindex, &args);
if (ret < 0)
goto done;
@@ -410,15 +413,16 @@ static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
else
vsps[vsps_count++].np = args.np;
- vsps[j].crtcs_mask |= BIT(i);
+ vsps[j].crtcs_mask |= BIT(swindex);
/*
* Store the VSP pointer and pipe index in the CRTC. If the
* second cell of the 'renesas,vsps' specifier isn't present,
* default to 0.
*/
- rcdu->crtcs[i].vsp = &rcdu->vsps[j];
- rcdu->crtcs[i].vsp_pipe = cells >= 1 ? args.args[0] : 0;
+ rcdu->crtcs[swindex].vsp = &rcdu->vsps[j];
+ rcdu->crtcs[swindex].vsp_pipe = cells >= 1 ? args.args[0] : 0;
+ swindex++;
}
/*
@@ -443,8 +447,36 @@ static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
return ret;
}
+static unsigned int rzg2l_du_connected_channels(struct rzg2l_du_device *rcdu)
+{
+ struct device_node *np = rcdu->dev->of_node;
+ unsigned int connected = 0;
+ struct device_node *ep_node;
+
+ for_each_endpoint_of_node(np, ep_node) {
+ struct of_endpoint ep;
+ struct device_node *remote;
+
+ if (!of_device_is_available(ep_node))
+ continue;
+
+ if (of_graph_parse_endpoint(ep_node, &ep))
+ continue;
+
+ remote = of_graph_get_remote_endpoint(ep_node);
+ if (!remote)
+ continue;
+ of_node_put(remote);
+
+ connected |= BIT(ep.port);
+ }
+
+ return rcdu->info->channels_mask & connected;
+}
+
int rzg2l_du_modeset_init(struct rzg2l_du_device *rcdu)
{
+ struct device_node *np = rcdu->dev->of_node;
struct drm_device *dev = &rcdu->ddev;
struct drm_encoder *encoder;
unsigned long channels_mask;
@@ -470,7 +502,15 @@ int rzg2l_du_modeset_init(struct rzg2l_du_device *rcdu)
dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 1920;
- channels_mask = rcdu->info->channels_mask;
+ if (rzg2l_du_ep_routing(np)) {
+ channels_mask = rzg2l_du_connected_channels(rcdu);
+ if (!channels_mask) {
+ dev_err(rcdu->dev, "no connected DU channels found in DT\n");
+ return -ENODEV;
+ }
+ } else {
+ channels_mask = rcdu->info->channels_mask;
+ }
rcdu->num_crtcs = hweight8(channels_mask);
/*
@@ -482,7 +522,7 @@ int rzg2l_du_modeset_init(struct rzg2l_du_device *rcdu)
return ret;
/* Initialize the compositors. */
- ret = rzg2l_du_vsps_init(rcdu);
+ ret = rzg2l_du_vsps_init(rcdu, channels_mask);
if (ret < 0)
return ret;
--
2.54.0