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

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
...@@ -55,7 +55,7 @@ struct capture_priv { ...@@ -55,7 +55,7 @@ struct capture_priv {
#define VID_MEM_LIMIT SZ_64M #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) 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, ...@@ -92,6 +92,41 @@ static int capture_querycap(struct file *file, void *fh,
return 0; 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, static int capture_g_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *f) struct v4l2_format *f)
{ {
...@@ -102,6 +137,75 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, ...@@ -102,6 +137,75 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh,
return 0; 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, static int capture_g_selection(struct file *file, void *fh,
struct v4l2_selection *s) struct v4l2_selection *s)
{ {
...@@ -132,6 +236,43 @@ static int capture_g_selection(struct file *file, void *fh, ...@@ -132,6 +236,43 @@ static int capture_g_selection(struct file *file, void *fh,
return 0; 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 * Legacy Video IOCTLs
*/ */
...@@ -734,10 +875,17 @@ static int capture_init_format(struct capture_priv *priv) ...@@ -734,10 +875,17 @@ static int capture_init_format(struct capture_priv *priv)
struct imx_media_video_dev *vdev = &priv->vdev; struct imx_media_video_dev *vdev = &priv->vdev;
int ret; int ret;
ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); if (priv->legacy_api) {
if (ret) { ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL,
dev_err(priv->dev, "failed to get source format\n"); &fmt_src);
return ret; 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); imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt, &fmt_src.format, NULL);
...@@ -832,7 +980,8 @@ imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd, ...@@ -832,7 +980,8 @@ imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
vfd->fops = &capture_fops; 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->minor = -1;
vfd->release = video_device_release; vfd->release = video_device_release;
vfd->vfl_dir = VFL_DIR_RX; vfd->vfl_dir = VFL_DIR_RX;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment