From: Nagaraju Siddineni <[email protected]>

- Implement VB2 queue operations for decoding (setup, buffer handling,
  streaming control, multi‑view and DMA direction support).
- Added header exposing decoder VB2 operations to other MFC components.

This introduces a full VB2 backend for video decoding, aligning the driver
with standard V4L2 buffer management.

Signed-off-by: Nagaraju Siddineni <[email protected]>
Signed-off-by: Himanshu Dewangan <[email protected]>
---
 .../platform/samsung/exynos-mfc/Makefile      |   2 +-
 .../platform/samsung/exynos-mfc/mfc_dec_vb2.c | 393 ++++++++++++++++++
 .../platform/samsung/exynos-mfc/mfc_dec_vb2.h |  19 +
 3 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c
 create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h

diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile 
b/drivers/media/platform/samsung/exynos-mfc/Makefile
index 19e38c886255..9127f2dc4df6 100644
--- a/drivers/media/platform/samsung/exynos-mfc/Makefile
+++ b/drivers/media/platform/samsung/exynos-mfc/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_VIDEO_EXYNOS_MFC) := exynos_mfc.o
 ccflags-y += -I$(srctree)/$(src)
 
 #Dev interface layer
-exynos_mfc-y += mfc.o
+exynos_mfc-y += mfc.o mfc_dec_vb2.o
 #Dev control layer
 exynos_mfc-y += mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o
 #Core interface layer
diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c 
b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c
new file mode 100644
index 000000000000..3097a6c0bf14
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * mfc_dec_vb2.c file
+ *
+ * Nagaraju Siddineni, <[email protected]>
+ * Himanshu Dewangan, <[email protected]>
+ */
+
+#include "mfc_dec_vb2.h"
+
+#include "mfc_rm.h"
+
+#include "base/mfc_queue.h"
+#include "base/mfc_utils.h"
+#include "base/mfc_buf.h"
+#include "base/mfc_mem.h"
+
+static int mfc_dec_queue_setup(struct vb2_queue *vq,
+                              unsigned int *buf_count, unsigned int 
*plane_count,
+                              unsigned int psize[], struct device 
*alloc_devs[])
+{
+       struct mfc_ctx *ctx = vq->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+       struct mfc_core *core;
+       struct mfc_core_ctx *core_ctx;
+       struct mfc_raw_info *raw;
+       int i, offset;
+
+       mfc_ctx_debug_enter();
+
+       raw = &ctx->raw_buf;
+
+       /*
+        * During queue_setup,
+        * context information is need to for only main core
+        */
+       core = mfc_get_main_core_lock(dev, ctx);
+       core_ctx = core->core_ctx[ctx->num];
+
+       /* Video output for decoding (source)
+        * this can be set after getting an instance
+        */
+       if (core_ctx->state == MFCINST_GOT_INST &&
+           vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mfc_debug(4, "dec src\n");
+               /* A single plane is required for input */
+               *plane_count = 1;
+               if (*buf_count < 1)
+                       *buf_count = 1;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               /* need to use minimum size to prevent qbuf fail */
+               psize[0] = 1;
+               alloc_devs[0] = dev->device;
+       /* Video capture for decoding (destination)
+        * this can be set after the header was parsed
+        */
+       } else if (core_ctx->state >= MFCINST_HEAD_PARSED &&
+               vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               mfc_debug(4, "dec dst\n");
+               /* Output plane count is different by the pixel format */
+               *plane_count = ctx->num_fd_frame;
+               /* Setup buffer count */
+               if (*buf_count < ctx->dpb_count)
+                       *buf_count = ctx->dpb_count;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               if (ctx->dst_fmt->mem_planes == 1) {
+                       psize[0] = raw->total_plane_size;
+                       alloc_devs[0] = dev->device;
+               } else {
+                       for (i = 0; i < ctx->dst_fmt->num_planes; i++) {
+                               psize[i] = ctx->min_dpb_size[i];
+                               alloc_devs[i] = dev->device;
+                       }
+               }
+               if (ctx->multi_view_enable) {
+                       offset = 
ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1].offset;
+                       if (ctx->dst_fmt->mem_planes == 1) {
+                               psize[offset] = raw->total_plane_size;
+                               alloc_devs[offset] = dev->device;
+                       } else {
+                               for (i = 0; i < ctx->dst_fmt->num_planes; i++) {
+                                       psize[offset + i] = 
ctx->min_dpb_size[i];
+                                       alloc_devs[offset + i] = dev->device;
+                               }
+                       }
+               }
+
+               /* Decoder DPB should be read for reference */
+               vq->dma_dir = DMA_BIDIRECTIONAL;
+       } else {
+               mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+                       core_ctx->state, vq->type);
+               return -EINVAL;
+       }
+
+       mfc_debug(2, "buf_count: %d, plane_count: %d, type: %#x\n",
+                 *buf_count, *plane_count, vq->type);
+       for (i = 0; i < *plane_count; i++)
+               mfc_debug(2, "plane[%d] size: %d\n", i, psize[i]);
+
+       mfc_ctx_debug_leave();
+
+       return 0;
+}
+
+static void mfc_dec_unlock(struct vb2_queue *q)
+{
+       struct mfc_ctx *ctx = q->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+
+       mutex_unlock(&dev->mfc_mutex);
+}
+
+static void mfc_dec_lock(struct vb2_queue *q)
+{
+       struct mfc_ctx *ctx = q->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+
+       mutex_lock(&dev->mfc_mutex);
+}
+
+static int mfc_dec_buf_init(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct mfc_ctx *ctx = vq->drv_priv;
+       struct mfc_buf *buf = vb_to_mfc_buf(vb);
+       int ret;
+
+       mfc_ctx_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = mfc_check_vb_with_fmt(ctx->dst_fmt, vb);
+               if (ret < 0)
+                       return ret;
+               mfc_calc_base_addr(ctx, vb, ctx->dst_fmt);
+
+               buf->paddr = mfc_mem_get_paddr_vb(vb);
+               mfc_ctx_debug(2, "[DPB] vb index [%d] vb paddr %#llx daddr 
%#llx\n",
+                             vb->index, buf->paddr, buf->addr[0][0]);
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_DST,
+                            vb->index) < 0)
+                       mfc_ctx_err("failed in init_buf_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = mfc_check_vb_with_fmt(ctx->src_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               buf->addr[0][0] = mfc_mem_get_daddr_vb(vb, 0);
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC,
+                            vb->index) < 0)
+                       mfc_ctx_err("failed in init_buf_ctrls\n");
+       } else {
+               mfc_ctx_err("unknown queue type\n");
+               return -EINVAL;
+       }
+
+       mfc_ctx_debug_leave();
+
+       return 0;
+}
+
+static int mfc_dec_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct mfc_ctx *ctx = vq->drv_priv;
+       struct mfc_buf *buf = vb_to_mfc_buf(vb);
+       struct mfc_raw_info *raw;
+       unsigned int index = vb->index;
+       size_t buf_size;
+       int i;
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               raw = &ctx->raw_buf;
+               /* check the size per plane */
+               if (ctx->dst_fmt->mem_planes == 1) {
+                       buf_size = vb2_plane_size(vb, 0);
+                       mfc_ctx_debug(2, "[FRAME] single plane vb size: %lu, 
calc size: %d\n",
+                                     buf_size, raw->total_plane_size);
+                       if (buf_size < raw->total_plane_size) {
+                               mfc_ctx_err("[FRAME] single plane size(%lu) is 
smaller than (%d)\n",
+                                           buf_size, raw->total_plane_size);
+                               return -EINVAL;
+                       }
+               } else {
+                       for (i = 0; i < ctx->dst_fmt->mem_planes; i++) {
+                               buf_size = vb2_plane_size(vb, i);
+                               mfc_ctx_debug(2, "[FRAME] plane[%d] vb size: 
%lu, calc size: %d\n",
+                                             i, buf_size, raw->plane_size[i]);
+                               if (buf_size < raw->plane_size[i]) {
+                                       mfc_ctx_err("[FRAME] plane[%d] 
size(%lu) is smaller than (%d)\n",
+                                                   i, buf_size, 
raw->plane_size[i]);
+                                       return -EINVAL;
+                               }
+                       }
+               }
+               /* Copy dst buffer flag to buf_ctrl */
+               buf->flag = call_cop(ctx, get_buf_ctrl_val, ctx,
+                                    &ctx->dst_ctrls[index],
+                                    V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG);
+
+               mfc_mem_buf_prepare(vb, 0);
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               buf_size = vb2_plane_size(vb, 0);
+               buf->sg_size = mfc_mem_get_sg_length(ctx->dev, 
vb2_dma_sg_plane_desc(vb, 0));
+               vb->planes[0].bytesused = buf->vb.planes[0].bytesused;
+               vb->planes[0].data_offset = buf->vb.planes[0].data_offset;
+               mfc_ctx_debug(2, "[STREAM] vb size, %ld, %s, %ld, sg_size, %zu, 
%s, %u, %s, %u\n",
+                             buf_size,
+                             "dbuf size", vb->planes[0].dbuf->size,
+                             buf->sg_size,
+                             "bytesused", vb->planes[0].bytesused,
+                             "data_offset", vb->planes[0].data_offset);
+
+               call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[index]);
+
+               /* Copy src buffer flag to buf_ctrl */
+               buf->flag = call_cop(ctx, get_buf_ctrl_val, ctx,
+                                    &ctx->src_ctrls[index],
+                                    V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG);
+
+               mfc_mem_buf_prepare(vb, 1);
+       }
+
+       return 0;
+}
+
+static void mfc_dec_buf_finish(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct mfc_ctx *ctx = vq->drv_priv;
+       struct mfc_buf *buf = vb_to_mfc_buf(vb);
+       unsigned int index = vb->index;
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               /* Copy to dst buffer flag */
+               call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index],
+                        V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, buf->flag);
+               mfc_ctx_debug(4, "[FLAG] dst update buf[%d] flag = %#x\n",
+                             index, buf->flag);
+
+               call_cop(ctx, to_ctx_ctrls, ctx, &ctx->dst_ctrls[index]);
+
+               mfc_mem_buf_finish(vb, 0);
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               /* Copy to src buffer flag */
+               call_cop(ctx, update_buf_val, ctx, &ctx->src_ctrls[index],
+                        V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, buf->flag);
+               mfc_ctx_debug(4, "[FLAG] src update buf[%d] flag = %#x\n",
+                             index, buf->flag);
+
+               call_cop(ctx, to_ctx_ctrls, ctx, &ctx->src_ctrls[index]);
+       }
+}
+
+static void mfc_dec_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct mfc_ctx *ctx = vq->drv_priv;
+       unsigned int index = vb->index;
+
+       mfc_ctx_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                            MFC_CTRL_TYPE_DST, index) < 0)
+                       mfc_ctx_err("failed in cleanup_buf_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                            MFC_CTRL_TYPE_SRC, index) < 0)
+                       mfc_ctx_err("failed in cleanup_buf_ctrls\n");
+       } else {
+               mfc_ctx_err("unknown queue type\n");
+       }
+
+       mfc_ctx_debug_leave();
+}
+
+static int mfc_dec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct mfc_ctx *ctx = q->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+
+       mfc_rm_update_real_time(ctx);
+
+       mfc_rm_request_work(dev, MFC_WORK_TRY, ctx);
+
+       return 0;
+}
+
+static void mfc_dec_stop_streaming(struct vb2_queue *q)
+{
+       struct mfc_ctx *ctx = q->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+
+       mfc_ctx_info("dec stop_streaming is called, type : %d\n", q->type);
+       MFC_TRACE_CTX("** DEC streamoff(type:%d)\n", q->type);
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               mfc_rm_request_work(ctx->dev, MFC_WORK_BUTLER, ctx);
+
+       mfc_rm_instance_dec_stop(dev, ctx, q->type);
+}
+
+static void mfc_dec_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct mfc_ctx *ctx = vq->drv_priv;
+       struct mfc_dev *dev = ctx->dev;
+       struct mfc_dec *dec = ctx->dec_priv;
+       struct mfc_buf *buf = vb_to_mfc_buf(vb);
+       int i;
+       unsigned char *stream_vir = NULL;
+
+       mfc_ctx_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mutex_lock(&ctx->op_mode_mutex);
+               buf->src_index = ctx->serial_src_index++;
+               mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add src index: %d(%d), 
addr: 0x%08llx\n",
+                             ctx->num, vb->index, buf->src_index,
+                             buf->addr[0][0]);
+               mutex_unlock(&ctx->op_mode_mutex);
+
+               if (vb->memory == V4L2_MEMORY_DMABUF &&
+                   mfc_rm_query_state(ctx, SMALLER, MFCINST_HEAD_PARSED))
+                       stream_vir = vb2_plane_vaddr(vb, 0);
+
+               buf->vir_addr[0] = stream_vir;
+
+               mfc_add_tail_buf(ctx, &ctx->src_buf_ready_queue, buf);
+
+               if (dev->debugfs.debug_ts == 1)
+                       mfc_ctx_info("[TS] framerate: %ld, timestamp: %lld\n",
+                                    ctx->framerate, buf->vb.vb2_buf.timestamp);
+
+               MFC_TRACE_CTX("Q src[%d](%d) fd: %d, %#llx\n",
+                             vb->index, buf->src_index, vb->planes[0].m.fd, 
buf->addr[0][0]);
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               for (i = 0; i < ctx->num_fd_frame; i++) {
+                       // ToDo: if multi_view, vir_addr array_size could be 
over 3
+                       buf->vir_addr[i] = vb2_plane_vaddr(vb, i);
+                       mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add dst index: %d, 
addr[0][%d]: 0x%08llx\n",
+                                     ctx->num, vb->index, i, buf->addr[0][i]);
+                       if (ctx->multi_view_enable) {
+                               mfc_ctx_debug(2, "[BUFINFO-VIEW1] ctx[%d] add 
dst index: %d, addr[2][%d]: 0x%08llx\n",
+                                             ctx->num, vb->index, i, 
buf->addr[2][i]);
+                       }
+               }
+               mfc_store_dpb(ctx, vb);
+
+               if ((vb->memory == V4L2_MEMORY_USERPTR || vb->memory == 
V4L2_MEMORY_DMABUF) &&
+                   mfc_is_queue_count_same(&ctx->buf_queue_lock,
+                                           &ctx->dst_buf_queue, 
dec->total_dpb_count))
+                       ctx->capture_state = QUEUE_BUFS_MMAPED;
+
+               MFC_TRACE_CTX("Q dst[%d][%d] fd: %d, %#llx / used %#lx\n",
+                             vb->index, buf->dpb_index, vb->planes[0].m.fd,
+                             buf->addr[0][0], dec->dynamic_used);
+       } else {
+               mfc_ctx_err("Unsupported buffer type (%d)\n", vq->type);
+       }
+
+       mfc_rm_request_work(dev, MFC_WORK_TRY, ctx);
+
+       mfc_ctx_debug_leave();
+}
+
+static const struct vb2_ops mfc_dec_qops = {
+       .queue_setup            = mfc_dec_queue_setup,
+       .wait_prepare           = mfc_dec_unlock,
+       .wait_finish            = mfc_dec_lock,
+       .buf_init               = mfc_dec_buf_init,
+       .buf_prepare            = mfc_dec_buf_prepare,
+       .buf_finish             = mfc_dec_buf_finish,
+       .buf_cleanup            = mfc_dec_buf_cleanup,
+       .start_streaming        = mfc_dec_start_streaming,
+       .stop_streaming         = mfc_dec_stop_streaming,
+       .buf_queue              = mfc_dec_buf_queue,
+};
+
+const struct vb2_ops *mfc_get_dec_vb2_ops(void)
+{
+       return &mfc_dec_qops;
+}
diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h 
b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h
new file mode 100644
index 000000000000..3d0cb7b14ce5
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * mfc_dec_vb2.h file
+ *
+ * Nagaraju Siddineni, <[email protected]>
+ * Himanshu Dewangan, <[email protected]>
+ */
+
+#ifndef __MFC_DEC_VB2_H
+#define __MFC_DEC_VB2_H __FILE__
+
+#include "base/mfc_common.h"
+
+const struct vb2_ops *mfc_get_dec_vb2_ops(void);
+
+#endif /* __MFC_DEC_VB2_H */
-- 
2.34.1

Reply via email to