OSDN Git Service

media: imx: capture: Decouple video node from source with MC-centric API
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 15 Feb 2021 04:26:51 +0000 (05:26 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Thu, 11 Mar 2021 10:59:49 +0000 (11:59 +0100)
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: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Rui Miguel Silva <rmfrfs@gmail.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/staging/media/imx/imx-media-capture.c

index 700e99d..99133b7 100644 (file)
@@ -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,10 +875,17 @@ 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 (ret) {
-               dev_err(priv->dev, "failed to get source format\n");
-               return ret;
+       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);
@@ -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;