]> 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, 26 Sep 2025 14:59:40 +0000 (16:59 +0200)
committerLuca Ceresoli <luca.ceresoli@bootlin.com>
Fri, 3 Oct 2025 06:48:43 +0000 (08:48 +0200)
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.

This is tricky because the pointers are currently stored directly in the
drm_bridge_connector in the loop, but there is no nice and clean way to put
those pointers on error return paths. To overcome this, store all pointers
in temporary local variables with a cleanup action, and only on success
copy them into struct drm_bridge_connector (getting another ref while
copying).

Additionally four of these pointers (edid, hpd, detect and modes) can be
written in multiple loop iterations, in order to eventually store the last
matching bridge. However, 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.

Finally, there is also a function-local panel_bridge pointer taken inside
the loop and used after the loop. Use a cleanup action as well to ensure it
is put on return.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250926-drm-bridge-alloc-getput-bridge-connector-v2-1-138b4bb70576@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
drivers/gpu/drm/display/drm_bridge_connector.c

index a5bdd6c1064399ece6b19560f145b877c9e0680e..7b18be3ff9a32b362468351835bdab43c3f524f1 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
@@ -638,7 +652,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
        struct drm_bridge_connector *bridge_connector;
        struct drm_connector *connector;
        struct i2c_adapter *ddc = NULL;
-       struct drm_bridge *panel_bridge = NULL;
+       struct drm_bridge *panel_bridge      __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_edid       __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_hpd        __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_detect     __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_modes      __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_hdmi       __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_hdmi_audio __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_dp_audio   __free(drm_bridge_put) = NULL;
+       struct drm_bridge *bridge_hdmi_cec   __free(drm_bridge_put) = NULL;
        unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
        unsigned int max_bpc = 8;
        bool support_hdcp = false;
@@ -649,6 +671,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,22 +698,30 @@ 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_edid);
+                       bridge_edid = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_HPD) {
+                       drm_bridge_put(bridge_hpd);
+                       bridge_hpd = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
+                       drm_bridge_put(bridge_detect);
+                       bridge_detect = drm_bridge_get(bridge);
+               }
+               if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+                       drm_bridge_put(bridge_modes);
+                       bridge_modes = drm_bridge_get(bridge);
+               }
                if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
-                       if (bridge_connector->bridge_hdmi)
+                       if (bridge_hdmi)
                                return ERR_PTR(-EBUSY);
                        if (!bridge->funcs->hdmi_write_infoframe ||
                            !bridge->funcs->hdmi_clear_infoframe)
                                return ERR_PTR(-EINVAL);
 
-                       bridge_connector->bridge_hdmi = bridge;
+                       bridge_hdmi = drm_bridge_get(bridge);
 
                        if (bridge->supported_formats)
                                supported_formats = bridge->supported_formats;
@@ -696,10 +730,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
-                       if (bridge_connector->bridge_hdmi_audio)
+                       if (bridge_hdmi_audio)
                                return ERR_PTR(-EBUSY);
 
-                       if (bridge_connector->bridge_dp_audio)
+                       if (bridge_dp_audio)
                                return ERR_PTR(-EBUSY);
 
                        if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -710,14 +744,14 @@ 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_hdmi_audio = drm_bridge_get(bridge);
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
-                       if (bridge_connector->bridge_dp_audio)
+                       if (bridge_dp_audio)
                                return ERR_PTR(-EBUSY);
 
-                       if (bridge_connector->bridge_hdmi_audio)
+                       if (bridge_hdmi_audio)
                                return ERR_PTR(-EBUSY);
 
                        if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -728,7 +762,7 @@ 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_dp_audio = drm_bridge_get(bridge);
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
@@ -739,10 +773,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                }
 
                if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
-                       if (bridge_connector->bridge_hdmi_cec)
+                       if (bridge_hdmi_cec)
                                return ERR_PTR(-EBUSY);
 
-                       bridge_connector->bridge_hdmi_cec = bridge;
+                       bridge_hdmi_cec = drm_bridge_get(bridge);
 
                        if (!bridge->funcs->hdmi_cec_enable ||
                            !bridge->funcs->hdmi_cec_log_addr ||
@@ -762,7 +796,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                        ddc = bridge->ddc;
 
                if (drm_bridge_is_panel(bridge))
-                       panel_bridge = bridge;
+                       panel_bridge = drm_bridge_get(bridge);
 
                if (bridge->support_hdcp)
                        support_hdcp = true;
@@ -771,13 +805,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
        if (connector_type == DRM_MODE_CONNECTOR_Unknown)
                return ERR_PTR(-EINVAL);
 
-       if (bridge_connector->bridge_hdmi) {
+       if (bridge_hdmi) {
                if (!connector->ycbcr_420_allowed)
                        supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
 
                ret = drmm_connector_hdmi_init(drm, connector,
-                                              bridge_connector->bridge_hdmi->vendor,
-                                              bridge_connector->bridge_hdmi->product,
+                                              bridge_hdmi->vendor,
+                                              bridge_hdmi->product,
                                               &drm_bridge_connector_funcs,
                                               &drm_bridge_connector_hdmi_funcs,
                                               connector_type, ddc,
@@ -793,15 +827,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                        return ERR_PTR(ret);
        }
 
-       if (bridge_connector->bridge_hdmi_audio ||
-           bridge_connector->bridge_dp_audio) {
+       if (bridge_hdmi_audio || bridge_dp_audio) {
                struct device *dev;
                struct drm_bridge *bridge;
 
-               if (bridge_connector->bridge_hdmi_audio)
-                       bridge = bridge_connector->bridge_hdmi_audio;
+               if (bridge_hdmi_audio)
+                       bridge = bridge_hdmi_audio;
                else
-                       bridge = bridge_connector->bridge_dp_audio;
+                       bridge = bridge_dp_audio;
 
                dev = bridge->hdmi_audio_dev;
 
@@ -815,9 +848,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                        return ERR_PTR(ret);
        }
 
-       if (bridge_connector->bridge_hdmi_cec &&
-           bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
-               struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+       if (bridge_hdmi_cec &&
+           bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
+               struct drm_bridge *bridge = bridge_hdmi_cec;
 
                ret = drmm_connector_hdmi_cec_notifier_register(connector,
                                                                NULL,
@@ -826,9 +859,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                        return ERR_PTR(ret);
        }
 
-       if (bridge_connector->bridge_hdmi_cec &&
-           bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
-               struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+       if (bridge_hdmi_cec &&
+           bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
+               struct drm_bridge *bridge = bridge_hdmi_cec;
 
                ret = drmm_connector_hdmi_cec_register(connector,
                                                       &drm_bridge_connector_hdmi_cec_funcs,
@@ -841,9 +874,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 
        drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
 
-       if (bridge_connector->bridge_hpd)
+       if (bridge_hpd)
                connector->polled = DRM_CONNECTOR_POLL_HPD;
-       else if (bridge_connector->bridge_detect)
+       else if (bridge_detect)
                connector->polled = DRM_CONNECTOR_POLL_CONNECT
                                  | DRM_CONNECTOR_POLL_DISCONNECT;
 
@@ -854,6 +887,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
            IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
                drm_connector_attach_content_protection_property(connector, true);
 
+       bridge_connector->bridge_edid       = drm_bridge_get(bridge_edid);
+       bridge_connector->bridge_hpd        = drm_bridge_get(bridge_hpd);
+       bridge_connector->bridge_detect     = drm_bridge_get(bridge_detect);
+       bridge_connector->bridge_modes      = drm_bridge_get(bridge_modes);
+       bridge_connector->bridge_hdmi       = drm_bridge_get(bridge_hdmi);
+       bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge_hdmi_audio);
+       bridge_connector->bridge_dp_audio   = drm_bridge_get(bridge_dp_audio);
+       bridge_connector->bridge_hdmi_cec   = drm_bridge_get(bridge_hdmi_cec);
+
        return connector;
 }
 EXPORT_SYMBOL_GPL(drm_bridge_connector_init);