1
2
3
4
5
6
7
8
9
10
11
12#include <linux/module.h>
13#include <linux/pci.h>
14#include <linux/usb.h>
15#include <media/media-device.h>
16#include <media/media-entity.h>
17#include <media/v4l2-fh.h>
18#include <media/v4l2-mc.h>
19#include <media/v4l2-subdev.h>
20#include <media/videobuf2-core.h>
21
22int v4l2_mc_create_media_graph(struct media_device *mdev)
23
24{
25 struct media_entity *entity;
26 struct media_entity *if_vid = NULL, *if_aud = NULL;
27 struct media_entity *tuner = NULL, *decoder = NULL;
28 struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
29 bool is_webcam = false;
30 u32 flags;
31 int ret, pad_sink, pad_source;
32
33 if (!mdev)
34 return 0;
35
36 media_device_for_each_entity(entity, mdev) {
37 switch (entity->function) {
38 case MEDIA_ENT_F_IF_VID_DECODER:
39 if_vid = entity;
40 break;
41 case MEDIA_ENT_F_IF_AUD_DECODER:
42 if_aud = entity;
43 break;
44 case MEDIA_ENT_F_TUNER:
45 tuner = entity;
46 break;
47 case MEDIA_ENT_F_ATV_DECODER:
48 decoder = entity;
49 break;
50 case MEDIA_ENT_F_IO_V4L:
51 io_v4l = entity;
52 break;
53 case MEDIA_ENT_F_IO_VBI:
54 io_vbi = entity;
55 break;
56 case MEDIA_ENT_F_IO_SWRADIO:
57 io_swradio = entity;
58 break;
59 case MEDIA_ENT_F_CAM_SENSOR:
60 is_webcam = true;
61 break;
62 }
63 }
64
65
66 if (!io_v4l && !io_vbi && !io_swradio) {
67 dev_warn(mdev->dev, "Didn't find any I/O entity\n");
68 return -EINVAL;
69 }
70
71
72
73
74
75
76
77
78 if (is_webcam) {
79 if (!io_v4l) {
80 dev_warn(mdev->dev, "Didn't find a MEDIA_ENT_F_IO_V4L\n");
81 return -EINVAL;
82 }
83
84 media_device_for_each_entity(entity, mdev) {
85 if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
86 continue;
87 ret = media_create_pad_link(entity, 0,
88 io_v4l, 0,
89 MEDIA_LNK_FL_ENABLED);
90 if (ret) {
91 dev_warn(mdev->dev, "Failed to create a sensor link\n");
92 return ret;
93 }
94 }
95 if (!decoder)
96 return 0;
97 }
98
99
100 if (!decoder) {
101 dev_warn(mdev->dev, "Decoder not found\n");
102 return -EINVAL;
103 }
104
105
106 if (tuner) {
107 if (if_vid) {
108 pad_source = media_get_pad_index(tuner, false,
109 PAD_SIGNAL_ANALOG);
110 pad_sink = media_get_pad_index(if_vid, true,
111 PAD_SIGNAL_ANALOG);
112 if (pad_source < 0 || pad_sink < 0) {
113 dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n",
114 pad_source, pad_sink);
115 return -EINVAL;
116 }
117 ret = media_create_pad_link(tuner, pad_source,
118 if_vid, pad_sink,
119 MEDIA_LNK_FL_ENABLED);
120 if (ret) {
121 dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n");
122 return ret;
123 }
124
125 pad_source = media_get_pad_index(if_vid, false,
126 PAD_SIGNAL_ANALOG);
127 pad_sink = media_get_pad_index(decoder, true,
128 PAD_SIGNAL_ANALOG);
129 if (pad_source < 0 || pad_sink < 0) {
130 dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n",
131 pad_source, pad_sink);
132 return -EINVAL;
133 }
134 ret = media_create_pad_link(if_vid, pad_source,
135 decoder, pad_sink,
136 MEDIA_LNK_FL_ENABLED);
137 if (ret) {
138 dev_warn(mdev->dev, "couldn't link PLL to decoder\n");
139 return ret;
140 }
141 } else {
142 pad_source = media_get_pad_index(tuner, false,
143 PAD_SIGNAL_ANALOG);
144 pad_sink = media_get_pad_index(decoder, true,
145 PAD_SIGNAL_ANALOG);
146 if (pad_source < 0 || pad_sink < 0) {
147 dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n",
148 pad_source, pad_sink);
149 return -EINVAL;
150 }
151 ret = media_create_pad_link(tuner, pad_source,
152 decoder, pad_sink,
153 MEDIA_LNK_FL_ENABLED);
154 if (ret)
155 return ret;
156 }
157
158 if (if_aud) {
159 pad_source = media_get_pad_index(tuner, false,
160 PAD_SIGNAL_AUDIO);
161 pad_sink = media_get_pad_index(if_aud, true,
162 PAD_SIGNAL_AUDIO);
163 if (pad_source < 0 || pad_sink < 0) {
164 dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n",
165 pad_source, pad_sink);
166 return -EINVAL;
167 }
168 ret = media_create_pad_link(tuner, pad_source,
169 if_aud, pad_sink,
170 MEDIA_LNK_FL_ENABLED);
171 if (ret) {
172 dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n");
173 return ret;
174 }
175 } else {
176 if_aud = tuner;
177 }
178
179 }
180
181
182 if (io_v4l) {
183 pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
184 if (pad_source < 0) {
185 dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n");
186 return -EINVAL;
187 }
188 ret = media_create_pad_link(decoder, pad_source,
189 io_v4l, 0,
190 MEDIA_LNK_FL_ENABLED);
191 if (ret) {
192 dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n");
193 return ret;
194 }
195 }
196
197 if (io_swradio) {
198 pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
199 if (pad_source < 0) {
200 dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n");
201 return -EINVAL;
202 }
203 ret = media_create_pad_link(decoder, pad_source,
204 io_swradio, 0,
205 MEDIA_LNK_FL_ENABLED);
206 if (ret) {
207 dev_warn(mdev->dev, "couldn't link decoder output to SDR\n");
208 return ret;
209 }
210 }
211
212 if (io_vbi) {
213 pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
214 if (pad_source < 0) {
215 dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n");
216 return -EINVAL;
217 }
218 ret = media_create_pad_link(decoder, pad_source,
219 io_vbi, 0,
220 MEDIA_LNK_FL_ENABLED);
221 if (ret) {
222 dev_warn(mdev->dev, "couldn't link decoder output to VBI\n");
223 return ret;
224 }
225 }
226
227
228 flags = MEDIA_LNK_FL_ENABLED;
229 media_device_for_each_entity(entity, mdev) {
230 switch (entity->function) {
231 case MEDIA_ENT_F_CONN_RF:
232 if (!tuner)
233 continue;
234 pad_sink = media_get_pad_index(tuner, true,
235 PAD_SIGNAL_ANALOG);
236 if (pad_sink < 0) {
237 dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");
238 return -EINVAL;
239 }
240 ret = media_create_pad_link(entity, 0, tuner,
241 pad_sink,
242 flags);
243 break;
244 case MEDIA_ENT_F_CONN_SVIDEO:
245 case MEDIA_ENT_F_CONN_COMPOSITE:
246 pad_sink = media_get_pad_index(decoder, true,
247 PAD_SIGNAL_ANALOG);
248 if (pad_sink < 0) {
249 dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");
250 return -EINVAL;
251 }
252 ret = media_create_pad_link(entity, 0, decoder,
253 pad_sink,
254 flags);
255 break;
256 default:
257 continue;
258 }
259 if (ret)
260 return ret;
261
262 flags = 0;
263 }
264
265 return 0;
266}
267EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
268
269int v4l_enable_media_source(struct video_device *vdev)
270{
271 struct media_device *mdev = vdev->entity.graph_obj.mdev;
272 int ret = 0, err;
273
274 if (!mdev)
275 return 0;
276
277 mutex_lock(&mdev->graph_mutex);
278 if (!mdev->enable_source)
279 goto end;
280 err = mdev->enable_source(&vdev->entity, &vdev->pipe);
281 if (err)
282 ret = -EBUSY;
283end:
284 mutex_unlock(&mdev->graph_mutex);
285 return ret;
286}
287EXPORT_SYMBOL_GPL(v4l_enable_media_source);
288
289void v4l_disable_media_source(struct video_device *vdev)
290{
291 struct media_device *mdev = vdev->entity.graph_obj.mdev;
292
293 if (mdev) {
294 mutex_lock(&mdev->graph_mutex);
295 if (mdev->disable_source)
296 mdev->disable_source(&vdev->entity);
297 mutex_unlock(&mdev->graph_mutex);
298 }
299}
300EXPORT_SYMBOL_GPL(v4l_disable_media_source);
301
302int v4l_vb2q_enable_media_source(struct vb2_queue *q)
303{
304 struct v4l2_fh *fh = q->owner;
305
306 if (fh && fh->vdev)
307 return v4l_enable_media_source(fh->vdev);
308 return 0;
309}
310EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
311
312int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
313 struct media_pad *sink)
314{
315 struct fwnode_handle *endpoint;
316 struct v4l2_subdev *sink_sd;
317
318 if (!(sink->flags & MEDIA_PAD_FL_SINK) ||
319 !is_media_entity_v4l2_subdev(sink->entity))
320 return -EINVAL;
321
322 sink_sd = media_entity_to_v4l2_subdev(sink->entity);
323
324 fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) {
325 struct fwnode_handle *remote_ep;
326 int src_idx, sink_idx, ret;
327 struct media_pad *src;
328
329 src_idx = media_entity_get_fwnode_pad(&src_sd->entity,
330 endpoint,
331 MEDIA_PAD_FL_SOURCE);
332 if (src_idx < 0)
333 continue;
334
335 remote_ep = fwnode_graph_get_remote_endpoint(endpoint);
336 if (!remote_ep)
337 continue;
338
339
340
341
342
343 sink_idx = media_entity_get_fwnode_pad(&sink_sd->entity,
344 remote_ep,
345 MEDIA_PAD_FL_SINK);
346 fwnode_handle_put(remote_ep);
347
348 if (sink_idx < 0 || sink_idx != sink->index)
349 continue;
350
351
352
353
354
355
356
357
358
359 src = &src_sd->entity.pads[src_idx];
360
361
362 if (media_entity_find_link(src, sink))
363 continue;
364
365 dev_dbg(sink_sd->dev, "creating link %s:%d -> %s:%d\n",
366 src_sd->entity.name, src_idx,
367 sink_sd->entity.name, sink_idx);
368
369 ret = media_create_pad_link(&src_sd->entity, src_idx,
370 &sink_sd->entity, sink_idx, 0);
371 if (ret) {
372 dev_err(sink_sd->dev,
373 "link %s:%d -> %s:%d failed with %d\n",
374 src_sd->entity.name, src_idx,
375 sink_sd->entity.name, sink_idx, ret);
376
377 fwnode_handle_put(endpoint);
378 return ret;
379 }
380 }
381
382 return 0;
383}
384EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad);
385
386int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd,
387 struct v4l2_subdev *sink_sd)
388{
389 unsigned int i;
390
391 for (i = 0; i < sink_sd->entity.num_pads; i++) {
392 struct media_pad *pad = &sink_sd->entity.pads[i];
393 int ret;
394
395 if (!(pad->flags & MEDIA_PAD_FL_SINK))
396 continue;
397
398 ret = v4l2_create_fwnode_links_to_pad(src_sd, pad);
399 if (ret)
400 return ret;
401 }
402
403 return 0;
404}
405EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links);
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434static int pipeline_pm_use_count(struct media_entity *entity,
435 struct media_graph *graph)
436{
437 int use = 0;
438
439 media_graph_walk_start(graph, entity);
440
441 while ((entity = media_graph_walk_next(graph))) {
442 if (is_media_entity_v4l2_video_device(entity))
443 use += entity->use_count;
444 }
445
446 return use;
447}
448
449
450
451
452
453
454
455
456
457
458
459
460static int pipeline_pm_power_one(struct media_entity *entity, int change)
461{
462 struct v4l2_subdev *subdev;
463 int ret;
464
465 subdev = is_media_entity_v4l2_subdev(entity)
466 ? media_entity_to_v4l2_subdev(entity) : NULL;
467
468 if (entity->use_count == 0 && change > 0 && subdev != NULL) {
469 ret = v4l2_subdev_call(subdev, core, s_power, 1);
470 if (ret < 0 && ret != -ENOIOCTLCMD)
471 return ret;
472 }
473
474 entity->use_count += change;
475 WARN_ON(entity->use_count < 0);
476
477 if (entity->use_count == 0 && change < 0 && subdev != NULL)
478 v4l2_subdev_call(subdev, core, s_power, 0);
479
480 return 0;
481}
482
483
484
485
486
487
488
489
490
491
492
493static int pipeline_pm_power(struct media_entity *entity, int change,
494 struct media_graph *graph)
495{
496 struct media_entity *first = entity;
497 int ret = 0;
498
499 if (!change)
500 return 0;
501
502 media_graph_walk_start(graph, entity);
503
504 while (!ret && (entity = media_graph_walk_next(graph)))
505 if (is_media_entity_v4l2_subdev(entity))
506 ret = pipeline_pm_power_one(entity, change);
507
508 if (!ret)
509 return ret;
510
511 media_graph_walk_start(graph, first);
512
513 while ((first = media_graph_walk_next(graph))
514 && first != entity)
515 if (is_media_entity_v4l2_subdev(first))
516 pipeline_pm_power_one(first, -change);
517
518 return ret;
519}
520
521static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use)
522{
523 struct media_device *mdev = entity->graph_obj.mdev;
524 int change = use ? 1 : -1;
525 int ret;
526
527 mutex_lock(&mdev->graph_mutex);
528
529
530 entity->use_count += change;
531 WARN_ON(entity->use_count < 0);
532
533
534 ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
535 if (ret < 0)
536 entity->use_count -= change;
537
538 mutex_unlock(&mdev->graph_mutex);
539
540 return ret;
541}
542
543int v4l2_pipeline_pm_get(struct media_entity *entity)
544{
545 return v4l2_pipeline_pm_use(entity, 1);
546}
547EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get);
548
549void v4l2_pipeline_pm_put(struct media_entity *entity)
550{
551
552 WARN_ON(v4l2_pipeline_pm_use(entity, 0));
553}
554EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put);
555
556int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
557 unsigned int notification)
558{
559 struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk;
560 struct media_entity *source = link->source->entity;
561 struct media_entity *sink = link->sink->entity;
562 int source_use;
563 int sink_use;
564 int ret = 0;
565
566 source_use = pipeline_pm_use_count(source, graph);
567 sink_use = pipeline_pm_use_count(sink, graph);
568
569 if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
570 !(flags & MEDIA_LNK_FL_ENABLED)) {
571
572 pipeline_pm_power(source, -sink_use, graph);
573 pipeline_pm_power(sink, -source_use, graph);
574 return 0;
575 }
576
577 if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
578 (flags & MEDIA_LNK_FL_ENABLED)) {
579
580 ret = pipeline_pm_power(source, sink_use, graph);
581 if (ret < 0)
582 return ret;
583
584 ret = pipeline_pm_power(sink, source_use, graph);
585 if (ret < 0)
586 pipeline_pm_power(source, -sink_use, graph);
587 }
588
589 return ret;
590}
591EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
592