There are already several helpers to find a udevice based on its position in a device tree, like getting a child or a node pointed by a phandle, but there was no support for graph endpoints, which are very common in display pipelines.
Add a new helper, named uclass_get_device_by_endpoint() which enters the child graph reprensentation, looks for a specific port, then follows the remote endpoint, and finally retrieves the first parent of the given uclass_id. This is a very handy and straightforward way to get a bridge or a panel handle. Signed-off-by: Miquel Raynal <miquel.ray...@bootlin.com> --- drivers/core/uclass.c | 62 +++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass.h | 21 +++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index e46d5717aa6..5803fd51986 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -572,6 +572,68 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, ret = uclass_find_device_by_phandle(id, parent, name, &dev); return uclass_get_device_tail(dev, ret, devp); } + +int uclass_get_device_by_endpoint(enum uclass_id class_id, struct udevice *dev, + u32 ep_idx, struct udevice **devp) +{ + ofnode port = ofnode_null(), ep, remote_ep; + struct udevice **target; + u32 remote_phandle; + int ret; + + if (!ep_idx) + port = dev_read_subnode(dev, "port"); + + if (!ofnode_valid(port)) { + char port_name[32]; + + port = dev_read_subnode(dev, "ports"); + if (!ofnode_valid(port)) { + debug("%s: no 'port'/'ports' subnode\n", + dev_read_name(dev)); + return -EINVAL; + } + + snprintf(port_name, sizeof(port_name), "port@%x", ep_idx); + port = ofnode_find_subnode(port, port_name); + if (!ofnode_valid(port)) { + debug("%s: no 'port@%x' subnode\n", + dev_read_name(dev), ep_idx); + return -EINVAL; + } + } + + ep = ofnode_find_subnode(port, "endpoint"); + if (!ofnode_valid(ep)) { + debug("%s: no 'endpoint' in %s subnode\n", + ofnode_get_name(port), dev_read_name(dev)); + return -EINVAL; + } + + ret = ofnode_read_u32(ep, "remote-endpoint", &remote_phandle); + if (ret) + return ret; + + remote_ep = ofnode_get_by_phandle(remote_phandle); + if (!ofnode_valid(remote_ep)) + return -EINVAL; + + while (ofnode_valid(remote_ep)) { + remote_ep = ofnode_get_parent(remote_ep); + debug("trying subnode: %s\n", ofnode_get_name(remote_ep)); + if (!ofnode_valid(remote_ep)) { + debug("%s: no more remote devices\n", + ofnode_get_name(remote_ep)); + return -EINVAL; + } + + ret = uclass_find_device_by_ofnode(class_id, remote_ep, target); + if (!ret) + break; + }; + + return uclass_get_device_tail(*target, ret, devp); +} #endif /* diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 456eef7f2f3..1194174ca9b 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -333,6 +333,27 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv, struct udevice **devp); +/** + * uclass_get_device_by_endpoint() - Get a uclass device for a remote endpoint + * + * This searches through the parents of the specified remote endpoint + * for the first device matching the uclass. Said otherwise, this helper + * goes through the graph (endpoint) representation and searches for + * matching devices. Endpoints can be subnodes of the "port" node or + * subnodes of ports identified with a reg property, themselves in a + * "ports" container. + * + * The device is probed to activate it ready for use. + * + * @class_id: uclass ID to look up + * @dev: Device to start from + * @ep_idx: Index of the endpoint to follow, 0 if there is none. + * @target: Returns pointer to the first device matching the expected uclass. + * Return: 0 if OK, -ve on error + */ +int uclass_get_device_by_endpoint(enum uclass_id class_id, struct udevice *dev, + u32 ep_idx, struct udevice **target); + /** * uclass_first_device() - Get the first device in a uclass * -- 2.43.0