[PATCH v4 01/10] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback

From: Sebastian Reichel

Date: Tue Jul 21 2026 - 15:07:06 EST


Currently the Synopsys DesignWare DP controller driver's bind function
requests lots of resources using device managed functions. These are
free'd on driver removal instead of at unbind time. Fix this discrepancy
by introducing a new probe helper function and moving over most of the
early resource acquisitions.

Signed-off-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 128 +++++++++++++++---------------
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 53 ++++++++-----
include/drm/bridge/dw_dp.h | 5 +-
3 files changed, 99 insertions(+), 87 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 3445c82e6f50..368e1f85e625 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1954,20 +1954,72 @@ static const struct regmap_config dw_dp_regmap_config = {
.rd_table = &dw_dp_readable_table,
};

-static void dw_dp_phy_exit(void *data)
+int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
{
- struct dw_dp *dp = data;
+ struct device *dev = dp->dev;
+ struct drm_bridge *bridge;
+ int ret;
+
+ bridge = &dp->bridge;
+ bridge->of_node = dev->of_node;
+ bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+ bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
+ bridge->ycbcr_420_allowed = true;
+
+ drm_bridge_add(bridge);
+
+ dp->aux.dev = dev;
+ dp->aux.drm_dev = encoder->dev;
+ dp->aux.name = dev_name(dev);
+ dp->aux.transfer = dw_dp_aux_transfer;
+ ret = drm_dp_aux_register(&dp->aux);
+ if (ret) {
+ dev_err_probe(dev, ret, "Aux register failed\n");
+ goto unregister_bridge;
+ }
+
+ ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to attach bridge\n");
+ goto unregister_aux;
+ }
+
+ dw_dp_init_hw(dp);

+ ret = phy_init(dp->phy);
+ if (ret) {
+ dev_err_probe(dev, ret, "phy init failed\n");
+ goto unregister_aux;
+ }
+
+ enable_irq(dp->irq);
+
+ return 0;
+
+unregister_aux:
+ drm_dp_aux_unregister(&dp->aux);
+
+unregister_bridge:
+ drm_bridge_remove(bridge);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dw_dp_bind);
+
+void dw_dp_unbind(struct dw_dp *dp)
+{
+ disable_irq(dp->irq);
phy_exit(dp->phy);
+ drm_dp_aux_unregister(&dp->aux);
+ drm_bridge_remove(&dp->bridge);
}
+EXPORT_SYMBOL_GPL(dw_dp_unbind);

-struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
- const struct dw_dp_plat_data *plat_data)
+struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct dw_dp *dp;
- struct drm_bridge *bridge;
+ struct device *dev = &pdev->dev;
void __iomem *res;
+ struct dw_dp *dp;
int ret;

dp = devm_drm_bridge_alloc(dev, struct dw_dp, bridge, &dw_dp_bridge_funcs);
@@ -1976,9 +2028,8 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,

dp->dev = dev;
dp->pixel_mode = plat_data->pixel_mode;
-
dp->plat_data.max_link_rate = plat_data->max_link_rate;
- bridge = &dp->bridge;
+
mutex_init(&dp->irq_lock);
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
init_completion(&dp->complete);
@@ -2035,69 +2086,20 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
return ERR_CAST(dp->rstc);
}

- bridge->of_node = dev->of_node;
- bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
- bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
- bridge->ycbcr_420_allowed = true;
-
- ret = devm_drm_bridge_add(dev, bridge);
- if (ret)
- return ERR_PTR(ret);
-
- dp->aux.dev = dev;
- dp->aux.drm_dev = encoder->dev;
- dp->aux.name = dev_name(dev);
- dp->aux.transfer = dw_dp_aux_transfer;
- ret = drm_dp_aux_register(&dp->aux);
- if (ret) {
- dev_err_probe(dev, ret, "Aux register failed\n");
- return ERR_PTR(ret);
- }
-
- ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
- if (ret) {
- dev_err_probe(dev, ret, "Failed to attach bridge\n");
- goto unregister_aux;
- }
-
- dw_dp_init_hw(dp);
-
- ret = phy_init(dp->phy);
- if (ret) {
- dev_err_probe(dev, ret, "phy init failed\n");
- goto unregister_aux;
- }
-
- ret = devm_add_action_or_reset(dev, dw_dp_phy_exit, dp);
- if (ret)
- goto unregister_aux;
-
dp->irq = platform_get_irq(pdev, 0);
- if (dp->irq < 0) {
- ret = dp->irq;
- goto unregister_aux;
- }
+ if (dp->irq < 0)
+ return ERR_PTR(dp->irq);

