The RZ/G3E SoC has 2 LCD controllers (LCDC), each containing a Frame
Compression Processor (FCPVD), a Video Signal Processor (VSPD), and a
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 based on the requested duty cycle:
- Index 0 for LVDS -> CDIV7_DSIx_CLK (DUTY H/L=4/3, 4/7 duty cycle)
- Index 1 for DSI/DPAD -> CSDIV_2to16_PLLDSIx (symmetric 50% duty cycle)
To support this behavior, introduce the `RZG2L_DU_FEATURE_SMUX2_DSI_CLK`
feature flag.
Add support for the RZ/G3E SoC by introducing:
- `rzg2l_du_r9a09g047_info` structure
- The `renesas,r9a09g047-du` compatible string
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.
Bump RZG2L_DU_MAX_CRTCS and RZG2L_DU_MAX_VSPS to 2.
Signed-off-by: Tommaso Merciai <[email protected]>
---
v6->v7:
- Removed feature flag usage, rebased on top of T2H/N2H LCDC series [1]
[1]
https://patchwork.global.renesas.com/project/spl2-reviews/list/?series=694&state=%2A&archive=both
- Reworked commit body as now outputs routing is based on endpoint id
instead of port number.
- Fixed rzg2l_du_r9a09g047_info instead of rzg2l_du_r9a09g047_du_info.
- Added bump of RZG2L_DU_MAX_CRTCS and RZG2L_DU_MAX_VSPS to 2.
v5->v6:
- Aligned ports numbering with the bindings changes.
v4->v5:
- Fixed RG2L_DU_FEATURE_SMUX2_DSI_CLK to RZG2L_DU_FEATURE_SMUX2_DSI_CLK,
update commit body accordingly.
- Added features field documentation.
v3->v4:
- No changes.
v2->v3:
- No changes.
v1->v2:
- Instead of using clk-provider API to select the right parent clock,
based on the outputs. Just set the correct duty cycle based on the
output, this reflects at CPG lvl to select the right parent.
- Updated commit message accordingly.
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 48 +++++++++++++++++++
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 28 ++++++++++-
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 7 ++-
3 files changed, 80 insertions(+), 3 deletions(-)
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 3e8b6cca6d57..5c455391015f 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
@@ -65,10 +65,31 @@
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);
+ struct rzg2l_du_device *rcdu = rcrtc->dev;
unsigned long mode_clock = mode->clock * 1000;
u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;
clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);
+
+ if (rzg2l_du_has(rcdu, RZG2L_DU_FEATURE_SMUX2_DSI_CLK)) {
+ struct clk *clk_parent;
+
+ clk_parent = clk_get_parent(rcrtc->rzg2l_clocks.dclk);
+
+ /*
+ * Request appropriate duty cycle to let the clock driver select
+ * the correct parent:
+ * - CDIV7_DSIx_CLK (LVDS path): DUTY H/L=4/3, 4/7 duty cycle
+ * - CSDIV_2to16_PLLDSIx (DSI/RGB path): symmetric 50% duty
cycle
+ */
+ if (rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS0) ||
+ rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS1))
+ clk_set_duty_cycle(clk_parent, 4, 7);
+ else
+ clk_set_duty_cycle(clk_parent, 1, 2);
+ }
+
clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);
ditr0 = (DU_DITR0_DEMD_HIGH
@@ -252,6 +273,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_commit *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_commit *state)
{
@@ -300,6 +347,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 887b840e63d2..59dd7572c730 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -53,6 +53,29 @@ static const struct rzg2l_du_device_info
rzg2l_du_r9a07g044_info = {
}
};
+static const struct rzg2l_du_device_info rzg2l_du_r9a09g047_info = {
+ .features = RZG2L_DU_FEATURE_SMUX2_DSI_CLK,
+ .channels_mask = BIT(0) | BIT(1),
+ .routes = {
+ [RZG2L_DU_OUTPUT_DSI0] = {
+ .possible_outputs = BIT(0) | BIT(1),
+ .ep = 0,
+ },
+ [RZG2L_DU_OUTPUT_DPAD0] = {
+ .possible_outputs = BIT(0) | BIT(1),
+ .ep = 1,
+ },
+ [RZG2L_DU_OUTPUT_LVDS0] = {
+ .possible_outputs = BIT(0) | BIT(1),
+ .ep = 2,
+ },
+ [RZG2L_DU_OUTPUT_LVDS1] = {
+ .possible_outputs = BIT(0) | BIT(1),
+ .ep = 3,
+ },
+ },
+};
+
static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
.channels_mask = BIT(0),
.routes = {
@@ -79,6 +102,7 @@ static const struct rzg2l_du_device_info
rzg2l_du_r9a09g077_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-du", .data =
&rzg2l_du_r9a09g047_info },
{ .compatible = "renesas,r9a09g057-du", .data =
&rzg2l_du_r9a09g057_info },
{ .compatible = "renesas,r9a09g077-du", .data =
&rzg2l_du_r9a09g077_info },
{ /* sentinel */ }
@@ -90,7 +114,9 @@ const char *rzg2l_du_output_name(enum rzg2l_du_output output)
{
static const char * const names[] = {
[RZG2L_DU_OUTPUT_DSI0] = "DSI0",
- [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0"
+ [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0",
+ [RZG2L_DU_OUTPUT_LVDS0] = "LVDS0",
+ [RZG2L_DU_OUTPUT_LVDS1] = "LVDS1"
};
if (output >= ARRAY_SIZE(names))
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 eed8e1215f08..649cd9b70223 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
@@ -21,10 +21,13 @@ struct device;
struct drm_property;
#define RZG2L_DU_FEATURE_DPIO_OE BIT(0) /* Has DPIO output enable
control */
+#define RZG2L_DU_FEATURE_SMUX2_DSI_CLK BIT(1) /* Select SMUX2 clock parent
via duty cycle */
enum rzg2l_du_output {
RZG2L_DU_OUTPUT_DSI0,
RZG2L_DU_OUTPUT_DPAD0,
+ RZG2L_DU_OUTPUT_LVDS0,
+ RZG2L_DU_OUTPUT_LVDS1,
RZG2L_DU_OUTPUT_MAX,
};
@@ -62,8 +65,8 @@ struct rzg2l_du_device_info {
unsigned int features;
};
-#define RZG2L_DU_MAX_CRTCS 1
-#define RZG2L_DU_MAX_VSPS 1
+#define RZG2L_DU_MAX_CRTCS 2
+#define RZG2L_DU_MAX_VSPS 2
#define RZG2L_DU_MAX_DSI 1
struct rzg2l_du_device {
--
2.54.0