mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] Add support for Renesas RZ/G3L LVDS encoder
@ 2026-04-17 17:52 Biju
  2026-04-17 17:52 ` [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L Biju
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Biju @ 2026-04-17 17:52 UTC (permalink / raw)
  To: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel, Geert Uytterhoeven, Magnus Damm
  Cc: Biju Das, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
	Tommaso Merciai, dri-devel, devicetree, linux-kernel,
	linux-renesas-soc, Prabhakar Mahadev Lad, Biju Das

From: Biju Das <biju.das.jz@bp.renesas.com>

Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
from 25 to 87 MHz. The LVDS module cannot be used at the same time as
MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
module.

Biju Das (3):
  dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L
  dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder
  drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder

 .../bridge/renesas,r9a08g046-lvds.yaml        | 128 +++++++
 .../devicetree/bindings/mfd/syscon.yaml       |   1 +
 drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
 drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
 .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
 7 files changed, 524 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h

-- 
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L
  2026-04-17 17:52 [PATCH 0/3] Add support for Renesas RZ/G3L LVDS encoder Biju
@ 2026-04-17 17:52 ` Biju
  2026-04-20 16:21   ` Conor Dooley
  2026-04-17 17:52 ` [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder Biju
  2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
  2 siblings, 1 reply; 15+ messages in thread
From: Biju @ 2026-04-17 17:52 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm
  Cc: Biju Das, devicetree, linux-kernel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Biju Das, Tommaso Merciai

From: Biju Das <biju.das.jz@bp.renesas.com>

The RZ/G3{E,L} SoCs have an LVDS Common (LVDS_CMN) region which is common
to all LVDS channels. The RZ/G3L has single-link, but the RZ/G3E has both
single and dual-link.

Use the syscon interface to access these registers for scalability.

Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index e22867088063..9c81010d5a74 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -221,6 +221,7 @@ properties:
               - nxp,s32g3-gpr
               - qcom,apq8064-mmss-sfpb
               - qcom,apq8064-sps-sic
+              - renesas,r9a08g046-lvds-cmn
               - rockchip,px30-qos
               - rockchip,rk3036-qos
               - rockchip,rk3066-qos
-- 
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder
  2026-04-17 17:52 [PATCH 0/3] Add support for Renesas RZ/G3L LVDS encoder Biju
  2026-04-17 17:52 ` [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L Biju
@ 2026-04-17 17:52 ` Biju
  2026-04-20 16:21   ` Conor Dooley
  2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
  2 siblings, 1 reply; 15+ messages in thread
From: Biju @ 2026-04-17 17:52 UTC (permalink / raw)
  To: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm
  Cc: Biju Das, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
	Tommaso Merciai, dri-devel, devicetree, linux-kernel,
	linux-renesas-soc, Prabhakar Mahadev Lad, Biju Das

From: Biju Das <biju.das.jz@bp.renesas.com>

Document the LVDS encoder IP found on the RZ/G3L SoC. It supports
single-link mode. LVDS and the DSI interface share a peripheral clock and
the MIPI_DSI_PRESET_N reset signal. However, the LVDS module cannot be
used at the same time as MIPI-DSI.

Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 .../bridge/renesas,r9a08g046-lvds.yaml        | 128 ++++++++++++++++++
 1 file changed, 128 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml

diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml
new file mode 100644
index 000000000000..b1f6d020ae7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml
@@ -0,0 +1,128 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/renesas,r9a08g046-lvds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/G3L LVDS Encoder
+
+maintainers:
+  - Biju Das <biju.das.jz@bp.renesas.com>
+  - Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
+
+description: |
+  This binding describe the LVDS encoder embedded in the Renesas RZ/G3L
+  SoC. The encoder can operate in LVDS Single-link mode with 4 lanes
+  (Data) + 1 lane (Clock).
+
+properties:
+  compatible:
+    const: renesas,r9a08g046-lvds
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Peripheral clock
+      - description: PHY clock
+      - description: Dot clock
+
+  clock-names:
+    items:
+      - const: pclk
+      - const: phyclk
+      - const: dotclk
+
+  resets:
+    items:
+      - description: LVDS_RESET_N
+      - description: MIPI_DSI_PRESET_N
+      - description: MIPI_DSI_CMN_RSTB
+      - description: MIPI_DSI_ARESET_N
+
+  reset-names:
+    items:
+      - const: lvdrst
+      - const: prst
+      - const: rst
+      - const: arst
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input channel, directly connected to the Display Unit.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: |
+          Output channel, directly connected to the LVDS panel or bridge.
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - power-domains
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/renesas,r9a08g046-cpg.h>
+
+    lvds-cmn@108a0000 {
+        compatible = "renesas,r9a08g046-lvds-cmn",
+                     "simple-mfd", "syscon";
+        reg = <0x108a0000 0x10000>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        lvds0: lvds@10 {
+            compatible = "renesas,r9a08g046-lvds";
+            reg = <0x10 0x8>;
+            clocks = <&cpg CPG_MOD R9A08G046_MIPI_DSI_PCLK>,
+                     <&cpg CPG_MOD R9A08G046_LVDS_PLLCLK>,
+                     <&cpg CPG_MOD R9A08G046_LVDS_CLK_DOT0>;
+            clock-names = "pclk", "phyclk", "dotclk";
+            resets = <&cpg R9A08G046_LVDS_RESET_N>,
+                     <&cpg R9A08G046_MIPI_DSI_PRESET_N>,
+                     <&cpg R9A08G046_MIPI_DSI_CMN_RSTB>,
+                     <&cpg R9A08G046_MIPI_DSI_ARESET_N>;
+            reset-names = "lvdrst", "prst", "rst", "arst";
+            power-domains = <&cpg>;
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    lvds0_in: endpoint {
+                        remote-endpoint = <&du_out_lvds0>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+                    lvds0_out: endpoint {
+                        remote-endpoint = <&panel_in>;
+                    };
+                };
+            };
+        };
+    };
+...
-- 
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-17 17:52 [PATCH 0/3] Add support for Renesas RZ/G3L LVDS encoder Biju
  2026-04-17 17:52 ` [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L Biju
  2026-04-17 17:52 ` [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder Biju
@ 2026-04-17 17:52 ` Biju
  2026-04-19 15:58   ` Dmitry Baryshkov
                     ` (2 more replies)
  2 siblings, 3 replies; 15+ messages in thread
From: Biju @ 2026-04-17 17:52 UTC (permalink / raw)
  To: Biju Das, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	Magnus Damm
  Cc: linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Biju Das, Tommaso Merciai

From: Biju Das <biju.das.jz@bp.renesas.com>

Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
from 25 to 87 MHz. The LVDS module cannot be used at the same time as
MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
module.

Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
 drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
 .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
 5 files changed, 395 insertions(+)
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
 create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h

diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
index 7f2ef7137ae5..cbfc7b6bccb8 100644
--- a/drivers/gpu/drm/renesas/rz-du/Kconfig
+++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
@@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI
 	def_tristate DRM_RZG2L_DU
 	depends on DRM_RZG2L_USE_MIPI_DSI
 	select DRM_MIPI_DSI
+
+config DRM_RZG3L_USE_LVDS
+	bool "RZ/G3L DU LVDS Encoder Support"
+	depends on DRM_BRIDGE && OF
+	default DRM_RZG2L_DU
+	help
+	  Enable support for the RZ/G3L Display Unit embedded LVDS encoders.
+
+config DRM_RZG3L_LVDS
+	def_tristate DRM_RZG2L_DU
+	depends on DRM_RZG3L_USE_LVDS
+	select DRM_KMS_HELPER
+	select DRM_PANEL
diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile
index 2987900ea6b6..46decb7ac4f1 100644
--- a/drivers/gpu/drm/renesas/rz-du/Makefile
+++ b/drivers/gpu/drm/renesas/rz-du/Makefile
@@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1)	+= rzg2l_du_vsp.o
 obj-$(CONFIG_DRM_RZG2L_DU)		+= rzg2l-du-drm.o
 
 obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)	+= rzg2l_mipi_dsi.o
