From patchwork Thu Mar 20 15:42:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 14024157 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 82934C35FFF for ; Thu, 20 Mar 2025 16:03:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=76ShvGbKTWCnojDi2WXjYtsq+IEWC3ZZCKont8aYmXs=; b=3As0Qm7BfKerfQNJGhnodalu5V cBOdbvZ+VdL0DOfWz5pv5d7ocf61PuqXBOZP+lHPF0i92kyKV6VLgX0joSetmpbuFdGUR0a+7//if +rojMGQOVDfo2kCk2UayeMw5KyyRbFd++vjb4hn3cKnABzPejqUqktgpmtWxnoSN10waWgoqC2MlH pd0WSX1mc5HmTSUbClljD/+LEEYrGeXWeK3xoIGuv3TdyIiqKVuKHe4SDYInfbDVC9FkNQ4NSPru2 PqlxviKC+nKiN8tGGKQGtLj2dFWFqCrIkUA3dvGaPyJ+gj0Bk4hnIGHgiaghruDFGs9kLhGe8rH5q INJTNXIw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tvIME-0000000Cbwh-3W1j; Thu, 20 Mar 2025 16:02:58 +0000 Received: from relay8-d.mail.gandi.net ([2001:4b98:dc4:8::228]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tvI2Z-0000000CZCw-2oZ7 for linux-arm-kernel@lists.infradead.org; Thu, 20 Mar 2025 15:42:41 +0000 Received: by mail.gandi.net (Postfix) with ESMTPSA id 41BE644412; Thu, 20 Mar 2025 15:42:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1742485357; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=76ShvGbKTWCnojDi2WXjYtsq+IEWC3ZZCKont8aYmXs=; b=kmLmW1isJD2fDW3va4maqtq0Zut9KNgM1yAQVqQWKGbx68j0Z6hXG+iOBcgyk9Mhm7GRtF bqZgb/r21RFRD81PLLx6pnfwYoZo9FBFOnc37/0L0+LmD6UxpphiXszqEhqWSC667of06F 98BACERuCZG0m/iK5L7XbU+yJAT0rVK5Bv3I3+KN1SXaAeZO6qywCIprBQTEUxq6VKcCTe C5o88BxwvcRcTVlULTRO4Ghi9TcrYf0rfHFN7+gkuUliS/RSC2h/IDX2KWlNMTlgwUCSaZ akX5ykoHh2IbMwJ6IB86TkpvDmHBRBG0EOPc1XfgAL7KQTngxnNBlm4wvawEgA== From: Luca Ceresoli Date: Thu, 20 Mar 2025 16:42:11 +0100 Subject: [PATCH v8 2/5] drm/bridge: add support for refcounting MIME-Version: 1.0 Message-Id: <20250320-drm-bridge-refcount-v8-2-b3ddaa9f1368@bootlin.com> References: <20250320-drm-bridge-refcount-v8-0-b3ddaa9f1368@bootlin.com> In-Reply-To: <20250320-drm-bridge-refcount-v8-0-b3ddaa9f1368@bootlin.com> To: Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Marek Vasut , Stefan Agner , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Inki Dae , Jagan Teki , Marek Szyprowski Cc: Thomas Petazzoni , Anusha Srivatsa , Paul Kocialkowski , Dmitry Baryshkov , =?utf-8?q?Herv=C3=A9_Codina?= , Hui Pu , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddugeekiedtucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuifetpfffkfdpucggtfgfnhhsuhgsshgtrhhisggvnecuuegrihhlohhuthemuceftddunecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhfffugggtgffkfhgjvfevofesthejredtredtjeenucfhrhhomhepnfhutggrucevvghrvghsohhlihcuoehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmqeenucggtffrrghtthgvrhhnpeeiieeuvdfftefgueduleehueetgffgjeeitedtteetkeeuueeuueekveevvdeuveenucfkphepvdgrtddvmeeijedtmedvtddvtdemvggrtddumegsvgegudemleehvgejmeefgeefmeeludefvgenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemsggvgedumeelhegvjeemfeegfeemledufegvpdhhvghloheplgduledvrdduieekrddujeekrdduudekngdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefuddprhgtphhtthhopehrfhhoshhssehkvghrnhgvlhdrohhrghdprhgtphhtthhopegrnhgurhiivghjrdhhrghjuggrsehinhhtvghlrdgtohhmpdhrtghpthhtohepthiiihhmmhgvrhhmrghnnhesshhushgvrdguvgdprhgtphhtthhopehimhigs ehlihhsthhsrdhlihhnuhigrdguvghvpdhrtghpthhtohepjhgvrhhnvghjrdhskhhrrggsvggtsehgmhgrihhlrdgtohhmpdhrtghpthhtoheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdprhgtphhtthhopefnrghurhgvnhhtrdhpihhntghhrghrthesihguvggrshhonhgsohgrrhgurdgtohhmpdhrtghpthhtohephhgvrhhvvgdrtghoughinhgrsegsohhothhlihhnrdgtohhm X-GND-Sasl: luca.ceresoli@bootlin.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250320_084239_864357_7303FEE5 X-CRM114-Status: GOOD ( 23.56 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org 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 Reviewed-by: Maxime Ripard --- Changes in v8: - move back drm_bridge_get/put() to the .c file, make __drm_bridge_free private - move back drm_bridge_put_void() to .c, not used in this series outside of drm_bridge.c - remove drm_bridge_put_and_clear(), not used in this series - minor docs and coding style tweaks Changes in v7: - export drm_bridge_put_void - struct drm_bridge: use container pointer instead of container_offset - remove drm_bridge_is_refcounted() - remove all DRM_DEBUG()s - drm_bridge_get/put: accept NULL pointer and return the bridge pointer to allow pass-through calls - extract to separate patches: - the addition of drm_bridge_alloc - the addition of drm_bridge_get/put() to drm_bridge_add/remove() - the addition of drm_bridge_get/put() to drm_bridge_attach/detach() - fix a typo, slightly improve kerneldoc Changes in v6: - use drm_warn, not WARN_ON (Jani Nikula) - Add devm_drm_bridge_alloc() to replace drm_bridge_init() (similar to drmm_encoder_alloc) - Remove .destroy func: deallocation is done via the struct offset computed by the devm_drm_bridge_alloc() macro - use fixed free callback, as the same callback is used in all cases anyway (remove free_cb, add bool is_refcounted) - add drm_bridge_get/put() to drm_bridge_attach/detach() (add the bridge to a list) - make some DRM_DEBUG() strings more informative This patch was added in v5. --- drivers/gpu/drm/drm_bridge.c | 72 +++++++++++++++++++++++++++++++++++++++++++- include/drm/drm_bridge.h | 19 ++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 84fa1a1330cbabd309526829fff70971cfed1dcd..2f2ecb73308e601b1a53ec8e7110933cef59b5da 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -199,23 +199,93 @@ 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) + return 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. + * + * Returns: + * Pointer to @bridge. + */ +struct drm_bridge *drm_bridge_put(struct drm_bridge *bridge) +{ + if (!bridge) + return bridge; + + kref_put(&bridge->refcount, __drm_bridge_free); + + return bridge; +} +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; } diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index a59277674d5a2937e324d3ce48f934418788053f..076f40c17dd86b224bbe6517b71f1aa6c24bfbd7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -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); +struct drm_bridge *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. */