linux/drivers/media/v4l2-core/v4l2-mc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2
   3/*
   4 * Media Controller ancillary functions
   5 *
   6 * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@kernel.org>
   7 * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
   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
  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;
  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                return -EINVAL;
  68
  69        /*
  70         * Here, webcams are modelled on a very simple way: the sensor is
  71         * connected directly to the I/O entity. All dirty details, like
  72         * scaler and crop HW are hidden. While such mapping is not enough
  73         * for mc-centric hardware, it is enough for v4l2 interface centric
  74         * PC-consumer's hardware.
  75         */
  76        if (is_webcam) {
  77                if (!io_v4l)
  78                        return -EINVAL;
  79
  80                media_device_for_each_entity(entity, mdev) {
  81                        if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
  82                                continue;
  83                        ret = media_create_pad_link(entity, 0,
  84                                                    io_v4l, 0,
  85                                                    MEDIA_LNK_FL_ENABLED);
  86                        if (ret)
  87                                return ret;
  88                }
  89                if (!decoder)
  90                        return 0;
  91        }
  92
  93        /* The device isn't a webcam. So, it should have a decoder */
  94        if (!decoder)
  95                return -EINVAL;
  96
  97        /* Link the tuner and IF video output pads */
  98        if (tuner) {
  99                if (if_vid) {
 100                        ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
 101                                                    if_vid,
 102                                                    IF_VID_DEC_PAD_IF_INPUT,
 103                                                    MEDIA_LNK_FL_ENABLED);
 104                        if (ret)
 105                                return ret;
 106                        ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
 107                                                decoder, DEMOD_PAD_IF_INPUT,
 108                                                MEDIA_LNK_FL_ENABLED);
 109                        if (ret)
 110                                return ret;
 111                } else {
 112                        ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
 113                                                decoder, DEMOD_PAD_IF_INPUT,
 114                                                MEDIA_LNK_FL_ENABLED);
 115                        if (ret)
 116                                return ret;
 117                }
 118
 119                if (if_aud) {
 120                        ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
 121                                                    if_aud,
 122                                                    IF_AUD_DEC_PAD_IF_INPUT,
 123                                                    MEDIA_LNK_FL_ENABLED);
 124                        if (ret)
 125                                return ret;
 126                } else {
 127                        if_aud = tuner;
 128                }
 129
 130        }
 131
 132        /* Create demod to V4L, VBI and SDR radio links */
 133        if (io_v4l) {
 134                ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
 135                                        io_v4l, 0,
 136                                        MEDIA_LNK_FL_ENABLED);
 137                if (ret)
 138                        return ret;
 139        }
 140
 141        if (io_swradio) {
 142                ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
 143                                        io_swradio, 0,
 144                                        MEDIA_LNK_FL_ENABLED);
 145                if (ret)
 146                        return ret;
 147        }
 148
 149        if (io_vbi) {
 150                ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
 151                                            io_vbi, 0,
 152                                            MEDIA_LNK_FL_ENABLED);
 153                if (ret)
 154                        return ret;
 155        }
 156
 157        /* Create links for the media connectors */
 158        flags = MEDIA_LNK_FL_ENABLED;
 159        media_device_for_each_entity(entity, mdev) {
 160                switch (entity->function) {
 161                case MEDIA_ENT_F_CONN_RF:
 162                        if (!tuner)
 163                                continue;
 164
 165                        ret = media_create_pad_link(entity, 0, tuner,
 166                                                    TUNER_PAD_RF_INPUT,
 167                                                    flags);
 168                        break;
 169                case MEDIA_ENT_F_CONN_SVIDEO:
 170                case MEDIA_ENT_F_CONN_COMPOSITE:
 171                        ret = media_create_pad_link(entity, 0, decoder,
 172                                                    DEMOD_PAD_IF_INPUT,
 173                                                    flags);
 174                        break;
 175                default:
 176                        continue;
 177                }
 178                if (ret)
 179                        return ret;
 180
 181                flags = 0;
 182        }
 183
 184        return 0;
 185}
 186EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
 187
 188int v4l_enable_media_source(struct video_device *vdev)
 189{
 190        struct media_device *mdev = vdev->entity.graph_obj.mdev;
 191        int ret = 0, err;
 192
 193        if (!mdev)
 194                return 0;
 195
 196        mutex_lock(&mdev->graph_mutex);
 197        if (!mdev->enable_source)
 198                goto end;
 199        err = mdev->enable_source(&vdev->entity, &vdev->pipe);
 200        if (err)
 201                ret = -EBUSY;
 202end:
 203        mutex_unlock(&mdev->graph_mutex);
 204        return ret;
 205}
 206EXPORT_SYMBOL_GPL(v4l_enable_media_source);
 207
 208void v4l_disable_media_source(struct video_device *vdev)
 209{
 210        struct media_device *mdev = vdev->entity.graph_obj.mdev;
 211
 212        if (mdev) {
 213                mutex_lock(&mdev->graph_mutex);
 214                if (mdev->disable_source)
 215                        mdev->disable_source(&vdev->entity);
 216                mutex_unlock(&mdev->graph_mutex);
 217        }
 218}
 219EXPORT_SYMBOL_GPL(v4l_disable_media_source);
 220
 221int v4l_vb2q_enable_media_source(struct vb2_queue *q)
 222{
 223        struct v4l2_fh *fh = q->owner;
 224
 225        if (fh && fh->vdev)
 226                return v4l_enable_media_source(fh->vdev);
 227        return 0;
 228}
 229EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
 230
 231/* -----------------------------------------------------------------------------
 232 * Pipeline power management
 233 *
 234 * Entities must be powered up when part of a pipeline that contains at least
 235 * one open video device node.
 236 *
 237 * To achieve this use the entity use_count field to track the number of users.
 238 * For entities corresponding to video device nodes the use_count field stores
 239 * the users count of the node. For entities corresponding to subdevs the
 240 * use_count field stores the total number of users of all video device nodes
 241 * in the pipeline.
 242 *
 243 * The v4l2_pipeline_pm_use() function must be called in the open() and
 244 * close() handlers of video device nodes. It increments or decrements the use
 245 * count of all subdev entities in the pipeline.
 246 *
 247 * To react to link management on powered pipelines, the link setup notification
 248 * callback updates the use count of all entities in the source and sink sides
 249 * of the link.
 250 */
 251
 252/*
 253 * pipeline_pm_use_count - Count the number of users of a pipeline
 254 * @entity: The entity
 255 *
 256 * Return the total number of users of all video device nodes in the pipeline.
 257 */
 258static int pipeline_pm_use_count(struct media_entity *entity,
 259        struct media_graph *graph)
 260{
 261        int use = 0;
 262
 263        media_graph_walk_start(graph, entity);
 264
 265        while ((entity = media_graph_walk_next(graph))) {
 266                if (is_media_entity_v4l2_video_device(entity))
 267                        use += entity->use_count;
 268        }
 269
 270        return use;
 271}
 272
 273/*
 274 * pipeline_pm_power_one - Apply power change to an entity
 275 * @entity: The entity
 276 * @change: Use count change
 277 *
 278 * Change the entity use count by @change. If the entity is a subdev update its
 279 * power state by calling the core::s_power operation when the use count goes
 280 * from 0 to != 0 or from != 0 to 0.
 281 *
 282 * Return 0 on success or a negative error code on failure.
 283 */
 284static int pipeline_pm_power_one(struct media_entity *entity, int change)
 285{
 286        struct v4l2_subdev *subdev;
 287        int ret;
 288
 289        subdev = is_media_entity_v4l2_subdev(entity)
 290               ? media_entity_to_v4l2_subdev(entity) : NULL;
 291
 292        if (entity->use_count == 0 && change > 0 && subdev != NULL) {
 293                ret = v4l2_subdev_call(subdev, core, s_power, 1);
 294                if (ret < 0 && ret != -ENOIOCTLCMD)
 295                        return ret;
 296        }
 297
 298        entity->use_count += change;
 299        WARN_ON(entity->use_count < 0);
 300
 301        if (entity->use_count == 0 && change < 0 && subdev != NULL)
 302                v4l2_subdev_call(subdev, core, s_power, 0);
 303
 304        return 0;
 305}
 306
 307/*
 308 * pipeline_pm_power - Apply power change to all entities in a pipeline
 309 * @entity: The entity
 310 * @change: Use count change
 311 *
 312 * Walk the pipeline to update the use count and the power state of all non-node
 313 * entities.
 314 *
 315 * Return 0 on success or a negative error code on failure.
 316 */
 317static int pipeline_pm_power(struct media_entity *entity, int change,
 318        struct media_graph *graph)
 319{
 320        struct media_entity *first = entity;
 321        int ret = 0;
 322
 323        if (!change)
 324                return 0;
 325
 326        media_graph_walk_start(graph, entity);
 327
 328        while (!ret && (entity = media_graph_walk_next(graph)))
 329                if (is_media_entity_v4l2_subdev(entity))
 330                        ret = pipeline_pm_power_one(entity, change);
 331
 332        if (!ret)
 333                return ret;
 334
 335        media_graph_walk_start(graph, first);
 336
 337        while ((first = media_graph_walk_next(graph))
 338               && first != entity)
 339                if (is_media_entity_v4l2_subdev(first))
 340                        pipeline_pm_power_one(first, -change);
 341
 342        return ret;
 343}
 344
 345int v4l2_pipeline_pm_use(struct media_entity *entity, int use)
 346{
 347        struct media_device *mdev = entity->graph_obj.mdev;
 348        int change = use ? 1 : -1;
 349        int ret;
 350
 351        mutex_lock(&mdev->graph_mutex);
 352
 353        /* Apply use count to node. */
 354        entity->use_count += change;
 355        WARN_ON(entity->use_count < 0);
 356
 357        /* Apply power change to connected non-nodes. */
 358        ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
 359        if (ret < 0)
 360                entity->use_count -= change;
 361
 362        mutex_unlock(&mdev->graph_mutex);
 363
 364        return ret;
 365}
 366EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
 367
 368int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
 369                              unsigned int notification)
 370{
 371        struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk;
 372        struct media_entity *source = link->source->entity;
 373        struct media_entity *sink = link->sink->entity;
 374        int source_use;
 375        int sink_use;
 376        int ret = 0;
 377
 378        source_use = pipeline_pm_use_count(source, graph);
 379        sink_use = pipeline_pm_use_count(sink, graph);
 380
 381        if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
 382            !(flags & MEDIA_LNK_FL_ENABLED)) {
 383                /* Powering off entities is assumed to never fail. */
 384                pipeline_pm_power(source, -sink_use, graph);
 385                pipeline_pm_power(sink, -source_use, graph);
 386                return 0;
 387        }
 388
 389        if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
 390                (flags & MEDIA_LNK_FL_ENABLED)) {
 391
 392                ret = pipeline_pm_power(source, sink_use, graph);
 393                if (ret < 0)
 394                        return ret;
 395
 396                ret = pipeline_pm_power(sink, source_use, graph);
 397                if (ret < 0)
 398                        pipeline_pm_power(source, -sink_use, graph);
 399        }
 400
 401        return ret;
 402}
 403EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
 404