Adds two functions:

media_create_fwnode_pad_links(), which converts fwnode endpoints that
connect a local pad to all pads on a remote entity into media links.

media_create_fwnode_links(), which converts fwnode endpoints that
connect all pads from a local entity to all pads on a remote entity into
media links.

Signed-off-by: Steve Longerbeam <slongerb...@gmail.com>
---
 drivers/media/mc/mc-entity.c | 178 +++++++++++++++++++++++++++++++++++
 include/media/media-entity.h |  71 ++++++++++++++
 2 files changed, 249 insertions(+)

diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
index e9e090244fd4..45bbd6c91019 100644
--- a/drivers/media/mc/mc-entity.c
+++ b/drivers/media/mc/mc-entity.c
@@ -787,6 +787,184 @@ int media_create_pad_links(const struct media_device 
*mdev,
 }
 EXPORT_SYMBOL_GPL(media_create_pad_links);
 
+static int __media_create_fwnode_pad_link(struct media_pad *local_pad,
+                                       struct media_entity *remote,
+                                       const struct fwnode_handle *remote_ep,
+                                       const u32 flags)
+{
+       struct media_entity *local = local_pad->entity;
+       unsigned long local_dir = local_pad->flags;
+       unsigned long remote_dir = (local_dir & MEDIA_PAD_FL_SOURCE) ?
+               MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+       struct media_entity *src, *sink;
+       int src_pad, sink_pad;
+       int remote_pad;
+       int ret;
+
+       remote_pad = media_entity_get_fwnode_pad(remote, remote_ep,
+                                                remote_dir);
+       if (remote_pad < 0)
+               return 0;
+
+       if (local_dir & MEDIA_PAD_FL_SOURCE) {
+               src = local;
+               src_pad = local_pad->index;
+               sink = remote;
+               sink_pad = remote_pad;
+       } else {
+               src = remote;
+               src_pad = remote_pad;
+               sink = local;
+               sink_pad = local_pad->index;
+       }
+
+       /* make sure link doesn't already exist */
+       if (media_entity_find_link(&src->pads[src_pad],
+                                  &sink->pads[sink_pad]))
+               return 0;
+
+       ret = media_create_pad_link(src, src_pad, sink, sink_pad, flags);
+       if (ret) {
+               dev_dbg(sink->graph_obj.mdev->dev,
+                       "%s:%d -> %s:%d failed with %d\n",
+                       src->name, src_pad, sink->name, sink_pad,
+                       ret);
+               return ret;
+       }
+
+       dev_dbg(sink->graph_obj.mdev->dev, "%s:%d -> %s:%d\n",
+               src->name, src_pad, sink->name, sink_pad);
+
+       return 0;
+}
+
+int __media_create_fwnode_pad_links(struct media_pad *local_pad,
+                                   const struct fwnode_handle *local_fwnode,
+                                   struct media_entity *remote,
+                                   const struct fwnode_handle *remote_fwnode,
+                                   const u32 link_flags)
+{
+       struct fwnode_handle *endpoint;
+
+       fwnode_graph_for_each_endpoint(remote_fwnode, endpoint) {
+               struct fwnode_link link;
+               int ret;
+
+               ret = fwnode_graph_parse_link(endpoint, &link);
+               if (ret)
+                       continue;
+
+               /*
+                * if this endpoint does not link to the local fwnode
+                * device, ignore it and continue.
+                */
+               if (link.remote_port_parent != local_fwnode) {
+                       fwnode_graph_put_link(&link);
+                       continue;
+               }
+
+               ret = __media_create_fwnode_pad_link(local_pad,
+                                                    remote, endpoint,
+                                                    link_flags);
+
+               fwnode_graph_put_link(&link);
+
+               if (ret) {
+                       fwnode_handle_put(endpoint);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__media_create_fwnode_pad_links);
+
+int media_create_fwnode_pad_links(struct media_pad *local_pad,
+                                 const struct fwnode_handle *local_fwnode,
+                                 struct media_entity *remote,
+                                 const struct fwnode_handle *remote_fwnode,
+                                 const u32 link_flags)
+{
+       struct media_device *mdev = local_pad->entity->graph_obj.mdev;
+       int ret;
+
+       mutex_lock(&mdev->graph_mutex);
+       ret = __media_create_fwnode_pad_links(local_pad, local_fwnode,
+                                             remote, remote_fwnode,
+                                             link_flags);
+       mutex_unlock(&mdev->graph_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(media_create_fwnode_pad_links);
+
+int __media_create_fwnode_links(struct media_entity *local,
+                               const struct fwnode_handle *local_fwnode,
+                               struct media_entity *remote,
+                               const struct fwnode_handle *remote_fwnode,
+                               const u32 link_flags)
+{
+       struct fwnode_handle *endpoint;
+
+       fwnode_graph_for_each_endpoint(local_fwnode, endpoint) {
+               struct fwnode_link link;
+               int local_pad;
+               int ret;
+
+               local_pad = media_entity_get_fwnode_pad(local, endpoint,
+                                                       MEDIA_PAD_FL_SINK |
+                                                       MEDIA_PAD_FL_SOURCE);
+               if (local_pad < 0)
+                       continue;
+
+               ret = fwnode_graph_parse_link(endpoint, &link);
+               if (ret)
+                       continue;
+
+               /*
+                * if this endpoint does not link to the remote fwnode
+                * device, ignore it and continue.
+                */
+               if (link.remote_port_parent != remote_fwnode) {
+                       fwnode_graph_put_link(&link);
+                       continue;
+               }
+
+               ret = __media_create_fwnode_pad_link(&local->pads[local_pad],
+                                                    remote,
+                                                    link.remote.local_fwnode,
+                                                    link_flags);
+
+               fwnode_graph_put_link(&link);
+
+               if (ret) {
+                       fwnode_handle_put(endpoint);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__media_create_fwnode_links);
+
+int media_create_fwnode_links(struct media_entity *local,
+                             const struct fwnode_handle *local_fwnode,
+                             struct media_entity *remote,
+                             const struct fwnode_handle *remote_fwnode,
+                             const u32 link_flags)
+{
+       struct media_device *mdev = local->graph_obj.mdev;
+       int ret;
+
+       mutex_lock(&mdev->graph_mutex);
+       ret = __media_create_fwnode_links(local, local_fwnode,
+                                         remote, remote_fwnode, link_flags);
+       mutex_unlock(&mdev->graph_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(media_create_fwnode_links);
+
 void __media_entity_remove_links(struct media_entity *entity)
 {
        struct media_link *link, *tmp;
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index de7fc3676b5a..781ac2df6460 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -772,6 +772,77 @@ int media_create_pad_links(const struct media_device *mdev,
                           u32 flags,
                           const bool allow_both_undefined);
 
+/**
+ * media_create_fwnode_links() - create links formed by the fwnode
+ *                          endpoints between a known local pad and
+ *                          a remote entity.
+ *
+ * @local_pad: Pointer to &media_pad of the local media pad.
+ * @local_fwnode: Pointer to the local device's firmware node.
+ * @remote: Pointer to &media_entity of the remote device.
+ * @remote_fwnode: Pointer to the remote device's firmware node.
+ * @link_flags: Link flags, as defined in include/uapi/linux/media.h.
+ *
+ * .. note::
+ *
+ *    Before calling this function, media_entity_pads_init() and
+ *    media_device_register_entity() should be called previously for
+ *    both entities to be linked.
+ *
+ *    Locked (via the mdev graph_mutex) and unlocked versions of this
+ *    function are provided. If this function is called from v4l2-async
+ *    notifier bound handlers, the locked version should be used to
+ *    prevent races with other subdevices loading and binding to their
+ *    notifiers in parallel. The unlocked version can for example be
+ *    called from v4l2-async notifier complete handlers, after all
+ *    subdevices have loaded and bound.
+ */
+int __media_create_fwnode_pad_links(struct media_pad *local_pad,
+                                   const struct fwnode_handle *local_fwnode,
+                                   struct media_entity *remote,
+                                   const struct fwnode_handle *remote_fwnode,
+                                   const u32 link_flags);
+int media_create_fwnode_pad_links(struct media_pad *local_pad,
+                                 const struct fwnode_handle *local_fwnode,
+                                 struct media_entity *remote,
+                                 const struct fwnode_handle *remote_fwnode,
+                                 const u32 link_flags);
+
+/**
+ * media_create_fwnode_links() - create links formed by the fwnode
+ *                          endpoints between two entities.
+ *
+ * @local: Pointer to &media_entity of the local device.
+ * @local_fwnode: Pointer to the local device's firmware node.
+ * @remote: Pointer to &media_entity of the remote device.
+ * @remote_fwnode: Pointer to the remote device's firmware node.
+ * @link_flags: Link flags, as defined in include/uapi/linux/media.h.
+ *
+ * .. note::
+ *
+ *    Before calling this function, media_entity_pads_init() and
+ *    media_device_register_entity() should be called previously for
+ *    both entities to be linked.
+ *
+ *    Locked (via the mdev graph_mutex) and unlocked versions of this
+ *    function are provided. If this function is called from v4l2-async
+ *    notifier bound handlers, the locked version should be used to
+ *    prevent races with other subdevices loading and binding to their
+ *    notifiers in parallel. The unlocked version can for example be
+ *    called from v4l2-async notifier complete handlers, after all
+ *    subdevices have loaded and bound.
+ */
+int __media_create_fwnode_links(struct media_entity *local,
+                               const struct fwnode_handle *local_fwnode,
+                               struct media_entity *remote,
+                               const struct fwnode_handle *remote_fwnode,
+                               const u32 link_flags);
+int media_create_fwnode_links(struct media_entity *local,
+                             const struct fwnode_handle *local_fwnode,
+                             struct media_entity *remote,
+                             const struct fwnode_handle *remote_fwnode,
+                             const u32 link_flags);
+
 void __media_entity_remove_links(struct media_entity *entity);
 
 /**
-- 
2.17.1

Reply via email to