Commit 13adb8c9 authored by Luca Ceresoli's avatar Luca Ceresoli
Browse files

drm/display: bridge_connector: get/put the stored bridges



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: default avatarLouis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: default avatarDmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> # db410c
Tested-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Tested-by: default avatarNicolas Frattaroli <nicolas.frattaroli@collabora.com>
Tested-by: default avatarTommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Tested-by: default avatarMarek 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: default avatarLuca Ceresoli <luca.ceresoli@bootlin.com>
parent b4027536
Loading
Loading
Loading
Loading
+39 −13
Original line number Diff line number Diff line
@@ -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 ||