aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSowjanya Komatineni2020-08-12 02:27:18 +0200
committerMauro Carvalho Chehab2020-08-28 15:12:07 +0200
commitbdcad5ce6dde6e4f0767aef7b63c164b9435bd8c (patch)
tree3347f9a70e266cad76a779a7480bd8dc70b19daf
parent1ebaeb09830f36c1111b72a95420814225bd761c (diff)
media: tegra-video: Add support for selection ioctl ops
This patch adds selection v4l2 ioctl operations to allow configuring a selection rectangle in the sensor through the Tegra video device node. Some sensor drivers supporting crop uses try_crop rectangle from v4l2_subdev_pad_config during try format for computing binning. So with selection ops support, this patch also updates try format to use try crop rectangle either from subdev frame size enumeration or from subdev crop boundary. Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-rw-r--r--drivers/staging/media/tegra-video/vi.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 48dfcf575214..b4c3ac1a9993 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -474,6 +474,13 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
struct v4l2_subdev *subdev;
struct v4l2_subdev_format fmt;
struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_subdev_selection sdsel = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .target = V4L2_SEL_TGT_CROP_BOUNDS,
+ };
int ret;
subdev = tegra_channel_get_remote_source_subdev(chan);
@@ -499,6 +506,24 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
fmt.which = V4L2_SUBDEV_FORMAT_TRY;
fmt.pad = 0;
v4l2_fill_mbus_format(&fmt.format, pix, fmtinfo->code);
+
+ /*
+ * Attempt to obtain the format size from subdev.
+ * If not available, try to get crop boundary from subdev.
+ */
+ fse.code = fmtinfo->code;
+ ret = v4l2_subdev_call(subdev, pad, enum_frame_size, pad_cfg, &fse);
+ if (ret) {
+ ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel);
+ if (ret)
+ return -EINVAL;
+ pad_cfg->try_crop.width = sdsel.r.width;
+ pad_cfg->try_crop.height = sdsel.r.height;
+ } else {
+ pad_cfg->try_crop.width = fse.max_width;
+ pad_cfg->try_crop.height = fse.max_height;
+ }
+
ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt);
if (ret < 0)
return ret;
@@ -588,6 +613,85 @@ static int tegra_channel_set_subdev_active_fmt(struct tegra_vi_channel *chan)
return 0;
}
+static int tegra_channel_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *sel)
+{
+ struct tegra_vi_channel *chan = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_subdev_selection sdsel = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .target = sel->target,
+ };
+ int ret;
+
+ subdev = tegra_channel_get_remote_source_subdev(chan);
+ if (!v4l2_subdev_has_op(subdev, pad, get_selection))
+ return -ENOTTY;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ /*
+ * Try the get selection operation and fallback to get format if not
+ * implemented.
+ */
+ ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel);
+ if (!ret)
+ sel->r = sdsel.r;
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = fmt.format.width;
+ sel->r.height = fmt.format.height;
+
+ return 0;
+}
+
+static int tegra_channel_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct tegra_vi_channel *chan = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+ struct v4l2_subdev_selection sdsel = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .target = sel->target,
+ .flags = sel->flags,
+ .r = sel->r,
+ };
+
+ subdev = tegra_channel_get_remote_source_subdev(chan);
+ if (!v4l2_subdev_has_op(subdev, pad, set_selection))
+ return -ENOTTY;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (vb2_is_busy(&chan->queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel);
+ if (!ret) {
+ sel->r = sdsel.r;
+ /*
+ * Subdev active format resolution may have changed during
+ * set selection operation. So, update channel format to
+ * the sub-device active format.
+ */
+ return tegra_channel_set_subdev_active_fmt(chan);
+ }
+
+ return ret;
+}
+
static int tegra_channel_enum_input(struct file *file, void *fh,
struct v4l2_input *inp)
{
@@ -645,6 +749,8 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = {
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_selection = tegra_channel_g_selection,
+ .vidioc_s_selection = tegra_channel_s_selection,
};
/*