1676 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Media entity4 *5 * Copyright (C) 2010 Nokia Corporation6 *7 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>8 * Sakari Ailus <sakari.ailus@iki.fi>9 */10 11#include <linux/bitmap.h>12#include <linux/list.h>13#include <linux/property.h>14#include <linux/slab.h>15#include <media/media-entity.h>16#include <media/media-device.h>17 18static inline const char *intf_type(struct media_interface *intf)19{20 switch (intf->type) {21 case MEDIA_INTF_T_DVB_FE:22 return "dvb-frontend";23 case MEDIA_INTF_T_DVB_DEMUX:24 return "dvb-demux";25 case MEDIA_INTF_T_DVB_DVR:26 return "dvb-dvr";27 case MEDIA_INTF_T_DVB_CA:28 return "dvb-ca";29 case MEDIA_INTF_T_DVB_NET:30 return "dvb-net";31 case MEDIA_INTF_T_V4L_VIDEO:32 return "v4l-video";33 case MEDIA_INTF_T_V4L_VBI:34 return "v4l-vbi";35 case MEDIA_INTF_T_V4L_RADIO:36 return "v4l-radio";37 case MEDIA_INTF_T_V4L_SUBDEV:38 return "v4l-subdev";39 case MEDIA_INTF_T_V4L_SWRADIO:40 return "v4l-swradio";41 case MEDIA_INTF_T_V4L_TOUCH:42 return "v4l-touch";43 default:44 return "unknown-intf";45 }46};47 48static inline const char *link_type_name(struct media_link *link)49{50 switch (link->flags & MEDIA_LNK_FL_LINK_TYPE) {51 case MEDIA_LNK_FL_DATA_LINK:52 return "data";53 case MEDIA_LNK_FL_INTERFACE_LINK:54 return "interface";55 case MEDIA_LNK_FL_ANCILLARY_LINK:56 return "ancillary";57 default:58 return "unknown";59 }60}61 62__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum,63 struct media_device *mdev)64{65 int idx_max;66 67 idx_max = ALIGN(mdev->entity_internal_idx_max + 1, BITS_PER_LONG);68 ent_enum->bmap = bitmap_zalloc(idx_max, GFP_KERNEL);69 if (!ent_enum->bmap)70 return -ENOMEM;71 72 ent_enum->idx_max = idx_max;73 74 return 0;75}76EXPORT_SYMBOL_GPL(media_entity_enum_init);77 78void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)79{80 bitmap_free(ent_enum->bmap);81}82EXPORT_SYMBOL_GPL(media_entity_enum_cleanup);83 84/**85 * dev_dbg_obj - Prints in debug mode a change on some object86 *87 * @event_name: Name of the event to report. Could be __func__88 * @gobj: Pointer to the object89 *90 * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it91 * won't produce any code.92 */93static void dev_dbg_obj(const char *event_name, struct media_gobj *gobj)94{95#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG)96 switch (media_type(gobj)) {97 case MEDIA_GRAPH_ENTITY:98 dev_dbg(gobj->mdev->dev,99 "%s id %u: entity '%s'\n",100 event_name, media_id(gobj),101 gobj_to_entity(gobj)->name);102 break;103 case MEDIA_GRAPH_LINK:104 {105 struct media_link *link = gobj_to_link(gobj);106 107 dev_dbg(gobj->mdev->dev,108 "%s id %u: %s link id %u ==> id %u\n",109 event_name, media_id(gobj), link_type_name(link),110 media_id(link->gobj0),111 media_id(link->gobj1));112 break;113 }114 case MEDIA_GRAPH_PAD:115 {116 struct media_pad *pad = gobj_to_pad(gobj);117 118 dev_dbg(gobj->mdev->dev,119 "%s id %u: %s%spad '%s':%d\n",120 event_name, media_id(gobj),121 pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "",122 pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "",123 pad->entity->name, pad->index);124 break;125 }126 case MEDIA_GRAPH_INTF_DEVNODE:127 {128 struct media_interface *intf = gobj_to_intf(gobj);129 struct media_intf_devnode *devnode = intf_to_devnode(intf);130 131 dev_dbg(gobj->mdev->dev,132 "%s id %u: intf_devnode %s - major: %d, minor: %d\n",133 event_name, media_id(gobj),134 intf_type(intf),135 devnode->major, devnode->minor);136 break;137 }138 }139#endif140}141 142void media_gobj_create(struct media_device *mdev,143 enum media_gobj_type type,144 struct media_gobj *gobj)145{146 BUG_ON(!mdev);147 148 gobj->mdev = mdev;149 150 /* Create a per-type unique object ID */151 gobj->id = media_gobj_gen_id(type, ++mdev->id);152 153 switch (type) {154 case MEDIA_GRAPH_ENTITY:155 list_add_tail(&gobj->list, &mdev->entities);156 break;157 case MEDIA_GRAPH_PAD:158 list_add_tail(&gobj->list, &mdev->pads);159 break;160 case MEDIA_GRAPH_LINK:161 list_add_tail(&gobj->list, &mdev->links);162 break;163 case MEDIA_GRAPH_INTF_DEVNODE:164 list_add_tail(&gobj->list, &mdev->interfaces);165 break;166 }167 168 mdev->topology_version++;169 170 dev_dbg_obj(__func__, gobj);171}172 173void media_gobj_destroy(struct media_gobj *gobj)174{175 /* Do nothing if the object is not linked. */176 if (gobj->mdev == NULL)177 return;178 179 dev_dbg_obj(__func__, gobj);180 181 gobj->mdev->topology_version++;182 183 /* Remove the object from mdev list */184 list_del(&gobj->list);185 186 gobj->mdev = NULL;187}188 189/*190 * TODO: Get rid of this.191 */192#define MEDIA_ENTITY_MAX_PADS 512193 194int media_entity_pads_init(struct media_entity *entity, u16 num_pads,195 struct media_pad *pads)196{197 struct media_device *mdev = entity->graph_obj.mdev;198 struct media_pad *iter;199 unsigned int i = 0;200 int ret = 0;201 202 if (num_pads >= MEDIA_ENTITY_MAX_PADS)203 return -E2BIG;204 205 entity->num_pads = num_pads;206 entity->pads = pads;207 208 if (mdev)209 mutex_lock(&mdev->graph_mutex);210 211 media_entity_for_each_pad(entity, iter) {212 iter->entity = entity;213 iter->index = i++;214 215 if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK |216 MEDIA_PAD_FL_SOURCE)) != 1) {217 ret = -EINVAL;218 break;219 }220 221 if (mdev)222 media_gobj_create(mdev, MEDIA_GRAPH_PAD,223 &iter->graph_obj);224 }225 226 if (ret && mdev) {227 media_entity_for_each_pad(entity, iter)228 media_gobj_destroy(&iter->graph_obj);229 }230 231 if (mdev)232 mutex_unlock(&mdev->graph_mutex);233 234 return ret;235}236EXPORT_SYMBOL_GPL(media_entity_pads_init);237 238/* -----------------------------------------------------------------------------239 * Graph traversal240 */241 242/**243 * media_entity_has_pad_interdep - Check interdependency between two pads244 *245 * @entity: The entity246 * @pad0: The first pad index247 * @pad1: The second pad index248 *249 * This function checks the interdependency inside the entity between @pad0250 * and @pad1. If two pads are interdependent they are part of the same pipeline251 * and enabling one of the pads means that the other pad will become "locked"252 * and doesn't allow configuration changes.253 *254 * This function uses the &media_entity_operations.has_pad_interdep() operation255 * to check the dependency inside the entity between @pad0 and @pad1. If the256 * has_pad_interdep operation is not implemented, all pads of the entity are257 * considered to be interdependent.258 *259 * One of @pad0 and @pad1 must be a sink pad and the other one a source pad.260 * The function returns false if both pads are sinks or sources.261 *262 * The caller must hold entity->graph_obj.mdev->mutex.263 *264 * Return: true if the pads are connected internally and false otherwise.265 */266static bool media_entity_has_pad_interdep(struct media_entity *entity,267 unsigned int pad0, unsigned int pad1)268{269 if (pad0 >= entity->num_pads || pad1 >= entity->num_pads)270 return false;271 272 if (entity->pads[pad0].flags & entity->pads[pad1].flags &273 (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE))274 return false;275 276 if (!entity->ops || !entity->ops->has_pad_interdep)277 return true;278 279 return entity->ops->has_pad_interdep(entity, pad0, pad1);280}281 282static struct media_entity *283media_entity_other(struct media_entity *entity, struct media_link *link)284{285 if (link->source->entity == entity)286 return link->sink->entity;287 else288 return link->source->entity;289}290 291/* push an entity to traversal stack */292static void stack_push(struct media_graph *graph,293 struct media_entity *entity)294{295 if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {296 WARN_ON(1);297 return;298 }299 graph->top++;300 graph->stack[graph->top].link = entity->links.next;301 graph->stack[graph->top].entity = entity;302}303 304static struct media_entity *stack_pop(struct media_graph *graph)305{306 struct media_entity *entity;307 308 entity = graph->stack[graph->top].entity;309 graph->top--;310 311 return entity;312}313 314#define link_top(en) ((en)->stack[(en)->top].link)315#define stack_top(en) ((en)->stack[(en)->top].entity)316 317/**318 * media_graph_walk_init - Allocate resources for graph walk319 * @graph: Media graph structure that will be used to walk the graph320 * @mdev: Media device321 *322 * Reserve resources for graph walk in media device's current323 * state. The memory must be released using324 * media_graph_walk_cleanup().325 *326 * Returns error on failure, zero on success.327 */328__must_check int media_graph_walk_init(329 struct media_graph *graph, struct media_device *mdev)330{331 return media_entity_enum_init(&graph->ent_enum, mdev);332}333EXPORT_SYMBOL_GPL(media_graph_walk_init);334 335/**336 * media_graph_walk_cleanup - Release resources related to graph walking337 * @graph: Media graph structure that was used to walk the graph338 */339void media_graph_walk_cleanup(struct media_graph *graph)340{341 media_entity_enum_cleanup(&graph->ent_enum);342}343EXPORT_SYMBOL_GPL(media_graph_walk_cleanup);344 345void media_graph_walk_start(struct media_graph *graph,346 struct media_entity *entity)347{348 media_entity_enum_zero(&graph->ent_enum);349 media_entity_enum_set(&graph->ent_enum, entity);350 351 graph->top = 0;352 graph->stack[graph->top].entity = NULL;353 stack_push(graph, entity);354 dev_dbg(entity->graph_obj.mdev->dev,355 "begin graph walk at '%s'\n", entity->name);356}357EXPORT_SYMBOL_GPL(media_graph_walk_start);358 359static void media_graph_walk_iter(struct media_graph *graph)360{361 struct media_entity *entity = stack_top(graph);362 struct media_link *link;363 struct media_entity *next;364 365 link = list_entry(link_top(graph), typeof(*link), list);366 367 /* If the link is not a data link, don't follow it */368 if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {369 link_top(graph) = link_top(graph)->next;370 return;371 }372 373 /* The link is not enabled so we do not follow. */374 if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {375 link_top(graph) = link_top(graph)->next;376 dev_dbg(entity->graph_obj.mdev->dev,377 "walk: skipping disabled link '%s':%u -> '%s':%u\n",378 link->source->entity->name, link->source->index,379 link->sink->entity->name, link->sink->index);380 return;381 }382 383 /* Get the entity at the other end of the link. */384 next = media_entity_other(entity, link);385 386 /* Has the entity already been visited? */387 if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {388 link_top(graph) = link_top(graph)->next;389 dev_dbg(entity->graph_obj.mdev->dev,390 "walk: skipping entity '%s' (already seen)\n",391 next->name);392 return;393 }394 395 /* Push the new entity to stack and start over. */396 link_top(graph) = link_top(graph)->next;397 stack_push(graph, next);398 dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",399 next->name);400 lockdep_assert_held(&entity->graph_obj.mdev->graph_mutex);401}402 403struct media_entity *media_graph_walk_next(struct media_graph *graph)404{405 struct media_entity *entity;406 407 if (stack_top(graph) == NULL)408 return NULL;409 410 /*411 * Depth first search. Push entity to stack and continue from412 * top of the stack until no more entities on the level can be413 * found.414 */415 while (link_top(graph) != &stack_top(graph)->links)416 media_graph_walk_iter(graph);417 418 entity = stack_pop(graph);419 dev_dbg(entity->graph_obj.mdev->dev,420 "walk: returning entity '%s'\n", entity->name);421 422 return entity;423}424EXPORT_SYMBOL_GPL(media_graph_walk_next);425 426/* -----------------------------------------------------------------------------427 * Pipeline management428 */429 430/*431 * The pipeline traversal stack stores pads that are reached during graph432 * traversal, with a list of links to be visited to continue the traversal.433 * When a new pad is reached, an entry is pushed on the top of the stack and434 * points to the incoming pad and the first link of the entity.435 *436 * To find further pads in the pipeline, the traversal algorithm follows437 * internal pad dependencies in the entity, and then links in the graph. It438 * does so by iterating over all links of the entity, and following enabled439 * links that originate from a pad that is internally connected to the incoming440 * pad, as reported by the media_entity_has_pad_interdep() function.441 */442 443/**444 * struct media_pipeline_walk_entry - Entry in the pipeline traversal stack445 *446 * @pad: The media pad being visited447 * @links: Links left to be visited448 */449struct media_pipeline_walk_entry {450 struct media_pad *pad;451 struct list_head *links;452};453 454/**455 * struct media_pipeline_walk - State used by the media pipeline traversal456 * algorithm457 *458 * @mdev: The media device459 * @stack: Depth-first search stack460 * @stack.size: Number of allocated entries in @stack.entries461 * @stack.top: Index of the top stack entry (-1 if the stack is empty)462 * @stack.entries: Stack entries463 */464struct media_pipeline_walk {465 struct media_device *mdev;466 467 struct {468 unsigned int size;469 int top;470 struct media_pipeline_walk_entry *entries;471 } stack;472};473 474#define MEDIA_PIPELINE_STACK_GROW_STEP 16475 476static struct media_pipeline_walk_entry *477media_pipeline_walk_top(struct media_pipeline_walk *walk)478{479 return &walk->stack.entries[walk->stack.top];480}481 482static bool media_pipeline_walk_empty(struct media_pipeline_walk *walk)483{484 return walk->stack.top == -1;485}486 487/* Increase the stack size by MEDIA_PIPELINE_STACK_GROW_STEP elements. */488static int media_pipeline_walk_resize(struct media_pipeline_walk *walk)489{490 struct media_pipeline_walk_entry *entries;491 unsigned int new_size;492 493 /* Safety check, to avoid stack overflows in case of bugs. */494 if (walk->stack.size >= 256)495 return -E2BIG;496 497 new_size = walk->stack.size + MEDIA_PIPELINE_STACK_GROW_STEP;498 499 entries = krealloc(walk->stack.entries,500 new_size * sizeof(*walk->stack.entries),501 GFP_KERNEL);502 if (!entries)503 return -ENOMEM;504 505 walk->stack.entries = entries;506 walk->stack.size = new_size;507 508 return 0;509}510 511/* Push a new entry on the stack. */512static int media_pipeline_walk_push(struct media_pipeline_walk *walk,513 struct media_pad *pad)514{515 struct media_pipeline_walk_entry *entry;516 int ret;517 518 if (walk->stack.top + 1 >= walk->stack.size) {519 ret = media_pipeline_walk_resize(walk);520 if (ret)521 return ret;522 }523 524 walk->stack.top++;525 entry = media_pipeline_walk_top(walk);526 entry->pad = pad;527 entry->links = pad->entity->links.next;528 529 dev_dbg(walk->mdev->dev,530 "media pipeline: pushed entry %u: '%s':%u\n",531 walk->stack.top, pad->entity->name, pad->index);532 533 return 0;534}535 536/*537 * Move the top entry link cursor to the next link. If all links of the entry538 * have been visited, pop the entry itself. Return true if the entry has been539 * popped.540 */541static bool media_pipeline_walk_pop(struct media_pipeline_walk *walk)542{543 struct media_pipeline_walk_entry *entry;544 545 if (WARN_ON(walk->stack.top < 0))546 return false;547 548 entry = media_pipeline_walk_top(walk);549 550 if (entry->links->next == &entry->pad->entity->links) {551 dev_dbg(walk->mdev->dev,552 "media pipeline: entry %u has no more links, popping\n",553 walk->stack.top);554 555 walk->stack.top--;556 return true;557 }558 559 entry->links = entry->links->next;560 561 dev_dbg(walk->mdev->dev,562 "media pipeline: moved entry %u to next link\n",563 walk->stack.top);564 565 return false;566}567 568/* Free all memory allocated while walking the pipeline. */569static void media_pipeline_walk_destroy(struct media_pipeline_walk *walk)570{571 kfree(walk->stack.entries);572}573 574/* Add a pad to the pipeline and push it to the stack. */575static int media_pipeline_add_pad(struct media_pipeline *pipe,576 struct media_pipeline_walk *walk,577 struct media_pad *pad)578{579 struct media_pipeline_pad *ppad;580 581 list_for_each_entry(ppad, &pipe->pads, list) {582 if (ppad->pad == pad) {583 dev_dbg(pad->graph_obj.mdev->dev,584 "media pipeline: already contains pad '%s':%u\n",585 pad->entity->name, pad->index);586 return 0;587 }588 }589 590 ppad = kzalloc(sizeof(*ppad), GFP_KERNEL);591 if (!ppad)592 return -ENOMEM;593 594 ppad->pipe = pipe;595 ppad->pad = pad;596 597 list_add_tail(&ppad->list, &pipe->pads);598 599 dev_dbg(pad->graph_obj.mdev->dev,600 "media pipeline: added pad '%s':%u\n",601 pad->entity->name, pad->index);602 603 return media_pipeline_walk_push(walk, pad);604}605 606/* Explore the next link of the entity at the top of the stack. */607static int media_pipeline_explore_next_link(struct media_pipeline *pipe,608 struct media_pipeline_walk *walk)609{610 struct media_pipeline_walk_entry *entry = media_pipeline_walk_top(walk);611 struct media_pad *origin;612 struct media_link *link;613 struct media_pad *local;614 struct media_pad *remote;615 bool last_link;616 int ret;617 618 origin = entry->pad;619 link = list_entry(entry->links, typeof(*link), list);620 last_link = media_pipeline_walk_pop(walk);621 622 if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {623 dev_dbg(walk->mdev->dev,624 "media pipeline: skipping link (not data-link)\n");625 return 0;626 }627 628 dev_dbg(walk->mdev->dev,629 "media pipeline: exploring link '%s':%u -> '%s':%u\n",630 link->source->entity->name, link->source->index,631 link->sink->entity->name, link->sink->index);632 633 /* Get the local pad and remote pad. */634 if (link->source->entity == origin->entity) {635 local = link->source;636 remote = link->sink;637 } else {638 local = link->sink;639 remote = link->source;640 }641 642 /*643 * Skip links that originate from a different pad than the incoming pad644 * that is not connected internally in the entity to the incoming pad.645 */646 if (origin != local &&647 !media_entity_has_pad_interdep(origin->entity, origin->index,648 local->index)) {649 dev_dbg(walk->mdev->dev,650 "media pipeline: skipping link (no route)\n");651 goto done;652 }653 654 /*655 * Add the local pad of the link to the pipeline and push it to the656 * stack, if not already present.657 */658 ret = media_pipeline_add_pad(pipe, walk, local);659 if (ret)660 return ret;661 662 /* Similarly, add the remote pad, but only if the link is enabled. */663 if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {664 dev_dbg(walk->mdev->dev,665 "media pipeline: skipping link (disabled)\n");666 goto done;667 }668 669 ret = media_pipeline_add_pad(pipe, walk, remote);670 if (ret)671 return ret;672 673done:674 /*675 * If we're done iterating over links, iterate over pads of the entity.676 * This is necessary to discover pads that are not connected with any677 * link. Those are dead ends from a pipeline exploration point of view,678 * but are still part of the pipeline and need to be added to enable679 * proper validation.680 */681 if (!last_link)682 return 0;683 684 dev_dbg(walk->mdev->dev,685 "media pipeline: adding unconnected pads of '%s'\n",686 local->entity->name);687 688 media_entity_for_each_pad(origin->entity, local) {689 /*690 * Skip the origin pad (already handled), pad that have links691 * (already discovered through iterating over links) and pads692 * not internally connected.693 */694 if (origin == local || !local->num_links ||695 !media_entity_has_pad_interdep(origin->entity, origin->index,696 local->index))697 continue;698 699 ret = media_pipeline_add_pad(pipe, walk, local);700 if (ret)701 return ret;702 }703 704 return 0;705}706 707static void media_pipeline_cleanup(struct media_pipeline *pipe)708{709 while (!list_empty(&pipe->pads)) {710 struct media_pipeline_pad *ppad;711 712 ppad = list_first_entry(&pipe->pads, typeof(*ppad), list);713 list_del(&ppad->list);714 kfree(ppad);715 }716}717 718static int media_pipeline_populate(struct media_pipeline *pipe,719 struct media_pad *pad)720{721 struct media_pipeline_walk walk = { };722 struct media_pipeline_pad *ppad;723 int ret;724 725 /*726 * Populate the media pipeline by walking the media graph, starting727 * from @pad.728 */729 INIT_LIST_HEAD(&pipe->pads);730 pipe->mdev = pad->graph_obj.mdev;731 732 walk.mdev = pipe->mdev;733 walk.stack.top = -1;734 ret = media_pipeline_add_pad(pipe, &walk, pad);735 if (ret)736 goto done;737 738 /*739 * Use a depth-first search algorithm: as long as the stack is not740 * empty, explore the next link of the top entry. The741 * media_pipeline_explore_next_link() function will either move to the742 * next link, pop the entry if fully visited, or add new entries on743 * top.744 */745 while (!media_pipeline_walk_empty(&walk)) {746 ret = media_pipeline_explore_next_link(pipe, &walk);747 if (ret)748 goto done;749 }750 751 dev_dbg(pad->graph_obj.mdev->dev,752 "media pipeline populated, found pads:\n");753 754 list_for_each_entry(ppad, &pipe->pads, list)755 dev_dbg(pad->graph_obj.mdev->dev, "- '%s':%u\n",756 ppad->pad->entity->name, ppad->pad->index);757 758 WARN_ON(walk.stack.top != -1);759 760 ret = 0;761 762done:763 media_pipeline_walk_destroy(&walk);764 765 if (ret)766 media_pipeline_cleanup(pipe);767 768 return ret;769}770 771__must_check int __media_pipeline_start(struct media_pad *pad,772 struct media_pipeline *pipe)773{774 struct media_device *mdev = pad->graph_obj.mdev;775 struct media_pipeline_pad *err_ppad;776 struct media_pipeline_pad *ppad;777 int ret;778 779 lockdep_assert_held(&mdev->graph_mutex);780 781 /*782 * If the pad is already part of a pipeline, that pipeline must be the783 * same as the pipe given to media_pipeline_start().784 */785 if (WARN_ON(pad->pipe && pad->pipe != pipe))786 return -EINVAL;787 788 /*789 * If the pipeline has already been started, it is guaranteed to be790 * valid, so just increase the start count.791 */792 if (pipe->start_count) {793 pipe->start_count++;794 return 0;795 }796 797 /*798 * Populate the pipeline. This populates the media_pipeline pads list799 * with media_pipeline_pad instances for each pad found during graph800 * walk.801 */802 ret = media_pipeline_populate(pipe, pad);803 if (ret)804 return ret;805 806 /*807 * Now that all the pads in the pipeline have been gathered, perform808 * the validation steps.809 */810 811 list_for_each_entry(ppad, &pipe->pads, list) {812 struct media_pad *pad = ppad->pad;813 struct media_entity *entity = pad->entity;814 bool has_enabled_link = false;815 struct media_link *link;816 817 dev_dbg(mdev->dev, "Validating pad '%s':%u\n", pad->entity->name,818 pad->index);819 820 /*821 * 1. Ensure that the pad doesn't already belong to a different822 * pipeline.823 */824 if (pad->pipe) {825 dev_dbg(mdev->dev, "Failed to start pipeline: pad '%s':%u busy\n",826 pad->entity->name, pad->index);827 ret = -EBUSY;828 goto error;829 }830 831 /*832 * 2. Validate all active links whose sink is the current pad.833 * Validation of the source pads is performed in the context of834 * the connected sink pad to avoid duplicating checks.835 */836 for_each_media_entity_data_link(entity, link) {837 /* Skip links unrelated to the current pad. */838 if (link->sink != pad && link->source != pad)839 continue;840 841 /* Record if the pad has links and enabled links. */842 if (link->flags & MEDIA_LNK_FL_ENABLED)843 has_enabled_link = true;844 845 /*846 * Validate the link if it's enabled and has the847 * current pad as its sink.848 */849 if (!(link->flags & MEDIA_LNK_FL_ENABLED))850 continue;851 852 if (link->sink != pad)853 continue;854 855 if (!entity->ops || !entity->ops->link_validate)856 continue;857 858 ret = entity->ops->link_validate(link);859 if (ret) {860 dev_dbg(mdev->dev,861 "Link '%s':%u -> '%s':%u failed validation: %d\n",862 link->source->entity->name,863 link->source->index,864 link->sink->entity->name,865 link->sink->index, ret);866 goto error;867 }868 869 dev_dbg(mdev->dev,870 "Link '%s':%u -> '%s':%u is valid\n",871 link->source->entity->name,872 link->source->index,873 link->sink->entity->name,874 link->sink->index);875 }876 877 /*878 * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,879 * ensure that it has either no link or an enabled link.880 */881 if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) &&882 !has_enabled_link) {883 dev_dbg(mdev->dev,884 "Pad '%s':%u must be connected by an enabled link\n",885 pad->entity->name, pad->index);886 ret = -ENOLINK;887 goto error;888 }889 890 /* Validation passed, store the pipe pointer in the pad. */891 pad->pipe = pipe;892 }893 894 pipe->start_count++;895 896 return 0;897 898error:899 /*900 * Link validation on graph failed. We revert what we did and901 * return the error.902 */903 904 list_for_each_entry(err_ppad, &pipe->pads, list) {905 if (err_ppad == ppad)906 break;907 908 err_ppad->pad->pipe = NULL;909 }910 911 media_pipeline_cleanup(pipe);912 913 return ret;914}915EXPORT_SYMBOL_GPL(__media_pipeline_start);916 917__must_check int media_pipeline_start(struct media_pad *pad,918 struct media_pipeline *pipe)919{920 struct media_device *mdev = pad->graph_obj.mdev;921 int ret;922 923 mutex_lock(&mdev->graph_mutex);924 ret = __media_pipeline_start(pad, pipe);925 mutex_unlock(&mdev->graph_mutex);926 return ret;927}928EXPORT_SYMBOL_GPL(media_pipeline_start);929 930void __media_pipeline_stop(struct media_pad *pad)931{932 struct media_pipeline *pipe = pad->pipe;933 struct media_pipeline_pad *ppad;934 935 /*936 * If the following check fails, the driver has performed an937 * unbalanced call to media_pipeline_stop()938 */939 if (WARN_ON(!pipe))940 return;941 942 if (--pipe->start_count)943 return;944 945 list_for_each_entry(ppad, &pipe->pads, list)946 ppad->pad->pipe = NULL;947 948 media_pipeline_cleanup(pipe);949 950 if (pipe->allocated)951 kfree(pipe);952}953EXPORT_SYMBOL_GPL(__media_pipeline_stop);954 955void media_pipeline_stop(struct media_pad *pad)956{957 struct media_device *mdev = pad->graph_obj.mdev;958 959 mutex_lock(&mdev->graph_mutex);960 __media_pipeline_stop(pad);961 mutex_unlock(&mdev->graph_mutex);962}963EXPORT_SYMBOL_GPL(media_pipeline_stop);964 965__must_check int media_pipeline_alloc_start(struct media_pad *pad)966{967 struct media_device *mdev = pad->graph_obj.mdev;968 struct media_pipeline *new_pipe = NULL;969 struct media_pipeline *pipe;970 int ret;971 972 mutex_lock(&mdev->graph_mutex);973 974 /*975 * Is the pad already part of a pipeline? If not, we need to allocate976 * a pipe.977 */978 pipe = media_pad_pipeline(pad);979 if (!pipe) {980 new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL);981 if (!new_pipe) {982 ret = -ENOMEM;983 goto out;984 }985 986 pipe = new_pipe;987 pipe->allocated = true;988 }989 990 ret = __media_pipeline_start(pad, pipe);991 if (ret)992 kfree(new_pipe);993 994out:995 mutex_unlock(&mdev->graph_mutex);996 997 return ret;998}999EXPORT_SYMBOL_GPL(media_pipeline_alloc_start);1000 1001struct media_pad *1002__media_pipeline_pad_iter_next(struct media_pipeline *pipe,1003 struct media_pipeline_pad_iter *iter,1004 struct media_pad *pad)1005{1006 if (!pad)1007 iter->cursor = pipe->pads.next;1008 1009 if (iter->cursor == &pipe->pads)1010 return NULL;1011 1012 pad = list_entry(iter->cursor, struct media_pipeline_pad, list)->pad;1013 iter->cursor = iter->cursor->next;1014 1015 return pad;1016}1017EXPORT_SYMBOL_GPL(__media_pipeline_pad_iter_next);1018 1019int media_pipeline_entity_iter_init(struct media_pipeline *pipe,1020 struct media_pipeline_entity_iter *iter)1021{1022 return media_entity_enum_init(&iter->ent_enum, pipe->mdev);1023}1024EXPORT_SYMBOL_GPL(media_pipeline_entity_iter_init);1025 1026void media_pipeline_entity_iter_cleanup(struct media_pipeline_entity_iter *iter)1027{1028 media_entity_enum_cleanup(&iter->ent_enum);1029}1030EXPORT_SYMBOL_GPL(media_pipeline_entity_iter_cleanup);1031 1032struct media_entity *1033__media_pipeline_entity_iter_next(struct media_pipeline *pipe,1034 struct media_pipeline_entity_iter *iter,1035 struct media_entity *entity)1036{1037 if (!entity)1038 iter->cursor = pipe->pads.next;1039 1040 while (iter->cursor != &pipe->pads) {1041 struct media_pipeline_pad *ppad;1042 struct media_entity *entity;1043 1044 ppad = list_entry(iter->cursor, struct media_pipeline_pad, list);1045 entity = ppad->pad->entity;1046 iter->cursor = iter->cursor->next;1047 1048 if (!media_entity_enum_test_and_set(&iter->ent_enum, entity))1049 return entity;1050 }1051 1052 return NULL;1053}1054EXPORT_SYMBOL_GPL(__media_pipeline_entity_iter_next);1055 1056/* -----------------------------------------------------------------------------1057 * Links management1058 */1059 1060static struct media_link *media_add_link(struct list_head *head)1061{1062 struct media_link *link;1063 1064 link = kzalloc(sizeof(*link), GFP_KERNEL);1065 if (link == NULL)1066 return NULL;1067 1068 list_add_tail(&link->list, head);1069 1070 return link;1071}1072 1073static void __media_entity_remove_link(struct media_entity *entity,1074 struct media_link *link)1075{1076 struct media_link *rlink, *tmp;1077 struct media_entity *remote;1078 1079 /* Remove the reverse links for a data link. */1080 if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {1081 link->source->num_links--;1082 link->sink->num_links--;1083 1084 if (link->source->entity == entity)1085 remote = link->sink->entity;1086 else1087 remote = link->source->entity;1088 1089 list_for_each_entry_safe(rlink, tmp, &remote->links, list) {1090 if (rlink != link->reverse)1091 continue;1092 1093 if (link->source->entity == entity)1094 remote->num_backlinks--;1095 1096 /* Remove the remote link */1097 list_del(&rlink->list);1098 media_gobj_destroy(&rlink->graph_obj);1099 kfree(rlink);1100 1101 if (--remote->num_links == 0)1102 break;1103 }1104 }1105 1106 list_del(&link->list);1107 media_gobj_destroy(&link->graph_obj);1108 kfree(link);1109}1110 1111int media_get_pad_index(struct media_entity *entity, u32 pad_type,1112 enum media_pad_signal_type sig_type)1113{1114 unsigned int i;1115 1116 if (!entity)1117 return -EINVAL;1118 1119 for (i = 0; i < entity->num_pads; i++) {1120 if ((entity->pads[i].flags &1121 (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type)1122 continue;1123 1124 if (entity->pads[i].sig_type == sig_type)1125 return i;1126 }1127 return -EINVAL;1128}1129EXPORT_SYMBOL_GPL(media_get_pad_index);1130 1131int1132media_create_pad_link(struct media_entity *source, u16 source_pad,1133 struct media_entity *sink, u16 sink_pad, u32 flags)1134{1135 struct media_link *link;1136 struct media_link *backlink;1137 1138 if (flags & MEDIA_LNK_FL_LINK_TYPE)1139 return -EINVAL;1140 1141 flags |= MEDIA_LNK_FL_DATA_LINK;1142 1143 if (WARN_ON(!source || !sink) ||1144 WARN_ON(source_pad >= source->num_pads) ||1145 WARN_ON(sink_pad >= sink->num_pads))1146 return -EINVAL;1147 if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE)))1148 return -EINVAL;1149 if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))1150 return -EINVAL;1151 1152 link = media_add_link(&source->links);1153 if (link == NULL)1154 return -ENOMEM;1155 1156 link->source = &source->pads[source_pad];1157 link->sink = &sink->pads[sink_pad];1158 link->flags = flags;1159 1160 /* Initialize graph object embedded at the new link */1161 media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,1162 &link->graph_obj);1163 1164 /* Create the backlink. Backlinks are used to help graph traversal and1165 * are not reported to userspace.1166 */1167 backlink = media_add_link(&sink->links);1168 if (backlink == NULL) {1169 __media_entity_remove_link(source, link);1170 return -ENOMEM;1171 }1172 1173 backlink->source = &source->pads[source_pad];1174 backlink->sink = &sink->pads[sink_pad];1175 backlink->flags = flags;1176 backlink->is_backlink = true;1177 1178 /* Initialize graph object embedded at the new link */1179 media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK,1180 &backlink->graph_obj);1181 1182 link->reverse = backlink;1183 backlink->reverse = link;1184 1185 sink->num_backlinks++;1186 sink->num_links++;1187 source->num_links++;1188 1189 link->source->num_links++;1190 link->sink->num_links++;1191 1192 return 0;1193}1194EXPORT_SYMBOL_GPL(media_create_pad_link);1195 1196int media_create_pad_links(const struct media_device *mdev,1197 const u32 source_function,1198 struct media_entity *source,1199 const u16 source_pad,1200 const u32 sink_function,1201 struct media_entity *sink,1202 const u16 sink_pad,1203 u32 flags,1204 const bool allow_both_undefined)1205{1206 struct media_entity *entity;1207 unsigned function;1208 int ret;1209 1210 /* Trivial case: 1:1 relation */1211 if (source && sink)1212 return media_create_pad_link(source, source_pad,1213 sink, sink_pad, flags);1214 1215 /* Worse case scenario: n:n relation */1216 if (!source && !sink) {1217 if (!allow_both_undefined)1218 return 0;1219 media_device_for_each_entity(source, mdev) {1220 if (source->function != source_function)1221 continue;1222 media_device_for_each_entity(sink, mdev) {1223 if (sink->function != sink_function)1224 continue;1225 ret = media_create_pad_link(source, source_pad,1226 sink, sink_pad,1227 flags);1228 if (ret)1229 return ret;1230 flags &= ~(MEDIA_LNK_FL_ENABLED |1231 MEDIA_LNK_FL_IMMUTABLE);1232 }1233 }1234 return 0;1235 }1236 1237 /* Handle 1:n and n:1 cases */1238 if (source)1239 function = sink_function;1240 else1241 function = source_function;1242 1243 media_device_for_each_entity(entity, mdev) {1244 if (entity->function != function)1245 continue;1246 1247 if (source)1248 ret = media_create_pad_link(source, source_pad,1249 entity, sink_pad, flags);1250 else1251 ret = media_create_pad_link(entity, source_pad,1252 sink, sink_pad, flags);1253 if (ret)1254 return ret;1255 flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);1256 }1257 return 0;1258}1259EXPORT_SYMBOL_GPL(media_create_pad_links);1260 1261void __media_entity_remove_links(struct media_entity *entity)1262{1263 struct media_link *link, *tmp;1264 1265 list_for_each_entry_safe(link, tmp, &entity->links, list)1266 __media_entity_remove_link(entity, link);1267 1268 entity->num_links = 0;1269 entity->num_backlinks = 0;1270}1271EXPORT_SYMBOL_GPL(__media_entity_remove_links);1272 1273void media_entity_remove_links(struct media_entity *entity)1274{1275 struct media_device *mdev = entity->graph_obj.mdev;1276 1277 /* Do nothing if the entity is not registered. */1278 if (mdev == NULL)1279 return;1280 1281 mutex_lock(&mdev->graph_mutex);1282 __media_entity_remove_links(entity);1283 mutex_unlock(&mdev->graph_mutex);1284}1285EXPORT_SYMBOL_GPL(media_entity_remove_links);1286 1287static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)1288{1289 int ret;1290 1291 /* Notify both entities. */1292 ret = media_entity_call(link->source->entity, link_setup,1293 link->source, link->sink, flags);1294 if (ret < 0 && ret != -ENOIOCTLCMD)1295 return ret;1296 1297 ret = media_entity_call(link->sink->entity, link_setup,1298 link->sink, link->source, flags);1299 if (ret < 0 && ret != -ENOIOCTLCMD) {1300 media_entity_call(link->source->entity, link_setup,1301 link->source, link->sink, link->flags);1302 return ret;1303 }1304 1305 link->flags = flags;1306 link->reverse->flags = link->flags;1307 1308 return 0;1309}1310 1311int __media_entity_setup_link(struct media_link *link, u32 flags)1312{1313 const u32 mask = MEDIA_LNK_FL_ENABLED;1314 struct media_device *mdev;1315 struct media_pad *source, *sink;1316 int ret = -EBUSY;1317 1318 if (link == NULL)1319 return -EINVAL;1320 1321 /* The non-modifiable link flags must not be modified. */1322 if ((link->flags & ~mask) != (flags & ~mask))1323 return -EINVAL;1324 1325 if (link->flags & MEDIA_LNK_FL_IMMUTABLE)1326 return link->flags == flags ? 0 : -EINVAL;1327 1328 if (link->flags == flags)1329 return 0;1330 1331 source = link->source;1332 sink = link->sink;1333 1334 if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&1335 (media_pad_is_streaming(source) || media_pad_is_streaming(sink)))1336 return -EBUSY;1337 1338 mdev = source->graph_obj.mdev;1339 1340 if (mdev->ops && mdev->ops->link_notify) {1341 ret = mdev->ops->link_notify(link, flags,1342 MEDIA_DEV_NOTIFY_PRE_LINK_CH);1343 if (ret < 0)1344 return ret;1345 }1346 1347 ret = __media_entity_setup_link_notify(link, flags);1348 1349 if (mdev->ops && mdev->ops->link_notify)1350 mdev->ops->link_notify(link, flags,1351 MEDIA_DEV_NOTIFY_POST_LINK_CH);1352 1353 return ret;1354}1355EXPORT_SYMBOL_GPL(__media_entity_setup_link);1356 1357int media_entity_setup_link(struct media_link *link, u32 flags)1358{1359 int ret;1360 1361 mutex_lock(&link->graph_obj.mdev->graph_mutex);1362 ret = __media_entity_setup_link(link, flags);1363 mutex_unlock(&link->graph_obj.mdev->graph_mutex);1364 1365 return ret;1366}1367EXPORT_SYMBOL_GPL(media_entity_setup_link);1368 1369struct media_link *1370media_entity_find_link(struct media_pad *source, struct media_pad *sink)1371{1372 struct media_link *link;1373 1374 for_each_media_entity_data_link(source->entity, link) {1375 if (link->source->entity == source->entity &&1376 link->source->index == source->index &&1377 link->sink->entity == sink->entity &&1378 link->sink->index == sink->index)1379 return link;1380 }1381 1382 return NULL;1383}1384EXPORT_SYMBOL_GPL(media_entity_find_link);1385 1386struct media_pad *media_pad_remote_pad_first(const struct media_pad *pad)1387{1388 struct media_link *link;1389 1390 for_each_media_entity_data_link(pad->entity, link) {1391 if (!(link->flags & MEDIA_LNK_FL_ENABLED))1392 continue;1393 1394 if (link->source == pad)1395 return link->sink;1396 1397 if (link->sink == pad)1398 return link->source;1399 }1400 1401 return NULL;1402 1403}1404EXPORT_SYMBOL_GPL(media_pad_remote_pad_first);1405 1406struct media_pad *1407media_entity_remote_pad_unique(const struct media_entity *entity,1408 unsigned int type)1409{1410 struct media_pad *pad = NULL;1411 struct media_link *link;1412 1413 list_for_each_entry(link, &entity->links, list) {1414 struct media_pad *local_pad;1415 struct media_pad *remote_pad;1416 1417 if (((link->flags & MEDIA_LNK_FL_LINK_TYPE) !=1418 MEDIA_LNK_FL_DATA_LINK) ||1419 !(link->flags & MEDIA_LNK_FL_ENABLED))1420 continue;1421 1422 if (type == MEDIA_PAD_FL_SOURCE) {1423 local_pad = link->sink;1424 remote_pad = link->source;1425 } else {1426 local_pad = link->source;1427 remote_pad = link->sink;1428 }1429 1430 if (local_pad->entity == entity) {1431 if (pad)1432 return ERR_PTR(-ENOTUNIQ);1433 1434 pad = remote_pad;1435 }1436 }1437 1438 if (!pad)1439 return ERR_PTR(-ENOLINK);1440 1441 return pad;1442}1443EXPORT_SYMBOL_GPL(media_entity_remote_pad_unique);1444 1445struct media_pad *media_pad_remote_pad_unique(const struct media_pad *pad)1446{1447 struct media_pad *found_pad = NULL;1448 struct media_link *link;1449 1450 list_for_each_entry(link, &pad->entity->links, list) {1451 struct media_pad *remote_pad;1452 1453 if (!(link->flags & MEDIA_LNK_FL_ENABLED))1454 continue;1455 1456 if (link->sink == pad)1457 remote_pad = link->source;1458 else if (link->source == pad)1459 remote_pad = link->sink;1460 else1461 continue;1462 1463 if (found_pad)1464 return ERR_PTR(-ENOTUNIQ);1465 1466 found_pad = remote_pad;1467 }1468 1469 if (!found_pad)1470 return ERR_PTR(-ENOLINK);1471 1472 return found_pad;1473}1474EXPORT_SYMBOL_GPL(media_pad_remote_pad_unique);1475 1476int media_entity_get_fwnode_pad(struct media_entity *entity,1477 const struct fwnode_handle *fwnode,1478 unsigned long direction_flags)1479{1480 struct fwnode_endpoint endpoint;1481 unsigned int i;1482 int ret;1483 1484 if (!entity->ops || !entity->ops->get_fwnode_pad) {1485 for (i = 0; i < entity->num_pads; i++) {1486 if (entity->pads[i].flags & direction_flags)1487 return i;1488 }1489 1490 return -ENXIO;1491 }1492 1493 ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);1494 if (ret)1495 return ret;1496 1497 ret = entity->ops->get_fwnode_pad(entity, &endpoint);1498 if (ret < 0)1499 return ret;1500 1501 if (ret >= entity->num_pads)1502 return -ENXIO;1503 1504 if (!(entity->pads[ret].flags & direction_flags))1505 return -ENXIO;1506 1507 return ret;1508}1509EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);1510 1511struct media_pipeline *media_entity_pipeline(struct media_entity *entity)1512{1513 struct media_pad *pad;1514 1515 media_entity_for_each_pad(entity, pad) {1516 if (pad->pipe)1517 return pad->pipe;1518 }1519 1520 return NULL;1521}1522EXPORT_SYMBOL_GPL(media_entity_pipeline);1523 1524struct media_pipeline *media_pad_pipeline(struct media_pad *pad)1525{1526 return pad->pipe;1527}1528EXPORT_SYMBOL_GPL(media_pad_pipeline);1529 1530static void media_interface_init(struct media_device *mdev,1531 struct media_interface *intf,1532 u32 gobj_type,1533 u32 intf_type, u32 flags)1534{1535 intf->type = intf_type;1536 intf->flags = flags;1537 INIT_LIST_HEAD(&intf->links);1538 1539 media_gobj_create(mdev, gobj_type, &intf->graph_obj);1540}1541 1542/* Functions related to the media interface via device nodes */1543 1544struct media_intf_devnode *media_devnode_create(struct media_device *mdev,1545 u32 type, u32 flags,1546 u32 major, u32 minor)1547{1548 struct media_intf_devnode *devnode;1549 1550 devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);1551 if (!devnode)1552 return NULL;1553 1554 devnode->major = major;1555 devnode->minor = minor;1556 1557 media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE,1558 type, flags);1559 1560 return devnode;1561}1562EXPORT_SYMBOL_GPL(media_devnode_create);1563 1564void media_devnode_remove(struct media_intf_devnode *devnode)1565{1566 media_remove_intf_links(&devnode->intf);1567 media_gobj_destroy(&devnode->intf.graph_obj);1568 kfree(devnode);1569}1570EXPORT_SYMBOL_GPL(media_devnode_remove);1571 1572struct media_link *media_create_intf_link(struct media_entity *entity,1573 struct media_interface *intf,1574 u32 flags)1575{1576 struct media_link *link;1577 1578 link = media_add_link(&intf->links);1579 if (link == NULL)1580 return NULL;1581 1582 link->intf = intf;1583 link->entity = entity;1584 link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK;1585 1586 /* Initialize graph object embedded at the new link */1587 media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK,1588 &link->graph_obj);1589 1590 return link;1591}1592EXPORT_SYMBOL_GPL(media_create_intf_link);1593 1594void __media_remove_intf_link(struct media_link *link)1595{1596 list_del(&link->list);1597 media_gobj_destroy(&link->graph_obj);1598 kfree(link);1599}1600EXPORT_SYMBOL_GPL(__media_remove_intf_link);1601 1602void media_remove_intf_link(struct media_link *link)1603{1604 struct media_device *mdev = link->graph_obj.mdev;1605 1606 /* Do nothing if the intf is not registered. */1607 if (mdev == NULL)1608 return;1609 1610 mutex_lock(&mdev->graph_mutex);1611 __media_remove_intf_link(link);1612 mutex_unlock(&mdev->graph_mutex);1613}1614EXPORT_SYMBOL_GPL(media_remove_intf_link);1615 1616void __media_remove_intf_links(struct media_interface *intf)1617{1618 struct media_link *link, *tmp;1619 1620 list_for_each_entry_safe(link, tmp, &intf->links, list)1621 __media_remove_intf_link(link);1622 1623}1624EXPORT_SYMBOL_GPL(__media_remove_intf_links);1625 1626void media_remove_intf_links(struct media_interface *intf)1627{1628 struct media_device *mdev = intf->graph_obj.mdev;1629 1630 /* Do nothing if the intf is not registered. */1631 if (mdev == NULL)1632 return;1633 1634 mutex_lock(&mdev->graph_mutex);1635 __media_remove_intf_links(intf);1636 mutex_unlock(&mdev->graph_mutex);1637}1638EXPORT_SYMBOL_GPL(media_remove_intf_links);1639 1640struct media_link *media_create_ancillary_link(struct media_entity *primary,1641 struct media_entity *ancillary)1642{1643 struct media_link *link;1644 1645 link = media_add_link(&primary->links);1646 if (!link)1647 return ERR_PTR(-ENOMEM);1648 1649 link->gobj0 = &primary->graph_obj;1650 link->gobj1 = &ancillary->graph_obj;1651 link->flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED |1652 MEDIA_LNK_FL_ANCILLARY_LINK;1653 1654 /* Initialize graph object embedded in the new link */1655 media_gobj_create(primary->graph_obj.mdev, MEDIA_GRAPH_LINK,1656 &link->graph_obj);1657 1658 return link;1659}1660EXPORT_SYMBOL_GPL(media_create_ancillary_link);1661 1662struct media_link *__media_entity_next_link(struct media_entity *entity,1663 struct media_link *link,1664 unsigned long link_type)1665{1666 link = link ? list_next_entry(link, list)1667 : list_first_entry(&entity->links, typeof(*link), list);1668 1669 list_for_each_entry_from(link, &entity->links, list)1670 if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == link_type)1671 return link;1672 1673 return NULL;1674}1675EXPORT_SYMBOL_GPL(__media_entity_next_link);1676