+obj-$(CONFIG_DRM_RZG3L_LVDS)		+= rzg3l_lvds.o
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
new file mode 100644
index 000000000000..bedeedbdfada
--- /dev/null
+++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G3L LVDS Encoder Driver
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "rzg3l_lvds.h"
+#include "rzg3l_lvds_regs.h"
+
+enum rzg3l_lvds_mode {
+	RZG3L_LVDS_MODE_JEIDA = 0,
+	RZG3L_LVDS_MODE_JEIDA_MIRROR = 1,
+	RZG3L_LVDS_MODE_MODE2 = 2,
+	RZG3L_LVDS_MODE_MODE2_MIRROR = 3,
+	RZG3L_LVDS_MODE_VESA = 4,
+	RZG3L_LVDS_MODE_VESA_MIRROR = 5,
+	RZG3L_LVDS_MODE_MODE6 = 6,
+	RZG3L_LVDS_MODE_MODE6_MIRROR = 7,
+};
+
+struct rzg3l_lvds {
+	struct device *dev;
+	struct reset_control *prstc;
+	struct reset_control *lvd_rstc;
+	struct regmap *regmap;
+
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+};
+
+#define bridge_to_rzg3l_lvds(b) \
+	container_of(b, struct rzg3l_lvds, bridge)
+
+/* -----------------------------------------------------------------------------
+ * Bridge
+ */
+static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
+				     struct drm_atomic_state *state)
+{
+	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
+	const struct drm_bridge_state *bridge_state;
+	int ret;
+	u32 fmt;
+
+	/* Get the LVDS format from the bridge state. */
+	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+	if (!bridge_state) {
+		dev_err(lvds->dev, "failed to get bridge state\n");
+		return;
+	}
+
+	switch (bridge_state->output_bus_cfg.format) {
+	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+		fmt = RZG3L_LVDS_MODE_JEIDA;
+		break;
+	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+		fmt = RZG3L_LVDS_MODE_VESA;
+		break;
+	default:
+		fmt = RZG3L_LVDS_MODE_VESA;
+		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
+			 bridge_state->output_bus_cfg.format);
+		break;
+	}
+
+	ret = pm_runtime_resume_and_get(lvds->dev);
+	if (ret < 0) {
+		dev_err(lvds->dev, "pm_runtime_resume_and_get error\n");
+		return;
+	}
+
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
+	usleep_range(20, 25);
+
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
+	usleep_range(10, 15);
+
+	regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
+	regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
+			   LVDS_0_CTL_FMT_SEL_MSK,
+			   FIELD_PREP(LVDS_0_CTL_FMT_SEL_MSK, fmt));
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_IO_EN_MSK, LVDS_0_PHY_CH_IO_EN);
+	regmap_write(lvds->regmap, LVDS_CMN,
+		     LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
+	usleep_range(100, 150);
+}
+
+static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
+				      struct drm_atomic_state *state)
+{
+	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
+
+	regmap_update_bits(lvds->regmap, LVDS_CMN, LVDS_CMN_PHY_RESET, 0);
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_IO_EN_MSK, 0);
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_EN_LDO, 0);
+	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+			   LVDS_0_PHY_CH_EN_BGR, 0);
+
+	pm_runtime_put(lvds->dev);
+}
+
+static int rzg3l_lvds_attach(struct drm_bridge *bridge,
+			     struct drm_encoder *encoder,
+			     enum drm_bridge_attach_flags flags)
+{
+	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
+
+	if (!lvds->next_bridge)
+		return 0;
+
+	return drm_bridge_attach(encoder, lvds->next_bridge, bridge, flags);
+}
+
+static enum drm_mode_status
+rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge,
+			     const struct drm_display_info *info,
+			     const struct drm_display_mode *mode)
+{
+	if (mode->clock > 87000)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock < 25000)
+		return MODE_CLOCK_LOW;
+
+	return MODE_OK;
+}
+
+bool rzg3l_lvds_is_connected(struct drm_bridge *bridge)
+{
+	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
+
+	return !!lvds->next_bridge;
+}
+EXPORT_SYMBOL_GPL(rzg3l_lvds_is_connected);
+
+static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = {
+	.attach = rzg3l_lvds_attach,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.atomic_enable = rzg3l_lvds_atomic_enable,
+	.atomic_disable = rzg3l_lvds_atomic_disable,
+	.mode_valid = rzg3l_lvds_bridge_mode_valid,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int rzg3l_lvds_pm_runtime_suspend(struct device *dev)
+{
+	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
+	int ret;
+
+	ret = reset_control_assert(lvds->lvd_rstc);
+	if (ret)
+		return ret;
+
+	ret = reset_control_assert(lvds->prstc);
+	if (ret)
+		goto err_deassert_lvd_rstc;
+
+	return 0;
+
+err_deassert_lvd_rstc:
+	reset_control_deassert(lvds->lvd_rstc);
+	return ret;
+}
+
+static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
+{
+	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
+	int ret;
+
+	ret = reset_control_deassert(lvds->prstc);
+	if (ret)
+		return ret;
+
+	return reset_control_deassert(lvds->lvd_rstc);
+	if (ret)
+		goto err_assert_prstc;
+
+	return 0;
+
+err_assert_prstc:
+	reset_control_assert(lvds->prstc);
+	return ret;
+}
+
+static const struct dev_pm_ops rzg3l_lvds_pm_ops = {
+	RUNTIME_PM_OPS(rzg3l_lvds_pm_runtime_suspend,
+		       rzg3l_lvds_pm_runtime_resume, NULL)
+	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int rzg3l_lvds_parse_dt(struct rzg3l_lvds *lvds)
+{
+	struct device *dev = lvds->dev;
+	struct drm_panel *panel;
+	int ret;
+
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,
+					  &panel, &lvds->next_bridge);
+	if (ret)
+		return ret;
+
+	if (panel) {
+		lvds->next_bridge = devm_drm_panel_bridge_add(dev, panel);
+		if (IS_ERR_OR_NULL(lvds->next_bridge))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rzg3l_lvds_probe(struct platform_device *pdev)
+{
+	struct reset_control *rstc, *arstc;
+	struct device *dev = &pdev->dev;
+	struct rzg3l_lvds *lvds;
+	int ret;
+
+	lvds = devm_drm_bridge_alloc(dev, struct rzg3l_lvds, bridge,
+				     &rzg3l_lvds_bridge_ops);
+	if (IS_ERR(lvds))
+		return PTR_ERR(lvds);
+
+	lvds->dev = dev;
+	lvds->bridge.of_node = pdev->dev.of_node;
+
+	lvds->regmap = syscon_node_to_regmap(dev->of_node->parent);
+	if (IS_ERR(lvds->regmap))
+		return PTR_ERR(lvds->regmap);
+
+	rstc = devm_reset_control_get_optional_exclusive(dev, "rst");
+	if (IS_ERR(rstc))
+		return dev_err_probe(dev, PTR_ERR(rstc), "failed to get rst\n");
+
+	arstc = devm_reset_control_get_optional_exclusive(dev, "arst");
+	if (IS_ERR(arstc))
+		return dev_err_probe(dev, PTR_ERR(arstc),
+				     "failed to get arst\n");
+
+	lvds->prstc = devm_reset_control_get_shared(dev, "prst");
+	if (IS_ERR(lvds->prstc))
+		return dev_err_probe(dev, PTR_ERR(lvds->prstc),
+				     "failed to get prst\n");
+
+	lvds->lvd_rstc = devm_reset_control_get_shared(dev, "lvdrst");
+	if (IS_ERR(lvds->lvd_rstc))
+		return dev_err_probe(dev, PTR_ERR(lvds->lvd_rstc),
+				     "failed to get core reset\n");
+
+	platform_set_drvdata(pdev, lvds);
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable Runtime PM\n");
+
+	ret = rzg3l_lvds_parse_dt(lvds);
+	if (ret < 0)
+		return ret;
+
+	ret = reset_control_assert(rstc);
+	if (ret < 0)
+		return ret;
+
+	ret = reset_control_assert(arstc);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_drm_bridge_add(dev, &lvds->bridge);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to register drm bridge\n");
+
+	return ret;
+}
+
+static const struct of_device_id rzg3l_lvds_of_table[] = {
+	{ .compatible = "renesas,r9a08g046-lvds" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, rzg3l_lvds_of_table);
+
+static struct platform_driver rzg3l_lvds_platform_driver = {
+	.probe		= rzg3l_lvds_probe,
+	.driver		= {
+		.name	= "rzg3l-lvds",
+		.pm	= pm_ptr(&rzg3l_lvds_pm_ops),
+		.of_match_table = rzg3l_lvds_of_table,
+	},
+};
+
+module_platform_driver(rzg3l_lvds_platform_driver);
+
+MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
+MODULE_AUTHOR("Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/G3L LVDS Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
new file mode 100644
index 000000000000..2ea3cf4e1cae
--- /dev/null
+++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RZ/G3L LVDS Encoder
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ *
+ */
+
+#ifndef __RZG3L_LVDS_H__
+#define __RZG3L_LVDS_H__
+
+struct drm_bridge;
+
+#if IS_ENABLED(CONFIG_DRM_RZG3L_LVDS)
+bool rzg3l_lvds_is_connected(struct drm_bridge *bridge);
+#else
+static inline bool rzg3l_lvds_is_connected(struct drm_bridge *bridge)
+{
+	return false;
+}
+#endif /* CONFIG_DRM_RZG3L_LVDS */
+#endif /* __RZG3L_LVDS_H__ */
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
new file mode 100644
index 000000000000..281b7648f168
--- /dev/null
+++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RZ/G3L LVDS Interface Registers Definitions
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ *
+ */
+
+#ifndef __RZG3L_LVDS_REGS_H__
+#define __RZG3L_LVDS_REGS_H__
+
+#define LVDS_CMN			0x00
+#define LVDS_CMN_RST_PHY0_SEL		(1 << 24)
+#define LVDS_CMN_RST_PHY0_SEL_CH0	(1 << 24)
+#define LVDS_CMN_PHY_RESET		(1 << 0)
+
+#define LVDS_0_PHY_OFFSET		0x10
+#define LVDS_0_PHY_CH_IO_EN_MSK		(0x1f)
+#define LVDS_0_PHY_CH_IO_EN		(LVDS_0_PHY_CH_IO_EN_MSK << 0)
+#define LVDS_0_PHY_CH_EN_BGR		BIT(8)
+#define LVDS_0_PHY_CH_EN_LDO		BIT(9)
+
+#define LVDS_0_CTL_OFFSET		0x14
+#define LVDS_0_CTL_FMT_SEL_MSK		GENMASK(23, 20)
+
+#endif /* __RZG3L_LVDS_REGS_H__ */
-- 
2.43.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
@ 2026-04-19 15:58   ` Dmitry Baryshkov
  2026-04-21  9:11     ` Claudiu Beznea
  2026-04-21 16:21     ` Biju Das
  2026-04-20  7:31   ` Biju Das
  2026-04-21  9:12   ` Claudiu Beznea
  2 siblings, 2 replies; 15+ messages in thread
From: Dmitry Baryshkov @ 2026-04-19 15:58 UTC (permalink / raw)
  To: Biju
  Cc: Biju Das, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	Magnus Damm, linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Tommaso Merciai

On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
> mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
> from 25 to 87 MHz. The LVDS module cannot be used at the same time as
> MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
> the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
> MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
> module.
> 
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
>  drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
>  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
>  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
>  .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
>  5 files changed, 395 insertions(+)
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> 
> diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
> index 7f2ef7137ae5..cbfc7b6bccb8 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Kconfig
> +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
> @@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI
>  	def_tristate DRM_RZG2L_DU
>  	depends on DRM_RZG2L_USE_MIPI_DSI
>  	select DRM_MIPI_DSI
> +
> +config DRM_RZG3L_USE_LVDS
> +	bool "RZ/G3L DU LVDS Encoder Support"
> +	depends on DRM_BRIDGE && OF
> +	default DRM_RZG2L_DU
> +	help
> +	  Enable support for the RZ/G3L Display Unit embedded LVDS encoders.
> +
> +config DRM_RZG3L_LVDS
> +	def_tristate DRM_RZG2L_DU
> +	depends on DRM_RZG3L_USE_LVDS
> +	select DRM_KMS_HELPER
> +	select DRM_PANEL
> diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile
> index 2987900ea6b6..46decb7ac4f1 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Makefile
> +++ b/drivers/gpu/drm/renesas/rz-du/Makefile
> @@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1)	+= rzg2l_du_vsp.o
>  obj-$(CONFIG_DRM_RZG2L_DU)		+= rzg2l-du-drm.o
>  
>  obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)	+= rzg2l_mipi_dsi.o
> +obj-$(CONFIG_DRM_RZG3L_LVDS)		+= rzg3l_lvds.o
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> new file mode 100644
> index 000000000000..bedeedbdfada
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> @@ -0,0 +1,333 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3L LVDS Encoder Driver
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "rzg3l_lvds.h"
> +#include "rzg3l_lvds_regs.h"
> +
> +enum rzg3l_lvds_mode {
> +	RZG3L_LVDS_MODE_JEIDA = 0,
> +	RZG3L_LVDS_MODE_JEIDA_MIRROR = 1,
> +	RZG3L_LVDS_MODE_MODE2 = 2,
> +	RZG3L_LVDS_MODE_MODE2_MIRROR = 3,
> +	RZG3L_LVDS_MODE_VESA = 4,
> +	RZG3L_LVDS_MODE_VESA_MIRROR = 5,
> +	RZG3L_LVDS_MODE_MODE6 = 6,
> +	RZG3L_LVDS_MODE_MODE6_MIRROR = 7,
> +};
> +
> +struct rzg3l_lvds {
> +	struct device *dev;
> +	struct reset_control *prstc;
> +	struct reset_control *lvd_rstc;
> +	struct regmap *regmap;
> +
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;

Please use next_bridge from the drm_bridge struct.

> +};
> +
> +#define bridge_to_rzg3l_lvds(b) \
> +	container_of(b, struct rzg3l_lvds, bridge)
> +
> +/* -----------------------------------------------------------------------------
> + * Bridge
> + */
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> +				     struct drm_atomic_state *state)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +	const struct drm_bridge_state *bridge_state;
> +	int ret;
> +	u32 fmt;
> +
> +	/* Get the LVDS format from the bridge state. */
> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> +	if (!bridge_state) {
> +		dev_err(lvds->dev, "failed to get bridge state\n");
> +		return;
> +	}
> +
> +	switch (bridge_state->output_bus_cfg.format) {
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> +		fmt = RZG3L_LVDS_MODE_JEIDA;
> +		break;
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		break;
> +	default:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> +			 bridge_state->output_bus_cfg.format);
> +		break;
> +	}
> +
> +	ret = pm_runtime_resume_and_get(lvds->dev);

If this  fails for any reason, the atomic_disable() would still be
called and it will decrement the counter, potentially undeflowing it.
Consider switching to pm_runtime_get_sync(), which suits better here.


> +	if (ret < 0) {
> +		dev_err(lvds->dev, "pm_runtime_resume_and_get error\n");
> +		return;
> +	}
> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> +	usleep_range(20, 25);
> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> +	usleep_range(10, 15);
> +
> +	regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> +	regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> +			   LVDS_0_CTL_FMT_SEL_MSK,
> +			   FIELD_PREP(LVDS_0_CTL_FMT_SEL_MSK, fmt));
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, LVDS_0_PHY_CH_IO_EN);
> +	regmap_write(lvds->regmap, LVDS_CMN,
> +		     LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> +	usleep_range(100, 150);
> +}
> +
> +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> +				      struct drm_atomic_state *state)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	regmap_update_bits(lvds->regmap, LVDS_CMN, LVDS_CMN_PHY_RESET, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, 0);
> +
> +	pm_runtime_put(lvds->dev);
> +}
> +
> +static int rzg3l_lvds_attach(struct drm_bridge *bridge,
> +			     struct drm_encoder *encoder,
> +			     enum drm_bridge_attach_flags flags)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	if (!lvds->next_bridge)
> +		return 0;
> +
> +	return drm_bridge_attach(encoder, lvds->next_bridge, bridge, flags);
> +}
> +
> +static enum drm_mode_status
> +rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge,
> +			     const struct drm_display_info *info,
> +			     const struct drm_display_mode *mode)
> +{
> +	if (mode->clock > 87000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock < 25000)
> +		return MODE_CLOCK_LOW;
> +
> +	return MODE_OK;
> +}
> +
> +bool rzg3l_lvds_is_connected(struct drm_bridge *bridge)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	return !!lvds->next_bridge;
> +}
> +EXPORT_SYMBOL_GPL(rzg3l_lvds_is_connected);

How is this going to be used? I don't see the user in the patch. Please
drop the unused API.

> +
> +static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = {
> +	.attach = rzg3l_lvds_attach,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.atomic_enable = rzg3l_lvds_atomic_enable,
> +	.atomic_disable = rzg3l_lvds_atomic_disable,
> +	.mode_valid = rzg3l_lvds_bridge_mode_valid,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Power Management
> + */
> +
> +static int rzg3l_lvds_pm_runtime_suspend(struct device *dev)
> +{
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = reset_control_assert(lvds->lvd_rstc);
> +	if (ret)
> +		return ret;
> +
> +	ret = reset_control_assert(lvds->prstc);
> +	if (ret)
> +		goto err_deassert_lvd_rstc;
> +
> +	return 0;
> +
> +err_deassert_lvd_rstc:
> +	reset_control_deassert(lvds->lvd_rstc);
> +	return ret;
> +}
> +
> +static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
> +{
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = reset_control_deassert(lvds->prstc);
> +	if (ret)
> +		return ret;
> +
> +	return reset_control_deassert(lvds->lvd_rstc);
> +	if (ret)
> +		goto err_assert_prstc;
> +
> +	return 0;
> +
> +err_assert_prstc:
> +	reset_control_assert(lvds->prstc);
> +	return ret;
> +}
> +
> +static const struct dev_pm_ops rzg3l_lvds_pm_ops = {
> +	RUNTIME_PM_OPS(rzg3l_lvds_pm_runtime_suspend,
> +		       rzg3l_lvds_pm_runtime_resume, NULL)
> +	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
> +};

DEFINE_RUNTIME_DEV_PM_OPS()

> +
> +/* -----------------------------------------------------------------------------
> + * Probe & Remove
> + */
> +
> +static int rzg3l_lvds_parse_dt(struct rzg3l_lvds *lvds)
> +{
> +	struct device *dev = lvds->dev;
> +	struct drm_panel *panel;
> +	int ret;
> +
> +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,
> +					  &panel, &lvds->next_bridge);
> +	if (ret)
> +		return ret;
> +
> +	if (panel) {
> +		lvds->next_bridge = devm_drm_panel_bridge_add(dev, panel);
> +		if (IS_ERR_OR_NULL(lvds->next_bridge))
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rzg3l_lvds_probe(struct platform_device *pdev)
> +{
> +	struct reset_control *rstc, *arstc;
> +	struct device *dev = &pdev->dev;
> +	struct rzg3l_lvds *lvds;
> +	int ret;
> +
> +	lvds = devm_drm_bridge_alloc(dev, struct rzg3l_lvds, bridge,
> +				     &rzg3l_lvds_bridge_ops);
> +	if (IS_ERR(lvds))
> +		return PTR_ERR(lvds);
> +
> +	lvds->dev = dev;
> +	lvds->bridge.of_node = pdev->dev.of_node;
> +
> +	lvds->regmap = syscon_node_to_regmap(dev->of_node->parent);
> +	if (IS_ERR(lvds->regmap))
> +		return PTR_ERR(lvds->regmap);
> +
> +	rstc = devm_reset_control_get_optional_exclusive(dev, "rst");
> +	if (IS_ERR(rstc))
> +		return dev_err_probe(dev, PTR_ERR(rstc), "failed to get rst\n");
> +
> +	arstc = devm_reset_control_get_optional_exclusive(dev, "arst");
> +	if (IS_ERR(arstc))
> +		return dev_err_probe(dev, PTR_ERR(arstc),
> +				     "failed to get arst\n");
> +
> +	lvds->prstc = devm_reset_control_get_shared(dev, "prst");
> +	if (IS_ERR(lvds->prstc))
> +		return dev_err_probe(dev, PTR_ERR(lvds->prstc),
> +				     "failed to get prst\n");
> +
> +	lvds->lvd_rstc = devm_reset_control_get_shared(dev, "lvdrst");
> +	if (IS_ERR(lvds->lvd_rstc))
> +		return dev_err_probe(dev, PTR_ERR(lvds->lvd_rstc),
> +				     "failed to get core reset\n");
> +
> +	platform_set_drvdata(pdev, lvds);
> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to enable Runtime PM\n");
> +
> +	ret = rzg3l_lvds_parse_dt(lvds);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = reset_control_assert(rstc);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = reset_control_assert(arstc);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = devm_drm_bridge_add(dev, &lvds->bridge);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "Failed to register drm bridge\n");
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id rzg3l_lvds_of_table[] = {
> +	{ .compatible = "renesas,r9a08g046-lvds" },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, rzg3l_lvds_of_table);
> +
> +static struct platform_driver rzg3l_lvds_platform_driver = {
> +	.probe		= rzg3l_lvds_probe,
> +	.driver		= {
> +		.name	= "rzg3l-lvds",
> +		.pm	= pm_ptr(&rzg3l_lvds_pm_ops),
> +		.of_match_table = rzg3l_lvds_of_table,
> +	},
> +};
> +
> +module_platform_driver(rzg3l_lvds_platform_driver);
> +
> +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
> +MODULE_AUTHOR("Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>");
> +MODULE_DESCRIPTION("Renesas RZ/G3L LVDS Encoder Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
> new file mode 100644
> index 000000000000..2ea3cf4e1cae
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * RZ/G3L LVDS Encoder
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + *
> + */
> +
> +#ifndef __RZG3L_LVDS_H__
> +#define __RZG3L_LVDS_H__
> +
> +struct drm_bridge;
> +
> +#if IS_ENABLED(CONFIG_DRM_RZG3L_LVDS)
> +bool rzg3l_lvds_is_connected(struct drm_bridge *bridge);
> +#else
> +static inline bool rzg3l_lvds_is_connected(struct drm_bridge *bridge)
> +{
> +	return false;
> +}
> +#endif /* CONFIG_DRM_RZG3L_LVDS */
> +#endif /* __RZG3L_LVDS_H__ */
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> new file mode 100644
> index 000000000000..281b7648f168
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * RZ/G3L LVDS Interface Registers Definitions
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + *
> + */
> +
> +#ifndef __RZG3L_LVDS_REGS_H__
> +#define __RZG3L_LVDS_REGS_H__
> +
> +#define LVDS_CMN			0x00
> +#define LVDS_CMN_RST_PHY0_SEL		(1 << 24)
> +#define LVDS_CMN_RST_PHY0_SEL_CH0	(1 << 24)
> +#define LVDS_CMN_PHY_RESET		(1 << 0)
> +
> +#define LVDS_0_PHY_OFFSET		0x10
> +#define LVDS_0_PHY_CH_IO_EN_MSK		(0x1f)
> +#define LVDS_0_PHY_CH_IO_EN		(LVDS_0_PHY_CH_IO_EN_MSK << 0)
> +#define LVDS_0_PHY_CH_EN_BGR		BIT(8)
> +#define LVDS_0_PHY_CH_EN_LDO		BIT(9)
> +
> +#define LVDS_0_CTL_OFFSET		0x14
> +#define LVDS_0_CTL_FMT_SEL_MSK		GENMASK(23, 20)
> +
> +#endif /* __RZG3L_LVDS_REGS_H__ */
> -- 
> 2.43.0
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
  2026-04-19 15:58   ` Dmitry Baryshkov
@ 2026-04-20  7:31   ` Biju Das
  2026-04-21  9:12   ` Claudiu Beznea
  2 siblings, 0 replies; 15+ messages in thread
From: Biju Das @ 2026-04-20  7:31 UTC (permalink / raw)
  To: biju.das.au, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	magnus.damm
  Cc: linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, biju.das.au, Tommaso Merciai

Hi all,

> -----Original Message-----
> From: Biju <biju.das.au@gmail.com>
> Sent: 17 April 2026 18:53
> Subject: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
> 
> From: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Add support for the RZ/G3L LVDS encoder driver. It operates in single-link mode with 4 lanes (Data) + 1
> lane (Clock) and supports pixel clock rates from 25 to 87 MHz. The LVDS module cannot be used at the
> same time as MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and the
> MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and MIPI_DSI_ARESET_N reset signals must be
> asserted before using the LVDS module.
> 
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
>  drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
>  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
>  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
>  .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
>  5 files changed, 395 insertions(+)
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
>  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> 
> diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
> index 7f2ef7137ae5..cbfc7b6bccb8 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Kconfig
> +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
> @@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI
>  	def_tristate DRM_RZG2L_DU
>  	depends on DRM_RZG2L_USE_MIPI_DSI
>  	select DRM_MIPI_DSI
> +
> +config DRM_RZG3L_USE_LVDS
> +	bool "RZ/G3L DU LVDS Encoder Support"
> +	depends on DRM_BRIDGE && OF
> +	default DRM_RZG2L_DU
> +	help
> +	  Enable support for the RZ/G3L Display Unit embedded LVDS encoders.
> +
> +config DRM_RZG3L_LVDS
> +	def_tristate DRM_RZG2L_DU
> +	depends on DRM_RZG3L_USE_LVDS
> +	select DRM_KMS_HELPER
> +	select DRM_PANEL
> diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile
> index 2987900ea6b6..46decb7ac4f1 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Makefile
> +++ b/drivers/gpu/drm/renesas/rz-du/Makefile
> @@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1)	+= rzg2l_du_vsp.o
>  obj-$(CONFIG_DRM_RZG2L_DU)		+= rzg2l-du-drm.o
> 
>  obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)	+= rzg2l_mipi_dsi.o
> +obj-$(CONFIG_DRM_RZG3L_LVDS)		+= rzg3l_lvds.o
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> new file mode 100644
> index 000000000000..bedeedbdfada
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> @@ -0,0 +1,333 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3L LVDS Encoder Driver
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation  */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "rzg3l_lvds.h"
> +#include "rzg3l_lvds_regs.h"
> +
> +enum rzg3l_lvds_mode {
> +	RZG3L_LVDS_MODE_JEIDA = 0,
> +	RZG3L_LVDS_MODE_JEIDA_MIRROR = 1,
> +	RZG3L_LVDS_MODE_MODE2 = 2,
> +	RZG3L_LVDS_MODE_MODE2_MIRROR = 3,
> +	RZG3L_LVDS_MODE_VESA = 4,
> +	RZG3L_LVDS_MODE_VESA_MIRROR = 5,
> +	RZG3L_LVDS_MODE_MODE6 = 6,
> +	RZG3L_LVDS_MODE_MODE6_MIRROR = 7,
> +};
> +
> +struct rzg3l_lvds {
> +	struct device *dev;
> +	struct reset_control *prstc;
> +	struct reset_control *lvd_rstc;
> +	struct regmap *regmap;
> +
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +};
> +
> +#define bridge_to_rzg3l_lvds(b) \
> +	container_of(b, struct rzg3l_lvds, bridge)
> +
> +/*
> +-----------------------------------------------------------------------
> +------
> + * Bridge
> + */
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> +				     struct drm_atomic_state *state) {
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +	const struct drm_bridge_state *bridge_state;
> +	int ret;
> +	u32 fmt;
> +
> +	/* Get the LVDS format from the bridge state. */
> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> +	if (!bridge_state) {
> +		dev_err(lvds->dev, "failed to get bridge state\n");
> +		return;
> +	}
> +
> +	switch (bridge_state->output_bus_cfg.format) {
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> +		fmt = RZG3L_LVDS_MODE_JEIDA;
> +		break;
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		break;
> +	default:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> +			 bridge_state->output_bus_cfg.format);
> +		break;
> +	}
> +
> +	ret = pm_runtime_resume_and_get(lvds->dev);
> +	if (ret < 0) {
> +		dev_err(lvds->dev, "pm_runtime_resume_and_get error\n");
> +		return;
> +	}
> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> +	usleep_range(20, 25);
> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> +	usleep_range(10, 15);
> +
> +	regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> +	regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> +			   LVDS_0_CTL_FMT_SEL_MSK,
> +			   FIELD_PREP(LVDS_0_CTL_FMT_SEL_MSK, fmt));
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, LVDS_0_PHY_CH_IO_EN);
> +	regmap_write(lvds->regmap, LVDS_CMN,
> +		     LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> +	usleep_range(100, 150);
> +}
> +
> +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> +				      struct drm_atomic_state *state) {
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	regmap_update_bits(lvds->regmap, LVDS_CMN, LVDS_CMN_PHY_RESET, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, 0);
> +
> +	pm_runtime_put(lvds->dev);
> +}
> +
> +static int rzg3l_lvds_attach(struct drm_bridge *bridge,
> +			     struct drm_encoder *encoder,
> +			     enum drm_bridge_attach_flags flags) {
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	if (!lvds->next_bridge)
> +		return 0;
> +
> +	return drm_bridge_attach(encoder, lvds->next_bridge, bridge, flags); }
> +
> +static enum drm_mode_status
> +rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge,
> +			     const struct drm_display_info *info,
> +			     const struct drm_display_mode *mode) {
> +	if (mode->clock > 87000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock < 25000)
> +		return MODE_CLOCK_LOW;
> +
> +	return MODE_OK;
> +}
> +
> +bool rzg3l_lvds_is_connected(struct drm_bridge *bridge) {
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	return !!lvds->next_bridge;
> +}
> +EXPORT_SYMBOL_GPL(rzg3l_lvds_is_connected);
> +
> +static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = {
> +	.attach = rzg3l_lvds_attach,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.atomic_enable = rzg3l_lvds_atomic_enable,
> +	.atomic_disable = rzg3l_lvds_atomic_disable,
> +	.mode_valid = rzg3l_lvds_bridge_mode_valid, };
> +
> +/*
> +-----------------------------------------------------------------------
> +------
> + * Power Management
> + */
> +
> +static int rzg3l_lvds_pm_runtime_suspend(struct device *dev) {
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = reset_control_assert(lvds->lvd_rstc);
> +	if (ret)
> +		return ret;
> +
> +	ret = reset_control_assert(lvds->prstc);
> +	if (ret)
> +		goto err_deassert_lvd_rstc;
> +
> +	return 0;
> +
> +err_deassert_lvd_rstc:
> +	reset_control_deassert(lvds->lvd_rstc);
> +	return ret;
> +}
> +
> +static int rzg3l_lvds_pm_runtime_resume(struct device *dev) {
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = reset_control_deassert(lvds->prstc);
> +	if (ret)
> +		return ret;
> +
> +	return reset_control_deassert(lvds->lvd_rstc);

This should be,

ret = reset_control_deassert(lvds->lvd_rstc);

Cheers,
Biju

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder
  2026-04-17 17:52 ` [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder Biju
@ 2026-04-20 16:21   ` Conor Dooley
  0 siblings, 0 replies; 15+ messages in thread
From: Conor Dooley @ 2026-04-20 16:21 UTC (permalink / raw)
  To: Biju
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Biju Das, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Tommaso Merciai, dri-devel,
	devicetree, linux-kernel, linux-renesas-soc,
	Prabhakar Mahadev Lad

[-- Attachment #1: Type: text/plain, Size: 573 bytes --]

On Fri, Apr 17, 2026 at 06:52:29PM +0100, Biju wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Document the LVDS encoder IP found on the RZ/G3L SoC. It supports
> single-link mode. LVDS and the DSI interface share a peripheral clock and
> the MIPI_DSI_PRESET_N reset signal. However, the LVDS module cannot be
> used at the same time as MIPI-DSI.
> 
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L
  2026-04-17 17:52 ` [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L Biju
@ 2026-04-20 16:21   ` Conor Dooley
  0 siblings, 0 replies; 15+ messages in thread
From: Conor Dooley @ 2026-04-20 16:21 UTC (permalink / raw)
  To: Biju
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Biju Das, devicetree,
	linux-kernel, linux-renesas-soc, Prabhakar Mahadev Lad,
	Tommaso Merciai

[-- Attachment #1: Type: text/plain, Size: 569 bytes --]

On Fri, Apr 17, 2026 at 06:52:28PM +0100, Biju wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
> 
> The RZ/G3{E,L} SoCs have an LVDS Common (LVDS_CMN) region which is common
> to all LVDS channels. The RZ/G3L has single-link, but the RZ/G3E has both
> single and dual-link.
> 
> Use the syscon interface to access these registers for scalability.
> 
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-19 15:58   ` Dmitry Baryshkov
@ 2026-04-21  9:11     ` Claudiu Beznea
  2026-04-21 11:22       ` Dmitry Baryshkov
  2026-04-21 16:21     ` Biju Das
  1 sibling, 1 reply; 15+ messages in thread
From: Claudiu Beznea @ 2026-04-21  9:11 UTC (permalink / raw)
  To: Dmitry Baryshkov, Biju
  Cc: Biju Das, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	Magnus Damm, linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Tommaso Merciai

Hi,

On 4/19/26 18:58, Dmitry Baryshkov wrote:
> On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
>> From: Biju Das <biju.das.jz@bp.renesas.com>
>>
>> Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
>> mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
>> from 25 to 87 MHz. The LVDS module cannot be used at the same time as
>> MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
>> the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
>> MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
>> module.
>>
>> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
>> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
>> ---

[ ...]

>> +/* -----------------------------------------------------------------------------
>> + * Bridge
>> + */
>> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
>> +				     struct drm_atomic_state *state)
>> +{
>> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
>> +	const struct drm_bridge_state *bridge_state;
>> +	int ret;
>> +	u32 fmt;
>> +
>> +	/* Get the LVDS format from the bridge state. */
>> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
>> +	if (!bridge_state) {
>> +		dev_err(lvds->dev, "failed to get bridge state\n");
>> +		return;
>> +	}
>> +
>> +	switch (bridge_state->output_bus_cfg.format) {
>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
>> +		fmt = RZG3L_LVDS_MODE_JEIDA;
>> +		break;
>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
>> +		fmt = RZG3L_LVDS_MODE_VESA;
>> +		break;
>> +	default:
>> +		fmt = RZG3L_LVDS_MODE_VESA;
>> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
>> +			 bridge_state->output_bus_cfg.format);
>> +		break;
>> +	}
>> +
>> +	ret = pm_runtime_resume_and_get(lvds->dev);
> 
> If this  fails for any reason, the atomic_disable() would still be
> called and it will decrement the counter, potentially undeflowing it.
> Consider switching to pm_runtime_get_sync(), which suits better here.

AFAIK, the clocks of this HW blocks have MSTOP functionality. HW manual of 
RZ/G3S [1] (should be the same for RZ/G3L as well) mentions the following in the 
chapter 41.2.1. "If the master accesses a module that has the clock stopped and 
the MSTOP bit set, a bus error will occur". [1]
MSTOP is set though the clock enable/disable APIs.

The clocks on RZ/G3L are part of clock power domains. If the 
pm_runtime_resume_and_get() fails (or any runtime PM resume calls), the clocks 
will be off and MSTOP set. In this case, calling atomic_disable() or any API 
setting HW registers will lead to sync aborts.

Thank you,
Claudiu

[1] 
https://renesasweb.s3.us-west-2.amazonaws.com/prod/docs/X014/X0141882/r01uh1014ej0120-rzg3s.pdf?response-content-disposition=inline%3B%20filename%3D%22r01uh1014ej0120-rzg3s.pdf%22&response-content-type=application%2Fpdf&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA3WXFIBJWSDVTHJX3%2F20260421%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20260421T075408Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86700&X-Amz-Signature=435eb49a37a080094aecf291bc3dc2d5b7ee62d3de8075151082e9db6c6acbe7


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
  2026-04-19 15:58   ` Dmitry Baryshkov
  2026-04-20  7:31   ` Biju Das
@ 2026-04-21  9:12   ` Claudiu Beznea
  2 siblings, 0 replies; 15+ messages in thread
From: Claudiu Beznea @ 2026-04-21  9:12 UTC (permalink / raw)
  To: Biju, Biju Das, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Philipp Zabel,
	Geert Uytterhoeven, Magnus Damm
  Cc: linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Tommaso Merciai

Hi,

On 4/17/26 20:52, Biju wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
> mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
> from 25 to 87 MHz. The LVDS module cannot be used at the same time as
> MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
> the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
> MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
> module.
> 
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>   drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
>   drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
>   drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
>   drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
>   .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
>   5 files changed, 395 insertions(+)
>   create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
>   create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
>   create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> 
> diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
> index 7f2ef7137ae5..cbfc7b6bccb8 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Kconfig
> +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
> @@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI
>   	def_tristate DRM_RZG2L_DU
>   	depends on DRM_RZG2L_USE_MIPI_DSI
>   	select DRM_MIPI_DSI
> +
> +config DRM_RZG3L_USE_LVDS
> +	bool "RZ/G3L DU LVDS Encoder Support"
> +	depends on DRM_BRIDGE && OF
> +	default DRM_RZG2L_DU
> +	help
> +	  Enable support for the RZ/G3L Display Unit embedded LVDS encoders.
> +
> +config DRM_RZG3L_LVDS
> +	def_tristate DRM_RZG2L_DU
> +	depends on DRM_RZG3L_USE_LVDS
> +	select DRM_KMS_HELPER
> +	select DRM_PANEL
> diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile
> index 2987900ea6b6..46decb7ac4f1 100644
> --- a/drivers/gpu/drm/renesas/rz-du/Makefile
> +++ b/drivers/gpu/drm/renesas/rz-du/Makefile
> @@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1)	+= rzg2l_du_vsp.o
>   obj-$(CONFIG_DRM_RZG2L_DU)		+= rzg2l-du-drm.o
>   
>   obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)	+= rzg2l_mipi_dsi.o
> +obj-$(CONFIG_DRM_RZG3L_LVDS)		+= rzg3l_lvds.o
> diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> new file mode 100644
> index 000000000000..bedeedbdfada
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> @@ -0,0 +1,333 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3L LVDS Encoder Driver
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "rzg3l_lvds.h"
> +#include "rzg3l_lvds_regs.h"
> +
> +enum rzg3l_lvds_mode {
> +	RZG3L_LVDS_MODE_JEIDA = 0,
> +	RZG3L_LVDS_MODE_JEIDA_MIRROR = 1,
> +	RZG3L_LVDS_MODE_MODE2 = 2,
> +	RZG3L_LVDS_MODE_MODE2_MIRROR = 3,
> +	RZG3L_LVDS_MODE_VESA = 4,
> +	RZG3L_LVDS_MODE_VESA_MIRROR = 5,
> +	RZG3L_LVDS_MODE_MODE6 = 6,
> +	RZG3L_LVDS_MODE_MODE6_MIRROR = 7,
> +};
> +
> +struct rzg3l_lvds {
> +	struct device *dev;
> +	struct reset_control *prstc;
> +	struct reset_control *lvd_rstc;
> +	struct regmap *regmap;
> +
> +	struct drm_bridge bridge;
> +	struct drm_bridge *next_bridge;
> +};
> +
> +#define bridge_to_rzg3l_lvds(b) \
> +	container_of(b, struct rzg3l_lvds, bridge)
> +
> +/* -----------------------------------------------------------------------------
> + * Bridge
> + */
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> +				     struct drm_atomic_state *state)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +	const struct drm_bridge_state *bridge_state;
> +	int ret;
> +	u32 fmt;
> +
> +	/* Get the LVDS format from the bridge state. */
> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> +	if (!bridge_state) {
> +		dev_err(lvds->dev, "failed to get bridge state\n");
> +		return;
> +	}
> +
> +	switch (bridge_state->output_bus_cfg.format) {
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> +		fmt = RZG3L_LVDS_MODE_JEIDA;
> +		break;
> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		break;
> +	default:
> +		fmt = RZG3L_LVDS_MODE_VESA;
> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> +			 bridge_state->output_bus_cfg.format);
> +		break;
> +	}
> +
> +	ret = pm_runtime_resume_and_get(lvds->dev);
> +	if (ret < 0) {
> +		dev_err(lvds->dev, "pm_runtime_resume_and_get error\n");
> +		return;
> +	}
> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> +	usleep_range(20, 25);

maybe fsleep()? Same for the above usleep_range()

> +
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> +	usleep_range(10, 15);
> +
> +	regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> +	regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> +			   LVDS_0_CTL_FMT_SEL_MSK,
> +			   FIELD_PREP(LVDS_0_CTL_FMT_SEL_MSK, fmt));
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, LVDS_0_PHY_CH_IO_EN);
> +	regmap_write(lvds->regmap, LVDS_CMN,
> +		     LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> +	usleep_range(100, 150);
> +}
> +
> +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> +				      struct drm_atomic_state *state)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	regmap_update_bits(lvds->regmap, LVDS_CMN, LVDS_CMN_PHY_RESET, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_IO_EN_MSK, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_LDO, 0);
> +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> +			   LVDS_0_PHY_CH_EN_BGR, 0);
> +
> +	pm_runtime_put(lvds->dev);
> +}
> +
> +static int rzg3l_lvds_attach(struct drm_bridge *bridge,
> +			     struct drm_encoder *encoder,
> +			     enum drm_bridge_attach_flags flags)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	if (!lvds->next_bridge)
> +		return 0;
> +
> +	return drm_bridge_attach(encoder, lvds->next_bridge, bridge, flags);
> +}
> +
> +static enum drm_mode_status
> +rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge,
> +			     const struct drm_display_info *info,
> +			     const struct drm_display_mode *mode)
> +{
> +	if (mode->clock > 87000)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock < 25000)
> +		return MODE_CLOCK_LOW;
> +
> +	return MODE_OK;
> +}
> +
> +bool rzg3l_lvds_is_connected(struct drm_bridge *bridge)
> +{
> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> +
> +	return !!lvds->next_bridge;
> +}
> +EXPORT_SYMBOL_GPL(rzg3l_lvds_is_connected);
> +
> +static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = {
> +	.attach = rzg3l_lvds_attach,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.atomic_enable = rzg3l_lvds_atomic_enable,
> +	.atomic_disable = rzg3l_lvds_atomic_disable,
> +	.mode_valid = rzg3l_lvds_bridge_mode_valid,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Power Management
> + */
> +
> +static int rzg3l_lvds_pm_runtime_suspend(struct device *dev)
> +{
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);

You can use:

	struct reset_control_bulk_data resets[] = {
		{ .rstc = lvds->lvd_rstc },
		{ .rstc = lvds->prstc },
	};


	return reset_control_bulk_assert(ARRAY_SIZE(resets), resets);

> +	int ret;
> +
> +	ret = reset_control_assert(lvds->lvd_rstc);
> +	if (ret)
> +		return ret;
> +
> +	ret = reset_control_assert(lvds->prstc);
> +	if (ret)
> +		goto err_deassert_lvd_rstc;
> +
> +	return 0;
> +
> +err_deassert_lvd_rstc:
> +	reset_control_deassert(lvds->lvd_rstc);
> +	return ret;
> +}
> +
> +static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
> +{
> +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> +	int ret;

Same here.

> +
> +	ret = reset_control_deassert(lvds->prstc);
> +	if (ret)
> +		return ret;
> +
> +	return reset_control_deassert(lvds->lvd_rstc);
> +	if (ret)
> +		goto err_assert_prstc;
> +
> +	return 0;
> +
> +err_assert_prstc:
> +	reset_control_assert(lvds->prstc);
> +	return ret;
> +}
> +
> +static const struct dev_pm_ops rzg3l_lvds_pm_ops = {
> +	RUNTIME_PM_OPS(rzg3l_lvds_pm_runtime_suspend,
> +		       rzg3l_lvds_pm_runtime_resume, NULL)
> +	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Probe & Remove
> + */
> +
> +static int rzg3l_lvds_parse_dt(struct rzg3l_lvds *lvds)
> +{
> +	struct device *dev = lvds->dev;
> +	struct drm_panel *panel;
> +	int ret;
> +
> +	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,
> +					  &panel, &lvds->next_bridge);

 From documentation of this function:

  * This function is deprecated and should not be used in new drivers. Use 

  * devm_drm_of_get_bridge() instead. 


devm_of_get_bridged() seems to be doing exactly what rzg3l_lvds_parse_dt() is doing.

Thank you,
Claudiu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-21  9:11     ` Claudiu Beznea
@ 2026-04-21 11:22       ` Dmitry Baryshkov
  2026-04-22  8:55         ` Claudiu Beznea
  0 siblings, 1 reply; 15+ messages in thread
From: Dmitry Baryshkov @ 2026-04-21 11:22 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: Biju, Biju Das, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Philipp Zabel,
	Geert Uytterhoeven, Magnus Damm, linux-kernel, dri-devel,
	linux-renesas-soc, Prabhakar Mahadev Lad, Tommaso Merciai

On Tue, Apr 21, 2026 at 12:11:28PM +0300, Claudiu Beznea wrote:
> Hi,
> 
> On 4/19/26 18:58, Dmitry Baryshkov wrote:
> > On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
> > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > 
> > > Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
> > > mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
> > > from 25 to 87 MHz. The LVDS module cannot be used at the same time as
> > > MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
> > > the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
> > > MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
> > > module.
> > > 
> > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > ---
> 
> [ ...]
> 
> > > +/* -----------------------------------------------------------------------------
> > > + * Bridge
> > > + */
> > > +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> > > +				     struct drm_atomic_state *state)
> > > +{
> > > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > > +	const struct drm_bridge_state *bridge_state;
> > > +	int ret;
> > > +	u32 fmt;
> > > +
> > > +	/* Get the LVDS format from the bridge state. */
> > > +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> > > +	if (!bridge_state) {
> > > +		dev_err(lvds->dev, "failed to get bridge state\n");
> > > +		return;
> > > +	}
> > > +
> > > +	switch (bridge_state->output_bus_cfg.format) {
> > > +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> > > +		fmt = RZG3L_LVDS_MODE_JEIDA;
> > > +		break;
> > > +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> > > +		fmt = RZG3L_LVDS_MODE_VESA;
> > > +		break;
> > > +	default:
> > > +		fmt = RZG3L_LVDS_MODE_VESA;
> > > +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> > > +			 bridge_state->output_bus_cfg.format);
> > > +		break;
> > > +	}
> > > +
> > > +	ret = pm_runtime_resume_and_get(lvds->dev);
> > 
> > If this  fails for any reason, the atomic_disable() would still be
> > called and it will decrement the counter, potentially undeflowing it.
> > Consider switching to pm_runtime_get_sync(), which suits better here.
> 
> AFAIK, the clocks of this HW blocks have MSTOP functionality. HW manual of
> RZ/G3S [1] (should be the same for RZ/G3L as well) mentions the following in
> the chapter 41.2.1. "If the master accesses a module that has the clock
> stopped and the MSTOP bit set, a bus error will occur". [1]
> MSTOP is set though the clock enable/disable APIs.
> 
> The clocks on RZ/G3L are part of clock power domains. If the
> pm_runtime_resume_and_get() fails (or any runtime PM resume calls), the
> clocks will be off and MSTOP set. In this case, calling atomic_disable() or
> any API setting HW registers will lead to sync aborts.

Then you've identified a bug in the code. The atomic_enable() doesn't
fail, so for each enable there always will be an atomic_disable() call.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-19 15:58   ` Dmitry Baryshkov
  2026-04-21  9:11     ` Claudiu Beznea
@ 2026-04-21 16:21     ` Biju Das
  1 sibling, 0 replies; 15+ messages in thread
From: Biju Das @ 2026-04-21 16:21 UTC (permalink / raw)
  To: Dmitry Baryshkov, biju.das.au
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	magnus.damm, linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Tommaso Merciai

Hi Dmitry Baryshkov,

Thanks for the feedback.

> -----Original Message-----
> From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Sent: 19 April 2026 16:59
> Subject: Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
> 
> On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
> > From: Biju Das <biju.das.jz@bp.renesas.com>
> >
> > Add support for the RZ/G3L LVDS encoder driver. It operates in
> > single-link mode with 4 lanes (Data) + 1 lane (Clock) and supports
> > pixel clock rates from 25 to 87 MHz. The LVDS module cannot be used at
> > the same time as MIPI-DSI. However, LVDS and the DSI interface share a
> > peripheral clock and the MIPI_DSI_PRESET_N reset signal. Also, the
> > MIPI_DSI_CMN_RSTB and MIPI_DSI_ARESET_N reset signals must be asserted
> > before using the LVDS module.
> >
> > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> > ---
> >  drivers/gpu/drm/renesas/rz-du/Kconfig         |  13 +
> >  drivers/gpu/drm/renesas/rz-du/Makefile        |   1 +
> >  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c    | 333 ++++++++++++++++++
> >  drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h    |  22 ++
> >  .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h   |  26 ++
> >  5 files changed, 395 insertions(+)
> >  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> >  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.h
> >  create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> >
> > diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig
> > b/drivers/gpu/drm/renesas/rz-du/Kconfig
> > index 7f2ef7137ae5..cbfc7b6bccb8 100644
> > --- a/drivers/gpu/drm/renesas/rz-du/Kconfig
> > +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
> > @@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI
> >  	def_tristate DRM_RZG2L_DU
> >  	depends on DRM_RZG2L_USE_MIPI_DSI
> >  	select DRM_MIPI_DSI
> > +
> > +config DRM_RZG3L_USE_LVDS
> > +	bool "RZ/G3L DU LVDS Encoder Support"
> > +	depends on DRM_BRIDGE && OF
> > +	default DRM_RZG2L_DU
> > +	help
> > +	  Enable support for the RZ/G3L Display Unit embedded LVDS encoders.
> > +
> > +config DRM_RZG3L_LVDS
> > +	def_tristate DRM_RZG2L_DU
> > +	depends on DRM_RZG3L_USE_LVDS
> > +	select DRM_KMS_HELPER
> > +	select DRM_PANEL
> > diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile
> > b/drivers/gpu/drm/renesas/rz-du/Makefile
> > index 2987900ea6b6..46decb7ac4f1 100644
> > --- a/drivers/gpu/drm/renesas/rz-du/Makefile
> > +++ b/drivers/gpu/drm/renesas/rz-du/Makefile
> > @@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1)	+= rzg2l_du_vsp.o
> >  obj-$(CONFIG_DRM_RZG2L_DU)		+= rzg2l-du-drm.o
> >
> >  obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)	+= rzg2l_mipi_dsi.o
> > +obj-$(CONFIG_DRM_RZG3L_LVDS)		+= rzg3l_lvds.o
> > diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> > b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> > new file mode 100644
> > index 000000000000..bedeedbdfada
> > --- /dev/null
> > +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> > @@ -0,0 +1,333 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * RZ/G3L LVDS Encoder Driver
> > + *
> > + * Copyright (C) 2026 Renesas Electronics Corporation  */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/media-bus-format.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/reset.h>
> > +
> > +#include <drm/drm_atomic.h>
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_of.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_probe_helper.h>
> > +
> > +#include "rzg3l_lvds.h"
> > +#include "rzg3l_lvds_regs.h"
> > +
> > +enum rzg3l_lvds_mode {
> > +	RZG3L_LVDS_MODE_JEIDA = 0,
> > +	RZG3L_LVDS_MODE_JEIDA_MIRROR = 1,
> > +	RZG3L_LVDS_MODE_MODE2 = 2,
> > +	RZG3L_LVDS_MODE_MODE2_MIRROR = 3,
> > +	RZG3L_LVDS_MODE_VESA = 4,
> > +	RZG3L_LVDS_MODE_VESA_MIRROR = 5,
> > +	RZG3L_LVDS_MODE_MODE6 = 6,
> > +	RZG3L_LVDS_MODE_MODE6_MIRROR = 7,
> > +};
> > +
> > +struct rzg3l_lvds {
> > +	struct device *dev;
> > +	struct reset_control *prstc;
> > +	struct reset_control *lvd_rstc;
> > +	struct regmap *regmap;
> > +
> > +	struct drm_bridge bridge;
> > +	struct drm_bridge *next_bridge;
> 
> Please use next_bridge from the drm_bridge struct.

OK.

> 
> > +};
> > +
> > +#define bridge_to_rzg3l_lvds(b) \
> > +	container_of(b, struct rzg3l_lvds, bridge)
> > +
> > +/*
> > +---------------------------------------------------------------------
> > +--------
> > + * Bridge
> > + */
> > +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> > +				     struct drm_atomic_state *state) {
> > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > +	const struct drm_bridge_state *bridge_state;
> > +	int ret;
> > +	u32 fmt;
> > +
> > +	/* Get the LVDS format from the bridge state. */
> > +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> > +	if (!bridge_state) {
> > +		dev_err(lvds->dev, "failed to get bridge state\n");
> > +		return;
> > +	}
> > +
> > +	switch (bridge_state->output_bus_cfg.format) {
> > +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> > +		fmt = RZG3L_LVDS_MODE_JEIDA;
> > +		break;
> > +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> > +		fmt = RZG3L_LVDS_MODE_VESA;
> > +		break;
> > +	default:
> > +		fmt = RZG3L_LVDS_MODE_VESA;
> > +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> > +			 bridge_state->output_bus_cfg.format);
> > +		break;
> > +	}
> > +
> > +	ret = pm_runtime_resume_and_get(lvds->dev);
> 
> If this  fails for any reason, the atomic_disable() would still be called and it will decrement the
> counter, potentially undeflowing it.
> Consider switching to pm_runtime_get_sync(), which suits better here.

Agreed.

> 
> 
> > +	if (ret < 0) {
> > +		dev_err(lvds->dev, "pm_runtime_resume_and_get error\n");
> > +		return;
> > +	}
> > +
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> > +	usleep_range(20, 25);
> > +
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> > +	usleep_range(10, 15);
> > +
> > +	regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> > +	regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> > +			   LVDS_0_CTL_FMT_SEL_MSK,
> > +			   FIELD_PREP(LVDS_0_CTL_FMT_SEL_MSK, fmt));
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_IO_EN_MSK, LVDS_0_PHY_CH_IO_EN);
> > +	regmap_write(lvds->regmap, LVDS_CMN,
> > +		     LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> > +	usleep_range(100, 150);
> > +}
> > +
> > +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> > +				      struct drm_atomic_state *state) {
> > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > +
> > +	regmap_update_bits(lvds->regmap, LVDS_CMN, LVDS_CMN_PHY_RESET, 0);
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_IO_EN_MSK, 0);
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_EN_LDO, 0);
> > +	regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> > +			   LVDS_0_PHY_CH_EN_BGR, 0);
> > +
> > +	pm_runtime_put(lvds->dev);
> > +}
> > +
> > +static int rzg3l_lvds_attach(struct drm_bridge *bridge,
> > +			     struct drm_encoder *encoder,
> > +			     enum drm_bridge_attach_flags flags) {
> > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > +
> > +	if (!lvds->next_bridge)
> > +		return 0;
> > +
> > +	return drm_bridge_attach(encoder, lvds->next_bridge, bridge, flags);
> > +}
> > +
> > +static enum drm_mode_status
> > +rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge,
> > +			     const struct drm_display_info *info,
> > +			     const struct drm_display_mode *mode) {
> > +	if (mode->clock > 87000)
> > +		return MODE_CLOCK_HIGH;
> > +
> > +	if (mode->clock < 25000)
> > +		return MODE_CLOCK_LOW;
> > +
> > +	return MODE_OK;
> > +}
> > +
> > +bool rzg3l_lvds_is_connected(struct drm_bridge *bridge) {
> > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > +
> > +	return !!lvds->next_bridge;
> > +}
> > +EXPORT_SYMBOL_GPL(rzg3l_lvds_is_connected);
> 
> How is this going to be used? I don't see the user in the patch. Please drop the unused API.

OK, will drop this patch as it is not required for this platform.


> 
> > +
> > +static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = {
> > +	.attach = rzg3l_lvds_attach,
> > +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> > +	.atomic_reset = drm_atomic_helper_bridge_reset,
> > +	.atomic_enable = rzg3l_lvds_atomic_enable,
> > +	.atomic_disable = rzg3l_lvds_atomic_disable,
> > +	.mode_valid = rzg3l_lvds_bridge_mode_valid, };
> > +
> > +/*
> > +---------------------------------------------------------------------
> > +--------
> > + * Power Management
> > + */
> > +
> > +static int rzg3l_lvds_pm_runtime_suspend(struct device *dev) {
> > +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = reset_control_assert(lvds->lvd_rstc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = reset_control_assert(lvds->prstc);
> > +	if (ret)
> > +		goto err_deassert_lvd_rstc;
> > +
> > +	return 0;
> > +
> > +err_deassert_lvd_rstc:
> > +	reset_control_deassert(lvds->lvd_rstc);
> > +	return ret;
> > +}
> > +
> > +static int rzg3l_lvds_pm_runtime_resume(struct device *dev) {
> > +	struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	ret = reset_control_deassert(lvds->prstc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return reset_control_deassert(lvds->lvd_rstc);
> > +	if (ret)
> > +		goto err_assert_prstc;
> > +
> > +	return 0;
> > +
> > +err_assert_prstc:
> > +	reset_control_assert(lvds->prstc);
> > +	return ret;
> > +}
> > +
> > +static const struct dev_pm_ops rzg3l_lvds_pm_ops = {
> > +	RUNTIME_PM_OPS(rzg3l_lvds_pm_runtime_suspend,
> > +		       rzg3l_lvds_pm_runtime_resume, NULL)
> > +	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > +pm_runtime_force_resume) };
> 
> DEFINE_RUNTIME_DEV_PM_OPS()

OK. Will send v2 with the above changes.

Cheers,
Biju

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-21 11:22       ` Dmitry Baryshkov
@ 2026-04-22  8:55         ` Claudiu Beznea
  2026-04-22  9:09           ` Biju Das
  2026-04-22 18:45           ` Dmitry Baryshkov
  0 siblings, 2 replies; 15+ messages in thread
From: Claudiu Beznea @ 2026-04-22  8:55 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Biju, Biju Das, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Philipp Zabel,
	Geert Uytterhoeven, Magnus Damm, linux-kernel, dri-devel,
	linux-renesas-soc, Prabhakar Mahadev Lad, Tommaso Merciai



On 4/21/26 14:22, Dmitry Baryshkov wrote:
> On Tue, Apr 21, 2026 at 12:11:28PM +0300, Claudiu Beznea wrote:
>> Hi,
>>
>> On 4/19/26 18:58, Dmitry Baryshkov wrote:
>>> On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
>>>> From: Biju Das <biju.das.jz@bp.renesas.com>
>>>>
>>>> Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
>>>> mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
>>>> from 25 to 87 MHz. The LVDS module cannot be used at the same time as
>>>> MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
>>>> the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
>>>> MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
>>>> module.
>>>>
>>>> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
>>>> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
>>>> ---
>>
>> [ ...]
>>
>>>> +/* -----------------------------------------------------------------------------
>>>> + * Bridge
>>>> + */
>>>> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
>>>> +				     struct drm_atomic_state *state)
>>>> +{
>>>> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
>>>> +	const struct drm_bridge_state *bridge_state;
>>>> +	int ret;
>>>> +	u32 fmt;
>>>> +
>>>> +	/* Get the LVDS format from the bridge state. */
>>>> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
>>>> +	if (!bridge_state) {
>>>> +		dev_err(lvds->dev, "failed to get bridge state\n");
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	switch (bridge_state->output_bus_cfg.format) {
>>>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
>>>> +		fmt = RZG3L_LVDS_MODE_JEIDA;
>>>> +		break;
>>>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
>>>> +		fmt = RZG3L_LVDS_MODE_VESA;
>>>> +		break;
>>>> +	default:
>>>> +		fmt = RZG3L_LVDS_MODE_VESA;
>>>> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
>>>> +			 bridge_state->output_bus_cfg.format);
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	ret = pm_runtime_resume_and_get(lvds->dev);
>>>
>>> If this  fails for any reason, the atomic_disable() would still be
>>> called and it will decrement the counter, potentially undeflowing it.
>>> Consider switching to pm_runtime_get_sync(), which suits better here.
>>
>> AFAIK, the clocks of this HW blocks have MSTOP functionality. HW manual of
>> RZ/G3S [1] (should be the same for RZ/G3L as well) mentions the following in
>> the chapter 41.2.1. "If the master accesses a module that has the clock
>> stopped and the MSTOP bit set, a bus error will occur". [1]
>> MSTOP is set though the clock enable/disable APIs.
>>
>> The clocks on RZ/G3L are part of clock power domains. If the
>> pm_runtime_resume_and_get() fails (or any runtime PM resume calls), the
>> clocks will be off and MSTOP set. In this case, calling atomic_disable() or
>> any API setting HW registers will lead to sync aborts.
> 
> Then you've identified a bug in the code. The atomic_enable() doesn't
> fail, so for each enable there always will be an atomic_disable() call.
> 

Is this something that should be solved by individual drivers providing struct 
drm_bridge_funcs to the upper layers or by the subsystem itself?

Accessing HW w/o its power being on (whatever power means here, e.g. clocks, 
resets, regulators) seems odd and may lead to critical failures.

On some Renesas SoCs this used to work previously but it is not anymore with the 
addition of the so called MSTOP functionality.

Thank you,
Claudiu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-22  8:55         ` Claudiu Beznea
@ 2026-04-22  9:09           ` Biju Das
  2026-04-22 18:45           ` Dmitry Baryshkov
  1 sibling, 0 replies; 15+ messages in thread
From: Biju Das @ 2026-04-22  9:09 UTC (permalink / raw)
  To: Claudiu.Beznea, Dmitry Baryshkov
  Cc: biju.das.au, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Philipp Zabel, Geert Uytterhoeven,
	magnus.damm, linux-kernel, dri-devel, linux-renesas-soc,
	Prabhakar Mahadev Lad, Tommaso Merciai



> -----Original Message-----
> From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
> Sent: 22 April 2026 09:56
> Subject: Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
> 
> 
> 
> On 4/21/26 14:22, Dmitry Baryshkov wrote:
> > On Tue, Apr 21, 2026 at 12:11:28PM +0300, Claudiu Beznea wrote:
> >> Hi,
> >>
> >> On 4/19/26 18:58, Dmitry Baryshkov wrote:
> >>> On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
> >>>> From: Biju Das <biju.das.jz@bp.renesas.com>
> >>>>
> >>>> Add support for the RZ/G3L LVDS encoder driver. It operates in
> >>>> single-link mode with 4 lanes (Data) + 1 lane (Clock) and supports
> >>>> pixel clock rates from 25 to 87 MHz. The LVDS module cannot be used
> >>>> at the same time as MIPI-DSI. However, LVDS and the DSI interface
> >>>> share a peripheral clock and the MIPI_DSI_PRESET_N reset signal.
> >>>> Also, the MIPI_DSI_CMN_RSTB and MIPI_DSI_ARESET_N reset signals
> >>>> must be asserted before using the LVDS module.
> >>>>
> >>>> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> >>>> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> >>>> ---
> >>
> >> [ ...]
> >>
> >>>> +/*
> >>>> +------------------------------------------------------------------
> >>>> +-----------
> >>>> + * Bridge
> >>>> + */
> >>>> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> >>>> +				     struct drm_atomic_state *state) {
> >>>> +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> >>>> +	const struct drm_bridge_state *bridge_state;
> >>>> +	int ret;
> >>>> +	u32 fmt;
> >>>> +
> >>>> +	/* Get the LVDS format from the bridge state. */
> >>>> +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> >>>> +	if (!bridge_state) {
> >>>> +		dev_err(lvds->dev, "failed to get bridge state\n");
> >>>> +		return;
> >>>> +	}
> >>>> +
> >>>> +	switch (bridge_state->output_bus_cfg.format) {
> >>>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> >>>> +		fmt = RZG3L_LVDS_MODE_JEIDA;
> >>>> +		break;
> >>>> +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> >>>> +		fmt = RZG3L_LVDS_MODE_VESA;
> >>>> +		break;
> >>>> +	default:
> >>>> +		fmt = RZG3L_LVDS_MODE_VESA;
> >>>> +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> >>>> +			 bridge_state->output_bus_cfg.format);
> >>>> +		break;
> >>>> +	}
> >>>> +
> >>>> +	ret = pm_runtime_resume_and_get(lvds->dev);
> >>>
> >>> If this  fails for any reason, the atomic_disable() would still be
> >>> called and it will decrement the counter, potentially undeflowing it.
> >>> Consider switching to pm_runtime_get_sync(), which suits better here.
> >>
> >> AFAIK, the clocks of this HW blocks have MSTOP functionality. HW
> >> manual of RZ/G3S [1] (should be the same for RZ/G3L as well) mentions
> >> the following in the chapter 41.2.1. "If the master accesses a module
> >> that has the clock stopped and the MSTOP bit set, a bus error will
> >> occur". [1] MSTOP is set though the clock enable/disable APIs.
> >>
> >> The clocks on RZ/G3L are part of clock power domains. If the
> >> pm_runtime_resume_and_get() fails (or any runtime PM resume calls),
> >> the clocks will be off and MSTOP set. In this case, calling
> >> atomic_disable() or any API setting HW registers will lead to sync aborts.
> >
> > Then you've identified a bug in the code. The atomic_enable() doesn't
> > fail, so for each enable there always will be an atomic_disable() call.
> >
> 
> Is this something that should be solved by individual drivers providing struct drm_bridge_funcs to the
> upper layers or by the subsystem itself?

