* [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI
@ 2026-10-30 7:47 ` Icenowy Zheng
0 siblings, 0 replies; 14+ messages in thread
From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: dri-devel, devicetree, linux-kernel, linux-rockchip,
linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao,
Icenowy Zheng
T-Head TH1520 contains a DesignWare MIPI DSI host controller paired with
an internal DesignWare PHY, as a similar design (but with a different
PHY) with the Rockchip DW MIPI DSI.
The functionality to write to the PHY is shared between these two
implementations of DW MIPI DSI, so it's extracted from the Rockchip
driver to the common code. (The same sequence could be seen in the
HiSilicon Kirin DRM driver, but that one hasn't been converted to common
DW MIPI DSI.)
Tested on a Lichee Console 4A (with a Mingjun MJ070BI30IA2 panel) with
further pending DT patches.
One question remains: I have to copy the extra lane bandwidth headroom
from the Rockchip driver, otherwise the Mingjun panel cannot keep sync
to the display signal; from my testing, changing the hardcoded
PHY_STOP_WAIT_TIME from 0x20 to 0x2 (which is written by the vendor
driver) can also make this panel to work w/o the extra headroom. I want
some advice on whether to keep the extra headroom or changing the common
code to make the PHY_STOP_WAIT_TIME parameter adjustable.
Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
Icenowy Zheng (3):
dt-bindings: display: bridge: document TH1520 MIPI DSI controller
drm/bridge: dw-mipi-dsi: add PHY write function
drm/bridge: add TH1520 DesignWare MIPI DSI host controller
.../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 ++++
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 +
drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 +++++++++++++++++++++
drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++--
include/drm/bridge/dw_mipi_dsi.h | 3 +
7 files changed, 700 insertions(+), 53 deletions(-)
---
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
change-id: 20261030-th1520-dw-mipi-dsi-580d5b2792f9
Best regards,
--
Icenowy Zheng <zhengxingda@iscas.ac.cn>
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng T-Head TH1520 contains a DesignWare MIPI DSI host controller paired with an internal DesignWare PHY, as a similar design (but with a different PHY) with the Rockchip DW MIPI DSI. The functionality to write to the PHY is shared between these two implementations of DW MIPI DSI, so it's extracted from the Rockchip driver to the common code. (The same sequence could be seen in the HiSilicon Kirin DRM driver, but that one hasn't been converted to common DW MIPI DSI.) Tested on a Lichee Console 4A (with a Mingjun MJ070BI30IA2 panel) with further pending DT patches. One question remains: I have to copy the extra lane bandwidth headroom from the Rockchip driver, otherwise the Mingjun panel cannot keep sync to the display signal; from my testing, changing the hardcoded PHY_STOP_WAIT_TIME from 0x20 to 0x2 (which is written by the vendor driver) can also make this panel to work w/o the extra headroom. I want some advice on whether to keep the extra headroom or changing the common code to make the PHY_STOP_WAIT_TIME parameter adjustable. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- Icenowy Zheng (3): dt-bindings: display: bridge: document TH1520 MIPI DSI controller drm/bridge: dw-mipi-dsi: add PHY write function drm/bridge: add TH1520 DesignWare MIPI DSI host controller .../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 ++++ drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 + drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 +++++++++++++++++++++ drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++-- include/drm/bridge/dw_mipi_dsi.h | 3 + 7 files changed, 700 insertions(+), 53 deletions(-) --- base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222 change-id: 20261030-th1520-dw-mipi-dsi-580d5b2792f9 Best regards, -- Icenowy Zheng <zhengxingda@iscas.ac.cn> ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: devicetree, Han Gao, linux-kernel, dri-devel, linux-rockchip, Icenowy Zheng, linux-riscv, linux-arm-kernel T-Head TH1520 contains a DesignWare MIPI DSI host controller paired with an internal DesignWare PHY, as a similar design (but with a different PHY) with the Rockchip DW MIPI DSI. The functionality to write to the PHY is shared between these two implementations of DW MIPI DSI, so it's extracted from the Rockchip driver to the common code. (The same sequence could be seen in the HiSilicon Kirin DRM driver, but that one hasn't been converted to common DW MIPI DSI.) Tested on a Lichee Console 4A (with a Mingjun MJ070BI30IA2 panel) with further pending DT patches. One question remains: I have to copy the extra lane bandwidth headroom from the Rockchip driver, otherwise the Mingjun panel cannot keep sync to the display signal; from my testing, changing the hardcoded PHY_STOP_WAIT_TIME from 0x20 to 0x2 (which is written by the vendor driver) can also make this panel to work w/o the extra headroom. I want some advice on whether to keep the extra headroom or changing the common code to make the PHY_STOP_WAIT_TIME parameter adjustable. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- Icenowy Zheng (3): dt-bindings: display: bridge: document TH1520 MIPI DSI controller drm/bridge: dw-mipi-dsi: add PHY write function drm/bridge: add TH1520 DesignWare MIPI DSI host controller .../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 ++++ drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 + drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 +++++++++++++++++++++ drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++-- include/drm/bridge/dw_mipi_dsi.h | 3 + 7 files changed, 700 insertions(+), 53 deletions(-) --- base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222 change-id: 20261030-th1520-dw-mipi-dsi-580d5b2792f9 Best regards, -- Icenowy Zheng <zhengxingda@iscas.ac.cn> _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller 2026-10-30 7:47 ` Icenowy Zheng (?) @ 2026-10-30 7:47 ` Icenowy Zheng -1 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng T-Head TH1520 SoC contains two DesignWare MIPI DSI host controllers. Add a binding document for them, referencing the common dw-mipi-dsi binding file. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- .../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml new file mode 100644 index 0000000000000..23c249f1c1b71 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/thead,th1520-dw-mipi-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 DesignWare MIPI DSI Host Controller + +maintainers: + - Icenowy Zheng <zhengxingda@iscas.ac.cn> + +description: + The MIPI DSI host controller in TH1520 is a Synopsys DesignWare MIPI DSI + host controller paired with an integrated Synopsys MIPI D-PHY. + +allOf: + - $ref: snps,dw-mipi-dsi.yaml# + +properties: + compatible: + const: thead,th1520-dw-mipi-dsi + + interrupts: + maxItems: 1 + + clocks: + items: + - description: DSI controller APB clock + - description: PHY configuration clock + - description: PHY reference clock + - description: DSI controller input pixel clock gate + + clock-names: + items: + - const: pclk + - const: phy_cfg + - const: ref + - const: pixel + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the VO subsystem system controller. + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - interrupts + - syscon + - ports + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/thead,th1520-clk-ap.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/thead,th1520-reset.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dsi0: dsi@ffef500000 { + compatible = "thead,th1520-dw-mipi-dsi"; + reg = <0xff 0xef500000 0x0 0x1000>; + interrupts = <119 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_vo CLK_MIPI_DSI0_PCLK>, + <&clk_vo CLK_MIPI_DSI0_CFG>, + <&clk_vo CLK_MIPI_DSI0_REFCLK>, + <&clk_vo CLK_MIPIDSI0_PIXCLK>; + clock-names = "pclk", "phy_cfg", "ref", "pixel"; + resets = <&rst TH1520_RESET_ID_DSI0_APB>; + reset-names = "apb"; + syscon = <&syscon_vo>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi0_in: endpoint { + remote-endpoint = <&dpu_out_dpi0>; + }; + }; + + dsi0_out_port: port@1 { + reg = <1>; + + dsi0_out_panel: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + }; -- 2.56.0 _______________________________________________ Linux-rockchip mailing list Linux-rockchip@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-rockchip ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng T-Head TH1520 SoC contains two DesignWare MIPI DSI host controllers. Add a binding document for them, referencing the common dw-mipi-dsi binding file. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- .../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml new file mode 100644 index 0000000000000..23c249f1c1b71 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/thead,th1520-dw-mipi-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 DesignWare MIPI DSI Host Controller + +maintainers: + - Icenowy Zheng <zhengxingda@iscas.ac.cn> + +description: + The MIPI DSI host controller in TH1520 is a Synopsys DesignWare MIPI DSI + host controller paired with an integrated Synopsys MIPI D-PHY. + +allOf: + - $ref: snps,dw-mipi-dsi.yaml# + +properties: + compatible: + const: thead,th1520-dw-mipi-dsi + + interrupts: + maxItems: 1 + + clocks: + items: + - description: DSI controller APB clock + - description: PHY configuration clock + - description: PHY reference clock + - description: DSI controller input pixel clock gate + + clock-names: + items: + - const: pclk + - const: phy_cfg + - const: ref + - const: pixel + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the VO subsystem system controller. + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - interrupts + - syscon + - ports + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/thead,th1520-clk-ap.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/thead,th1520-reset.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dsi0: dsi@ffef500000 { + compatible = "thead,th1520-dw-mipi-dsi"; + reg = <0xff 0xef500000 0x0 0x1000>; + interrupts = <119 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_vo CLK_MIPI_DSI0_PCLK>, + <&clk_vo CLK_MIPI_DSI0_CFG>, + <&clk_vo CLK_MIPI_DSI0_REFCLK>, + <&clk_vo CLK_MIPIDSI0_PIXCLK>; + clock-names = "pclk", "phy_cfg", "ref", "pixel"; + resets = <&rst TH1520_RESET_ID_DSI0_APB>; + reset-names = "apb"; + syscon = <&syscon_vo>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi0_in: endpoint { + remote-endpoint = <&dpu_out_dpi0>; + }; + }; + + dsi0_out_port: port@1 { + reg = <1>; + + dsi0_out_panel: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + }; -- 2.56.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: devicetree, Han Gao, linux-kernel, dri-devel, linux-rockchip, Icenowy Zheng, linux-riscv, linux-arm-kernel T-Head TH1520 SoC contains two DesignWare MIPI DSI host controllers. Add a binding document for them, referencing the common dw-mipi-dsi binding file. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- .../display/bridge/thead,th1520-dw-mipi-dsi.yaml | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml new file mode 100644 index 0000000000000..23c249f1c1b71 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/thead,th1520-dw-mipi-dsi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/thead,th1520-dw-mipi-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 DesignWare MIPI DSI Host Controller + +maintainers: + - Icenowy Zheng <zhengxingda@iscas.ac.cn> + +description: + The MIPI DSI host controller in TH1520 is a Synopsys DesignWare MIPI DSI + host controller paired with an integrated Synopsys MIPI D-PHY. + +allOf: + - $ref: snps,dw-mipi-dsi.yaml# + +properties: + compatible: + const: thead,th1520-dw-mipi-dsi + + interrupts: + maxItems: 1 + + clocks: + items: + - description: DSI controller APB clock + - description: PHY configuration clock + - description: PHY reference clock + - description: DSI controller input pixel clock gate + + clock-names: + items: + - const: pclk + - const: phy_cfg + - const: ref + - const: pixel + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the VO subsystem system controller. + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - interrupts + - syscon + - ports + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/thead,th1520-clk-ap.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/thead,th1520-reset.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dsi0: dsi@ffef500000 { + compatible = "thead,th1520-dw-mipi-dsi"; + reg = <0xff 0xef500000 0x0 0x1000>; + interrupts = <119 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_vo CLK_MIPI_DSI0_PCLK>, + <&clk_vo CLK_MIPI_DSI0_CFG>, + <&clk_vo CLK_MIPI_DSI0_REFCLK>, + <&clk_vo CLK_MIPIDSI0_PIXCLK>; + clock-names = "pclk", "phy_cfg", "ref", "pixel"; + resets = <&rst TH1520_RESET_ID_DSI0_APB>; + reset-names = "apb"; + syscon = <&syscon_vo>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi0_in: endpoint { + remote-endpoint = <&dpu_out_dpi0>; + }; + }; + + dsi0_out_port: port@1 { + reg = <1>; + + dsi0_out_panel: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + }; -- 2.56.0 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function 2026-10-30 7:47 ` Icenowy Zheng (?) @ 2026-10-30 7:47 ` Icenowy Zheng -1 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng The dw_mipi_dsi_phy_write() function in the rockchip glue code only accesses registers defined in the DSI host controller, and the same process is also present in the kirin dw_drm_dsi driver (which isn't converted to use the common dw-mipi-dsi code yet) and downstream DSI driver for T-Head TH1520. Move this function from dw-mipi-dsi-rockchip driver to the common dw-mipi-dsi code, and change "test code" (which seems to be the name used for addresses by DW DPHY) to "address" (which seems to be the name used by the DW DSI host controller). Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 ++++++ drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++++++++++--------------- include/drm/bridge/dw_mipi_dsi.h | 3 + 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index 5dd475fcaacf8..87f210ba113ee 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1104,6 +1104,27 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .attach = dw_mipi_dsi_bridge_attach, }; +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data) +{ + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current address. Data is programmed + * internally by rising edge on TESTCLK. + */ + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(addr)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(data)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); +} +EXPORT_SYMBOL_GPL(dw_mipi_dsi_phy_write); + #ifdef CONFIG_DEBUG_FS static int dw_mipi_dsi_debugfs_write(void *data, u64 val) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 268d4993a04fd..469123620f302 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -379,28 +379,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val) writel(val, dsi->base + reg); } -static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi, - u8 test_code, - u8 test_data) -{ - /* - * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content - * is latched internally as the current test code. Test data is - * programmed internally by rising edge on TESTCLK. - */ - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_code)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_data)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); -} - /* * ns2bc - Nanoseconds to byte clock cycles */ @@ -420,6 +398,7 @@ static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns) static int dw_mipi_dsi_phy_init(void *priv_data) { struct dw_mipi_dsi_rockchip *dsi = priv_data; + struct dw_mipi_dsi *dmd = dsi->dmd; int ret, i, vco; if (dsi->phy) @@ -453,24 +432,24 @@ static int dw_mipi_dsi_phy_init(void *priv_data) return ret; } - dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, BYPASS_VCO_RANGE | VCO_RANGE_CON_SEL(vco) | VCO_IN_CAP_CON_LOW | REF_BIAS_CUR_SEL); - dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, + dw_mipi_dsi_phy_write(dmd, PLL_CP_CONTROL_PLL_LOCK_BYPASS, CP_CURRENT_SEL(dppa_map[i].icpctrl)); - dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_LPF_AND_CP_CONTROL, CP_PROGRAM_EN | LPF_PROGRAM_EN | LPF_RESISTORS_SEL(dppa_map[i].lpfctrl)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_DIVIDER_RATIO, INPUT_DIVIDER(dsi->input_div)); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_LOW_SEL(dsi->feedback_div) | LOW_PROGRAM_EN); /* @@ -479,53 +458,53 @@ static int dw_mipi_dsi_phy_init(void *priv_data) * and lab test results. * Only in this way can we get correct mipi phy pll frequency. */ - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_HIGH_SEL(dsi->feedback_div) | HIGH_PROGRAM_EN); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7)); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10)); - dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL, + dw_mipi_dsi_phy_write(dmd, BANDGAP_AND_BIAS_CONTROL, POWER_CONTROL | INTERNAL_REG_CURRENT | BIAS_BLOCK_ON | BANDGAP_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_LOW | TER_CAL_DONE | SETRD_MAX | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | SETRD_MAX | POWER_MANAGE | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, BIT(5) | (ns2bc(dsi, 60) + 7)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); clk_disable_unprepare(dsi->phy_cfg_clk); @@ -1224,6 +1203,7 @@ static int dw_mipi_dsi_dphy_configure(struct phy *phy, union phy_configure_opts static int dw_mipi_dsi_dphy_power_on(struct phy *phy) { struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy); + struct dw_mipi_dsi *dmd = dsi->dmd; int i, ret; DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n", @@ -1273,15 +1253,15 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy) * Configure hsfreqrange according to frequency values * Set clock lane and hsfreqrange by lane0(test code 0x44) */ - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_CLK, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_1, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_2, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_3, 0); /* Normal operation */ - dw_mipi_dsi_phy_write(dsi, 0x0, 0); + dw_mipi_dsi_phy_write(dmd, 0x0, 0); clk_disable_unprepare(dsi->phy_cfg_clk); clk_disable_unprepare(dsi->grf_clk); diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 65d5e68065e3d..16fa3ffd434f1 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -85,4 +85,7 @@ void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi); void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave); struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi); +/* PHY configuration */ +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data); + #endif /* __DW_MIPI_DSI__ */ -- 2.56.0 _______________________________________________ Linux-rockchip mailing list Linux-rockchip@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-rockchip ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng The dw_mipi_dsi_phy_write() function in the rockchip glue code only accesses registers defined in the DSI host controller, and the same process is also present in the kirin dw_drm_dsi driver (which isn't converted to use the common dw-mipi-dsi code yet) and downstream DSI driver for T-Head TH1520. Move this function from dw-mipi-dsi-rockchip driver to the common dw-mipi-dsi code, and change "test code" (which seems to be the name used for addresses by DW DPHY) to "address" (which seems to be the name used by the DW DSI host controller). Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 ++++++ drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++++++++++--------------- include/drm/bridge/dw_mipi_dsi.h | 3 + 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index 5dd475fcaacf8..87f210ba113ee 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1104,6 +1104,27 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .attach = dw_mipi_dsi_bridge_attach, }; +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data) +{ + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current address. Data is programmed + * internally by rising edge on TESTCLK. + */ + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(addr)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(data)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); +} +EXPORT_SYMBOL_GPL(dw_mipi_dsi_phy_write); + #ifdef CONFIG_DEBUG_FS static int dw_mipi_dsi_debugfs_write(void *data, u64 val) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 268d4993a04fd..469123620f302 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -379,28 +379,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val) writel(val, dsi->base + reg); } -static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi, - u8 test_code, - u8 test_data) -{ - /* - * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content - * is latched internally as the current test code. Test data is - * programmed internally by rising edge on TESTCLK. - */ - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_code)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_data)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); -} - /* * ns2bc - Nanoseconds to byte clock cycles */ @@ -420,6 +398,7 @@ static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns) static int dw_mipi_dsi_phy_init(void *priv_data) { struct dw_mipi_dsi_rockchip *dsi = priv_data; + struct dw_mipi_dsi *dmd = dsi->dmd; int ret, i, vco; if (dsi->phy) @@ -453,24 +432,24 @@ static int dw_mipi_dsi_phy_init(void *priv_data) return ret; } - dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, BYPASS_VCO_RANGE | VCO_RANGE_CON_SEL(vco) | VCO_IN_CAP_CON_LOW | REF_BIAS_CUR_SEL); - dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, + dw_mipi_dsi_phy_write(dmd, PLL_CP_CONTROL_PLL_LOCK_BYPASS, CP_CURRENT_SEL(dppa_map[i].icpctrl)); - dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_LPF_AND_CP_CONTROL, CP_PROGRAM_EN | LPF_PROGRAM_EN | LPF_RESISTORS_SEL(dppa_map[i].lpfctrl)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_DIVIDER_RATIO, INPUT_DIVIDER(dsi->input_div)); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_LOW_SEL(dsi->feedback_div) | LOW_PROGRAM_EN); /* @@ -479,53 +458,53 @@ static int dw_mipi_dsi_phy_init(void *priv_data) * and lab test results. * Only in this way can we get correct mipi phy pll frequency. */ - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_HIGH_SEL(dsi->feedback_div) | HIGH_PROGRAM_EN); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7)); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10)); - dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL, + dw_mipi_dsi_phy_write(dmd, BANDGAP_AND_BIAS_CONTROL, POWER_CONTROL | INTERNAL_REG_CURRENT | BIAS_BLOCK_ON | BANDGAP_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_LOW | TER_CAL_DONE | SETRD_MAX | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | SETRD_MAX | POWER_MANAGE | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, BIT(5) | (ns2bc(dsi, 60) + 7)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); clk_disable_unprepare(dsi->phy_cfg_clk); @@ -1224,6 +1203,7 @@ static int dw_mipi_dsi_dphy_configure(struct phy *phy, union phy_configure_opts static int dw_mipi_dsi_dphy_power_on(struct phy *phy) { struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy); + struct dw_mipi_dsi *dmd = dsi->dmd; int i, ret; DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n", @@ -1273,15 +1253,15 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy) * Configure hsfreqrange according to frequency values * Set clock lane and hsfreqrange by lane0(test code 0x44) */ - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_CLK, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_1, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_2, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_3, 0); /* Normal operation */ - dw_mipi_dsi_phy_write(dsi, 0x0, 0); + dw_mipi_dsi_phy_write(dmd, 0x0, 0); clk_disable_unprepare(dsi->phy_cfg_clk); clk_disable_unprepare(dsi->grf_clk); diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 65d5e68065e3d..16fa3ffd434f1 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -85,4 +85,7 @@ void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi); void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave); struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi); +/* PHY configuration */ +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data); + #endif /* __DW_MIPI_DSI__ */ -- 2.56.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: devicetree, Han Gao, linux-kernel, dri-devel, linux-rockchip, Icenowy Zheng, linux-riscv, linux-arm-kernel The dw_mipi_dsi_phy_write() function in the rockchip glue code only accesses registers defined in the DSI host controller, and the same process is also present in the kirin dw_drm_dsi driver (which isn't converted to use the common dw-mipi-dsi code yet) and downstream DSI driver for T-Head TH1520. Move this function from dw-mipi-dsi-rockchip driver to the common dw-mipi-dsi code, and change "test code" (which seems to be the name used for addresses by DW DPHY) to "address" (which seems to be the name used by the DW DSI host controller). Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 21 ++++++ drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 86 ++++++++++--------------- include/drm/bridge/dw_mipi_dsi.h | 3 + 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index 5dd475fcaacf8..87f210ba113ee 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1104,6 +1104,27 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .attach = dw_mipi_dsi_bridge_attach, }; +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data) +{ + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current address. Data is programmed + * internally by rising edge on TESTCLK. + */ + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(addr)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(data)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); +} +EXPORT_SYMBOL_GPL(dw_mipi_dsi_phy_write); + #ifdef CONFIG_DEBUG_FS static int dw_mipi_dsi_debugfs_write(void *data, u64 val) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 268d4993a04fd..469123620f302 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -379,28 +379,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val) writel(val, dsi->base + reg); } -static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi, - u8 test_code, - u8 test_data) -{ - /* - * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content - * is latched internally as the current test code. Test data is - * programmed internally by rising edge on TESTCLK. - */ - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_code)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_data)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); -} - /* * ns2bc - Nanoseconds to byte clock cycles */ @@ -420,6 +398,7 @@ static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns) static int dw_mipi_dsi_phy_init(void *priv_data) { struct dw_mipi_dsi_rockchip *dsi = priv_data; + struct dw_mipi_dsi *dmd = dsi->dmd; int ret, i, vco; if (dsi->phy) @@ -453,24 +432,24 @@ static int dw_mipi_dsi_phy_init(void *priv_data) return ret; } - dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, BYPASS_VCO_RANGE | VCO_RANGE_CON_SEL(vco) | VCO_IN_CAP_CON_LOW | REF_BIAS_CUR_SEL); - dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, + dw_mipi_dsi_phy_write(dmd, PLL_CP_CONTROL_PLL_LOCK_BYPASS, CP_CURRENT_SEL(dppa_map[i].icpctrl)); - dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_LPF_AND_CP_CONTROL, CP_PROGRAM_EN | LPF_PROGRAM_EN | LPF_RESISTORS_SEL(dppa_map[i].lpfctrl)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_DIVIDER_RATIO, INPUT_DIVIDER(dsi->input_div)); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_LOW_SEL(dsi->feedback_div) | LOW_PROGRAM_EN); /* @@ -479,53 +458,53 @@ static int dw_mipi_dsi_phy_init(void *priv_data) * and lab test results. * Only in this way can we get correct mipi phy pll frequency. */ - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + dw_mipi_dsi_phy_write(dmd, PLL_LOOP_DIVIDER_RATIO, LOOP_DIV_HIGH_SEL(dsi->feedback_div) | HIGH_PROGRAM_EN); - dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + dw_mipi_dsi_phy_write(dmd, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7)); - dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + dw_mipi_dsi_phy_write(dmd, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10)); - dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL, + dw_mipi_dsi_phy_write(dmd, BANDGAP_AND_BIAS_CONTROL, POWER_CONTROL | INTERNAL_REG_CURRENT | BIAS_BLOCK_ON | BANDGAP_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_LOW | TER_CAL_DONE | SETRD_MAX | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + dw_mipi_dsi_phy_write(dmd, TERMINATION_RESISTER_CONTROL, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | SETRD_MAX | POWER_MANAGE | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, BIT(5) | (ns2bc(dsi, 60) + 7)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8)); - dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, + dw_mipi_dsi_phy_write(dmd, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, BIT(5) | ns2bc(dsi, 100)); clk_disable_unprepare(dsi->phy_cfg_clk); @@ -1224,6 +1203,7 @@ static int dw_mipi_dsi_dphy_configure(struct phy *phy, union phy_configure_opts static int dw_mipi_dsi_dphy_power_on(struct phy *phy) { struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy); + struct dw_mipi_dsi *dmd = dsi->dmd; int i, ret; DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n", @@ -1273,15 +1253,15 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy) * Configure hsfreqrange according to frequency values * Set clock lane and hsfreqrange by lane0(test code 0x44) */ - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_CLK, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0); - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_1, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_2, 0); + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_3, 0); /* Normal operation */ - dw_mipi_dsi_phy_write(dsi, 0x0, 0); + dw_mipi_dsi_phy_write(dmd, 0x0, 0); clk_disable_unprepare(dsi->phy_cfg_clk); clk_disable_unprepare(dsi->grf_clk); diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 65d5e68065e3d..16fa3ffd434f1 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -85,4 +85,7 @@ void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi); void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave); struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi); +/* PHY configuration */ +void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 addr, u8 data); + #endif /* __DW_MIPI_DSI__ */ -- 2.56.0 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function 2026-10-30 7:47 ` Icenowy Zheng (?) (?) @ 2026-10-30 7:56 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-10-30 7:56 UTC (permalink / raw) To: Icenowy Zheng; +Cc: devicetree, dri-devel, robh, conor+dt Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] drm/rockchip: dsi: uninitialized dsi->dmd in dw_mipi_dsi_dphy_power_on() -- commit bdfb343509e10a4a68b931e1345f0a0c640cfd6f Author: Icenowy Zheng <zhengxingda@iscas.ac.cn> drm/bridge: dw-mipi-dsi: add PHY write function This commit moves dw_mipi_dsi_phy_write() from the Rockchip glue driver to the common dw-mipi-dsi bridge code and updates the Rockchip DSI driver to call the shared helper using its struct dw_mipi_dsi pointer. > diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c > index 268d4993a04fd..469123620f302 100644 > --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c > +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c [ ... ] > @@ -1224,6 +1203,7 @@ static int dw_mipi_dsi_dphy_configure(struct phy *phy, union phy_configure_opts > static int dw_mipi_dsi_dphy_power_on(struct phy *phy) > { > struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy); > + struct dw_mipi_dsi *dmd = dsi->dmd; > int i, ret; > > DRM_DEV_DEBUG(dsi->dev, "lanes %d - data_rate_mbps %u\n", > @@ -1273,15 +1253,15 @@ static int dw_mipi_dsi_dphy_power_on(struct phy *phy) > * Configure hsfreqrange according to frequency values > * Set clock lane and hsfreqrange by lane0(test code 0x44) > */ > - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0); > - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, > + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_CLK, 0); > + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_0, > HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); > - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0); > - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0); > - dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0); > + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_1, 0); > + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_2, 0); > + dw_mipi_dsi_phy_write(dmd, HS_RX_CONTROL_OF_LANE_3, 0); > > /* Normal operation */ > - dw_mipi_dsi_phy_write(dsi, 0x0, 0); > + dw_mipi_dsi_phy_write(dmd, 0x0, 0); [Severity: High] Can dsi->dmd be NULL or an ERR_PTR when dw_mipi_dsi_dphy_power_on() is called? In dw_mipi_dsi_rockchip_probe(), the PHY provider is registered before dsi->dmd is initialized: drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c:dw_mipi_dsi_rockchip_probe() { ... phy_set_drvdata(dsi->dphy, dsi); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(phy_provider)) return PTR_ERR(phy_provider); dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata); if (IS_ERR(dsi->dmd)) { ... } Previously, dw_mipi_dsi_dphy_power_on() wrote through dsi->base, which is already mapped before devm_of_phy_provider_register(). With this change, dw_mipi_dsi_dphy_power_on() passes dsi->dmd to dw_mipi_dsi_phy_write(), which dereferences dmd->base. If a PHY consumer (such as rkisp1 on RK3399) acquires the PHY and calls phy_power_on() -> dw_mipi_dsi_dphy_power_on() while dw_mipi_dsi_probe() is still running, or after dw_mipi_dsi_probe() fails and before devm unwinds the PHY provider registration, wouldn't dw_mipi_dsi_phy_write() dereference a NULL or ERR_PTR dmd? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030-th1520-dw-mipi-dsi-v1-0-753f34fd304f@iscas.ac.cn?part=2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller 2026-10-30 7:47 ` Icenowy Zheng (?) @ 2026-10-30 7:47 ` Icenowy Zheng -1 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng The T-Head TH1520 SoC contains a Synopsys DesignWare MIPI DSI host controller with an internal DesignWare MIPI D-PHY. Add a glue driver configuring clocks and PHY parameters for it. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 ++++++++++++++++++++++++++++ 3 files changed, 541 insertions(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 4a57d49b4c6d3..b005623ba26bf 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -375,6 +375,16 @@ config DRM_THEAD_TH1520_DW_HDMI Choose this to enable support for the internal HDMI bridge found on the T-Head TH1520 SoC. +config DRM_THEAD_TH1520_DW_MIPI_DSI + tristate "T-Head TH1520 DesignWare MIPI DSI bridge" + depends on OF + depends on COMMON_CLK + depends on ARCH_THEAD || COMPILE_TEST + select DRM_DW_MIPI_DSI + help + Choose this to enable support for the internal MIPI DSI bridge + found on the T-Head TH1520 SoC. + config DRM_TOSHIBA_TC358762 tristate "TC358762 DSI/DPI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 15cc821d85b7e..5af871ba5ce77 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o obj-$(CONFIG_DRM_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o +obj-$(CONFIG_DRM_THEAD_TH1520_DW_MIPI_DSI) += th1520-dw-mipi-dsi.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o diff --git a/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c new file mode 100644 index 0000000000000..e5c7c5d6dcc71 --- /dev/null +++ b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Author: + * Icenowy Zheng <zhengxingda@iscas.ac.cn> + * + * Based on dw_mipi_dsi-stm.c, which is: + * Copyright (C) STMicroelectronics SA 2017 + * + * Based on dw-mipi-dsi-rockchip.c, which is: + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Based on vendor kernel phy-dw-mipi-dphy.c, which is: + * Copyright (C) Alibaba Group Holding Limited. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> + +/* DPHY registers */ + +#define PLL_DELAY_LP_TX_START_LP11 0x02 +#define TXDINDLYTIME_9_2 GENMASK(7, 0) + +#define PLL_FSM_CNTRL 0x03 + +#define PLL_PROP_CHARGE_PUMP_CTRL 0x0e +#define PLL_PROP_CNTRL GENMASK(5, 0) + +#define PLL_INT_CHARGE_PUMP_CTRL 0x0f +#define PLL_INT_CNTRL GENMASK(5, 0) + +#define PLL_VCO_CTRL 0x12 +#define PLL_VCO_CNTRL_OVR_EN BIT(6) +#define PLL_VCO_CNTRL_OVR GENMASK(5, 0) + +#define PLL_GMP_CTRL_DIGITAL_TEST 0x13 +#define PLL_TESTLOCK BIT(7) +#define PLL_GMP_CNTRL GENMASK(5, 4) +#define TSTPLLDIG GENMASK(3, 1) +#define TSTPLLDIG_LOCK (0x0 << 1) +#define TSTPLLDIG_REFCLK (0x1 << 1) +#define TSTPLLDIG_FBCLK (0x2 << 1) +#define TSTPLLDIG_INT_CLKIN (0x3 << 1) +#define TSTPLLDIG_LOCK_FROM_DETECTOR (0x4 << 1) +#define TSTPLLDIG_OUTPUT (0x5 << 1) +#define TSTPLLDIG_BUF_VCOCLK (0x6 << 1) +#define TSTPLLDIG_UPDATEPLL (0x7 << 1) + +#define PLL_N_DIV_FSM_SIG 0x17 +#define PLL_N_OVR GENMASK(3, 0) + +#define PLL_M_DIV 0x18 +#define PLL_M_OVR_4_0 GENMASK(4, 0) +#define PLL_M_OVR_9_5 GENMASK(4, 0) + +#define PLL_N_AND_M_DIV_CTRL 0x19 +#define PLL_M_OVR_EN BIT(5) +#define PLL_N_OVR_EN BIT(4) + +#define PLL_CHARGE_PUMP_BIAS_CTRL 0x1c +#define PLL_CPBIAS_CNTRL GENMASK(6, 0) + +#define PLL_ANALOG_PROGRAM_CONTROL 0x1f +#define PLL_MPLL_PROG_5_0 GENMASK(5, 0) +#define PLL_MPLL_PROG_11_6 GENMASK(5, 0) +#define PLL_MPLL_PROG_16_12 GENMASK(4, 0) + +#define HS_FREQ_RANGE_OF_OPERATION 0x44 +#define HSFREQRANGE_OVR_EN BIT(7) +#define HSFREQRANGE_OVR GENMASK(6, 0) +#define HSFREQRANGE HSFREQRANGE_OVR + +#define PG_LP_BIAS_LANE0 0x4a +#define PRG_ON_LANE0 BIT(6) + +#define SR_FSM_OVR_CNTRL 0xa0 +#define SR_DDL_LOOP_CONF 0xa3 + +/* syscon registers */ + +#define MIPIDSI0_SYSREG 0x8 +#define MIPIDSI1_SYSREG 0xc +#define MIPIDSI_HSFREQRANGE GENMASK(9, 3) +#define MIPIDSI_CFGCLKFREQRANGE GENMASK(15, 10) +#define MIPIDSI_CFGCLKFREQRANGE_DEF (0x1c << 10) + +struct pll_hsfreqrange { + unsigned int max_mbps; + u8 hsfreqrange; +}; + +static const struct pll_hsfreqrange hsfreqrange_table[] = { + { 80, 0x00 }, + { 90, 0x10 }, + { 100, 0x20 }, + { 110, 0x30 }, + { 120, 0x01 }, + { 130, 0x11 }, + { 140, 0x21 }, + { 150, 0x31 }, + { 160, 0x02 }, + { 170, 0x12 }, + { 180, 0x22 }, + { 190, 0x32 }, + { 205, 0x03 }, + { 220, 0x13 }, + { 235, 0x23 }, + { 250, 0x33 }, + { 275, 0x04 }, + { 300, 0x14 }, + { 325, 0x25 }, + { 350, 0x35 }, + { 400, 0x05 }, + { 450, 0x16 }, + { 500, 0x26 }, + { 550, 0x37 }, + { 600, 0x07 }, + { 650, 0x18 }, + { 700, 0x28 }, + { 750, 0x39 }, + { 800, 0x09 }, + { 850, 0x19 }, + { 900, 0x29 }, + { 950, 0x3A }, + { 1000, 0x0A }, + { 1050, 0x1A }, + { 1100, 0x2A }, + { 1150, 0x3B }, + { 1200, 0x0B }, + { 1250, 0x1B }, + { 1300, 0x2B }, + { 1350, 0x3C }, + { 1400, 0x0C }, + { 1450, 0x1C }, + { 1500, 0x2C }, + { 1550, 0x3D }, + { 1600, 0x0D }, + { 1650, 0x1D }, + { 1700, 0x2E }, + { 1750, 0x3E }, + { 1800, 0x0E }, + { 1850, 0x1E }, + { 1900, 0x2F }, + { 1950, 0x3F }, + { 2000, 0x0F }, + { 2050, 0x40 }, + { 2100, 0x41 }, + { 2150, 0x42 }, + { 2200, 0x43 }, + { 2250, 0x44 }, + { 2300, 0x45 }, + { 2350, 0x46 }, + { 2400, 0x47 }, + { 2450, 0x48 }, + { 2500, 0x49 }, +}; + +struct hstt { + unsigned int max_mbps; + struct dw_mipi_dsi_dphy_timing timing; +}; + +#define HSTT(_max_mbps, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \ +{ \ + .max_mbps = _max_mbps, \ + .timing = { \ + .clk_lp2hs = _c_lp2hs, \ + .clk_hs2lp = _c_hs2lp, \ + .data_lp2hs = _d_lp2hs, \ + .data_hs2lp = _d_hs2lp, \ + } \ +} + +static struct hstt hstt_table[] = { + HSTT(80, 21, 17, 15, 10), + HSTT(90, 23, 17, 16, 10), + HSTT(100, 22, 17, 16, 10), + HSTT(110, 25, 18, 17, 11), + HSTT(120, 26, 20, 18, 11), + HSTT(130, 27, 19, 19, 11), + HSTT(140, 27, 19, 19, 11), + HSTT(150, 28, 20, 20, 12), + HSTT(160, 30, 21, 22, 13), + HSTT(170, 30, 21, 23, 13), + HSTT(180, 31, 21, 23, 13), + HSTT(190, 32, 22, 24, 13), + HSTT(205, 35, 22, 25, 13), + HSTT(220, 37, 26, 27, 15), + HSTT(235, 38, 28, 27, 16), + HSTT(250, 41, 29, 30, 17), + HSTT(275, 43, 29, 32, 18), + HSTT(300, 45, 32, 35, 19), + HSTT(325, 48, 33, 36, 18), + HSTT(350, 51, 35, 40, 20), + HSTT(400, 59, 37, 44, 21), + HSTT(450, 65, 40, 49, 23), + HSTT(500, 71, 41, 54, 24), + HSTT(550, 77, 44, 57, 26), + HSTT(600, 82, 46, 64, 27), + HSTT(650, 87, 48, 67, 28), + HSTT(700, 94, 52, 71, 29), + HSTT(750, 99, 52, 75, 31), + HSTT(800, 105, 55, 82, 32), + HSTT(850, 110, 58, 85, 32), + HSTT(900, 115, 58, 88, 35), + HSTT(950, 120, 62, 93, 36), + HSTT(1000, 128, 63, 99, 38), + HSTT(1050, 132, 65, 102, 38), + HSTT(1100, 138, 67, 106, 39), + HSTT(1150, 146, 69, 112, 42), + HSTT(1200, 151, 71, 117, 43), + HSTT(1250, 153, 74, 120, 45), + HSTT(1300, 160, 73, 124, 46), + HSTT(1350, 165, 76, 130, 47), + HSTT(1400, 172, 78, 134, 49), + HSTT(1450, 177, 80, 138, 49), + HSTT(1500, 183, 81, 143, 52), + HSTT(1550, 191, 84, 147, 52), + HSTT(1600, 194, 85, 152, 52), + HSTT(1650, 201, 86, 155, 53), + HSTT(1700, 208, 88, 161, 53), + HSTT(1750, 212, 89, 165, 53), + HSTT(1800, 220, 90, 171, 54), + HSTT(1850, 223, 92, 175, 54), + HSTT(1900, 231, 91, 180, 55), + HSTT(1950, 236, 95, 185, 56), + HSTT(2000, 243, 97, 190, 56), + HSTT(2050, 248, 99, 194, 58), + HSTT(2100, 252, 100, 199, 59), + HSTT(2150, 259, 102, 204, 61), + HSTT(2200, 266, 105, 210, 62), + HSTT(2250, 269, 109, 213, 63), + HSTT(2300, 272, 109, 217, 65), + HSTT(2350, 281, 112, 225, 66), + HSTT(2400, 283, 115, 226, 66), + HSTT(2450, 282, 115, 226, 67), + HSTT(2500, 281, 118, 227, 67), +}; + +struct dsi_chip_data { + u64 reg; + + u32 sysreg; +}; + +struct th1520_dw_mipi_dsi { + struct device *dev; + + struct dw_mipi_dsi *dmd; + struct dw_mipi_dsi_plat_data pdata; + const struct dsi_chip_data *cdata; + + struct regmap *syscon; + struct clk *clk_pix; + struct clk *clk_ref; + struct clk *clk_cfg; + + unsigned int lane_mbps; + u8 hsfreqrange; +}; + +static int dsi_phy_init(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + u32 val; + + val = FIELD_PREP(MIPIDSI_HSFREQRANGE, dsi->hsfreqrange) | + MIPIDSI_CFGCLKFREQRANGE_DEF; + + regmap_update_bits(dsi->syscon, dsi->cdata->sysreg, + MIPIDSI_HSFREQRANGE | MIPIDSI_CFGCLKFREQRANGE, val); + + /* disable slew rate calibration */ + dw_mipi_dsi_phy_write(dsi->dmd, SR_DDL_LOOP_CONF, 0); + dw_mipi_dsi_phy_write(dsi->dmd, SR_FSM_OVR_CNTRL, 1); + + /* configure prg_on_lane0 */ + dw_mipi_dsi_phy_write(dsi->dmd, PG_LP_BIAS_LANE0, PRG_ON_LANE0); + + /* configure PLL charge pump */ + if (dsi->lane_mbps >= 1150) + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0xE); + else + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0x8); + dw_mipi_dsi_phy_write(dsi->dmd, PLL_CHARGE_PUMP_BIAS_CTRL, 0x10); + + return 0; +} + +static void dsi_phy_power_on(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_prepare_enable(dsi->clk_pix); +} + +static void dsi_phy_power_off(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_disable_unprepare(dsi->clk_pix); +} + +static unsigned int +calculate_lane_mbps(const struct drm_display_mode *mode, + unsigned long mode_flags, + u32 lanes, u32 format) +{ + int bpp = mipi_dsi_pixel_format_to_bpp(format); + union phy_configure_opts phy_opts; + + /* + * FIXME: the extra bandwidth headroom logic mimics what the Rockchip + * driver does, and test shows that it's necessary for the hardware + * to work with the mainline dw-mipi-dsi common code. + */ + phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8, + bpp, lanes, + &phy_opts.mipi_dphy); + + return DIV_ROUND_UP(phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); +} + +static int +dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + int i; + + *lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + + for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++) + if (*lane_mbps <= hsfreqrange_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hsfreqrange_table)) + return -EINVAL; + + *lane_mbps = hsfreqrange_table[i].max_mbps; + dsi->lane_mbps = *lane_mbps; + dsi->hsfreqrange = hsfreqrange_table[i].hsfreqrange; + + return 0; +} + +static int +dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hstt_table); i++) + if (lane_mbps <= hstt_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hstt_table)) + return -EINVAL; + + *timing = hstt_table[i].timing; + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops th1520_dw_mipi_dsi_phy_ops = { + .init = dsi_phy_init, + .power_on = dsi_phy_power_on, + .power_off = dsi_phy_power_off, + .get_lane_mbps = dsi_get_lane_mbps, + .get_timing = dsi_phy_get_timing, +}; + +static enum drm_mode_status +dsi_mode_valid(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format) +{ + unsigned int lane_mbps, max_mbps; + + lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + max_mbps = hsfreqrange_table[ARRAY_SIZE(hsfreqrange_table) - 1].max_mbps; + + if (lane_mbps > max_mbps) + return MODE_NOCLOCK; + + return MODE_OK; +} + +static struct dsi_chip_data th1520_chip_data[] = { + { + .reg = 0xffef500000, + .sysreg = MIPIDSI0_SYSREG, + + /* + * TODO: The user manual shows a mux register for DSI0 but the + * vendor driver doesn't touch it either. + */ + }, + { + .reg = 0xffef510000, + .sysreg = MIPIDSI1_SYSREG, + }, + { /* sentinel */ }, +}; + +static int th1520_dw_mipi_dsi_probe(struct platform_device *pdev) +{ + const struct dsi_chip_data *cdata; + struct device *dev = &pdev->dev; + struct th1520_dw_mipi_dsi *dsi; + struct resource *res; + void __iomem *base; + int i, ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + dsi->dev = dev; + + dsi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(dsi->syscon)) { + ret = PTR_ERR(dsi->syscon); + dev_err_probe(dev, ret, "Unable to get system controller regmap\n"); + return ret; + } + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) { + dev_err_probe(dev, ret, "Unable to get DSI registers\n"); + return PTR_ERR(base); + } + + cdata = of_device_get_match_data(dev); + i = 0; + while (cdata[i].reg) { + if (cdata[i].reg == res->start) { + dsi->cdata = &cdata[i]; + break; + } + + i++; + } + + if (!dsi->cdata) { + dev_err(dev, "Failed to find chip data for this controller\n"); + return -EINVAL; + } + + dsi->clk_ref = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(dsi->clk_ref)) { + ret = PTR_ERR(dsi->clk_ref); + dev_err_probe(dev, ret, "Unable to get PHY reference clock\n"); + return ret; + } + + dsi->clk_cfg = devm_clk_get_enabled(dev, "phy_cfg"); + if (IS_ERR(dsi->clk_cfg)) { + ret = PTR_ERR(dsi->clk_cfg); + dev_err_probe(dev, ret, "Unable to get PHY configuration clock\n"); + return ret; + } + + dsi->clk_pix = devm_clk_get(dev, "pixel"); + if (IS_ERR(dsi->clk_pix)) { + ret = PTR_ERR(dsi->clk_pix); + dev_err_probe(dev, ret, "Unable to get PHY pixel clock\n"); + return ret; + } + + dsi->pdata.base = base; + dsi->pdata.priv_data = dsi; + dsi->pdata.max_data_lanes = 4; + dsi->pdata.phy_ops = &th1520_dw_mipi_dsi_phy_ops; + dsi->pdata.mode_valid = dsi_mode_valid; + + platform_set_drvdata(pdev, dsi); + + dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata); + if (IS_ERR(dsi->dmd)) { + ret = PTR_ERR(dsi->dmd); + dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static void th1520_dw_mipi_dsi_remove(struct platform_device *pdev) +{ + struct th1520_dw_mipi_dsi *dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(dsi->dmd); +} + +static const struct of_device_id th1520_dw_mipi_dsi_dt_ids[] = { + { .compatible = "thead,th1520-dw-mipi-dsi", .data = &th1520_chip_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, th1520_dw_mipi_dsi_dt_ids); + +static struct platform_driver th1520_dw_mipi_dsi_driver = { + .probe = th1520_dw_mipi_dsi_probe, + .remove = th1520_dw_mipi_dsi_remove, + .driver = { + .of_match_table = th1520_dw_mipi_dsi_dt_ids, + .name = "th1520-dw-mipi-dsi", + }, +}; + +module_platform_driver(th1520_dw_mipi_dsi_driver); + +MODULE_AUTHOR("Icenowy Zheng <zhengxingda@iscas.ac.cn>"); +MODULE_DESCRIPTION("T-Head TH1520 MIPI DSI host controller driver"); +MODULE_LICENSE("GPL"); -- 2.56.0 _______________________________________________ Linux-rockchip mailing list Linux-rockchip@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-rockchip ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: dri-devel, devicetree, linux-kernel, linux-rockchip, linux-arm-kernel, linux-riscv, Icneowy Zheng, Han Gao, Icenowy Zheng The T-Head TH1520 SoC contains a Synopsys DesignWare MIPI DSI host controller with an internal DesignWare MIPI D-PHY. Add a glue driver configuring clocks and PHY parameters for it. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 ++++++++++++++++++++++++++++ 3 files changed, 541 insertions(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 4a57d49b4c6d3..b005623ba26bf 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -375,6 +375,16 @@ config DRM_THEAD_TH1520_DW_HDMI Choose this to enable support for the internal HDMI bridge found on the T-Head TH1520 SoC. +config DRM_THEAD_TH1520_DW_MIPI_DSI + tristate "T-Head TH1520 DesignWare MIPI DSI bridge" + depends on OF + depends on COMMON_CLK + depends on ARCH_THEAD || COMPILE_TEST + select DRM_DW_MIPI_DSI + help + Choose this to enable support for the internal MIPI DSI bridge + found on the T-Head TH1520 SoC. + config DRM_TOSHIBA_TC358762 tristate "TC358762 DSI/DPI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 15cc821d85b7e..5af871ba5ce77 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o obj-$(CONFIG_DRM_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o +obj-$(CONFIG_DRM_THEAD_TH1520_DW_MIPI_DSI) += th1520-dw-mipi-dsi.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o diff --git a/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c new file mode 100644 index 0000000000000..e5c7c5d6dcc71 --- /dev/null +++ b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Author: + * Icenowy Zheng <zhengxingda@iscas.ac.cn> + * + * Based on dw_mipi_dsi-stm.c, which is: + * Copyright (C) STMicroelectronics SA 2017 + * + * Based on dw-mipi-dsi-rockchip.c, which is: + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Based on vendor kernel phy-dw-mipi-dphy.c, which is: + * Copyright (C) Alibaba Group Holding Limited. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> + +/* DPHY registers */ + +#define PLL_DELAY_LP_TX_START_LP11 0x02 +#define TXDINDLYTIME_9_2 GENMASK(7, 0) + +#define PLL_FSM_CNTRL 0x03 + +#define PLL_PROP_CHARGE_PUMP_CTRL 0x0e +#define PLL_PROP_CNTRL GENMASK(5, 0) + +#define PLL_INT_CHARGE_PUMP_CTRL 0x0f +#define PLL_INT_CNTRL GENMASK(5, 0) + +#define PLL_VCO_CTRL 0x12 +#define PLL_VCO_CNTRL_OVR_EN BIT(6) +#define PLL_VCO_CNTRL_OVR GENMASK(5, 0) + +#define PLL_GMP_CTRL_DIGITAL_TEST 0x13 +#define PLL_TESTLOCK BIT(7) +#define PLL_GMP_CNTRL GENMASK(5, 4) +#define TSTPLLDIG GENMASK(3, 1) +#define TSTPLLDIG_LOCK (0x0 << 1) +#define TSTPLLDIG_REFCLK (0x1 << 1) +#define TSTPLLDIG_FBCLK (0x2 << 1) +#define TSTPLLDIG_INT_CLKIN (0x3 << 1) +#define TSTPLLDIG_LOCK_FROM_DETECTOR (0x4 << 1) +#define TSTPLLDIG_OUTPUT (0x5 << 1) +#define TSTPLLDIG_BUF_VCOCLK (0x6 << 1) +#define TSTPLLDIG_UPDATEPLL (0x7 << 1) + +#define PLL_N_DIV_FSM_SIG 0x17 +#define PLL_N_OVR GENMASK(3, 0) + +#define PLL_M_DIV 0x18 +#define PLL_M_OVR_4_0 GENMASK(4, 0) +#define PLL_M_OVR_9_5 GENMASK(4, 0) + +#define PLL_N_AND_M_DIV_CTRL 0x19 +#define PLL_M_OVR_EN BIT(5) +#define PLL_N_OVR_EN BIT(4) + +#define PLL_CHARGE_PUMP_BIAS_CTRL 0x1c +#define PLL_CPBIAS_CNTRL GENMASK(6, 0) + +#define PLL_ANALOG_PROGRAM_CONTROL 0x1f +#define PLL_MPLL_PROG_5_0 GENMASK(5, 0) +#define PLL_MPLL_PROG_11_6 GENMASK(5, 0) +#define PLL_MPLL_PROG_16_12 GENMASK(4, 0) + +#define HS_FREQ_RANGE_OF_OPERATION 0x44 +#define HSFREQRANGE_OVR_EN BIT(7) +#define HSFREQRANGE_OVR GENMASK(6, 0) +#define HSFREQRANGE HSFREQRANGE_OVR + +#define PG_LP_BIAS_LANE0 0x4a +#define PRG_ON_LANE0 BIT(6) + +#define SR_FSM_OVR_CNTRL 0xa0 +#define SR_DDL_LOOP_CONF 0xa3 + +/* syscon registers */ + +#define MIPIDSI0_SYSREG 0x8 +#define MIPIDSI1_SYSREG 0xc +#define MIPIDSI_HSFREQRANGE GENMASK(9, 3) +#define MIPIDSI_CFGCLKFREQRANGE GENMASK(15, 10) +#define MIPIDSI_CFGCLKFREQRANGE_DEF (0x1c << 10) + +struct pll_hsfreqrange { + unsigned int max_mbps; + u8 hsfreqrange; +}; + +static const struct pll_hsfreqrange hsfreqrange_table[] = { + { 80, 0x00 }, + { 90, 0x10 }, + { 100, 0x20 }, + { 110, 0x30 }, + { 120, 0x01 }, + { 130, 0x11 }, + { 140, 0x21 }, + { 150, 0x31 }, + { 160, 0x02 }, + { 170, 0x12 }, + { 180, 0x22 }, + { 190, 0x32 }, + { 205, 0x03 }, + { 220, 0x13 }, + { 235, 0x23 }, + { 250, 0x33 }, + { 275, 0x04 }, + { 300, 0x14 }, + { 325, 0x25 }, + { 350, 0x35 }, + { 400, 0x05 }, + { 450, 0x16 }, + { 500, 0x26 }, + { 550, 0x37 }, + { 600, 0x07 }, + { 650, 0x18 }, + { 700, 0x28 }, + { 750, 0x39 }, + { 800, 0x09 }, + { 850, 0x19 }, + { 900, 0x29 }, + { 950, 0x3A }, + { 1000, 0x0A }, + { 1050, 0x1A }, + { 1100, 0x2A }, + { 1150, 0x3B }, + { 1200, 0x0B }, + { 1250, 0x1B }, + { 1300, 0x2B }, + { 1350, 0x3C }, + { 1400, 0x0C }, + { 1450, 0x1C }, + { 1500, 0x2C }, + { 1550, 0x3D }, + { 1600, 0x0D }, + { 1650, 0x1D }, + { 1700, 0x2E }, + { 1750, 0x3E }, + { 1800, 0x0E }, + { 1850, 0x1E }, + { 1900, 0x2F }, + { 1950, 0x3F }, + { 2000, 0x0F }, + { 2050, 0x40 }, + { 2100, 0x41 }, + { 2150, 0x42 }, + { 2200, 0x43 }, + { 2250, 0x44 }, + { 2300, 0x45 }, + { 2350, 0x46 }, + { 2400, 0x47 }, + { 2450, 0x48 }, + { 2500, 0x49 }, +}; + +struct hstt { + unsigned int max_mbps; + struct dw_mipi_dsi_dphy_timing timing; +}; + +#define HSTT(_max_mbps, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \ +{ \ + .max_mbps = _max_mbps, \ + .timing = { \ + .clk_lp2hs = _c_lp2hs, \ + .clk_hs2lp = _c_hs2lp, \ + .data_lp2hs = _d_lp2hs, \ + .data_hs2lp = _d_hs2lp, \ + } \ +} + +static struct hstt hstt_table[] = { + HSTT(80, 21, 17, 15, 10), + HSTT(90, 23, 17, 16, 10), + HSTT(100, 22, 17, 16, 10), + HSTT(110, 25, 18, 17, 11), + HSTT(120, 26, 20, 18, 11), + HSTT(130, 27, 19, 19, 11), + HSTT(140, 27, 19, 19, 11), + HSTT(150, 28, 20, 20, 12), + HSTT(160, 30, 21, 22, 13), + HSTT(170, 30, 21, 23, 13), + HSTT(180, 31, 21, 23, 13), + HSTT(190, 32, 22, 24, 13), + HSTT(205, 35, 22, 25, 13), + HSTT(220, 37, 26, 27, 15), + HSTT(235, 38, 28, 27, 16), + HSTT(250, 41, 29, 30, 17), + HSTT(275, 43, 29, 32, 18), + HSTT(300, 45, 32, 35, 19), + HSTT(325, 48, 33, 36, 18), + HSTT(350, 51, 35, 40, 20), + HSTT(400, 59, 37, 44, 21), + HSTT(450, 65, 40, 49, 23), + HSTT(500, 71, 41, 54, 24), + HSTT(550, 77, 44, 57, 26), + HSTT(600, 82, 46, 64, 27), + HSTT(650, 87, 48, 67, 28), + HSTT(700, 94, 52, 71, 29), + HSTT(750, 99, 52, 75, 31), + HSTT(800, 105, 55, 82, 32), + HSTT(850, 110, 58, 85, 32), + HSTT(900, 115, 58, 88, 35), + HSTT(950, 120, 62, 93, 36), + HSTT(1000, 128, 63, 99, 38), + HSTT(1050, 132, 65, 102, 38), + HSTT(1100, 138, 67, 106, 39), + HSTT(1150, 146, 69, 112, 42), + HSTT(1200, 151, 71, 117, 43), + HSTT(1250, 153, 74, 120, 45), + HSTT(1300, 160, 73, 124, 46), + HSTT(1350, 165, 76, 130, 47), + HSTT(1400, 172, 78, 134, 49), + HSTT(1450, 177, 80, 138, 49), + HSTT(1500, 183, 81, 143, 52), + HSTT(1550, 191, 84, 147, 52), + HSTT(1600, 194, 85, 152, 52), + HSTT(1650, 201, 86, 155, 53), + HSTT(1700, 208, 88, 161, 53), + HSTT(1750, 212, 89, 165, 53), + HSTT(1800, 220, 90, 171, 54), + HSTT(1850, 223, 92, 175, 54), + HSTT(1900, 231, 91, 180, 55), + HSTT(1950, 236, 95, 185, 56), + HSTT(2000, 243, 97, 190, 56), + HSTT(2050, 248, 99, 194, 58), + HSTT(2100, 252, 100, 199, 59), + HSTT(2150, 259, 102, 204, 61), + HSTT(2200, 266, 105, 210, 62), + HSTT(2250, 269, 109, 213, 63), + HSTT(2300, 272, 109, 217, 65), + HSTT(2350, 281, 112, 225, 66), + HSTT(2400, 283, 115, 226, 66), + HSTT(2450, 282, 115, 226, 67), + HSTT(2500, 281, 118, 227, 67), +}; + +struct dsi_chip_data { + u64 reg; + + u32 sysreg; +}; + +struct th1520_dw_mipi_dsi { + struct device *dev; + + struct dw_mipi_dsi *dmd; + struct dw_mipi_dsi_plat_data pdata; + const struct dsi_chip_data *cdata; + + struct regmap *syscon; + struct clk *clk_pix; + struct clk *clk_ref; + struct clk *clk_cfg; + + unsigned int lane_mbps; + u8 hsfreqrange; +}; + +static int dsi_phy_init(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + u32 val; + + val = FIELD_PREP(MIPIDSI_HSFREQRANGE, dsi->hsfreqrange) | + MIPIDSI_CFGCLKFREQRANGE_DEF; + + regmap_update_bits(dsi->syscon, dsi->cdata->sysreg, + MIPIDSI_HSFREQRANGE | MIPIDSI_CFGCLKFREQRANGE, val); + + /* disable slew rate calibration */ + dw_mipi_dsi_phy_write(dsi->dmd, SR_DDL_LOOP_CONF, 0); + dw_mipi_dsi_phy_write(dsi->dmd, SR_FSM_OVR_CNTRL, 1); + + /* configure prg_on_lane0 */ + dw_mipi_dsi_phy_write(dsi->dmd, PG_LP_BIAS_LANE0, PRG_ON_LANE0); + + /* configure PLL charge pump */ + if (dsi->lane_mbps >= 1150) + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0xE); + else + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0x8); + dw_mipi_dsi_phy_write(dsi->dmd, PLL_CHARGE_PUMP_BIAS_CTRL, 0x10); + + return 0; +} + +static void dsi_phy_power_on(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_prepare_enable(dsi->clk_pix); +} + +static void dsi_phy_power_off(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_disable_unprepare(dsi->clk_pix); +} + +static unsigned int +calculate_lane_mbps(const struct drm_display_mode *mode, + unsigned long mode_flags, + u32 lanes, u32 format) +{ + int bpp = mipi_dsi_pixel_format_to_bpp(format); + union phy_configure_opts phy_opts; + + /* + * FIXME: the extra bandwidth headroom logic mimics what the Rockchip + * driver does, and test shows that it's necessary for the hardware + * to work with the mainline dw-mipi-dsi common code. + */ + phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8, + bpp, lanes, + &phy_opts.mipi_dphy); + + return DIV_ROUND_UP(phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); +} + +static int +dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + int i; + + *lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + + for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++) + if (*lane_mbps <= hsfreqrange_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hsfreqrange_table)) + return -EINVAL; + + *lane_mbps = hsfreqrange_table[i].max_mbps; + dsi->lane_mbps = *lane_mbps; + dsi->hsfreqrange = hsfreqrange_table[i].hsfreqrange; + + return 0; +} + +static int +dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hstt_table); i++) + if (lane_mbps <= hstt_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hstt_table)) + return -EINVAL; + + *timing = hstt_table[i].timing; + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops th1520_dw_mipi_dsi_phy_ops = { + .init = dsi_phy_init, + .power_on = dsi_phy_power_on, + .power_off = dsi_phy_power_off, + .get_lane_mbps = dsi_get_lane_mbps, + .get_timing = dsi_phy_get_timing, +}; + +static enum drm_mode_status +dsi_mode_valid(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format) +{ + unsigned int lane_mbps, max_mbps; + + lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + max_mbps = hsfreqrange_table[ARRAY_SIZE(hsfreqrange_table) - 1].max_mbps; + + if (lane_mbps > max_mbps) + return MODE_NOCLOCK; + + return MODE_OK; +} + +static struct dsi_chip_data th1520_chip_data[] = { + { + .reg = 0xffef500000, + .sysreg = MIPIDSI0_SYSREG, + + /* + * TODO: The user manual shows a mux register for DSI0 but the + * vendor driver doesn't touch it either. + */ + }, + { + .reg = 0xffef510000, + .sysreg = MIPIDSI1_SYSREG, + }, + { /* sentinel */ }, +}; + +static int th1520_dw_mipi_dsi_probe(struct platform_device *pdev) +{ + const struct dsi_chip_data *cdata; + struct device *dev = &pdev->dev; + struct th1520_dw_mipi_dsi *dsi; + struct resource *res; + void __iomem *base; + int i, ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + dsi->dev = dev; + + dsi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(dsi->syscon)) { + ret = PTR_ERR(dsi->syscon); + dev_err_probe(dev, ret, "Unable to get system controller regmap\n"); + return ret; + } + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) { + dev_err_probe(dev, ret, "Unable to get DSI registers\n"); + return PTR_ERR(base); + } + + cdata = of_device_get_match_data(dev); + i = 0; + while (cdata[i].reg) { + if (cdata[i].reg == res->start) { + dsi->cdata = &cdata[i]; + break; + } + + i++; + } + + if (!dsi->cdata) { + dev_err(dev, "Failed to find chip data for this controller\n"); + return -EINVAL; + } + + dsi->clk_ref = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(dsi->clk_ref)) { + ret = PTR_ERR(dsi->clk_ref); + dev_err_probe(dev, ret, "Unable to get PHY reference clock\n"); + return ret; + } + + dsi->clk_cfg = devm_clk_get_enabled(dev, "phy_cfg"); + if (IS_ERR(dsi->clk_cfg)) { + ret = PTR_ERR(dsi->clk_cfg); + dev_err_probe(dev, ret, "Unable to get PHY configuration clock\n"); + return ret; + } + + dsi->clk_pix = devm_clk_get(dev, "pixel"); + if (IS_ERR(dsi->clk_pix)) { + ret = PTR_ERR(dsi->clk_pix); + dev_err_probe(dev, ret, "Unable to get PHY pixel clock\n"); + return ret; + } + + dsi->pdata.base = base; + dsi->pdata.priv_data = dsi; + dsi->pdata.max_data_lanes = 4; + dsi->pdata.phy_ops = &th1520_dw_mipi_dsi_phy_ops; + dsi->pdata.mode_valid = dsi_mode_valid; + + platform_set_drvdata(pdev, dsi); + + dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata); + if (IS_ERR(dsi->dmd)) { + ret = PTR_ERR(dsi->dmd); + dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static void th1520_dw_mipi_dsi_remove(struct platform_device *pdev) +{ + struct th1520_dw_mipi_dsi *dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(dsi->dmd); +} + +static const struct of_device_id th1520_dw_mipi_dsi_dt_ids[] = { + { .compatible = "thead,th1520-dw-mipi-dsi", .data = &th1520_chip_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, th1520_dw_mipi_dsi_dt_ids); + +static struct platform_driver th1520_dw_mipi_dsi_driver = { + .probe = th1520_dw_mipi_dsi_probe, + .remove = th1520_dw_mipi_dsi_remove, + .driver = { + .of_match_table = th1520_dw_mipi_dsi_dt_ids, + .name = "th1520-dw-mipi-dsi", + }, +}; + +module_platform_driver(th1520_dw_mipi_dsi_driver); + +MODULE_AUTHOR("Icenowy Zheng <zhengxingda@iscas.ac.cn>"); +MODULE_DESCRIPTION("T-Head TH1520 MIPI DSI host controller driver"); +MODULE_LICENSE("GPL"); -- 2.56.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller @ 2026-10-30 7:47 ` Icenowy Zheng 0 siblings, 0 replies; 14+ messages in thread From: Icenowy Zheng @ 2026-10-30 7:47 UTC (permalink / raw) To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sandy Huang, Heiko Stübner, Andy Yan Cc: devicetree, Han Gao, linux-kernel, dri-devel, linux-rockchip, Icenowy Zheng, linux-riscv, linux-arm-kernel The T-Head TH1520 SoC contains a Synopsys DesignWare MIPI DSI host controller with an internal DesignWare MIPI D-PHY. Add a glue driver configuring clocks and PHY parameters for it. Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn> --- drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c | 530 ++++++++++++++++++++++++++++ 3 files changed, 541 insertions(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 4a57d49b4c6d3..b005623ba26bf 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -375,6 +375,16 @@ config DRM_THEAD_TH1520_DW_HDMI Choose this to enable support for the internal HDMI bridge found on the T-Head TH1520 SoC. +config DRM_THEAD_TH1520_DW_MIPI_DSI + tristate "T-Head TH1520 DesignWare MIPI DSI bridge" + depends on OF + depends on COMMON_CLK + depends on ARCH_THEAD || COMPILE_TEST + select DRM_DW_MIPI_DSI + help + Choose this to enable support for the internal MIPI DSI bridge + found on the T-Head TH1520 SoC. + config DRM_TOSHIBA_TC358762 tristate "TC358762 DSI/DPI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 15cc821d85b7e..5af871ba5ce77 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o obj-$(CONFIG_DRM_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_DRM_THEAD_TH1520_DW_HDMI) += th1520-dw-hdmi.o +obj-$(CONFIG_DRM_THEAD_TH1520_DW_MIPI_DSI) += th1520-dw-mipi-dsi.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o diff --git a/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c new file mode 100644 index 0000000000000..e5c7c5d6dcc71 --- /dev/null +++ b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Author: + * Icenowy Zheng <zhengxingda@iscas.ac.cn> + * + * Based on dw_mipi_dsi-stm.c, which is: + * Copyright (C) STMicroelectronics SA 2017 + * + * Based on dw-mipi-dsi-rockchip.c, which is: + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Based on vendor kernel phy-dw-mipi-dphy.c, which is: + * Copyright (C) Alibaba Group Holding Limited. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> + +/* DPHY registers */ + +#define PLL_DELAY_LP_TX_START_LP11 0x02 +#define TXDINDLYTIME_9_2 GENMASK(7, 0) + +#define PLL_FSM_CNTRL 0x03 + +#define PLL_PROP_CHARGE_PUMP_CTRL 0x0e +#define PLL_PROP_CNTRL GENMASK(5, 0) + +#define PLL_INT_CHARGE_PUMP_CTRL 0x0f +#define PLL_INT_CNTRL GENMASK(5, 0) + +#define PLL_VCO_CTRL 0x12 +#define PLL_VCO_CNTRL_OVR_EN BIT(6) +#define PLL_VCO_CNTRL_OVR GENMASK(5, 0) + +#define PLL_GMP_CTRL_DIGITAL_TEST 0x13 +#define PLL_TESTLOCK BIT(7) +#define PLL_GMP_CNTRL GENMASK(5, 4) +#define TSTPLLDIG GENMASK(3, 1) +#define TSTPLLDIG_LOCK (0x0 << 1) +#define TSTPLLDIG_REFCLK (0x1 << 1) +#define TSTPLLDIG_FBCLK (0x2 << 1) +#define TSTPLLDIG_INT_CLKIN (0x3 << 1) +#define TSTPLLDIG_LOCK_FROM_DETECTOR (0x4 << 1) +#define TSTPLLDIG_OUTPUT (0x5 << 1) +#define TSTPLLDIG_BUF_VCOCLK (0x6 << 1) +#define TSTPLLDIG_UPDATEPLL (0x7 << 1) + +#define PLL_N_DIV_FSM_SIG 0x17 +#define PLL_N_OVR GENMASK(3, 0) + +#define PLL_M_DIV 0x18 +#define PLL_M_OVR_4_0 GENMASK(4, 0) +#define PLL_M_OVR_9_5 GENMASK(4, 0) + +#define PLL_N_AND_M_DIV_CTRL 0x19 +#define PLL_M_OVR_EN BIT(5) +#define PLL_N_OVR_EN BIT(4) + +#define PLL_CHARGE_PUMP_BIAS_CTRL 0x1c +#define PLL_CPBIAS_CNTRL GENMASK(6, 0) + +#define PLL_ANALOG_PROGRAM_CONTROL 0x1f +#define PLL_MPLL_PROG_5_0 GENMASK(5, 0) +#define PLL_MPLL_PROG_11_6 GENMASK(5, 0) +#define PLL_MPLL_PROG_16_12 GENMASK(4, 0) + +#define HS_FREQ_RANGE_OF_OPERATION 0x44 +#define HSFREQRANGE_OVR_EN BIT(7) +#define HSFREQRANGE_OVR GENMASK(6, 0) +#define HSFREQRANGE HSFREQRANGE_OVR + +#define PG_LP_BIAS_LANE0 0x4a +#define PRG_ON_LANE0 BIT(6) + +#define SR_FSM_OVR_CNTRL 0xa0 +#define SR_DDL_LOOP_CONF 0xa3 + +/* syscon registers */ + +#define MIPIDSI0_SYSREG 0x8 +#define MIPIDSI1_SYSREG 0xc +#define MIPIDSI_HSFREQRANGE GENMASK(9, 3) +#define MIPIDSI_CFGCLKFREQRANGE GENMASK(15, 10) +#define MIPIDSI_CFGCLKFREQRANGE_DEF (0x1c << 10) + +struct pll_hsfreqrange { + unsigned int max_mbps; + u8 hsfreqrange; +}; + +static const struct pll_hsfreqrange hsfreqrange_table[] = { + { 80, 0x00 }, + { 90, 0x10 }, + { 100, 0x20 }, + { 110, 0x30 }, + { 120, 0x01 }, + { 130, 0x11 }, + { 140, 0x21 }, + { 150, 0x31 }, + { 160, 0x02 }, + { 170, 0x12 }, + { 180, 0x22 }, + { 190, 0x32 }, + { 205, 0x03 }, + { 220, 0x13 }, + { 235, 0x23 }, + { 250, 0x33 }, + { 275, 0x04 }, + { 300, 0x14 }, + { 325, 0x25 }, + { 350, 0x35 }, + { 400, 0x05 }, + { 450, 0x16 }, + { 500, 0x26 }, + { 550, 0x37 }, + { 600, 0x07 }, + { 650, 0x18 }, + { 700, 0x28 }, + { 750, 0x39 }, + { 800, 0x09 }, + { 850, 0x19 }, + { 900, 0x29 }, + { 950, 0x3A }, + { 1000, 0x0A }, + { 1050, 0x1A }, + { 1100, 0x2A }, + { 1150, 0x3B }, + { 1200, 0x0B }, + { 1250, 0x1B }, + { 1300, 0x2B }, + { 1350, 0x3C }, + { 1400, 0x0C }, + { 1450, 0x1C }, + { 1500, 0x2C }, + { 1550, 0x3D }, + { 1600, 0x0D }, + { 1650, 0x1D }, + { 1700, 0x2E }, + { 1750, 0x3E }, + { 1800, 0x0E }, + { 1850, 0x1E }, + { 1900, 0x2F }, + { 1950, 0x3F }, + { 2000, 0x0F }, + { 2050, 0x40 }, + { 2100, 0x41 }, + { 2150, 0x42 }, + { 2200, 0x43 }, + { 2250, 0x44 }, + { 2300, 0x45 }, + { 2350, 0x46 }, + { 2400, 0x47 }, + { 2450, 0x48 }, + { 2500, 0x49 }, +}; + +struct hstt { + unsigned int max_mbps; + struct dw_mipi_dsi_dphy_timing timing; +}; + +#define HSTT(_max_mbps, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \ +{ \ + .max_mbps = _max_mbps, \ + .timing = { \ + .clk_lp2hs = _c_lp2hs, \ + .clk_hs2lp = _c_hs2lp, \ + .data_lp2hs = _d_lp2hs, \ + .data_hs2lp = _d_hs2lp, \ + } \ +} + +static struct hstt hstt_table[] = { + HSTT(80, 21, 17, 15, 10), + HSTT(90, 23, 17, 16, 10), + HSTT(100, 22, 17, 16, 10), + HSTT(110, 25, 18, 17, 11), + HSTT(120, 26, 20, 18, 11), + HSTT(130, 27, 19, 19, 11), + HSTT(140, 27, 19, 19, 11), + HSTT(150, 28, 20, 20, 12), + HSTT(160, 30, 21, 22, 13), + HSTT(170, 30, 21, 23, 13), + HSTT(180, 31, 21, 23, 13), + HSTT(190, 32, 22, 24, 13), + HSTT(205, 35, 22, 25, 13), + HSTT(220, 37, 26, 27, 15), + HSTT(235, 38, 28, 27, 16), + HSTT(250, 41, 29, 30, 17), + HSTT(275, 43, 29, 32, 18), + HSTT(300, 45, 32, 35, 19), + HSTT(325, 48, 33, 36, 18), + HSTT(350, 51, 35, 40, 20), + HSTT(400, 59, 37, 44, 21), + HSTT(450, 65, 40, 49, 23), + HSTT(500, 71, 41, 54, 24), + HSTT(550, 77, 44, 57, 26), + HSTT(600, 82, 46, 64, 27), + HSTT(650, 87, 48, 67, 28), + HSTT(700, 94, 52, 71, 29), + HSTT(750, 99, 52, 75, 31), + HSTT(800, 105, 55, 82, 32), + HSTT(850, 110, 58, 85, 32), + HSTT(900, 115, 58, 88, 35), + HSTT(950, 120, 62, 93, 36), + HSTT(1000, 128, 63, 99, 38), + HSTT(1050, 132, 65, 102, 38), + HSTT(1100, 138, 67, 106, 39), + HSTT(1150, 146, 69, 112, 42), + HSTT(1200, 151, 71, 117, 43), + HSTT(1250, 153, 74, 120, 45), + HSTT(1300, 160, 73, 124, 46), + HSTT(1350, 165, 76, 130, 47), + HSTT(1400, 172, 78, 134, 49), + HSTT(1450, 177, 80, 138, 49), + HSTT(1500, 183, 81, 143, 52), + HSTT(1550, 191, 84, 147, 52), + HSTT(1600, 194, 85, 152, 52), + HSTT(1650, 201, 86, 155, 53), + HSTT(1700, 208, 88, 161, 53), + HSTT(1750, 212, 89, 165, 53), + HSTT(1800, 220, 90, 171, 54), + HSTT(1850, 223, 92, 175, 54), + HSTT(1900, 231, 91, 180, 55), + HSTT(1950, 236, 95, 185, 56), + HSTT(2000, 243, 97, 190, 56), + HSTT(2050, 248, 99, 194, 58), + HSTT(2100, 252, 100, 199, 59), + HSTT(2150, 259, 102, 204, 61), + HSTT(2200, 266, 105, 210, 62), + HSTT(2250, 269, 109, 213, 63), + HSTT(2300, 272, 109, 217, 65), + HSTT(2350, 281, 112, 225, 66), + HSTT(2400, 283, 115, 226, 66), + HSTT(2450, 282, 115, 226, 67), + HSTT(2500, 281, 118, 227, 67), +}; + +struct dsi_chip_data { + u64 reg; + + u32 sysreg; +}; + +struct th1520_dw_mipi_dsi { + struct device *dev; + + struct dw_mipi_dsi *dmd; + struct dw_mipi_dsi_plat_data pdata; + const struct dsi_chip_data *cdata; + + struct regmap *syscon; + struct clk *clk_pix; + struct clk *clk_ref; + struct clk *clk_cfg; + + unsigned int lane_mbps; + u8 hsfreqrange; +}; + +static int dsi_phy_init(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + u32 val; + + val = FIELD_PREP(MIPIDSI_HSFREQRANGE, dsi->hsfreqrange) | + MIPIDSI_CFGCLKFREQRANGE_DEF; + + regmap_update_bits(dsi->syscon, dsi->cdata->sysreg, + MIPIDSI_HSFREQRANGE | MIPIDSI_CFGCLKFREQRANGE, val); + + /* disable slew rate calibration */ + dw_mipi_dsi_phy_write(dsi->dmd, SR_DDL_LOOP_CONF, 0); + dw_mipi_dsi_phy_write(dsi->dmd, SR_FSM_OVR_CNTRL, 1); + + /* configure prg_on_lane0 */ + dw_mipi_dsi_phy_write(dsi->dmd, PG_LP_BIAS_LANE0, PRG_ON_LANE0); + + /* configure PLL charge pump */ + if (dsi->lane_mbps >= 1150) + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0xE); + else + dw_mipi_dsi_phy_write(dsi->dmd, PLL_PROP_CHARGE_PUMP_CTRL, 0x8); + dw_mipi_dsi_phy_write(dsi->dmd, PLL_CHARGE_PUMP_BIAS_CTRL, 0x10); + + return 0; +} + +static void dsi_phy_power_on(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_prepare_enable(dsi->clk_pix); +} + +static void dsi_phy_power_off(void *priv_data) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + + clk_disable_unprepare(dsi->clk_pix); +} + +static unsigned int +calculate_lane_mbps(const struct drm_display_mode *mode, + unsigned long mode_flags, + u32 lanes, u32 format) +{ + int bpp = mipi_dsi_pixel_format_to_bpp(format); + union phy_configure_opts phy_opts; + + /* + * FIXME: the extra bandwidth headroom logic mimics what the Rockchip + * driver does, and test shows that it's necessary for the hardware + * to work with the mainline dw-mipi-dsi common code. + */ + phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8, + bpp, lanes, + &phy_opts.mipi_dphy); + + return DIV_ROUND_UP(phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); +} + +static int +dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct th1520_dw_mipi_dsi *dsi = priv_data; + int i; + + *lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + + for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++) + if (*lane_mbps <= hsfreqrange_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hsfreqrange_table)) + return -EINVAL; + + *lane_mbps = hsfreqrange_table[i].max_mbps; + dsi->lane_mbps = *lane_mbps; + dsi->hsfreqrange = hsfreqrange_table[i].hsfreqrange; + + return 0; +} + +static int +dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hstt_table); i++) + if (lane_mbps <= hstt_table[i].max_mbps) + break; + + if (i == ARRAY_SIZE(hstt_table)) + return -EINVAL; + + *timing = hstt_table[i].timing; + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops th1520_dw_mipi_dsi_phy_ops = { + .init = dsi_phy_init, + .power_on = dsi_phy_power_on, + .power_off = dsi_phy_power_off, + .get_lane_mbps = dsi_get_lane_mbps, + .get_timing = dsi_phy_get_timing, +}; + +static enum drm_mode_status +dsi_mode_valid(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format) +{ + unsigned int lane_mbps, max_mbps; + + lane_mbps = calculate_lane_mbps(mode, mode_flags, lanes, format); + max_mbps = hsfreqrange_table[ARRAY_SIZE(hsfreqrange_table) - 1].max_mbps; + + if (lane_mbps > max_mbps) + return MODE_NOCLOCK; + + return MODE_OK; +} + +static struct dsi_chip_data th1520_chip_data[] = { + { + .reg = 0xffef500000, + .sysreg = MIPIDSI0_SYSREG, + + /* + * TODO: The user manual shows a mux register for DSI0 but the + * vendor driver doesn't touch it either. + */ + }, + { + .reg = 0xffef510000, + .sysreg = MIPIDSI1_SYSREG, + }, + { /* sentinel */ }, +}; + +static int th1520_dw_mipi_dsi_probe(struct platform_device *pdev) +{ + const struct dsi_chip_data *cdata; + struct device *dev = &pdev->dev; + struct th1520_dw_mipi_dsi *dsi; + struct resource *res; + void __iomem *base; + int i, ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + dsi->dev = dev; + + dsi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(dsi->syscon)) { + ret = PTR_ERR(dsi->syscon); + dev_err_probe(dev, ret, "Unable to get system controller regmap\n"); + return ret; + } + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) { + dev_err_probe(dev, ret, "Unable to get DSI registers\n"); + return PTR_ERR(base); + } + + cdata = of_device_get_match_data(dev); + i = 0; + while (cdata[i].reg) { + if (cdata[i].reg == res->start) { + dsi->cdata = &cdata[i]; + break; + } + + i++; + } + + if (!dsi->cdata) { + dev_err(dev, "Failed to find chip data for this controller\n"); + return -EINVAL; + } + + dsi->clk_ref = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(dsi->clk_ref)) { + ret = PTR_ERR(dsi->clk_ref); + dev_err_probe(dev, ret, "Unable to get PHY reference clock\n"); + return ret; + } + + dsi->clk_cfg = devm_clk_get_enabled(dev, "phy_cfg"); + if (IS_ERR(dsi->clk_cfg)) { + ret = PTR_ERR(dsi->clk_cfg); + dev_err_probe(dev, ret, "Unable to get PHY configuration clock\n"); + return ret; + } + + dsi->clk_pix = devm_clk_get(dev, "pixel"); + if (IS_ERR(dsi->clk_pix)) { + ret = PTR_ERR(dsi->clk_pix); + dev_err_probe(dev, ret, "Unable to get PHY pixel clock\n"); + return ret; + } + + dsi->pdata.base = base; + dsi->pdata.priv_data = dsi; + dsi->pdata.max_data_lanes = 4; + dsi->pdata.phy_ops = &th1520_dw_mipi_dsi_phy_ops; + dsi->pdata.mode_valid = dsi_mode_valid; + + platform_set_drvdata(pdev, dsi); + + dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata); + if (IS_ERR(dsi->dmd)) { + ret = PTR_ERR(dsi->dmd); + dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static void th1520_dw_mipi_dsi_remove(struct platform_device *pdev) +{ + struct th1520_dw_mipi_dsi *dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(dsi->dmd); +} + +static const struct of_device_id th1520_dw_mipi_dsi_dt_ids[] = { + { .compatible = "thead,th1520-dw-mipi-dsi", .data = &th1520_chip_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, th1520_dw_mipi_dsi_dt_ids); + +static struct platform_driver th1520_dw_mipi_dsi_driver = { + .probe = th1520_dw_mipi_dsi_probe, + .remove = th1520_dw_mipi_dsi_remove, + .driver = { + .of_match_table = th1520_dw_mipi_dsi_dt_ids, + .name = "th1520-dw-mipi-dsi", + }, +}; + +module_platform_driver(th1520_dw_mipi_dsi_driver); + +MODULE_AUTHOR("Icenowy Zheng <zhengxingda@iscas.ac.cn>"); +MODULE_DESCRIPTION("T-Head TH1520 MIPI DSI host controller driver"); +MODULE_LICENSE("GPL"); -- 2.56.0 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller 2026-10-30 7:47 ` Icenowy Zheng (?) (?) @ 2026-10-30 7:58 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-10-30 7:58 UTC (permalink / raw) To: Icenowy Zheng; +Cc: dri-devel, devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] drm/bridge: missing GENERIC_PHY_MIPI_DPHY and MFD_SYSCON select in Kconfig - [Medium] drm/bridge: uninitialized ret in th1520_dw_mipi_dsi_probe() error path - [Medium] drm/bridge: 32-bit signed overflow in calculate_lane_mbps() -- commit 1daf55a66c6c757f635f5bb068b6e103e284e178 Author: Icenowy Zheng <zhengxingda@iscas.ac.cn> drm/bridge: add TH1520 DesignWare MIPI DSI host controller This commit adds a glue driver for the Synopsys DesignWare MIPI DSI host controller and internal D-PHY on the T-Head TH1520 SoC. > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index 4a57d49b4c6d3..b005623ba26bf 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -375,6 +375,16 @@ config DRM_THEAD_TH1520_DW_HDMI > Choose this to enable support for the internal HDMI bridge found > on the T-Head TH1520 SoC. > > +config DRM_THEAD_TH1520_DW_MIPI_DSI > + tristate "T-Head TH1520 DesignWare MIPI DSI bridge" > + depends on OF > + depends on COMMON_CLK > + depends on ARCH_THEAD || COMPILE_TEST > + select DRM_DW_MIPI_DSI > + help > + Choose this to enable support for the internal MIPI DSI bridge > + found on the T-Head TH1520 SoC. [Severity: Medium] Should DRM_THEAD_TH1520_DW_MIPI_DSI in drivers/gpu/drm/bridge/Kconfig also select GENERIC_PHY_MIPI_DPHY and MFD_SYSCON? In drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c, calculate_lane_mbps() calls phy_mipi_dphy_get_default_config(), which has no inline stub fallback when CONFIG_GENERIC_PHY_MIPI_DPHY is disabled. Because GENERIC_PHY_MIPI_DPHY is a promptless symbol and DRM_DW_MIPI_DSI does not select it, building with CONFIG_DRM_THEAD_TH1520_DW_MIPI_DSI enabled will fail at link time if no other enabled driver selects GENERIC_PHY_MIPI_DPHY. Similarly, th1520_dw_mipi_dsi_probe() calls syscon_regmap_lookup_by_phandle() and dsi_phy_init() calls regmap_update_bits(). When CONFIG_MFD_SYSCON is not enabled, syscon_regmap_lookup_by_phandle() falls back to a stub returning ERR_PTR(-ENOTSUPP), causing probe to unconditionally fail. > diff --git a/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c > new file mode 100644 > index 0000000000000..e5c7c5d6dcc71 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/th1520-dw-mipi-dsi.c [ ... ] > +static unsigned int > +calculate_lane_mbps(const struct drm_display_mode *mode, > + unsigned long mode_flags, > + u32 lanes, u32 format) > +{ > + int bpp = mipi_dsi_pixel_format_to_bpp(format); > + union phy_configure_opts phy_opts; > + > + /* > + * FIXME: the extra bandwidth headroom logic mimics what the Rockchip > + * driver does, and test shows that it's necessary for the hardware > + * to work with the mainline dw-mipi-dsi common code. > + */ > + phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8, > + bpp, lanes, > + &phy_opts.mipi_dphy); [Severity: Medium] Can the pixel clock multiplication in calculate_lane_mbps() overflow a 32-bit signed int? Since mode->clock in struct drm_display_mode is an int (in kHz) and 1000, 10, and 8 are signed int literals, mode->clock * 1000 * 10 is evaluated in 32-bit signed arithmetic before being converted to the unsigned long parameter of phy_mipi_dphy_get_default_config(). When mode->clock is 214749 kHz or higher, mode->clock * 1000 * 10 exceeds INT_MAX. With 4 lanes and up to 2500 Mbps per lane in hsfreqrange_table, the hardware supports 24-bpp modes up to 333333 kHz, which will wrap to a negative value, sign-extend to a large unsigned long, and be rejected by dsi_mode_valid() and dsi_get_lane_mbps(). Higher out-of-range pixel clocks (such as 594000 kHz) can also wrap around to a small positive value and falsely pass dsi_mode_valid(). Would using 1000UL (or mul_u32_u32()) avoid this overflow? > + > + return DIV_ROUND_UP(phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); > +} [ ... ] > +static int th1520_dw_mipi_dsi_probe(struct platform_device *pdev) > +{ > + const struct dsi_chip_data *cdata; > + struct device *dev = &pdev->dev; > + struct th1520_dw_mipi_dsi *dsi; > + struct resource *res; > + void __iomem *base; > + int i, ret; > + > + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); > + if (!dsi) > + return -ENOMEM; > + > + dsi->dev = dev; > + > + dsi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); > + if (IS_ERR(dsi->syscon)) { > + ret = PTR_ERR(dsi->syscon); > + dev_err_probe(dev, ret, "Unable to get system controller regmap\n"); > + return ret; > + } > + > + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); > + if (IS_ERR(base)) { > + dev_err_probe(dev, ret, "Unable to get DSI registers\n"); > + return PTR_ERR(base);\n> + } [Severity: Medium] In th1520_dw_mipi_dsi_probe(), if syscon_regmap_lookup_by_phandle() succeeds and devm_platform_get_and_ioremap_resource() fails, isn't ret uninitialized when passed to dev_err_probe()? Should this pass PTR_ERR(base) (or return dev_err_probe(dev, PTR_ERR(base), ...)) instead? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030-th1520-dw-mipi-dsi-v1-0-753f34fd304f@iscas.ac.cn?part=3 ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-10-30 7:58 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-30 7:47 [PATCH 0/3] drm/bridge: add glue driver for T-Head TH1520 DW MIPI DSI Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` [PATCH 1/3] dt-bindings: display: bridge: document TH1520 MIPI DSI controller Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` [PATCH 2/3] drm/bridge: dw-mipi-dsi: add PHY write function Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:56 ` sashiko-bot 2026-10-30 7:47 ` [PATCH 3/3] drm/bridge: add TH1520 DesignWare MIPI DSI host controller Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:47 ` Icenowy Zheng 2026-10-30 7:58 ` sashiko-bot
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.