drm/bridge: add support for refcounting

DRM bridges are currently considered as a fixed element of a DRM card, and
thus their lifetime is assumed to extend for as long as the card
exists. New use cases, such as hot-pluggable hardware with video bridges,
require DRM bridges to be added to and removed from a DRM card without
tearing the card down. This is possible for connectors already (used by DP
MST), it is now needed for DRM bridges as well.

As a first preliminary step, make bridges reference-counted to allow a
struct drm_bridge (along with the private driver structure embedding it) to
stay allocated even after the driver has been removed, until the last
reference is put.

Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20250326-drm-bridge-refcount-v9-2-5e0661fe1f84@bootlin.com
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
This commit is contained in:
Luca Ceresoli
2025-03-26 18:47:36 +01:00
committed by Louis Chauvet
parent 0cc6aadd7f
commit 30d1b37d4c
2 changed files with 81 additions and 1 deletions

View File

@@ -199,23 +199,84 @@
static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list);
static void __drm_bridge_free(struct kref *kref)
{
struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
kfree(bridge->container);
}
/**
* drm_bridge_get - Acquire a bridge reference
* @bridge: DRM bridge
*
* This function increments the bridge's refcount.
*
* Returns:
* Pointer to @bridge.
*/
struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge)
{
if (bridge)
kref_get(&bridge->refcount);
return bridge;
}
EXPORT_SYMBOL(drm_bridge_get);
/**
* drm_bridge_put - Release a bridge reference
* @bridge: DRM bridge
*
* This function decrements the bridge's reference count and frees the
* object if the reference count drops to zero.
*/
void drm_bridge_put(struct drm_bridge *bridge)
{
if (bridge)
kref_put(&bridge->refcount, __drm_bridge_free);
}
EXPORT_SYMBOL(drm_bridge_put);
/**
* drm_bridge_put_void - wrapper to drm_bridge_put() taking a void pointer
*
* @data: pointer to @struct drm_bridge, cast to a void pointer
*
* Wrapper of drm_bridge_put() to be used when a function taking a void
* pointer is needed, for example as a devm action.
*/
static void drm_bridge_put_void(void *data)
{
struct drm_bridge *bridge = (struct drm_bridge *)data;
drm_bridge_put(bridge);
}
void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
const struct drm_bridge_funcs *funcs)
{
void *container;
struct drm_bridge *bridge;
int err;
if (!funcs) {
dev_warn(dev, "Missing funcs pointer\n");
return ERR_PTR(-EINVAL);
}
container = devm_kzalloc(dev, size, GFP_KERNEL);
container = kzalloc(size, GFP_KERNEL);
if (!container)
return ERR_PTR(-ENOMEM);
bridge = container + offset;
bridge->container = container;
bridge->funcs = funcs;
kref_init(&bridge->refcount);
err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge);
if (err)
return ERR_PTR(err);
return container;
}

View File

@@ -840,6 +840,18 @@ struct drm_bridge {
const struct drm_bridge_timings *timings;
/** @funcs: control functions */
const struct drm_bridge_funcs *funcs;
/**
* @container: Pointer to the private driver struct embedding this
* @struct drm_bridge.
*/
void *container;
/**
* @refcount: reference count of users referencing this bridge.
*/
struct kref refcount;
/** @driver_private: pointer to the bridge driver's internal context */
void *driver_private;
/** @ops: bitmask of operations supported by the bridge */
@@ -941,6 +953,9 @@ drm_priv_to_bridge(struct drm_private_obj *priv)
return container_of(priv, struct drm_bridge, base);
}
struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge);
void drm_bridge_put(struct drm_bridge *bridge);
void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
const struct drm_bridge_funcs *funcs);
@@ -951,6 +966,10 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
* @member: the name of the &drm_bridge within @type
* @funcs: callbacks for this bridge
*
* The reference count of the returned bridge is initialized to 1. This
* reference will be automatically dropped via devm (by calling
* drm_bridge_put()) when @dev is removed.
*
* Returns:
* Pointer to new bridge, or ERR_PTR on failure.
*/