diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-mc.c')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-mc.c | 297 |
1 files changed, 249 insertions, 48 deletions
diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 303980b71aae..937d358697e1 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Media Controller ancillary functions * @@ -5,16 +7,6 @@ * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com> * Copyright (C) 2006-2010 Nokia Corporation * Copyright (c) 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> @@ -25,8 +17,6 @@ #include <media/v4l2-fh.h> #include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> -#include <media/media-device.h> -#include <media/v4l2-mc.h> #include <media/videobuf2-core.h> int v4l2_mc_create_media_graph(struct media_device *mdev) @@ -38,7 +28,7 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL; bool is_webcam = false; u32 flags; - int ret; + int ret, pad_sink, pad_source; if (!mdev) return 0; @@ -73,8 +63,10 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) } /* It should have at least one I/O entity */ - if (!io_v4l && !io_vbi && !io_swradio) + if (!io_v4l && !io_vbi && !io_swradio) { + dev_warn(mdev->dev, "Didn't find any I/O entity\n"); return -EINVAL; + } /* * Here, webcams are modelled on a very simple way: the sensor is @@ -84,8 +76,10 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) * PC-consumer's hardware. */ if (is_webcam) { - if (!io_v4l) + if (!io_v4l) { + dev_warn(mdev->dev, "Didn't find a MEDIA_ENT_F_IO_V4L\n"); return -EINVAL; + } media_device_for_each_entity(entity, mdev) { if (entity->function != MEDIA_ENT_F_CAM_SENSOR) @@ -93,46 +87,99 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) ret = media_create_pad_link(entity, 0, io_v4l, 0, MEDIA_LNK_FL_ENABLED); - if (ret) + if (ret) { + dev_warn(mdev->dev, "Failed to create a sensor link\n"); return ret; + } } if (!decoder) return 0; } /* The device isn't a webcam. So, it should have a decoder */ - if (!decoder) + if (!decoder) { + dev_warn(mdev->dev, "Decoder not found\n"); return -EINVAL; + } /* Link the tuner and IF video output pads */ if (tuner) { if (if_vid) { - ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, - if_vid, - IF_VID_DEC_PAD_IF_INPUT, + pad_source = media_get_pad_index(tuner, + MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_ANALOG); + pad_sink = media_get_pad_index(if_vid, + MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_source < 0 || pad_sink < 0) { + dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n", + pad_source, pad_sink); + return -EINVAL; + } + ret = media_create_pad_link(tuner, pad_source, + if_vid, pad_sink, MEDIA_LNK_FL_ENABLED); - if (ret) + if (ret) { + dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n"); return ret; - ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT, - decoder, DEMOD_PAD_IF_INPUT, - MEDIA_LNK_FL_ENABLED); - if (ret) + } + + pad_source = media_get_pad_index(if_vid, + MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_ANALOG); + pad_sink = media_get_pad_index(decoder, + MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_source < 0 || pad_sink < 0) { + dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n", + pad_source, pad_sink); + return -EINVAL; + } + ret = media_create_pad_link(if_vid, pad_source, + decoder, pad_sink, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_warn(mdev->dev, "couldn't link PLL to decoder\n"); return ret; + } } else { - ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, - decoder, DEMOD_PAD_IF_INPUT, - MEDIA_LNK_FL_ENABLED); + pad_source = media_get_pad_index(tuner, + MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_ANALOG); + pad_sink = media_get_pad_index(decoder, + MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_source < 0 || pad_sink < 0) { + dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n", + pad_source, pad_sink); + return -EINVAL; + } + ret = media_create_pad_link(tuner, pad_source, + decoder, pad_sink, + MEDIA_LNK_FL_ENABLED); if (ret) return ret; } if (if_aud) { - ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT, - if_aud, - IF_AUD_DEC_PAD_IF_INPUT, + pad_source = media_get_pad_index(tuner, + MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_AUDIO); + pad_sink = media_get_pad_index(if_aud, + MEDIA_PAD_FL_SINK, + PAD_SIGNAL_AUDIO); + if (pad_source < 0 || pad_sink < 0) { + dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n", + pad_source, pad_sink); + return -EINVAL; + } + ret = media_create_pad_link(tuner, pad_source, + if_aud, pad_sink, MEDIA_LNK_FL_ENABLED); - if (ret) + if (ret) { + dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n"); return ret; + } } else { if_aud = tuner; } @@ -141,27 +188,51 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) /* Create demod to V4L, VBI and SDR radio links */ if (io_v4l) { - ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, - io_v4l, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) + pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_DV); + if (pad_source < 0) { + dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n"); + return -EINVAL; + } + ret = media_create_pad_link(decoder, pad_source, + io_v4l, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n"); return ret; + } } if (io_swradio) { - ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, - io_swradio, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) + pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_DV); + if (pad_source < 0) { + dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n"); + return -EINVAL; + } + ret = media_create_pad_link(decoder, pad_source, + io_swradio, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_warn(mdev->dev, "couldn't link decoder output to SDR\n"); return ret; + } } if (io_vbi) { - ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT, + pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_DV); + if (pad_source < 0) { + dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n"); + return -EINVAL; + } + ret = media_create_pad_link(decoder, pad_source, io_vbi, 0, MEDIA_LNK_FL_ENABLED); - if (ret) + if (ret) { + dev_warn(mdev->dev, "couldn't link decoder output to VBI\n"); return ret; + } } /* Create links for the media connectors */ @@ -171,15 +242,27 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) case MEDIA_ENT_F_CONN_RF: if (!tuner) continue; - + pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_sink < 0) { + dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n"); + return -EINVAL; + } ret = media_create_pad_link(entity, 0, tuner, - TUNER_PAD_RF_INPUT, + pad_sink, flags); break; case MEDIA_ENT_F_CONN_SVIDEO: case MEDIA_ENT_F_CONN_COMPOSITE: + pad_sink = media_get_pad_index(decoder, + MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_sink < 0) { + dev_warn(mdev->dev, "couldn't get decoder analog pad sink\n"); + return -EINVAL; + } ret = media_create_pad_link(entity, 0, decoder, - DEMOD_PAD_IF_INPUT, + pad_sink, flags); break; default: @@ -238,6 +321,112 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); +int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, + struct media_pad *sink, u32 flags) +{ + struct fwnode_handle *endpoint; + + if (!(sink->flags & MEDIA_PAD_FL_SINK)) + return -EINVAL; + + fwnode_graph_for_each_endpoint(src_sd->fwnode, endpoint) { + struct fwnode_handle *remote_ep; + int src_idx, sink_idx, ret; + struct media_pad *src; + + src_idx = media_entity_get_fwnode_pad(&src_sd->entity, + endpoint, + MEDIA_PAD_FL_SOURCE); + if (src_idx < 0) { + dev_dbg(src_sd->dev, "no source pad found for %pfw\n", + endpoint); + continue; + } + + remote_ep = fwnode_graph_get_remote_endpoint(endpoint); + if (!remote_ep) { + dev_dbg(src_sd->dev, "no remote ep found for %pfw\n", + endpoint); + continue; + } + + /* + * ask the sink to verify it owns the remote endpoint, + * and translate to a sink pad. + */ + sink_idx = media_entity_get_fwnode_pad(sink->entity, + remote_ep, + MEDIA_PAD_FL_SINK); + fwnode_handle_put(remote_ep); + + if (sink_idx < 0 || sink_idx != sink->index) { + dev_dbg(src_sd->dev, + "sink pad index mismatch or error (is %d, expected %u)\n", + sink_idx, sink->index); + continue; + } + + /* + * the source endpoint corresponds to one of its source pads, + * the source endpoint connects to an endpoint at the sink + * entity, and the sink endpoint corresponds to the sink + * pad requested, so we have found an endpoint connection + * that works, create the media link for it. + */ + + src = &src_sd->entity.pads[src_idx]; + + /* skip if link already exists */ + if (media_entity_find_link(src, sink)) { + dev_dbg(src_sd->dev, + "link %s:%d -> %s:%d already exists\n", + src_sd->entity.name, src_idx, + sink->entity->name, sink_idx); + continue; + } + + dev_dbg(src_sd->dev, "creating link %s:%d -> %s:%d\n", + src_sd->entity.name, src_idx, + sink->entity->name, sink_idx); + + ret = media_create_pad_link(&src_sd->entity, src_idx, + sink->entity, sink_idx, flags); + if (ret) { + dev_err(src_sd->dev, + "link %s:%d -> %s:%d failed with %d\n", + src_sd->entity.name, src_idx, + sink->entity->name, sink_idx, ret); + + fwnode_handle_put(endpoint); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad); + +int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd, + struct v4l2_subdev *sink_sd) +{ + unsigned int i; + + for (i = 0; i < sink_sd->entity.num_pads; i++) { + struct media_pad *pad = &sink_sd->entity.pads[i]; + int ret; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + continue; + + ret = v4l2_create_fwnode_links_to_pad(src_sd, pad, 0); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links); + /* ----------------------------------------------------------------------------- * Pipeline power management * @@ -250,7 +439,7 @@ EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); * use_count field stores the total number of users of all video device nodes * in the pipeline. * - * The v4l2_pipeline_pm_use() function must be called in the open() and + * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and * close() handlers of video device nodes. It increments or decrements the use * count of all subdev entities in the pipeline. * @@ -352,7 +541,7 @@ static int pipeline_pm_power(struct media_entity *entity, int change, return ret; } -int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use) { struct media_device *mdev = entity->graph_obj.mdev; int change = use ? 1 : -1; @@ -373,7 +562,19 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use) return ret; } -EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use); + +int v4l2_pipeline_pm_get(struct media_entity *entity) +{ + return v4l2_pipeline_pm_use(entity, 1); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get); + +void v4l2_pipeline_pm_put(struct media_entity *entity) +{ + /* Powering off entities shouldn't fail. */ + WARN_ON(v4l2_pipeline_pm_use(entity, 0)); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put); int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) |