This use case is like system suspend/resume right.

For each system resume call, there will be a system suspend call in future.

Cheers,
Biju

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
  2026-04-22  8:55         ` Claudiu Beznea
  2026-04-22  9:09           ` Biju Das
@ 2026-04-22 18:45           ` Dmitry Baryshkov
  1 sibling, 0 replies; 15+ messages in thread
From: Dmitry Baryshkov @ 2026-04-22 18:45 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: Biju, Biju Das, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Philipp Zabel,
	Geert Uytterhoeven, Magnus Damm, linux-kernel, dri-devel,
	linux-renesas-soc, Prabhakar Mahadev Lad, Tommaso Merciai

On Wed, Apr 22, 2026 at 11:55:37AM +0300, Claudiu Beznea wrote:
> 
> 
> On 4/21/26 14:22, Dmitry Baryshkov wrote:
> > On Tue, Apr 21, 2026 at 12:11:28PM +0300, Claudiu Beznea wrote:
> > > Hi,
> > > 
> > > On 4/19/26 18:58, Dmitry Baryshkov wrote:
> > > > On Fri, Apr 17, 2026 at 06:52:30PM +0100, Biju wrote:
> > > > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > 
> > > > > Add support for the RZ/G3L LVDS encoder driver. It operates in single-link
> > > > > mode with 4 lanes (Data) + 1 lane (Clock) and supports pixel clock rates
> > > > > from 25 to 87 MHz. The LVDS module cannot be used at the same time as
> > > > > MIPI-DSI. However, LVDS and the DSI interface share a peripheral clock and
> > > > > the MIPI_DSI_PRESET_N reset signal. Also, the MIPI_DSI_CMN_RSTB and
> > > > > MIPI_DSI_ARESET_N reset signals must be asserted before using the LVDS
> > > > > module.
> > > > > 
> > > > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > > > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > ---
> > > 
> > > [ ...]
> > > 
> > > > > +/* -----------------------------------------------------------------------------
> > > > > + * Bridge
> > > > > + */
> > > > > +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> > > > > +				     struct drm_atomic_state *state)
> > > > > +{
> > > > > +	struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> > > > > +	const struct drm_bridge_state *bridge_state;
> > > > > +	int ret;
> > > > > +	u32 fmt;
> > > > > +
> > > > > +	/* Get the LVDS format from the bridge state. */
> > > > > +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> > > > > +	if (!bridge_state) {
> > > > > +		dev_err(lvds->dev, "failed to get bridge state\n");
> > > > > +		return;
> > > > > +	}
> > > > > +
> > > > > +	switch (bridge_state->output_bus_cfg.format) {
> > > > > +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> > > > > +		fmt = RZG3L_LVDS_MODE_JEIDA;
> > > > > +		break;
> > > > > +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> > > > > +		fmt = RZG3L_LVDS_MODE_VESA;
> > > > > +		break;
> > > > > +	default:
> > > > > +		fmt = RZG3L_LVDS_MODE_VESA;
> > > > > +		dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n",
> > > > > +			 bridge_state->output_bus_cfg.format);
> > > > > +		break;
> > > > > +	}
> > > > > +
> > > > > +	ret = pm_runtime_resume_and_get(lvds->dev);
> > > > 
> > > > If this  fails for any reason, the atomic_disable() would still be
> > > > called and it will decrement the counter, potentially undeflowing it.
> > > > Consider switching to pm_runtime_get_sync(), which suits better here.
> > > 
> > > AFAIK, the clocks of this HW blocks have MSTOP functionality. HW manual of
> > > RZ/G3S [1] (should be the same for RZ/G3L as well) mentions the following in
> > > the chapter 41.2.1. "If the master accesses a module that has the clock
> > > stopped and the MSTOP bit set, a bus error will occur". [1]
> > > MSTOP is set though the clock enable/disable APIs.
> > > 
> > > The clocks on RZ/G3L are part of clock power domains. If the
> > > pm_runtime_resume_and_get() fails (or any runtime PM resume calls), the
> > > clocks will be off and MSTOP set. In this case, calling atomic_disable() or
> > > any API setting HW registers will lead to sync aborts.
> > 
> > Then you've identified a bug in the code. The atomic_enable() doesn't
> > fail, so for each enable there always will be an atomic_disable() call.
> > 
> 
> Is this something that should be solved by individual drivers providing
> struct drm_bridge_funcs to the upper layers or by the subsystem itself?
> 
> Accessing HW w/o its power being on (whatever power means here, e.g. clocks,
> resets, regulators) seems odd and may lead to critical failures.

The major problem is that atomic_enable() can't fail for the design
reasons. So, yes, currently this needs to be handled by the driver which
calls pm_resume in the atomic_enable() path.

> 
> On some Renesas SoCs this used to work previously but it is not anymore with
> the addition of the so called MSTOP functionality.
> 
> Thank you,
> Claudiu

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-04-22 18:45 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-17 17:52 [PATCH 0/3] Add support for Renesas RZ/G3L LVDS encoder Biju
2026-04-17 17:52 ` [PATCH 1/3] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L Biju
2026-04-20 16:21   ` Conor Dooley
2026-04-17 17:52 ` [PATCH 2/3] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder Biju
2026-04-20 16:21   ` Conor Dooley
2026-04-17 17:52 ` [PATCH 3/3] drm: renesas: rz-du: Add support for " Biju
2026-04-19 15:58   ` Dmitry Baryshkov
2026-04-21  9:11     ` Claudiu Beznea
2026-04-21 11:22       ` Dmitry Baryshkov
2026-04-22  8:55         ` Claudiu Beznea
2026-04-22  9:09           ` Biju Das
2026-04-22 18:45           ` Dmitry Baryshkov
2026-04-21 16:21     ` Biju Das
2026-04-20  7:31   ` Biju Das
2026-04-21  9:12   ` Claudiu Beznea

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®