From 53e269c102fbaf77e7dc526b1606ad4a48e57200 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Wed, 9 Dec 2009 08:40:00 -0300 Subject: [media] media: Entities, pads and links As video hardware pipelines become increasingly complex and configurable, the current hardware description through v4l2 subdevices reaches its limits. In addition to enumerating and configuring subdevices, video camera drivers need a way to discover and modify at runtime how those subdevices are connected. This is done through new elements called entities, pads and links. An entity is a basic media hardware building block. It can correspond to a large variety of logical blocks such as physical hardware devices (CMOS sensor for instance), logical hardware devices (a building block in a System-on-Chip image processing pipeline), DMA channels or physical connectors. A pad is a connection endpoint through which an entity can interact with other entities. Data (not restricted to video) produced by an entity flows from the entity's output to one or more entity inputs. Pads should not be confused with physical pins at chip boundaries. A link is a point-to-point oriented connection between two pads, either on the same entity or on different entities. Data flows from a source pad to a sink pad. Links are stored in the source entity. To make backwards graph walk faster, a copy of all links is also stored in the sink entity. The copy is known as a backlink and is only used to help graph traversal. The entity API is made of three functions: - media_entity_init() initializes an entity. The caller must provide an array of pads as well as an estimated number of links. The links array is allocated dynamically and will be reallocated if it grows beyond the initial estimate. - media_entity_cleanup() frees resources allocated for an entity. It must be called during the cleanup phase after unregistering the entity and before freeing it. - media_entity_create_link() creates a link between two entities. An entry in the link array of each entity is allocated and stores pointers to source and sink pads. When a media device is unregistered, all its entities are unregistered automatically. The code is based on Hans Verkuil <hverkuil@xs4all.nl> initial work. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/media-entity.c | 147 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 drivers/media/media-entity.c (limited to 'drivers/media/media-entity.c') diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c new file mode 100644 index 000000000000..5c31df9ed765 --- /dev/null +++ b/drivers/media/media-entity.c @@ -0,0 +1,147 @@ +/* + * Media entity + * + * Copyright (C) 2010 Nokia Corporation + * + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * Sakari Ailus <sakari.ailus@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <media/media-entity.h> + +/** + * media_entity_init - Initialize a media entity + * + * @num_pads: Total number of sink and source pads. + * @extra_links: Initial estimate of the number of extra links. + * @pads: Array of 'num_pads' pads. + * + * The total number of pads is an intrinsic property of entities known by the + * entity driver, while the total number of links depends on hardware design + * and is an extrinsic property unknown to the entity driver. However, in most + * use cases the entity driver can guess the number of links which can safely + * be assumed to be equal to or larger than the number of pads. + * + * For those reasons the links array can be preallocated based on the entity + * driver guess and will be reallocated later if extra links need to be + * created. + * + * This function allocates a links array with enough space to hold at least + * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will + * be set to the number of allocated elements. + * + * The pads array is managed by the entity driver and passed to + * media_entity_init() where its pointer will be stored in the entity structure. + */ +int +media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links) +{ + struct media_link *links; + unsigned int max_links = num_pads + extra_links; + unsigned int i; + + links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL); + if (links == NULL) + return -ENOMEM; + + entity->group_id = 0; + entity->max_links = max_links; + entity->num_links = 0; + entity->num_backlinks = 0; + entity->num_pads = num_pads; + entity->pads = pads; + entity->links = links; + + for (i = 0; i < num_pads; i++) { + pads[i].entity = entity; + pads[i].index = i; + } + + return 0; +} +EXPORT_SYMBOL_GPL(media_entity_init); + +void +media_entity_cleanup(struct media_entity *entity) +{ + kfree(entity->links); +} +EXPORT_SYMBOL_GPL(media_entity_cleanup); + +static struct media_link *media_entity_add_link(struct media_entity *entity) +{ + if (entity->num_links >= entity->max_links) { + struct media_link *links = entity->links; + unsigned int max_links = entity->max_links + 2; + unsigned int i; + + links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL); + if (links == NULL) + return NULL; + + for (i = 0; i < entity->num_links; i++) + links[i].reverse->reverse = &links[i]; + + entity->max_links = max_links; + entity->links = links; + } + + return &entity->links[entity->num_links++]; +} + +int +media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags) +{ + struct media_link *link; + struct media_link *backlink; + + BUG_ON(source == NULL || sink == NULL); + BUG_ON(source_pad >= source->num_pads); + BUG_ON(sink_pad >= sink->num_pads); + + link = media_entity_add_link(source); + if (link == NULL) + return -ENOMEM; + + link->source = &source->pads[source_pad]; + link->sink = &sink->pads[sink_pad]; + link->flags = flags; + + /* Create the backlink. Backlinks are used to help graph traversal and + * are not reported to userspace. + */ + backlink = media_entity_add_link(sink); + if (backlink == NULL) { + source->num_links--; + return -ENOMEM; + } + + backlink->source = &source->pads[source_pad]; + backlink->sink = &sink->pads[sink_pad]; + backlink->flags = flags; + + link->reverse = backlink; + backlink->reverse = link; + + sink->num_backlinks++; + + return 0; +} +EXPORT_SYMBOL_GPL(media_entity_create_link); -- cgit v1.2.3 From a5ccc48a7c48610e7f92fa599406738d69195d51 Mon Sep 17 00:00:00 2001 From: Sakari Ailus <sakari.ailus@iki.fi> Date: Sun, 7 Mar 2010 16:14:14 -0300 Subject: [media] media: Entity graph traversal Add media entity graph traversal. The traversal follows enabled links by depth first. Traversing graph backwards is prevented by comparing the next possible entity in the graph with the previous one. Multiply connected graphs are thus not supported. Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Vimarsh Zutshi <vimarsh.zutshi@gmail.com> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- Documentation/media-framework.txt | 42 ++++++++++++++ drivers/media/media-entity.c | 115 ++++++++++++++++++++++++++++++++++++++ include/media/media-entity.h | 15 +++++ 3 files changed, 172 insertions(+) (limited to 'drivers/media/media-entity.c') diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 0257bad2a104..ab17f33ddedc 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -216,3 +216,45 @@ Links have flags that describe the link capabilities and state. modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always enabled. + + +Graph traversal +--------------- + +The media framework provides APIs to iterate over entities in a graph. + +To iterate over all entities belonging to a media device, drivers can use the +media_device_for_each_entity macro, defined in include/media/media-device.h. + + struct media_entity *entity; + + media_device_for_each_entity(entity, mdev) { + /* entity will point to each entity in turn */ + ... + } + +Drivers might also need to iterate over all entities in a graph that can be +reached only through enabled links starting at a given entity. The media +framework provides a depth-first graph traversal API for that purpose. + +Note that graphs with cycles (whether directed or undirected) are *NOT* +supported by the graph traversal API. To prevent infinite loops, the graph +traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH, +currently defined as 16. + +Drivers initiate a graph traversal by calling + + media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); + +The graph structure, provided by the caller, is initialized to start graph +traversal at the given entity. + +Drivers can then retrieve the next entity by calling + + media_entity_graph_walk_next(struct media_entity_graph *graph); + +When the graph traversal is complete the function will return NULL. + +Graph traversal can be interrupted at any moment. No cleanup function call is +required and the graph structure can be freed normally. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 5c31df9ed765..166f2b5505ce 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -84,6 +84,121 @@ media_entity_cleanup(struct media_entity *entity) } EXPORT_SYMBOL_GPL(media_entity_cleanup); +/* ----------------------------------------------------------------------------- + * Graph traversal + */ + +static struct media_entity * +media_entity_other(struct media_entity *entity, struct media_link *link) +{ + if (link->source->entity == entity) + return link->sink->entity; + else + return link->source->entity; +} + +/* push an entity to traversal stack */ +static void stack_push(struct media_entity_graph *graph, + struct media_entity *entity) +{ + if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { + WARN_ON(1); + return; + } + graph->top++; + graph->stack[graph->top].link = 0; + graph->stack[graph->top].entity = entity; +} + +static struct media_entity *stack_pop(struct media_entity_graph *graph) +{ + struct media_entity *entity; + + entity = graph->stack[graph->top].entity; + graph->top--; + + return entity; +} + +#define stack_peek(en) ((en)->stack[(en)->top - 1].entity) +#define link_top(en) ((en)->stack[(en)->top].link) +#define stack_top(en) ((en)->stack[(en)->top].entity) + +/** + * media_entity_graph_walk_start - Start walking the media graph at a given entity + * @graph: Media graph structure that will be used to walk the graph + * @entity: Starting entity + * + * This function initializes the graph traversal structure to walk the entities + * graph starting at the given entity. The traversal structure must not be + * modified by the caller during graph traversal. When done the structure can + * safely be freed. + */ +void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity) +{ + graph->top = 0; + graph->stack[graph->top].entity = NULL; + stack_push(graph, entity); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); + +/** + * media_entity_graph_walk_next - Get the next entity in the graph + * @graph: Media graph structure + * + * Perform a depth-first traversal of the given media entities graph. + * + * The graph structure must have been previously initialized with a call to + * media_entity_graph_walk_start(). + * + * Return the next entity in the graph or NULL if the whole graph have been + * traversed. + */ +struct media_entity * +media_entity_graph_walk_next(struct media_entity_graph *graph) +{ + if (stack_top(graph) == NULL) + return NULL; + + /* + * Depth first search. Push entity to stack and continue from + * top of the stack until no more entities on the level can be + * found. + */ + while (link_top(graph) < stack_top(graph)->num_links) { + struct media_entity *entity = stack_top(graph); + struct media_link *link = &entity->links[link_top(graph)]; + struct media_entity *next; + + /* The link is not enabled so we do not follow. */ + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { + link_top(graph)++; + continue; + } + + /* Get the entity in the other end of the link . */ + next = media_entity_other(entity, link); + + /* Was it the entity we came here from? */ + if (next == stack_peek(graph)) { + link_top(graph)++; + continue; + } + + /* Push the new entity to stack and start over. */ + link_top(graph)++; + stack_push(graph, next); + } + + return stack_pop(graph); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); + +/* ----------------------------------------------------------------------------- + * Links management + */ + static struct media_link *media_entity_add_link(struct media_entity *entity) { if (entity->num_links >= entity->max_links) { diff --git a/include/media/media-entity.h b/include/media/media-entity.h index f6c856c9ac16..28f61f6ee549 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -113,10 +113,25 @@ static inline u32 media_entity_subtype(struct media_entity *entity) return entity->type & MEDIA_ENT_SUBTYPE_MASK; } +#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 + +struct media_entity_graph { + struct { + struct media_entity *entity; + int link; + } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + int top; +}; + int media_entity_init(struct media_entity *entity, u16 num_pads, struct media_pad *pads, u16 extra_links); void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); +struct media_entity * +media_entity_graph_walk_next(struct media_entity_graph *graph); + #endif -- cgit v1.2.3 From 503c3d829eaf48837dd5bff5d97ad66369bb955a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Sun, 7 Mar 2010 15:04:59 -0300 Subject: [media] media: Entity use count Due to the wide differences between drivers regarding power management needs, the media controller does not implement power management. However, the media_entity structure includes a use_count field that media drivers can use to track the number of users of every entity for power management needs. The use_count field is owned by media drivers and must not be touched by entity drivers. Access to the field must be protected by the media device graph_mutex lock. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- Documentation/media-framework.txt | 13 +++++++++++ drivers/media/media-device.c | 1 + drivers/media/media-entity.c | 46 +++++++++++++++++++++++++++++++++++++++ include/media/media-device.h | 4 ++++ include/media/media-entity.h | 9 ++++++++ 5 files changed, 73 insertions(+) (limited to 'drivers/media/media-entity.c') diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index ab17f33ddedc..78ae02095372 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -258,3 +258,16 @@ When the graph traversal is complete the function will return NULL. Graph traversal can be interrupted at any moment. No cleanup function call is required and the graph structure can be freed normally. + + +Use count and power handling +---------------------------- + +Due to the wide differences between drivers regarding power management needs, +the media controller does not implement power management. However, the +media_entity structure includes a use_count field that media drivers can use to +track the number of users of every entity for power management needs. + +The use_count field is owned by media drivers and must not be touched by entity +drivers. Access to the field must be protected by the media device graph_mutex +lock. diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index a36509a1df09..d2bc809d7a2a 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -73,6 +73,7 @@ int __must_check media_device_register(struct media_device *mdev) mdev->entity_id = 1; INIT_LIST_HEAD(&mdev->entities); spin_lock_init(&mdev->lock); + mutex_init(&mdev->graph_mutex); /* Register the device node. */ mdev->devnode.fops = &media_device_fops; diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 166f2b5505ce..3e7e2d569cec 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <media/media-entity.h> +#include <media/media-device.h> /** * media_entity_init - Initialize a media entity @@ -195,6 +196,51 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) } EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); +/* ----------------------------------------------------------------------------- + * Module use count + */ + +/* + * media_entity_get - Get a reference to the parent module + * @entity: The entity + * + * Get a reference to the parent media device module. + * + * The function will return immediately if @entity is NULL. + * + * Return a pointer to the entity on success or NULL on failure. + */ +struct media_entity *media_entity_get(struct media_entity *entity) +{ + if (entity == NULL) + return NULL; + + if (entity->parent->dev && + !try_module_get(entity->parent->dev->driver->owner)) + return NULL; + + return entity; +} +EXPORT_SYMBOL_GPL(media_entity_get); + +/* + * media_entity_put - Release the reference to the parent module + * @entity: The entity + * + * Release the reference count acquired by media_entity_get(). + * + * The function will return immediately if @entity is NULL. + */ +void media_entity_put(struct media_entity *entity) +{ + if (entity == NULL) + return; + + if (entity->parent->dev) + module_put(entity->parent->dev->driver->owner); +} +EXPORT_SYMBOL_GPL(media_entity_put); + /* ----------------------------------------------------------------------------- * Links management */ diff --git a/include/media/media-device.h b/include/media/media-device.h index a8390fe87e83..5d2bff4fc9e0 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -25,6 +25,7 @@ #include <linux/device.h> #include <linux/list.h> +#include <linux/mutex.h> #include <linux/spinlock.h> #include <media/media-devnode.h> @@ -42,6 +43,7 @@ * @entity_id: ID of the next entity to be registered * @entities: List of registered entities * @lock: Entities list lock + * @graph_mutex: Entities graph operation lock * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The @@ -69,6 +71,8 @@ struct media_device { /* Protects the entities list */ spinlock_t lock; + /* Serializes graph operations. */ + struct mutex graph_mutex; }; /* media_devnode to media_device */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 28f61f6ee549..a9b31d98e3c6 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -81,6 +81,12 @@ struct media_entity { struct media_pad *pads; /* Pads array (num_pads elements) */ struct media_link *links; /* Links array (max_links elements)*/ + /* Reference counts must never be negative, but are signed integers on + * purpose: a simple WARN_ON(<0) check can be used to detect reference + * count bugs that would make them negative. + */ + int use_count; /* Use count for the entity. */ + union { /* Node specifications */ struct { @@ -129,6 +135,9 @@ void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +struct media_entity *media_entity_get(struct media_entity *entity); +void media_entity_put(struct media_entity *entity); + void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * -- cgit v1.2.3 From 97548ed4c4661502cdfd1aabd5d3876fa4f5cc2e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Wed, 9 Dec 2009 08:40:03 -0300 Subject: [media] media: Links setup Create the following ioctl and implement it at the media device level to setup links. - MEDIA_IOC_SETUP_LINK: Modify the properties of a given link The only property that can currently be modified is the ENABLED link flag to enable/disable a link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. Enabling or disabling a link has effects on entities' use count. Those changes are automatically propagated through the graph. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- Documentation/DocBook/media-entities.tmpl | 2 + Documentation/DocBook/v4l/media-controller.xml | 1 + Documentation/DocBook/v4l/media-ioc-setup-link.xml | 90 ++++++++++++ Documentation/media-framework.txt | 42 ++++++ drivers/media/media-device.c | 45 ++++++ drivers/media/media-entity.c | 155 +++++++++++++++++++++ include/linux/media.h | 1 + include/media/media-device.h | 3 + include/media/media-entity.h | 17 +++ 9 files changed, 356 insertions(+) create mode 100644 Documentation/DocBook/v4l/media-ioc-setup-link.xml (limited to 'drivers/media/media-entity.c') diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 2bd7b27f8553..121db1549244 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -94,6 +94,7 @@ <!ENTITY MEDIA-IOC-DEVICE-INFO "<link linkend='media-ioc-device-info'><constant>MEDIA_IOC_DEVICE_INFO</constant></link>"> <!ENTITY MEDIA-IOC-ENUM-ENTITIES "<link linkend='media-ioc-enum-entities'><constant>MEDIA_IOC_ENUM_ENTITIES</constant></link>"> <!ENTITY MEDIA-IOC-ENUM-LINKS "<link linkend='media-ioc-enum-links'><constant>MEDIA_IOC_ENUM_LINKS</constant></link>"> +<!ENTITY MEDIA-IOC-SETUP-LINK "<link linkend='media-ioc-setup-link'><constant>MEDIA_IOC_SETUP_LINK</constant></link>"> <!-- Types --> <!ENTITY v4l2-std-id "<link linkend='v4l2-std-id'>v4l2_std_id</link>"> @@ -348,6 +349,7 @@ <!ENTITY sub-media-ioc-device-info SYSTEM "v4l/media-ioc-device-info.xml"> <!ENTITY sub-media-ioc-enum-entities SYSTEM "v4l/media-ioc-enum-entities.xml"> <!ENTITY sub-media-ioc-enum-links SYSTEM "v4l/media-ioc-enum-links.xml"> +<!ENTITY sub-media-ioc-setup-link SYSTEM "v4l/media-ioc-setup-link.xml"> <!-- Function Reference --> <!ENTITY close SYSTEM "v4l/func-close.xml"> diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index 2c4fd2b27683..2dc25e1d4089 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -85,4 +85,5 @@ &sub-media-ioc-device-info; &sub-media-ioc-enum-entities; &sub-media-ioc-enum-links; + &sub-media-ioc-setup-link; </appendix> diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml new file mode 100644 index 000000000000..09ab3d2b3a52 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml @@ -0,0 +1,90 @@ +<refentry id="media-ioc-setup-link"> + <refmeta> + <refentrytitle>ioctl MEDIA_IOC_SETUP_LINK</refentrytitle> + &manvol; + </refmeta> + + <refnamediv> + <refname>MEDIA_IOC_SETUP_LINK</refname> + <refpurpose>Modify the properties of a link</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>ioctl</function></funcdef> + <paramdef>int <parameter>fd</parameter></paramdef> + <paramdef>int <parameter>request</parameter></paramdef> + <paramdef>struct media_link_desc *<parameter>argp</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Arguments</title> + + <variablelist> + <varlistentry> + <term><parameter>fd</parameter></term> + <listitem> + <para>File descriptor returned by + <link linkend='media-func-open'><function>open()</function></link>.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>request</parameter></term> + <listitem> + <para>MEDIA_IOC_ENUM_LINKS</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>argp</parameter></term> + <listitem> + <para></para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Description</title> + + <para>To change link properties applications fill a &media-link-desc; with + link identification information (source and sink pad) and the new requested + link flags. They then call the MEDIA_IOC_SETUP_LINK ioctl with a pointer to + that structure.</para> + <para>The only configurable property is the <constant>ENABLED</constant> + link flag to enable/disable a link. Links marked with the + <constant>IMMUTABLE</constant> link flag can not be enabled or disabled. + </para> + <para>Link configuration has no side effect on other links. If an enabled + link at the sink pad prevents the link from being enabled, the driver + returns with an &EBUSY;.</para> + <para>If the specified link can't be found the driver returns with an + &EINVAL;.</para> + </refsect1> + + <refsect1> + &return-value; + + <variablelist> + <varlistentry> + <term><errorcode>EBUSY</errorcode></term> + <listitem> + <para>The link properties can't be changed because the link is + currently busy. This can be caused, for instance, by an active media + stream (audio or video) on the link. The ioctl shouldn't be retried if + no other action is performed before to fix the problem.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><errorcode>EINVAL</errorcode></term> + <listitem> + <para>The &media-link-desc; references a non-existing link, or the + link is immutable and an attempt to modify its configuration was made. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> +</refentry> diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 78ae02095372..4809221c0ff9 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -259,6 +259,16 @@ When the graph traversal is complete the function will return NULL. Graph traversal can be interrupted at any moment. No cleanup function call is required and the graph structure can be freed normally. +Helper functions can be used to find a link between two given pads, or a pad +connected to another pad through an enabled link + + media_entity_find_link(struct media_pad *source, + struct media_pad *sink); + + media_entity_remote_source(struct media_pad *pad); + +Refer to the kerneldoc documentation for more information. + Use count and power handling ---------------------------- @@ -271,3 +281,35 @@ track the number of users of every entity for power management needs. The use_count field is owned by media drivers and must not be touched by entity drivers. Access to the field must be protected by the media device graph_mutex lock. + + +Links setup +----------- + +Link properties can be modified at runtime by calling + + media_entity_setup_link(struct media_link *link, u32 flags); + +The flags argument contains the requested new link flags. + +The only configurable property is the ENABLED link flag to enable/disable a +link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. + +When a link is enabled or disabled, the media framework calls the +link_setup operation for the two entities at the source and sink of the link, +in that order. If the second link_setup call fails, another link_setup call is +made on the first entity to restore the original link flags. + +Media device drivers can be notified of link setup operations by setting the +media_device::link_notify pointer to a callback function. If provided, the +notification callback will be called before enabling and after disabling +links. + +Entity drivers must implement the link_setup operation if any of their links +is non-immutable. The operation must either configure the hardware or store +the configuration information to be applied later. + +Link configuration must not have any side effect on other links. If an enabled +link at a sink pad prevents another link at the same pad from being disabled, +the link_setup operation must return -EBUSY and can't implicitly disable the +first enabled link. diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 648a9d892ac1..16b70b4412f7 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -172,6 +172,44 @@ static long media_device_enum_links(struct media_device *mdev, return 0; } +static long media_device_setup_link(struct media_device *mdev, + struct media_link_desc __user *_ulink) +{ + struct media_link *link = NULL; + struct media_link_desc ulink; + struct media_entity *source; + struct media_entity *sink; + int ret; + + if (copy_from_user(&ulink, _ulink, sizeof(ulink))) + return -EFAULT; + + /* Find the source and sink entities and link. + */ + source = find_entity(mdev, ulink.source.entity); + sink = find_entity(mdev, ulink.sink.entity); + + if (source == NULL || sink == NULL) + return -EINVAL; + + if (ulink.source.index >= source->num_pads || + ulink.sink.index >= sink->num_pads) + return -EINVAL; + + link = media_entity_find_link(&source->pads[ulink.source.index], + &sink->pads[ulink.sink.index]); + if (link == NULL) + return -EINVAL; + + /* Setup the link on both entities. */ + ret = __media_entity_setup_link(link, ulink.flags); + + if (copy_to_user(_ulink, &ulink, sizeof(ulink))) + return -EFAULT; + + return ret; +} + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -197,6 +235,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, mutex_unlock(&dev->graph_mutex); break; + case MEDIA_IOC_SETUP_LINK: + mutex_lock(&dev->graph_mutex); + ret = media_device_setup_link(dev, + (struct media_link_desc __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; + default: ret = -ENOIOCTLCMD; } diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 3e7e2d569cec..6795c920d460 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -306,3 +306,158 @@ 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; + + 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); diff --git a/include/linux/media.h b/include/linux/media.h index 17c93a413677..7c69913c0ad2 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -126,5 +126,6 @@ struct media_links_enum { #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) +#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) #endif /* __LINUX_MEDIA_H */ diff --git a/include/media/media-device.h b/include/media/media-device.h index 5d2bff4fc9e0..6a27d916c250 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -73,6 +73,9 @@ struct media_device { spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; + + int (*link_notify)(struct media_pad *source, + struct media_pad *sink, u32 flags); }; /* media_devnode to media_device */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 51bdafce72c7..d889dcc67d0d 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -39,6 +39,12 @@ struct media_pad { unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ }; +struct media_entity_operations { + int (*link_setup)(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags); +}; + struct media_entity { struct list_head list; struct media_device *parent; /* Media device this entity belongs to*/ @@ -59,6 +65,8 @@ struct media_entity { struct media_pad *pads; /* Pads array (num_pads elements) */ struct media_link *links; /* Links array (max_links elements)*/ + const struct media_entity_operations *ops; /* Entity operations */ + /* Reference counts must never be negative, but are signed integers on * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. @@ -112,6 +120,11 @@ int media_entity_init(struct media_entity *entity, u16 num_pads, void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +int __media_entity_setup_link(struct media_link *link, u32 flags); +int media_entity_setup_link(struct media_link *link, u32 flags); +struct media_link *media_entity_find_link(struct media_pad *source, + struct media_pad *sink); +struct media_pad *media_entity_remote_source(struct media_pad *pad); struct media_entity *media_entity_get(struct media_entity *entity); void media_entity_put(struct media_entity *entity); @@ -121,4 +134,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); +#define media_entity_call(entity, operation, args...) \ + (((entity)->ops && (entity)->ops->operation) ? \ + (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) + #endif -- cgit v1.2.3 From e02188c90f6ef61f0844c42508fe603c5d4fa42b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Wed, 25 Aug 2010 09:00:41 -0300 Subject: [media] media: Pipelines and media streams Drivers often need to associate pipeline objects to entities, and to take stream state into account when configuring entities and links. The pipeline API helps drivers manage that information. When starting streaming, drivers call media_entity_pipeline_start(). The function marks all entities connected to the given entity through enabled links, either directly or indirectly, as streaming. Similarly, when stopping the stream, drivers call media_entity_pipeline_stop(). The media_entity_pipeline_start() function takes a pointer to a media pipeline and stores it in every entity in the graph. Drivers should embed the media_pipeline structure in higher-level pipeline structures and can then access the pipeline through the media_entity structure. Link configuration will fail with -EBUSY by default if either end of the link is a streaming entity, unless the link is marked with the MEDIA_LNK_FL_DYNAMIC flag. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- Documentation/DocBook/v4l/media-ioc-enum-links.xml | 5 ++ Documentation/DocBook/v4l/media-ioc-setup-link.xml | 3 + Documentation/media-framework.txt | 38 +++++++++++ drivers/media/media-entity.c | 73 ++++++++++++++++++++++ include/linux/media.h | 1 + include/media/media-entity.h | 10 +++ 6 files changed, 130 insertions(+) (limited to 'drivers/media/media-entity.c') diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/v4l/media-ioc-enum-links.xml index 6da884159cab..d2fc73ef8d56 100644 --- a/Documentation/DocBook/v4l/media-ioc-enum-links.xml +++ b/Documentation/DocBook/v4l/media-ioc-enum-links.xml @@ -179,6 +179,11 @@ <entry>The link enabled state can't be modified at runtime. An immutable link is always enabled.</entry> </row> + <row> + <entry><constant>MEDIA_LNK_FL_DYNAMIC</constant></entry> + <entry>The link enabled state can be modified during streaming. This + flag is set by drivers and is read-only for applications.</entry> + </row> </tbody> </tgroup> </table> diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml index 09ab3d2b3a52..2331e76ded17 100644 --- a/Documentation/DocBook/v4l/media-ioc-setup-link.xml +++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml @@ -60,6 +60,9 @@ <para>Link configuration has no side effect on other links. If an enabled link at the sink pad prevents the link from being enabled, the driver returns with an &EBUSY;.</para> + <para>Only links marked with the <constant>DYNAMIC</constant> link flag can + be enabled/disabled while streaming media data. Attempting to enable or + disable a streaming non-dynamic link will return an &EBUSY;.</para> <para>If the specified link can't be found the driver returns with an &EINVAL;.</para> </refsect1> diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 4809221c0ff9..fd48add02cb0 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -313,3 +313,41 @@ Link configuration must not have any side effect on other links. If an enabled link at a sink pad prevents another link at the same pad from being disabled, the link_setup operation must return -EBUSY and can't implicitly disable the first enabled link. + + +Pipelines and media streams +--------------------------- + +When starting streaming, drivers must notify all entities in the pipeline to +prevent link states from being modified during streaming by calling + + media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); + +The function will mark all entities connected to the given entity through +enabled links, either directly or indirectly, as streaming. + +The media_pipeline instance pointed to by the pipe argument will be stored in +every entity in the pipeline. Drivers should embed the media_pipeline structure +in higher-level pipeline structures and can then access the pipeline through +the media_entity pipe field. + +Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must +be identical for all nested calls to the function. + +When stopping the stream, drivers must notify the entities with + + media_entity_pipeline_stop(struct media_entity *entity); + +If multiple calls to media_entity_pipeline_start() have been made the same +number of media_entity_pipeline_stop() calls are required to stop streaming. The +media_entity pipe field is reset to NULL on the last nested stop call. + +Link configuration will fail with -EBUSY by default if either end of the link is +a streaming entity. Links that can be modified while streaming must be marked +with the MEDIA_LNK_FL_DYNAMIC flag. + +If other operations need to be disallowed on streaming entities (such as +changing entities configuration parameters) drivers can explictly check the +media_entity stream_count field to find out if an entity is streaming. This +operation must be done with the media_device graph_mutex held. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 6795c920d460..23640ed44d85 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -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 */ @@ -364,6 +433,10 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) 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) { diff --git a/include/linux/media.h b/include/linux/media.h index 7c69913c0ad2..7ed23b43f43b 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -106,6 +106,7 @@ struct media_pad_desc { #define MEDIA_LNK_FL_ENABLED (1 << 0) #define MEDIA_LNK_FL_IMMUTABLE (1 << 1) +#define MEDIA_LNK_FL_DYNAMIC (1 << 2) struct media_link_desc { struct media_pad_desc source; diff --git a/include/media/media-entity.h b/include/media/media-entity.h index d889dcc67d0d..cd8bca63a502 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -26,6 +26,9 @@ #include <linux/list.h> #include <linux/media.h> +struct media_pipeline { +}; + struct media_link { struct media_pad *source; /* Source pad */ struct media_pad *sink; /* Sink pad */ @@ -71,8 +74,11 @@ struct media_entity { * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. */ + int stream_count; /* Stream count for the entity. */ int use_count; /* Use count for the entity. */ + struct media_pipeline *pipe; /* Pipeline this entity belongs to. */ + union { /* Node specifications */ struct { @@ -118,6 +124,7 @@ struct media_entity_graph { int media_entity_init(struct media_entity *entity, u16 num_pads, struct media_pad *pads, u16 extra_links); void media_entity_cleanup(struct media_entity *entity); + int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); int __media_entity_setup_link(struct media_link *link, u32 flags); @@ -133,6 +140,9 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); +void media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); +void media_entity_pipeline_stop(struct media_entity *entity); #define media_entity_call(entity, operation, args...) \ (((entity)->ops && (entity)->ops->operation) ? \ -- cgit v1.2.3