linux-clk.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem
       [not found] <CGME20260904132708eucas1p1adfa26ef0fe5109eca63a3aeadf74915@eucas1p1.samsung.com>
@ 2026-09-04 13:27 ` Michal Wilczynski
  2026-09-04 13:27   ` [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy Michal Wilczynski
                     ` (20 more replies)
  0 siblings, 21 replies; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

This series enables the display subsystem on the StarFive JH7110.

The dom_vout block holds the display controller (dc8200), the clock
generator (voutcrg) and the HDMI IP, all inside PD_VOUT. The HDMI IP is
a single register block containing both the controller and the PHY, and
it has a circular clock dependency with voutcrg:

  - the HDMI controller needs pclk/mclk/bclk from voutcrg
  - voutcrg needs the pixel clock for its dc8200 pixel MUXes, and that
    clock is generated by the HDMI PHY

The loop only exists if the HDMI block is treated as one device. The
PHY's reference clock is xin24m, not a voutcrg output, so splitting the
node into a parent plus phy and controller children gives deferred probe
a linear order: hdmi-phy, then voutcrg, then hdmi-controller.

The parent maps the register block and owns the regmap its two children
share. Everything in the region sits behind one NoC port whose clock and
reset gate access to it, inside PD_VOUT, so the vout subsystem node from
the RFC is back and owns those for as long as any child exists.

Patch 10 adds a .mode_valid platform op to inno-hdmi.
inno_hdmi_bridge_mode_valid() checks the pixel clock against
hdmi->refclk, but that clock only exists where a "ref" clock is
described. The JH7110 gets its pixel clock from the PHY, so refclk is
NULL and the check was skipped: unsupported modes were advertised, the
modeset then "succeeded" because the atomic enable path cannot fail, and
the display stayed blank.

Patches 15-17 drop the PHY duplication from the RFC. The JH7110 has the
same Innosilicon PHY as the RK3328, offset by 0x100 because it sits
behind the controller in the shared register block. Patch 15 factors out
the pre-PLL config format, table lookup, determine_rate, recalc_rate and
the pre-PLL programming; patch 16 moves Rockchip onto it; patch 17 adds
the JH7110 driver. Pixel clock tables, post-PLL and analog config stay
SoC specific.

Patch 16 should be a no-op for Rockchip - same writes, same order, same
values - and RK3228, whose pre-PLL is at different addresses, keeps its
own register code and shares only the lookup. I have no Rockchip
hardware, so it is build tested only (arm and riscv). A Tested-by would
help.

The dc8200 driver, th1520 reset controller and inno-hdmi bridge that the
RFC listed as prerequisites are all upstream now, so there are no
out-of-tree dependencies.

Testing
=======

Tested on a VisionFive 2 v1.3B using modetest.

All 42 modes the sink advertises work, with nothing in dmesg. Pixel
clocks run from 25.175 MHz (640x480@59.94) up to 297 MHz
(4096x2160@30), including 3840x2160 and the full 1920x1080 and 1280x720
rate families.

The four modes the RFC reported as broken work now too: 2560x1440@59.95,
2048x1080@60.00, 2048x1080@24.00 and 720x400@70.08.

Before patch 10, four of the advertised modes failed: 1680x1050@59.95
(146.250 MHz), 1400x1050@59.98 (121.750), 1152x864@59.97 (81.768) and
1280x768@60.35 (80.140). Those pixel clocks are not in the PHY pre-PLL
table, so clk_set_rate() returned -EINVAL and the screen stayed black
while userspace saw a successful modeset. They are rejected in
.mode_valid now; the other refresh rates of those resolutions still work.

Every commit builds for riscv, and the Rockchip PHY also for arm.

Notes
=====

The JH7110 has no central MAINTAINERS entry and maintainership is
fragmented, so patch 19 adds one for the display subsystem and I am
happy to help maintain it. The new PHY library lives under drivers/phy/,
already covered by the generic PHY framework entry.

checkpatch warns "does MAINTAINERS need updating?" on the patches adding
files, because that entry comes in patch 19.

Thanks to Icenowy Zheng for the dc8200 driver and for explaining how the
SoC and the display pipeline fit together.

Thanks also to Dominique Belhachemi, who got rid of the vout-subsystem
wrapper and helped with the testing, to Maud Spierings for testing on a
Framework 13 panel, and to Graham Markall for testing
the JH7110 display patches independently and writing up the results:
https://big-grey.co.uk/2026/01/26/testing-starfive-jh7110-display-controller-patches/

Link to v1: https://lore.kernel.org/all/20251108-jh7110-clean-send-v1-0-06bf43bb76b1@samsung.com/

---
Changes in v3:
- Brought back the vout subsystem node and driver, now owning the NoC
  bus clock, its reset and PD_VOUT for the whole region, with dc8200,
  the HDMI block, the syscon and voutcrg as its children (Icenowy Zheng).
- Fixed a hard hang when the bridge is built as a module: the PHY's
  .is_prepared read a register in the window gated by the controller's
  system clock, so clk_disable_unused() wedged the CPU before the
  controller had bound. The op is gone; the framework uses the software
  prepare count instead. (Marek Szyprowski)
- The HDMI controller now programs the display mux in dom_vout_syscon
  from the port graph rather than inheriting whatever the bootloader
  left, with a phandle to the syscon (Icenowy Zheng).
- The register access clock is named "pclk" to match the existing
  inno-hdmi binding, so the generic driver no longer picks up the pixel
  clock. Previously it held the pre-PLL powered from probe and sized the
  DDC divider from the wrong rate.
- Dropped the clk suffixes and the single-entry -names properties from
  the bindings (Conor Dooley). mclk and bclk keep their names: per TRM
  5.3 they are the HDMI audio clocks, not module and bus clocks, so the
  descriptions say that instead.
- Replaced patternProperties with plain properties in the hdmi-subsystem
  binding (Conor Dooley).
- dc8200 gets an SoC specific compatible, and inherits dma-noncoherent
  from the subsystem bus node, so it validates against verisilicon,dc.
- Added the pre-PLL entry for the Framework 13 panel and fixed two
  devicetree whitespace nits (Maud Spierings).
- select REGMAP_MMIO, CLK_SET_RATE_NO_REPARENT on the dc8200 pixel MUXes
  so clk_set_rate() cannot reroute them, and inno-hdmi register reads
  return 0 instead of stack garbage when regmap_read() fails.
- phy: rockchip: dropped the local pre-PLL lookup wrapper and the 28 now
  unused RK3328 pre-PLL macros, and restored the VCO debug output, this
  time in the shared helper so both drivers get it (Jonas Karlman).
- Rebased onto v7.3-rc1.
- Link to v2: https://lore.kernel.org/r/20260828-jh7110-clean-send-v2-0-331680c8b9d1@samsung.com

Changes since the RFC:
- Dropped the vout-subsystem wrapper driver and its binding, along with
  the patch relaxing the voutcrg binding; genpd handles PD_VOUT per
  node.
- Renamed the compatible to starfive,jh7110-hdmi-subsystem, dropping
  "mfd" as a Linux term (Conor Dooley).
- Absolute $refs in the bindings, unused example labels dropped, and the
  examples deduplicated between parent and children (Conor Dooley).
- Added the .mode_valid platform operation (patch 7).
- Split the inno-hdmi rework into a mechanical probe/bind split (patch
  4)
  and the regmap-from-parent change (patch 5). struct inno_hdmi is no
  longer exported; no platform glue dereferences it.
- Replaced the duplicated PHY driver with a shared Innosilicon library
  and moved Rockchip onto it (patches 11-13).
- Fixed pre-PLL lock detection, which masked the status read with the
  register address instead of the lock bit.
- Fixed a pixel clock refcount underflow: enable returns early on
  failure while disable tore down unconditionally.
- voutcrg patch reduced to adding CLK_SET_RATE_PARENT to the two dc8200
  pixel MUXes.
- Rebased onto v7.2.

---
Michal Wilczynski (19):
      dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy
      dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller
      dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem
      dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon
      dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem
      dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200
      drm/bridge: inno-hdmi: Split probe out of bind
      drm/bridge: inno-hdmi: Allow the register map to come from a parent
      drm/bridge: inno-hdmi: Add .disable platform operation
      drm/bridge: inno-hdmi: Add .mode_valid platform operation
      soc: starfive: Add jh7110-hdmi-subsystem driver
      soc: starfive: Add jh7110-vout-subsystem driver
      clk: starfive: jh7110-vout: Allow pixel clock rate propagation
      drm/bridge: starfive: Add JH7110 HDMI controller driver
      phy: Add common Innosilicon HDMI PHY helpers
      phy: rockchip: inno-hdmi: Use the common Innosilicon PHY helpers
      phy: starfive: Add jh7110-inno-hdmi-phy driver
      riscv: dts: starfive: jh7110: Update DT for display subsystem
      MAINTAINERS: Add StarFive JH7110 display subsystem entry

 .../starfive,jh7110-inno-hdmi-controller.yaml      | 121 +++++
 .../bindings/display/verisilicon,dc.yaml           |   1 +
 .../mfd/starfive,jh7110-hdmi-subsystem.yaml        |  95 ++++
 .../phy/starfive,jh7110-inno-hdmi-phy.yaml         |  49 ++
 .../soc/starfive/starfive,jh7110-syscon.yaml       |   6 +
 .../starfive/starfive,jh7110-vout-subsystem.yaml   | 100 ++++
 MAINTAINERS                                        |  13 +
 arch/riscv/boot/dts/starfive/jh7110-common.dtsi    | 121 ++++-
 arch/riscv/boot/dts/starfive/jh7110.dtsi           | 102 +++-
 drivers/clk/starfive/clk-starfive-jh7110-vout.c    |   6 +-
 drivers/gpu/drm/bridge/Kconfig                     |  11 +
 drivers/gpu/drm/bridge/Makefile                    |   1 +
 drivers/gpu/drm/bridge/inno-hdmi.c                 |  84 ++-
 drivers/gpu/drm/bridge/jh7110-inno-hdmi.c          | 318 +++++++++++
 drivers/phy/Kconfig                                |   8 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-inno-hdmi.c                        | 298 +++++++++++
 drivers/phy/rockchip/Kconfig                       |   1 +
 drivers/phy/rockchip/phy-rockchip-inno-hdmi.c      | 165 +-----
 drivers/phy/starfive/Kconfig                       |  20 +
 drivers/phy/starfive/Makefile                      |   1 +
 drivers/phy/starfive/phy-jh7110-inno-hdmi.c        | 579 +++++++++++++++++++++
 drivers/soc/Kconfig                                |   1 +
 drivers/soc/Makefile                               |   1 +
 drivers/soc/starfive/Kconfig                       |  43 ++
 drivers/soc/starfive/Makefile                      |   3 +
 drivers/soc/starfive/jh7110-hdmi-subsystem.c       |  74 +++
 drivers/soc/starfive/jh7110-vout-subsystem.c       |  83 +++
 include/drm/bridge/inno_hdmi.h                     |  10 +-
 include/linux/phy/inno-hdmi-phy.h                  |  85 +++
 30 files changed, 2227 insertions(+), 174 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20251031-jh7110-clean-send-7d2242118026

Best regards,
-- 
Michal Wilczynski <m.wilczynski@samsung.com>


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

* [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:30     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller Michal Wilczynski
                     ` (19 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Add the dt-binding for the StarFive JH7110 Innosilicon HDMI PHY.

This device is a child of the starfive,jh7110-hdmi-subsystem node. It
functions as both a PHY provider for the controller and as a clock
provider for the variable pixel clock (hdmi_pclk), which it generates
from its refoclk.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 .../phy/starfive,jh7110-inno-hdmi-phy.yaml         | 49 ++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-inno-hdmi-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-inno-hdmi-phy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b66cd57640a876777336c3fdd7cdfd303a4074aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-inno-hdmi-phy.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/starfive,jh7110-inno-hdmi-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive JH7110 Innosilicon INNO HDMI PHY
+
+maintainers:
+  - Michal Wilczynski <m.wilczynski@samsung.com>
+
+description:
+  The PHY portion of the StarFive JH7110 INNO HDMI IP.
+
+properties:
+  compatible:
+    const: starfive,jh7110-inno-hdmi-phy
+
+  clocks:
+    maxItems: 1
+    description: Reference oscillator.
+
+  '#clock-cells':
+    const: 0
+
+  '#phy-cells':
+    const: 0
+
+required:
+  - compatible
+  - clocks
+  - '#clock-cells'
+  - '#phy-cells'
+
+additionalProperties: false
+
+examples:
+  # Shown as a child of the HDMI subsystem node; see
+  # Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
+  # for the full node.
+  - |
+    phy {
+        compatible = "starfive,jh7110-inno-hdmi-phy";
+        clocks = <&xin24m>;
+        #clock-cells = <0>;
+        #phy-cells = <0>;
+    };
+
+...

-- 
2.34.1


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

* [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
  2026-09-04 13:27   ` [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:35     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem Michal Wilczynski
                     ` (18 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Add the dt-binding for the StarFive JH7110 Innosilicon HDMI controller
(DRM bridge).

This device is the second child of the starfive,jh7110-hdmi-subsystem node.
It consumes register access clocks (sys, mclk, bclk) from the voutcrg and
both the pixel clock (pclk) and the PHY from its hdmi_phy sibling.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 .../starfive,jh7110-inno-hdmi-controller.yaml      | 121 +++++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml b/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ea489a7722d0279b3ed67bc2576ec144e31bd05d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive JH7110 Innosilicon HDMI Controller
+
+maintainers:
+  - Michal Wilczynski <m.wilczynski@samsung.com>
+
+description:
+  The controller portion of the StarFive JH7110 INNO HDMI IP.
+
+properties:
+  compatible:
+    const: starfive,jh7110-inno-hdmi-controller
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: The HDMI controller main clock
+      - description: Audio master clock
+      - description: Audio bit clock, from the I2S transmitter
+      - description: The pixel clock, generated by the PHY
+
+  clock-names:
+    items:
+      - const: pclk
+      - const: mclk
+      - const: bclk
+      - const: pixel
+
+  resets:
+    maxItems: 1
+
+  phys:
+    maxItems: 1
+
+  starfive,vout-syscon:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the dom_vout_syscon system controller, which holds the mux
+      selecting the DC8200 output that drives the HDMI transmitter.
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Input from the DC8200. The remote port number selects the DC8200
+          panel and the remote endpoint number the interface on that panel,
+          0 for DPI and 1 for DP.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output to the HDMI connector.
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - phys
+  - starfive,vout-syscon
+  - ports
+
+additionalProperties: false
+
+examples:
+  # Shown as a child of the HDMI subsystem node; see
+  # Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
+  # for the full node.
+  - |
+    #include <dt-bindings/clock/starfive,jh7110-crg.h>
+    #include <dt-bindings/reset/starfive,jh7110-crg.h>
+
+    controller {
+        compatible = "starfive,jh7110-inno-hdmi-controller";
+        clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+                 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+                 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+                 <&hdmi_phy>;
+        clock-names = "pclk", "mclk", "bclk", "pixel";
+        interrupts = <99>;
+        phys = <&hdmi_phy>;
+        resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+        starfive,vout-syscon = <&vout_syscon>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+
+                endpoint {
+                    remote-endpoint = <&dpu_out_dpi0>;
+                };
+            };
+
+            port@1 {
+                reg = <1>;
+
+                endpoint {
+                    remote-endpoint = <&hdmi_con_in>;
+                };
+            };
+        };
+    };
+
+...

-- 
2.34.1


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

* [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
  2026-09-04 13:27   ` [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy Michal Wilczynski
  2026-09-04 13:27   ` [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:37     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon Michal Wilczynski
                     ` (17 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Add the dt-binding for the StarFive JH7110 HDMI subsystem.

The JH7110 HDMI IP is a monolithic block containing both the digital
controller and analog PHY in a single register space. This binding
defines the parent device, which holds the shared register map and
populates its two children: the PHY and the controller. This is
necessary to resolve a circular clock dependency between the HDMI block
and the VOUT clock generator.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 .../mfd/starfive,jh7110-hdmi-subsystem.yaml        | 95 ++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml b/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..bd35f191835a690e9754caa1084e2bee9b0b472b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/starfive,jh7110-hdmi-subsystem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive JH7110 HDMI subsystem
+
+maintainers:
+  - Michal Wilczynski <m.wilczynski@samsung.com>
+
+description:
+  The StarFive JH7110 HDMI block is a monolithic IP containing both
+  the digital controller logic and the analog PHY logic in a single
+  register space. It sits inside the video output subsystem, which owns
+  the NoC bus clock and reset gating this region and the power domain it
+  belongs to.
+
+properties:
+  compatible:
+    const: starfive,jh7110-hdmi-subsystem
+
+  reg:
+    maxItems: 1
+
+  phy:
+    $ref: /schemas/phy/starfive,jh7110-inno-hdmi-phy.yaml#
+
+  controller:
+    $ref: /schemas/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml#
+
+required:
+  - compatible
+  - reg
+  - phy
+  - controller
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/starfive,jh7110-crg.h>
+    #include <dt-bindings/reset/starfive,jh7110-crg.h>
+
+    soc {
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        hdmi@29590000 {
+            compatible = "starfive,jh7110-hdmi-subsystem";
+            reg = <0x29590000 0x4000>;
+
+            controller {
+                compatible = "starfive,jh7110-inno-hdmi-controller";
+                clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+                         <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+                         <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+                         <&hdmi_phy>;
+                clock-names = "pclk", "mclk", "bclk", "pixel";
+                interrupts = <99>;
+                phys = <&hdmi_phy>;
+                resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+                starfive,vout-syscon = <&vout_syscon>;
+
+                ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    port@0 {
+                        reg = <0>;
+
+                        endpoint {
+                            remote-endpoint = <&dpu_out_dpi0>;
+                        };
+                    };
+
+                    port@1 {
+                        reg = <1>;
+
+                        endpoint {
+                            remote-endpoint = <&hdmi_con_in>;
+                        };
+                    };
+                };
+            };
+
+            hdmi_phy: phy {
+                compatible = "starfive,jh7110-inno-hdmi-phy";
+                clocks = <&xin24m>;
+                #clock-cells = <0>;
+                #phy-cells = <0>;
+            };
+        };
+    };
+...

-- 
2.34.1


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

* [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (2 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:30     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem Michal Wilczynski
                     ` (16 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The video output subsystem has its own system controller, documented as
dom_vout_syscon. Among other things it holds the mux that selects which
DC8200 output drives the HDMI transmitter, so the HDMI controller needs
to reach it.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 .../devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml    | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
index 0039319e91fe6e6aadc4300114626348e4dfb7ce..47334f6c1243c9950e6fba44daacd41f42b55781 100644
--- a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
+++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml
@@ -24,6 +24,7 @@ properties:
           - enum:
               - starfive,jh7110-aon-syscon
               - starfive,jh7110-stg-syscon
+              - starfive,jh7110-vout-syscon
           - const: syscon
 
   reg:
@@ -90,4 +91,9 @@ examples:
         #power-domain-cells = <1>;
     };
 
+    syscon@295b0000 {
+        compatible = "starfive,jh7110-vout-syscon", "syscon";
+        reg = <0x295b0000 0x10000>;
+    };
+
 ...

-- 
2.34.1


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

* [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (3 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:36     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200 Michal Wilczynski
                     ` (15 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The JH7110 groups its display hardware into a video output subsystem,
dom_vout_top, covering the DC8200, the HDMI transmitter, the video
output clock generator and the video output system controller.

They share one NoC port whose clock and reset gate access to every
register in the region, and the region sits in the PD_VOUT power
domain. Describe the subsystem as a bus node so those resources are
owned by the block that shares them, rather than by whichever consumer
happens to probe first.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 .../starfive/starfive,jh7110-vout-subsystem.yaml   | 100 +++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-vout-subsystem.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-vout-subsystem.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..17a6bf61df1e7ca95c07e15347c9d688163c926f
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-vout-subsystem.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/starfive/starfive,jh7110-vout-subsystem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive JH7110 video output subsystem
+
+maintainers:
+  - Michal Wilczynski <m.wilczynski@samsung.com>
+
+description:
+  The JH7110 groups its display hardware into a video output subsystem, named
+  dom_vout_top in the documentation. It covers the DC8200 display controller,
+  the HDMI transmitter, the video output clock generator and the video output
+  system controller, all behind one NoC port. That port's clock and reset gate
+  access to every register in the region, and the region sits in the PD_VOUT
+  power domain, so this node owns those resources on behalf of its children.
+
+properties:
+  compatible:
+    const: starfive,jh7110-vout-subsystem
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: NoC display bus clock.
+
+  resets:
+    maxItems: 1
+    description: NoC display bus reset.
+
+  power-domains:
+    maxItems: 1
+
+  dma-noncoherent: true
+
+  ranges: true
+
+  "#address-cells":
+    const: 2
+
+  "#size-cells":
+    const: 2
+
+patternProperties:
+  "^display@[0-9a-f]+$":
+    $ref: /schemas/display/verisilicon,dc.yaml#
+
+  "^hdmi@[0-9a-f]+$":
+    $ref: /schemas/mfd/starfive,jh7110-hdmi-subsystem.yaml#
+
+  "^clock-controller@[0-9a-f]+$":
+    $ref: /schemas/clock/starfive,jh7110-voutcrg.yaml#
+
+  "^syscon@[0-9a-f]+$":
+    $ref: /schemas/soc/starfive/starfive,jh7110-syscon.yaml#
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - resets
+  - power-domains
+  - ranges
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/starfive,jh7110-crg.h>
+    #include <dt-bindings/power/starfive,jh7110-pmu.h>
+    #include <dt-bindings/reset/starfive,jh7110-crg.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        display-subsystem@29400000 {
+            compatible = "starfive,jh7110-vout-subsystem";
+            reg = <0x0 0x29400000 0x0 0x200000>;
+            #address-cells = <2>;
+            #size-cells = <2>;
+            ranges;
+            clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>;
+            dma-noncoherent;
+            power-domains = <&pwrc JH7110_PD_VOUT>;
+            resets = <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>;
+
+            syscon@295b0000 {
+                compatible = "starfive,jh7110-vout-syscon", "syscon";
+                reg = <0x0 0x295b0000 0x0 0x10000>;
+            };
+        };
+    };
+...

-- 
2.34.1


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

* [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (4 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:31     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind Michal Wilczynski
                     ` (14 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The JH7110 embeds a DC8200. The binding requires an SoC specific
compatible ahead of the generic one, so add the JH7110 to the enum.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 Documentation/devicetree/bindings/display/verisilicon,dc.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
index 919a9001220120054cb62d1d01b7293367266f95..05a202633cd273fec358c095c64584d959d96d90 100644
--- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
+++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
@@ -16,6 +16,7 @@ properties:
   compatible:
     items:
       - enum:
+          - starfive,jh7110-dc8200
           - thead,th1520-dc8200
       - const: verisilicon,dc # DC IPs have discoverable ID/revision registers
 

-- 
2.34.1


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

* [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (5 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200 Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:42     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent Michal Wilczynski
                     ` (13 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

inno_hdmi_bind() both sets up the bridge and attaches it to a DRM
encoder. A platform whose HDMI controller is a child of a larger device
needs the first half without the second, since it registers as its own
platform driver and lets the DRM core bind the bridge later.

Move the setup into a new exported inno_hdmi_probe(), with a matching
inno_hdmi_remove(), and reduce inno_hdmi_bind() to a wrapper around it.

No functional change intended.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 40 +++++++++++++++++++++++++++++++++-----
 include/drm/bridge/inno_hdmi.h     |  4 ++++
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index dd35f5a875d3d842fee6d2ead0de88503f9914e1..aab474740f7f57aa34007e6dac49e2414561593f 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -1061,11 +1061,24 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
 	return adap;
 }
 
-struct inno_hdmi *inno_hdmi_bind(struct device *dev,
-				 struct drm_encoder *encoder,
-				 const struct inno_hdmi_plat_data *plat_data)
+/**
+ * inno_hdmi_probe - Internal helper to perform common setup
+ * @pdev: platform device
+ * @plat_data: SoC-specific platform data
+ *
+ * This function handles all the common hardware setup: allocating the main
+ * struct, mapping registers, getting clocks, initializing the hardware,
+ * setting up the IRQ, and initializing the DDC adapter and bridge struct.
+ * It returns a pointer to the inno_hdmi struct on success, or an ERR_PTR
+ * on failure.
+ *
+ * This function is used by modern, decoupled MFD/glue drivers. It registers
+ * the bridge but does not attach it.
+ */
+struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
+				  const struct inno_hdmi_plat_data *plat_data)
 {
-	struct platform_device *pdev = to_platform_device(dev);
+	struct device *dev = &pdev->dev;
 	struct inno_hdmi *hdmi;
 	int irq;
 	int ret;
@@ -1128,7 +1141,24 @@ struct inno_hdmi *inno_hdmi_bind(struct device *dev,
 	if (ret)
 		return ERR_PTR(ret);
 
-	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+	return hdmi;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_probe);
+
+struct inno_hdmi *inno_hdmi_bind(struct device *dev,
+				 struct drm_encoder *encoder,
+				 const struct inno_hdmi_plat_data *plat_data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct inno_hdmi *hdmi;
+	int ret;
+
+	hdmi = inno_hdmi_probe(pdev, plat_data);
+	if (IS_ERR(hdmi))
+		return hdmi;
+
+	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL,
+				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
 	if (ret)
 		return ERR_PTR(ret);
 
diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h
index 5bbcaeea94e2a20fd0dc0ae0c6946eb5604bbaf6..81da9d9bcd79db8fe26c6dff4569371e484df608 100644
--- a/include/drm/bridge/inno_hdmi.h
+++ b/include/drm/bridge/inno_hdmi.h
@@ -12,6 +12,7 @@ struct device;
 struct drm_encoder;
 struct drm_display_mode;
 struct inno_hdmi;
+struct platform_device;
 
 struct inno_hdmi_plat_ops {
 	void (*enable)(struct device *pdev, struct drm_display_mode *mode);
@@ -32,4 +33,7 @@ struct inno_hdmi_plat_data {
 struct inno_hdmi *inno_hdmi_bind(struct device *pdev,
 				 struct drm_encoder *encoder,
 				 const struct inno_hdmi_plat_data *plat_data);
+
+struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
+				  const struct inno_hdmi_plat_data *plat_data);
 #endif /* __INNO_HDMI__ */

-- 
2.34.1


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

* [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (6 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:43     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation Michal Wilczynski
                     ` (12 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

On some SoCs the HDMI controller does not own its register window. The
StarFive JH7110 documents one 64 KB block, u0_hdmitx, that holds both
the controller and the PHY, so the parent device maps it and owns the
regmap while the two children share it.

Use a regmap supplied by the parent device when there is one, and keep
mapping our own resource when there is not, so platforms that own their
register window are unaffected.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index aab474740f7f57aa34007e6dac49e2414561593f..8bd771761180473d221f59d5c0cd2a636e96ac4c 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -397,6 +397,7 @@ struct inno_hdmi {
 	struct clk *pclk;
 	struct clk *refclk;
 	void __iomem *regs;
+	struct regmap *regmap;
 	struct regmap *grf;
 
 	struct inno_hdmi_i2c *i2c;
@@ -470,11 +471,25 @@ static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
 
 static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
 {
+	u32 val;
+
+	if (hdmi->regmap) {
+		if (regmap_read(hdmi->regmap, offset * 4, &val))
+			return 0;
+
+		return val;
+	}
+
 	return readl_relaxed(hdmi->regs + (offset) * 0x04);
 }
 
 static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
 {
+	if (hdmi->regmap) {
+		regmap_write(hdmi->regmap, offset * 4, val);
+		return;
+	}
+
 	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
 }
 
@@ -1095,9 +1110,19 @@ struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
 	hdmi->dev = dev;
 	hdmi->plat_data = plat_data;
 
-	hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(hdmi->regs))
-		return ERR_CAST(hdmi->regs);
+	/*
+	 * On platforms where the controller shares a register space with
+	 * other blocks, the parent owns the regmap. Fall back to mapping
+	 * our own resource where it does not.
+	 */
+	if (dev->parent)
+		hdmi->regmap = dev_get_regmap(dev->parent, NULL);
+
+	if (!hdmi->regmap) {
+		hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
+		if (IS_ERR(hdmi->regs))
+			return ERR_CAST(hdmi->regs);
+	}
 
 	hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk");
 	if (IS_ERR(hdmi->pclk)) {

-- 
2.34.1


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

* [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (7 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:49     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid " Michal Wilczynski
                     ` (11 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The Innosilicon HDMI driver supports platform-specific behavior through
the `inno_hdmi_plat_ops`. While it provides an `.enable` hook for
platform-specific power up sequences (like enabling PHYs), it lacks a
corresponding hook for power down.

This patch adds a new `.disable` op to the `inno_hdmi_plat_ops` struct
and calls it at the beginning of `inno_hdmi_bridge_atomic_disable()`.

This allows platform specific drivers, such as the StarFive JH7110,
to implement their own power down sequence (e.g., calling
phy_power_off() and clk_disable_unprepare()).

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 4 ++++
 include/drm/bridge/inno_hdmi.h     | 1 +
 2 files changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index 8bd771761180473d221f59d5c0cd2a636e96ac4c..410a9eaa13541dfdacc5c96ba9a4d9dc021bc271 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -886,6 +886,10 @@ static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
 					    struct drm_atomic_commit *state)
 {
 	struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+	const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
+
+	if (plat_ops && plat_ops->disable)
+		plat_ops->disable(hdmi->dev);
 
 	inno_hdmi_standby(hdmi);
 }
diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h
index 81da9d9bcd79db8fe26c6dff4569371e484df608..5fca651c56b056d814124aa9c7e91aa7fd7ed1cd 100644
--- a/include/drm/bridge/inno_hdmi.h
+++ b/include/drm/bridge/inno_hdmi.h
@@ -16,6 +16,7 @@ struct platform_device;
 
 struct inno_hdmi_plat_ops {
 	void (*enable)(struct device *pdev, struct drm_display_mode *mode);
+	void (*disable)(struct device *pdev);
 };
 
 struct inno_hdmi_phy_config {

-- 
2.34.1


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

* [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid platform operation
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (8 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:40     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver Michal Wilczynski
                     ` (10 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

inno_hdmi_bridge_mode_valid() validates the pixel clock against
hdmi->refclk, but that clock is optional and is only present on
platforms that describe a "ref" clock. Platforms where the pixel clock
is produced by a separate device - such as the StarFive JH7110, whose
PHY is both the clock and the PHY provider - have no "ref" clock, so
the check is skipped entirely and every mode is reported as valid.

A mode the platform cannot generate is then advertised to userspace.
The subsequent modeset appears to succeed, since the atomic enable path
cannot fail, and the display silently stays blank.

Add a .mode_valid platform operation so platforms can reject modes they
are unable to drive. Platforms that do not implement it are unaffected.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 9 +++++++++
 include/drm/bridge/inno_hdmi.h     | 5 ++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index 410a9eaa13541dfdacc5c96ba9a4d9dc021bc271..e6ca0479e0186023dc41105e4f28f708656db859 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -823,6 +823,7 @@ static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridg
 							const struct drm_display_mode *mode)
 {
 	struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+	const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
 	unsigned long mpixelclk, max_tolerance;
 	long rounded_refclk;
 
@@ -838,6 +839,14 @@ static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridg
 	if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
 		return MODE_CLOCK_HIGH;
 
+	if (plat_ops && plat_ops->mode_valid) {
+		enum drm_mode_status status;
+
+		status = plat_ops->mode_valid(hdmi->dev, mode);
+		if (status != MODE_OK)
+			return status;
+	}
+
 	if (hdmi->refclk) {
 		rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk);
 		if (rounded_refclk < 0)
diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h
index 5fca651c56b056d814124aa9c7e91aa7fd7ed1cd..1e9974401c39e3773bb339eaec0b45ff1dd9c975 100644
--- a/include/drm/bridge/inno_hdmi.h
+++ b/include/drm/bridge/inno_hdmi.h
@@ -8,15 +8,18 @@
 
 #include <linux/types.h>
 
+#include <drm/drm_modes.h>
+
 struct device;
 struct drm_encoder;
-struct drm_display_mode;
 struct inno_hdmi;
 struct platform_device;
 
 struct inno_hdmi_plat_ops {
 	void (*enable)(struct device *pdev, struct drm_display_mode *mode);
 	void (*disable)(struct device *pdev);
+	enum drm_mode_status (*mode_valid)(struct device *pdev,
+					   const struct drm_display_mode *mode);
 };
 
 struct inno_hdmi_phy_config {

-- 
2.34.1


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

* [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (9 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid " Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:52     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver Michal Wilczynski
                     ` (9 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

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 that gate access to this region, and
the PD_VOUT power domain it sits in, are held by the video output
subsystem parent for as long as this device exists, so there is nothing
for this driver to bring up itself.

Co-developed-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/soc/Kconfig                          |  1 +
 drivers/soc/Makefile                         |  1 +
 drivers/soc/starfive/Kconfig                 | 28 +++++++++++
 drivers/soc/starfive/Makefile                |  2 +
 drivers/soc/starfive/jh7110-hdmi-subsystem.c | 74 ++++++++++++++++++++++++++++
 5 files changed, 106 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..e738638ab0f755fbbabca259264abb56f3b6101f
--- /dev/null
+++ b/drivers/soc/starfive/Kconfig
@@ -0,0 +1,28 @@
+# 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
+	select REGMAP_MMIO
+	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..e05b91825be4db31043c5d1512de269d3e151da2
--- /dev/null
+++ b/drivers/soc/starfive/jh7110-hdmi-subsystem.c
@@ -0,0 +1,74 @@
+// 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@samsung.com>
+ *
+ * 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/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+static const struct regmap_config starfive_hdmi_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.max_register = 0x3fff,
+};
+
+static int starfive_hdmi_subsys_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct regmap *regmap;
+	void __iomem *regs;
+	int ret;
+
+	/*
+	 * The NoC display bus clock and reset that gate access to this region,
+	 * and the PD_VOUT power domain it sits in, are held by the video
+	 * output subsystem parent for as long as this device exists.
+	 */
+	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)
+		return dev_err_probe(dev, ret,
+				     "Failed to populate child devices\n");
+
+	return 0;
+}
+
+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@samsung.com>");
+MODULE_DESCRIPTION("StarFive JH7110 HDMI subsystem Driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


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

* [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (10 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:47     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation Michal Wilczynski
                     ` (8 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Nothing in the video output subsystem can reach its own registers until
the NoC display bus clock and reset are up and PD_VOUT is powered, and
those are shared by every device in the region. Leaving them to whichever
consumer probes first works by accident and stops working as soon as the
probe order changes.

Add a driver for the subsystem node that takes the bus clock and reset,
holds a runtime PM reference so genpd keeps PD_VOUT powered, and then
populates its children. All three are released only once the last child
is gone.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/soc/starfive/Kconfig                 | 15 +++++
 drivers/soc/starfive/Makefile                |  1 +
 drivers/soc/starfive/jh7110-vout-subsystem.c | 83 ++++++++++++++++++++++++++++
 3 files changed, 99 insertions(+)

diff --git a/drivers/soc/starfive/Kconfig b/drivers/soc/starfive/Kconfig
index e738638ab0f755fbbabca259264abb56f3b6101f..cf5e626b8a39a75768917eee188a79256b36f1aa 100644
--- a/drivers/soc/starfive/Kconfig
+++ b/drivers/soc/starfive/Kconfig
@@ -6,6 +6,21 @@
 if ARCH_STARFIVE || COMPILE_TEST
 menu "Starfive SoC drivers"
 
+config SOC_STARFIVE_JH7110_VOUT_SUBSYSTEM
+	tristate "StarFive JH7110 video output subsystem driver"
+	depends on OF
+	select PM
+	help
+	  This option enables the parent driver for the StarFive JH7110
+	  video output subsystem, which the documentation calls dom_vout_top.
+
+	  The subsystem covers the DC8200 display controller, the HDMI
+	  transmitter, the video output clock generator and the video output
+	  system controller. They share one NoC port whose clock and reset
+	  gate access to the whole register region, and the region sits in
+	  the PD_VOUT power domain. This driver owns those resources and
+	  holds them for as long as any of its children exist.
+
 config SOC_STARFIVE_JH7110_HDMI_SUBSYSTEM
 	tristate "StarFive JH7110 HDMI subsystem driver"
 	depends on OF
diff --git a/drivers/soc/starfive/Makefile b/drivers/soc/starfive/Makefile
index be89d8119212b7a7038817c2f0e8eac1984ada88..6c229020c1e563b37df43f6ac1665465a11333cc 100644
--- a/drivers/soc/starfive/Makefile
+++ b/drivers/soc/starfive/Makefile
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_SOC_STARFIVE_JH7110_HDMI_SUBSYSTEM) += jh7110-hdmi-subsystem.o
+obj-$(CONFIG_SOC_STARFIVE_JH7110_VOUT_SUBSYSTEM) += jh7110-vout-subsystem.o
diff --git a/drivers/soc/starfive/jh7110-vout-subsystem.c b/drivers/soc/starfive/jh7110-vout-subsystem.c
new file mode 100644
index 0000000000000000000000000000000000000000..d06ce43d80b1637995a4c5f100d89945d27a192a
--- /dev/null
+++ b/drivers/soc/starfive/jh7110-vout-subsystem.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the StarFive JH7110 video output subsystem
+ *
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ *
+ * The display hardware sits behind a single NoC port whose clock and reset
+ * gate access to every register in the region, inside the PD_VOUT power
+ * domain. Nothing below this node can reach its own registers until all three
+ * are up, so bring them up here and hold them for as long as any child device
+ * exists, rather than leaving them to whichever consumer happens to probe
+ * first.
+ */
+
+#include <linux/clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+static void jh7110_vout_subsys_pm_put(void *data)
+{
+	pm_runtime_put_sync(data);
+}
+
+static int jh7110_vout_subsys_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct reset_control *bus_rst;
+	struct clk *bus_clk;
+	int ret;
+
+	/*
+	 * Take a runtime PM reference for the lifetime of this device. genpd
+	 * only keeps PD_VOUT powered while something actually holds it, and
+	 * an unclocked or unpowered access to this region wedges the bus.
+	 */
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to power on PD_VOUT\n");
+
+	ret = devm_add_action_or_reset(dev, jh7110_vout_subsys_pm_put, dev);
+	if (ret)
+		return ret;
+
+	bus_clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(bus_clk))
+		return dev_err_probe(dev, PTR_ERR(bus_clk),
+				     "Failed to enable NoC bus clock\n");
+
+	bus_rst = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+	if (IS_ERR(bus_rst))
+		return dev_err_probe(dev, PTR_ERR(bus_rst),
+				     "Failed to deassert NoC bus reset\n");
+
+	return devm_of_platform_populate(dev);
+}
+
+static const struct of_device_id jh7110_vout_subsys_of_match[] = {
+	{ .compatible = "starfive,jh7110-vout-subsystem", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jh7110_vout_subsys_of_match);
+
+static struct platform_driver jh7110_vout_subsys_driver = {
+	.probe = jh7110_vout_subsys_probe,
+	.driver = {
+		.name = "jh7110-vout-subsystem",
+		.of_match_table = jh7110_vout_subsys_of_match,
+	},
+};
+module_platform_driver(jh7110_vout_subsys_driver);
+
+MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
+MODULE_DESCRIPTION("StarFive JH7110 video output subsystem driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


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

* [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (11 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:44     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver Michal Wilczynski
                     ` (7 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The dc8200_pix0 and dc8200_pix1 MUXes feed the display controller, and
the rate they carry is ultimately produced by the HDMI PHY, which
registers hdmitx0_pixelclk as a clock provider.

Add CLK_SET_RATE_PARENT to both MUXes so a rate requested on them
reaches that parent, instead of being clamped to whatever rate the
parent already happens to run at.

Co-developed-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/clk/starfive/clk-starfive-jh7110-vout.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/starfive/clk-starfive-jh7110-vout.c b/drivers/clk/starfive/clk-starfive-jh7110-vout.c
index bad20d5d794a72f071b4d547b7304786a8ba9afa..aeba42cd3c44ba205ced52b1826d2d2de82ad78f 100644
--- a/drivers/clk/starfive/clk-starfive-jh7110-vout.c
+++ b/drivers/clk/starfive/clk-starfive-jh7110-vout.c
@@ -40,10 +40,12 @@ static const struct jh71x0_clk_data jh7110_voutclk_data[] = {
 	JH71X0_GATE(JH7110_VOUTCLK_DC8200_AXI, "dc8200_axi", 0, JH7110_VOUTCLK_VOUT_TOP_AXI),
 	JH71X0_GATE(JH7110_VOUTCLK_DC8200_CORE, "dc8200_core", 0, JH7110_VOUTCLK_VOUT_TOP_AXI),
 	JH71X0_GATE(JH7110_VOUTCLK_DC8200_AHB, "dc8200_ahb", 0, JH7110_VOUTCLK_VOUT_TOP_AHB),
-	JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX0, "dc8200_pix0", 0, 2,
+	JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX0, "dc8200_pix0",
+		    CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 2,
 		    JH7110_VOUTCLK_DC8200_PIX,
 		    JH7110_VOUTCLK_HDMITX0_PIXELCLK),
-	JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX1, "dc8200_pix1", 0, 2,
+	JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX1, "dc8200_pix1",
+		    CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 2,
 		    JH7110_VOUTCLK_DC8200_PIX,
 		    JH7110_VOUTCLK_HDMITX0_PIXELCLK),
 	/* LCD */

-- 
2.34.1


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

* [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (12 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:39     ` Icenowy Zheng
  2026-09-04 13:57     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers Michal Wilczynski
                     ` (6 subsequent siblings)
  20 siblings, 2 replies; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Add the HDMI controller (bridge) driver for the StarFive JH7110.

This driver binds to the starfive,jh7110-inno-hdmi-controller node.
It gets its shared regmap from its parent and its register access,
module and bus clocks from voutcrg. It consumes the pixel clock and the
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().

The DC8200 has two panels, each exposing a DP and a DPI interface, and a
mux in the video output system controller picks which of them drives the
HDMI transmitter. Program that mux from the port graph rather than
relying on whatever the bootloader left behind, taking the panel from the
remote port number and the interface from the remote endpoint number.

The generic driver holds the clock it looks up as the register access
clock enabled for its lifetime, and derives the DDC divider from that
clock's rate, so point it at the system clock. Naming the pixel clock
there instead would keep the PHY pre-PLL powered from probe onwards and
size the divider from the wrong rate.

The PHY can only generate the discrete set of pixel clocks described by
its pre-PLL table, so .mode_valid rejects any mode clk_round_rate()
cannot satisfy. Without it such a mode would be advertised to userspace
and the modeset would appear to succeed while the display stayed blank.

.enable returns early when the rate is unsupported or the PHY fails to
power on, so track whether the pixel clock was actually enabled and let
.disable tear down only what was brought up, otherwise the clock
refcount underflows.

The clocks and the reset are torn down through devm rather than from
.remove, so that they outlive the bridge that inno_hdmi_probe() adds with
devm_drm_bridge_add(). Releasing them in .remove runs before devres
unwinds and would leave the bridge registered with its clocks already
gated.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/gpu/drm/bridge/Kconfig            |  11 ++
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/jh7110-inno-hdmi.c | 318 ++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 4a57d49b4c6d3ab4b965228835b372d191647197..75b1cf6727d5a32310dcf9fe5734d95e14eea8fe 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -359,6 +359,17 @@ config DRM_SOLOMON_SSD2825
 	  Say M here if you want to support this hardware as a module.
 	  The module will be named "ssd2825".
 
+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 subsystem
+	  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 15cc821d85b7ea6f3cdc313f3e521b028de567d7..5d843f4ad7ed50b28cb75286c5e22789d91a0836 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -30,6 +30,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_SOLOMON_SSD2825) += ssd2825.o
 obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o
 obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.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..b0bf6abaa55fb452a90021586faf5f220c150968
--- /dev/null
+++ b/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) StarFive Technology Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ *
+ * HDMI controller (bridge) driver for the StarFive JH7110 HDMI subsystem.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.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>
+#include <drm/drm_modes.h>
+
+/* dom_vout_syscon: HDMI pixel data mapping */
+#define VOUT_SYSCFG_4			0x4
+#define VOUT_HDMI_DP_BIT_DEPTH		BIT(25)
+#define VOUT_HDMI_DP_YUV_MODE		GENMASK(27, 26)
+#define VOUT_HDMI_DP_YUV_MODE_RGB	3
+#define VOUT_HDMI_DPI_BIT_DEPTH		GENMASK(29, 28)
+#define VOUT_HDMI_DPI_BIT_DEPTH_8BIT	0
+#define VOUT_HDMI_DPI_DP_SEL		BIT(30)
+
+/* u2_display_panel_mux feeds HDMI_Ctrl, see the block diagram in 5.1 */
+#define VOUT_SYSCFG_8			0x8
+#define VOUT_HDMI_PANEL_SEL		BIT(4)
+
+enum stf_hdmi_ctrl_clocks { CLK_SYS = 0, CLK_M, CLK_B, CLK_PCLK, CLK_CTRL_NUM };
+
+struct stf_inno_hdmi_controller {
+	struct device *dev;
+	struct clk_bulk_data clks[CLK_CTRL_NUM];
+	struct reset_control *tx_rst;
+	struct phy *phy;
+	bool enabled;
+};
+
+static enum drm_mode_status
+inno_hdmi_starfive_mode_valid(struct device *dev,
+			      const struct drm_display_mode *mode)
+{
+	struct stf_inno_hdmi_controller *ctrl = dev_get_drvdata(dev);
+	unsigned long pixelclk = mode->clock * 1000;
+	long rounded;
+
+	/*
+	 * The PHY can only generate the discrete set of pixel clocks described
+	 * by its pre-PLL table, and clk_round_rate() fails for anything else.
+	 * Reject those modes here: without this the modeset would appear to
+	 * succeed while the PHY never produces a signal.
+	 */
+	rounded = clk_round_rate(ctrl->clks[CLK_PCLK].clk, pixelclk);
+	if (rounded < 0 || rounded != pixelclk)
+		return MODE_NOCLOCK;
+
+	return MODE_OK;
+}
+
+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;
+	}
+
+	ctrl->enabled = true;
+}
+
+static void inno_hdmi_starfive_disable(struct device *dev)
+{
+	struct stf_inno_hdmi_controller *ctrl = dev_get_drvdata(dev);
+
+	/*
+	 * .enable bails out early if the pixel clock rate is unsupported or
+	 * the PHY fails to power on, leaving pclk and the PHY untouched.
+	 * Only tear down what was actually brought up, otherwise the clock
+	 * refcount underflows.
+	 */
+	if (!ctrl->enabled)
+		return;
+
+	phy_power_off(ctrl->phy);
+	clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
+	ctrl->enabled = false;
+}
+
+/*
+ * The DC8200 has two panels, each exposing a DP and a DPI interface, and a mux
+ * in dom_vout_syscon picks which of them drives the HDMI transmitter. Derive
+ * the mux setting from the port graph: the remote port number selects the
+ * DC8200 panel, and the remote endpoint number the interface on that panel
+ * (0 for DPI, 1 for DP). Both drive 8-bit RGB, the only format this driver
+ * currently produces.
+ */
+static int stf_inno_hdmi_setup_mux(struct device *dev)
+{
+	struct device_node *ep, *remote;
+	struct of_endpoint endpoint;
+	struct regmap *syscon;
+	u32 mask, val;
+	int ret;
+
+	syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
+						 "starfive,vout-syscon");
+	if (IS_ERR(syscon))
+		return dev_err_probe(dev, PTR_ERR(syscon),
+				     "Failed to get vout syscon\n");
+
+	ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+	if (!ep)
+		return dev_err_probe(dev, -ENODEV, "No input endpoint\n");
+
+	remote = of_graph_get_remote_endpoint(ep);
+	of_node_put(ep);
+	if (!remote)
+		return dev_err_probe(dev, -ENODEV,
+				     "Input endpoint is not connected\n");
+
+	ret = of_graph_parse_endpoint(remote, &endpoint);
+	of_node_put(remote);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to parse the remote endpoint\n");
+
+	if (endpoint.port > 1 || endpoint.id > 1)
+		return dev_err_probe(dev, -EINVAL,
+				     "Unsupported DC8200 output %u/%u\n",
+				     endpoint.port, endpoint.id);
+
+	/* Data mapping: 8-bit RGB on whichever interface is in use. */
+	mask = VOUT_HDMI_DPI_DP_SEL | VOUT_HDMI_DP_BIT_DEPTH |
+	       VOUT_HDMI_DP_YUV_MODE | VOUT_HDMI_DPI_BIT_DEPTH;
+	val = FIELD_PREP(VOUT_HDMI_DPI_DP_SEL, endpoint.id) |
+	      FIELD_PREP(VOUT_HDMI_DP_YUV_MODE, VOUT_HDMI_DP_YUV_MODE_RGB) |
+	      FIELD_PREP(VOUT_HDMI_DPI_BIT_DEPTH, VOUT_HDMI_DPI_BIT_DEPTH_8BIT);
+
+	ret = regmap_update_bits(syscon, VOUT_SYSCFG_4, mask, val);
+	if (ret)
+		return ret;
+
+	/* Which DC8200 panel drives the HDMI transmitter. */
+	return regmap_update_bits(syscon, VOUT_SYSCFG_8, VOUT_HDMI_PANEL_SEL,
+				  FIELD_PREP(VOUT_HDMI_PANEL_SEL,
+					     endpoint.port));
+}
+
+static void stf_inno_hdmi_clk_disable(void *data)
+{
+	struct stf_inno_hdmi_controller *ctrl = data;
+
+	clk_bulk_disable_unprepare(CLK_CTRL_NUM - 1, ctrl->clks);
+}
+
+static void stf_inno_hdmi_rst_assert(void *data)
+{
+	reset_control_assert(data);
+}
+
+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 *regmap;
+	struct inno_hdmi *inno;
+	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 parent */
+	regmap = dev_get_regmap(parent, NULL);
+	if (!regmap) {
+		dev_err(dev, "Failed to get parent regmap\n");
+		return -ENODEV;
+	}
+
+	ctrl->phy = devm_phy_get(dev, NULL);
+	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, NULL);
+	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 = "pclk";
+	ctrl->clks[CLK_M].id = "mclk";
+	ctrl->clks[CLK_B].id = "bclk";
+	ctrl->clks[CLK_PCLK].id = "pixel"; /* Generated by the 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");
+
+	/*
+	 * Tear the clocks and the reset down through devm, so that they outlive
+	 * everything registered after them. The bridge is added with
+	 * devm_drm_bridge_add(), and unwinding in the wrong order would leave it
+	 * registered while its clocks are already gated.
+	 *
+	 * The pixel clock is enabled on demand during modeset.
+	 */
+	ret = clk_bulk_prepare_enable(CLK_CTRL_NUM - 1, ctrl->clks);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, stf_inno_hdmi_clk_disable, ctrl);
+	if (ret)
+		return ret;
+
+	ret = reset_control_deassert(ctrl->tx_rst);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, stf_inno_hdmi_rst_assert,
+				       ctrl->tx_rst);
+	if (ret)
+		return ret;
+
+	ret = stf_inno_hdmi_setup_mux(dev);
+	if (ret)
+		return ret;
+
+	plat_data = of_device_get_match_data(dev);
+
+	/* Hand off to the generic library to create the bridge. */
+	inno = inno_hdmi_probe(pdev, plat_data);
+	if (IS_ERR(inno))
+		return PTR_ERR(inno);
+
+	return 0;
+}
+
+/*
+ * 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,
+	.mode_valid = inno_hdmi_starfive_mode_valid,
+};
+
+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,
+	.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@samsung.com>");
+MODULE_DESCRIPTION("StarFive INNO HDMI Controller Driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


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

* [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (13 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:55     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon " Michal Wilczynski
                     ` (5 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The Innosilicon HDMI PHY IP is used by several SoCs. They differ in
where the PHY register block sits in the register space and in which
pixel clocks they support, but the pre-PLL programming sequence and the
layout of its registers are the same.

Add a small library holding that shared part: the pre-PLL configuration
table format, a lookup, clk_ops determine_rate and recalc_rate helpers,
and the pre-PLL register programming. Callers pass a regmap, a register
offset for the PHY block, and their own pixel clock table.

No driver uses it yet; the users are converted separately.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/phy/Kconfig               |   8 +
 drivers/phy/Makefile              |   1 +
 drivers/phy/phy-inno-hdmi.c       | 298 ++++++++++++++++++++++++++++++++++++++
 include/linux/phy/inno-hdmi-phy.h |  85 +++++++++++
 4 files changed, 392 insertions(+)

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19f3b7d12b7d492d16000fa52483dc5002590d7a..0e973b3b02a30704d339db9f15d4695bcbec1c75 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -168,6 +168,14 @@ config PHY_XGENE
 	help
 	  This option enables support for APM X-Gene SoC multi-purpose PHY.
 
+config PHY_INNO_HDMI
+	tristate
+	depends on GENERIC_PHY
+	select REGMAP
+	help
+	  Common helpers for the Innosilicon HDMI PHY, shared by the SoC
+	  drivers that instantiate it. Selected by those drivers.
+
 source "drivers/phy/allwinner/Kconfig"
 source "drivers/phy/amlogic/Kconfig"
 source "drivers/phy/apple/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index d7aa516bcc49e7662c9c9db421833fe591dfbc05..e93c473dfc6d837ed6ba540f0b0204a0d68d25bf 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PHY_COMMON_PROPS)		+= phy-common-props.o
 obj-$(CONFIG_PHY_COMMON_PROPS_TEST)	+= phy-common-props-test.o
 obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
 obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY)	+= phy-core-mipi-dphy.o
+obj-$(CONFIG_PHY_INNO_HDMI)		+= phy-inno-hdmi.o
 obj-$(CONFIG_PHY_AIROHA_PCIE)		+= phy-airoha-pcie.o
 obj-$(CONFIG_PHY_CAN_TRANSCEIVER)	+= phy-can-transceiver.o
 obj-$(CONFIG_PHY_ECONET_PCIE)		+= phy-econet-pcie.o
diff --git a/drivers/phy/phy-inno-hdmi.c b/drivers/phy/phy-inno-hdmi.c
new file mode 100644
index 0000000000000000000000000000000000000000..77e745a87d22b5f3776593e642af1f03305bb67e
--- /dev/null
+++ b/drivers/phy/phy-inno-hdmi.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Common helpers for the Innosilicon HDMI PHY.
+ *
+ * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Author: Zheng Yang <zhengyang@rock-chips.com>
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ *
+ * Derived from drivers/phy/rockchip/phy-rockchip-inno-hdmi.c.
+ *
+ * The same PHY IP is used by several SoCs, which differ in where the PHY
+ * register block sits and in the pixel clock table they support, but share
+ * the pre-PLL programming sequence.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/phy/inno-hdmi-phy.h>
+
+#define INNO_PRE_PLL_CONTROL			0xa0
+#define INNO_PRE_PLL_POWER_DOWN			BIT(0)
+#define INNO_PCLK_VCO_DIV_5_MASK		BIT(1)
+#define INNO_PCLK_VCO_DIV_5(x)			FIELD_PREP(BIT(1), x)
+
+#define INNO_PRE_PLL_DIV_1			0xa1
+#define INNO_PRE_PLL_PRE_DIV_MASK		GENMASK(5, 0)
+#define INNO_PRE_PLL_PRE_DIV(x)			FIELD_PREP(GENMASK(5, 0), x)
+
+#define INNO_PRE_PLL_DIV_2			0xa2
+#define INNO_SPREAD_SPECTRUM_MOD_DISABLE	BIT(6)
+#define INNO_PRE_PLL_FRAC_DIV_DISABLE		FIELD_PREP(GENMASK(5, 4), 3)
+#define INNO_PRE_PLL_FB_DIV_11_8_MASK		GENMASK(3, 0)
+#define INNO_PRE_PLL_FB_DIV_11_8(x)		FIELD_PREP(GENMASK(3, 0), (x) >> 8)
+
+#define INNO_PRE_PLL_DIV_3			0xa3
+#define INNO_PRE_PLL_FB_DIV_7_0(x)		FIELD_PREP(GENMASK(7, 0), x)
+
+#define INNO_PRE_PLL_TMDSCLK_DIV		0xa4
+#define INNO_PRE_PLL_TMDSCLK_DIV_C(x)		FIELD_PREP(GENMASK(1, 0), x)
+#define INNO_PRE_PLL_TMDSCLK_DIV_B(x)		FIELD_PREP(GENMASK(3, 2), x)
+#define INNO_PRE_PLL_TMDSCLK_DIV_A(x)		FIELD_PREP(GENMASK(5, 4), x)
+
+#define INNO_PCLK_DIV_AB			0xa5
+#define INNO_PCLK_DIV_B_MASK			GENMASK(6, 5)
+#define INNO_PCLK_DIV_B(x)			FIELD_PREP(GENMASK(6, 5), x)
+#define INNO_PCLK_DIV_A_MASK			GENMASK(4, 0)
+#define INNO_PCLK_DIV_A(x)			FIELD_PREP(GENMASK(4, 0), x)
+
+#define INNO_PCLK_DIV_CD			0xa6
+#define INNO_PCLK_DIV_C(x)			FIELD_PREP(GENMASK(6, 5), x)
+#define INNO_PCLK_DIV_D_MASK			GENMASK(4, 0)
+#define INNO_PCLK_DIV_D(x)			FIELD_PREP(GENMASK(4, 0), x)
+
+#define INNO_PRE_PLL_LOCK_STATUS		0xa9
+#define INNO_PRE_PLL_LOCK			BIT(0)
+
+#define INNO_PRE_PLL_FRAC_DIV_23_16		0xd1
+#define INNO_PRE_PLL_FRAC_DIV_15_8		0xd2
+#define INNO_PRE_PLL_FRAC_DIV_7_0		0xd3
+#define INNO_PRE_PLL_FRAC_DIV(x)		FIELD_PREP(GENMASK(7, 0), x)
+
+#define INNO_FRAC_DIV_WIDTH			24
+
+static unsigned int inno_reg(const struct inno_hdmi_phy_pre_pll *pll,
+			     unsigned int reg)
+{
+	return (pll->offset + reg) * 4;
+}
+
+static void inno_write(const struct inno_hdmi_phy_pre_pll *pll,
+		       unsigned int reg, u8 val)
+{
+	regmap_write(pll->regmap, inno_reg(pll, reg), val);
+}
+
+static u8 inno_read(const struct inno_hdmi_phy_pre_pll *pll, unsigned int reg)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(pll->regmap, inno_reg(pll, reg), &val);
+	if (ret)
+		return 0;
+
+	return val;
+}
+
+static void inno_update_bits(const struct inno_hdmi_phy_pre_pll *pll,
+			     unsigned int reg, u8 mask, u8 val)
+{
+	regmap_update_bits(pll->regmap, inno_reg(pll, reg), mask, val);
+}
+
+/**
+ * inno_hdmi_phy_pre_pll_lookup - find the settings for a pixel clock
+ * @pll: pre-PLL instance
+ * @pixclock: requested pixel clock, in Hz
+ * @tmdsclock: requested TMDS clock, in Hz
+ *
+ * Return: the matching table entry, or an ERR_PTR if the PHY cannot generate
+ * the requested combination.
+ */
+const struct inno_hdmi_phy_pre_pll_config *
+inno_hdmi_phy_pre_pll_lookup(const struct inno_hdmi_phy_pre_pll *pll,
+			     unsigned long pixclock, unsigned long tmdsclock)
+{
+	const struct inno_hdmi_phy_pre_pll_config *cfg;
+
+	for (cfg = pll->table; cfg->pixclock != 0; cfg++)
+		if (cfg->pixclock == pixclock && cfg->tmdsclock == tmdsclock)
+			return cfg;
+
+	return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_lookup);
+
+/**
+ * inno_hdmi_phy_pre_pll_determine_rate - clk_ops.determine_rate helper
+ * @pll: pre-PLL instance
+ * @req: rate request, updated with the rate the PHY would produce
+ *
+ * The PHY can only generate the pixel clocks described by its table, so a
+ * request that does not appear there is rejected rather than rounded.
+ *
+ * Return: 0 on success, -EINVAL if the rate is not supported.
+ */
+int inno_hdmi_phy_pre_pll_determine_rate(const struct inno_hdmi_phy_pre_pll *pll,
+					 struct clk_rate_request *req)
+{
+	const struct inno_hdmi_phy_pre_pll_config *cfg;
+	unsigned long rate = rounddown(req->rate, 1000);
+
+	for (cfg = pll->table; cfg->pixclock != 0; cfg++) {
+		if (cfg->pixclock == rate) {
+			req->rate = cfg->pixclock;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_determine_rate);
+
+/**
+ * inno_hdmi_phy_pre_pll_recalc_rate - clk_ops.recalc_rate helper
+ * @pll: pre-PLL instance
+ * @parent_rate: rate of the reference clock, in Hz
+ *
+ * Return: the pixel clock the pre-PLL is currently programmed for, in Hz.
+ */
+unsigned long
+inno_hdmi_phy_pre_pll_recalc_rate(const struct inno_hdmi_phy_pre_pll *pll,
+				  unsigned long parent_rate)
+{
+	u8 nd, no_a, no_b, no_d;
+	unsigned long frac, rate;
+	u64 vco, vco_rate;
+	u16 nf;
+
+	nd = inno_read(pll, INNO_PRE_PLL_DIV_1) & INNO_PRE_PLL_PRE_DIV_MASK;
+	nf = (inno_read(pll, INNO_PRE_PLL_DIV_2) &
+	      INNO_PRE_PLL_FB_DIV_11_8_MASK) << 8;
+	nf |= inno_read(pll, INNO_PRE_PLL_DIV_3);
+	vco = parent_rate * nf;
+
+	if (!(inno_read(pll, INNO_PRE_PLL_DIV_2) &
+	      INNO_PRE_PLL_FRAC_DIV_DISABLE)) {
+		frac = inno_read(pll, INNO_PRE_PLL_FRAC_DIV_7_0) |
+		       (inno_read(pll, INNO_PRE_PLL_FRAC_DIV_15_8) << 8) |
+		       (inno_read(pll, INNO_PRE_PLL_FRAC_DIV_23_16) << 16);
+		vco += DIV_ROUND_CLOSEST(parent_rate * frac,
+					 BIT(INNO_FRAC_DIV_WIDTH));
+	}
+
+	vco_rate = vco;
+
+	if (inno_read(pll, INNO_PRE_PLL_CONTROL) & INNO_PCLK_VCO_DIV_5_MASK) {
+		do_div(vco, nd * 5);
+	} else {
+		no_a = inno_read(pll, INNO_PCLK_DIV_AB) & INNO_PCLK_DIV_A_MASK;
+		no_b = FIELD_GET(INNO_PCLK_DIV_B_MASK,
+				 inno_read(pll, INNO_PCLK_DIV_AB)) + 2;
+		no_d = inno_read(pll, INNO_PCLK_DIV_CD) & INNO_PCLK_DIV_D_MASK;
+
+		do_div(vco, nd * (no_a == 1 ? no_b : no_a) * no_d * 2);
+	}
+
+	rate = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000;
+
+	/* The VCO is only in spec between 1.4 and 3.2 GHz, so log it. */
+	dev_dbg(pll->dev, "%s rate %lu vco %llu\n", __func__, rate, vco_rate);
+
+	return rate;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_recalc_rate);
+
+/**
+ * inno_hdmi_phy_pre_pll_configure - program the pre-PLL dividers
+ * @pll: pre-PLL instance
+ * @cfg: settings to program
+ *
+ * The caller is responsible for powering the pre-PLL down beforehand and back
+ * up afterwards, and for waiting for it to lock.
+ */
+void inno_hdmi_phy_pre_pll_configure(const struct inno_hdmi_phy_pre_pll *pll,
+				     const struct inno_hdmi_phy_pre_pll_config *cfg)
+{
+	u8 val;
+
+	inno_update_bits(pll, INNO_PRE_PLL_CONTROL, INNO_PCLK_VCO_DIV_5_MASK,
+			 INNO_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
+	inno_write(pll, INNO_PRE_PLL_DIV_1, INNO_PRE_PLL_PRE_DIV(cfg->prediv));
+
+	val = INNO_SPREAD_SPECTRUM_MOD_DISABLE;
+	if (!cfg->fracdiv)
+		val |= INNO_PRE_PLL_FRAC_DIV_DISABLE;
+
+	inno_write(pll, INNO_PRE_PLL_DIV_2,
+		   INNO_PRE_PLL_FB_DIV_11_8(cfg->fbdiv) | val);
+	inno_write(pll, INNO_PRE_PLL_DIV_3,
+		   INNO_PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
+
+	inno_write(pll, INNO_PCLK_DIV_AB,
+		   INNO_PCLK_DIV_A(cfg->pclk_div_a) |
+		   INNO_PCLK_DIV_B(cfg->pclk_div_b));
+	inno_write(pll, INNO_PCLK_DIV_CD,
+		   INNO_PCLK_DIV_C(cfg->pclk_div_c) |
+		   INNO_PCLK_DIV_D(cfg->pclk_div_d));
+
+	inno_write(pll, INNO_PRE_PLL_TMDSCLK_DIV,
+		   INNO_PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
+		   INNO_PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b) |
+		   INNO_PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c));
+
+	inno_write(pll, INNO_PRE_PLL_FRAC_DIV_7_0,
+		   INNO_PRE_PLL_FRAC_DIV(cfg->fracdiv));
+	inno_write(pll, INNO_PRE_PLL_FRAC_DIV_15_8,
+		   INNO_PRE_PLL_FRAC_DIV(cfg->fracdiv >> 8));
+	inno_write(pll, INNO_PRE_PLL_FRAC_DIV_23_16,
+		   INNO_PRE_PLL_FRAC_DIV(cfg->fracdiv >> 16));
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_configure);
+
+/**
+ * inno_hdmi_phy_pre_pll_power_down - power the pre-PLL down or up
+ * @pll: pre-PLL instance
+ * @power_down: true to power down, false to power up
+ */
+void inno_hdmi_phy_pre_pll_power_down(const struct inno_hdmi_phy_pre_pll *pll,
+				      bool power_down)
+{
+	inno_update_bits(pll, INNO_PRE_PLL_CONTROL, INNO_PRE_PLL_POWER_DOWN,
+			 power_down ? INNO_PRE_PLL_POWER_DOWN : 0);
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_power_down);
+
+/**
+ * inno_hdmi_phy_pre_pll_is_locked - report whether the pre-PLL has locked
+ * @pll: pre-PLL instance
+ */
+bool inno_hdmi_phy_pre_pll_is_locked(const struct inno_hdmi_phy_pre_pll *pll)
+{
+	return inno_read(pll, INNO_PRE_PLL_LOCK_STATUS) & INNO_PRE_PLL_LOCK;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_is_locked);
+
+/**
+ * inno_hdmi_phy_pre_pll_wait_locked - wait for the pre-PLL to lock
+ * @pll: pre-PLL instance
+ * @timeout_us: how long to wait, in microseconds
+ *
+ * Return: 0 once locked, -ETIMEDOUT otherwise.
+ */
+int inno_hdmi_phy_pre_pll_wait_locked(const struct inno_hdmi_phy_pre_pll *pll,
+				      unsigned int timeout_us)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(pll->regmap,
+					inno_reg(pll, INNO_PRE_PLL_LOCK_STATUS),
+					val, val & INNO_PRE_PLL_LOCK,
+					1000, timeout_us);
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_phy_pre_pll_wait_locked);
+
+MODULE_DESCRIPTION("Innosilicon HDMI PHY common helpers");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/phy/inno-hdmi-phy.h b/include/linux/phy/inno-hdmi-phy.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f243d883ea94f385b167fffd602e70d3e1a3759
--- /dev/null
+++ b/include/linux/phy/inno-hdmi-phy.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Common helpers for the Innosilicon HDMI PHY.
+ *
+ * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Author: Zheng Yang <zhengyang@rock-chips.com>
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ *
+ * Derived from drivers/phy/rockchip/phy-rockchip-inno-hdmi.c.
+ */
+
+#ifndef __PHY_INNO_HDMI_H__
+#define __PHY_INNO_HDMI_H__
+
+#include <linux/types.h>
+
+struct clk_rate_request;
+struct regmap;
+
+/**
+ * struct inno_hdmi_phy_pre_pll_config - pre-PLL settings for one pixel clock
+ * @pixclock: pixel clock this entry describes, in Hz
+ * @tmdsclock: TMDS clock this entry describes, in Hz
+ * @prediv: pre-PLL reference divider
+ * @fbdiv: pre-PLL feedback divider
+ * @tmds_div_a: TMDS clock divider A
+ * @tmds_div_b: TMDS clock divider B
+ * @tmds_div_c: TMDS clock divider C
+ * @pclk_div_a: pixel clock divider A
+ * @pclk_div_b: pixel clock divider B
+ * @pclk_div_c: pixel clock divider C
+ * @pclk_div_d: pixel clock divider D
+ * @vco_div_5_en: divide the VCO output by five
+ * @fracdiv: pre-PLL fractional divider, zero to use integer mode
+ */
+struct inno_hdmi_phy_pre_pll_config {
+	unsigned long pixclock;
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 tmds_div_a;
+	u8 tmds_div_b;
+	u8 tmds_div_c;
+	u8 pclk_div_a;
+	u8 pclk_div_b;
+	u8 pclk_div_c;
+	u8 pclk_div_d;
+	u8 vco_div_5_en;
+	u32 fracdiv;
+};
+
+/**
+ * struct inno_hdmi_phy_pre_pll - a pre-PLL instance
+ * @regmap: register map covering the PHY registers
+ * @offset: offset of the PHY register block, in units of the register index.
+ *	    Zero where the PHY block starts the register space, 0x100 on the
+ *	    StarFive JH7110 where it is preceded by the HDMI controller.
+ * @table: pixel clock table, terminated by an entry with a zero pixclock
+ */
+struct inno_hdmi_phy_pre_pll {
+	struct device *dev;
+	struct regmap *regmap;
+	unsigned int offset;
+	const struct inno_hdmi_phy_pre_pll_config *table;
+};
+
+const struct inno_hdmi_phy_pre_pll_config *
+inno_hdmi_phy_pre_pll_lookup(const struct inno_hdmi_phy_pre_pll *pll,
+			     unsigned long pixclock, unsigned long tmdsclock);
+int inno_hdmi_phy_pre_pll_determine_rate(const struct inno_hdmi_phy_pre_pll *pll,
+					 struct clk_rate_request *req);
+unsigned long
+inno_hdmi_phy_pre_pll_recalc_rate(const struct inno_hdmi_phy_pre_pll *pll,
+				  unsigned long parent_rate);
+void inno_hdmi_phy_pre_pll_configure(const struct inno_hdmi_phy_pre_pll *pll,
+				     const struct inno_hdmi_phy_pre_pll_config *cfg);
+void inno_hdmi_phy_pre_pll_power_down(const struct inno_hdmi_phy_pre_pll *pll,
+				      bool power_down);
+bool inno_hdmi_phy_pre_pll_is_locked(const struct inno_hdmi_phy_pre_pll *pll);
+int inno_hdmi_phy_pre_pll_wait_locked(const struct inno_hdmi_phy_pre_pll *pll,
+				      unsigned int timeout_us);
+
+#endif /* __PHY_INNO_HDMI_H__ */

-- 
2.34.1


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

* [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon PHY helpers
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (14 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:59     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver Michal Wilczynski
                     ` (4 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The RK3328 pre-PLL programming, its table lookup and its recalc_rate and
determine_rate implementations are the generic Innosilicon ones, so drop
the local copies and use the shared helpers instead.

The RK3228 pre-PLL sits at different register addresses, so it keeps its
own register level code and only shares the table lookup.

The now unused RK3328 pre-PLL register macros go with it, as does the
local pre-PLL config lookup wrapper; both call sites already have the
TMDS clock to hand and call inno_hdmi_phy_pre_pll_lookup() directly.

The RK3328 recalc_rate used to log the pre-PLL output as "vco". The
shared helper logs it too, and reports the actual VCO frequency
alongside the rate, which is the value with the 1.4-3.2 GHz constraint.

The register writes, their order and the values written are unchanged.
No functional change intended.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/phy/rockchip/Kconfig                  |   1 +
 drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 165 ++++----------------------
 2 files changed, 26 insertions(+), 140 deletions(-)

diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 14698571b607590dc0e7a29f3b39eb9a5d4e3d25..9732c5be87717dc3ddbea1c60080952336a07af9 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -34,6 +34,7 @@ config PHY_ROCKCHIP_INNO_HDMI
 	depends on COMMON_CLK
 	depends on HAS_IOMEM
 	select GENERIC_PHY
+	select PHY_INNO_HDMI
 	help
 	  Enable this to support the Rockchip Innosilicon HDMI PHY.
 
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index c3d257a0f4a65b4c475ed7246568dd463b46ed86..17229d039132e90d46097d875531eb70385b6687 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -17,6 +17,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/phy/inno-hdmi-phy.h>
 #include <linux/phy/phy.h>
 #include <linux/slab.h>
 
@@ -130,42 +131,17 @@
 #define RK3328_INT_VSS_AGND_ESD_DET			BIT(1)
 #define RK3328_INT_AGND_VSS_ESD_DET			BIT(0)
 /* REG: 0xa0 */
-#define RK3328_PCLK_VCO_DIV_5_MASK			BIT(1)
-#define RK3328_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
 #define RK3328_PRE_PLL_POWER_DOWN			BIT(0)
 /* REG: 0xa1 */
-#define RK3328_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
-#define RK3328_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
 /* REG: 0xa2 */
 /* unset means center spread */
 #define RK3328_SPREAD_SPECTRUM_MOD_DOWN			BIT(7)
 #define RK3328_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)
-#define RK3328_PRE_PLL_FRAC_DIV_DISABLE			UPDATE(3, 5, 4)
-#define RK3328_PRE_PLL_FB_DIV_11_8_MASK			GENMASK(3, 0)
-#define RK3328_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
 /* REG: 0xa3 */
-#define RK3328_PRE_PLL_FB_DIV_7_0(x)			UPDATE(x, 7, 0)
 /* REG: 0xa4*/
-#define RK3328_PRE_PLL_TMDSCLK_DIV_C_MASK		GENMASK(1, 0)
-#define RK3328_PRE_PLL_TMDSCLK_DIV_C(x)			UPDATE(x, 1, 0)
-#define RK3328_PRE_PLL_TMDSCLK_DIV_B_MASK		GENMASK(3, 2)
-#define RK3328_PRE_PLL_TMDSCLK_DIV_B(x)			UPDATE(x, 3, 2)
-#define RK3328_PRE_PLL_TMDSCLK_DIV_A_MASK		GENMASK(5, 4)
-#define RK3328_PRE_PLL_TMDSCLK_DIV_A(x)			UPDATE(x, 5, 4)
 /* REG: 0xa5 */
-#define RK3328_PRE_PLL_PCLK_DIV_B_SHIFT			5
-#define RK3328_PRE_PLL_PCLK_DIV_B_MASK			GENMASK(6, 5)
-#define RK3328_PRE_PLL_PCLK_DIV_B(x)			UPDATE(x, 6, 5)
-#define RK3328_PRE_PLL_PCLK_DIV_A_MASK			GENMASK(4, 0)
-#define RK3328_PRE_PLL_PCLK_DIV_A(x)			UPDATE(x, 4, 0)
 /* REG: 0xa6 */
-#define RK3328_PRE_PLL_PCLK_DIV_C_SHIFT			5
-#define RK3328_PRE_PLL_PCLK_DIV_C_MASK			GENMASK(6, 5)
-#define RK3328_PRE_PLL_PCLK_DIV_C(x)			UPDATE(x, 6, 5)
-#define RK3328_PRE_PLL_PCLK_DIV_D_MASK			GENMASK(4, 0)
-#define RK3328_PRE_PLL_PCLK_DIV_D(x)			UPDATE(x, 4, 0)
 /* REG: 0xa9 */
-#define RK3328_PRE_PLL_LOCK_STATUS			BIT(0)
 /* REG: 0xaa */
 #define RK3328_POST_PLL_POST_DIV_ENABLE			GENMASK(3, 2)
 #define RK3328_POST_PLL_REFCLK_SEL_TMDS			BIT(1)
@@ -218,11 +194,8 @@
 #define RK3328_TMDS_TERM_RESIST_1000			BIT(1)
 #define RK3328_TMDS_TERM_RESIST_2000			BIT(0)
 /* REG: 0xd1 */
-#define RK3328_PRE_PLL_FRAC_DIV_23_16(x)		UPDATE((x) >> 16, 7, 0)
 /* REG: 0xd2 */
-#define RK3328_PRE_PLL_FRAC_DIV_15_8(x)			UPDATE((x) >> 8, 7, 0)
 /* REG: 0xd3 */
-#define RK3328_PRE_PLL_FRAC_DIV_7_0(x)			UPDATE(x, 7, 0)
 
 struct inno_hdmi_phy_drv_data;
 
@@ -240,6 +213,8 @@ struct inno_hdmi_phy {
 	const struct inno_hdmi_phy_drv_data *plat_data;
 	int chip_version;
 
+	struct inno_hdmi_phy_pre_pll pre_pll;
+
 	/* clk provider */
 	struct clk_hw hw;
 	struct clk *phyclk;
@@ -248,21 +223,6 @@ struct inno_hdmi_phy {
 	unsigned long opts_tmds_char_rate;
 };
 
-struct pre_pll_config {
-	unsigned long pixclock;
-	unsigned long tmdsclock;
-	u8 prediv;
-	u16 fbdiv;
-	u8 tmds_div_a;
-	u8 tmds_div_b;
-	u8 tmds_div_c;
-	u8 pclk_div_a;
-	u8 pclk_div_b;
-	u8 pclk_div_c;
-	u8 pclk_div_d;
-	u8 vco_div_5_en;
-	u32 fracdiv;
-};
 
 struct post_pll_config {
 	unsigned long tmdsclock;
@@ -291,7 +251,7 @@ struct inno_hdmi_phy_drv_data {
 	const struct phy_config		*phy_cfg_table;
 };
 
-static const struct pre_pll_config pre_pll_cfg_table[] = {
+static const struct inno_hdmi_phy_pre_pll_config pre_pll_cfg_table[] = {
 	{ 25175000,  25175000,  3,  125, 3, 1, 1,  1, 3, 3,  4, 0, 0xe00000},
 	{ 25175000,  31468750,  1,   41, 0, 3, 3,  1, 3, 3,  4, 0, 0xf5554f},
 	{ 27000000,  27000000,  1,   36, 0, 3, 3,  1, 2, 3,  4, 0,      0x0},
@@ -603,7 +563,7 @@ static irqreturn_t inno_hdmi_phy_rk3328_irq(int irq, void *dev_id)
 static int inno_hdmi_phy_validate(struct phy *phy, enum phy_mode mode,
 				  int submode, union phy_configure_opts *opts)
 {
-	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	const struct inno_hdmi_phy_pre_pll_config *cfg = pre_pll_cfg_table;
 	unsigned long tmdsclock;
 
 	if (!(mode == PHY_MODE_HDMI && submode == PHY_HDMI_MODE_TMDS))
@@ -717,23 +677,6 @@ static const struct phy_ops inno_hdmi_phy_ops = {
 	.validate = inno_hdmi_phy_validate,
 };
 
-static const
-struct pre_pll_config *inno_hdmi_phy_get_pre_pll_cfg(struct inno_hdmi_phy *inno,
-						     unsigned long rate)
-{
-	const struct pre_pll_config *cfg = pre_pll_cfg_table;
-	unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
-
-	for (; cfg->pixclock != 0; cfg++)
-		if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
-			break;
-
-	if (cfg->pixclock == 0)
-		return ERR_PTR(-EINVAL);
-
-	return cfg;
-}
-
 static int inno_hdmi_phy_rk3228_clk_is_prepared(struct clk_hw *hw)
 {
 	struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
@@ -797,7 +740,7 @@ unsigned long inno_hdmi_phy_rk3228_clk_recalc_rate(struct clk_hw *hw,
 static int inno_hdmi_phy_rk3228_clk_determine_rate(struct clk_hw *hw,
 						   struct clk_rate_request *req)
 {
-	const struct pre_pll_config *cfg = pre_pll_cfg_table;
+	const struct inno_hdmi_phy_pre_pll_config *cfg = pre_pll_cfg_table;
 
 	req->rate = (req->rate / 1000) * 1000;
 
@@ -818,7 +761,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
 					     unsigned long parent_rate)
 {
 	struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
-	const struct pre_pll_config *cfg;
+	const struct inno_hdmi_phy_pre_pll_config *cfg;
 	unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
 	u32 v;
 	int ret;
@@ -829,7 +772,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
 	if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
 		return 0;
 
-	cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
+	cfg = inno_hdmi_phy_pre_pll_lookup(&inno->pre_pll, rate, tmdsclock);
 	if (IS_ERR(cfg))
 		return PTR_ERR(cfg);
 
@@ -915,39 +858,11 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
 						   unsigned long parent_rate)
 {
 	struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
-	unsigned long frac;
-	u8 nd, no_a, no_b, no_d;
-	u64 vco;
-	u16 nf;
-
-	nd = inno_read(inno, 0xa1) & RK3328_PRE_PLL_PRE_DIV_MASK;
-	nf = ((inno_read(inno, 0xa2) & RK3328_PRE_PLL_FB_DIV_11_8_MASK) << 8);
-	nf |= inno_read(inno, 0xa3);
-	vco = parent_rate * nf;
-
-	if (!(inno_read(inno, 0xa2) & RK3328_PRE_PLL_FRAC_DIV_DISABLE)) {
-		frac = inno_read(inno, 0xd3) |
-		       (inno_read(inno, 0xd2) << 8) |
-		       (inno_read(inno, 0xd1) << 16);
-		vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
-	}
-
-	if (inno_read(inno, 0xa0) & RK3328_PCLK_VCO_DIV_5_MASK) {
-		do_div(vco, nd * 5);
-	} else {
-		no_a = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_A_MASK;
-		no_b = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_B_MASK;
-		no_b >>= RK3328_PRE_PLL_PCLK_DIV_B_SHIFT;
-		no_b += 2;
-		no_d = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_D_MASK;
-
-		do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
-	}
 
-	inno->pixclock = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000;
+	inno->pixclock = inno_hdmi_phy_pre_pll_recalc_rate(&inno->pre_pll,
+							   parent_rate);
 
-	dev_dbg(inno->dev, "%s rate %lu vco %llu\n",
-		__func__, inno->pixclock, vco);
+	dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock);
 
 	return inno->pixclock;
 }
@@ -955,20 +870,9 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
 static int inno_hdmi_phy_rk3328_clk_determine_rate(struct clk_hw *hw,
 						   struct clk_rate_request *req)
 {
-	const struct pre_pll_config *cfg = pre_pll_cfg_table;
-
-	req->rate = (req->rate / 1000) * 1000;
-
-	for (; cfg->pixclock != 0; cfg++)
-		if (cfg->pixclock == req->rate)
-			break;
-
-	if (cfg->pixclock == 0)
-		return -EINVAL;
-
-	req->rate = cfg->pixclock;
+	struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
 
-	return 0;
+	return inno_hdmi_phy_pre_pll_determine_rate(&inno->pre_pll, req);
 }
 
 static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
@@ -976,9 +880,8 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
 					     unsigned long parent_rate)
 {
 	struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
-	const struct pre_pll_config *cfg;
+	const struct inno_hdmi_phy_pre_pll_config *cfg;
 	unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
-	u32 val;
 	int ret;
 
 	dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
@@ -987,39 +890,15 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
 	if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
 		return 0;
 
-	cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
+	cfg = inno_hdmi_phy_pre_pll_lookup(&inno->pre_pll, rate, tmdsclock);
 	if (IS_ERR(cfg))
 		return PTR_ERR(cfg);
 
-	inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN,
-			 RK3328_PRE_PLL_POWER_DOWN);
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, true);
+	inno_hdmi_phy_pre_pll_configure(&inno->pre_pll, cfg);
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, false);
 
-	/* Configure pre-pll */
-	inno_update_bits(inno, 0xa0, RK3328_PCLK_VCO_DIV_5_MASK,
-			 RK3328_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
-	inno_write(inno, 0xa1, RK3328_PRE_PLL_PRE_DIV(cfg->prediv));
-
-	val = RK3328_SPREAD_SPECTRUM_MOD_DISABLE;
-	if (!cfg->fracdiv)
-		val |= RK3328_PRE_PLL_FRAC_DIV_DISABLE;
-	inno_write(inno, 0xa2, RK3328_PRE_PLL_FB_DIV_11_8(cfg->fbdiv) | val);
-	inno_write(inno, 0xa3, RK3328_PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
-	inno_write(inno, 0xa5, RK3328_PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a) |
-		   RK3328_PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b));
-	inno_write(inno, 0xa6, RK3328_PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
-		   RK3328_PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d));
-	inno_write(inno, 0xa4, RK3328_PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
-		   RK3328_PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
-		   RK3328_PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b));
-	inno_write(inno, 0xd3, RK3328_PRE_PLL_FRAC_DIV_7_0(cfg->fracdiv));
-	inno_write(inno, 0xd2, RK3328_PRE_PLL_FRAC_DIV_15_8(cfg->fracdiv));
-	inno_write(inno, 0xd1, RK3328_PRE_PLL_FRAC_DIV_23_16(cfg->fracdiv));
-
-	inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN, 0);
-
-	/* Wait for Pre-PLL lock */
-	ret = inno_poll(inno, 0xa9, val, val & RK3328_PRE_PLL_LOCK_STATUS,
-			1000, 10000);
+	ret = inno_hdmi_phy_pre_pll_wait_locked(&inno->pre_pll, 10000);
 	if (ret) {
 		dev_err(inno->dev, "Pre-PLL locking failed\n");
 		return ret;
@@ -1418,6 +1297,12 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev)
 	if (IS_ERR(inno->regmap))
 		return PTR_ERR(inno->regmap);
 
+	/* The PHY block starts the register space on these SoCs. */
+	inno->pre_pll.dev = inno->dev;
+	inno->pre_pll.regmap = inno->regmap;
+	inno->pre_pll.offset = 0;
+	inno->pre_pll.table = pre_pll_cfg_table;
+
 	/* only the newer rk3328 hdmiphy has an interrupt */
 	inno->irq = platform_get_irq(pdev, 0);
 	if (inno->irq > 0) {

-- 
2.34.1


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

* [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (15 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon " Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 13:57     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem Michal Wilczynski
                     ` (3 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Add the HDMI PHY driver for the StarFive JH7110.

This driver binds to the starfive,jh7110-inno-hdmi-phy node and
gets its regmap from the parent. It has no dependencies on voutcrg, only
on its reference oscillator (xin24m), which breaks the probe-time
circular dependency.

This driver provides two main functions:
 - Clock Provider: It registers clk_ops to provide the variable pixel
   clock. The .set_rate operation configures the Pre-PLL registers
   (0x1a0+) based on the requested rate.

 - PHY Provider: It registers phy_ops for the controller. The .power_on
   op configures and enables the Post-PLL and other analog blocks (BIAS,
   LDO, Serializer, etc.).

The clock ops reach registers in the window shared with the HDMI
controller, and access to that window is gated by the controller's
system clock, which only the sibling controller driver holds. The clock
framework walks every registered clock regardless of which drivers have
bound, so deliberately do not implement .is_prepared, which lets the
framework fall back to the software prepare count, and return the cached
rate from .recalc_rate whenever the clock is not prepared. Without this,
clk_disable_unused() reads a PHY register with that clock gated and the
resulting stalled bus transaction wedges the CPU hard enough that it
stops responding to NMIs. It is reproducible by building the bridge
driver as a module so that the controller has not bound by then.

The JH7110 instantiates the same Innosilicon IP as the Rockchip RK3328,
with the PHY register block shifted by 0x100 because it sits behind the
HDMI controller in the shared register space. The pre-PLL programming
therefore comes from the common Innosilicon helpers; this driver adds
the JH7110 pixel clock table, the post-PLL and the analog configuration.

Co-developed-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 drivers/phy/starfive/Kconfig                |  20 +
 drivers/phy/starfive/Makefile               |   1 +
 drivers/phy/starfive/phy-jh7110-inno-hdmi.c | 579 ++++++++++++++++++++++++++++
 3 files changed, 600 insertions(+)

diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig
index d0cdd7cb4a13de22ff643c89a79d99cce57284d7..1da417ffbf224c13224e42a3ce322e3d0c272824 100644
--- a/drivers/phy/starfive/Kconfig
+++ b/drivers/phy/starfive/Kconfig
@@ -25,6 +25,26 @@ config PHY_STARFIVE_JH7110_DPHY_TX
 	  system. If M is selected, the module will be called
 	  phy-jh7110-dphy-tx.ko.
 
+config PHY_STARFIVE_JH7110_INNO_HDMI
+	tristate "Starfive JH7110 INNO HDMI PHY"
+	select PHY_INNO_HDMI
+	depends on COMMON_CLK
+	select GENERIC_PHY
+	help
+	  This option enables the driver for the analog HDMI PHY (Physical
+	  Layer) on the StarFive JH7110 SoC.
+
+	  This driver binds to a child node of the 'starfive,jh7110-hdmi-subsystem'
+	  parent driver and gets its register map from that parent.
+
+	  It is responsible for two main functions:
+	  1. PHY Provider: It provides standard PHY operations (.power_on,
+	     .power_off) for the HDMI controller (bridge) driver. This
+	     involves configuring the Post-PLL and analog TMDS blocks.
+	  2. Clock Provider: It registers as a clock provider to supply the
+	     variable pixel clock (hdmi_pclk) to the HDMI controller and
+	     the VOUT subsystem, which it generates using the Pre-PLL.
+
 config PHY_STARFIVE_JH7110_PCIE
 	tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support"
 	depends on HAS_IOMEM
diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile
index eedc4a6fec156320c99ac0a0da609083b6a6a695..e7b13f00880b500f933f21b6037384d5c6884e3e 100644
--- a/drivers/phy/starfive/Makefile
+++ b/drivers/phy/starfive/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_PHY_STARFIVE_JH7110_DPHY_RX)	+= phy-jh7110-dphy-rx.o
 obj-$(CONFIG_PHY_STARFIVE_JH7110_DPHY_TX)	+= phy-jh7110-dphy-tx.o
+obj-$(CONFIG_PHY_STARFIVE_JH7110_INNO_HDMI)	+= phy-jh7110-inno-hdmi.o
 obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE)		+= phy-jh7110-pcie.o
 obj-$(CONFIG_PHY_STARFIVE_JH7110_USB)		+= phy-jh7110-usb.o
diff --git a/drivers/phy/starfive/phy-jh7110-inno-hdmi.c b/drivers/phy/starfive/phy-jh7110-inno-hdmi.c
new file mode 100644
index 0000000000000000000000000000000000000000..2575cb5ed624a82dddd485d3ebd83c846b6a3602
--- /dev/null
+++ b/drivers/phy/starfive/phy-jh7110-inno-hdmi.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Author: Zheng Yang <zhengyang@rock-chips.com>
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ *
+ * The register layout and programming sequence derive from
+ * drivers/phy/rockchip/phy-rockchip-inno-hdmi.c; the JH7110 places the
+ * same Innosilicon PHY block at a 0x100 register offset.
+ *
+ * This driver handles the PHY portion of the StarFive Innosilicon HDMI IP,
+ * which is part of a monolithic HDMI block. It provides the variable pixel
+ * clock (from the Pre-PLL) and the PHY operations (for the Post-PLL/analog).
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/phy/inno-hdmi-phy.h>
+#include <linux/phy/phy.h>
+#include <linux/slab.h>
+
+/*
+ * StarFive (JH7110) Innosilicon HDMI PHY Register Definitions
+ */
+
+/* REG: 0x1aa */
+#define STF_INNO_POST_PLL_DIV_1		0x1aa
+#define STF_INNO_POST_PLL_POST_DIV_ENABLE	GENMASK(3, 2)
+#define STF_INNO_POST_PLL_REFCLK_SEL_TMDS	BIT(1)
+#define STF_INNO_POST_PLL_POWER_DOWN		BIT(0)
+
+/* REG: 0x1ab */
+#define STF_INNO_POST_PLL_DIV_2		0x1ab
+#define STF_INNO_POST_PLL_PRE_DIV(x)		FIELD_PREP(GENMASK(5, 0), x)
+#define STF_INNO_POST_PLL_FB_DIV_8(x)		FIELD_PREP(BIT(7), (x) >> 8)
+
+/* REG: 0x1ac */
+#define STF_INNO_POST_PLL_DIV_3		0x1ac
+#define STF_INNO_POST_PLL_FB_DIV_7_0(x)	FIELD_PREP(GENMASK(7, 0), x)
+
+/* REG: 0x1ad */
+#define STF_INNO_POST_PLL_DIV_4		0x1ad
+#define STF_INNO_POST_PLL_POST_DIV_MASK	GENMASK(1, 0)
+
+/* REG: 0x1af */
+#define STF_INNO_POST_PLL_LOCK_STATUS		0x1af
+#define STF_INNO_POST_PLL_LOCK		BIT(0)
+
+/* REG: 0x1b0 */
+#define STF_INNO_BIAS_CONTROL			0x1b0
+#define STF_INNO_BIAS_ENABLE			BIT(2)
+
+/* REG: 0x1b2 */
+#define STF_INNO_TMDS_CONTROL			0x1b2
+#define STF_INNO_TMDS_CLK_DRIVER_EN		BIT(3)
+#define STF_INNO_TMDS_D2_DRIVER_EN		BIT(2)
+#define STF_INNO_TMDS_D1_DRIVER_EN		BIT(1)
+#define STF_INNO_TMDS_D0_DRIVER_EN		BIT(0)
+#define STF_INNO_TMDS_DRIVER_ENABLE		(STF_INNO_TMDS_CLK_DRIVER_EN | \
+						 STF_INNO_TMDS_D2_DRIVER_EN | \
+						 STF_INNO_TMDS_D1_DRIVER_EN | \
+						 STF_INNO_TMDS_D0_DRIVER_EN)
+
+/* REG: 0x1b4 */
+#define STF_INNO_LDO_CONTROL			0x1b4
+#define STF_INNO_LDO_ENABLE			(BIT(2) | BIT(1) | BIT(0))
+
+/* REG: 0x1be */
+#define STF_INNO_SERIALIER_CONTROL		0x1be
+#define STF_INNO_SERIALIER_ENABLE		(BIT(6) | BIT(5) | BIT(4) | BIT(0))
+
+/* REG: 0x1cc */
+#define STF_INNO_RX_CONTROL			0x1cc
+#define STF_INNO_RX_ENABLE			(BIT(3) | BIT(2) | BIT(1) | BIT(0))
+
+/*
+ * These tables are copied from the monolithic driver.
+ * They match the Rockchip PHY driver tables.
+ */
+
+struct post_pll_config {
+	unsigned long tmdsclock;
+	u8 prediv;
+	u16 fbdiv;
+	u8 postdiv;
+	u8 post_div_en;
+};
+
+static const struct inno_hdmi_phy_pre_pll_config pre_pll_cfg_table[] = {
+	{ 25175000, 25175000, 1, 100, 2, 3, 3, 12, 3, 3, 4, 0, 0xF55555 },
+	{ 25200000, 25200000, 1, 100, 2, 3, 3, 12, 3, 3, 4, 0, 0 },
+	{ 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0 },
+	{ 27027000, 27027000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0x170A3D },
+	{ 28320000, 28320000, 1, 28, 2, 1, 1, 3, 0, 3, 4, 0, 0x51EB85 },
+	{ 30240000, 30240000, 1, 30, 2, 1, 1, 3, 0, 3, 4, 0, 0x3D70A3 },
+	{ 31500000, 31500000, 1, 31, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 33750000, 33750000, 1, 33, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 36000000, 36000000, 1, 36, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0 },
+	{ 46970000, 46970000, 1, 46, 2, 1, 1, 3, 0, 3, 4, 0, 0xF851EB },
+	{ 49500000, 49500000, 1, 49, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 49000000, 49000000, 1, 49, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 50000000, 50000000, 1, 50, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 54000000, 54000000, 1, 54, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 54054000, 54054000, 1, 54, 2, 1, 1, 3, 0, 3, 4, 0, 0x0DD2F1 },
+	{ 57284000, 57284000, 1, 57, 2, 1, 1, 3, 0, 3, 4, 0, 0x48B439 },
+	{ 58230000, 58230000, 1, 58, 2, 1, 1, 3, 0, 3, 4, 0, 0x3AE147 },
+	{ 59341000, 59341000, 1, 59, 2, 1, 1, 3, 0, 3, 4, 0, 0x574BC6 },
+	{ 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0 },
+	{ 65000000, 65000000, 1, 130, 2, 2, 2, 12, 0, 2, 2, 0, 0 },
+	{ 68250000, 68250000, 1, 68, 2, 1, 1, 3, 0, 3, 4, 0, 0x3FFFFF },
+	{ 71000000, 71000000, 1, 71, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B },
+	{ 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0 },
+	{ 75000000, 75000000, 1, 75, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 78750000, 78750000, 1, 78, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 79500000, 79500000, 1, 79, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 83500000, 83500000, 2, 167, 2, 1, 1, 1, 0, 0, 6, 0, 0 },
+	{ 83500000, 104375000, 1, 104, 2, 1, 1, 1, 1, 0, 5, 0, 0x600000 },
+	{ 85500000, 85500000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 85750000, 85750000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 85800000, 85800000, 1, 85, 2, 1, 1, 3, 0, 3, 4, 0, 0xCCCCCC },
+	{ 88750000, 88750000, 1, 88, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 89910000, 89910000, 1, 89, 2, 1, 1, 3, 0, 3, 4, 0, 0xE8F5C1 },
+	{ 90000000, 90000000, 1, 90, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 101000000, 101000000, 1, 101, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 102250000, 102250000, 1, 102, 2, 1, 1, 3, 0, 3, 4, 0, 0x3FFFFF },
+	{ 106500000, 106500000, 1, 106, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 108000000, 108000000, 1, 90, 3, 0, 0, 5, 0, 2, 2, 0, 0 },
+	{ 119000000, 119000000, 1, 119, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 131481000, 131481000, 1, 131, 2, 1, 1, 3, 0, 3, 4, 0, 0x7B22D1 },
+	{ 135000000, 135000000, 1, 135, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 136750000, 136750000, 1, 136, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 147180000, 147180000, 1, 147, 2, 1, 1, 3, 0, 3, 4, 0, 0x2E147A },
+	{ 148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B },
+	{ 148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0 },
+	{ 154000000, 154000000, 1, 154, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 156000000, 156000000, 1, 156, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 157000000, 157000000, 1, 157, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 162000000, 162000000, 1, 162, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 174250000, 174250000, 1, 145, 3, 0, 0, 5, 0, 2, 2, 0, 0x355555 },
+	{ 174500000, 174500000, 1, 174, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 174570000, 174570000, 1, 174, 2, 1, 1, 3, 0, 3, 4, 0, 0x91EB84 },
+	{ 175500000, 175500000, 1, 175, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 185590000, 185590000, 1, 185, 2, 1, 1, 3, 0, 3, 4, 0, 0x970A3C },
+	{ 187000000, 187000000, 1, 187, 2, 1, 1, 3, 0, 3, 4, 0, 0 },
+	{ 235690000, 235690000, 1, 235, 2, 1, 1, 3, 0, 3, 4, 0, 0xB0A3D6 },
+	{ 241500000, 241500000, 1, 161, 1, 1, 1, 4, 0, 2, 2, 0, 0 },
+	{ 241700000, 241700000, 1, 241, 2, 1, 1, 3, 0, 3, 4, 0, 0xB33332 },
+	{ 262750000, 262750000, 1, 262, 2, 1, 1, 3, 0, 3, 4, 0, 0xCFFFFF },
+	{ 296500000, 296500000, 1, 296, 2, 1, 1, 3, 0, 3, 4, 0, 0x7FFFFF },
+	{ 296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B },
+	{ 297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0 },
+	{ 594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+	{ 25200000, 1, 80, 13, 3 },
+	{ 27000000, 1, 40, 11, 3 },
+	{ 27027000, 1, 40, 11, 3 },
+	{ 33750000, 1, 40, 11, 3 },
+	{ 49000000, 1, 20, 1, 3 },
+	{ 65000000, 1, 20, 1, 3 },
+	{ 74250000, 1, 20, 1, 3 },
+	{ 88750000, 1, 20, 1, 3 },
+	{ 108000000, 1, 20, 1, 3 },
+	{ 148500000, 1, 20, 1, 3 },
+	{ 162000000, 1, 20, 1, 3 },
+	{ 174250000, 1, 20, 1, 3 },
+	{ 187000000, 1, 20, 1, 3 },
+	{ 241700000, 1, 20, 1, 3 },
+	{ 297000000, 4, 20, 0, 0 },
+	{ 594000000, 4, 20, 0, 0 }, /* postpll_postdiv_en = 0 */
+	{ /* sentinel */ }
+};
+
+struct starfive_hdmi_phy {
+	struct device *dev;
+	struct regmap *regmap;
+	struct phy *phy;
+	struct clk *refoclk;
+
+	struct inno_hdmi_phy_pre_pll pre_pll;
+
+	struct clk_hw hw;
+	struct clk *phyclk;
+	unsigned long pixclock;
+	unsigned long tmdsclock;
+};
+
+static inline void inno_write(struct starfive_hdmi_phy *inno, u32 reg, u8 val)
+{
+	regmap_write(inno->regmap, reg * 4, val);
+}
+
+static inline u8 inno_read(struct starfive_hdmi_phy *inno, u32 reg)
+{
+	u32 val;
+
+	regmap_read(inno->regmap, reg * 4, &val);
+	return val;
+}
+
+static inline void inno_update_bits(struct starfive_hdmi_phy *inno, u16 reg,
+				    u8 mask, u8 val)
+{
+	regmap_update_bits(inno->regmap, reg * 4, mask, val);
+}
+
+#define inno_poll(inno, reg, val, cond, sleep_us, timeout_us) \
+	regmap_read_poll_timeout((inno)->regmap, (reg) * 4, val, cond, \
+				 sleep_us, timeout_us)
+
+static inline struct starfive_hdmi_phy *to_starfive_hdmi_phy(struct clk_hw *hw)
+{
+	return container_of(hw, struct starfive_hdmi_phy, hw);
+}
+
+static int starfive_hdmi_phy_clk_prepare(struct clk_hw *hw)
+{
+	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
+	int ret;
+
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, false);
+
+	if (inno_hdmi_phy_pre_pll_is_locked(&inno->pre_pll))
+		return 0;
+
+	ret = inno_hdmi_phy_pre_pll_wait_locked(&inno->pre_pll, 100000);
+	if (ret) {
+		dev_err(inno->dev, "Timeout waiting for pre-PLL lock\n");
+		inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void starfive_hdmi_phy_clk_unprepare(struct clk_hw *hw)
+{
+	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
+
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, true);
+	inno->pixclock = 0; /* Invalidate cached rate */
+}
+
+static unsigned long starfive_hdmi_phy_clk_recalc_rate(struct clk_hw *hw,
+						       unsigned long parent_rate)
+{
+	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
+
+	/*
+	 * Reading the PLL back requires the HDMI controller's system clock,
+	 * which the sibling controller driver holds and this driver cannot
+	 * request without creating a probe cycle with voutcrg. The clock
+	 * framework walks every registered clock regardless of which drivers
+	 * have bound, so fall back to the cached rate whenever this clock is
+	 * not prepared; an unclocked access to the shared register window
+	 * wedges the bus hard enough that the CPU stops taking NMIs.
+	 */
+	if (!clk_hw_is_prepared(hw))
+		return inno->pixclock;
+
+	inno->pixclock = inno_hdmi_phy_pre_pll_recalc_rate(&inno->pre_pll,
+							   parent_rate);
+
+	return inno->pixclock;
+}
+
+static int starfive_hdmi_phy_clk_determine_rate(struct clk_hw *hw,
+						struct clk_rate_request *req)
+{
+	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
+
+	return inno_hdmi_phy_pre_pll_determine_rate(&inno->pre_pll, req);
+}
+
+static int starfive_hdmi_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+					  unsigned long parent_rate)
+{
+	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
+	const struct inno_hdmi_phy_pre_pll_config *cfg;
+
+	/*
+	 * The JH7110 only drives 8bpc, so the TMDS clock always matches the
+	 * pixel clock.
+	 */
+	cfg = inno_hdmi_phy_pre_pll_lookup(&inno->pre_pll, rate, rate);
+	if (IS_ERR(cfg))
+		return PTR_ERR(cfg);
+
+	dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
+		__func__, rate, cfg->tmdsclock);
+
+	if (inno->pixclock == rate && inno->tmdsclock == cfg->tmdsclock)
+		return 0;
+
+	inno_update_bits(inno, STF_INNO_BIAS_CONTROL,
+			 STF_INNO_BIAS_ENABLE, STF_INNO_BIAS_ENABLE);
+	inno_write(inno, STF_INNO_RX_CONTROL, STF_INNO_RX_ENABLE);
+
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, true);
+	inno_hdmi_phy_pre_pll_configure(&inno->pre_pll, cfg);
+	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, false);
+
+	inno->pixclock = rate;
+	inno->tmdsclock = cfg->tmdsclock;
+
+	return 0;
+}
+
+static const struct clk_ops starfive_hdmi_phy_clk_ops = {
+	.prepare = starfive_hdmi_phy_clk_prepare,
+	.unprepare = starfive_hdmi_phy_clk_unprepare,
+	.recalc_rate = starfive_hdmi_phy_clk_recalc_rate,
+	.determine_rate = starfive_hdmi_phy_clk_determine_rate,
+	.set_rate = starfive_hdmi_phy_clk_set_rate,
+};
+
+static void starfive_hdmi_phy_power_down(struct starfive_hdmi_phy *inno)
+{
+	inno_write(inno, STF_INNO_TMDS_CONTROL, 0x00);
+	inno_write(inno, STF_INNO_SERIALIER_CONTROL, 0x00);
+	inno_write(inno, STF_INNO_LDO_CONTROL, 0x00);
+	inno_update_bits(inno, STF_INNO_BIAS_CONTROL,
+			 STF_INNO_BIAS_ENABLE, 0x00);
+	inno_write(inno, STF_INNO_RX_CONTROL, 0x00);
+
+	/* Power down Post-PLL */
+	inno_update_bits(inno, STF_INNO_POST_PLL_DIV_1,
+			 STF_INNO_POST_PLL_POWER_DOWN,
+			 STF_INNO_POST_PLL_POWER_DOWN);
+
+	inno->tmdsclock = 0;
+	inno->pixclock = 0;
+}
+
+static int starfive_hdmi_phy_power_on(struct phy *phy)
+{
+	struct starfive_hdmi_phy *inno = phy_get_drvdata(phy);
+	const struct post_pll_config *cfg = post_pll_cfg_table;
+	const struct inno_hdmi_phy_pre_pll_config *pre_cfg;
+	unsigned long tmdsclock;
+	u8 reg_1ad_value;
+	u8 reg_1aa_value;
+
+	u32 v;
+	int ret;
+
+	tmdsclock = clk_get_rate(inno->phyclk);
+	pre_cfg = inno_hdmi_phy_pre_pll_lookup(&inno->pre_pll, tmdsclock,
+					       tmdsclock);
+	if (IS_ERR(pre_cfg)) {
+		dev_err(inno->dev, "no pre-PLL config for current pixel clock\n");
+		return PTR_ERR(pre_cfg);
+	}
+	tmdsclock = pre_cfg->tmdsclock;
+	inno->tmdsclock = tmdsclock;
+
+	inno_update_bits(inno, STF_INNO_BIAS_CONTROL,
+			 STF_INNO_BIAS_ENABLE, STF_INNO_BIAS_ENABLE);
+	inno_write(inno, STF_INNO_RX_CONTROL, STF_INNO_RX_ENABLE);
+
+	/* Find Post-PLL config */
+	for (; cfg->tmdsclock != 0; cfg++)
+		if (tmdsclock <= cfg->tmdsclock)
+			break;
+
+	if (cfg->tmdsclock == 0) {
+		dev_err(inno->dev, "Failed to find Post-PLL config\n");
+		ret = -EINVAL;
+		goto err_power_down;
+	}
+	dev_dbg(inno->dev, "Inno HDMI PHY Power On: pixclk %lu, tmdsclk %lu\n",
+		inno->pixclock, tmdsclock);
+
+	reg_1ad_value = cfg->post_div_en ? cfg->postdiv : 0x00;
+	reg_1aa_value = cfg->post_div_en ? 0x0e : 0x02;
+
+	/*
+	 * Pre-PLL is already prepared and running at inno->pixclock
+	 * via the clk_set_rate and prepare calls from the controller/bridge.
+	 * Now, configure and enable the Post-PLL and TMDS outputs.
+	 */
+
+	inno_write(inno, STF_INNO_POST_PLL_DIV_2,
+		   STF_INNO_POST_PLL_PRE_DIV(cfg->prediv));
+	inno_write(inno, STF_INNO_POST_PLL_DIV_3, cfg->fbdiv & 0xff);
+	inno_write(inno, STF_INNO_POST_PLL_DIV_4, reg_1ad_value);
+
+	/* Power up Post-PLL */
+	inno_write(inno, STF_INNO_POST_PLL_DIV_1, reg_1aa_value);
+
+	/* Wait for post PLL lock */
+	ret = inno_poll(inno, STF_INNO_POST_PLL_LOCK_STATUS, v,
+			v & STF_INNO_POST_PLL_LOCK, 1000, 100000);
+	if (ret) {
+		dev_err(inno->dev, "Post-PLL locking failed\n");
+		goto err_power_down;
+	}
+
+	inno_write(inno, STF_INNO_LDO_CONTROL, STF_INNO_LDO_ENABLE);
+	inno_write(inno, STF_INNO_SERIALIER_CONTROL,
+		   STF_INNO_SERIALIER_ENABLE);
+	inno_write(inno, STF_INNO_TMDS_CONTROL, 0x8f);
+
+	return 0;
+
+	/*
+	 * The PHY core leaves power_count at zero when .power_on fails and
+	 * never calls .power_off, so undo the blocks enabled above here.
+	 */
+err_power_down:
+	starfive_hdmi_phy_power_down(inno);
+	return ret;
+}
+
+static int starfive_hdmi_phy_power_off(struct phy *phy)
+{
+	struct starfive_hdmi_phy *inno = phy_get_drvdata(phy);
+
+	dev_dbg(inno->dev, "Inno HDMI PHY Power Off\n");
+
+	starfive_hdmi_phy_power_down(inno);
+
+	return 0;
+}
+
+static const struct phy_ops starfive_hdmi_phy_ops = {
+	.owner = THIS_MODULE,
+	.power_on = starfive_hdmi_phy_power_on,
+	.power_off = starfive_hdmi_phy_power_off,
+};
+
+static int starfive_hdmi_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device *parent = dev->parent;
+	struct starfive_hdmi_phy *inno;
+	struct phy_provider *phy_provider;
+	struct regmap *regmap;
+	struct clk_init_data init = {};
+	const char *refoclk_name;
+	int ret;
+
+	inno = devm_kzalloc(dev, sizeof(*inno), GFP_KERNEL);
+	if (!inno)
+		return -ENOMEM;
+
+	inno->dev = dev;
+
+	/* Get the regmap from the parent device */
+	regmap = dev_get_regmap(parent, NULL);
+	if (!regmap) {
+		dev_err(dev, "Failed to get parent regmap\n");
+		return -ENODEV;
+	}
+	inno->regmap = regmap;
+
+	/*
+	 * The PHY block sits behind the HDMI controller in the shared register
+	 * space, so the common pre-PLL helpers need a 0x100 register offset.
+	 */
+	inno->pre_pll.dev = dev;
+	inno->pre_pll.regmap = regmap;
+	inno->pre_pll.offset = 0x100;
+	inno->pre_pll.table = pre_pll_cfg_table;
+
+	/* Get the input reference clock */
+	inno->refoclk = devm_clk_get(inno->dev, NULL);
+	if (IS_ERR(inno->refoclk)) {
+		ret = PTR_ERR(inno->refoclk);
+		dev_err(inno->dev, "failed to get oscillator-ref clock: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* We must prepare/enable refoclk here so .set_rate/.recalc_rate work */
+	ret = clk_prepare_enable(inno->refoclk);
+	if (ret) {
+		dev_err(dev, "Failed to enable refoclk: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, inno);
+
+	/* Initialize and register the clock provider */
+	refoclk_name = __clk_get_name(inno->refoclk);
+	init.parent_names = &refoclk_name;
+	init.num_parents = 1;
+	init.flags = 0;
+	init.name = "hdmi_pclk";
+	init.ops = &starfive_hdmi_phy_clk_ops;
+
+	inno->hw.init = &init;
+	inno->phyclk = devm_clk_register(dev, &inno->hw);
+	if (IS_ERR(inno->phyclk)) {
+		ret = PTR_ERR(inno->phyclk);
+		dev_err(dev, "Failed to register clock provider: %d\n", ret);
+		goto err_disable_refoclk;
+	}
+
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get, inno->phyclk);
+	if (ret) {
+		dev_err(dev, "Failed to add clock provider: %d\n", ret);
+		goto err_disable_refoclk;
+	}
+
+	ret = clk_set_rate(inno->phyclk, 297000000);
+	if (ret) {
+		dev_err(dev, "Failed to set default rate: %d\n", ret);
+		goto err_del_clk_provider;
+	}
+
+	/* Create and register the PHY provider */
+	inno->phy = devm_phy_create(inno->dev, NULL, &starfive_hdmi_phy_ops);
+	if (IS_ERR(inno->phy)) {
+		ret = PTR_ERR(inno->phy);
+		dev_err(inno->dev, "failed to create HDMI PHY: %d\n", ret);
+		goto err_del_clk_provider;
+	}
+
+	phy_set_drvdata(inno->phy, inno);
+
+	phy_provider = devm_of_phy_provider_register(inno->dev,
+						     of_phy_simple_xlate);
+	ret = PTR_ERR_OR_ZERO(phy_provider);
+	if (ret)
+		goto err_del_clk_provider;
+
+	return 0;
+
+err_del_clk_provider:
+	of_clk_del_provider(dev->of_node);
+err_disable_refoclk:
+	clk_disable_unprepare(inno->refoclk);
+	return ret;
+}
+
+static void starfive_hdmi_phy_remove(struct platform_device *pdev)
+{
+	struct starfive_hdmi_phy *inno = platform_get_drvdata(pdev);
+
+	of_clk_del_provider(pdev->dev.of_node);
+	clk_disable_unprepare(inno->refoclk);
+}
+
+static const struct of_device_id starfive_hdmi_phy_of_match[] = {
+	{ .compatible = "starfive,jh7110-inno-hdmi-phy", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, starfive_hdmi_phy_of_match);
+
+static struct platform_driver starfive_hdmi_phy_driver = {
+	.probe = starfive_hdmi_phy_probe,
+	.remove = starfive_hdmi_phy_remove,
+	.driver = {
+		.name = "starfive-inno-hdmi-phy",
+		.of_match_table = starfive_hdmi_phy_of_match,
+	},
+};
+module_platform_driver(starfive_hdmi_phy_driver);
+
+MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
+MODULE_DESCRIPTION("StarFive JH7110 Innosilicon HDMI PHY Driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


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

* [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (16 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 14:03     ` sashiko-bot
  2026-09-04 13:27   ` [PATCH v3 19/19] MAINTAINERS: Add StarFive JH7110 display subsystem entry Michal Wilczynski
                     ` (2 subsequent siblings)
  20 siblings, 1 reply; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

Activate the display subsystem by describing the video output subsystem,
which the documentation calls dom_vout_top, as a bus node covering the
whole 0x29400000 region, with dc8200, the HDMI block, the video output
system controller and voutcrg as its children.

The subsystem node owns the resources its children share: the NoC display
bus clock and reset that gate access to every register in the region, and
the PD_VOUT power domain. Modelling it this way keeps a shared gate owned
by the block that shares it rather than by whichever consumer happens to
probe first, and lets dc8200 inherit dma-noncoherent for framebuffer DMA
from the bus it sits on.

The monolithic hdmi node is in turn modelled as a container with hdmi_phy
and hdmi_controller children. This lets the PHY (clock provider,
depending only on xin24m) and the controller (clock consumer) probe
independently, breaking the circular clock dependency with voutcrg.

voutcrg consumes the pixel clock from the &hdmi_phy node instead of the
old fixed-clock, and dc8200 gets its pixel clocks from voutcrg's MUXes.
The old, incorrect hdmitx0-pixel-clock fixed-clock node is removed.

The HDMI controller gets a phandle to the video output system controller,
which holds the mux selecting the DC8200 output that drives the HDMI
transmitter.

CMA is enlarged and constrained to memory the display controller can
address, because the default pool is too small for its framebuffers.

Co-developed-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Dominique Belhachemi <db@domibel.de>
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 arch/riscv/boot/dts/starfive/jh7110-common.dtsi | 121 +++++++++++++++++++++++-
 arch/riscv/boot/dts/starfive/jh7110.dtsi        | 102 ++++++++++++++++----
 2 files changed, 200 insertions(+), 23 deletions(-)

diff --git a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
index a7a1c09a2c9075711f3a214a49618911fdc7b421..5105a7c29120ddec4770b6a712563b6ee421768e 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
@@ -33,6 +33,27 @@ memory@40000000 {
 		bootph-pre-ram;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/*
+		 * The display controller allocates its framebuffers from CMA,
+		 * and the default pool is too small for them. Keep the pool
+		 * inside the range the controller can address; it fits in the
+		 * memory every VisionFive 2 variant has.
+		 */
+		linux,cma {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0x0 0x20000000>;
+			alignment = <0x0 0x1000>;
+			alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
+			linux,cma-default;
+		};
+	};
+
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
@@ -73,12 +94,47 @@ codec {
 			};
 		};
 	};
+
+	hdmi-connector {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&hdmi_out_con>;
+			};
+		};
+	};
 };
 
 &cpus {
 	timebase-frequency = <4000000>;
 };
 
+&dc8200 {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		dpu_port0: port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dpu_out_dpi0: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&hdmi_in>;
+			};
+		};
+
+		dpu_port1: port@1 {
+			reg = <1>;
+		};
+	};
+};
+
 &dvp_clk {
 	clock-frequency = <74250000>;
 };
@@ -99,8 +155,31 @@ &gmac1_rmii_refin {
 	clock-frequency = <50000000>;
 };
 
-&hdmitx0_pixelclk {
-	clock-frequency = <297000000>;
+&hdmi_controller {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_pins>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			hdmi_in: endpoint {
+				remote-endpoint = <&dpu_out_dpi0>;
+			};
+		};
+
+		hdmi_out_port: port@1 {
+			reg = <1>;
+
+			hdmi_out_con: endpoint {
+				remote-endpoint = <&hdmi_con_in>;
+			};
+		};
+	};
 };
 
 &i2srx_bclk_ext {
@@ -351,6 +430,40 @@ &syscrg {
 };
 
 &sysgpio {
+	hdmi_pins: hdmi-0 {
+		hdmi-cec-pins {
+			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
+					      GPOEN_SYS_HDMI_CEC_SDA,
+					      GPI_SYS_HDMI_CEC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-hpd-pins {
+			pinmux = <GPIOMUX(15, GPOUT_HIGH,
+					      GPOEN_ENABLE,
+					      GPI_SYS_HDMI_HPD)>;
+			input-enable;
+			bias-disable; /* external pull-up */
+		};
+
+		hdmi-scl-pins {
+			pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
+					     GPOEN_SYS_HDMI_DDC_SCL,
+					     GPI_SYS_HDMI_DDC_SCL)>;
+			input-enable;
+			bias-pull-up;
+		};
+
+		hdmi-sda-pins {
+			pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
+					     GPOEN_SYS_HDMI_DDC_SDA,
+					     GPI_SYS_HDMI_DDC_SDA)>;
+			input-enable;
+			bias-pull-up;
+		};
+	};
+
 	i2c0_pins: i2c0-0 {
 		i2c-pins {
 			pinmux = <GPIOMUX(57, GPOUT_LOW,
@@ -629,3 +742,7 @@ &U74_3 {
 &U74_4 {
 	cpu-supply = <&vdd_cpu>;
 };
+
+&voutcrg {
+	status = "okay";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 9c3e4598747eb5541effe697044484715569a285..0f981f4c89586269b987be8591037c9461e04703 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -290,12 +290,6 @@ gmac1_rmii_refin: gmac1-rmii-refin-clock {
 		#clock-cells = <0>;
 	};
 
-	hdmitx0_pixelclk: hdmitx0-pixel-clock {
-		compatible = "fixed-clock";
-		clock-output-names = "hdmitx0_pixelclk";
-		#clock-cells = <0>;
-	};
-
 	i2srx_bclk_ext: i2srx-bclk-ext-clock {
 		compatible = "fixed-clock";
 		clock-output-names = "i2srx_bclk_ext";
@@ -352,6 +346,13 @@ tdm_ext: tdm-ext-clock {
 		#clock-cells = <0>;
 	};
 
+	xin24m: xin24m {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <24000000>;
+		clock-output-names = "xin24m";
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -1199,22 +1200,81 @@ csi_phy: phy@19820000 {
 			#phy-cells = <0>;
 		};
 
-		voutcrg: clock-controller@295c0000 {
-			compatible = "starfive,jh7110-voutcrg";
-			reg = <0x0 0x295c0000 0x0 0x10000>;
-			clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>,
-				 <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>,
-				 <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
-				 <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>,
-				 <&syscrg JH7110_SYSCLK_I2STX0_BCLK>,
-				 <&hdmitx0_pixelclk>;
-			clock-names = "vout_src", "vout_top_ahb",
-				      "vout_top_axi", "vout_top_hdmitx0_mclk",
-				      "i2stx0_bclk", "hdmitx0_pixelclk";
-			resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>;
-			#clock-cells = <1>;
-			#reset-cells = <1>;
+		vout_subsystem: display-subsystem@29400000 {
+			compatible = "starfive,jh7110-vout-subsystem";
+			reg = <0x0 0x29400000 0x0 0x200000>;
+			ranges;
+			#address-cells = <2>;
+			#size-cells = <2>;
+			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>;
+			dma-noncoherent;
 			power-domains = <&pwrc JH7110_PD_VOUT>;
+			resets = <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>;
+
+			dc8200: display@29400000 {
+				compatible = "starfive,jh7110-dc8200", "verisilicon,dc";
+				reg = <0x0 0x29400000 0x0 0x2800>;
+				clocks = <&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
+					 <&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
+					 <&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
+					 <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
+					 <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>;
+				clock-names = "core", "axi", "ahb", "pix0", "pix1";
+				interrupts = <95>;
+				resets = <&voutcrg JH7110_VOUTRST_DC8200_CORE>,
+					 <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
+					 <&voutcrg JH7110_VOUTRST_DC8200_AHB>;
+				reset-names = "core", "axi", "ahb";
+			};
+
+			hdmi_subsystem: hdmi@29590000 {
+				compatible = "starfive,jh7110-hdmi-subsystem";
+				reg = <0x0 0x29590000 0x0 0x4000>;
+
+				hdmi_controller: controller {
+					compatible =
+						"starfive,jh7110-inno-hdmi-controller";
+					clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
+						 <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
+						 <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
+						 <&hdmi_phy>;
+					clock-names = "pclk", "mclk", "bclk", "pixel";
+					interrupts = <99>;
+					phys = <&hdmi_phy>;
+					resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
+					starfive,vout-syscon = <&vout_syscon>;
+				};
+
+				hdmi_phy: phy {
+					compatible = "starfive,jh7110-inno-hdmi-phy";
+					clocks = <&xin24m>;
+					#clock-cells = <0>;
+					#phy-cells = <0>;
+				};
+			};
+
+			vout_syscon: syscon@295b0000 {
+				compatible = "starfive,jh7110-vout-syscon", "syscon";
+				reg = <0x0 0x295b0000 0x0 0x10000>;
+			};
+
+			voutcrg: clock-controller@295c0000 {
+				compatible = "starfive,jh7110-voutcrg";
+				reg = <0x0 0x295c0000 0x0 0x10000>;
+				clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>,
+					 <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>,
+					 <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
+					 <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>,
+					 <&syscrg JH7110_SYSCLK_I2STX0_BCLK>,
+					 <&hdmi_phy>;
+				clock-names = "vout_src", "vout_top_ahb",
+					      "vout_top_axi", "vout_top_hdmitx0_mclk",
+					      "i2stx0_bclk", "hdmitx0_pixelclk";
+				power-domains = <&pwrc JH7110_PD_VOUT>;
+				resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>;
+				#clock-cells = <1>;
+				#reset-cells = <1>;
+			};
 		};
 
 		pcie0: pcie@940000000 {

-- 
2.34.1


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

* [PATCH v3 19/19] MAINTAINERS: Add StarFive JH7110 display subsystem entry
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (17 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem Michal Wilczynski
@ 2026-09-04 13:27   ` Michal Wilczynski
  2026-09-04 15:13   ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Joshua Peisach
  2026-09-05  5:21   ` Maud Spierings
  20 siblings, 0 replies; 41+ messages in thread
From: Michal Wilczynski @ 2026-09-04 13:27 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andrzej Hajda, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng,
	Michal Wilczynski

The JH7110 display subsystem spans a PHY driver, a DRM bridge driver,
a parent device and their bindings, and has no existing maintainer entry.

Add an entry covering them so patches reach someone who can review them.

Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
---
 MAINTAINERS | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c9dfb48e900df71a86d4686d3ec2c..055a6d0f3967c8171a0c6a3f9dde0ea72f166274 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26078,6 +26078,19 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml
 F:	drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
 
+STARFIVE JH7110 DISPLAY SUBSYSTEM
+M:	Michal Wilczynski <m.wilczynski@samsung.com>
+R:	Icenowy Zheng <zhengxingda@iscas.ac.cn>
+S:	Maintained
+F:	Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml
+F:	Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
+F:	Documentation/devicetree/bindings/phy/starfive,jh7110-inno-hdmi-phy.yaml
+F:	Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-vout-subsystem.yaml
+F:	drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
+F:	drivers/phy/starfive/phy-jh7110-inno-hdmi.c
+F:	drivers/soc/starfive/jh7110-hdmi-subsystem.c
+F:	drivers/soc/starfive/jh7110-vout-subsystem.c
+
 STARFIVE JH7110 DPHY RX DRIVER
 M:	Jack Zhu <jack.zhu@starfivetech.com>
 M:	Changhuang Liang <changhuang.liang@starfivetech.com>

-- 
2.34.1


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

* Re: [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon
  2026-09-04 13:27   ` [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon Michal Wilczynski
@ 2026-09-04 13:30     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:30 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: neil.armstrong, linux-phy, mfd, devicetree, olteanv, lee,
	conor+dt, linux-clk, dri-devel, vkoul, robh

> The video output subsystem has its own system controller, documented as
> dom_vout_syscon. Among other things it holds the mux that selects which
> DC8200 output drives the HDMI transmitter, so the HDMI controller needs
> to reach it.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=4


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

* Re: [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy
  2026-09-04 13:27   ` [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy Michal Wilczynski
@ 2026-09-04 13:30     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:30 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: devicetree, linux-clk, robh, linux-phy, mfd, vkoul, conor+dt, lee,
	neil.armstrong, olteanv, dri-devel

> Add the dt-binding for the StarFive JH7110 Innosilicon HDMI PHY.
> 
> This device is a child of the starfive,jh7110-hdmi-subsystem node. It
> functions as both a PHY provider for the controller and as a clock
> provider for the variable pixel clock (hdmi_pclk), which it generates
> from its refoclk.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=1


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

* Re: [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200
  2026-09-04 13:27   ` [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200 Michal Wilczynski
@ 2026-09-04 13:31     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:31 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: conor+dt, linux-phy, neil.armstrong, robh, vkoul, mfd, linux-clk,
	lee, devicetree, olteanv, dri-devel

> The JH7110 embeds a DC8200. The binding requires an SoC specific
> compatible ahead of the generic one, so add the JH7110 to the enum.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=6


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

* Re: [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller
  2026-09-04 13:27   ` [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller Michal Wilczynski
@ 2026-09-04 13:35     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:35 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: dri-devel, linux-clk, mfd, lee, olteanv, neil.armstrong,
	linux-phy, robh, conor+dt, devicetree, vkoul

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The clock names described in the commit message contradict the clock names defined in the schema.
--

commit d8a94c9a1ba333a313d74a23f9f5fb6dcc401472
Author: Michal Wilczynski <m.wilczynski@samsung.com>

dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller

This commit adds the device tree binding schema for the StarFive JH7110
Innosilicon HDMI controller.

> It consumes register access clocks (sys, mclk, bclk) from the voutcrg and
> both the pixel clock (pclk) and the PHY from its hdmi_phy sibling.

[ ... ]

> diff --git a/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml b/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml
> new file mode 100644
> index 0000000000000..ea489a7722d02
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/starfive,jh7110-inno-hdmi-controller.yaml

[ ... ]

> +  clock-names:
> +    items:
> +      - const: pclk
> +      - const: mclk
> +      - const: bclk
> +      - const: pixel

[Severity: Medium]
The clock names described in the commit message contradict the clock names
defined in the schema.

The commit message states that the device consumes register access clocks
"(sys, mclk, bclk)" and the pixel clock "(pclk)". However, the schema defines
the expected clock names as "pclk" for the main clock and "pixel" for the
pixel clock.

Is the commit message inaccurate, or should the schema use "sys" and "pclk"
instead of "pclk" and "pixel" for these respective clocks?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=2

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

* Re: [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem
  2026-09-04 13:27   ` [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem Michal Wilczynski
@ 2026-09-04 13:36     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:36 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: linux-clk, linux-phy, mfd, dri-devel, lee, devicetree,
	neil.armstrong, robh, conor+dt, vkoul, olteanv

> The JH7110 groups its display hardware into a video output subsystem,
> dom_vout_top, covering the DC8200, the HDMI transmitter, the video
> output clock generator and the video output system controller.
> 
> They share one NoC port whose clock and reset gate access to every
> register in the region, and the region sits in the PD_VOUT power
> domain. Describe the subsystem as a bus node so those resources are
> owned by the block that shares them, rather than by whichever consumer
> happens to probe first.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=5


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

* Re: [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem
  2026-09-04 13:27   ` [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem Michal Wilczynski
@ 2026-09-04 13:37     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:37 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: devicetree, conor+dt, lee, olteanv, robh, linux-phy, dri-devel,
	neil.armstrong, linux-clk, mfd, vkoul

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Shared bus clocks and resets are incorrectly assigned to the child controller instead of the parent, which will cause a system bus fault when the PHY child accesses the shared registers.
- [Medium] Splitting a monolithic hardware block into an MFD purely to resolve a software probe ordering issue violates core Device Tree design principles.
- [Medium] The binding requires a bespoke MFD driver for a simple shared register space by omitting standard fallback compatible strings.
--

commit c934e468899df3820b545d81d3b4b4a9ae4b2097
Author: Michal Wilczynski <m.wilczynski@samsung.com>

dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem

Adds a device tree binding for the StarFive JH7110 HDMI subsystem.
The binding creates a multi-function device parent with phy and controller
children to resolve a circular clock dependency.

> diff --git a/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml b/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
> new file mode 100644
> index 0000000000000..bd35f191835a6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/starfive,jh7110-hdmi-subsystem.yaml
> @@ -0,0 +1,95 @@
[ ... ]
> +description:
> +  The StarFive JH7110 HDMI block is a monolithic IP containing both
> +  the digital controller logic and the analog PHY logic in a single
> +  register space. It sits inside the video output subsystem, which owns

[Severity: Medium]
Since the hardware is a single monolithic IP block in a single register
space, does splitting it into a multi-function device parent and child nodes
conflict with the core device tree principle of describing hardware topology
rather than software abstraction limitations?

> +  the NoC bus clock and reset gating this region and the power domain it
> +  belongs to.
> +
> +properties:
> +  compatible:
> +    const: starfive,jh7110-hdmi-subsystem

[Severity: Medium]
Does limiting the compatible property strictly to this string without allowing
standard fallbacks like "simple-mfd" force the creation of a bespoke driver
for a simple shared register space?

> +
> +  reg:
> +    maxItems: 1
[ ... ]
> +required:
> +  - compatible
> +  - reg
> +  - phy
> +  - controller
> +
> +additionalProperties: false

[Severity: High]
With this property set to false, it is impossible for the parent node to
request clocks or resets. In the example below, the module system clock
(JH7110_VOUTCLK_HDMI_TX_SYS) is assigned to the controller child node.

Because the controller depends on the phy child for its pixel clock, the phy
must probe first. When the phy probes and attempts read-modify-write
operations on the shared regmap, will this cause a system bus fault and hard
CPU lockup because the module system clock is not yet enabled by the
controller?

> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/starfive,jh7110-crg.h>
> +    #include <dt-bindings/reset/starfive,jh7110-crg.h>
> +
> +    soc {
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +
> +        hdmi@29590000 {
> +            compatible = "starfive,jh7110-hdmi-subsystem";
> +            reg = <0x29590000 0x4000>;
> +
> +            controller {
> +                compatible = "starfive,jh7110-inno-hdmi-controller";
> +                clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,

[Severity: High]
Can this clock assignment lead to the system bus fault mentioned above? It
seems the parent node should be responsible for enabling the bus clock before
the children probe.

> +                         <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
> +                         <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
> +                         <&hdmi_phy>;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=3

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

* Re: [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver
  2026-09-04 13:27   ` [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver Michal Wilczynski
@ 2026-09-04 13:39     ` Icenowy Zheng
  2026-09-04 13:57     ` sashiko-bot
  1 sibling, 0 replies; 41+ messages in thread
From: Icenowy Zheng @ 2026-09-04 13:39 UTC (permalink / raw)
  To: Michal Wilczynski, Vinod Koul, Neil Armstrong, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall

在 2026-09-04五的 15:27 +0200,Michal Wilczynski写道:
> Add the HDMI controller (bridge) driver for the StarFive JH7110.
> 
> This driver binds to the starfive,jh7110-inno-hdmi-controller node.
> It gets its shared regmap from its parent and its register access,
> module and bus clocks from voutcrg. It consumes the pixel clock and
> the
> 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().
> 
> The DC8200 has two panels, each exposing a DP and a DPI interface,
> and a
> mux in the video output system controller picks which of them drives
> the
> HDMI transmitter. Program that mux from the port graph rather than
> relying on whatever the bootloader left behind, taking the panel from
> the
> remote port number and the interface from the remote endpoint number.
> 
> The generic driver holds the clock it looks up as the register access
> clock enabled for its lifetime, and derives the DDC divider from that
> clock's rate, so point it at the system clock. Naming the pixel clock
> there instead would keep the PHY pre-PLL powered from probe onwards
> and
> size the divider from the wrong rate.
> 
> The PHY can only generate the discrete set of pixel clocks described
> by
> its pre-PLL table, so .mode_valid rejects any mode clk_round_rate()
> cannot satisfy. Without it such a mode would be advertised to
> userspace
> and the modeset would appear to succeed while the display stayed
> blank.
> 
> .enable returns early when the rate is unsupported or the PHY fails
> to
> power on, so track whether the pixel clock was actually enabled and
> let
> .disable tear down only what was brought up, otherwise the clock
> refcount underflows.
> 
> The clocks and the reset are torn down through devm rather than from
> .remove, so that they outlive the bridge that inno_hdmi_probe() adds
> with
> devm_drm_bridge_add(). Releasing them in .remove runs before devres
> unwinds and would leave the bridge registered with its clocks already
> gated.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
> ---
>  drivers/gpu/drm/bridge/Kconfig            |  11 ++
>  drivers/gpu/drm/bridge/Makefile           |   1 +
>  drivers/gpu/drm/bridge/jh7110-inno-hdmi.c | 318
> ++++++++++++++++++++++++++++++
>  3 files changed, 330 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig
> b/drivers/gpu/drm/bridge/Kconfig
> index
> 4a57d49b4c6d3ab4b965228835b372d191647197..75b1cf6727d5a32310dcf9fe573
> 4d95e14eea8fe 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -359,6 +359,17 @@ config DRM_SOLOMON_SSD2825
>  	  Say M here if you want to support this hardware as a
> module.
>  	  The module will be named "ssd2825".
>  
> +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
> subsystem
> +	  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
> 15cc821d85b7ea6f3cdc313f3e521b028de567d7..5d843f4ad7ed50b28cb75286c5e
> 22789d91a0836 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -30,6 +30,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_SOLOMON_SSD2825) += ssd2825.o
>  obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o
>  obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.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..b0bf6abaa55fb452a90021586fa
> f5f220c150968
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) StarFive Technology Co., Ltd.
> + * Copyright (c) 2025 Samsung Electronics Co., Ltd.
> + * Author: Michal Wilczynski <m.wilczynski@samsung.com>
> + *
> + * HDMI controller (bridge) driver for the StarFive JH7110 HDMI
> subsystem.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.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>
> +#include <drm/drm_modes.h>
> +
> +/* dom_vout_syscon: HDMI pixel data mapping */
> +#define VOUT_SYSCFG_4			0x4
> +#define VOUT_HDMI_DP_BIT_DEPTH		BIT(25)
> +#define VOUT_HDMI_DP_YUV_MODE		GENMASK(27, 26)
> +#define VOUT_HDMI_DP_YUV_MODE_RGB	3
> +#define VOUT_HDMI_DPI_BIT_DEPTH		GENMASK(29, 28)
> +#define VOUT_HDMI_DPI_BIT_DEPTH_8BIT	0
> +#define VOUT_HDMI_DPI_DP_SEL		BIT(30)
> +
> +/* u2_display_panel_mux feeds HDMI_Ctrl, see the block diagram in
> 5.1 */
> +#define VOUT_SYSCFG_8			0x8
> +#define VOUT_HDMI_PANEL_SEL		BIT(4)
> +
> +enum stf_hdmi_ctrl_clocks { CLK_SYS = 0, CLK_M, CLK_B, CLK_PCLK,
> CLK_CTRL_NUM };
> +
> +struct stf_inno_hdmi_controller {
> +	struct device *dev;
> +	struct clk_bulk_data clks[CLK_CTRL_NUM];
> +	struct reset_control *tx_rst;
> +	struct phy *phy;
> +	bool enabled;
> +};
> +
> +static enum drm_mode_status
> +inno_hdmi_starfive_mode_valid(struct device *dev,
> +			      const struct drm_display_mode *mode)
> +{
> +	struct stf_inno_hdmi_controller *ctrl =
> dev_get_drvdata(dev);
> +	unsigned long pixelclk = mode->clock * 1000;
> +	long rounded;
> +
> +	/*
> +	 * The PHY can only generate the discrete set of pixel
> clocks described
> +	 * by its pre-PLL table, and clk_round_rate() fails for
> anything else.
> +	 * Reject those modes here: without this the modeset would
> appear to
> +	 * succeed while the PHY never produces a signal.
> +	 */
> +	rounded = clk_round_rate(ctrl->clks[CLK_PCLK].clk,
> pixelclk);
> +	if (rounded < 0 || rounded != pixelclk)
> +		return MODE_NOCLOCK;
> +
> +	return MODE_OK;
> +}
> +
> +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;
> +	}
> +
> +	ctrl->enabled = true;
> +}
> +
> +static void inno_hdmi_starfive_disable(struct device *dev)
> +{
> +	struct stf_inno_hdmi_controller *ctrl =
> dev_get_drvdata(dev);
> +
> +	/*
> +	 * .enable bails out early if the pixel clock rate is
> unsupported or
> +	 * the PHY fails to power on, leaving pclk and the PHY
> untouched.
> +	 * Only tear down what was actually brought up, otherwise
> the clock
> +	 * refcount underflows.
> +	 */
> +	if (!ctrl->enabled)
> +		return;
> +
> +	phy_power_off(ctrl->phy);
> +	clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
> +	ctrl->enabled = false;
> +}
> +
> +/*
> + * The DC8200 has two panels, each exposing a DP and a DPI
> interface, and a mux
> + * in dom_vout_syscon picks which of them drives the HDMI
> transmitter. Derive
> + * the mux setting from the port graph: the remote port number
> selects the
> + * DC8200 panel, and the remote endpoint number the interface on
> that panel
> + * (0 for DPI, 1 for DP). Both drive 8-bit RGB, the only format this
> driver
> + * currently produces.
> + */
> +static int stf_inno_hdmi_setup_mux(struct device *dev)
> +{
> +	struct device_node *ep, *remote;
> +	struct of_endpoint endpoint;
> +	struct regmap *syscon;
> +	u32 mask, val;
> +	int ret;
> +
> +	syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
> +						 "starfive,vout-
> syscon");
> +	if (IS_ERR(syscon))
> +		return dev_err_probe(dev, PTR_ERR(syscon),
> +				     "Failed to get vout syscon\n");
> +
> +	ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
> +	if (!ep)
> +		return dev_err_probe(dev, -ENODEV, "No input
> endpoint\n");
> +
> +	remote = of_graph_get_remote_endpoint(ep);
> +	of_node_put(ep);
> +	if (!remote)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "Input endpoint is not
> connected\n");
> +
> +	ret = of_graph_parse_endpoint(remote, &endpoint);
> +	of_node_put(remote);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "Failed to parse the remote
> endpoint\n");
> +
> +	if (endpoint.port > 1 || endpoint.id > 1)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Unsupported DC8200 output
> %u/%u\n",
> +				     endpoint.port, endpoint.id);
> +
> +	/* Data mapping: 8-bit RGB on whichever interface is in use.
> */
> +	mask = VOUT_HDMI_DPI_DP_SEL | VOUT_HDMI_DP_BIT_DEPTH |
> +	       VOUT_HDMI_DP_YUV_MODE | VOUT_HDMI_DPI_BIT_DEPTH;
> +	val = FIELD_PREP(VOUT_HDMI_DPI_DP_SEL, endpoint.id) |
> +	      FIELD_PREP(VOUT_HDMI_DP_YUV_MODE,
> VOUT_HDMI_DP_YUV_MODE_RGB) |
> +	      FIELD_PREP(VOUT_HDMI_DPI_BIT_DEPTH,
> VOUT_HDMI_DPI_BIT_DEPTH_8BIT);

Well it looks like the vendor driver never sets DP interface, is it
tested? I doubt whether the SoC designer messed it up.

> +
> +	ret = regmap_update_bits(syscon, VOUT_SYSCFG_4, mask, val);
> +	if (ret)
> +		return ret;
> +
> +	/* Which DC8200 panel drives the HDMI transmitter. */
> +	return regmap_update_bits(syscon, VOUT_SYSCFG_8,
> VOUT_HDMI_PANEL_SEL,
> +				  FIELD_PREP(VOUT_HDMI_PANEL_SEL,
> +					     endpoint.port));
> +}
> +
> +static void stf_inno_hdmi_clk_disable(void *data)
> +{
> +	struct stf_inno_hdmi_controller *ctrl = data;
> +
> +	clk_bulk_disable_unprepare(CLK_CTRL_NUM - 1, ctrl->clks);
> +}
> +
> +static void stf_inno_hdmi_rst_assert(void *data)
> +{
> +	reset_control_assert(data);
> +}
> +
> +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 *regmap;
> +	struct inno_hdmi *inno;
> +	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 parent */
> +	regmap = dev_get_regmap(parent, NULL);
> +	if (!regmap) {
> +		dev_err(dev, "Failed to get parent regmap\n");
> +		return -ENODEV;
> +	}
> +
> +	ctrl->phy = devm_phy_get(dev, NULL);
> +	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, NULL);
> +	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 = "pclk";
> +	ctrl->clks[CLK_M].id = "mclk";
> +	ctrl->clks[CLK_B].id = "bclk";
> +	ctrl->clks[CLK_PCLK].id = "pixel"; /* Generated by the 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");
> +
> +	/*
> +	 * Tear the clocks and the reset down through devm, so that
> they outlive
> +	 * everything registered after them. The bridge is added
> with
> +	 * devm_drm_bridge_add(), and unwinding in the wrong order
> would leave it
> +	 * registered while its clocks are already gated.
> +	 *
> +	 * The pixel clock is enabled on demand during modeset.
> +	 */
> +	ret = clk_bulk_prepare_enable(CLK_CTRL_NUM - 1, ctrl->clks);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_add_action_or_reset(dev,
> stf_inno_hdmi_clk_disable, ctrl);
> +	if (ret)
> +		return ret;
> +
> +	ret = reset_control_deassert(ctrl->tx_rst);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_add_action_or_reset(dev,
> stf_inno_hdmi_rst_assert,
> +				       ctrl->tx_rst);
> +	if (ret)
> +		return ret;
> +
> +	ret = stf_inno_hdmi_setup_mux(dev);
> +	if (ret)
> +		return ret;
> +
> +	plat_data = of_device_get_match_data(dev);
> +
> +	/* Hand off to the generic library to create the bridge. */
> +	inno = inno_hdmi_probe(pdev, plat_data);
> +	if (IS_ERR(inno))
> +		return PTR_ERR(inno);
> +
> +	return 0;
> +}
> +
> +/*
> + * 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 */
> +};

If it's just such a upper bound, why don't just override the function
as a bound check?

Or... should the real table be used here? I start to wonder whether
this is related to Maud's failure on the Framework panel.

Thanks,
Icenowy

> +
> +static const struct inno_hdmi_plat_ops stf_inno_hdmi_plat_ops = {
> +	.enable = inno_hdmi_starfive_enable,
> +	.disable = inno_hdmi_starfive_disable,
> +	.mode_valid = inno_hdmi_starfive_mode_valid,
> +};
> +
> +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,
> +	.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@samsung.com>");
> +MODULE_DESCRIPTION("StarFive INNO HDMI Controller Driver");
> +MODULE_LICENSE("GPL");


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

* Re: [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid platform operation
  2026-09-04 13:27   ` [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid " Michal Wilczynski
@ 2026-09-04 13:40     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:40 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: robh, mfd, neil.armstrong, lee, vkoul, linux-clk, olteanv,
	devicetree, linux-phy, conor+dt, dri-devel

> inno_hdmi_bridge_mode_valid() validates the pixel clock against
> hdmi->refclk, but that clock is optional and is only present on
> platforms that describe a "ref" clock. Platforms where the pixel clock
> is produced by a separate device - such as the StarFive JH7110, whose
> PHY is both the clock and the PHY provider - have no "ref" clock, so
> the check is skipped entirely and every mode is reported as valid.
> 
> A mode the platform cannot generate is then advertised to userspace.
> The subsequent modeset appears to succeed, since the atomic enable path
> cannot fail, and the display silently stays blank.
> 
> Add a .mode_valid platform operation so platforms can reject modes they
> are unable to drive. Platforms that do not implement it are unaffected.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=10


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

* Re: [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind
  2026-09-04 13:27   ` [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind Michal Wilczynski
@ 2026-09-04 13:42     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:42 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: lee, linux-phy, robh, linux-clk, devicetree, vkoul,
	neil.armstrong, olteanv, mfd, conor+dt, dri-devel

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Using `devm_` APIs during the component bind phase causes permanent resource leaks and breaks EPROBE_DEFER.
- [High] The hotplug interrupt handler `inno_hdmi_irq` can fire before the bridge is attached, leading to a NULL pointer dereference.
- [High] The I2C adapter is exposed before its private data is initialized, leading to a NULL pointer dereference on concurrent access.
--

commit 72f1334365740cfcdf0865d9dfd6562b9c3b40a8
Author: Michal Wilczynski <m.wilczynski@samsung.com>
Subject: drm/bridge: inno-hdmi: Split probe out of bind

This commit splits the hardware setup of the inno-hdmi driver into a new
inno_hdmi_probe() function. This separates the bridge registration from its
attachment in inno_hdmi_bind(), allowing decoupled MFD/glue drivers to manage
them independently.

> diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
> index dd35f5a875d3d..aab474740f7f5 100644
> --- a/drivers/gpu/drm/bridge/inno-hdmi.c
> +++ b/drivers/gpu/drm/bridge/inno-hdmi.c
> @@ -1061,11 +1061,24 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
>  	return adap;
>  }

[Severity: High]
This is a pre-existing issue, but the I2C adapter is exposed before its private
data is initialized, which can lead to a NULL pointer dereference on concurrent
access.

Just above this line, devm_i2c_add_adapter() makes the bus visible to the system
before hdmi->i2c is assigned:

    ret = devm_i2c_add_adapter(hdmi->dev, adap);
    if (ret) {
        ...
    }

    hdmi->i2c = i2c;

If a concurrent I2C transfer is initiated (e.g. via udev running i2cdetect),
inno_hdmi_i2c_xfer() can be invoked. It will read the uninitialized hdmi->i2c
and attempt to lock its mutex:

drivers/gpu/drm/bridge/inno-hdmi.c:inno_hdmi_i2c_xfer() {
    struct inno_hdmi_i2c *i2c = hdmi->i2c;
    ...
    mutex_lock(&i2c->lock);
    ...
}

Could hdmi->i2c be assigned before calling devm_i2c_add_adapter() to prevent
this race?

> -struct inno_hdmi *inno_hdmi_bind(struct device *dev,
> -				 struct drm_encoder *encoder,
> -				 const struct inno_hdmi_plat_data *plat_data)
> +/**
> + * inno_hdmi_probe - Internal helper to perform common setup
> + * @pdev: platform device
> + * @plat_data: SoC-specific platform data
> + *
> + * This function handles all the common hardware setup: allocating the main
> + * struct, mapping registers, getting clocks, initializing the hardware,
> + * setting up the IRQ, and initializing the DDC adapter and bridge struct.
> + * It returns a pointer to the inno_hdmi struct on success, or an ERR_PTR
> + * on failure.
> + *
> + * This function is used by modern, decoupled MFD/glue drivers. It registers
> + * the bridge but does not attach it.
> + */
> +struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
> +				  const struct inno_hdmi_plat_data *plat_data)
>  {

[Severity: High]
This isn't a bug introduced by this patch, but using devm_ APIs in the
component bind phase (now moved to inno_hdmi_probe()) causes permanent
resource leaks and breaks EPROBE_DEFER.

Because inno_hdmi_bind() is called from inno_hdmi_rockchip_bind() (a
component bind callback), the platform device is not removed when a component
unbinds. If another component in the DRM pipeline defers, the devm_ resources
are not freed.

Subsequent bind attempts will fail with -EBUSY when re-requesting the IRQ or
I/O regions:

drivers/gpu/drm/bridge/inno-hdmi.c:inno_hdmi_probe() {
    ...
    ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
                                    inno_hdmi_irq, IRQF_SHARED,
                                    dev_name(dev), hdmi);
    ...
}

Should this driver be converted to request resources during the actual probe
phase rather than bind?

[ ... ]

> +struct inno_hdmi *inno_hdmi_bind(struct device *dev,
> +				 struct drm_encoder *encoder,
> +				 const struct inno_hdmi_plat_data *plat_data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct inno_hdmi *hdmi;
> +	int ret;
> +
> +	hdmi = inno_hdmi_probe(pdev, plat_data);
> +	if (IS_ERR(hdmi))
> +		return hdmi;
> +
> +	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL,
> +				DRM_BRIDGE_ATTACH_NO_CONNECTOR);

[Severity: High]
This is a pre-existing issue, but this refactoring significantly widens the race
window for a NULL pointer dereference. The hotplug interrupt handler can fire
before the bridge is attached.

inno_hdmi_probe() unmasks the HPD interrupt and registers the IRQ handler with
devm_request_threaded_irq(). If a monitor is physically connected, the
interrupt can fire immediately.

The IRQ handler then attempts to dereference hdmi->bridge.dev:

drivers/gpu/drm/bridge/inno-hdmi.c:inno_hdmi_irq() {
    ...
    drm_helper_hpd_irq_event(hdmi->bridge.dev);
    ...
}

However, hdmi->bridge.dev is only assigned when drm_bridge_attach() is called.
Since this patch decouples probe from attach for modern MFD/glue drivers, the
attach phase happens much later, leaving a dangerous race window.

Should the IRQ registration be moved to after the bridge is attached, or should
the IRQ handler check if the bridge is attached before proceeding?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=7

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

* Re: [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent
  2026-09-04 13:27   ` [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent Michal Wilczynski
@ 2026-09-04 13:43     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:43 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: neil.armstrong, vkoul, dri-devel, conor+dt, olteanv, devicetree,
	lee, linux-phy, robh, mfd, linux-clk

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Early HPD interrupt can cause a NULL pointer dereference of `hdmi->bridge.dev` if it fires before `drm_bridge_attach` completes.
--

commit 1a1229643b989df67626f64f580add186cebd6ed
Author: Michal Wilczynski <m.wilczynski@samsung.com>

drm/bridge: inno-hdmi: Allow the register map to come from a parent
    
On some SoCs the HDMI controller does not own its register window. The
StarFive JH7110 documents one 64 KB block, u0_hdmitx, that holds both
the controller and the PHY, so the parent device maps it and owns the
regmap while the two children share it.

> diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
> index aab474740f7f5..8bd7717611804 100644
> --- a/drivers/gpu/drm/bridge/inno-hdmi.c
> +++ b/drivers/gpu/drm/bridge/inno-hdmi.c
[ ... ]
> @@ -1095,9 +1110,19 @@ struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
>  	hdmi->dev = dev;
>  	hdmi->plat_data = plat_data;
>  
> -	hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
> -	if (IS_ERR(hdmi->regs))
> -		return ERR_CAST(hdmi->regs);
> +	/*
> +	 * On platforms where the controller shares a register space with
> +	 * other blocks, the parent owns the regmap. Fall back to mapping
> +	 * our own resource where it does not.
> +	 */
> +	if (dev->parent)
> +		hdmi->regmap = dev_get_regmap(dev->parent, NULL);
> +
> +	if (!hdmi->regmap) {
> +		hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
> +		if (IS_ERR(hdmi->regs))
> +			return ERR_CAST(hdmi->regs);
> +	}
>  
>  	hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk");
>  	if (IS_ERR(hdmi->pclk)) {

[Severity: High]
This is a pre-existing issue, but could this code trigger a NULL pointer
dereference if the hotplug interrupt fires early?

Looking at the rest of inno_hdmi_probe(), the hardware interrupt is unmuted
in inno_hdmi_init_hw(), and then the handler is registered:

    inno_hdmi_init_hw(hdmi);
    ...
    ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
                                    inno_hdmi_irq, IRQF_SHARED,
                                    dev_name(dev), hdmi);

If a cable is already connected or plugged in at this exact moment, the
interrupt can fire immediately. The threaded handler inno_hdmi_irq() will
then execute:

    drm_helper_hpd_irq_event(hdmi->bridge.dev);

However, hdmi->bridge.dev appears to be NULL at this point, as it is only
populated later when the upstream display controller calls
drm_bridge_attach().

Is there something preventing this race condition, or should the IRQ handler
registration be deferred?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=8

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

* Re: [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation
  2026-09-04 13:27   ` [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation Michal Wilczynski
@ 2026-09-04 13:44     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:44 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: conor+dt, neil.armstrong, vkoul, olteanv, devicetree, lee,
	dri-devel, linux-clk, robh, linux-phy, mfd

> The dc8200_pix0 and dc8200_pix1 MUXes feed the display controller, and
> the rate they carry is ultimately produced by the HDMI PHY, which
> registers hdmitx0_pixelclk as a clock provider.
> 
> Add CLK_SET_RATE_PARENT to both MUXes so a rate requested on them
> reaches that parent, instead of being clamped to whatever rate the
> parent already happens to run at.
> 
> Co-developed-by: Dominique Belhachemi <db@domibel.de>
> Signed-off-by: Dominique Belhachemi <db@domibel.de>
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=13


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

* Re: [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver
  2026-09-04 13:27   ` [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver Michal Wilczynski
@ 2026-09-04 13:47     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:47 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: vkoul, neil.armstrong, linux-phy, devicetree, robh, conor+dt,
	olteanv, lee, mfd, dri-devel, linux-clk

> Nothing in the video output subsystem can reach its own registers until
> the NoC display bus clock and reset are up and PD_VOUT is powered, and
> those are shared by every device in the region. Leaving them to whichever
> consumer probes first works by accident and stops working as soon as the
> probe order changes.
> 
> Add a driver for the subsystem node that takes the bus clock and reset,
> holds a runtime PM reference so genpd keeps PD_VOUT powered, and then
> populates its children. All three are released only once the last child
> is gone.
> 
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=12


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

* Re: [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation
  2026-09-04 13:27   ` [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation Michal Wilczynski
@ 2026-09-04 13:49     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:49 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: vkoul, dri-devel, neil.armstrong, olteanv, lee, linux-clk,
	conor+dt, devicetree, mfd, linux-phy, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware Teardown Order Inversion Risking MMIO Aborts
--

commit 3edb8b3d87e91fedec7589ce07bc351965b21765
Author: Michal Wilczynski <m.wilczynski@samsung.com>

drm/bridge: inno-hdmi: Add .disable platform operation

This commit adds a new .disable platform operation to the Innosilicon HDMI
driver to support platform-specific power down sequences. The new callback
is executed during the bridge's atomic disable phase.

> diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
> --- a/drivers/gpu/drm/bridge/inno-hdmi.c
> +++ b/drivers/gpu/drm/bridge/inno-hdmi.c
[ ... ]
> @@ -886,6 +886,10 @@ static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
>  					    struct drm_atomic_commit *state)
>  {
>  	struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
> +	const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
> +
> +	if (plat_ops && plat_ops->disable)
> +		plat_ops->disable(hdmi->dev);
>  
>  	inno_hdmi_standby(hdmi);

[Severity: High]
Does calling the platform disable callback before inno_hdmi_standby() risk
MMIO aborts?

The platform disable hook (such as the JH7110 implementation mentioned in the
commit message) typically powers off the PHY and disables clocks. However,
inno_hdmi_standby() attempts to perform MMIO writes to PHY registers like
HDMI_PHY_DRIVER and HDMI_PHY_PRE_EMPHASIS.

Accessing hardware registers after the PHY has been powered off and clocks
disabled can cause a synchronous external abort (bus hang) or silent
failures on many SoC architectures.

Since plat_ops->enable() is called prior to configuring the PHY registers
during setup, should the teardown follow a strict LIFO order where
inno_hdmi_standby() is called before plat_ops->disable()?

>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=9

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

* Re: [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver
  2026-09-04 13:27   ` [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver Michal Wilczynski
@ 2026-09-04 13:52     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:52 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: dri-devel, linux-clk, lee, mfd, linux-phy, vkoul, conor+dt,
	devicetree, robh, neil.armstrong, olteanv

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incorrect regmap configuration (.val_bits = 8) forces byte-wise accesses on a 32-bit register file, which may cause bus faults or ignored writes.
--

commit 83ac96938fc0f15d53cdae0501c6e430a17c1c1d
Author: Michal Wilczynski <m.wilczynski@samsung.com>
soc: starfive: Add jh7110-hdmi-subsystem driver

This commit introduces a parent driver for the monolithic StarFive JH7110
HDMI IP block. It maps the shared register block and creates child devices
for the PHY and controller using a shared regmap.

> diff --git a/drivers/soc/starfive/jh7110-hdmi-subsystem.c b/drivers/soc/starfive/jh7110-hdmi-subsystem.c
> new file mode 100644
> index 0000000000000..e05b91825be4d
> --- /dev/null
> +++ b/drivers/soc/starfive/jh7110-hdmi-subsystem.c
[ ... ]
> +static const struct regmap_config starfive_hdmi_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 8,

[Severity: High]
Will this regmap configuration cause byte-wise memory accesses on a 32-bit
register file? 

Setting .val_bits = 8 forces the regmap core to use 8-bit readb()/writeb()
accesses instead of 32-bit readl()/writel(). The underlying Innosilicon
HDMI IP has registers spaced by 4 bytes and natively expects 32-bit
accesses.

On RISC-V platforms like the JH7110, byte-wise access to 32-bit APB
peripherals typically triggers a Store/Load Access Fault or results in
silently ignored writes.

> +	.max_register = 0x3fff,
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=11

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

* Re: [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers
  2026-09-04 13:27   ` [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers Michal Wilczynski
@ 2026-09-04 13:55     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:55 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: olteanv, conor+dt, dri-devel, lee, neil.armstrong, linux-clk,
	robh, mfd, linux-phy, devicetree, vkoul

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Kernel panic from division by zero in clock rate recalculation if regmap_read fails or registers are uninitialized.
- [High] Severe 32-bit integer overflows in VCO clock calculations on 32-bit architectures, leading to heavily truncated clock rates.
--

commit 3a039ecf7fc3d9336647b49a4ed8daf640a4fd42
Author: Michal Wilczynski <m.wilczynski@samsung.com>

phy: Add common Innosilicon HDMI PHY helpers

This commit extracts shared Innosilicon HDMI PHY logic into a common
library. It provides pre-PLL configuration, lookup, and clock rate
recalculation helpers for SoC drivers that instantiate this IP.

> diff --git a/drivers/phy/phy-inno-hdmi.c b/drivers/phy/phy-inno-hdmi.c
> new file mode 100644
> index 0000000000000..77e745a87d22b
> --- /dev/null
> +++ b/drivers/phy/phy-inno-hdmi.c

[ ... ]

> +static u8 inno_read(const struct inno_hdmi_phy_pre_pll *pll, unsigned int reg)
> +{
> +	unsigned int val;
> +	int ret;
> +
> +	ret = regmap_read(pll->regmap, inno_reg(pll, reg), &val);
> +	if (ret)
> +		return 0;

[Severity: High]
This isn't a bug introduced by this patch, but does explicitly returning 0
on a regmap read failure set up a deterministic kernel panic downstream?

If the registers are uninitialized or the read fails, the returned 0 will
propagate to the division operations in inno_hdmi_phy_pre_pll_recalc_rate().

[ ... ]

> +unsigned long
> +inno_hdmi_phy_pre_pll_recalc_rate(const struct inno_hdmi_phy_pre_pll *pll,
> +				  unsigned long parent_rate)
> +{
> +	u8 nd, no_a, no_b, no_d;
> +	unsigned long frac, rate;
> +	u64 vco, vco_rate;
> +	u16 nf;
> +
> +	nd = inno_read(pll, INNO_PRE_PLL_DIV_1) & INNO_PRE_PLL_PRE_DIV_MASK;
> +	nf = (inno_read(pll, INNO_PRE_PLL_DIV_2) &
> +	      INNO_PRE_PLL_FB_DIV_11_8_MASK) << 8;
> +	nf |= inno_read(pll, INNO_PRE_PLL_DIV_3);
> +	vco = parent_rate * nf;

[Severity: High]
This is a pre-existing issue, but on 32-bit architectures where
parent_rate is a 32-bit unsigned long, does this multiplication silently
overflow before being assigned to the 64-bit vco variable?

The nf variable can be up to 4095, which would severely truncate the
result if it exceeds the 32-bit limit. Should parent_rate be cast to
u64 here?

> +
> +	if (!(inno_read(pll, INNO_PRE_PLL_DIV_2) &
> +	      INNO_PRE_PLL_FRAC_DIV_DISABLE)) {
> +		frac = inno_read(pll, INNO_PRE_PLL_FRAC_DIV_7_0) |
> +		       (inno_read(pll, INNO_PRE_PLL_FRAC_DIV_15_8) << 8) |
> +		       (inno_read(pll, INNO_PRE_PLL_FRAC_DIV_23_16) << 16);
> +		vco += DIV_ROUND_CLOSEST(parent_rate * frac,
> +					 BIT(INNO_FRAC_DIV_WIDTH));

[Severity: High]
This isn't a bug introduced by this patch, but does the parent_rate * frac
multiplication silently overflow 32-bit limits as well?

If this calculation is upgraded to 64-bit to avoid the overflow, would it
also require switching DIV_ROUND_CLOSEST to DIV_ROUND_CLOSEST_ULL to prevent
__udivdi3 link errors on 32-bit systems?

> +	}
> +
> +	vco_rate = vco;
> +
> +	if (inno_read(pll, INNO_PRE_PLL_CONTROL) & INNO_PCLK_VCO_DIV_5_MASK) {
> +		do_div(vco, nd * 5);

[Severity: High]
This is a pre-existing issue, but if nd evaluates to 0 from a failed read,
will this do_div() trigger a divide by zero exception during probe?

> +	} else {
> +		no_a = inno_read(pll, INNO_PCLK_DIV_AB) & INNO_PCLK_DIV_A_MASK;
> +		no_b = FIELD_GET(INNO_PCLK_DIV_B_MASK,
> +				 inno_read(pll, INNO_PCLK_DIV_AB)) + 2;
> +		no_d = inno_read(pll, INNO_PCLK_DIV_CD) & INNO_PCLK_DIV_D_MASK;
> +
> +		do_div(vco, nd * (no_a == 1 ? no_b : no_a) * no_d * 2);

[Severity: High]
This is also a pre-existing issue, but similar to the above, if nd, no_a,
or no_d evaluate to 0 due to read failures, does this also cause a kernel
panic?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=15

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

* Re: [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver
  2026-09-04 13:27   ` [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver Michal Wilczynski
  2026-09-04 13:39     ` Icenowy Zheng
@ 2026-09-04 13:57     ` sashiko-bot
  1 sibling, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:57 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: mfd, robh, devicetree, linux-clk, olteanv, conor+dt, lee,
	dri-devel, neil.armstrong, vkoul, linux-phy

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The `regmap` variable is assigned and checked but never used or passed to the generic driver, contradicting the commit message.
- [Medium] Missing mandatory `phy_init()` and `phy_exit()` calls for the HDMI PHY.
--

commit a25b9a0e71017577ee552276d0b9a0aa22a5f9f0
Author: Michal Wilczynski <m.wilczynski@samsung.com>
drm/bridge: starfive: Add JH7110 HDMI controller driver

This commit adds the HDMI controller (bridge) driver for the StarFive
JH7110. It registers as a DRM bridge, manages clock rates and PHY power,
and configures the mux setting based on the port graph.

> The driver calls the generic inno_hdmi_probe function and passes the
> shared regmap to it, registering as a DRM bridge.

[Severity: Medium]
Is this description accurate regarding the regmap? Looking at the
starfive_inno_hdmi_controller_probe() function below, the regmap is fetched
but doesn't appear to actually be passed to inno_hdmi_probe() or used
anywhere else.

> 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 0000000000000..b0bf6abaa55fb
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/jh7110-inno-hdmi.c
[ ... ]
> +static void inno_hdmi_starfive_enable(struct device *dev,
> +				      struct drm_display_mode *mode)
> +{
[ ... ]
> +	/*
> +	 * 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);

[Severity: Medium]
Does this code omit a required initialization step? The generic PHY API
strictly requires phy_init() to be called before phy_power_on(). Even if
the current starfive-inno-hdmi-phy driver lacks an .init callback, omitting
phy_init() here violates the API contract and could lead to initialization
failures if the PHY driver is updated or a different PHY is bound.

> +	if (ret) {
> +		dev_err(dev, "Failed to power on PHY: %d\n", ret);
> +		clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
> +		return;
> +	}
> +
> +	ctrl->enabled = true;
> +}
> +
> +static void inno_hdmi_starfive_disable(struct device *dev)
> +{
> +	struct stf_inno_hdmi_controller *ctrl = dev_get_drvdata(dev);
> +
> +	/*
> +	 * .enable bails out early if the pixel clock rate is unsupported or
> +	 * the PHY fails to power on, leaving pclk and the PHY untouched.
> +	 * Only tear down what was actually brought up, otherwise the clock
> +	 * refcount underflows.
> +	 */
> +	if (!ctrl->enabled)
> +		return;
> +
> +	phy_power_off(ctrl->phy);

[Severity: Medium]
Should there be a call to phy_exit() here? Following the same API contract,
phy_exit() should generally be invoked to clean up resources after
phy_power_off() completes.

> +	clk_disable_unprepare(ctrl->clks[CLK_PCLK].clk);
> +	ctrl->enabled = false;
> +}
[ ... ]
> +static int starfive_inno_hdmi_controller_probe(struct platform_device *pdev)
> +{
[ ... ]
> +	/* Get the shared regmap from the parent */
> +	regmap = dev_get_regmap(parent, NULL);
> +	if (!regmap) {
> +		dev_err(dev, "Failed to get parent regmap\n");
> +		return -ENODEV;
> +	}

[Severity: Medium]
Could this be dead code? The regmap is fetched and checked here, but
subsequent code within starfive_inno_hdmi_controller_probe() never uses it.

[ ... ]
> +	plat_data = of_device_get_match_data(dev);
> +
> +	/* Hand off to the generic library to create the bridge. */
> +	inno = inno_hdmi_probe(pdev, plat_data);

[Severity: Medium]
As mentioned regarding the commit message, should the regmap have been
passed to inno_hdmi_probe() here?

> +	if (IS_ERR(inno))
> +		return PTR_ERR(inno);
> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=14

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

* Re: [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver
  2026-09-04 13:27   ` [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver Michal Wilczynski
@ 2026-09-04 13:57     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:57 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: linux-clk, lee, devicetree, conor+dt, vkoul, robh, dri-devel, mfd,
	neil.armstrong, olteanv, linux-phy

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Unconditional register access in `clk_set_rate` and `probe` will cause a CPU wedge if the HDMI system clock is gated.
- [High] `phy_power_off` unconditionally disables shared analog blocks (BIAS and RX), breaking the pixel clock output if `hdmi_pclk` is still running.
- [Medium] `clk_set_rate` unconditionally powers on the Pre-PLL and analog blocks, violating CCF semantics and leaking power.
- [Medium] Data race on state variables and unprotected register sequences between Clock and PHY callbacks.
--

commit 08ad435ba48340f7f566c8d19a6a67d1faa2ca9d
Author: Michal Wilczynski <m.wilczynski@samsung.com>
Subject: phy: starfive: Add jh7110-inno-hdmi-phy driver

This commit adds the HDMI PHY driver for the StarFive JH7110. It registers 
as a clock provider for the pre-PLL pixel clock and a PHY provider for the 
post-PLL analog blocks.

> diff --git a/drivers/phy/starfive/phy-jh7110-inno-hdmi.c b/drivers/phy/starfive/phy-jh7110-inno-hdmi.c
> index 0000000000000..2575cb5ed624a
> --- /dev/null
> +++ b/drivers/phy/starfive/phy-jh7110-inno-hdmi.c

[ ... ]

> +static int starfive_hdmi_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +					  unsigned long parent_rate)
> +{
> +	struct starfive_hdmi_phy *inno = to_starfive_hdmi_phy(hw);
> +	const struct inno_hdmi_phy_pre_pll_config *cfg;

[ ... ]

> +	if (inno->pixclock == rate && inno->tmdsclock == cfg->tmdsclock)
> +		return 0;
> +
> +	inno_update_bits(inno, STF_INNO_BIAS_CONTROL,
> +			 STF_INNO_BIAS_ENABLE, STF_INNO_BIAS_ENABLE);
> +	inno_write(inno, STF_INNO_RX_CONTROL, STF_INNO_RX_ENABLE);

[Severity: High]
Does this register access execute blindly without checking if the clock is
prepared? 

The commit message mentions that unclocked accesses wedge the bus hard enough
that the CPU stops taking NMIs. If clk_set_rate() is called while the clock is
unprepared, could this cause the described system crash?

[Severity: Medium]
Additionally, could this unconditionally power on the shared analog blocks
and violate the Common Clock Framework semantics? 

CCF allows clk_set_rate() to be called on an unprepared clock, which should
merely update the configuration. If the clock remains unprepared, do these
blocks remain powered on and unnecessarily leak power?

> +
> +	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, true);
> +	inno_hdmi_phy_pre_pll_configure(&inno->pre_pll, cfg);
> +	inno_hdmi_phy_pre_pll_power_down(&inno->pre_pll, false);

[Severity: Medium]
Does this also force the Pre-PLL hardware into a powered-up and running state
without checking if the clock is prepared, potentially leaking power?

> +
> +	inno->pixclock = rate;
> +	inno->tmdsclock = cfg->tmdsclock;

[Severity: Medium]
Is it safe to modify this shared state under the CCF's prepare_lock?

The PHY framework might concurrently modify these variables under phy->mutex in
starfive_hdmi_phy_power_down(). Could this data race cause inconsistent
internal state, or allow the multi-register hardware configuration sequences
to dangerously interleave?

> +
> +	return 0;
> +}

[ ... ]

> +static void starfive_hdmi_phy_power_down(struct starfive_hdmi_phy *inno)
> +{
> +	inno_write(inno, STF_INNO_TMDS_CONTROL, 0x00);
> +	inno_write(inno, STF_INNO_SERIALIER_CONTROL, 0x00);
> +	inno_write(inno, STF_INNO_LDO_CONTROL, 0x00);
> +	inno_update_bits(inno, STF_INNO_BIAS_CONTROL,
> +			 STF_INNO_BIAS_ENABLE, 0x00);
> +	inno_write(inno, STF_INNO_RX_CONTROL, 0x00);

[Severity: High]
Does this unconditionally disable the shared BIAS and RX analog blocks, even
if the hdmi_pclk clock provider is still running? 

If the PHY is powered off, but the VOUT subsystem still expects the pixel
clock to be active, will this abruptly kill the clock signal behind the clock
framework's back?

> +
> +	/* Power down Post-PLL */
> +	inno_update_bits(inno, STF_INNO_POST_PLL_DIV_1,
> +			 STF_INNO_POST_PLL_POWER_DOWN,
> +			 STF_INNO_POST_PLL_POWER_DOWN);
> +
> +	inno->tmdsclock = 0;
> +	inno->pixclock = 0;

[Severity: Medium]
This appears to be the other side of the data race mentioned above. Does
modifying this state under the PHY mutex race with the CCF reading or
writing it via clk_set_rate() or clk_recalc_rate()?

> +}

[ ... ]

> +static int starfive_hdmi_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device *parent = dev->parent;

[ ... ]

> +	ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get, inno->phyclk);
> +	if (ret) {
> +		dev_err(dev, "Failed to add clock provider: %d\n", ret);
> +		goto err_disable_refoclk;
> +	}
> +
> +	ret = clk_set_rate(inno->phyclk, 297000000);
> +	if (ret) {
> +		dev_err(dev, "Failed to set default rate: %d\n", ret);
> +		goto err_del_clk_provider;
> +	}

[Severity: High]
Does this synchronous clk_set_rate() call hit the PHY registers before the
sibling controller is guaranteed to have enabled the system clock?

Because the PHY driver can probe before the bridge driver, could this happen
while the clock is gated, triggering the system crash (CPU NMI stall)
described in the commit message?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=17

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

* Re: [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon PHY helpers
  2026-09-04 13:27   ` [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon " Michal Wilczynski
@ 2026-09-04 13:59     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 13:59 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: dri-devel, neil.armstrong, robh, conor+dt, linux-clk, olteanv,
	devicetree, vkoul, linux-phy, mfd, lee

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Unused macros `RK3328_SPREAD_SPECTRUM_MOD_DOWN` and `RK3328_SPREAD_SPECTRUM_MOD_DISABLE` were left behind despite the commit message claiming that unused pre-PLL macros were removed.
--

commit 103e72298ee1993440baf6c4457495f034d88185
Author: Michal Wilczynski <m.wilczynski@samsung.com>

phy: rockchip: inno-hdmi: Use the common Innosilicon PHY helpers

This commit refactors the RK3328 pre-PLL programming to use shared
Innosilicon PHY helpers. It removes local copies of register-level code,
macros, and table lookups. The RK3228 retains its own register code while
sharing the table lookup.

> diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
> index c3d257a0f4a65..17229d039132e 100644
> --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
> +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
> @@ -130,42 +131,17 @@
>  #define RK3328_INT_VSS_AGND_ESD_DET			BIT(1)
>  #define RK3328_INT_AGND_VSS_ESD_DET			BIT(0)
>  /* REG: 0xa0 */
> -#define RK3328_PCLK_VCO_DIV_5_MASK			BIT(1)
> -#define RK3328_PCLK_VCO_DIV_5(x)			UPDATE(x, 1, 1)
>  #define RK3328_PRE_PLL_POWER_DOWN			BIT(0)
>  /* REG: 0xa1 */
> -#define RK3328_PRE_PLL_PRE_DIV_MASK			GENMASK(5, 0)
> -#define RK3328_PRE_PLL_PRE_DIV(x)			UPDATE(x, 5, 0)
>  /* REG: 0xa2 */
>  /* unset means center spread */
>  #define RK3328_SPREAD_SPECTRUM_MOD_DOWN			BIT(7)
>  #define RK3328_SPREAD_SPECTRUM_MOD_DISABLE		BIT(6)

[Severity: Low]
Are these macros still needed? The commit message states that unused pre-PLL
macros are being removed, but these appear to have been left behind. Since
the shared helper uses its own definitions, are these now orphaned dead code?

> -#define RK3328_PRE_PLL_FRAC_DIV_DISABLE			UPDATE(3, 5, 4)
> -#define RK3328_PRE_PLL_FB_DIV_11_8_MASK			GENMASK(3, 0)
> -#define RK3328_PRE_PLL_FB_DIV_11_8(x)			UPDATE((x) >> 8, 3, 0)
>  /* REG: 0xa3 */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=16

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

* Re: [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem
  2026-09-04 13:27   ` [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem Michal Wilczynski
@ 2026-09-04 14:03     ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-09-04 14:03 UTC (permalink / raw)
  To: Michal Wilczynski
  Cc: neil.armstrong, vkoul, linux-clk, robh, devicetree, lee, conor+dt,
	olteanv, linux-phy, dri-devel, mfd

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The HDMI HPD (Hot Plug Detect) pin is incorrectly configured to drive a logic HIGH output, causing electrical contention and breaking hotplug detection.
- [Medium] Hardware peripheral nodes in the SoC dtsi lack 'status = "disabled"', which will break DT validation for custom boards.
- [Low] The device tree introduces a redundant `xin24m` fixed-clock which duplicates the existing `osc` clock.
--

commit ca7f9b150e9f3d43ce6202c42577591e3273c2a4
Author: Michal Wilczynski <m.wilczynski@samsung.com>

riscv: dts: starfive: jh7110: Update DT for display subsystem

Activate the display subsystem by describing the video output subsystem,
which the documentation calls dom_vout_top, as a bus node covering the
whole 0x29400000 region.

> diff --git a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
> --- a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
[ ... ]
> @@ -351,6 +430,40 @@ &syscrg {
>  };
>  
>  &sysgpio {
> +	hdmi_pins: hdmi-0 {
> +		hdmi-cec-pins {
> +			pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
> +					      GPOEN_SYS_HDMI_CEC_SDA,
> +					      GPI_SYS_HDMI_CEC_SDA)>;
> +			input-enable;
> +			bias-pull-up;
> +		};
> +
> +		hdmi-hpd-pins {
> +			pinmux = <GPIOMUX(15, GPOUT_HIGH,
> +					      GPOEN_ENABLE,
> +					      GPI_SYS_HDMI_HPD)>;

[Severity: High]
Does this configuration incorrectly drive a logic HIGH output on the HPD pin?

The HDMI HPD (Hot Plug Detect) pin is structurally an input driven by the
external HDMI sink. By statically driving the output driver HIGH from the SoC
side (using GPOEN_ENABLE and GPOUT_HIGH), the physical line is held high
regardless of what the monitor tries to drive.

Can this break hotplug detection logic and cause electrical contention if the
monitor pulls it low? Standard input pins usually use GPOEN_DISABLE to prevent
driving the line.

> +			input-enable;
> +			bias-disable; /* external pull-up */
> +		};
[ ... ]
> diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
> --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
> +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
[ ... ]
> @@ -352,6 +346,13 @@ tdm_ext: tdm-ext-clock {
>  		#clock-cells = <0>;
>  	};
>  
> +	xin24m: xin24m {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <24000000>;
> +		clock-output-names = "xin24m";
> +	};
> +

[Severity: Low]
Is this xin24m fixed-clock redundant?

The board's 24MHz oscillator is typically already modeled via an existing osc
node which receives its frequency from board-specific common files. 

Does adding xin24m here unnecessarily duplicate the existing oscillator
node rather than reusing it?

>  	soc {
>  		compatible = "simple-bus";
>  		interrupt-parent = <&plic>;
[ ... ]
> @@ -1199,22 +1200,81 @@ csi_phy: phy@19820000 {
>  			#phy-cells = <0>;
>  		};
>  
> +		vout_subsystem: display-subsystem@29400000 {
> +			compatible = "starfive,jh7110-vout-subsystem";
> +			reg = <0x0 0x29400000 0x0 0x200000>;
> +			ranges;
> +			#address-cells = <2>;
> +			#size-cells = <2>;
> +			clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>;
> +			dma-noncoherent;
>  			power-domains = <&pwrc JH7110_PD_VOUT>;
> +			resets = <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>;
> +
> +			dc8200: display@29400000 {
> +				compatible = "starfive,jh7110-dc8200", "verisilicon,dc";
> +				reg = <0x0 0x29400000 0x0 0x2800>;

[Severity: Medium]
Should this hardware peripheral node include status = "disabled";?

Hardware peripheral nodes in the SoC dtsi are generally disabled by default.
Because the dc8200 node requires board-specific properties (like ports) that
are only provided in jh7110-common.dtsi, leaving it active by default here
can cause DT schema validation failures.

Can this break dtbs_check or cause driver probe crashes on custom boards that
include jh7110.dtsi but do not wire up the display components?

> +				clocks = <&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
[ ... ]
> +				reset-names = "core", "axi", "ahb";
> +			};
> +
> +			hdmi_subsystem: hdmi@29590000 {
> +				compatible = "starfive,jh7110-hdmi-subsystem";
> +				reg = <0x0 0x29590000 0x0 0x4000>;

[Severity: Medium]
Should the hdmi_subsystem node also include status = "disabled";?

Similar to the dc8200 node, leaving this active by default can lead to
validation errors if it is left unconfigured by board files.

> +
> +				hdmi_controller: controller {
[ ... ]
> +				hdmi_phy: phy {
> +					compatible = "starfive,jh7110-inno-hdmi-phy";
> +					clocks = <&xin24m>;

[Severity: Low]
Should this consume the standard <&osc> clock instead of <&xin24m>?

If the xin24m node is redundant as mentioned above, this would likely need
to be updated to point to the main oscillator node.

> +					#clock-cells = <0>;
> +					#phy-cells = <0>;
> +				};
> +			};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-jh7110-clean-send-v3-0-484f9ae72715@samsung.com?part=18

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

* Re: [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (18 preceding siblings ...)
  2026-09-04 13:27   ` [PATCH v3 19/19] MAINTAINERS: Add StarFive JH7110 display subsystem entry Michal Wilczynski
@ 2026-09-04 15:13   ` Joshua Peisach
  2026-09-05  5:21   ` Maud Spierings
  20 siblings, 0 replies; 41+ messages in thread
From: Joshua Peisach @ 2026-09-04 15:13 UTC (permalink / raw)
  To: Michal Wilczynski, Vinod Koul, Neil Armstrong, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Maud Spierings, Graham Markall, Icenowy Zheng

On Fri Sep 4, 2026 at 9:27 AM EDT, Michal Wilczynski wrote:
> This series enables the display subsystem on the StarFive JH7110.
>
> The dom_vout block holds the display controller (dc8200), the clock
> generator (voutcrg) and the HDMI IP, all inside PD_VOUT. The HDMI IP is
> a single register block containing both the controller and the PHY, and
> it has a circular clock dependency with voutcrg:
>
>   - the HDMI controller needs pclk/mclk/bclk from voutcrg
>   - voutcrg needs the pixel clock for its dc8200 pixel MUXes, and that
>     clock is generated by the HDMI PHY
>
> The loop only exists if the HDMI block is treated as one device. The
> PHY's reference clock is xin24m, not a voutcrg output, so splitting the
> node into a parent plus phy and controller children gives deferred probe
> a linear order: hdmi-phy, then voutcrg, then hdmi-controller.
>
> The parent maps the register block and owns the regmap its two children
> share. Everything in the region sits behind one NoC port whose clock and
> reset gate access to it, inside PD_VOUT, so the vout subsystem node from
> the RFC is back and owns those for as long as any child exists.
>
> Patch 10 adds a .mode_valid platform op to inno-hdmi.
> inno_hdmi_bridge_mode_valid() checks the pixel clock against
> hdmi->refclk, but that clock only exists where a "ref" clock is
> described. The JH7110 gets its pixel clock from the PHY, so refclk is
> NULL and the check was skipped: unsupported modes were advertised, the
> modeset then "succeeded" because the atomic enable path cannot fail, and
> the display stayed blank.
>
> Patches 15-17 drop the PHY duplication from the RFC. The JH7110 has the
> same Innosilicon PHY as the RK3328, offset by 0x100 because it sits
> behind the controller in the shared register block. Patch 15 factors out
> the pre-PLL config format, table lookup, determine_rate, recalc_rate and
> the pre-PLL programming; patch 16 moves Rockchip onto it; patch 17 adds
> the JH7110 driver. Pixel clock tables, post-PLL and analog config stay
> SoC specific.
>
> Patch 16 should be a no-op for Rockchip - same writes, same order, same
> values - and RK3228, whose pre-PLL is at different addresses, keeps its
> own register code and shares only the lookup. I have no Rockchip
> hardware, so it is build tested only (arm and riscv). A Tested-by would
> help.
>
> The dc8200 driver, th1520 reset controller and inno-hdmi bridge that the
> RFC listed as prerequisites are all upstream now, so there are no
> out-of-tree dependencies.
>
> Testing
> =======
>
> Tested on a VisionFive 2 v1.3B using modetest.
>
> All 42 modes the sink advertises work, with nothing in dmesg. Pixel
> clocks run from 25.175 MHz (640x480@59.94) up to 297 MHz
> (4096x2160@30), including 3840x2160 and the full 1920x1080 and 1280x720
> rate families.
>
> The four modes the RFC reported as broken work now too: 2560x1440@59.95,
> 2048x1080@60.00, 2048x1080@24.00 and 720x400@70.08.
>
> Before patch 10, four of the advertised modes failed: 1680x1050@59.95
> (146.250 MHz), 1400x1050@59.98 (121.750), 1152x864@59.97 (81.768) and
> 1280x768@60.35 (80.140). Those pixel clocks are not in the PHY pre-PLL
> table, so clk_set_rate() returned -EINVAL and the screen stayed black
> while userspace saw a successful modeset. They are rejected in
> .mode_valid now; the other refresh rates of those resolutions still work.
>
> Every commit builds for riscv, and the Rockchip PHY also for arm.
>
> Notes
> =====
>
> The JH7110 has no central MAINTAINERS entry and maintainership is
> fragmented, so patch 19 adds one for the display subsystem and I am
> happy to help maintain it. The new PHY library lives under drivers/phy/,
> already covered by the generic PHY framework entry.
>
> checkpatch warns "does MAINTAINERS need updating?" on the patches adding
> files, because that entry comes in patch 19.
>
> Thanks to Icenowy Zheng for the dc8200 driver and for explaining how the
> SoC and the display pipeline fit together.
>
> Thanks also to Dominique Belhachemi, who got rid of the vout-subsystem
> wrapper and helped with the testing, to Maud Spierings for testing on a
> Framework 13 panel, and to Graham Markall for testing
> the JH7110 display patches independently and writing up the results:
> https://big-grey.co.uk/2026/01/26/testing-starfive-jh7110-display-controller-patches/
>
> Link to v1: https://lore.kernel.org/all/20251108-jh7110-clean-send-v1-0-06bf43bb76b1@samsung.com/
>
> ---
> Changes in v3:
> - Brought back the vout subsystem node and driver, now owning the NoC
>   bus clock, its reset and PD_VOUT for the whole region, with dc8200,
>   the HDMI block, the syscon and voutcrg as its children (Icenowy Zheng).
> - Fixed a hard hang when the bridge is built as a module: the PHY's
>   .is_prepared read a register in the window gated by the controller's
>   system clock, so clk_disable_unused() wedged the CPU before the
>   controller had bound. The op is gone; the framework uses the software
>   prepare count instead. (Marek Szyprowski)
> - The HDMI controller now programs the display mux in dom_vout_syscon
>   from the port graph rather than inheriting whatever the bootloader
>   left, with a phandle to the syscon (Icenowy Zheng).
> - The register access clock is named "pclk" to match the existing
>   inno-hdmi binding, so the generic driver no longer picks up the pixel
>   clock. Previously it held the pre-PLL powered from probe and sized the
>   DDC divider from the wrong rate.
> - Dropped the clk suffixes and the single-entry -names properties from
>   the bindings (Conor Dooley). mclk and bclk keep their names: per TRM
>   5.3 they are the HDMI audio clocks, not module and bus clocks, so the
>   descriptions say that instead.
> - Replaced patternProperties with plain properties in the hdmi-subsystem
>   binding (Conor Dooley).
> - dc8200 gets an SoC specific compatible, and inherits dma-noncoherent
>   from the subsystem bus node, so it validates against verisilicon,dc.
> - Added the pre-PLL entry for the Framework 13 panel and fixed two
>   devicetree whitespace nits (Maud Spierings).
> - select REGMAP_MMIO, CLK_SET_RATE_NO_REPARENT on the dc8200 pixel MUXes
>   so clk_set_rate() cannot reroute them, and inno-hdmi register reads
>   return 0 instead of stack garbage when regmap_read() fails.
> - phy: rockchip: dropped the local pre-PLL lookup wrapper and the 28 now
>   unused RK3328 pre-PLL macros, and restored the VCO debug output, this
>   time in the shared helper so both drivers get it (Jonas Karlman).
> - Rebased onto v7.3-rc1.
> - Link to v2: https://lore.kernel.org/r/20260828-jh7110-clean-send-v2-0-331680c8b9d1@samsung.com
>
> Changes since the RFC:
> - Dropped the vout-subsystem wrapper driver and its binding, along with
>   the patch relaxing the voutcrg binding; genpd handles PD_VOUT per
>   node.
> - Renamed the compatible to starfive,jh7110-hdmi-subsystem, dropping
>   "mfd" as a Linux term (Conor Dooley).
> - Absolute $refs in the bindings, unused example labels dropped, and the
>   examples deduplicated between parent and children (Conor Dooley).
> - Added the .mode_valid platform operation (patch 7).
> - Split the inno-hdmi rework into a mechanical probe/bind split (patch
>   4)
>   and the regmap-from-parent change (patch 5). struct inno_hdmi is no
>   longer exported; no platform glue dereferences it.
> - Replaced the duplicated PHY driver with a shared Innosilicon library
>   and moved Rockchip onto it (patches 11-13).
> - Fixed pre-PLL lock detection, which masked the status read with the
>   register address instead of the lock bit.
> - Fixed a pixel clock refcount underflow: enable returns early on
>   failure while disable tore down unconditionally.
> - voutcrg patch reduced to adding CLK_SET_RATE_PARENT to the two dc8200
>   pixel MUXes.
> - Rebased onto v7.2.
>
> ---
> Michal Wilczynski (19):
>       dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy
>       dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller
>       dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem
>       dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon
>       dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem
>       dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200
>       drm/bridge: inno-hdmi: Split probe out of bind
>       drm/bridge: inno-hdmi: Allow the register map to come from a parent
>       drm/bridge: inno-hdmi: Add .disable platform operation
>       drm/bridge: inno-hdmi: Add .mode_valid platform operation
>       soc: starfive: Add jh7110-hdmi-subsystem driver
>       soc: starfive: Add jh7110-vout-subsystem driver
>       clk: starfive: jh7110-vout: Allow pixel clock rate propagation
>       drm/bridge: starfive: Add JH7110 HDMI controller driver
>       phy: Add common Innosilicon HDMI PHY helpers
>       phy: rockchip: inno-hdmi: Use the common Innosilicon PHY helpers
>       phy: starfive: Add jh7110-inno-hdmi-phy driver
>       riscv: dts: starfive: jh7110: Update DT for display subsystem
>       MAINTAINERS: Add StarFive JH7110 display subsystem entry
>
>  .../starfive,jh7110-inno-hdmi-controller.yaml      | 121 +++++
>  .../bindings/display/verisilicon,dc.yaml           |   1 +
>  .../mfd/starfive,jh7110-hdmi-subsystem.yaml        |  95 ++++
>  .../phy/starfive,jh7110-inno-hdmi-phy.yaml         |  49 ++
>  .../soc/starfive/starfive,jh7110-syscon.yaml       |   6 +
>  .../starfive/starfive,jh7110-vout-subsystem.yaml   | 100 ++++
>  MAINTAINERS                                        |  13 +
>  arch/riscv/boot/dts/starfive/jh7110-common.dtsi    | 121 ++++-
>  arch/riscv/boot/dts/starfive/jh7110.dtsi           | 102 +++-
>  drivers/clk/starfive/clk-starfive-jh7110-vout.c    |   6 +-
>  drivers/gpu/drm/bridge/Kconfig                     |  11 +
>  drivers/gpu/drm/bridge/Makefile                    |   1 +
>  drivers/gpu/drm/bridge/inno-hdmi.c                 |  84 ++-
>  drivers/gpu/drm/bridge/jh7110-inno-hdmi.c          | 318 +++++++++++
>  drivers/phy/Kconfig                                |   8 +
>  drivers/phy/Makefile                               |   1 +
>  drivers/phy/phy-inno-hdmi.c                        | 298 +++++++++++
>  drivers/phy/rockchip/Kconfig                       |   1 +
>  drivers/phy/rockchip/phy-rockchip-inno-hdmi.c      | 165 +-----
>  drivers/phy/starfive/Kconfig                       |  20 +
>  drivers/phy/starfive/Makefile                      |   1 +
>  drivers/phy/starfive/phy-jh7110-inno-hdmi.c        | 579 +++++++++++++++++++++
>  drivers/soc/Kconfig                                |   1 +
>  drivers/soc/Makefile                               |   1 +
>  drivers/soc/starfive/Kconfig                       |  43 ++
>  drivers/soc/starfive/Makefile                      |   3 +
>  drivers/soc/starfive/jh7110-hdmi-subsystem.c       |  74 +++
>  drivers/soc/starfive/jh7110-vout-subsystem.c       |  83 +++
>  include/drm/bridge/inno_hdmi.h                     |  10 +-
>  include/linux/phy/inno-hdmi-phy.h                  |  85 +++
>  30 files changed, 2227 insertions(+), 174 deletions(-)
> ---
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> change-id: 20251031-jh7110-clean-send-7d2242118026
>
> Best regards,

So as a kernel newbie, and someone who happens to have this device,
it's nice to see it here.

It looks good to me, just a few questions:

- One patch mentioned in a comment "the docs" - is there documentation
for the device?

- There are multiple pieces, like the hdmi and vout subsystem, and also
the inno helpers. Should those be separate patches? I honestly don't
have enough experience (or authority) to suggest doing so.


I thought this would be great as a possible driver I could do to learn
kernel dev.. looks like I was very wrong. Great work, and hopefully
in the future I get my hands on some device that needs a driver to be
written for it.

I'm at university, once my board gets mailed from home I'll be able to
test. For now,

Reviewed-by: Joshua Peisach <jpeisach@ubuntu.com>

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

* Re: [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem
  2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
                     ` (19 preceding siblings ...)
  2026-09-04 15:13   ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Joshua Peisach
@ 2026-09-05  5:21   ` Maud Spierings
  20 siblings, 0 replies; 41+ messages in thread
From: Maud Spierings @ 2026-09-05  5:21 UTC (permalink / raw)
  To: Michal Wilczynski, Vinod Koul, Neil Armstrong, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lee Jones, Andy Yan, Philipp Zabel,
	Emil Renner Berthing, Hal Feng, Michael Turquette, Stephen Boyd,
	Brian Masney, Heiko Stuebner, Conor Dooley, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dominique Belhachemi,
	Brian Masney, Jerome Brunet
  Cc: linux-phy, devicetree, linux-kernel, dri-devel, mfd, linux-clk,
	linux-arm-kernel, linux-rockchip, linux-riscv, Andy Yan,
	Marek Szyprowski, Graham Markall, Icenowy Zheng

Hello Michal,

This new version added some more issues on my fml13v01 board, this time 
2 registers in the VOUT_CRG are misconfigured that were correct in v2, 
namely:

0x295c0004 was 0x00000004 on the previous version but is now 0x00000006
0x295c001c was 0x81000000 on the previous version but is now 0x80000000

xx04 sets a slightly different clock divider, but it doesn't seem to 
matter, display also works with this changed value.

xx1c seems to select the wrong clock mux?

After correcting 0x295c001c (and the ones that I mentioned before in the 
hdmitx block), the display comes to life again.

Not sure why these are changed.

Besides that it works great!

Kind regards,
Maud

On 9/4/26 15:27, Michal Wilczynski wrote:
> This series enables the display subsystem on the StarFive JH7110.
> 
> The dom_vout block holds the display controller (dc8200), the clock
> generator (voutcrg) and the HDMI IP, all inside PD_VOUT. The HDMI IP is
> a single register block containing both the controller and the PHY, and
> it has a circular clock dependency with voutcrg:
> 
>    - the HDMI controller needs pclk/mclk/bclk from voutcrg
>    - voutcrg needs the pixel clock for its dc8200 pixel MUXes, and that
>      clock is generated by the HDMI PHY
> 
> The loop only exists if the HDMI block is treated as one device. The
> PHY's reference clock is xin24m, not a voutcrg output, so splitting the
> node into a parent plus phy and controller children gives deferred probe
> a linear order: hdmi-phy, then voutcrg, then hdmi-controller.
> 
> The parent maps the register block and owns the regmap its two children
> share. Everything in the region sits behind one NoC port whose clock and
> reset gate access to it, inside PD_VOUT, so the vout subsystem node from
> the RFC is back and owns those for as long as any child exists.
> 
> Patch 10 adds a .mode_valid platform op to inno-hdmi.
> inno_hdmi_bridge_mode_valid() checks the pixel clock against
> hdmi->refclk, but that clock only exists where a "ref" clock is
> described. The JH7110 gets its pixel clock from the PHY, so refclk is
> NULL and the check was skipped: unsupported modes were advertised, the
> modeset then "succeeded" because the atomic enable path cannot fail, and
> the display stayed blank.
> 
> Patches 15-17 drop the PHY duplication from the RFC. The JH7110 has the
> same Innosilicon PHY as the RK3328, offset by 0x100 because it sits
> behind the controller in the shared register block. Patch 15 factors out
> the pre-PLL config format, table lookup, determine_rate, recalc_rate and
> the pre-PLL programming; patch 16 moves Rockchip onto it; patch 17 adds
> the JH7110 driver. Pixel clock tables, post-PLL and analog config stay
> SoC specific.
> 
> Patch 16 should be a no-op for Rockchip - same writes, same order, same
> values - and RK3228, whose pre-PLL is at different addresses, keeps its
> own register code and shares only the lookup. I have no Rockchip
> hardware, so it is build tested only (arm and riscv). A Tested-by would
> help.
> 
> The dc8200 driver, th1520 reset controller and inno-hdmi bridge that the
> RFC listed as prerequisites are all upstream now, so there are no
> out-of-tree dependencies.
> 
> Testing
> =======
> 
> Tested on a VisionFive 2 v1.3B using modetest.
> 
> All 42 modes the sink advertises work, with nothing in dmesg. Pixel
> clocks run from 25.175 MHz (640x480@59.94) up to 297 MHz
> (4096x2160@30), including 3840x2160 and the full 1920x1080 and 1280x720
> rate families.
> 
> The four modes the RFC reported as broken work now too: 2560x1440@59.95,
> 2048x1080@60.00, 2048x1080@24.00 and 720x400@70.08.
> 
> Before patch 10, four of the advertised modes failed: 1680x1050@59.95
> (146.250 MHz), 1400x1050@59.98 (121.750), 1152x864@59.97 (81.768) and
> 1280x768@60.35 (80.140). Those pixel clocks are not in the PHY pre-PLL
> table, so clk_set_rate() returned -EINVAL and the screen stayed black
> while userspace saw a successful modeset. They are rejected in
> .mode_valid now; the other refresh rates of those resolutions still work.
> 
> Every commit builds for riscv, and the Rockchip PHY also for arm.
> 
> Notes
> =====
> 
> The JH7110 has no central MAINTAINERS entry and maintainership is
> fragmented, so patch 19 adds one for the display subsystem and I am
> happy to help maintain it. The new PHY library lives under drivers/phy/,
> already covered by the generic PHY framework entry.
> 
> checkpatch warns "does MAINTAINERS need updating?" on the patches adding
> files, because that entry comes in patch 19.
> 
> Thanks to Icenowy Zheng for the dc8200 driver and for explaining how the
> SoC and the display pipeline fit together.
> 
> Thanks also to Dominique Belhachemi, who got rid of the vout-subsystem
> wrapper and helped with the testing, to Maud Spierings for testing on a
> Framework 13 panel, and to Graham Markall for testing
> the JH7110 display patches independently and writing up the results:
> https://big-grey.co.uk/2026/01/26/testing-starfive-jh7110-display-controller-patches/
> 

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

end of thread, other threads:[~2026-09-05  5:21 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CGME20260904132708eucas1p1adfa26ef0fe5109eca63a3aeadf74915@eucas1p1.samsung.com>
2026-09-04 13:27 ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Michal Wilczynski
2026-09-04 13:27   ` [PATCH v3 01/19] dt-bindings: phy: Add starfive,jh7110-inno-hdmi-phy Michal Wilczynski
2026-09-04 13:30     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 02/19] dt-bindings: display: bridge: Add starfive,jh7110-inno-hdmi-controller Michal Wilczynski
2026-09-04 13:35     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 03/19] dt-bindings: mfd: Add starfive,jh7110-hdmi-subsystem Michal Wilczynski
2026-09-04 13:37     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 04/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-syscon Michal Wilczynski
2026-09-04 13:30     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 05/19] dt-bindings: soc: starfive: Add starfive,jh7110-vout-subsystem Michal Wilczynski
2026-09-04 13:36     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 06/19] dt-bindings: display: verisilicon: Add starfive,jh7110-dc8200 Michal Wilczynski
2026-09-04 13:31     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 07/19] drm/bridge: inno-hdmi: Split probe out of bind Michal Wilczynski
2026-09-04 13:42     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 08/19] drm/bridge: inno-hdmi: Allow the register map to come from a parent Michal Wilczynski
2026-09-04 13:43     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 09/19] drm/bridge: inno-hdmi: Add .disable platform operation Michal Wilczynski
2026-09-04 13:49     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 10/19] drm/bridge: inno-hdmi: Add .mode_valid " Michal Wilczynski
2026-09-04 13:40     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 11/19] soc: starfive: Add jh7110-hdmi-subsystem driver Michal Wilczynski
2026-09-04 13:52     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 12/19] soc: starfive: Add jh7110-vout-subsystem driver Michal Wilczynski
2026-09-04 13:47     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 13/19] clk: starfive: jh7110-vout: Allow pixel clock rate propagation Michal Wilczynski
2026-09-04 13:44     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 14/19] drm/bridge: starfive: Add JH7110 HDMI controller driver Michal Wilczynski
2026-09-04 13:39     ` Icenowy Zheng
2026-09-04 13:57     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 15/19] phy: Add common Innosilicon HDMI PHY helpers Michal Wilczynski
2026-09-04 13:55     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 16/19] phy: rockchip: inno-hdmi: Use the common Innosilicon " Michal Wilczynski
2026-09-04 13:59     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 17/19] phy: starfive: Add jh7110-inno-hdmi-phy driver Michal Wilczynski
2026-09-04 13:57     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 18/19] riscv: dts: starfive: jh7110: Update DT for display subsystem Michal Wilczynski
2026-09-04 14:03     ` sashiko-bot
2026-09-04 13:27   ` [PATCH v3 19/19] MAINTAINERS: Add StarFive JH7110 display subsystem entry Michal Wilczynski
2026-09-04 15:13   ` [PATCH v3 00/19] drm: starfive: jh7110: Enable display subsystem Joshua Peisach
2026-09-05  5:21   ` Maud Spierings

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).