[PATCH] drm/bridge: cdns-mhdp8546: cancel work before teardown
From: Hongyan Xu
Date: Tue Jul 28 2026 - 09:58:13 EST
The interrupt handler can queue hpd_work, while link training and HDCP
can queue modeset_retry_work, check_work and prop_work. The remove path
currently tears down the DRM bridge, firmware, PHY and runtime PM state
before synchronizing only the first two work items. It explicitly leaves
the HDCP work items alone.
Consequently, hpd_work can notify a bridge whose HPD mutex has already
been destroyed, and the other work items can access connector or device
state after teardown.
Mark the bridge detached and mask interrupts before freeing the IRQ, so
no interrupt handler can queue new HPD work. Then cancel all four work
items before removing the bridge or stopping the hardware. Cancel the
self-rearming HDCP check work before the property work because the check
path can queue a property update.
This issue was found by a static analysis tool.
Fixes: fb43aa0acdfd ("drm: bridge: Add support for Cadence MHDP8546 DPI/DP bridge")
Fixes: 6a3608eae6d3 ("drm: bridge: cdns-mhdp8546: Enable HDCP")
Signed-off-by: Hongyan Xu <getshell@xxxxxxxxxx>
---
.../drm/bridge/cadence/cdns-mhdp8546-core.c | 24 ++++++++++++-------
.../drm/bridge/cadence/cdns-mhdp8546-core.h | 1 +
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index 36c07b71f..aa8d12bb8 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -2265,7 +2265,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
unsigned long rate;
struct clk *clk;
int ret;
- int irq;
mhdp = devm_drm_bridge_alloc(dev, struct cdns_mhdp_device, bridge,
&cdns_mhdp_bridge_funcs);
@@ -2338,12 +2337,12 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
- irq = platform_get_irq(pdev, 0);
- ret = devm_request_threaded_irq(mhdp->dev, irq, NULL,
+ mhdp->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(mhdp->dev, mhdp->irq, NULL,
cdns_mhdp_irq_handler, IRQF_ONESHOT,
"mhdp8546", mhdp);
if (ret) {
- dev_err(dev, "cannot install IRQ %d\n", irq);
+ dev_err(dev, "cannot install IRQ %d\n", mhdp->irq);
ret = -EIO;
goto plat_fini;
}
@@ -2406,6 +2405,19 @@ static void cdns_mhdp_remove(struct platform_device *pdev)
unsigned long timeout = msecs_to_jiffies(100);
int ret;
+ spin_lock(&mhdp->start_lock);
+ mhdp->bridge_attached = false;
+ spin_unlock(&mhdp->start_lock);
+ writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
+ devm_free_irq(mhdp->dev, mhdp->irq, mhdp);
+
+ cancel_work_sync(&mhdp->hpd_work);
+ cancel_work_sync(&mhdp->modeset_retry_work);
+ if (mhdp->hdcp_supported) {
+ cancel_delayed_work_sync(&mhdp->hdcp.check_work);
+ cancel_work_sync(&mhdp->hdcp.prop_work);
+ }
+
drm_bridge_remove(&mhdp->bridge);
ret = wait_event_timeout(mhdp->fw_load_wq,
@@ -2432,10 +2444,6 @@ static void cdns_mhdp_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- cancel_work_sync(&mhdp->modeset_retry_work);
- flush_work(&mhdp->hpd_work);
- /* Ignoring mhdp->hdcp.check_work and mhdp->hdcp.prop_work here. */
}
static const struct of_device_id mhdp_ids[] = {
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
index b53335b0d..2d09df8d2 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
@@ -359,6 +359,7 @@ struct cdns_mhdp_device {
struct device *dev;
struct clk *clk;
struct phy *phy;
+ int irq;
const struct cdns_mhdp_platform_info *info;
--
2.50.1.windows.1