]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
media: dw100: Implement V4L2 requests support
authorStefan Klug <stefan.klug@ideasonboard.com>
Wed, 4 Mar 2026 15:50:22 +0000 (16:50 +0100)
committerHans Verkuil <hverkuil+cisco@kernel.org>
Tue, 24 Mar 2026 21:14:44 +0000 (22:14 +0100)
The dw100 dewarper hardware present on the NXP i.MX8MP allows very
flexible dewarping using a freely configurable vertex map. Aside from
lens dewarping the vertex map can be used to implement things like
arbitrary zoom, pan and rotation. The current driver supports setting
that vertex map before calling VIDIOC_STREAMON.

To control above mentioned features during streaming it is necessary to
update the vertex map dynamically. To do that in a race free manner V4L2
requests support is required. Add V4L2 requests support to prepare for
dynamic vertex map updates.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patch.msgid.link/20260304-sklug-v6-16-topic-dw100-v3-1-dev-v5-1-1a7e1f721b50@ideasonboard.com
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
drivers/media/platform/nxp/dw100/dw100.c

index bdebbe3f4198542d7a49ad131fed34e7a2e24bfd..2c5d0e1657c8accdea58d797d1c8bc241ad8ef1a 100644 (file)
@@ -459,6 +459,15 @@ static int dw100_queue_setup(struct vb2_queue *vq,
        return 0;
 }
 
+static int dw100_buf_out_validate(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+       vbuf->field = V4L2_FIELD_NONE;
+
+       return 0;
+}
+
 static int dw100_buf_prepare(struct vb2_buffer *vb)
 {
        unsigned int i;
@@ -500,6 +509,13 @@ static void dw100_buf_queue(struct vb2_buffer *vb)
        v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
 }
 
+static void dw100_buf_request_complete(struct vb2_buffer *vb)
+{
+       struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+       v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
 static void dw100_return_all_buffers(struct vb2_queue *q,
                                     enum vb2_buffer_state state)
 {
@@ -553,11 +569,13 @@ static void dw100_stop_streaming(struct vb2_queue *q)
 }
 
 static const struct vb2_ops dw100_qops = {
-       .queue_setup     = dw100_queue_setup,
-       .buf_prepare     = dw100_buf_prepare,
-       .buf_queue       = dw100_buf_queue,
-       .start_streaming = dw100_start_streaming,
-       .stop_streaming  = dw100_stop_streaming,
+       .queue_setup          = dw100_queue_setup,
+       .buf_out_validate     = dw100_buf_out_validate,
+       .buf_prepare          = dw100_buf_prepare,
+       .buf_queue            = dw100_buf_queue,
+       .start_streaming      = dw100_start_streaming,
+       .stop_streaming       = dw100_stop_streaming,
+       .buf_request_complete = dw100_buf_request_complete,
 };
 
 static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -575,6 +593,7 @@ static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->vq_mutex;
        src_vq->dev = ctx->dw_dev->v4l2_dev.dev;
+       src_vq->supports_requests = true;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -1058,7 +1077,6 @@ static const struct v4l2_ioctl_ops dw100_ioctl_ops = {
 static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error)
 {
        struct dw100_ctx *curr_ctx;
-       struct vb2_v4l2_buffer *src_vb, *dst_vb;
        enum vb2_buffer_state buf_state;
 
        curr_ctx = v4l2_m2m_get_curr_priv(dw_dev->m2m_dev);
@@ -1069,21 +1087,16 @@ static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error)
                return;
        }
 
-       src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
-       dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
-
        if (likely(!with_error))
                buf_state = VB2_BUF_STATE_DONE;
        else
                buf_state = VB2_BUF_STATE_ERROR;
 
-       v4l2_m2m_buf_done(src_vb, buf_state);
-       v4l2_m2m_buf_done(dst_vb, buf_state);
-
        dev_dbg(&dw_dev->pdev->dev, "Finishing transaction with%s error(s)\n",
                with_error ? "" : "out");
 
-       v4l2_m2m_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
+       v4l2_m2m_buf_done_and_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx,
+                                        buf_state);
 }
 
 static void dw100_hw_reset(struct dw100_device *dw_dev)
@@ -1460,6 +1473,16 @@ static void dw100_device_run(void *priv)
        src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
        dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
 
+       v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
+                               &ctx->hdl);
+
+       /*
+        * As the hardware does not update any volatile controls, we can
+        * complete control handling before starting the dewarper.
+        */
+       v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
+                                  &ctx->hdl);
+
        dw100_start(ctx, src_buf, dst_buf);
 }
 
@@ -1467,6 +1490,11 @@ static const struct v4l2_m2m_ops dw100_m2m_ops = {
        .device_run     = dw100_device_run,
 };
 
+static const struct media_device_ops dw100_m2m_media_ops = {
+       .req_validate = vb2_request_validate,
+       .req_queue = v4l2_m2m_request_queue,
+};
+
 static struct video_device *dw100_init_video_device(struct dw100_device *dw_dev)
 {
        struct video_device *vfd = &dw_dev->vfd;
@@ -1578,6 +1606,7 @@ static int dw100_probe(struct platform_device *pdev)
        dw_dev->mdev.dev = &pdev->dev;
        strscpy(dw_dev->mdev.model, "dw100", sizeof(dw_dev->mdev.model));
        media_device_init(&dw_dev->mdev);
+       dw_dev->mdev.ops = &dw100_m2m_media_ops;
        dw_dev->v4l2_dev.mdev = &dw_dev->mdev;
 
        ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);