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