brintos

brintos / linux-shallow public Read only

0
0
Text · 40.3 KiB · 96dd0f6 Raw
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