From patchwork Thu Feb 6 18:14:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963448 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 7F354C02194 for ; Thu, 6 Feb 2025 18:14:42 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 04BE210E8FF; Thu, 6 Feb 2025 18:14:42 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="pSYX7hZk"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9AF2D10E8FF for ; Thu, 6 Feb 2025 18:14:39 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id A7669442BE; Thu, 6 Feb 2025 18:14:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865678; 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=odUR82B5vLalXzIZ7+pzz/xGL9A+mo30n5q7u/o+zps=; b=pSYX7hZk/jJApOTnNbtJvQqEK0oJuJTPIfulo/4uXvYJ+ScZneEAFoiyvl4eapc5E0F/x0 dYpB+cpgHPXR0c3YMPXlvivQuSitx/R9OyBl4trq2ItNB+Y1fRWYDU92rh9eGqXpVVqAUe 9AvaI3iQNqypOcE6ypK32J2Yhn/UbOTe9MGKcixugQV9Sw4mzCMREQM1QV+xM4Rgz+7a5h dDgQfOM6j7pW1326P5giNgkhZ2zMb9umJODTjTrWGMHGLsqwm6juNdeU8vFpmX9HrmMxqi gafU8h/B2FLYrqTdLLa3BjropRnoETqcFwq187Iv+gc+IwwT3ol+XPh+tFok+w== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:16 +0100 Subject: [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-1-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" idx is an unsigned int, use %u for printk-style strings. Signed-off-by: Luca Ceresoli Reviewed-by: Dmitry Baryshkov --- This patch was added in v6. --- drivers/gpu/drm/drm_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 536409a35df406dae0dd7ade01b3f3d1e2c9e8f9..6b2178864c7ee12db9aa1f562e106b2f604439f8 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -748,7 +748,7 @@ static int bridges_show(struct seq_file *m, void *data) unsigned int idx = 0; drm_for_each_bridge_in_chain(encoder, bridge) { - drm_printf(&p, "bridge[%d]: %ps\n", idx++, bridge->funcs); + drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs); drm_printf(&p, "\ttype: [%d] %s\n", bridge->type, drm_get_connector_type_name(bridge->type)); From patchwork Thu Feb 6 18:14:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963449 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 0DDC4C02199 for ; Thu, 6 Feb 2025 18:14:44 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8331E10E901; Thu, 6 Feb 2025 18:14:43 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="gYVKgB5n"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 727BF10E900 for ; Thu, 6 Feb 2025 18:14:42 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 8512644264; Thu, 6 Feb 2025 18:14:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865681; 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=h90MSpDBhdj+tdxru3ofuCE+vpdZ8Riig8QaGtbMWYA=; b=gYVKgB5npL16q143mSSwYJkMbCaAc3DA9U9LfY/++zdxWFACW6ZNM4Av6CoGa/JUHYhlbh 1+WWo+mH356pJcNPRKrtTdzt8fy46Ij+4ivPK1cG0RoqrtFJL5qLbpsy1pEDWebOkh/olu kSqitX6XRfU9Ul2nheqQMJWiVOapDWWLMJyp6rJ9ZYleFjDtbnsb7iC3JM/pkXbA28g0ny ouQrwDxOdfySL60TkxA2LHiTULrEdcK3lBUe6YF///8DPpf4ap3Tm5czhl1bgeKIOjdo3I 6bEyv8Yu9NqS1YeuB1B+T0EgobNeiFJVJfMSVy1cGrNLc7vPeim8v69RDnbEKQ== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:17 +0100 Subject: [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-2-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" This comment is misleading as it refers to one of the inner if() branches only, not the whole outer if(). Move it to the branch it refers to. Signed-off-by: Luca Ceresoli Reviewed-by: Dmitry Baryshkov --- This patch was added in v6. --- drivers/gpu/drm/drm_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 5530919e0ba05f7ce1806730b292319f36e905ed..d0183dea770308e77f05da364ffe087d53f3be36 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -268,9 +268,9 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, *panel = NULL; } - /* No panel found yet, check for a bridge next. */ if (bridge) { if (ret) { + /* No panel found yet, check for a bridge next. */ *bridge = of_drm_find_bridge(remote); if (*bridge) ret = 0; From patchwork Thu Feb 6 18:14:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963450 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 B7F3FC0219B for ; Thu, 6 Feb 2025 18:14:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3ADB210E907; Thu, 6 Feb 2025 18:14:46 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="nBPCLegJ"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 4873510E900 for ; Thu, 6 Feb 2025 18:14:45 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 607F4442D6; Thu, 6 Feb 2025 18:14:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865684; 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=o9jzTvNrSGUeVzkBEfyBdb91nDpav0kBKqx6oQfnKpo=; b=nBPCLegJruzf6a2wi9/QUC/Mhs97RRCoDm+Jvo+kYm8mkViCn6fCDb1+x0xjKECU7hAc8L JEkOJP5JLTesKSzlsjHst7jHL5ubylUej3mRr2Xi+bAnMwqjkecvFt29ctX3CtYtd7GC13 jg2gBU+ufpNkO7+xtgo9jBeBB0YeDYjyzsmu17EfhGzrAVi1fy46pwp8zXc6xEERMCqj2a lB9wKSL253jKcIJQgMeqSmEXFPsY8ul2J0XpAuscq3I2fiCKHMYoKNcPwNqsLVtQjSVOyI b8tsZU6xXgOYcNDUlGWiaiDM0shH0L0tG8rRXvIghg8qqQb4exE3WSKlxcncBw== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:18 +0100 Subject: [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-3-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" drm_panel_bridge_remove() reads bridge->funcs to find out whether this is a panel bridge or another kind of bridge. drm_bridge_is_panel() is made exactly for that, so use it. Signed-off-by: Luca Ceresoli Reviewed-by: Dmitry Baryshkov --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 6e88339dec0f5faee690b7c53e8dcd0f1ee2281c..0c5db13b11dcb90ee88b9932b91aa05fc48d59bd 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -322,7 +322,7 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) if (!bridge) return; - if (bridge->funcs != &panel_bridge_bridge_funcs) + if (!drm_bridge_is_panel(bridge)) return; panel_bridge = drm_bridge_to_panel_bridge(bridge); From patchwork Thu Feb 6 18:14:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963451 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 550DCC02194 for ; Thu, 6 Feb 2025 18:14:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C3EB710E900; Thu, 6 Feb 2025 18:14:49 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="f65KJvU3"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 31B7110E904 for ; Thu, 6 Feb 2025 18:14:48 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 370CB43297; Thu, 6 Feb 2025 18:14:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865687; 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=bA3l5r5TezclsqpRm22nq8eHPme2k8afvoNTGTGzecw=; b=f65KJvU3ja4MsQN1B0ue3lbORqGGh8mh49JLGgJapyJwuY9HYkNeIYmemJFOkneBBfgYHb dD+193/LZ6bLdNdthWwJBqulxfQwZT3pa3Hc6ga6teLuqPdI7+j183RYEpcP8HfDXTRQrU W6U4zEs1cw0WDH15qVU6P371aJFIN09GVRe+yZc7BEosX1Ef6hAqesm9lZTu2MEIqRPzOL r/Sb1x8k3ITpoulWEjGXACPz1PqnI/OaSro1KMRlofQl+dXo+3h1k/pJte/KV6PhGjyzrI 3Pvq0WBq+5D0mGRwP3SXbGTB5XplGFVIcca6BhvjIgN9FWO3vgwMIEjzv8tZrg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:19 +0100 Subject: [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-4-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" This function is for panel_bridge instances only. The silent return when invoked on other bridges might hide actual errors, so avoid them to go unnoticed. Signed-off-by: Luca Ceresoli Reviewed-by: Maxime Ripard --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 0c5db13b11dcb90ee88b9932b91aa05fc48d59bd..c57036b06493a6922e2cae38bcd1733930ff0073 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -322,8 +322,10 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) if (!bridge) return; - if (!drm_bridge_is_panel(bridge)) + if (!drm_bridge_is_panel(bridge)) { + drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__); return; + } panel_bridge = drm_bridge_to_panel_bridge(bridge); From patchwork Thu Feb 6 18:14:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963452 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 24D92C02199 for ; Thu, 6 Feb 2025 18:14:53 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 99FC010E8FE; Thu, 6 Feb 2025 18:14:52 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="KexksfEm"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1B4F010E906 for ; Thu, 6 Feb 2025 18:14:50 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 1C8CF44264; Thu, 6 Feb 2025 18:14:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865689; 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=yP620PYatjyFXh/aDySr+BDD8kR1Bb16xt407+qxPo4=; b=KexksfEmeiTlO2+62+R7roIyq7DiZ7ru87zBYA62jX6Zn9XbWYDS6ieujw1g1oyjev8huh ebXQY+XaBl1UQGziOaE/A/R0zIDExVj1DW3A4Km5EKBDlY54FEhWApjrytd4zRDHwTYJIR YD121I2CHo0LNrV3xaQ0Y5TfZiMZagwsW4b12gcRlBVr/YTUm/hkWYVSjl/uDzej+pPYVp XeJw2zVLCkmNSnsOn+gRryNmBBBwte5aQY7F6sER4PdduowkUJs2S7LNgqDKVlCjhO6fDU 9Dwu68kgLVemO2oY7YpnXJj/q4c0bCQ4XF45/y+vDfaGSYwwntUf22nhTwMRKQ== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:20 +0100 Subject: [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-5-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeegnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" The global bridges_list holding all the bridges between drm_bridge_add() and drm_bridge_remove() cannot be inspected via debugfs. Add a file showing it. To avoid code duplication, move the code printing a bridge info to a common function. Note: this change requires exporting bridge_list and the mutex protecting it. Also add a comment about bridge_lock to make checkpatch happy. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_bridge.c | 5 +-- drivers/gpu/drm/drm_debugfs.c | 70 +++++++++++++++++++++++++++++------------- drivers/gpu/drm/drm_drv.c | 1 + drivers/gpu/drm/drm_internal.h | 9 ++++++ 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 241a384ebce39b4a3db58c208af27960904fc662..87cebec2de806781cee22da54d666eee9bde3648 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -195,8 +195,9 @@ * driver. */ -static DEFINE_MUTEX(bridge_lock); -static LIST_HEAD(bridge_list); +/* Protect bridge_list */ +DEFINE_MUTEX(bridge_lock); +LIST_HEAD(bridge_list); /** * drm_bridge_add - add the given bridge to the global bridge list diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 6b2178864c7ee12db9aa1f562e106b2f604439f8..7424d5237e7615d63de6bba572ee6050da6709d0 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -740,6 +740,30 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc) crtc->debugfs_entry = NULL; } +static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx) +{ + drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); + drm_printf(p, "\ttype: [%d] %s\n", + bridge->type, + drm_get_connector_type_name(bridge->type)); + + if (bridge->of_node) + drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node); + + drm_printf(p, "\tops: [0x%x]", bridge->ops); + if (bridge->ops & DRM_BRIDGE_OP_DETECT) + drm_puts(p, " detect"); + if (bridge->ops & DRM_BRIDGE_OP_EDID) + drm_puts(p, " edid"); + if (bridge->ops & DRM_BRIDGE_OP_HPD) + drm_puts(p, " hpd"); + if (bridge->ops & DRM_BRIDGE_OP_MODES) + drm_puts(p, " modes"); + if (bridge->ops & DRM_BRIDGE_OP_HDMI) + drm_puts(p, " hdmi"); + drm_puts(p, "\n"); +} + static int bridges_show(struct seq_file *m, void *data) { struct drm_encoder *encoder = m->private; @@ -747,28 +771,8 @@ static int bridges_show(struct seq_file *m, void *data) struct drm_bridge *bridge; unsigned int idx = 0; - drm_for_each_bridge_in_chain(encoder, bridge) { - drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs); - drm_printf(&p, "\ttype: [%d] %s\n", - bridge->type, - drm_get_connector_type_name(bridge->type)); - - if (bridge->of_node) - drm_printf(&p, "\tOF: %pOFfc\n", bridge->of_node); - - drm_printf(&p, "\tops: [0x%x]", bridge->ops); - if (bridge->ops & DRM_BRIDGE_OP_DETECT) - drm_puts(&p, " detect"); - if (bridge->ops & DRM_BRIDGE_OP_EDID) - drm_puts(&p, " edid"); - if (bridge->ops & DRM_BRIDGE_OP_HPD) - drm_puts(&p, " hpd"); - if (bridge->ops & DRM_BRIDGE_OP_MODES) - drm_puts(&p, " modes"); - if (bridge->ops & DRM_BRIDGE_OP_HDMI) - drm_puts(&p, " hdmi"); - drm_puts(&p, "\n"); - } + drm_for_each_bridge_in_chain(encoder, bridge) + bridge_print(&p, bridge, idx++); return 0; } @@ -802,3 +806,25 @@ void drm_debugfs_encoder_remove(struct drm_encoder *encoder) debugfs_remove_recursive(encoder->debugfs_entry); encoder->debugfs_entry = NULL; } + +static int allbridges_show(struct seq_file *m, void *data) +{ + struct drm_printer p = drm_seq_file_printer(m); + struct drm_bridge *bridge; + unsigned int idx = 0; + + mutex_lock(&bridge_lock); + + list_for_each_entry(bridge, &bridge_list, list) + bridge_print(&p, bridge, idx++); + + mutex_unlock(&bridge_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(allbridges); + +void drm_debugfs_global_add(struct dentry *root) +{ + debugfs_create_file("bridges", 0444, root, NULL, &allbridges_fops); +} diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf440eee8a2ab3de134d925db8f1d2ce68062b7..9b6d7bd16ba409b6a9155a9fecbec6bfdd5ea0c2 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -1120,6 +1120,7 @@ static int __init drm_core_init(void) } drm_debugfs_root = debugfs_create_dir("dri", NULL); + drm_debugfs_global_add(drm_debugfs_root); ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops); if (ret < 0) diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index b2b6a8e49dda46f1cb3b048ef7b28356dd3aaa4e..b6e875d4b25faae6bb0bb952c3c12bd4819698ec 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -48,6 +48,10 @@ struct drm_prime_file_private; struct drm_printer; struct drm_vblank_crtc; +// for drm_debugfs.c +extern struct mutex bridge_lock; +extern struct list_head bridge_list; + /* drm_client_event.c */ #if defined(CONFIG_DRM_CLIENT) void drm_client_debugfs_init(struct drm_device *dev); @@ -196,6 +200,7 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc); void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc); void drm_debugfs_encoder_add(struct drm_encoder *encoder); void drm_debugfs_encoder_remove(struct drm_encoder *encoder); +void drm_debugfs_global_add(struct dentry *drm_debugfs_root); #else static inline void drm_debugfs_dev_fini(struct drm_device *dev) { @@ -241,6 +246,10 @@ static inline void drm_debugfs_encoder_remove(struct drm_encoder *encoder) { } +static inline void drm_debugfs_global_add(struct dentry *drm_debugfs_root) +{ +} + #endif drm_ioctl_t drm_version; From patchwork Thu Feb 6 18:14:21 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963453 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 2C258C02194 for ; Thu, 6 Feb 2025 18:14:56 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A019810E904; Thu, 6 Feb 2025 18:14:55 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="SiKEVP/u"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 229BA10E904 for ; Thu, 6 Feb 2025 18:14:54 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 09201442BE; Thu, 6 Feb 2025 18:14:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865692; 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=ClZenlZkreIR18gU2iJqAyPZKi/vUkaFG3bGFi6CUoA=; b=SiKEVP/u1tuzm0LYaI/VilTC4uLEPoAPX+ZddcGMjo2Fti3XBCLUaTln2UY6xtqzPETR85 EBntDFeyquBpYs/tIIMgH2h27s4bfSpO88m4jJUW7fwgYtCLzusd3DsbWIzFQyukDK+7/0 KpqcnwNijjNWlwe/9zEPFizVuEWaLLhrCr4o+r/K3ThVS0Mv9ShC0ZkvbDmLlJFsb7poyb qFajZodZgC7jtQQTHNCFwNeMFpgPT4w42Mw7DhezmoAxAAJ6a+8m/BVUsoLCW3+dGa4mDo QzpBMqRgl/ZOUseK/DaJu8p6SioFJ+FdwusL7b14ttbC8YNk48DgBj6DgmN5mA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:21 +0100 Subject: [PATCH v6 06/26] drm/panel: move all code into bridge/panel.c MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-6-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepleeuudeifeelkefgfeefhedtgfetfefhgfevueeufffffeeulefhieeiheegvdelnecuffhomhgrihhnpehkvghrnhgvlhdrohhrghdpfhhrvggvuggvshhkthhophdrohhrghenucfkphepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrt ghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghomhdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" In preparation to let panels always create a panel bridge, we need the drm_panel.c code to call bridge/panel.c code. However this would create a cyclic dependency between .c files and between modules (drm -> drm_kms_helper -> drm), indeed now the two components would depend on each other. In the short term, resolve this by moving all code to a single file so both will end up in the drm_kms_helper module. As a beneficial side effect, this will slightly reduce code size for configurations without bridges and panels. This step also requires moving drm_of_find_panel_or_bridge() from drm_of.c because it is referenced by devm_drm_of_get_bridge() and drmm_of_get_bridge(). In the long term, a good plan is probably that the panel becomes just "a bridge", embedding a struct drm_bridge instead of allocating it separately. In addition to moving around code without changing it, other changes are: * update Makefile * update #includes as needed * update bridge/Kconfig to select DRM_PANEL_BRIDGE instead of DRM_PANEL * update MAINTAINERS * fix a trivial checkpatch issue Link: https://lore.kernel.org/all/emuj2innmp6zmzd7pyakqzjqpdzhly6qfhakya3ydwmd63pl26@5jwxaidpikjw/ Suggested-by: Dmitry Baryshkov Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- Documentation/gpu/drm-kms-helpers.rst | 5 +- MAINTAINERS | 1 - drivers/gpu/drm/Makefile | 1 - drivers/gpu/drm/atmel-hlcdc/Kconfig | 2 +- drivers/gpu/drm/bridge/Kconfig | 25 +- drivers/gpu/drm/bridge/panel.c | 610 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_kms_helper_common.c | 1 + drivers/gpu/drm/drm_of.c | 68 ---- drivers/gpu/drm/drm_panel.c | 575 ------------------------------ 9 files changed, 626 insertions(+), 662 deletions(-) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index b4ee25af1702b0019e0de5f9ee66d2dbdac2c664..79c8d3e63f7e06136440ed38972697b5f057d5d1 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -209,15 +209,12 @@ Panel-Bridge Helper Reference Panel Helper Reference ====================== -.. kernel-doc:: drivers/gpu/drm/drm_panel.c +.. kernel-doc:: drivers/gpu/drm/bridge/panel.c :doc: drm panel .. kernel-doc:: include/drm/drm_panel.h :internal: -.. kernel-doc:: drivers/gpu/drm/drm_panel.c - :export: - .. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c :export: diff --git a/MAINTAINERS b/MAINTAINERS index f789e9e54110914dc266fb7d1937a92277c507bb..090153a4f40aa3ec4982c85c809a97dca0396ab8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7996,7 +7996,6 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/panel/ -F: drivers/gpu/drm/drm_panel.c F: drivers/gpu/drm/panel/ F: include/drm/drm_panel.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 19fb370fbc56772077973c864df71e4b8e0bf99b..98a42805b529ccf307e3a78857be47b544a601d8 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -79,7 +79,6 @@ drm-$(CONFIG_DRM_CLIENT) += \ drm_client_modeset.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_COMPAT) += drm_ioc32.o -drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o drm-$(CONFIG_PCI) += drm_pci.o drm-$(CONFIG_DEBUG_FS) += \ diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig index f8b9c91907d8ec697d072269090da8deaa54a6d9..7360991307ceaf7a4a594b1586114b2b7d6c1253 100644 --- a/drivers/gpu/drm/atmel-hlcdc/Kconfig +++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig @@ -5,7 +5,7 @@ config DRM_ATMEL_HLCDC select DRM_CLIENT_SELECTION select DRM_GEM_DMA_HELPER select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help Choose this option if you have an ATMEL SoC with an HLCDC display controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family). diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 6b4664d91faa80f096ac6a0548ed342e802ae68b..393214e6ed6656674d2ffbfc36d45541253bc31d 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -10,7 +10,7 @@ config DRM_PANEL_BRIDGE depends on DRM_BRIDGE select DRM_PANEL help - DRM bridge wrapper of DRM panels + DRM panels (including DRM bridge wrapper of such panels) config DRM_AUX_BRIDGE tristate @@ -195,7 +195,7 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw" depends on OF select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help This is a driver for the display bridges of GE B850v3 that convert dual channel LVDS @@ -230,14 +230,14 @@ config DRM_NXP_PTN3460 tristate "NXP PTN3460 DP/LVDS bridge" depends on OF select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help NXP PTN3460 eDP-LVDS bridge chip driver. config DRM_PARADE_PS8622 tristate "Parade eDP/LVDS bridge" depends on OF - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select BACKLIGHT_CLASS_DEVICE help @@ -251,7 +251,7 @@ config DRM_PARADE_PS8640 select DRM_DISPLAY_DP_AUX_BUS select DRM_KMS_HELPER select DRM_MIPI_DSI - select DRM_PANEL + select DRM_PANEL_BRIDGE help Choose this option if you have PS8640 for display The PS8640 is a high-performance and low-power @@ -326,7 +326,7 @@ config DRM_TOSHIBA_TC358764 depends on OF select DRM_MIPI_DSI select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help Toshiba TC358764 DSI/LVDS bridge driver. @@ -338,7 +338,7 @@ config DRM_TOSHIBA_TC358767 select DRM_KMS_HELPER select REGMAP_I2C select DRM_MIPI_DSI - select DRM_PANEL + select DRM_PANEL_BRIDGE help Toshiba TC358767 eDP bridge chip driver. @@ -347,7 +347,7 @@ config DRM_TOSHIBA_TC358768 depends on OF select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI select VIDEOMODE_HELPERS help @@ -360,15 +360,16 @@ config DRM_TOSHIBA_TC358775 select DRM_DISPLAY_HELPER select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI help Toshiba TC358775 DSI/LVDS bridge chip driver. config DRM_TI_DLPC3433 tristate "TI DLPC3433 Display controller" - depends on DRM && DRM_PANEL + depends on DRM depends on OF + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI help TI DLPC3433 is a MIPI DSI based display controller bridge @@ -400,7 +401,7 @@ config DRM_TI_SN65DSI83 depends on OF select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI help Texas Instruments SN65DSI83 and SN65DSI84 DSI to LVDS Bridge driver @@ -413,7 +414,7 @@ config DRM_TI_SN65DSI86 select DRM_BRIDGE_CONNECTOR select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI select AUXILIARY_BUS select DRM_DISPLAY_DP_AUX_BUS diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index c57036b06493a6922e2cae38bcd1733930ff0073..bd61e57e1a2dd3d1eb034da4f9213a6bcb3e6dc5 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -2,9 +2,12 @@ /* * Copyright (C) 2016 Laurent Pinchart * Copyright (C) 2017 Broadcom + * Copyright (C) 2013 NVIDIA Corporation */ +#include #include +#include #include #include @@ -17,6 +20,613 @@ #include #include +static DEFINE_MUTEX(panel_lock); +static LIST_HEAD(panel_list); + +/** + * DOC: drm panel + * + * The DRM panel helpers allow drivers to register panel objects with a + * central registry and provide functions to retrieve those panels in display + * drivers. + * + * For easy integration into drivers using the &drm_bridge infrastructure please + * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add(). + */ + +/** + * drm_panel_init - initialize a panel + * @panel: DRM panel + * @dev: parent device of the panel + * @funcs: panel operations + * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to + * the panel interface + * + * Initialize the panel structure for subsequent registration with + * drm_panel_add(). + */ +void drm_panel_init(struct drm_panel *panel, struct device *dev, + const struct drm_panel_funcs *funcs, int connector_type) +{ + INIT_LIST_HEAD(&panel->list); + INIT_LIST_HEAD(&panel->followers); + mutex_init(&panel->follower_lock); + panel->dev = dev; + panel->funcs = funcs; + panel->connector_type = connector_type; +} +EXPORT_SYMBOL(drm_panel_init); + +/** + * drm_panel_add - add a panel to the global registry + * @panel: panel to add + * + * Add a panel to the global registry so that it can be looked up by display + * drivers. + */ +void drm_panel_add(struct drm_panel *panel) +{ + mutex_lock(&panel_lock); + list_add_tail(&panel->list, &panel_list); + mutex_unlock(&panel_lock); +} +EXPORT_SYMBOL(drm_panel_add); + +/** + * drm_panel_remove - remove a panel from the global registry + * @panel: DRM panel + * + * Removes a panel from the global registry. + */ +void drm_panel_remove(struct drm_panel *panel) +{ + mutex_lock(&panel_lock); + list_del_init(&panel->list); + mutex_unlock(&panel_lock); +} +EXPORT_SYMBOL(drm_panel_remove); + +/** + * drm_panel_prepare - power on a panel + * @panel: DRM panel + * + * Calling this function will enable power and deassert any reset signals to + * the panel. After this has completed it is possible to communicate with any + * integrated circuitry via a command bus. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_prepare(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return -EINVAL; + + if (panel->prepared) { + dev_warn(panel->dev, "Skipping prepare of already prepared panel\n"); + return 0; + } + + mutex_lock(&panel->follower_lock); + + if (panel->funcs && panel->funcs->prepare) { + ret = panel->funcs->prepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = true; + + list_for_each_entry(follower, &panel->followers, list) { + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + + ret = 0; +exit: + mutex_unlock(&panel->follower_lock); + + return ret; +} +EXPORT_SYMBOL(drm_panel_prepare); + +/** + * drm_panel_unprepare - power off a panel + * @panel: DRM panel + * + * Calling this function will completely power off a panel (assert the panel's + * reset, turn off power supplies, ...). After this function has completed, it + * is usually no longer possible to communicate with the panel until another + * call to drm_panel_prepare(). + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_unprepare(struct drm_panel *panel) +{ + struct drm_panel_follower *follower; + int ret; + + if (!panel) + return -EINVAL; + + /* + * If you are seeing the warning below it likely means one of two things: + * - Your panel driver incorrectly calls drm_panel_unprepare() in its + * shutdown routine. You should delete this. + * - You are using panel-edp or panel-simple and your DRM modeset + * driver's shutdown() callback happened after the panel's shutdown(). + * In this case the warning is harmless though ideally you should + * figure out how to reverse the order of the shutdown() callbacks. + */ + if (!panel->prepared) { + dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n"); + return 0; + } + + mutex_lock(&panel->follower_lock); + + list_for_each_entry(follower, &panel->followers, list) { + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + + if (panel->funcs && panel->funcs->unprepare) { + ret = panel->funcs->unprepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = false; + + ret = 0; +exit: + mutex_unlock(&panel->follower_lock); + + return ret; +} +EXPORT_SYMBOL(drm_panel_unprepare); + +/** + * drm_panel_enable - enable a panel + * @panel: DRM panel + * + * Calling this function will cause the panel display drivers to be turned on + * and the backlight to be enabled. Content will be visible on screen after + * this call completes. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_enable(struct drm_panel *panel) +{ + int ret; + + if (!panel) + return -EINVAL; + + if (panel->enabled) { + dev_warn(panel->dev, "Skipping enable of already enabled panel\n"); + return 0; + } + + if (panel->funcs && panel->funcs->enable) { + ret = panel->funcs->enable(panel); + if (ret < 0) + return ret; + } + panel->enabled = true; + + ret = backlight_enable(panel->backlight); + if (ret < 0) + DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n", + ret); + + return 0; +} +EXPORT_SYMBOL(drm_panel_enable); + +/** + * drm_panel_disable - disable a panel + * @panel: DRM panel + * + * This will typically turn off the panel's backlight or disable the display + * drivers. For smart panels it should still be possible to communicate with + * the integrated circuitry via any command bus after this call. + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_disable(struct drm_panel *panel) +{ + int ret; + + if (!panel) + return -EINVAL; + + /* + * If you are seeing the warning below it likely means one of two things: + * - Your panel driver incorrectly calls drm_panel_disable() in its + * shutdown routine. You should delete this. + * - You are using panel-edp or panel-simple and your DRM modeset + * driver's shutdown() callback happened after the panel's shutdown(). + * In this case the warning is harmless though ideally you should + * figure out how to reverse the order of the shutdown() callbacks. + */ + if (!panel->enabled) { + dev_warn(panel->dev, "Skipping disable of already disabled panel\n"); + return 0; + } + + ret = backlight_disable(panel->backlight); + if (ret < 0) + DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n", + ret); + + if (panel->funcs && panel->funcs->disable) { + ret = panel->funcs->disable(panel); + if (ret < 0) + return ret; + } + panel->enabled = false; + + return 0; +} +EXPORT_SYMBOL(drm_panel_disable); + +/** + * drm_panel_get_modes - probe the available display modes of a panel + * @panel: DRM panel + * @connector: DRM connector + * + * The modes probed from the panel are automatically added to the connector + * that the panel is attached to. + * + * Return: The number of modes available from the panel on success, or 0 on + * failure (no modes). + */ +int drm_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + if (!panel) + return 0; + + if (panel->funcs && panel->funcs->get_modes) { + int num; + + num = panel->funcs->get_modes(panel, connector); + if (num > 0) + return num; + } + + return 0; +} +EXPORT_SYMBOL(drm_panel_get_modes); + +#ifdef CONFIG_OF +/** + * of_drm_find_panel - look up a panel using a device tree node + * @np: device tree node of the panel + * + * Searches the set of registered panels for one that matches the given device + * tree node. If a matching panel is found, return a pointer to it. + * + * Return: A pointer to the panel registered for the specified device tree + * node or an ERR_PTR() if no panel matching the device tree node can be found. + * + * Possible error codes returned by this function: + * + * - EPROBE_DEFER: the panel device has not been probed yet, and the caller + * should retry later + * - ENODEV: the device is not available (status != "okay" or "ok") + */ +struct drm_panel *of_drm_find_panel(const struct device_node *np) +{ + struct drm_panel *panel; + + if (!of_device_is_available(np)) + return ERR_PTR(-ENODEV); + + mutex_lock(&panel_lock); + + list_for_each_entry(panel, &panel_list, list) { + if (panel->dev->of_node == np) { + mutex_unlock(&panel_lock); + return panel; + } + } + + mutex_unlock(&panel_lock); + return ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(of_drm_find_panel); + +/** + * drm_of_find_panel_or_bridge - return connected panel or bridge device + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * @panel: pointer to hold returned drm_panel + * @bridge: pointer to hold returned drm_bridge + * + * Given a DT node's port and endpoint number, find the connected node and + * return either the associated struct drm_panel or drm_bridge device. Either + * @panel or @bridge must not be NULL. + * + * This function is deprecated and should not be used in new drivers. Use + * devm_drm_of_get_bridge() instead. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + int ret = -EPROBE_DEFER; + struct device_node *remote; + + if (!panel && !bridge) + return -EINVAL; + if (panel) + *panel = NULL; + + /* + * of_graph_get_remote_node() produces a noisy error message if port + * node isn't found and the absence of the port is a legit case here, + * so at first we silently check whether graph presents in the + * device-tree node. + */ + if (!of_graph_is_present(np)) + return -ENODEV; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + if (panel) { + *panel = of_drm_find_panel(remote); + if (!IS_ERR(*panel)) + ret = 0; + else + *panel = NULL; + } + + if (bridge) { + if (ret) { + /* No panel found yet, check for a bridge next. */ + *bridge = of_drm_find_bridge(remote); + if (*bridge) + ret = 0; + } else { + *bridge = NULL; + } + } + + of_node_put(remote); + return ret; +} +EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); + +/** + * of_drm_get_panel_orientation - look up the orientation of the panel through + * the "rotation" binding from a device tree node + * @np: device tree node of the panel + * @orientation: orientation enum to be filled in + * + * Looks up the rotation of a panel in the device tree. The orientation of the + * panel is expressed as a property name "rotation" in the device tree. The + * rotation in the device tree is counter clockwise. + * + * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the + * rotation property doesn't exist. Return a negative error code on failure. + */ +int of_drm_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation) +{ + int rotation, ret; + + ret = of_property_read_u32(np, "rotation", &rotation); + if (ret == -EINVAL) { + /* Don't return an error if there's no rotation property. */ + *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + return 0; + } + + if (ret < 0) + return ret; + + if (rotation == 0) + *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; + else if (rotation == 90) + *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; + else if (rotation == 180) + *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; + else if (rotation == 270) + *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; + else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(of_drm_get_panel_orientation); +#endif + +/** + * drm_is_panel_follower() - Check if the device is a panel follower + * @dev: The 'struct device' to check + * + * This checks to see if a device needs to be power sequenced together with + * a panel using the panel follower API. + * At the moment panels can only be followed on device tree enabled systems. + * The "panel" property of the follower points to the panel to be followed. + * + * Return: true if we should be power sequenced with a panel; false otherwise. + */ +bool drm_is_panel_follower(struct device *dev) +{ + /* + * The "panel" property is actually a phandle, but for simplicity we + * don't bother trying to parse it here. We just need to know if the + * property is there. + */ + return of_property_present(dev->of_node, "panel"); +} +EXPORT_SYMBOL(drm_is_panel_follower); + +/** + * drm_panel_add_follower() - Register something to follow panel state. + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * A panel follower is called right after preparing the panel and right before + * unpreparing the panel. It's primary intention is to power on an associated + * touchscreen, though it could be used for any similar devices. Multiple + * devices are allowed the follow the same panel. + * + * If a follower is added to a panel that's already been turned on, the + * follower's prepare callback is called right away. + * + * At the moment panels can only be followed on device tree enabled systems. + * The "panel" property of the follower points to the panel to be followed. + * + * Return: 0 or an error code. Note that -ENODEV means that we detected that + * follower_dev is not actually following a panel. The caller may + * choose to ignore this return value if following a panel is optional. + */ +int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + struct device_node *panel_np; + struct drm_panel *panel; + int ret; + + panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); + if (!panel_np) + return -ENODEV; + + panel = of_drm_find_panel(panel_np); + of_node_put(panel_np); + if (IS_ERR(panel)) + return PTR_ERR(panel); + + get_device(panel->dev); + follower->panel = panel; + + mutex_lock(&panel->follower_lock); + + list_add_tail(&follower->list, &panel->followers); + if (panel->prepared) { + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + + mutex_unlock(&panel->follower_lock); + + return 0; +} +EXPORT_SYMBOL(drm_panel_add_follower); + +/** + * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). + * @follower: The panel follower descriptor for the follower. + * + * Undo drm_panel_add_follower(). This includes calling the follower's + * unprepare function if we're removed from a panel that's currently prepared. + * + * Return: 0 or an error code. + */ +void drm_panel_remove_follower(struct drm_panel_follower *follower) +{ + struct drm_panel *panel = follower->panel; + int ret; + + mutex_lock(&panel->follower_lock); + + if (panel->prepared) { + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + list_del_init(&follower->list); + + mutex_unlock(&panel->follower_lock); + + put_device(panel->dev); +} +EXPORT_SYMBOL(drm_panel_remove_follower); + +static void drm_panel_remove_follower_void(void *follower) +{ + drm_panel_remove_follower(follower); +} + +/** + * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * Handles calling drm_panel_remove_follower() using devm on the follower_dev. + * + * Return: 0 or an error code. + */ +int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + int ret; + + ret = drm_panel_add_follower(follower_dev, follower); + if (ret) + return ret; + + return devm_add_action_or_reset(follower_dev, + drm_panel_remove_follower_void, follower); +} +EXPORT_SYMBOL(devm_drm_panel_add_follower); + +#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) +/** + * drm_panel_of_backlight - use backlight device node for backlight + * @panel: DRM panel + * + * Use this function to enable backlight handling if your panel + * uses device tree and has a backlight phandle. + * + * When the panel is enabled backlight will be enabled after a + * successful call to &drm_panel_funcs.enable() + * + * When the panel is disabled backlight will be disabled before the + * call to &drm_panel_funcs.disable(). + * + * A typical implementation for a panel driver supporting device tree + * will call this function at probe time. Backlight will then be handled + * transparently without requiring any intervention from the driver. + * drm_panel_of_backlight() must be called after the call to drm_panel_init(). + * + * Return: 0 on success or a negative error code on failure. + */ +int drm_panel_of_backlight(struct drm_panel *panel) +{ + struct backlight_device *backlight; + + if (!panel || !panel->dev) + return -EINVAL; + + backlight = devm_of_find_backlight(panel->dev); + + if (IS_ERR(backlight)) + return PTR_ERR(backlight); + + panel->backlight = backlight; + return 0; +} +EXPORT_SYMBOL(drm_panel_of_backlight); +#endif + struct panel_bridge { struct drm_bridge bridge; struct drm_connector connector; diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 0c7550c0462b5f2ae7084ae70633f501e02dac78..47660db72958a2937ac3344180b59c52110bebe7 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -28,5 +28,6 @@ #include MODULE_AUTHOR("David Airlie, Jesse Barnes"); +MODULE_AUTHOR("Thierry Reding "); MODULE_DESCRIPTION("DRM KMS helper"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index d0183dea770308e77f05da364ffe087d53f3be36..9ba36ad0bac0e74af139d703c305f5814eb64ca8 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -217,74 +217,6 @@ int drm_of_encoder_active_endpoint(struct device_node *node, } EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); -/** - * drm_of_find_panel_or_bridge - return connected panel or bridge device - * @np: device tree node containing encoder output ports - * @port: port in the device tree node - * @endpoint: endpoint in the device tree node - * @panel: pointer to hold returned drm_panel - * @bridge: pointer to hold returned drm_bridge - * - * Given a DT node's port and endpoint number, find the connected node and - * return either the associated struct drm_panel or drm_bridge device. Either - * @panel or @bridge must not be NULL. - * - * This function is deprecated and should not be used in new drivers. Use - * devm_drm_of_get_bridge() instead. - * - * Returns zero if successful, or one of the standard error codes if it fails. - */ -int drm_of_find_panel_or_bridge(const struct device_node *np, - int port, int endpoint, - struct drm_panel **panel, - struct drm_bridge **bridge) -{ - int ret = -EPROBE_DEFER; - struct device_node *remote; - - if (!panel && !bridge) - return -EINVAL; - if (panel) - *panel = NULL; - - /* - * of_graph_get_remote_node() produces a noisy error message if port - * node isn't found and the absence of the port is a legit case here, - * so at first we silently check whether graph presents in the - * device-tree node. - */ - if (!of_graph_is_present(np)) - return -ENODEV; - - remote = of_graph_get_remote_node(np, port, endpoint); - if (!remote) - return -ENODEV; - - if (panel) { - *panel = of_drm_find_panel(remote); - if (!IS_ERR(*panel)) - ret = 0; - else - *panel = NULL; - } - - if (bridge) { - if (ret) { - /* No panel found yet, check for a bridge next. */ - *bridge = of_drm_find_bridge(remote); - if (*bridge) - ret = 0; - } else { - *bridge = NULL; - } - - } - - of_node_put(remote); - return ret; -} -EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); - enum drm_of_lvds_pixels { DRM_OF_LVDS_EVEN = BIT(0), DRM_OF_LVDS_ODD = BIT(1), diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c deleted file mode 100644 index 9940e96d35e302080c32b49154bbf19a51c0665e..0000000000000000000000000000000000000000 --- a/drivers/gpu/drm/drm_panel.c +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (C) 2013, NVIDIA Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include - -#include -#include -#include - -static DEFINE_MUTEX(panel_lock); -static LIST_HEAD(panel_list); - -/** - * DOC: drm panel - * - * The DRM panel helpers allow drivers to register panel objects with a - * central registry and provide functions to retrieve those panels in display - * drivers. - * - * For easy integration into drivers using the &drm_bridge infrastructure please - * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add(). - */ - -/** - * drm_panel_init - initialize a panel - * @panel: DRM panel - * @dev: parent device of the panel - * @funcs: panel operations - * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to - * the panel interface - * - * Initialize the panel structure for subsequent registration with - * drm_panel_add(). - */ -void drm_panel_init(struct drm_panel *panel, struct device *dev, - const struct drm_panel_funcs *funcs, int connector_type) -{ - INIT_LIST_HEAD(&panel->list); - INIT_LIST_HEAD(&panel->followers); - mutex_init(&panel->follower_lock); - panel->dev = dev; - panel->funcs = funcs; - panel->connector_type = connector_type; -} -EXPORT_SYMBOL(drm_panel_init); - -/** - * drm_panel_add - add a panel to the global registry - * @panel: panel to add - * - * Add a panel to the global registry so that it can be looked up by display - * drivers. - */ -void drm_panel_add(struct drm_panel *panel) -{ - mutex_lock(&panel_lock); - list_add_tail(&panel->list, &panel_list); - mutex_unlock(&panel_lock); -} -EXPORT_SYMBOL(drm_panel_add); - -/** - * drm_panel_remove - remove a panel from the global registry - * @panel: DRM panel - * - * Removes a panel from the global registry. - */ -void drm_panel_remove(struct drm_panel *panel) -{ - mutex_lock(&panel_lock); - list_del_init(&panel->list); - mutex_unlock(&panel_lock); -} -EXPORT_SYMBOL(drm_panel_remove); - -/** - * drm_panel_prepare - power on a panel - * @panel: DRM panel - * - * Calling this function will enable power and deassert any reset signals to - * the panel. After this has completed it is possible to communicate with any - * integrated circuitry via a command bus. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_panel_prepare(struct drm_panel *panel) -{ - struct drm_panel_follower *follower; - int ret; - - if (!panel) - return -EINVAL; - - if (panel->prepared) { - dev_warn(panel->dev, "Skipping prepare of already prepared panel\n"); - return 0; - } - - mutex_lock(&panel->follower_lock); - - if (panel->funcs && panel->funcs->prepare) { - ret = panel->funcs->prepare(panel); - if (ret < 0) - goto exit; - } - panel->prepared = true; - - list_for_each_entry(follower, &panel->followers, list) { - ret = follower->funcs->panel_prepared(follower); - if (ret < 0) - dev_info(panel->dev, "%ps failed: %d\n", - follower->funcs->panel_prepared, ret); - } - - ret = 0; -exit: - mutex_unlock(&panel->follower_lock); - - return ret; -} -EXPORT_SYMBOL(drm_panel_prepare); - -/** - * drm_panel_unprepare - power off a panel - * @panel: DRM panel - * - * Calling this function will completely power off a panel (assert the panel's - * reset, turn off power supplies, ...). After this function has completed, it - * is usually no longer possible to communicate with the panel until another - * call to drm_panel_prepare(). - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_panel_unprepare(struct drm_panel *panel) -{ - struct drm_panel_follower *follower; - int ret; - - if (!panel) - return -EINVAL; - - /* - * If you are seeing the warning below it likely means one of two things: - * - Your panel driver incorrectly calls drm_panel_unprepare() in its - * shutdown routine. You should delete this. - * - You are using panel-edp or panel-simple and your DRM modeset - * driver's shutdown() callback happened after the panel's shutdown(). - * In this case the warning is harmless though ideally you should - * figure out how to reverse the order of the shutdown() callbacks. - */ - if (!panel->prepared) { - dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n"); - return 0; - } - - mutex_lock(&panel->follower_lock); - - list_for_each_entry(follower, &panel->followers, list) { - ret = follower->funcs->panel_unpreparing(follower); - if (ret < 0) - dev_info(panel->dev, "%ps failed: %d\n", - follower->funcs->panel_unpreparing, ret); - } - - if (panel->funcs && panel->funcs->unprepare) { - ret = panel->funcs->unprepare(panel); - if (ret < 0) - goto exit; - } - panel->prepared = false; - - ret = 0; -exit: - mutex_unlock(&panel->follower_lock); - - return ret; -} -EXPORT_SYMBOL(drm_panel_unprepare); - -/** - * drm_panel_enable - enable a panel - * @panel: DRM panel - * - * Calling this function will cause the panel display drivers to be turned on - * and the backlight to be enabled. Content will be visible on screen after - * this call completes. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_panel_enable(struct drm_panel *panel) -{ - int ret; - - if (!panel) - return -EINVAL; - - if (panel->enabled) { - dev_warn(panel->dev, "Skipping enable of already enabled panel\n"); - return 0; - } - - if (panel->funcs && panel->funcs->enable) { - ret = panel->funcs->enable(panel); - if (ret < 0) - return ret; - } - panel->enabled = true; - - ret = backlight_enable(panel->backlight); - if (ret < 0) - DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n", - ret); - - return 0; -} -EXPORT_SYMBOL(drm_panel_enable); - -/** - * drm_panel_disable - disable a panel - * @panel: DRM panel - * - * This will typically turn off the panel's backlight or disable the display - * drivers. For smart panels it should still be possible to communicate with - * the integrated circuitry via any command bus after this call. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_panel_disable(struct drm_panel *panel) -{ - int ret; - - if (!panel) - return -EINVAL; - - /* - * If you are seeing the warning below it likely means one of two things: - * - Your panel driver incorrectly calls drm_panel_disable() in its - * shutdown routine. You should delete this. - * - You are using panel-edp or panel-simple and your DRM modeset - * driver's shutdown() callback happened after the panel's shutdown(). - * In this case the warning is harmless though ideally you should - * figure out how to reverse the order of the shutdown() callbacks. - */ - if (!panel->enabled) { - dev_warn(panel->dev, "Skipping disable of already disabled panel\n"); - return 0; - } - - ret = backlight_disable(panel->backlight); - if (ret < 0) - DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n", - ret); - - if (panel->funcs && panel->funcs->disable) { - ret = panel->funcs->disable(panel); - if (ret < 0) - return ret; - } - panel->enabled = false; - - return 0; -} -EXPORT_SYMBOL(drm_panel_disable); - -/** - * drm_panel_get_modes - probe the available display modes of a panel - * @panel: DRM panel - * @connector: DRM connector - * - * The modes probed from the panel are automatically added to the connector - * that the panel is attached to. - * - * Return: The number of modes available from the panel on success, or 0 on - * failure (no modes). - */ -int drm_panel_get_modes(struct drm_panel *panel, - struct drm_connector *connector) -{ - if (!panel) - return 0; - - if (panel->funcs && panel->funcs->get_modes) { - int num; - - num = panel->funcs->get_modes(panel, connector); - if (num > 0) - return num; - } - - return 0; -} -EXPORT_SYMBOL(drm_panel_get_modes); - -#ifdef CONFIG_OF -/** - * of_drm_find_panel - look up a panel using a device tree node - * @np: device tree node of the panel - * - * Searches the set of registered panels for one that matches the given device - * tree node. If a matching panel is found, return a pointer to it. - * - * Return: A pointer to the panel registered for the specified device tree - * node or an ERR_PTR() if no panel matching the device tree node can be found. - * - * Possible error codes returned by this function: - * - * - EPROBE_DEFER: the panel device has not been probed yet, and the caller - * should retry later - * - ENODEV: the device is not available (status != "okay" or "ok") - */ -struct drm_panel *of_drm_find_panel(const struct device_node *np) -{ - struct drm_panel *panel; - - if (!of_device_is_available(np)) - return ERR_PTR(-ENODEV); - - mutex_lock(&panel_lock); - - list_for_each_entry(panel, &panel_list, list) { - if (panel->dev->of_node == np) { - mutex_unlock(&panel_lock); - return panel; - } - } - - mutex_unlock(&panel_lock); - return ERR_PTR(-EPROBE_DEFER); -} -EXPORT_SYMBOL(of_drm_find_panel); - -/** - * of_drm_get_panel_orientation - look up the orientation of the panel through - * the "rotation" binding from a device tree node - * @np: device tree node of the panel - * @orientation: orientation enum to be filled in - * - * Looks up the rotation of a panel in the device tree. The orientation of the - * panel is expressed as a property name "rotation" in the device tree. The - * rotation in the device tree is counter clockwise. - * - * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the - * rotation property doesn't exist. Return a negative error code on failure. - */ -int of_drm_get_panel_orientation(const struct device_node *np, - enum drm_panel_orientation *orientation) -{ - int rotation, ret; - - ret = of_property_read_u32(np, "rotation", &rotation); - if (ret == -EINVAL) { - /* Don't return an error if there's no rotation property. */ - *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; - return 0; - } - - if (ret < 0) - return ret; - - if (rotation == 0) - *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; - else if (rotation == 90) - *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; - else if (rotation == 180) - *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; - else if (rotation == 270) - *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; - else - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(of_drm_get_panel_orientation); -#endif - -/** - * drm_is_panel_follower() - Check if the device is a panel follower - * @dev: The 'struct device' to check - * - * This checks to see if a device needs to be power sequenced together with - * a panel using the panel follower API. - * At the moment panels can only be followed on device tree enabled systems. - * The "panel" property of the follower points to the panel to be followed. - * - * Return: true if we should be power sequenced with a panel; false otherwise. - */ -bool drm_is_panel_follower(struct device *dev) -{ - /* - * The "panel" property is actually a phandle, but for simplicity we - * don't bother trying to parse it here. We just need to know if the - * property is there. - */ - return of_property_present(dev->of_node, "panel"); -} -EXPORT_SYMBOL(drm_is_panel_follower); - -/** - * drm_panel_add_follower() - Register something to follow panel state. - * @follower_dev: The 'struct device' for the follower. - * @follower: The panel follower descriptor for the follower. - * - * A panel follower is called right after preparing the panel and right before - * unpreparing the panel. It's primary intention is to power on an associated - * touchscreen, though it could be used for any similar devices. Multiple - * devices are allowed the follow the same panel. - * - * If a follower is added to a panel that's already been turned on, the - * follower's prepare callback is called right away. - * - * At the moment panels can only be followed on device tree enabled systems. - * The "panel" property of the follower points to the panel to be followed. - * - * Return: 0 or an error code. Note that -ENODEV means that we detected that - * follower_dev is not actually following a panel. The caller may - * choose to ignore this return value if following a panel is optional. - */ -int drm_panel_add_follower(struct device *follower_dev, - struct drm_panel_follower *follower) -{ - struct device_node *panel_np; - struct drm_panel *panel; - int ret; - - panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); - if (!panel_np) - return -ENODEV; - - panel = of_drm_find_panel(panel_np); - of_node_put(panel_np); - if (IS_ERR(panel)) - return PTR_ERR(panel); - - get_device(panel->dev); - follower->panel = panel; - - mutex_lock(&panel->follower_lock); - - list_add_tail(&follower->list, &panel->followers); - if (panel->prepared) { - ret = follower->funcs->panel_prepared(follower); - if (ret < 0) - dev_info(panel->dev, "%ps failed: %d\n", - follower->funcs->panel_prepared, ret); - } - - mutex_unlock(&panel->follower_lock); - - return 0; -} -EXPORT_SYMBOL(drm_panel_add_follower); - -/** - * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). - * @follower: The panel follower descriptor for the follower. - * - * Undo drm_panel_add_follower(). This includes calling the follower's - * unprepare function if we're removed from a panel that's currently prepared. - * - * Return: 0 or an error code. - */ -void drm_panel_remove_follower(struct drm_panel_follower *follower) -{ - struct drm_panel *panel = follower->panel; - int ret; - - mutex_lock(&panel->follower_lock); - - if (panel->prepared) { - ret = follower->funcs->panel_unpreparing(follower); - if (ret < 0) - dev_info(panel->dev, "%ps failed: %d\n", - follower->funcs->panel_unpreparing, ret); - } - list_del_init(&follower->list); - - mutex_unlock(&panel->follower_lock); - - put_device(panel->dev); -} -EXPORT_SYMBOL(drm_panel_remove_follower); - -static void drm_panel_remove_follower_void(void *follower) -{ - drm_panel_remove_follower(follower); -} - -/** - * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() - * @follower_dev: The 'struct device' for the follower. - * @follower: The panel follower descriptor for the follower. - * - * Handles calling drm_panel_remove_follower() using devm on the follower_dev. - * - * Return: 0 or an error code. - */ -int devm_drm_panel_add_follower(struct device *follower_dev, - struct drm_panel_follower *follower) -{ - int ret; - - ret = drm_panel_add_follower(follower_dev, follower); - if (ret) - return ret; - - return devm_add_action_or_reset(follower_dev, - drm_panel_remove_follower_void, follower); -} -EXPORT_SYMBOL(devm_drm_panel_add_follower); - -#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) -/** - * drm_panel_of_backlight - use backlight device node for backlight - * @panel: DRM panel - * - * Use this function to enable backlight handling if your panel - * uses device tree and has a backlight phandle. - * - * When the panel is enabled backlight will be enabled after a - * successful call to &drm_panel_funcs.enable() - * - * When the panel is disabled backlight will be disabled before the - * call to &drm_panel_funcs.disable(). - * - * A typical implementation for a panel driver supporting device tree - * will call this function at probe time. Backlight will then be handled - * transparently without requiring any intervention from the driver. - * drm_panel_of_backlight() must be called after the call to drm_panel_init(). - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_panel_of_backlight(struct drm_panel *panel) -{ - struct backlight_device *backlight; - - if (!panel || !panel->dev) - return -EINVAL; - - backlight = devm_of_find_backlight(panel->dev); - - if (IS_ERR(backlight)) - return PTR_ERR(backlight); - - panel->backlight = backlight; - return 0; -} -EXPORT_SYMBOL(drm_panel_of_backlight); -#endif - -MODULE_AUTHOR("Thierry Reding "); -MODULE_DESCRIPTION("DRM panel infrastructure"); -MODULE_LICENSE("GPL and additional rights"); From patchwork Thu Feb 6 18:14:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963454 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 5DD98C0219B for ; Thu, 6 Feb 2025 18:14:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D3F4410E906; Thu, 6 Feb 2025 18:14:58 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="Q9jQ0R4K"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 0750210E903 for ; Thu, 6 Feb 2025 18:14:56 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 1128643297; Thu, 6 Feb 2025 18:14:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865695; 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=7hEIMHOGsS3+ATVWYr8D/aXMIeb192mdar/am881FQI=; b=Q9jQ0R4KMalETViSW+r0bhr24TWVJb/1mXJe9b8MrFtitkFWCkQlJdBKFEk45jqtb60UFX JAcsGbZ+YyMhxJR6myPefToAm7CJtn+STTAE8Jy4zHHKf/3ERgYKAaAU9nDcAQCuCBB+9N o2WEtgVRE92wzsO8cx2CBBjHLEeB1ustH0WkmAxXETPTeVl67f2H/5iJvfP0vLIzLouxKK Q8do/LvDCnla/UaPo0iFl+JRchKWlDzzKbAdtcBnw+bMhfz5+3fOOCs5NPAr9X82p84CNm yKGGE288BwGuBnNpmTGhgCZ7kQFQP6UWtkb0JcqafPkVldLBV10F4RPiPaA4JA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:22 +0100 Subject: [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-7-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeehnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Having an DRM_MODE_CONNECTOR_Unknown connector type is consuidered bad, and drm_panel_bridge_add_typed() and derivatives are deprecated for this. drm_panel_init() won't prevent initializing a panel with a DRM_MODE_CONNECTOR_Unknown connector type. Luckily there are no in-tree users doing it, so take this as an opportinuty to document a valid connector type must be passed. Returning an error if this rule is violated is not possible because drm_panel_init() is a void function. Add at least a warning to make any violations noticeable, especially to non-upstream drivers. Signed-off-by: Luca Ceresoli Reviewed-by: Dmitry Baryshkov --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index bd61e57e1a2dd3d1eb034da4f9213a6bcb3e6dc5..58570ff6952ca313b3def084262c9bb3772272ef 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -40,7 +40,7 @@ static LIST_HEAD(panel_list); * @dev: parent device of the panel * @funcs: panel operations * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to - * the panel interface + * the panel interface (must NOT be DRM_MODE_CONNECTOR_Unknown) * * Initialize the panel structure for subsequent registration with * drm_panel_add(). @@ -48,6 +48,9 @@ static LIST_HEAD(panel_list); void drm_panel_init(struct drm_panel *panel, struct device *dev, const struct drm_panel_funcs *funcs, int connector_type) { + if (connector_type == DRM_MODE_CONNECTOR_Unknown) + DRM_WARN("%s: %s: a valid connector type is required!\n", __func__, dev_name(dev)); + INIT_LIST_HEAD(&panel->list); INIT_LIST_HEAD(&panel->followers); mutex_init(&panel->follower_lock); From patchwork Thu Feb 6 18:14:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963455 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 36A46C0219C for ; Thu, 6 Feb 2025 18:15:01 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B68EC10E908; Thu, 6 Feb 2025 18:15:00 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="SdqACOx7"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id EB2D910E908 for ; Thu, 6 Feb 2025 18:14:59 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id E8304442D4; Thu, 6 Feb 2025 18:14:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865698; 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=B+OPzT2FHHN3WygeM9lYiYLW3k/bXtOwVIdLFAeKE+w=; b=SdqACOx7FL/XXpVicJ8aL7ABDDQemT+XfNtaUoVrVg9NnehK6Gs4eq5JzVnTy1fynaprHV sQaobwiLKXUSUGfvTEsI6ZmIj3QcZlSrsu5FW9JGaRZEysXuio+iTI7SfrpFNgGet7U6kk P3D1w2a7qqTtXiIR4/H0OhZL/B97/S3pjfEjD3lGngoIEsE3iXJU9IkinXBN8Ia10EkMKu Gxt261q3EApHc/AV6KpkuGGT+juh+rkD/gQawW+WqoJL8MopwzKJQR4YhqXvykFQzzlicb Hd2P5fVREPrsTr+vgsHx5Oev0Ko/6YyVyu3BKH2/zNIt23g7a/svpXgQ1Rod0A== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:23 +0100 Subject: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-8-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeehnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Adding a panel does currently not add a panel_bridge wrapping it. Usually the panel_bridge creation happens when some other driver (e.g. the previous bridge or the encoder) calls *_of_get_bridge() and the following element in the pipeline is a panel. This has some drawbacks: * the panel_bridge is not created in the context of the driver of the underlying physical device (the panel driver), but of some other driver * that "other driver" is not aware of whether the returned drm_bridge pointer is a panel_bridge created on the fly, a pre-existing panel_bridge or a non-panel bridge * removal of a panel_bridge requires calling drm_panel_bridge_remove(), but the "other driver" doesn't know whether this is needed because it doesn't know whether it has created a panel_bridge or not So far this approach has been working because devm and drmm ensure the panel bridge would be dealloacted at some later point. However with the upcoming implementation of dynamic bridge lifetime this will get more complicated. Correct removal of a panel_bridge might possibly be obtained by adding more devm/drmm technology to have it freed correctly at all times. However this would add more complexity and not help making lifetime more understandable. Use a different approach instead: always create a panel_bridge with a drm_panel, thus matching the lifetime of the drm_panel and the panel_bridge wrapping it. This makes lifetime much more straightforward to understand and to further develop on. With the panel_bridge always created, the functions to get a bridge [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because the bridge they are looking for exists already (if it can exist at all). In turn, this is implemented based on a variant of drm_of_find_panel_or_bridge() that only looks for panels: of_drm_find_bridge_by_endpoint(). In the future drm_of_find_panel_or_bridge() can be progressively removed because there will never be a panel not exposing a bridge. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++--------- include/drm/drm_panel.h | 8 +++++ 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 58570ff6952ca313b3def084262c9bb3772272ef..6995de605e7317dd1eb153afd475746ced764712 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -69,6 +69,9 @@ EXPORT_SYMBOL(drm_panel_init); */ void drm_panel_add(struct drm_panel *panel) { + panel->bridge = drm_panel_bridge_add(panel); + WARN_ON(!panel->bridge); + mutex_lock(&panel_lock); list_add_tail(&panel->list, &panel_list); mutex_unlock(&panel_lock); @@ -86,6 +89,9 @@ void drm_panel_remove(struct drm_panel *panel) mutex_lock(&panel_lock); list_del_init(&panel->list); mutex_unlock(&panel_lock); + + drm_panel_bridge_remove(panel->bridge); + panel->bridge = NULL; } EXPORT_SYMBOL(drm_panel_remove); @@ -412,6 +418,49 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); +/** + * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * @bridge: pointer to hold returned drm_bridge (must not be NULL) + * + * Given a DT node's port and endpoint number, find the connected node and + * return the associated struct drm_bridge. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +static int of_drm_find_bridge_by_endpoint(const struct device_node *np, + int port, int endpoint, + struct drm_bridge **bridge) +{ + int ret = -EPROBE_DEFER; + struct device_node *remote; + + if (!bridge) + return -EINVAL; + + /* + * of_graph_get_remote_node() produces a noisy error message if port + * node isn't found and the absence of the port is a legit case here, + * so at first we silently check whether graph presents in the + * device-tree node. + */ + if (!of_graph_is_present(np)) + return -ENODEV; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + *bridge = of_drm_find_bridge(remote); + if (*bridge) + ret = 0; + + of_node_put(remote); + return ret; +} + /** * of_drm_get_panel_orientation - look up the orientation of the panel through * the "rotation" binding from a device tree node @@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, { struct drm_bridge **ptr, *bridge; + if (panel->bridge) { + DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge); + return panel->bridge; + } + ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) @@ -1106,8 +1160,7 @@ EXPORT_SYMBOL(drm_panel_bridge_connector); * @endpoint: endpoint in the device tree node * * Given a DT node's port and endpoint number, finds the connected node - * and returns the associated bridge if any, or creates and returns a - * drm panel bridge instance if a panel is connected. + * and returns the associated bridge if any. * * Returns a pointer to the bridge if successful, or an error pointer * otherwise. @@ -1117,17 +1170,12 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, u32 port, u32 endpoint) { struct drm_bridge *bridge; - struct drm_panel *panel; int ret; - ret = drm_of_find_panel_or_bridge(np, port, endpoint, - &panel, &bridge); + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); if (ret) return ERR_PTR(ret); - if (panel) - bridge = devm_drm_panel_bridge_add(dev, panel); - return bridge; } EXPORT_SYMBOL(devm_drm_of_get_bridge); @@ -1140,8 +1188,7 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge); * @endpoint: endpoint in the device tree node * * Given a DT node's port and endpoint number, finds the connected node - * and returns the associated bridge if any, or creates and returns a - * drm panel bridge instance if a panel is connected. + * and returns the associated bridge if any. * * Returns a drmm managed pointer to the bridge if successful, or an error * pointer otherwise. @@ -1151,17 +1198,12 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, u32 port, u32 endpoint) { struct drm_bridge *bridge; - struct drm_panel *panel; int ret; - ret = drm_of_find_panel_or_bridge(np, port, endpoint, - &panel, &bridge); + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); if (ret) return ERR_PTR(ret); - if (panel) - bridge = drmm_panel_bridge_add(drm, panel); - return bridge; } EXPORT_SYMBOL(drmm_of_get_bridge); diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 10015891b056f816c7a992a2052b36fd26943c5b..7ace6c1389c5353c08de5ac46127e46e1de69359 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -196,6 +196,14 @@ struct drm_panel { */ struct device *dev; + /** + * @bridge: + * + * Pointer to the panel bridge that allows accessing this panel as + * a DRM bridge. + */ + struct drm_bridge *bridge; + /** * @backlight: * From patchwork Thu Feb 6 18:14:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963456 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 65936C02194 for ; Thu, 6 Feb 2025 18:15:04 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CBD4410E909; Thu, 6 Feb 2025 18:15:03 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="edUmkGGM"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id BC09310E905 for ; Thu, 6 Feb 2025 18:15:02 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id D90DC442D7; Thu, 6 Feb 2025 18:14:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865701; 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=WfEAnvIODb0MmG3S/Br6HR0LaSDyjKlXquR7BwTnfnk=; b=edUmkGGMvGYPJ7a/FimUusEEcNKQwLB8Qxmr9qnFbT6OUb1ynvPdZyYQrkPyK4sajWFfER 1YrNGUg1lovimdcjL+4LRQOVjhFYLrAVrpy8813GsnzjanRabbWQzXScs1P8nqfVQuez6R vwT9S9N+9/+OGQUu2am8pLam6z+eVoq1dc44PBACppAYeSTet9FRJY804CVOeJ6B/6GiYp 3lha6aXmJvtc7u7j9jbBTJlYu3xTfgRUZqA0l0DOKgIIwe4XMr2RFH2UdyZN41tB5fUQf5 T3OVPdx/TZ6lRbcNF1iLGPstcX2+dpOcCGQQtOyUtTw4trgRDqWujH+gmZnDkA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:24 +0100 Subject: [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-9-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeejnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" devm_drm_of_get_bridge() and drmm_of_get_bridge() do not have anything to do with struct drm_panel anymore, they just manage bridges. So move them from bridge/panel.c to drm_bridge.c. Move also of_drm_find_bridge_by_endpoint() which is used only by devm_drm_of_get_bridge() and drmm_of_get_bridge(). No code changes, only move functions to a different file within the same module and add an #include as needed. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 102 ----------------------------------------- drivers/gpu/drm/drm_bridge.c | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 102 deletions(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 6995de605e7317dd1eb153afd475746ced764712..1230ae50b2020e7a9306cac83009dd600dd61d26 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -418,49 +418,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); -/** - * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint - * @np: device tree node containing encoder output ports - * @port: port in the device tree node - * @endpoint: endpoint in the device tree node - * @bridge: pointer to hold returned drm_bridge (must not be NULL) - * - * Given a DT node's port and endpoint number, find the connected node and - * return the associated struct drm_bridge. - * - * Returns zero if successful, or one of the standard error codes if it fails. - */ -static int of_drm_find_bridge_by_endpoint(const struct device_node *np, - int port, int endpoint, - struct drm_bridge **bridge) -{ - int ret = -EPROBE_DEFER; - struct device_node *remote; - - if (!bridge) - return -EINVAL; - - /* - * of_graph_get_remote_node() produces a noisy error message if port - * node isn't found and the absence of the port is a legit case here, - * so at first we silently check whether graph presents in the - * device-tree node. - */ - if (!of_graph_is_present(np)) - return -ENODEV; - - remote = of_graph_get_remote_node(np, port, endpoint); - if (!remote) - return -ENODEV; - - *bridge = of_drm_find_bridge(remote); - if (*bridge) - ret = 0; - - of_node_put(remote); - return ret; -} - /** * of_drm_get_panel_orientation - look up the orientation of the panel through * the "rotation" binding from a device tree node @@ -1150,62 +1107,3 @@ struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) return &panel_bridge->connector; } EXPORT_SYMBOL(drm_panel_bridge_connector); - -#ifdef CONFIG_OF -/** - * devm_drm_of_get_bridge - Return next bridge in the chain - * @dev: device to tie the bridge lifetime to - * @np: device tree node containing encoder output ports - * @port: port in the device tree node - * @endpoint: endpoint in the device tree node - * - * Given a DT node's port and endpoint number, finds the connected node - * and returns the associated bridge if any. - * - * Returns a pointer to the bridge if successful, or an error pointer - * otherwise. - */ -struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, - struct device_node *np, - u32 port, u32 endpoint) -{ - struct drm_bridge *bridge; - int ret; - - ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); - if (ret) - return ERR_PTR(ret); - - return bridge; -} -EXPORT_SYMBOL(devm_drm_of_get_bridge); - -/** - * drmm_of_get_bridge - Return next bridge in the chain - * @drm: device to tie the bridge lifetime to - * @np: device tree node containing encoder output ports - * @port: port in the device tree node - * @endpoint: endpoint in the device tree node - * - * Given a DT node's port and endpoint number, finds the connected node - * and returns the associated bridge if any. - * - * Returns a drmm managed pointer to the bridge if successful, or an error - * pointer otherwise. - */ -struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, - struct device_node *np, - u32 port, u32 endpoint) -{ - struct drm_bridge *bridge; - int ret; - - ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); - if (ret) - return ERR_PTR(ret); - - return bridge; -} -EXPORT_SYMBOL(drmm_of_get_bridge); - -#endif diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 87cebec2de806781cee22da54d666eee9bde3648..2aa17fbe538b86066c4e68f0d0e8046e9ca9b965 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1334,6 +1335,105 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) return NULL; } EXPORT_SYMBOL(of_drm_find_bridge); + +/** + * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * @bridge: pointer to hold returned drm_bridge (must not be NULL) + * + * Given a DT node's port and endpoint number, find the connected node and + * return the associated struct drm_bridge. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +static int of_drm_find_bridge_by_endpoint(const struct device_node *np, + int port, int endpoint, + struct drm_bridge **bridge) +{ + int ret = -EPROBE_DEFER; + struct device_node *remote; + + if (!bridge) + return -EINVAL; + + /* + * of_graph_get_remote_node() produces a noisy error message if port + * node isn't found and the absence of the port is a legit case here, + * so at first we silently check whether graph presents in the + * device-tree node. + */ + if (!of_graph_is_present(np)) + return -ENODEV; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + *bridge = of_drm_find_bridge(remote); + if (*bridge) + ret = 0; + + of_node_put(remote); + return ret; +} + +/** + * devm_drm_of_get_bridge - Return next bridge in the chain + * @dev: device to tie the bridge lifetime to + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * + * Given a DT node's port and endpoint number, finds the connected node + * and returns the associated bridge if any. + * + * Returns a pointer to the bridge if successful, or an error pointer + * otherwise. + */ +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, + struct device_node *np, + u32 port, u32 endpoint) +{ + struct drm_bridge *bridge; + int ret; + + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); + if (ret) + return ERR_PTR(ret); + + return bridge; +} +EXPORT_SYMBOL(devm_drm_of_get_bridge); + +/** + * drmm_of_get_bridge - Return next bridge in the chain + * @drm: device to tie the bridge lifetime to + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * + * Given a DT node's port and endpoint number, finds the connected node + * and returns the associated bridge if any. + * + * Returns a drmm managed pointer to the bridge if successful, or an error + * pointer otherwise. + */ +struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, + struct device_node *np, + u32 port, u32 endpoint) +{ + struct drm_bridge *bridge; + int ret; + + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge); + if (ret) + return ERR_PTR(ret); + + return bridge; +} +EXPORT_SYMBOL(drmm_of_get_bridge); #endif MODULE_AUTHOR("Ajay Kumar "); From patchwork Thu Feb 6 18:14:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963457 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 78A79C0219B for ; Thu, 6 Feb 2025 18:15:07 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id E680910E90A; Thu, 6 Feb 2025 18:15:06 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="VZfRnZJN"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5579310E90A for ; Thu, 6 Feb 2025 18:15:05 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id A883743297; Thu, 6 Feb 2025 18:15:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865704; 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=3BUqEtfCEh3q+AMxh9LslActlP7bwrUqRLfwXX1jD/I=; b=VZfRnZJNdlAHyGR7Y5JUB+4Zs1FeePP7fAQWkoeWfCu2x/H34TMAIfz0AMwTC9ykMNM7mR 9kMl0ajWjarJu7PMb1NamwCsLBAlIOouA5yd39/YtPfgRuzWmR7XtD31N+TsazT3qrjGZy qMjwBWMd/uiKO0Wh91+kHdpMy+1alr4FDE8RaL+UXBrbNygoFfZaOv9+IX+tw5B30mFcB6 ehGRkPZM960fD3+h2eKCGmtZs25jLDLj/DAu1Kw5/jbF4RwDLzK0m+Tivuu2zYk9112bXC 2Eil9YKnOK1v4GxWgRr/AA40F3GH2G6f3kJVmvWBY/uC1LAcoYkqlQE0hhDYWw== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:25 +0100 Subject: [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node() MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-10-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeejnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" devm_drm_of_get_bridge(), which is based on graph links, is the recommended function to get a pointer to the following bridge. This is valid even for panels, for which the recommended device tree description is via graph links and not (or not only) panel subnodes of a panel controller (e.g. "dsi@1234" controller node with a "panel@0" subnode). However there are drivers supporting the panel subnode description in addition to the graph links. For those drivers add a _by_node variant that takes the node of the target node. Suggested-by: Dmitry Baryshkov Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_bridge.c | 30 ++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 2aa17fbe538b86066c4e68f0d0e8046e9ca9b965..b0834b8644284e5f7751cec81724af849b4180e7 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1407,6 +1407,36 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, } EXPORT_SYMBOL(devm_drm_of_get_bridge); +/** + * devm_drm_of_get_bridge_by_node - Return bridge for a given OF node + * @dev: device to tie the bridge lifetime to + * @bridge_node: device node of the remote bridge + * + * Given a bridge DT node, returns the associated bridge if any. This + * should be used in addition to devm_drm_of_get_bridge() when the regular + * graph link search is not enough, e.g. for drivers that need to support + * panels described only as subnodes. + * + * RETURNS: + * A pointer to the bridge if successful, or an error pointer otherwise. + */ +struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, + struct device_node *bridge_node) +{ + struct drm_bridge *bridge; + int ret; + + if (!bridge_node) + return ERR_PTR(-EINVAL); + + bridge = of_drm_find_bridge(bridge_node); + if (!bridge) + return ERR_PTR(-ENODEV); + + return bridge; +} +EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node); + /** * drmm_of_get_bridge - Return next bridge in the chain * @drm: device to tie the bridge lifetime to diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 496dbbd2ad7edff7f091adfbe62de1e33ef0cf07..1561347c4991dac6022319774510f9560c9283c3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1088,6 +1088,8 @@ static inline int drm_panel_bridge_set_orientation(struct drm_connector *connect #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE) struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node, u32 port, u32 endpoint); +struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, + struct device_node *bridge_node); struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, u32 endpoint); #else @@ -1099,6 +1101,12 @@ static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, return ERR_PTR(-ENODEV); } +static inline struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, + struct device_node *bridge_node) +{ + return ERR_PTR(-ENODEV); +} + static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, From patchwork Thu Feb 6 18:14:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963458 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 D616DC02199 for ; Thu, 6 Feb 2025 18:15:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 508FB10E903; Thu, 6 Feb 2025 18:15:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="alWZYw5+"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id DD50C10E90B for ; Thu, 6 Feb 2025 18:15:07 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 40FB8442D4; Thu, 6 Feb 2025 18:15:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865706; 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=vHGKRRwUFJ1hKE8GkVs2Ez/vUs0X0HmXOnbYhe5x4PU=; b=alWZYw5+dkL1Byea1MLItdJnR6sEkiLtF/qRlFzH1fH1o4wc/B4cVix0giMXpxuxotAKXa NUxXCTo2oIqOHWXSlduliLid5Crv24bFILYvn1IbnSK27Whtmcb/TBmea6vTn7FN2YQYXE mJra3HiBro5kWZ8s/BtEmjYo0TNah5iAwh/NBNB2dDz/+lIc2p+veWwuQ3AwsXn361k+oo yooIP1PA4lKDQRWoZF797UY8kLWybJI8TgpEQ1QVjmtKuxqkpQ7jk7QOrPlhyRwiXFN6pc pcJD+hz+3tEYSBdhayBdlJKDSXWdzIRwxeLILbi0VeHKoHLZzVp8olo3WUcW2Q== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:26 +0100 Subject: [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-11-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeelnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" In order to support panels described either via graph links or via a subnode (e.g. "panel@0"), this driver uses low-level deprecated functions to find the next bridge. The resulting logic is complex and duplicates code already present in the DRM bridge core. Switch to the new APIs in DRM bridge core that allow to do the same in a much cleaner way. Note there are two slight changes in the new logic intended to improve the final result: * the old code looks for a subnode with any name except "port" or "ports", while the new code uses the node passed as a parameter * the old code looks for a subnode first and falls back to a graph link, while the new code uses the reverse order because graph links are the recommended device tree representation now The first change makes the code more robust by avoiding the risk of using an unrelated node which is not describing a panel and not names "port" or "ports". The second change is not expected to expose regressions because, in the cases where both a subnode and a graph link are used to describe a panel, the graph link should point to the subnode itself, such as in arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts As a further cleanup, use a temporary variable to assign dsi->out_bridge only on success. This avoids the risk of leaving a non-NULL value in dsi->out_bridge when samsung_dsim_host_attach() fails. Suggested-by: Dmitry Baryshkov Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/bridge/samsung-dsim.c | 55 ++++++----------------------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index f8b4fb8357659018ec0db65374ee5d05330639ae..bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1704,55 +1704,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, const struct samsung_dsim_plat_data *pdata = dsi->plat_data; struct device *dev = dsi->dev; struct device_node *np = dev->of_node; - struct device_node *remote; - struct drm_panel *panel; + struct drm_bridge *out_bridge; int ret; - /* - * Devices can also be child nodes when we also control that device - * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device). - * - * Lookup for a child node of the given parent that isn't either port - * or ports. - */ - for_each_available_child_of_node(np, remote) { - if (of_node_name_eq(remote, "port") || - of_node_name_eq(remote, "ports")) - continue; + out_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); + if (IS_ERR(out_bridge) && PTR_ERR(out_bridge) != -EPROBE_DEFER) + out_bridge = devm_drm_of_get_bridge_by_node(dev, device->dev.of_node); - goto of_find_panel_or_bridge; - } - - /* - * of_graph_get_remote_node() produces a noisy error message if port - * node isn't found and the absence of the port is a legit case here, - * so at first we silently check whether graph presents in the - * device-tree node. - */ - if (!of_graph_is_present(np)) - return -ENODEV; - - remote = of_graph_get_remote_node(np, 1, 0); - -of_find_panel_or_bridge: - if (!remote) - return -ENODEV; - - panel = of_drm_find_panel(remote); - if (!IS_ERR(panel)) { - dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel); - } else { - dsi->out_bridge = of_drm_find_bridge(remote); - if (!dsi->out_bridge) - dsi->out_bridge = ERR_PTR(-EINVAL); - } - - of_node_put(remote); - - if (IS_ERR(dsi->out_bridge)) { - ret = PTR_ERR(dsi->out_bridge); - DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret); - return ret; + if (IS_ERR(out_bridge)) { + DRM_DEV_ERROR(dev, "failed to find the bridge: %ld\n", PTR_ERR(out_bridge)); + return PTR_ERR(out_bridge); } DRM_DEV_INFO(dev, "Attached %s device (lanes:%d bpp:%d mode-flags:0x%lx)\n", @@ -1784,6 +1745,8 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, dsi->format = device->format; dsi->mode_flags = device->mode_flags; + dsi->out_bridge = out_bridge; + return 0; } From patchwork Thu Feb 6 18:14:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963459 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 A91E8C0219B for ; Thu, 6 Feb 2025 18:15:12 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 2534810E905; Thu, 6 Feb 2025 18:15:12 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="lBoVSeHl"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8891810E90B for ; Thu, 6 Feb 2025 18:15:10 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id C97AF442D7; Thu, 6 Feb 2025 18:15:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865709; 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=8iJmfJohMLskNWh1/d0vxg24JxdxKf1x+OOa7rjoPoQ=; b=lBoVSeHlKoZKgObkr/7yH+eB6a28/qBL2c815XIuOrLvGMzwIO27CvahjbLLJROTvzCE5L 3YotwQGDyNa6viU6GczZ7FATkIvf6fxX4rgB7bhbgZ8cHCmB3uCGKyLRPxFjHQ47iqZG1I 94jMS/fhat15/+DcEg8+cFPfIR7V8/Pn9qJXL2Ry9LWrsDHyb+kZ1nYyOgWAz468z3mAh7 IPmWTzzP2wZVS/bJpqm7lgpZRA8eTtMJ5iPH3dfwvphiPTbt7y9pNdG3ZcVnlC3XtwybOh UkkZX6nQW/s8PqE2cmf9S9xN42UgSc01janAnUjiCVEiOSz5fxKe92B+Ql/XVg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:27 +0100 Subject: [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-12-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeelnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrnhgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmp dhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" In preparation for allowing bridges to be added to and removed from a DRM card without destroying the whole card, add a new DRM bridge function called on addition and removal of bridges. Signed-off-by: Luca Ceresoli --- Changed in v6: - rebased fixing conflicts Changed in v5: - fixed kerneldoc errors This patch was added in v4. --- drivers/gpu/drm/drm_bridge.c | 12 ++++++++++++ include/drm/drm_bridge.h | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index b0834b8644284e5f7751cec81724af849b4180e7..1955a231378050abf1071d74a145831b425c47c5 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -207,12 +207,18 @@ LIST_HEAD(bridge_list); */ void drm_bridge_add(struct drm_bridge *bridge) { + struct drm_bridge *br, *tmp; + mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) bridge->ycbcr_420_allowed = !!(bridge->supported_formats & BIT(HDMI_COLORSPACE_YUV420)); + list_for_each_entry_safe(br, tmp, &bridge_list, list) + if (br->funcs->bridge_event_notify) + br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_ADD, bridge); + mutex_lock(&bridge_lock); list_add_tail(&bridge->list, &bridge_list); mutex_unlock(&bridge_lock); @@ -249,10 +255,16 @@ EXPORT_SYMBOL(devm_drm_bridge_add); */ void drm_bridge_remove(struct drm_bridge *bridge) { + struct drm_bridge *br, *tmp; + mutex_lock(&bridge_lock); list_del_init(&bridge->list); mutex_unlock(&bridge_lock); + list_for_each_entry_safe(br, tmp, &bridge_list, list) + if (br->funcs->bridge_event_notify) + br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge); + mutex_destroy(&bridge->hpd_mutex); } EXPORT_SYMBOL(drm_bridge_remove); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 1561347c4991dac6022319774510f9560c9283c3..ad7ba444a13e5ecf16f996de3742e4ac67dc21f1 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -56,6 +56,11 @@ enum drm_bridge_attach_flags { DRM_BRIDGE_ATTACH_NO_CONNECTOR = BIT(0), }; +enum drm_bridge_event_type { + DRM_EVENT_BRIDGE_ADD, + DRM_EVENT_BRIDGE_REMOVE, +}; + /** * struct drm_bridge_funcs - drm_bridge control functions */ @@ -729,6 +734,24 @@ struct drm_bridge_funcs { struct drm_bridge *bridge, bool enable, int direction); + /** + * @bridge_event_notify: + * + * Notify that another bridge is being added or removed. + * + * This callback is optional. Bridges implementing it must always + * check whether the event refers to a bridge they actually need to + * interact with. + * + * @bridge: bridge being notified + * @event: event happened (add/remove bridge) + * @event_bridge: the bridge mentioned by the event (i.e. the + * bridge being added or removed) + */ + void (*bridge_event_notify)(struct drm_bridge *bridge, + enum drm_bridge_event_type event, + struct drm_bridge *event_bridge); + /** * @debugfs_init: * From patchwork Thu Feb 6 18:14:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963460 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 57379C02194 for ; Thu, 6 Feb 2025 18:15:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D17C110E90B; Thu, 6 Feb 2025 18:15:14 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="ngpa34VP"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 41F3210E90B for ; Thu, 6 Feb 2025 18:15:13 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 719A543297; Thu, 6 Feb 2025 18:15:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865712; 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=y3TMHUj+K2chHxrw2j/AgnSqeNwHfofOR/atkdn41Jk=; b=ngpa34VP7GHOG616ZNKcHSS9uQYSTzIiw6mhtigy+a6n5jbwCrs76Xvp+R7Nt8NZ5V9bQT frZELq1z13fvvesWr4NL0UgPRv/utgsj6hizuPoeprSpLQtIZQ49HOzItZgPPHX57gpJa8 4Zp8luzqOPbt2QZWYICUb3XTdLmamMMgmKaj01nP86ulbH5WNIiqhA32yXI+hj+yT/xCFg I4Fmgc0Pu4/zUuxRf4qfeHrQyURQKdTaSd4dy+qor+ie4FgIXJGtTTnptxHNxUGjSF1A5d Yxd+eMEOqQ0BZUV7GPeXpRvhXkxM0mYb7ZUUNph9GVi57OycYM+fIhGEJMZqsw== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:28 +0100 Subject: [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from() MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-13-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeduudenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Supporting hardware whose final part of the DRM pipeline can be physically removed requires the ability to detach all bridges from a given point to the end of the pipeline. Introduce a variant of drm_encoder_cleanup() for this. Signed-off-by: Luca Ceresoli --- Changes in v6: none Changes in v5: none Changes in v4: none Changes in v3: none Changed in v2: - fix a typo in a comment --- drivers/gpu/drm/drm_encoder.c | 21 +++++++++++++++++++++ include/drm/drm_encoder.h | 1 + 2 files changed, 22 insertions(+) diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 8f2bc6a28482229fd0b030a1958f87753ad7885f..472dfbefe2960924a4e83bec425af8c7ef5f5265 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -207,6 +207,27 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) } EXPORT_SYMBOL(drm_encoder_cleanup); +/** + * drm_encoder_cleanup_from - remove a given bridge and all the following + * @encoder: encoder whole list of bridges shall be pruned + * @bridge: first bridge to remove + * + * Removes from an encoder all the bridges starting with a given bridge + * and until the end of the chain. + * + * This should not be used in "normal" DRM pipelines. It is only useful for + * devices whose final part of the DRM chain can be physically removed and + * later reconnected (possibly with different hardware). + */ +void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge) +{ + struct drm_bridge *next; + + list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node) + drm_bridge_detach(bridge); +} +EXPORT_SYMBOL(drm_encoder_cleanup_from); + static void drmm_encoder_alloc_release(struct drm_device *dev, void *ptr) { struct drm_encoder *encoder = ptr; diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 977a9381c8ba943b4d3e021635ea14856df8a17d..bafcabb242674880a97dfb62a50d93cc4d80c1d4 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -320,6 +320,7 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, } void drm_encoder_cleanup(struct drm_encoder *encoder); +void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge); /** * drm_for_each_encoder_mask - iterate over encoders specified by bitmask From patchwork Thu Feb 6 18:14:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963461 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 B202CC0219B for ; Thu, 6 Feb 2025 18:15:17 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 38A3D10E90D; Thu, 6 Feb 2025 18:15:17 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="MRH2pw9D"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1920510E90E for ; Thu, 6 Feb 2025 18:15:15 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 32A37442D8; Thu, 6 Feb 2025 18:15:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865714; 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=MW7x6+H+CPMoQ7un393dL3lH/3gZIL6wfaKY7GNjZhk=; b=MRH2pw9DDs9Q9h8FHUocP2hhZimNMsi2OdFxk28Esfb/X6jJ9JTVvKYBTmL8H4qInRFFp0 dB9yWTmwQvkoc5DGUhMdqOEgD7t18pvKzWe9Q6e1OtGWS4+GqSZERffB1gWrCmQiEv9Gdm ec3Y4/nLI2rNDtSfdeO9NM9HfSRGzqSyLWkKSfIPsGn2UA6DAcJgadEa0IoWFVJTWno5QV RhxW0Y7D0pLILZLgBQLJ5upF++S1phSPuKG0ZS/wQci7hpZJzuefK1/j97ncHHyh1vRVEQ 7kIwlxgYO0x+pX+KLgK3Ait08Wr229OkvY39pZxpMtT4pCL+Pe+LWMesjtU9JA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:29 +0100 Subject: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-14-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeduudenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 and removed to a DRM card without tearing the card down. This is possible for connectors already (used by DP MST), so add this possibility to DRM bridges as well. Implementation is based on drm_connector_init() as far as it makes sense, and differs when it doesn't. A difference is that bridges are not exposed to userspace, hence struct drm_bridge does not embed a struct drm_mode_object which would provide the refcount. Instead we add to struct drm_bridge a refcount field (we don't need other struct drm_mode_object fields here) and instead of using the drm_mode_object_*() functions we reimplement from those functions the few lines that drm_bridge needs for refcounting. Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted bridge. Signed-off-by: Luca Ceresoli --- 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 | 76 ++++++++++++++++++++++++++-- include/drm/drm_bridge.h | 117 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1955a231378050abf1071d74a145831b425c47c5..f694b32ca59cb91c32846bc00b43360df41cc1ad 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -200,6 +200,57 @@ DEFINE_MUTEX(bridge_lock); LIST_HEAD(bridge_list); +/* Internal function (for refcounted bridges) */ +void __drm_bridge_free(struct kref *kref) +{ + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount); + void *container = ((void *)bridge) - bridge->container_offset; + + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container); + + kfree(container); +} +EXPORT_SYMBOL(__drm_bridge_free); + +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 = kzalloc(size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + bridge = container + offset; + bridge->container_offset = offset; + bridge->funcs = funcs; + kref_init(&bridge->refcount); + bridge->is_refcounted = 1; + + err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge); + if (err) + return ERR_PTR(err); + + DRM_DEBUG("bridge=%p, container=%p, funcs=%ps ALLOC\n", bridge, container, funcs); + + return container; +} +EXPORT_SYMBOL(__devm_drm_bridge_alloc); + /** * drm_bridge_add - add the given bridge to the global bridge list * @@ -209,6 +260,10 @@ void drm_bridge_add(struct drm_bridge *bridge) { struct drm_bridge *br, *tmp; + DRM_DEBUG("bridge=%p ADD\n", bridge); + + drm_bridge_get(bridge); + mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) @@ -257,6 +312,8 @@ void drm_bridge_remove(struct drm_bridge *bridge) { struct drm_bridge *br, *tmp; + DRM_DEBUG("bridge=%p REMOVE\n", bridge); + mutex_lock(&bridge_lock); list_del_init(&bridge->list); mutex_unlock(&bridge_lock); @@ -266,6 +323,8 @@ void drm_bridge_remove(struct drm_bridge *bridge) br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge); mutex_destroy(&bridge->hpd_mutex); + + drm_bridge_put(bridge); } EXPORT_SYMBOL(drm_bridge_remove); @@ -326,11 +385,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, if (!encoder || !bridge) return -EINVAL; - if (previous && (!previous->dev || previous->encoder != encoder)) - return -EINVAL; + drm_bridge_get(bridge); - if (bridge->dev) - return -EBUSY; + if (previous && (!previous->dev || previous->encoder != encoder)) { + ret = -EINVAL; + goto err_put_bridge; + } + + if (bridge->dev) { + ret = -EBUSY; + goto err_put_bridge; + } bridge->dev = encoder->dev; bridge->encoder = encoder; @@ -379,6 +444,8 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, "failed to attach bridge %pOF to encoder %s\n", bridge->of_node, encoder->name); +err_put_bridge: + drm_bridge_put(bridge); return ret; } EXPORT_SYMBOL(drm_bridge_attach); @@ -399,6 +466,7 @@ void drm_bridge_detach(struct drm_bridge *bridge) list_del(&bridge->chain_node); bridge->dev = NULL; + drm_bridge_put(bridge); } /** diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -31,6 +31,7 @@ #include #include #include +#include struct device_node; @@ -863,6 +864,22 @@ struct drm_bridge { const struct drm_bridge_timings *timings; /** @funcs: control functions */ const struct drm_bridge_funcs *funcs; + + /** + * @container_offset: Offset of this struct within the container + * struct embedding it. Used for refcounted bridges to free the + * embeddeing struct when the refcount drops to zero. Unused on + * legacy bridges. + */ + size_t container_offset; + /** + * @refcount: reference count for bridges with dynamic lifetime + * (see drm_bridge_init) + */ + struct kref refcount; + /** @is_refcounted: this bridge has dynamic lifetime management */ + bool is_refcounted; + /** @driver_private: pointer to the bridge driver's internal context */ void *driver_private; /** @ops: bitmask of operations supported by the bridge */ @@ -964,6 +981,106 @@ drm_priv_to_bridge(struct drm_private_obj *priv) return container_of(priv, struct drm_bridge, base); } +static inline bool drm_bridge_is_refcounted(struct drm_bridge *bridge) +{ + return bridge->is_refcounted; +} + +void __drm_bridge_free(struct kref *kref); + +/** + * drm_bridge_get - Acquire a bridge reference + * @bridge: DRM bridge + * + * This function increments the bridge's refcount. + * + * It does nothing on non-refcounted bridges. See drm_bridge_init(). + */ +static inline void drm_bridge_get(struct drm_bridge *bridge) +{ + if (!drm_bridge_is_refcounted(bridge)) + return; + + DRM_DEBUG("bridge=%p GET\n", bridge); + + kref_get(&bridge->refcount); +} + +/** + * 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. + * + * It does nothing on non-refcounted bridges. See drm_bridge_init(). + * + * See also drm_bridge_put_and_clear() which is more handy in many cases. + */ +static inline void drm_bridge_put(struct drm_bridge *bridge) +{ + if (!drm_bridge_is_refcounted(bridge)) + return; + + DRM_DEBUG("bridge=%p PUT\n", bridge); + + kref_put(&bridge->refcount, __drm_bridge_free); +} + +/** + * drm_bridge_put_and_clear - Given a bridge pointer, clear the pointer + * then put the bridge + * + * @bridge_pp: pointer to pointer to a struct drm_bridge + * + * Helper to put a DRM bridge (whose pointer is passed), but only after + * setting its pointer to NULL. Useful for drivers having struct drm_bridge + * pointers they need to dispose of, without leaving a use-after-free + * window where the pointed bridge might have been freed while still + * holding a pointer to it. + * + * For example a driver having this private struct:: + * + * struct my_bridge { + * struct drm_bridge *remote_bridge; + * ... + * }; + * + * can dispose of remote_bridge using:: + * + * drm_bridge_put_and_clear(&my_bridge->remote_bridge); + */ +static inline void drm_bridge_put_and_clear(struct drm_bridge **bridge_pp) +{ + struct drm_bridge *bridge = *bridge_pp; + + *bridge_pp = NULL; + drm_bridge_put(bridge); +} + +void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_bridge_funcs *funcs); + +/** + * devm_drm_bridge_alloc - Allocate and initialize an refcounted bridge + * @dev: struct device of the bridge device + * @type: the type of the struct which contains struct &drm_bridge + * @member: the name of the &drm_bridge within @type + * @funcs: callbacks for this bridge + * + * The bridge will have a dynamic lifetime (aka a refcounted bridge). + * + * The returned refcount is initialized to 1. This reference will be + * automatically dropped via devm (by calling drm_bridge_put()) when the + * device is removed. + * + * Returns: + * Pointer to new bridge, or ERR_PTR on failure. + */ +#define devm_drm_bridge_alloc(dev, type, member, funcs) \ + ((type *)__devm_drm_bridge_alloc(dev, sizeof(type), \ + offsetof(type, member), funcs)) + void drm_bridge_add(struct drm_bridge *bridge); int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); From patchwork Thu Feb 6 18:14:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963462 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 6BEC0C02199 for ; Thu, 6 Feb 2025 18:15:21 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D13F010E912; Thu, 6 Feb 2025 18:15:20 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="DZ/1NcWu"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id DFE7010E910 for ; Thu, 6 Feb 2025 18:15:18 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 06C4344264; Thu, 6 Feb 2025 18:15:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865717; 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=nGVO+QbU0hs3mabM5lWL+R6oDUNoq4UxfAsW2GhkoGA=; b=DZ/1NcWug2cys6H/JNqGshU/Xo24P11YOh993tdanJxk9kvVnFe475NPxvy644vsLb1PxQ wMwSsJHTbaPq0eTPiBjNOzfBLFs2h27LXxtKsK8a2yUYWvLFB1C0G5JbmHrhKQVwTc47ci 2U9kF6U/XChUQn4GifaEgqdT9XXCkpPPfBYJVYTmjyE8uim/smB3dMqib28HMRiXl7lopS rQuz6SPtkxt0FmExvCi5u2hiG/EPtyZ3aDtCb2zerdVye0ceF3bEM4wAdGiU0s/jrQSHwf vWH8LewgF31ZkOGjE0rv882i4ucnJyW5i4kaOfzviMfo3wTuY0aTf8OumoNwhg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:30 +0100 Subject: [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-15-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedufeenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Add a devm/drmm action to these functions so the bridge reference is dropped automatically when the caller is removed. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_bridge.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index f694b32ca59cb91c32846bc00b43360df41cc1ad..497ec06dfcb05ab5dee8ea5e8f1eafb9c2807612 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -1459,6 +1460,13 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np, return ret; } +static void devm_drm_bridge_put_void(void *data) +{ + struct drm_bridge *bridge = (struct drm_bridge *)data; + + drm_bridge_put(bridge); +} + /** * devm_drm_of_get_bridge - Return next bridge in the chain * @dev: device to tie the bridge lifetime to @@ -1469,6 +1477,10 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np, * Given a DT node's port and endpoint number, finds the connected node * and returns the associated bridge if any. * + * The refcount of the returned bridge is incremented, but the caller does + * not have to call drm_bridge_put() when done with the bridge. It will be + * done by devres when @dev is removed. + * * Returns a pointer to the bridge if successful, or an error pointer * otherwise. */ @@ -1483,6 +1495,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, if (ret) return ERR_PTR(ret); + ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge); + if (ret) + return ERR_PTR(ret); + return bridge; } EXPORT_SYMBOL(devm_drm_of_get_bridge); @@ -1497,6 +1513,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge); * graph link search is not enough, e.g. for drivers that need to support * panels described only as subnodes. * + * The refcount of the returned bridge is incremented, but the caller does + * not have to call drm_bridge_put() when done with the bridge. It will be + * done by devres when @dev is removed. + * * RETURNS: * A pointer to the bridge if successful, or an error pointer otherwise. */ @@ -1513,10 +1533,21 @@ struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, if (!bridge) return ERR_PTR(-ENODEV); + ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge); + if (ret) + return ERR_PTR(ret); + return bridge; } EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node); +static void drmm_bridge_put_void(struct drm_device *drm, void *ptr) +{ + struct drm_bridge *bridge = ptr; + + drm_bridge_put(bridge); +} + /** * drmm_of_get_bridge - Return next bridge in the chain * @drm: device to tie the bridge lifetime to @@ -1527,6 +1558,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node); * Given a DT node's port and endpoint number, finds the connected node * and returns the associated bridge if any. * + * The refcount of the returned bridge is incremented, but the caller does + * not have to call drm_bridge_put() when done with the bridge. It will be + * done by drmm when @dev is removed. + * * Returns a drmm managed pointer to the bridge if successful, or an error * pointer otherwise. */ @@ -1541,6 +1576,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, if (ret) return ERR_PTR(ret); + ret = drmm_add_action_or_reset(drm, drmm_bridge_put_void, bridge); + if (ret) + return ERR_PTR(ret); + return bridge; } EXPORT_SYMBOL(drmm_of_get_bridge); From patchwork Thu Feb 6 18:14:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963463 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 B4D63C0219B for ; Thu, 6 Feb 2025 18:15:23 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3FA0310E910; Thu, 6 Feb 2025 18:15:23 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="Ci/80IxI"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id AB8DC10E910 for ; Thu, 6 Feb 2025 18:15:21 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id D1C3E442BE; Thu, 6 Feb 2025 18:15:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865720; 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=0Zj5Q6s/d5EIW0olggTNshWUfmVTZl2kWagfZZCInD8=; b=Ci/80IxI6xaxgZEI9U0V+ciUsKC54BM2vX/S4qsWvnckQ4TMQHgkiSjYgp/rLGNy19FQSv wXoKmbKfXzoK7M3NR3ijymyhxQfTh/aiCouM9zvmW5Imry/VQsRll6DM/g86V/dSf9eAG7 6kzZpU78pcQLEcLSU81zYB2/R4vJZU9x4hJ8a5wkbQ84KYWckv/berR7Q+0DWtYHrEGnD+ h5X9DLbk9xzKZsnooaz2o1JV5Yy/HnPD0rlb75mfRWDlmURJCBX8oJNJYwfV2pi6DQPnDn BdYjk5uLv0Jnpr39h0XL12xe6P5ZkVOD79kOGCVwlOOowPupSEzhWEDopmFp8w== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:31 +0100 Subject: [PATCH v6 16/26] drm/bridge: increment refcount in of_drm_find_bridge() MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-16-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedufeenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" of_drm_find_bridge() returns a pointer to a bridge, so it has to get a reference and the caller will be in charge of putting it. Callers of of_drm_find_bridge() are: 1. drm_of_panel_bridge_remove() 2. of_drm_find_bridge_by_endpoint() 3. drm_of_find_panel_or_bridge() 4. various DRM drivers Add the corresponding drm_bridge_put() call for 1 and propagate documentation to 2 and 3. Other callers (4) are to be adapted in following commits. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/bridge/panel.c | 3 +++ drivers/gpu/drm/drm_bridge.c | 7 +++++++ include/drm/drm_of.h | 1 + 3 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 1230ae50b2020e7a9306cac83009dd600dd61d26..3c0e22e61c1092de1571d800ac440aad7b5c86bc 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -363,6 +363,9 @@ EXPORT_SYMBOL(of_drm_find_panel); * return either the associated struct drm_panel or drm_bridge device. Either * @panel or @bridge must not be NULL. * + * If a bridge is returned in @bridge, the bridge refcount is + * incremented. Use drm_bridge_put() when done with the bridge. + * * This function is deprecated and should not be used in new drivers. Use * devm_drm_of_get_bridge() instead. * diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 497ec06dfcb05ab5dee8ea5e8f1eafb9c2807612..fca860d582f86b35c9172b27be20060de086e38f 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1396,6 +1396,9 @@ EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify); * * @np: device node * + * On return the bridge refcount is incremented (if the bridge is + * refcounted). Use drm_bridge_put() when done with the bridge. + * * RETURNS: * drm_bridge control struct on success, NULL on failure */ @@ -1407,6 +1410,7 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) list_for_each_entry(bridge, &bridge_list, list) { if (bridge->of_node == np) { + drm_bridge_get(bridge); mutex_unlock(&bridge_lock); return bridge; } @@ -1427,6 +1431,9 @@ EXPORT_SYMBOL(of_drm_find_bridge); * Given a DT node's port and endpoint number, find the connected node and * return the associated struct drm_bridge. * + * On success the returned @bridge refcount is incremented. Use + * drm_bridge_put() when done with the bridge. + * * Returns zero if successful, or one of the standard error codes if it fails. */ static int of_drm_find_bridge_by_endpoint(const struct device_node *np, diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 7f0256dae3f13de1d4109e9265d66684ef2a08ee..948672c27d2eb3034b2519e0bba0fcb52d5c697b 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -172,6 +172,7 @@ static inline int drm_of_panel_bridge_remove(const struct device_node *np, bridge = of_drm_find_bridge(remote); drm_panel_bridge_remove(bridge); + drm_bridge_put(bridge); return 0; #else From patchwork Thu Feb 6 18:14:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963464 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 16AC3C02199 for ; Thu, 6 Feb 2025 18:15:26 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9278E10E913; Thu, 6 Feb 2025 18:15:25 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="dcwocTIP"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8087A10E913 for ; Thu, 6 Feb 2025 18:15:24 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 9A325442D4; Thu, 6 Feb 2025 18:15:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865723; 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=dGZLluwkDCjp1kHB/F87oV4x0FXPfwe2Z3Cbt+cBMY0=; b=dcwocTIPbXtLHpayEIyBz/uxIiTZQEiHi4KaM+Mz28d/scIiEZQyjWjQzRu+kIuOEhJn71 wvsmE0NLkbQdkXP7O8e0ZEyLviqni70ms1O4TQdIRajiqg6SA/yjtWM58Kea4YGeiqUa7R owoU7X6AwiXhIk2zLrVLEozHTKUOXKXbpkdJV+NRNmRLvoW2WR36iTU2FAthzcO1z02Gz9 GXaQs1qoQswQj2h8a7KjfDzv+ZjL/7vGHtqsH47s1/gHCth/jX0+kBLr0n4oG+MV4GOq3E LMP7xMdYbBRaJ/loMAzukV/i6J4Gorer6VYYwaDMTvuI70lmfY5xTfPL8+N3TA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:32 +0100 Subject: [PATCH v6 17/26] drm/bridge: add devm_drm_put_bridge() and devm_drm_put_and_clear_bridge() MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-17-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeduheenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Bridges obtained via devm_drm_of_get_bridge() will be put when the requesting device is removed. However drivers which obtained them may need to put the obtained reference way before being destroyed, especially in case of hot-unplug of the bridge they hold a reference to. Add devm_drm_put_bridge() to manually release a devm-obtained bridge. Also add a macro to additionally clear the pointer in a safe way. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_bridge.c | 36 ++++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 6 ++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index fca860d582f86b35c9172b27be20060de086e38f..92ce40adfaa59a278a972ac862bebee06970ff83 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1548,6 +1548,42 @@ struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, } EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node); +/** + * devm_drm_put_bridge - Release a bridge reference obtained via devm + * @dev: device that got the bridge via devm + * @bridge: pointer to a struct drm_bridge obtained via devm + * + * Same as drm_bridge_put() for bridge pointers obtained via devm functions + * such as devm_drm_of_get_bridge(). + * + * See also devm_drm_put_and_clear_bridge() which is more handy in many + * cases. + */ +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) +{ + devm_release_action(dev, devm_drm_bridge_put_void, bridge); +} +EXPORT_SYMBOL(devm_drm_put_bridge); + +/** + * devm_drm_put_and_clear_bridge - Given a bridge pointer obained via devm, + * clear the pointer then put the bridge + * + * @dev: device that got the bridge via devm + * @bridge_pp: pointer to pointer to a struct drm_bridge obtained via devm + * + * Same as drm_bridge_put_and_clear() for bridge pointers obtained via devm + * functions such as devm_drm_of_get_bridge(). + */ +void devm_drm_put_and_clear_bridge(struct device *dev, struct drm_bridge **bridge_pp) +{ + struct drm_bridge *bridge = *bridge_pp; + + *bridge_pp = NULL; + devm_drm_put_bridge(dev, bridge); +} +EXPORT_SYMBOL(devm_drm_put_and_clear_bridge); + static void drmm_bridge_put_void(struct drm_device *drm, void *ptr) { struct drm_bridge *bridge = ptr; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 43cef0f6ccd36034f64ad2babfebea62db1d9e43..b6b76161a3c6bb2a4df4b3331bc152a560823fd7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1230,6 +1230,8 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node u32 port, u32 endpoint); struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev, struct device_node *bridge_node); +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge); +void devm_drm_put_and_clear_bridge(struct device *dev, struct drm_bridge **bridge_pp); struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, u32 endpoint); #else @@ -1247,6 +1249,10 @@ static inline struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *d return ERR_PTR(-ENODEV); } +static inline void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) {} +static inline void devm_drm_put_and_clear_bridge(struct device *dev, + struct drm_bridge **bridge_pp) {} + static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, From patchwork Thu Feb 6 18:14:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963465 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 2E8D7C02199 for ; Thu, 6 Feb 2025 18:15:29 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A7B0410E90C; Thu, 6 Feb 2025 18:15:28 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="Y+b0DGvB"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 54C8110E90C for ; Thu, 6 Feb 2025 18:15:27 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 6AA68442CD; Thu, 6 Feb 2025 18:15:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865726; 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=PhgEt87E7UIGCThpFWjahsK9oH1YxL7v6qfXLrIRhII=; b=Y+b0DGvBc4nAYsoODrTUxn9MyMTDEc0v4mnKcGvD3TkfbOxASNOfpU9EATuQwbJysCzcPu 9Q04YED3EIuj8i1aShXqd2h9pO+2K+eVSQs2AtzASC7k5rYSf/qSyce/l5WPdlO15jIzkM RDOJiE1D2d8bviu68z0pQJmH7LtOd/OuIPz0JigjbZKVQ18paUeu5fj7nYq9RCiXGsY3bD n6dH2aD5S0OW3O5q4LQErN9axJvxN9VZzdUdaIdKbe7E+1fT1LRo5Wv3VvCBecwpnYcaon zBq3AoP8uTK7vVQda9ELLzvG5yX1f4iahvi+xavgd19r2WF+dXeVots3+ZjiZQ== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:33 +0100 Subject: [PATCH v6 18/26] drm/bridge: add documentation of refcounted bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-18-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeduheenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Document in detail the new refcounted bridges as well as the "legacy" bridges. Signed-off-by: Luca Ceresoli --- Changes in v6: - update to the new devm_drm_bridge_alloc() API - rewrite and improve various sentences for clarity - fix typos (Randy Dunlap) This patch was added in v5. --- Documentation/gpu/drm-kms-helpers.rst | 6 ++ drivers/gpu/drm/drm_bridge.c | 118 ++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 79c8d3e63f7e06136440ed38972697b5f057d5d1..027c6ab65aa5c3848c4afab6fbc8ab93f9a285ba 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -151,6 +151,12 @@ Overview .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :doc: overview +Bridge lifecycle +---------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge.c + :doc: bridge lifecycle + Display Driver Integration -------------------------- diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 92ce40adfaa59a278a972ac862bebee06970ff83..fc44a5d227a89a12b5c3299a29776cfddb36ce27 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -62,6 +62,124 @@ * encoder chain. */ +/** + * DOC: bridge lifecycle + * + * Allocation, initialization and teardown of a bridge can be implemented + * in one of two ways: *refcounted* mode and *legacy* mode. + * + * In **refcounted** mode: + * + * - each &struct drm_bridge is reference counted since its allocation + * - any code taking a pointer to a bridge has get and put APIs to refcount + * it and so ensure the bridge won't be deallocated while there is still + * a reference to it + * - the driver implementing the bridge also holds a reference, but the + * allocated struct can survive it + * - deallocation is done when the last put happens, dropping the refcount + * to zero + * + * A bridge using refcounted mode is called a *refcounted bridge*. + * + * In **legacy** mode the &struct drm_bridge lifetime is tied to the device + * instantiating it: it is allocated on probe and freed on removal. Any + * other kernel code holding a pointer to the bridge could incur in + * use-after-free in case the bridge is deallocated at runtime. + * + * Legacy mode used to be the only one until refcounted bridges were + * introduced, hence the name. It is still fine in case the bridges are a + * fixed part of the pipeline, i.e. if the bridges are removed only when + * tearing down the entire card. Refcounted bridges support both that case + * and the case of more dynamic hardware with bridges that can be removed + * at runtime without tearing down the entire card. + * + * Usage of refcounted bridges happens in two sides: the bridge *provider* + * and the bridge *consumers*. The bridge provider is the driver + * implementing the bridge. The bridge consumers are all parts of the + * kernel taking a &struct drm_bridge pointer, including other bridges, + * encoders and the DRM core. + * + * For bridge **providers**, in both refcounted and legacy modes the common + * and expected pattern is that the bridge driver declares a + * driver-specific struct embedding a &struct drm_bridge. E.g.:: + * + * struct my_bridge { + * ... + * struct drm_bridge bridge; + * ... + * }; + * + * When using refcounted mode, the driver should allocate and initialize + * ``struct my_bridge`` using devm_drm_bridge_alloc(), as in this example:: + * + * static int my_bridge_probe(...) + * { + * struct device *dev = ...; + * struct my_bridge *mybr; + * + * mybr = devm_drm_bridge_alloc(dev, struct my_bridge, bridge, &my_bridge_funcs); + * if (IS_ERR(mybr)) + * return PTR_ERR(mybr); + * + * // Get resources, initialize my_bridge members... + * drm_bridge_add(); + * ... + * } + * + * static void my_bridge_remove() + * { + * struct my_bridge *mybr = ...; + * + * drm_bridge_remove(&mybr->bridge); + * // Free resources + * // ... NO kfree here! + * } + * + * In legacy mode, the driver can either use ``devm_`` allocation or + * equivalently free ``struct my_bridge`` in their remove function:: + * + * static int my_bridge_probe(...) + * { + * struct device *dev = ...; + * struct my_bridge *mybr; + * + * mybr = devm_kzalloc(dev, sizeof(*mybr), GFP_KERNEL); + * if (!mybr) + * return -ENOMEM; + * + * // Get resources, initialize my_bridge members... + * mybr->funcs = &my_bridge_funcs; + * drm_bridge_add(); + * ... + * } + * + * static void my_bridge_remove() + * { + * struct my_bridge *mybr = ...; + * + * drm_bridge_remove(&mybr->bridge); + * // Free resources + * // kfree(mybr) if not using devm_*() for allocation + * } + * + * Bridge **consumers** need to handle the case of a bridge being removed + * while they have a pointer to it. As this can happen at any time, such + * code can incur in use-after-free. To avoid that, consumers have to call + * drm_bridge_get() when taking a pointer and drm_bridge_put() after they + * are done using it. This will extend the allocation lifetime of the + * bridge struct until the last reference has been put, potentially a long + * time after the bridge device has been removed from the kernel. + * + * Functions that return a pointer to a bridge, such as + * of_drm_find_bridge(), internally call drm_bridge_get() on the bridge + * they are about to return, so in this case the consumers do not have to + * do it. + * + * Calling drm_bridge_get() and drm_bridge_put() on a bridge that is not + * refcounted does nothing, so code using these two APIs will work both on + * refcounted bridges and legacy bridges. + */ + /** * DOC: display driver integration * From patchwork Thu Feb 6 18:14:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963466 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 E2B63C02194 for ; Thu, 6 Feb 2025 18:15:31 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5F49B10E90E; Thu, 6 Feb 2025 18:15:31 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="IBixXvFR"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 173DA10E90E for ; Thu, 6 Feb 2025 18:15:29 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 4304344264; Thu, 6 Feb 2025 18:15:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865728; 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=EBTI6/Bcswhpw8thl+NLYiq9PLKUDYQ9vhvmnSdscmM=; b=IBixXvFRbyY/P5+MTKGjMacyLR8bGcsqgmm3KY22v1Cdo5rb6K7WZ5F8RqhTqswlBLkos/ zhyJFtk3xhdqU3oinvUwC1iBIIGjcv9cDVU0YfxcmNXBNNVMDGlCu5TzgeHCR3/Gc/8mpy yn17NVwxcEf09RzdlxcM7xZ/9LnoHtKoSw+GhgENWZhNG6E7dATtzsEAmNqt5N4ZZNYVe2 o9lPiF/vWt1BkXJTDrWU/5veT4QYSoeVE4rxlek/bUdmFYppcYqtOI6Ym4KfICaRafaRW3 f3/d2BABH6ZjtJoNFriDF2Ttdg8h89KgN+9/OpTEBuEdqf46Ts474RUtfPOHVg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:34 +0100 Subject: [PATCH v6 19/26] drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy) MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-19-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpeduheenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Add a basic KUnit test for the newly introduced drm_bridge_alloc(). Signed-off-by: Luca Ceresoli --- Changed in v6: - update to new drm_bridge_alloc() API - remove drm_test_drm_bridge_put test, not straightforward to write with the new API and the current notification mechanism - do not allocate a drm_device: a bridge is allocated without one - rename some identifiers for easier code reading This patch was added in v5. --- drivers/gpu/drm/tests/Makefile | 1 + drivers/gpu/drm/tests/drm_bridge_test.c | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 56dab563abd7a7ee7c147bd6b4927e2436b82e1d..909f98a132bb1d057b2666e8b891683ffb11cca4 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \ drm_kunit_helpers.o obj-$(CONFIG_DRM_KUNIT_TEST) += \ + drm_bridge_test.o \ drm_buddy_test.o \ drm_cmdline_parser_test.o \ drm_connector_test.o \ diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c new file mode 100644 index 0000000000000000000000000000000000000000..dc5e9260cedaa29126fd6af25a6ba2f6eee05a87 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_bridge_test.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit test for DRM bridges + */ + +#include + +#include +#include + +struct drm_bridge_test_ctx { + struct device *dev; +}; + +/* + * Mimick the typical struct defined by a bridge driver, which embeds a + * bridge plus other fields. + */ +struct dummy_drm_bridge { + int dummy; // ensure we test non-zero @bridge offset + struct drm_bridge bridge; +}; + +static const struct drm_bridge_funcs drm_bridge_dummy_funcs = { +}; + +static int drm_test_bridge_init(struct kunit *test) +{ + struct drm_bridge_test_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->dev = kunit_device_register(test, "drm-bridge-dev"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev); + + test->priv = ctx; + return 0; +} + +/* + * Test that the allocation and initialization of a bridge works as + * expected and doesn't report any error. + */ +static void drm_test_drm_bridge_alloc(struct kunit *test) +{ + struct drm_bridge_test_ctx *ctx = test->priv; + struct dummy_drm_bridge *dummy; + + dummy = devm_drm_bridge_alloc(ctx->dev, struct dummy_drm_bridge, bridge, + &drm_bridge_dummy_funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy); +} + +static struct kunit_case drm_bridge_alloc_tests[] = { + KUNIT_CASE(drm_test_drm_bridge_alloc), + { } +}; + +static struct kunit_suite drm_bridge_alloc_test_suite = { + .name = "drm_bridge_alloc", + .init = drm_test_bridge_init, + .test_cases = drm_bridge_alloc_tests, +}; + +kunit_test_suites( + &drm_bridge_alloc_test_suite, +); + +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_DESCRIPTION("Kunit test for drm_bridge functions"); +MODULE_LICENSE("GPL"); From patchwork Thu Feb 6 18:14:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963467 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 DEB16C0219B for ; Thu, 6 Feb 2025 18:15:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 612CD10E911; Thu, 6 Feb 2025 18:15:34 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="pR9z6iaM"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id C3E6C10E911 for ; Thu, 6 Feb 2025 18:15:32 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 0594443297; Thu, 6 Feb 2025 18:15:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865731; 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=W1dyvuSoKzEddJsfYldzEVfa/MmYSJUDzhZO1Mphdyc=; b=pR9z6iaMA4MmRIYCSeFn2g3o4cAlKmx6c7dA5IoxSDVWChae97WHa4QMSqirikSPUb8jsy IHkBsZEGkNMDMuQPANYstrdBMqlkQNH3I8Sd9bR49WLvrNlL26nGMKavjdRTdllvdjdPQq mEkHV+FmwisCxQX0vVETwEP6roQxsxZxbz5YoNciVTLWa7hatSw9cIEoCgVbDxWrSwB2GI Iv4su/iIbW72fctloDmbmuKXXInFumtxjj3aMJOSFpMjJaxFZDFRBl21jJ0kvbnIjHtvFo kbXrg07BtFCC/MCY5Ba/vNjQVEtYxVnbrCtBagh77SaFDoT+UZEfQT1CvrjssA== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:35 +0100 Subject: [PATCH v6 20/26] drm/debugfs: bridges_show: show refcount MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-20-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedukeenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Now that bridges are optionally refcounted, it is useful to know about that in debugfs. Suggested-by: Dmitry Baryshkov Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_debugfs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 7424d5237e7615d63de6bba572ee6050da6709d0..629074247ffec4fa18df7af2d9023255abed501c 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -743,6 +743,12 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc) static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx) { drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); + + if (drm_bridge_is_refcounted(bridge)) + drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount)); + else + drm_printf(p, "\trefcount: N/A\n"); + drm_printf(p, "\ttype: [%d] %s\n", bridge->type, drm_get_connector_type_name(bridge->type)); From patchwork Thu Feb 6 18:14:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963468 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 E85DAC02199 for ; Thu, 6 Feb 2025 18:15:37 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6071A10E915; Thu, 6 Feb 2025 18:15:37 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="AREd3kQj"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 746C110E914 for ; Thu, 6 Feb 2025 18:15:35 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id AF2A8442BE; Thu, 6 Feb 2025 18:15:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865734; 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=ZCyiZrk1wZv+6lsBh4aT/JUbtqG9ImrmSJSb0HiDc9A=; b=AREd3kQjJ5MTwvg4Dp7t6dvP4wFGZE9QXGJLQY+LDRgEiRXM1FTT8rIKcYUayo9OK1OPZW GXu667LdmHyFEYqs8ycBhfOZzN6QB96v/XKBV9zSNqqXDEHkztUyQsxt2NqALN6g6MU1MV 91LnmL9cUXLJ6tgH1r2zRWhZT7d+6g/xNVaEmC1sfOOpntSbrysfY3/42ZtJX6XqWz8INN vbgpmcFXbGf/ay46QDA7qKOLCRBJwe6WaItl7ZiIQnWWsP99GTO/KflixkdfD87igkqpXo 1UGorCaUmLK8li4s39xnfjtrsTcOjZ3iXly1Axlz9SvA5hnARHSC1CYDrSOyHg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:36 +0100 Subject: [PATCH v6 21/26] drm/bridge: add list of removed refcounted bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-21-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedukeenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Between drm_bridge_add() and drm_bridge_remove() bridges "published" to the DRM core via the global bridge_list and visible in /sys/kernel/debug/dri/bridges. However between drm_bridge_remove() and the last drm_bridge_put(), a refcounted bridge memory is still allocated even though the bridge is not "published", i.e. not in bridges_list, so it is invisible in debugfs. This prevents debugging refcounted bridges lifetime, especially leaks doe to any missing drm_bridge_put(). In order to allow debugfs to also show the removed refcounted bridges, move such bridges into a new ad-hoc list until they are freed. Note this requires adding INIT_LIST_HEAD(&bridge->list) in the bridge initialization code: the lack of such init was not exposing any bug so far, but it would with the new code. Document the new behaviour of drm_bridge_remove() and update the drm_bridge_add() documentation to stay consistent. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_bridge.c | 23 ++++++++++++++++++++--- drivers/gpu/drm/drm_internal.h | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index fc44a5d227a89a12b5c3299a29776cfddb36ce27..b315c7c4e32cc27723c69c795efe4f3313134204 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -315,9 +315,10 @@ * driver. */ -/* Protect bridge_list */ +/* Protect bridge_list and bridge_removed_list */ DEFINE_MUTEX(bridge_lock); LIST_HEAD(bridge_list); +LIST_HEAD(bridge_removed_list); /* Internal function (for refcounted bridges) */ void __drm_bridge_free(struct kref *kref) @@ -327,6 +328,10 @@ void __drm_bridge_free(struct kref *kref) DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container); + mutex_lock(&bridge_lock); + list_del(&bridge->list); + mutex_unlock(&bridge_lock); + kfree(container); } EXPORT_SYMBOL(__drm_bridge_free); @@ -355,6 +360,8 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, return ERR_PTR(-ENOMEM); bridge = container + offset; + + INIT_LIST_HEAD(&bridge->list); bridge->container_offset = offset; bridge->funcs = funcs; kref_init(&bridge->refcount); @@ -371,7 +378,10 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, EXPORT_SYMBOL(__devm_drm_bridge_alloc); /** - * drm_bridge_add - add the given bridge to the global bridge list + * drm_bridge_add - add to published bridges + * + * Adds the given bridge to the global bridge list, so it can be found by + * of_drm_find_bridge(). * * @bridge: bridge control structure */ @@ -423,7 +433,12 @@ int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge) EXPORT_SYMBOL(devm_drm_bridge_add); /** - * drm_bridge_remove - remove the given bridge from the global bridge list + * drm_bridge_remove - remove from published bridges + * + * Remove the given bridge from the global bridge list, so it won't be + * found by of_drm_find_bridge(). If the bridge is refcounted it also adds + * it to the removed bridge list, to keep track of removed bridges until + * their allocated memory is finally freed. * * @bridge: bridge control structure */ @@ -435,6 +450,8 @@ void drm_bridge_remove(struct drm_bridge *bridge) mutex_lock(&bridge_lock); list_del_init(&bridge->list); + if (drm_bridge_is_refcounted(bridge)) + list_add_tail(&bridge->list, &bridge_removed_list); mutex_unlock(&bridge_lock); list_for_each_entry_safe(br, tmp, &bridge_list, list) diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index b6e875d4b25faae6bb0bb952c3c12bd4819698ec..bbfc938b21c13bc69c36d3833f6cb6d5d22d1c54 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -51,6 +51,7 @@ struct drm_vblank_crtc; // for drm_debugfs.c extern struct mutex bridge_lock; extern struct list_head bridge_list; +extern struct list_head bridge_removed_list; /* drm_client_event.c */ #if defined(CONFIG_DRM_CLIENT) From patchwork Thu Feb 6 18:14:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963469 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 1D827C0219B for ; Thu, 6 Feb 2025 18:15:40 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9902A10E916; Thu, 6 Feb 2025 18:15:39 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="JznA0qyI"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 29D7810E916 for ; Thu, 6 Feb 2025 18:15:38 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 648AC44264; Thu, 6 Feb 2025 18:15:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865736; 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=jyt92zCCqkFkC7In83mYaBu4PIN5nSKfNfG/5T0DTUw=; b=JznA0qyIaTSfICk4caWACJbFbfNzXB1pg7S6uD3cgTrCOwAGOR2jl4hYbaD6qwNFYLQjN/ GGCvKL2OKdNYgXo8ypms6t1EC1NNA6QPCklEjsnL19iJYH0AXm0WWTl8xNNiC85x2JJVcp 1x59+fg5iQbMzBtYLad904nKO9R1wxem87yt4xMpaVM/y8jD8ki2oCT4FzIBeRepdHWSZ2 +hh+gRudoYHKq0zrujEIp6PXOlNDXDErASHdc8bbvJ47Tvnap9VPBO1UcRfqZj479G8BER uXcFAiFImmpFZ3ycDUn47hzc6H5utTKcEUj9jZUBZUnGq7PK/kz0Z9E/71WSHg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:37 +0100 Subject: [PATCH v6 22/26] drm/debugfs: show removed bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-22-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedvtdenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" The usefulness of /sys/kernel/debug/dri/bridges is limited as it only shows bridges betweeb drm_bridge_add() and drm_bridge_remove(). However refcounted bridges can stay allocated for way longer, and a memory leak due to a missing drm_bridge_put() would not be visible in this debugfs file. Add removed bridges to the /sys/kernel/debug/dri/bridges output. Now however if a bridge is added and removed multiple times (as in hot-pluggable hardware) and not freed immediately, there would be multiple identical entries for the same bridge model/driver. To distinguish between each instance add the bridge pointer to the output. Signed-off-by: Luca Ceresoli --- This patch was added in v6. --- drivers/gpu/drm/drm_debugfs.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 629074247ffec4fa18df7af2d9023255abed501c..b04e1fba8faf7320794277c2017c97216320f017 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -740,12 +740,16 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc) crtc->debugfs_entry = NULL; } -static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx) +static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx, + bool removed) { drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); + drm_printf(p, "\taddr: %p\n", bridge); + if (drm_bridge_is_refcounted(bridge)) - drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount)); + drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount), + removed ? " [removed]" : ""); else drm_printf(p, "\trefcount: N/A\n"); @@ -753,7 +757,8 @@ static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsig bridge->type, drm_get_connector_type_name(bridge->type)); - if (bridge->of_node) + /* The OF node could be freed after drm_bridge_remove() */ + if (bridge->of_node && !removed) drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node); drm_printf(p, "\tops: [0x%x]", bridge->ops); @@ -778,7 +783,7 @@ static int bridges_show(struct seq_file *m, void *data) unsigned int idx = 0; drm_for_each_bridge_in_chain(encoder, bridge) - bridge_print(&p, bridge, idx++); + bridge_print(&p, bridge, idx++, false); return 0; } @@ -822,7 +827,10 @@ static int allbridges_show(struct seq_file *m, void *data) mutex_lock(&bridge_lock); list_for_each_entry(bridge, &bridge_list, list) - bridge_print(&p, bridge, idx++); + bridge_print(&p, bridge, idx++, false); + + list_for_each_entry(bridge, &bridge_removed_list, list) + bridge_print(&p, bridge, idx++, true); mutex_unlock(&bridge_lock); From patchwork Thu Feb 6 18:14:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963470 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 BB55EC02199 for ; Thu, 6 Feb 2025 18:15:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 43DB710E918; Thu, 6 Feb 2025 18:15:43 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="kGjz6Kxs"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id DC1FC10E917 for ; Thu, 6 Feb 2025 18:15:40 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 16A5143297; Thu, 6 Feb 2025 18:15:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865739; 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=aGzraMmw+fA8wevuBLQcisstrYKOBwuJV7qXecxusco=; b=kGjz6KxsyWT27vY60CZKrslgCFbs+dKlEkuHKWDcoVNbh0Gc7ociJPjPpnqlPIrM4ez4+w gbgn7+t9jYJjDI0j6E05eqGO9e2Jf9P5t7mpaEvKsyax1U4/lJDnY44sV4PeuO52dYKcEg FTPFvTP9NgedbG7Ln014Yqq8HhCGMgjwRH17I3xqEXk0jzNPgTgivit6ioysGy7ZFfBVAx nDOA9MXl7n0rp5qxk4osl+iMPrRzvG+KR8VSd6QU1O3VJgeN3YsBZJtRFEB1OQia2iYCFP J/ZBmJQI8dpDwMBm+iJY+ngg7UMSHXSfzkPP8eEgwUmXvoZeryT86IXSoCfwoQ== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:38 +0100 Subject: [PATCH v6 23/26] drm/bridge: samsung-dsim: use refcounting for the out_bridge MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-23-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedvtdenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" In case the samsung-dsim is fixed and the following bridge is hot-unplugged, we need to handle the dynamic lifetime of the following bridge by putting the reference when disposing of it. The devm functions used to get the next bridge reference will put it only when this device is removed. Do it explicitly on detach and in the error paths. Signed-off-by: Luca Ceresoli --- Changed in v6: - use new devm_drm_put[_and_clear]_bridge() This patch was added in v5. --- drivers/gpu/drm/bridge/samsung-dsim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46..8e94f099d67bee93655129625b40d4c1af023fcc 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1732,13 +1732,13 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) { ret = samsung_dsim_register_te_irq(dsi, &device->dev); if (ret) - return ret; + goto err_devm_put_bridge; } if (pdata->host_ops && pdata->host_ops->attach) { ret = pdata->host_ops->attach(dsi, device); if (ret) - return ret; + goto err_devm_put_bridge; } dsi->lanes = device->lanes; @@ -1748,6 +1748,10 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, dsi->out_bridge = out_bridge; return 0; + +err_devm_put_bridge: + devm_drm_put_bridge(dev, out_bridge); + return ret; } static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi) @@ -1764,7 +1768,7 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host, struct samsung_dsim *dsi = host_to_dsi(host); const struct samsung_dsim_plat_data *pdata = dsi->plat_data; - dsi->out_bridge = NULL; + devm_drm_put_and_clear_bridge(dsi->dev, &dsi->out_bridge); if (pdata->host_ops && pdata->host_ops->detach) pdata->host_ops->detach(dsi, device); From patchwork Thu Feb 6 18:14:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963471 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 34AB8C02194 for ; Thu, 6 Feb 2025 18:15:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A8F2510E917; Thu, 6 Feb 2025 18:15:45 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="mXH6sR2b"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9C8CA10E919 for ; Thu, 6 Feb 2025 18:15:43 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id C8CA2442D4; Thu, 6 Feb 2025 18:15:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865742; 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=Q3HXQmUNKOGcrVx+4skU8AtvzrMrwBaFmyRg1A65z18=; b=mXH6sR2bScGZa368+p+txwchUWu0H7r3BJqTfnqYRALeDQWRzv8PuXpdP4siz0CZQTK0r6 hJyx94iTov+4FxJuGRfis1VTABZLZKbZDn2exYVjTIheSyBMuJwmCaYaYAsYvzjcCsDjOl 8qGBozPeqt9DMoElk1x2gdHwTZUOR0/JdwVzIBqn+2NAc1+Sl8kJZUSsiz90uS1BkabjPx i9x21Nl7VvQro4uN6WL9phl55P19LnTeKL8X/YiGQpjv94vXUkpswNwFz63uSnRVSCLLkI nJslDd/3s4YezSYQWr4pj2kN5vaSKD8QfjjiqWDzZAKO5MFhQlg4zsj8pkidpg== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:39 +0100 Subject: [PATCH v6 24/26] drm/bridge: panel: use dynamic lifetime management MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-24-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedvvdenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Allow this bridge to be removable without dangling pointers and use-after-free, together with proper use of drm_bridge_get() and _put() by consumers. Signed-off-by: Luca Ceresoli --- Changed in v6: - Update to use devm_drm_bridge_alloc(), remove .destroy - Update the *_of_get_bridge() functions to put instead of kfree on devm/drmm events This patch was added in v5. --- drivers/gpu/drm/bridge/panel.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 3c0e22e61c1092de1571d800ac440aad7b5c86bc..5950c752ff2469f7e5c28f9d5833141551b98227 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -912,15 +912,14 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, if (!panel) return ERR_PTR(-EINVAL); - panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), - GFP_KERNEL); - if (!panel_bridge) - return ERR_PTR(-ENOMEM); + panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge, + &panel_bridge_bridge_funcs); + if (IS_ERR(panel_bridge)) + return (void *)panel_bridge; panel_bridge->connector_type = connector_type; panel_bridge->panel = panel; - panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; panel_bridge->bridge.of_node = panel->dev->of_node; panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; panel_bridge->bridge.type = connector_type; @@ -939,8 +938,6 @@ EXPORT_SYMBOL(drm_panel_bridge_add_typed); */ void drm_panel_bridge_remove(struct drm_bridge *bridge) { - struct panel_bridge *panel_bridge; - if (!bridge) return; @@ -949,10 +946,7 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) return; } - panel_bridge = drm_bridge_to_panel_bridge(bridge); - drm_bridge_remove(bridge); - devm_kfree(panel_bridge->panel->dev, bridge); } EXPORT_SYMBOL(drm_panel_bridge_remove); From patchwork Thu Feb 6 18:14:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963472 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 9DF34C0219B for ; Thu, 6 Feb 2025 18:15:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1B9FE10E919; Thu, 6 Feb 2025 18:15:48 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="h3+fI3ZX"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 589DC10E919 for ; Thu, 6 Feb 2025 18:15:46 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 8CCD9442D7; Thu, 6 Feb 2025 18:15:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865745; 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=idqS613Xm08a2H6cIuqtamLIUouR/CqrkF7y3uWXZyU=; b=h3+fI3ZXzSWbrMkmro0quY9ipebZgI8r1JPnhGPltxKVhkP8zFlLGKqyldYPSRg5WeDImb CyWhoMn1L/BNBG7c60F4vMBtXR7GsbD15AYe4cIPQSIqWW4dT+Rs9NTn2pZaYoh0w0hTee fRGFMVZYhy7kMg2nOgDzK/FlVVfJLAL4f31hDmUBiCcH/xK+j0TWZhH9CArKhb9D8bGKLf O9gBnZCG5AxtNH7uEnyZTg0TsxLFoPcx65/oCQeqJsPh50otpi1nSYvXQFKH7OGmY5U/cd uvZHkl96TTe2jdUiXs4bj2pb0U1s4X9WlPZ1QIP5NC/FtyyueczB9c0IvikE3Q== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:40 +0100 Subject: [PATCH v6 25/26] drm/bridge: ti-sn65dsi83: use dynamic lifetime management MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-25-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnhepieeiuedvffetgfeuudelheeutefggfejieettdetteekueeuueeukeevvedvueevnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedvvdenucfrrghrrghmpehinhgvthepvdgrtddvmeeijedtmedvtddvtdemvggrtddumeehrgegtdemvdgufheimegrudelvgemudgtjeegpdhhvghloheplgduvdejrddtrddurddungdpmhgrihhlfhhrohhmpehluhgtrgdrtggvrhgvshholhhisegsohhothhlihhnrdgtohhmpdhnsggprhgtphhtthhopeefkedprhgtphhtthhopegtrghtrghlihhnrdhmrghrihhnrghssegrrhhmrdgtohhmpdhrtghpthhtohepshdrhhgruhgvrhesphgvnhhguhhtrhhonhhigidruggvpdhrtghpthhtoheprghlvgigrghnughrvgdrsggvlhhlohhnihessghoohhtlhhinhdrtghom hdprhgtphhtthhopegtlhgruhguihhurdgsvgiinhgvrgesthhugihonhdruggvvhdprhgtphhtthhopehmrdhsiiihphhrohifshhkihesshgrmhhsuhhnghdrtghomhdprhgtphhtthhopegtohhrsggvtheslhifnhdrnhgvthdprhgtphhtthhopehsihhmohhnrgesfhhffihllhdrtghhpdhrtghpthhtohepughrihdquggvvhgvlheslhhishhtshdrfhhrvggvuggvshhkthhophdrohhrgh X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Allow this bridge to be removable without dangling pointers and use-after-free, together with proper use of drm_bridge_get() and _put() by consumers. Signed-off-by: Luca Ceresoli --- Changed in v6: - Update to use devm_drm_bridge_alloc(), remove .destroy This patch was added in v5. --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index e683b62d340564978bda37698f2cb1bffd961df5..14e991e999db857725f4c46de3e4c4e4861c3c8a 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -941,9 +941,9 @@ static int sn65dsi83_probe(struct i2c_client *client) struct sn65dsi83 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct sn65dsi83, bridge, &sn65dsi83_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; INIT_WORK(&ctx->reset_work, sn65dsi83_reset_work); @@ -983,7 +983,6 @@ static int sn65dsi83_probe(struct i2c_client *client) dev_set_drvdata(dev, ctx); i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = &sn65dsi83_funcs; ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; drm_bridge_add(&ctx->bridge); From patchwork Thu Feb 6 18:14:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 13963473 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 3BC6AC02194 for ; Thu, 6 Feb 2025 18:15:52 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id AF45F10E91A; Thu, 6 Feb 2025 18:15:51 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.b="YimDnbJS"; dkim-atps=neutral Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 4291510E91A for ; Thu, 6 Feb 2025 18:15:49 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 41A3D43297; Thu, 6 Feb 2025 18:15:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1738865748; 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=uBfiLtFh0wan0anUirVMv2KVTez36OIDHAf1JqafqA8=; b=YimDnbJSdT8tEJ4UCzUi+Ix87kp0yelbhOF5WHznixV7QMIiIBN0+WOTfKWPJq0foiYiJA 16j0CKFzJbqzn7/ZwOCvJdjpd7GM71mGcFJiD7fBY5+Jl4RoaMXRDvr38U9XhjHW6jismn YF3xfJ97ZOrfbwpKq7bceTpDtxoBy46cwZu4s1dyCb9gQtDgMJelFDf+aKFFo+sDnxnh+6 kBeMpZgeca7tLKk0fdywYeobPQe2oBhVYxko5BAKpFgJCwF72/D61ZQnZYSobmi3LfSXUa bzuO86rk24MoeDlaoSdyyHrex4UPzKFv2Z6N2WsJaTGVSTKHQunclwcPczR6HQ== From: Luca Ceresoli Date: Thu, 06 Feb 2025 19:14:41 +0100 Subject: [PATCH v6 26/26] drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges MIME-Version: 1.0 Message-Id: <20250206-hotplug-drm-bridge-v6-26-9d6f2c9c3058@bootlin.com> References: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> In-Reply-To: <20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com> To: Simona Vetter , Inki Dae , Jagan Teki , Marek Szyprowski , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Daniel Thompson , Andrzej Hajda , Jonathan Corbet , Sam Ravnborg , Boris Brezillon , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jessica Zhang Cc: Paul Kocialkowski , Maxime Ripard , Dmitry Baryshkov , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Thomas Zimmermann , David Airlie , =?utf-8?q?Herv=C3=A9_Codina?= , Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Paul Kocialkowski , Luca Ceresoli X-Mailer: b4 0.14.2 X-GND-State: clean X-GND-Score: -100 X-GND-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedtkecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfitefpfffkpdcuggftfghnshhusghstghrihgsvgenuceurghilhhouhhtmecufedtudenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujfgurhephfffufggtgfgkfhfjgfvvefosehtjeertdertdejnecuhfhrohhmpefnuhgtrgcuvegvrhgvshholhhiuceolhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomheqnecuggftrfgrthhtvghrnheptdekfeekgfejieeggfdvgfejieekjeeuuedttddugfelgfegtddufeekffeljeeknecuffhomhgrihhnpehfrhgvvgguvghskhhtohhprdhorhhgnecukfhppedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpedvrgdtvdemieejtdemvddtvddtmegvrgdtudemhegrgedtmedvughfieemrgdulegvmedutgejgedphhgvlhhopegluddvjedrtddruddrudgnpdhmrghilhhfrhhomheplhhutggrrdgtvghrvghsohhlihessghoohhtlhhinhdrtghomhdpnhgspghrtghpthhtohepfeekpdhrtghpthhtoheptggrthgrlhhinhdrmhgrrhhinhgrshesrghrmhdrtghomhdprhgtphhtthhopehsrdhhrghuvghrsehpvghnghhuthhrohhnihigrdguvgdprhgtphhtthhopegrlhgvgigrn hgurhgvrdgsvghllhhonhhisegsohhothhlihhnrdgtohhmpdhrtghpthhtoheptghlrghuughiuhdrsggviihnvggrsehtuhigohhnrdguvghvpdhrtghpthhtohepmhdrshiihihprhhofihskhhisehsrghmshhunhhgrdgtohhmpdhrtghpthhtoheptghorhgsvghtsehlfihnrdhnvghtpdhrtghpthhtohepshhimhhonhgrsehffhiflhhlrdgthhdprhgtphhtthhopegurhhiqdguvghvvghlsehlihhsthhsrdhfrhgvvgguvghskhhtohhprdhorhhg X-GND-Sasl: luca.ceresoli@bootlin.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" This driver implements the physical connector on a video pipeline allowing to remove of all the following bridges up to the panel. The DRM subsystem currently allows hotplug of the monitor but not preceding components. However there are embedded devices where the "tail" of the DRM pipeline, including one or more bridges, can be physically removed: .------------------------. | DISPLAY CONTROLLER | | .---------. .------. | | | ENCODER |<--| CRTC | | | '---------' '------' | '------|-----------------' | | HOTPLUG V CONNECTOR .---------. .--. .-. .---------. .-------. | 0 to N | | _| _| | | 1 to N | | | | BRIDGES |--DSI-->||_ |_ |--DSI-->| BRIDGES |--LVDS-->| PANEL | | | | | | | | | | | '---------' '--' '-' '---------' '-------' [--- fixed components --] [----------- removable add-on -----------] This driver supports such a device, where the final segment of a MIPI DSI bus, including one or more bridges, can be physically disconnected and reconnected at runtime, possibly with a different model. The add-on supported by this driver has a MIPI DSI bus traversing the hotplug connector and a DSI to LVDS bridge and an LVDS panel on the add-on. Hovever this driver is designed to be as far as possible generic and extendable to other busses that have no native hotplug and model ID discovery. This driver does not itself add and remove the bridges or panel on the add-on: this needs to be done by other means, e.g. device tree overlay runtime insertion and removal. The hotplug-bridge gets notified by the DRM bridge core after a removable bridge gets added or before it is removed. The hotplug-bridge role is to implement the "hot-pluggable connector" in the bridge chain. In this position, what the hotplug-bridge should ideally do is: * communicate with the previous component (bridge or encoder) so that it believes it always has a connected bridge following it and the DRM card is always present * be notified of the addition and removal of the following bridge and attach/detach to/from it * communicate with the following bridge so that it will attach and detach using the normal procedure (as if the entire pipeline were being created or destroyed, not only the tail) * add/remove an LVDS connector on hot(un)plug, similarly to what the DisplayPort MST code does However some aspects make it a bit more complex than that. Most notably: * the next bridge can be probed and removed at any moment and all probing sequences need to be handled * the DSI host/device registration process, which adds to the DRM bridge attach process, makes the initial card registration tricky * the need to register and deregister the following bridges at runtime without tearing down the whole DRM card prevents using some of the functions that are normally recommended * the automatic mechanism to call the appropriate .get_modes operation (typically provided by the panel bridge) cannot work as the panel can disappear and reappear as a different model, so an ad-hoc lookup is needed The code handling these and other tricky aspects is accurately documented by comments in the code. Additionally this driver is adding a DSI connector representing the video lines of the hotplug connector; the status is always "disconnected" (no panel is ever attached directly to it). This is supposed to disappear but it is still present for now. For this reason the userspace representation when the add-on is not connected is: # modetest -c | grep -i '^[a-z0-9]' Connectors: id encoder status name size (mm) modes encoders 38 0 disconnected DSI-1 0x0 0 37 And when it is connected, when the new connector appears, it becomes: # modetest -c | grep -i '^[a-z0-9]' Connectors: id encoder status name size (mm) modes encoders 38 0 disconnected DSI-1 0x0 0 37 39 0 connected LVDS-1 344x194 1 37 Co-developed-by: Paul Kocialkowski Signed-off-by: Paul Kocialkowski Signed-off-by: Luca Ceresoli --- *NOTE* In the grand plan, the "DSI-1" connector is not going to be present and this driver might be removed entirely. Changed in v6: - update to use devm_drm_bridge_alloc(), remove .destroy - use the devm APIs to get/put the next bridge pointer, remove open-coded search code - stop managing a panel bridge: the following component is not necessarily a panel Changed in v5: - use drm_bridge dynamic lifetime management - refcount bridge_modes and next_bridge via drm_bridge_get()/drm_bridge_put() - fix dynconn removal by using drm_connector_put() and making the .destroy callback work - add hotplug_bridge_grab() in hotplug_bridge_attach() to handle the case where the encoder driver is probed last - migrate platform driver from .remove_new to .remove Changed in v4: - convert from generic notifiers to the new bridge_event_notify bridge func - improved and updated commit message, adding 'modetest' output with/without add-on - select DRM_DISPLAY_HELPER and DRM_BRIDGE_CONNECTOR, required after commit 9da7ec9b19d8 ("drm/bridge-connector: move to DRM_DISPLAY_HELPER module") Changed in v3: - dynamically add/remove the LVDS connector on hot(un)plug - take the firmware node normally via dev->of_node instead of using device_set_node(); this makes code more self-contained and generic - minor rewordings and cleanups Changed in v2: - change to be a platform device instantiated from the connector driver instead of a self-standing OF driver - add missing error handling for devm_drm_bridge_add() - various cleanups and style improvements - fix typo in comment --- MAINTAINERS | 5 + drivers/gpu/drm/bridge/Kconfig | 17 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/hotplug-bridge.c | 666 ++++++++++++++++++++++++++++++++ 4 files changed, 689 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 090153a4f40aa3ec4982c85c809a97dca0396ab8..992e1421cd2450b81dbc0d436fbddf4d4af97e0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7268,6 +7268,11 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml F: drivers/gpu/drm/panel/panel-himax-hx8394.c +DRM DRIVER FOR HOTPLUG VIDEO CONNECTOR BRIDGE +M: Luca Ceresoli +S: Maintained +F: drivers/gpu/drm/bridge/hotplug-bridge.c + DRM DRIVER FOR HX8357D PANELS S: Orphan T: git https://gitlab.freedesktop.org/drm/misc/kernel.git diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 393214e6ed6656674d2ffbfc36d45541253bc31d..98436ab7a9da50fb09ded823b19cb0c8f4d4e484 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -90,6 +90,23 @@ config DRM_FSL_LDB help Support for i.MX8MP DPI-to-LVDS on-SoC encoder. +config DRM_HOTPLUG_BRIDGE + tristate "Hotplug DRM bridge support" + depends on OF + select DRM_PANEL_BRIDGE + select DRM_MIPI_DSI + select DRM_KMS_HELPER + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + help + Driver for a DRM bridge representing a physical connector that + splits a DRM pipeline into a fixed part and a physically + removable part. The fixed part includes up to the encoder and + zero or more bridges. The removable part includes any following + bridges up to the connector and panel and can be physically + removed and connected at runtime, possibly with different + components. + config DRM_ITE_IT6263 tristate "ITE IT6263 LVDS/HDMI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 97304b429a530c108dcbff906965cda091b0a7a2..2f6ae1a97d15045316ee191c04dbc65650162bab 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o +obj-$(CONFIG_DRM_HOTPLUG_BRIDGE) += hotplug-bridge.o obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o diff --git a/drivers/gpu/drm/bridge/hotplug-bridge.c b/drivers/gpu/drm/bridge/hotplug-bridge.c new file mode 100644 index 0000000000000000000000000000000000000000..0912397dabb90605d219d7aa59251d39c846d0cb --- /dev/null +++ b/drivers/gpu/drm/bridge/hotplug-bridge.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A DRM bridge representing the split point between a fixed part of the + * DRM pipeline and a physically removable part. The fixed part includes up + * to the encoder and zero or more bridges. Insertion and removal of the + * "downstream" components happens via device driver probe/removal. + * + * Copyright (C) 2024, GE HealthCare + * + * Authors: + * Luca Ceresoli + * Paul Kocialkowski + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Internal hotplug-bridge data. + * + * We have two 'struct drm_connector' here: + * - fixconn represents the DSI video lines on the hotplug connector where + * the removable part attaches. It is thus always instantiated. + * - dynconn represents the LVDS video lines where the panel is attached. + * It is part of the removable part of the video pipeline and as such is + * added and removed dynamically based on when the downstream devices + * appear and disappear. + */ +struct hotplug_bridge { + struct device *dev; + + /* Local bridge */ + struct drm_bridge bridge; + + /* Always-present connector (where the removal part will connect to) */ + struct drm_connector *fixconn; + + /* Downstream bridge (next in the chain) */ + struct drm_bridge *next_bridge; + /* Protect next_bridge */ + struct mutex next_bridge_mutex; + + /* Pointer to the last bridge exposing OP_MODES */ + struct drm_bridge *bridge_modes; + + /* The "tail" connector that gets added/removed at runtime */ + struct drm_connector dynconn; + + /* Local DSI host, for the downstream DSI device to attach to */ + struct mipi_dsi_host dsi_host; + /* Local DSI device, attached to the upstream DSI host */ + struct mipi_dsi_device *dsi_dev; + /* Upstream DSI host (the actual DSI controller) */ + struct mipi_dsi_host *prev_dsi_host; + + struct work_struct hpd_work; +}; + +static struct hotplug_bridge *hotplug_bridge_from_drm_bridge(struct drm_bridge *bridge) +{ + return container_of(bridge, struct hotplug_bridge, bridge); +} + +/* -------------------------------------------------------------------------- + * dynconn implementation + */ +static struct hotplug_bridge *hotplug_bridge_from_dynconn(struct drm_connector *conn) +{ + return container_of(conn, struct hotplug_bridge, dynconn); +} + +static int hotplug_bridge_dynconn_get_modes(struct drm_connector *connector) +{ + struct hotplug_bridge *hpb = hotplug_bridge_from_dynconn(connector); + + if (hpb->bridge_modes) + return hpb->bridge_modes->funcs->get_modes(hpb->bridge_modes, connector); + + return 0; +} + +static const struct drm_connector_helper_funcs hotplug_bridge_dynconn_connector_helper_funcs = { + .get_modes = hotplug_bridge_dynconn_get_modes, +}; + +static void hotplug_bridge_dynconn_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs dynconn_funcs = { + .destroy = hotplug_bridge_dynconn_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .fill_modes = drm_helper_probe_single_connector_modes, +}; + +/* + * In non-removable pipelines using a "bridge connector", + * drm_bridge_connector_init() stores in the bridge_connector a pointer to + * the last bridge having OP_MODES (typically the panel bridge), so the + * .get_modes op will automatically be called on that bridge (it also takes + * pointers to other bridges which we don't care about). The "bridge + * connector" is too restrictive for our use case so we cannot use it. But + * we need a pointer to the modes-providing bridge, so we need to replicate + * that bit of its logic. + * + * If no modes bridge is found, nothing is done. This is allowed. + * + * Also get the modes bridge to tie its lifetime to ours. + */ +static void hotplug_bridge_dynconn_bridge_modes_get(struct hotplug_bridge *hpb) +{ + struct drm_bridge *bridge; + + if (WARN_ON(!hpb->next_bridge || !hpb->bridge.encoder)) + return; + + drm_for_each_bridge_in_chain(hpb->bridge.encoder, bridge) + if (bridge->ops & DRM_BRIDGE_OP_MODES) { + drm_bridge_get(bridge); + hpb->bridge_modes = bridge; + } +} + +static void hotplug_bridge_dynconn_bridge_modes_put(struct hotplug_bridge *hpb) +{ + if (hpb->bridge_modes) + drm_bridge_put_and_clear(&hpb->bridge_modes); +} + +static int hotplug_bridge_dynconn_add(struct hotplug_bridge *hpb) +{ + int err; + + err = drm_connector_init(hpb->bridge.dev, &hpb->dynconn, &dynconn_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (err) + return err; + + drm_atomic_helper_connector_reset(&hpb->dynconn); + + drm_connector_helper_add(&hpb->dynconn, + &hotplug_bridge_dynconn_connector_helper_funcs); + + drm_connector_attach_encoder(&hpb->dynconn, hpb->bridge.encoder); + if (err) + goto err_cleanup; + + hotplug_bridge_dynconn_bridge_modes_get(hpb); + + err = drm_connector_register(&hpb->dynconn); + if (err) + goto err_cleanup; + + return 0; + +err_cleanup: + drm_connector_cleanup(&hpb->dynconn); + hotplug_bridge_dynconn_bridge_modes_put(hpb); + return err; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Attach the remote bridge to the encoder and to the next bridge in the + * chain, if possible. For this to succeed, we need to know: + * + * - the encoder, which is set at the first drm_bridge_attach() time + * - the next bridge, which is obtained via a notifier whenever the next + * bridge is (re)probed, or at probe time in case it was probed before us + * + * In order to handle different execution sequences, this function can be + * called from multiple places and needs to check all the prerequisites + * every time, and it will act only if both are met. + * + * Must be called with hpb->next_bridge_mutex held. + * + * Returns 0 if the encoder was attached successfully, -ENODEV if any of + * the two prerequisites above is not met (no encoder or no next bridge), + * the error returned by drm_bridge_attach() otherwise. + */ +static int hotplug_bridge_attach_to_encoder_chain(struct hotplug_bridge *hpb) +{ + int ret; + + if (!hpb->next_bridge || !hpb->bridge.encoder) + return -ENODEV; + + ret = drm_bridge_attach(hpb->bridge.encoder, hpb->next_bridge, &hpb->bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + return dev_err_probe(hpb->dev, ret, "drm_bridge_attach failed\n"); + + dev_dbg(hpb->dev, "attached to encoder chain\n"); + + return 0; +} + +/* + * Stop the video pipeline and detach next_bridge. + * + * Must be called with hpb->next_bridge_mutex held. + */ +static void hotplug_bridge_detach_from_encoder_chain(struct hotplug_bridge *hpb) +{ + WARN_ON_ONCE(!hpb->next_bridge); + + dev_dbg(hpb->dev, "detaching from encoder chain\n"); + + drm_atomic_helper_shutdown(hpb->bridge.dev); + + drm_encoder_cleanup_from(hpb->bridge.encoder, hpb->next_bridge); +} + +static void hotplug_bridge_grab(struct hotplug_bridge *hpb) +{ + struct device *dev = hpb->dev; + struct drm_bridge *bridge; + int err; + + mutex_lock(&hpb->next_bridge_mutex); + + if (hpb->next_bridge) + goto out_unlock; + + bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(bridge)) + goto out_unlock; + + hpb->next_bridge = bridge; + + dev_dbg(dev, "grabbed next bridge (%pOFn)\n", hpb->next_bridge->of_node); + + hpb->bridge.pre_enable_prev_first = hpb->next_bridge->pre_enable_prev_first; + + err = hotplug_bridge_attach_to_encoder_chain(hpb); + if (err) + goto err_panel_bridge_remove; + + err = hotplug_bridge_dynconn_add(hpb); + if (err) + goto err_detach_from_encoder_chain; + + queue_work(system_wq, &hpb->hpd_work); + goto out_unlock; + +err_detach_from_encoder_chain: + hotplug_bridge_detach_from_encoder_chain(hpb); +err_panel_bridge_remove: + devm_drm_put_and_clear_bridge(dev, &hpb->next_bridge); +out_unlock: + mutex_unlock(&hpb->next_bridge_mutex); +} + +/* + * Detach from the next bridge and remove the panel bridge, either on + * release or when the downstream bridge is being removed. + * + * Can be called in these ways: + * + * - bridge_being_removed is NULL: detach unconditionally + * (this is useful on .remove() to teardown everything) + * - bridge_being_removed == hpb->next_bridge: detach + * (the downstream bridge is being removed) + * - bridge_being_removed != hpb->next_bridge: do nothing + * (the bridge being removed is not the downstream bridge) + * + * In all cases, does nothing when there is no downstream bridge. + */ +static void hotplug_bridge_release(struct hotplug_bridge *hpb, + struct drm_bridge *bridge_being_removed) +{ + struct device *dev = hpb->dev; + + mutex_lock(&hpb->next_bridge_mutex); + + if (!hpb->next_bridge) + goto out; + + if (bridge_being_removed && bridge_being_removed != hpb->next_bridge) + goto out; + + if (hpb->bridge_modes) + hotplug_bridge_dynconn_bridge_modes_put(hpb); + + dev_dbg(dev, "releasing next bridge (%pOFn)\n", hpb->next_bridge->of_node); + hotplug_bridge_detach_from_encoder_chain(hpb); + + dev_dbg(dev, "removing %s connector\n", hpb->dynconn.name); + drm_connector_put(&hpb->dynconn); + + devm_drm_put_and_clear_bridge(dev, &hpb->next_bridge); + + queue_work(system_wq, &hpb->hpd_work); + +out: + mutex_unlock(&hpb->next_bridge_mutex); +} + +static void hotplug_bridge_bridge_event_notify(struct drm_bridge *bridge, + enum drm_bridge_event_type event, + struct drm_bridge *event_bridge) +{ + struct hotplug_bridge *hpb = container_of(bridge, struct hotplug_bridge, bridge); + + switch (event) { + case DRM_EVENT_BRIDGE_ADD: + hotplug_bridge_grab(hpb); + break; + case DRM_EVENT_BRIDGE_REMOVE: + hotplug_bridge_release(hpb, event_bridge); + break; + } +} + +static int hotplug_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct hotplug_bridge *hpb = hotplug_bridge_from_drm_bridge(bridge); + struct device *dev = hpb->dev; + struct drm_connector *connector; + struct drm_encoder *encoder = hpb->bridge.encoder; + int err; + + /* Encoder was not yet provided to our bridge */ + if (!encoder) + return -ENODEV; + + /* Connector was already created */ + if (hpb->fixconn) + return dev_err_probe(dev, -EBUSY, "connector already created\n"); + + connector = drm_bridge_connector_init(bridge->dev, encoder); + if (IS_ERR(connector)) + return dev_err_probe(dev, PTR_ERR(connector), "failed to initialize connector\n"); + + drm_connector_attach_encoder(connector, encoder); + + hpb->fixconn = connector; + + drm_connector_register(connector); + + mutex_lock(&hpb->next_bridge_mutex); + err = hotplug_bridge_attach_to_encoder_chain(hpb); + mutex_unlock(&hpb->next_bridge_mutex); + + /* -ENODEV is acceptable, in case next_bridge is not yet known */ + if (err == -ENODEV) + err = 0; + + /* + * If the encoder driver is probed last, the + * hotplug_bridge_attach_to_encoder_chain() call in + * hotplug_bridge_grab() fails because hpb->bridge.encoder is still + * NULL, and hotplug_bridge_grab() will not have another chance to + * execute. So call it now, at the end of the encoder attach + * process. + */ + hotplug_bridge_grab(hpb); + + return err; +} + +static void hotplug_bridge_detach(struct drm_bridge *bridge) +{ + struct hotplug_bridge *hpb = hotplug_bridge_from_drm_bridge(bridge); + + mutex_lock(&hpb->next_bridge_mutex); + hotplug_bridge_detach_from_encoder_chain(hpb); + mutex_unlock(&hpb->next_bridge_mutex); + + if (hpb->fixconn) { + drm_connector_unregister(hpb->fixconn); + drm_connector_cleanup(hpb->fixconn); + hpb->fixconn = NULL; + } +} + +/* + * The fixed connector is never attached to a panel, so it should always be + * reported as disconnected. + */ +static enum drm_connector_status hotplug_bridge_detect(struct drm_bridge *bridge) +{ + return connector_status_disconnected; +} + +static void hotplug_bridge_hpd_work_func(struct work_struct *work) +{ + struct hotplug_bridge *hpb = container_of(work, struct hotplug_bridge, hpd_work); + + if (hpb->bridge.dev) + drm_helper_hpd_irq_event(hpb->bridge.dev); +} + +static const struct drm_bridge_funcs hotplug_bridge_funcs = { + .attach = hotplug_bridge_attach, + .detach = hotplug_bridge_detach, + .detect = hotplug_bridge_detect, + .bridge_event_notify = hotplug_bridge_bridge_event_notify, +}; + +static int hotplug_bridge_dsi_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device_remote) +{ + struct hotplug_bridge *hpb = dev_get_drvdata(host->dev); + + if (!hpb->dsi_dev) + return -ENODEV; + + mipi_dsi_detach(hpb->dsi_dev); + mipi_dsi_device_unregister(hpb->dsi_dev); + hpb->dsi_dev = NULL; + + return 0; +} + +/* + * Attach the local DSI device to the upstream DSI host, possibly with a + * "null" format. + * + * In "normal" bridges this function should be _only_ used as the .attach + * callback of hotplug_bridge_dsi_ops. But "normal" bridges have their + * downstream DSI device always connected, which we don't. When booting + * without anything connected downstream, our upstream bridge could be not + * even calling drm_bridge_add() until we do attach ourselves as a DSI + * device, preventing the whole DRM card from being instantiated. + * + * In order to always have a DRM card after boot, we do call this same + * function while probing in order to attach as a DSI device to the DSI + * master. However during probe we don't know the bus format yet. It would + * be nice to be able to update the format afterwards when a downstream DSI + * device is attaching to our local host, but there is no callback for + * that. To overcome this limitation, this function can be called in two + * ways: + * + * - during probe, to make the upstream bridge happy, when there is no + * next_dsi_dev yet and thus the lanes/format/etc are unknown + * - as the mipi_dsi_host_ops.attach callback proper, as soon as the + * next_dsi_dev is known + * + * The resulting call sequence is: + * + * 1. hotplug_bridge_dsi_attach() called by hotplug_bridge_probe() with + * next_dsi_dev == NULL: we attach to the host but with a fake format + * so the DRM card can be populated. hpb->dsi_dev becomes non-NULL. + * 2. hotplug_bridge_dsi_attach() called as .attach callback from a + * downstream device when it becomes available: we need to detach in + * order to re-attach with the format of the device. hpb->dsi_dev + * is found non-NULL, then reused so it will be non-NULL again. + * 3. hotplug_bridge_dsi_detach() called as the .detach callback by a + * downstream device: cleans up everything normally. hpb->dsi_dev goes + * from non-NULL to NULL. + * 4. hotplug_bridge_dsi_attach() called by a downstream device: attaches + * normally to the upstream DSI host. hpb->dsi_dev goes from NULL to + * non-NULL. + * + * Steps 3 and 4 are the "normal" attach/detach steps as on "normal" + * bridges. + * + * Steps 1 and 2 happen only the first time, steps 3 and 4 will happen + * every time the downstream bridge disconnects and reconnects. + */ +static int hotplug_bridge_dsi_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *next_dsi_dev) +{ + struct device *dev = host->dev; + struct hotplug_bridge *hpb = dev_get_drvdata(dev); + struct mipi_dsi_device *dsi_dev; + const struct mipi_dsi_device_info dsi_info = { + .type = "hotplug-bridge", + .channel = 0, + .node = NULL, + }; + int err; + + /* + * Step 2 only (first time we are called for an actual device + * attaching): clean up the fake attach done at step 1 + */ + if (hpb->dsi_dev) + hotplug_bridge_dsi_detach(&hpb->dsi_host, NULL); + + /* Register a local DSI device with the remote DSI host */ + dsi_dev = mipi_dsi_device_register_full(hpb->prev_dsi_host, + &dsi_info); + if (IS_ERR(dsi_dev)) + return PTR_ERR(dsi_dev); + + /* At step 1 we have no downstream device to get the format from */ + if (next_dsi_dev) { + dsi_dev->channel = next_dsi_dev->channel; + dsi_dev->lanes = next_dsi_dev->lanes; + dsi_dev->format = next_dsi_dev->format; + dsi_dev->mode_flags = next_dsi_dev->mode_flags; + } + + /* Attach our local DSI device to the remote DSI host */ + err = mipi_dsi_attach(dsi_dev); + if (err) { + mipi_dsi_device_unregister(dsi_dev); + return dev_err_probe(dev, err, "failed to attach hotplug dsi device to host\n"); + } + + hpb->dsi_dev = dsi_dev; + + return 0; +} + +/* + * Propagate mipi_dsi_device_transfer() to the upstream DSI host. + * + * Reimplements identically the minimal needed part of + * mipi_dsi_device_transfer(), including the -ENOSYS return value. + */ +static ssize_t hotplug_bridge_dsi_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct hotplug_bridge *hpb = dev_get_drvdata(host->dev); + const struct mipi_dsi_host_ops *ops; + + if (!hpb->dsi_dev) + return -ENODEV; + + ops = hpb->dsi_dev->host->ops; + + if (!ops || !ops->transfer) + return -ENOSYS; + + return ops->transfer(hpb->dsi_dev->host, msg); +} + +static const struct mipi_dsi_host_ops hotplug_bridge_dsi_ops = { + .attach = hotplug_bridge_dsi_attach, + .detach = hotplug_bridge_dsi_detach, + .transfer = hotplug_bridge_dsi_transfer, +}; + +/* + * Find the upstream DSI host and register our downstream-facing DSI host. + */ +static int hotplug_bridge_dsi_setup(struct hotplug_bridge *hpb) +{ + struct device *dev = hpb->dev; + struct device_node *endpoint; + struct device_node *node; + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); + node = of_graph_get_remote_port_parent(endpoint); + + hpb->prev_dsi_host = of_find_mipi_dsi_host_by_node(node); + + of_node_put(node); + of_node_put(endpoint); + + if (!hpb->prev_dsi_host) + return -EPROBE_DEFER; + + hpb->dsi_host.dev = dev; + hpb->dsi_host.ops = &hotplug_bridge_dsi_ops; + + return mipi_dsi_host_register(&hpb->dsi_host); +} + +static void hotplug_bridge_dsi_cleanup(struct hotplug_bridge *hpb) +{ + mipi_dsi_host_unregister(&hpb->dsi_host); +} + +static int hotplug_bridge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hotplug_bridge *hpb; + struct drm_bridge *bridge; + int err; + + hpb = devm_drm_bridge_alloc(dev, struct hotplug_bridge, bridge, &hotplug_bridge_funcs); + if (IS_ERR(hpb)) + return PTR_ERR(hpb); + + hpb->dev = dev; + + mutex_init(&hpb->next_bridge_mutex); + INIT_WORK(&hpb->hpd_work, hotplug_bridge_hpd_work_func); + + err = hotplug_bridge_dsi_setup(hpb); + if (err) + return dev_err_probe(dev, err, "failed to setup DSI\n"); + + bridge = &hpb->bridge; + bridge->of_node = dev->of_node; + bridge->type = DRM_MODE_CONNECTOR_DSI; + bridge->ops |= DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_HPD; + + platform_set_drvdata(pdev, hpb); + + err = devm_drm_bridge_add(dev, bridge); + if (err) { + dev_err_probe(dev, err, "failed adding bridge\n"); + goto err_dsi_cleanup; + } + + err = hotplug_bridge_dsi_attach(&hpb->dsi_host, NULL); + if (err) { + dev_err_probe(dev, err, "failed first attach to upstream DSI host\n"); + goto err_dsi_cleanup; + } + + /* + * Since devm_drm_bridge_add() we can be notified of any bridges + * appearing, but also check now, in case the next bridge was + * probed earlier + */ + hotplug_bridge_grab(hpb); + + return 0; + +err_dsi_cleanup: + hotplug_bridge_dsi_cleanup(hpb); + return err; +} + +static void hotplug_bridge_remove(struct platform_device *pdev) +{ + struct hotplug_bridge *hpb = platform_get_drvdata(pdev); + + cancel_work_sync(&hpb->hpd_work); + + hotplug_bridge_release(hpb, NULL); + + hotplug_bridge_dsi_cleanup(hpb); +} + +static const struct platform_device_id hotplug_bridge_platform_ids[] = { + { .name = "hotplug-dsi-bridge" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, hotplug_bridge_platform_ids); + +static struct platform_driver hotplug_bridge_driver = { + .probe = hotplug_bridge_probe, + .remove = hotplug_bridge_remove, + .id_table = hotplug_bridge_platform_ids, + .driver = { + .name = "hotplug-drm-bridge", + }, +}; + +module_platform_driver(hotplug_bridge_driver); + +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_AUTHOR("Paul Kocialkowski "); +MODULE_DESCRIPTION("Hotplug DRM Bridge"); +MODULE_LICENSE("GPL");