/*1* Media entity2*3* Copyright (C) 2010 Nokia Corporation4*5* Contacts: Laurent Pinchart <[email protected]>6* Sakari Ailus <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*/2122#include <linux/module.h>23#include <linux/slab.h>24#include <media/media-entity.h>25#include <media/media-device.h>2627/**28* media_entity_init - Initialize a media entity29*30* @num_pads: Total number of sink and source pads.31* @extra_links: Initial estimate of the number of extra links.32* @pads: Array of 'num_pads' pads.33*34* The total number of pads is an intrinsic property of entities known by the35* entity driver, while the total number of links depends on hardware design36* and is an extrinsic property unknown to the entity driver. However, in most37* use cases the entity driver can guess the number of links which can safely38* be assumed to be equal to or larger than the number of pads.39*40* For those reasons the links array can be preallocated based on the entity41* driver guess and will be reallocated later if extra links need to be42* created.43*44* This function allocates a links array with enough space to hold at least45* 'num_pads' + 'extra_links' elements. The media_entity::max_links field will46* be set to the number of allocated elements.47*48* The pads array is managed by the entity driver and passed to49* media_entity_init() where its pointer will be stored in the entity structure.50*/51int52media_entity_init(struct media_entity *entity, u16 num_pads,53struct media_pad *pads, u16 extra_links)54{55struct media_link *links;56unsigned int max_links = num_pads + extra_links;57unsigned int i;5859links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL);60if (links == NULL)61return -ENOMEM;6263entity->group_id = 0;64entity->max_links = max_links;65entity->num_links = 0;66entity->num_backlinks = 0;67entity->num_pads = num_pads;68entity->pads = pads;69entity->links = links;7071for (i = 0; i < num_pads; i++) {72pads[i].entity = entity;73pads[i].index = i;74}7576return 0;77}78EXPORT_SYMBOL_GPL(media_entity_init);7980void81media_entity_cleanup(struct media_entity *entity)82{83kfree(entity->links);84}85EXPORT_SYMBOL_GPL(media_entity_cleanup);8687/* -----------------------------------------------------------------------------88* Graph traversal89*/9091static struct media_entity *92media_entity_other(struct media_entity *entity, struct media_link *link)93{94if (link->source->entity == entity)95return link->sink->entity;96else97return link->source->entity;98}99100/* push an entity to traversal stack */101static void stack_push(struct media_entity_graph *graph,102struct media_entity *entity)103{104if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {105WARN_ON(1);106return;107}108graph->top++;109graph->stack[graph->top].link = 0;110graph->stack[graph->top].entity = entity;111}112113static struct media_entity *stack_pop(struct media_entity_graph *graph)114{115struct media_entity *entity;116117entity = graph->stack[graph->top].entity;118graph->top--;119120return entity;121}122123#define stack_peek(en) ((en)->stack[(en)->top - 1].entity)124#define link_top(en) ((en)->stack[(en)->top].link)125#define stack_top(en) ((en)->stack[(en)->top].entity)126127/**128* media_entity_graph_walk_start - Start walking the media graph at a given entity129* @graph: Media graph structure that will be used to walk the graph130* @entity: Starting entity131*132* This function initializes the graph traversal structure to walk the entities133* graph starting at the given entity. The traversal structure must not be134* modified by the caller during graph traversal. When done the structure can135* safely be freed.136*/137void media_entity_graph_walk_start(struct media_entity_graph *graph,138struct media_entity *entity)139{140graph->top = 0;141graph->stack[graph->top].entity = NULL;142stack_push(graph, entity);143}144EXPORT_SYMBOL_GPL(media_entity_graph_walk_start);145146/**147* media_entity_graph_walk_next - Get the next entity in the graph148* @graph: Media graph structure149*150* Perform a depth-first traversal of the given media entities graph.151*152* The graph structure must have been previously initialized with a call to153* media_entity_graph_walk_start().154*155* Return the next entity in the graph or NULL if the whole graph have been156* traversed.157*/158struct media_entity *159media_entity_graph_walk_next(struct media_entity_graph *graph)160{161if (stack_top(graph) == NULL)162return NULL;163164/*165* Depth first search. Push entity to stack and continue from166* top of the stack until no more entities on the level can be167* found.168*/169while (link_top(graph) < stack_top(graph)->num_links) {170struct media_entity *entity = stack_top(graph);171struct media_link *link = &entity->links[link_top(graph)];172struct media_entity *next;173174/* The link is not enabled so we do not follow. */175if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {176link_top(graph)++;177continue;178}179180/* Get the entity in the other end of the link . */181next = media_entity_other(entity, link);182183/* Was it the entity we came here from? */184if (next == stack_peek(graph)) {185link_top(graph)++;186continue;187}188189/* Push the new entity to stack and start over. */190link_top(graph)++;191stack_push(graph, next);192}193194return stack_pop(graph);195}196EXPORT_SYMBOL_GPL(media_entity_graph_walk_next);197198/* -----------------------------------------------------------------------------199* Pipeline management200*/201202/**203* media_entity_pipeline_start - Mark a pipeline as streaming204* @entity: Starting entity205* @pipe: Media pipeline to be assigned to all entities in the pipeline.206*207* Mark all entities connected to a given entity through enabled links, either208* directly or indirectly, as streaming. The given pipeline object is assigned to209* every entity in the pipeline and stored in the media_entity pipe field.210*211* Calls to this function can be nested, in which case the same number of212* media_entity_pipeline_stop() calls will be required to stop streaming. The213* pipeline pointer must be identical for all nested calls to214* media_entity_pipeline_start().215*/216void media_entity_pipeline_start(struct media_entity *entity,217struct media_pipeline *pipe)218{219struct media_device *mdev = entity->parent;220struct media_entity_graph graph;221222mutex_lock(&mdev->graph_mutex);223224media_entity_graph_walk_start(&graph, entity);225226while ((entity = media_entity_graph_walk_next(&graph))) {227entity->stream_count++;228WARN_ON(entity->pipe && entity->pipe != pipe);229entity->pipe = pipe;230}231232mutex_unlock(&mdev->graph_mutex);233}234EXPORT_SYMBOL_GPL(media_entity_pipeline_start);235236/**237* media_entity_pipeline_stop - Mark a pipeline as not streaming238* @entity: Starting entity239*240* Mark all entities connected to a given entity through enabled links, either241* directly or indirectly, as not streaming. The media_entity pipe field is242* reset to NULL.243*244* If multiple calls to media_entity_pipeline_start() have been made, the same245* number of calls to this function are required to mark the pipeline as not246* streaming.247*/248void media_entity_pipeline_stop(struct media_entity *entity)249{250struct media_device *mdev = entity->parent;251struct media_entity_graph graph;252253mutex_lock(&mdev->graph_mutex);254255media_entity_graph_walk_start(&graph, entity);256257while ((entity = media_entity_graph_walk_next(&graph))) {258entity->stream_count--;259if (entity->stream_count == 0)260entity->pipe = NULL;261}262263mutex_unlock(&mdev->graph_mutex);264}265EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);266267/* -----------------------------------------------------------------------------268* Module use count269*/270271/*272* media_entity_get - Get a reference to the parent module273* @entity: The entity274*275* Get a reference to the parent media device module.276*277* The function will return immediately if @entity is NULL.278*279* Return a pointer to the entity on success or NULL on failure.280*/281struct media_entity *media_entity_get(struct media_entity *entity)282{283if (entity == NULL)284return NULL;285286if (entity->parent->dev &&287!try_module_get(entity->parent->dev->driver->owner))288return NULL;289290return entity;291}292EXPORT_SYMBOL_GPL(media_entity_get);293294/*295* media_entity_put - Release the reference to the parent module296* @entity: The entity297*298* Release the reference count acquired by media_entity_get().299*300* The function will return immediately if @entity is NULL.301*/302void media_entity_put(struct media_entity *entity)303{304if (entity == NULL)305return;306307if (entity->parent->dev)308module_put(entity->parent->dev->driver->owner);309}310EXPORT_SYMBOL_GPL(media_entity_put);311312/* -----------------------------------------------------------------------------313* Links management314*/315316static struct media_link *media_entity_add_link(struct media_entity *entity)317{318if (entity->num_links >= entity->max_links) {319struct media_link *links = entity->links;320unsigned int max_links = entity->max_links + 2;321unsigned int i;322323links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL);324if (links == NULL)325return NULL;326327for (i = 0; i < entity->num_links; i++)328links[i].reverse->reverse = &links[i];329330entity->max_links = max_links;331entity->links = links;332}333334return &entity->links[entity->num_links++];335}336337int338media_entity_create_link(struct media_entity *source, u16 source_pad,339struct media_entity *sink, u16 sink_pad, u32 flags)340{341struct media_link *link;342struct media_link *backlink;343344BUG_ON(source == NULL || sink == NULL);345BUG_ON(source_pad >= source->num_pads);346BUG_ON(sink_pad >= sink->num_pads);347348link = media_entity_add_link(source);349if (link == NULL)350return -ENOMEM;351352link->source = &source->pads[source_pad];353link->sink = &sink->pads[sink_pad];354link->flags = flags;355356/* Create the backlink. Backlinks are used to help graph traversal and357* are not reported to userspace.358*/359backlink = media_entity_add_link(sink);360if (backlink == NULL) {361source->num_links--;362return -ENOMEM;363}364365backlink->source = &source->pads[source_pad];366backlink->sink = &sink->pads[sink_pad];367backlink->flags = flags;368369link->reverse = backlink;370backlink->reverse = link;371372sink->num_backlinks++;373374return 0;375}376EXPORT_SYMBOL_GPL(media_entity_create_link);377378static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)379{380int ret;381382/* Notify both entities. */383ret = media_entity_call(link->source->entity, link_setup,384link->source, link->sink, flags);385if (ret < 0 && ret != -ENOIOCTLCMD)386return ret;387388ret = media_entity_call(link->sink->entity, link_setup,389link->sink, link->source, flags);390if (ret < 0 && ret != -ENOIOCTLCMD) {391media_entity_call(link->source->entity, link_setup,392link->source, link->sink, link->flags);393return ret;394}395396link->flags = flags;397link->reverse->flags = link->flags;398399return 0;400}401402/**403* __media_entity_setup_link - Configure a media link404* @link: The link being configured405* @flags: Link configuration flags406*407* The bulk of link setup is handled by the two entities connected through the408* link. This function notifies both entities of the link configuration change.409*410* If the link is immutable or if the current and new configuration are411* identical, return immediately.412*413* The user is expected to hold link->source->parent->mutex. If not,414* media_entity_setup_link() should be used instead.415*/416int __media_entity_setup_link(struct media_link *link, u32 flags)417{418const u32 mask = MEDIA_LNK_FL_ENABLED;419struct media_device *mdev;420struct media_entity *source, *sink;421int ret = -EBUSY;422423if (link == NULL)424return -EINVAL;425426/* The non-modifiable link flags must not be modified. */427if ((link->flags & ~mask) != (flags & ~mask))428return -EINVAL;429430if (link->flags & MEDIA_LNK_FL_IMMUTABLE)431return link->flags == flags ? 0 : -EINVAL;432433if (link->flags == flags)434return 0;435436source = link->source->entity;437sink = link->sink->entity;438439if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&440(source->stream_count || sink->stream_count))441return -EBUSY;442443mdev = source->parent;444445if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) {446ret = mdev->link_notify(link->source, link->sink,447MEDIA_LNK_FL_ENABLED);448if (ret < 0)449return ret;450}451452ret = __media_entity_setup_link_notify(link, flags);453if (ret < 0)454goto err;455456if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)457mdev->link_notify(link->source, link->sink, 0);458459return 0;460461err:462if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)463mdev->link_notify(link->source, link->sink, 0);464465return ret;466}467468int media_entity_setup_link(struct media_link *link, u32 flags)469{470int ret;471472mutex_lock(&link->source->entity->parent->graph_mutex);473ret = __media_entity_setup_link(link, flags);474mutex_unlock(&link->source->entity->parent->graph_mutex);475476return ret;477}478EXPORT_SYMBOL_GPL(media_entity_setup_link);479480/**481* media_entity_find_link - Find a link between two pads482* @source: Source pad483* @sink: Sink pad484*485* Return a pointer to the link between the two entities. If no such link486* exists, return NULL.487*/488struct media_link *489media_entity_find_link(struct media_pad *source, struct media_pad *sink)490{491struct media_link *link;492unsigned int i;493494for (i = 0; i < source->entity->num_links; ++i) {495link = &source->entity->links[i];496497if (link->source->entity == source->entity &&498link->source->index == source->index &&499link->sink->entity == sink->entity &&500link->sink->index == sink->index)501return link;502}503504return NULL;505}506EXPORT_SYMBOL_GPL(media_entity_find_link);507508/**509* media_entity_remote_source - Find the source pad at the remote end of a link510* @pad: Sink pad at the local end of the link511*512* Search for a remote source pad connected to the given sink pad by iterating513* over all links originating or terminating at that pad until an enabled link514* is found.515*516* Return a pointer to the pad at the remote end of the first found enabled517* link, or NULL if no enabled link has been found.518*/519struct media_pad *media_entity_remote_source(struct media_pad *pad)520{521unsigned int i;522523for (i = 0; i < pad->entity->num_links; i++) {524struct media_link *link = &pad->entity->links[i];525526if (!(link->flags & MEDIA_LNK_FL_ENABLED))527continue;528529if (link->source == pad)530return link->sink;531532if (link->sink == pad)533return link->source;534}535536return NULL;537538}539EXPORT_SYMBOL_GPL(media_entity_remote_source);540541542