media: hantro: Implement support for encoder commands
authorChen-Yu Tsai <wenst@chromium.org>
Thu, 31 Mar 2022 09:16:28 +0000 (10:16 +0100)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Sun, 24 Apr 2022 07:52:26 +0000 (08:52 +0100)
The V4L2 stateful encoder uAPI specification requires that drivers
support the ENCODER_CMD ioctl to allow draining of buffers. This
however was not implemented, and causes issues for some userspace
applications.

Implement support for the ENCODER_CMD ioctl using v4l2-mem2mem helpers.
This is entirely based on existing code found in the vicodec test
driver.

Fixes: 775fec69008d ("media: add Rockchip VPU JPEG encoder driver")
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/staging/media/hantro/hantro_drv.c
drivers/staging/media/hantro/hantro_v4l2.c

index 58b9ddd..ac232b5 100644 (file)
@@ -56,6 +56,10 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts)
        return hantro_get_dec_buf_addr(ctx, buf);
 }
 
+static const struct v4l2_event hantro_eos_event = {
+       .type = V4L2_EVENT_EOS
+};
+
 static void hantro_job_finish_no_pm(struct hantro_dev *vpu,
                                    struct hantro_ctx *ctx,
                                    enum vb2_buffer_state result)
@@ -73,6 +77,12 @@ static void hantro_job_finish_no_pm(struct hantro_dev *vpu,
        src->sequence = ctx->sequence_out++;
        dst->sequence = ctx->sequence_cap++;
 
+       if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src)) {
+               dst->flags |= V4L2_BUF_FLAG_LAST;
+               v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event);
+               v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
+       }
+
        v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx,
                                         result);
 }
@@ -810,10 +820,13 @@ static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid)
        snprintf(vfd->name, sizeof(vfd->name), "%s-%s", match->compatible,
                 funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER ? "enc" : "dec");
 
-       if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER)
+       if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) {
                vpu->encoder = func;
-       else
+       } else {
                vpu->decoder = func;
+               v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
+               v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
+       }
 
        video_set_drvdata(vfd, vpu);
 
index 67148ba..8b8276f 100644 (file)
@@ -628,6 +628,38 @@ static int vidioc_s_selection(struct file *file, void *priv,
        return 0;
 }
 
+static const struct v4l2_event hantro_eos_event = {
+       .type = V4L2_EVENT_EOS
+};
+
+static int vidioc_encoder_cmd(struct file *file, void *priv,
+                             struct v4l2_encoder_cmd *ec)
+{
+       struct hantro_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, ec);
+       if (ret < 0)
+               return ret;
+
+       if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) ||
+           !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)))
+               return 0;
+
+       ret = v4l2_m2m_ioctl_encoder_cmd(file, priv, ec);
+       if (ret < 0)
+               return ret;
+
+       if (ec->cmd == V4L2_ENC_CMD_STOP &&
+           v4l2_m2m_has_stopped(ctx->fh.m2m_ctx))
+               v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event);
+
+       if (ec->cmd == V4L2_ENC_CMD_START)
+               vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q);
+
+       return 0;
+}
+
 const struct v4l2_ioctl_ops hantro_ioctl_ops = {
        .vidioc_querycap = vidioc_querycap,
        .vidioc_enum_framesizes = vidioc_enum_framesizes,
@@ -657,6 +689,9 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = {
 
        .vidioc_g_selection = vidioc_g_selection,
        .vidioc_s_selection = vidioc_s_selection,
+
+       .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+       .vidioc_encoder_cmd = vidioc_encoder_cmd,
 };
 
 static int
@@ -744,6 +779,22 @@ static void hantro_buf_queue(struct vb2_buffer *vb)
        struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 
+       if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) &&
+           vb2_is_streaming(vb->vb2_queue) &&
+           v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) {
+               unsigned int i;
+
+               for (i = 0; i < vb->num_planes; i++)
+                       vb2_set_plane_payload(vb, i, 0);
+
+               vbuf->field = V4L2_FIELD_NONE;
+               vbuf->sequence = ctx->sequence_cap++;
+
+               v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf);
+               v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event);
+               return;
+       }
+
        v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
 }
 
@@ -759,6 +810,8 @@ static int hantro_start_streaming(struct vb2_queue *q, unsigned int count)
        struct hantro_ctx *ctx = vb2_get_drv_priv(q);
        int ret = 0;
 
+       v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q);
+
        if (V4L2_TYPE_IS_OUTPUT(q->type))
                ctx->sequence_out = 0;
        else
@@ -831,6 +884,12 @@ static void hantro_stop_streaming(struct vb2_queue *q)
                hantro_return_bufs(q, v4l2_m2m_src_buf_remove);
        else
                hantro_return_bufs(q, v4l2_m2m_dst_buf_remove);
+
+       v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q);
+
+       if (V4L2_TYPE_IS_OUTPUT(q->type) &&
+           v4l2_m2m_has_stopped(ctx->fh.m2m_ctx))
+               v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event);
 }
 
 static void hantro_buf_request_complete(struct vb2_buffer *vb)