#include "vsp1.h"
 #include "vsp1_dl.h"
 #include "vsp1_entity.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
 
 static inline struct vsp1_entity *
 media_entity_to_vsp1_entity(struct media_entity *entity)
        return container_of(entity, struct vsp1_entity, subdev.entity);
 }
 
-void vsp1_entity_route_setup(struct vsp1_entity *source,
+void vsp1_entity_route_setup(struct vsp1_entity *entity,
+                            struct vsp1_pipeline *pipe,
                             struct vsp1_dl_list *dl)
 {
+       struct vsp1_entity *source;
        struct vsp1_entity *sink;
 
+       source = entity;
        if (source->route->reg == 0)
                return;
 
  * Media Operations
  */
 
-int vsp1_entity_link_setup(struct media_entity *entity,
-                          const struct media_pad *local,
-                          const struct media_pad *remote, u32 flags)
+static int vsp1_entity_link_setup_source(const struct media_pad *source_pad,
+                                        const struct media_pad *sink_pad,
+                                        u32 flags)
 {
        struct vsp1_entity *source;
 
-       if (!(local->flags & MEDIA_PAD_FL_SOURCE))
-               return 0;
-
-       source = media_entity_to_vsp1_entity(local->entity);
+       source = media_entity_to_vsp1_entity(source_pad->entity);
 
        if (!source->route)
                return 0;
 
        if (flags & MEDIA_LNK_FL_ENABLED) {
-               if (source->sink)
-                       return -EBUSY;
-               source->sink = remote->entity;
-               source->sink_pad = remote->index;
+               struct vsp1_entity *sink
+                       = media_entity_to_vsp1_entity(sink_pad->entity);
+
+               /*
+                * Fan-out is limited to one for the normal data path plus
+                * optional HGO and HGT. We ignore the HGO and HGT here.
+                */
+               if (sink->type != VSP1_ENTITY_HGO &&
+                   sink->type != VSP1_ENTITY_HGT) {
+                       if (source->sink)
+                               return -EBUSY;
+                       source->sink = sink_pad->entity;
+                       source->sink_pad = sink_pad->index;
+               }
        } else {
                source->sink = NULL;
                source->sink_pad = 0;
        return 0;
 }
 
+static int vsp1_entity_link_setup_sink(const struct media_pad *source_pad,
+                                      const struct media_pad *sink_pad,
+                                      u32 flags)
+{
+       struct vsp1_entity *sink;
+
+       sink = media_entity_to_vsp1_entity(sink_pad->entity);
+
+       if (flags & MEDIA_LNK_FL_ENABLED) {
+               /* Fan-in is limited to one. */
+               if (sink->sources[sink_pad->index])
+                       return -EBUSY;
+
+               sink->sources[sink_pad->index] = source_pad->entity;
+       } else {
+               sink->sources[sink_pad->index] = NULL;
+       }
+
+       return 0;
+}
+
+int vsp1_entity_link_setup(struct media_entity *entity,
+                          const struct media_pad *local,
+                          const struct media_pad *remote, u32 flags)
+{
+       if (local->flags & MEDIA_PAD_FL_SOURCE)
+               return vsp1_entity_link_setup_source(local, remote, flags);
+       else
+               return vsp1_entity_link_setup_sink(remote, local, flags);
+}
+
+/**
+ * vsp1_entity_remote_pad - Find the pad at the remote end of a link
+ * @pad: Pad at the local end of the link
+ *
+ * Search for a remote pad connected to the given pad by iterating over all
+ * links originating or terminating at that pad until an enabled link is found.
+ *
+ * Our link setup implementation guarantees that the output fan-out will not be
+ * higher than one for the data pipelines, except for the links to the HGO and
+ * HGT that can be enabled in addition to a regular data link. When traversing
+ * outgoing links this function ignores HGO and HGT entities and should thus be
+ * used in place of the generic media_entity_remote_pad() function to traverse
+ * data pipelines.
+ *
+ * Return a pointer to the pad at the remote end of the first found enabled
+ * link, or NULL if no enabled link has been found.
+ */
+struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
+{
+       struct media_link *link;
+
+       list_for_each_entry(link, &pad->entity->links, list) {
+               struct vsp1_entity *entity;
+
+               if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+                       continue;
+
+               /* If we're the sink the source will never be an HGO or HGT. */
+               if (link->sink == pad)
+                       return link->source;
+
+               if (link->source != pad)
+                       continue;
+
+               /* If the sink isn't a subdevice it can't be an HGO or HGT. */
+               if (!is_media_entity_v4l2_subdev(link->sink->entity))
+                       return link->sink;
+
+               entity = media_entity_to_vsp1_entity(link->sink->entity);
+               if (entity->type != VSP1_ENTITY_HGO &&
+                   entity->type != VSP1_ENTITY_HGT)
+                       return link->sink;
+       }
+
+       return NULL;
+
+}
+
 /* -----------------------------------------------------------------------------
  * Initialization
  */
        for (i = 0; i < num_pads - 1; ++i)
                entity->pads[i].flags = MEDIA_PAD_FL_SINK;
 
-       entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
+       entity->sources = devm_kcalloc(vsp1->dev, max(num_pads - 1, 1U),
+                                      sizeof(*entity->sources), GFP_KERNEL);
+       if (entity->sources == NULL)
+               return -ENOMEM;
+
+       /* Single-pad entities only have a sink. */
+       entity->pads[num_pads - 1].flags = num_pads > 1 ? MEDIA_PAD_FL_SOURCE
+                                        : MEDIA_PAD_FL_SINK;
 
        /* Initialize the media entity. */
        ret = media_entity_pads_init(&entity->subdev.entity, num_pads,
 
        if (ret < 0)
                return ret;
 
-       pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
+       /*
+        * The main data path doesn't include the HGO or HGT, use
+        * vsp1_entity_remote_pad() to traverse the graph.
+        */
+
+       pad = vsp1_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
 
        while (1) {
                if (pad == NULL) {
                                        : &input->entity;
                }
 
-               /*
-                * Follow the source link. The link setup operations ensure
-                * that the output fan-out can't be more than one, there is thus
-                * no need to verify here that only a single source link is
-                * activated.
-                */
+               /* Follow the source link, ignoring any HGO or HGT. */
                pad = &entity->pads[entity->source_pad];
-               pad = media_entity_remote_pad(pad);
+               pad = vsp1_entity_remote_pad(pad);
        }
 
        /* The last entity must be the output WPF. */
        }
 
        list_for_each_entry(entity, &pipe->entities, list_pipe) {
-               vsp1_entity_route_setup(entity, pipe->dl);
+               vsp1_entity_route_setup(entity, pipe, pipe->dl);
 
                if (entity->ops->configure)
                        entity->ops->configure(entity, pipe, pipe->dl,