* [PATCH v4 0/2] Add support for Renesas RZ/G3L LVDS encoder
@ 2026-06-19 10:10 Biju
2026-06-19 10:10 ` [PATCH v4 1/2] dt-bindings: display: bridge: Document " Biju
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
0 siblings, 2 replies; 8+ messages in thread
From: Biju @ 2026-06-19 10:10 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, David Airlie,
Simona Vetter, Philipp Zabel, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: Biju Das, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, 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.
This patch series depend upon [1]
[1]
https://lore.kernel.org/all/20260608-drm-no-more-bridge-reset-v2-0-0a91018bf886@kernel.org/
v3->v4:
* Reworked bindings, dropping parent node containing simple-mfd and
syson.
* Dropped the tags
* Dropped the header files clk.h and syscon.h
* Dropped next_bridge check in attach().
* Dropped syscon for getting regmap.
* Replaced the below macros to match the hardware manual:
LVDS_0_CTL_FMT_SEL_MSK->LVDS_0_CTL_FMT_SEL0_MSK
LVDS_0_PHY_CH_IO_EN_MSK->LVDS_0_PHY_CH_IO_EN0_MSK
Replaced LVDS_0_PHY_CH_IO_EN->LVDS_0_PHY_CH_IO_EN0
* Replaced atomic_reset()->atomic_create_state().
v2->v3:
* Collected tags.
v2->v2[1]:
* Dropped patch#1 as it is accepted.
* Replace drm_atomic_state with drm_atomic_commit in
rzg3l_lvds_atomic_{en,dis}able().
* Drop local variable ret and dev_err() messages in
rzg3l_lvds_atomic_enable(); use WARN_ON() instead to
capture unexpected failures since atomic_enable should not fail.
* Drop local variable next_bridge from rzg3l_lvds_probe().
[1] https://lore.kernel.org/all/20260524194457.479681-1-biju.das.jz@bp.renesas.com/
v1->v2:
* Collected the tags for binding patches.
* Dropped unused function rzg3l_lvds_is_connected() and removed the
corresponding header file rzg3l_lvds.h
* Dropped next_bridge from struct rzg3l_lvds instead using bridge's
next_bridge.
* Replaced pm_runtime_resume_and_get()->pm_runtime_get_sync() as
atomic_enable doesn't fail and for each enable there always will be an
atomic_disable() call.
* Started using DEFINE_RUNTIME_DEV_PM_OPS for PM callback.
* Replaced rzg3l_lvds_parse_dt() with devm_drm_of_get_bridge() in probe()
* Started using reset_control_bulk_*() in rzg3l_lvds_pm_runtime_{suspend,
resume}().
Biju Das (2):
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 | 120 ++++++++
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 | 285 ++++++++++++++++++
.../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h | 26 ++
5 files changed, 445 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_regs.h
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v4 1/2] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder
2026-06-19 10:10 [PATCH v4 0/2] Add support for Renesas RZ/G3L LVDS encoder Biju
@ 2026-06-19 10:10 ` Biju
2026-06-24 6:54 ` Krzysztof Kozlowski
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
1 sibling, 1 reply; 8+ messages in thread
From: Biju @ 2026-06-19 10:10 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: Biju Das, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, 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>
---
v3->v4:
* Dropped the tags as it is a rework dropping parent node that contains
simple-mfd and syscon.
v2->v3:
* Collected tag.
v2->v2[1]:
* No change.
[1] https://lore.kernel.org/all/20260524195829.960401F000E9@smtp.kernel.org/
v1->v2:
* Collected tag.
---
.../bridge/renesas,r9a08g046-lvds.yaml | 120 ++++++++++++++++++
1 file changed, 120 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..4cd7b688fbf7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml
@@ -0,0 +1,120 @@
+# 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 describes 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@108a0000 {
+ compatible = "renesas,r9a08g046-lvds";
+ reg = <0x108a0000 0x10000>;
+ 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 related [flat|nested] 8+ messages in thread
* [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
2026-06-19 10:10 [PATCH v4 0/2] Add support for Renesas RZ/G3L LVDS encoder Biju
2026-06-19 10:10 ` [PATCH v4 1/2] dt-bindings: display: bridge: Document " Biju
@ 2026-06-19 10:10 ` Biju
2026-06-19 10:20 ` sashiko-bot
` (2 more replies)
1 sibling, 3 replies; 8+ messages in thread
From: Biju @ 2026-06-19 10:10 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>
---
v3->v4:
* Dropped the header files clk.h and syscon.h
* Dropped next_bridge check in attach().
* Dropped syscon for getting regmap.
* Replaced the below macros to match with hardware manual:
LVDS_0_CTL_FMT_SEL_MSK->LVDS_0_CTL_FMT_SEL0_MSK
LVDS_0_PHY_CH_IO_EN_MSK->LVDS_0_PHY_CH_IO_EN0_MSK
Replaced LVDS_0_PHY_CH_IO_EN->LVDS_0_PHY_CH_IO_EN0
* Replaced atomic_reset()->atomic_create_state()
* Dropped the tags as there are new changes.
v2->v3:
* Collected tags.
v2->v2[1]:
* Replace drm_atomic_state with drm_atomic_commit in
rzg3l_lvds_atomic_{en,dis}able().
* Drop local variable ret and dev_err() messages in
rzg3l_lvds_atomic_enable(); use WARN_ON() instead to
capture unexpected failures since atomic_enable should not fail.
* Drop local variable next_bridge from rzg3l_lvds_probe().
[1] https://lore.kernel.org/all/20260524194457.479681-3-biju.das.jz@bp.renesas.com/
v1->v2:
* Dropped unused function rzg3l_lvds_is_connected() and removed the
corresponding header file rzg3l_lvds.h
* Dropped next_bridge from struct rzg3l_lvds instead using bridge's
next_bridge.
* Replaced pm_runtime_resume_and_get()->pm_runtime_get_sync() as
atomic_enable doesn't fail and for each enable there always will be an
atomic_disable() call.
* Started using DEFINE_RUNTIME_DEV_PM_OPS for PM callback.
* Replaced rzg3l_lvds_parse_dt() with devm_drm_of_get_bridge() in probe()
* Started using reset_control_bulk_*() in rzg3l_lvds_pm_runtime_{suspend,
resume}()
---
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 | 285 ++++++++++++++++++
.../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h | 26 ++
4 files changed, 325 insertions(+)
create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
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..1e5b3dd1c0de 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 encoder.
+
+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..6100888ea728
--- /dev/null
+++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G3L LVDS Encoder Driver
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.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_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;
+};
+
+#define bridge_to_rzg3l_lvds(b) \
+ container_of(b, struct rzg3l_lvds, bridge)
+
+static const struct regmap_config rzg3l_lvds_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = LVDS_0_CTL_OFFSET,
+};
+
+/* -----------------------------------------------------------------------------
+ * Bridge
+ */
+
+static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *state)
+{
+ struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
+ const struct drm_bridge_state *bridge_state;
+ u32 fmt;
+
+ /* Get the LVDS format from the bridge state. */
+ bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+ if (WARN_ON(!bridge_state))
+ 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;
+ }
+
+ if (WARN_ON(pm_runtime_get_sync(lvds->dev) < 0))
+ return;
+
+ regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+ LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
+ fsleep(20);
+
+ regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+ LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
+ fsleep(10);
+
+ regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
+ regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
+ LVDS_0_CTL_FMT_SEL0_MSK,
+ FIELD_PREP(LVDS_0_CTL_FMT_SEL0_MSK, fmt));
+ regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
+ LVDS_0_PHY_CH_IO_EN0_MSK, LVDS_0_PHY_CH_IO_EN0);
+ regmap_write(lvds->regmap, LVDS_CMN,
+ LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
+ fsleep(100);
+}
+
+static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *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_EN0_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);
+
+ return drm_bridge_attach(encoder, lvds->bridge.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;
+}
+
+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_create_state = drm_atomic_helper_bridge_create_state,
+ .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);
+ struct reset_control_bulk_data resets[] = {
+ { .rstc = lvds->lvd_rstc },
+ { .rstc = lvds->prstc },
+ };
+
+ return reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
+}
+
+static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
+{
+ struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
+ struct reset_control_bulk_data resets[] = {
+ { .rstc = lvds->lvd_rstc },
+ { .rstc = lvds->prstc },
+ };
+
+ return reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(rzg3l_lvds_pm_ops,
+ rzg3l_lvds_pm_runtime_suspend,
+ rzg3l_lvds_pm_runtime_resume, NULL);
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int rzg3l_lvds_probe(struct platform_device *pdev)
+{
+ struct reset_control *rstc, *arstc;
+ struct device *dev = &pdev->dev;
+ struct rzg3l_lvds *lvds;
+ void __iomem *base;
+ 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;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ lvds->regmap = devm_regmap_init_mmio(dev, base, &rzg3l_lvds_regmap_config);
+ if (IS_ERR(lvds->regmap))
+ return dev_err_probe(dev, PTR_ERR(lvds->regmap),
+ "failed to init regmap\n");
+
+ rstc = devm_reset_control_get_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_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_exclusive(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_exclusive(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");
+
+ lvds->bridge.next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(lvds->bridge.next_bridge))
+ return dev_err_probe(dev, PTR_ERR(lvds->bridge.next_bridge),
+ "failed to get next bridge\n");
+
+ 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_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
new file mode 100644
index 000000000000..3dca3b630818
--- /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_EN0_MSK (0x1f)
+#define LVDS_0_PHY_CH_IO_EN0 (LVDS_0_PHY_CH_IO_EN0_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_SEL0_MSK GENMASK(23, 20)
+
+#endif /* __RZG3L_LVDS_REGS_H__ */
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
@ 2026-06-19 10:20 ` sashiko-bot
2026-06-19 11:19 ` Biju Das
2026-06-24 9:52 ` Tommaso Merciai
2026-06-24 15:45 ` Tommaso Merciai
2 siblings, 1 reply; 8+ messages in thread
From: sashiko-bot @ 2026-06-19 10:20 UTC (permalink / raw)
To: Biju; +Cc: wsa+renesas, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Sleeping functions called in atomic context during bridge enable
- [High] Early returns in `atomic_enable` cause unbalanced PM counter and unpowered hardware access in `atomic_disable`
--
commit 0eae00dcaa6988008a1dca84233d19bc97469ed0
Author: Biju Das <biju.das.jz@bp.renesas.com>
drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
Adds support for the RZ/G3L LVDS encoder driver operating in single-link
mode with 4 lanes. It supports pixel clocks from 25 to 87 MHz and shares
peripherals with MIPI-DSI.
> 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 0000000000000..6100888ea7284
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
[ ... ]
> @@ -0,0 +1,285 @@
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *state)
> +{
> + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> + const struct drm_bridge_state *bridge_state;
> + u32 fmt;
> +
> + /* Get the LVDS format from the bridge state. */
> + bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> + if (WARN_ON(!bridge_state))
> + return;
[Severity: High]
If drm_atomic_get_new_bridge_state() returns NULL and takes this early return,
will rzg3l_lvds_atomic_disable() later execute unconditionally and access
unpowered hardware registers or cause a runtime PM usage counter underflow?
Since atomic_enable returns void, the DRM core might still consider the bridge
enabled.
> +
> + 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;
> + }
> +
> + if (WARN_ON(pm_runtime_get_sync(lvds->dev) < 0))
> + return;
[Severity: High]
Similarly, if pm_runtime_get_sync() fails, returning early here leaves the
device unpowered. When rzg3l_lvds_atomic_disable() is called during teardown,
could this lead to a bus fault from accessing unpowered MMIO registers and
an unbalanced pm_runtime_put() counter?
[Severity: High]
Also, since atomic_enable callbacks run in an atomic commit context where
sleeping is forbidden, is it safe to use pm_runtime_get_sync() here? This
function blocks and sleeps while waiting for the device to resume, which might
cause a scheduling while atomic regression.
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> + fsleep(20);
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> + fsleep(10);
[Severity: High]
Because this is an atomic commit path, delay functions that can sleep like
fsleep() are unsafe. Could these fsleep() calls (which map to usleep_range()
or msleep()) lead to scheduling while atomic bugs? Would udelay() or ndelay()
be more appropriate for these hardware delays?
> +
> + regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> + regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> + LVDS_0_CTL_FMT_SEL0_MSK,
> + FIELD_PREP(LVDS_0_CTL_FMT_SEL0_MSK, fmt));
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_IO_EN0_MSK, LVDS_0_PHY_CH_IO_EN0);
> + regmap_write(lvds->regmap, LVDS_CMN,
> + LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> + fsleep(100);
[Severity: High]
Could this fsleep(100) call also sleep in this atomic context?
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260619101026.323633-1-biju.das.jz@bp.renesas.com?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
2026-06-19 10:20 ` sashiko-bot
@ 2026-06-19 11:19 ` Biju Das
0 siblings, 0 replies; 8+ messages in thread
From: Biju Das @ 2026-06-19 11:19 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev, biju.das.au
Cc: wsa+renesas, dri-devel@lists.freedesktop.org
Hi sashiko,
Thanks for the review.
> -----Original Message-----
> From: dri-devel <dri-devel-bounces@lists.freedesktop.org> On Behalf Of sashiko-bot@kernel.org
> Sent: 19 June 2026 11:20
> To: biju.das.au <biju.das.au@gmail.com>
> Cc: wsa+renesas <wsa+renesas@sang-engineering.com>; dri-devel@lists.freedesktop.org
> Subject: Re: [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
>
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] Sleeping functions called in atomic context during bridge enable
Can you please let me know which is atomic context here?
Probe: No sleeping context
[ 19.611090] rzg3l_lvds_atomic_enable+0x18/0x16c [rzg3l_lvds] (P)
[ 19.611106] drm_atomic_bridge_chain_enable+0x60/0xb0 [drm]
[ 19.611287] drm_atomic_helper_commit_encoder_bridge_enable+0xdc/0x140 [drm_kms_helper]
[ 19.611386] drm_atomic_helper_commit_tail_rpm+0x80/0xe0 [drm_kms_helper]
[ 19.611456] commit_tail+0xa4/0x1a4 [drm_kms_helper]
[ 19.611524] drm_atomic_helper_commit+0x17c/0x1b0 [drm_kms_helper]
[ 19.611593] drm_atomic_commit+0x8c/0xcc [drm]
[ 19.611766] drm_client_modeset_commit_atomic+0x224/0x294 [drm]
[ 19.611937] drm_client_modeset_commit_locked+0x5c/0x188 [drm]
[ 19.612108] drm_client_modeset_commit+0x2c/0x58 [drm]
[ 19.612280] __drm_fb_helper_restore_fbdev_mode_unlocked.part.0+0x90/0xa0 [drm_kms_helper]
[ 19.612348] drm_fb_helper_set_par+0x68/0x84 [drm_kms_helper]
[ 19.612417] fbcon_init+0x4fc/0x520
[ 19.612435] visual_init+0xb4/0x104
[ 19.612449] do_bind_con_driver.isra.0+0x1c4/0x374
[ 19.612462] do_take_over_console+0x1a0/0x200
[ 19.612474] do_fbcon_takeover+0x6c/0xe0
[ 19.612485] fbcon_fb_registered+0x1dc/0x1e0
[ 19.612497] do_register_framebuffer+0x198/0x254
[ 19.612507] register_framebuffer+0x2c/0x50
[ 19.612516] __drm_fb_helper_initial_config_and_unlock+0x330/0x5cc [drm_kms_helper]
[ 19.612583] drm_fb_helper_initial_config+0x44/0x54 [drm_kms_helper]
[ 19.612651] drm_fbdev_client_hotplug+0x7c/0xdc [drm_client_lib]
[ 19.612669] drm_client_register+0x58/0x9c [drm]
[ 19.612839] drm_fbdev_client_setup+0xa4/0xce0 [drm_client_lib]
[ 19.612853] drm_client_setup+0xb0/0xe0 [drm_client_lib]
[ 19.612863] rzg2l_du_probe+0x134/0x174 [rzg2l_du_drm]
Suspend to RAM: no sleeping context
[ 79.238581] rzg3l_lvds_atomic_enable+0x18/0x16c [rzg3l_lvds] (P)
[ 79.238595] drm_atomic_bridge_chain_enable+0x60/0xb0 [drm]
[ 79.238764] drm_atomic_helper_commit_encoder_bridge_enable+0xdc/0x140 [drm_kms_helper]
[ 79.238876] drm_atomic_helper_commit_tail_rpm+0x80/0xe0 [drm_kms_helper]
[ 79.238947] commit_tail+0xa4/0x1a4 [drm_kms_helper]
[ 79.239014] drm_atomic_helper_commit+0x17c/0x1b0 [drm_kms_helper]
[ 79.239080] drm_atomic_commit+0x8c/0xcc [drm]
[ 79.239251] drm_atomic_helper_commit_duplicated_state+0x134/0x148 [drm_kms_helper]
[ 79.239319] drm_atomic_helper_resume+0xac/0x19c [drm_kms_helper]
[ 79.239389] drm_mode_config_helper_resume+0x24/0xc0 [drm_kms_helper]
[ 79.239459] rzg2l_du_pm_resume+0x18/0x24 [rzg2l_du_drm]
[ 79.239479] device_resume+0xc8/0x1c4
[ 79.239496] dpm_resume+0x198/0x1b8
[ 79.239505] dpm_resume_end+0x18/0x34
[ 79.239514] suspend_devices_and_enter+0x224/0x590
[ 79.239530] pm_suspend+0x194/0x1c0
[ 79.239539] state_store+0x80/0xf4
[ 79.239549] kobj_attr_store+0x18/0x34
[ 79.239563] sysfs_kf_write+0x7c/0xa0
[ 79.239575] kernfs_fop_write_iter+0x130/0x200
Hotplug:
] rzg3l_lvds_atomic_enable+0x18/0x16c [rzg3l_lvds] (P)
[ 235.264272] drm_atomic_bridge_chain_enable+0x60/0xb0 [drm]
[ 235.272544] drm_atomic_helper_commit_encoder_bridge_enable+0xdc/0x140 [drm_kms_helper]
[ 235.281610] drm_atomic_helper_commit_tail_rpm+0x80/0xe0 [drm_kms_helper]
[ 235.289443] commit_tail+0xa4/0x1a4 [drm_kms_helper]
[ 235.295459] commit_work+0x14/0x20 [drm_kms_helper]
[ 235.301397] process_one_work+0x154/0x290
[ 235.305625] worker_thread+0x184/0x2f4
[ 235.309571] kthread+0x118/0x130
[ 235.312982] ret_from_fork+0x10/0x20
> - [High] Early returns in `atomic_enable` cause unbalanced PM counter and unpowered hardware access in
> `atomic_disable`
> --
So far during testing no issues found, WARN_ON is added to find
any such issues in future.
Cheers,
Biju
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 1/2] dt-bindings: display: bridge: Document Renesas RZ/G3L LVDS encoder
2026-06-19 10:10 ` [PATCH v4 1/2] dt-bindings: display: bridge: Document " Biju
@ 2026-06-24 6:54 ` Krzysztof Kozlowski
0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-24 6:54 UTC (permalink / raw)
To: Biju
Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Biju Das, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Tommaso Merciai,
dri-devel, devicetree, linux-kernel, linux-renesas-soc,
Prabhakar Mahadev Lad
On Fri, Jun 19, 2026 at 11:10:16AM +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>
> ---
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
2026-06-19 10:20 ` sashiko-bot
@ 2026-06-24 9:52 ` Tommaso Merciai
2026-06-24 15:45 ` Tommaso Merciai
2 siblings, 0 replies; 8+ messages in thread
From: Tommaso Merciai @ 2026-06-24 9:52 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
Hi Biju,
Thanks for your patch.
On Fri, Jun 19, 2026 at 11:10:17AM +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.
>
Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Reviewed-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Kind Regards,
Tommaso
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
> v3->v4:
> * Dropped the header files clk.h and syscon.h
> * Dropped next_bridge check in attach().
> * Dropped syscon for getting regmap.
> * Replaced the below macros to match with hardware manual:
> LVDS_0_CTL_FMT_SEL_MSK->LVDS_0_CTL_FMT_SEL0_MSK
> LVDS_0_PHY_CH_IO_EN_MSK->LVDS_0_PHY_CH_IO_EN0_MSK
> Replaced LVDS_0_PHY_CH_IO_EN->LVDS_0_PHY_CH_IO_EN0
> * Replaced atomic_reset()->atomic_create_state()
> * Dropped the tags as there are new changes.
> v2->v3:
> * Collected tags.
> v2->v2[1]:
> * Replace drm_atomic_state with drm_atomic_commit in
> rzg3l_lvds_atomic_{en,dis}able().
> * Drop local variable ret and dev_err() messages in
> rzg3l_lvds_atomic_enable(); use WARN_ON() instead to
> capture unexpected failures since atomic_enable should not fail.
> * Drop local variable next_bridge from rzg3l_lvds_probe().
> [1] https://lore.kernel.org/all/20260524194457.479681-3-biju.das.jz@bp.renesas.com/
> v1->v2:
> * Dropped unused function rzg3l_lvds_is_connected() and removed the
> corresponding header file rzg3l_lvds.h
> * Dropped next_bridge from struct rzg3l_lvds instead using bridge's
> next_bridge.
> * Replaced pm_runtime_resume_and_get()->pm_runtime_get_sync() as
> atomic_enable doesn't fail and for each enable there always will be an
> atomic_disable() call.
> * Started using DEFINE_RUNTIME_DEV_PM_OPS for PM callback.
> * Replaced rzg3l_lvds_parse_dt() with devm_drm_of_get_bridge() in probe()
> * Started using reset_control_bulk_*() in rzg3l_lvds_pm_runtime_{suspend,
> resume}()
> ---
> 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 | 285 ++++++++++++++++++
> .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h | 26 ++
> 4 files changed, 325 insertions(+)
> create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> 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..1e5b3dd1c0de 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 encoder.
> +
> +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..6100888ea728
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> @@ -0,0 +1,285 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3L LVDS Encoder Driver
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.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_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;
> +};
> +
> +#define bridge_to_rzg3l_lvds(b) \
> + container_of(b, struct rzg3l_lvds, bridge)
> +
> +static const struct regmap_config rzg3l_lvds_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = LVDS_0_CTL_OFFSET,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Bridge
> + */
> +
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *state)
> +{
> + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> + const struct drm_bridge_state *bridge_state;
> + u32 fmt;
> +
> + /* Get the LVDS format from the bridge state. */
> + bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> + if (WARN_ON(!bridge_state))
> + 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;
> + }
> +
> + if (WARN_ON(pm_runtime_get_sync(lvds->dev) < 0))
> + return;
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> + fsleep(20);
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> + fsleep(10);
> +
> + regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> + regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> + LVDS_0_CTL_FMT_SEL0_MSK,
> + FIELD_PREP(LVDS_0_CTL_FMT_SEL0_MSK, fmt));
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_IO_EN0_MSK, LVDS_0_PHY_CH_IO_EN0);
> + regmap_write(lvds->regmap, LVDS_CMN,
> + LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> + fsleep(100);
> +}
> +
> +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *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_EN0_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);
> +
> + return drm_bridge_attach(encoder, lvds->bridge.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;
> +}
> +
> +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_create_state = drm_atomic_helper_bridge_create_state,
> + .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);
> + struct reset_control_bulk_data resets[] = {
> + { .rstc = lvds->lvd_rstc },
> + { .rstc = lvds->prstc },
> + };
> +
> + return reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
> +}
> +
> +static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
> +{
> + struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> + struct reset_control_bulk_data resets[] = {
> + { .rstc = lvds->lvd_rstc },
> + { .rstc = lvds->prstc },
> + };
> +
> + return reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(rzg3l_lvds_pm_ops,
> + rzg3l_lvds_pm_runtime_suspend,
> + rzg3l_lvds_pm_runtime_resume, NULL);
> +
> +/* -----------------------------------------------------------------------------
> + * Probe & Remove
> + */
> +
> +static int rzg3l_lvds_probe(struct platform_device *pdev)
> +{
> + struct reset_control *rstc, *arstc;
> + struct device *dev = &pdev->dev;
> + struct rzg3l_lvds *lvds;
> + void __iomem *base;
> + 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;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + lvds->regmap = devm_regmap_init_mmio(dev, base, &rzg3l_lvds_regmap_config);
> + if (IS_ERR(lvds->regmap))
> + return dev_err_probe(dev, PTR_ERR(lvds->regmap),
> + "failed to init regmap\n");
> +
> + rstc = devm_reset_control_get_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_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_exclusive(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_exclusive(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");
> +
> + lvds->bridge.next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> + if (IS_ERR(lvds->bridge.next_bridge))
> + return dev_err_probe(dev, PTR_ERR(lvds->bridge.next_bridge),
> + "failed to get next bridge\n");
> +
> + 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_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> new file mode 100644
> index 000000000000..3dca3b630818
> --- /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_EN0_MSK (0x1f)
> +#define LVDS_0_PHY_CH_IO_EN0 (LVDS_0_PHY_CH_IO_EN0_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_SEL0_MSK GENMASK(23, 20)
> +
> +#endif /* __RZG3L_LVDS_REGS_H__ */
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 2/2] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
2026-06-19 10:20 ` sashiko-bot
2026-06-24 9:52 ` Tommaso Merciai
@ 2026-06-24 15:45 ` Tommaso Merciai
2 siblings, 0 replies; 8+ messages in thread
From: Tommaso Merciai @ 2026-06-24 15:45 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
On Fri, Jun 19, 2026 at 11:10:17AM +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>
> ---
> v3->v4:
> * Dropped the header files clk.h and syscon.h
> * Dropped next_bridge check in attach().
> * Dropped syscon for getting regmap.
> * Replaced the below macros to match with hardware manual:
> LVDS_0_CTL_FMT_SEL_MSK->LVDS_0_CTL_FMT_SEL0_MSK
> LVDS_0_PHY_CH_IO_EN_MSK->LVDS_0_PHY_CH_IO_EN0_MSK
> Replaced LVDS_0_PHY_CH_IO_EN->LVDS_0_PHY_CH_IO_EN0
> * Replaced atomic_reset()->atomic_create_state()
> * Dropped the tags as there are new changes.
> v2->v3:
> * Collected tags.
> v2->v2[1]:
> * Replace drm_atomic_state with drm_atomic_commit in
> rzg3l_lvds_atomic_{en,dis}able().
> * Drop local variable ret and dev_err() messages in
> rzg3l_lvds_atomic_enable(); use WARN_ON() instead to
> capture unexpected failures since atomic_enable should not fail.
> * Drop local variable next_bridge from rzg3l_lvds_probe().
> [1] https://lore.kernel.org/all/20260524194457.479681-3-biju.das.jz@bp.renesas.com/
> v1->v2:
> * Dropped unused function rzg3l_lvds_is_connected() and removed the
> corresponding header file rzg3l_lvds.h
> * Dropped next_bridge from struct rzg3l_lvds instead using bridge's
> next_bridge.
> * Replaced pm_runtime_resume_and_get()->pm_runtime_get_sync() as
> atomic_enable doesn't fail and for each enable there always will be an
> atomic_disable() call.
> * Started using DEFINE_RUNTIME_DEV_PM_OPS for PM callback.
> * Replaced rzg3l_lvds_parse_dt() with devm_drm_of_get_bridge() in probe()
> * Started using reset_control_bulk_*() in rzg3l_lvds_pm_runtime_{suspend,
> resume}()
> ---
> 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 | 285 ++++++++++++++++++
> .../gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h | 26 ++
> 4 files changed, 325 insertions(+)
> create mode 100644 drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> 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..1e5b3dd1c0de 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 encoder.
> +
> +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..6100888ea728
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c
> @@ -0,0 +1,285 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3L LVDS Encoder Driver
> + *
> + * Copyright (C) 2026 Renesas Electronics Corporation
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/media-bus-format.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_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;
> +};
> +
> +#define bridge_to_rzg3l_lvds(b) \
> + container_of(b, struct rzg3l_lvds, bridge)
> +
> +static const struct regmap_config rzg3l_lvds_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = LVDS_0_CTL_OFFSET,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Bridge
> + */
> +
> +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *state)
> +{
> + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge);
> + const struct drm_bridge_state *bridge_state;
> + u32 fmt;
> +
> + /* Get the LVDS format from the bridge state. */
> + bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> + if (WARN_ON(!bridge_state))
> + 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;
> + }
> +
> + if (WARN_ON(pm_runtime_get_sync(lvds->dev) < 0))
> + return;
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR);
> + fsleep(20);
> +
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO);
> + fsleep(10);
> +
> + regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL);
> + regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET,
> + LVDS_0_CTL_FMT_SEL0_MSK,
> + FIELD_PREP(LVDS_0_CTL_FMT_SEL0_MSK, fmt));
> + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET,
> + LVDS_0_PHY_CH_IO_EN0_MSK, LVDS_0_PHY_CH_IO_EN0);
> + regmap_write(lvds->regmap, LVDS_CMN,
> + LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET);
> + fsleep(100);
> +}
> +
> +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *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_EN0_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);
> +
> + return drm_bridge_attach(encoder, lvds->bridge.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;
> +}
> +
> +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_create_state = drm_atomic_helper_bridge_create_state,
> + .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);
> + struct reset_control_bulk_data resets[] = {
> + { .rstc = lvds->lvd_rstc },
> + { .rstc = lvds->prstc },
> + };
> +
> + return reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
> +}
> +
> +static int rzg3l_lvds_pm_runtime_resume(struct device *dev)
> +{
> + struct rzg3l_lvds *lvds = dev_get_drvdata(dev);
> + struct reset_control_bulk_data resets[] = {
> + { .rstc = lvds->lvd_rstc },
> + { .rstc = lvds->prstc },
> + };
> +
> + return reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(rzg3l_lvds_pm_ops,
> + rzg3l_lvds_pm_runtime_suspend,
> + rzg3l_lvds_pm_runtime_resume, NULL);
> +
> +/* -----------------------------------------------------------------------------
> + * Probe & Remove
> + */
> +
> +static int rzg3l_lvds_probe(struct platform_device *pdev)
> +{
> + struct reset_control *rstc, *arstc;
> + struct device *dev = &pdev->dev;
> + struct rzg3l_lvds *lvds;
> + void __iomem *base;
> + 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;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + lvds->regmap = devm_regmap_init_mmio(dev, base, &rzg3l_lvds_regmap_config);
> + if (IS_ERR(lvds->regmap))
> + return dev_err_probe(dev, PTR_ERR(lvds->regmap),
> + "failed to init regmap\n");
> +
> + rstc = devm_reset_control_get_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_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_exclusive(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_exclusive(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");
> +
> + lvds->bridge.next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
> + if (IS_ERR(lvds->bridge.next_bridge))
> + return dev_err_probe(dev, PTR_ERR(lvds->bridge.next_bridge),
> + "failed to get next bridge\n");
> +
> + 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_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h
> new file mode 100644
> index 000000000000..3dca3b630818
> --- /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_EN0_MSK (0x1f)
One second toughts I think here we can go with plain numbers.
Apart from that the rest LGTM. You can keep my tags.
Thank you.
Kind Regards,
Tommaso
> +#define LVDS_0_PHY_CH_IO_EN0 (LVDS_0_PHY_CH_IO_EN0_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_SEL0_MSK GENMASK(23, 20)
> +
> +#endif /* __RZG3L_LVDS_REGS_H__ */
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-24 15:46 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-19 10:10 [PATCH v4 0/2] Add support for Renesas RZ/G3L LVDS encoder Biju
2026-06-19 10:10 ` [PATCH v4 1/2] dt-bindings: display: bridge: Document " Biju
2026-06-24 6:54 ` Krzysztof Kozlowski
2026-06-19 10:10 ` [PATCH v4 2/2] drm: renesas: rz-du: Add support for " Biju
2026-06-19 10:20 ` sashiko-bot
2026-06-19 11:19 ` Biju Das
2026-06-24 9:52 ` Tommaso Merciai
2026-06-24 15:45 ` Tommaso Merciai
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.