From: Nagaraju Siddineni <[email protected]> Introduce encoder VB2 support. This enables proper V4L2 output for the Exynos MFC encoder, handling queue setup, buffers, and streaming control.
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_enc_vb2.c | 443 ++++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_enc_vb2.h | 19 + 3 files changed, 463 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/media/platform/samsung/exynos-mfc/Makefile index a257d5b0a576..dad94a7c468c 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 mfc_dec_v4l2.o mfc_dec_vb2.o +exynos_mfc-y += mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o mfc_enc_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_enc_vb2.c b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c new file mode 100644 index 000000000000..7164c334585b --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_enc_vb2.c file + * + * Nagaraju Siddineni, <[email protected]> + * Himanshu Dewangan, <[email protected]> + */ + +#include "mfc_rm.h" + +#include "base/mfc_queue.h" +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" +#include "mfc_enc_vb2.h" + +static int mfc_enc_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_enc *enc = ctx->enc_priv; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i; + + mfc_ctx_debug_enter(); + + /* Encoder works only single core */ + core = mfc_get_main_core_lock(dev, ctx); + core_ctx = core->core_ctx[ctx->num]; + + if (core_ctx->state != MFCINST_GOT_INST && + vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_err("invalid state: %d\n", core_ctx->state); + return -EINVAL; + } + if (core_ctx->state >= MFCINST_FINISHING && + vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_err("invalid state: %d\n", core_ctx->state); + return -EINVAL; + } + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_debug(4, "enc dst\n"); + if (ctx->dst_fmt) + *plane_count = ctx->dst_fmt->mem_planes; + else + *plane_count = MFC_ENC_CAP_PLANE_COUNT; + + if (*buf_count < 1) + *buf_count = 1; + if (*buf_count > MFC_MAX_BUFFERS) + *buf_count = MFC_MAX_BUFFERS; + + psize[0] = enc->dst_buf_size; + alloc_devs[0] = dev->device; + /* In case of VP8/VP9 encoder, part of stream buffer should be read */ + vq->dma_dir = DMA_BIDIRECTIONAL; + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src\n"); + + if (ctx->src_fmt) + *plane_count = ctx->num_fd_frame; + else + *plane_count = MFC_ENC_OUT_PLANE_COUNT; + + 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 */ + if (*plane_count == 1) { + psize[0] = 1; + alloc_devs[0] = dev->device; + } else { + for (i = 0; i < *plane_count; i++) { + psize[i] = 1; + alloc_devs[i] = dev->device; + } + } + } else { + mfc_err("invalid queue type: %d\n", 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_enc_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_enc_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_enc_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct mfc_ctx *ctx = vq->drv_priv; + 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; + + 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; + + 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("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_enc_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct mfc_ctx *ctx = vq->drv_priv; + struct mfc_enc *enc = ctx->enc_priv; + struct mfc_raw_info *raw; + unsigned int index = vb->index; + struct mfc_buf *buf = vb_to_mfc_buf(vb); + struct dma_buf *bufcon_dmabuf[MFC_MAX_PLANES]; + int i, mem_get_count = 0; + size_t buf_size; + + mfc_ctx_debug_enter(); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf_size = vb2_plane_size(vb, 0); + mfc_ctx_debug(2, "[STREAM] vb size: %lu, calc size: %u\n", + buf_size, enc->dst_buf_size); + + if (buf_size < enc->dst_buf_size) { + mfc_ctx_err("[STREAM] size(%lu) is smaller than (%d)\n", + buf_size, enc->dst_buf_size); + return -EINVAL; + } + + buf->addr[0][0] = mfc_mem_get_daddr_vb(vb, 0); + + /* 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); + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + raw = &ctx->raw_buf; + if (ctx->src_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->src_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; + } + } + } + + for (i = 0; i < ctx->src_fmt->mem_planes; i++) { + bufcon_dmabuf[i] = dma_buf_get(vb->planes[i].m.fd); + if (IS_ERR(bufcon_dmabuf[i])) { + mfc_ctx_err("failed to get bufcon dmabuf\n"); + goto err_mem_put; + } + mem_get_count++; + + dma_buf_put(bufcon_dmabuf[i]); + mfc_calc_base_addr(ctx, vb, ctx->src_fmt); + } + + 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); + } else { + mfc_ctx_err("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + mfc_mem_buf_prepare(vb, 0); + + mfc_ctx_debug_leave(); + return 0; + +err_mem_put: + for (i = 0; i < mem_get_count; i++) + dma_buf_put(bufcon_dmabuf[i]); + + return -ENOMEM; +} + +static void mfc_enc_buf_finish(struct vb2_buffer *vb) +{ + struct mfc_buf *buf = vb_to_mfc_buf(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct mfc_ctx *ctx = vq->drv_priv; + 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, 1); + } 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_enc_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_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mfc_ctx *ctx = q->drv_priv; + struct mfc_dev *dev = ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + + /* Encoder works only single core */ + core = mfc_get_main_core_lock(dev, ctx); + core_ctx = core->core_ctx[ctx->num]; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + core_ctx->state == MFCINST_FINISHED) { + mfc_change_state(core_ctx, MFCINST_GOT_INST); + mfc_info("enc start_streaming changes state %d\n", + core_ctx->state); + MFC_TRACE_CTX("** ENC streamon, state: %d\n", + core_ctx->state); + } + + mfc_rm_update_real_time(ctx); + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + return 0; +} + +static void mfc_enc_stop_streaming(struct vb2_queue *q) +{ + struct mfc_ctx *ctx = q->drv_priv; + struct mfc_dev *dev = ctx->dev; + + mfc_ctx_info("enc stop_streaming is called, type : %d\n", q->type); + MFC_TRACE_CTX("** ENC streamoff(type:%d)\n", q->type); + + mfc_rm_instance_enc_stop(dev, ctx, q->type); +} + +static void mfc_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx = vq->drv_priv; + struct mfc_dev *dev = ctx->dev; + struct mfc_buf *buf = vb_to_mfc_buf(vb); + int i; + int is_dst_buf_ready; + + mfc_ctx_debug_enter(); + + buf->next_index = 0; + buf->done_index = 0; + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add dst index: %d, addr: 0x%08llx\n", + ctx->num, vb->index, buf->addr[0][0]); + + /* Mark destination as available for use by MFC */ + mfc_add_tail_buf(ctx, &ctx->dst_buf_queue, buf); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + buf->src_index = ctx->serial_src_index++; + if (ctx->multi_view_enable) { + for (i = 0; i < ctx->raw_buf.num_planes; i++) + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[0][%d]: 0x%08llx\n", + "[BUFINFO-view0] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, + buf->addr[0][i]); + for (i = 0; i < ctx->raw_buf.num_planes; i++) + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[2][%d]: 0x%08llx\n", + "[BUFINFO-view1] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, + buf->addr[2][i]); + } else { + if (ctx->num_fd_frame > 3) { + mfc_ctx_err("if not multi_view_enable, num_fd_frame must be <= 3\n"); + } else { + for (i = 0; i < ctx->num_fd_frame; i++) { + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[%d]: 0x%08llx\n", + "[BUFINFO] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, buf->addr[0][i]); + } + } + } + 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_rate_update_last_framerate(ctx, buf->vb.vb2_buf.timestamp); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + } else { + mfc_ctx_err("unsupported buffer type (%d)\n", vq->type); + } + + if (ctx->stream_op_mode == MFC_OP_TWO_MODE1) + is_dst_buf_ready = + mfc_is_queue_count_greater(&ctx->buf_queue_lock, + &ctx->dst_buf_queue, 0); + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + if (!mfc_rm_query_state(ctx, EQUAL_BIGGER, MFCINST_HEAD_PARSED) && + ctx->stream_op_mode == MFC_OP_TWO_MODE1 && is_dst_buf_ready) { + core = mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] main core is NULL\n"); + return; + } + core_ctx = core->core_ctx[ctx->num]; + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + mfc_ctx_err("[RM] sub core header parsing failed\n"); + return; + } + + mfc_ctx_info("[2CORE] start the sub core\n"); + if (ctx->op_core_num[MFC_CORE_SUB] == MFC_CORE_INVALID) { + if (mfc_rm_instance_setup(dev, ctx)) + mfc_ctx_err("[2CORE] failed to setup sub core\n"); + } else { + if (mfc_rm_subcore_seq_start(dev, ctx)) + mfc_ctx_err("[2CORE] failed to seq_start sub core\n"); + } + } + + mfc_ctx_debug_leave(); +} + +static const struct vb2_ops mfc_enc_qops = { + .queue_setup = mfc_enc_queue_setup, + .wait_prepare = mfc_enc_unlock, + .wait_finish = mfc_enc_lock, + .buf_init = mfc_enc_buf_init, + .buf_prepare = mfc_enc_buf_prepare, + .buf_finish = mfc_enc_buf_finish, + .buf_cleanup = mfc_enc_buf_cleanup, + .start_streaming = mfc_enc_start_streaming, + .stop_streaming = mfc_enc_stop_streaming, + .buf_queue = mfc_enc_buf_queue, +}; + +const struct vb2_ops *mfc_get_enc_vb2_ops(void) +{ + return &mfc_enc_qops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h new file mode 100644 index 000000000000..d3cb99f0bf84 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_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_enc_vb2.h file + * + * Nagaraju Siddineni, <[email protected]> + * Himanshu Dewangan, <[email protected]> + */ + +#ifndef __MFC_ENC_VB2_H +#define __MFC_ENC_VB2_H __FILE__ + +#include "base/mfc_common.h" + +const struct vb2_ops *mfc_get_enc_vb2_ops(void); + +#endif /* __MFC_ENC_VB2_H */ -- 2.34.1
