]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/display: bridge_connector: get/put the stored bridges
authorLuca Ceresoli <luca.ceresoli@bootlin.com>
Fri, 17 Oct 2025 16:15:05 +0000 (18:15 +0200)
committerLuca Ceresoli <luca.ceresoli@bootlin.com>
Mon, 3 Nov 2025 11:17:54 +0000 (12:17 +0100)
drm_bridge_connector_init() takes eight pointers to various bridges, some
of which can be identical, and stores them in pointers inside struct
drm_bridge_connector. Get a reference to each of the taken bridges and put
it on cleanup.

Achieve this by adding a drmm cleanup callback whic puts all the non-NULL
bridges. Using drmm ensures the cleanup happens on drm_device teardown,
whichever is the return value of this function.

Four of these pointers (edid, hpd, detect and modes) can be written
multiple times (up to once per loop iterations), in order to eventually
store the last matching bridge. So when one of those pointers is
overwritten, we need to put the reference that we got during the previous
assignment. Add a drm_bridge_put() before writing them to handle this.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> # db410c
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://patch.msgid.link/20251017-drm-bridge-alloc-getput-bridge-connector-fix-hdmi_cec-v2-2-667abf6d47c0@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
drivers/gpu/drm/display/drm_bridge_connector.c

index a5bdd6c1064399ece6b19560f145b877c9e0680e..95ccf86527129edaa6fcc75c6202985e73c46da8 100644 (file)
@@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
  * Bridge Connector Initialisation
  */
 
+static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+       struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+       drm_bridge_put(bridge_connector->bridge_edid);
+       drm_bridge_put(bridge_connector->bridge_hpd);
+       drm_bridge_put(bridge_connector->bridge_detect);
+       drm_bridge_put(bridge_connector->bridge_modes);
+       drm_bridge_put(bridge_connector->bridge_hdmi);
+       drm_bridge_put(bridge_connector->bridge_hdmi_audio);
+       drm_bridge_put(bridge_connector->bridge_dp_audio);
+       drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+}
+
 /**
  * drm_bridge_connector_init - Initialise a connector for a chain of bridges
  * @drm: the DRM device
@@ -649,6 +663,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
        if (!bridge_connector)
                return ERR_PTR(-ENOMEM);
 
+       ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+       if (ret)
+               return ERR_PTR(ret);
+
        bridge_connector->encoder = encoder;
 
        /*
@@ -672,14 +690,22 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                if (!bridge->ycbcr_420_allowed)
                        connector->ycbcr_420_allowed = false;
 
-               if (bridge->ops & DRM_BRIDGE_OP_EDID)
-                       bridge_connector->bridge_edid = bridge;
-               if (bridge->ops & DRM_BRIDGE_OP_HPD)
-                       bridge_connector->bridge_hpd = bridge;
-               if (bridge->ops & DRM_BRIDGE_OP_DETECT)
-                       bridge_connector->bridge_detect = bridge;
-               if (bridge->ops & DRM_BRIDGE_OP_MODES)
-                       bridge_connector->bridge_modes = bridge;
+               if (bridge->ops & DRM_BRIDGE_OP_EDID) {
+                       drm_bridge_put(bridge_connector->bridge_edid);
+                       bridge_connector->bridge_edid = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_HPD) {
+                       drm_bridge_put(bridge_connector->bridge_hpd);
+                       bridge_connector->bridge_hpd = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
+                       drm_bridge_put(bridge_connector->bridge_detect);
+                       bridge_connector->bridge_detect = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+                       drm_bridge_put(bridge_connector->bridge_modes);
+                       bridge_connector->bridge_modes = drm_bridge_get(bridge);
+               }
                if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
                        if (bridge_connector->bridge_hdmi)
                                return ERR_PTR(-EBUSY);
@@ -687,7 +713,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                            !bridge->funcs->hdmi_clear_infoframe)
                                return ERR_PTR(-EINVAL);
 
-                       bridge_connector->bridge_hdmi = bridge;
+                       bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
 
                        if (bridge->supported_formats)
                                supported_formats = bridge->supported_formats;
@@ -710,7 +736,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                            !bridge->funcs->hdmi_audio_shutdown)
                                return ERR_PTR(-EINVAL);
 
-                       bridge_connector->bridge_hdmi_audio = bridge;
+                       bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge);
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
@@ -728,21 +754,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                            !bridge->funcs->dp_audio_shutdown)
                                return ERR_PTR(-EINVAL);
 
-                       bridge_connector->bridge_dp_audio = bridge;
+                       bridge_connector->bridge_dp_audio = drm_bridge_get(bridge);
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
                        if (bridge_connector->bridge_hdmi_cec)
                                return ERR_PTR(-EBUSY);
 
-                       bridge_connector->bridge_hdmi_cec = bridge;
+                       bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
                        if (bridge_connector->bridge_hdmi_cec)
                                return ERR_PTR(-EBUSY);
 
-                       bridge_connector->bridge_hdmi_cec = bridge;
+                       bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
 
                        if (!bridge->funcs->hdmi_cec_enable ||
                            !bridge->funcs->hdmi_cec_log_addr ||