In preparation to handle refcounting of the out_bridge, we need to ensure the out_bridge pointer contains either a valid bridge pointer or NULL, not an ERR_PTR. Otherwise calls such as drm_bridge_get/put() would try to redeference an ERR_PTR.
As a preliminary cleanup, add a temporary local 'next_bridge' pointer and only copy it in dsi->out_bridge as late as possible, i.e. just before calling pdata->host_ops->attach() which uses it (only in the exynos driver). Not strictly needed, but for symmetry move the clearing of dsi->out_bridge in samsung_dsim_host_detach() to after pdata->host_ops->detach(). Signed-off-by: Luca Ceresoli <[email protected]> --- Changes in v2: - fix "pointer set too late" bug (NULL pointer deref in the exynos case) - *not* add Acked-by: Maxime as the patch has changed --- drivers/gpu/drm/bridge/samsung-dsim.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index eabc4c32f6ab..8dd058124e93 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1886,6 +1886,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, { struct samsung_dsim *dsi = host_to_dsi(host); const struct samsung_dsim_plat_data *pdata = dsi->plat_data; + struct drm_bridge *next_bridge; struct device *dev = dsi->dev; struct device_node *np = dev->of_node; struct device_node *remote; @@ -1924,17 +1925,17 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, panel = of_drm_find_panel(remote); if (!IS_ERR(panel)) { - dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel); + next_bridge = devm_drm_panel_bridge_add(dev, panel); } else { - dsi->out_bridge = of_drm_find_bridge(remote); - if (!dsi->out_bridge) - dsi->out_bridge = ERR_PTR(-EINVAL); + next_bridge = of_drm_find_bridge(remote); + if (!next_bridge) + next_bridge = ERR_PTR(-EINVAL); } of_node_put(remote); - if (IS_ERR(dsi->out_bridge)) { - ret = PTR_ERR(dsi->out_bridge); + if (IS_ERR(next_bridge)) { + ret = PTR_ERR(next_bridge); DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret); return ret; } @@ -1958,10 +1959,13 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, return ret; } + // The next bridge can be used by host_ops->attach + dsi->out_bridge = next_bridge; + if (pdata->host_ops && pdata->host_ops->attach) { ret = pdata->host_ops->attach(dsi, device); if (ret) - return ret; + goto err_release_next_bridge; } dsi->lanes = device->lanes; @@ -1969,6 +1973,10 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, dsi->mode_flags = device->mode_flags; return 0; + +err_release_next_bridge: + dsi->out_bridge = NULL; + return ret; } static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi) @@ -1985,11 +1993,11 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host, struct samsung_dsim *dsi = host_to_dsi(host); const struct samsung_dsim_plat_data *pdata = dsi->plat_data; - dsi->out_bridge = NULL; - if (pdata->host_ops && pdata->host_ops->detach) pdata->host_ops->detach(dsi, device); + dsi->out_bridge = NULL; + samsung_dsim_unregister_te_irq(dsi); drm_bridge_remove(&dsi->bridge); -- 2.52.0
