* [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver
@ 2026-10-16 8:56 Jason Yang
2026-10-16 8:56 ` [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions Jason Yang
` (7 more replies)
0 siblings, 8 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The Samsung MIPI D-/C-PHY block on RK3588 and RK3576 has a transmitter
for DSI and a receiver for CSI-2. Both can be used at the same time.
Mainline only supports the transmitter and models the block as a single
struct phy. The PHY core refcounts power_on() per struct phy, so the
receiver cannot be powered independently of it, and the single
devicetree cell selects the mode, so the provider cannot tell which of
the two a consumer refers to.
This series registers a struct phy for each of them, adds a second
devicetree cell with generic PHY_DIRECTION_TX and PHY_DIRECTION_RX
constants so that a consumer can select one of them, and implements the
D-PHY receiver. A single cell keeps referring to the transmitter, so
existing device trees are unaffected.
1/8 adds PHY_DIRECTION_TX and PHY_DIRECTION_RX to the generic header
2/8 allows a second cell in the rockchip,rk3588-mipi-dcphy binding
to select the transmitter or the receiver
3/8 moves the block-level setup to runtime resume
4/8 names the transmitter helpers and ops (no functional change)
5/8 registers the receiver's struct phy and takes the second cell
in of_xlate()
6/8 implements the D-PHY receiver bring-up
7/8 switches the RK3588 DC-PHYs to two cells and lets DSI select
the transmitter
8/8 adds the MIPI CSI-2 receiver nodes on RK3588 and connects them
to VICAP
Testing
=======
Tested on an RK3588 board, with a DSI panel and an IMX219 camera on
DCPHY0, and an LVDS panel behind a DSI-to-LVDS bridge on DCPHY1.
- The transmitter and the receiver run at the same time: with 7/8
and 8/8, both panels light from boot, and the camera stream is
shown live on the DCPHY0 panel.
- The two can be powered independently: the DCPHY0 panel is turned
off and back on during a 1200-frame capture, and the capture
completes.
- The receiver does not need the transmitter: with dsi0 disabled,
the camera alone brings the block up and streams, which relies
on 3/8 moving the block-level setup to runtime resume.
- Existing device trees keep working: with a single PHY_TYPE_DPHY
cell in dsi0 as before 7/8, the DCPHY0 panel lights.
No WARNING, underflow, "Internal error" or Oops in dmesg in any of
these.
Not covered: the receiver of DCPHY1, the 3- and 4-lane receiver
paths, and RK3576.
The DSI panels only light with the U-Boot SPLL fix [1] applied:
without it the bootloader leaves the SPLL at its 351 MHz bootrom
default while the devicetree describes it as a 702 MHz fixed clock,
halving the rate of both DSI hosts. The receiver path is unaffected;
the camera captures exercise it independently.
[1] https://lore.kernel.org/u-boot/20260804002643.2308515-3-jonas@kwiboo.se/
---
Changes in v5:
- Rebase onto phy/next. The runtime PM fix this series applied on top
of is now commit 261e84f9278f ("phy: rockchip-samsung-dcphy: Enable
runtime PM at PHY core level"), so it is no longer carried here.
- 1/8: use PHY_DIRECTION_TX and PHY_DIRECTION_RX instead of
PHY_TYPE_DSI and PHY_TYPE_CSI. (Michael)
- 2/8: use PHY_DIRECTION_* for the second cell and rewrite the
description.
- Drop v4's 5/7: it has no benefit over the switch, and it moved a
DC-PHY shared function into a D-PHY helper. (Michael)
- 5/8: rename protocol to direction in of_xlate() to follow 1/8.
- 6/8: use a switch in the receiver's power_on() and power_off(), as
the transmitter does.
- New patches 7/8 and 8/8: switch the RK3588 DC-PHYs to two cells and
add the MIPI CSI-2 receiver nodes.
- Link to v4: https://lore.kernel.org/r/20260821-dcphy-rx-v1-v4-0-f1797634233d@gmail.com
Changes in v4:
- Put the second cell back: the first cell selects the mode, the
second the transmitter or the receiver.
- Drop the private use count and mutex; the runtime PM usage count
does the same job.
- New patch 3/7: move the block-level setup to runtime resume.
- Split v3's 3/5 into 4/7 and 5/7.
- of_xlate() rejects a mix of D-PHY and C-PHY consumers.
- The runtime PM probe-order fix is now a separate patch [1] that this
series applies on top of.
- Bryan O'Donoghue's Reviewed-by from v2 is not carried over; that
patch was rewritten.
- Link to v3: https://lore.kernel.org/r/20260810-dcphy-rx-v1-v3-0-a2d25c29adfc@gmail.com
---
Jason Yang (8):
dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions
dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX
phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume
phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops
phy: rockchip-samsung-dcphy: Add a second PHY for the receiver
phy: rockchip-samsung-dcphy: Add MIPI D-PHY receiver support
arm64: dts: rockchip: use two phy cells for the rk3588 mipi dcphys
arm64: dts: rockchip: add mipi csi-2 receiver nodes for the dcphys on rk3588
.../bindings/phy/rockchip,rk3588-mipi-dcphy.yaml | 20 +-
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 76 ++-
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 529 +++++++++++++++++++--
include/dt-bindings/phy/phy.h | 3 +
4 files changed, 580 insertions(+), 48 deletions(-)
---
base-commit: 8f56fdf3d8cb9f1dbdc93bf9ac816698aa6f70b7
change-id: 20260721-dcphy-rx-v1-9af57f3edfd9
Best regards,
--
Jason Yang <jason98166@gmail.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 10:38 ` Bryan O'Donoghue
2026-10-16 8:56 ` [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX Jason Yang
` (6 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
A MIPI D-PHY or C-PHY can be a transmitter or a receiver. Some PHY
blocks have one of each and can use them at the same time, so one PHY
node offers two PHYs. The existing cell, PHY_TYPE_DPHY or
PHY_TYPE_CPHY, selects the mode of the whole block, not one of the two
PHYs.
Add PHY_DIRECTION_TX and PHY_DIRECTION_RX for a cell that selects the
transmitter or the receiver. The first user is the Rockchip RK3588 MIPI
DC-PHY, where the transmitter serves a DSI host and the receiver a CSI-2
host.
Suggested-by: Michael Riesch <michael.riesch@collabora.com>
Link: https://lore.kernel.org/r/7425ac83-f6d8-4443-a26e-a47f9306aa24@collabora.com
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
include/dt-bindings/phy/phy.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h
index 979b5dfd8353..9ead74d9a172 100644
--- a/include/dt-bindings/phy/phy.h
+++ b/include/dt-bindings/phy/phy.h
@@ -29,4 +29,7 @@
#define PHY_POL_INVERT 1
#define PHY_POL_AUTO 2
+#define PHY_DIRECTION_TX 0
+#define PHY_DIRECTION_RX 1
+
#endif /* _DT_BINDINGS_PHY */
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
2026-10-16 8:56 ` [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 10:43 ` Bryan O'Donoghue
2026-10-16 14:01 ` Rob Herring
2026-10-16 8:56 ` [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume Jason Yang
` (5 subsequent siblings)
7 siblings, 2 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The Samsung MIPI D-/C-PHY block on RK3588 and RK3576 has a transmitter
for DSI and a receiver for CSI-2, and two consumers can use them at the
same time. The single cell of this binding selects the mode and refers
to the transmitter, so the receiver cannot be described.
Allow a second cell that selects the transmitter or the receiver. With
D-PHY:
/* the MIPI DSI host, driving the transmitter */
phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_TX>;
/* the MIPI CSI-2 host, driving the receiver */
phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_RX>;
and with C-PHY:
phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_TX>;
phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_RX>;
The transmitter and the receiver cannot operate in different modes [1],
so the first cell must match in both consumers.
Two cells describe the receiver as well. A single cell keeps referring
to the transmitter, so existing device trees are unaffected.
[1] RK3588 TRM: section 22.1 (overview): "The TX and RX only support
operate in the same PHY mode."
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
.../bindings/phy/rockchip,rk3588-mipi-dcphy.yaml | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
index c8ff5ba22a86..6e8cc56e4986 100644
--- a/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
+++ b/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
@@ -20,11 +20,25 @@ properties:
maxItems: 1
"#phy-cells":
- const: 1
+ enum: [1, 2]
description: |
- Argument is mode to operate in. Supported modes are:
+ The first cell selects the mode to operate in:
- PHY_TYPE_DPHY
- PHY_TYPE_CPHY
+
+ A single cell refers to the transmitter and is kept for existing
+ device trees.
+
+ The second cell selects the transmitter or the receiver, so that a
+ MIPI DSI host and a MIPI CSI-2 host can be described as independent
+ consumers of the same PHY node:
+ - PHY_DIRECTION_TX
+ - PHY_DIRECTION_RX
+
+ The transmitter and the receiver cannot operate in different modes,
+ so when both are described, the first cell must match in both
+ consumers.
+
See include/dt-bindings/phy/phy.h for constants.
clocks:
@@ -82,6 +96,6 @@ examples:
<&cru SRST_S_MIPI_DCPHY0>;
reset-names = "m_phy", "apb", "grf", "s_phy";
rockchip,grf = <&mipidcphy0_grf>;
- #phy-cells = <1>;
+ #phy-cells = <2>;
};
};
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
2026-10-16 8:56 ` [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions Jason Yang
2026-10-16 8:56 ` [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 9:09 ` sashiko-bot
2026-10-16 10:46 ` Bryan O'Donoghue
2026-10-16 8:56 ` [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops Jason Yang
` (4 subsequent siblings)
7 siblings, 2 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The APB reset and the BIAS references have to be treated as block-level
[1]: both the transmitter and the receiver need them, and they only have
to be programmed once. They are programmed from the transmitter's
power-on path today. A later change registers one struct phy for each of
them, and a second consumer coming up would then reset the block
underneath a PHY that is already running.
Program them from runtime resume instead, where whichever of the two
comes up first brings the block up and a second one no longer resets it.
This puts the BIAS writes ahead of the per-PHY reset assert, where the
TRM's worked example has them after [1]. That is safe: neither M_RESETN
nor S_RESETN covers the BIAS registers, and the values written are those
registers' reset defaults.
The output level select in BIAS_CON4 stays on the power-on path: it
selects 400mV for D-PHY and 530mV for C-PHY, and the driver core resumes
a runtime-PM supplier before the consumer's probe runs, so runtime resume
cannot yet know which of the two applies.
[1] RK3588 TRM: sections 22.4.1 (address map), 22.4.2 (reset values),
22.6.1.2 (reset) and 22.6.4.1 (D-PHY transmitter start-up)
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 25 ++++++++++++-----------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index 7382043e1f31..1471a1563d2a 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -980,13 +980,6 @@ static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *sams
I_LADDER_1_00V);
regmap_write(samsung->regmap, BIAS_CON2, REG_325M_325MV | REG_LP_400M_400MV |
REG_400M_400MV | REG_645M_645MV);
-
- /* default output voltage select:
- * dphy: 400mv
- * cphy: 530mv
- */
- regmap_update_bits(samsung->regmap, BIAS_CON4,
- I_MUX_SEL_MASK, I_MUX_400MV);
}
static void samsung_mipi_dphy_lane_enable(struct samsung_mipi_dcphy *samsung)
@@ -1338,7 +1331,13 @@ static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
reset_control_assert(samsung->m_phy_rst);
- samsung_mipi_dcphy_bias_block_enable(samsung);
+ /* default output voltage select:
+ * dphy: 400mv
+ * cphy: 530mv
+ */
+ regmap_update_bits(samsung->regmap, BIAS_CON4,
+ I_MUX_SEL_MASK, I_MUX_400MV);
+
samsung_mipi_dcphy_pll_configure(samsung);
samsung_mipi_dphy_clk_lane_timing_init(samsung);
samsung_mipi_dphy_data_lane_timing_init(samsung);
@@ -1362,10 +1361,6 @@ static int samsung_mipi_dcphy_power_on(struct phy *phy)
{
struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
- reset_control_assert(samsung->apb_rst);
- udelay(1);
- reset_control_deassert(samsung->apb_rst);
-
switch (samsung->type) {
case PHY_TYPE_DPHY:
return samsung_mipi_dphy_power_on(samsung);
@@ -1656,6 +1651,12 @@ static __maybe_unused int samsung_mipi_dcphy_runtime_resume(struct device *dev)
return ret;
}
+ reset_control_assert(samsung->apb_rst);
+ udelay(1);
+ reset_control_deassert(samsung->apb_rst);
+
+ samsung_mipi_dcphy_bias_block_enable(samsung);
+
return 0;
}
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
` (2 preceding siblings ...)
2026-10-16 8:56 ` [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 9:07 ` sashiko-bot
2026-10-16 8:56 ` [PATCH v5 5/8] phy: rockchip-samsung-dcphy: Add a second PHY for the receiver Jason Yang
` (3 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The block is a combo PHY: it exposes a transmitter and a receiver, and a
later change registers one struct phy for each. Prepare for that. Give
every helper that programs the master lanes a tx_ prefix, and move the
phy pointer and the lane count, which only the transmitter uses, into a
struct samsung_mipi_phy that the receiver can have one of too.
Name the phy_ops and its callbacks after the transmitter as well, so
that the receiver can bring its own rather than share these.
The BIAS block and the PLL keep their names: the TRM puts both in the
part common to the whole block [1], so neither belongs to one PHY even
though only the transmitter drives the PLL.
No functional change intended.
[1] RK3588 TRM: sections 22.3.1 (bias generator), 22.3.2 (PLL) and
22.4.1 (address map)
Assisted-by: LLM
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 67 ++++++++++++-----------
1 file changed, 36 insertions(+), 31 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index 1471a1563d2a..e4293c8d2b29 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -280,6 +280,11 @@ struct samsung_mipi_dcphy_plat_data {
u32 dphy_tx_max_lane_kbps;
};
+struct samsung_mipi_phy {
+ struct phy *phy;
+ unsigned int lanes;
+};
+
struct samsung_mipi_dcphy {
struct device *dev;
struct clk *ref_clk;
@@ -290,8 +295,7 @@ struct samsung_mipi_dcphy {
struct reset_control *s_phy_rst;
struct reset_control *apb_rst;
struct reset_control *grf_apb_rst;
- unsigned int lanes;
- struct phy *phy;
+ struct samsung_mipi_phy tx;
u8 type;
const struct samsung_mipi_dcphy_plat_data *pdata;
@@ -982,13 +986,13 @@ static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *sams
REG_400M_400MV | REG_645M_645MV);
}
-static void samsung_mipi_dphy_lane_enable(struct samsung_mipi_dcphy *samsung)
+static void samsung_mipi_dphy_tx_lane_enable(struct samsung_mipi_dcphy *samsung)
{
regmap_write(samsung->regmap, DPHY_MC_GNR_CON1, T_PHY_READY(0x2000));
regmap_update_bits(samsung->regmap, DPHY_MC_GNR_CON0,
PHY_ENABLE, PHY_ENABLE);
- switch (samsung->lanes) {
+ switch (samsung->tx.lanes) {
case 4:
regmap_write(samsung->regmap, DPHY_MD3_GNR_CON1,
T_PHY_READY(0x2000));
@@ -1017,9 +1021,9 @@ static void samsung_mipi_dphy_lane_enable(struct samsung_mipi_dcphy *samsung)
}
}
-static void samsung_mipi_dphy_lane_disable(struct samsung_mipi_dcphy *samsung)
+static void samsung_mipi_dphy_tx_lane_disable(struct samsung_mipi_dcphy *samsung)
{
- switch (samsung->lanes) {
+ switch (samsung->tx.lanes) {
case 4:
regmap_update_bits(samsung->regmap, DPHY_MD3_GNR_CON0,
PHY_ENABLE, 0);
@@ -1094,7 +1098,7 @@ static void samsung_mipi_dcphy_pll_disable(struct samsung_mipi_dcphy *samsung)
}
static const struct samsung_mipi_dphy_timing *
-samsung_mipi_dphy_get_timing(struct samsung_mipi_dcphy *samsung)
+samsung_mipi_dphy_tx_get_timing(struct samsung_mipi_dcphy *samsung)
{
const struct samsung_mipi_dphy_timing *timings;
unsigned int num_timings;
@@ -1201,13 +1205,13 @@ samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
}
static void
-samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung)
+samsung_mipi_dphy_tx_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung)
{
const struct samsung_mipi_dphy_timing *timing;
unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
u32 val, res_up, res_down;
- timing = samsung_mipi_dphy_get_timing(samsung);
+ timing = samsung_mipi_dphy_tx_get_timing(samsung);
regmap_write(samsung->regmap, DPHY_MC_GNR_CON0, 0xf000);
/*
@@ -1256,13 +1260,13 @@ samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung)
}
static void
-samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung)
+samsung_mipi_dphy_tx_data_lane_timing_init(struct samsung_mipi_dcphy *samsung)
{
const struct samsung_mipi_dphy_timing *timing;
unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
u32 val, res_up, res_down;
- timing = samsung_mipi_dphy_get_timing(samsung);
+ timing = samsung_mipi_dphy_tx_get_timing(samsung);
/*
* The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the
@@ -1325,7 +1329,7 @@ samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung)
regmap_write(samsung->regmap, DPHY_MD3_TIME_CON4, 0x1f4);
}
-static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
+static int samsung_mipi_dphy_tx_power_on(struct samsung_mipi_dcphy *samsung)
{
int ret;
@@ -1339,13 +1343,13 @@ static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
I_MUX_SEL_MASK, I_MUX_400MV);
samsung_mipi_dcphy_pll_configure(samsung);
- samsung_mipi_dphy_clk_lane_timing_init(samsung);
- samsung_mipi_dphy_data_lane_timing_init(samsung);
+ samsung_mipi_dphy_tx_clk_lane_timing_init(samsung);
+ samsung_mipi_dphy_tx_data_lane_timing_init(samsung);
ret = samsung_mipi_dcphy_pll_enable(samsung);
if (ret < 0)
return ret;
- samsung_mipi_dphy_lane_enable(samsung);
+ samsung_mipi_dphy_tx_lane_enable(samsung);
reset_control_deassert(samsung->m_phy_rst);
@@ -1357,13 +1361,13 @@ static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
return 0;
}
-static int samsung_mipi_dcphy_power_on(struct phy *phy)
+static int samsung_mipi_dcphy_tx_power_on(struct phy *phy)
{
struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
switch (samsung->type) {
case PHY_TYPE_DPHY:
- return samsung_mipi_dphy_power_on(samsung);
+ return samsung_mipi_dphy_tx_power_on(samsung);
default:
/* CPHY part to be implemented later */
return -EOPNOTSUPP;
@@ -1372,13 +1376,13 @@ static int samsung_mipi_dcphy_power_on(struct phy *phy)
return 0;
}
-static int samsung_mipi_dcphy_power_off(struct phy *phy)
+static int samsung_mipi_dcphy_tx_power_off(struct phy *phy)
{
struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
switch (samsung->type) {
case PHY_TYPE_DPHY:
- samsung_mipi_dphy_lane_disable(samsung);
+ samsung_mipi_dphy_tx_lane_disable(samsung);
break;
default:
/* CPHY part to be implemented later */
@@ -1477,13 +1481,13 @@ samsung_mipi_dcphy_pll_calc_rate(struct samsung_mipi_dcphy *samsung,
}
}
-static int samsung_mipi_dcphy_configure(struct phy *phy,
- union phy_configure_opts *opts)
+static int samsung_mipi_dcphy_tx_configure(struct phy *phy,
+ union phy_configure_opts *opts)
{
struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
unsigned long long target_rate = opts->mipi_dphy.hs_clk_rate;
- samsung->lanes = opts->mipi_dphy.lanes > 4 ? 4 : opts->mipi_dphy.lanes;
+ samsung->tx.lanes = opts->mipi_dphy.lanes > 4 ? 4 : opts->mipi_dphy.lanes;
samsung_mipi_dcphy_pll_calc_rate(samsung, target_rate);
opts->mipi_dphy.hs_clk_rate = samsung->pll.rate;
@@ -1507,10 +1511,10 @@ static int samsung_mipi_dcphy_exit(struct phy *phy)
return 0;
}
-static const struct phy_ops samsung_mipi_dcphy_ops = {
- .configure = samsung_mipi_dcphy_configure,
- .power_on = samsung_mipi_dcphy_power_on,
- .power_off = samsung_mipi_dcphy_power_off,
+static const struct phy_ops samsung_mipi_dcphy_tx_ops = {
+ .configure = samsung_mipi_dcphy_tx_configure,
+ .power_on = samsung_mipi_dcphy_tx_power_on,
+ .power_off = samsung_mipi_dcphy_tx_power_off,
.init = samsung_mipi_dcphy_init,
.exit = samsung_mipi_dcphy_exit,
.owner = THIS_MODULE,
@@ -1540,7 +1544,7 @@ static struct phy *samsung_mipi_dcphy_xlate(struct device *dev,
samsung->type = args->args[0];
- return samsung->phy;
+ return samsung->tx.phy;
}
static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
@@ -1609,11 +1613,12 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
- samsung->phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_ops);
- if (IS_ERR(samsung->phy))
- return dev_err_probe(dev, PTR_ERR(samsung->phy), "Failed to create MIPI DC-PHY\n");
+ samsung->tx.phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_tx_ops);
+ if (IS_ERR(samsung->tx.phy))
+ return dev_err_probe(dev, PTR_ERR(samsung->tx.phy),
+ "Failed to create MIPI DC-PHY transmitter\n");
- phy_set_drvdata(samsung->phy, samsung);
+ phy_set_drvdata(samsung->tx.phy, samsung);
phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
if (IS_ERR(phy_provider))
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 5/8] phy: rockchip-samsung-dcphy: Add a second PHY for the receiver
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
` (3 preceding siblings ...)
2026-10-16 8:56 ` [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 8:56 ` [PATCH v5 6/8] phy: rockchip-samsung-dcphy: Add MIPI D-PHY receiver support Jason Yang
` (2 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
Give the receiver a struct phy of its own, with its own phy_ops and its
own samsung_mipi_phy for the state the two do not share.
of_xlate() takes the direction from the second cell and falls back to
the transmitter for a single-cell provider, so existing device trees
keep working. It now rejects an unknown value in either cell, and a mix
of D-PHY and C-PHY consumers, which the TRM does not support [1].
The receiver's callbacks return -EOPNOTSUPP until its bring-up is added
in the next change.
[1] RK3588 TRM: section 22.1 (overview)
Assisted-by: LLM
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 69 +++++++++++++++++++++--
1 file changed, 63 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index e4293c8d2b29..316d4c4d49c1 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -296,6 +296,8 @@ struct samsung_mipi_dcphy {
struct reset_control *apb_rst;
struct reset_control *grf_apb_rst;
struct samsung_mipi_phy tx;
+ struct samsung_mipi_phy rx;
+ /* PHY mode, PHY_TYPE_DPHY or PHY_TYPE_CPHY. */
u8 type;
const struct samsung_mipi_dcphy_plat_data *pdata;
@@ -1511,6 +1513,22 @@ static int samsung_mipi_dcphy_exit(struct phy *phy)
return 0;
}
+static int samsung_mipi_dcphy_rx_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ return -EOPNOTSUPP;
+}
+
+static int samsung_mipi_dcphy_rx_power_on(struct phy *phy)
+{
+ return -EOPNOTSUPP;
+}
+
+static int samsung_mipi_dcphy_rx_power_off(struct phy *phy)
+{
+ return -EOPNOTSUPP;
+}
+
static const struct phy_ops samsung_mipi_dcphy_tx_ops = {
.configure = samsung_mipi_dcphy_tx_configure,
.power_on = samsung_mipi_dcphy_tx_power_on,
@@ -1520,6 +1538,15 @@ static const struct phy_ops samsung_mipi_dcphy_tx_ops = {
.owner = THIS_MODULE,
};
+static const struct phy_ops samsung_mipi_dcphy_rx_ops = {
+ .configure = samsung_mipi_dcphy_rx_configure,
+ .power_on = samsung_mipi_dcphy_rx_power_on,
+ .power_off = samsung_mipi_dcphy_rx_power_off,
+ .init = samsung_mipi_dcphy_init,
+ .exit = samsung_mipi_dcphy_exit,
+ .owner = THIS_MODULE,
+};
+
static const struct regmap_config samsung_mipi_dcphy_regmap_config = {
.name = "dcphy",
.reg_bits = 32,
@@ -1532,19 +1559,42 @@ static struct phy *samsung_mipi_dcphy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);
+ /* Device trees without the second cell describe the transmitter. */
+ u32 direction = PHY_DIRECTION_TX;
+ u32 type;
- if (args->args_count != 1) {
+ if (args->args_count < 1 || args->args_count > 2) {
dev_err(dev, "invalid number of arguments\n");
return ERR_PTR(-EINVAL);
}
- if (samsung->type != PHY_NONE && samsung->type != args->args[0])
- dev_warn(dev, "phy type select %d overwriting type %d\n",
- args->args[0], samsung->type);
+ type = args->args[0];
+ if (type != PHY_TYPE_DPHY && type != PHY_TYPE_CPHY) {
+ dev_err(dev, "invalid phy type %u\n", type);
+ return ERR_PTR(-EINVAL);
+ }
- samsung->type = args->args[0];
+ if (args->args_count == 2)
+ direction = args->args[1];
- return samsung->tx.phy;
+ if (direction != PHY_DIRECTION_TX && direction != PHY_DIRECTION_RX) {
+ dev_err(dev, "invalid direction %u\n", direction);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * The TRM (section 22.1) does not support the transmitter and the
+ * receiver running in different modes, so the mode belongs to the
+ * block.
+ */
+ if (samsung->type != PHY_NONE && samsung->type != type) {
+ dev_err(dev, "phy type %u conflicts with type %u already selected\n",
+ type, samsung->type);
+ return ERR_PTR(-EINVAL);
+ }
+ samsung->type = type;
+
+ return direction == PHY_DIRECTION_RX ? samsung->rx.phy : samsung->tx.phy;
}
static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
@@ -1620,6 +1670,13 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
phy_set_drvdata(samsung->tx.phy, samsung);
+ samsung->rx.phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_rx_ops);
+ if (IS_ERR(samsung->rx.phy))
+ return dev_err_probe(dev, PTR_ERR(samsung->rx.phy),
+ "Failed to create MIPI DC-PHY receiver\n");
+
+ phy_set_drvdata(samsung->rx.phy, samsung);
+
phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
if (IS_ERR(phy_provider))
return dev_err_probe(dev, PTR_ERR(phy_provider),
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 6/8] phy: rockchip-samsung-dcphy: Add MIPI D-PHY receiver support
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
` (4 preceding siblings ...)
2026-10-16 8:56 ` [PATCH v5 5/8] phy: rockchip-samsung-dcphy: Add a second PHY for the receiver Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 8:56 ` [PATCH v5 7/8] arm64: dts: rockchip: use two phy cells for the rk3588 mipi dcphys Jason Yang
2026-10-16 8:56 ` [PATCH v5 8/8] arm64: dts: rockchip: add mipi csi-2 receiver nodes for the dcphys on rk3588 Jason Yang
7 siblings, 0 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
Add the receiver half of the block, so a MIPI CSI-2 camera can use the
same PHY that already drives a MIPI DSI output on RK3588. It follows the
receiver start-up sequence in the RK3588 TRM [1]. The HS-RX settle values
come from the RK3588 vendor kernel.
The initial deskew calibration that the D-PHY specification asks for
above 1.5 Gbps is not implemented; the vendor kernel programs it from
RK3576 onwards only.
Tested on RK3588 with an IMX219 on the receiver and a DSI panel on the
transmitter of the same PHY.
[1] RK3588 TRM: sections 22.6.3 (receiver start-up) and 22.6.4.3
(worked receiver example)
Assisted-by: LLM
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 390 +++++++++++++++++++++-
1 file changed, 387 insertions(+), 3 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index 316d4c4d49c1..54a7af610282 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -244,6 +244,63 @@
#define T_TA_GET(x) FIELD_PREP(GENMASK(7, 4), x)
#define T_TA_GO(x) FIELD_PREP(GENMASK(3, 0), x)
+/* D-PHY receiver registers (clock lane + four data lanes) */
+#define DPHY_SC_GNR_CON0 0x0b00
+#define DPHY_SC_GNR_CON1 0x0b04
+#define DPHY_SC_ANA_CON1 0x0b0c
+#define HS_RX_BIAS_CON(x) FIELD_PREP(GENMASK(15, 11), x)
+#define DPHY_SC_ANA_CON2 0x0b10
+#define HS_TERM_SW(x) FIELD_PREP(GENMASK(2, 0), x)
+#define DPHY_SC_ANA_CON3 0x0b14
+#define ULPS_HYS_SW_DPHY(x) FIELD_PREP(GENMASK(10, 8), x)
+#define DPHY_SC_TIME_CON0 0x0b30
+#define T_CLK_SETTLE(x) FIELD_PREP(GENMASK(7, 0), x)
+#define T_CLK_MISS(x) FIELD_PREP(GENMASK(11, 8), x)
+#define COMBO_SD0_GNR_CON0 0x0c00
+#define COMBO_SD0_GNR_CON1 0x0c04
+#define COMBO_SD0_ANA_CON1 0x0c0c
+#define COMBO_SD0_ANA_CON2 0x0c10
+#define SKEW_DLYSEL(x) FIELD_PREP(GENMASK(9, 8), x)
+#define RX_TERM_SW(x) FIELD_PREP(GENMASK(2, 0), x)
+#define COMBO_SD0_ANA_CON3 0x0c14
+#define SEL_ESCPOL BIT(11)
+#define LP_HYS_SW(x) FIELD_PREP(GENMASK(5, 4), x)
+#define COMBO_SD0_ANA_CON7 0x0c24
+#define CLK_DBL_CTRL(x) FIELD_PREP(GENMASK(7, 6), x)
+#define COMBO_SD0_TIME_CON0 0x0c30
+#define T_HS_SETTLE(x) FIELD_PREP(GENMASK(7, 0), x)
+#define SETTLE_CLK_SEL BIT(8)
+#define COMBO_SD0_TIME_CON1 0x0c34
+#define T_ERR_SOT_SYNC(x) FIELD_PREP(GENMASK(7, 0), x)
+#define COMBO_SD0_DESKEW_CON2 0x0c48
+#define SKEW_CAL_CLK_COARSE_SET(x) FIELD_PREP(GENMASK(4, 0), x)
+#define COMBO_SD1_GNR_CON0 0x0d00
+#define COMBO_SD1_GNR_CON1 0x0d04
+#define COMBO_SD1_ANA_CON1 0x0d0c
+#define COMBO_SD1_ANA_CON2 0x0d10
+#define COMBO_SD1_ANA_CON3 0x0d14
+#define COMBO_SD1_ANA_CON7 0x0d24
+#define COMBO_SD1_TIME_CON0 0x0d30
+#define COMBO_SD1_TIME_CON1 0x0d34
+#define COMBO_SD1_DESKEW_CON2 0x0d48
+#define COMBO_SD2_GNR_CON0 0x0e00
+#define COMBO_SD2_GNR_CON1 0x0e04
+#define COMBO_SD2_ANA_CON1 0x0e0c
+#define COMBO_SD2_ANA_CON2 0x0e10
+#define COMBO_SD2_ANA_CON3 0x0e14
+#define COMBO_SD2_ANA_CON7 0x0e24
+#define COMBO_SD2_TIME_CON0 0x0e30
+#define COMBO_SD2_TIME_CON1 0x0e34
+#define COMBO_SD2_DESKEW_CON2 0x0e48
+#define DPHY_SD3_GNR_CON0 0x0f00
+#define DPHY_SD3_GNR_CON1 0x0f04
+#define DPHY_SD3_ANA_CON1 0x0f0c
+#define DPHY_SD3_ANA_CON2 0x0f10
+#define DPHY_SD3_ANA_CON3 0x0f14
+#define DPHY_SD3_TIME_CON0 0x0f30
+#define DPHY_SD3_TIME_CON1 0x0f34
+#define DPHY_SD3_DESKEW_CON2 0x0f48
+
/* MIPI_CDPHY_GRF registers */
#define MIPI_DCPHY_GRF_CON0 0x0000
#define S_CPHY_MODE FIELD_PREP_WM16(BIT(3), 1)
@@ -283,6 +340,7 @@ struct samsung_mipi_dcphy_plat_data {
struct samsung_mipi_phy {
struct phy *phy;
unsigned int lanes;
+ unsigned long long hs_clk_rate;
};
struct samsung_mipi_dcphy {
@@ -979,6 +1037,260 @@ struct samsung_mipi_dphy_timing samsung_mipi_dphy_timing_table[] = {
{ 80, 2, 0, 0, 28, 5, 0, 22, 2, 0, 5},
};
+/* D-PHY receiver HS-RX configuration lookup */
+struct samsung_mipi_dphy_rx_hsfreq_range {
+ u32 range_h_mbps;
+ u16 cfg_bit;
+};
+
+/*
+ * HS RX settle values taken verbatim from the rk3588 vendor kernel.
+ * The TRM defines these fields but defers the per-data-rate table to a
+ * timing supplement. Each cfg_bit is a pre-combined SETTLE_CLK_SEL |
+ * T_HS_SETTLE value for the data-lane TIME_CON0: bit 8 selects the
+ * divide-by-2 settle clock below 1500 Mbps per the TRM (the vendor
+ * table switches one bucket early, at 1490 Mbps, and is kept
+ * unchanged). The TRM marks bit 8 read-only, but the vendor kernel has
+ * always programmed it. Sorted by .range_h_mbps ascending.
+ */
+static const struct samsung_mipi_dphy_rx_hsfreq_range samsung_mipi_dphy_rx_hsfreq_ranges[] = {
+ { 80, 0x105 }, { 100, 0x106 }, { 120, 0x107 }, { 140, 0x108 },
+ { 160, 0x109 }, { 180, 0x10a }, { 200, 0x10b }, { 220, 0x10c },
+ { 240, 0x10d }, { 270, 0x10e }, { 290, 0x10f }, { 310, 0x110 },
+ { 330, 0x111 }, { 350, 0x112 }, { 370, 0x113 }, { 390, 0x114 },
+ { 410, 0x115 }, { 430, 0x116 }, { 450, 0x117 }, { 470, 0x118 },
+ { 490, 0x119 }, { 510, 0x11a }, { 540, 0x11b }, { 560, 0x11c },
+ { 580, 0x11d }, { 600, 0x11e }, { 620, 0x11f }, { 640, 0x120 },
+ { 660, 0x121 }, { 680, 0x122 }, { 700, 0x123 }, { 720, 0x124 },
+ { 740, 0x125 }, { 760, 0x126 }, { 790, 0x127 }, { 810, 0x128 },
+ { 830, 0x129 }, { 850, 0x12a }, { 870, 0x12b }, { 890, 0x12c },
+ { 910, 0x12d }, { 930, 0x12e }, { 950, 0x12f }, { 970, 0x130 },
+ { 990, 0x131 }, { 1010, 0x132 }, { 1030, 0x133 }, { 1060, 0x134 },
+ { 1080, 0x135 }, { 1100, 0x136 }, { 1120, 0x137 }, { 1140, 0x138 },
+ { 1160, 0x139 }, { 1180, 0x13a }, { 1200, 0x13b }, { 1220, 0x13c },
+ { 1240, 0x13d }, { 1260, 0x13e }, { 1280, 0x13f }, { 1310, 0x140 },
+ { 1330, 0x141 }, { 1350, 0x142 }, { 1370, 0x143 }, { 1390, 0x144 },
+ { 1410, 0x145 }, { 1430, 0x146 }, { 1450, 0x147 }, { 1470, 0x148 },
+ { 1490, 0x149 }, { 1580, 0x007 }, { 1740, 0x008 }, { 1910, 0x009 },
+ { 2070, 0x00a }, { 2240, 0x00b }, { 2410, 0x00c }, { 2570, 0x00d },
+ { 2740, 0x00e }, { 2910, 0x00f }, { 3070, 0x010 }, { 3240, 0x011 },
+ { 3410, 0x012 }, { 3570, 0x013 }, { 3740, 0x014 }, { 3890, 0x015 },
+ { 4070, 0x016 }, { 4240, 0x017 }, { 4400, 0x018 }, { 4500, 0x019 },
+};
+
+static int samsung_mipi_dphy_rx_lookup_hsfreq(u32 lane_mbps, u16 *cfg_bit)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(samsung_mipi_dphy_rx_hsfreq_ranges); i++) {
+ if (lane_mbps <= samsung_mipi_dphy_rx_hsfreq_ranges[i].range_h_mbps) {
+ *cfg_bit = samsung_mipi_dphy_rx_hsfreq_ranges[i].cfg_bit;
+ return 0;
+ }
+ }
+
+ return -ERANGE;
+}
+
+/* SKEW_DLYSEL per data rate, from the RK3588 TRM ANA_CON2 description */
+static u32 samsung_mipi_dphy_rx_data_lane_dlysel(u32 lane_mbps)
+{
+ if (lane_mbps < 1500)
+ return 0;
+ if (lane_mbps < 2000)
+ return SKEW_DLYSEL(3);
+ if (lane_mbps < 3000)
+ return SKEW_DLYSEL(2);
+ if (lane_mbps < 4000)
+ return SKEW_DLYSEL(1);
+ return 0;
+}
+
+/*
+ * Per-data-lane register bases, indexed by lane number. The clock lane
+ * shares the same block spacing but is programmed separately: its
+ * TIME_CON0 holds T_CLK_SETTLE/T_CLK_MISS rather than the per-rate
+ * T_HS_SETTLE/SETTLE_CLK_SEL, and it has no deskew configuration.
+ */
+static const u32 rx_data_lane_gnr_con0[] = {
+ COMBO_SD0_GNR_CON0, COMBO_SD1_GNR_CON0,
+ COMBO_SD2_GNR_CON0, DPHY_SD3_GNR_CON0,
+};
+
+static const u32 rx_data_lane_gnr_con1[] = {
+ COMBO_SD0_GNR_CON1, COMBO_SD1_GNR_CON1,
+ COMBO_SD2_GNR_CON1, DPHY_SD3_GNR_CON1,
+};
+
+static const u32 rx_lane_time_con0[] = {
+ COMBO_SD0_TIME_CON0, COMBO_SD1_TIME_CON0,
+ COMBO_SD2_TIME_CON0, DPHY_SD3_TIME_CON0,
+};
+
+static const u32 rx_lane_time_con1[] = {
+ COMBO_SD0_TIME_CON1, COMBO_SD1_TIME_CON1,
+ COMBO_SD2_TIME_CON1, DPHY_SD3_TIME_CON1,
+};
+
+/*
+ * These RX analog tuning values come from the vendor kernel. The
+ * termination (RX_TERM_SW/HS_TERM_SW = 2) is the 96.6 ohm setting the
+ * TRM ANA_CON2 value list annotates as the default, although the
+ * register itself resets to 102 ohm. Data lane 0 differs on purpose:
+ * the vendor's default receive profile applies the LP hysteresis and
+ * the swapped escape clock polarity to lane 0 only, and no manual
+ * skew-calibration delay there against three 30 ps steps on the other
+ * lanes. Data lane 3 is a plain D-PHY lane with no ANA_CON7, so it
+ * gets no CLK_DBL_CTRL write.
+ */
+static void samsung_mipi_dphy_rx_config_common(struct samsung_mipi_dcphy *samsung)
+{
+ struct samsung_mipi_phy *rx = &samsung->rx;
+ u32 dlysel = samsung_mipi_dphy_rx_data_lane_dlysel(div_u64(rx->hs_clk_rate,
+ 1000000));
+ u32 ana_con2_common = dlysel | RX_TERM_SW(2);
+
+ /* Clock lane */
+ regmap_write(samsung->regmap, DPHY_SC_ANA_CON1, HS_RX_BIAS_CON(0x10));
+ regmap_write(samsung->regmap, DPHY_SC_ANA_CON2, HS_TERM_SW(2));
+ regmap_write(samsung->regmap, DPHY_SC_ANA_CON3, ULPS_HYS_SW_DPHY(6));
+
+ /* Data lane 0; a zero lane count is rejected at power-on */
+ regmap_write(samsung->regmap, COMBO_SD0_ANA_CON1, HS_RX_BIAS_CON(0x10));
+ regmap_write(samsung->regmap, COMBO_SD0_ANA_CON2, ana_con2_common);
+ regmap_write(samsung->regmap, COMBO_SD0_ANA_CON3,
+ ULPS_HYS_SW_DPHY(6) | LP_HYS_SW(3) | SEL_ESCPOL);
+ regmap_write(samsung->regmap, COMBO_SD0_ANA_CON7, CLK_DBL_CTRL(1));
+ regmap_write(samsung->regmap, COMBO_SD0_DESKEW_CON2,
+ SKEW_CAL_CLK_COARSE_SET(0));
+
+ /* Data lane 1 */
+ if (rx->lanes > 1) {
+ regmap_write(samsung->regmap, COMBO_SD1_ANA_CON1,
+ HS_RX_BIAS_CON(0x10));
+ regmap_write(samsung->regmap, COMBO_SD1_ANA_CON2,
+ ana_con2_common);
+ regmap_write(samsung->regmap, COMBO_SD1_ANA_CON3,
+ ULPS_HYS_SW_DPHY(6));
+ regmap_write(samsung->regmap, COMBO_SD1_ANA_CON7,
+ CLK_DBL_CTRL(1));
+ regmap_write(samsung->regmap, COMBO_SD1_DESKEW_CON2,
+ SKEW_CAL_CLK_COARSE_SET(3));
+ }
+
+ /* Data lane 2 */
+ if (rx->lanes > 2) {
+ regmap_write(samsung->regmap, COMBO_SD2_ANA_CON1,
+ HS_RX_BIAS_CON(0x10));
+ regmap_write(samsung->regmap, COMBO_SD2_ANA_CON2,
+ ana_con2_common);
+ regmap_write(samsung->regmap, COMBO_SD2_ANA_CON3,
+ ULPS_HYS_SW_DPHY(6));
+ regmap_write(samsung->regmap, COMBO_SD2_ANA_CON7,
+ CLK_DBL_CTRL(1));
+ regmap_write(samsung->regmap, COMBO_SD2_DESKEW_CON2,
+ SKEW_CAL_CLK_COARSE_SET(3));
+ }
+
+ /* Data lane 3 */
+ if (rx->lanes > 3) {
+ regmap_write(samsung->regmap, DPHY_SD3_ANA_CON1,
+ HS_RX_BIAS_CON(0x10));
+ regmap_write(samsung->regmap, DPHY_SD3_ANA_CON2,
+ ana_con2_common);
+ regmap_write(samsung->regmap, DPHY_SD3_ANA_CON3,
+ ULPS_HYS_SW_DPHY(6));
+ regmap_write(samsung->regmap, DPHY_SD3_DESKEW_CON2,
+ SKEW_CAL_CLK_COARSE_SET(3));
+ }
+}
+
+static int samsung_mipi_dphy_rx_config_settle(struct samsung_mipi_dcphy *samsung)
+{
+ struct samsung_mipi_phy *rx = &samsung->rx;
+ u32 lane_mbps = div_u64(rx->hs_clk_rate, 1000000);
+ unsigned int i;
+ u16 cfg_bit;
+ int ret;
+
+ ret = samsung_mipi_dphy_rx_lookup_hsfreq(lane_mbps, &cfg_bit);
+ if (ret) {
+ dev_err(samsung->dev, "no RX hsfreq cfg for %u Mbps\n",
+ lane_mbps);
+ return ret;
+ }
+
+ /*
+ * Clock-lane settle uses the fixed value from the TRM RX bring-up
+ * example, unlike the per-rate data-lane settle below.
+ */
+ regmap_write(samsung->regmap, DPHY_SC_TIME_CON0,
+ T_CLK_SETTLE(0x01) | T_CLK_MISS(0x03));
+
+ for (i = 0; i < rx->lanes; i++) {
+ regmap_update_bits(samsung->regmap, rx_lane_time_con0[i],
+ T_HS_SETTLE(0xff) | SETTLE_CLK_SEL, cfg_bit);
+ regmap_update_bits(samsung->regmap, rx_lane_time_con1[i],
+ T_ERR_SOT_SYNC(0xff), T_ERR_SOT_SYNC(0x03));
+ }
+
+ return 0;
+}
+
+static void samsung_mipi_dphy_rx_lane_disable(struct samsung_mipi_dcphy *samsung)
+{
+ struct samsung_mipi_phy *rx = &samsung->rx;
+ unsigned int i;
+
+ regmap_update_bits(samsung->regmap, DPHY_SC_GNR_CON0, PHY_ENABLE, 0);
+ for (i = 0; i < rx->lanes; i++)
+ regmap_update_bits(samsung->regmap, rx_data_lane_gnr_con0[i],
+ PHY_ENABLE, 0);
+}
+
+static int samsung_mipi_dphy_rx_lane_enable(struct samsung_mipi_dcphy *samsung)
+{
+ struct samsung_mipi_phy *rx = &samsung->rx;
+ unsigned int i;
+ u32 sts;
+ int ret;
+
+ regmap_write(samsung->regmap, DPHY_SC_GNR_CON1, T_PHY_READY(0x1450));
+ regmap_update_bits(samsung->regmap, DPHY_SC_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+
+ for (i = 0; i < rx->lanes; i++) {
+ regmap_write(samsung->regmap, rx_data_lane_gnr_con1[i],
+ T_PHY_READY(0x1450));
+ regmap_update_bits(samsung->regmap, rx_data_lane_gnr_con0[i],
+ PHY_ENABLE, PHY_ENABLE);
+ }
+
+ ret = regmap_read_poll_timeout(samsung->regmap, DPHY_SC_GNR_CON0,
+ sts, sts & PHY_READY, 200, 4000);
+ if (ret) {
+ dev_err(samsung->dev, "RX clock lane not ready\n");
+ goto err_lane_disable;
+ }
+
+ for (i = 0; i < rx->lanes; i++) {
+ ret = regmap_read_poll_timeout(samsung->regmap,
+ rx_data_lane_gnr_con0[i],
+ sts, sts & PHY_READY,
+ 200, 2000);
+ if (ret) {
+ dev_err(samsung->dev, "RX data lane %u not ready\n", i);
+ goto err_lane_disable;
+ }
+ }
+
+ return 0;
+
+err_lane_disable:
+ samsung_mipi_dphy_rx_lane_disable(samsung);
+ return ret;
+}
+
static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung)
{
regmap_write(samsung->regmap, BIAS_CON0, I_DEV_DIV_6 | I_RES_100_2UA);
@@ -988,6 +1300,55 @@ static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *sams
REG_400M_400MV | REG_645M_645MV);
}
+static int samsung_mipi_dphy_rx_power_on(struct samsung_mipi_dcphy *samsung)
+{
+ struct samsung_mipi_phy *rx = &samsung->rx;
+ int ret;
+
+ if (!rx->hs_clk_rate || !rx->lanes)
+ return -EINVAL;
+
+ reset_control_assert(samsung->s_phy_rst);
+
+ /* default output voltage select:
+ * dphy: 400mv
+ * cphy: 530mv
+ */
+ regmap_update_bits(samsung->regmap, BIAS_CON4,
+ I_MUX_SEL_MASK, I_MUX_400MV);
+
+ samsung_mipi_dphy_rx_config_common(samsung);
+
+ ret = samsung_mipi_dphy_rx_config_settle(samsung);
+ if (ret)
+ goto out_deassert;
+
+ ret = samsung_mipi_dphy_rx_lane_enable(samsung);
+
+out_deassert:
+ reset_control_deassert(samsung->s_phy_rst);
+
+ return ret;
+}
+
+static int samsung_mipi_dphy_rx_power_off(struct samsung_mipi_dcphy *samsung)
+{
+ reset_control_assert(samsung->s_phy_rst);
+
+ samsung_mipi_dphy_rx_lane_disable(samsung);
+
+ reset_control_deassert(samsung->s_phy_rst);
+
+ /*
+ * Let the lanes settle out of reset before the receiver may be
+ * brought up again. The delay follows the vendor driver; the TRM
+ * does not document a teardown sequence.
+ */
+ usleep_range(500, 1000);
+
+ return 0;
+}
+
static void samsung_mipi_dphy_tx_lane_enable(struct samsung_mipi_dcphy *samsung)
{
regmap_write(samsung->regmap, DPHY_MC_GNR_CON1, T_PHY_READY(0x2000));
@@ -1516,17 +1877,40 @@ static int samsung_mipi_dcphy_exit(struct phy *phy)
static int samsung_mipi_dcphy_rx_configure(struct phy *phy,
union phy_configure_opts *opts)
{
- return -EOPNOTSUPP;
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ samsung->rx.lanes = opts->mipi_dphy.lanes > 4 ? 4 : opts->mipi_dphy.lanes;
+
+ /* The sensor supplies the link clock, so the PLL stays off. */
+ samsung->rx.hs_clk_rate = opts->mipi_dphy.hs_clk_rate;
+
+ return 0;
}
static int samsung_mipi_dcphy_rx_power_on(struct phy *phy)
{
- return -EOPNOTSUPP;
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ switch (samsung->type) {
+ case PHY_TYPE_DPHY:
+ return samsung_mipi_dphy_rx_power_on(samsung);
+ default:
+ /* CPHY part to be implemented later */
+ return -EOPNOTSUPP;
+ }
}
static int samsung_mipi_dcphy_rx_power_off(struct phy *phy)
{
- return -EOPNOTSUPP;
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ switch (samsung->type) {
+ case PHY_TYPE_DPHY:
+ return samsung_mipi_dphy_rx_power_off(samsung);
+ default:
+ /* CPHY part to be implemented later */
+ return -EOPNOTSUPP;
+ }
}
static const struct phy_ops samsung_mipi_dcphy_tx_ops = {
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 7/8] arm64: dts: rockchip: use two phy cells for the rk3588 mipi dcphys
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
` (5 preceding siblings ...)
2026-10-16 8:56 ` [PATCH v5 6/8] phy: rockchip-samsung-dcphy: Add MIPI D-PHY receiver support Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
2026-10-16 8:56 ` [PATCH v5 8/8] arm64: dts: rockchip: add mipi csi-2 receiver nodes for the dcphys on rk3588 Jason Yang
7 siblings, 0 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The MIPI DC-PHY binding takes a second cell that selects the transmitter
or the receiver. Switch mipidcphy0 and mipidcphy1 to two cells and let
dsi0 and dsi1 select the transmitter.
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 376ad04e0786..94d68fd89753 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1788,7 +1788,7 @@ dsi0: dsi@fde20000 {
resets = <&cru SRST_P_DSIHOST0>;
reset-names = "apb";
power-domains = <&power RK3588_PD_VOP>;
- phys = <&mipidcphy0 PHY_TYPE_DPHY>;
+ phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_TX>;
phy-names = "dcphy";
rockchip,grf = <&vop_grf>;
status = "disabled";
@@ -1816,7 +1816,7 @@ dsi1: dsi@fde30000 {
resets = <&cru SRST_P_DSIHOST1>;
reset-names = "apb";
power-domains = <&power RK3588_PD_VOP>;
- phys = <&mipidcphy1 PHY_TYPE_DPHY>;
+ phys = <&mipidcphy1 PHY_TYPE_DPHY PHY_DIRECTION_TX>;
phy-names = "dcphy";
rockchip,grf = <&vop_grf>;
status = "disabled";
@@ -3429,7 +3429,7 @@ mipidcphy0: phy@feda0000 {
<&cru SRST_P_MIPI_DCPHY0_GRF>,
<&cru SRST_S_MIPI_DCPHY0>;
reset-names = "m_phy", "apb", "grf", "s_phy";
- #phy-cells = <1>;
+ #phy-cells = <2>;
status = "disabled";
};
@@ -3445,7 +3445,7 @@ mipidcphy1: phy@fedb0000 {
<&cru SRST_P_MIPI_DCPHY1_GRF>,
<&cru SRST_S_MIPI_DCPHY1>;
reset-names = "m_phy", "apb", "grf", "s_phy";
- #phy-cells = <1>;
+ #phy-cells = <2>;
status = "disabled";
};
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 8/8] arm64: dts: rockchip: add mipi csi-2 receiver nodes for the dcphys on rk3588
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
` (6 preceding siblings ...)
2026-10-16 8:56 ` [PATCH v5 7/8] arm64: dts: rockchip: use two phy cells for the rk3588 mipi dcphys Jason Yang
@ 2026-10-16 8:56 ` Jason Yang
7 siblings, 0 replies; 15+ messages in thread
From: Jason Yang @ 2026-10-16 8:56 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, Bryan O'Donoghue,
linux-phy, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Jason Yang
The MIPI0 and MIPI1 receiver units are connected to MIPI DCPHY0 and
MIPI DCPHY1, whose receiver side is now supported. Add the two nodes and
wire them to the VICAP ports.
Signed-off-by: Jason Yang <jason98166@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 68 +++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 94d68fd89753..30bc69a09102 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1516,10 +1516,18 @@ vicap_dvp: port@0 {
vicap_mipi0: port@1 {
reg = <0x1>;
+
+ vicap_mipi0_input: endpoint {
+ remote-endpoint = <&csi0_output>;
+ };
};
vicap_mipi1: port@2 {
reg = <0x2>;
+
+ vicap_mipi1_input: endpoint {
+ remote-endpoint = <&csi1_output>;
+ };
};
vicap_mipi2: port@3 {
@@ -1568,6 +1576,66 @@ vicap_mmu: iommu@fdce0800 {
status = "disabled";
};
+ csi0: csi@fdd10000 {
+ compatible = "rockchip,rk3588-mipi-csi2", "rockchip,rk3568-mipi-csi2";
+ reg = <0x0 0xfdd10000 0x0 0x10000>;
+ interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "err1", "err2";
+ clocks = <&cru PCLK_CSI_HOST_0>;
+ phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_RX>;
+ power-domains = <&power RK3588_PD_VI>;
+ resets = <&cru SRST_P_CSI_HOST_0>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ csi0_in: port@0 {
+ reg = <0>;
+ };
+
+ csi0_out: port@1 {
+ reg = <1>;
+
+ csi0_output: endpoint {
+ remote-endpoint = <&vicap_mipi0_input>;
+ };
+ };
+ };
+ };
+
+ csi1: csi@fdd20000 {
+ compatible = "rockchip,rk3588-mipi-csi2", "rockchip,rk3568-mipi-csi2";
+ reg = <0x0 0xfdd20000 0x0 0x10000>;
+ interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "err1", "err2";
+ clocks = <&cru PCLK_CSI_HOST_1>;
+ phys = <&mipidcphy1 PHY_TYPE_DPHY PHY_DIRECTION_RX>;
+ power-domains = <&power RK3588_PD_VI>;
+ resets = <&cru SRST_P_CSI_HOST_1>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ csi1_in: port@0 {
+ reg = <0>;
+ };
+
+ csi1_out: port@1 {
+ reg = <1>;
+
+ csi1_output: endpoint {
+ remote-endpoint = <&vicap_mipi1_input>;
+ };
+ };
+ };
+ };
+
csi2: csi@fdd30000 {
compatible = "rockchip,rk3588-mipi-csi2", "rockchip,rk3568-mipi-csi2";
reg = <0x0 0xfdd30000 0x0 0x10000>;
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops
2026-10-16 8:56 ` [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops Jason Yang
@ 2026-10-16 9:07 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-16 9:07 UTC (permalink / raw)
To: Jason Yang
Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] phy: missing PLL disable and reset deassert on tx_power_on error
--
commit 779b5f96ef2afd3f131c3123d0f3f45979f568d8
Author: Jason Yang <jason98166@gmail.com>
phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops
The block is a combo PHY: it exposes a transmitter and a receiver, and a
later change registers one struct phy for each. Prepare for that. Give
every helper that programs the master lanes a tx_ prefix, and move the
phy pointer and the lane count, which only the transmitter uses, into a
struct samsung_mipi_phy that the receiver can have one of too.
> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
> index 1471a1563d2a1..e4293c8d2b292 100644
> --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
[ ... ]
> @@ -1339,13 +1343,13 @@ static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
> I_MUX_SEL_MASK, I_MUX_400MV);
>
> samsung_mipi_dcphy_pll_configure(samsung);
> - samsung_mipi_dphy_clk_lane_timing_init(samsung);
> - samsung_mipi_dphy_data_lane_timing_init(samsung);
> + samsung_mipi_dphy_tx_clk_lane_timing_init(samsung);
> + samsung_mipi_dphy_tx_data_lane_timing_init(samsung);
> ret = samsung_mipi_dcphy_pll_enable(samsung);
> if (ret < 0)
> return ret;
[Severity: Medium]
If samsung_mipi_dcphy_pll_enable() times out, does this error path return
early without clearing the PLL_EN bit or deasserting the m_phy_rst reset line?
When samsung_mipi_dcphy_pll_enable() fails, it returns an error but the PHY is
left with the PLL_EN bit set and m_phy_rst asserted. The PHY framework does not
call phy_power_off() if phy_power_on() fails, so this hardware state could leak,
potentially preventing successful lock on future relock attempts.
>
> - samsung_mipi_dphy_lane_enable(samsung);
> + samsung_mipi_dphy_tx_lane_enable(samsung);
>
> reset_control_deassert(samsung->m_phy_rst);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-dcphy-rx-v1-v5-0-e63b49eabe2d@gmail.com?part=4
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume
2026-10-16 8:56 ` [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume Jason Yang
@ 2026-10-16 9:09 ` sashiko-bot
2026-10-16 10:46 ` Bryan O'Donoghue
1 sibling, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-16 9:09 UTC (permalink / raw)
To: Jason Yang
Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
olteanv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] phy: dcphy: PLL left enabled on lock failure, preventing recovery
--
commit 0436bfe5522a31ef65724b71dd2f683f01ebeaa1
Author: Jason Yang <jason98166@gmail.com>
phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume
This commit moves the block-level APB reset and BIAS configuration
from the power-on path to runtime resume. This prevents resetting
the block underneath a PHY that is already running when multiple
consumers are present.
> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
> index 7382043e1f319..1471a1563d2a1 100644
> --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
[ ... ]
> @@ -1362,10 +1361,6 @@ static int samsung_mipi_dcphy_power_on(struct phy *phy)
> {
> struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
>
> - reset_control_assert(samsung->apb_rst);
> - udelay(1);
> - reset_control_deassert(samsung->apb_rst);
> -
[Severity: Medium]
If samsung_mipi_dcphy_pll_enable() fails to lock, it returns without clearing
the PLL_EN bit in PLL_CON0:
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c:samsung_mipi_dcphy_pll_enable() {
regmap_update_bits(samsung->regmap, PLL_CON0, PLL_EN, PLL_EN);
ret = regmap_read_poll_timeout(samsung->regmap, PLL_STAT0,
sts, (sts & PLL_LOCK), 1000, 20000);
if (ret < 0)
dev_err(samsung->dev, "DC-PHY pll failed to lock\n");
return ret;
}
Because this patch removes the apb_rst sequence from the power_on path, the
stale PLL_EN bit is no longer cleared by the hardware reset on subsequent
retries.
Does this leave the hardware in an active but failed state, potentially
causing undefined behavior or preventing the PLL from locking during the
next power_on attempt?
> switch (samsung->type) {
> case PHY_TYPE_DPHY:
> return samsung_mipi_dphy_power_on(samsung);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-dcphy-rx-v1-v5-0-e63b49eabe2d@gmail.com?part=3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions
2026-10-16 8:56 ` [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions Jason Yang
@ 2026-10-16 10:38 ` Bryan O'Donoghue
0 siblings, 0 replies; 15+ messages in thread
From: Bryan O'Donoghue @ 2026-10-16 10:38 UTC (permalink / raw)
To: Jason Yang, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Guochun Huang, Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, linux-phy, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel
On 16/10/2026 09:56, Jason Yang wrote:
> A MIPI D-PHY or C-PHY can be a transmitter or a receiver. Some PHY
> blocks have one of each and can use them at the same time, so one PHY
> node offers two PHYs. The existing cell, PHY_TYPE_DPHY or
> PHY_TYPE_CPHY, selects the mode of the whole block, not one of the two
> PHYs.
>
> Add PHY_DIRECTION_TX and PHY_DIRECTION_RX for a cell that selects the
> transmitter or the receiver. The first user is the Rockchip RK3588 MIPI
> DC-PHY, where the transmitter serves a DSI host and the receiver a CSI-2
> host.
>
> Suggested-by: Michael Riesch <michael.riesch@collabora.com>
> Link: https://lore.kernel.org/r/7425ac83-f6d8-4443-a26e-a47f9306aa24@collabora.com
> Signed-off-by: Jason Yang <jason98166@gmail.com>
> ---
> include/dt-bindings/phy/phy.h | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h
> index 979b5dfd8353..9ead74d9a172 100644
> --- a/include/dt-bindings/phy/phy.h
> +++ b/include/dt-bindings/phy/phy.h
> @@ -29,4 +29,7 @@
> #define PHY_POL_INVERT 1
> #define PHY_POL_AUTO 2
>
> +#define PHY_DIRECTION_TX 0
> +#define PHY_DIRECTION_RX 1
> +
> #endif /* _DT_BINDINGS_PHY */
>
This looks like a neat solution to your problem.
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX
2026-10-16 8:56 ` [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX Jason Yang
@ 2026-10-16 10:43 ` Bryan O'Donoghue
2026-10-16 14:01 ` Rob Herring
1 sibling, 0 replies; 15+ messages in thread
From: Bryan O'Donoghue @ 2026-10-16 10:43 UTC (permalink / raw)
To: Jason Yang, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Guochun Huang, Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, linux-phy, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel
On 16/10/2026 09:56, Jason Yang wrote:
> The Samsung MIPI D-/C-PHY block on RK3588 and RK3576 has a transmitter
> for DSI and a receiver for CSI-2, and two consumers can use them at the
> same time.
Two consumers use them - which them ? Two consumers use the receivers at
the same time ?
Its minor but instead of "them" define what the them is.
The single cell of this binding selects the mode and refers
> to the transmitter, so the receiver cannot be described.
>
> Allow a second cell that selects the transmitter or the receiver. With
> D-PHY:
>
> /* the MIPI DSI host, driving the transmitter */
> phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_TX>;
>
> /* the MIPI CSI-2 host, driving the receiver */
> phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_RX>;
>
> and with C-PHY:
>
> phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_TX>;
> phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_RX>;
>
> The transmitter and the receiver cannot operate in different modes [1],
> so the first cell must match in both consumers.
>
> Two cells describe the receiver as well. A single cell keeps referring
> to the transmitter, so existing device trees are unaffected.
>
> [1] RK3588 TRM: section 22.1 (overview): "The TX and RX only support
> operate in the same PHY mode."
>
> Signed-off-by: Jason Yang <jason98166@gmail.com>
> ---
> .../bindings/phy/rockchip,rk3588-mipi-dcphy.yaml | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
> index c8ff5ba22a86..6e8cc56e4986 100644
> --- a/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
> +++ b/Documentation/devicetree/bindings/phy/rockchip,rk3588-mipi-dcphy.yaml
> @@ -20,11 +20,25 @@ properties:
> maxItems: 1
>
> "#phy-cells":
> - const: 1
> + enum: [1, 2]
> description: |
> - Argument is mode to operate in. Supported modes are:
> + The first cell selects the mode to operate in:
> - PHY_TYPE_DPHY
> - PHY_TYPE_CPHY
> +
> + A single cell refers to the transmitter and is kept for existing
> + device trees.
> +
> + The second cell selects the transmitter or the receiver, so that a
The second cell, if present
-> you want to make clear it is optional.
> + MIPI DSI host and a MIPI CSI-2 host can be described as independent
> + consumers of the same PHY node:
> + - PHY_DIRECTION_TX
> + - PHY_DIRECTION_RX
> +
> + The transmitter and the receiver cannot operate in different modes,
> + so when both are described, the first cell must match in both
> + consumers.
> +
Not really related to this patch but how do you envision that
requirement being enforced, per driver ?
> See include/dt-bindings/phy/phy.h for constants.
>
> clocks:
> @@ -82,6 +96,6 @@ examples:
> <&cru SRST_S_MIPI_DCPHY0>;
> reset-names = "m_phy", "apb", "grf", "s_phy";
> rockchip,grf = <&mipidcphy0_grf>;
> - #phy-cells = <1>;
> + #phy-cells = <2>;
> };
> };
>
Looks pretty good to me I'm interested in adding an RB here - please
include me in CC for next versions so I may do so.
---
bod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume
2026-10-16 8:56 ` [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume Jason Yang
2026-10-16 9:09 ` sashiko-bot
@ 2026-10-16 10:46 ` Bryan O'Donoghue
1 sibling, 0 replies; 15+ messages in thread
From: Bryan O'Donoghue @ 2026-10-16 10:46 UTC (permalink / raw)
To: Jason Yang, Vinod Koul, Neil Armstrong, Manivannan Sadhasivam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Guochun Huang, Philipp Zabel
Cc: Michael Riesch, Sebastian Reichel, linux-phy, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel
On 16/10/2026 09:56, Jason Yang wrote:
> Program them from runtime resume instead, where whichever of the two
> comes up first brings the block up and a second one no longer resets it.
> This puts the BIAS writes ahead of the per-PHY reset assert, where the
> TRM's worked example has them after [1]. That is safe: neither M_RESETN
> nor S_RESETN covers the BIAS registers, and the values written are those
> registers' reset defaults.
Would you not be better off scheduling a work job when you know both
blocks are out of suspend ?
That way you always have a known sequence out of reset ?
---
bod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX
2026-10-16 8:56 ` [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX Jason Yang
2026-10-16 10:43 ` Bryan O'Donoghue
@ 2026-10-16 14:01 ` Rob Herring
1 sibling, 0 replies; 15+ messages in thread
From: Rob Herring @ 2026-10-16 14:01 UTC (permalink / raw)
To: Jason Yang
Cc: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Guochun Huang,
Philipp Zabel, Michael Riesch, Sebastian Reichel,
Bryan O'Donoghue, linux-phy, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
On Fri, Oct 16, 2026 at 04:56:36PM +0800, Jason Yang wrote:
> The Samsung MIPI D-/C-PHY block on RK3588 and RK3576 has a transmitter
> for DSI and a receiver for CSI-2, and two consumers can use them at the
> same time. The single cell of this binding selects the mode and refers
> to the transmitter, so the receiver cannot be described.
>
> Allow a second cell that selects the transmitter or the receiver. With
> D-PHY:
>
> /* the MIPI DSI host, driving the transmitter */
> phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_TX>;
>
> /* the MIPI CSI-2 host, driving the receiver */
> phys = <&mipidcphy0 PHY_TYPE_DPHY PHY_DIRECTION_RX>;
>
> and with C-PHY:
>
> phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_TX>;
> phys = <&mipidcphy0 PHY_TYPE_CPHY PHY_DIRECTION_RX>;
>
> The transmitter and the receiver cannot operate in different modes [1],
> so the first cell must match in both consumers.
>
> Two cells describe the receiver as well. A single cell keeps referring
> to the transmitter, so existing device trees are unaffected.
>
> [1] RK3588 TRM: section 22.1 (overview): "The TX and RX only support
> operate in the same PHY mode."
The problem here is this is an ABI break. An existing OS will not know
what to do with 2 cells. Perhaps adding flags to the upper bits of the
existing cell would work.
Rob
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-16 14:01 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16 8:56 [PATCH v5 0/8] phy: rockchip-samsung-dcphy: Add the MIPI D-PHY receiver Jason Yang
2026-10-16 8:56 ` [PATCH v5 1/8] dt-bindings: phy: Add PHY_DIRECTION_TX/RX definitions Jason Yang
2026-10-16 10:38 ` Bryan O'Donoghue
2026-10-16 8:56 ` [PATCH v5 2/8] dt-bindings: phy: rockchip,rk3588-mipi-dcphy: Add a cell to select TX or RX Jason Yang
2026-10-16 10:43 ` Bryan O'Donoghue
2026-10-16 14:01 ` Rob Herring
2026-10-16 8:56 ` [PATCH v5 3/8] phy: rockchip-samsung-dcphy: Move block-level setup to runtime resume Jason Yang
2026-10-16 9:09 ` sashiko-bot
2026-10-16 10:46 ` Bryan O'Donoghue
2026-10-16 8:56 ` [PATCH v5 4/8] phy: rockchip-samsung-dcphy: Name the transmitter helpers and ops Jason Yang
2026-10-16 9:07 ` sashiko-bot
2026-10-16 8:56 ` [PATCH v5 5/8] phy: rockchip-samsung-dcphy: Add a second PHY for the receiver Jason Yang
2026-10-16 8:56 ` [PATCH v5 6/8] phy: rockchip-samsung-dcphy: Add MIPI D-PHY receiver support Jason Yang
2026-10-16 8:56 ` [PATCH v5 7/8] arm64: dts: rockchip: use two phy cells for the rk3588 mipi dcphys Jason Yang
2026-10-16 8:56 ` [PATCH v5 8/8] arm64: dts: rockchip: add mipi csi-2 receiver nodes for the dcphys on rk3588 Jason Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox