mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02: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:
parent
0cc6aadd7f
commit
30d1b37d4c
|
|
@ -199,23 +199,84 @@
|
||||||
static DEFINE_MUTEX(bridge_lock);
|
static DEFINE_MUTEX(bridge_lock);
|
||||||
static LIST_HEAD(bridge_list);
|
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,
|
void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
|
||||||
const struct drm_bridge_funcs *funcs)
|
const struct drm_bridge_funcs *funcs)
|
||||||
{
|
{
|
||||||
void *container;
|
void *container;
|
||||||
struct drm_bridge *bridge;
|
struct drm_bridge *bridge;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (!funcs) {
|
if (!funcs) {
|
||||||
dev_warn(dev, "Missing funcs pointer\n");
|
dev_warn(dev, "Missing funcs pointer\n");
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
container = devm_kzalloc(dev, size, GFP_KERNEL);
|
container = kzalloc(size, GFP_KERNEL);
|
||||||
if (!container)
|
if (!container)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
bridge = container + offset;
|
bridge = container + offset;
|
||||||
|
bridge->container = container;
|
||||||
bridge->funcs = funcs;
|
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;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -840,6 +840,18 @@ struct drm_bridge {
|
||||||
const struct drm_bridge_timings *timings;
|
const struct drm_bridge_timings *timings;
|
||||||
/** @funcs: control functions */
|
/** @funcs: control functions */
|
||||||
const struct drm_bridge_funcs *funcs;
|
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 */
|
/** @driver_private: pointer to the bridge driver's internal context */
|
||||||
void *driver_private;
|
void *driver_private;
|
||||||
/** @ops: bitmask of operations supported by the bridge */
|
/** @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);
|
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,
|
void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
|
||||||
const struct drm_bridge_funcs *funcs);
|
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
|
* @member: the name of the &drm_bridge within @type
|
||||||
* @funcs: callbacks for this bridge
|
* @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:
|
* Returns:
|
||||||
* Pointer to new bridge, or ERR_PTR on failure.
|
* Pointer to new bridge, or ERR_PTR on failure.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user