The RZ/G3E Soc has 2 LCD controller (LCDC), contain a Frame Compression
Processor (FCPVD), a Video Signal Processor (VSPD), Video Signal
Processor (VSPD), and Display Unit (DU).
LCDC0 supports DSI and LVDS (single or dual-channel) outputs.
LCDC1 supports DSI, LVDS (single-channel), and RGB outputs.
Depending on the selected output, the correct SMUX2 clock parent must be
chosen:
- Index 0 if LVDS0 or LVDS1 is used
- Index 1 for all other cases
To support this behavior, introduce the `RG2L_DU_FEATURE_SMUX2_DSI_CLK`
feature flag and extend the `rzg2l_du_device_info` structure to include a
features field. Also, add a new helper function `rzg2l_du_has()` to check
for feature flags.
Add support for the RZ/G3E SoC by introducing:
- `rzg2l_du_r9a09g047_du{0,1}_info` structures
- The `renesas,r9a09g047-du{0,1}` compatible strings
Additionally, introduce the missing output definitions
`RZG2L_DU_OUTPUT_LVDS{0,1}`.
Introduce `rzg2l_du_crtc_atomic_check()` helper to store the routes from
the CRTC output to the DU outputs.
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 51 +++++++++++++++++++
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 42 +++++++++++++++
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 11 ++++
3 files changed, 104 insertions(+)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
index 6e7aac6219be..044ac16256c7 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
@@ -8,6 +8,7 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -64,11 +65,34 @@
static void rzg2l_du_crtc_set_display_timing(struct rzg2l_du_crtc *rcrtc)
{
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
+ struct rzg2l_du_crtc_state *rstate =
+ to_rzg2l_crtc_state(rcrtc->crtc.state);
unsigned long mode_clock = mode->clock * 1000;
u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;
struct rzg2l_du_device *rcdu = rcrtc->dev;
clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);
+
+ if (rzg2l_du_has(rcdu, RG2L_DU_FEATURE_SMUX2_DSI_CLK)) {
+ struct clk_hw *hw_parent, *hw_pparent;
+ struct clk *clk_parent;
+
+ clk_parent = clk_get_parent(rcrtc->rzg2l_clocks.dclk);
+ hw_parent = __clk_get_hw(clk_parent);
+
+ /*
+ * SMUX2_DSI0_CLK: if LVDS0 is used, be sure to set 0b.
+ * SMUX2_DSI1_CLK: if LVDS1 is used, be sure to set 0b.
+ */
+ if (rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS0) ||
+ rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS1))
+ hw_pparent = clk_hw_get_parent_by_index(hw_parent, 0);
+ else
+ hw_pparent = clk_hw_get_parent_by_index(hw_parent, 1);
+
+ clk_set_parent(clk_parent, hw_pparent->clk);
+ }
+
clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);
ditr0 = (DU_DITR0_DEMD_HIGH
@@ -248,6 +272,32 @@ static void rzg2l_du_crtc_stop(struct rzg2l_du_crtc *rcrtc)
* CRTC Functions
*/
+static int rzg2l_du_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+ crtc);
+ struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(crtc_state);
+ struct drm_encoder *encoder;
+
+ /* Store the routes from the CRTC output to the DU outputs. */
+ rstate->outputs = 0;
+
+ drm_for_each_encoder_mask(encoder, crtc->dev,
+ crtc_state->encoder_mask) {
+ struct rzg2l_du_encoder *renc;
+
+ /* Skip the writeback encoder. */
+ if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
+ continue;
+
+ renc = to_rzg2l_encoder(encoder);
+ rstate->outputs |= BIT(renc->output);
+ }
+
+ return 0;
+}
+
static void rzg2l_du_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
@@ -296,6 +346,7 @@ static void rzg2l_du_crtc_atomic_flush(struct drm_crtc *crtc,
}
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+ .atomic_check = rzg2l_du_crtc_atomic_check,
.atomic_flush = rzg2l_du_crtc_atomic_flush,
.atomic_enable = rzg2l_du_crtc_atomic_enable,
.atomic_disable = rzg2l_du_crtc_atomic_disable,
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 0fef33a5a089..73ff095e49ae 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -51,6 +51,44 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
}
};
+static const struct rzg2l_du_device_info rzg2l_du_r9a09g047_du0_info = {
+ .features = RG2L_DU_FEATURE_SMUX2_DSI_CLK,
+ .channels_mask = BIT(0),
+ .routes = {
+ [RZG2L_DU_OUTPUT_DSI0] = {
+ .possible_outputs = BIT(0),
+ .port = 0,
+ },
+ [RZG2L_DU_OUTPUT_LVDS0] = {
+ .possible_outputs = BIT(0),
+ .port = 1,
+ },
+ [RZG2L_DU_OUTPUT_LVDS1] = {
+ .possible_outputs = BIT(0),
+ .port = 2,
+ },
+ },
+};
+
+static const struct rzg2l_du_device_info rzg2l_du_r9a09g047_du1_info = {
+ .features = RG2L_DU_FEATURE_SMUX2_DSI_CLK,
+ .channels_mask = BIT(0),
+ .routes = {
+ [RZG2L_DU_OUTPUT_DSI0] = {
+ .possible_outputs = BIT(0),
+ .port = 0,
+ },
+ [RZG2L_DU_OUTPUT_LVDS0] = {
+ .possible_outputs = BIT(0),
+ .port = 1,
+ },
+ [RZG2L_DU_OUTPUT_DPAD0] = {
+ .possible_outputs = BIT(0),
+ .port = 2,
+ },
+ },
+};
+
static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
.channels_mask = BIT(0),
.routes = {
@@ -64,6 +102,8 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
static const struct of_device_id rzg2l_du_of_table[] = {
{ .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info },
{ .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info },
+ { .compatible = "renesas,r9a09g047-du0", .data = &rzg2l_du_r9a09g047_du0_info },
+ { .compatible = "renesas,r9a09g047-du1", .data = &rzg2l_du_r9a09g047_du1_info },
{ .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info },
{ /* sentinel */ }
};
@@ -74,6 +114,8 @@ const char *rzg2l_du_output_name(enum rzg2l_du_output output)
{
static const char * const names[] = {
[RZG2L_DU_OUTPUT_DSI0] = "DSI0",
+ [RZG2L_DU_OUTPUT_LVDS0] = "LVDS0",
+ [RZG2L_DU_OUTPUT_LVDS1] = "LVDS1",
[RZG2L_DU_OUTPUT_DPAD0] = "DPAD0"
};
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 58806c2a8f2b..c6f9dc46ab31 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
@@ -20,8 +20,12 @@
struct device;
struct drm_property;
+#define RG2L_DU_FEATURE_SMUX2_DSI_CLK BIT(0) /* Per output mux */
+
enum rzg2l_du_output {
RZG2L_DU_OUTPUT_DSI0,
+ RZG2L_DU_OUTPUT_LVDS0,
+ RZG2L_DU_OUTPUT_LVDS1,
RZG2L_DU_OUTPUT_DPAD0,
RZG2L_DU_OUTPUT_MAX,
};
@@ -46,6 +50,7 @@ struct rzg2l_du_output_routing {
* @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*)
*/
struct rzg2l_du_device_info {
+ unsigned int features;
unsigned int channels_mask;
struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX];
};
@@ -73,6 +78,12 @@ static inline struct rzg2l_du_device *to_rzg2l_du_device(struct drm_device *dev)
return container_of(dev, struct rzg2l_du_device, ddev);
}
+static inline bool rzg2l_du_has(struct rzg2l_du_device *rcdu,
+ unsigned int feature)
+{
+ return rcdu->info->features & feature;
+}
+
const char *rzg2l_du_output_name(enum rzg2l_du_output output);
#endif /* __RZG2L_DU_DRV_H__ */
--
2.43.0
Hi Geert, Laurent,
On Wed, Nov 26, 2025 at 03:07:26PM +0100, Tommaso Merciai wrote:
> The RZ/G3E Soc has 2 LCD controller (LCDC), contain a Frame Compression
> Processor (FCPVD), a Video Signal Processor (VSPD), Video Signal
> Processor (VSPD), and Display Unit (DU).
>
> LCDC0 supports DSI and LVDS (single or dual-channel) outputs.
> LCDC1 supports DSI, LVDS (single-channel), and RGB outputs.
>
> Depending on the selected output, the correct SMUX2 clock parent must be
> chosen:
>
> - Index 0 if LVDS0 or LVDS1 is used
> - Index 1 for all other cases
>
> To support this behavior, introduce the `RG2L_DU_FEATURE_SMUX2_DSI_CLK`
> feature flag and extend the `rzg2l_du_device_info` structure to include a
> features field. Also, add a new helper function `rzg2l_du_has()` to check
> for feature flags.
>
> Add support for the RZ/G3E SoC by introducing:
> - `rzg2l_du_r9a09g047_du{0,1}_info` structures
> - The `renesas,r9a09g047-du{0,1}` compatible strings
>
> Additionally, introduce the missing output definitions
> `RZG2L_DU_OUTPUT_LVDS{0,1}`.
>
> Introduce `rzg2l_du_crtc_atomic_check()` helper to store the routes from
> the CRTC output to the DU outputs.
>
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> ---
> drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 51 +++++++++++++++++++
> drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 42 +++++++++++++++
> drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 11 ++++
> 3 files changed, 104 insertions(+)
>
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
> index 6e7aac6219be..044ac16256c7 100644
> --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
> @@ -8,6 +8,7 @@
> */
>
> #include <linux/clk.h>
> +#include <linux/clk-provider.h>
> #include <linux/mutex.h>
> #include <linux/platform_device.h>
> #include <linux/reset.h>
> @@ -64,11 +65,34 @@
> static void rzg2l_du_crtc_set_display_timing(struct rzg2l_du_crtc *rcrtc)
> {
> const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
> + struct rzg2l_du_crtc_state *rstate =
> + to_rzg2l_crtc_state(rcrtc->crtc.state);
> unsigned long mode_clock = mode->clock * 1000;
> u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;
> struct rzg2l_du_device *rcdu = rcrtc->dev;
>
> clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);
> +
> + if (rzg2l_du_has(rcdu, RG2L_DU_FEATURE_SMUX2_DSI_CLK)) {
> + struct clk_hw *hw_parent, *hw_pparent;
> + struct clk *clk_parent;
> +
> + clk_parent = clk_get_parent(rcrtc->rzg2l_clocks.dclk);
> + hw_parent = __clk_get_hw(clk_parent);
> +
> + /*
> + * SMUX2_DSI0_CLK: if LVDS0 is used, be sure to set 0b.
> + * SMUX2_DSI1_CLK: if LVDS1 is used, be sure to set 0b.
> + */
> + if (rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS0) ||
> + rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS1))
> + hw_pparent = clk_hw_get_parent_by_index(hw_parent, 0);
> + else
> + hw_pparent = clk_hw_get_parent_by_index(hw_parent, 1);
> +
> + clk_set_parent(clk_parent, hw_pparent->clk);
> + }
> +
Here is the reason on why we need the CLK_TYPE_PLLDSI_SMUX.
As LVDS needs clock parent = vclk * 7
For that we need the custom mux (rzv2h_cpg_plldsi_smux_determine_rate())
to generate the rights pll rate.
What do you think? Please gently let me know.
Thanks & Regards,
Tommmaso
> clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);
>
> ditr0 = (DU_DITR0_DEMD_HIGH
> @@ -248,6 +272,32 @@ static void rzg2l_du_crtc_stop(struct rzg2l_du_crtc *rcrtc)
> * CRTC Functions
> */
>
> +static int rzg2l_du_crtc_atomic_check(struct drm_crtc *crtc,
> + struct drm_atomic_state *state)
> +{
> + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> + crtc);
> + struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(crtc_state);
> + struct drm_encoder *encoder;
> +
> + /* Store the routes from the CRTC output to the DU outputs. */
> + rstate->outputs = 0;
> +
> + drm_for_each_encoder_mask(encoder, crtc->dev,
> + crtc_state->encoder_mask) {
> + struct rzg2l_du_encoder *renc;
> +
> + /* Skip the writeback encoder. */
> + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
> + continue;
> +
> + renc = to_rzg2l_encoder(encoder);
> + rstate->outputs |= BIT(renc->output);
> + }
> +
> + return 0;
> +}
> +
> static void rzg2l_du_crtc_atomic_enable(struct drm_crtc *crtc,
> struct drm_atomic_state *state)
> {
> @@ -296,6 +346,7 @@ static void rzg2l_du_crtc_atomic_flush(struct drm_crtc *crtc,
> }
>
> static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
> + .atomic_check = rzg2l_du_crtc_atomic_check,
> .atomic_flush = rzg2l_du_crtc_atomic_flush,
> .atomic_enable = rzg2l_du_crtc_atomic_enable,
> .atomic_disable = rzg2l_du_crtc_atomic_disable,
> 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 0fef33a5a089..73ff095e49ae 100644
> --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
> @@ -51,6 +51,44 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
> }
> };
>
> +static const struct rzg2l_du_device_info rzg2l_du_r9a09g047_du0_info = {
> + .features = RG2L_DU_FEATURE_SMUX2_DSI_CLK,
> + .channels_mask = BIT(0),
> + .routes = {
> + [RZG2L_DU_OUTPUT_DSI0] = {
> + .possible_outputs = BIT(0),
> + .port = 0,
> + },
> + [RZG2L_DU_OUTPUT_LVDS0] = {
> + .possible_outputs = BIT(0),
> + .port = 1,
> + },
> + [RZG2L_DU_OUTPUT_LVDS1] = {
> + .possible_outputs = BIT(0),
> + .port = 2,
> + },
> + },
> +};
> +
> +static const struct rzg2l_du_device_info rzg2l_du_r9a09g047_du1_info = {
> + .features = RG2L_DU_FEATURE_SMUX2_DSI_CLK,
> + .channels_mask = BIT(0),
> + .routes = {
> + [RZG2L_DU_OUTPUT_DSI0] = {
> + .possible_outputs = BIT(0),
> + .port = 0,
> + },
> + [RZG2L_DU_OUTPUT_LVDS0] = {
> + .possible_outputs = BIT(0),
> + .port = 1,
> + },
> + [RZG2L_DU_OUTPUT_DPAD0] = {
> + .possible_outputs = BIT(0),
> + .port = 2,
> + },
> + },
> +};
> +
> static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
> .channels_mask = BIT(0),
> .routes = {
> @@ -64,6 +102,8 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
> static const struct of_device_id rzg2l_du_of_table[] = {
> { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info },
> { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info },
> + { .compatible = "renesas,r9a09g047-du0", .data = &rzg2l_du_r9a09g047_du0_info },
> + { .compatible = "renesas,r9a09g047-du1", .data = &rzg2l_du_r9a09g047_du1_info },
> { .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info },
> { /* sentinel */ }
> };
> @@ -74,6 +114,8 @@ const char *rzg2l_du_output_name(enum rzg2l_du_output output)
> {
> static const char * const names[] = {
> [RZG2L_DU_OUTPUT_DSI0] = "DSI0",
> + [RZG2L_DU_OUTPUT_LVDS0] = "LVDS0",
> + [RZG2L_DU_OUTPUT_LVDS1] = "LVDS1",
> [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0"
> };
>
> 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 58806c2a8f2b..c6f9dc46ab31 100644
> --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
> @@ -20,8 +20,12 @@
> struct device;
> struct drm_property;
>
> +#define RG2L_DU_FEATURE_SMUX2_DSI_CLK BIT(0) /* Per output mux */
> +
> enum rzg2l_du_output {
> RZG2L_DU_OUTPUT_DSI0,
> + RZG2L_DU_OUTPUT_LVDS0,
> + RZG2L_DU_OUTPUT_LVDS1,
> RZG2L_DU_OUTPUT_DPAD0,
> RZG2L_DU_OUTPUT_MAX,
> };
> @@ -46,6 +50,7 @@ struct rzg2l_du_output_routing {
> * @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*)
> */
> struct rzg2l_du_device_info {
> + unsigned int features;
> unsigned int channels_mask;
> struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX];
> };
> @@ -73,6 +78,12 @@ static inline struct rzg2l_du_device *to_rzg2l_du_device(struct drm_device *dev)
> return container_of(dev, struct rzg2l_du_device, ddev);
> }
>
> +static inline bool rzg2l_du_has(struct rzg2l_du_device *rcdu,
> + unsigned int feature)
> +{
> + return rcdu->info->features & feature;
> +}
> +
> const char *rzg2l_du_output_name(enum rzg2l_du_output output);
>
> #endif /* __RZG2L_DU_DRV_H__ */
> --
> 2.43.0
>
© 2016 - 2026 Red Hat, Inc.