Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
[deliverable/linux.git] / drivers / media / media-entity.c
index 6926e0685d0aa2cf5d25b7aea69c21fb495afe88..e89d85a7d31b56286a6d8c9d492b5565dc6a4d43 100644 (file)
@@ -70,6 +70,27 @@ static inline const char *intf_type(struct media_interface *intf)
        }
 };
 
+__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
+                                         int idx_max)
+{
+       ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG),
+                                sizeof(long), GFP_KERNEL);
+       if (!ent_enum->bmap)
+               return -ENOMEM;
+
+       bitmap_zero(ent_enum->bmap, idx_max);
+       ent_enum->idx_max = idx_max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__media_entity_enum_init);
+
+void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
+{
+       kfree(ent_enum->bmap);
+}
+EXPORT_SYMBOL_GPL(media_entity_enum_cleanup);
+
 /**
  *  dev_dbg_obj - Prints in debug mode a change on some object
  *
@@ -85,8 +106,8 @@ static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
        switch (media_type(gobj)) {
        case MEDIA_GRAPH_ENTITY:
                dev_dbg(gobj->mdev->dev,
-                       "%s: id 0x%08x entity#%d: '%s'\n",
-                       event_name, gobj->id, media_localid(gobj),
+                       "%s id %u: entity '%s'\n",
+                       event_name, media_id(gobj),
                        gobj_to_entity(gobj)->name);
                break;
        case MEDIA_GRAPH_LINK:
@@ -94,14 +115,12 @@ static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
                struct media_link *link = gobj_to_link(gobj);
 
                dev_dbg(gobj->mdev->dev,
-                       "%s: id 0x%08x link#%d: %s#%d ==> %s#%d\n",
-                       event_name, gobj->id, media_localid(gobj),
-
-                       gobj_type(media_type(link->gobj0)),
-                       media_localid(link->gobj0),
-
-                       gobj_type(media_type(link->gobj1)),
-                       media_localid(link->gobj1));
+                       "%s id %u: %s link id %u ==> id %u\n",
+                       event_name, media_id(gobj),
+                       media_type(link->gobj0) == MEDIA_GRAPH_PAD ?
+                               "data" : "interface",
+                       media_id(link->gobj0),
+                       media_id(link->gobj1));
                break;
        }
        case MEDIA_GRAPH_PAD:
@@ -109,11 +128,10 @@ static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
                struct media_pad *pad = gobj_to_pad(gobj);
 
                dev_dbg(gobj->mdev->dev,
-                       "%s: id 0x%08x %s%spad#%d: '%s':%d\n",
-                       event_name, gobj->id,
-                       pad->flags & MEDIA_PAD_FL_SINK   ? "  sink " : "",
+                       "%s id %u: %s%spad '%s':%d\n",
+                       event_name, media_id(gobj),
+                       pad->flags & MEDIA_PAD_FL_SINK   ? "sink " : "",
                        pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "",
-                       media_localid(gobj),
                        pad->entity->name, pad->index);
                break;
        }
@@ -123,8 +141,8 @@ static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
                struct media_intf_devnode *devnode = intf_to_devnode(intf);
 
                dev_dbg(gobj->mdev->dev,
-                       "%s: id 0x%08x intf_devnode#%d: %s - major: %d, minor: %d\n",
-                       event_name, gobj->id, media_localid(gobj),
+                       "%s id %u: intf_devnode %s - major: %d, minor: %d\n",
+                       event_name, media_id(gobj),
                        intf_type(intf),
                        devnode->major, devnode->minor);
                break;
@@ -142,21 +160,19 @@ void media_gobj_create(struct media_device *mdev,
        gobj->mdev = mdev;
 
        /* Create a per-type unique object ID */
+       gobj->id = media_gobj_gen_id(type, ++mdev->id);
+
        switch (type) {
        case MEDIA_GRAPH_ENTITY:
-               gobj->id = media_gobj_gen_id(type, ++mdev->entity_id);
                list_add_tail(&gobj->list, &mdev->entities);
                break;
        case MEDIA_GRAPH_PAD:
-               gobj->id = media_gobj_gen_id(type, ++mdev->pad_id);
                list_add_tail(&gobj->list, &mdev->pads);
                break;
        case MEDIA_GRAPH_LINK:
-               gobj->id = media_gobj_gen_id(type, ++mdev->link_id);
                list_add_tail(&gobj->list, &mdev->links);
                break;
        case MEDIA_GRAPH_INTF_DEVNODE:
-               gobj->id = media_gobj_gen_id(type, ++mdev->intf_devnode_id);
                list_add_tail(&gobj->list, &mdev->interfaces);
                break;
        }
@@ -242,17 +258,47 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph)
 #define link_top(en)   ((en)->stack[(en)->top].link)
 #define stack_top(en)  ((en)->stack[(en)->top].entity)
 