ret = devm_request_threaded_irq(dev, dp->irq, NULL, dw_dp_irq,
- IRQF_ONESHOT, dev_name(dev), dp);
+ IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(dev), dp);
if (ret) {
dev_err_probe(dev, ret, "failed to request irq\n");
- goto unregister_aux;
+ return ERR_PTR(ret);
}

return dp;
-
-unregister_aux:
- drm_dp_aux_unregister(&dp->aux);
- return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(dw_dp_bind);
-
-void dw_dp_unbind(struct dw_dp *dp)
-{
- drm_dp_aux_unregister(&dp->aux);
-}
-EXPORT_SYMBOL_GPL(dw_dp_unbind);
+EXPORT_SYMBOL_GPL(dw_dp_probe);

MODULE_AUTHOR("Andy Yan <andyshrk@xxxxxxx>");
MODULE_DESCRIPTION("DW DP Core Library");
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index b23efb153c9e..005938dc66c9 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -26,7 +26,7 @@
struct rockchip_dw_dp {
struct dw_dp *base;
struct device *dev;
- struct rockchip_encoder encoder;
+ struct rockchip_encoder *encoder;
};

static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder,
@@ -73,37 +73,28 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {

static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
- const struct dw_dp_plat_data *plat_data;
+ struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
- struct rockchip_dw_dp *dp;
struct drm_encoder *encoder;
struct drm_connector *connector;
int ret;

- dp = drmm_kzalloc(drm_dev, sizeof(*dp), GFP_KERNEL);
- if (!dp)
+ dp->encoder = drmm_kzalloc(drm_dev, sizeof(*dp->encoder), GFP_KERNEL);
+ if (!dp->encoder)
return -ENOMEM;

- dp->dev = dev;
- platform_set_drvdata(pdev, dp);
-
- plat_data = of_device_get_match_data(dev);
- if (!plat_data)
- return -ENODEV;
-
- encoder = &dp->encoder.encoder;
+ encoder = &dp->encoder->encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
- rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0);
+ rockchip_drm_encoder_set_crtc_endpoint_id(dp->encoder, dev->of_node, 0, 0);

ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);

- dp->base = dw_dp_bind(dev, encoder, plat_data);
- if (IS_ERR(dp->base))
- return PTR_ERR(dp->base);
+ ret = dw_dp_bind(dp->base, encoder);
+ if (ret)
+ return ret;

connector = drm_bridge_connector_init(drm_dev, encoder);
if (IS_ERR(connector)) {
@@ -128,12 +119,30 @@ static const struct component_ops dw_dp_rockchip_component_ops = {
.unbind = dw_dp_rockchip_unbind,
};

-static int dw_dp_probe(struct platform_device *pdev)
+static int dw_dp_rockchip_probe(struct platform_device *pdev)
{
+ const struct dw_dp_plat_data *plat_data;
+ struct device *dev = &pdev->dev;
+ struct rockchip_dw_dp *dp;
+
+ plat_data = of_device_get_match_data(dev);
+ if (!plat_data)
+ return -ENODEV;
+
+ dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, dp);
+ dp->dev = dev;
+
+ dp->base = dw_dp_probe(pdev, plat_data);
+ if (IS_ERR(dp->base))
+ return PTR_ERR(dp->base);
+
return component_add(&pdev->dev, &dw_dp_rockchip_component_ops);
}

-static void dw_dp_remove(struct platform_device *pdev)
+static void dw_dp_rockchip_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dw_dp_rockchip_component_ops);
}
@@ -161,8 +170,8 @@ static const struct of_device_id dw_dp_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_dp_of_match);

struct platform_driver dw_dp_driver = {
- .probe = dw_dp_probe,
- .remove = dw_dp_remove,
+ .probe = dw_dp_rockchip_probe,
+ .remove = dw_dp_rockchip_remove,
.driver = {
.name = "dw-dp",
.of_match_table = dw_dp_of_match,
diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h
index 22105c3e8e4d..a82412a9e769 100644
--- a/include/drm/bridge/dw_dp.h
+++ b/include/drm/bridge/dw_dp.h
@@ -22,7 +22,8 @@ struct dw_dp_plat_data {
u8 pixel_mode;
};

-struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
- const struct dw_dp_plat_data *plat_data);
+int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder);
void dw_dp_unbind(struct dw_dp *dp);
+
+struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data);
#endif /* __DW_DP__ */

--
2.53.0