From b71a623598d319cae2acd9df1b297cc948c58bc7 Mon Sep 17 00:00:00 2001 From: Albert Esteve Date: Fri, 17 Jul 2026 16:02:06 +0200 Subject: [PATCH] drm/panel: of_drm_find_panel() return a counted reference Callers of of_drm_find_panel() and drm_of_find_panel_or_bridge() receive a pointer with no reference held, creating a window where the panel device can be unregistered and freed between the lookup and first use (e.g., drm_panel_prepare()). Fix the lookup function by acquiring a reference with drm_panel_get() before returning, under panel_lock. Callers are now responsible for calling drm_panel_put() when they no longer need the pointer. For bridge drivers that immediately wrap the panel in a panel_bridge (which acquires its own reference), release the lookup reference right after the bridge creation call. For analogix-anx6345, which stores the panel for direct use, release the reference in the i2c remove path. For platform drivers using analogix_dp_core with a component lifecycle (exynos_dp, rockchip analogix_dp), release the lookup reference in the platform remove() function. The panel_bridge created during bind() holds a separate reference that devm cleanup releases after remove() returns. Also fix devm_drm_of_get_bridge() and drmm_of_get_bridge() in bridge/panel.c itself, update a second batch of drivers calling of_drm_find_panel() or drm_of_find_panel_or_bridge() to release the lookup reference after wrapping the panel in a bridge, and handle the cases where a panel is found but cannot be used, dropping the reference immediately in those paths. Assisted-by: Claude:claude-opus-4-6 Acked-by: Maxime Ripard Signed-off-by: Albert Esteve Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260717-drm_refcount_wiring-v3-3-023900c32e01@redhat.com --- .../drm/bridge/analogix/analogix-anx6345.c | 12 ++++++++++ drivers/gpu/drm/bridge/fsl-ldb.c | 1 + drivers/gpu/drm/bridge/lontium-lt9211.c | 1 + drivers/gpu/drm/bridge/lvds-codec.c | 1 + drivers/gpu/drm/bridge/panel.c | 8 +++++-- drivers/gpu/drm/bridge/samsung-dsim.c | 1 + drivers/gpu/drm/bridge/ssd2825.c | 1 + drivers/gpu/drm/bridge/tc358767.c | 2 ++ drivers/gpu/drm/bridge/tc358768.c | 1 + drivers/gpu/drm/bridge/waveshare-dsi.c | 1 + drivers/gpu/drm/drm_of.c | 3 ++- drivers/gpu/drm/drm_panel.c | 12 ++++++---- drivers/gpu/drm/exynos/exynos_dp.c | 19 ++++++++++++++- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 3 +++ drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 18 ++++++++++++++ drivers/gpu/drm/imx/dcss/dcss-kms.c | 3 +++ drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 +++- drivers/gpu/drm/logicvc/logicvc_interface.c | 12 ++++++++++ drivers/gpu/drm/mcde/mcde_drv.c | 1 + drivers/gpu/drm/mcde/mcde_dsi.c | 1 + drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 + drivers/gpu/drm/omapdrm/dss/output.c | 1 + drivers/gpu/drm/pl111/pl111_drv.c | 1 + .../gpu/drm/renesas/rcar-du/rcar_du_encoder.c | 1 + drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 1 + .../gpu/drm/renesas/rz-du/rzg2l_du_encoder.c | 1 + .../gpu/drm/rockchip/analogix_dp-rockchip.c | 11 +++++++++ drivers/gpu/drm/rockchip/rockchip_lvds.c | 4 ++++ drivers/gpu/drm/rockchip/rockchip_rgb.c | 3 +++ drivers/gpu/drm/sti/sti_dvo.c | 3 +++ drivers/gpu/drm/stm/ltdc.c | 1 + drivers/gpu/drm/stm/lvds.c | 12 +++++++--- drivers/gpu/drm/sun4i/sun4i_lvds.c | 13 ++++++++++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 13 ++++++++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 ++ drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 6 ++++- drivers/gpu/drm/tegra/dsi.c | 5 ++++ drivers/gpu/drm/tegra/output.c | 24 ++++++++++++++++--- drivers/gpu/drm/tidss/tidss_kms.c | 16 +++++++++---- drivers/gpu/drm/tve200/tve200_drv.c | 1 + 40 files changed, 204 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index eba5c6dcb5ad..d152c02419f5 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -663,6 +663,11 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345) return false; } +static void anx6345_panel_put_action(void *data) +{ + drm_panel_put(data); +} + static int anx6345_i2c_probe(struct i2c_client *client) { struct anx6345 *anx6345; @@ -691,6 +696,13 @@ static int anx6345_i2c_probe(struct i2c_client *client) if (err) DRM_DEBUG("No panel found\n"); + if (anx6345->panel) { + err = devm_add_action_or_reset(dev, anx6345_panel_put_action, + anx6345->panel); + if (err) + return err; + } + /* 1.2V digital core power regulator */ anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12"); if (IS_ERR(anx6345->dvdd12)) { diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index cc1f88e7873e..26cc72948f31 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -348,6 +348,7 @@ static int fsl_ldb_probe(struct platform_device *pdev) fsl_ldb->use_termination_resistor = true; fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(fsl_ldb->panel_bridge)) return PTR_ERR(fsl_ldb->panel_bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index f39d83a5ae37..6d97ae5640c4 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -660,6 +660,7 @@ static int lt9211_parse_dt(struct lt9211 *ctx) return ret; if (panel) { panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); } diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index a82ea0c944eb..f97c8586273d 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -156,6 +156,7 @@ static int lvds_codec_probe(struct platform_device *pdev) lvds_codec->panel_bridge = devm_drm_panel_bridge_add_typed(dev, panel, lvds_codec->connector_type); + drm_panel_put(panel); if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 01e342a9e0b5..02388a3de626 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -515,8 +515,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, if (ret) return ERR_PTR(ret); - if (panel) + if (panel) { bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); + } return bridge; } @@ -549,8 +551,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, if (ret) return ERR_PTR(ret); - if (panel) + if (panel) { bridge = drmm_panel_bridge_add(drm, panel); + drm_panel_put(panel); + } return bridge; } diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index 25ab475309e8..e2fc69fc51b6 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1934,6 +1934,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, panel = of_drm_find_panel(remote); if (!IS_ERR(panel)) { next_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(next_bridge)) { ret = PTR_ERR(next_bridge); next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c index 00a4ed4a0700..b0142d6d40dc 100644 --- a/drivers/gpu/drm/bridge/ssd2825.c +++ b/drivers/gpu/drm/bridge/ssd2825.c @@ -301,6 +301,7 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index ce1b73214f26..8f1648f799a7 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2329,6 +2329,7 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) if (panel) { bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } @@ -2359,6 +2360,7 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) struct drm_bridge *panel_bridge; panel_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 0d85120fcc7a..112a421f99b5 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -479,6 +479,7 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c index 9c9825c9b7c5..420f3b870a74 100644 --- a/drivers/gpu/drm/bridge/waveshare-dsi.c +++ b/drivers/gpu/drm/bridge/waveshare-dsi.c @@ -169,6 +169,7 @@ static int ws_bridge_probe(struct i2c_client *i2c) return dev_err_probe(dev, ret, "Failed to find remote panel\n"); ws->next_bridge = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); if (IS_ERR(ws->next_bridge)) return PTR_ERR(ws->next_bridge); diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 1f4cff6bb4a7..e495117735e1 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -269,7 +269,8 @@ EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation); * @np: device tree node containing encoder output ports * @port: port in the device tree node * @endpoint: endpoint in the device tree node - * @panel: pointer to hold returned drm_panel, must not be NULL + * @panel: pointer to hold returned drm_panel, must not be NULL. On success + * the caller must call drm_panel_put() when done with the panel * @bridge: pointer to hold returned drm_bridge * * Given a DT node's port and endpoint number, find the connected node and diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 68b5a2b7694c..32fba1a562fb 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -459,14 +459,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc); #ifdef CONFIG_OF /** - * of_drm_find_panel - look up a panel using a device tree node + * of_drm_find_panel - look up and reference a panel by device tree node * @np: device tree node of the panel * * Searches the set of registered panels for one that matches the given device - * tree node. If a matching panel is found, return a pointer to it. + * tree node. If a matching panel is found, the panel's reference count is + * incremented before returning a pointer to it. The caller must call + * drm_panel_put() when it no longer needs the panel pointer. * - * Return: A pointer to the panel registered for the specified device tree - * node or an ERR_PTR() if no panel matching the device tree node can be found. + * Return: A reference-counted pointer to the panel registered for the specified + * device tree node or an ERR_PTR() if no panel matching the device tree node + * can be found. * * Possible error codes returned by this function: * @@ -485,6 +488,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) list_for_each_entry(panel, &panel_list, list) { if (panel->dev->of_node == np) { + drm_panel_get(panel); mutex_unlock(&panel_lock); return panel; } diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index b80540328150..a418011f7d4d 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -182,8 +182,15 @@ static int exynos_dp_probe(struct platform_device *pdev) out: dp->adp = analogix_dp_probe(dev, &dp->plat_data); - if (IS_ERR(dp->adp)) + if (IS_ERR(dp->adp)) { + /* + * The driver core does not invoke remove() for failed probes, + * so release the probe-time panel reference here. + */ + if (dp->plat_data.panel) + drm_panel_put(dp->plat_data.panel); return PTR_ERR(dp->adp); + } if (dp->plat_data.panel || dp->plat_data.next_bridge) return component_add(&pdev->dev, &exynos_dp_ops); @@ -193,6 +200,16 @@ static int exynos_dp_probe(struct platform_device *pdev) static void exynos_dp_remove(struct platform_device *pdev) { + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + /* + * Release the probe-time reference from of_drm_find_panel(). If bind + * ran, the panel_bridge holds a second reference that devm cleanup + * will release when the bridge is destroyed after remove() returns. + */ + if (dp->plat_data.panel) + drm_panel_put(dp->plat_data.panel); + component_del(&pdev->dev, &exynos_dp_ops); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 0dc36df6ada3..9d15a0035ea9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -245,5 +245,8 @@ int exynos_dpi_remove(struct drm_encoder *encoder) exynos_dpi_disable(&ctx->encoder); + if (ctx->panel) + drm_panel_put(ctx->panel); + return 0; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 84eff7519e32..ec71fbbb0eb8 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -109,6 +109,13 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, return ret; } +static void fsl_dcu_panel_put_action(void *data) +{ + struct drm_panel *panel = data; + + drm_panel_put(panel); +} + int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) { struct device_node *panel_node; @@ -124,6 +131,12 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) if (IS_ERR(fsl_dev->connector.panel)) return PTR_ERR(fsl_dev->connector.panel); + ret = devm_add_action_or_reset(fsl_dev->dev, + fsl_dcu_panel_put_action, + fsl_dev->connector.panel); + if (ret) + return ret; + return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); } @@ -132,6 +145,11 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) return ret; if (panel) { + ret = devm_add_action_or_reset(fsl_dev->dev, + fsl_dcu_panel_put_action, panel); + if (ret) + return ret; + fsl_dev->connector.panel = panel; return fsl_dcu_attach_panel(fsl_dev, panel); } diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c index 50bd7f36d36d..01e0c10b6ea1 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.c +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -77,6 +77,9 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) if (ret) return ret; + if (panel) + drm_panel_put(panel); + if (!bridge) { dev_err(ddev->dev, "No bridge found %d.\n", ret); return -ENODEV; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 7e569af22391..738a80b2550f 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1297,9 +1297,11 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) goto err_drvdata; } - if (panel) + if (panel) { bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); + } ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder, NULL, DRM_MODE_ENCODER_DPI, NULL); diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.c b/drivers/gpu/drm/logicvc/logicvc_interface.c index 689049d395c0..81f760dc07f8 100644 --- a/drivers/gpu/drm/logicvc/logicvc_interface.c +++ b/drivers/gpu/drm/logicvc/logicvc_interface.c @@ -28,6 +28,11 @@ #define logicvc_interface_from_drm_connector(c) \ container_of(c, struct logicvc_interface, drm_connector) +static void logicvc_panel_put_action(void *data) +{ + drm_panel_put(data); +} + static void logicvc_encoder_enable(struct drm_encoder *drm_encoder) { struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev); @@ -160,6 +165,13 @@ int logicvc_interface_init(struct logicvc_drm *logicvc) if (ret == -EPROBE_DEFER) goto error_early; + if (interface->drm_panel) { + ret = devm_add_action_or_reset(dev, logicvc_panel_put_action, + interface->drm_panel); + if (ret) + goto error_early; + } + ret = drm_encoder_init(drm_dev, &interface->drm_encoder, &logicvc_encoder_funcs, encoder_type, NULL); if (ret) { diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 5f2c462bad7e..53275b575f0c 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -153,6 +153,7 @@ static int mcde_modeset_init(struct drm_device *drm) if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) { dev_err(drm->dev, "Could not connect panel bridge\n"); diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index 5cf44ccb02cf..694372581840 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -1127,6 +1127,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); + drm_panel_put(panel); if (IS_ERR(bridge)) { dev_err(dev, "error adding panel bridge\n"); return PTR_ERR(bridge); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 9b8fbda85d28..7cb19c15bf39 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -127,6 +127,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb) if (panel) { bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(bridge); } diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index ca891aba3820..6e9bc605ee22 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -43,6 +43,7 @@ int omapdss_device_init_output(struct omap_dss_device *out, struct drm_bridge *bridge; bridge = drm_panel_bridge_add(out->panel); + drm_panel_put(out->panel); if (IS_ERR(bridge)) { dev_err(out->dev, "unable to create panel bridge (%ld)\n", diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index ac7b1d12a0f5..8ec659b3c08e 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -145,6 +145,7 @@ static int pl111_modeset_init(struct drm_device *dev) if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_Unknown); + drm_panel_put(panel); if (IS_ERR(bridge)) { ret = PTR_ERR(bridge); goto finish; diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c index db2088529b48..d8e7e9877ba8 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c @@ -69,6 +69,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(no_free_ptr(bridge)); diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index e433ce61d431..9527d3637e6c 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -791,6 +791,7 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) if (lvds->panel) { lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev, lvds->panel); + drm_panel_put(lvds->panel); if (IS_ERR_OR_NULL(lvds->next_bridge)) { ret = -EINVAL; goto done; diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c index f50d166b764f..3d0999e4fcfd 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c @@ -90,6 +90,7 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu, bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_panel_put(panel); if (IS_ERR(bridge)) return PTR_ERR(no_free_ptr(bridge)); diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 46c245e35d21..587e60232ec7 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -485,6 +486,16 @@ static int rockchip_dp_probe(struct platform_device *pdev) static void rockchip_dp_remove(struct platform_device *pdev) { + struct rockchip_dp_device *dp = platform_get_drvdata(pdev); + + /* + * Release the probe-time reference from of_drm_find_panel(). If bind + * ran, the panel_bridge holds a second reference that devm cleanup + * will release when the bridge is destroyed after remove() returns. + */ + if (dp->plat_data.panel) + drm_panel_put(dp->plat_data.panel); + component_del(&pdev->dev, &rockchip_dp_component_ops); } diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 262f81875278..505c6c56736d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -610,6 +610,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, if (lvds->panel) { lvds->bridge = drm_panel_bridge_add_typed(lvds->panel, DRM_MODE_CONNECTOR_LVDS); + drm_panel_put(lvds->panel); + lvds->panel = NULL; if (IS_ERR(lvds->bridge)) { ret = PTR_ERR(lvds->bridge); goto err_free_encoder; @@ -646,6 +648,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, err_free_encoder: drm_encoder_cleanup(encoder); err_put_remote: + if (lvds->panel) + drm_panel_put(lvds->panel); of_node_put(remote); err_put_port: of_node_put(port); diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index 2ad24b914989..0e1830bc40ee 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -135,6 +135,8 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev, if (ret < 0) { DRM_DEV_ERROR(drm_dev->dev, "failed to initialize encoder: %d\n", ret); + if (panel) + drm_panel_put(panel); return ERR_PTR(ret); } @@ -143,6 +145,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev, if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_LVDS); + drm_panel_put(panel); if (IS_ERR(bridge)) return ERR_CAST(bridge); } diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index b48099468eb9..cdb19b75f5e1 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -498,6 +498,9 @@ static void sti_dvo_unbind(struct device *dev, { struct sti_dvo *dvo = dev_get_drvdata(dev); + if (dvo->panel) + drm_panel_put(dvo->panel); + drm_bridge_remove(&dvo->bridge); } diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 95fcfa48d8be..daf198edb42f 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1982,6 +1982,7 @@ int ltdc_load(struct drm_device *ddev) if (panel) { bridge = drmm_panel_bridge_add(ddev, panel); + drm_panel_put(panel); if (IS_ERR(bridge)) { drm_err(ddev, "panel-bridge endpoint %d\n", i); ret = PTR_ERR(bridge); diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index 90a44e722057..1072dbe7bd6a 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -1068,20 +1068,20 @@ static int lvds_probe(struct platform_device *pdev) if (IS_ERR(lvds->base)) { ret = PTR_ERR(lvds->base); dev_err(dev, "Unable to get regs %d\n", ret); - return ret; + goto err_put_panel; } lvds->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(lvds->pclk)) { ret = PTR_ERR(lvds->pclk); dev_err(dev, "Unable to get peripheral clock: %d\n", ret); - return ret; + goto err_put_panel; } ret = clk_prepare_enable(lvds->pclk); if (ret) { dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__); - return ret; + goto err_put_panel; } rstc = devm_reset_control_get_exclusive(dev, NULL); @@ -1181,6 +1181,9 @@ static int lvds_probe(struct platform_device *pdev) err_lvds_probe: clk_disable_unprepare(lvds->pclk); +err_put_panel: + if (lvds->panel) + drm_panel_put(lvds->panel); return ret; } @@ -1189,6 +1192,9 @@ static void lvds_remove(struct platform_device *pdev) { struct stm_lvds *lvds = platform_get_drvdata(pdev); + if (lvds->panel) + drm_panel_put(lvds->panel); + lvds_pixel_clk_unregister(lvds); drm_bridge_remove(&lvds->lvds_bridge); diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c index 35a3f987c37a..6b98d20a8890 100644 --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -18,6 +18,11 @@ #include "sun4i_tcon.h" #include "sun4i_lvds.h" +static void sun4i_panel_put_action(void *data) +{ + drm_panel_put(data); +} + struct sun4i_lvds { struct drm_connector connector; struct drm_encoder encoder; @@ -117,6 +122,14 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon) return 0; } + if (lvds->panel) { + ret = devm_add_action_or_reset(tcon->dev, + sun4i_panel_put_action, + lvds->panel); + if (ret) + return ret; + } + drm_encoder_helper_add(&lvds->encoder, &sun4i_lvds_enc_helper_funcs); ret = drm_encoder_init(drm, &lvds->encoder, &sun4i_lvds_enc_funcs, diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index 9c3fbf1b949e..3da9a9283c6e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -43,6 +43,11 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder) encoder); } +static void sun4i_panel_put_action(void *data) +{ + drm_panel_put(data); +} + static int sun4i_rgb_get_modes(struct drm_connector *connector) { struct sun4i_rgb *rgb = @@ -209,6 +214,14 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon) return 0; } + if (rgb->panel) { + ret = devm_add_action_or_reset(tcon->dev, + sun4i_panel_put_action, + rgb->panel); + if (ret) + return ret; + } + drm_encoder_helper_add(&rgb->encoder, &sun4i_rgb_enc_helper_funcs); ret = drm_encoder_init(drm, &rgb->encoder, &sun4i_rgb_enc_funcs, diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 960e83c8291d..d4c1723c5e3d 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -1326,6 +1326,8 @@ static int sun4i_tcon_probe(struct platform_device *pdev) ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge); if (ret == -EPROBE_DEFER) return ret; + if (panel) + drm_panel_put(panel); } return component_add(&pdev->dev, &sun4i_tcon_ops); diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index f0c9f0e573d2..d504ae583294 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -971,8 +971,10 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host, if (IS_ERR(panel)) return PTR_ERR(panel); - if (!dsi->drm || !dsi->drm->registered) + if (!dsi->drm || !dsi->drm->registered) { + drm_panel_put(panel); return -EPROBE_DEFER; + } dsi->panel = panel; dsi->device = device; @@ -989,6 +991,8 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host, { struct sun6i_dsi *dsi = host_to_sun6i_dsi(host); + if (dsi->panel) + drm_panel_put(dsi->panel); dsi->panel = NULL; dsi->device = NULL; diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index e7fdd8c7ac12..cb88aafbd36f 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -1527,6 +1527,10 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host, if (!dsi->master) { struct tegra_output *output = &dsi->output; + /* + * tegra_output_probe() never populates a panel for DSI + * outputs, so output->panel is always NULL here. + */ output->panel = of_drm_find_panel(device->dev.of_node); if (IS_ERR(output->panel)) output->panel = NULL; @@ -1545,6 +1549,7 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host, struct tegra_output *output = &dsi->output; if (output->panel && &device->dev == output->panel->dev) { + drm_panel_put(output->panel); output->panel = NULL; if (output->connector.dev) diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 49e4f63a5550..d0ba3bd0aab8 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -117,11 +117,19 @@ int tegra_output_probe(struct tegra_output *output) */ WARN_ON(output->panel || output->bridge); + if (output->panel) { + drm_panel_put(output->panel); + output->panel = NULL; + } + output->panel = of_drm_find_panel(panel); of_node_put(panel); - if (IS_ERR(output->panel)) - return PTR_ERR(output->panel); + if (IS_ERR(output->panel)) { + err = PTR_ERR(output->panel); + output->panel = NULL; + return err; + } } ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0); @@ -131,7 +139,7 @@ int tegra_output_probe(struct tegra_output *output) if (!output->ddc) { err = -EPROBE_DEFER; - return err; + goto put_i2c; } } @@ -185,6 +193,11 @@ int tegra_output_probe(struct tegra_output *output) return 0; put_i2c: + if (output->panel) { + drm_panel_put(output->panel); + output->panel = NULL; + } + if (output->ddc) i2c_put_adapter(output->ddc); @@ -195,6 +208,11 @@ int tegra_output_probe(struct tegra_output *output) void tegra_output_remove(struct tegra_output *output) { + if (output->panel) { + drm_panel_put(output->panel); + output->panel = NULL; + } + if (output->hpd_gpio) free_irq(output->hpd_irq, output); diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 1512ee2574b6..70c14c3be10d 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -162,6 +162,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) if (panel) { u32 conn_type; + int ret; dev_dbg(dev, "Setting up panel for port %d\n", i); @@ -176,7 +177,8 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) break; default: WARN_ON(1); - return -EINVAL; + ret = -EINVAL; + goto put_panel; } if (panel->connector_type != conn_type) { @@ -184,16 +186,20 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) "%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n", __func__, dev_name(panel->dev), i, panel->connector_type, conn_type); - return -EINVAL; + ret = -EINVAL; + goto put_panel; } bridge = devm_drm_panel_bridge_add(dev, panel); - if (IS_ERR(bridge)) { + ret = PTR_ERR_OR_ZERO(bridge); + if (ret) dev_err(dev, "failed to set up panel bridge for port %d\n", i); - return PTR_ERR(bridge); - } +put_panel: + drm_panel_put(panel); + if (ret) + return ret; } pipes[num_pipes].hw_videoport = i; diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 562f3f11812a..f858c58ccb99 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -84,6 +84,7 @@ static int tve200_modeset_init(struct drm_device *dev) if (panel) { bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_Unknown); + drm_panel_put(panel); if (IS_ERR(bridge)) { ret = PTR_ERR(bridge); goto out_bridge;