[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250930040348.3702923-18-h.dewangan@samsung.com>
Date: Tue, 30 Sep 2025 09:33:36 +0530
From: Himanshu Dewangan <h.dewangan@...sung.com>
To: mchehab@...nel.org, robh@...nel.org, krzk+dt@...nel.org,
conor+dt@...nel.org, sumit.semwal@...aro.org, christian.koenig@....com,
alim.akhtar@...sung.com, manjun@...sung.com, nagaraju.s@...sung.com,
ih0206.lee@...sung.com, jehyung.lee@...sung.com
Cc: linux-arm-kernel@...ts.infradead.org, linux-media@...r.kernel.org,
devicetree@...r.kernel.org, linux-samsung-soc@...r.kernel.org,
linux-kernel@...r.kernel.org, dri-devel@...ts.freedesktop.org,
linaro-mm-sig@...ts.linaro.org, Himanshu Dewangan <h.dewangan@...sung.com>
Subject: [PATCH 17/29] media: mfc: Add VB2 decoder support
From: Nagaraju Siddineni <nagaraju.s@...sung.com>
- 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 <nagaraju.s@...sung.com>
Signed-off-by: Himanshu Dewangan <h.dewangan@...sung.com>
---
.../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, <nagaraju.s@...sung.com>
+ * Himanshu Dewangan, <h.dewangan@...sung.com>
+ */
+
+#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, <nagaraju.s@...sung.com>
+ * Himanshu Dewangan, <h.dewangan@...sung.com>
+ */
+
+#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
Powered by blists - more mailing lists