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