xref: /OK3568_Linux_fs/kernel/drivers/media/mc/mc-entity.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Media entity
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2010 Nokia Corporation
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
8*4882a593Smuzhiyun  *	     Sakari Ailus <sakari.ailus@iki.fi>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/bitmap.h>
12*4882a593Smuzhiyun #include <linux/property.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <media/media-entity.h>
15*4882a593Smuzhiyun #include <media/media-device.h>
16*4882a593Smuzhiyun 
gobj_type(enum media_gobj_type type)17*4882a593Smuzhiyun static inline const char *gobj_type(enum media_gobj_type type)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun 	switch (type) {
20*4882a593Smuzhiyun 	case MEDIA_GRAPH_ENTITY:
21*4882a593Smuzhiyun 		return "entity";
22*4882a593Smuzhiyun 	case MEDIA_GRAPH_PAD:
23*4882a593Smuzhiyun 		return "pad";
24*4882a593Smuzhiyun 	case MEDIA_GRAPH_LINK:
25*4882a593Smuzhiyun 		return "link";
26*4882a593Smuzhiyun 	case MEDIA_GRAPH_INTF_DEVNODE:
27*4882a593Smuzhiyun 		return "intf-devnode";
28*4882a593Smuzhiyun 	default:
29*4882a593Smuzhiyun 		return "unknown";
30*4882a593Smuzhiyun 	}
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun 
intf_type(struct media_interface * intf)33*4882a593Smuzhiyun static inline const char *intf_type(struct media_interface *intf)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	switch (intf->type) {
36*4882a593Smuzhiyun 	case MEDIA_INTF_T_DVB_FE:
37*4882a593Smuzhiyun 		return "dvb-frontend";
38*4882a593Smuzhiyun 	case MEDIA_INTF_T_DVB_DEMUX:
39*4882a593Smuzhiyun 		return "dvb-demux";
40*4882a593Smuzhiyun 	case MEDIA_INTF_T_DVB_DVR:
41*4882a593Smuzhiyun 		return "dvb-dvr";
42*4882a593Smuzhiyun 	case MEDIA_INTF_T_DVB_CA:
43*4882a593Smuzhiyun 		return  "dvb-ca";
44*4882a593Smuzhiyun 	case MEDIA_INTF_T_DVB_NET:
45*4882a593Smuzhiyun 		return "dvb-net";
46*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_VIDEO:
47*4882a593Smuzhiyun 		return "v4l-video";
48*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_VBI:
49*4882a593Smuzhiyun 		return "v4l-vbi";
50*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_RADIO:
51*4882a593Smuzhiyun 		return "v4l-radio";
52*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_SUBDEV:
53*4882a593Smuzhiyun 		return "v4l-subdev";
54*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_SWRADIO:
55*4882a593Smuzhiyun 		return "v4l-swradio";
56*4882a593Smuzhiyun 	case MEDIA_INTF_T_V4L_TOUCH:
57*4882a593Smuzhiyun 		return "v4l-touch";
58*4882a593Smuzhiyun 	default:
59*4882a593Smuzhiyun 		return "unknown-intf";
60*4882a593Smuzhiyun 	}
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun 
__media_entity_enum_init(struct media_entity_enum * ent_enum,int idx_max)63*4882a593Smuzhiyun __must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
64*4882a593Smuzhiyun 					  int idx_max)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	idx_max = ALIGN(idx_max, BITS_PER_LONG);
67*4882a593Smuzhiyun 	ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long),
68*4882a593Smuzhiyun 				 GFP_KERNEL);
69*4882a593Smuzhiyun 	if (!ent_enum->bmap)
70*4882a593Smuzhiyun 		return -ENOMEM;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	bitmap_zero(ent_enum->bmap, idx_max);
73*4882a593Smuzhiyun 	ent_enum->idx_max = idx_max;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	return 0;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_entity_enum_init);
78*4882a593Smuzhiyun 
media_entity_enum_cleanup(struct media_entity_enum * ent_enum)79*4882a593Smuzhiyun void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	kfree(ent_enum->bmap);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_enum_cleanup);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /**
86*4882a593Smuzhiyun  *  dev_dbg_obj - Prints in debug mode a change on some object
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  * @event_name:	Name of the event to report. Could be __func__
89*4882a593Smuzhiyun  * @gobj:	Pointer to the object
90*4882a593Smuzhiyun  *
91*4882a593Smuzhiyun  * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it
92*4882a593Smuzhiyun  * won't produce any code.
93*4882a593Smuzhiyun  */
dev_dbg_obj(const char * event_name,struct media_gobj * gobj)94*4882a593Smuzhiyun static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun #if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG)
97*4882a593Smuzhiyun 	switch (media_type(gobj)) {
98*4882a593Smuzhiyun 	case MEDIA_GRAPH_ENTITY:
99*4882a593Smuzhiyun 		dev_dbg(gobj->mdev->dev,
100*4882a593Smuzhiyun 			"%s id %u: entity '%s'\n",
101*4882a593Smuzhiyun 			event_name, media_id(gobj),
102*4882a593Smuzhiyun 			gobj_to_entity(gobj)->name);
103*4882a593Smuzhiyun 		break;
104*4882a593Smuzhiyun 	case MEDIA_GRAPH_LINK:
105*4882a593Smuzhiyun 	{
106*4882a593Smuzhiyun 		struct media_link *link = gobj_to_link(gobj);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 		dev_dbg(gobj->mdev->dev,
109*4882a593Smuzhiyun 			"%s id %u: %s link id %u ==> id %u\n",
110*4882a593Smuzhiyun 			event_name, media_id(gobj),
111*4882a593Smuzhiyun 			media_type(link->gobj0) == MEDIA_GRAPH_PAD ?
112*4882a593Smuzhiyun 				"data" : "interface",
113*4882a593Smuzhiyun 			media_id(link->gobj0),
114*4882a593Smuzhiyun 			media_id(link->gobj1));
115*4882a593Smuzhiyun 		break;
116*4882a593Smuzhiyun 	}
117*4882a593Smuzhiyun 	case MEDIA_GRAPH_PAD:
118*4882a593Smuzhiyun 	{
119*4882a593Smuzhiyun 		struct media_pad *pad = gobj_to_pad(gobj);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 		dev_dbg(gobj->mdev->dev,
122*4882a593Smuzhiyun 			"%s id %u: %s%spad '%s':%d\n",
123*4882a593Smuzhiyun 			event_name, media_id(gobj),
124*4882a593Smuzhiyun 			pad->flags & MEDIA_PAD_FL_SINK   ? "sink " : "",
125*4882a593Smuzhiyun 			pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "",
126*4882a593Smuzhiyun 			pad->entity->name, pad->index);
127*4882a593Smuzhiyun 		break;
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 	case MEDIA_GRAPH_INTF_DEVNODE:
130*4882a593Smuzhiyun 	{
131*4882a593Smuzhiyun 		struct media_interface *intf = gobj_to_intf(gobj);
132*4882a593Smuzhiyun 		struct media_intf_devnode *devnode = intf_to_devnode(intf);
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		dev_dbg(gobj->mdev->dev,
135*4882a593Smuzhiyun 			"%s id %u: intf_devnode %s - major: %d, minor: %d\n",
136*4882a593Smuzhiyun 			event_name, media_id(gobj),
137*4882a593Smuzhiyun 			intf_type(intf),
138*4882a593Smuzhiyun 			devnode->major, devnode->minor);
139*4882a593Smuzhiyun 		break;
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 	}
142*4882a593Smuzhiyun #endif
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
media_gobj_create(struct media_device * mdev,enum media_gobj_type type,struct media_gobj * gobj)145*4882a593Smuzhiyun void media_gobj_create(struct media_device *mdev,
146*4882a593Smuzhiyun 			   enum media_gobj_type type,
147*4882a593Smuzhiyun 			   struct media_gobj *gobj)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	BUG_ON(!mdev);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	gobj->mdev = mdev;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* Create a per-type unique object ID */
154*4882a593Smuzhiyun 	gobj->id = media_gobj_gen_id(type, ++mdev->id);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	switch (type) {
157*4882a593Smuzhiyun 	case MEDIA_GRAPH_ENTITY:
158*4882a593Smuzhiyun 		list_add_tail(&gobj->list, &mdev->entities);
159*4882a593Smuzhiyun 		break;
160*4882a593Smuzhiyun 	case MEDIA_GRAPH_PAD:
161*4882a593Smuzhiyun 		list_add_tail(&gobj->list, &mdev->pads);
162*4882a593Smuzhiyun 		break;
163*4882a593Smuzhiyun 	case MEDIA_GRAPH_LINK:
164*4882a593Smuzhiyun 		list_add_tail(&gobj->list, &mdev->links);
165*4882a593Smuzhiyun 		break;
166*4882a593Smuzhiyun 	case MEDIA_GRAPH_INTF_DEVNODE:
167*4882a593Smuzhiyun 		list_add_tail(&gobj->list, &mdev->interfaces);
168*4882a593Smuzhiyun 		break;
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	mdev->topology_version++;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	dev_dbg_obj(__func__, gobj);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
media_gobj_destroy(struct media_gobj * gobj)176*4882a593Smuzhiyun void media_gobj_destroy(struct media_gobj *gobj)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	/* Do nothing if the object is not linked. */
179*4882a593Smuzhiyun 	if (gobj->mdev == NULL)
180*4882a593Smuzhiyun 		return;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	dev_dbg_obj(__func__, gobj);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	gobj->mdev->topology_version++;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	/* Remove the object from mdev list */
187*4882a593Smuzhiyun 	list_del(&gobj->list);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	gobj->mdev = NULL;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun /*
193*4882a593Smuzhiyun  * TODO: Get rid of this.
194*4882a593Smuzhiyun  */
195*4882a593Smuzhiyun #define MEDIA_ENTITY_MAX_PADS		512
196*4882a593Smuzhiyun 
media_entity_pads_init(struct media_entity * entity,u16 num_pads,struct media_pad * pads)197*4882a593Smuzhiyun int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
198*4882a593Smuzhiyun 			   struct media_pad *pads)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	struct media_device *mdev = entity->graph_obj.mdev;
201*4882a593Smuzhiyun 	unsigned int i;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (num_pads >= MEDIA_ENTITY_MAX_PADS)
204*4882a593Smuzhiyun 		return -E2BIG;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	entity->num_pads = num_pads;
207*4882a593Smuzhiyun 	entity->pads = pads;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (mdev)
210*4882a593Smuzhiyun 		mutex_lock(&mdev->graph_mutex);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	for (i = 0; i < num_pads; i++) {
213*4882a593Smuzhiyun 		pads[i].entity = entity;
214*4882a593Smuzhiyun 		pads[i].index = i;
215*4882a593Smuzhiyun 		if (mdev)
216*4882a593Smuzhiyun 			media_gobj_create(mdev, MEDIA_GRAPH_PAD,
217*4882a593Smuzhiyun 					&entity->pads[i].graph_obj);
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (mdev)
221*4882a593Smuzhiyun 		mutex_unlock(&mdev->graph_mutex);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	return 0;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_pads_init);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
228*4882a593Smuzhiyun  * Graph traversal
229*4882a593Smuzhiyun  */
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun static struct media_entity *
media_entity_other(struct media_entity * entity,struct media_link * link)232*4882a593Smuzhiyun media_entity_other(struct media_entity *entity, struct media_link *link)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun 	if (link->source->entity == entity)
235*4882a593Smuzhiyun 		return link->sink->entity;
236*4882a593Smuzhiyun 	else
237*4882a593Smuzhiyun 		return link->source->entity;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun /* push an entity to traversal stack */
stack_push(struct media_graph * graph,struct media_entity * entity)241*4882a593Smuzhiyun static void stack_push(struct media_graph *graph,
242*4882a593Smuzhiyun 		       struct media_entity *entity)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {
245*4882a593Smuzhiyun 		WARN_ON(1);
246*4882a593Smuzhiyun 		return;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 	graph->top++;
249*4882a593Smuzhiyun 	graph->stack[graph->top].link = entity->links.next;
250*4882a593Smuzhiyun 	graph->stack[graph->top].entity = entity;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
stack_pop(struct media_graph * graph)253*4882a593Smuzhiyun static struct media_entity *stack_pop(struct media_graph *graph)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	struct media_entity *entity;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	entity = graph->stack[graph->top].entity;
258*4882a593Smuzhiyun 	graph->top--;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	return entity;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun #define link_top(en)	((en)->stack[(en)->top].link)
264*4882a593Smuzhiyun #define stack_top(en)	((en)->stack[(en)->top].entity)
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun /**
267*4882a593Smuzhiyun  * media_graph_walk_init - Allocate resources for graph walk
268*4882a593Smuzhiyun  * @graph: Media graph structure that will be used to walk the graph
269*4882a593Smuzhiyun  * @mdev: Media device
270*4882a593Smuzhiyun  *
271*4882a593Smuzhiyun  * Reserve resources for graph walk in media device's current
272*4882a593Smuzhiyun  * state. The memory must be released using
273*4882a593Smuzhiyun  * media_graph_walk_free().
274*4882a593Smuzhiyun  *
275*4882a593Smuzhiyun  * Returns error on failure, zero on success.
276*4882a593Smuzhiyun  */
media_graph_walk_init(struct media_graph * graph,struct media_device * mdev)277*4882a593Smuzhiyun __must_check int media_graph_walk_init(
278*4882a593Smuzhiyun 	struct media_graph *graph, struct media_device *mdev)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun 	return media_entity_enum_init(&graph->ent_enum, mdev);
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_graph_walk_init);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun /**
285*4882a593Smuzhiyun  * media_graph_walk_cleanup - Release resources related to graph walking
286*4882a593Smuzhiyun  * @graph: Media graph structure that was used to walk the graph
287*4882a593Smuzhiyun  */
media_graph_walk_cleanup(struct media_graph * graph)288*4882a593Smuzhiyun void media_graph_walk_cleanup(struct media_graph *graph)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	media_entity_enum_cleanup(&graph->ent_enum);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_graph_walk_cleanup);
293*4882a593Smuzhiyun 
media_graph_walk_start(struct media_graph * graph,struct media_entity * entity)294*4882a593Smuzhiyun void media_graph_walk_start(struct media_graph *graph,
295*4882a593Smuzhiyun 			    struct media_entity *entity)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	media_entity_enum_zero(&graph->ent_enum);
298*4882a593Smuzhiyun 	media_entity_enum_set(&graph->ent_enum, entity);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	graph->top = 0;
301*4882a593Smuzhiyun 	graph->stack[graph->top].entity = NULL;
302*4882a593Smuzhiyun 	stack_push(graph, entity);
303*4882a593Smuzhiyun 	dev_dbg(entity->graph_obj.mdev->dev,
304*4882a593Smuzhiyun 		"begin graph walk at '%s'\n", entity->name);
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_graph_walk_start);
307*4882a593Smuzhiyun 
media_graph_walk_iter(struct media_graph * graph)308*4882a593Smuzhiyun static void media_graph_walk_iter(struct media_graph *graph)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun 	struct media_entity *entity = stack_top(graph);
311*4882a593Smuzhiyun 	struct media_link *link;
312*4882a593Smuzhiyun 	struct media_entity *next;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	link = list_entry(link_top(graph), typeof(*link), list);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	/* The link is not enabled so we do not follow. */
317*4882a593Smuzhiyun 	if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
318*4882a593Smuzhiyun 		link_top(graph) = link_top(graph)->next;
319*4882a593Smuzhiyun 		dev_dbg(entity->graph_obj.mdev->dev,
320*4882a593Smuzhiyun 			"walk: skipping disabled link '%s':%u -> '%s':%u\n",
321*4882a593Smuzhiyun 			link->source->entity->name, link->source->index,
322*4882a593Smuzhiyun 			link->sink->entity->name, link->sink->index);
323*4882a593Smuzhiyun 		return;
324*4882a593Smuzhiyun 	}
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	/* Get the entity in the other end of the link . */
327*4882a593Smuzhiyun 	next = media_entity_other(entity, link);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	/* Has the entity already been visited? */
330*4882a593Smuzhiyun 	if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
331*4882a593Smuzhiyun 		link_top(graph) = link_top(graph)->next;
332*4882a593Smuzhiyun 		dev_dbg(entity->graph_obj.mdev->dev,
333*4882a593Smuzhiyun 			"walk: skipping entity '%s' (already seen)\n",
334*4882a593Smuzhiyun 			next->name);
335*4882a593Smuzhiyun 		return;
336*4882a593Smuzhiyun 	}
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	/* Push the new entity to stack and start over. */
339*4882a593Smuzhiyun 	link_top(graph) = link_top(graph)->next;
340*4882a593Smuzhiyun 	stack_push(graph, next);
341*4882a593Smuzhiyun 	dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",
342*4882a593Smuzhiyun 		next->name);
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun 
media_graph_walk_next(struct media_graph * graph)345*4882a593Smuzhiyun struct media_entity *media_graph_walk_next(struct media_graph *graph)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun 	struct media_entity *entity;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (stack_top(graph) == NULL)
350*4882a593Smuzhiyun 		return NULL;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	/*
353*4882a593Smuzhiyun 	 * Depth first search. Push entity to stack and continue from
354*4882a593Smuzhiyun 	 * top of the stack until no more entities on the level can be
355*4882a593Smuzhiyun 	 * found.
356*4882a593Smuzhiyun 	 */
357*4882a593Smuzhiyun 	while (link_top(graph) != &stack_top(graph)->links)
358*4882a593Smuzhiyun 		media_graph_walk_iter(graph);
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	entity = stack_pop(graph);
361*4882a593Smuzhiyun 	dev_dbg(entity->graph_obj.mdev->dev,
362*4882a593Smuzhiyun 		"walk: returning entity '%s'\n", entity->name);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	return entity;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_graph_walk_next);
367*4882a593Smuzhiyun 
media_entity_get_fwnode_pad(struct media_entity * entity,struct fwnode_handle * fwnode,unsigned long direction_flags)368*4882a593Smuzhiyun int media_entity_get_fwnode_pad(struct media_entity *entity,
369*4882a593Smuzhiyun 				struct fwnode_handle *fwnode,
370*4882a593Smuzhiyun 				unsigned long direction_flags)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct fwnode_endpoint endpoint;
373*4882a593Smuzhiyun 	unsigned int i;
374*4882a593Smuzhiyun 	int ret;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	if (!entity->ops || !entity->ops->get_fwnode_pad) {
377*4882a593Smuzhiyun 		for (i = 0; i < entity->num_pads; i++) {
378*4882a593Smuzhiyun 			if (entity->pads[i].flags & direction_flags)
379*4882a593Smuzhiyun 				return i;
380*4882a593Smuzhiyun 		}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 		return -ENXIO;
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);
386*4882a593Smuzhiyun 	if (ret)
387*4882a593Smuzhiyun 		return ret;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	ret = entity->ops->get_fwnode_pad(entity, &endpoint);
390*4882a593Smuzhiyun 	if (ret < 0)
391*4882a593Smuzhiyun 		return ret;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	if (ret >= entity->num_pads)
394*4882a593Smuzhiyun 		return -ENXIO;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	if (!(entity->pads[ret].flags & direction_flags))
397*4882a593Smuzhiyun 		return -ENXIO;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	return ret;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
404*4882a593Smuzhiyun  * Pipeline management
405*4882a593Smuzhiyun  */
406*4882a593Smuzhiyun 
__media_pipeline_start(struct media_entity * entity,struct media_pipeline * pipe)407*4882a593Smuzhiyun __must_check int __media_pipeline_start(struct media_entity *entity,
408*4882a593Smuzhiyun 					struct media_pipeline *pipe)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	struct media_device *mdev = entity->graph_obj.mdev;
411*4882a593Smuzhiyun 	struct media_graph *graph = &pipe->graph;
412*4882a593Smuzhiyun 	struct media_entity *entity_err = entity;
413*4882a593Smuzhiyun 	struct media_link *link;
414*4882a593Smuzhiyun 	int ret;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	if (!pipe->streaming_count++) {
417*4882a593Smuzhiyun 		ret = media_graph_walk_init(&pipe->graph, mdev);
418*4882a593Smuzhiyun 		if (ret)
419*4882a593Smuzhiyun 			goto error_graph_walk_start;
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	media_graph_walk_start(&pipe->graph, entity);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	while ((entity = media_graph_walk_next(graph))) {
425*4882a593Smuzhiyun 		DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS);
426*4882a593Smuzhiyun 		DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 		entity->stream_count++;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 		if (entity->pipe && entity->pipe != pipe) {
431*4882a593Smuzhiyun 			pr_err("Pipe active for %s. Can't start for %s\n",
432*4882a593Smuzhiyun 				entity->name,
433*4882a593Smuzhiyun 				entity_err->name);
434*4882a593Smuzhiyun 			ret = -EBUSY;
435*4882a593Smuzhiyun 			goto error;
436*4882a593Smuzhiyun 		}
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 		entity->pipe = pipe;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 		/* Already streaming --- no need to check. */
441*4882a593Smuzhiyun 		if (entity->stream_count > 1)
442*4882a593Smuzhiyun 			continue;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 		if (!entity->ops || !entity->ops->link_validate)
445*4882a593Smuzhiyun 			continue;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 		bitmap_zero(active, entity->num_pads);
448*4882a593Smuzhiyun 		bitmap_fill(has_no_links, entity->num_pads);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 		list_for_each_entry(link, &entity->links, list) {
451*4882a593Smuzhiyun 			struct media_pad *pad = link->sink->entity == entity
452*4882a593Smuzhiyun 						? link->sink : link->source;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 			/* Mark that a pad is connected by a link. */
455*4882a593Smuzhiyun 			bitmap_clear(has_no_links, pad->index, 1);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 			/*
458*4882a593Smuzhiyun 			 * Pads that either do not need to connect or
459*4882a593Smuzhiyun 			 * are connected through an enabled link are
460*4882a593Smuzhiyun 			 * fine.
461*4882a593Smuzhiyun 			 */
462*4882a593Smuzhiyun 			if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) ||
463*4882a593Smuzhiyun 			    link->flags & MEDIA_LNK_FL_ENABLED)
464*4882a593Smuzhiyun 				bitmap_set(active, pad->index, 1);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 			/*
467*4882a593Smuzhiyun 			 * Link validation will only take place for
468*4882a593Smuzhiyun 			 * sink ends of the link that are enabled.
469*4882a593Smuzhiyun 			 */
470*4882a593Smuzhiyun 			if (link->sink != pad ||
471*4882a593Smuzhiyun 			    !(link->flags & MEDIA_LNK_FL_ENABLED))
472*4882a593Smuzhiyun 				continue;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 			ret = entity->ops->link_validate(link);
475*4882a593Smuzhiyun 			if (ret < 0 && ret != -ENOIOCTLCMD) {
476*4882a593Smuzhiyun 				dev_dbg(entity->graph_obj.mdev->dev,
477*4882a593Smuzhiyun 					"link validation failed for '%s':%u -> '%s':%u, error %d\n",
478*4882a593Smuzhiyun 					link->source->entity->name,
479*4882a593Smuzhiyun 					link->source->index,
480*4882a593Smuzhiyun 					entity->name, link->sink->index, ret);
481*4882a593Smuzhiyun 				goto error;
482*4882a593Smuzhiyun 			}
483*4882a593Smuzhiyun 		}
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 		/* Either no links or validated links are fine. */
486*4882a593Smuzhiyun 		bitmap_or(active, active, has_no_links, entity->num_pads);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 		if (!bitmap_full(active, entity->num_pads)) {
489*4882a593Smuzhiyun 			ret = -ENOLINK;
490*4882a593Smuzhiyun 			dev_dbg(entity->graph_obj.mdev->dev,
491*4882a593Smuzhiyun 				"'%s':%u must be connected by an enabled link\n",
492*4882a593Smuzhiyun 				entity->name,
493*4882a593Smuzhiyun 				(unsigned)find_first_zero_bit(
494*4882a593Smuzhiyun 					active, entity->num_pads));
495*4882a593Smuzhiyun 			goto error;
496*4882a593Smuzhiyun 		}
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	return 0;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun error:
502*4882a593Smuzhiyun 	/*
503*4882a593Smuzhiyun 	 * Link validation on graph failed. We revert what we did and
504*4882a593Smuzhiyun 	 * return the error.
505*4882a593Smuzhiyun 	 */
506*4882a593Smuzhiyun 	media_graph_walk_start(graph, entity_err);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	while ((entity_err = media_graph_walk_next(graph))) {
509*4882a593Smuzhiyun 		/* Sanity check for negative stream_count */
510*4882a593Smuzhiyun 		if (!WARN_ON_ONCE(entity_err->stream_count <= 0)) {
511*4882a593Smuzhiyun 			entity_err->stream_count--;
512*4882a593Smuzhiyun 			if (entity_err->stream_count == 0)
513*4882a593Smuzhiyun 				entity_err->pipe = NULL;
514*4882a593Smuzhiyun 		}
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 		/*
517*4882a593Smuzhiyun 		 * We haven't increased stream_count further than this
518*4882a593Smuzhiyun 		 * so we quit here.
519*4882a593Smuzhiyun 		 */
520*4882a593Smuzhiyun 		if (entity_err == entity)
521*4882a593Smuzhiyun 			break;
522*4882a593Smuzhiyun 	}
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun error_graph_walk_start:
525*4882a593Smuzhiyun 	if (!--pipe->streaming_count)
526*4882a593Smuzhiyun 		media_graph_walk_cleanup(graph);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	return ret;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_pipeline_start);
531*4882a593Smuzhiyun 
media_pipeline_start(struct media_entity * entity,struct media_pipeline * pipe)532*4882a593Smuzhiyun __must_check int media_pipeline_start(struct media_entity *entity,
533*4882a593Smuzhiyun 				      struct media_pipeline *pipe)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun 	struct media_device *mdev = entity->graph_obj.mdev;
536*4882a593Smuzhiyun 	int ret;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	mutex_lock(&mdev->graph_mutex);
539*4882a593Smuzhiyun 	ret = __media_pipeline_start(entity, pipe);
540*4882a593Smuzhiyun 	mutex_unlock(&mdev->graph_mutex);
541*4882a593Smuzhiyun 	return ret;
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_pipeline_start);
544*4882a593Smuzhiyun 
__media_pipeline_stop(struct media_entity * entity)545*4882a593Smuzhiyun void __media_pipeline_stop(struct media_entity *entity)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	struct media_graph *graph = &entity->pipe->graph;
548*4882a593Smuzhiyun 	struct media_pipeline *pipe = entity->pipe;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	/*
551*4882a593Smuzhiyun 	 * If the following check fails, the driver has performed an
552*4882a593Smuzhiyun 	 * unbalanced call to media_pipeline_stop()
553*4882a593Smuzhiyun 	 */
554*4882a593Smuzhiyun 	if (WARN_ON(!pipe))
555*4882a593Smuzhiyun 		return;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	media_graph_walk_start(graph, entity);
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	while ((entity = media_graph_walk_next(graph))) {
560*4882a593Smuzhiyun 		/* Sanity check for negative stream_count */
561*4882a593Smuzhiyun 		if (!WARN_ON_ONCE(entity->stream_count <= 0)) {
562*4882a593Smuzhiyun 			entity->stream_count--;
563*4882a593Smuzhiyun 			if (entity->stream_count == 0)
564*4882a593Smuzhiyun 				entity->pipe = NULL;
565*4882a593Smuzhiyun 		}
566*4882a593Smuzhiyun 	}
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	if (!--pipe->streaming_count)
569*4882a593Smuzhiyun 		media_graph_walk_cleanup(graph);
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_pipeline_stop);
573*4882a593Smuzhiyun 
media_pipeline_stop(struct media_entity * entity)574*4882a593Smuzhiyun void media_pipeline_stop(struct media_entity *entity)
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun 	struct media_device *mdev = entity->graph_obj.mdev;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	mutex_lock(&mdev->graph_mutex);
579*4882a593Smuzhiyun 	__media_pipeline_stop(entity);
580*4882a593Smuzhiyun 	mutex_unlock(&mdev->graph_mutex);
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_pipeline_stop);
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
585*4882a593Smuzhiyun  * Links management
586*4882a593Smuzhiyun  */
587*4882a593Smuzhiyun 
media_add_link(struct list_head * head)588*4882a593Smuzhiyun static struct media_link *media_add_link(struct list_head *head)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun 	struct media_link *link;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	link = kzalloc(sizeof(*link), GFP_KERNEL);
593*4882a593Smuzhiyun 	if (link == NULL)
594*4882a593Smuzhiyun 		return NULL;
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	list_add_tail(&link->list, head);
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	return link;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
__media_entity_remove_link(struct media_entity * entity,struct media_link * link)601*4882a593Smuzhiyun static void __media_entity_remove_link(struct media_entity *entity,
602*4882a593Smuzhiyun 				       struct media_link *link)
603*4882a593Smuzhiyun {
604*4882a593Smuzhiyun 	struct media_link *rlink, *tmp;
605*4882a593Smuzhiyun 	struct media_entity *remote;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	if (link->source->entity == entity)
608*4882a593Smuzhiyun 		remote = link->sink->entity;
609*4882a593Smuzhiyun 	else
610*4882a593Smuzhiyun 		remote = link->source->entity;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
613*4882a593Smuzhiyun 		if (rlink != link->reverse)
614*4882a593Smuzhiyun 			continue;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 		if (link->source->entity == entity)
617*4882a593Smuzhiyun 			remote->num_backlinks--;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 		/* Remove the remote link */
620*4882a593Smuzhiyun 		list_del(&rlink->list);
621*4882a593Smuzhiyun 		media_gobj_destroy(&rlink->graph_obj);
622*4882a593Smuzhiyun 		kfree(rlink);
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 		if (--remote->num_links == 0)
625*4882a593Smuzhiyun 			break;
626*4882a593Smuzhiyun 	}
627*4882a593Smuzhiyun 	list_del(&link->list);
628*4882a593Smuzhiyun 	media_gobj_destroy(&link->graph_obj);
629*4882a593Smuzhiyun 	kfree(link);
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun 
media_get_pad_index(struct media_entity * entity,bool is_sink,enum media_pad_signal_type sig_type)632*4882a593Smuzhiyun int media_get_pad_index(struct media_entity *entity, bool is_sink,
633*4882a593Smuzhiyun 			enum media_pad_signal_type sig_type)
634*4882a593Smuzhiyun {
635*4882a593Smuzhiyun 	int i;
636*4882a593Smuzhiyun 	bool pad_is_sink;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	if (!entity)
639*4882a593Smuzhiyun 		return -EINVAL;
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	for (i = 0; i < entity->num_pads; i++) {
642*4882a593Smuzhiyun 		if (entity->pads[i].flags & MEDIA_PAD_FL_SINK)
643*4882a593Smuzhiyun 			pad_is_sink = true;
644*4882a593Smuzhiyun 		else if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE)
645*4882a593Smuzhiyun 			pad_is_sink = false;
646*4882a593Smuzhiyun 		else
647*4882a593Smuzhiyun 			continue;	/* This is an error! */
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun 		if (pad_is_sink != is_sink)
650*4882a593Smuzhiyun 			continue;
651*4882a593Smuzhiyun 		if (entity->pads[i].sig_type == sig_type)
652*4882a593Smuzhiyun 			return i;
653*4882a593Smuzhiyun 	}
654*4882a593Smuzhiyun 	return -EINVAL;
655*4882a593Smuzhiyun }
656*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_get_pad_index);
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun int
media_create_pad_link(struct media_entity * source,u16 source_pad,struct media_entity * sink,u16 sink_pad,u32 flags)659*4882a593Smuzhiyun media_create_pad_link(struct media_entity *source, u16 source_pad,
660*4882a593Smuzhiyun 			 struct media_entity *sink, u16 sink_pad, u32 flags)
661*4882a593Smuzhiyun {
662*4882a593Smuzhiyun 	struct media_link *link;
663*4882a593Smuzhiyun 	struct media_link *backlink;
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	if (WARN_ON(!source || !sink) ||
666*4882a593Smuzhiyun 	    WARN_ON(source_pad >= source->num_pads) ||
667*4882a593Smuzhiyun 	    WARN_ON(sink_pad >= sink->num_pads))
668*4882a593Smuzhiyun 		return -EINVAL;
669*4882a593Smuzhiyun 	if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE)))
670*4882a593Smuzhiyun 		return -EINVAL;
671*4882a593Smuzhiyun 	if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))
672*4882a593Smuzhiyun 		return -EINVAL;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	link = media_add_link(&source->links);
675*4882a593Smuzhiyun 	if (link == NULL)
676*4882a593Smuzhiyun 		return -ENOMEM;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	link->source = &source->pads[source_pad];
679*4882a593Smuzhiyun 	link->sink = &sink->pads[sink_pad];
680*4882a593Smuzhiyun 	link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK;
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	/* Initialize graph object embedded at the new link */
683*4882a593Smuzhiyun 	media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,
684*4882a593Smuzhiyun 			&link->graph_obj);
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	/* Create the backlink. Backlinks are used to help graph traversal and
687*4882a593Smuzhiyun 	 * are not reported to userspace.
688*4882a593Smuzhiyun 	 */
689*4882a593Smuzhiyun 	backlink = media_add_link(&sink->links);
690*4882a593Smuzhiyun 	if (backlink == NULL) {
691*4882a593Smuzhiyun 		__media_entity_remove_link(source, link);
692*4882a593Smuzhiyun 		return -ENOMEM;
693*4882a593Smuzhiyun 	}
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	backlink->source = &source->pads[source_pad];
696*4882a593Smuzhiyun 	backlink->sink = &sink->pads[sink_pad];
697*4882a593Smuzhiyun 	backlink->flags = flags;
698*4882a593Smuzhiyun 	backlink->is_backlink = true;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	/* Initialize graph object embedded at the new link */
701*4882a593Smuzhiyun 	media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK,
702*4882a593Smuzhiyun 			&backlink->graph_obj);
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	link->reverse = backlink;
705*4882a593Smuzhiyun 	backlink->reverse = link;
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	sink->num_backlinks++;
708*4882a593Smuzhiyun 	sink->num_links++;
709*4882a593Smuzhiyun 	source->num_links++;
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	return 0;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_create_pad_link);
714*4882a593Smuzhiyun 
media_create_pad_links(const struct media_device * mdev,const u32 source_function,struct media_entity * source,const u16 source_pad,const u32 sink_function,struct media_entity * sink,const u16 sink_pad,u32 flags,const bool allow_both_undefined)715*4882a593Smuzhiyun int media_create_pad_links(const struct media_device *mdev,
716*4882a593Smuzhiyun 			   const u32 source_function,
717*4882a593Smuzhiyun 			   struct media_entity *source,
718*4882a593Smuzhiyun 			   const u16 source_pad,
719*4882a593Smuzhiyun 			   const u32 sink_function,
720*4882a593Smuzhiyun 			   struct media_entity *sink,
721*4882a593Smuzhiyun 			   const u16 sink_pad,
722*4882a593Smuzhiyun 			   u32 flags,
723*4882a593Smuzhiyun 			   const bool allow_both_undefined)
724*4882a593Smuzhiyun {
725*4882a593Smuzhiyun 	struct media_entity *entity;
726*4882a593Smuzhiyun 	unsigned function;
727*4882a593Smuzhiyun 	int ret;
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	/* Trivial case: 1:1 relation */
730*4882a593Smuzhiyun 	if (source && sink)
731*4882a593Smuzhiyun 		return media_create_pad_link(source, source_pad,
732*4882a593Smuzhiyun 					     sink, sink_pad, flags);
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	/* Worse case scenario: n:n relation */
735*4882a593Smuzhiyun 	if (!source && !sink) {
736*4882a593Smuzhiyun 		if (!allow_both_undefined)
737*4882a593Smuzhiyun 			return 0;
738*4882a593Smuzhiyun 		media_device_for_each_entity(source, mdev) {
739*4882a593Smuzhiyun 			if (source->function != source_function)
740*4882a593Smuzhiyun 				continue;
741*4882a593Smuzhiyun 			media_device_for_each_entity(sink, mdev) {
742*4882a593Smuzhiyun 				if (sink->function != sink_function)
743*4882a593Smuzhiyun 					continue;
744*4882a593Smuzhiyun 				ret = media_create_pad_link(source, source_pad,
745*4882a593Smuzhiyun 							    sink, sink_pad,
746*4882a593Smuzhiyun 							    flags);
747*4882a593Smuzhiyun 				if (ret)
748*4882a593Smuzhiyun 					return ret;
749*4882a593Smuzhiyun 				flags &= ~(MEDIA_LNK_FL_ENABLED |
750*4882a593Smuzhiyun 					   MEDIA_LNK_FL_IMMUTABLE);
751*4882a593Smuzhiyun 			}
752*4882a593Smuzhiyun 		}
753*4882a593Smuzhiyun 		return 0;
754*4882a593Smuzhiyun 	}
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	/* Handle 1:n and n:1 cases */
757*4882a593Smuzhiyun 	if (source)
758*4882a593Smuzhiyun 		function = sink_function;
759*4882a593Smuzhiyun 	else
760*4882a593Smuzhiyun 		function = source_function;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	media_device_for_each_entity(entity, mdev) {
763*4882a593Smuzhiyun 		if (entity->function != function)
764*4882a593Smuzhiyun 			continue;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 		if (source)
767*4882a593Smuzhiyun 			ret = media_create_pad_link(source, source_pad,
768*4882a593Smuzhiyun 						    entity, sink_pad, flags);
769*4882a593Smuzhiyun 		else
770*4882a593Smuzhiyun 			ret = media_create_pad_link(entity, source_pad,
771*4882a593Smuzhiyun 						    sink, sink_pad, flags);
772*4882a593Smuzhiyun 		if (ret)
773*4882a593Smuzhiyun 			return ret;
774*4882a593Smuzhiyun 		flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
775*4882a593Smuzhiyun 	}
776*4882a593Smuzhiyun 	return 0;
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_create_pad_links);
779*4882a593Smuzhiyun 
__media_entity_remove_links(struct media_entity * entity)780*4882a593Smuzhiyun void __media_entity_remove_links(struct media_entity *entity)
781*4882a593Smuzhiyun {
782*4882a593Smuzhiyun 	struct media_link *link, *tmp;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	list_for_each_entry_safe(link, tmp, &entity->links, list)
785*4882a593Smuzhiyun 		__media_entity_remove_link(entity, link);
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	entity->num_links = 0;
788*4882a593Smuzhiyun 	entity->num_backlinks = 0;
789*4882a593Smuzhiyun }
790*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_entity_remove_links);
791*4882a593Smuzhiyun 
media_entity_remove_links(struct media_entity * entity)792*4882a593Smuzhiyun void media_entity_remove_links(struct media_entity *entity)
793*4882a593Smuzhiyun {
794*4882a593Smuzhiyun 	struct media_device *mdev = entity->graph_obj.mdev;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	/* Do nothing if the entity is not registered. */
797*4882a593Smuzhiyun 	if (mdev == NULL)
798*4882a593Smuzhiyun 		return;
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 	mutex_lock(&mdev->graph_mutex);
801*4882a593Smuzhiyun 	__media_entity_remove_links(entity);
802*4882a593Smuzhiyun 	mutex_unlock(&mdev->graph_mutex);
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_remove_links);
805*4882a593Smuzhiyun 
__media_entity_setup_link_notify(struct media_link * link,u32 flags)806*4882a593Smuzhiyun static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
807*4882a593Smuzhiyun {
808*4882a593Smuzhiyun 	int ret;
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	/* Notify both entities. */
811*4882a593Smuzhiyun 	ret = media_entity_call(link->source->entity, link_setup,
812*4882a593Smuzhiyun 				link->source, link->sink, flags);
813*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOIOCTLCMD)
814*4882a593Smuzhiyun 		return ret;
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 	ret = media_entity_call(link->sink->entity, link_setup,
817*4882a593Smuzhiyun 				link->sink, link->source, flags);
818*4882a593Smuzhiyun 	if (ret < 0 && ret != -ENOIOCTLCMD) {
819*4882a593Smuzhiyun 		media_entity_call(link->source->entity, link_setup,
820*4882a593Smuzhiyun 				  link->source, link->sink, link->flags);
821*4882a593Smuzhiyun 		return ret;
822*4882a593Smuzhiyun 	}
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun 	link->flags = flags;
825*4882a593Smuzhiyun 	link->reverse->flags = link->flags;
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	return 0;
828*4882a593Smuzhiyun }
829*4882a593Smuzhiyun 
__media_entity_setup_link(struct media_link * link,u32 flags)830*4882a593Smuzhiyun int __media_entity_setup_link(struct media_link *link, u32 flags)
831*4882a593Smuzhiyun {
832*4882a593Smuzhiyun 	const u32 mask = MEDIA_LNK_FL_ENABLED;
833*4882a593Smuzhiyun 	struct media_device *mdev;
834*4882a593Smuzhiyun 	struct media_entity *source, *sink;
835*4882a593Smuzhiyun 	int ret = -EBUSY;
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	if (link == NULL)
838*4882a593Smuzhiyun 		return -EINVAL;
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	/* The non-modifiable link flags must not be modified. */
841*4882a593Smuzhiyun 	if ((link->flags & ~mask) != (flags & ~mask))
842*4882a593Smuzhiyun 		return -EINVAL;
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
845*4882a593Smuzhiyun 		return link->flags == flags ? 0 : -EINVAL;
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	if (link->flags == flags)
848*4882a593Smuzhiyun 		return 0;
849*4882a593Smuzhiyun 
850*4882a593Smuzhiyun 	source = link->source->entity;
851*4882a593Smuzhiyun 	sink = link->sink->entity;
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
854*4882a593Smuzhiyun 	    (source->stream_count || sink->stream_count))
855*4882a593Smuzhiyun 		return -EBUSY;
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	mdev = source->graph_obj.mdev;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	if (mdev->ops && mdev->ops->link_notify) {
860*4882a593Smuzhiyun 		ret = mdev->ops->link_notify(link, flags,
861*4882a593Smuzhiyun 					     MEDIA_DEV_NOTIFY_PRE_LINK_CH);
862*4882a593Smuzhiyun 		if (ret < 0)
863*4882a593Smuzhiyun 			return ret;
864*4882a593Smuzhiyun 	}
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	ret = __media_entity_setup_link_notify(link, flags);
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 	if (mdev->ops && mdev->ops->link_notify)
869*4882a593Smuzhiyun 		mdev->ops->link_notify(link, flags,
870*4882a593Smuzhiyun 				       MEDIA_DEV_NOTIFY_POST_LINK_CH);
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	return ret;
873*4882a593Smuzhiyun }
874*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_entity_setup_link);
875*4882a593Smuzhiyun 
media_entity_setup_link(struct media_link * link,u32 flags)876*4882a593Smuzhiyun int media_entity_setup_link(struct media_link *link, u32 flags)
877*4882a593Smuzhiyun {
878*4882a593Smuzhiyun 	int ret;
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 	mutex_lock(&link->graph_obj.mdev->graph_mutex);
881*4882a593Smuzhiyun 	ret = __media_entity_setup_link(link, flags);
882*4882a593Smuzhiyun 	mutex_unlock(&link->graph_obj.mdev->graph_mutex);
883*4882a593Smuzhiyun 
884*4882a593Smuzhiyun 	return ret;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_setup_link);
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun struct media_link *
media_entity_find_link(struct media_pad * source,struct media_pad * sink)889*4882a593Smuzhiyun media_entity_find_link(struct media_pad *source, struct media_pad *sink)
890*4882a593Smuzhiyun {
891*4882a593Smuzhiyun 	struct media_link *link;
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	list_for_each_entry(link, &source->entity->links, list) {
894*4882a593Smuzhiyun 		if (link->source->entity == source->entity &&
895*4882a593Smuzhiyun 		    link->source->index == source->index &&
896*4882a593Smuzhiyun 		    link->sink->entity == sink->entity &&
897*4882a593Smuzhiyun 		    link->sink->index == sink->index)
898*4882a593Smuzhiyun 			return link;
899*4882a593Smuzhiyun 	}
900*4882a593Smuzhiyun 
901*4882a593Smuzhiyun 	return NULL;
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_find_link);
904*4882a593Smuzhiyun 
media_entity_remote_pad(const struct media_pad * pad)905*4882a593Smuzhiyun struct media_pad *media_entity_remote_pad(const struct media_pad *pad)
906*4882a593Smuzhiyun {
907*4882a593Smuzhiyun 	struct media_link *link;
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	list_for_each_entry(link, &pad->entity->links, list) {
910*4882a593Smuzhiyun 		if (!(link->flags & MEDIA_LNK_FL_ENABLED))
911*4882a593Smuzhiyun 			continue;
912*4882a593Smuzhiyun 
913*4882a593Smuzhiyun 		if (link->source == pad)
914*4882a593Smuzhiyun 			return link->sink;
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 		if (link->sink == pad)
917*4882a593Smuzhiyun 			return link->source;
918*4882a593Smuzhiyun 	}
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	return NULL;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_entity_remote_pad);
924*4882a593Smuzhiyun 
media_interface_init(struct media_device * mdev,struct media_interface * intf,u32 gobj_type,u32 intf_type,u32 flags)925*4882a593Smuzhiyun static void media_interface_init(struct media_device *mdev,
926*4882a593Smuzhiyun 				 struct media_interface *intf,
927*4882a593Smuzhiyun 				 u32 gobj_type,
928*4882a593Smuzhiyun 				 u32 intf_type, u32 flags)
929*4882a593Smuzhiyun {
930*4882a593Smuzhiyun 	intf->type = intf_type;
931*4882a593Smuzhiyun 	intf->flags = flags;
932*4882a593Smuzhiyun 	INIT_LIST_HEAD(&intf->links);
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 	media_gobj_create(mdev, gobj_type, &intf->graph_obj);
935*4882a593Smuzhiyun }
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun /* Functions related to the media interface via device nodes */
938*4882a593Smuzhiyun 
media_devnode_create(struct media_device * mdev,u32 type,u32 flags,u32 major,u32 minor)939*4882a593Smuzhiyun struct media_intf_devnode *media_devnode_create(struct media_device *mdev,
940*4882a593Smuzhiyun 						u32 type, u32 flags,
941*4882a593Smuzhiyun 						u32 major, u32 minor)
942*4882a593Smuzhiyun {
943*4882a593Smuzhiyun 	struct media_intf_devnode *devnode;
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun 	devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
946*4882a593Smuzhiyun 	if (!devnode)
947*4882a593Smuzhiyun 		return NULL;
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun 	devnode->major = major;
950*4882a593Smuzhiyun 	devnode->minor = minor;
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE,
953*4882a593Smuzhiyun 			     type, flags);
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun 	return devnode;
956*4882a593Smuzhiyun }
957*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_devnode_create);
958*4882a593Smuzhiyun 
media_devnode_remove(struct media_intf_devnode * devnode)959*4882a593Smuzhiyun void media_devnode_remove(struct media_intf_devnode *devnode)
960*4882a593Smuzhiyun {
961*4882a593Smuzhiyun 	media_remove_intf_links(&devnode->intf);
962*4882a593Smuzhiyun 	media_gobj_destroy(&devnode->intf.graph_obj);
963*4882a593Smuzhiyun 	kfree(devnode);
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_devnode_remove);
966*4882a593Smuzhiyun 
media_create_intf_link(struct media_entity * entity,struct media_interface * intf,u32 flags)967*4882a593Smuzhiyun struct media_link *media_create_intf_link(struct media_entity *entity,
968*4882a593Smuzhiyun 					    struct media_interface *intf,
969*4882a593Smuzhiyun 					    u32 flags)
970*4882a593Smuzhiyun {
971*4882a593Smuzhiyun 	struct media_link *link;
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	link = media_add_link(&intf->links);
974*4882a593Smuzhiyun 	if (link == NULL)
975*4882a593Smuzhiyun 		return NULL;
976*4882a593Smuzhiyun 
977*4882a593Smuzhiyun 	link->intf = intf;
978*4882a593Smuzhiyun 	link->entity = entity;
979*4882a593Smuzhiyun 	link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	/* Initialize graph object embedded at the new link */
982*4882a593Smuzhiyun 	media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK,
983*4882a593Smuzhiyun 			&link->graph_obj);
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	return link;
986*4882a593Smuzhiyun }
987*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_create_intf_link);
988*4882a593Smuzhiyun 
__media_remove_intf_link(struct media_link * link)989*4882a593Smuzhiyun void __media_remove_intf_link(struct media_link *link)
990*4882a593Smuzhiyun {
991*4882a593Smuzhiyun 	list_del(&link->list);
992*4882a593Smuzhiyun 	media_gobj_destroy(&link->graph_obj);
993*4882a593Smuzhiyun 	kfree(link);
994*4882a593Smuzhiyun }
995*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_remove_intf_link);
996*4882a593Smuzhiyun 
media_remove_intf_link(struct media_link * link)997*4882a593Smuzhiyun void media_remove_intf_link(struct media_link *link)
998*4882a593Smuzhiyun {
999*4882a593Smuzhiyun 	struct media_device *mdev = link->graph_obj.mdev;
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun 	/* Do nothing if the intf is not registered. */
1002*4882a593Smuzhiyun 	if (mdev == NULL)
1003*4882a593Smuzhiyun 		return;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	mutex_lock(&mdev->graph_mutex);
1006*4882a593Smuzhiyun 	__media_remove_intf_link(link);
1007*4882a593Smuzhiyun 	mutex_unlock(&mdev->graph_mutex);
1008*4882a593Smuzhiyun }
1009*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_remove_intf_link);
1010*4882a593Smuzhiyun 
__media_remove_intf_links(struct media_interface * intf)1011*4882a593Smuzhiyun void __media_remove_intf_links(struct media_interface *intf)
1012*4882a593Smuzhiyun {
1013*4882a593Smuzhiyun 	struct media_link *link, *tmp;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	list_for_each_entry_safe(link, tmp, &intf->links, list)
1016*4882a593Smuzhiyun 		__media_remove_intf_link(link);
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun }
1019*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(__media_remove_intf_links);
1020*4882a593Smuzhiyun 
media_remove_intf_links(struct media_interface * intf)1021*4882a593Smuzhiyun void media_remove_intf_links(struct media_interface *intf)
1022*4882a593Smuzhiyun {
1023*4882a593Smuzhiyun 	struct media_device *mdev = intf->graph_obj.mdev;
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	/* Do nothing if the intf is not registered. */
1026*4882a593Smuzhiyun 	if (mdev == NULL)
1027*4882a593Smuzhiyun 		return;
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	mutex_lock(&mdev->graph_mutex);
1030*4882a593Smuzhiyun 	__media_remove_intf_links(intf);
1031*4882a593Smuzhiyun 	mutex_unlock(&mdev->graph_mutex);
1032*4882a593Smuzhiyun }
1033*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(media_remove_intf_links);
1034