[RFC PATCH] drm/bridge: cdns-dsi: stop pre-enable after PHY wait failures

From: Pengpeng Hou

Date: Tue Jun 23 2026 - 10:02:12 EST


cdns_dsi_hs_init() ignored PHY API failures and converted the PLL-lock
wait into a WARN_ON_ONCE(). The bridge pre-enable path also only logged
a lane-ready timeout and then continued programming video registers and
enabling the interface.

Return errors from the PHY initialization helper, unwind the PHY when
initialization fails, and stop bridge pre-enable when either the PLL or
lane-ready wait fails. The bridge hook is still void, so this is
intended as an RFC patch for maintainer review of the failure policy.

Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
.../gpu/drm/bridge/cadence/cdns-dsi-core.c | 67 +++++++++++++++++--
1 file changed, 55 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
index cf90d4468..86f31eb46 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
@@ -631,13 +631,14 @@ static void cdns_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge,
pm_runtime_put(dsi->base.dev);
}

-static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
+static int cdns_dsi_hs_init(struct cdns_dsi *dsi)
{
struct cdns_dsi_output *output = &dsi->output;
u32 status;
+ int ret;

if (dsi->phy_initialized)
- return;
+ return 0;
/*
* Power all internal DPHY blocks down and maintain their reset line
* asserted before changing the DPHY config.
@@ -646,22 +647,45 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
DPHY_CMN_PDN | DPHY_PLL_PDN,
dsi->regs + MCTL_DPHY_CFG0);

- phy_init(dsi->dphy);
- phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
- phy_configure(dsi->dphy, &output->phy_opts);
- phy_power_on(dsi->dphy);
+ ret = phy_init(dsi->dphy);
+ if (ret)
+ return ret;
+
+ ret = phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
+ if (ret)
+ goto err_phy_exit;
+
+ ret = phy_configure(dsi->dphy, &output->phy_opts);
+ if (ret)
+ goto err_phy_exit;
+
+ ret = phy_power_on(dsi->dphy);
+ if (ret)
+ goto err_phy_exit;

/* Activate the PLL and wait until it's locked. */
writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN,
dsi->regs + MCTL_DPHY_CFG0);
- WARN_ON_ONCE(readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status,
- status & PLL_LOCKED, 100, 100));
+ ret = readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status,
+ status & PLL_LOCKED, 100, 100);
+ if (ret)
+ goto err_phy_power_off;
+
/* De-assert data and clock reset lines. */
writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
dsi->regs + MCTL_DPHY_CFG0);
dsi->phy_initialized = true;
+
+ return 0;
+
+err_phy_power_off:
+ phy_power_off(dsi->dphy);
+err_phy_exit:
+ phy_exit(dsi->dphy);
+
+ return ret;
}

static void cdns_dsi_init_link(struct cdns_dsi *dsi)
@@ -716,7 +740,7 @@ static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
unsigned long tx_byte_period;
struct cdns_dsi_cfg dsi_cfg;
u32 tmp, reg_wakeup, div, status;
- int nlanes;
+ int nlanes, ret;

/*
* The cdns-dsi controller needs to be enabled before it's DPI source
@@ -753,7 +777,12 @@ static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
nlanes = output->dev->lanes;

cdns_dsi_init_link(dsi);
- cdns_dsi_hs_init(dsi);
+ ret = cdns_dsi_hs_init(dsi);
+ if (ret) {
+ dev_err(dsi->base.dev, "failed to initialize DSI PHY: %d\n",
+ ret);
+ goto err_disable;
+ }

/*
* Now that the DSI Link and DSI Phy are initialized,
@@ -763,10 +792,13 @@ static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
for (int i = 0; i < nlanes; i++)
tmp |= DATA_LANE_RDY(i);

- if (readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status,
- (tmp == (status & tmp)), 100, 500000))
+ ret = readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status,
+ (tmp == (status & tmp)), 100, 500000);
+ if (ret) {
dev_err(dsi->base.dev,
"Timed Out: DSI-DPhy Clock and Data Lanes not ready.\n");
+ goto err_disable;
+ }

writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
dsi->regs + VID_HSIZE1);
@@ -886,6 +918,19 @@ static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,

tmp = readl(dsi->regs + MCTL_MAIN_EN) | IF_EN(input->id);
writel(tmp, dsi->regs + MCTL_MAIN_EN);
+
+ return;
+
+err_disable:
+ if (dsi->phy_initialized) {
+ dsi->phy_initialized = false;
+ phy_power_off(dsi->dphy);
+ phy_exit(dsi->dphy);
+ }
+ dsi->link_initialized = false;
+ if (dsi->platform_ops && dsi->platform_ops->disable)
+ dsi->platform_ops->disable(dsi);
+ pm_runtime_put(dsi->base.dev);
}

static u32 *cdns_dsi_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
--
2.50.1 (Apple Git-155)