+/*
+ * TODO: Get rid of this.
+ */
+#define MEDIA_ENTITY_MAX_PADS          512
+
+/**
+ * media_entity_graph_walk_init - Allocate resources for graph walk
+ * @graph: Media graph structure that will be used to walk the graph
+ * @mdev: Media device
+ *
+ * Reserve resources for graph walk in media device's current
+ * state. The memory must be released using
+ * media_entity_graph_walk_free().
+ *
+ * Returns error on failure, zero on success.
+ */
+__must_check int media_entity_graph_walk_init(
+       struct media_entity_graph *graph, struct media_device *mdev)
+{
+       return media_entity_enum_init(&graph->ent_enum, mdev);
+}
+EXPORT_SYMBOL_GPL(media_entity_graph_walk_init);
+
+/**
+ * media_entity_graph_walk_cleanup - Release resources related to graph walking
+ * @graph: Media graph structure that was used to walk the graph
+ */
+void media_entity_graph_walk_cleanup(struct media_entity_graph *graph)
+{
+       media_entity_enum_cleanup(&graph->ent_enum);
+}
+EXPORT_SYMBOL_GPL(media_entity_graph_walk_cleanup);
+
 void media_entity_graph_walk_start(struct media_entity_graph *graph,
                                   struct media_entity *entity)
 {
+       media_entity_enum_zero(&graph->ent_enum);
+       media_entity_enum_set(&graph->ent_enum, entity);
+
        graph->top = 0;
        graph->stack[graph->top].entity = NULL;
-       bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID);
-
-       if (WARN_ON(media_entity_id(entity) >= MEDIA_ENTITY_ENUM_MAX_ID))
-               return;
-
-       __set_bit(media_entity_id(entity), graph->entities);
        stack_push(graph, entity);
 }
 EXPORT_SYMBOL_GPL(media_entity_graph_walk_start);
@@ -283,11 +329,9 @@ media_entity_graph_walk_next(struct media_entity_graph *graph)
 
                /* Get the entity in the other end of the link . */
                next = media_entity_other(entity, link);
-               if (WARN_ON(media_entity_id(next) >= MEDIA_ENTITY_ENUM_MAX_ID))
-                       return NULL;
 
                /* Has the entity already been visited? */
-               if (__test_and_set_bit(media_entity_id(next), graph->entities)) {
+               if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
                        link_top(graph) = link_top(graph)->next;
                        continue;
                }
