]> Git Repo - linux.git/blob - drivers/media/v4l2-core/v4l2-mc.c
Linux 6.14-rc3
[linux.git] / drivers / media / v4l2-core / v4l2-mc.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4  * Media Controller ancillary functions
5  *
6  * Copyright (c) 2016 Mauro Carvalho Chehab <[email protected]>
7  * Copyright (C) 2016 Shuah Khan <[email protected]>
8  * Copyright (C) 2006-2010 Nokia Corporation
9  * Copyright (c) 2016 Intel Corporation.
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
22 int 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         /* It should have at least one I/O entity */
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          * Here, webcams are modelled on a very simple way: the sensor is
73          * connected directly to the I/O entity. All dirty details, like
74          * scaler and crop HW are hidden. While such mapping is not enough
75          * for mc-centric hardware, it is enough for v4l2 interface centric
76          * PC-consumer's hardware.
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         /* The device isn't a webcam. So, it should have a decoder */
100         if (!decoder) {
101                 dev_warn(mdev->dev, "Decoder not found\n");
102                 return -EINVAL;
103         }
104
105         /* Link the tuner and IF video output pads */
106         if (tuner) {
107                 if (if_vid) {
108                         pad_source = media_get_pad_index(tuner,
109                                                          MEDIA_PAD_FL_SOURCE,
110                                                          PAD_SIGNAL_ANALOG);
111                         pad_sink = media_get_pad_index(if_vid,
112                                                        MEDIA_PAD_FL_SINK,
113                                                        PAD_SIGNAL_ANALOG);
114                         if (pad_source < 0 || pad_sink < 0) {
115                                 dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n",
116                                          pad_source, pad_sink);
117                                 return -EINVAL;
118                         }
119                         ret = media_create_pad_link(tuner, pad_source,
120                                                     if_vid, pad_sink,
121                                                     MEDIA_LNK_FL_ENABLED);
122                         if (ret) {
123                                 dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n");
124                                 return ret;
125                         }
126
127                         pad_source = media_get_pad_index(if_vid,
128                                                          MEDIA_PAD_FL_SOURCE,
129                                                          PAD_SIGNAL_ANALOG);
130                         pad_sink = media_get_pad_index(decoder,
131                                                        MEDIA_PAD_FL_SINK,
132                                                        PAD_SIGNAL_ANALOG);
133                         if (pad_source < 0 || pad_sink < 0) {
134                                 dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n",
135                                          pad_source, pad_sink);
136                                 return -EINVAL;
137                         }
138                         ret = media_create_pad_link(if_vid, pad_source,
139                                                     decoder, pad_sink,
140                                                     MEDIA_LNK_FL_ENABLED);
141                         if (ret) {
142                                 dev_warn(mdev->dev, "couldn't link PLL to decoder\n");
143                                 return ret;
144                         }
145                 } else {
146                         pad_source = media_get_pad_index(tuner,
147                                                          MEDIA_PAD_FL_SOURCE,
148                                                          PAD_SIGNAL_ANALOG);
149                         pad_sink = media_get_pad_index(decoder,
150                                                        MEDIA_PAD_FL_SINK,
151                                                        PAD_SIGNAL_ANALOG);
152                         if (pad_source < 0 || pad_sink < 0) {
153                                 dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n",
154                                          pad_source, pad_sink);
155                                 return -EINVAL;
156                         }
157                         ret = media_create_pad_link(tuner, pad_source,
158                                                     decoder, pad_sink,
159                                                     MEDIA_LNK_FL_ENABLED);
160                         if (ret)
161                                 return ret;
162                 }
163
164                 if (if_aud) {
165                         pad_source = media_get_pad_index(tuner,
166                                                          MEDIA_PAD_FL_SOURCE,
167                                                          PAD_SIGNAL_AUDIO);
168                         pad_sink = media_get_pad_index(if_aud,
169                                                        MEDIA_PAD_FL_SINK,
170                                                        PAD_SIGNAL_AUDIO);
171                         if (pad_source < 0 || pad_sink < 0) {
172                                 dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n",
173                                          pad_source, pad_sink);
174                                 return -EINVAL;
175                         }
176                         ret = media_create_pad_link(tuner, pad_source,
177                                                     if_aud, pad_sink,
178                                                     MEDIA_LNK_FL_ENABLED);
179                         if (ret) {
180                                 dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n");
181                                 return ret;
182                         }
183                 } else {
184                         if_aud = tuner;
185                 }
186
187         }
188
189         /* Create demod to V4L, VBI and SDR radio links */
190         if (io_v4l) {
191                 pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
192                                                  PAD_SIGNAL_DV);
193                 if (pad_source < 0) {
194                         dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n");
195                         return -EINVAL;
196                 }
197                 ret = media_create_pad_link(decoder, pad_source,
198                                             io_v4l, 0,
199                                             MEDIA_LNK_FL_ENABLED);
200                 if (ret) {
201                         dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n");
202                         return ret;
203                 }
204         }
205
206         if (io_swradio) {
207                 pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
208                                                  PAD_SIGNAL_DV);
209                 if (pad_source < 0) {
210                         dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n");
211                         return -EINVAL;
212                 }
213                 ret = media_create_pad_link(decoder, pad_source,
214                                             io_swradio, 0,
215                                             MEDIA_LNK_FL_ENABLED);
216                 if (ret) {
217                         dev_warn(mdev->dev, "couldn't link decoder output to SDR\n");
218                         return ret;
219                 }
220         }
221
222         if (io_vbi) {
223                 pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
224                                                  PAD_SIGNAL_DV);
225                 if (pad_source < 0) {
226                         dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n");
227                         return -EINVAL;
228                 }
229                 ret = media_create_pad_link(decoder, pad_source,
230                                             io_vbi, 0,
231                                             MEDIA_LNK_FL_ENABLED);
232                 if (ret) {
233                         dev_warn(mdev->dev, "couldn't link decoder output to VBI\n");
234                         return ret;
235                 }
236         }
237
238         /* Create links for the media connectors */
239         flags = MEDIA_LNK_FL_ENABLED;
240         media_device_for_each_entity(entity, mdev) {
241                 switch (entity->function) {
242                 case MEDIA_ENT_F_CONN_RF:
243                         if (!tuner)
244                                 continue;
245                         pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,
246                                                        PAD_SIGNAL_ANALOG);
247                         if (pad_sink < 0) {
248                                 dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");
249                                 return -EINVAL;
250                         }
251                         ret = media_create_pad_link(entity, 0, tuner,
252                                                     pad_sink,
253                                                     flags);
254                         break;
255                 case MEDIA_ENT_F_CONN_SVIDEO:
256                 case MEDIA_ENT_F_CONN_COMPOSITE:
257                         pad_sink = media_get_pad_index(decoder,
258                                                        MEDIA_PAD_FL_SINK,
259                                                        PAD_SIGNAL_ANALOG);
260                         if (pad_sink < 0) {
261                                 dev_warn(mdev->dev, "couldn't get decoder analog pad sink\n");
262                                 return -EINVAL;
263                         }
264                         ret = media_create_pad_link(entity, 0, decoder,
265                                                     pad_sink,
266                                                     flags);
267                         break;
268                 default:
269                         continue;
270                 }
271                 if (ret)
272                         return ret;
273
274                 flags = 0;
275         }
276
277         return 0;
278 }
279 EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
280
281 int v4l_enable_media_source(struct video_device *vdev)
282 {
283         struct media_device *mdev = vdev->entity.graph_obj.mdev;
284         int ret = 0, err;
285
286         if (!mdev)
287                 return 0;
288
289         mutex_lock(&mdev->graph_mutex);
290         if (!mdev->enable_source)
291                 goto end;
292         err = mdev->enable_source(&vdev->entity, &vdev->pipe);
293         if (err)
294                 ret = -EBUSY;
295 end:
296         mutex_unlock(&mdev->graph_mutex);
297         return ret;
298 }
299 EXPORT_SYMBOL_GPL(v4l_enable_media_source);
300
301 void v4l_disable_media_source(struct video_device *vdev)
302 {
303         struct media_device *mdev = vdev->entity.graph_obj.mdev;
304
305         if (mdev) {
306                 mutex_lock(&mdev->graph_mutex);
307                 if (mdev->disable_source)
308                         mdev->disable_source(&vdev->entity);
309                 mutex_unlock(&mdev->graph_mutex);
310         }
311 }
312 EXPORT_SYMBOL_GPL(v4l_disable_media_source);
313
314 int v4l_vb2q_enable_media_source(struct vb2_queue *q)
315 {
316         struct v4l2_fh *fh = q->owner;
317
318         if (fh && fh->vdev)
319                 return v4l_enable_media_source(fh->vdev);
320         return 0;
321 }
322 EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
323
324 int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
325                                     struct media_pad *sink, u32 flags)
326 {
327         struct fwnode_handle *endpoint;
328
329         if (!(sink->flags & MEDIA_PAD_FL_SINK))
330                 return -EINVAL;
331
332         fwnode_graph_for_each_endpoint(src_sd->fwnode, endpoint) {
333                 struct fwnode_handle *remote_ep;
334                 int src_idx, sink_idx, ret;
335                 struct media_pad *src;
336
337                 src_idx = media_entity_get_fwnode_pad(&src_sd->entity,
338                                                       endpoint,
339                                                       MEDIA_PAD_FL_SOURCE);
340                 if (src_idx < 0) {
341                         dev_dbg(src_sd->dev, "no source pad found for %pfw\n",
342                                 endpoint);
343                         continue;
344                 }
345
346                 remote_ep = fwnode_graph_get_remote_endpoint(endpoint);
347                 if (!remote_ep) {
348                         dev_dbg(src_sd->dev, "no remote ep found for %pfw\n",
349                                 endpoint);
350                         continue;
351                 }
352
353                 /*
354                  * ask the sink to verify it owns the remote endpoint,
355                  * and translate to a sink pad.
356                  */
357                 sink_idx = media_entity_get_fwnode_pad(sink->entity,
358                                                        remote_ep,
359                                                        MEDIA_PAD_FL_SINK);
360                 fwnode_handle_put(remote_ep);
361
362                 if (sink_idx < 0 || sink_idx != sink->index) {
363                         dev_dbg(src_sd->dev,
364                                 "sink pad index mismatch or error (is %d, expected %u)\n",
365                                 sink_idx, sink->index);
366                         continue;
367                 }
368
369                 /*
370                  * the source endpoint corresponds to one of its source pads,
371                  * the source endpoint connects to an endpoint at the sink
372                  * entity, and the sink endpoint corresponds to the sink
373                  * pad requested, so we have found an endpoint connection
374                  * that works, create the media link for it.
375                  */
376
377                 src = &src_sd->entity.pads[src_idx];
378
379                 /* skip if link already exists */
380                 if (media_entity_find_link(src, sink)) {
381                         dev_dbg(src_sd->dev,
382                                 "link %s:%d -> %s:%d already exists\n",
383                                 src_sd->entity.name, src_idx,
384                                 sink->entity->name, sink_idx);
385                         continue;
386                 }
387
388                 dev_dbg(src_sd->dev, "creating link %s:%d -> %s:%d\n",
389                         src_sd->entity.name, src_idx,
390                         sink->entity->name, sink_idx);
391
392                 ret = media_create_pad_link(&src_sd->entity, src_idx,
393                                             sink->entity, sink_idx, flags);
394                 if (ret) {
395                         dev_err(src_sd->dev,
396                                 "link %s:%d -> %s:%d failed with %d\n",
397                                 src_sd->entity.name, src_idx,
398                                 sink->entity->name, sink_idx, ret);
399
400                         fwnode_handle_put(endpoint);
401                         return ret;
402                 }
403         }
404
405         return 0;
406 }
407 EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad);
408
409 int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd,
410                              struct v4l2_subdev *sink_sd)
411 {
412         unsigned int i;
413
414         for (i = 0; i < sink_sd->entity.num_pads; i++) {
415                 struct media_pad *pad = &sink_sd->entity.pads[i];
416                 int ret;
417
418                 if (!(pad->flags & MEDIA_PAD_FL_SINK))
419                         continue;
420
421                 ret = v4l2_create_fwnode_links_to_pad(src_sd, pad, 0);
422                 if (ret)
423                         return ret;
424         }
425
426         return 0;
427 }
428 EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links);
429
430 /* -----------------------------------------------------------------------------
431  * Pipeline power management
432  *
433  * Entities must be powered up when part of a pipeline that contains at least
434  * one open video device node.
435  *
436  * To achieve this use the entity use_count field to track the number of users.
437  * For entities corresponding to video device nodes the use_count field stores
438  * the users count of the node. For entities corresponding to subdevs the
439  * use_count field stores the total number of users of all video device nodes
440  * in the pipeline.
441  *
442  * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and
443  * close() handlers of video device nodes. It increments or decrements the use
444  * count of all subdev entities in the pipeline.
445  *
446  * To react to link management on powered pipelines, the link setup notification
447  * callback updates the use count of all entities in the source and sink sides
448  * of the link.
449  */
450
451 /*
452  * pipeline_pm_use_count - Count the number of users of a pipeline
453  * @entity: The entity
454  *
455  * Return the total number of users of all video device nodes in the pipeline.
456  */
457 static int pipeline_pm_use_count(struct media_entity *entity,
458         struct media_graph *graph)
459 {
460         int use = 0;
461
462         media_graph_walk_start(graph, entity);
463
464         while ((entity = media_graph_walk_next(graph))) {
465                 if (is_media_entity_v4l2_video_device(entity))
466                         use += entity->use_count;
467         }
468
469         return use;
470 }
471
472 /*
473  * pipeline_pm_power_one - Apply power change to an entity
474  * @entity: The entity
475  * @change: Use count change
476  *
477  * Change the entity use count by @change. If the entity is a subdev update its
478  * power state by calling the core::s_power operation when the use count goes
479  * from 0 to != 0 or from != 0 to 0.
480  *
481  * Return 0 on success or a negative error code on failure.
482  */
483 static int pipeline_pm_power_one(struct media_entity *entity, int change)
484 {
485         struct v4l2_subdev *subdev;
486         int ret;
487
488         subdev = is_media_entity_v4l2_subdev(entity)
489                ? media_entity_to_v4l2_subdev(entity) : NULL;
490
491         if (entity->use_count == 0 && change > 0 && subdev != NULL) {
492                 ret = v4l2_subdev_call(subdev, core, s_power, 1);
493                 if (ret < 0 && ret != -ENOIOCTLCMD)
494                         return ret;
495         }
496
497         entity->use_count += change;
498         WARN_ON(entity->use_count < 0);
499
500         if (entity->use_count == 0 && change < 0 && subdev != NULL)
501                 v4l2_subdev_call(subdev, core, s_power, 0);
502
503         return 0;
504 }
505
506 /*
507  * pipeline_pm_power - Apply power change to all entities in a pipeline
508  * @entity: The entity
509  * @change: Use count change
510  *
511  * Walk the pipeline to update the use count and the power state of all non-node
512  * entities.
513  *
514  * Return 0 on success or a negative error code on failure.
515  */
516 static int pipeline_pm_power(struct media_entity *entity, int change,
517         struct media_graph *graph)
518 {
519         struct media_entity *first = entity;
520         int ret = 0;
521
522         if (!change)
523                 return 0;
524
525         media_graph_walk_start(graph, entity);
526
527         while (!ret && (entity = media_graph_walk_next(graph)))
528                 if (is_media_entity_v4l2_subdev(entity))
529                         ret = pipeline_pm_power_one(entity, change);
530
531         if (!ret)
532                 return ret;
533
534         media_graph_walk_start(graph, first);
535
536         while ((first = media_graph_walk_next(graph))
537                && first != entity)
538                 if (is_media_entity_v4l2_subdev(first))
539                         pipeline_pm_power_one(first, -change);
540
541         return ret;
542 }
543
544 static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use)
545 {
546         struct media_device *mdev = entity->graph_obj.mdev;
547         int change = use ? 1 : -1;
548         int ret;
549
550         mutex_lock(&mdev->graph_mutex);
551
552         /* Apply use count to node. */
553         entity->use_count += change;
554         WARN_ON(entity->use_count < 0);
555
556         /* Apply power change to connected non-nodes. */
557         ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
558         if (ret < 0)
559                 entity->use_count -= change;
560
561         mutex_unlock(&mdev->graph_mutex);
562
563         return ret;
564 }
565
566 int v4l2_pipeline_pm_get(struct media_entity *entity)
567 {
568         return v4l2_pipeline_pm_use(entity, 1);
569 }
570 EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get);
571
572 void v4l2_pipeline_pm_put(struct media_entity *entity)
573 {
574         /* Powering off entities shouldn't fail. */
575         WARN_ON(v4l2_pipeline_pm_use(entity, 0));
576 }
577 EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put);
578
579 int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
580                               unsigned int notification)
581 {
582         struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk;
583         struct media_entity *source = link->source->entity;
584         struct media_entity *sink = link->sink->entity;
585         int source_use;
586         int sink_use;
587         int ret = 0;
588
589         source_use = pipeline_pm_use_count(source, graph);
590         sink_use = pipeline_pm_use_count(sink, graph);
591
592         if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
593             !(flags & MEDIA_LNK_FL_ENABLED)) {
594                 /* Powering off entities is assumed to never fail. */
595                 pipeline_pm_power(source, -sink_use, graph);
596                 pipeline_pm_power(sink, -source_use, graph);
597                 return 0;
598         }
599
600         if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
601                 (flags & MEDIA_LNK_FL_ENABLED)) {
602
603                 ret = pipeline_pm_power(source, sink_use, graph);
604                 if (ret < 0)
605                         return ret;
606
607                 ret = pipeline_pm_power(sink, source_use, graph);
608                 if (ret < 0)
609                         pipeline_pm_power(source, -sink_use, graph);
610         }
611
612         return ret;
613 }
614 EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
This page took 0.066245 seconds and 4 git commands to generate.