Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[pandora-kernel.git] / drivers / media / media-entity.c
index 3e7e2d5..23640ed 100644 (file)
@@ -196,6 +196,75 @@ media_entity_graph_walk_next(struct media_entity_graph *graph)
 }
 EXPORT_SYMBOL_GPL(media_entity_graph_walk_next);
 
+/* -----------------------------------------------------------------------------
+ * Pipeline management
+ */
+
+/**
+ * media_entity_pipeline_start - Mark a pipeline as streaming
+ * @entity: Starting entity
+ * @pipe: Media pipeline to be assigned to all entities in the pipeline.
+ *
+ * Mark all entities connected to a given entity through enabled links, either
+ * directly or indirectly, as streaming. The given pipeline object is assigned to
+ * every entity in the pipeline and stored in the media_entity pipe field.
+ *
+ * Calls to this function can be nested, in which case the same number of
+ * media_entity_pipeline_stop() calls will be required to stop streaming. The
+ * pipeline pointer must be identical for all nested calls to
+ * media_entity_pipeline_start().
+ */
+void media_entity_pipeline_start(struct media_entity *entity,
+                                struct media_pipeline *pipe)
+{
+       struct media_device *mdev = entity->parent;
+       struct media_entity_graph graph;
+
+       mutex_lock(&mdev->graph_mutex);
+
+       media_entity_graph_walk_start(&graph, entity);
+
+       while ((entity = media_entity_graph_walk_next(&graph))) {
+               entity->stream_count++;
+               WARN_ON(entity->pipe && entity->pipe != pipe);
+               entity->pipe = pipe;
+       }
+
+       mutex_unlock(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_entity_pipeline_start);
+
+/**
+ * media_entity_pipeline_stop - Mark a pipeline as not streaming
+ * @entity: Starting entity
+ *
+ * Mark all entities connected to a given entity through enabled links, either
+ * directly or indirectly, as not streaming. The media_entity pipe field is
+ * reset to NULL.
+ *
+ * If multiple calls to media_entity_pipeline_start() have been made, the same
+ * number of calls to this function are required to mark the pipeline as not
+ * streaming.
+ */
+void media_entity_pipeline_stop(struct media_entity *entity)
+{
+       struct media_device *mdev = entity->parent;
+       struct media_entity_graph graph;
+
+       mutex_lock(&mdev->graph_mutex);
+
+       media_entity_graph_walk_start(&graph, entity);
+
+       while ((entity = media_entity_graph_walk_next(&graph))) {
+               entity->stream_count--;
+               if (entity->stream_count == 0)
+                       entity->pipe = NULL;
+       }
+
+       mutex_unlock(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);
+
 /* -----------------------------------------------------------------------------
  * Module use count
  */
@@ -306,3 +375,162 @@ media_entity_create_link(struct media_entity *source, u16 source_pad,
        return 0;
 }
 EXPORT_SYMBOL_GPL(media_entity_create_link);
+
+static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
+{
+       const u32 mask = MEDIA_LNK_FL_ENABLED;
+       int ret;
+
+       /* Notify both entities. */
+       ret = media_entity_call(link->source->entity, link_setup,
+                               link->source, link->sink, flags);
+       if (ret < 0 && ret != -ENOIOCTLCMD)
+               return ret;
+
+       ret = media_entity_call(link->sink->entity, link_setup,
+                               link->sink, link->source, flags);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               media_entity_call(link->source->entity, link_setup,
+                                 link->source, link->sink, link->flags);
+               return ret;
+       }
+
+       link->flags = (link->flags & ~mask) | (flags & mask);
+       link->reverse->flags = link->flags;
+
+       return 0;
+}
+
+/**
+ * __media_entity_setup_link - Configure a media link
+ * @link: The link being configured
+ * @flags: Link configuration flags
+ *
+ * The bulk of link setup is handled by the two entities connected through the
+ * link. This function notifies both entities of the link configuration change.
+ *
+ * If the link is immutable or if the current and new configuration are
+ * identical, return immediately.
+ *
+ * The user is expected to hold link->source->parent->mutex. If not,
+ * media_entity_setup_link() should be used instead.
+ */
+int __media_entity_setup_link(struct media_link *link, u32 flags)
+{
+       struct media_device *mdev;
+       struct media_entity *source, *sink;
+       int ret = -EBUSY;
+
+       if (link == NULL)
+               return -EINVAL;
+
+       if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
+               return link->flags == flags ? 0 : -EINVAL;
+
+       if (link->flags == flags)
+               return 0;
+
+       source = link->source->entity;
+       sink = link->sink->entity;
+
+       if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
+           (source->stream_count || sink->stream_count))
+               return -EBUSY;
+
+       mdev = source->parent;
+
+       if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) {
+               ret = mdev->link_notify(link->source, link->sink,
+                                       MEDIA_LNK_FL_ENABLED);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = __media_entity_setup_link_notify(link, flags);
+       if (ret < 0)
+               goto err;
+
+       if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
+               mdev->link_notify(link->source, link->sink, 0);
+
+       return 0;
+
+err:
+       if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
+               mdev->link_notify(link->source, link->sink, 0);
+
+       return ret;
+}
+
+int media_entity_setup_link(struct media_link *link, u32 flags)
+{
+       int ret;
+
+       mutex_lock(&link->source->entity->parent->graph_mutex);
+       ret = __media_entity_setup_link(link, flags);
+       mutex_unlock(&link->source->entity->parent->graph_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(media_entity_setup_link);
+
+/**
+ * media_entity_find_link - Find a link between two pads
+ * @source: Source pad
+ * @sink: Sink pad
+ *
+ * Return a pointer to the link between the two entities. If no such link
+ * exists, return NULL.
+ */
+struct media_link *
+media_entity_find_link(struct media_pad *source, struct media_pad *sink)
+{
+       struct media_link *link;
+       unsigned int i;
+
+       for (i = 0; i < source->entity->num_links; ++i) {
+               link = &source->entity->links[i];
+
+               if (link->source->entity == source->entity &&
+                   link->source->index == source->index &&
+                   link->sink->entity == sink->entity &&
+                   link->sink->index == sink->index)
+                       return link;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(media_entity_find_link);
+
+/**
+ * media_entity_remote_source - Find the source pad at the remote end of a link
+ * @pad: Sink pad at the local end of the link
+ *
+ * Search for a remote source pad connected to the given sink pad by iterating
+ * over all links originating or terminating at that pad until an enabled link
+ * is found.
+ *
+ * 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 *media_entity_remote_source(struct media_pad *pad)
+{
+       unsigned int i;
+
+       for (i = 0; i < pad->entity->num_links; i++) {
+               struct media_link *link = &pad->entity->links[i];
+
+               if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+                       continue;
+
+               if (link->source == pad)
+                       return link->sink;
+
+               if (link->sink == pad)
+                       return link->source;
+       }
+
+       return NULL;
+
+}
+EXPORT_SYMBOL_GPL(media_entity_remote_source);