linux/drivers/media/v4l2-core/v4l2-mc.c
<<
>>
Prefs
   1/*
   2 * Media Controller ancillary functions
   3 *
   4 * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com>
   5 * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
   6 * Copyright (C) 2006-2010 Nokia Corporation
   7 * Copyright (c) 2016 Intel Corporation.
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; either version 2 of the License, or
  12 *  (at your option) any later version.
  13 *
  14 *  This program is distributed in the hope that it will be useful,
  15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 *  GNU General Public License for more details.
  18 */
  19
  20#include <linux/module.h>
  21#include <linux/pci.h>
  22#include <linux/usb.h>
  23#include <media/media-device.h>
  24#include <media/media-entity.h>
  25#include <media/v4l2-fh.h>
  26#include <media/v4l2-mc.h>
  27#include <media/v4l2-subdev.h>
  28#include <media/media-device.h>
  29#include <media/v4l2-mc.h>
  30#include <media/videobuf2-core.h>
  31
  32int v4l2_mc_create_media_graph(struct media_device *mdev)
  33
  34{
  35        struct media_entity *entity;
  36        struct media_entity *if_vid = NULL, *if_aud = NULL;
  37        struct media_entity *tuner = NULL, *decoder = NULL;
  38        struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
  39        bool is_webcam = false;
  40        u32 flags;
  41        int ret;
  42
  43        if (!mdev)
  44                return 0;
  45
  46        media_device_for_each_entity(entity, mdev) {
  47                switch (entity->function) {
  48                case MEDIA_ENT_F_IF_VID_DECODER:
  49                        if_vid = entity;
  50                        break;
  51                case MEDIA_ENT_F_IF_AUD_DECODER:
  52                        if_aud = entity;
  53                        break;
  54                case MEDIA_ENT_F_TUNER:
  55                        tuner = entity;
  56                        break;
  57                case MEDIA_ENT_F_ATV_DECODER:
  58                        decoder = entity;
  59                        break;
  60                case MEDIA_ENT_F_IO_V4L:
  61                        io_v4l = entity;
  62                        break;
  63                case MEDIA_ENT_F_IO_VBI:
  64                        io_vbi = entity;
  65                        break;
  66                case MEDIA_ENT_F_IO_SWRADIO:
  67                        io_swradio = entity;
  68                        break;
  69                case MEDIA_ENT_F_CAM_SENSOR:
  70                        is_webcam = true;
  71                        break;
  72                }
  73        }
  74
  75        /* It should have at least one I/O entity */
  76        if (!io_v4l && !io_vbi && !io_swradio)
  77                return -EINVAL;
  78
  79        /*
  80         * Here, webcams are modelled on a very simple way: the sensor is
  81         * connected directly to the I/O entity. All dirty details, like
  82         * scaler and crop HW are hidden. While such mapping is not enough
  83         * for mc-centric hardware, it is enough for v4l2 interface centric
  84         * PC-consumer's hardware.
  85         */
  86        if (is_webcam) {
  87                if (!io_v4l)
  88                        return -EINVAL;
  89
  90                media_device_for_each_entity(entity, mdev) {
  91                        if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
  92                                continue;
  93                        ret = media_create_pad_link(entity, 0,
  94                                                    io_v4l, 0,
  95                                                    MEDIA_LNK_FL_ENABLED);
  96                        if (ret)
  97                                return ret;
  98                }
  99                if (!decoder)
 100                        return 0;
 101        }
 102
 103        /* The device isn't a webcam. So, it should have a decoder */
 104        if (!decoder)
 105                return -EINVAL;
 106
 107        /* Link the tuner and IF video output pads */
 108        if (tuner) {
 109                if (if_vid) {
 110                        ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
 111                                                    if_vid,
 112                                                    IF_VID_DEC_PAD_IF_INPUT,
 113                                                    MEDIA_LNK_FL_ENABLED);
 114                        if (ret)
 115                                return ret;
 116                        ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
 117                                                decoder, DEMOD_PAD_IF_INPUT,
 118                                                MEDIA_LNK_FL_ENABLED);
 119                        if (ret)
 120                                return ret;
 121                } else {
 122                        ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
 123                                                decoder, DEMOD_PAD_IF_INPUT,
 124                                                MEDIA_LNK_FL_ENABLED);
 125                        if (ret)
 126                                return ret;
 127                }
 128
 129                if (if_aud) {
 130                        ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
 131                                                    if_aud,
 132                                                    IF_AUD_DEC_PAD_IF_INPUT,
 133                                                    MEDIA_LNK_FL_ENABLED);
 134                        if (ret)
 135                                return ret;
 136                } else {
 137                        if_aud = tuner;
 138                }
 139
 140        }
 141
 142        /* Create demod to V4L, VBI and SDR radio links */
 143        if (io_v4l) {
 144                ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
 145                                        io_v4l, 0,
 146                                        MEDIA_LNK_FL_ENABLED);
 147                if (ret)
 148                        return ret;
 149        }
 150
 151        if (io_swradio) {
 152                ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
 153                                        io_swradio, 0,
 154                                        MEDIA_LNK_FL_ENABLED);
 155                if (ret)
 156                        return ret;
 157        }
 158
 159        if (io_vbi) {
 160                ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
 161                                            io_vbi, 0,
 162                                            MEDIA_LNK_FL_ENABLED);
 163                if (ret)
 164                        return ret;
 165        }
 166
 167        /* Create links for the media connectors */
 168        flags = MEDIA_LNK_FL_ENABLED;
 169        media_device_for_each_entity(entity, mdev) {
 170                switch (entity->function) {
 171                case MEDIA_ENT_F_CONN_RF:
 172                        if (!tuner)
 173                                continue;
 174
 175                        ret = media_create_pad_link(entity, 0, tuner,
 176                                                    TUNER_PAD_RF_INPUT,
 177                                                    flags);
 178                        break;
 179                case MEDIA_ENT_F_CONN_SVIDEO:
 180                case MEDIA_ENT_F_CONN_COMPOSITE:
 181                        ret = media_create_pad_link(entity, 0, decoder,
 182                                                    DEMOD_PAD_IF_INPUT,
 183                                                    flags);
 184                        break;
 185                default:
 186                        continue;
 187                }
 188                if (ret)
 189                        return ret;
 190
 191                flags = 0;
 192        }
 193
 194        return 0;
 195}
 196EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
 197
 198int v4l_enable_media_source(struct video_device *vdev)
 199{
 200        struct media_device *mdev = vdev->entity.graph_obj.mdev;
 201        int ret;
 202
 203        if (!mdev || !mdev->enable_source)
 204                return 0;
 205        ret = mdev->enable_source(&vdev->entity, &vdev->pipe);
 206        if (ret)
 207                return -EBUSY;
 208        return 0;
 209}
 210EXPORT_SYMBOL_GPL(v4l_enable_media_source);
 211
 212void v4l_disable_media_source(struct video_device *vdev)
 213{
 214        struct media_device *mdev = vdev->entity.graph_obj.mdev;
 215
 216        if (mdev && mdev->disable_source)
 217                mdev->disable_source(&vdev->entity);
 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_entity_graph *graph)
 260{
 261        int use = 0;
 262
 263        media_entity_graph_walk_start(graph, entity);
 264
 265        while ((entity = media_entity_graph_walk_next(graph))) {
 266                if (is_media_entity_v4l2_io(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_entity_graph *graph)
 319{
 320        struct media_entity *first = entity;
 321        int ret = 0;
 322
 323        if (!change)
 324                return 0;
 325
 326        media_entity_graph_walk_start(graph, entity);
 327
 328        while (!ret && (entity = media_entity_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_entity_graph_walk_start(graph, first);
 336
 337        while ((first = media_entity_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_entity_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