[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20251108-jh7110-clean-send-v1-11-06bf43bb76b1@samsung.com>
Date: Sat, 08 Nov 2025 02:04:45 +0100
From: Michal Wilczynski <m.wilczynski@...sung.com>
To: Michal Wilczynski <m.wilczynski@...sung.com>, Conor Dooley
<conor@...nel.org>, Rob Herring <robh@...nel.org>, Krzysztof Kozlowski
<krzk+dt@...nel.org>, Emil Renner Berthing <kernel@...il.dk>, Hal Feng
<hal.feng@...rfivetech.com>, Michael Turquette <mturquette@...libre.com>,
Stephen Boyd <sboyd@...nel.org>, Conor Dooley <conor+dt@...nel.org>, Xingyu
Wu <xingyu.wu@...rfivetech.com>, Vinod Koul <vkoul@...nel.org>, Kishon
Vijay Abraham I <kishon@...nel.org>, Andrzej Hajda
<andrzej.hajda@...el.com>, Neil Armstrong <neil.armstrong@...aro.org>,
Robert Foss <rfoss@...nel.org>, Laurent Pinchart
<Laurent.pinchart@...asonboard.com>, Jonas Karlman <jonas@...boo.se>,
Jernej Skrabec <jernej.skrabec@...il.com>, David Airlie
<airlied@...il.com>, Simona Vetter <simona@...ll.ch>, Maarten Lankhorst
<maarten.lankhorst@...ux.intel.com>, Maxime Ripard <mripard@...nel.org>,
Thomas Zimmermann <tzimmermann@...e.de>, Lee Jones <lee@...nel.org>,
Philipp Zabel <p.zabel@...gutronix.de>, Paul Walmsley
<paul.walmsley@...ive.com>, Palmer Dabbelt <palmer@...belt.com>, Albert Ou
<aou@...s.berkeley.edu>, Alexandre Ghiti <alex@...ti.fr>, Marek Szyprowski
<m.szyprowski@...sung.com>, Icenowy Zheng <uwu@...nowy.me>, Maud Spierings
<maudspierings@...ontroll.com>, Andy Yan <andyshrk@....com>, Heiko Stuebner
<heiko@...ech.de>
Cc: devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-clk@...r.kernel.org, linux-phy@...ts.infradead.org,
dri-devel@...ts.freedesktop.org, linux-riscv@...ts.infradead.org
Subject: [PATCH RFC 11/13] drm: bridge: starfive: Add hdmi-controller driver
Add the HDMI controller (bridge) driver for the StarFive JH7110.
This driver binds to the starfive,jh7110-inno-hdmi-controller MFD child.
It gets its shared regmap from the MFD parent and its clocks (sys, mclk,
bclk) from voutcrg. It consumes the pclk (pixel clock) and PHY from its
hdmi_phy sibling.
The driver calls the generic inno_hdmi_probe function and passes the
shared regmap to it, registering as a DRM bridge. The .enable hook is
responsible for setting the PHY's pixel clock rate via clk_set_rate()
and powering on the PHY via phy_power_on().
Signed-off-by: Michal Wilczynski <m.wilczynski@...sung.com>
---
MAINTAINERS | 1 +
drivers/gpu/drm/bridge/Kconfig | 11 ++
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/jh7110-inno-hdmi.c | 190 ++++++++++++++++++++++++++++++
4 files changed, 203 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f1867018ee92fb754689934f6d238f9c9f185161..5984b83e55aeadb59c25a6e8f01057fb9d982d81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24053,6 +24053,7 @@ F: Documentation/devicetree/bindings/phy/starfive,jh7110-inno-hdmi-phy.yaml
F: Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-vout-subsystem.yaml
F: drivers/soc/starfive/jh7110-hdmi-mfd.c
F: drivers/soc/starfive/jh7110-vout-subsystem.c
+F: drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
STARFIVE JH7110 DPHY RX DRIVER
M: Jack Zhu <jack.zhu@...rfivetech.com>
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index a5b8df9655ba70c6d653183780089258946b0e5a..2bf97ec0096ed093ed078b48300d9aa12088c486 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -323,6 +323,17 @@ config DRM_SIMPLE_BRIDGE
Support for non-programmable DRM bridges, such as ADI ADV7123, TI
THS8134 and THS8135 or passive resistor ladder DACs.
+config DRM_STARFIVE_JH7110_INNO_HDMI
+ tristate "Starfive JH7110 Innosilicon HDMI bridge"
+ depends on OF
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ select DRM_INNO_HDMI
+ help
+ Enable support for the StarFive JH7110 specific implementation
+ of the Innosilicon HDMI controller.
+ This driver acts as a glue layer between the JH7110 HDMI MFD
+ parent driver and the generic Innosilicon HDMI bridge driver.
+
config DRM_THINE_THC63LVD1024
tristate "Thine THC63LVD1024 LVDS decoder bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4bc2236c8ae9169ac998e9d0448badff457cabaa..b2f5835ec05bcbb4367499d1cebb5403d7b9f247 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
obj-$(CONFIG_DRM_SII902X) += sii902x.o
obj-$(CONFIG_DRM_SII9234) += sii9234.o
obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
+obj-$(CONFIG_DRM_STARFIVE_JH7110_INNO_HDMI) += jh7110-inno-hdmi.o
obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o
diff --git a/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c b/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d3e1c3e736b801882b1b057199fc341142bba52
--- /dev/null
+++ b/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) StarFive Technology Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ * Author: Michal Wilczynski <m.wilczynski@...sung.com>
+ *
+ * HDMI Controller (bridge) driver for StarFive JH7110 MFD.
+ */
+
+#include <linux/clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/bridge/inno_hdmi.h>
+
+enum stf_hdmi_ctrl_clocks { CLK_SYS = 0, CLK_M, CLK_B, CLK_PCLK, CLK_CTRL_NUM };
+
+struct stf_inno_hdmi_controller {
+ struct inno_hdmi *inno;
+ struct device *dev;
+ struct clk_bulk_data clks[CLK_CTRL_NUM];
+ struct reset_control *tx_rst;
+ struct phy *phy;
+};
+
+static void inno_hdmi_starfive_enable(struct device *dev,
+ struct drm_display_mode *mode)
+{
+ struct stf_inno_hdmi_controller *ctrl = dev_get_drvdata(dev);
+ int ret;
+
+ /*
+ * 1. Set the pixel clock rate. This calls the PHY driver's .set_rate op.
+ */
+ ret = clk_set_rate(ctrl->clks[CLK_PCLK].clk, mode->clock * 1000);
+ if (ret) {
+ dev_err(dev, "Failed to set pclk rate %d: %d\n",
+ mode->clock * 1000, ret);
+ return;
+ }
+
+ /*
+ * 2. Enable the pixel clock. This calls the PHY driver's .prepare op.
+ */
+ ret = clk_prepare_enable(ctrl->clks[CLK_PCLK].clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable pclk: %d\n", ret);
+ return;
+ }
+
+ /*
+ * 3. Power on the PHY. This calls the PHY driver's .power_on op,
+ * which configures the Post-PLL and analog blocks.
+ */
+ ret = phy_power_on(ctrl->phy);
+ if (ret) {
+ dev_err(dev, "Failed to power on PHY: %d\n", ret);
+ clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
+ return;
+ }
+}
+
+static void inno_hdmi_starfive_disable(struct device *dev)
+{
+ struct stf_inno_hdmi_controller *ctrl = dev_get_drvdata(dev);
+
+ phy_power_off(ctrl->phy);
+ clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
+}
+
+static int starfive_inno_hdmi_controller_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+ struct stf_inno_hdmi_controller *ctrl;
+ const struct inno_hdmi_plat_data *plat_data;
+ struct regmap *mfd_regmap;
+ int ret;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->dev = dev;
+ platform_set_drvdata(pdev, ctrl);
+
+ /* Get the shared regmap from the MFD parent */
+ mfd_regmap = dev_get_regmap(parent, NULL);
+ if (!mfd_regmap) {
+ dev_err(dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ ctrl->phy = devm_phy_get(dev, "hdmi-phy");
+ if (IS_ERR(ctrl->phy))
+ return dev_err_probe(dev, PTR_ERR(ctrl->phy), "Failed to get PHY\n");
+
+ ctrl->tx_rst = devm_reset_control_get_exclusive(dev, "hdmi_tx");
+ if (IS_ERR(ctrl->tx_rst))
+ return dev_err_probe(dev, PTR_ERR(ctrl->tx_rst), "failed to get tx reset\n");
+
+ /* Populate the clock names this controller *consumes* */
+ ctrl->clks[CLK_SYS].id = "sys";
+ ctrl->clks[CLK_M].id = "mclk";
+ ctrl->clks[CLK_B].id = "bclk";
+ ctrl->clks[CLK_PCLK].id = "pclk"; /* Pixel clock *from* PHY */
+
+ ret = devm_clk_bulk_get(dev, CLK_CTRL_NUM, ctrl->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to get controller clocks\n");
+
+ /* pclk is enabled on demand during modeset */
+ ret = clk_bulk_prepare_enable(CLK_CTRL_NUM - 1, ctrl->clks);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(ctrl->tx_rst);
+ if (ret) {
+ clk_bulk_disable_unprepare(CLK_CTRL_NUM - 1, ctrl->clks);
+ return ret;
+ }
+
+ plat_data = of_device_get_match_data(dev);
+
+ /* Hand off to the generic library to create the bridge. */
+ ctrl->inno = inno_hdmi_probe(pdev, plat_data);
+ if (IS_ERR(ctrl->inno)) {
+ reset_control_assert(ctrl->tx_rst);
+ clk_bulk_disable_unprepare(CLK_CTRL_NUM - 1, ctrl->clks);
+ return PTR_ERR(ctrl->inno);
+ }
+
+ return 0;
+}
+
+static void starfive_inno_hdmi_controller_remove(struct platform_device *pdev)
+{
+ struct stf_inno_hdmi_controller *ctrl = platform_get_drvdata(pdev);
+
+ inno_hdmi_remove(ctrl->inno);
+
+ reset_control_assert(ctrl->tx_rst);
+ clk_bulk_disable_unprepare(CLK_CTRL_NUM - 1, ctrl->clks);
+}
+
+/*
+ * This table is now only used for the generic .mode_valid check.
+ * The real validation happens in the PHY driver's .round_rate.
+ */
+static struct inno_hdmi_phy_config stf_hdmi_phy_configs[] = {
+ { 297000000, 0x00, 0x00 },
+ { ~0UL, 0x00, 0x00 }, /* Sentinel */
+};
+
+static const struct inno_hdmi_plat_ops stf_inno_hdmi_plat_ops = {
+ .enable = inno_hdmi_starfive_enable,
+ .disable = inno_hdmi_starfive_disable,
+};
+
+static const struct inno_hdmi_plat_data stf_inno_hdmi_plat_data = {
+ .ops = &stf_inno_hdmi_plat_ops,
+ .phy_configs = stf_hdmi_phy_configs,
+ .default_phy_config = &stf_hdmi_phy_configs[0],
+};
+
+static const struct of_device_id starfive_hdmi_controller_dt_ids[] = {
+ { .compatible = "starfive,jh7110-inno-hdmi-controller",
+ .data = &stf_inno_hdmi_plat_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_controller_dt_ids);
+
+struct platform_driver starfive_inno_hdmi_controller_driver = {
+ .probe = starfive_inno_hdmi_controller_probe,
+ .remove = starfive_inno_hdmi_controller_remove,
+ .driver = {
+ .name = "starfive-inno-hdmi-controller",
+ .of_match_table = starfive_hdmi_controller_dt_ids,
+ },
+};
+module_platform_driver(starfive_inno_hdmi_controller_driver);
+
+MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@...sung.com>");
+MODULE_DESCRIPTION("StarFive INNO HDMI Controller Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
Powered by blists - more mailing lists