mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
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:
committed by
Louis Chauvet
parent
0cc6aadd7f
commit
30d1b37d4c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user