From: Nagaraju Siddineni <[email protected]> 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 <[email protected]> Signed-off-by: Himanshu Dewangan <[email protected]> --- .../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
