From nobody Fri Apr 3 08:02:05 2026 Received: from imap4.hz.codethink.co.uk (imap4.hz.codethink.co.uk [188.40.203.114]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 120CD334C1E for ; Wed, 18 Feb 2026 22:27:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=188.40.203.114 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771453628; cv=none; b=hOSTM4JA/ctkvYBrM48A9XB3FfPARP6/Q1rKgS4ESicCnBoJCdf71yVDpBZL87uI6+3ZLmCcfrDHgGh306una46eZqbUjHqBNqw1n9hLy2BMfF2fBsOtHp5hOKnDNTWMNJxl/sLrqs4GjSJV2htlg5JgCykTIzLrk3Y0i7AvsNM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771453628; c=relaxed/simple; bh=AH4OmYxCTk0oj22Jcm2FuF5UY10EV5pOkoswObxkI2A=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=cZ+3HFU+3jcmuxV1szoQio4PMSU7wbXrlic16i+y6ri0SHigEpINYbYWjhAjTVhAPLds8fVdnSUwEvFflcCNbcJtyjz/DHWBK8W8jubcm3COo5Q3opj+4U0/NXTXhqui//DL/T7mU9ozkxwU42P11o0+Su3qJgtiHkzsPPKn6PE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=codethink.co.uk; spf=pass smtp.mailfrom=codethink.com; dkim=pass (2048-bit key) header.d=codethink.co.uk header.i=@codethink.co.uk header.b=Knw2L7S1; arc=none smtp.client-ip=188.40.203.114 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=codethink.co.uk Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=codethink.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=codethink.co.uk header.i=@codethink.co.uk header.b="Knw2L7S1" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=codethink.co.uk; s=imap4-20230908; h=Sender:Content-Transfer-Encoding: MIME-Version:Message-Id:Date:Subject:Cc:To:From:Reply-To:In-Reply-To: References; bh=4XFFuoOsWpLPrzDAC/fYwo+2tbrwanNtYfWlQGixUvc=; b=Knw2L7S1DSKHkL heCtVOyOltjeWKQl5CpiTLMYBU8Lt1wHgU2sAxxOLyDR6Bkyq5vX4S5UTEdd8L5LuKx9PAuoX5lMq iurRPFfDyZTu9hfbaKdcQms8nf9mu7VgjfBt0IqVO9TzWpX5XbSmQCdcb82zp5VMzovQxyLXD4Fkf Rh8xkn3Vos48c9nDB/p7mLgv4ghTxxAJ6T2KYSksfbichbqPdmvIwZIVUWabBNTSkT69c7j4Wll4B OPSlC4+EjgJ+FVmkV/ZIeTQWKBRSxyqp75x9JUcQq2D7fEY9NAmFcSbwkBoZ8luTaC7Ia2Asd0xlW OFUwDan56f5AkWIhPTIQ==; Received: from [167.98.27.226] (helo=rainbowdash) by imap4.hz.codethink.co.uk with esmtpsa (Exim 4.94.2 #2 (Debian)) id 1vspdT-007v15-I3; Wed, 18 Feb 2026 22:03:07 +0000 Received: from ben by rainbowdash with local (Exim 4.99.1) (envelope-from ) id 1vspdT-000000057Wf-1G2I; Wed, 18 Feb 2026 22:03:07 +0000 From: Ben Dooks To: gregkh@linuxfoundation.org, rafael@kernel.org, dakr@kernel.org, dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org, james.thomas@codethink.co.uk, robert.mccann@codethink.co.uk, Ben Dooks Subject: [RFC] drivers: base: component: these can be part of more than one aggregate Date: Wed, 18 Feb 2026 22:03:03 +0000 Message-Id: <20260218220303.1220560-1-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.37.2.352.g3c44437643 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: srv_ts003@codethink.com Content-Type: text/plain; charset="utf-8" Sending as an RFC as there is still a couple of todo comments and I am not completely sure this is bug-free yet. When dealing with newer qualcomm devices they can have a gpu associated with more than one display controller. With each displayer controller being an aggregate device only one will start with the others being held as they will never find the gpu and thus never get bound. To try and sort this out, make each component have a list of all the aggregate devices it is part of. To avoid adding more structs we add a list node to each of the match which can have a component and use that to avoid adding a lot more code. Signed-off-by: Ben Dooks --- drivers/base/component.c | 93 +++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index 5d1eb5d82462a..f9e1a6ecf3143 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -37,6 +37,7 @@ */ =20 struct component; +struct aggregate_device; =20 struct component_match_array { void *data; @@ -44,6 +45,8 @@ struct component_match_array { int (*compare_typed)(struct device *, int, void *); void (*release)(struct device *, void *); struct component *component; + struct list_head component_list; + struct aggregate_device *adev; bool duplicate; }; =20 @@ -64,7 +67,7 @@ struct aggregate_device { =20 struct component { struct list_head node; - struct aggregate_device *adev; + struct list_head agdevs; bool bound; =20 const struct component_ops *ops; @@ -156,9 +159,6 @@ static struct component *find_component(struct aggregat= e_device *adev, struct component *c; =20 list_for_each_entry(c, &component_list, node) { - if (c->adev && c->adev !=3D adev) - continue; - if (mc->compare && mc->compare(c->dev, mc->data)) return c; =20 @@ -170,6 +170,38 @@ static struct component *find_component(struct aggrega= te_device *adev, return NULL; } =20 +static bool component_match_adev(struct component *c, + struct aggregate_device *adev) +{ + struct component_match *match =3D adev->match; + size_t i; + + for (i =3D 0; i < match->num; i++) { + struct component_match_array *mc =3D &match->compare[i]; + + if (mc->component =3D=3D c) + return true; + } + + return false; +} + +static void component_remove_adev(struct component *c, + struct aggregate_device *adev) +{ + struct component_match *match =3D adev->match; + size_t i; + + for (i =3D 0; i < match->num; i++) { + struct component_match_array *mc =3D &match->compare[i]; + + if (mc->component =3D=3D c) { + list_del(&mc->component_list); + mc->component =3D NULL; + } + } +} + static int find_components(struct aggregate_device *adev) { struct component_match *match =3D adev->match; @@ -183,6 +215,7 @@ static int find_components(struct aggregate_device *ade= v) for (i =3D 0; i < match->num; i++) { struct component_match_array *mc =3D &match->compare[i]; struct component *c; + bool duplicate; =20 dev_dbg(adev->parent, "Looking for component %zu\n", i); =20 @@ -195,28 +228,19 @@ static int find_components(struct aggregate_device *a= dev) break; } =20 + duplicate =3D component_match_adev(c, adev); dev_dbg(adev->parent, "found component %s, duplicate %u\n", - dev_name(c->dev), !!c->adev); + dev_name(c->dev), duplicate); =20 /* Attach this component to the adev */ - match->compare[i].duplicate =3D !!c->adev; + match->compare[i].duplicate =3D duplicate; match->compare[i].component =3D c; - c->adev =3D adev; + match->compare[i].adev =3D adev; + list_add(&match->compare[i].component_list, &c->agdevs); } return ret; } =20 -/* Detach component from associated aggregate_device */ -static void remove_component(struct aggregate_device *adev, struct compone= nt *c) -{ - size_t i; - - /* Detach the component from this adev. */ - for (i =3D 0; i < adev->match->num; i++) - if (adev->match->compare[i].component =3D=3D c) - adev->match->compare[i].component =3D NULL; -} - /* * Try to bring up an aggregate device. If component is NULL, we're inter= ested * in this aggregate device, otherwise it's a component which must be pres= ent @@ -236,7 +260,7 @@ static int try_to_bring_up_aggregate_device(struct aggr= egate_device *adev, return 0; } =20 - if (component && component->adev !=3D adev) { + if (component && !component_match_adev(component, adev)) { dev_dbg(adev->parent, "master is not for this component (%s)\n", dev_name(component->dev)); return 0; @@ -494,7 +518,7 @@ static void free_aggregate_device(struct aggregate_devi= ce *adev) for (i =3D 0; i < match->num; i++) { struct component *c =3D match->compare[i].component; if (c) - c->adev =3D NULL; + component_remove_adev(c, adev); } } =20 @@ -749,8 +773,21 @@ static int __component_add(struct device *dev, const s= truct component_ops *ops, =20 ret =3D try_to_bring_up_masters(component); if (ret < 0) { - if (component->adev) - remove_component(component->adev, component); + // if we don't bind, do we have to remove all masters from component? + // todo: also do we need to remove in reverse order? + struct list_head *ptr, *tmp; + struct component_match_array *mc; + + list_for_each_safe(ptr, tmp, &component->agdevs) { + mc =3D list_entry(ptr, struct component_match_array, component_list); + + list_del(&mc->component_list); + mc->component =3D NULL; + + if (!mc->duplicate) + take_down_aggregate_device(mc->adev); + } + list_del(&component->node); =20 kfree(component); @@ -822,6 +859,8 @@ EXPORT_SYMBOL_GPL(component_add); void component_del(struct device *dev, const struct component_ops *ops) { struct component *c, *component =3D NULL; + struct list_head *ptr, *tmp; + struct component_match_array *mc; =20 mutex_lock(&component_mutex); list_for_each_entry(c, &component_list, node) @@ -831,9 +870,13 @@ void component_del(struct device *dev, const struct co= mponent_ops *ops) break; } =20 - if (component && component->adev) { - take_down_aggregate_device(component->adev); - remove_component(component->adev, component); + list_for_each_safe(ptr, tmp, &component->agdevs) { + mc =3D list_entry(ptr, struct component_match_array, component_list); + list_del(&mc->component_list); + mc->component =3D NULL; + + if (!mc->duplicate) + take_down_aggregate_device(mc->adev); } =20 mutex_unlock(&component_mutex); --=20 2.37.2.352.g3c44437643