From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6647FF8FA9E for ; Tue, 21 Apr 2026 17:29:25 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CE08F10E56A; Tue, 21 Apr 2026 17:29:24 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Kl1uu5ij"; dkim-atps=neutral Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8F9B210E905 for ; Tue, 21 Apr 2026 17:29:19 +0000 (UTC) Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-48a563e4ef7so9485965e9.0 for ; Tue, 21 Apr 2026 10:29:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776792558; x=1777397358; darn=lists.freedesktop.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4Eo4cos6iOs2QlAA15r5L7hzrPOpyuNUq7YQebe52Hk=; b=Kl1uu5ijFFvwQj0dbhixfZo+dA2034f0vLTXGJ0vwDtdX5aQ7t8HQDRUKcNkVHFCP+ KltQDml7woPXEMvDe+KTOH43dlrjzeaC7yhZ5zBuINxM6UiO6mLLu5Nb41iBLlCIP/wo Ds6wo8BvP5oFfsdeYN3HpCM0SJtcMNOBzeuvtY/67vhXO+FdvcG5Xm9wgo6zDvtoFvjb 8Bg+OAmC1zUZgctJ8EGmFBYBUgJ37DqmAjn6XV3HOTA3d0lTZvseiL9i0KpxXmI5qEOy AGBJA6X9iCmd4jz7XSvNbVOVjR9+YvUcRgjUNGs4A4bbmb3HcGK1X5kfVrpNSt+q1Sj2 d+PA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776792558; x=1777397358; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=4Eo4cos6iOs2QlAA15r5L7hzrPOpyuNUq7YQebe52Hk=; b=rfUTcRaDZ3tpMQXoesjO/OODOvBz5+gai/gcN/iqtXobo1K2zD3gO9EB3jaRNGSPx6 3ux+SXpJfi65vqn9tufqA2oHAoGlSXO2UQf7uFcFMvpJMk2g73Emt0D93CuprWS+hxlh 1YmS+d1UNRGH/64wS50kuIQvQ3osDQi+o0bsxiPZACh6T+y/vClH51feVvkN+u0CGyjx oCIocd8LyxxC0s22bIgPfsi5qkDjwyfmBw1iaQgW+t3reM+8g1FTQNLGrDZGghd+cUaL Q+c2hPkLQ7v4nR6wXpt3OWKORTKT2+dv/BRB+XhDYAencKV+xgVO2OJ5+3Qh8YDwV/Yn h2Tw== X-Forwarded-Encrypted: i=1; AFNElJ//VtMpI6APzj7gl/d9V3r66qfYpObeneeyurQYk6myay539SSWyrV7fAP1mWu8f6oKWoHHTJnMRnI=@lists.freedesktop.org X-Gm-Message-State: AOJu0YznUxz90kMFYs6+OUiEKWKyHUf4U1tALPB02JCdP99kHLvSF+8j N0ZVv64V3sIDwumUG3XO5Cp8HaR7lGUmvpvae4cjsMpKAC0XjCco9BS7 X-Gm-Gg: AeBDiesNqWHLhxQ1zoOfMroESkfxYd+FgNutdgnR9IU1h3p/0geWWIm0LHFpCVSjAxj nOClq/Pbk69DUSvUsSmkoDsg+85rzh1PyDUAyt0X/ambJjCsGMN73kdqkDPZPZFSp3Da+frvQsJ FkBi9zhVCpCXc3tPvn3xIUZHozEnzPRiiQcLzLP5U4E9xOHeigphgqpJmcK2BZyoFPQxM/334fE aqLB4GsyeZo6QlJ0ZJnTb7hWQ/fwuMglrxEiJ+rD9JiyGDHfKjJi9jzTNRTWctG6JOLXTxcftfV xTBRvWZ32SiRnEPpYmIwU6IUrI1vSceEs4lnc1sYD4fVlsO+490XTbgwHzHRq7WePpY0m/A8YHa B21h1kV+SJgq46llEgJSh86vdjx8hzm8pq2KftS59WDtH12A/ybXzzdnOtNpdPLHBO0aVen7/VQ 66L7rGHcqa/uZ1mKES4tnnvI8NkCtstY4tWKq8ixKIb+bl2zIW/ji1dwRV8Qs= X-Received: by 2002:a05:600c:8587:b0:488:a797:f0ac with SMTP id 5b1f17b1804b1-488fb7880bdmr189424205e9.28.1776792557801; Tue, 21 Apr 2026 10:29:17 -0700 (PDT) Received: from localhost.localdomain ([2a00:23c4:a758:8a01:de57:c37d:677b:c772]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48a525a0b1asm57445855e9.2.2026.04.21.10.29.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 10:29:17 -0700 (PDT) From: Biju X-Google-Original-From: Biju To: Biju Das , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Philipp Zabel , Geert Uytterhoeven , Magnus Damm Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-renesas-soc@vger.kernel.org, Prabhakar Mahadev Lad , Biju Das , Tommaso Merciai Subject: [PATCH v2 3/3] drm: renesas: rz-du: Add support for RZ/G3L LVDS encoder Date: Tue, 21 Apr 2026 18:29:05 +0100 Message-ID: <20260421172910.218497-4-biju.das.jz@bp.renesas.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260421172910.218497-1-biju.das.jz@bp.renesas.com> References: <20260421172910.218497-1-biju.das.jz@bp.renesas.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Biju Das 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 Signed-off-by: Biju Das --- 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..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..60fd63b7be93 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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) + +/* ----------------------------------------------------------------------------- + * 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_get_sync(lvds->dev); + if (ret < 0) { + dev_err(lvds->dev, "pm_runtime_get_sync error\n"); + 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_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); + fsleep(100); +} + +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->bridge.next_bridge) + return 0; + + 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_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); + 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->prstc }, + { .rstc = lvds->lvd_rstc }, + }; + + 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 drm_bridge *next_bridge; + 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"); + + next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(next_bridge)) + return dev_err_probe(dev, PTR_ERR(next_bridge), + "failed to get next bridge\n"); + + lvds->bridge.next_bridge = next_bridge; + 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 "); +MODULE_AUTHOR("Tommaso Merciai "); +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..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