[PATCH v2 08/15] soc: starfive: Add jh7110-hdmi-subsystem driver

From: Michal Wilczynski

Date: Fri Aug 28 2026 - 09:53:30 EST


Add the parent driver for the monolithic JH7110 HDMI IP block.

This driver binds to the starfive,jh7110-hdmi-subsystem node. It maps the
shared register block, creates a regmap, and calls
devm_of_platform_populate() to create its hdmi_phy and hdmi_controller
child devices, which retrieve the shared regmap from this parent.

The NoC display-bus clock and reset gate access to the whole vout register
region, and this subsystem's PHY child is the first device there to touch
registers. Enable the bus before populating the children; PD_VOUT is
handled by genpd through the power-domains property.

Co-developed-by: Dominique Belhachemi <db@xxxxxxxxxx>
Signed-off-by: Dominique Belhachemi <db@xxxxxxxxxx>
Signed-off-by: Michal Wilczynski <m.wilczynski@xxxxxxxxxxx>
---
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/starfive/Kconfig | 27 +++++++
drivers/soc/starfive/Makefile | 2 +
drivers/soc/starfive/jh7110-hdmi-subsystem.c | 114 +++++++++++++++++++++++++++
5 files changed, 145 insertions(+)

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index a2d65adffb8052c0ac5a6b60bf33fa9c644701bb..b3b01fc38139d98076c14f626a42ae3b7ef7c5d6 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -24,6 +24,7 @@ source "drivers/soc/renesas/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/samsung/Kconfig"
source "drivers/soc/sophgo/Kconfig"
+source "drivers/soc/starfive/Kconfig"
source "drivers/soc/sunxi/Kconfig"
source "drivers/soc/tegra/Kconfig"
source "drivers/soc/ti/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index c9e689080ceb759384f690c2b65a82b3cb451c74..009f85ff891a15e0455f92c5d5a4059d8b1fcd3f 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -30,6 +30,7 @@ obj-y += renesas/
obj-y += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-y += sophgo/
+obj-y += starfive/
obj-y += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += ti/
diff --git a/drivers/soc/starfive/Kconfig b/drivers/soc/starfive/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..7592d87dea37a7c14560b3eb9b23e49e503c2d71
--- /dev/null
+++ b/drivers/soc/starfive/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Starfive SoC drivers
+#
+
+if ARCH_STARFIVE || COMPILE_TEST
+menu "Starfive SoC drivers"
+
+config SOC_STARFIVE_JH7110_HDMI_SUBSYSTEM
+ tristate "StarFive JH7110 HDMI subsystem driver"
+ depends on OF
+ help
+ This option enables the parent driver
+ for the monolithic StarFive JH7110 HDMI peripheral.
+
+ The JH7110 HDMI IP block contains both the digital controller
+ (DRM bridge) and the analog PHY (clock/phy provider) logic within
+ a single shared register space.
+
+ This driver acts as a wrapper. Its only job is to map the
+ shared registers and create separate logical child devices
+ for the "PHY" and the "controller". This is required to
+ correctly manage resources and break a circular clock dependency
+ between the PHY and the VOUT clock generator at probe time.
+
+endmenu
+endif
diff --git a/drivers/soc/starfive/Makefile b/drivers/soc/starfive/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..be89d8119212b7a7038817c2f0e8eac1984ada88
--- /dev/null
+++ b/drivers/soc/starfive/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SOC_STARFIVE_JH7110_HDMI_SUBSYSTEM) += jh7110-hdmi-subsystem.o
diff --git a/drivers/soc/starfive/jh7110-hdmi-subsystem.c b/drivers/soc/starfive/jh7110-hdmi-subsystem.c
new file mode 100644
index 0000000000000000000000000000000000000000..d893c1c29f98b072b09d261343d3317dbf4c75fe
--- /dev/null
+++ b/drivers/soc/starfive/jh7110-hdmi-subsystem.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the StarFive JH7110 HDMI subsystem
+ *
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ * Author: Michal Wilczynski <m.wilczynski@xxxxxxxxxxx>
+ *
+ * This driver binds to the monolithic HDMI block and creates separate
+ * logical platform devices for the HDMI Controller (bridge) and the
+ * HDMI PHY (clock/phy provider), allowing them to share a single regmap
+ * and breaking the probing circular dependency.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+static const struct regmap_config starfive_hdmi_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = 0x3fff,
+};
+
+static void starfive_hdmi_subsys_clk_disable(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void starfive_hdmi_subsys_rst_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int starfive_hdmi_subsys_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct reset_control *bus_rst;
+ void __iomem *regs;
+ struct regmap *regmap;
+ struct clk *bus_clk;
+ int ret;
+
+ /*
+ * The NoC display-bus clock and reset gate access to the whole vout
+ * register region, and this subsystem's PHY child is the first device in
+ * that region to touch registers. Bring the bus up here before
+ * populating the children; PD_VOUT is powered on by genpd through the
+ * power-domains property.
+ */
+ bus_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(bus_clk))
+ return dev_err_probe(dev, PTR_ERR(bus_clk),
+ "Failed to get NoC bus clock\n");
+
+ ret = clk_prepare_enable(bus_clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable NoC bus clock\n");
+
+ ret = devm_add_action_or_reset(dev, starfive_hdmi_subsys_clk_disable, bus_clk);
+ if (ret)
+ return ret;
+
+ bus_rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(bus_rst))
+ return dev_err_probe(dev, PTR_ERR(bus_rst),
+ "Failed to get NoC bus reset\n");
+
+ ret = reset_control_deassert(bus_rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to deassert NoC bus reset\n");
+
+ ret = devm_add_action_or_reset(dev, starfive_hdmi_subsys_rst_assert, bus_rst);
+ if (ret)
+ return ret;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ regmap = devm_regmap_init_mmio(dev, regs,
+ &starfive_hdmi_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to init shared regmap\n");
+
+ ret = devm_of_platform_populate(dev);
+ if (ret)
+ dev_err(dev, "Failed to populate child devices: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id starfive_hdmi_subsys_of_match[] = {
+ { .compatible = "starfive,jh7110-hdmi-subsystem", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_subsys_of_match);
+
+static struct platform_driver starfive_hdmi_subsys_driver = {
+ .probe = starfive_hdmi_subsys_probe,
+ .driver = {
+ .name = "starfive-hdmi-subsystem",
+ .of_match_table = starfive_hdmi_subsys_of_match,
+ },
+};
+module_platform_driver(starfive_hdmi_subsys_driver);
+
+MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("StarFive JH7110 HDMI subsystem Driver");
+MODULE_LICENSE("GPL");

--
2.34.1