615 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/*4 * Media Controller ancillary functions5 *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 Corporation9 * 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, 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 is73 * connected directly to the I/O entity. All dirty details, like74 * scaler and crop HW are hidden. While such mapping is not enough75 * for mc-centric hardware, it is enough for v4l2 interface centric76 * 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}279EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);280 281int 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;295end:296 mutex_unlock(&mdev->graph_mutex);297 return ret;298}299EXPORT_SYMBOL_GPL(v4l_enable_media_source);300 301void 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}312EXPORT_SYMBOL_GPL(v4l_disable_media_source);313 314int 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}322EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);323 324int 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(dev_fwnode(src_sd->dev), 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 sink372 * entity, and the sink endpoint corresponds to the sink373 * pad requested, so we have found an endpoint connection374 * 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}407EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad);408 409int 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}428EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links);429 430/* -----------------------------------------------------------------------------431 * Pipeline power management432 *433 * Entities must be powered up when part of a pipeline that contains at least434 * 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 stores438 * the users count of the node. For entities corresponding to subdevs the439 * use_count field stores the total number of users of all video device nodes440 * in the pipeline.441 *442 * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and443 * close() handlers of video device nodes. It increments or decrements the use444 * count of all subdev entities in the pipeline.445 *446 * To react to link management on powered pipelines, the link setup notification447 * callback updates the use count of all entities in the source and sink sides448 * of the link.449 */450 451/*452 * pipeline_pm_use_count - Count the number of users of a pipeline453 * @entity: The entity454 *455 * Return the total number of users of all video device nodes in the pipeline.456 */457static 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 entity474 * @entity: The entity475 * @change: Use count change476 *477 * Change the entity use count by @change. If the entity is a subdev update its478 * power state by calling the core::s_power operation when the use count goes479 * from 0 to != 0 or from != 0 to 0.480 *481 * Return 0 on success or a negative error code on failure.482 */483static 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 pipeline508 * @entity: The entity509 * @change: Use count change510 *511 * Walk the pipeline to update the use count and the power state of all non-node512 * entities.513 *514 * Return 0 on success or a negative error code on failure.515 */516static 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 544static 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 566int v4l2_pipeline_pm_get(struct media_entity *entity)567{568 return v4l2_pipeline_pm_use(entity, 1);569}570EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get);571 572void 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}577EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put);578 579int 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}614EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);615