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