@@ -309,21 +353,32 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
                                             struct media_pipeline *pipe)
 {
        struct media_device *mdev = entity->graph_obj.mdev;
-       struct media_entity_graph graph;
+       struct media_entity_graph *graph = &pipe->graph;
        struct media_entity *entity_err = entity;
        struct media_link *link;
        int ret;
 
        mutex_lock(&mdev->graph_mutex);
 
-       media_entity_graph_walk_start(&graph, entity);
+       if (!pipe->streaming_count++) {
+               ret = media_entity_graph_walk_init(&pipe->graph, mdev);
+               if (ret)
+                       goto error_graph_walk_start;
+       }
+
+       media_entity_graph_walk_start(&pipe->graph, entity);
 
-       while ((entity = media_entity_graph_walk_next(&graph))) {
+       while ((entity = media_entity_graph_walk_next(graph))) {
                DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS);
                DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS);
 
                entity->stream_count++;
-               WARN_ON(entity->pipe && entity->pipe != pipe);
+
+               if (WARN_ON(entity->pipe && entity->pipe != pipe)) {
+                       ret = -EBUSY;
+                       goto error;
+               }
+
                entity->pipe = pipe;
 
                /* Already streaming --- no need to check. */
@@ -394,9 +449,9 @@ error:
         * Link validation on graph failed. We revert what we did and
         * return the error.
         */
-       media_entity_graph_walk_start(&graph, entity_err);
+       media_entity_graph_walk_start(graph, entity_err);
 
-       while ((entity_err = media_entity_graph_walk_next(&graph))) {
+       while ((entity_err = media_entity_graph_walk_next(graph))) {
                entity_err->stream_count--;
                if (entity_err->stream_count == 0)
                        entity_err->pipe = NULL;
@@ -409,6 +464,10 @@ error:
                        break;
        }
 
+error_graph_walk_start:
+       if (!--pipe->streaming_count)
+               media_entity_graph_walk_cleanup(graph);
+
        mutex_unlock(&mdev->graph_mutex);
 
        return ret;
@@ -418,18 +477,23 @@ EXPORT_SYMBOL_GPL(media_entity_pipeline_start);
 void media_entity_pipeline_stop(struct media_entity *entity)
 {
        struct media_device *mdev = entity->graph_obj.mdev;
-       struct media_entity_graph graph;
+       struct media_entity_graph *graph = &entity->pipe->graph;
+       struct media_pipeline *pipe = entity->pipe;
 
        mutex_lock(&mdev->graph_mutex);
 
-       media_entity_graph_walk_start(&graph, entity);
+       WARN_ON(!pipe->streaming_count);
+       media_entity_graph_walk_start(graph, entity);
 
-       while ((entity = media_entity_graph_walk_next(&graph))) {
+       while ((entity = media_entity_graph_walk_next(graph))) {
                entity->stream_count--;
                if (entity->stream_count == 0)
                        entity->pipe = NULL;
        }
 
+       if (!--pipe->streaming_count)
+               media_entity_graph_walk_cleanup(graph);
+
        mutex_unlock(&mdev->graph_mutex);
 }
 EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);
@@ -561,6 +625,71 @@ media_create_pad_link(struct media_entity *source, u16 source_pad,
 }
 EXPORT_SYMBOL_GPL(media_create_pad_link);
 
+int media_create_pad_links(const struct media_device *mdev,
+                          const u32 source_function,
+                          struct media_entity *source,
+                          const u16 source_pad,
+                          const u32 sink_function,
+                          struct media_entity *sink,
+                          const u16 sink_pad,
+                          u32 flags,
+                          const bool allow_both_undefined)
+{
+       struct media_entity *entity;
+       unsigned function;
+       int ret;
+
+       /* Trivial case: 1:1 relation */
+       if (source && sink)
+               return media_create_pad_link(source, source_pad,
+                                            sink, sink_pad, flags);
+
+       /* Worse case scenario: n:n relation */
+       if (!source && !sink) {
+               if (!allow_both_undefined)
+                       return 0;
+               media_device_for_each_entity(source, mdev) {
+                       if (source->function != source_function)
+                               continue;
+                       media_device_for_each_entity(sink, mdev) {
+                               if (sink->function != sink_function)
+                                       continue;
+                               ret = media_create_pad_link(source, source_pad,
+                                                           sink, sink_pad,
+                                                           flags);
+                               if (ret)
+                                       return ret;
+                               flags &= ~(MEDIA_LNK_FL_ENABLED |
+                                          MEDIA_LNK_FL_IMMUTABLE);
+                       }
+               }
+               return 0;
+       }
+
+       /* Handle 1:n and n:1 cases */
+       if (source)
+               function = sink_function;
+       else
+               function = source_function;
+
+       media_device_for_each_entity(entity, mdev) {
+               if (entity->function != function)
+                       continue;
+
+               if (source)
+                       ret = media_create_pad_link(source, source_pad,
+                                                   entity, sink_pad, flags);
+               else
+                       ret = media_create_pad_link(entity, source_pad,
+                                                   sink, sink_pad, flags);
+               if (ret)
+                       return ret;
+               flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(media_create_pad_links);
+
 void __media_entity_remove_links(struct media_entity *entity)
 {
        struct media_link *link, *tmp;
@@ -575,13 +704,15 @@ EXPORT_SYMBOL_GPL(__media_entity_remove_links);
 
 void media_entity_remove_links(struct media_entity *entity)
 {
+       struct media_device *mdev = entity->graph_obj.mdev;
+
        /* Do nothing if the entity is not registered. */
-       if (entity->graph_obj.mdev == NULL)
+       if (mdev == NULL)
                return;
 
-       spin_lock(&entity->graph_obj.mdev->lock);
+       spin_lock(&mdev->lock);
        __media_entity_remove_links(entity);
-       spin_unlock(&entity->graph_obj.mdev->lock);
+       spin_unlock(&mdev->lock);
 }
 EXPORT_SYMBOL_GPL(media_entity_remove_links);
 
@@ -657,9 +788,9 @@ int media_entity_setup_link(struct media_link *link, u32 flags)
 {
        int ret;
 
-       spin_lock(&link->source->entity->graph_obj.mdev->lock);
+       mutex_lock(&link->graph_obj.mdev->graph_mutex);
        ret = __media_entity_setup_link(link, flags);
-       spin_unlock(&link->source->entity->graph_obj.mdev->lock);
+       mutex_unlock(&link->graph_obj.mdev->graph_mutex);
 
        return ret;
 }
@@ -776,9 +907,15 @@ EXPORT_SYMBOL_GPL(__media_remove_intf_link);
 
 void media_remove_intf_link(struct media_link *link)
 {
-       spin_lock(&link->graph_obj.mdev->lock);
+       struct media_device *mdev = link->graph_obj.mdev;
+
+       /* Do nothing if the intf is not registered. */
+       if (mdev == NULL)
+               return;
+
+       spin_lock(&mdev->lock);
        __media_remove_intf_link(link);
-       spin_unlock(&link->graph_obj.mdev->lock);
+       spin_unlock(&mdev->lock);
 }
 EXPORT_SYMBOL_GPL(media_remove_intf_link);
 
@@ -794,12 +931,14 @@ EXPORT_SYMBOL_GPL(__media_remove_intf_links);
 
 void media_remove_intf_links(struct media_interface *intf)
 {
+       struct media_device *mdev = intf->graph_obj.mdev;
+
        /* Do nothing if the intf is not registered. */
-       if (intf->graph_obj.mdev == NULL)
+       if (mdev == NULL)
                return;
 
-       spin_lock(&intf->graph_obj.mdev->lock);
+       spin_lock(&mdev->lock);
        __media_remove_intf_links(intf);
-       spin_unlock(&intf->graph_obj.mdev->lock);
+       spin_unlock(&mdev->lock);
 }
 EXPORT_SYMBOL_GPL(media_remove_intf_links);
This page took 0.047542 seconds and 5 git commands to generate.