diff mbox series

[1/8] dm: core: Add a helper to retrieve devices through graph endpoints

Message ID 20240910101344.110633-2-miquel.raynal@bootlin.com
State Changes Requested
Delegated to: Fabio Estevam
Headers show
Series Add imx8mp video support | expand

Commit Message

Miquel Raynal Sept. 10, 2024, 10:13 a.m. UTC
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.raynal@bootlin.com>
---
 drivers/core/uclass.c | 62 +++++++++++++++++++++++++++++++++++++++++++
 include/dm/uclass.h   | 21 +++++++++++++++
 2 files changed, 83 insertions(+)

Comments

Simon Glass Sept. 12, 2024, 1:01 a.m. UTC | #1
Hi Miquel,

On Tue, 10 Sept 2024 at 04:13, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> 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.raynal@bootlin.com>
> ---
>  drivers/core/uclass.c | 62 +++++++++++++++++++++++++++++++++++++++++++
>  include/dm/uclass.h   | 21 +++++++++++++++
>  2 files changed, 83 insertions(+)

This is fine, but please add a test when changing driver model.

Perhaps it is in another patch? I don't seem to have the series so I
wonder if I have been unsubscribed again...

>
> 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
>

Regards,
SImon
diff mbox series

Patch

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
  *