Commit b41e9326 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

media: imx: capture: Decouple video node from source with MC-centric API



When operating in MC-centric mode, the behaviour of video nodes shall
not be influenced by the active configuration of the source subdev. Add
a set of ioctl handlers that implement this mode, and select them when
support for the legacy API is not requested.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: default avatarRui Miguel Silva <rmfrfs@gmail.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 5e14568c
Loading
Loading
Loading
Loading
+155 −6
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ struct capture_priv {
#define VID_MEM_LIMIT	SZ_64M

/* -----------------------------------------------------------------------------
 * Common Video IOCTLs
 * MC-Centric Video IOCTLs
 */

static const struct imx_media_pixfmt *capture_find_format(u32 code, u32 fourcc)
@@ -92,6 +92,41 @@ static int capture_querycap(struct file *file, void *fh,
	return 0;
}

static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
				    struct v4l2_fmtdesc *f)
{
	return imx_media_enum_pixel_formats(&f->pixelformat, f->index,
					    PIXFMT_SEL_ANY, 0);
}

static int capture_enum_framesizes(struct file *file, void *fh,
				   struct v4l2_frmsizeenum *fsize)
{
	const struct imx_media_pixfmt *cc;

	if (fsize->index > 0)
		return -EINVAL;

	cc = imx_media_find_pixel_format(fsize->pixel_format, PIXFMT_SEL_ANY);
	if (!cc)
		return -EINVAL;

	/*
	 * TODO: The constraints are hardware-specific and may depend on the
	 * pixel format. This should come from the driver using
	 * imx_media_capture.
	 */
	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
	fsize->stepwise.min_width = 1;
	fsize->stepwise.max_width = 65535;
	fsize->stepwise.min_height = 1;
	fsize->stepwise.max_height = 65535;
	fsize->stepwise.step_width = 1;
	fsize->stepwise.step_height = 1;

	return 0;
}

static int capture_g_fmt_vid_cap(struct file *file, void *fh,
				 struct v4l2_format *f)
{
@@ -102,6 +137,75 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh,
	return 0;
}

static const struct imx_media_pixfmt *
__capture_try_fmt(struct v4l2_pix_format *pixfmt, struct v4l2_rect *compose)
{
	struct v4l2_mbus_framefmt fmt_src;
	const struct imx_media_pixfmt *cc;

	/*
	 * Find the pixel format, default to the first supported format if not
	 * found.
	 */
	cc = imx_media_find_pixel_format(pixfmt->pixelformat, PIXFMT_SEL_ANY);
	if (!cc) {
		imx_media_enum_pixel_formats(&pixfmt->pixelformat, 0,
					     PIXFMT_SEL_ANY, 0);
		cc = imx_media_find_pixel_format(pixfmt->pixelformat,
						 PIXFMT_SEL_ANY);
	}

	/* Allow IDMAC interweave but enforce field order from source. */
	if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) {
		switch (pixfmt->field) {
		case V4L2_FIELD_SEQ_TB:
			pixfmt->field = V4L2_FIELD_INTERLACED_TB;
			break;
		case V4L2_FIELD_SEQ_BT:
			pixfmt->field = V4L2_FIELD_INTERLACED_BT;
			break;
		default:
			break;
		}
	}

	v4l2_fill_mbus_format(&fmt_src, pixfmt, 0);
	imx_media_mbus_fmt_to_pix_fmt(pixfmt, &fmt_src, cc);

	if (compose) {
		compose->width = fmt_src.width;
		compose->height = fmt_src.height;
	}

	return cc;
}

static int capture_try_fmt_vid_cap(struct file *file, void *fh,
				   struct v4l2_format *f)
{
	__capture_try_fmt(&f->fmt.pix, NULL);
	return 0;
}

static int capture_s_fmt_vid_cap(struct file *file, void *fh,
				 struct v4l2_format *f)
{
	struct capture_priv *priv = video_drvdata(file);
	const struct imx_media_pixfmt *cc;

	if (vb2_is_busy(&priv->q)) {
		dev_err(priv->dev, "%s queue busy\n", __func__);
		return -EBUSY;
	}

	cc = __capture_try_fmt(&f->fmt.pix, &priv->vdev.compose);

	priv->vdev.cc = cc;
	priv->vdev.fmt = f->fmt.pix;

	return 0;
}

static int capture_g_selection(struct file *file, void *fh,
			       struct v4l2_selection *s)
{
@@ -132,6 +236,43 @@ static int capture_g_selection(struct file *file, void *fh,
	return 0;
}

static int capture_subscribe_event(struct v4l2_fh *fh,
				   const struct v4l2_event_subscription *sub)
{
	switch (sub->type) {
	case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR:
		return v4l2_event_subscribe(fh, sub, 0, NULL);
	default:
		return -EINVAL;
	}
}

static const struct v4l2_ioctl_ops capture_ioctl_ops = {
	.vidioc_querycap		= capture_querycap,

	.vidioc_enum_fmt_vid_cap	= capture_enum_fmt_vid_cap,
	.vidioc_enum_framesizes		= capture_enum_framesizes,

	.vidioc_g_fmt_vid_cap		= capture_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap		= capture_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap		= capture_s_fmt_vid_cap,

	.vidioc_g_selection		= capture_g_selection,

	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
	.vidioc_querybuf		= vb2_ioctl_querybuf,
	.vidioc_qbuf			= vb2_ioctl_qbuf,
	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
	.vidioc_expbuf			= vb2_ioctl_expbuf,
	.vidioc_streamon		= vb2_ioctl_streamon,
	.vidioc_streamoff		= vb2_ioctl_streamoff,

	.vidioc_subscribe_event		= capture_subscribe_event,
	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
};

/* -----------------------------------------------------------------------------
 * Legacy Video IOCTLs
 */
@@ -734,11 +875,18 @@ static int capture_init_format(struct capture_priv *priv)
	struct imx_media_video_dev *vdev = &priv->vdev;
	int ret;

	ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
	if (priv->legacy_api) {
		ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL,
				       &fmt_src);
		if (ret) {
			dev_err(priv->dev, "failed to get source format\n");
			return ret;
		}
	} else {
		fmt_src.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
		fmt_src.format.width = IMX_MEDIA_DEF_PIX_WIDTH;
		fmt_src.format.height = IMX_MEDIA_DEF_PIX_HEIGHT;
	}

	imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt, &fmt_src.format, NULL);
	vdev->compose.width = fmt_src.format.width;
@@ -832,7 +980,8 @@ imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd,
		return ERR_PTR(-ENOMEM);

	vfd->fops = &capture_fops;
	vfd->ioctl_ops = &capture_legacy_ioctl_ops;
	vfd->ioctl_ops = legacy_api ? &capture_legacy_ioctl_ops
		       : &capture_ioctl_ops;
	vfd->minor = -1;
	vfd->release = video_device_release;
	vfd->vfl_dir = VFL_DIR_RX;