[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250930040348.3702923-19-h.dewangan@samsung.com>
Date: Tue, 30 Sep 2025 09:33:37 +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 18/29] media: mfc: Add V4L2 decoder driver
From: Nagaraju Siddineni <nagaraju.s@...sung.com>
Add a V4L2‑based decoder for Exynos MFC.
- Implement full decoder V4L2 ioctl, control, buffer, and stream handling.
- Provide ioctl getter and default format helper.
Move the decoder to the standard V4L2 framework,
to enable proper media‑device registration and user‑space interaction.
Signed-off-by: Nagaraju Siddineni <nagaraju.s@...sung.com>
Signed-off-by: Himanshu Dewangan <h.dewangan@...sung.com>
---
.../platform/samsung/exynos-mfc/Makefile | 2 +-
.../samsung/exynos-mfc/mfc_dec_v4l2.c | 1741 +++++++++++++++++
.../samsung/exynos-mfc/mfc_dec_v4l2.h | 22 +
3 files changed, 1764 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c
create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h
diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/media/platform/samsung/exynos-mfc/Makefile
index 9127f2dc4df6..b6b312ae7f22 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_vb2.o
+exynos_mfc-y += mfc.o mfc_dec_v4l2.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_v4l2.c b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c
new file mode 100644
index 000000000000..dd59dc352e34
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c
@@ -0,0 +1,1741 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mfc_dec_v4l2.c
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/vmalloc.h>
+#include <kunit/visibility.h>
+
+#include "mfc_dec_v4l2.h"
+#include "mfc_dec_vb2.h"
+#include "mfc_rm.h"
+
+#include "mfc_core_hwlock.h"
+
+#include "base/mfc_format.h"
+#include "base/mfc_queue.h"
+#include "base/mfc_utils.h"
+#include "base/mfc_buf.h"
+#include "base/mfc_mem.h"
+
+#define MAX_FRAME_SIZE (2 * SZ_1K * SZ_1K)
+static struct v4l2_queryctrl dec_controls[] = {
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H.264 Display Delay",
+ .minimum = -1,
+ .maximum = 32,
+ .step = 1,
+ .default_value = -1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mpeg4 Loop Filter Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Slice Interface Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Packed PB Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Frame Tag",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "CRC enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "CRC data",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "CRC data",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Display status",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Frame type",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Frame pack sei parse flag",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "I frame decoding mode",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Frames per second in 1000x scale",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 60000,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Immediate Display Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Decoding Timestamp Mode Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Wait until buffer setting done",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_GET_VERSION_INFO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Get MFC version information",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Set Dual DPB mode",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_QOS_RATIO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "QoS ratio value",
+ .minimum = 20,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 100,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Set dynamic DPB",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Set dynamic DPB",
+ .minimum = 0,
+ .maximum = U16_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_GET_EXT_INFO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Get extra information",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Set buffer process type",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_GET_10BIT_INFO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "10 bit contents information",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Set black bar detection option",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_HDR_USER_SHARED_HANDLE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Dynamic HDR10+ SEI metadata",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_USER_SHARED_HANDLE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "AV1 Film Grain SEI metadata",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_PRESENT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "AV1 Film Grain presented",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODING_ORDER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "decoding order enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_UNCOMP_FMT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Uncompressed format",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GET_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "display delay for first frame",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_POC,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Frame POC",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Buffer flag",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Buffer flag",
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "skip lazy unmap",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_PRIORITY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "priority",
+ .minimum = 0,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_HEIF_MODE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "heif mode",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Multi-View Enable",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+#define DEC_NUM_CTRLS ARRAY_SIZE(dec_controls)
+/* Find selected format description */
+VISIBLE_IF_KUNIT struct mfc_fmt *__mfc_dec_find_format(struct mfc_ctx *ctx,
+ unsigned int pixelformat)
+{
+ struct mfc_dev *dev = ctx->dev;
+ struct mfc_fmt *fmt = NULL;
+ unsigned long i;
+
+ for (i = 0; i < MFC_NUM_FORMATS; i++) {
+ if ((mfc_formats[i].type & MFC_FMT_STREAM) &&
+ !(mfc_formats[i].type & MFC_FMT_DEC)) {
+ continue;
+ }
+ if (mfc_formats[i].fourcc == pixelformat) {
+ fmt = (struct mfc_fmt *)&mfc_formats[i];
+ break;
+ }
+ }
+
+ if (fmt && !dev->pdata->support_10bit && (fmt->type & MFC_FMT_10BIT)) {
+ mfc_ctx_err("[FRAME] 10bit is not supported\n");
+ fmt = NULL;
+ }
+ if (fmt && !dev->pdata->support_422 && (fmt->type & MFC_FMT_422)) {
+ mfc_ctx_err("[FRAME] 422 is not supported\n");
+ fmt = NULL;
+ }
+ if (fmt && (fmt->type & MFC_FMT_RGB)) {
+ mfc_ctx_err("[FRAME] RGB is not supported by decoder\n");
+ fmt = NULL;
+ }
+ if (fmt && (fmt->type & MFC_FMT_STREAM) &&
+ dev->pdata->mfc_resource[fmt->codec_mode].op_core_type == MFC_OP_CORE_NOT_FIXED) {
+ mfc_ctx_err("[STREAM] %s is not supported\n", fmt->name);
+ fmt = NULL;
+ }
+
+ return fmt;
+}
+EXPORT_SYMBOL_IF_KUNIT(__mfc_dec_find_format);
+
+static struct v4l2_queryctrl *__mfc_dec_get_ctrl(int id)
+{
+ unsigned long i;
+
+ for (i = 0; i < DEC_NUM_CTRLS; ++i)
+ if (id == dec_controls[i].id)
+ return &dec_controls[i];
+
+ return NULL;
+}
+
+/* Check whether a ctrl value if correct */
+static int __mfc_dec_check_ctrl_val(struct mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+ struct v4l2_queryctrl *c;
+
+ c = __mfc_dec_get_ctrl(ctrl->id);
+ if (!c) {
+ mfc_ctx_err("[CTRLS] not supported control id (%#x)\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ if (ctrl->value < c->minimum || ctrl->value > c->maximum ||
+ (c->step != 0 && ctrl->value % c->step != 0)) {
+ mfc_ctx_err("[CTRLS][%s] id: %#x, invalid value (%d)\n",
+ c->name, ctrl->id, ctrl->value);
+ return -ERANGE;
+ }
+
+ mfc_ctx_debug(5, "[CTRLS][%s] id: %#x, value: %d (%#x)\n",
+ c->name, ctrl->id, ctrl->value, ctrl->value);
+
+ return 0;
+}
+
+/* Query capabilities of the device */
+static int mfc_dec_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "MFC", sizeof(cap->driver));
+ strscpy(cap->card, "decoder", sizeof(cap->card));
+
+ return 0;
+}
+
+static int __mfc_dec_enum_fmt(struct mfc_dev *dev, struct v4l2_fmtdesc *f,
+ unsigned int type)
+{
+ struct mfc_fmt *fmt;
+ unsigned long i, j = 0;
+
+ for (i = 0; i < MFC_NUM_FORMATS; ++i) {
+ if (!(mfc_formats[i].type & type))
+ continue;
+ if (!dev->pdata->support_10bit && (mfc_formats[i].type & MFC_FMT_10BIT))
+ continue;
+ if (!dev->pdata->support_422 && (mfc_formats[i].type & MFC_FMT_422))
+ continue;
+
+ if (j == f->index) {
+ fmt = &mfc_formats[i];
+ strscpy(f->description, fmt->name,
+ sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+ }
+
+ ++j;
+ }
+
+ return -EINVAL;
+}
+
+static int mfc_dec_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mfc_dev *dev = video_drvdata(file);
+
+ return __mfc_dec_enum_fmt(dev, f, MFC_FMT_FRAME);
+}
+
+static int mfc_dec_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ struct mfc_dev *dev = video_drvdata(file);
+
+ return __mfc_dec_enum_fmt(dev, f, MFC_FMT_STREAM);
+}
+
+static void __mfc_dec_change_format_8bit(struct mfc_ctx *ctx)
+{
+ u32 org_fmt = ctx->dst_fmt->fourcc;
+
+ switch (org_fmt) {
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_YUV420M:
+ case V4L2_PIX_FMT_YVU420M:
+ /* It is right format */
+ break;
+ case V4L2_PIX_FMT_NV61M:
+ /* change to CrCb order format */
+ ctx->dst_fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV21M);
+ break;
+ default:
+ ctx->dst_fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV12M);
+ break;
+ }
+}
+
+static void __mfc_dec_change_format_8bit_422(struct mfc_ctx *ctx)
+{
+ u32 org_fmt = ctx->dst_fmt->fourcc;
+
+ switch (org_fmt) {
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ /* It is right format */
+ break;
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_YVU420M:
+ /* change to CrCb order format */
+ ctx->dst_fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV61M);
+ break;
+ default:
+ ctx->dst_fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV16M);
+ break;
+ }
+}
+
+static void __mfc_dec_change_format(struct mfc_ctx *ctx)
+{
+ u32 org_fmt = ctx->dst_fmt->fourcc;
+
+ if (ctx->is_422)
+ __mfc_dec_change_format_8bit_422(ctx);
+ else
+ __mfc_dec_change_format_8bit(ctx);
+
+ ctx->raw_buf.num_planes = ctx->dst_fmt->num_planes;
+ if (org_fmt != ctx->dst_fmt->fourcc)
+ mfc_ctx_info("[FRAME] format is changed to %s\n", ctx->dst_fmt->name);
+}
+
+static void __mfc_dec_set_num_fd_frame(struct mfc_ctx *ctx,
+ struct v4l2_pix_format_mplane *pix_fmt_mp)
+{
+ int calc_num_planes;
+ int num_fd_depth_map = 0;
+ int num_view = 1;
+ int num_fd_sub_view_meta = 0;
+
+ if (ctx->multi_view_enable) {
+ pix_fmt_mp->flags |= MFC_FMT_FLAG_MULTI_VIEW;
+ num_view = MFC_NUM_MULTI_VIEW;
+ num_fd_sub_view_meta = MFC_NUM_FD_SUB_VIEW_META;
+
+ // ToDo: Depth is not supported yet.
+ pix_fmt_mp->flags &= ~MFC_FMT_FLAG_DEPTH_MAP;
+ num_fd_depth_map = 0;
+
+ calc_num_planes =
+ (ctx->dst_fmt->mem_planes + num_fd_depth_map) * num_view +
+ num_fd_sub_view_meta;
+ } else {
+ pix_fmt_mp->flags &= ~MFC_FMT_FLAG_MULTI_VIEW;
+ calc_num_planes = ctx->dst_fmt->mem_planes;
+ }
+
+ mfc_set_view_buf_info(ctx, ctx->dst_fmt->mem_planes,
+ num_fd_depth_map, num_fd_sub_view_meta);
+
+ ctx->num_fd_frame = calc_num_planes;
+ pix_fmt_mp->num_planes = calc_num_planes;
+}
+
+static void __mfc_dec_update_pix_format(struct mfc_ctx *ctx, struct v4l2_format *f)
+{
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ struct mfc_raw_info *raw;
+ int i;
+
+ raw = &ctx->raw_buf;
+
+ pix_fmt_mp->width = ctx->img_width;
+ pix_fmt_mp->height = ctx->img_height;
+ __mfc_dec_set_num_fd_frame(ctx, pix_fmt_mp);
+
+ if (dec->is_interlaced)
+ pix_fmt_mp->field = V4L2_FIELD_INTERLACED;
+ else
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+
+ /* Set pixelformat to the format in which MFC outputs the decoded frame */
+ pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+ for (i = 0; i < ctx->dst_fmt->mem_planes; i++) {
+ pix_fmt_mp->plane_fmt[i].bytesperline = raw->stride[i];
+ if (ctx->dst_fmt->mem_planes == 1)
+ pix_fmt_mp->plane_fmt[i].sizeimage = raw->total_plane_size;
+ else
+ pix_fmt_mp->plane_fmt[i].sizeimage = raw->plane_size[i];
+ }
+}
+
+/* Get format */
+static int mfc_dec_g_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dev *dev = ctx->dev;
+ struct mfc_core *core;
+ struct mfc_core_ctx *core_ctx;
+ struct mfc_dec *dec = ctx->dec_priv;
+ int ret;
+
+ mfc_ctx_debug_enter();
+
+ /* During g_fmt, context information is need to for only main core */
+ core = mfc_get_main_core_lock(dev, ctx);
+ core_ctx = core->core_ctx[ctx->num];
+
+ mfc_debug(2, "dec dst g_fmt, state: %d wait_state: %d\n",
+ core_ctx->state, ctx->wait_state);
+ MFC_TRACE_CTX("** DEC g_fmt(state:%d wait_state:%d)\n",
+ core_ctx->state, ctx->wait_state);
+
+ mutex_lock(&ctx->drc_wait_mutex);
+ if (dec->disp_drc.disp_res_change) {
+ __mfc_dec_update_pix_format(ctx, f);
+ mutex_unlock(&ctx->drc_wait_mutex);
+ return 0;
+ }
+ mutex_unlock(&ctx->drc_wait_mutex);
+
+ if (core_ctx->state == MFCINST_GOT_INST ||
+ core_ctx->state == MFCINST_RES_CHANGE_INIT ||
+ core_ctx->state == MFCINST_RES_CHANGE_FLUSH ||
+ core_ctx->state == MFCINST_RES_CHANGE_FLUSH_FINISHED ||
+ core_ctx->state == MFCINST_RES_CHANGE_END) {
+ /* If there is no source buffer to parsing, we can't SEQ_START */
+ mutex_lock(&ctx->drc_wait_mutex);
+ if (((ctx->wait_state & WAIT_G_FMT) != 0) &&
+ mfc_is_queue_count_same(&ctx->buf_queue_lock,
+ &ctx->src_buf_ready_queue, 0) &&
+ mfc_is_queue_count_same(&ctx->buf_queue_lock,
+ &core_ctx->src_buf_queue, 0)) {
+ mfc_err("There is no source buffer to parsing, keep previous resolution\n");
+ mutex_unlock(&ctx->drc_wait_mutex);
+ return -EAGAIN;
+ }
+ mutex_unlock(&ctx->drc_wait_mutex);
+
+ /*
+ * If the MFC is parsing the header,
+ * so wait until it is finished.
+ */
+ ret = mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_SEQ_DONE_RET);
+ if (ret) {
+ if (core_ctx->int_err == MFC_REG_ERR_UNSUPPORTED_FEATURE) {
+ mfc_err("header parsing failed by unsupported feature\n");
+ return -EINVAL;
+ }
+ mfc_err("header parsing failed\n");
+ return -EAGAIN;
+ }
+ }
+
+ if (core_ctx->state >= MFCINST_HEAD_PARSED &&
+ core_ctx->state < MFCINST_ABORT) {
+ if (mfc_check_resolution(ctx)) {
+ mfc_ctx_err("Unsupported product resolution\n");
+ return -EAGAIN;
+ }
+
+ /* This is run on CAPTURE (decode output) */
+ if (ctx->stream_op_mode == MFC_OP_TWO_MODE1 ||
+ ctx->stream_op_mode == MFC_OP_TWO_MODE2) {
+ mfc_info("[2CORE] start the sub core\n");
+ if (mfc_rm_instance_setup(dev, ctx)) {
+ mfc_err("[2CORE] failed to setup sub core\n");
+ return -EAGAIN;
+ }
+ }
+
+ /*
+ * The format should be changed according to various conditions.
+ * 1. bit depth (8bit or 10bit)
+ * 2. chroma order (CbCr or CrCb)
+ * 3. component in memory (multi or single)
+ */
+
+ __mfc_dec_change_format(ctx);
+
+ /* Width and height are set to the dimensions
+ * of the movie, the buffer is bigger and
+ * further processing stages should crop to this
+ * rectangle.
+ */
+ mfc_dec_calc_dpb_size(ctx, &ctx->raw_buf, ctx->dst_fmt);
+
+ if (IS_LOW_MEM) {
+ unsigned int dpb_size;
+ /*
+ * If total memory requirement is too big for this device,
+ * then it returns error.
+ * DPB size : Total plane size * the number of DPBs
+ * 5: the number of extra DPBs
+ * 3: the number of DPBs for Android framework
+ * 600MB: being used to return an error,
+ * when 8K resolution video clip is being tried to be decoded
+ */
+ dpb_size = (ctx->raw_buf.total_plane_size *
+ (ctx->dpb_count + MFC_EXTRA_DPB + 3));
+ if (dpb_size > SZ_6M) {
+ mfc_info("required memory size is too big (%dx%d, dpb: %d)\n",
+ ctx->img_width, ctx->img_height, ctx->dpb_count);
+ return -EINVAL;
+ }
+ }
+
+ __mfc_dec_update_pix_format(ctx, f);
+ }
+
+ mutex_lock(&ctx->drc_wait_mutex);
+ if ((ctx->wait_state & WAIT_G_FMT) != 0) {
+ ctx->wait_state &= ~(WAIT_G_FMT);
+ mfc_debug(2, "clear WAIT_G_FMT %d\n", ctx->wait_state);
+ MFC_TRACE_CTX("** DEC clear WAIT_G_FMT(wait_state %d)\n", ctx->wait_state);
+ }
+ mutex_unlock(&ctx->drc_wait_mutex);
+
+ mfc_ctx_debug_leave();
+
+ return 0;
+}
+
+static int mfc_dec_g_fmt_vid_out_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ mfc_ctx_debug_enter();
+
+ mfc_ctx_debug(4, "dec src g_fmt\n");
+
+ /* This is run on OUTPUT
+ * The buffer contains compressed image
+ * so width and height have no meaning
+ */
+ pix_fmt_mp->width = 0;
+ pix_fmt_mp->height = 0;
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->plane_fmt[0].bytesperline = dec->src_buf_size;
+ pix_fmt_mp->plane_fmt[0].sizeimage = dec->src_buf_size;
+ pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->src_fmt->mem_planes;
+
+ mfc_ctx_debug_leave();
+
+ return 0;
+}
+
+/* Try format */
+static int mfc_dec_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ fmt = __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat);
+ if (!fmt) {
+ mfc_ctx_err("Unsupported format for %s\n",
+ V4L2_TYPE_IS_OUTPUT(f->type) ? "source" : "destination");
+ return -EINVAL;
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ fmt->codec_mode == MFC_FORMATS_NO_CODEC) {
+ mfc_ctx_err("MFC_FORMATS_NO_CODEC is invalid to src(fmt is %s)\n",
+ fmt->name);
+ return -EINVAL;
+ }
+
+ /* For resource reservation */
+ ctx->img_width = pix_fmt_mp->width;
+ ctx->img_height = pix_fmt_mp->height;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ctx->src_fmt = fmt;
+ ctx->codec_mode = ctx->src_fmt->codec_mode;
+ ctx->op_core_type = ctx->dev->pdata->mfc_resource[ctx->codec_mode].op_core_type;
+ }
+
+ mfc_ctx_debug(2, "[%s] resolution %dx%d, %s : %s\n",
+ V4L2_TYPE_IS_OUTPUT(f->type) ? "STREAM" : "FRAME",
+ ctx->img_width, ctx->img_height,
+ V4L2_TYPE_IS_OUTPUT(f->type) ? "codectype" : "pixelformat", fmt->name);
+
+ return 0;
+}
+
+/* Set format */
+static int mfc_dec_s_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ struct mfc_fmt *fmt = NULL;
+
+ mfc_ctx_debug_enter();
+
+ if (ctx->vq_dst.streaming) {
+ mfc_ctx_err("queue busy\n");
+ return -EBUSY;
+ }
+
+ fmt = __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat);
+ if (!fmt) {
+ mfc_ctx_err("Unsupported format for destination\n");
+ return -EINVAL;
+ }
+ ctx->dst_fmt = fmt;
+
+ if ((!ctx->img_width && !ctx->img_height) &&
+ pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) {
+ ctx->img_width = pix_fmt_mp->width;
+ ctx->img_height = pix_fmt_mp->height;
+ }
+
+ ctx->raw_buf.num_planes = ctx->dst_fmt->num_planes;
+ mfc_ctx_info("[FRAME] dec dst pixelformat : %s (%d x %d)\n",
+ ctx->dst_fmt->name, ctx->img_width, ctx->img_height);
+
+ mfc_ctx_debug_leave();
+
+ return 0;
+}
+
+static int mfc_dec_s_fmt_vid_out_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mfc_dev *dev = video_drvdata(file);
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ struct mfc_fmt *fmt = NULL;
+ int ret = 0;
+
+ mfc_ctx_debug_enter();
+
+ if (ctx->vq_src.streaming) {
+ mfc_ctx_err("queue busy\n");
+ return -EBUSY;
+ }
+
+ fmt = __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat);
+ if (!fmt) {
+ mfc_ctx_err("Unsupported format for source\n");
+ return -EINVAL;
+ }
+ ctx->src_fmt = fmt;
+
+ ctx->codec_mode = ctx->src_fmt->codec_mode;
+ mfc_ctx_info("[STREAM] codectype: %s(%d)\n",
+ ctx->src_fmt->name, ctx->codec_mode);
+
+ ctx->pix_format = pix_fmt_mp->pixelformat;
+ if (pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) {
+ ctx->img_height = pix_fmt_mp->height;
+ ctx->img_width = pix_fmt_mp->width;
+ }
+
+ /* As this buffer will contain compressed data, the size is set
+ * to the maximum size.
+ */
+ if (pix_fmt_mp->plane_fmt[0].sizeimage)
+ dec->src_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
+ else
+ dec->src_buf_size = MAX_FRAME_SIZE;
+ mfc_ctx_debug(2, "[STREAM] sizeimage: %d\n", pix_fmt_mp->plane_fmt[0].sizeimage);
+ pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+
+ ret = mfc_rm_instance_open(dev, ctx);
+ if (ret)
+ mfc_ctx_err("Failed to instance open\n");
+
+ mfc_ctx_debug_leave();
+
+ return ret;
+}
+
+/* Request buffers */
+static int mfc_dec_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct mfc_dev *dev = video_drvdata(file);
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct mfc_core *core;
+ struct mfc_core_ctx *core_ctx;
+ int i, ret = 0;
+
+ mfc_ctx_debug_enter();
+
+ if (reqbufs->memory == V4L2_MEMORY_MMAP) {
+ mfc_ctx_err("Not supported memory type (%d)\n", reqbufs->memory);
+ return -EINVAL;
+ }
+
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_ctx_debug(4, "dec src reqbuf(%d)\n", reqbufs->count);
+ /* Can only request buffers after
+ * an instance has been opened.
+ */
+ if (mfc_rm_query_state(ctx, EQUAL, MFCINST_GOT_INST)) {
+ if (reqbufs->count == 0) {
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ ctx->output_state = QUEUE_FREE;
+ return ret;
+ }
+
+ /* Decoding */
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_ctx_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret) {
+ mfc_ctx_err("vb2_reqbufs on src failed\n");
+ return ret;
+ }
+
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_ctx_debug(4, "dec dst reqbuf(%d)\n", reqbufs->count);
+ if (reqbufs->count == 0) {
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+
+ if (!dec->inter_res_change) {
+ for (i = 0; i < MFC_CORE_TYPE_NUM; i++) {
+ if (ctx->op_core_num[i] == MFC_CORE_INVALID)
+ break;
+ core = dev->core[ctx->op_core_num[i]];
+
+ core_ctx = core->core_ctx[ctx->num];
+ mfc_release_codec_buffers(core_ctx);
+ }
+ }
+ ctx->capture_state = QUEUE_FREE;
+ return ret;
+ }
+
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_ctx_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret) {
+ mfc_ctx_err("vb2_reqbufs on capture failed\n");
+ return ret;
+ }
+
+ if (reqbufs->count < ctx->dpb_count) {
+ mfc_ctx_err("Not enough buffers allocated\n");
+ reqbufs->count = 0;
+ vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
+ }
+
+ dec->total_dpb_count = reqbufs->count;
+
+ if (!dec->inter_res_change) {
+ for (i = 0; i < MFC_CORE_TYPE_NUM; i++) {
+ if (ctx->op_core_num[i] == MFC_CORE_INVALID)
+ break;
+
+ core = dev->core[ctx->op_core_num[i]];
+ core_ctx = core->core_ctx[ctx->num];
+ ret = mfc_alloc_codec_buffers(core_ctx);
+ if (ret) {
+ mfc_err("Failed to allocate decoding buffers\n");
+ reqbufs->count = 0;
+ vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
+ }
+ }
+ }
+
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+
+ mfc_rm_request_work(dev, MFC_WORK_TRY, ctx);
+ }
+
+ mfc_ctx_debug_leave();
+
+ return ret;
+}
+
+/* Query buffer */
+static int mfc_dec_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ int ret;
+
+ mfc_ctx_debug_enter();
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_ctx_debug(4, "dec dst querybuf\n");
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ if (ret != 0) {
+ mfc_ctx_err("dec dst: error in vb2_querybuf()\n");
+ return ret;
+ }
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_ctx_debug(4, "dec src querybuf\n");
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ if (ret != 0) {
+ mfc_ctx_err("dec src: error in vb2_querybuf()\n");
+ return ret;
+ }
+ } else {
+ mfc_ctx_err("invalid buf type (%d)\n", buf->type);
+ return -EINVAL;
+ }
+
+ mfc_ctx_debug_leave();
+
+ return ret;
+}
+
+/* Queue a buffer */
+static int mfc_dec_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dev *dev = ctx->dev;
+ int ret = -EINVAL;
+
+ mfc_ctx_debug_enter();
+
+ if (mfc_rm_query_state(ctx, EQUAL_OR, MFCINST_ERROR)) {
+ mfc_ctx_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+ mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type);
+ return -EINVAL;
+ }
+
+ if (!buf->length) {
+ mfc_ctx_err("multiplanar but length is zero\n");
+ return -EIO;
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_ctx_debug(4, "dec src buf[%d] Q\n", buf->index);
+ if (buf->m.planes[0].bytesused > buf->m.planes[0].length) {
+ mfc_ctx_err("data size (%d) %s(%d)\n",
+ buf->m.planes[0].bytesused,
+ "must be less than buffer size",
+ buf->m.planes[0].length);
+ return -EIO;
+ }
+
+ mfc_idle_update_queued(dev, ctx);
+ mfc_rate_update_bitrate(ctx, buf->m.planes[0].bytesused);
+ mfc_rate_update_bufq_framerate(ctx, MFC_TS_SRC_Q);
+ mfc_rate_update_framerate(ctx);
+ mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER);
+
+ if (!buf->m.planes[0].bytesused) {
+ buf->m.planes[0].bytesused = buf->m.planes[0].length;
+ mfc_ctx_debug(2, "Src size zero, changed to buf size %d\n",
+ buf->m.planes[0].bytesused);
+ } else {
+ mfc_ctx_debug(2, "Src size, %d, buf length, %d\n",
+ buf->m.planes[0].bytesused,
+ buf->m.planes[0].length);
+ }
+
+ ret = vb2_qbuf(&ctx->vq_src, NULL, buf);
+ } else {
+ mfc_ctx_debug(4, "dec dst buf[%d] Q\n", buf->index);
+ mfc_idle_update_queued(dev, ctx);
+ mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_Q);
+ mfc_rate_update_framerate(ctx);
+ mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER);
+ ret = vb2_qbuf(&ctx->vq_dst, NULL, buf);
+ }
+
+ mfc_ctx_debug_leave();
+ return ret;
+}
+
+/* Dequeue a buffer */
+static int mfc_dec_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct dec_dpb_ref_info *dst_buf, *src_buf;
+ int ret;
+ int ncount = 0;
+
+ mfc_ctx_debug_enter();
+
+ if (mfc_rm_query_state(ctx, EQUAL, MFCINST_ERROR)) {
+ mfc_ctx_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+ mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type);
+ return -EINVAL;
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ mfc_ctx_debug(4, "dec src buf[%d] DQ\n", buf->index);
+ } else {
+ ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ mfc_ctx_debug(4, "dec dst buf[%d] DQ\n", buf->index);
+
+ if (buf->index >= MFC_MAX_BUFFERS) {
+ mfc_ctx_err("buffer index[%d] range over\n", buf->index);
+ return -EINVAL;
+ }
+
+ /* Memcpy from dec->ref_info to shared memory */
+ if (dec->ref_info) {
+ src_buf = &dec->ref_info[buf->index];
+ for (ncount = 0; ncount < MFC_MAX_BUFFERS; ncount++) {
+ if (src_buf->dpb[ncount].fd[0] == MFC_INFO_INIT_FD)
+ break;
+ mfc_ctx_debug(2, "[REFINFO] DQ index[%d] Released FD = %d\n",
+ buf->index, src_buf->dpb[ncount].fd[0]);
+ }
+
+ if (dec->sh_handle_dpb.vaddr) {
+ dst_buf = (struct dec_dpb_ref_info *)
+ dec->sh_handle_dpb.vaddr + buf->index;
+ memcpy(dst_buf, src_buf, sizeof(struct dec_dpb_ref_info));
+ dst_buf->index = buf->index;
+ }
+ }
+ }
+ mfc_ctx_debug_leave();
+ return ret;
+}
+
+/* Stream on */
+static int mfc_dec_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ int ret = -EINVAL;
+
+ mfc_ctx_debug_enter();
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_ctx_debug(4, "dec src streamon\n");
+ ret = vb2_streamon(&ctx->vq_src, type);
+ } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_ctx_debug(4, "dec dst streamon\n");
+ ret = vb2_streamon(&ctx->vq_dst, type);
+ if (!ret)
+ mfc_rm_qos_control(ctx, MFC_QOS_ON);
+ } else {
+ mfc_ctx_err("unknown v4l2 buffer type\n");
+ }
+
+ mfc_ctx_debug(2, "src: %d, dst: %d, dpb_count = %d\n",
+ mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue),
+ mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+ ctx->dpb_count);
+
+ mfc_ctx_debug_leave();
+
+ return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int mfc_dec_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ int ret = -EINVAL;
+
+ mfc_ctx_debug_enter();
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_ctx_debug(4, "dec src streamoff\n");
+ ret = vb2_streamoff(&ctx->vq_src, type);
+ } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_ctx_debug(4, "dec dst streamoff\n");
+ mfc_rate_reset_bufq_framerate(ctx);
+ ret = vb2_streamoff(&ctx->vq_dst, type);
+ if (!ret)
+ mfc_rm_qos_control(ctx, MFC_QOS_OFF);
+ } else {
+ mfc_ctx_err("unknown v4l2 buffer type\n");
+ }
+
+ mfc_ctx_debug_leave();
+
+ return ret;
+}
+
+static int __mfc_dec_ext_info(struct mfc_ctx *ctx)
+{
+ struct mfc_dev *dev = ctx->dev;
+ int val = 0;
+
+ val |= DEC_SET_DYNAMIC_DPB;
+ val |= DEC_SET_C2_INTERFACE;
+ val |= DEC_SET_BUF_FLAG_CTRL;
+ val |= DEC_SET_FRAME_ERR_TYPE;
+ val |= DEC_SET_OPERATING_FPS;
+ val |= DEC_SET_PRIORITY;
+
+ if (MFC_FEATURE_SUPPORT(dev, dev->pdata->skype))
+ val |= DEC_SET_SKYPE_FLAG;
+
+ mfc_ctx_debug(5, "[CTRLS] ext info val: %#x\n", val);
+
+ return val;
+}
+
+/* Get ctrl */
+static int __mfc_dec_get_ctrl_val(struct mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+ struct mfc_dev *dev = ctx->dev;
+ struct mfc_core *core;
+ struct mfc_core_ctx *core_ctx;
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct mfc_ctx_ctrl *ctx_ctrl;
+ int found = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctrl->value = dec->loop_filter_mpeg4;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ ctrl->value = dec->display_delay;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ /* These context information is need to for only main core */
+ core = mfc_get_main_core_lock(dev, ctx);
+ core_ctx = core->core_ctx[ctx->num];
+
+ if (core_ctx->state >= MFCINST_HEAD_PARSED &&
+ core_ctx->state < MFCINST_ABORT) {
+ ctrl->value = ctx->dpb_count;
+ break;
+ } else if (core_ctx->state != MFCINST_INIT) {
+ mfc_err("Decoding not initialised\n");
+ return -EINVAL;
+ }
+
+ /* Should wait for the header to be parsed */
+ if (mfc_wait_for_done_core_ctx(core_ctx,
+ MFC_REG_R2H_CMD_SEQ_DONE_RET)) {
+ core->sched->yield_work(core, core_ctx);
+ return -EIO;
+ }
+
+ if (core_ctx->state >= MFCINST_HEAD_PARSED &&
+ core_ctx->state < MFCINST_ABORT) {
+ ctrl->value = ctx->dpb_count;
+ } else {
+ mfc_err("Decoding not initialised\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ ctrl->value = dec->slice_enable;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB:
+ /* Not used */
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE:
+ ctrl->value = dec->crc_enable;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE:
+ core = mfc_get_main_core_lock(dev, ctx);
+ core_ctx = core->core_ctx[ctx->num];
+ if (ctx->is_dpb_realloc &&
+ mfc_rm_query_state(ctx, EQUAL, MFCINST_HEAD_PARSED))
+ ctrl->value = MFCSTATE_DEC_S3D_REALLOC;
+ else if (core_ctx->state == MFCINST_RES_CHANGE_FLUSH ||
+ core_ctx->state == MFCINST_RES_CHANGE_END ||
+ core_ctx->state == MFCINST_HEAD_PARSED ||
+ dec->inter_res_change)
+ ctrl->value = MFCSTATE_DEC_RES_DETECT;
+ else if (mfc_rm_query_state(ctx, EQUAL, MFCINST_FINISHING))
+ ctrl->value = MFCSTATE_DEC_TERMINATING;
+ else
+ ctrl->value = MFCSTATE_PROCESSING;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING:
+ ctrl->value = dec->sei_parse;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING:
+ ctrl->value = dec->idr_decoding;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE:
+ ctrl->value = mfc_rate_get_framerate(ctx);
+ break;
+ case V4L2_CID_MPEG_MFC_GET_VERSION_INFO:
+ ctrl->value = dev->pdata->ip_ver;
+ break;
+ case V4L2_CID_MPEG_VIDEO_QOS_RATIO:
+ ctrl->value = ctx->qos_ratio;
+ break;
+ case V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE:
+ ctrl->value = dec->is_dynamic_dpb;
+ break;
+ case V4L2_CID_MPEG_MFC_GET_EXT_INFO:
+ ctrl->value = __mfc_dec_ext_info(ctx);
+ break;
+ case V4L2_CID_MPEG_MFC_GET_DRIVER_INFO:
+ ctrl->value = MFC_DRIVER_INFO;
+ break;
+ case V4L2_CID_MPEG_VIDEO_UNCOMP_FMT:
+ if (dec->uncomp_fmt)
+ ctrl->value = dec->uncomp_fmt->fourcc;
+ else
+ ctrl->value = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GET_DISPLAY_DELAY:
+ /* These context information is need to for only main core */
+ core = mfc_get_main_core_lock(dev, ctx);
+ core_ctx = core->core_ctx[ctx->num];
+ if (core_ctx->state >= MFCINST_HEAD_PARSED) {
+ ctrl->value = dec->frame_display_delay;
+ } else {
+ mfc_err("display delay information not parsed yet\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_PRIORITY:
+ ctrl->value = ctx->prio;
+ mfc_ctx_debug(2, "[PRIO] user get priority: %d (%d)\n",
+ ctrl->value, ctx->user_prio);
+ break;
+ case V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE:
+ ctrl->value = (ctx->multi_view_enable || ctx->ready_to_be_multi_view_enable);
+ break;
+ default:
+ list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+ if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET))
+ continue;
+
+ if (ctx_ctrl->id == ctrl->id) {
+ if (ctx_ctrl->get.has_new) {
+ ctx_ctrl->get.has_new = 0;
+ ctrl->value = ctx_ctrl->get.val;
+ } else {
+ mfc_ctx_debug(5, "[CTRLS] %s: 0x%08x\n",
+ "Control value is not up to date",
+ ctrl->id);
+ return -EINVAL;
+ }
+
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ mfc_ctx_debug(5, "[CTRLS] get id: %#x, value: %d\n", ctrl->id, ctrl->value);
+
+ return 0;
+}
+
+/* Set a ctrl */
+static int mfc_dec_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dec *dec = ctx->dec_priv;
+ struct mfc_ctx_ctrl *ctx_ctrl;
+ int ret = 0;
+ int found = 0;
+
+ mfc_ctx_debug_enter();
+
+ ret = __mfc_dec_check_ctrl_val(ctx, ctrl);
+ if (ret)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ dec->loop_filter_mpeg4 = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ dec->display_delay = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ dec->slice_enable = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB:
+ /* Not used */
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE:
+ dec->crc_enable = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING:
+ dec->sei_parse = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING:
+ dec->idr_decoding = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY:
+ dec->immediate_display = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE:
+ dec->is_dts_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START:
+ mutex_lock(&ctx->drc_wait_mutex);
+ ctx->wait_state = ctrl->value;
+ mutex_unlock(&ctx->drc_wait_mutex);
+ break;
+ case V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE:
+ mfc_ctx_err("[DPB] not supported CID: 0x%x\n", ctrl->id);
+ break;
+ case V4L2_CID_MPEG_VIDEO_QOS_RATIO:
+ ctx->qos_ratio = ctrl->value;
+ mfc_ctx_info("[QoS] set %d qos_ratio\n", ctrl->value);
+ break;
+ case V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE:
+ /* is_dynamic_dpb is controlled by driver */
+ if (dec->is_dynamic_dpb == 0)
+ mfc_ctx_debug(2, "[PLUGIN] is_dynamic_dpb is disabled by driver\n");
+ if (!ctrl->value)
+ mfc_ctx_err("[DPB] user has to enable is_dynamic_dpb by default\n");
+ break;
+ case V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE:
+ if (dec->sh_handle_dpb.fd == -1) {
+ dec->sh_handle_dpb.fd = ctrl->value;
+ if (mfc_mem_get_user_shared_handle(ctx, &dec->sh_handle_dpb, "DPB"))
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE:
+ ctx->buf_process_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT:
+ dec->detect_black_bar = ctrl->value;
+ if (IS_BLACKBAR_OFF(ctx)) {
+ mfc_ctx_info("[BLACKBAR] black bar detection doesn't work\n");
+ dec->detect_black_bar = 0;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODING_ORDER:
+ dec->decoding_order = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP:
+ ctx->skip_lazy_unmap = ctrl->value;
+ mfc_ctx_debug(2, "[LAZY_UNMAP] lazy unmap %s\n",
+ ctx->skip_lazy_unmap ? "disable" : "enable");
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE:
+ mfc_ctx_debug(2, "[QoS] user set the operating frame rate: %d\n", ctrl->value);
+ ctx->operating_framerate = ctrl->value;
+ mfc_rm_update_real_time(ctx);
+ break;
+ case V4L2_CID_MPEG_VIDEO_PRIORITY:
+ mfc_ctx_debug(2, "[PRIO] user set priority: %d\n", ctrl->value);
+ ctx->user_prio = ctrl->value;
+ mfc_rm_update_real_time(ctx);
+ break;
+ case V4L2_CID_MPEG_MFC_HEIF_MODE:
+ mfc_ctx_debug(2, "[HEIF] heif mode: %d\n", ctrl->value);
+ ctx->is_heif_mode = ctrl->value;
+ break;
+ default:
+ list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+ if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET))
+ continue;
+
+ if (ctx_ctrl->id == ctrl->id) {
+ ctx_ctrl->set.has_new = 1;
+ ctx_ctrl->set.val = ctrl->value;
+
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ mfc_ctx_debug_leave();
+
+ return 0;
+}
+
+static int mfc_dec_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct v4l2_ext_control *ext_ctrl;
+ struct v4l2_control ctrl;
+ int i;
+ int ret = 0;
+
+ if (f->which != V4L2_CTRL_CLASS_CODEC && f->which != V4L2_CTRL_CLASS_USER)
+ return -EINVAL;
+
+ for (i = 0; i < f->count; i++) {
+ ext_ctrl = (f->controls + i);
+
+ ctrl.id = ext_ctrl->id;
+ ctrl.value = ext_ctrl->value;
+
+ ret = mfc_dec_s_ctrl(file, priv, &ctrl);
+ if (ret != 0) {
+ f->error_idx = i;
+ break;
+ }
+ mfc_ctx_debug(5, "[CTRLS] set id: %#x, value: %d\n", ctrl.id, ctrl.value);
+ }
+
+ return 0;
+}
+
+static void __mfc_dec_update_disp_res(struct mfc_ctx *ctx, struct v4l2_selection *s)
+{
+ struct mfc_dec *dec = ctx->dec_priv;
+
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = dec->disp_drc.width[dec->disp_drc.pop_idx];
+ s->r.height = dec->disp_drc.height[dec->disp_drc.pop_idx];
+ mfc_ctx_debug(2, "[FRAME] Composing info: w=%d h=%d\n", s->r.width, s->r.height);
+
+ dec->disp_drc.disp_res_change--;
+ mfc_ctx_debug(3, "[DRC] disp_res_change[%d] count %d\n",
+ dec->disp_drc.pop_idx, dec->disp_drc.disp_res_change);
+ dec->disp_drc.pop_idx = (dec->disp_drc.pop_idx + 1) % MFC_MAX_DRC_FRAME;
+
+ if (!dec->disp_drc.disp_res_change) {
+ dec->disp_drc.push_idx = 0;
+ dec->disp_drc.pop_idx = 0;
+ }
+
+ /*
+ * Do not clear WAIT_G_FMT except RUNNING state
+ * because the resolution change (DRC) case uses WAIT_G_FMT
+ */
+ if (mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING) &&
+ (ctx->wait_state & WAIT_G_FMT) != 0) {
+ ctx->wait_state &= ~(WAIT_G_FMT);
+ mfc_ctx_debug(2, "clear WAIT_G_FMT %d\n", ctx->wait_state);
+ MFC_TRACE_CTX("** DEC clear WAIT_G_FMT(wait_state %d)\n", ctx->wait_state);
+ }
+}
+
+/* Get cropping information */
+static int mfc_dec_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct mfc_dev *dev = ctx->dev;
+ struct mfc_core *core;
+ struct mfc_core_ctx *core_ctx;
+ struct mfc_dec *dec = ctx->dec_priv;
+
+ mfc_ctx_debug_enter();
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ core = mfc_get_main_core_lock(dev, ctx);
+ core_ctx = core->core_ctx[ctx->num];
+
+ mutex_lock(&ctx->drc_wait_mutex);
+ if (dec->disp_drc.disp_res_change) {
+ __mfc_dec_update_disp_res(ctx, s);
+ mutex_unlock(&ctx->drc_wait_mutex);
+ return 0;
+ }
+ mutex_unlock(&ctx->drc_wait_mutex);
+
+ if (!NEED_TO_GET_CROP(core_ctx)) {
+ mfc_err("ready to get compose failed\n");
+ return -EINVAL;
+ }
+
+ if (mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING) &&
+ dec->detect_black_bar && dec->black_bar_updated) {
+ s->r.left = dec->black_bar.left;
+ s->r.top = dec->black_bar.top;
+ s->r.width = dec->black_bar.width;
+ s->r.height = dec->black_bar.height;
+ mfc_debug(2, "[FRAME][BLACKBAR] Cropping info: l=%d t=%d w=%d h=%d\n",
+ dec->black_bar.left,
+ dec->black_bar.top,
+ dec->black_bar.width,
+ dec->black_bar.height);
+ } else {
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264 ||
+ ctx->src_fmt->fourcc == V4L2_PIX_FMT_HEVC) {
+ s->r.left = dec->cr_left;
+ s->r.top = dec->cr_top;
+ s->r.width = ctx->img_width - dec->cr_left - dec->cr_right;
+ s->r.height = ctx->img_height - dec->cr_top - dec->cr_bot;
+ mfc_debug(2, "[FRAME] %s: l=%d t=%d w=%d h=%d (r=%d b=%d fw=%d fh=%d)\n",
+ "Composing info", s->r.left, s->r.top, s->r.width, s->r.height,
+ dec->cr_right, dec->cr_bot, ctx->img_width, ctx->img_height);
+ } else {
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = ctx->img_width;
+ s->r.height = ctx->img_height;
+ mfc_debug(2, "[FRAME] Composing info: w=%d h=%d fw=%d fh=%d\n",
+ s->r.width, s->r.height,
+ ctx->img_width, ctx->img_height);
+ }
+ }
+
+ mfc_ctx_debug_leave();
+ return 0;
+}
+
+static int mfc_dec_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+ struct v4l2_ext_control *ext_ctrl;
+ struct v4l2_control ctrl;
+ int i;
+ int ret = 0;
+
+ if (f->which != V4L2_CTRL_CLASS_CODEC && f->which != V4L2_CTRL_CLASS_USER)
+ return -EINVAL;
+
+ for (i = 0; i < f->count; i++) {
+ ext_ctrl = (f->controls + i);
+
+ ctrl.id = ext_ctrl->id;
+
+ ret = __mfc_dec_get_ctrl_val(ctx, &ctrl);
+ if (ret == 0) {
+ ext_ctrl->value = ctrl.value;
+ } else {
+ f->error_idx = i;
+ break;
+ }
+
+ mfc_ctx_debug(5, "[CTRLS][%d] id: %#x, value: %d\n",
+ i, ext_ctrl->id, ext_ctrl->value);
+ }
+
+ return ret;
+}
+
+/* Initialize for default format */
+void mfc_dec_set_default_format(struct mfc_ctx *ctx)
+{
+ struct mfc_fmt *fmt = NULL;
+
+ /* Set default format for source */
+ fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_H264);
+ if (!fmt) {
+ /* NEVER come here */
+ mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n");
+ fmt = &mfc_formats[0];
+ }
+ ctx->src_fmt = fmt;
+
+ /* Set default format for destination */
+ fmt = __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV12M);
+ if (!fmt) {
+ /* NEVER come here */
+ mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n");
+ fmt = &mfc_formats[0];
+ }
+ ctx->dst_fmt = fmt;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops mfc_dec_ioctl_ops = {
+ .vidioc_querycap = mfc_dec_querycap,
+ .vidioc_enum_fmt_vid_cap = mfc_dec_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = mfc_dec_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = mfc_dec_g_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_out_mplane = mfc_dec_g_fmt_vid_out_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = mfc_dec_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = mfc_dec_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = mfc_dec_s_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_out_mplane = mfc_dec_s_fmt_vid_out_mplane,
+ .vidioc_reqbufs = mfc_dec_reqbufs,
+ .vidioc_querybuf = mfc_dec_querybuf,
+ .vidioc_qbuf = mfc_dec_qbuf,
+ .vidioc_dqbuf = mfc_dec_dqbuf,
+ .vidioc_streamon = mfc_dec_streamon,
+ .vidioc_streamoff = mfc_dec_streamoff,
+ .vidioc_s_ext_ctrls = mfc_dec_s_ext_ctrls,
+ .vidioc_g_selection = mfc_dec_g_selection,
+ .vidioc_g_ext_ctrls = mfc_dec_g_ext_ctrls,
+};
+
+const struct v4l2_ioctl_ops *mfc_get_dec_v4l2_ioctl_ops(void)
+{
+ return &mfc_dec_ioctl_ops;
+}
diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h
new file mode 100644
index 000000000000..04652a71cd23
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * mfc_dec_v4l2.h
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __MFC_DEC_V4L2_H
+#define __MFC_DEC_V4L2_H __FILE__
+
+#include "base/mfc_common.h"
+
+const struct v4l2_ioctl_ops *mfc_get_dec_v4l2_ioctl_ops(void);
+void mfc_dec_set_default_format(struct mfc_ctx *ctx);
+
+#endif /* __MFC_DEC_V4L2_H */
--
2.34.1
Powered by blists - more mailing lists