[PATCH v7 08/13] drm: renesas: rz-du: Add endpoint-based output routing

From: Tommaso Merciai

Date: Thu May 07 2026 - 05:24:55 EST


On RZ/G3E all outputs of a DU instance share a single port and are
distinguished by endpoint id, unlike the port-per-output layout on
RZ/G2L.

Auto-detect the routing mode by checking whether any port exposes more
than one endpoint (rzg2l_du_ep_routing()). Add an 'ep' field in a union
with 'port' in rzg2l_du_output_routing to cover both conventions, and
update rzg2l_du_encoders_init() to match routes by ep.id when endpoint
routing is detected. Track a linked_outputs bitmask to skip duplicate
encoder creation when the same output type appears under multiple
DU-channel ports.

No functional change for existing platforms.

Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@xxxxxxxxxxxxxx>
---
v6->v7:
- New patch.

drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 6 ++-
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c | 57 +++++++++++++++++---
2 files changed, 55 insertions(+), 8 deletions(-)

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 d0e59b787cd7..eed8e1215f08 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
@@ -32,6 +32,7 @@ enum rzg2l_du_output {
* struct rzg2l_du_output_routing - Output routing specification
* @possible_outputs: bitmask of possible outputs
* @port: device tree port number corresponding to this output route
+ * @ep: device tree endpoint id corresponding to this output route
*
* The DU has 2 possible outputs (DPAD0, DSI0). Output routing data
* specify the valid SoC outputs, which CRTC can drive the output, and the type
@@ -39,7 +40,10 @@ enum rzg2l_du_output {
*/
struct rzg2l_du_output_routing {
unsigned int possible_outputs;
- unsigned int port;
+ union {
+ unsigned int port;
+ unsigned int ep;
+ };
};

/*
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 fc5ce8c7eea0..01b037594926 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
@@ -266,15 +266,37 @@ static int rzg2l_du_encoders_init_one(struct rzg2l_du_device *rcdu,
return ret;
}

+static bool rzg2l_du_ep_routing(const struct device_node *np)
+{
+ for_each_of_graph_port(np, port) {
+ unsigned int count = 0;
+
+ for_each_of_graph_port_endpoint(port, ep)
+ count++;
+
+ if (count > 1)
+ return true;
+ }
+ return false;
+}
+
static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
{
struct device_node *np = rcdu->dev->of_node;
+ bool ep_routing = rzg2l_du_ep_routing(np);
+ unsigned int linked_outputs = 0;
struct device_node *ep_node;
unsigned int num_encoders = 0;

/*
* Iterate over the endpoints and create one encoder for each output
* pipeline.
+ *
+ * Two routing modes are supported:
+ * - Port-based (default): each output lives on its own port, routes
+ * are matched by ep.port.
+ * - Endpoint-based: all outputs share a single port, each output lives
+ * on its own endpoint, routes are matched by ep.id.
*/
for_each_endpoint_of_node(np, ep_node) {
enum rzg2l_du_output output;
@@ -288,19 +310,39 @@ static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
return ret;
}

- /* Find the output route corresponding to the port number. */
+ /* Find the output route corresponding to the port/endpoint. */
for (i = 0; i < RZG2L_DU_OUTPUT_MAX; ++i) {
- if (rcdu->info->routes[i].possible_outputs &&
- rcdu->info->routes[i].port == ep.port) {
- output = i;
+ const struct rzg2l_du_output_routing *route =
+ &rcdu->info->routes[i];
+
+ if (!route->possible_outputs)
+ continue;
+
+ if (ep_routing ? route->ep == ep.id :
+ route->port == ep.port)
break;
- }
}

if (i == RZG2L_DU_OUTPUT_MAX) {
dev_warn(rcdu->dev,
- "port %u references unexisting output, skipping\n",
- ep.port);
+ "port %u endpoint %u references unexisting output, skipping\n",
+ ep.port, ep.id);
+ continue;
+ }
+
+ output = i;
+
+ /*
+ * With endpoint-based routing the same output type can appear
+ * under multiple DU-channel ports (e.g. DSI on both port@0 and
+ * port@1). Only create one encoder per output the encoder's
+ * possible_crtcs mask already covers all channels that can
+ * drive it.
+ */
+ if (ep_routing && (linked_outputs & BIT(output))) {
+ dev_dbg(rcdu->dev,
+ "output %s already linked, skipping port %u endpoint %u\n",
+ rzg2l_du_output_name(output), ep.port, ep.id);
continue;
}

@@ -315,6 +357,7 @@ static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
continue;
}

+ linked_outputs |= BIT(output);
num_encoders++;
}

--
2.54.0