162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Media entity
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
862306a36Sopenharmony_ci *	     Sakari Ailus <sakari.ailus@iki.fi>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitmap.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/property.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <media/media-entity.h>
1662306a36Sopenharmony_ci#include <media/media-device.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic inline const char *intf_type(struct media_interface *intf)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	switch (intf->type) {
2162306a36Sopenharmony_ci	case MEDIA_INTF_T_DVB_FE:
2262306a36Sopenharmony_ci		return "dvb-frontend";
2362306a36Sopenharmony_ci	case MEDIA_INTF_T_DVB_DEMUX:
2462306a36Sopenharmony_ci		return "dvb-demux";
2562306a36Sopenharmony_ci	case MEDIA_INTF_T_DVB_DVR:
2662306a36Sopenharmony_ci		return "dvb-dvr";
2762306a36Sopenharmony_ci	case MEDIA_INTF_T_DVB_CA:
2862306a36Sopenharmony_ci		return  "dvb-ca";
2962306a36Sopenharmony_ci	case MEDIA_INTF_T_DVB_NET:
3062306a36Sopenharmony_ci		return "dvb-net";
3162306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_VIDEO:
3262306a36Sopenharmony_ci		return "v4l-video";
3362306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_VBI:
3462306a36Sopenharmony_ci		return "v4l-vbi";
3562306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_RADIO:
3662306a36Sopenharmony_ci		return "v4l-radio";
3762306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_SUBDEV:
3862306a36Sopenharmony_ci		return "v4l-subdev";
3962306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_SWRADIO:
4062306a36Sopenharmony_ci		return "v4l-swradio";
4162306a36Sopenharmony_ci	case MEDIA_INTF_T_V4L_TOUCH:
4262306a36Sopenharmony_ci		return "v4l-touch";
4362306a36Sopenharmony_ci	default:
4462306a36Sopenharmony_ci		return "unknown-intf";
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline const char *link_type_name(struct media_link *link)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	switch (link->flags & MEDIA_LNK_FL_LINK_TYPE) {
5162306a36Sopenharmony_ci	case MEDIA_LNK_FL_DATA_LINK:
5262306a36Sopenharmony_ci		return "data";
5362306a36Sopenharmony_ci	case MEDIA_LNK_FL_INTERFACE_LINK:
5462306a36Sopenharmony_ci		return "interface";
5562306a36Sopenharmony_ci	case MEDIA_LNK_FL_ANCILLARY_LINK:
5662306a36Sopenharmony_ci		return "ancillary";
5762306a36Sopenharmony_ci	default:
5862306a36Sopenharmony_ci		return "unknown";
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum,
6362306a36Sopenharmony_ci					struct media_device *mdev)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	int idx_max;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	idx_max = ALIGN(mdev->entity_internal_idx_max + 1, BITS_PER_LONG);
6862306a36Sopenharmony_ci	ent_enum->bmap = bitmap_zalloc(idx_max, GFP_KERNEL);
6962306a36Sopenharmony_ci	if (!ent_enum->bmap)
7062306a36Sopenharmony_ci		return -ENOMEM;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	ent_enum->idx_max = idx_max;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_enum_init);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	bitmap_free(ent_enum->bmap);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_enum_cleanup);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci *  dev_dbg_obj - Prints in debug mode a change on some object
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * @event_name:	Name of the event to report. Could be __func__
8862306a36Sopenharmony_ci * @gobj:	Pointer to the object
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it
9162306a36Sopenharmony_ci * won't produce any code.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistatic void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG)
9662306a36Sopenharmony_ci	switch (media_type(gobj)) {
9762306a36Sopenharmony_ci	case MEDIA_GRAPH_ENTITY:
9862306a36Sopenharmony_ci		dev_dbg(gobj->mdev->dev,
9962306a36Sopenharmony_ci			"%s id %u: entity '%s'\n",
10062306a36Sopenharmony_ci			event_name, media_id(gobj),
10162306a36Sopenharmony_ci			gobj_to_entity(gobj)->name);
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	case MEDIA_GRAPH_LINK:
10462306a36Sopenharmony_ci	{
10562306a36Sopenharmony_ci		struct media_link *link = gobj_to_link(gobj);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		dev_dbg(gobj->mdev->dev,
10862306a36Sopenharmony_ci			"%s id %u: %s link id %u ==> id %u\n",
10962306a36Sopenharmony_ci			event_name, media_id(gobj), link_type_name(link),
11062306a36Sopenharmony_ci			media_id(link->gobj0),
11162306a36Sopenharmony_ci			media_id(link->gobj1));
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	case MEDIA_GRAPH_PAD:
11562306a36Sopenharmony_ci	{
11662306a36Sopenharmony_ci		struct media_pad *pad = gobj_to_pad(gobj);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		dev_dbg(gobj->mdev->dev,
11962306a36Sopenharmony_ci			"%s id %u: %s%spad '%s':%d\n",
12062306a36Sopenharmony_ci			event_name, media_id(gobj),
12162306a36Sopenharmony_ci			pad->flags & MEDIA_PAD_FL_SINK   ? "sink " : "",
12262306a36Sopenharmony_ci			pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "",
12362306a36Sopenharmony_ci			pad->entity->name, pad->index);
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	case MEDIA_GRAPH_INTF_DEVNODE:
12762306a36Sopenharmony_ci	{
12862306a36Sopenharmony_ci		struct media_interface *intf = gobj_to_intf(gobj);
12962306a36Sopenharmony_ci		struct media_intf_devnode *devnode = intf_to_devnode(intf);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		dev_dbg(gobj->mdev->dev,
13262306a36Sopenharmony_ci			"%s id %u: intf_devnode %s - major: %d, minor: %d\n",
13362306a36Sopenharmony_ci			event_name, media_id(gobj),
13462306a36Sopenharmony_ci			intf_type(intf),
13562306a36Sopenharmony_ci			devnode->major, devnode->minor);
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid media_gobj_create(struct media_device *mdev,
14362306a36Sopenharmony_ci			   enum media_gobj_type type,
14462306a36Sopenharmony_ci			   struct media_gobj *gobj)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	BUG_ON(!mdev);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	gobj->mdev = mdev;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Create a per-type unique object ID */
15162306a36Sopenharmony_ci	gobj->id = media_gobj_gen_id(type, ++mdev->id);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	switch (type) {
15462306a36Sopenharmony_ci	case MEDIA_GRAPH_ENTITY:
15562306a36Sopenharmony_ci		list_add_tail(&gobj->list, &mdev->entities);
15662306a36Sopenharmony_ci		break;
15762306a36Sopenharmony_ci	case MEDIA_GRAPH_PAD:
15862306a36Sopenharmony_ci		list_add_tail(&gobj->list, &mdev->pads);
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case MEDIA_GRAPH_LINK:
16162306a36Sopenharmony_ci		list_add_tail(&gobj->list, &mdev->links);
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	case MEDIA_GRAPH_INTF_DEVNODE:
16462306a36Sopenharmony_ci		list_add_tail(&gobj->list, &mdev->interfaces);
16562306a36Sopenharmony_ci		break;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mdev->topology_version++;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	dev_dbg_obj(__func__, gobj);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_civoid media_gobj_destroy(struct media_gobj *gobj)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	/* Do nothing if the object is not linked. */
17662306a36Sopenharmony_ci	if (gobj->mdev == NULL)
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dev_dbg_obj(__func__, gobj);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	gobj->mdev->topology_version++;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Remove the object from mdev list */
18462306a36Sopenharmony_ci	list_del(&gobj->list);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	gobj->mdev = NULL;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci * TODO: Get rid of this.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci#define MEDIA_ENTITY_MAX_PADS		512
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciint media_entity_pads_init(struct media_entity *entity, u16 num_pads,
19562306a36Sopenharmony_ci			   struct media_pad *pads)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct media_device *mdev = entity->graph_obj.mdev;
19862306a36Sopenharmony_ci	struct media_pad *iter;
19962306a36Sopenharmony_ci	unsigned int i = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (num_pads >= MEDIA_ENTITY_MAX_PADS)
20262306a36Sopenharmony_ci		return -E2BIG;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	entity->num_pads = num_pads;
20562306a36Sopenharmony_ci	entity->pads = pads;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (mdev)
20862306a36Sopenharmony_ci		mutex_lock(&mdev->graph_mutex);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	media_entity_for_each_pad(entity, iter) {
21162306a36Sopenharmony_ci		iter->entity = entity;
21262306a36Sopenharmony_ci		iter->index = i++;
21362306a36Sopenharmony_ci		if (mdev)
21462306a36Sopenharmony_ci			media_gobj_create(mdev, MEDIA_GRAPH_PAD,
21562306a36Sopenharmony_ci					  &iter->graph_obj);
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (mdev)
21962306a36Sopenharmony_ci		mutex_unlock(&mdev->graph_mutex);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_pads_init);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
22662306a36Sopenharmony_ci * Graph traversal
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/**
23062306a36Sopenharmony_ci * media_entity_has_pad_interdep - Check interdependency between two pads
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * @entity: The entity
23362306a36Sopenharmony_ci * @pad0: The first pad index
23462306a36Sopenharmony_ci * @pad1: The second pad index
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * This function checks the interdependency inside the entity between @pad0
23762306a36Sopenharmony_ci * and @pad1. If two pads are interdependent they are part of the same pipeline
23862306a36Sopenharmony_ci * and enabling one of the pads means that the other pad will become "locked"
23962306a36Sopenharmony_ci * and doesn't allow configuration changes.
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * This function uses the &media_entity_operations.has_pad_interdep() operation
24262306a36Sopenharmony_ci * to check the dependency inside the entity between @pad0 and @pad1. If the
24362306a36Sopenharmony_ci * has_pad_interdep operation is not implemented, all pads of the entity are
24462306a36Sopenharmony_ci * considered to be interdependent.
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * One of @pad0 and @pad1 must be a sink pad and the other one a source pad.
24762306a36Sopenharmony_ci * The function returns false if both pads are sinks or sources.
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * The caller must hold entity->graph_obj.mdev->mutex.
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci * Return: true if the pads are connected internally and false otherwise.
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_cistatic bool media_entity_has_pad_interdep(struct media_entity *entity,
25462306a36Sopenharmony_ci					  unsigned int pad0, unsigned int pad1)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	if (pad0 >= entity->num_pads || pad1 >= entity->num_pads)
25762306a36Sopenharmony_ci		return false;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (entity->pads[pad0].flags & entity->pads[pad1].flags &
26062306a36Sopenharmony_ci	    (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE))
26162306a36Sopenharmony_ci		return false;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (!entity->ops || !entity->ops->has_pad_interdep)
26462306a36Sopenharmony_ci		return true;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return entity->ops->has_pad_interdep(entity, pad0, pad1);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic struct media_entity *
27062306a36Sopenharmony_cimedia_entity_other(struct media_entity *entity, struct media_link *link)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	if (link->source->entity == entity)
27362306a36Sopenharmony_ci		return link->sink->entity;
27462306a36Sopenharmony_ci	else
27562306a36Sopenharmony_ci		return link->source->entity;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/* push an entity to traversal stack */
27962306a36Sopenharmony_cistatic void stack_push(struct media_graph *graph,
28062306a36Sopenharmony_ci		       struct media_entity *entity)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {
28362306a36Sopenharmony_ci		WARN_ON(1);
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	graph->top++;
28762306a36Sopenharmony_ci	graph->stack[graph->top].link = entity->links.next;
28862306a36Sopenharmony_ci	graph->stack[graph->top].entity = entity;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic struct media_entity *stack_pop(struct media_graph *graph)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct media_entity *entity;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	entity = graph->stack[graph->top].entity;
29662306a36Sopenharmony_ci	graph->top--;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return entity;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci#define link_top(en)	((en)->stack[(en)->top].link)
30262306a36Sopenharmony_ci#define stack_top(en)	((en)->stack[(en)->top].entity)
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/**
30562306a36Sopenharmony_ci * media_graph_walk_init - Allocate resources for graph walk
30662306a36Sopenharmony_ci * @graph: Media graph structure that will be used to walk the graph
30762306a36Sopenharmony_ci * @mdev: Media device
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Reserve resources for graph walk in media device's current
31062306a36Sopenharmony_ci * state. The memory must be released using
31162306a36Sopenharmony_ci * media_graph_walk_cleanup().
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci * Returns error on failure, zero on success.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_ci__must_check int media_graph_walk_init(
31662306a36Sopenharmony_ci	struct media_graph *graph, struct media_device *mdev)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	return media_entity_enum_init(&graph->ent_enum, mdev);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_init);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/**
32362306a36Sopenharmony_ci * media_graph_walk_cleanup - Release resources related to graph walking
32462306a36Sopenharmony_ci * @graph: Media graph structure that was used to walk the graph
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_civoid media_graph_walk_cleanup(struct media_graph *graph)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	media_entity_enum_cleanup(&graph->ent_enum);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_cleanup);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_civoid media_graph_walk_start(struct media_graph *graph,
33362306a36Sopenharmony_ci			    struct media_entity *entity)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	media_entity_enum_zero(&graph->ent_enum);
33662306a36Sopenharmony_ci	media_entity_enum_set(&graph->ent_enum, entity);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	graph->top = 0;
33962306a36Sopenharmony_ci	graph->stack[graph->top].entity = NULL;
34062306a36Sopenharmony_ci	stack_push(graph, entity);
34162306a36Sopenharmony_ci	dev_dbg(entity->graph_obj.mdev->dev,
34262306a36Sopenharmony_ci		"begin graph walk at '%s'\n", entity->name);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_start);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void media_graph_walk_iter(struct media_graph *graph)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct media_entity *entity = stack_top(graph);
34962306a36Sopenharmony_ci	struct media_link *link;
35062306a36Sopenharmony_ci	struct media_entity *next;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	link = list_entry(link_top(graph), typeof(*link), list);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* If the link is not a data link, don't follow it */
35562306a36Sopenharmony_ci	if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {
35662306a36Sopenharmony_ci		link_top(graph) = link_top(graph)->next;
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* The link is not enabled so we do not follow. */
36162306a36Sopenharmony_ci	if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
36262306a36Sopenharmony_ci		link_top(graph) = link_top(graph)->next;
36362306a36Sopenharmony_ci		dev_dbg(entity->graph_obj.mdev->dev,
36462306a36Sopenharmony_ci			"walk: skipping disabled link '%s':%u -> '%s':%u\n",
36562306a36Sopenharmony_ci			link->source->entity->name, link->source->index,
36662306a36Sopenharmony_ci			link->sink->entity->name, link->sink->index);
36762306a36Sopenharmony_ci		return;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Get the entity at the other end of the link. */
37162306a36Sopenharmony_ci	next = media_entity_other(entity, link);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Has the entity already been visited? */
37462306a36Sopenharmony_ci	if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
37562306a36Sopenharmony_ci		link_top(graph) = link_top(graph)->next;
37662306a36Sopenharmony_ci		dev_dbg(entity->graph_obj.mdev->dev,
37762306a36Sopenharmony_ci			"walk: skipping entity '%s' (already seen)\n",
37862306a36Sopenharmony_ci			next->name);
37962306a36Sopenharmony_ci		return;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* Push the new entity to stack and start over. */
38362306a36Sopenharmony_ci	link_top(graph) = link_top(graph)->next;
38462306a36Sopenharmony_ci	stack_push(graph, next);
38562306a36Sopenharmony_ci	dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",
38662306a36Sopenharmony_ci		next->name);
38762306a36Sopenharmony_ci	lockdep_assert_held(&entity->graph_obj.mdev->graph_mutex);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistruct media_entity *media_graph_walk_next(struct media_graph *graph)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct media_entity *entity;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (stack_top(graph) == NULL)
39562306a36Sopenharmony_ci		return NULL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/*
39862306a36Sopenharmony_ci	 * Depth first search. Push entity to stack and continue from
39962306a36Sopenharmony_ci	 * top of the stack until no more entities on the level can be
40062306a36Sopenharmony_ci	 * found.
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	while (link_top(graph) != &stack_top(graph)->links)
40362306a36Sopenharmony_ci		media_graph_walk_iter(graph);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	entity = stack_pop(graph);
40662306a36Sopenharmony_ci	dev_dbg(entity->graph_obj.mdev->dev,
40762306a36Sopenharmony_ci		"walk: returning entity '%s'\n", entity->name);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return entity;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_next);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
41462306a36Sopenharmony_ci * Pipeline management
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/*
41862306a36Sopenharmony_ci * The pipeline traversal stack stores pads that are reached during graph
41962306a36Sopenharmony_ci * traversal, with a list of links to be visited to continue the traversal.
42062306a36Sopenharmony_ci * When a new pad is reached, an entry is pushed on the top of the stack and
42162306a36Sopenharmony_ci * points to the incoming pad and the first link of the entity.
42262306a36Sopenharmony_ci *
42362306a36Sopenharmony_ci * To find further pads in the pipeline, the traversal algorithm follows
42462306a36Sopenharmony_ci * internal pad dependencies in the entity, and then links in the graph. It
42562306a36Sopenharmony_ci * does so by iterating over all links of the entity, and following enabled
42662306a36Sopenharmony_ci * links that originate from a pad that is internally connected to the incoming
42762306a36Sopenharmony_ci * pad, as reported by the media_entity_has_pad_interdep() function.
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/**
43162306a36Sopenharmony_ci * struct media_pipeline_walk_entry - Entry in the pipeline traversal stack
43262306a36Sopenharmony_ci *
43362306a36Sopenharmony_ci * @pad: The media pad being visited
43462306a36Sopenharmony_ci * @links: Links left to be visited
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistruct media_pipeline_walk_entry {
43762306a36Sopenharmony_ci	struct media_pad *pad;
43862306a36Sopenharmony_ci	struct list_head *links;
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * struct media_pipeline_walk - State used by the media pipeline traversal
44362306a36Sopenharmony_ci *				algorithm
44462306a36Sopenharmony_ci *
44562306a36Sopenharmony_ci * @mdev: The media device
44662306a36Sopenharmony_ci * @stack: Depth-first search stack
44762306a36Sopenharmony_ci * @stack.size: Number of allocated entries in @stack.entries
44862306a36Sopenharmony_ci * @stack.top: Index of the top stack entry (-1 if the stack is empty)
44962306a36Sopenharmony_ci * @stack.entries: Stack entries
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistruct media_pipeline_walk {
45262306a36Sopenharmony_ci	struct media_device *mdev;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	struct {
45562306a36Sopenharmony_ci		unsigned int size;
45662306a36Sopenharmony_ci		int top;
45762306a36Sopenharmony_ci		struct media_pipeline_walk_entry *entries;
45862306a36Sopenharmony_ci	} stack;
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci#define MEDIA_PIPELINE_STACK_GROW_STEP		16
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic struct media_pipeline_walk_entry *
46462306a36Sopenharmony_cimedia_pipeline_walk_top(struct media_pipeline_walk *walk)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	return &walk->stack.entries[walk->stack.top];
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic bool media_pipeline_walk_empty(struct media_pipeline_walk *walk)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	return walk->stack.top == -1;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/* Increase the stack size by MEDIA_PIPELINE_STACK_GROW_STEP elements. */
47562306a36Sopenharmony_cistatic int media_pipeline_walk_resize(struct media_pipeline_walk *walk)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct media_pipeline_walk_entry *entries;
47862306a36Sopenharmony_ci	unsigned int new_size;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* Safety check, to avoid stack overflows in case of bugs. */
48162306a36Sopenharmony_ci	if (walk->stack.size >= 256)
48262306a36Sopenharmony_ci		return -E2BIG;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	new_size = walk->stack.size + MEDIA_PIPELINE_STACK_GROW_STEP;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	entries = krealloc(walk->stack.entries,
48762306a36Sopenharmony_ci			   new_size * sizeof(*walk->stack.entries),
48862306a36Sopenharmony_ci			   GFP_KERNEL);
48962306a36Sopenharmony_ci	if (!entries)
49062306a36Sopenharmony_ci		return -ENOMEM;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	walk->stack.entries = entries;
49362306a36Sopenharmony_ci	walk->stack.size = new_size;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/* Push a new entry on the stack. */
49962306a36Sopenharmony_cistatic int media_pipeline_walk_push(struct media_pipeline_walk *walk,
50062306a36Sopenharmony_ci				    struct media_pad *pad)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct media_pipeline_walk_entry *entry;
50362306a36Sopenharmony_ci	int ret;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (walk->stack.top + 1 >= walk->stack.size) {
50662306a36Sopenharmony_ci		ret = media_pipeline_walk_resize(walk);
50762306a36Sopenharmony_ci		if (ret)
50862306a36Sopenharmony_ci			return ret;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	walk->stack.top++;
51262306a36Sopenharmony_ci	entry = media_pipeline_walk_top(walk);
51362306a36Sopenharmony_ci	entry->pad = pad;
51462306a36Sopenharmony_ci	entry->links = pad->entity->links.next;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	dev_dbg(walk->mdev->dev,
51762306a36Sopenharmony_ci		"media pipeline: pushed entry %u: '%s':%u\n",
51862306a36Sopenharmony_ci		walk->stack.top, pad->entity->name, pad->index);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/*
52462306a36Sopenharmony_ci * Move the top entry link cursor to the next link. If all links of the entry
52562306a36Sopenharmony_ci * have been visited, pop the entry itself. Return true if the entry has been
52662306a36Sopenharmony_ci * popped.
52762306a36Sopenharmony_ci */
52862306a36Sopenharmony_cistatic bool media_pipeline_walk_pop(struct media_pipeline_walk *walk)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct media_pipeline_walk_entry *entry;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (WARN_ON(walk->stack.top < 0))
53362306a36Sopenharmony_ci		return false;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	entry = media_pipeline_walk_top(walk);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (entry->links->next == &entry->pad->entity->links) {
53862306a36Sopenharmony_ci		dev_dbg(walk->mdev->dev,
53962306a36Sopenharmony_ci			"media pipeline: entry %u has no more links, popping\n",
54062306a36Sopenharmony_ci			walk->stack.top);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		walk->stack.top--;
54362306a36Sopenharmony_ci		return true;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	entry->links = entry->links->next;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	dev_dbg(walk->mdev->dev,
54962306a36Sopenharmony_ci		"media pipeline: moved entry %u to next link\n",
55062306a36Sopenharmony_ci		walk->stack.top);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return false;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/* Free all memory allocated while walking the pipeline. */
55662306a36Sopenharmony_cistatic void media_pipeline_walk_destroy(struct media_pipeline_walk *walk)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	kfree(walk->stack.entries);
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci/* Add a pad to the pipeline and push it to the stack. */
56262306a36Sopenharmony_cistatic int media_pipeline_add_pad(struct media_pipeline *pipe,
56362306a36Sopenharmony_ci				  struct media_pipeline_walk *walk,
56462306a36Sopenharmony_ci				  struct media_pad *pad)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct media_pipeline_pad *ppad;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	list_for_each_entry(ppad, &pipe->pads, list) {
56962306a36Sopenharmony_ci		if (ppad->pad == pad) {
57062306a36Sopenharmony_ci			dev_dbg(pad->graph_obj.mdev->dev,
57162306a36Sopenharmony_ci				"media pipeline: already contains pad '%s':%u\n",
57262306a36Sopenharmony_ci				pad->entity->name, pad->index);
57362306a36Sopenharmony_ci			return 0;
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	ppad = kzalloc(sizeof(*ppad), GFP_KERNEL);
57862306a36Sopenharmony_ci	if (!ppad)
57962306a36Sopenharmony_ci		return -ENOMEM;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	ppad->pipe = pipe;
58262306a36Sopenharmony_ci	ppad->pad = pad;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	list_add_tail(&ppad->list, &pipe->pads);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	dev_dbg(pad->graph_obj.mdev->dev,
58762306a36Sopenharmony_ci		"media pipeline: added pad '%s':%u\n",
58862306a36Sopenharmony_ci		pad->entity->name, pad->index);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return media_pipeline_walk_push(walk, pad);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/* Explore the next link of the entity at the top of the stack. */
59462306a36Sopenharmony_cistatic int media_pipeline_explore_next_link(struct media_pipeline *pipe,
59562306a36Sopenharmony_ci					    struct media_pipeline_walk *walk)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct media_pipeline_walk_entry *entry = media_pipeline_walk_top(walk);
59862306a36Sopenharmony_ci	struct media_pad *origin;
59962306a36Sopenharmony_ci	struct media_link *link;
60062306a36Sopenharmony_ci	struct media_pad *local;
60162306a36Sopenharmony_ci	struct media_pad *remote;
60262306a36Sopenharmony_ci	bool last_link;
60362306a36Sopenharmony_ci	int ret;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	origin = entry->pad;
60662306a36Sopenharmony_ci	link = list_entry(entry->links, typeof(*link), list);
60762306a36Sopenharmony_ci	last_link = media_pipeline_walk_pop(walk);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	dev_dbg(walk->mdev->dev,
61062306a36Sopenharmony_ci		"media pipeline: exploring link '%s':%u -> '%s':%u\n",
61162306a36Sopenharmony_ci		link->source->entity->name, link->source->index,
61262306a36Sopenharmony_ci		link->sink->entity->name, link->sink->index);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Get the local pad and remote pad. */
61562306a36Sopenharmony_ci	if (link->source->entity == origin->entity) {
61662306a36Sopenharmony_ci		local = link->source;
61762306a36Sopenharmony_ci		remote = link->sink;
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci		local = link->sink;
62062306a36Sopenharmony_ci		remote = link->source;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * Skip links that originate from a different pad than the incoming pad
62562306a36Sopenharmony_ci	 * that is not connected internally in the entity to the incoming pad.
62662306a36Sopenharmony_ci	 */
62762306a36Sopenharmony_ci	if (origin != local &&
62862306a36Sopenharmony_ci	    !media_entity_has_pad_interdep(origin->entity, origin->index,
62962306a36Sopenharmony_ci					   local->index)) {
63062306a36Sopenharmony_ci		dev_dbg(walk->mdev->dev,
63162306a36Sopenharmony_ci			"media pipeline: skipping link (no route)\n");
63262306a36Sopenharmony_ci		goto done;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	/*
63662306a36Sopenharmony_ci	 * Add the local pad of the link to the pipeline and push it to the
63762306a36Sopenharmony_ci	 * stack, if not already present.
63862306a36Sopenharmony_ci	 */
63962306a36Sopenharmony_ci	ret = media_pipeline_add_pad(pipe, walk, local);
64062306a36Sopenharmony_ci	if (ret)
64162306a36Sopenharmony_ci		return ret;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* Similarly, add the remote pad, but only if the link is enabled. */
64462306a36Sopenharmony_ci	if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
64562306a36Sopenharmony_ci		dev_dbg(walk->mdev->dev,
64662306a36Sopenharmony_ci			"media pipeline: skipping link (disabled)\n");
64762306a36Sopenharmony_ci		goto done;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	ret = media_pipeline_add_pad(pipe, walk, remote);
65162306a36Sopenharmony_ci	if (ret)
65262306a36Sopenharmony_ci		return ret;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cidone:
65562306a36Sopenharmony_ci	/*
65662306a36Sopenharmony_ci	 * If we're done iterating over links, iterate over pads of the entity.
65762306a36Sopenharmony_ci	 * This is necessary to discover pads that are not connected with any
65862306a36Sopenharmony_ci	 * link. Those are dead ends from a pipeline exploration point of view,
65962306a36Sopenharmony_ci	 * but are still part of the pipeline and need to be added to enable
66062306a36Sopenharmony_ci	 * proper validation.
66162306a36Sopenharmony_ci	 */
66262306a36Sopenharmony_ci	if (!last_link)
66362306a36Sopenharmony_ci		return 0;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	dev_dbg(walk->mdev->dev,
66662306a36Sopenharmony_ci		"media pipeline: adding unconnected pads of '%s'\n",
66762306a36Sopenharmony_ci		local->entity->name);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	media_entity_for_each_pad(origin->entity, local) {
67062306a36Sopenharmony_ci		/*
67162306a36Sopenharmony_ci		 * Skip the origin pad (already handled), pad that have links
67262306a36Sopenharmony_ci		 * (already discovered through iterating over links) and pads
67362306a36Sopenharmony_ci		 * not internally connected.
67462306a36Sopenharmony_ci		 */
67562306a36Sopenharmony_ci		if (origin == local || !local->num_links ||
67662306a36Sopenharmony_ci		    !media_entity_has_pad_interdep(origin->entity, origin->index,
67762306a36Sopenharmony_ci						   local->index))
67862306a36Sopenharmony_ci			continue;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		ret = media_pipeline_add_pad(pipe, walk, local);
68162306a36Sopenharmony_ci		if (ret)
68262306a36Sopenharmony_ci			return ret;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void media_pipeline_cleanup(struct media_pipeline *pipe)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	while (!list_empty(&pipe->pads)) {
69162306a36Sopenharmony_ci		struct media_pipeline_pad *ppad;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		ppad = list_first_entry(&pipe->pads, typeof(*ppad), list);
69462306a36Sopenharmony_ci		list_del(&ppad->list);
69562306a36Sopenharmony_ci		kfree(ppad);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int media_pipeline_populate(struct media_pipeline *pipe,
70062306a36Sopenharmony_ci				   struct media_pad *pad)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct media_pipeline_walk walk = { };
70362306a36Sopenharmony_ci	struct media_pipeline_pad *ppad;
70462306a36Sopenharmony_ci	int ret;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/*
70762306a36Sopenharmony_ci	 * Populate the media pipeline by walking the media graph, starting
70862306a36Sopenharmony_ci	 * from @pad.
70962306a36Sopenharmony_ci	 */
71062306a36Sopenharmony_ci	INIT_LIST_HEAD(&pipe->pads);
71162306a36Sopenharmony_ci	pipe->mdev = pad->graph_obj.mdev;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	walk.mdev = pipe->mdev;
71462306a36Sopenharmony_ci	walk.stack.top = -1;
71562306a36Sopenharmony_ci	ret = media_pipeline_add_pad(pipe, &walk, pad);
71662306a36Sopenharmony_ci	if (ret)
71762306a36Sopenharmony_ci		goto done;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/*
72062306a36Sopenharmony_ci	 * Use a depth-first search algorithm: as long as the stack is not
72162306a36Sopenharmony_ci	 * empty, explore the next link of the top entry. The
72262306a36Sopenharmony_ci	 * media_pipeline_explore_next_link() function will either move to the
72362306a36Sopenharmony_ci	 * next link, pop the entry if fully visited, or add new entries on
72462306a36Sopenharmony_ci	 * top.
72562306a36Sopenharmony_ci	 */
72662306a36Sopenharmony_ci	while (!media_pipeline_walk_empty(&walk)) {
72762306a36Sopenharmony_ci		ret = media_pipeline_explore_next_link(pipe, &walk);
72862306a36Sopenharmony_ci		if (ret)
72962306a36Sopenharmony_ci			goto done;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	dev_dbg(pad->graph_obj.mdev->dev,
73362306a36Sopenharmony_ci		"media pipeline populated, found pads:\n");
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	list_for_each_entry(ppad, &pipe->pads, list)
73662306a36Sopenharmony_ci		dev_dbg(pad->graph_obj.mdev->dev, "- '%s':%u\n",
73762306a36Sopenharmony_ci			ppad->pad->entity->name, ppad->pad->index);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	WARN_ON(walk.stack.top != -1);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	ret = 0;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cidone:
74462306a36Sopenharmony_ci	media_pipeline_walk_destroy(&walk);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (ret)
74762306a36Sopenharmony_ci		media_pipeline_cleanup(pipe);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	return ret;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci__must_check int __media_pipeline_start(struct media_pad *pad,
75362306a36Sopenharmony_ci					struct media_pipeline *pipe)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct media_device *mdev = pad->graph_obj.mdev;
75662306a36Sopenharmony_ci	struct media_pipeline_pad *err_ppad;
75762306a36Sopenharmony_ci	struct media_pipeline_pad *ppad;
75862306a36Sopenharmony_ci	int ret;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	lockdep_assert_held(&mdev->graph_mutex);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/*
76362306a36Sopenharmony_ci	 * If the pad is already part of a pipeline, that pipeline must be the
76462306a36Sopenharmony_ci	 * same as the pipe given to media_pipeline_start().
76562306a36Sopenharmony_ci	 */
76662306a36Sopenharmony_ci	if (WARN_ON(pad->pipe && pad->pipe != pipe))
76762306a36Sopenharmony_ci		return -EINVAL;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/*
77062306a36Sopenharmony_ci	 * If the pipeline has already been started, it is guaranteed to be
77162306a36Sopenharmony_ci	 * valid, so just increase the start count.
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	if (pipe->start_count) {
77462306a36Sopenharmony_ci		pipe->start_count++;
77562306a36Sopenharmony_ci		return 0;
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/*
77962306a36Sopenharmony_ci	 * Populate the pipeline. This populates the media_pipeline pads list
78062306a36Sopenharmony_ci	 * with media_pipeline_pad instances for each pad found during graph
78162306a36Sopenharmony_ci	 * walk.
78262306a36Sopenharmony_ci	 */
78362306a36Sopenharmony_ci	ret = media_pipeline_populate(pipe, pad);
78462306a36Sopenharmony_ci	if (ret)
78562306a36Sopenharmony_ci		return ret;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/*
78862306a36Sopenharmony_ci	 * Now that all the pads in the pipeline have been gathered, perform
78962306a36Sopenharmony_ci	 * the validation steps.
79062306a36Sopenharmony_ci	 */
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	list_for_each_entry(ppad, &pipe->pads, list) {
79362306a36Sopenharmony_ci		struct media_pad *pad = ppad->pad;
79462306a36Sopenharmony_ci		struct media_entity *entity = pad->entity;
79562306a36Sopenharmony_ci		bool has_enabled_link = false;
79662306a36Sopenharmony_ci		struct media_link *link;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		dev_dbg(mdev->dev, "Validating pad '%s':%u\n", pad->entity->name,
79962306a36Sopenharmony_ci			pad->index);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		/*
80262306a36Sopenharmony_ci		 * 1. Ensure that the pad doesn't already belong to a different
80362306a36Sopenharmony_ci		 * pipeline.
80462306a36Sopenharmony_ci		 */
80562306a36Sopenharmony_ci		if (pad->pipe) {
80662306a36Sopenharmony_ci			dev_dbg(mdev->dev, "Failed to start pipeline: pad '%s':%u busy\n",
80762306a36Sopenharmony_ci				pad->entity->name, pad->index);
80862306a36Sopenharmony_ci			ret = -EBUSY;
80962306a36Sopenharmony_ci			goto error;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		/*
81362306a36Sopenharmony_ci		 * 2. Validate all active links whose sink is the current pad.
81462306a36Sopenharmony_ci		 * Validation of the source pads is performed in the context of
81562306a36Sopenharmony_ci		 * the connected sink pad to avoid duplicating checks.
81662306a36Sopenharmony_ci		 */
81762306a36Sopenharmony_ci		for_each_media_entity_data_link(entity, link) {
81862306a36Sopenharmony_ci			/* Skip links unrelated to the current pad. */
81962306a36Sopenharmony_ci			if (link->sink != pad && link->source != pad)
82062306a36Sopenharmony_ci				continue;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci			/* Record if the pad has links and enabled links. */
82362306a36Sopenharmony_ci			if (link->flags & MEDIA_LNK_FL_ENABLED)
82462306a36Sopenharmony_ci				has_enabled_link = true;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci			/*
82762306a36Sopenharmony_ci			 * Validate the link if it's enabled and has the
82862306a36Sopenharmony_ci			 * current pad as its sink.
82962306a36Sopenharmony_ci			 */
83062306a36Sopenharmony_ci			if (!(link->flags & MEDIA_LNK_FL_ENABLED))
83162306a36Sopenharmony_ci				continue;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci			if (link->sink != pad)
83462306a36Sopenharmony_ci				continue;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			if (!entity->ops || !entity->ops->link_validate)
83762306a36Sopenharmony_ci				continue;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci			ret = entity->ops->link_validate(link);
84062306a36Sopenharmony_ci			if (ret) {
84162306a36Sopenharmony_ci				dev_dbg(mdev->dev,
84262306a36Sopenharmony_ci					"Link '%s':%u -> '%s':%u failed validation: %d\n",
84362306a36Sopenharmony_ci					link->source->entity->name,
84462306a36Sopenharmony_ci					link->source->index,
84562306a36Sopenharmony_ci					link->sink->entity->name,
84662306a36Sopenharmony_ci					link->sink->index, ret);
84762306a36Sopenharmony_ci				goto error;
84862306a36Sopenharmony_ci			}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci			dev_dbg(mdev->dev,
85162306a36Sopenharmony_ci				"Link '%s':%u -> '%s':%u is valid\n",
85262306a36Sopenharmony_ci				link->source->entity->name,
85362306a36Sopenharmony_ci				link->source->index,
85462306a36Sopenharmony_ci				link->sink->entity->name,
85562306a36Sopenharmony_ci				link->sink->index);
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		/*
85962306a36Sopenharmony_ci		 * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
86062306a36Sopenharmony_ci		 * ensure that it has either no link or an enabled link.
86162306a36Sopenharmony_ci		 */
86262306a36Sopenharmony_ci		if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) &&
86362306a36Sopenharmony_ci		    !has_enabled_link) {
86462306a36Sopenharmony_ci			dev_dbg(mdev->dev,
86562306a36Sopenharmony_ci				"Pad '%s':%u must be connected by an enabled link\n",
86662306a36Sopenharmony_ci				pad->entity->name, pad->index);
86762306a36Sopenharmony_ci			ret = -ENOLINK;
86862306a36Sopenharmony_ci			goto error;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		/* Validation passed, store the pipe pointer in the pad. */
87262306a36Sopenharmony_ci		pad->pipe = pipe;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	pipe->start_count++;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return 0;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cierror:
88062306a36Sopenharmony_ci	/*
88162306a36Sopenharmony_ci	 * Link validation on graph failed. We revert what we did and
88262306a36Sopenharmony_ci	 * return the error.
88362306a36Sopenharmony_ci	 */
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	list_for_each_entry(err_ppad, &pipe->pads, list) {
88662306a36Sopenharmony_ci		if (err_ppad == ppad)
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		err_ppad->pad->pipe = NULL;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	media_pipeline_cleanup(pipe);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return ret;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_start);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci__must_check int media_pipeline_start(struct media_pad *pad,
89962306a36Sopenharmony_ci				      struct media_pipeline *pipe)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct media_device *mdev = pad->graph_obj.mdev;
90262306a36Sopenharmony_ci	int ret;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
90562306a36Sopenharmony_ci	ret = __media_pipeline_start(pad, pipe);
90662306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
90762306a36Sopenharmony_ci	return ret;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_start);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_civoid __media_pipeline_stop(struct media_pad *pad)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct media_pipeline *pipe = pad->pipe;
91462306a36Sopenharmony_ci	struct media_pipeline_pad *ppad;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * If the following check fails, the driver has performed an
91862306a36Sopenharmony_ci	 * unbalanced call to media_pipeline_stop()
91962306a36Sopenharmony_ci	 */
92062306a36Sopenharmony_ci	if (WARN_ON(!pipe))
92162306a36Sopenharmony_ci		return;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (--pipe->start_count)
92462306a36Sopenharmony_ci		return;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	list_for_each_entry(ppad, &pipe->pads, list)
92762306a36Sopenharmony_ci		ppad->pad->pipe = NULL;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	media_pipeline_cleanup(pipe);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (pipe->allocated)
93262306a36Sopenharmony_ci		kfree(pipe);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_stop);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_civoid media_pipeline_stop(struct media_pad *pad)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct media_device *mdev = pad->graph_obj.mdev;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
94162306a36Sopenharmony_ci	__media_pipeline_stop(pad);
94262306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_stop);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci__must_check int media_pipeline_alloc_start(struct media_pad *pad)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct media_device *mdev = pad->graph_obj.mdev;
94962306a36Sopenharmony_ci	struct media_pipeline *new_pipe = NULL;
95062306a36Sopenharmony_ci	struct media_pipeline *pipe;
95162306a36Sopenharmony_ci	int ret;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/*
95662306a36Sopenharmony_ci	 * Is the pad already part of a pipeline? If not, we need to allocate
95762306a36Sopenharmony_ci	 * a pipe.
95862306a36Sopenharmony_ci	 */
95962306a36Sopenharmony_ci	pipe = media_pad_pipeline(pad);
96062306a36Sopenharmony_ci	if (!pipe) {
96162306a36Sopenharmony_ci		new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL);
96262306a36Sopenharmony_ci		if (!new_pipe) {
96362306a36Sopenharmony_ci			ret = -ENOMEM;
96462306a36Sopenharmony_ci			goto out;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		pipe = new_pipe;
96862306a36Sopenharmony_ci		pipe->allocated = true;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	ret = __media_pipeline_start(pad, pipe);
97262306a36Sopenharmony_ci	if (ret)
97362306a36Sopenharmony_ci		kfree(new_pipe);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ciout:
97662306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return ret;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_alloc_start);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistruct media_pad *
98362306a36Sopenharmony_ci__media_pipeline_pad_iter_next(struct media_pipeline *pipe,
98462306a36Sopenharmony_ci			       struct media_pipeline_pad_iter *iter,
98562306a36Sopenharmony_ci			       struct media_pad *pad)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	if (!pad)
98862306a36Sopenharmony_ci		iter->cursor = pipe->pads.next;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (iter->cursor == &pipe->pads)
99162306a36Sopenharmony_ci		return NULL;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	pad = list_entry(iter->cursor, struct media_pipeline_pad, list)->pad;
99462306a36Sopenharmony_ci	iter->cursor = iter->cursor->next;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	return pad;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_pad_iter_next);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ciint media_pipeline_entity_iter_init(struct media_pipeline *pipe,
100162306a36Sopenharmony_ci				    struct media_pipeline_entity_iter *iter)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	return media_entity_enum_init(&iter->ent_enum, pipe->mdev);
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_entity_iter_init);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_civoid media_pipeline_entity_iter_cleanup(struct media_pipeline_entity_iter *iter)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	media_entity_enum_cleanup(&iter->ent_enum);
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_entity_iter_cleanup);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistruct media_entity *
101462306a36Sopenharmony_ci__media_pipeline_entity_iter_next(struct media_pipeline *pipe,
101562306a36Sopenharmony_ci				  struct media_pipeline_entity_iter *iter,
101662306a36Sopenharmony_ci				  struct media_entity *entity)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	if (!entity)
101962306a36Sopenharmony_ci		iter->cursor = pipe->pads.next;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	while (iter->cursor != &pipe->pads) {
102262306a36Sopenharmony_ci		struct media_pipeline_pad *ppad;
102362306a36Sopenharmony_ci		struct media_entity *entity;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci		ppad = list_entry(iter->cursor, struct media_pipeline_pad, list);
102662306a36Sopenharmony_ci		entity = ppad->pad->entity;
102762306a36Sopenharmony_ci		iter->cursor = iter->cursor->next;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		if (!media_entity_enum_test_and_set(&iter->ent_enum, entity))
103062306a36Sopenharmony_ci			return entity;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	return NULL;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_entity_iter_next);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
103862306a36Sopenharmony_ci * Links management
103962306a36Sopenharmony_ci */
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic struct media_link *media_add_link(struct list_head *head)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct media_link *link;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	link = kzalloc(sizeof(*link), GFP_KERNEL);
104662306a36Sopenharmony_ci	if (link == NULL)
104762306a36Sopenharmony_ci		return NULL;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	list_add_tail(&link->list, head);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	return link;
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic void __media_entity_remove_link(struct media_entity *entity,
105562306a36Sopenharmony_ci				       struct media_link *link)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	struct media_link *rlink, *tmp;
105862306a36Sopenharmony_ci	struct media_entity *remote;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* Remove the reverse links for a data link. */
106162306a36Sopenharmony_ci	if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {
106262306a36Sopenharmony_ci		link->source->num_links--;
106362306a36Sopenharmony_ci		link->sink->num_links--;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci		if (link->source->entity == entity)
106662306a36Sopenharmony_ci			remote = link->sink->entity;
106762306a36Sopenharmony_ci		else
106862306a36Sopenharmony_ci			remote = link->source->entity;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
107162306a36Sopenharmony_ci			if (rlink != link->reverse)
107262306a36Sopenharmony_ci				continue;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci			if (link->source->entity == entity)
107562306a36Sopenharmony_ci				remote->num_backlinks--;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci			/* Remove the remote link */
107862306a36Sopenharmony_ci			list_del(&rlink->list);
107962306a36Sopenharmony_ci			media_gobj_destroy(&rlink->graph_obj);
108062306a36Sopenharmony_ci			kfree(rlink);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci			if (--remote->num_links == 0)
108362306a36Sopenharmony_ci				break;
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	list_del(&link->list);
108862306a36Sopenharmony_ci	media_gobj_destroy(&link->graph_obj);
108962306a36Sopenharmony_ci	kfree(link);
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ciint media_get_pad_index(struct media_entity *entity, u32 pad_type,
109362306a36Sopenharmony_ci			enum media_pad_signal_type sig_type)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	unsigned int i;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (!entity)
109862306a36Sopenharmony_ci		return -EINVAL;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	for (i = 0; i < entity->num_pads; i++) {
110162306a36Sopenharmony_ci		if ((entity->pads[i].flags &
110262306a36Sopenharmony_ci		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type)
110362306a36Sopenharmony_ci			continue;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (entity->pads[i].sig_type == sig_type)
110662306a36Sopenharmony_ci			return i;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci	return -EINVAL;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_get_pad_index);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ciint
111362306a36Sopenharmony_cimedia_create_pad_link(struct media_entity *source, u16 source_pad,
111462306a36Sopenharmony_ci			 struct media_entity *sink, u16 sink_pad, u32 flags)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct media_link *link;
111762306a36Sopenharmony_ci	struct media_link *backlink;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (flags & MEDIA_LNK_FL_LINK_TYPE)
112062306a36Sopenharmony_ci		return -EINVAL;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	flags |= MEDIA_LNK_FL_DATA_LINK;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (WARN_ON(!source || !sink) ||
112562306a36Sopenharmony_ci	    WARN_ON(source_pad >= source->num_pads) ||
112662306a36Sopenharmony_ci	    WARN_ON(sink_pad >= sink->num_pads))
112762306a36Sopenharmony_ci		return -EINVAL;
112862306a36Sopenharmony_ci	if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE)))
112962306a36Sopenharmony_ci		return -EINVAL;
113062306a36Sopenharmony_ci	if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))
113162306a36Sopenharmony_ci		return -EINVAL;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	link = media_add_link(&source->links);
113462306a36Sopenharmony_ci	if (link == NULL)
113562306a36Sopenharmony_ci		return -ENOMEM;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	link->source = &source->pads[source_pad];
113862306a36Sopenharmony_ci	link->sink = &sink->pads[sink_pad];
113962306a36Sopenharmony_ci	link->flags = flags;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/* Initialize graph object embedded at the new link */
114262306a36Sopenharmony_ci	media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,
114362306a36Sopenharmony_ci			&link->graph_obj);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* Create the backlink. Backlinks are used to help graph traversal and
114662306a36Sopenharmony_ci	 * are not reported to userspace.
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci	backlink = media_add_link(&sink->links);
114962306a36Sopenharmony_ci	if (backlink == NULL) {
115062306a36Sopenharmony_ci		__media_entity_remove_link(source, link);
115162306a36Sopenharmony_ci		return -ENOMEM;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	backlink->source = &source->pads[source_pad];
115562306a36Sopenharmony_ci	backlink->sink = &sink->pads[sink_pad];
115662306a36Sopenharmony_ci	backlink->flags = flags;
115762306a36Sopenharmony_ci	backlink->is_backlink = true;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* Initialize graph object embedded at the new link */
116062306a36Sopenharmony_ci	media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK,
116162306a36Sopenharmony_ci			&backlink->graph_obj);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	link->reverse = backlink;
116462306a36Sopenharmony_ci	backlink->reverse = link;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	sink->num_backlinks++;
116762306a36Sopenharmony_ci	sink->num_links++;
116862306a36Sopenharmony_ci	source->num_links++;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	link->source->num_links++;
117162306a36Sopenharmony_ci	link->sink->num_links++;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	return 0;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_link);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ciint media_create_pad_links(const struct media_device *mdev,
117862306a36Sopenharmony_ci			   const u32 source_function,
117962306a36Sopenharmony_ci			   struct media_entity *source,
118062306a36Sopenharmony_ci			   const u16 source_pad,
118162306a36Sopenharmony_ci			   const u32 sink_function,
118262306a36Sopenharmony_ci			   struct media_entity *sink,
118362306a36Sopenharmony_ci			   const u16 sink_pad,
118462306a36Sopenharmony_ci			   u32 flags,
118562306a36Sopenharmony_ci			   const bool allow_both_undefined)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	struct media_entity *entity;
118862306a36Sopenharmony_ci	unsigned function;
118962306a36Sopenharmony_ci	int ret;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	/* Trivial case: 1:1 relation */
119262306a36Sopenharmony_ci	if (source && sink)
119362306a36Sopenharmony_ci		return media_create_pad_link(source, source_pad,
119462306a36Sopenharmony_ci					     sink, sink_pad, flags);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	/* Worse case scenario: n:n relation */
119762306a36Sopenharmony_ci	if (!source && !sink) {
119862306a36Sopenharmony_ci		if (!allow_both_undefined)
119962306a36Sopenharmony_ci			return 0;
120062306a36Sopenharmony_ci		media_device_for_each_entity(source, mdev) {
120162306a36Sopenharmony_ci			if (source->function != source_function)
120262306a36Sopenharmony_ci				continue;
120362306a36Sopenharmony_ci			media_device_for_each_entity(sink, mdev) {
120462306a36Sopenharmony_ci				if (sink->function != sink_function)
120562306a36Sopenharmony_ci					continue;
120662306a36Sopenharmony_ci				ret = media_create_pad_link(source, source_pad,
120762306a36Sopenharmony_ci							    sink, sink_pad,
120862306a36Sopenharmony_ci							    flags);
120962306a36Sopenharmony_ci				if (ret)
121062306a36Sopenharmony_ci					return ret;
121162306a36Sopenharmony_ci				flags &= ~(MEDIA_LNK_FL_ENABLED |
121262306a36Sopenharmony_ci					   MEDIA_LNK_FL_IMMUTABLE);
121362306a36Sopenharmony_ci			}
121462306a36Sopenharmony_ci		}
121562306a36Sopenharmony_ci		return 0;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	/* Handle 1:n and n:1 cases */
121962306a36Sopenharmony_ci	if (source)
122062306a36Sopenharmony_ci		function = sink_function;
122162306a36Sopenharmony_ci	else
122262306a36Sopenharmony_ci		function = source_function;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	media_device_for_each_entity(entity, mdev) {
122562306a36Sopenharmony_ci		if (entity->function != function)
122662306a36Sopenharmony_ci			continue;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci		if (source)
122962306a36Sopenharmony_ci			ret = media_create_pad_link(source, source_pad,
123062306a36Sopenharmony_ci						    entity, sink_pad, flags);
123162306a36Sopenharmony_ci		else
123262306a36Sopenharmony_ci			ret = media_create_pad_link(entity, source_pad,
123362306a36Sopenharmony_ci						    sink, sink_pad, flags);
123462306a36Sopenharmony_ci		if (ret)
123562306a36Sopenharmony_ci			return ret;
123662306a36Sopenharmony_ci		flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci	return 0;
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_links);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_civoid __media_entity_remove_links(struct media_entity *entity)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	struct media_link *link, *tmp;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	list_for_each_entry_safe(link, tmp, &entity->links, list)
124762306a36Sopenharmony_ci		__media_entity_remove_link(entity, link);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	entity->num_links = 0;
125062306a36Sopenharmony_ci	entity->num_backlinks = 0;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_remove_links);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_civoid media_entity_remove_links(struct media_entity *entity)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct media_device *mdev = entity->graph_obj.mdev;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* Do nothing if the entity is not registered. */
125962306a36Sopenharmony_ci	if (mdev == NULL)
126062306a36Sopenharmony_ci		return;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
126362306a36Sopenharmony_ci	__media_entity_remove_links(entity);
126462306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remove_links);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	int ret;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	/* Notify both entities. */
127362306a36Sopenharmony_ci	ret = media_entity_call(link->source->entity, link_setup,
127462306a36Sopenharmony_ci				link->source, link->sink, flags);
127562306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD)
127662306a36Sopenharmony_ci		return ret;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	ret = media_entity_call(link->sink->entity, link_setup,
127962306a36Sopenharmony_ci				link->sink, link->source, flags);
128062306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD) {
128162306a36Sopenharmony_ci		media_entity_call(link->source->entity, link_setup,
128262306a36Sopenharmony_ci				  link->source, link->sink, link->flags);
128362306a36Sopenharmony_ci		return ret;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	link->flags = flags;
128762306a36Sopenharmony_ci	link->reverse->flags = link->flags;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return 0;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ciint __media_entity_setup_link(struct media_link *link, u32 flags)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	const u32 mask = MEDIA_LNK_FL_ENABLED;
129562306a36Sopenharmony_ci	struct media_device *mdev;
129662306a36Sopenharmony_ci	struct media_pad *source, *sink;
129762306a36Sopenharmony_ci	int ret = -EBUSY;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (link == NULL)
130062306a36Sopenharmony_ci		return -EINVAL;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	/* The non-modifiable link flags must not be modified. */
130362306a36Sopenharmony_ci	if ((link->flags & ~mask) != (flags & ~mask))
130462306a36Sopenharmony_ci		return -EINVAL;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
130762306a36Sopenharmony_ci		return link->flags == flags ? 0 : -EINVAL;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (link->flags == flags)
131062306a36Sopenharmony_ci		return 0;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	source = link->source;
131362306a36Sopenharmony_ci	sink = link->sink;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
131662306a36Sopenharmony_ci	    (media_pad_is_streaming(source) || media_pad_is_streaming(sink)))
131762306a36Sopenharmony_ci		return -EBUSY;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	mdev = source->graph_obj.mdev;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (mdev->ops && mdev->ops->link_notify) {
132262306a36Sopenharmony_ci		ret = mdev->ops->link_notify(link, flags,
132362306a36Sopenharmony_ci					     MEDIA_DEV_NOTIFY_PRE_LINK_CH);
132462306a36Sopenharmony_ci		if (ret < 0)
132562306a36Sopenharmony_ci			return ret;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	ret = __media_entity_setup_link_notify(link, flags);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (mdev->ops && mdev->ops->link_notify)
133162306a36Sopenharmony_ci		mdev->ops->link_notify(link, flags,
133262306a36Sopenharmony_ci				       MEDIA_DEV_NOTIFY_POST_LINK_CH);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	return ret;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_setup_link);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ciint media_entity_setup_link(struct media_link *link, u32 flags)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	int ret;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	mutex_lock(&link->graph_obj.mdev->graph_mutex);
134362306a36Sopenharmony_ci	ret = __media_entity_setup_link(link, flags);
134462306a36Sopenharmony_ci	mutex_unlock(&link->graph_obj.mdev->graph_mutex);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return ret;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_setup_link);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistruct media_link *
135162306a36Sopenharmony_cimedia_entity_find_link(struct media_pad *source, struct media_pad *sink)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct media_link *link;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	for_each_media_entity_data_link(source->entity, link) {
135662306a36Sopenharmony_ci		if (link->source->entity == source->entity &&
135762306a36Sopenharmony_ci		    link->source->index == source->index &&
135862306a36Sopenharmony_ci		    link->sink->entity == sink->entity &&
135962306a36Sopenharmony_ci		    link->sink->index == sink->index)
136062306a36Sopenharmony_ci			return link;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	return NULL;
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_find_link);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistruct media_pad *media_pad_remote_pad_first(const struct media_pad *pad)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct media_link *link;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	for_each_media_entity_data_link(pad->entity, link) {
137262306a36Sopenharmony_ci		if (!(link->flags & MEDIA_LNK_FL_ENABLED))
137362306a36Sopenharmony_ci			continue;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		if (link->source == pad)
137662306a36Sopenharmony_ci			return link->sink;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci		if (link->sink == pad)
137962306a36Sopenharmony_ci			return link->source;
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return NULL;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_remote_pad_first);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistruct media_pad *
138862306a36Sopenharmony_cimedia_entity_remote_pad_unique(const struct media_entity *entity,
138962306a36Sopenharmony_ci			       unsigned int type)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct media_pad *pad = NULL;
139262306a36Sopenharmony_ci	struct media_link *link;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	list_for_each_entry(link, &entity->links, list) {
139562306a36Sopenharmony_ci		struct media_pad *local_pad;
139662306a36Sopenharmony_ci		struct media_pad *remote_pad;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		if (((link->flags & MEDIA_LNK_FL_LINK_TYPE) !=
139962306a36Sopenharmony_ci		     MEDIA_LNK_FL_DATA_LINK) ||
140062306a36Sopenharmony_ci		    !(link->flags & MEDIA_LNK_FL_ENABLED))
140162306a36Sopenharmony_ci			continue;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		if (type == MEDIA_PAD_FL_SOURCE) {
140462306a36Sopenharmony_ci			local_pad = link->sink;
140562306a36Sopenharmony_ci			remote_pad = link->source;
140662306a36Sopenharmony_ci		} else {
140762306a36Sopenharmony_ci			local_pad = link->source;
140862306a36Sopenharmony_ci			remote_pad = link->sink;
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		if (local_pad->entity == entity) {
141262306a36Sopenharmony_ci			if (pad)
141362306a36Sopenharmony_ci				return ERR_PTR(-ENOTUNIQ);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci			pad = remote_pad;
141662306a36Sopenharmony_ci		}
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (!pad)
142062306a36Sopenharmony_ci		return ERR_PTR(-ENOLINK);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	return pad;
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remote_pad_unique);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistruct media_pad *media_pad_remote_pad_unique(const struct media_pad *pad)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct media_pad *found_pad = NULL;
142962306a36Sopenharmony_ci	struct media_link *link;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	list_for_each_entry(link, &pad->entity->links, list) {
143262306a36Sopenharmony_ci		struct media_pad *remote_pad;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		if (!(link->flags & MEDIA_LNK_FL_ENABLED))
143562306a36Sopenharmony_ci			continue;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		if (link->sink == pad)
143862306a36Sopenharmony_ci			remote_pad = link->source;
143962306a36Sopenharmony_ci		else if (link->source == pad)
144062306a36Sopenharmony_ci			remote_pad = link->sink;
144162306a36Sopenharmony_ci		else
144262306a36Sopenharmony_ci			continue;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		if (found_pad)
144562306a36Sopenharmony_ci			return ERR_PTR(-ENOTUNIQ);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		found_pad = remote_pad;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (!found_pad)
145162306a36Sopenharmony_ci		return ERR_PTR(-ENOLINK);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	return found_pad;
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_remote_pad_unique);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ciint media_entity_get_fwnode_pad(struct media_entity *entity,
145862306a36Sopenharmony_ci				const struct fwnode_handle *fwnode,
145962306a36Sopenharmony_ci				unsigned long direction_flags)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct fwnode_endpoint endpoint;
146262306a36Sopenharmony_ci	unsigned int i;
146362306a36Sopenharmony_ci	int ret;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (!entity->ops || !entity->ops->get_fwnode_pad) {
146662306a36Sopenharmony_ci		for (i = 0; i < entity->num_pads; i++) {
146762306a36Sopenharmony_ci			if (entity->pads[i].flags & direction_flags)
146862306a36Sopenharmony_ci				return i;
146962306a36Sopenharmony_ci		}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		return -ENXIO;
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);
147562306a36Sopenharmony_ci	if (ret)
147662306a36Sopenharmony_ci		return ret;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	ret = entity->ops->get_fwnode_pad(entity, &endpoint);
147962306a36Sopenharmony_ci	if (ret < 0)
148062306a36Sopenharmony_ci		return ret;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (ret >= entity->num_pads)
148362306a36Sopenharmony_ci		return -ENXIO;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (!(entity->pads[ret].flags & direction_flags))
148662306a36Sopenharmony_ci		return -ENXIO;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	return ret;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistruct media_pipeline *media_entity_pipeline(struct media_entity *entity)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct media_pad *pad;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	media_entity_for_each_pad(entity, pad) {
149762306a36Sopenharmony_ci		if (pad->pipe)
149862306a36Sopenharmony_ci			return pad->pipe;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	return NULL;
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_pipeline);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistruct media_pipeline *media_pad_pipeline(struct media_pad *pad)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	return pad->pipe;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_pipeline);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic void media_interface_init(struct media_device *mdev,
151262306a36Sopenharmony_ci				 struct media_interface *intf,
151362306a36Sopenharmony_ci				 u32 gobj_type,
151462306a36Sopenharmony_ci				 u32 intf_type, u32 flags)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	intf->type = intf_type;
151762306a36Sopenharmony_ci	intf->flags = flags;
151862306a36Sopenharmony_ci	INIT_LIST_HEAD(&intf->links);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	media_gobj_create(mdev, gobj_type, &intf->graph_obj);
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci/* Functions related to the media interface via device nodes */
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistruct media_intf_devnode *media_devnode_create(struct media_device *mdev,
152662306a36Sopenharmony_ci						u32 type, u32 flags,
152762306a36Sopenharmony_ci						u32 major, u32 minor)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct media_intf_devnode *devnode;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
153262306a36Sopenharmony_ci	if (!devnode)
153362306a36Sopenharmony_ci		return NULL;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	devnode->major = major;
153662306a36Sopenharmony_ci	devnode->minor = minor;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE,
153962306a36Sopenharmony_ci			     type, flags);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	return devnode;
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_create);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_civoid media_devnode_remove(struct media_intf_devnode *devnode)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	media_remove_intf_links(&devnode->intf);
154862306a36Sopenharmony_ci	media_gobj_destroy(&devnode->intf.graph_obj);
154962306a36Sopenharmony_ci	kfree(devnode);
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_remove);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistruct media_link *media_create_intf_link(struct media_entity *entity,
155462306a36Sopenharmony_ci					    struct media_interface *intf,
155562306a36Sopenharmony_ci					    u32 flags)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	struct media_link *link;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	link = media_add_link(&intf->links);
156062306a36Sopenharmony_ci	if (link == NULL)
156162306a36Sopenharmony_ci		return NULL;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	link->intf = intf;
156462306a36Sopenharmony_ci	link->entity = entity;
156562306a36Sopenharmony_ci	link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* Initialize graph object embedded at the new link */
156862306a36Sopenharmony_ci	media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK,
156962306a36Sopenharmony_ci			&link->graph_obj);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	return link;
157262306a36Sopenharmony_ci}
157362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_intf_link);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_civoid __media_remove_intf_link(struct media_link *link)
157662306a36Sopenharmony_ci{
157762306a36Sopenharmony_ci	list_del(&link->list);
157862306a36Sopenharmony_ci	media_gobj_destroy(&link->graph_obj);
157962306a36Sopenharmony_ci	kfree(link);
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_link);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_civoid media_remove_intf_link(struct media_link *link)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	struct media_device *mdev = link->graph_obj.mdev;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/* Do nothing if the intf is not registered. */
158862306a36Sopenharmony_ci	if (mdev == NULL)
158962306a36Sopenharmony_ci		return;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
159262306a36Sopenharmony_ci	__media_remove_intf_link(link);
159362306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
159462306a36Sopenharmony_ci}
159562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_link);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_civoid __media_remove_intf_links(struct media_interface *intf)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct media_link *link, *tmp;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	list_for_each_entry_safe(link, tmp, &intf->links, list)
160262306a36Sopenharmony_ci		__media_remove_intf_link(link);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_links);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_civoid media_remove_intf_links(struct media_interface *intf)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct media_device *mdev = intf->graph_obj.mdev;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/* Do nothing if the intf is not registered. */
161262306a36Sopenharmony_ci	if (mdev == NULL)
161362306a36Sopenharmony_ci		return;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	mutex_lock(&mdev->graph_mutex);
161662306a36Sopenharmony_ci	__media_remove_intf_links(intf);
161762306a36Sopenharmony_ci	mutex_unlock(&mdev->graph_mutex);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_links);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistruct media_link *media_create_ancillary_link(struct media_entity *primary,
162262306a36Sopenharmony_ci					       struct media_entity *ancillary)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	struct media_link *link;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	link = media_add_link(&primary->links);
162762306a36Sopenharmony_ci	if (!link)
162862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	link->gobj0 = &primary->graph_obj;
163162306a36Sopenharmony_ci	link->gobj1 = &ancillary->graph_obj;
163262306a36Sopenharmony_ci	link->flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED |
163362306a36Sopenharmony_ci		      MEDIA_LNK_FL_ANCILLARY_LINK;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/* Initialize graph object embedded in the new link */
163662306a36Sopenharmony_ci	media_gobj_create(primary->graph_obj.mdev, MEDIA_GRAPH_LINK,
163762306a36Sopenharmony_ci			  &link->graph_obj);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	return link;
164062306a36Sopenharmony_ci}
164162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_ancillary_link);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistruct media_link *__media_entity_next_link(struct media_entity *entity,
164462306a36Sopenharmony_ci					    struct media_link *link,
164562306a36Sopenharmony_ci					    unsigned long link_type)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	link = link ? list_next_entry(link, list)
164862306a36Sopenharmony_ci		    : list_first_entry(&entity->links, typeof(*link), list);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	list_for_each_entry_from(link, &entity->links, list)
165162306a36Sopenharmony_ci		if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == link_type)
165262306a36Sopenharmony_ci			return link;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	return NULL;
165562306a36Sopenharmony_ci}
165662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_next_link);
1657