media: mfc: add the MFC driver of kernel 4.9
authorJeonghee Kim <jhhhh.kim@samsung.com>
Mon, 15 Jan 2018 07:46:47 +0000 (16:46 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:20 +0000 (20:22 +0300)
Change-Id: Iaeee190bb1a9ddced67691e6d696d358cfdfee62
Signed-off-by: Jeonghee Kim <jhhhh.kim@samsung.com>
62 files changed:
drivers/media/platform/exynos/mfc/Kconfig [new file with mode: 0644]
drivers/media/platform/exynos/mfc/Makefile [new file with mode: 0644]
drivers/media/platform/exynos/mfc/exynos_mfc_media.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_buf.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_buf.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_cal.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_cal.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_cmd.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_cmd.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_common.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_data_struct.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_debug.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_dec.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_dec.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_dec_internal.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_dec_ops.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_dec_vb2_ops.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc_internal.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc_ops.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_enc_vb2_ops.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_hwfc_internal.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_inst.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_inst.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_irq.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_irq.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_macros.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_mem.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_mem.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_opr.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_opr.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_otf.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_otf.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_pm.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_pm.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_qos.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_qos.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_queue.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_queue.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_reg.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_reg.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_regs_v10.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_sync.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_sync.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_utils.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_utils.h [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.c [new file with mode: 0644]
drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.h [new file with mode: 0644]
include/media/s5p_mfc_hwfc.h [new file with mode: 0644]
include/trace/events/mfc.h [new file with mode: 0644]

diff --git a/drivers/media/platform/exynos/mfc/Kconfig b/drivers/media/platform/exynos/mfc/Kconfig
new file mode 100644 (file)
index 0000000..4f96290
--- /dev/null
@@ -0,0 +1,18 @@
+config VIDEO_EXYNOS_MFC
+       bool "Exynos MFC Driver"
+       default n
+       depends on VIDEO_EXYNOS
+       select VIDEOBUF2_CORE
+       select EXYNOS_MFC_V12
+       ---help---
+         MFC driver for V4L2.
+
+choice
+depends on VIDEO_EXYNOS_MFC
+prompt "MFC version"
+default EXYNOS_MFC_V12
+config EXYNOS_MFC_V12
+prompt "MFC version 12"
+       bool
+       depends on ARCH_EXYNOS9
+endchoice
diff --git a/drivers/media/platform/exynos/mfc/Makefile b/drivers/media/platform/exynos/mfc/Makefile
new file mode 100644 (file)
index 0000000..75e203e
--- /dev/null
@@ -0,0 +1,7 @@
+obj-$(CONFIG_VIDEO_EXYNOS_MFC) := s5p-mfc.o
+s5p-mfc-y += s5p_mfc.o s5p_mfc_irq.o s5p_mfc_dec.o s5p_mfc_dec_vb2_ops.o s5p_mfc_enc.o s5p_mfc_enc_vb2_ops.o
+s5p-mfc-y += s5p_mfc_ctrl.o s5p_mfc_hwlock.o s5p_mfc_nal_q.o s5p_mfc_watchdog.o s5p_mfc_opr.o s5p_mfc_sync.o
+s5p-mfc-y += s5p_mfc_pm.o s5p_mfc_inst.o s5p_mfc_cmd.o s5p_mfc_cal.o s5p_mfc_reg.o
+s5p-mfc-y += s5p_mfc_dec_ops.o s5p_mfc_enc_ops.o s5p_mfc_enc_param.o
+s5p-mfc-y += s5p_mfc_queue.o s5p_mfc_buf.o s5p_mfc_utils.o s5p_mfc_qos.o s5p_mfc_mem.o
+s5p-mfc-y += s5p_mfc_debugfs.o s5p_mfc_otf.o
diff --git a/drivers/media/platform/exynos/mfc/exynos_mfc_media.h b/drivers/media/platform/exynos/mfc/exynos_mfc_media.h
new file mode 100644 (file)
index 0000000..b6fdd00
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * drivers/media/platform/exynos/mfc/exynos_mfc_media.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 __EXYNOS_MFC_MEDIA_H
+#define __EXYNOS_MFC_MEDIA_H __FILE__
+
+#include <linux/videodev2_exynos_media.h>
+
+/* RGB formats */
+#define V4L2_PIX_FMT_RGB32X    v4l2_fourcc('R', 'G', 'B', 'X') /* 32  RGB-8-8-8-8   */
+
+/* two planes -- one Y, one Cr + Cb interleaved  */
+#define V4L2_PIX_FMT_YUV444_2P v4l2_fourcc('Y', 'U', '2', 'P') /* 24  Y/CbCr */
+#define V4L2_PIX_FMT_YVU444_2P v4l2_fourcc('Y', 'V', '2', 'P') /* 24  Y/CrCb */
+
+/* three planes -- one Y, one Cr, one Cb */
+#define V4L2_PIX_FMT_YUV444_3P v4l2_fourcc('Y', 'U', '3', 'P') /* 24  Y/Cb/Cr */
+
+/* two non contiguous planes - one Y, one Cr + Cb interleaved  */
+/* 21  Y/CrCb 4:2:0  */
+#define V4L2_PIX_FMT_NV21M    v4l2_fourcc('N', 'M', '2', '1')
+/* 12  Y/CbCr 4:2:0 16x16 macroblocks */
+#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2')
+
+/* compressed formats */
+#define V4L2_PIX_FMT_H264_MVC v4l2_fourcc('M', '2', '6', '4') /* H264 MVC */
+#define V4L2_PIX_FMT_FIMV     v4l2_fourcc('F', 'I', 'M', 'V') /* FIMV  */
+#define V4L2_PIX_FMT_FIMV1    v4l2_fourcc('F', 'I', 'M', '1') /* FIMV1 */
+#define V4L2_PIX_FMT_FIMV2    v4l2_fourcc('F', 'I', 'M', '2') /* FIMV2 */
+#define V4L2_PIX_FMT_FIMV3    v4l2_fourcc('F', 'I', 'M', '3') /* FIMV3 */
+#define V4L2_PIX_FMT_FIMV4    v4l2_fourcc('F', 'I', 'M', '4') /* FIMV4 */
+#define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_VP9      v4l2_fourcc('V', 'P', '9', '0') /* VP9 */
+#define V4L2_PIX_FMT_HEVC     v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC */
+#define V4L2_PIX_FMT_BPG      v4l2_fourcc('B', 'P', 'G', '0') /* BPG */
+
+enum v4l2_mpeg_mfc51_video_frame_type {
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_NOT_CODED      = 0,
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_I_FRAME        = 1,
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_P_FRAME        = 2,
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_B_FRAME        = 3,
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_SKIPPED        = 4,
+       V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_OTHERS         = 5,
+};
+
+enum v4l2_mpeg_video_hevc_hierarchical_coding_type {
+       V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B      = 0,
+       V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P      = 1,
+};
+
+/* new entry for enum v4l2_mpeg_video_mpeg4_level */
+#define V4L2_MPEG_VIDEO_MPEG4_LEVEL_6                  8
+
+/* new entry for enum v4l2_mpeg_video_header_mode */
+#define V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY       2
+
+/* new entry for enum v4l2_mpeg_video_multi_slice_mode */
+#define V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW            3
+#define V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES       4
+
+/* new entry for enum v4l2_mpeg_video_h264_profile */
+#define V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH  17
+
+#define V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_S_B \
+       V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+
+#define V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH  V4L2_CID_MPEG_VIDEO_GOP_SIZE
+#define V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH  V4L2_CID_MPEG_VIDEO_BITRATE
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH                \
+                                       V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE
+
+
+/* CID base for MFC controls (MPEG_CLASS) */
+#define V4L2_CID_MPEG_MFC_BASE         (V4L2_CTRL_CLASS_MPEG | 0x2000)
+
+#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 1)
+#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 2)
+#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 3)
+#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 4)
+#define V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 5)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 6)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 7)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 8)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 9)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA_BOT    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 10)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA_BOT  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 11)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_GENERATED                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 12)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 13)
+#define V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 14)
+#define V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 15)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 16)
+#define V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 17)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_COUNT          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 18)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 19)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 20)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 21)
+#define V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 22)
+#define V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 23)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 24)
+#define V4L2_CID_MPEG_MFC6X_VIDEO_FRAME_DELTA          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 25)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA1     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 26)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_LUMA   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 27)
+#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_CHROMA \
+                                       (V4L2_CID_MPEG_MFC_BASE + 28)
+
+#define V4L2_CID_MPEG_VIDEO_H264_MVC_VIEW_ID                   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 42)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 43)
+#define V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 44)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE                   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 45)
+#define V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 46)
+#define V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 47)
+#define V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 48)
+#define V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 49)
+#define V4L2_CID_MPEG_VIDEO_QOS_RATIO                          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 50)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT \
+                                       (V4L2_CID_MPEG_MFC_BASE + 51)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH \
+                                       (V4L2_CID_MPEG_MFC_BASE + 52)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 53)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 54)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 55)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 56)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 57)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 58)
+#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 59)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION                  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 60)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 61)
+#define V4L2_CID_MPEG_VIDEO_VP8_MIN_QP                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 62)
+#define V4L2_CID_MPEG_VIDEO_VP8_MAX_QP                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 63)
+#define V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 64)
+#define V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 65)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 66)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 67)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 68)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 69)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 70)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 71)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 72)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 73)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 74)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 75)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 76)
+#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 77)
+#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 78)
+#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH \
+                                       (V4L2_CID_MPEG_MFC_BASE + 79)
+#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 80)
+#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 81)
+#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 82)
+
+/* ~ 90 : Reserved for using later */
+
+#define V4L2_CID_MPEG_MFC_GET_VERSION_INFO                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 91)
+#define V4L2_CID_MPEG_MFC_GET_EXTRA_BUFFER_SIZE                        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 92)
+#define V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 93)
+#define V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 95)
+#define V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE               \
+                                       (V4L2_CID_MPEG_MFC_BASE + 96)
+#define V4L2_CID_MPEG_MFC_GET_EXT_INFO                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 97)
+#define V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 98)
+#define V4L2_CID_MPEG_MFC_GET_10BIT_INFO                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 99)
+#define V4L2_CID_MPEG_MFC_H264_ENABLE_LTR              \
+                                       (V4L2_CID_MPEG_MFC_BASE + 100)
+#define V4L2_CID_MPEG_MFC_H264_MARK_LTR                        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 101)
+#define V4L2_CID_MPEG_MFC_H264_USE_LTR                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 102)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 103)
+#define V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 104)
+#define V4L2_CID_MPEG_MFC_CONFIG_QP                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 105)
+#define V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 106)
+#define V4L2_CID_MPEG_MFC_GET_DRIVER_INFO                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 107)
+#define V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 108)
+
+/* CIDs for HEVC encoding. Number gaps are for compatibility */
+
+#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP                                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 110)
+#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP                                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 111)
+#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 112)
+#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 113)
+#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 114)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE \
+                                       (V4L2_CID_MPEG_MFC_BASE + 115)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 116)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 117)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 118)
+#define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 120)
+#define V4L2_CID_MPEG_VIDEO_HEVC_LEVEL                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 121)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 122)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 123)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 124)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 125)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE               \
+                                       (V4L2_CID_MPEG_MFC_BASE + 126)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 127)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 128)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 129)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 130)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 131)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 132)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 133)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 134)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE               \
+                                       (V4L2_CID_MPEG_MFC_BASE + 135)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 136)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 137)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 138)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 139)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 140)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 141)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 142)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 143)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 144)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 145)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 146)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 147)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 148)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE \
+                                       (V4L2_CID_MPEG_MFC_BASE + 149)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_QP_INDEX_CR             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 150)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_QP_INDEX_CB             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 151)
+#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 152)
+#define V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 153)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 154)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 155)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 156)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 157)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 158)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 159)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 160)
+#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 161)
+
+/* CIDs for VP9 encoding. Number gaps are for compatibility */
+#define V4L2_CID_MPEG_VIDEO_VP9_VERSION                                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 163)
+#define V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE                  \
+                                       (V4L2_CID_MPEG_MFC_BASE + 164)
+#define V4L2_CID_MPEG_VIDEO_VP9_MIN_QP                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 165)
+#define V4L2_CID_MPEG_VIDEO_VP9_MAX_QP                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 166)
+#define V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 167)
+#define V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 168)
+#define V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL                        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 169)
+#define V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD              \
+                                       (V4L2_CID_MPEG_MFC_BASE + 170)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 171)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 172)
+#define V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 173)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 174)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH \
+                                       (V4L2_CID_MPEG_MFC_BASE + 175)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 176)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 177)
+#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 178)
+#define V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 179)
+#define V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 180)
+#define V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 181)
+#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE                                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 182)
+
+/* CIDs for new common interface */
+#define V4L2_CID_MPEG_VIDEO_ROI_CONTROL                                \
+                                       (V4L2_CID_MPEG_MFC_BASE + 190)
+#define V4L2_CID_MPEG_VIDEO_ROI_ENABLE                         \
+                                       (V4L2_CID_MPEG_MFC_BASE + 191)
+#define V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 192)
+#define V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 193)
+#define V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT                   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 194)
+#define V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 195)
+#define V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 196)
+#define V4L2_CID_MPEG_VIDEO_YSUM                               \
+                                       (V4L2_CID_MPEG_MFC_BASE + 197)
+#define V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 198)
+
+/* QP BOUND interface */
+#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 201)
+#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 202)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 203)
+#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 204)
+#define V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 205)
+#define V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 206)
+#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 207)
+#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 208)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 209)
+#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 210)
+#define V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 211)
+#define V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P                       \
+                                       (V4L2_CID_MPEG_MFC_BASE + 212)
+#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 213)
+#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 214)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 215)
+#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 216)
+#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 217)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B                     \
+                                       (V4L2_CID_MPEG_MFC_BASE + 218)
+
+/* SEI & VUI PARSING FOR HDR DISPLAY */
+#define V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 219)
+#define V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT              \
+                                       (V4L2_CID_MPEG_MFC_BASE + 220)
+#define V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 221)
+#define V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE          \
+                                       (V4L2_CID_MPEG_MFC_BASE + 222)
+#define V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS                        \
+                                       (V4L2_CID_MPEG_MFC_BASE + 223)
+#define V4L2_CID_MPEG_VIDEO_FORMAT                             \
+                                       (V4L2_CID_MPEG_MFC_BASE + 224)
+#define V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 225)
+#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 226)
+#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 227)
+#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2            \
+                                       (V4L2_CID_MPEG_MFC_BASE + 228)
+#define V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 229)
+#define V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES                   \
+                                       (V4L2_CID_MPEG_MFC_BASE + 230)
+#define V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS           \
+                                       (V4L2_CID_MPEG_MFC_BASE + 231)
+
+#define V4L2_CID_MPEG_VIDEO_BPG_THUMBNAIL_SIZE                 \
+                                       (V4L2_CID_MPEG_MFC_BASE + 250)
+#define V4L2_CID_MPEG_VIDEO_BPG_EXIF_SIZE                      \
+                                       (V4L2_CID_MPEG_MFC_BASE + 251)
+#define V4L2_CID_MPEG_VIDEO_BPG_HEADER_SIZE                    \
+                                       (V4L2_CID_MPEG_MFC_BASE + 252)
+
+#endif /* __EXYNOS_MFC_MEDIA_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc.c b/drivers/media/platform/exynos/mfc/s5p_mfc.c
new file mode 100644 (file)
index 0000000..df72098
--- /dev/null
@@ -0,0 +1,1470 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc.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/module.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <video/videonode.h>
+#include <linux/of.h>
+#include <linux/smc.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "s5p_mfc_common.h"
+
+#include "s5p_mfc_irq.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_enc.h"
+
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_otf.h"
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_debugfs.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_inst.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/mfc.h>
+
+#define S5P_MFC_NAME                   "s5p-mfc"
+#define S5P_MFC_DEC_NAME               "s5p-mfc-dec"
+#define S5P_MFC_ENC_NAME               "s5p-mfc-enc"
+#define S5P_MFC_DEC_DRM_NAME           "s5p-mfc-dec-secure"
+#define S5P_MFC_ENC_DRM_NAME           "s5p-mfc-enc-secure"
+#define S5P_MFC_ENC_OTF_NAME           "s5p-mfc-enc-otf"
+#define S5P_MFC_ENC_OTF_DRM_NAME       "s5p-mfc-enc-otf-secure"
+
+struct _mfc_trace g_mfc_trace[MFC_TRACE_COUNT_MAX];
+struct _mfc_trace g_mfc_trace_hwlock[MFC_TRACE_COUNT_MAX];
+struct s5p_mfc_dev *g_mfc_dev;
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+static struct proc_dir_entry *mfc_proc_entry;
+
+#define MFC_PROC_ROOT                  "mfc"
+#define MFC_PROC_INSTANCE_NUMBER       "instance_number"
+#define MFC_PROC_DRM_INSTANCE_NUMBER   "drm_instance_number"
+#define MFC_PROC_FW_STATUS             "fw_status"
+#endif
+
+#define DEF_DEC_SRC_FMT        9
+#define DEF_DEC_DST_FMT        5
+
+#define DEF_ENC_SRC_FMT        5
+#define DEF_ENC_DST_FMT        13
+
+void s5p_mfc_butler_worker(struct work_struct *work)
+{
+       struct s5p_mfc_dev *dev;
+
+       dev = container_of(work, struct s5p_mfc_dev, butler_work);
+
+       s5p_mfc_try_run(dev);
+}
+
+extern struct s5p_mfc_ctrls_ops decoder_ctrls_ops;
+extern struct vb2_ops s5p_mfc_dec_qops;
+extern struct s5p_mfc_fmt dec_formats[];
+
+static void mfc_deinit_dec_ctx(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+
+       s5p_mfc_delete_queue(&ctx->src_buf_queue);
+       s5p_mfc_delete_queue(&ctx->dst_buf_queue);
+       s5p_mfc_delete_queue(&ctx->src_buf_nal_queue);
+       s5p_mfc_delete_queue(&ctx->dst_buf_nal_queue);
+       s5p_mfc_delete_queue(&ctx->ref_buf_queue);
+
+       s5p_mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle);
+       kfree(dec->ref_info);
+       kfree(dec);
+}
+
+static int mfc_init_dec_ctx(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       int ret = 0;
+       int i;
+
+       dec = kzalloc(sizeof(struct s5p_mfc_dec), GFP_KERNEL);
+       if (!dec) {
+               mfc_err_dev("failed to allocate decoder private data\n");
+               return -ENOMEM;
+       }
+       ctx->dec_priv = dec;
+
+       ctx->inst_no = MFC_NO_INSTANCE_SET;
+
+       s5p_mfc_create_queue(&ctx->src_buf_queue);
+       s5p_mfc_create_queue(&ctx->dst_buf_queue);
+       s5p_mfc_create_queue(&ctx->src_buf_nal_queue);
+       s5p_mfc_create_queue(&ctx->dst_buf_nal_queue);
+       s5p_mfc_create_queue(&ctx->ref_buf_queue);
+
+       for (i = 0; i < MFC_MAX_BUFFERS; i++) {
+               INIT_LIST_HEAD(&ctx->src_ctrls[i]);
+               INIT_LIST_HEAD(&ctx->dst_ctrls[i]);
+       }
+       ctx->src_ctrls_avail = 0;
+       ctx->dst_ctrls_avail = 0;
+
+       ctx->capture_state = QUEUE_FREE;
+       ctx->output_state = QUEUE_FREE;
+
+       s5p_mfc_change_state(ctx, MFCINST_INIT);
+       ctx->type = MFCINST_DECODER;
+       ctx->c_ops = &decoder_ctrls_ops;
+       ctx->src_fmt = &dec_formats[DEF_DEC_SRC_FMT];
+       ctx->dst_fmt = &dec_formats[DEF_DEC_DST_FMT];
+
+       s5p_mfc_qos_reset_framerate(ctx);
+
+       ctx->qos_ratio = 100;
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       INIT_LIST_HEAD(&ctx->qos_list);
+#endif
+       INIT_LIST_HEAD(&ctx->ts_list);
+
+       dec->display_delay = -1;
+       dec->is_interlaced = 0;
+       dec->immediate_display = 0;
+       dec->is_dts_mode = 0;
+       dec->err_reuse_flag = 0;
+
+       dec->is_dynamic_dpb = 1;
+       dec->dynamic_used = 0;
+       dec->is_dpb_full = 0;
+       s5p_mfc_cleanup_assigned_fd(ctx);
+       s5p_mfc_clear_assigned_dpb(ctx);
+       dec->sh_handle.fd = -1;
+       dec->ref_info = kzalloc(
+               (sizeof(struct dec_dpb_ref_info) * MFC_MAX_DPBS), GFP_KERNEL);
+       if (!dec->ref_info) {
+               mfc_err_dev("failed to allocate decoder information data\n");
+               ret = -ENOMEM;
+               goto fail_dec_init;
+       }
+       for (i = 0; i < MFC_MAX_BUFFERS; i++)
+               dec->ref_info[i].dpb[0].fd[0] = MFC_INFO_INIT_FD;
+
+       /* Init videobuf2 queue for OUTPUT */
+       ctx->vq_src.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+       ctx->vq_src.drv_priv = ctx;
+       ctx->vq_src.buf_struct_size = sizeof(struct s5p_mfc_buf);
+       ctx->vq_src.io_modes = VB2_USERPTR | VB2_DMABUF;
+       ctx->vq_src.ops = &s5p_mfc_dec_qops;
+       ctx->vq_src.mem_ops = s5p_mfc_mem_ops();
+       ctx->vq_src.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       ret = vb2_queue_init(&ctx->vq_src);
+       if (ret) {
+               mfc_err_dev("Failed to initialize videobuf2 queue(output)\n");
+               goto fail_dec_init;
+       }
+       /* Init videobuf2 queue for CAPTURE */
+       ctx->vq_dst.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+       ctx->vq_dst.drv_priv = ctx;
+       ctx->vq_dst.buf_struct_size = sizeof(struct s5p_mfc_buf);
+       ctx->vq_dst.io_modes = VB2_USERPTR | VB2_DMABUF;
+       ctx->vq_dst.ops = &s5p_mfc_dec_qops;
+       ctx->vq_dst.mem_ops = s5p_mfc_mem_ops();
+       ctx->vq_dst.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       ret = vb2_queue_init(&ctx->vq_dst);
+       if (ret) {
+               mfc_err_dev("Failed to initialize videobuf2 queue(capture)\n");
+               goto fail_dec_init;
+       }
+
+       return ret;
+
+fail_dec_init:
+       mfc_deinit_dec_ctx(ctx);
+       return ret;
+}
+
+extern struct s5p_mfc_ctrls_ops encoder_ctrls_ops;
+extern struct vb2_ops s5p_mfc_enc_qops;
+extern struct s5p_mfc_fmt enc_formats[];
+
+static void mfc_deinit_enc_ctx(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       s5p_mfc_delete_queue(&ctx->src_buf_queue);
+       s5p_mfc_delete_queue(&ctx->dst_buf_queue);
+       s5p_mfc_delete_queue(&ctx->src_buf_nal_queue);
+       s5p_mfc_delete_queue(&ctx->dst_buf_nal_queue);
+       s5p_mfc_delete_queue(&ctx->ref_buf_queue);
+
+       s5p_mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_svc);
+       s5p_mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_roi);
+       s5p_mfc_release_enc_roi_buffer(ctx);
+       kfree(enc);
+}
+
+static int mfc_init_enc_ctx(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc;
+       struct s5p_mfc_enc_params *p;
+       int ret = 0;
+       int i;
+
+       enc = kzalloc(sizeof(struct s5p_mfc_enc), GFP_KERNEL);
+       if (!enc) {
+               mfc_err_dev("failed to allocate encoder private data\n");
+               return -ENOMEM;
+       }
+       ctx->enc_priv = enc;
+
+       ctx->inst_no = MFC_NO_INSTANCE_SET;
+
+       s5p_mfc_create_queue(&ctx->src_buf_queue);
+       s5p_mfc_create_queue(&ctx->dst_buf_queue);
+       s5p_mfc_create_queue(&ctx->src_buf_nal_queue);
+       s5p_mfc_create_queue(&ctx->dst_buf_nal_queue);
+       s5p_mfc_create_queue(&ctx->ref_buf_queue);
+
+       for (i = 0; i < MFC_MAX_BUFFERS; i++) {
+               INIT_LIST_HEAD(&ctx->src_ctrls[i]);
+               INIT_LIST_HEAD(&ctx->dst_ctrls[i]);
+       }
+       ctx->src_ctrls_avail = 0;
+       ctx->dst_ctrls_avail = 0;
+
+       ctx->type = MFCINST_ENCODER;
+       ctx->c_ops = &encoder_ctrls_ops;
+       ctx->src_fmt = &enc_formats[DEF_ENC_SRC_FMT];
+       ctx->dst_fmt = &enc_formats[DEF_ENC_DST_FMT];
+
+       s5p_mfc_qos_reset_framerate(ctx);
+
+       ctx->qos_ratio = 100;
+
+       /* disable IVF header by default (VP8, VP9) */
+       p = &enc->params;
+       p->ivf_header_disable = 1;
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       INIT_LIST_HEAD(&ctx->qos_list);
+#endif
+       INIT_LIST_HEAD(&ctx->ts_list);
+
+       enc->sh_handle_svc.fd = -1;
+       enc->sh_handle_roi.fd = -1;
+
+       /* Init videobuf2 queue for OUTPUT */
+       ctx->vq_src.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+       ctx->vq_src.drv_priv = ctx;
+       ctx->vq_src.buf_struct_size = sizeof(struct s5p_mfc_buf);
+       ctx->vq_src.io_modes = VB2_USERPTR | VB2_DMABUF;
+       ctx->vq_src.ops = &s5p_mfc_enc_qops;
+       ctx->vq_src.mem_ops = s5p_mfc_mem_ops();
+       ctx->vq_src.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       ret = vb2_queue_init(&ctx->vq_src);
+       if (ret) {
+               mfc_err_dev("Failed to initialize videobuf2 queue(output)\n");
+               goto fail_enc_init;
+       }
+
+       /* Init videobuf2 queue for CAPTURE */
+       ctx->vq_dst.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+       ctx->vq_dst.drv_priv = ctx;
+       ctx->vq_dst.buf_struct_size = sizeof(struct s5p_mfc_buf);
+       ctx->vq_dst.io_modes = VB2_USERPTR | VB2_DMABUF;
+       ctx->vq_dst.ops = &s5p_mfc_enc_qops;
+       ctx->vq_dst.mem_ops = s5p_mfc_mem_ops();
+       ctx->vq_dst.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       ret = vb2_queue_init(&ctx->vq_dst);
+       if (ret) {
+               mfc_err_dev("Failed to initialize videobuf2 queue(capture)\n");
+               goto fail_enc_init;
+       }
+
+       return 0;
+
+fail_enc_init:
+       mfc_deinit_enc_ctx(ctx);
+       return 0;
+}
+
+static int mfc_init_instance(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       int ret = 0;
+
+       dev->watchdog_timer.expires = jiffies +
+               msecs_to_jiffies(WATCHDOG_TICK_INTERVAL);
+       add_timer(&dev->watchdog_timer);
+
+       /* Load the FW */
+       if (!dev->fw.status) {
+               ret = s5p_mfc_alloc_firmware(dev);
+               if (ret)
+                       goto err_fw_alloc;
+               dev->fw.status = 1;
+       }
+
+       ret = s5p_mfc_load_firmware(dev);
+       if (ret)
+               goto err_fw_load;
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       trace_mfc_dcpp_start(ctx->num, 1, dev->fw.drm_status);
+       if (!dev->drm_fw_buf.daddr) {
+               mfc_err_ctx("DRM F/W buffer is not allocated.\n");
+               dev->fw.drm_status = 0;
+       } else {
+               /* Request buffer protection for DRM F/W */
+               ret = exynos_smc(SMC_DRM_PPMP_MFCFW_PROT,
+                               dev->drm_fw_buf.daddr, 0, 0);
+               if (ret != DRMDRV_OK) {
+                       mfc_err_ctx("failed MFC DRM F/W prot(%#x)\n", ret);
+                       dev->fw.drm_status = 0;
+               } else {
+                       dev->fw.drm_status = 1;
+               }
+       }
+#endif
+       trace_mfc_dcpp_end(ctx->num, 1, dev->fw.drm_status);
+
+       ret = s5p_mfc_alloc_common_context(dev);
+       if (ret)
+               goto err_context_alloc;
+
+       if (dbg_enable)
+               s5p_mfc_alloc_dbg_info_buffer(dev);
+
+       MFC_TRACE_DEV_HWLOCK("**open\n");
+       ret = s5p_mfc_get_hwlock_dev(dev);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get hwlock.\n");
+               mfc_err_dev("dev.hwlock.dev = 0x%lx, bits = 0x%lx, owned_by_irq = %d, wl_count = %d, transfer_owner = %d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+               goto err_hw_lock;
+       }
+
+       mfc_debug(2, "power on\n");
+       ret = s5p_mfc_pm_power_on(dev);
+       if (ret < 0) {
+               mfc_err_ctx("power on failed\n");
+               goto err_pwr_enable;
+       }
+
+       dev->curr_ctx = ctx->num;
+       dev->preempt_ctx = MFC_NO_INSTANCE_SET;
+       dev->curr_ctx_is_drm = ctx->is_drm;
+
+       ret = s5p_mfc_init_hw(dev);
+       if (ret) {
+               mfc_err_ctx("Failed to init mfc h/w\n");
+               goto err_hw_init;
+       }
+
+       s5p_mfc_release_hwlock_dev(dev);
+
+#ifdef NAL_Q_ENABLE
+       dev->nal_q_handle = s5p_mfc_nal_q_create(dev);
+       if (dev->nal_q_handle == NULL)
+               mfc_err_dev("NAL Q: Can't create nal q\n");
+#endif
+
+       return ret;
+
+err_hw_init:
+       s5p_mfc_pm_power_off(dev);
+
+err_pwr_enable:
+       s5p_mfc_release_hwlock_dev(dev);
+
+err_hw_lock:
+       s5p_mfc_release_common_context(dev);
+
+err_context_alloc:
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (dev->fw.drm_status) {
+               int smc_ret = 0;
+               dev->fw.drm_status = 0;
+               /* Request buffer unprotection for DRM F/W */
+               smc_ret = exynos_smc(SMC_DRM_PPMP_MFCFW_UNPROT,
+                                       dev->drm_fw_buf.daddr, 0, 0);
+               if (smc_ret != DRMDRV_OK)
+                       mfc_err_ctx("failed MFC DRM F/W unprot(%#x)\n", smc_ret);
+       }
+#endif
+
+err_fw_load:
+err_fw_alloc:
+       del_timer_sync(&dev->watchdog_timer);
+
+       mfc_err_dev("failed to init first instance\n");
+       return ret;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+       struct s5p_mfc_ctx *ctx = NULL;
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       int ret = 0;
+       enum s5p_mfc_node_type node;
+       struct video_device *vdev = NULL;
+
+       mfc_debug(2, "mfc driver open called\n");
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               goto err_no_device;
+       }
+
+       if (mutex_lock_interruptible(&dev->mfc_mutex))
+               return -ERESTARTSYS;
+
+       node = s5p_mfc_get_node_type(file);
+       if (node == MFCNODE_INVALID) {
+               mfc_err_dev("cannot specify node type\n");
+               ret = -ENOENT;
+               goto err_node_type;
+       }
+
+       dev->num_inst++;        /* It is guarded by mfc_mutex in vfd */
+
+       /* Allocate memory for context */
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               mfc_err_dev("Not enough memory.\n");
+               ret = -ENOMEM;
+               goto err_ctx_alloc;
+       }
+
+       switch (node) {
+       case MFCNODE_DECODER:
+               vdev = dev->vfd_dec;
+               break;
+       case MFCNODE_ENCODER:
+               vdev = dev->vfd_enc;
+               break;
+       case MFCNODE_DECODER_DRM:
+               vdev = dev->vfd_dec_drm;
+               break;
+       case MFCNODE_ENCODER_DRM:
+               vdev = dev->vfd_enc_drm;
+               break;
+       case MFCNODE_ENCODER_OTF:
+               vdev = dev->vfd_enc_otf;
+               break;
+       case MFCNODE_ENCODER_OTF_DRM:
+               vdev = dev->vfd_enc_otf_drm;
+               break;
+       default:
+               mfc_err_dev("Invalid node(%d)\n", node);
+               break;
+       }
+
+       if (!vdev)
+               goto err_vdev;
+
+       v4l2_fh_init(&ctx->fh, vdev);
+       file->private_data = &ctx->fh;
+       v4l2_fh_add(&ctx->fh);
+
+       ctx->dev = dev;
+
+       /* Get context number */
+       ctx->num = 0;
+       while (dev->ctx[ctx->num]) {
+               ctx->num++;
+               if (ctx->num >= MFC_NUM_CONTEXTS) {
+                       mfc_err_dev("Too many open contexts.\n");
+                       ret = -EBUSY;
+                       goto err_ctx_num;
+               }
+       }
+
+       init_waitqueue_head(&ctx->cmd_wq);
+       s5p_mfc_init_listable_wq_ctx(ctx);
+       spin_lock_init(&ctx->buf_queue_lock);
+
+       if (s5p_mfc_is_decoder_node(node))
+               ret = mfc_init_dec_ctx(ctx);
+       else
+               ret = mfc_init_enc_ctx(ctx);
+       if (ret)
+               goto err_ctx_init;
+
+       if (s5p_mfc_is_encoder_otf_node(node))
+               ret = s5p_mfc_otf_init(ctx);
+       if (ret)
+               mfc_err_ctx("OTF: failed otf init\n");
+
+       ret = call_cop(ctx, init_ctx_ctrls, ctx);
+       if (ret) {
+               mfc_err_ctx("failed in init_ctx_ctrls\n");
+               goto err_ctx_ctrls;
+       }
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (s5p_mfc_is_drm_node(node)) {
+               if (dev->num_drm_inst < MFC_MAX_DRM_CTX) {
+                       if (ctx->raw_protect_flag || ctx->stream_protect_flag) {
+                               mfc_err_ctx("protect_flag(%#lx/%#lx) remained\n",
+                                               ctx->raw_protect_flag,
+                                               ctx->stream_protect_flag);
+                               ret = -EINVAL;
+                               goto err_drm_start;
+                       }
+                       dev->num_drm_inst++;
+                       ctx->is_drm = 1;
+
+                       mfc_info_ctx("DRM instance is opened [%d:%d]\n",
+                                       dev->num_drm_inst, dev->num_inst);
+               } else {
+                       mfc_err_ctx("Too many instance are opened for DRM\n");
+                       ret = -EINVAL;
+                       goto err_drm_start;
+               }
+       } else {
+               mfc_info_ctx("NORMAL instance is opened [%d:%d]\n",
+                               dev->num_drm_inst, dev->num_inst);
+       }
+#endif
+
+       /* Mark context as idle */
+       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+       dev->ctx[ctx->num] = ctx;
+
+       /* Load firmware if this is the first instance */
+       if (dev->num_inst == 1) {
+               ret = mfc_init_instance(dev, ctx);
+               if (ret)
+                       goto err_init_inst;
+       }
+
+       trace_mfc_node_open(ctx->num, dev->num_inst, ctx->type, ctx->is_drm);
+       mfc_info_ctx("MFC open completed [%d:%d] dev = %p, ctx = %p, version = %d\n",
+                       dev->num_drm_inst, dev->num_inst, dev, ctx, MFC_DRIVER_INFO);
+       mutex_unlock(&dev->mfc_mutex);
+       return ret;
+
+       /* Deinit when failure occured */
+err_init_inst:
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (ctx->is_drm)
+               dev->num_drm_inst--;
+
+err_drm_start:
+#endif
+       call_cop(ctx, cleanup_ctx_ctrls, ctx);
+
+err_ctx_ctrls:
+err_ctx_init:
+       dev->ctx[ctx->num] = 0;
+
+err_ctx_num:
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+
+err_vdev:
+       kfree(ctx);
+
+err_ctx_alloc:
+       dev->num_inst--;
+
+err_node_type:
+       mfc_info_dev("MFC driver open is failed [%d:%d]\n",
+                       dev->num_drm_inst, dev->num_inst);
+       mutex_unlock(&dev->mfc_mutex);
+
+err_no_device:
+
+       return ret;
+}
+
+static int mfc_wait_close_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       if (atomic_read(&dev->watchdog_run)) {
+               mfc_err_ctx("watchdog already running!\n");
+               return 0;
+       }
+
+       if (ctx->inst_no == MFC_NO_INSTANCE_SET) {
+               mfc_debug(2, "mfc no instance already\n");
+               return 0;
+       }
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_change_state(ctx, MFCINST_RETURN_INST);
+       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+
+       /* To issue the command 'CLOSE_INSTANCE' */
+       if (s5p_mfc_just_run(dev, ctx->num)) {
+               mfc_err_ctx("Failed to run MFC.\n");
+               return -EIO;
+       }
+
+       /* Wait until instance is returned or timeout occured */
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET) == 1) {
+               mfc_err_ctx("Waiting for CLOSE_INSTANCE timed out\n");
+               if (s5p_mfc_wait_for_done_ctx(ctx,
+                                       S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET)) {
+                       mfc_err_ctx("waiting once more but timed out\n");
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_CLOSE_INST);
+                       s5p_mfc_dump_info_and_stop_hw(dev);
+               }
+       }
+
+       ctx->inst_no = MFC_NO_INSTANCE_SET;
+
+       return 0;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dev *dev = NULL;
+       int ret = 0;
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&dev->mfc_mutex);
+
+       mfc_info_ctx("MFC driver release is called [%d:%d], is_drm(%d)\n",
+                       dev->num_drm_inst, dev->num_inst, ctx->is_drm);
+
+       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+
+       /* If a H/W operation is in progress, wait for it complete */
+       if (need_to_wait_nal_abort(ctx)) {
+               if (s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_NAL_ABORT_RET)) {
+                       mfc_err_ctx("Failed to wait nal abort.\n");
+                       s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               }
+       }
+       MFC_TRACE_CTX_HWLOCK("**release\n");
+       ret = s5p_mfc_get_hwlock_ctx(ctx);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get hwlock.\n");
+               mutex_unlock(&dev->mfc_mutex);
+               return -EBUSY;
+       }
+
+       if (call_cop(ctx, cleanup_ctx_ctrls, ctx) < 0)
+               mfc_err_ctx("failed in cleanup_ctx_ctrl\n");
+
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+
+       /* Mark context as idle */
+       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+
+       /* If instance was initialised then
+        * return instance and free reosurces */
+       ret = mfc_wait_close_inst(dev, ctx);
+       if (ret)
+               goto err_release_try;
+
+       if (ctx->is_drm)
+               dev->num_drm_inst--;
+       dev->num_inst--;
+
+       if (dev->num_inst == 0) {
+               s5p_mfc_deinit_hw(dev);
+               del_timer_sync(&dev->watchdog_timer);
+
+               flush_workqueue(dev->butler_wq);
+
+               mfc_debug(2, "power off\n");
+               s5p_mfc_pm_power_off(dev);
+
+               if (dbg_enable)
+                       s5p_mfc_release_dbg_info_buffer(dev);
+
+               s5p_mfc_release_common_context(dev);
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+               if (dev->fw.drm_status) {
+                       dev->fw.drm_status = 0;
+                       /* Request buffer unprotection for DRM F/W */
+                       ret = exynos_smc(SMC_DRM_PPMP_MFCFW_UNPROT,
+                                       dev->drm_fw_buf.daddr, 0, 0);
+                       if (ret != DRMDRV_OK) {
+                               mfc_err_ctx("failed MFC DRM F/W unprot(%#x)\n", ret);
+                               goto err_release;
+                       }
+               }
+#endif
+
+#ifdef NAL_Q_ENABLE
+               if (dev->nal_q_handle) {
+                       ret = s5p_mfc_nal_q_destroy(dev, dev->nal_q_handle);
+                       if (ret) {
+                               mfc_err_ctx("failed nal_q destroy\n");
+                               goto err_release;
+                       }
+               }
+#endif
+       }
+
+       s5p_mfc_qos_off(ctx);
+
+       s5p_mfc_release_codec_buffers(ctx);
+       s5p_mfc_release_instance_context(ctx);
+
+       s5p_mfc_release_hwlock_ctx(ctx);
+
+       /* Free resources */
+       vb2_queue_release(&ctx->vq_src);
+       vb2_queue_release(&ctx->vq_dst);
+
+       if (ctx->type == MFCINST_DECODER)
+               mfc_deinit_dec_ctx(ctx);
+       else if (ctx->type == MFCINST_ENCODER)
+               mfc_deinit_enc_ctx(ctx);
+
+       if (ctx->otf_handle)
+               s5p_mfc_otf_deinit(ctx);
+
+       s5p_mfc_destroy_listable_wq_ctx(ctx);
+
+       trace_mfc_node_close(ctx->num, dev->num_inst, ctx->type, ctx->is_drm);
+
+       dev->ctx[ctx->num] = 0;
+       kfree(ctx);
+
+       mfc_info_dev("mfc driver release finished [%d:%d], dev = %p\n",
+                       dev->num_drm_inst, dev->num_inst, dev);
+
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       mutex_unlock(&dev->mfc_mutex);
+       return ret;
+
+#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) || defined(NAL_Q_ENABLE)
+err_release:
+       s5p_mfc_release_hwlock_ctx(ctx);
+       mutex_unlock(&dev->mfc_mutex);
+       return ret;
+#endif
+
+err_release_try:
+       s5p_mfc_release_hwlock_ctx(ctx);
+       s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+       mutex_unlock(&dev->mfc_mutex);
+       return ret;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+                                struct poll_table_struct *wait)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       unsigned int ret = 0;
+       enum s5p_mfc_node_type node_type;
+
+       node_type = s5p_mfc_get_node_type(file);
+
+       if (s5p_mfc_is_decoder_node(node_type))
+               ret = vb2_poll(&ctx->vq_src, file, wait);
+       else
+               ret = vb2_poll(&ctx->vq_dst, file, wait);
+
+       return ret;
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       int ret;
+
+       mfc_debug_enter();
+
+       if (offset < DST_QUEUE_OFF_BASE) {
+               mfc_debug(2, "mmaping source.\n");
+               ret = vb2_mmap(&ctx->vq_src, vma);
+       } else {                /* capture */
+               mfc_debug(2, "mmaping destination.\n");
+               vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+               ret = vb2_mmap(&ctx->vq_dst, vma);
+       }
+       mfc_debug_leave();
+       return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+       .owner = THIS_MODULE,
+       .open = s5p_mfc_open,
+       .release = s5p_mfc_release,
+       .poll = s5p_mfc_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap = s5p_mfc_mmap,
+};
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+static int mfc_parse_mfc_qos_platdata(struct device_node *np, char *node_name,
+       struct s5p_mfc_qos *qosdata)
+{
+       int ret = 0;
+       struct device_node *np_qos;
+
+       np_qos = of_find_node_by_name(np, node_name);
+       if (!np_qos) {
+               pr_err("%s: could not find mfc_qos_platdata node\n",
+                       node_name);
+               return -EINVAL;
+       }
+
+       of_property_read_u32(np_qos, "thrd_mb", &qosdata->threshold_mb);
+       of_property_read_u32(np_qos, "freq_int", &qosdata->freq_int);
+       of_property_read_u32(np_qos, "freq_mif", &qosdata->freq_mif);
+       of_property_read_u32(np_qos, "freq_cpu", &qosdata->freq_cpu);
+       of_property_read_u32(np_qos, "freq_kfc", &qosdata->freq_kfc);
+       of_property_read_u32(np_qos, "mo_value", &qosdata->mo_value);
+       of_property_read_u32(np_qos, "mo_10bit_value", &qosdata->mo_10bit_value);
+       of_property_read_u32(np_qos, "mo_uhd_enc60_value", &qosdata->mo_uhd_enc60_value);
+       of_property_read_u32(np_qos, "time_fw", &qosdata->time_fw);
+
+       return ret;
+}
+#endif
+
+int s5p_mfc_sysmmu_fault_handler(struct iommu_domain *iodmn, struct device *device,
+               unsigned long addr, int id, void *param)
+{
+       struct s5p_mfc_dev *dev;
+
+       dev = (struct s5p_mfc_dev *)param;
+
+       /* OTF: If AxID is 1 in SYSMMU1 fault info, it is TS-MUX fault */
+       if (MFC_MMU1_READL(MFC_MMU_INTERRUPT_STATUS) &&
+                               ((MFC_MMU1_READL(MFC_MMU_FAULT_TRANS_INFO) &
+                                MFC_MMU_FAULT_TRANS_INFO_AXID_MASK) == 1)) {
+                       mfc_err_dev("There is TS-MUX page fault. skip SFR dump.\n");
+                       return 0;
+       }
+
+       if (MFC_MMU0_READL(MFC_MMU_INTERRUPT_STATUS)) {
+               if (MFC_MMU0_READL(MFC_MMU_FAULT_TRANS_INFO) & MFC_MMU_FAULT_TRANS_INFO_RW_MASK)
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_0WRITE_PAGE_FAULT);
+               else
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_0READ_PAGE_FAULT);
+               dev->logging_data->fault_status = MFC_MMU0_READL(MFC_MMU_INTERRUPT_STATUS);
+               dev->logging_data->fault_trans_info = MFC_MMU0_READL(MFC_MMU_FAULT_TRANS_INFO);
+       } else if (MFC_MMU1_READL(MFC_MMU_INTERRUPT_STATUS)) {
+               if (MFC_MMU1_READL(MFC_MMU_FAULT_TRANS_INFO) & MFC_MMU_FAULT_TRANS_INFO_RW_MASK)
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_1WRITE_PAGE_FAULT);
+               else
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_1READ_PAGE_FAULT);
+               dev->logging_data->fault_status = MFC_MMU1_READL(MFC_MMU_INTERRUPT_STATUS);
+               dev->logging_data->fault_trans_info = MFC_MMU1_READL(MFC_MMU_FAULT_TRANS_INFO);
+       } else {
+               mfc_err_dev("there isn't any fault interrupt of MFC\n");
+       }
+       dev->logging_data->fault_addr = (unsigned int)addr;
+
+       s5p_mfc_dump_buffer_info(dev, addr);
+       s5p_mfc_dump_info_and_stop_hw(dev);
+
+       return 0;
+}
+
+static void mfc_parse_dt(struct device_node *np, struct s5p_mfc_dev *mfc)
+{
+       struct s5p_mfc_platdata *pdata = mfc->pdata;
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       char node_name[50];
+       int i;
+#endif
+
+       if (!np)
+               return;
+
+       of_property_read_u32(np, "ip_ver", &pdata->ip_ver);
+       of_property_read_u32(np, "clock_rate", &pdata->clock_rate);
+       of_property_read_u32(np, "min_rate", &pdata->min_rate);
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       of_property_read_u32(np, "num_qos_steps", &pdata->num_qos_steps);
+       of_property_read_u32(np, "max_qos_steps", &pdata->max_qos_steps);
+       of_property_read_u32(np, "max_mb", &pdata->max_mb);
+
+       pdata->qos_table = devm_kzalloc(mfc->device,
+                       sizeof(struct s5p_mfc_qos) * pdata->max_qos_steps, GFP_KERNEL);
+
+       for (i = 0; i < pdata->max_qos_steps; i++) {
+               snprintf(node_name, sizeof(node_name), "mfc_qos_variant_%d", i);
+               mfc_parse_mfc_qos_platdata(np, node_name, &pdata->qos_table[i]);
+       }
+#endif
+}
+
+static void *mfc_get_drv_data(struct platform_device *pdev);
+
+static struct video_device *mfc_video_device_register(struct s5p_mfc_dev *dev,
+                               char *name, int node_num)
+{
+       struct video_device *vfd;
+       int ret = 0;
+
+       vfd = video_device_alloc();
+       if (!vfd) {
+               v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+               return NULL;
+       }
+       strncpy(vfd->name, name, sizeof(vfd->name) - 1);
+       vfd->fops = &s5p_mfc_fops;
+       vfd->minor = -1;
+       vfd->release = video_device_release;
+
+       if (IS_DEC_NODE(node_num))
+               vfd->ioctl_ops = s5p_mfc_get_dec_v4l2_ioctl_ops();
+       else if(IS_ENC_NODE(node_num))
+               vfd->ioctl_ops = s5p_mfc_get_enc_v4l2_ioctl_ops();
+
+       vfd->lock = &dev->mfc_mutex;
+       vfd->v4l2_dev = &dev->v4l2_dev;
+       vfd->vfl_dir = VFL_DIR_M2M;
+
+       snprintf(vfd->name, sizeof(vfd->name), "%s%d", vfd->name, dev->id);
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, node_num + 60 * dev->id);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev, "Failed to register video device /dev/video%d\n", node_num);
+               video_device_release(vfd);
+               return NULL;
+       }
+       v4l2_info(&dev->v4l2_dev, "video device registered as /dev/video%d\n",
+                                                               vfd->num);
+       video_set_drvdata(vfd, dev);
+
+       return vfd;
+}
+
+static int mfc_register_resource(struct platform_device *pdev, struct s5p_mfc_dev *dev)
+{
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get memory region resource\n");
+               return -ENOENT;
+       }
+       dev->mfc_mem = request_mem_region(res->start, resource_size(res),
+                                       pdev->name);
+       if (dev->mfc_mem == NULL) {
+               dev_err(&pdev->dev, "failed to get memory region\n");
+               return -ENOENT;
+       }
+       dev->regs_base = ioremap(dev->mfc_mem->start,
+                               resource_size(dev->mfc_mem));
+       if (dev->regs_base == NULL) {
+               dev_err(&pdev->dev, "failed to ioremap address region\n");
+               goto err_ioremap;
+       }
+       dev->sysmmu0_base = ioremap(MFC_MMU0_BASE_ADDR, MFC_MMU_SIZE);
+       if (dev->sysmmu0_base == NULL) {
+               dev_err(&pdev->dev, "failed to ioremap sysmmu0 address region\n");
+               goto err_ioremap_mmu0;
+       }
+       dev->sysmmu1_base = ioremap(MFC_MMU1_BASE_ADDR, MFC_MMU_SIZE);
+       if (dev->sysmmu1_base == NULL) {
+               dev_err(&pdev->dev, "failed to ioremap sysmmu1 address region\n");
+               goto err_ioremap_mmu1;
+       }
+       dev->hwfc_base = ioremap(HWFC_BASE_ADDR, HWFC_SIZE);
+       if (dev->hwfc_base == NULL) {
+               dev_err(&pdev->dev, "failed to ioremap hwfc adddress region\n");
+               goto err_ioremap_hwfc;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get irq resource\n");
+               goto err_res_irq;
+       }
+       dev->irq = res->start;
+       ret = request_threaded_irq(dev->irq, s5p_mfc_top_half_irq, s5p_mfc_irq,
+                               IRQF_ONESHOT, pdev->name, dev);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+               goto err_req_irq;
+       }
+
+       return 0;
+
+err_req_irq:
+err_res_irq:
+       iounmap(dev->hwfc_base);
+err_ioremap_hwfc:
+       iounmap(dev->sysmmu1_base);
+err_ioremap_mmu1:
+       iounmap(dev->sysmmu0_base);
+err_ioremap_mmu0:
+       iounmap(dev->regs_base);
+err_ioremap:
+       release_mem_region(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+       return -ENOENT;
+}
+
+/* MFC probe function */
+static int s5p_mfc_probe(struct platform_device *pdev)
+{
+       struct s5p_mfc_dev *dev;
+       int ret = -ENOENT;
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       int i;
+#endif
+
+       dev_dbg(&pdev->dev, "%s()\n", __func__);
+       dev = devm_kzalloc(&pdev->dev, sizeof(struct s5p_mfc_dev), GFP_KERNEL);
+       if (!dev) {
+               dev_err(&pdev->dev, "Not enough memory for MFC device.\n");
+               return -ENOMEM;
+       }
+
+       dev->device = &pdev->dev;
+       dev->pdata = pdev->dev.platform_data;
+
+       dev->variant = mfc_get_drv_data(pdev);
+
+       if (dev->device->of_node)
+               dev->id = of_alias_get_id(pdev->dev.of_node, "mfc");
+
+       dev_dbg(&pdev->dev, "of alias get id : mfc-%d \n", dev->id);
+
+       if (dev->id < 0 || dev->id >= dev->variant->num_entities) {
+               dev_err(&pdev->dev, "Invalid platform device id: %d\n", dev->id);
+               ret = -EINVAL;
+               goto err_pm;
+       }
+
+       dev->pdata = devm_kzalloc(&pdev->dev, sizeof(struct s5p_mfc_platdata), GFP_KERNEL);
+       if (!dev->pdata) {
+               dev_err(&pdev->dev, "no memory for state\n");
+               ret = -ENOMEM;
+               goto err_pm;
+       }
+
+       mfc_parse_dt(dev->device->of_node, dev);
+
+       atomic_set(&dev->trace_ref, 0);
+       atomic_set(&dev->trace_ref_hwlock, 0);
+       dev->mfc_trace = g_mfc_trace;
+       dev->mfc_trace_hwlock = g_mfc_trace_hwlock;
+
+       s5p_mfc_pm_init(dev);
+       ret = mfc_register_resource(pdev, dev);
+       if (ret)
+               goto err_res_mem;
+
+       mutex_init(&dev->mfc_mutex);
+
+       ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+       if (ret)
+               goto err_v4l2_dev;
+
+       init_waitqueue_head(&dev->cmd_wq);
+       s5p_mfc_init_listable_wq_dev(dev);
+
+       /* decoder */
+       dev->vfd_dec = mfc_video_device_register(dev, S5P_MFC_DEC_NAME,
+                       EXYNOS_VIDEONODE_MFC_DEC);
+       if (!dev->vfd_dec) {
+               ret = -ENOMEM;
+               goto alloc_vdev_dec;
+       }
+
+       /* encoder */
+       dev->vfd_enc = mfc_video_device_register(dev, S5P_MFC_ENC_NAME,
+                       EXYNOS_VIDEONODE_MFC_ENC);
+       if (!dev->vfd_enc) {
+               ret = -ENOMEM;
+               goto alloc_vdev_enc;
+       }
+
+       /* secure decoder */
+       dev->vfd_dec_drm = mfc_video_device_register(dev, S5P_MFC_DEC_DRM_NAME,
+                       EXYNOS_VIDEONODE_MFC_DEC_DRM);
+       if (!dev->vfd_dec_drm) {
+               ret = -ENOMEM;
+               goto alloc_vdev_dec_drm;
+       }
+
+       /* secure encoder */
+       dev->vfd_enc_drm = mfc_video_device_register(dev, S5P_MFC_ENC_DRM_NAME,
+                       EXYNOS_VIDEONODE_MFC_ENC_DRM);
+       if (!dev->vfd_enc_drm) {
+               ret = -ENOMEM;
+               goto alloc_vdev_enc_drm;
+       }
+
+       /* OTF encoder */
+       dev->vfd_enc_otf = mfc_video_device_register(dev, S5P_MFC_ENC_OTF_NAME,
+                       EXYNOS_VIDEONODE_MFC_ENC_OTF);
+       if (!dev->vfd_enc_otf) {
+               ret = -ENOMEM;
+               goto alloc_vdev_enc_otf;
+       }
+
+       /* OTF secure encoder */
+       dev->vfd_enc_otf_drm = mfc_video_device_register(dev, S5P_MFC_ENC_OTF_DRM_NAME,
+                       EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM);
+       if (!dev->vfd_enc_otf_drm) {
+               ret = -ENOMEM;
+               goto alloc_vdev_enc_otf_drm;
+       }
+       /* end of node setting*/
+
+       platform_set_drvdata(pdev, dev);
+
+       s5p_mfc_init_hwlock(dev);
+       s5p_mfc_create_bits(&dev->work_bits);
+
+       dev->watchdog_wq =
+               create_singlethread_workqueue("s5p_mfc/watchdog");
+       if (!dev->watchdog_wq) {
+               dev_err(&pdev->dev, "failed to create workqueue for watchdog\n");
+               goto err_wq_watchdog;
+       }
+       INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+       atomic_set(&dev->watchdog_tick_running, 0);
+       atomic_set(&dev->watchdog_tick_cnt, 0);
+       atomic_set(&dev->watchdog_run, 0);
+       init_timer(&dev->watchdog_timer);
+       dev->watchdog_timer.data = (unsigned long)dev;
+       dev->watchdog_timer.function = s5p_mfc_watchdog_tick;
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       INIT_LIST_HEAD(&dev->qos_queue);
+#endif
+
+       /* default FW alloc is added */
+       dev->butler_wq = alloc_workqueue("s5p_mfc/butler", WQ_UNBOUND
+                                       | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
+       if (dev->butler_wq == NULL) {
+               dev_err(&pdev->dev, "failed to create workqueue for butler\n");
+               goto err_butler_wq;
+       }
+       INIT_WORK(&dev->butler_work, s5p_mfc_butler_worker);
+
+#ifdef CONFIG_ION_EXYNOS
+       dev->mfc_ion_client = exynos_ion_client_create("mfc");
+       if (IS_ERR(dev->mfc_ion_client)) {
+               dev_err(&pdev->dev, "failed to ion_client_create\n");
+               goto err_ion_client;
+       }
+#endif
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       atomic_set(&dev->qos_req_cur, 0);
+
+       for (i = 0; i < dev->pdata->num_qos_steps; i++) {
+               mfc_info_dev("QoS table[%d] int : %d, mif : %d\n",
+                               i,
+                               dev->pdata->qos_table[i].freq_int,
+                               dev->pdata->qos_table[i].freq_mif);
+       }
+#endif
+
+       iovmm_set_fault_handler(dev->device,
+               s5p_mfc_sysmmu_fault_handler, dev);
+
+       g_mfc_dev = dev;
+
+       ret = iovmm_activate(&pdev->dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to activate iommu\n");
+               goto err_iovmm_active;
+       }
+
+       dev->logging_data = devm_kzalloc(&pdev->dev, sizeof(struct s5p_mfc_debug), GFP_KERNEL);
+       if (!dev->logging_data) {
+               dev_err(&pdev->dev, "no memory for logging data\n");
+               ret = -ENOMEM;
+               goto err_alloc_debug;
+       }
+
+       s5p_mfc_init_debugfs(dev);
+
+       pr_debug("%s--\n", __func__);
+       return 0;
+
+/* Deinit MFC if probe had failed */
+err_alloc_debug:
+       iovmm_deactivate(&pdev->dev);
+err_iovmm_active:
+#ifdef CONFIG_ION_EXYNOS
+       ion_client_destroy(dev->mfc_ion_client);
+err_ion_client:
+#endif
+       destroy_workqueue(dev->butler_wq);
+err_butler_wq:
+       destroy_workqueue(dev->watchdog_wq);
+err_wq_watchdog:
+       video_unregister_device(dev->vfd_enc_otf_drm);
+alloc_vdev_enc_otf_drm:
+       video_unregister_device(dev->vfd_enc_otf);
+alloc_vdev_enc_otf:
+       video_unregister_device(dev->vfd_enc_drm);
+alloc_vdev_enc_drm:
+       video_unregister_device(dev->vfd_dec_drm);
+alloc_vdev_dec_drm:
+       video_unregister_device(dev->vfd_enc);
+alloc_vdev_enc:
+       video_unregister_device(dev->vfd_dec);
+alloc_vdev_dec:
+       v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_dev:
+       mutex_destroy(&dev->mfc_mutex);
+       free_irq(dev->irq, dev);
+       iounmap(dev->hwfc_base);
+       iounmap(dev->sysmmu1_base);
+       iounmap(dev->sysmmu0_base);
+       iounmap(dev->regs_base);
+       release_mem_region(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+err_res_mem:
+       s5p_mfc_pm_final(dev);
+err_pm:
+       return ret;
+}
+
+/* Remove the driver */
+static int s5p_mfc_remove(struct platform_device *pdev)
+{
+       struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s++\n", __func__);
+       v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+       del_timer_sync(&dev->watchdog_timer);
+       flush_workqueue(dev->watchdog_wq);
+       destroy_workqueue(dev->watchdog_wq);
+       flush_workqueue(dev->butler_wq);
+       destroy_workqueue(dev->butler_wq);
+       video_unregister_device(dev->vfd_enc);
+       video_unregister_device(dev->vfd_dec);
+       video_unregister_device(dev->vfd_enc_otf);
+       video_unregister_device(dev->vfd_enc_otf_drm);
+       v4l2_device_unregister(&dev->v4l2_dev);
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       remove_proc_entry(MFC_PROC_FW_STATUS, mfc_proc_entry);
+       remove_proc_entry(MFC_PROC_DRM_INSTANCE_NUMBER, mfc_proc_entry);
+       remove_proc_entry(MFC_PROC_INSTANCE_NUMBER, mfc_proc_entry);
+       remove_proc_entry(MFC_PROC_ROOT, NULL);
+#endif
+       s5p_mfc_destroy_listable_wq_dev(dev);
+#ifdef CONFIG_ION_EXYNOS
+       ion_client_destroy(dev->mfc_ion_client);
+#endif
+       iovmm_deactivate(&pdev->dev);
+       mfc_debug(2, "Will now deinit HW\n");
+       s5p_mfc_deinit_hw(dev);
+       free_irq(dev->irq, dev);
+       iounmap(dev->sysmmu1_base);
+       iounmap(dev->sysmmu0_base);
+       iounmap(dev->regs_base);
+       iounmap(dev->hwfc_base);
+       release_mem_region(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+       s5p_mfc_pm_final(dev);
+       kfree(dev);
+       dev_dbg(&pdev->dev, "%s--\n", __func__);
+       return 0;
+}
+
+static void s5p_mfc_shutdown(struct platform_device *pdev)
+{
+       struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+       int ret;
+
+       mfc_info_dev("MFC shutdown is called\n");
+       MFC_TRACE_DEV_HWLOCK("**shutdown \n");
+
+       if (!s5p_mfc_pm_get_pwr_ref_cnt(dev)) {
+               dev->shutdown = 1;
+               mfc_info_dev("MFC is not running\n");
+               return;
+       }
+
+       ret = s5p_mfc_get_hwlock_dev(dev);
+       if (ret < 0)
+               mfc_err_dev("Failed to get hwlock.\n");
+
+       if (!dev->shutdown) {
+               s5p_mfc_risc_off(dev);
+               dev->shutdown = 1;
+               s5p_mfc_clear_all_bits(&dev->work_bits);
+               iovmm_deactivate(&pdev->dev);
+       }
+       s5p_mfc_release_hwlock_dev(dev);
+       mfc_info_dev("MFC shutdown completed\n");
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s5p_mfc_suspend(struct device *dev)
+{
+       struct s5p_mfc_dev *m_dev = platform_get_drvdata(to_platform_device(dev));
+       int ret;
+
+       if (!m_dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (m_dev->num_inst == 0)
+               return 0;
+
+       ret = s5p_mfc_sleep(m_dev);
+
+       return ret;
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+       struct s5p_mfc_dev *m_dev = platform_get_drvdata(to_platform_device(dev));
+       int ret;
+
+       if (!m_dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (m_dev->num_inst == 0)
+               return 0;
+
+       ret = s5p_mfc_wakeup(m_dev);
+
+       return ret;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int s5p_mfc_runtime_suspend(struct device *dev)
+{
+       mfc_debug(3, "mfc runtime suspend\n");
+
+       return 0;
+}
+
+static int s5p_mfc_runtime_idle(struct device *dev)
+{
+       return 0;
+}
+
+static int s5p_mfc_runtime_resume(struct device *dev)
+{
+       mfc_debug(3, "mfc runtime resume\n");
+
+       return 0;
+}
+#endif
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
+       SET_RUNTIME_PM_OPS(
+                       s5p_mfc_runtime_suspend,
+                       s5p_mfc_runtime_resume,
+                       s5p_mfc_runtime_idle
+       )
+};
+
+struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
+       .dev_ctx        = PAGE_ALIGN(0x7800),   /*  30KB */
+       .h264_dec_ctx   = PAGE_ALIGN(0x200000), /* 1.6MB */
+       .other_dec_ctx  = PAGE_ALIGN(0x7800),   /*  30KB */
+       .h264_enc_ctx   = PAGE_ALIGN(0x19000),  /* 100KB */
+       .hevc_enc_ctx   = PAGE_ALIGN(0xA000),   /*  40KB */
+       .other_enc_ctx  = PAGE_ALIGN(0x6400),   /*  25KB */
+       .shared_buf     = PAGE_ALIGN(0x2000),   /*   8KB */
+       .dbg_info_buf   = PAGE_ALIGN(0x1000),   /* 4KB for DEBUG INFO */
+};
+
+struct s5p_mfc_buf_size buf_size_v6 = {
+       .firmware_code  = PAGE_ALIGN(0x100000), /* 1MB */
+       .cpb_buf        = PAGE_ALIGN(0x300000), /* 3MB */
+       .buf            = &mfc_buf_size_v6,
+};
+
+struct s5p_mfc_buf_align mfc_buf_align_v6 = {
+       .mfc_base_align = 0,
+};
+
+
+static struct s5p_mfc_variant mfc_drvdata_v6 = {
+       .buf_size = &buf_size_v6,
+       .buf_align = &mfc_buf_align_v6,
+       .num_entities = 2,
+};
+
+static const struct of_device_id exynos_mfc_match[] = {
+       {
+               .compatible = "samsung,mfc-v6",
+               .data = &mfc_drvdata_v6,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_mfc_match);
+
+static void *mfc_get_drv_data(struct platform_device *pdev)
+{
+       struct s5p_mfc_variant *driver_data = NULL;
+
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(of_match_ptr(exynos_mfc_match),
+                               pdev->dev.of_node);
+               if (match)
+                       driver_data = (struct s5p_mfc_variant *)match->data;
+       } else {
+               driver_data = (struct s5p_mfc_variant *)
+                       platform_get_device_id(pdev)->driver_data;
+       }
+       return driver_data;
+}
+
+static struct platform_driver s5p_mfc_driver = {
+       .probe          = s5p_mfc_probe,
+       .remove         = s5p_mfc_remove,
+       .shutdown       = s5p_mfc_shutdown,
+       .driver = {
+               .name   = S5P_MFC_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &s5p_mfc_pm_ops,
+               .of_match_table = exynos_mfc_match,
+               .suppress_bind_attrs = true,
+       },
+};
+
+module_platform_driver(s5p_mfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_buf.c b/drivers/media/platform/exynos/mfc/s5p_mfc_buf.c
new file mode 100644 (file)
index 0000000..cc633c5
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_buf.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/smc.h>
+#include <linux/firmware.h>
+#include <trace/events/mfc.h>
+
+#include "s5p_mfc_buf.h"
+
+#include "s5p_mfc_mem.h"
+
+static int mfc_alloc_common_context(struct s5p_mfc_dev *dev,
+                                       enum mfc_buf_usage_type buf_type)
+{
+       struct s5p_mfc_special_buf *ctx_buf;
+       int firmware_size;
+       unsigned long fw_daddr;
+
+       mfc_debug_enter();
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       ctx_buf = &dev->common_ctx_buf;
+       fw_daddr = dev->fw_buf.daddr;
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (buf_type == MFCBUF_DRM) {
+               ctx_buf = &dev->drm_common_ctx_buf;
+               fw_daddr = dev->drm_fw_buf.daddr;
+       }
+#endif
+
+       firmware_size = dev->variant->buf_size->firmware_code;
+
+       ctx_buf->handle = NULL;
+       ctx_buf->vaddr = NULL;
+       ctx_buf->daddr = fw_daddr + firmware_size;
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Wrapper : allocate context buffers for SYS_INIT */
+int s5p_mfc_alloc_common_context(struct s5p_mfc_dev *dev)
+{
+       int ret = 0;
+
+       ret = mfc_alloc_common_context(dev, MFCBUF_NORMAL);
+       if (ret)
+               return ret;
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (dev->fw.drm_status) {
+               ret = mfc_alloc_common_context(dev, MFCBUF_DRM);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       return ret;
+}
+
+/* Release context buffers for SYS_INIT */
+static void mfc_release_common_context(struct s5p_mfc_dev *dev,
+                                       enum mfc_buf_usage_type buf_type)
+{
+       struct s5p_mfc_special_buf *ctx_buf;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       ctx_buf = &dev->common_ctx_buf;
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (buf_type == MFCBUF_DRM)
+               ctx_buf = &dev->drm_common_ctx_buf;
+#endif
+
+       ctx_buf->handle = NULL;
+       ctx_buf->vaddr = NULL;
+       ctx_buf->daddr = 0;
+}
+
+/* Release context buffers for SYS_INIT */
+void s5p_mfc_release_common_context(struct s5p_mfc_dev *dev)
+{
+       mfc_release_common_context(dev, MFCBUF_NORMAL);
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       mfc_release_common_context(dev, MFCBUF_DRM);
+#endif
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_context(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf_size_v6 *buf_size;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       buf_size = dev->variant->buf_size->buf;
+
+       switch (ctx->codec_mode) {
+       case S5P_FIMV_CODEC_H264_DEC:
+       case S5P_FIMV_CODEC_H264_MVC_DEC:
+       case S5P_FIMV_CODEC_HEVC_DEC:
+       case S5P_FIMV_CODEC_BPG_DEC:
+               ctx->instance_ctx_buf.size = buf_size->h264_dec_ctx;
+               break;
+       case S5P_FIMV_CODEC_MPEG4_DEC:
+       case S5P_FIMV_CODEC_H263_DEC:
+       case S5P_FIMV_CODEC_VC1_RCV_DEC:
+       case S5P_FIMV_CODEC_VC1_DEC:
+       case S5P_FIMV_CODEC_MPEG2_DEC:
+       case S5P_FIMV_CODEC_VP8_DEC:
+       case S5P_FIMV_CODEC_VP9_DEC:
+       case S5P_FIMV_CODEC_FIMV1_DEC:
+       case S5P_FIMV_CODEC_FIMV2_DEC:
+       case S5P_FIMV_CODEC_FIMV3_DEC:
+       case S5P_FIMV_CODEC_FIMV4_DEC:
+               ctx->instance_ctx_buf.size = buf_size->other_dec_ctx;
+               break;
+       case S5P_FIMV_CODEC_H264_ENC:
+               ctx->instance_ctx_buf.size = buf_size->h264_enc_ctx;
+               break;
+       case S5P_FIMV_CODEC_HEVC_ENC:
+       case S5P_FIMV_CODEC_BPG_ENC:
+               ctx->instance_ctx_buf.size = buf_size->hevc_enc_ctx;
+               break;
+       case S5P_FIMV_CODEC_MPEG4_ENC:
+       case S5P_FIMV_CODEC_H263_ENC:
+       case S5P_FIMV_CODEC_VP8_ENC:
+       case S5P_FIMV_CODEC_VP9_ENC:
+               ctx->instance_ctx_buf.size = buf_size->other_enc_ctx;
+               break;
+       default:
+               ctx->instance_ctx_buf.size = 0;
+               mfc_err_ctx("Codec type(%d) should be checked!\n", ctx->codec_mode);
+               return -ENOMEM;
+       }
+
+       if (ctx->is_drm)
+               ctx->instance_ctx_buf.buftype = MFCBUF_DRM;
+       else
+               ctx->instance_ctx_buf.buftype = MFCBUF_NORMAL;
+
+       if (s5p_mfc_mem_ion_alloc(dev, &ctx->instance_ctx_buf)) {
+               mfc_err_ctx("Allocating context buffer failed\n");
+               return -ENOMEM;
+       }
+
+       ctx->instance_ctx_buf.vaddr = s5p_mfc_mem_get_vaddr(dev, &ctx->instance_ctx_buf);
+
+       if (!ctx->instance_ctx_buf.vaddr) {
+               mfc_err_dev("failed to get instance ctx buffer vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &ctx->instance_ctx_buf);
+               return -ENOMEM;
+       }
+
+       mfc_debug(2, "Instance buf alloc, ctx: %d, size: %ld, addr: 0x%08llx\n",
+                       ctx->num, ctx->instance_ctx_buf.size, ctx->instance_ctx_buf.daddr);
+
+       return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_context(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       s5p_mfc_mem_ion_free(dev, &ctx->instance_ctx_buf);
+       mfc_debug(2, "Release the instance ctx buffer\n");
+
+       mfc_debug_leave();
+}
+
+static void mfc_calc_dec_codec_buffer_size(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       int i;
+
+       dec = ctx->dec_priv;
+
+       /* Codecs have different memory requirements */
+       switch (ctx->codec_mode) {
+       case S5P_FIMV_CODEC_H264_DEC:
+       case S5P_FIMV_CODEC_H264_MVC_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size +
+                       (dec->mv_count * ctx->mv_size);
+               break;
+       case S5P_FIMV_CODEC_MPEG4_DEC:
+       case S5P_FIMV_CODEC_FIMV1_DEC:
+       case S5P_FIMV_CODEC_FIMV2_DEC:
+       case S5P_FIMV_CODEC_FIMV3_DEC:
+       case S5P_FIMV_CODEC_FIMV4_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               if (dec->loop_filter_mpeg4) {
+                       ctx->loopfilter_luma_size = ALIGN(ctx->raw_buf.plane_size[0], 256);
+                       ctx->loopfilter_chroma_size = ALIGN(ctx->raw_buf.plane_size[1] +
+                                                       ctx->raw_buf.plane_size[2], 256);
+                       ctx->codec_buf.size = ctx->scratch_buf_size +
+                               (NUM_MPEG4_LF_BUF * (ctx->loopfilter_luma_size +
+                                                    ctx->loopfilter_chroma_size));
+               } else {
+                       ctx->codec_buf.size = ctx->scratch_buf_size;
+               }
+               break;
+       case S5P_FIMV_CODEC_VC1_RCV_DEC:
+       case S5P_FIMV_CODEC_VC1_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size = ctx->scratch_buf_size;
+               break;
+       case S5P_FIMV_CODEC_MPEG2_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size = ctx->scratch_buf_size;
+               break;
+       case S5P_FIMV_CODEC_H263_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size = ctx->scratch_buf_size;
+               break;
+       case S5P_FIMV_CODEC_VP8_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size = ctx->scratch_buf_size;
+               break;
+       case S5P_FIMV_CODEC_VP9_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size +
+                       DEC_STATIC_BUFFER_SIZE;
+               break;
+       case S5P_FIMV_CODEC_HEVC_DEC:
+       case S5P_FIMV_CODEC_BPG_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size +
+                       (dec->mv_count * ctx->mv_size);
+               break;
+       default:
+               ctx->codec_buf.size = 0;
+               mfc_err_ctx("invalid codec type: %d\n", ctx->codec_mode);
+               break;
+       }
+
+       for (i = 0; i < ctx->raw_buf.num_planes; i++)
+               mfc_debug(2, "Plane[%d] size:%d\n",
+                               i, ctx->raw_buf.plane_size[i]);
+       mfc_debug(2, "MV size: %zu, Totals bufs: %d\n",
+                       ctx->mv_size, dec->total_dpb_count);
+}
+
+static void mfc_calc_enc_codec_buffer_size(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc;
+       unsigned int mb_width, mb_height;
+       unsigned int lcu_width = 0, lcu_height = 0;
+
+       enc = ctx->enc_priv;
+       enc->tmv_buffer_size = 0;
+
+       mb_width = WIDTH_MB(ctx->img_width);
+       mb_height = HEIGHT_MB(ctx->img_height);
+
+       lcu_width = ENC_LCU_WIDTH(ctx->img_width);
+       lcu_height = ENC_LCU_HEIGHT(ctx->img_height);
+
+       /* default recon buffer size, it can be changed in case of 422, 10bit */
+       enc->luma_dpb_size =
+               ALIGN(ENC_LUMA_DPB_SIZE(ctx->img_width, ctx->img_height), 64);
+       enc->chroma_dpb_size =
+               ALIGN(ENC_CHROMA_DPB_SIZE(ctx->img_width, ctx->img_height), 64);
+       mfc_debug(2, "recon luma size: %zu chroma size: %zu\n",
+                       enc->luma_dpb_size, enc->chroma_dpb_size);
+
+       /* Codecs have different memory requirements */
+       switch (ctx->codec_mode) {
+       case S5P_FIMV_CODEC_H264_ENC:
+               enc->me_buffer_size =
+                       ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), 256);
+
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size + enc->tmv_buffer_size +
+                       (ctx->dpb_count * (enc->luma_dpb_size +
+                       enc->chroma_dpb_size + enc->me_buffer_size));
+
+               ctx->scratch_buf_size = max(ctx->scratch_buf_size, ctx->min_scratch_buf_size);
+               ctx->min_scratch_buf_size = 0;
+               break;
+       case S5P_FIMV_CODEC_MPEG4_ENC:
+       case S5P_FIMV_CODEC_H263_ENC:
+               enc->me_buffer_size =
+                       ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, mb_height), 256);
+
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size + enc->tmv_buffer_size +
+                       (ctx->dpb_count * (enc->luma_dpb_size +
+                       enc->chroma_dpb_size + enc->me_buffer_size));
+               break;
+       case S5P_FIMV_CODEC_VP8_ENC:
+               enc->me_buffer_size =
+                       ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), 256);
+
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size + enc->tmv_buffer_size +
+                       (ctx->dpb_count * (enc->luma_dpb_size +
+                       enc->chroma_dpb_size + enc->me_buffer_size));
+               break;
+       case S5P_FIMV_CODEC_VP9_ENC:
+               if (ctx->is_10bit) {
+                       enc->luma_dpb_size =
+                               ALIGN(ENC_VP9_LUMA_DPB_10B_SIZE(ctx->img_width, ctx->img_height), 64);
+                       enc->chroma_dpb_size =
+                               ALIGN(ENC_VP9_CHROMA_DPB_10B_SIZE(ctx->img_width, ctx->img_height), 64);
+                       mfc_debug(2, "VP9 10bit recon luma size: %zu chroma size: %zu\n",
+                                       enc->luma_dpb_size, enc->chroma_dpb_size);
+               }
+               enc->me_buffer_size =
+                       ALIGN(ENC_V100_VP9_ME_SIZE(lcu_width, lcu_height), 256);
+
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size + enc->tmv_buffer_size +
+                       (ctx->dpb_count * (enc->luma_dpb_size +
+                                          enc->chroma_dpb_size + enc->me_buffer_size));
+               break;
+       case S5P_FIMV_CODEC_HEVC_ENC:
+       case S5P_FIMV_CODEC_BPG_ENC:
+               if (ctx->is_10bit || ctx->is_422format) {
+                       enc->luma_dpb_size =
+                               ALIGN(ENC_HEVC_LUMA_DPB_10B_SIZE(ctx->img_width, ctx->img_height), 64);
+                       enc->chroma_dpb_size =
+                               ALIGN(ENC_HEVC_CHROMA_DPB_10B_SIZE(ctx->img_width, ctx->img_height), 64);
+                       mfc_debug(2, "HEVC 10bit or 422 recon luma size: %zu chroma size: %zu\n",
+                                       enc->luma_dpb_size, enc->chroma_dpb_size);
+               }
+               enc->me_buffer_size =
+                       ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 256);
+
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+               ctx->codec_buf.size =
+                       ctx->scratch_buf_size + enc->tmv_buffer_size +
+                       (ctx->dpb_count * (enc->luma_dpb_size +
+                                          enc->chroma_dpb_size + enc->me_buffer_size));
+               break;
+       default:
+               ctx->codec_buf.size = 0;
+               mfc_err_ctx("invalid codec type: %d\n", ctx->codec_mode);
+               break;
+       }
+}
+
+/* Allocate codec buffers */
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->type == MFCINST_DECODER) {
+               mfc_calc_dec_codec_buffer_size(ctx);
+       } else if (ctx->type == MFCINST_ENCODER) {
+               mfc_calc_enc_codec_buffer_size(ctx);
+       } else {
+               mfc_err_ctx("invalid type: %d\n", ctx->type);
+               return -EINVAL;
+       }
+
+       if (ctx->is_drm)
+               ctx->codec_buf.buftype = MFCBUF_DRM;
+       else
+               ctx->codec_buf.buftype = MFCBUF_NORMAL;
+
+       if (ctx->codec_buf.size > 0) {
+               if (s5p_mfc_mem_ion_alloc(dev, &ctx->codec_buf)) {
+                       mfc_err_ctx("Allocating codec buffer failed\n");
+                       return -ENOMEM;
+               }
+
+               ctx->codec_buf.vaddr = s5p_mfc_mem_get_vaddr(dev, &ctx->codec_buf);
+               if (!ctx->codec_buf.vaddr) {
+                       mfc_err_dev("failed to get codec buffer vaddr\n");
+                       s5p_mfc_mem_ion_free(dev, &ctx->codec_buf);
+                       return -ENOMEM;
+               }
+               ctx->codec_buffer_allocated = 1;
+       } else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG2_DEC) {
+               ctx->codec_buffer_allocated = 1;
+       }
+
+       mfc_debug(2, "Codec buf alloc, ctx: %d, size: %ld, addr: 0x%08llx\n",
+                       ctx->num, ctx->codec_buf.size, ctx->codec_buf.daddr);
+
+       return 0;
+}
+
+/* Release buffers allocated for codec */
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       s5p_mfc_mem_ion_free(dev, &ctx->codec_buf);
+       mfc_debug(2, "Release the codec buffer\n");
+}
+
+/* Allocation buffer of debug infor memory for FW debugging */
+int s5p_mfc_alloc_dbg_info_buffer(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->buf;
+
+       mfc_debug(2, "Allocate a debug-info buffer.\n");
+
+       dev->dbg_info_buf.buftype = MFCBUF_NORMAL;
+       dev->dbg_info_buf.size = buf_size->dbg_info_buf;
+       if (s5p_mfc_mem_ion_alloc(dev, &dev->dbg_info_buf)) {
+               mfc_err_dev("Allocating debug info buffer failed\n");
+               return -ENOMEM;
+       }
+       mfc_debug(2, "dev->dbg_info_buf.daddr = 0x%08llx\n",
+                       dev->dbg_info_buf.daddr);
+
+       dev->dbg_info_buf.vaddr = s5p_mfc_mem_get_vaddr(dev, &dev->dbg_info_buf);
+       if (!dev->dbg_info_buf.vaddr) {
+               mfc_err_dev("failed to get debug info buffer vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &dev->dbg_info_buf);
+               return -ENOMEM;
+       }
+       mfc_debug(2, "dev->dbg_info_buf.vaddr = 0x%p\n",
+                                       dev->dbg_info_buf.vaddr);
+
+       return 0;
+}
+
+/* Release buffer of debug infor memory for FW debugging */
+int s5p_mfc_release_dbg_info_buffer(struct s5p_mfc_dev *dev)
+{
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (!dev->dbg_info_buf.handle) {
+               mfc_debug(2, "debug info buffer is already freed\n");
+               return 0;
+       }
+
+       s5p_mfc_mem_ion_free(dev, &dev->dbg_info_buf);
+       mfc_debug(2, "Release the debug-info buffer\n");
+
+       return 0;
+}
+
+/* Allocation buffer of ROI macroblock information */
+static int mfc_alloc_enc_roi_buffer(struct s5p_mfc_ctx *ctx, struct s5p_mfc_special_buf *roi_buf)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->buf;
+
+       roi_buf->buftype = MFCBUF_NORMAL;
+       roi_buf->size = buf_size->shared_buf;
+       if (s5p_mfc_mem_ion_alloc(dev, roi_buf)) {
+               mfc_err_ctx("Allocating ROI buffer failed\n");
+               return -ENOMEM;
+       }
+       mfc_debug(2, "roi_buf.daddr = 0x%08llx\n", roi_buf->daddr);
+
+       roi_buf->vaddr = s5p_mfc_mem_get_vaddr(dev, roi_buf);
+       if (!roi_buf->vaddr) {
+               mfc_err_dev("failed to get ROI buffer vaddr\n");
+               s5p_mfc_mem_ion_free(dev, roi_buf);
+               return -ENOMEM;
+       }
+
+       memset(roi_buf->vaddr, 0, buf_size->shared_buf);
+       s5p_mfc_mem_clean(dev, roi_buf, 0, roi_buf->size);
+
+       return 0;
+}
+
+/* Wrapper : allocation ROI buffers */
+int s5p_mfc_alloc_enc_roi_buffer(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       int i;
+
+       for (i = 0; i < MFC_MAX_EXTRA_BUF; i++) {
+               if (mfc_alloc_enc_roi_buffer(ctx, &enc->roi_buf[i]) < 0) {
+                       mfc_err_dev("Remapping shared mem buffer failed.\n");
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+/* Release buffer of ROI macroblock information */
+void s5p_mfc_release_enc_roi_buffer(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       int i;
+
+       for (i = 0; i < MFC_MAX_EXTRA_BUF; i++)
+               if (enc->roi_buf[i].handle)
+                       s5p_mfc_mem_ion_free(ctx->dev, &enc->roi_buf[i]);
+}
+
+int s5p_mfc_otf_alloc_stream_buf(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_debug *debug = &handle->otf_debug;
+       struct s5p_mfc_special_buf *buf;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       int i;
+
+       mfc_debug_enter();
+
+       for (i = 0; i < OTF_MAX_BUF; i++) {
+               buf = &debug->stream_buf[i];
+               buf->buftype = MFCBUF_NORMAL;
+               buf->size = raw->total_plane_size;
+               if (s5p_mfc_mem_ion_alloc(dev, buf)) {
+                       mfc_err_ctx("OTF: Allocating stream buffer failed\n");
+                       return -EINVAL;
+               }
+               buf->vaddr = s5p_mfc_mem_get_vaddr(dev, buf);
+               if (!buf->vaddr) {
+                       mfc_err_dev("OTF: failed to get stream buffer vaddr\n");
+                       s5p_mfc_mem_ion_free(dev, buf);
+                       return -EINVAL;
+               }
+               memset(buf->vaddr, 0, raw->total_plane_size);
+               s5p_mfc_mem_clean(dev, buf, 0, buf->size);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void s5p_mfc_otf_release_stream_buf(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_debug *debug = &handle->otf_debug;
+       struct s5p_mfc_special_buf *buf;
+       int i;
+
+       mfc_debug_enter();
+
+       for (i = 0; i < OTF_MAX_BUF; i++) {
+               buf = &debug->stream_buf[i];
+               if (buf->handle)
+                       s5p_mfc_mem_ion_free(dev, buf);
+       }
+
+       mfc_debug_leave();
+}
+
+/* Allocate firmware */
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
+{
+       unsigned int base_align;
+       size_t firmware_size;
+       struct s5p_mfc_buf_size_v6 *buf_size;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       buf_size = dev->variant->buf_size->buf;
+       base_align = dev->variant->buf_align->mfc_base_align;
+       firmware_size = dev->variant->buf_size->firmware_code;
+       dev->fw.size = firmware_size + buf_size->dev_ctx;
+
+       if (dev->fw_buf.handle)
+               return 0;
+
+       mfc_debug(2, "Allocating memory for firmware.\n");
+       trace_mfc_loadfw_start(dev->fw.size, firmware_size);
+
+       dev->fw_buf.buftype = MFCBUF_NORMAL_FW;
+       dev->fw_buf.size = dev->fw.size;
+       if (s5p_mfc_mem_ion_alloc(dev, &dev->fw_buf)) {
+               mfc_err_dev("Allocating normal firmware buffer failed\n");
+               return -ENOMEM;
+       }
+
+       dev->fw_buf.vaddr = s5p_mfc_mem_get_vaddr(dev, &dev->fw_buf);
+       if (!dev->fw_buf.vaddr) {
+               mfc_err_dev("failed to get normal firmware buffer vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &dev->fw_buf);
+               return -EIO;
+       }
+       mfc_debug(2, "FW normal: 0x%08llx (vaddr: 0x%p), size: %08zu\n",
+                       dev->fw_buf.daddr, dev->fw_buf.vaddr,
+                       dev->fw_buf.size);
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       dev->drm_fw_buf.buftype = MFCBUF_DRM_FW;
+       dev->drm_fw_buf.size = dev->fw.size;
+       if (s5p_mfc_mem_ion_alloc(dev, &dev->drm_fw_buf)) {
+               mfc_err_dev("Allocating DRM firmware buffer failed\n");
+               return -ENOMEM;
+       }
+
+       dev->drm_fw_buf.vaddr = s5p_mfc_mem_get_vaddr(dev, &dev->drm_fw_buf);
+       if (!dev->drm_fw_buf.vaddr) {
+               mfc_err_dev("failed to get DRM firmware buffer vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &dev->fw_buf);
+               s5p_mfc_mem_ion_free(dev, &dev->drm_fw_buf);
+               return -EIO;
+       }
+
+       mfc_debug(2, "FW DRM: 0x%08llx (vaddr: 0x%p), size: %08zu\n",
+                       dev->drm_fw_buf.daddr, dev->drm_fw_buf.vaddr,
+                       dev->drm_fw_buf.size);
+#endif
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Load firmware to MFC */
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
+{
+       struct firmware *fw_blob;
+       size_t firmware_size;
+       int err;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       firmware_size = dev->variant->buf_size->firmware_code;
+
+       /* Firmare has to be present as a separate file or compiled
+        * into kernel. */
+       mfc_debug_enter();
+       mfc_debug(2, "Requesting fw\n");
+       err = request_firmware((const struct firmware **)&fw_blob,
+                                       MFC_FW_NAME, dev->v4l2_dev.dev);
+
+       if (err != 0) {
+               mfc_err_dev("Firmware is not present in the /lib/firmware directory nor compiled in kernel.\n");
+               return -EINVAL;
+       }
+
+       mfc_debug(2, "Ret of request_firmware: %d Size: %zu\n", err, fw_blob->size);
+
+       if (fw_blob->size > firmware_size) {
+               mfc_err_dev("MFC firmware is too big to be loaded.\n");
+               release_firmware(fw_blob);
+               return -ENOMEM;
+       }
+
+       if (dev->fw_buf.handle == NULL || dev->fw_buf.daddr == 0) {
+               mfc_err_dev("MFC firmware is not allocated or was not mapped correctly.\n");
+               release_firmware(fw_blob);
+               return -EINVAL;
+       }
+
+       memcpy(dev->fw_buf.vaddr, fw_blob->data, fw_blob->size);
+       s5p_mfc_mem_clean(dev, &dev->fw_buf, 0, fw_blob->size);
+       s5p_mfc_mem_invalidate(dev, &dev->fw_buf, 0, fw_blob->size);
+       if (dev->drm_fw_buf.vaddr) {
+               memcpy(dev->drm_fw_buf.vaddr, fw_blob->data, fw_blob->size);
+               mfc_debug(2, "copy firmware to secure region\n");
+               s5p_mfc_mem_clean(dev, &dev->drm_fw_buf, 0, fw_blob->size);
+               s5p_mfc_mem_invalidate(dev, &dev->drm_fw_buf, 0, fw_blob->size);
+       }
+       release_firmware(fw_blob);
+       trace_mfc_loadfw_end(dev->fw.size, firmware_size);
+       mfc_debug_leave();
+       return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
+{
+       /* Before calling this function one has to make sure
+        * that MFC is no longer processing */
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (!dev->fw_buf.handle) {
+               mfc_err_dev("firmware memory is already freed\n");
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       s5p_mfc_mem_ion_free(dev, &dev->drm_fw_buf);
+#endif
+
+       s5p_mfc_mem_ion_free(dev, &dev->fw_buf);
+
+       return 0;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_buf.h b/drivers/media/platform/exynos/mfc/s5p_mfc_buf.h
new file mode 100644 (file)
index 0000000..6ffcc96
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_buf.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 __S5P_MFC_BUF_H
+#define __S5P_MFC_BUF_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+/* Memory allocation */
+int s5p_mfc_alloc_common_context(struct s5p_mfc_dev *dev);
+void s5p_mfc_release_common_context(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_alloc_instance_context(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_instance_context(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_enc_roi_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_enc_roi_buffer(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_otf_alloc_stream_buf(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_otf_release_stream_buf(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_alloc_dbg_info_buffer(struct s5p_mfc_dev *dev);
+int s5p_mfc_release_dbg_info_buffer(struct s5p_mfc_dev *dev);
+
+#endif /* __S5P_MFC_BUF_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_cal.c b/drivers/media/platform/exynos/mfc/s5p_mfc_cal.c
new file mode 100644 (file)
index 0000000..b2dcd9c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_cal.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 <trace/events/mfc.h>
+
+#include "s5p_mfc_cal.h"
+
+/* Reset the device */
+int s5p_mfc_reset_mfc(struct s5p_mfc_dev *dev)
+{
+       int i;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       /* Zero Initialization of MFC registers */
+       MFC_WRITEL(0, S5P_FIMV_RISC2HOST_CMD);
+       MFC_WRITEL(0, S5P_FIMV_HOST2RISC_CMD);
+       MFC_WRITEL(0, S5P_FIMV_FW_VERSION);
+
+       for (i = 0; i < S5P_FIMV_REG_CLEAR_COUNT; i++)
+               MFC_WRITEL(0, S5P_FIMV_REG_CLEAR_BEGIN + (i*4));
+
+       MFC_WRITEL(0x1FFF, S5P_FIMV_MFC_RESET);
+       MFC_WRITEL(0, S5P_FIMV_MFC_RESET);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void s5p_mfc_set_risc_base_addr(struct s5p_mfc_dev *dev,
+                                       enum mfc_buf_usage_type buf_type)
+{
+       struct s5p_mfc_special_buf *fw_buf;
+
+       fw_buf = &dev->fw_buf;
+
+       if (buf_type == MFCBUF_DRM)
+               fw_buf = &dev->drm_fw_buf;
+
+       MFC_WRITEL(fw_buf->daddr, S5P_FIMV_RISC_BASE_ADDRESS);
+       mfc_debug(2, "%s F/W Base Address : %08llx\n",
+                       buf_type == MFCBUF_DRM ? "DRM" : "NORMAL", fw_buf->daddr);
+       MFC_TRACE_DEV("%s F/W Base Address : %08llx\n",
+                       buf_type == MFCBUF_DRM ? "DRM" : "NORMAL", fw_buf->daddr);
+}
+
+void s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd)
+{
+       mfc_debug(1, "Issue the command: %d\n", cmd);
+       MFC_TRACE_DEV(">> CMD : %d, (dev:0x%lx, bits:%lx, owned:%d, wl:%d, trans:%d)\n",
+                       cmd, dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                       dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+       trace_mfc_frame_start(dev->curr_ctx, cmd, 0, 0);
+       /* Reset RISC2HOST command except nal q stop command */
+       if (cmd != S5P_FIMV_H2R_CMD_STOP_QUEUE)
+               MFC_WRITEL(0x0, S5P_FIMV_RISC2HOST_CMD);
+
+       /* Start the timeout watchdog */
+       if ((cmd != S5P_FIMV_H2R_CMD_NAL_QUEUE) && (cmd != S5P_FIMV_H2R_CMD_STOP_QUEUE))
+               s5p_mfc_watchdog_start_tick(dev);
+
+       if (dbg_enable) {
+               /* For FW debugging */
+               s5p_mfc_dbg_set_addr(dev);
+               s5p_mfc_dbg_enable(dev);
+       }
+
+       /* Issue the command */
+       MFC_WRITEL(cmd, S5P_FIMV_HOST2RISC_CMD);
+       MFC_WRITEL(0x1, S5P_FIMV_HOST2RISC_INT);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_cal.h b/drivers/media/platform/exynos/mfc/s5p_mfc_cal.h
new file mode 100644 (file)
index 0000000..6413fcd
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_cal.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 __S5P_MFC_CAL_H
+#define __S5P_MFC_CAL_H __FILE__
+
+#include <linux/io.h>
+
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_common.h"
+
+#include "s5p_mfc_utils.h"
+
+
+#define s5p_mfc_get_int_reason()       (MFC_READL(S5P_FIMV_RISC2HOST_CMD)              \
+                                               & S5P_FIMV_RISC2HOST_CMD_MASK)
+#define s5p_mfc_clear_int_sfr()                                \
+               do {                                                    \
+                       MFC_WRITEL(0, S5P_FIMV_RISC2HOST_CMD);  \
+                       MFC_WRITEL(0, S5P_FIMV_RISC2HOST_INT);  \
+               } while (0)
+
+static inline int s5p_mfc_check_int_cmd(struct s5p_mfc_dev *dev)
+{
+       if (MFC_READL(S5P_FIMV_RISC2HOST_INT))
+               return MFC_READL(S5P_FIMV_RISC2HOST_CMD);
+       else
+               return 0;
+}
+
+static inline int s5p_mfc_stop_bus(struct s5p_mfc_dev *dev)
+{
+       unsigned int status;
+       unsigned long timeout;
+
+       /* Reset */
+       MFC_WRITEL(0x1, S5P_FIMV_MFC_BUS_RESET_CTRL);
+
+       timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+       /* Check bus status */
+       do {
+               if (time_after(jiffies, timeout)) {
+                       mfc_err_dev("Timeout while resetting MFC.\n");
+                       return -EIO;
+               }
+               status = MFC_READL(S5P_FIMV_MFC_BUS_RESET_CTRL);
+       } while ((status & 0x2) == 0);
+
+       return 0;
+}
+
+static inline void s5p_mfc_start_bus(struct s5p_mfc_dev *dev)
+{
+       int val;
+
+       val = MFC_READL(S5P_FIMV_MFC_BUS_RESET_CTRL);
+       val &= ~(0x1);
+       MFC_WRITEL(val, S5P_FIMV_MFC_BUS_RESET_CTRL);
+}
+
+static inline void s5p_mfc_risc_on(struct s5p_mfc_dev *dev)
+{
+       s5p_mfc_clean_dev_int_flags(dev);
+
+       MFC_WRITEL(0x1, S5P_FIMV_RISC_ON);
+       if (FW_HAS_HWACG(dev))
+               MFC_WRITEL(0x0, S5P_FIMV_MFC_OFF);
+       mfc_debug(1, "RISC_ON\n");
+       MFC_TRACE_DEV(">> RISC ON\n");
+}
+
+static inline void s5p_mfc_risc_off(struct s5p_mfc_dev *dev)
+{
+       unsigned int status;
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+       /* Check pending status */
+       do {
+               if (time_after(jiffies, timeout)) {
+                       mfc_err_dev("Timeout while pendng clear\n");
+                       mfc_err_dev("MFC access pending state: %#x\n", status);
+                       mfc_err_dev("MFC access pending R: %#x, W: %#x\n",
+                                       MFC_READL(S5P_FIMV_MFC_RPEND),
+                                       MFC_READL(S5P_FIMV_MFC_WPEND));
+                       break;
+               }
+               status = MFC_READL(S5P_FIMV_MFC_BUS_STATUS);
+       } while (status != 0);
+
+       MFC_WRITEL(0x0, S5P_FIMV_RISC_ON);
+}
+
+static inline void s5p_mfc_mfc_off(struct s5p_mfc_dev *dev)
+{
+       if (FW_HAS_HWACG(dev)) {
+               mfc_info_dev("MFC h/w state: %d\n",
+                               MFC_READL(S5P_FIMV_MFC_STATE) & 0x7);
+               MFC_WRITEL(0x1, S5P_FIMV_MFC_OFF);
+       }
+}
+
+static inline void s5p_mfc_enable_all_clocks(struct s5p_mfc_dev *dev)
+{
+       /* Enable all FW clock gating */
+       MFC_WRITEL(0xFFFFFFFF, S5P_FIMV_MFC_FW_CLOCK);
+}
+
+int s5p_mfc_reset_mfc(struct s5p_mfc_dev *dev);
+void s5p_mfc_set_risc_base_addr(struct s5p_mfc_dev *dev,
+                               enum mfc_buf_usage_type buf_type);
+void s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd);
+
+#endif /* __S5P_MFC_CAL_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_cmd.c b/drivers/media/platform/exynos/mfc/s5p_mfc_cmd.c
new file mode 100644 (file)
index 0000000..ca81737
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_cmd_v6.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 <trace/events/mfc.h>
+
+#include "s5p_mfc_cmd.h"
+
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+
+int s5p_mfc_cmd_sys_init(struct s5p_mfc_dev *dev,
+                                       enum mfc_buf_usage_type buf_type)
+{
+       struct s5p_mfc_buf_size_v6 *buf_size;
+       struct s5p_mfc_special_buf *ctx_buf;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       s5p_mfc_clean_dev_int_flags(dev);
+
+       buf_size = dev->variant->buf_size->buf;
+       ctx_buf = &dev->common_ctx_buf;
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (buf_type == MFCBUF_DRM)
+               ctx_buf = &dev->drm_common_ctx_buf;
+#endif
+       MFC_WRITEL(ctx_buf->daddr, S5P_FIMV_CONTEXT_MEM_ADDR);
+       MFC_WRITEL(buf_size->dev_ctx, S5P_FIMV_CONTEXT_MEM_SIZE);
+
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void s5p_mfc_cmd_sleep(struct s5p_mfc_dev *dev)
+{
+       mfc_debug_enter();
+
+       s5p_mfc_clean_dev_int_flags(dev);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP);
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_cmd_wakeup(struct s5p_mfc_dev *dev)
+{
+       mfc_debug_enter();
+
+       s5p_mfc_clean_dev_int_flags(dev);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP);
+
+       mfc_debug_leave();
+}
+
+/* Open a new instance and get its number */
+int s5p_mfc_cmd_open_inst(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       mfc_debug(2, "Requested codec mode: %d\n", ctx->codec_mode);
+
+       MFC_WRITEL(ctx->codec_mode, S5P_FIMV_CODEC_TYPE);
+       MFC_WRITEL(ctx->instance_ctx_buf.daddr, S5P_FIMV_CONTEXT_MEM_ADDR);
+       MFC_WRITEL(ctx->instance_ctx_buf.size, S5P_FIMV_CONTEXT_MEM_SIZE);
+       if (ctx->type == MFCINST_DECODER)
+               MFC_WRITEL(ctx->dec_priv->crc_enable, S5P_FIMV_D_CRC_CTRL);
+
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Close instance */
+int s5p_mfc_cmd_close_inst(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+int s5p_mfc_cmd_dpb_flush(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       if (ON_RES_CHANGE(ctx))
+               mfc_err_ctx("dpb flush on res change(state:%d)\n", ctx->state);
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_DPB_FLUSH);
+
+       return 0;
+}
+
+int s5p_mfc_cmd_cache_flush(struct s5p_mfc_dev *dev)
+{
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       s5p_mfc_clean_dev_int_flags(dev);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CACHE_FLUSH);
+
+       return 0;
+}
+
+int s5p_mfc_cmd_dec_init_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       unsigned int reg = 0, pix_val, mem_type = 0;
+       int ret;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_ctx("no mfc device to run\n");
+               return -EINVAL;
+       }
+       /* Initializing decoding - parsing header */
+       /* Header was parsed now starting processing
+        * First set the output frame buffers
+        * s5p_mfc_alloc_dec_buffers(ctx); */
+
+       switch (ctx->dst_fmt->fourcc) {
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV12N:
+       case V4L2_PIX_FMT_NV12MT_16X16:
+       case V4L2_PIX_FMT_NV16M:
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV21M:
+       case V4L2_PIX_FMT_NV61M:
+               pix_val = 1;
+               break;
+       case V4L2_PIX_FMT_YVU420M:
+               pix_val = 2;
+               break;
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YUV420N:
+               pix_val = 3;
+               break;
+       /* 10bit */
+       case V4L2_PIX_FMT_NV12N_10B:
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV16M_S10B:
+               mem_type = 0;
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV16M_P210:
+               mem_type = 1;
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV21M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+               mem_type = 0;
+               pix_val = 1;
+               break;
+       case V4L2_PIX_FMT_NV21M_P010:
+       case V4L2_PIX_FMT_NV61M_P210:
+               mem_type = 1;
+               pix_val = 1;
+               break;
+       default:
+               pix_val = 0;
+               break;
+       }
+       reg = 0;
+       reg |= pix_val;
+       reg |= (mem_type << 4);
+       MFC_WRITEL(reg, S5P_FIMV_PIXEL_FORMAT);
+       mfc_debug(2, "pixel format: %d, mem_type for 10bit: %d (reg: %#x)\n",
+                       pix_val, mem_type, reg);
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       ret = s5p_mfc_set_dec_codec_buffers(ctx);
+       if (ret) {
+               mfc_info_ctx("isn't enough codec buffer size, re-alloc!\n");
+               s5p_mfc_release_codec_buffers(ctx);
+               ret = s5p_mfc_alloc_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to allocate decoding buffers.\n");
+                       return ret;
+               }
+               ret = s5p_mfc_set_dec_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to alloc frame mem.\n");
+                       return ret;
+               }
+       }
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_INIT_BUFFERS);
+
+       return ret;
+}
+
+int s5p_mfc_cmd_enc_init_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       int ret;
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       /* Header was generated now starting processing
+        * First set the reference frame buffers
+        */
+       if (!ctx->codec_buf.handle) {
+               mfc_info_ctx("there isn't codec buffer, re-alloc!\n");
+               ret = s5p_mfc_alloc_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to allocate encoding buffers.\n");
+                       return ret;
+               }
+       }
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       ret = s5p_mfc_set_enc_codec_buffers(ctx);
+       if (ret) {
+               mfc_info_ctx("isn't enough codec buffer size, re-alloc!\n");
+               s5p_mfc_release_codec_buffers(ctx);
+               ret = s5p_mfc_alloc_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to allocate encoding buffers.\n");
+                       return ret;
+               }
+               ret = s5p_mfc_set_enc_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to set enc codec buffers.\n");
+                       return ret;
+               }
+       }
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_INIT_BUFFERS);
+
+       return ret;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_cmd.h b/drivers/media/platform/exynos/mfc/s5p_mfc_cmd.h
new file mode 100644 (file)
index 0000000..0455446
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_cmd.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 __S5P_MFC_CMD_H
+#define __S5P_MFC_CMD_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_cmd_sys_init(struct s5p_mfc_dev *dev,
+                               enum mfc_buf_usage_type buf_type);
+void s5p_mfc_cmd_sleep(struct s5p_mfc_dev *dev);
+void s5p_mfc_cmd_wakeup(struct s5p_mfc_dev *dev);
+int s5p_mfc_cmd_open_inst(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_cmd_close_inst(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_cmd_dpb_flush(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_cmd_cache_flush(struct s5p_mfc_dev *dev);
+int s5p_mfc_cmd_dec_init_buffers(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_cmd_enc_init_buffers(struct s5p_mfc_ctx *ctx);
+
+#endif /* __S5P_MFC_CMD_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_common.h b/drivers/media/platform/exynos/mfc/s5p_mfc_common.h
new file mode 100644 (file)
index 0000000..b70a86c
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_common.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 __S5P_MFC_COMMON_H
+#define __S5P_MFC_COMMON_H __FILE__
+
+#include <linux/exynos_iovmm.h>
+
+#include "s5p_mfc_regs_v10.h"
+#include "s5p_mfc_macros.h"
+#include "s5p_mfc_debug.h"
+#include "exynos_mfc_media.h"
+#include "s5p_mfc_data_struct.h"
+
+#define MFC_DRIVER_INFO                170522
+
+#define MFC_MAX_REF_BUFS       2
+#define MFC_FRAME_PLANES       2
+#define MFC_INFO_INIT_FD       -1
+
+#define MFC_MAX_DRM_CTX                2
+
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT                5000
+/* Interrupt short timeout */
+#define MFC_INT_SHORT_TIMEOUT  800
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT         500
+
+/* This value guarantees 299.4msec ~ 2.25sec according to MFC clock (668MHz ~ 89MHz)
+ * releated with S5P_FIMV_DEC_TIMEOUT_VALUE */
+#define MFC_TIMEOUT_VALUE      200000000
+
+#define NUM_MPEG4_LF_BUF       2
+
+#define DEFAULT_TAG            (0xE05)
+
+#define MFC_NO_INSTANCE_SET    -1
+
+#define MFC_ENC_CAP_PLANE_COUNT        1
+#define MFC_ENC_OUT_PLANE_COUNT        2
+
+#define MFC_NAME_LEN           16
+#define MFC_FW_NAME            "mfc_fw.bin"
+
+#define STUFF_BYTE             4
+
+#define MFC_BASE_MASK          ((1 << 17) - 1)
+
+#define FLAG_LAST_FRAME                0x80000000
+#define FLAG_EMPTY_DATA                0x40000000
+#define FLAG_CSD               0x20000000
+
+/* MFC conceal color is black */
+#define MFC_CONCEAL_COLOR      0x8020000
+
+#define vb_to_mfc_buf(x)               \
+       container_of(x, struct s5p_mfc_buf, vb.vb2_buf)
+
+#define fh_to_mfc_ctx(x)               \
+       container_of(x, struct s5p_mfc_ctx, fh)
+
+#define call_bop(b, op, args...)       \
+       (b->op ? b->op(args) : 0)
+
+#define call_cop(c, op, args...)       \
+       (((c)->c_ops->op) ?             \
+               ((c)->c_ops->op(args)) : 0)
+
+#define        MFC_CTRL_TYPE_GET       (MFC_CTRL_TYPE_GET_SRC | MFC_CTRL_TYPE_GET_DST)
+#define        MFC_CTRL_TYPE_SRC       (MFC_CTRL_TYPE_SET | MFC_CTRL_TYPE_GET_SRC)
+#define        MFC_CTRL_TYPE_DST       (MFC_CTRL_TYPE_GET_DST)
+
+#define MFC_FMT_DEC    0
+#define MFC_FMT_ENC    1
+#define MFC_FMT_RAW    2
+
+/* node check */
+#define IS_DEC_NODE(n)         ((n == EXYNOS_VIDEONODE_MFC_DEC) ||     \
+                               (n == EXYNOS_VIDEONODE_MFC_DEC_DRM))
+#define IS_ENC_NODE(n)         ((n == EXYNOS_VIDEONODE_MFC_ENC) ||     \
+                               (n == EXYNOS_VIDEONODE_MFC_ENC_DRM) ||  \
+                               (n == EXYNOS_VIDEONODE_MFC_ENC_OTF) ||  \
+                               (n == EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM))
+
+/* Decoder codec mode check */
+#define IS_H264_DEC(ctx)       ((ctx)->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+#define IS_H264_MVC_DEC(ctx)   ((ctx)->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC)
+#define IS_MPEG4_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
+#define IS_FIMV1_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_FIMV1_DEC)
+#define IS_FIMV2_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_FIMV2_DEC)
+#define IS_FIMV3_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_FIMV3_DEC)
+#define IS_FIMV4_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_FIMV4_DEC)
+#define IS_VC1_DEC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_VC1_DEC)
+#define IS_VC1_RCV_DEC(ctx)    ((ctx)->codec_mode == S5P_FIMV_CODEC_VC1_RCV_DEC)
+#define IS_MPEG2_DEC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_MPEG2_DEC)
+#define IS_HEVC_DEC(ctx)       ((ctx)->codec_mode == S5P_FIMV_CODEC_HEVC_DEC)
+#define IS_VP9_DEC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_VP9_DEC)
+#define IS_BPG_DEC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_BPG_DEC)
+
+/* Encoder codec mode check */
+#define IS_H264_ENC(ctx)       ((ctx)->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+#define IS_MPEG4_ENC(ctx)      ((ctx)->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
+#define IS_H263_ENC(ctx)       ((ctx)->codec_mode == S5P_FIMV_CODEC_H263_ENC)
+#define IS_VP8_ENC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_VP8_ENC)
+#define IS_HEVC_ENC(ctx)       ((ctx)->codec_mode == S5P_FIMV_CODEC_HEVC_ENC)
+#define IS_VP9_ENC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_VP9_ENC)
+#define IS_BPG_ENC(ctx)                ((ctx)->codec_mode == S5P_FIMV_CODEC_BPG_ENC)
+
+#define CODEC_NOT_CODED(ctx)   (IS_MPEG4_DEC(ctx) || IS_VC1_DEC(ctx) || IS_VC1_RCV_DEC(ctx))
+#define CODEC_INTERLACED(ctx)  (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) ||    \
+                               IS_MPEG2_DEC(ctx) || IS_MPEG4_DEC(ctx) ||       \
+                               IS_VC1_DEC(ctx) || IS_VC1_RCV_DEC(ctx))
+#define CODEC_MULTIFRAME(ctx)  (IS_MPEG4_DEC(ctx) || IS_VP9_DEC(ctx) ||        \
+                               IS_FIMV2_DEC(ctx) || IS_FIMV3_DEC(ctx) || IS_FIMV4_DEC(ctx))
+#define CODEC_10BIT(ctx)       (IS_HEVC_DEC(ctx) || IS_HEVC_ENC(ctx) ||        \
+                               IS_VP9_DEC(ctx) || IS_VP9_ENC(ctx) ||           \
+                               IS_BPG_DEC(ctx) || IS_BPG_ENC(ctx))
+#define CODEC_422FORMAT(ctx)   (IS_HEVC_DEC(ctx) || IS_HEVC_ENC(ctx) ||        \
+                               IS_BPG_DEC(ctx) || IS_BPG_ENC(ctx))
+#define ON_RES_CHANGE(ctx)     (((ctx)->state >= MFCINST_RES_CHANGE_INIT) &&   \
+                                ((ctx)->state <= MFCINST_RES_CHANGE_END))
+
+/* UHD resoluition */
+#define MFC_UHD_RES            (3840 * 2160)
+#define IS_UHD_RES(ctx)                (((ctx)->img_width * (ctx)->img_height) == MFC_UHD_RES)
+#define OVER_UHD_ENC60(ctx)    ((((ctx)->img_width * (ctx)->img_height) == MFC_UHD_RES) && \
+                               ((ctx)->type == MFCINST_ENCODER) &&     \
+                               ((ctx)->framerate / 1000) >= 60)
+
+/* Extra information for Decoder */
+#define        DEC_SET_DUAL_DPB                (1 << 0)
+#define        DEC_SET_DYNAMIC_DPB             (1 << 1)
+#define        DEC_SET_LAST_FRAME_INFO         (1 << 2)
+#define        DEC_SET_SKYPE_FLAG              (1 << 3)
+
+/* Extra information for Encoder */
+#define        ENC_SET_RGB_INPUT               (1 << 0)
+#define        ENC_SET_SPARE_SIZE              (1 << 1)
+#define        ENC_SET_TEMP_SVC_CH             (1 << 2)
+#define        ENC_SET_SKYPE_FLAG              (1 << 3)
+#define        ENC_SET_ROI_CONTROL             (1 << 4)
+#define        ENC_SET_QP_BOUND_PB             (1 << 5)
+#define        ENC_SET_FIXED_SLICE             (1 << 6)
+#define        ENC_SET_PVC_MODE                (1 << 7)
+#define        ENC_SET_RATIO_OF_INTRA          (1 << 8)
+
+#define MFC_VER_MAJOR(dev)     ((s5p_mfc_version(dev) >> 8) & 0xFF)
+#define MFC_VER_MINOR(dev)     (s5p_mfc_version(dev) & 0xFF)
+
+/*
+ * Version Description
+ */
+#define IS_MFCV10X(dev)                ((s5p_mfc_version(dev) == 0xA0) || \
+                               (s5p_mfc_version(dev) == 0xA01))
+#define IS_MFCV11X(dev)                (s5p_mfc_version(dev) == 0x1100)
+#define IS_MFCV12X(dev)                (s5p_mfc_version(dev) == 0x1200)
+#define FROM_MFCV11X(dev)      (IS_MFCV11X(dev) || IS_MFCV12X(dev))
+#define FROM_MFCV10X(dev)      (IS_MFCV10X(dev) || IS_MFCV11X(dev) || \
+                               IS_MFCV12X(dev))
+
+/* supported feature macros by F/W version */
+#define FW_HAS_CONCEAL_CONTROL(dev)    (FROM_MFCV10X(dev))
+#define FW_HAS_ROI_CONTROL(dev)                (FROM_MFCV10X(dev))
+#define FW_HAS_HWACG(dev)              (FROM_MFCV10X(dev))
+#define FW_HAS_SPECIAL_PARSING(dev)    (FROM_MFCV10X(dev))
+#define FW_SUPPORT_SKYPE(dev)          (FROM_MFCV10X(dev) &&           \
+                                       (dev->fw.date >= 0x150901))
+#define FW_HAS_VIDEO_SIGNAL_TYPE(dev)  (FROM_MFCV10X(dev) &&           \
+                                       (dev->fw.date >= 0x151223))
+#define FW_HAS_SEI_INFO_FOR_HDR(dev)   (FROM_MFCV10X(dev) &&           \
+                                       (dev->fw.date >= 0x160415))
+#define FW_HAS_BLACK_BAR_DETECT(dev)   (FROM_MFCV11X(dev) &&           \
+                                       (dev->fw.date >= 0x161017))
+#define FW_HAS_VP9_HDR(dev)            (IS_MFCV12X(dev) &&             \
+                                       (dev->fw.date >= 0x171023))
+#define FW_HAS_RATIO_INTRA_CTRL(dev)   (FROM_MFCV11X(dev) &&           \
+                                       (dev->fw.date >= 0x171113))
+
+static inline unsigned int s5p_mfc_version(struct s5p_mfc_dev *dev)
+{
+       unsigned int version = 0;
+
+       switch (dev->pdata->ip_ver) {
+       case IP_VER_MFC_4P_0:
+       case IP_VER_MFC_4P_1:
+       case IP_VER_MFC_4P_2:
+               version = 0x51;
+               break;
+       case IP_VER_MFC_5G_0:
+               version = 0x61;
+               break;
+       case IP_VER_MFC_5G_1:
+       case IP_VER_MFC_5A_0:
+       case IP_VER_MFC_5A_1:
+               version = 0x65;
+               break;
+       case IP_VER_MFC_6A_0:
+       case IP_VER_MFC_6A_1:
+               version = 0x72;
+               break;
+       case IP_VER_MFC_6A_2:
+               version = 0x723;
+               break;
+       case IP_VER_MFC_7A_0:
+               version = 0x80;
+               break;
+       case IP_VER_MFC_8I_0:
+               version = 0x90;
+               break;
+       case IP_VER_MFC_6I_0:
+               version = 0x78;
+               break;
+       case IP_VER_MFC_8J_0:
+               version = 0xA0;
+               break;
+       case IP_VER_MFC_8J_1:
+               version = 0xA01;
+               break;
+       case IP_VER_MFC_8K_0:
+               version = 0x1100;
+               break;
+       case IP_VER_MFC_7K_0:
+               version = 0x1120;
+               break;
+       case IP_VER_MFC_9L_0:
+               version = 0x1200;
+               break;
+       }
+
+       return version;
+}
+
+#endif /* __S5P_MFC_COMMON_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.c b/drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.c
new file mode 100644 (file)
index 0000000..a5c4ac8
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.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 "s5p_mfc_ctrl.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_utils.h"
+
+/* Initialize hardware */
+static int mfc_init_hw(struct s5p_mfc_dev *dev, enum mfc_buf_usage_type buf_type)
+{
+       int fw_ver;
+       int ret = 0;
+       int curr_ctx_is_drm_backup;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       curr_ctx_is_drm_backup = dev->curr_ctx_is_drm;
+
+       if (!dev->fw_buf.handle)
+               return -EINVAL;
+
+       /* 0. MFC reset */
+       mfc_debug(2, "MFC reset...\n");
+
+       /* At init time, do not call secure API */
+       if (buf_type == MFCBUF_NORMAL)
+               dev->curr_ctx_is_drm = 0;
+       else if (buf_type == MFCBUF_DRM)
+               dev->curr_ctx_is_drm = 1;
+
+       ret = s5p_mfc_pm_clock_on(dev);
+       if (ret) {
+               mfc_err_dev("Failed to enable clock before reset(%d)\n", ret);
+               dev->curr_ctx_is_drm = curr_ctx_is_drm_backup;
+               return ret;
+       }
+
+       ret = s5p_mfc_reset_mfc(dev);
+       if (ret) {
+               mfc_err_dev("Failed to reset MFC - timeout.\n");
+               goto err_init_hw;
+       }
+       mfc_debug(2, "Done MFC reset...\n");
+
+       /* 1. Set DRAM base Addr */
+       s5p_mfc_set_risc_base_addr(dev, buf_type);
+
+       /* 2. Release reset signal to the RISC */
+       s5p_mfc_risc_on(dev);
+
+       mfc_debug(2, "Will now wait for completion of firmware transfer.\n");
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+               mfc_err_dev("Failed to RISC_ON\n");
+               s5p_mfc_clean_dev_int_flags(dev);
+               ret = -EIO;
+               goto err_init_hw;
+       }
+
+       /* 3. Initialize firmware */
+       ret = s5p_mfc_cmd_sys_init(dev, buf_type);
+       if (ret) {
+               mfc_err_dev("Failed to send command to MFC - timeout.\n");
+               goto err_init_hw;
+       }
+
+       mfc_debug(2, "Ok, now will write a command to init the system\n");
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+               mfc_err_dev("Failed to SYS_INIT\n");
+               s5p_mfc_clean_dev_int_flags(dev);
+               ret = -EIO;
+               goto err_init_hw;
+       }
+
+       dev->int_condition = 0;
+       if (dev->int_err != 0 || dev->int_reason !=
+                                               S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+               /* Failure. */
+               mfc_err_dev("Failed to init firmware - error: %d"
+                               " int: %d.\n", dev->int_err, dev->int_reason);
+               ret = -EIO;
+               goto err_init_hw;
+       }
+
+       dev->fw.fimv_info = s5p_mfc_get_fimv_info();
+       if (dev->fw.fimv_info != 'D' && dev->fw.fimv_info != 'E')
+               dev->fw.fimv_info = 'N';
+
+       mfc_info_dev("MFC v%x.%x, F/W: %02xyy, %02xmm, %02xdd (%c)\n",
+                MFC_VER_MAJOR(dev),
+                MFC_VER_MINOR(dev),
+                s5p_mfc_get_fw_ver_year(),
+                s5p_mfc_get_fw_ver_month(),
+                s5p_mfc_get_fw_ver_date(),
+                dev->fw.fimv_info);
+
+       dev->fw.date = s5p_mfc_get_fw_ver_all();
+       /* Check MFC version and F/W version */
+       fw_ver = s5p_mfc_get_mfc_version();
+       if (fw_ver != s5p_mfc_version(dev)) {
+               mfc_err_dev("Invalid F/W version(0x%x) for MFC H/W(0x%x)\n",
+                               fw_ver, s5p_mfc_version(dev));
+               ret = -EIO;
+               goto err_init_hw;
+       }
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       /* Cache flush for base address change */
+       s5p_mfc_cmd_cache_flush(dev);
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_CACHE_FLUSH_RET)) {
+               mfc_err_dev("Failed to CACHE_FLUSH\n");
+               s5p_mfc_clean_dev_int_flags(dev);
+               ret = -EIO;
+               goto err_init_hw;
+       }
+
+       if (buf_type == MFCBUF_DRM && !curr_ctx_is_drm_backup) {
+               s5p_mfc_pm_clock_off(dev);
+               dev->curr_ctx_is_drm = curr_ctx_is_drm_backup;
+               s5p_mfc_pm_clock_on_with_base(dev, MFCBUF_NORMAL);
+       }
+#endif
+
+err_init_hw:
+       s5p_mfc_pm_clock_off(dev);
+       dev->curr_ctx_is_drm = curr_ctx_is_drm_backup;
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Wrapper : Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+       int ret;
+
+       ret = mfc_init_hw(dev, MFCBUF_NORMAL);
+       if (ret)
+               return ret;
+
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       if (dev->fw.drm_status) {
+               ret = mfc_init_hw(dev, MFCBUF_DRM);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       return ret;
+}
+
+/* Deinitialize hardware */
+void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
+{
+       int ret;
+
+       mfc_debug(2, "mfc deinit start\n");
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       ret = s5p_mfc_pm_clock_on(dev);
+       if (ret) {
+               mfc_err_dev("Failed to enable clock before reset(%d)\n", ret);
+               return;
+       }
+
+       s5p_mfc_mfc_off(dev);
+
+       s5p_mfc_pm_clock_off(dev);
+
+       mfc_debug(2, "mfc deinit completed\n");
+}
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_ctx *ctx;
+       int ret;
+       int old_state, i;
+       int need_cache_flush = 0;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       ctx = dev->ctx[dev->curr_ctx];
+       if (!ctx) {
+               for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+                       if (dev->ctx[i]) {
+                               ctx = dev->ctx[i];
+                               break;
+                       }
+               }
+               if (!ctx) {
+                       mfc_err_dev("no mfc context to run\n");
+                       return -EINVAL;
+               } else {
+                       mfc_info_dev("ctx is changed %d -> %d\n",
+                                       dev->curr_ctx, ctx->num);
+                       dev->curr_ctx = ctx->num;
+                       if (dev->curr_ctx_is_drm != ctx->is_drm) {
+                               need_cache_flush = 1;
+                               mfc_info_dev("DRM attribute is changed %d->%d\n",
+                                               dev->curr_ctx_is_drm, ctx->is_drm);
+                       }
+               }
+       }
+       old_state = ctx->state;
+       s5p_mfc_change_state(ctx, MFCINST_ABORT);
+       MFC_TRACE_DEV_HWLOCK("**sleep (ctx:%d)\n", ctx->num);
+       ret = s5p_mfc_get_hwlock_dev(dev);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get hwlock.\n");
+               mfc_err_dev("dev.hwlock.dev = 0x%lx, bits = 0x%lx, owned_by_irq = %d, wl_count = %d, transfer_owner = %d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+               return -EBUSY;
+       }
+
+       mfc_info_dev("curr_ctx_is_drm:%d, hwlock.bits:%lu, hwlock.dev:%lu\n",
+                       dev->curr_ctx_is_drm, dev->hwlock.bits, dev->hwlock.dev);
+
+       s5p_mfc_change_state(ctx, old_state);
+       s5p_mfc_pm_clock_on(dev);
+
+       if (need_cache_flush)
+               s5p_mfc_cache_flush(dev, ctx->is_drm);
+
+       s5p_mfc_cmd_sleep(dev);
+
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
+               mfc_err_dev("Failed to SLEEP\n");
+               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_SLEEP);
+               s5p_mfc_dump_info_and_stop_hw(dev);
+               return -EIO;
+       }
+
+       dev->int_condition = 0;
+       if (dev->int_err != 0 || dev->int_reason !=
+                                               S5P_FIMV_R2H_CMD_SLEEP_RET) {
+               /* Failure. */
+               mfc_err_dev("Failed to sleep - error: %d"
+                               " int: %d.\n", dev->int_err, dev->int_reason);
+               ret = -EIO;
+               goto err_mfc_sleep;
+       }
+
+       dev->sleep = 1;
+
+err_mfc_sleep:
+       s5p_mfc_mfc_off(dev);
+       s5p_mfc_pm_clock_off(dev);
+       s5p_mfc_release_hwlock_dev(dev);
+       mfc_debug_leave();
+
+       return ret;
+}
+
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
+{
+       enum mfc_buf_usage_type buf_type;
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       mfc_info_dev("curr_ctx_is_drm:%d\n", dev->curr_ctx_is_drm);
+
+       MFC_TRACE_DEV_HWLOCK("**wakeup\n");
+       ret = s5p_mfc_get_hwlock_dev(dev);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get hwlock.\n");
+               mfc_err_dev("dev.hwlock.dev = 0x%lx, bits = 0x%lx, owned_by_irq = %d, wl_count = %d, transfer_owner = %d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+               return -EBUSY;
+       }
+
+       dev->sleep = 0;
+
+       /* 0. MFC reset */
+       mfc_debug(2, "MFC reset...\n");
+
+       s5p_mfc_pm_clock_on(dev);
+
+       ret = s5p_mfc_reset_mfc(dev);
+       if (ret) {
+               mfc_err_dev("Failed to reset MFC - timeout.\n");
+               goto err_mfc_wakeup;
+       }
+       mfc_debug(2, "Done MFC reset...\n");
+       if (dev->curr_ctx_is_drm)
+               buf_type = MFCBUF_DRM;
+       else
+               buf_type = MFCBUF_NORMAL;
+
+       /* 1. Set DRAM base Addr */
+       s5p_mfc_set_risc_base_addr(dev, buf_type);
+
+       /* 2. Release reset signal to the RISC */
+       s5p_mfc_risc_on(dev);
+
+       mfc_debug(2, "Will now wait for completion of firmware transfer.\n");
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+               mfc_err_dev("Failed to RISC_ON\n");
+               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_RISC_ON);
+               s5p_mfc_dump_info_and_stop_hw(dev);
+               return -EIO;
+       }
+
+       mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
+       s5p_mfc_cmd_wakeup(dev);
+
+       mfc_debug(2, "Will now wait for completion of firmware wake up.\n");
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
+               mfc_err_dev("Failed to WAKEUP\n");
+               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_WAKEUP);
+               s5p_mfc_dump_info_and_stop_hw(dev);
+               return -EIO;
+       }
+
+       dev->int_condition = 0;
+       if (dev->int_err != 0 || dev->int_reason !=
+                                               S5P_FIMV_R2H_CMD_WAKEUP_RET) {
+               /* Failure. */
+               mfc_err_dev("Failed to wakeup - error: %d"
+                               " int: %d.\n", dev->int_err, dev->int_reason);
+               ret = -EIO;
+               goto err_mfc_wakeup;
+       }
+
+err_mfc_wakeup:
+       s5p_mfc_pm_clock_off(dev);
+
+       s5p_mfc_release_hwlock_dev(dev);
+
+       mfc_debug_leave();
+
+       return ret;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.h b/drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.h
new file mode 100644 (file)
index 0000000..600083f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_ctrl.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 __S5P_MFC_CTRL_H
+#define __S5P_MFC_CTRL_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
+
+#endif /* __S5P_MFC_CTRL_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_data_struct.h b/drivers/media/platform/exynos/mfc/s5p_mfc_data_struct.h
new file mode 100644 (file)
index 0000000..e9c6399
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_data_struct.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 __S5P_MFC_DATA_STRUCT_H
+#define __S5P_MFC_DATA_STRUCT_H __FILE__
+
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ
+#define CONFIG_MFC_USE_BUS_DEVFREQ
+#endif
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+#include <linux/pm_qos.h>
+#include <soc/samsung/bts.h>
+#endif
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "exynos_mfc_media.h"
+
+#define MFC_NUM_CONTEXTS               32
+#define MFC_MAX_PLANES                 3
+#define MFC_MAX_DPBS                   32
+#define MFC_MAX_BUFFERS                        32
+#define MFC_MAX_EXTRA_BUF              10
+#define MFC_TIME_INDEX                 15
+#define MFC_SFR_LOGGING_COUNT_SET1     4
+#define MFC_SFR_LOGGING_COUNT_SET2     23
+#define MFC_LOGGING_DATA_SIZE          256
+
+#define HWFC_MAX_BUF                   10
+#define OTF_MAX_BUF                    30
+
+/* Maximum number of temporal layers */
+#define VIDEO_MAX_TEMPORAL_LAYERS      7
+
+/*
+ *  MFC version
+ */
+enum mfc_ip_version {
+       IP_VER_MFC_4P_0,
+       IP_VER_MFC_4P_1,
+       IP_VER_MFC_4P_2,
+       IP_VER_MFC_5G_0,
+       IP_VER_MFC_5G_1,
+       IP_VER_MFC_5A_0,
+       IP_VER_MFC_5A_1,
+       IP_VER_MFC_6A_0,
+       IP_VER_MFC_6A_1,
+       IP_VER_MFC_6A_2,
+       IP_VER_MFC_7A_0,
+       IP_VER_MFC_8I_0,
+       IP_VER_MFC_6I_0,
+       IP_VER_MFC_8J_0,
+       IP_VER_MFC_8J_1,
+       IP_VER_MFC_8K_0,
+       IP_VER_MFC_7K_0,
+       IP_VER_MFC_9L_0,
+};
+
+/*
+ *  MFC region id for smc
+ */
+enum {
+       FC_MFC_EXYNOS_ID_MFC_SH        = 0,
+       FC_MFC_EXYNOS_ID_VIDEO         = 1,
+       FC_MFC_EXYNOS_ID_MFC_FW        = 2,
+       FC_MFC_EXYNOS_ID_SECTBL        = 3,
+       FC_MFC_EXYNOS_ID_G2D_WFD       = 4,
+       FC_MFC_EXYNOS_ID_MFC_NFW       = 5,
+       FC_MFC_EXYNOS_ID_VIDEO_EXT     = 6,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC device node.
+ */
+enum s5p_mfc_node_type {
+       MFCNODE_INVALID = -1,
+       MFCNODE_DECODER = 0,
+       MFCNODE_ENCODER = 1,
+       MFCNODE_DECODER_DRM = 2,
+       MFCNODE_ENCODER_DRM = 3,
+       MFCNODE_ENCODER_OTF = 4,
+       MFCNODE_ENCODER_OTF_DRM = 5,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC instance.
+ */
+enum s5p_mfc_inst_type {
+       MFCINST_INVALID = 0,
+       MFCINST_DECODER = 1,
+       MFCINST_ENCODER = 2,
+};
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+       MFCINST_FREE = 0,
+       MFCINST_INIT = 100,
+       MFCINST_GOT_INST,
+       MFCINST_HEAD_PARSED,
+       MFCINST_RUNNING_BUF_FULL,
+       MFCINST_RUNNING,
+       MFCINST_FINISHING,
+       MFCINST_RETURN_INST,
+       MFCINST_ERROR,
+       MFCINST_ABORT,
+       MFCINST_RES_CHANGE_INIT,
+       MFCINST_RES_CHANGE_FLUSH,
+       MFCINST_RES_CHANGE_END,
+       MFCINST_RUNNING_NO_OUTPUT,
+       MFCINST_ABORT_INST,
+       MFCINST_DPB_FLUSHING,
+       MFCINST_SPECIAL_PARSING,
+       MFCINST_SPECIAL_PARSING_NAL,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+       QUEUE_FREE = 0,
+       QUEUE_BUFS_REQUESTED,
+       QUEUE_BUFS_QUERIED,
+       QUEUE_BUFS_MMAPED,
+};
+
+enum mfc_dec_wait_state {
+       WAIT_NONE = 0,
+       WAIT_DECODING,
+       WAIT_INITBUF_DONE,
+};
+
+/**
+ * enum s5p_mfc_check_state - The state for user notification
+ */
+enum s5p_mfc_check_state {
+       MFCSTATE_PROCESSING = 0,
+       MFCSTATE_DEC_RES_DETECT,
+       MFCSTATE_DEC_TERMINATING,
+       MFCSTATE_ENC_NO_OUTPUT,
+       MFCSTATE_DEC_S3D_REALLOC,
+};
+
+enum mfc_buf_usage_type {
+       MFCBUF_INVALID = 0,
+       MFCBUF_NORMAL,
+       MFCBUF_DRM,
+       MFCBUF_NORMAL_FW,
+       MFCBUF_DRM_FW,
+};
+
+enum mfc_buf_process_type {
+       MFCBUFPROC_DEFAULT              = 0x0,
+       MFCBUFPROC_COPY                 = (1 << 0),
+       MFCBUFPROC_SHARE                = (1 << 1),
+       MFCBUFPROC_META                 = (1 << 2),
+       MFCBUFPROC_ANBSHARE             = (1 << 3),
+       MFCBUFPROC_ANBSHARE_NV12L       = (1 << 4),
+};
+
+enum s5p_mfc_ctrl_type {
+       MFC_CTRL_TYPE_GET_SRC   = 0x1,
+       MFC_CTRL_TYPE_GET_DST   = 0x2,
+       MFC_CTRL_TYPE_SET       = 0x4,
+};
+
+enum s5p_mfc_ctrl_mode {
+       MFC_CTRL_MODE_NONE      = 0x0,
+       MFC_CTRL_MODE_SFR       = 0x1,
+       MFC_CTRL_MODE_CST       = 0x2,
+};
+
+struct s5p_mfc_ctx;
+
+enum s5p_mfc_debug_cause {
+       MFC_CAUSE_0WRITE_PAGE_FAULT             = 0,
+       MFC_CAUSE_0READ_PAGE_FAULT              = 1,
+       MFC_CAUSE_1WRITE_PAGE_FAULT             = 2,
+       MFC_CAUSE_1READ_PAGE_FAULT              = 3,
+       MFC_CAUSE_NO_INTERRUPT                  = 4,
+       MFC_CAUSE_NO_SCHEDULING                 = 5,
+       MFC_CAUSE_FAIL_STOP_NAL_Q               = 6,
+       MFC_CAUSE_FAIL_STOP_NAL_Q_FOR_OTHER     = 7,
+       MFC_CAUSE_FAIL_CLOSE_INST               = 8,
+       MFC_CAUSE_FAIL_SLEEP                    = 9,
+       MFC_CAUSE_FAIL_WAKEUP                   = 10,
+       MFC_CAUSE_FAIL_RISC_ON                  = 11,
+       MFC_CAUSE_FAIL_DPB_FLUSH                = 12,
+       MFC_CAUSE_FAIL_CHACHE_FLUSH             = 13,
+};
+
+struct s5p_mfc_debug {
+       u32     cause;
+       u8      fault_status;
+       u32     fault_trans_info;
+       u32     fault_addr;
+       u8      SFRs_set1[MFC_SFR_LOGGING_COUNT_SET1];
+       u32     SFRs_set2[MFC_SFR_LOGGING_COUNT_SET2];
+       char    errorinfo[MFC_LOGGING_DATA_SIZE];
+};
+
+/**
+ * struct s5p_mfc_buf - MFC buffer
+ *
+ */
+struct s5p_mfc_buf {
+       struct vb2_v4l2_buffer vb;
+       struct list_head list;
+       union {
+               dma_addr_t raw[3];
+               dma_addr_t stream;
+       } planes;
+       int used;
+       unsigned char *vir_addr;
+};
+
+struct s5p_mfc_buf_queue {
+       struct list_head head;
+       unsigned int count;
+};
+
+struct s5p_mfc_bits {
+       unsigned long bits;
+       spinlock_t lock;
+};
+
+struct s5p_mfc_hwlock {
+       struct list_head waiting_list;
+       unsigned int wl_count;
+       unsigned long bits;
+       unsigned long dev;
+       unsigned int owned_by_irq;
+       unsigned int transfer_owner;
+       spinlock_t lock;
+};
+
+struct s5p_mfc_listable_wq {
+       struct list_head list;
+       wait_queue_head_t wait_queue;
+       struct mutex wait_mutex;
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_ctx *ctx;
+};
+
+struct s5p_mfc_pm {
+       struct clk      *clock;
+       atomic_t        pwr_ref;
+       struct device   *device;
+       spinlock_t      clklock;
+
+       int clock_on_steps;
+       int clock_off_steps;
+       enum mfc_buf_usage_type base_type;
+};
+
+struct s5p_mfc_fw {
+       int             date;
+       int             fimv_info;
+       size_t          size;
+       int             status;
+       int             drm_status;
+};
+
+struct s5p_mfc_buf_align {
+       unsigned int mfc_base_align;
+};
+
+struct s5p_mfc_buf_size_v6 {
+       size_t dev_ctx;
+       size_t h264_dec_ctx;
+       size_t other_dec_ctx;
+       size_t h264_enc_ctx;
+       size_t hevc_enc_ctx;
+       size_t other_enc_ctx;
+       size_t shared_buf;
+       size_t dbg_info_buf;
+};
+
+struct s5p_mfc_buf_size {
+       size_t firmware_code;
+       unsigned int cpb_buf;
+       void *buf;
+};
+
+struct s5p_mfc_variant {
+       struct s5p_mfc_buf_size *buf_size;
+       struct s5p_mfc_buf_align *buf_align;
+       int     num_entities;
+};
+
+struct s5p_mfc_debugfs {
+       struct dentry *root;
+       struct dentry *mfc_info;
+       struct dentry *debug_info;
+       struct dentry *debug_level;
+       struct dentry *debug_ts;
+       struct dentry *dbg_enable;
+       struct dentry *nal_q_dump;
+       struct dentry *nal_q_disable;
+       struct dentry *nal_q_parallel_disable;
+       struct dentry *otf_dump;
+};
+
+/**
+ * struct s5p_mfc_special_buf - represents internal used buffer
+ * @daddr:             device virtual address
+ * @virt:              kernel virtual address, only valid when the
+ *                     buffer accessed by driver
+ */
+struct s5p_mfc_special_buf {
+       enum mfc_buf_usage_type         buftype;
+       struct ion_handle               *handle;
+       struct dma_buf                  *dma_buf;
+       struct dma_buf_attachment       *attachment;
+       struct sg_table                 *sgt;
+       dma_addr_t                      daddr;
+       void                            *vaddr;
+       size_t                          size;
+};
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+struct mfc_qos_bw_data {
+       unsigned long   peak;
+       unsigned long   read;
+       unsigned long   write;
+};
+
+struct s5p_mfc_qos_bw {
+       struct mfc_qos_bw_data h264_dec_uhd_bw;
+       struct mfc_qos_bw_data hevc_dec_uhd_bw;
+       struct mfc_qos_bw_data hevc_dec_uhd_10bit_bw;
+       struct mfc_qos_bw_data vp8_dec_uhd_bw;
+       struct mfc_qos_bw_data vp9_dec_uhd_bw;
+       struct mfc_qos_bw_data mpeg4_dec_uhd_bw;
+       struct mfc_qos_bw_data h264_enc_uhd_bw;
+       struct mfc_qos_bw_data hevc_enc_uhd_bw;
+       struct mfc_qos_bw_data hevc_enc_uhd_10bit_bw;
+       struct mfc_qos_bw_data vp8_enc_uhd_bw;
+       struct mfc_qos_bw_data vp9_enc_uhd_bw;
+       struct mfc_qos_bw_data mpeg4_enc_uhd_bw;
+};
+
+/*
+ * threshold_mb - threshold of total MB(macroblock) count
+ * Total MB count can be calculated by
+ *     (MB of width) * (MB of height) * fps
+ */
+struct s5p_mfc_qos {
+       unsigned int threshold_mb;
+       unsigned int freq_mfc;
+       unsigned int freq_int;
+       unsigned int freq_mif;
+       unsigned int freq_cpu;
+       unsigned int freq_kfc;
+       unsigned int mo_value;
+       unsigned int mo_10bit_value;
+       unsigned int mo_uhd_enc60_value;
+       unsigned int time_fw;
+};
+#endif
+
+struct s5p_mfc_platdata {
+       enum mfc_ip_version ip_ver;
+       int clock_rate;
+       int min_rate;
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       int num_qos_steps;
+       int max_qos_steps;
+       int max_mb;
+       struct s5p_mfc_qos *qos_table;
+#endif
+};
+
+/************************ NAL_Q data structure ************************/
+#define NAL_Q_ENABLE 1
+
+#define NAL_Q_IN_ENTRY_SIZE            256
+#define NAL_Q_OUT_ENTRY_SIZE           256
+
+#define NAL_Q_IN_DEC_STR_SIZE          112
+#define NAL_Q_IN_ENC_STR_SIZE          204
+#define NAL_Q_OUT_DEC_STR_SIZE         248
+#define NAL_Q_OUT_ENC_STR_SIZE         64
+
+#define NAL_Q_IN_QUEUE_SIZE            16 /* 256*16 = 4096 bytes */
+#define NAL_Q_OUT_QUEUE_SIZE           16 /* 256*16 = 4096 bytes */
+
+typedef struct __DecoderInputStr {
+       int StartCode; /* = 0xAAAAAAAA; Decoder input structure marker */
+       int CommandId;
+       int InstanceId;
+       int PictureTag;
+       unsigned int CpbBufferAddr;
+       int CpbBufferSize;
+       int CpbBufferOffset;
+       int StreamDataSize;
+       int AvailableDpbFlagUpper;
+       int AvailableDpbFlagLower;
+       int DynamicDpbFlagUpper;
+       int DynamicDpbFlagLower;
+       unsigned int FrameAddr[3];
+       int FrameSize[3];
+       int NalStartOptions;
+       int FrameStrideSize[3];
+       int Frame2BitSize[2];
+       int Frame2BitStrideSize[2];
+       unsigned int ScratchBufAddr;
+       int ScratchBufSize;
+       char reserved[NAL_Q_IN_ENTRY_SIZE - NAL_Q_IN_DEC_STR_SIZE];
+} DecoderInputStr; /* 28*4 = 112 bytes */
+
+typedef struct __EncoderInputStr {
+       int StartCode; /* 0xBBBBBBBB; Encoder input structure marker */
+       int CommandId;
+       int InstanceId;
+       int PictureTag;
+       unsigned int FrameAddr[3];
+       unsigned int StreamBufferAddr;
+       int StreamBufferSize;
+       int StreamBufferOffset;
+       int RcRoiCtrl;
+       unsigned int RoiBufferAddr;
+       int ParamChange;
+       int IrSize;
+       int GopConfig;
+       int RcFrameRate;
+       int RcBitRate;
+       int MsliceMode;
+       int MsliceSizeMb;
+       int MsliceSizeBits;
+       int FrameInsertion;
+       int HierarchicalBitRateLayer[7];
+       int H264RefreshPeriod;
+       int HevcRefreshPeriod;
+       int RcQpBound;
+       int RcQpBoundPb;
+       int FixedPictureQp;
+       int PictureProfile;
+       int BitCountEnable;
+       int MaxBitCount;
+       int MinBitCount;
+       int NumTLayer;
+       int H264NalControl;
+       int HevcNalControl;
+       int Vp8NalControl;
+       int Vp9NalControl;
+       int H264HDSvcExtension0;
+       int H264HDSvcExtension1;
+       int GopConfig2;
+       int Frame2bitAddr[2];
+       int Weight;
+       int ExtCtbQpAddr;
+       int WeightUpper;
+       int RcMode;
+       char reserved[NAL_Q_IN_ENTRY_SIZE - NAL_Q_IN_ENC_STR_SIZE];
+} EncoderInputStr; /* 51*4 = 204 bytes */
+
+typedef struct __DecoderOutputStr {
+       int StartCode; /* 0xAAAAAAAA; Decoder output structure marker */
+       int CommandId;
+       int InstanceId;
+       int ErrorCode;
+       int PictureTagTop;
+       int PictureTimeTop;
+       int DisplayFrameWidth;
+       int DisplayFrameHeight;
+       int DisplayStatus;
+       unsigned int DisplayAddr[3];
+       int DisplayFrameType;
+       int DisplayCropInfo1;
+       int DisplayCropInfo2;
+       int DisplayPictureProfile;
+       int DisplayAspectRatio;
+       int DisplayExtendedAr;
+       int DecodedNalSize;
+       int UsedDpbFlagUpper;
+       int UsedDpbFlagLower;
+       int SeiAvail;
+       int FramePackArrgmentId;
+       int FramePackSeiInfo;
+       int FramePackGridPos;
+       int DisplayRecoverySeiInfo;
+       int H264Info;
+       int DisplayFirstCrc;
+       int DisplaySecondCrc;
+       int DisplayThirdCrc;
+       int DisplayFirst2BitCrc;
+       int DisplaySecond2BitCrc;
+       int DecodedFrameWidth;
+       int DecodedFrameHeight;
+       int DecodedStatus;
+       unsigned int DecodedAddr[3];
+       int DecodedFrameType;
+       int DecodedCropInfo1;
+       int DecodedCropInfo2;
+       int DecodedPictureProfile;
+       int DecodedRecoverySeiInfo;
+       int DecodedFirstCrc;
+       int DecodedSecondCrc;
+       int DecodedThirdCrc;
+       int DecodedFirst2BitCrc;
+       int DecodedSecond2BitCrc;
+       int PictureTagBot;
+       int PictureTimeBot;
+       int ChromaFormat;
+       int Mpeg4Info;
+       int HevcInfo;
+       int Vc1Info;
+       int VideoSignalType;
+       int ContentLightLevelInfoSei;
+       int MasteringDisplayColourVolumeSei0;
+       int MasteringDisplayColourVolumeSei1;
+       int MasteringDisplayColourVolumeSei2;
+       int MasteringDisplayColourVolumeSei3;
+       int MasteringDisplayColourVolumeSei4;
+       int MasteringDisplayColourVolumeSei5;
+       char reserved[NAL_Q_OUT_ENTRY_SIZE - NAL_Q_OUT_DEC_STR_SIZE];
+} DecoderOutputStr; /* 62*4 =  248 bytes */
+
+typedef struct __EncoderOutputStr {
+       int StartCode; /* 0xBBBBBBBB; Encoder output structure marker */
+       int CommandId;
+       int InstanceId;
+       int ErrorCode;
+       int PictureTag;
+       unsigned int EncodedFrameAddr[3];
+       unsigned int StreamBufferAddr;
+       int StreamBufferOffset;
+       int StreamSize;
+       int SliceType;
+       int NalDoneInfo;
+       unsigned int ReconLumaDpbAddr;
+       unsigned int ReconChromaDpbAddr;
+       int EncCnt;
+       char reserved[NAL_Q_OUT_ENTRY_SIZE - NAL_Q_OUT_ENC_STR_SIZE];
+} EncoderOutputStr; /* 16*4 = 64 bytes */
+
+/**
+ * enum nal_queue_state - The state for nal queue operation.
+ */
+typedef enum _nal_queue_state {
+       NAL_Q_STATE_CREATED = 0,
+       NAL_Q_STATE_INITIALIZED,
+       NAL_Q_STATE_STARTED, /* when s5p_mfc_nal_q_start() is called */
+       NAL_Q_STATE_STOPPED, /* when s5p_mfc_nal_q_stop() is called */
+} nal_queue_state;
+
+typedef struct _nal_in_queue {
+       union {
+               DecoderInputStr dec;
+               EncoderInputStr enc;
+       } entry[NAL_Q_IN_QUEUE_SIZE];
+} nal_in_queue;
+
+typedef struct _nal_out_queue {
+       union {
+               DecoderOutputStr dec;
+               EncoderOutputStr enc;
+       } entry[NAL_Q_OUT_QUEUE_SIZE];
+} nal_out_queue;
+
+struct _nal_queue_handle;
+typedef struct _nal_queue_in_handle {
+       struct _nal_queue_handle *nal_q_handle;
+       struct s5p_mfc_special_buf in_buf;
+       unsigned int in_exe_count;
+       nal_in_queue *nal_q_in_addr;
+       spinlock_t lock;
+} nal_queue_in_handle;
+
+typedef struct _nal_queue_out_handle {
+       struct _nal_queue_handle *nal_q_handle;
+       struct s5p_mfc_special_buf out_buf;
+       unsigned int out_exe_count;
+       nal_out_queue *nal_q_out_addr;
+       int nal_q_ctx;
+} nal_queue_out_handle;
+
+typedef struct _nal_queue_handle {
+       nal_queue_in_handle *nal_q_in_handle;
+       nal_queue_out_handle *nal_q_out_handle;
+       nal_queue_state nal_q_state;
+       int nal_q_exception;
+} nal_queue_handle;
+
+/************************ OTF data structure ************************/
+struct _otf_buf_addr {
+       dma_addr_t otf_daddr[HWFC_MAX_BUF][3];
+       struct dma_buf_attachment *otf_buf_attach[HWFC_MAX_BUF];
+};
+
+struct _otf_buf_info {
+       int pixel_format;
+       int width;
+       int height;
+       int buffer_count;
+       struct dma_buf *bufs[HWFC_MAX_BUF];
+};
+
+struct _otf_debug {
+       struct s5p_mfc_special_buf stream_buf[OTF_MAX_BUF];
+       unsigned int stream_size[OTF_MAX_BUF];
+       unsigned char frame_cnt;
+};
+
+struct _otf_handle {
+       int otf_work_bit;
+       int otf_buf_index;
+       int otf_job_id;
+       u64 otf_time_stamp;
+       struct _otf_buf_addr otf_buf_addr;
+       struct _otf_buf_info otf_buf_info;
+       struct _otf_debug otf_debug;
+};
+/********************************************************************/
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ */
+struct s5p_mfc_dev {
+       struct v4l2_device      v4l2_dev;
+       struct video_device     *vfd_dec;
+       struct video_device     *vfd_enc;
+       struct video_device     *vfd_dec_drm;
+       struct video_device     *vfd_enc_drm;
+       struct video_device     *vfd_enc_otf;
+       struct video_device     *vfd_enc_otf_drm;
+       struct device           *device;
+#ifdef CONFIG_ION_EXYNOS
+       struct ion_client       *mfc_ion_client;
+#endif
+
+       void __iomem            *regs_base;
+       void __iomem            *sysmmu0_base;
+       void __iomem            *sysmmu1_base;
+       void __iomem            *hwfc_base;
+
+       int                     irq;
+       struct resource         *mfc_mem;
+
+       struct s5p_mfc_pm       pm;
+       struct s5p_mfc_fw       fw;
+       struct s5p_mfc_variant  *variant;
+       struct s5p_mfc_platdata *pdata;
+       struct s5p_mfc_debug    *logging_data;
+
+       int num_inst;
+
+       struct mutex mfc_mutex;
+
+       int int_condition;
+       int int_reason;
+       unsigned int int_err;
+
+       wait_queue_head_t cmd_wq;
+       struct s5p_mfc_listable_wq hwlock_wq;
+
+       /*
+       struct clk *clock1;
+       struct clk *clock2;
+       */
+
+       struct s5p_mfc_special_buf common_ctx_buf;
+       struct s5p_mfc_special_buf drm_common_ctx_buf;
+
+       struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+       int curr_ctx;
+       int preempt_ctx;
+
+       struct s5p_mfc_bits work_bits;
+
+       struct s5p_mfc_hwlock hwlock;
+
+       atomic_t watchdog_tick_running;
+       atomic_t watchdog_tick_cnt;
+       atomic_t watchdog_run;
+       struct timer_list watchdog_timer;
+       struct workqueue_struct *watchdog_wq;
+       struct work_struct watchdog_work;
+
+       /* for DRM */
+       int curr_ctx_is_drm;
+       int num_drm_inst;
+       struct s5p_mfc_special_buf fw_buf;
+       struct s5p_mfc_special_buf drm_fw_buf;
+
+       struct workqueue_struct *butler_wq;
+       struct work_struct butler_work;
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       struct list_head qos_queue;
+       atomic_t qos_req_cur;
+       struct pm_qos_request qos_req_int;
+       struct pm_qos_request qos_req_mif;
+#ifdef CONFIG_ARM_EXYNOS_MP_CPUFREQ
+       struct pm_qos_request qos_req_cluster1;
+       struct pm_qos_request qos_req_cluster0;
+#endif
+       int qos_has_enc_ctx;
+#endif
+       int id;
+       atomic_t clk_ref;
+
+       atomic_t trace_ref;
+       struct _mfc_trace *mfc_trace;
+       atomic_t trace_ref_hwlock;
+       struct _mfc_trace *mfc_trace_hwlock;
+       bool continue_clock_on;
+
+       bool shutdown;
+       bool sleep;
+
+       nal_queue_handle *nal_q_handle;
+
+       struct s5p_mfc_special_buf dbg_info_buf;
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       struct bts_bw mfc_bw;
+#endif
+
+       struct s5p_mfc_debugfs debugfs;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_h264_enc_params {
+       enum v4l2_mpeg_video_h264_profile profile;
+       u8 level;
+       u8 interlace;
+       enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
+       s8 loop_filter_alpha;
+       s8 loop_filter_beta;
+       enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+       u8 _8x8_transform;
+       u32 rc_framerate;
+       u8 rc_frame_qp;
+       u8 rc_min_qp;
+       u8 rc_max_qp;
+       u8 rc_min_qp_p;
+       u8 rc_max_qp_p;
+       u8 rc_min_qp_b;
+       u8 rc_max_qp_b;
+       u8 rc_mb_dark;
+       u8 rc_mb_smooth;
+       u8 rc_mb_static;
+       u8 rc_mb_activity;
+       u8 rc_p_frame_qp;
+       u8 rc_b_frame_qp;
+       u8 ar_vui;
+       enum v4l2_mpeg_video_h264_vui_sar_idc ar_vui_idc;
+       u16 ext_sar_width;
+       u16 ext_sar_height;
+       u8 open_gop;
+       u16 open_gop_size;
+       u8 hier_qp_enable;
+       enum v4l2_mpeg_video_h264_hierarchical_coding_type hier_qp_type;
+       u8 num_hier_layer;
+       u8 hier_ref_type;
+       u8 hier_qp_layer[7];
+       u32 hier_bit_layer[7];
+       u8 sei_gen_enable;
+       u8 sei_fp_curr_frame_0;
+       enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_arrangement_type;
+       u32 fmo_enable;
+       u32 fmo_slice_map_type;
+       u32 fmo_slice_num_grp;
+       u32 fmo_run_length[4];
+       u32 fmo_sg_dir;
+       u32 fmo_sg_rate;
+       u32 aso_enable;
+       u32 aso_slice_order[8];
+
+       u32 prepend_sps_pps_to_idr;
+       u8 enable_ltr;
+       u8 num_of_ltr;
+       u32 set_priority;
+       u32 base_priority;
+       u32 vui_enable;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_mpeg4_enc_params {
+       /* MPEG4 Only */
+       enum v4l2_mpeg_video_mpeg4_profile profile;
+       u8 level;
+       u8 quarter_pixel;
+       u16 vop_time_res;
+       u16 vop_frm_delta;
+       u8 rc_b_frame_qp;
+       /* Common for MPEG4, H263 */
+       u32 rc_framerate;
+       u8 rc_frame_qp;
+       u8 rc_min_qp;
+       u8 rc_max_qp;
+       u8 rc_min_qp_p;
+       u8 rc_max_qp_p;
+       u8 rc_min_qp_b;
+       u8 rc_max_qp_b;
+       u8 rc_p_frame_qp;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_vp9_enc_params {
+       /* VP9 Only */
+       u32 rc_framerate;
+       u8 vp9_version;
+       u8 rc_min_qp;
+       u8 rc_max_qp;
+       u8 rc_min_qp_p;
+       u8 rc_max_qp_p;
+       u8 rc_frame_qp;
+       u8 rc_p_frame_qp;
+       u8 vp9_goldenframesel;
+       u16 vp9_gfrefreshperiod;
+       u8 hier_qp_enable;
+       u8 hier_qp_layer[3];
+       u32 hier_bit_layer[3];
+       u8 num_hier_layer;
+       u8 max_partition_depth;
+       u8 intra_pu_split_disable;
+       u8 profile;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_vp8_enc_params {
+       /* VP8 Only */
+       u32 rc_framerate;
+       u8 vp8_version;
+       u8 rc_min_qp;
+       u8 rc_max_qp;
+       u8 rc_min_qp_p;
+       u8 rc_max_qp_p;
+       u8 rc_frame_qp;
+       u8 rc_p_frame_qp;
+       u8 vp8_numberofpartitions;
+       u8 vp8_filterlevel;
+       u8 vp8_filtersharpness;
+       u8 vp8_goldenframesel;
+       u16 vp8_gfrefreshperiod;
+       u8 hier_qp_enable;
+       u8 hier_qp_layer[3];
+       u32 hier_bit_layer[3];
+       u8 intra_4x4mode_disable;
+       u8 num_hier_layer;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_hevc_enc_params {
+       u8 profile;
+       u8 level;
+       u8 tier_flag;
+       /* HEVC Only */
+       u32 rc_framerate;
+       u8 rc_min_qp;
+       u8 rc_max_qp;
+       u8 rc_min_qp_p;
+       u8 rc_max_qp_p;
+       u8 rc_min_qp_b;
+       u8 rc_max_qp_b;
+       u8 rc_lcu_dark;
+       u8 rc_lcu_smooth;
+       u8 rc_lcu_static;
+       u8 rc_lcu_activity;
+       u8 rc_frame_qp;
+       u8 rc_p_frame_qp;
+       u8 rc_b_frame_qp;
+       u8 max_partition_depth;
+       u8 refreshtype;
+       u16 refreshperiod;
+       s32 lf_beta_offset_div2;
+       s32 lf_tc_offset_div2;
+       u8 loopfilter_disable;
+       u8 loopfilter_across;
+       u8 nal_control_length_filed;
+       u8 nal_control_user_ref;
+       u8 nal_control_store_ref;
+       u8 const_intra_period_enable;
+       u8 lossless_cu_enable;
+       u8 wavefront_enable;
+       u8 enable_ltr;
+       u8 hier_qp_enable;
+       enum v4l2_mpeg_video_hevc_hierarchical_coding_type hier_qp_type;
+       u8 hier_ref_type;
+       u8 num_hier_layer;
+       u8 hier_qp_layer[7];
+       u32 hier_bit_layer[7];
+       u8 general_pb_enable;
+       u8 temporal_id_enable;
+       u8 strong_intra_smooth;
+       u8 intra_pu_split_disable;
+       u8 tmv_prediction_disable;
+       u8 max_num_merge_mv;
+       u8 eco_mode_enable;
+       u8 encoding_nostartcode_enable;
+       u8 size_of_length_field;
+       u8 user_ref;
+       u8 store_ref;
+       u8 prepend_sps_pps_to_idr;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_bpg_enc_params {
+       u32 thumb_size;
+       u32 exif_size;
+};
+
+/**
+ *
+ */
+struct s5p_mfc_enc_params {
+       u16 width;
+       u16 height;
+
+       u32 gop_size;
+       enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+       u32 slice_mb;
+       u32 slice_bit;
+       u32 slice_mb_row;
+       u32 intra_refresh_mb;
+       u8 pad;
+       u8 pad_luma;
+       u8 pad_cb;
+       u8 pad_cr;
+       u8 rc_frame;
+       u32 rc_bitrate;
+       u16 rc_reaction_coeff;
+       u32 config_qp;
+       u32 dynamic_qp;
+       u8 frame_tag;
+       u8 ratio_intra;
+
+       u8 num_b_frame;         /* H.264, HEVC, MPEG4 */
+       u8 num_refs_for_p;      /* H.264, HEVC, VP8, VP9 */
+       u8 rc_mb;               /* H.264: MFCv5, MPEG4/H.263: MFCv6 */
+       u8 rc_pvc;
+       u16 vbv_buf_size;
+       enum v4l2_mpeg_video_header_mode seq_hdr_mode;
+       enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
+       u8 fixed_target_bit;
+       u8 num_hier_max_layer;
+       u8 weighted_enable;
+       u8 roi_enable;
+       u8 ivf_header_disable;  /* VP8, VP9 */
+
+       u16 rc_frame_delta;     /* MFC6.1 Only */
+
+       u32 i_frm_ctrl_mode;
+       u32 i_frm_ctrl;
+
+       union {
+               struct s5p_mfc_h264_enc_params h264;
+               struct s5p_mfc_mpeg4_enc_params mpeg4;
+               struct s5p_mfc_vp8_enc_params vp8;
+               struct s5p_mfc_vp9_enc_params vp9;
+               struct s5p_mfc_hevc_enc_params hevc;
+               struct s5p_mfc_bpg_enc_params bpg;
+       } codec;
+};
+
+struct s5p_mfc_ctx_ctrl {
+       struct list_head list;
+       enum s5p_mfc_ctrl_type type;
+       unsigned int id;
+       unsigned int addr;
+       int has_new;
+       int val;
+};
+
+struct s5p_mfc_buf_ctrl {
+       struct list_head list;
+       unsigned int id;
+       enum s5p_mfc_ctrl_type type;
+       int has_new;
+       int val;
+       unsigned int old_val;           /* only for MFC_CTRL_TYPE_SET */
+       unsigned int old_val2;          /* only for MFC_CTRL_TYPE_SET */
+       unsigned int is_volatile;       /* only for MFC_CTRL_TYPE_SET */
+       unsigned int updated;
+       unsigned int mode;
+       unsigned int addr;
+       unsigned int mask;
+       unsigned int shft;
+       unsigned int flag_mode;         /* only for MFC_CTRL_TYPE_SET */
+       unsigned int flag_addr;         /* only for MFC_CTRL_TYPE_SET */
+       unsigned int flag_shft;         /* only for MFC_CTRL_TYPE_SET */
+       int (*read_cst) (struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl);
+       void (*write_cst) (struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl);
+};
+
+struct s5p_mfc_ctrl_cfg {
+       enum s5p_mfc_ctrl_type type;
+       unsigned int id;
+       unsigned int is_volatile;       /* only for MFC_CTRL_TYPE_SET */
+       unsigned int mode;
+       unsigned int addr;
+       unsigned int mask;
+       unsigned int shft;
+       unsigned int flag_mode;         /* only for MFC_CTRL_TYPE_SET */
+       unsigned int flag_addr;         /* only for MFC_CTRL_TYPE_SET */
+       unsigned int flag_shft;         /* only for MFC_CTRL_TYPE_SET */
+       int (*read_cst) (struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl);
+       void (*write_cst) (struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl);
+};
+
+/* per buffer contol */
+struct s5p_mfc_ctrls_ops {
+       /* controls per buffer */
+       int (*init_ctx_ctrls) (struct s5p_mfc_ctx *ctx);
+       int (*cleanup_ctx_ctrls) (struct s5p_mfc_ctx *ctx);
+       int (*init_buf_ctrls) (struct s5p_mfc_ctx *ctx,
+                       enum s5p_mfc_ctrl_type type, unsigned int index);
+       void (*reset_buf_ctrls) (struct list_head *head);
+       int (*cleanup_buf_ctrls) (struct s5p_mfc_ctx *ctx,
+                       enum s5p_mfc_ctrl_type type, unsigned int index);
+       int (*to_buf_ctrls) (struct s5p_mfc_ctx *ctx, struct list_head *head);
+       int (*to_ctx_ctrls) (struct s5p_mfc_ctx *ctx, struct list_head *head);
+       int (*set_buf_ctrls_val) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head);
+       int (*get_buf_ctrls_val) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head);
+       int (*recover_buf_ctrls_val) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head);
+       int (*get_buf_update_val) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, unsigned int id, int value);
+       int (*set_buf_ctrls_val_nal_q_dec) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, DecoderInputStr *pInStr);
+       int (*get_buf_ctrls_val_nal_q_dec) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, DecoderOutputStr *pOutStr);
+       int (*set_buf_ctrls_val_nal_q_enc) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, EncoderInputStr *pInStr);
+       int (*get_buf_ctrls_val_nal_q_enc) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, EncoderOutputStr *pOutStr);
+       int (*recover_buf_ctrls_nal_q) (struct s5p_mfc_ctx *ctx,
+                       struct list_head *head);
+};
+
+struct stored_dpb_info {
+       int fd[MFC_MAX_PLANES];
+};
+
+struct dec_dpb_ref_info {
+       int index;
+       struct stored_dpb_info dpb[MFC_MAX_DPBS];
+};
+
+struct temporal_layer_info {
+       unsigned int temporal_layer_count;
+       unsigned int temporal_layer_bitrate[VIDEO_MAX_TEMPORAL_LAYERS];
+};
+
+struct mfc_enc_roi_info {
+       char *addr;
+       int size;
+       int upper_qp;
+       int lower_qp;
+       bool enable;
+};
+
+struct mfc_user_shared_handle {
+       int fd;
+       struct ion_handle *ion_handle;
+       void *vaddr;
+};
+
+struct s5p_mfc_raw_info {
+       int num_planes;
+       int stride[3];
+       int plane_size[3];
+       int stride_2bits[3];
+       int plane_size_2bits[3];
+       unsigned int total_plane_size;
+};
+
+struct mfc_timestamp {
+       struct list_head list;
+       struct timeval timestamp;
+       int index;
+       int interval;
+};
+
+struct s5p_mfc_dec {
+       int total_dpb_count;
+
+       unsigned int src_buf_size;
+
+       int loop_filter_mpeg4;
+       int display_delay;
+       int immediate_display;
+       int slice_enable;
+       int mv_count;
+       int idr_decoding;
+       int is_interlaced;
+       int is_dts_mode;
+
+       int crc_enable;
+       int crc_luma0;
+       int crc_chroma0;
+       int crc_luma1;
+       int crc_chroma1;
+
+       unsigned long consumed;
+       unsigned long remained_size;
+
+       enum v4l2_memory dst_memtype;
+       int sei_parse;
+       int stored_tag;
+       dma_addr_t y_addr_for_pb;
+
+       int cr_left, cr_right, cr_top, cr_bot;
+
+       int detect_black_bar;
+       bool black_bar_updated;
+       struct v4l2_rect black_bar;
+
+       /* For dynamic DPB */
+       int is_dynamic_dpb;
+       unsigned long available_dpb;
+       unsigned int dynamic_set;
+       unsigned int dynamic_used;
+
+       struct dec_dpb_ref_info *ref_info;
+       int assigned_fd[MFC_MAX_DPBS];
+       struct mfc_user_shared_handle sh_handle;
+       struct s5p_mfc_buf *assigned_dpb[MFC_MAX_DPBS];
+
+       int has_multiframe;
+       int is_dpb_full;
+
+       unsigned int err_reuse_flag;
+
+       /* for debugging about black bar detection */
+       void *frame_vaddr[3][30];
+       dma_addr_t frame_daddr[3][30];
+       int index[3][30];
+       int fd[3][30];
+       unsigned int frame_size[3][30];
+       unsigned char frame_cnt;
+
+       unsigned int num_of_tile_over_4;
+};
+
+struct s5p_mfc_enc {
+       struct s5p_mfc_enc_params params;
+
+       unsigned int dst_buf_size;
+       unsigned int header_size;
+
+       enum v4l2_mpeg_mfc51_video_frame_type frame_type;
+       enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
+
+       size_t luma_dpb_size;
+       size_t chroma_dpb_size;
+       size_t me_buffer_size;
+       size_t tmv_buffer_size;
+
+       unsigned int slice_mode;
+       union {
+               unsigned int mb;
+               unsigned int bits;
+       } slice_size;
+       unsigned int in_slice;
+       unsigned int buf_full;
+
+       int stored_tag;
+       struct mfc_user_shared_handle sh_handle_svc;
+       struct mfc_user_shared_handle sh_handle_roi;
+       int roi_index;
+       struct s5p_mfc_special_buf roi_buf[MFC_MAX_EXTRA_BUF];
+       struct mfc_enc_roi_info roi_info[MFC_MAX_EXTRA_BUF];
+};
+
+struct s5p_mfc_fmt {
+       char *name;
+       u32 fourcc;
+       u32 codec_mode;
+       u32 type;
+       u32 num_planes;
+       u32 mem_planes;
+};
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ */
+struct s5p_mfc_ctx {
+       struct s5p_mfc_dev *dev;
+       struct v4l2_fh fh;
+       int num;
+
+       int int_condition;
+       int int_reason;
+       unsigned int int_err;
+
+       wait_queue_head_t cmd_wq;
+       struct s5p_mfc_listable_wq hwlock_wq;
+
+       struct s5p_mfc_fmt *src_fmt;
+       struct s5p_mfc_fmt *dst_fmt;
+
+       struct vb2_queue vq_src;
+       struct vb2_queue vq_dst;
+
+       struct s5p_mfc_buf_queue src_buf_queue;
+       struct s5p_mfc_buf_queue dst_buf_queue;
+       struct s5p_mfc_buf_queue src_buf_nal_queue;
+       struct s5p_mfc_buf_queue dst_buf_nal_queue;
+       struct s5p_mfc_buf_queue ref_buf_queue;
+       spinlock_t buf_queue_lock;
+
+       enum s5p_mfc_inst_type type;
+       enum s5p_mfc_inst_state state;
+       int inst_no;
+
+       int img_width;
+       int img_height;
+       int dpb_count;
+       int buf_stride;
+
+       int old_img_width;
+       int old_img_height;
+       int min_dpb_size[3];
+
+       unsigned int enc_drc_flag;
+       int enc_res_change;
+       int enc_res_change_state;
+       int enc_res_change_re_input;
+       size_t min_scratch_buf_size;
+
+       struct s5p_mfc_raw_info raw_buf;
+       size_t mv_size;
+
+       struct s5p_mfc_special_buf codec_buf;
+       int codec_buffer_allocated;
+
+       enum s5p_mfc_queue_state capture_state;
+       enum s5p_mfc_queue_state output_state;
+
+       struct list_head ctrls;
+
+       struct list_head src_ctrls[MFC_MAX_BUFFERS];
+       struct list_head dst_ctrls[MFC_MAX_BUFFERS];
+
+       unsigned long src_ctrls_avail;
+       unsigned long dst_ctrls_avail;
+
+       unsigned int sequence;
+
+       /* Control values */
+       int codec_mode;
+       __u32 pix_format;
+
+       /* Extra Buffers */
+       struct s5p_mfc_special_buf instance_ctx_buf;
+
+       struct s5p_mfc_dec *dec_priv;
+       struct s5p_mfc_enc *enc_priv;
+
+       struct s5p_mfc_ctrls_ops *c_ops;
+
+       size_t scratch_buf_size;
+       size_t loopfilter_luma_size;
+       size_t loopfilter_chroma_size;
+
+       /* Profile infomation */
+       int is_10bit;
+       int is_422format;
+
+       /* for DRM */
+       int is_drm;
+
+       int is_dpb_realloc;
+       enum mfc_dec_wait_state wait_state;
+       int clear_work_bit;
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       int qos_req_step;
+       struct list_head qos_list;
+#endif
+       unsigned int qos_ratio;
+       unsigned long framerate;
+       unsigned long last_framerate;
+
+       struct mfc_timestamp ts_array[MFC_TIME_INDEX];
+       struct list_head ts_list;
+       int ts_count;
+       int ts_is_full;
+
+       unsigned int color_range;
+       unsigned int color_space;
+
+       int buf_process_type;
+
+       unsigned long raw_protect_flag;
+       unsigned long stream_protect_flag;
+       struct _otf_handle *otf_handle;
+};
+
+#endif /* __S5P_MFC_DATA_STRUCT_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_debug.h b/drivers/media/platform/exynos/mfc/s5p_mfc_debug.h
new file mode 100644 (file)
index 0000000..f3fb645
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_debug.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 __S5P_MFC_DEBUG_H
+#define __S5P_MFC_DEBUG_H __FILE__
+
+#define DEBUG
+
+#ifdef DEBUG
+
+extern unsigned int debug_level;
+extern unsigned int debug_ts;
+extern unsigned int dbg_enable;
+extern unsigned int nal_q_dump;
+extern unsigned int nal_q_disable;
+extern unsigned int nal_q_parallel_disable;
+extern unsigned int otf_dump;
+
+#define mfc_debug(level, fmt, args...)                         \
+       do {                                                    \
+               if (debug_level >= level)                               \
+                       printk(KERN_DEBUG "%s:%d: " fmt,        \
+                               __func__, __LINE__, ##args);    \
+       } while (0)
+#else
+#define mfc_debug(fmt, args...)
+#endif
+
+#define mfc_debug_enter() mfc_debug(5, "enter\n")
+#define mfc_debug_leave() mfc_debug(5, "leave\n")
+
+#define mfc_err_dev(fmt, args...)                      \
+       do {                                            \
+               printk(KERN_ERR "%s:%d: " fmt,          \
+                      __func__, __LINE__, ##args);     \
+       } while (0)
+
+#define mfc_err_ctx(fmt, args...)                      \
+       do {                                            \
+               printk(KERN_ERR "[c:%d] %s:%d: " fmt,   \
+                       ctx->num,                       \
+                      __func__, __LINE__, ##args);     \
+       } while (0)
+
+#define mfc_info_dev(fmt, args...)                     \
+       do {                                            \
+               printk(KERN_INFO "%s:%d: " fmt,         \
+                       __func__, __LINE__, ##args);    \
+       } while (0)
+
+#define mfc_info_ctx(fmt, args...)                     \
+       do {                                            \
+               printk(KERN_INFO "[c:%d] %s:%d: " fmt,  \
+                       ctx->num,                       \
+                       __func__, __LINE__, ##args);    \
+       } while (0)
+
+#define MFC_TRACE_STR_LEN              80
+#define MFC_TRACE_COUNT_MAX            1024
+#define MFC_TRACE_COUNT_PRINT          30
+
+struct _mfc_trace {
+       unsigned long long time;
+       char str[MFC_TRACE_STR_LEN];
+};
+
+/* If there is no ctx structure */
+#define MFC_TRACE_DEV(fmt, args...)                                                            \
+       do {                                                                                    \
+               int cpu = raw_smp_processor_id();                                               \
+               int cnt;                                                                        \
+               cnt = atomic_inc_return(&dev->trace_ref) & (MFC_TRACE_COUNT_MAX - 1);           \
+               dev->mfc_trace[cnt].time = cpu_clock(cpu);                                      \
+               snprintf(dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN,                            \
+                               fmt, ##args);                           \
+       } while (0)
+
+/* If there is ctx structure */
+#define MFC_TRACE_CTX(fmt, args...)                                                            \
+       do {                                                                                    \
+               int cpu = raw_smp_processor_id();                                               \
+               int cnt;                                                                        \
+               cnt = atomic_inc_return(&dev->trace_ref) & (MFC_TRACE_COUNT_MAX - 1);           \
+               dev->mfc_trace[cnt].time = cpu_clock(cpu);                                      \
+               snprintf(dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN,                            \
+                               "[c:%d] " fmt, ctx->num, ##args);                               \
+       } while (0)
+
+
+/* If there is no ctx structure */
+#define MFC_TRACE_DEV_HWLOCK(fmt, args...)                                                     \
+       do {                                                                                    \
+               int cpu = raw_smp_processor_id();                                               \
+               int cnt;                                                                        \
+               cnt = atomic_inc_return(&dev->trace_ref_hwlock) & (MFC_TRACE_COUNT_MAX - 1);    \
+               dev->mfc_trace_hwlock[cnt].time = cpu_clock(cpu);                               \
+               snprintf(dev->mfc_trace_hwlock[cnt].str, MFC_TRACE_STR_LEN,                     \
+                               fmt, ##args);                                                   \
+       } while (0)
+
+/* If there is ctx structure */
+#define MFC_TRACE_CTX_HWLOCK(fmt, args...)                                                     \
+       do {                                                                                    \
+               int cpu = raw_smp_processor_id();                                               \
+               int cnt;                                                                        \
+               cnt = atomic_inc_return(&dev->trace_ref_hwlock) & (MFC_TRACE_COUNT_MAX - 1);    \
+               dev->mfc_trace_hwlock[cnt].time = cpu_clock(cpu);                               \
+               snprintf(dev->mfc_trace_hwlock[cnt].str, MFC_TRACE_STR_LEN,                     \
+                               "[c:%d] " fmt, ctx->num, ##args);                               \
+       } while (0)
+
+
+#endif /* __S5P_MFC_DEBUG_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.c b/drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.c
new file mode 100644 (file)
index 0000000..f29b008
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_debug.c
+ *
+ * Copyright (c) 2017 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/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "s5p_mfc_debugfs.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+
+#include "s5p_mfc_queue.h"
+
+unsigned int debug_level;
+unsigned int debug_ts;
+unsigned int dbg_enable;
+unsigned int nal_q_dump;
+unsigned int nal_q_disable;
+unsigned int nal_q_parallel_disable;
+unsigned int otf_dump;
+
+static int mfc_info_show(struct seq_file *s, void *unused)
+{
+       struct s5p_mfc_dev *dev = s->private;
+       struct s5p_mfc_ctx *ctx = NULL;
+       int i;
+       char *codec_name = NULL;
+
+       seq_puts(s, ">> MFC device information(common)\n");
+       seq_printf(s, "[VERSION] H/W: v%x.%x, F/W: %06x(%c), DRV: %d\n",
+                MFC_VER_MAJOR(dev), MFC_VER_MINOR(dev), dev->fw.date,
+                dev->fw.fimv_info, MFC_DRIVER_INFO);
+       seq_printf(s, "[PM] power: %d, clock: %d\n",
+                       s5p_mfc_pm_get_pwr_ref_cnt(dev), s5p_mfc_pm_get_clk_ref_cnt(dev));
+       seq_printf(s, "[CTX] num_inst: %d, num_drm_inst: %d, curr_ctx: %d(is_drm: %d)\n",
+                       dev->num_inst, dev->num_drm_inst, dev->curr_ctx, dev->curr_ctx_is_drm);
+       seq_printf(s, "[HWLOCK] bits: %#lx, dev: %#lx, owned_by_irq = %d, wl_count = %d\n",
+                       dev->hwlock.bits, dev->hwlock.dev,
+                       dev->hwlock.owned_by_irq, dev->hwlock.wl_count);
+       if (dev->nal_q_handle)
+               seq_printf(s, "[NAL-Q] state: %d\n", dev->nal_q_handle->nal_q_state);
+
+       seq_puts(s, ">> MFC device information(instance)\n");
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               ctx = dev->ctx[i];
+               if (ctx) {
+                       if (ctx->type == MFCINST_DECODER)
+                               codec_name = ctx->src_fmt->name;
+                       else
+                               codec_name = ctx->dst_fmt->name;
+
+                       seq_printf(s, "[CTX:%d] codec: %s(%s), width: %d, height: %d, state: %d\n",
+                               ctx->num, ctx->type == MFCINST_DECODER ? "DEC" : "ENC", codec_name,
+                               ctx->img_width, ctx->img_height, ctx->state);
+                       seq_printf(s, "        queue(src: %d, dst: %d, src_nal: %d, dst_nal: %d, ref: %d)\n",
+                               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+                               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue),
+                               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue),
+                               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue));
+               }
+       }
+
+       return 0;
+}
+
+static int mfc_debug_info_show(struct seq_file *s, void *unused)
+{
+       seq_puts(s, ">> MFC debug information\n");
+       return 0;
+}
+
+static int mfc_info_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mfc_info_show, inode->i_private);
+}
+
+static int mfc_debug_info_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mfc_debug_info_show, inode->i_private);
+}
+
+static const struct file_operations mfc_info_fops = {
+       .open = mfc_info_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static const struct file_operations debug_info_fops = {
+       .open = mfc_debug_info_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+void s5p_mfc_init_debugfs(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_debugfs *debugfs = &dev->debugfs;
+
+       debugfs->root = debugfs_create_dir("mfc", NULL);
+       if (!debugfs->root) {
+               mfc_err_dev("debugfs: failed to create root derectory.\n");
+               return;
+       }
+
+       debugfs->mfc_info = debugfs_create_file("mfc_info",
+                       0444, debugfs->root, dev, &mfc_info_fops);
+       debugfs->debug_info = debugfs_create_file("debug_info",
+                       0444, debugfs->root, dev, &debug_info_fops);
+       debugfs->debug_level = debugfs_create_u32("debug",
+                       0644, debugfs->root, &debug_level);
+       debugfs->debug_ts = debugfs_create_u32("debug_ts",
+                       0644, debugfs->root, &debug_ts);
+       debugfs->dbg_enable = debugfs_create_u32("dbg_enable",
+                       0644, debugfs->root, &dbg_enable);
+       debugfs->nal_q_dump = debugfs_create_u32("nal_q_dump",
+                       0644, debugfs->root, &nal_q_dump);
+       debugfs->nal_q_disable = debugfs_create_u32("nal_q_disable",
+                       0644, debugfs->root, &nal_q_disable);
+       debugfs->nal_q_parallel_disable = debugfs_create_u32("nal_q_parallel_disable",
+                       0644, debugfs->root, &nal_q_parallel_disable);
+       debugfs->otf_dump = debugfs_create_u32("otf_dump",
+                       0644, debugfs->root, &otf_dump);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.h b/drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.h
new file mode 100644 (file)
index 0000000..a4186e9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_debugfs.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 __S5P_MFC_DEBUGFS_H
+#define __S5P_MFC_DEBUGFS_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+void s5p_mfc_init_debugfs(struct s5p_mfc_dev *dev);
+
+#endif /* __S5P_MFC_DEBUGFS_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_dec.c b/drivers/media/platform/exynos/mfc/s5p_mfc_dec.c
new file mode 100644 (file)
index 0000000..1fb877c
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_dec.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 "s5p_mfc_dec.h"
+#include "s5p_mfc_dec_internal.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+#define MAX_FRAME_SIZE         (2*1024*1024)
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *mfc_dec_find_format(struct v4l2_format *f, unsigned int t)
+{
+       unsigned long i;
+
+       for (i = 0; i < NUM_FORMATS; i++) {
+               if (dec_formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+                   dec_formats[i].type == t)
+                       return (struct s5p_mfc_fmt *)&dec_formats[i];
+       }
+
+       return NULL;
+}
+
+static struct v4l2_queryctrl *mfc_dec_get_ctrl(int id)
+{
+       unsigned long i;
+
+       for (i = 0; i < NUM_CTRLS; ++i)
+               if (id == controls[i].id)
+                       return &controls[i];
+
+       return NULL;
+}
+
+/* Check whether a ctrl value if correct */
+static int mfc_dec_check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct v4l2_queryctrl *c;
+
+       c = mfc_dec_get_ctrl(ctrl->id);
+       if (!c)
+               return -EINVAL;
+
+       if (ctrl->value < c->minimum || ctrl->value > c->maximum
+               || (c->step != 0 && ctrl->value % c->step != 0)) {
+               mfc_err_ctx("Invalid control value (%#x)\n", ctrl->value);
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+                          struct v4l2_capability *cap)
+{
+       strncpy(cap->driver, "MFC", sizeof(cap->driver) - 1);
+       strncpy(cap->card, "decoder", sizeof(cap->card) - 1);
+       cap->bus_info[0] = 0;
+       cap->version = KERNEL_VERSION(1, 0, 0);
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
+                       | V4L2_CAP_VIDEO_OUTPUT
+                       | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+                       | V4L2_CAP_VIDEO_OUTPUT_MPLANE
+                       | V4L2_CAP_STREAMING
+                       | V4L2_CAP_DEVICE_CAPS;
+
+       cap->capabilities = cap->device_caps;
+
+       return 0;
+}
+
+/* Enumerate format */
+static int mfc_dec_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+       struct s5p_mfc_fmt *fmt;
+       unsigned long i, j = 0;
+
+       for (i = 0; i < NUM_FORMATS; ++i) {
+               if (mplane && dec_formats[i].mem_planes == 1)
+                       continue;
+               else if (!mplane && dec_formats[i].mem_planes > 1)
+                       continue;
+               if (out && dec_formats[i].type != MFC_FMT_DEC)
+                       continue;
+               else if (!out && dec_formats[i].type != MFC_FMT_RAW)
+                       continue;
+
+               if (j == f->index)
+                       break;
+               ++j;
+       }
+       if (i == NUM_FORMATS)
+               return -EINVAL;
+
+       fmt = &dec_formats[i];
+       strlcpy(f->description, fmt->name, sizeof(f->description));
+       f->pixelformat = fmt->fourcc;
+
+       return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+                                                       struct v4l2_fmtdesc *f)
+{
+       return mfc_dec_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+                                                       struct v4l2_fmtdesc *f)
+{
+       return mfc_dec_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+                                                       struct v4l2_fmtdesc *f)
+{
+       return mfc_dec_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+                                                       struct v4l2_fmtdesc *f)
+{
+       return mfc_dec_enum_fmt(f, true, true);
+}
+
+static void mfc_dec_change_format(struct s5p_mfc_ctx *ctx)
+{
+       u32 org_fmt = ctx->dst_fmt->fourcc;
+
+       if (ctx->is_10bit && ctx->is_422format) {
+               switch (org_fmt) {
+               case V4L2_PIX_FMT_NV16M_S10B:
+               case V4L2_PIX_FMT_NV61M_S10B:
+               case V4L2_PIX_FMT_NV16M_P210:
+               case V4L2_PIX_FMT_NV61M_P210:
+                       break;
+               case V4L2_PIX_FMT_NV12M:
+               case V4L2_PIX_FMT_NV16M:
+               case V4L2_PIX_FMT_NV12M_S10B:
+               case V4L2_PIX_FMT_NV12M_P010:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[14];
+                       break;
+               case V4L2_PIX_FMT_NV21M:
+               case V4L2_PIX_FMT_NV61M:
+               case V4L2_PIX_FMT_NV21M_S10B:
+               case V4L2_PIX_FMT_NV21M_P010:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[17];
+                       break;
+               default:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[14];
+                       break;
+               }
+               ctx->raw_buf.num_planes = 2;
+       } else if (ctx->is_10bit && !ctx->is_422format) {
+               if (ctx->dst_fmt->mem_planes == 1) {
+                       /* YUV420 only supports the single plane */
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[8];
+               } else {
+                       switch (org_fmt) {
+                       case V4L2_PIX_FMT_NV12M_S10B:
+                       case V4L2_PIX_FMT_NV21M_S10B:
+                       case V4L2_PIX_FMT_NV12M_P010:
+                       case V4L2_PIX_FMT_NV21M_P010:
+                               break;
+                       case V4L2_PIX_FMT_NV12M:
+                       case V4L2_PIX_FMT_NV16M:
+                       case V4L2_PIX_FMT_NV16M_S10B:
+                       case V4L2_PIX_FMT_NV16M_P210:
+                               ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[7];
+                               break;
+                       case V4L2_PIX_FMT_NV21M:
+                       case V4L2_PIX_FMT_NV61M:
+                       case V4L2_PIX_FMT_NV61M_S10B:
+                       case V4L2_PIX_FMT_NV61M_P210:
+                               ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[11];
+                               break;
+                       default:
+                               ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[7];
+                               break;
+                       }
+               }
+               ctx->raw_buf.num_planes = 2;
+       } else if (!ctx->is_10bit && ctx->is_422format) {
+               switch (org_fmt) {
+               case V4L2_PIX_FMT_NV16M:
+               case V4L2_PIX_FMT_NV61M:
+                       break;
+               case V4L2_PIX_FMT_NV12M:
+               case V4L2_PIX_FMT_NV12M_S10B:
+               case V4L2_PIX_FMT_NV16M_S10B:
+               case V4L2_PIX_FMT_NV12M_P010:
+               case V4L2_PIX_FMT_NV16M_P210:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[13];
+                       break;
+               case V4L2_PIX_FMT_NV21M:
+               case V4L2_PIX_FMT_NV21M_S10B:
+               case V4L2_PIX_FMT_NV61M_S10B:
+               case V4L2_PIX_FMT_NV21M_P010:
+               case V4L2_PIX_FMT_NV61M_P210:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[16];
+                       break;
+               default:
+                       ctx->dst_fmt = (struct s5p_mfc_fmt *)&dec_formats[13];
+                       break;
+               }
+               ctx->raw_buf.num_planes = 2;
+       }
+
+       if (org_fmt != ctx->dst_fmt->fourcc)
+               mfc_info_ctx("format is changed to %s\n", ctx->dst_fmt->name);
+}
+
+/* Get format */
+static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec;
+       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+       struct s5p_mfc_raw_info *raw;
+       int i;
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+       mfc_debug_enter();
+       mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+
+       if (ctx->state == MFCINST_GOT_INST ||
+           ctx->state == MFCINST_RES_CHANGE_FLUSH ||
+           ctx->state == MFCINST_RES_CHANGE_END) {
+               /* If the MFC is parsing the header,
+                * so wait until it is finished */
+               if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_FIMV_R2H_CMD_SEQ_DONE_RET)) {
+                               mfc_err_dev("header parsing failed\n");
+                               return -EAGAIN;
+               }
+       }
+
+       if (ctx->state >= MFCINST_HEAD_PARSED &&
+           ctx->state < MFCINST_ABORT) {
+               /* This is run on CAPTURE (deocde output) */
+
+               /* only NV16(61) format is supported for 422 format */
+               /* only 2 plane is supported for 10bit */
+               mfc_dec_change_format(ctx);
+
+               raw = &ctx->raw_buf;
+               /* Width and height are set to the dimensions
+                  of the movie, the buffer is bigger and
+                  further processing stages should crop to this
+                  rectangle. */
+               s5p_mfc_dec_calc_dpb_size(ctx);
+
+               pix_mp->width = ctx->img_width;
+               pix_mp->height = ctx->img_height;
+               pix_mp->num_planes = ctx->dst_fmt->mem_planes;
+
+               if (dec->is_interlaced)
+                       pix_mp->field = V4L2_FIELD_INTERLACED;
+               else
+                       pix_mp->field = V4L2_FIELD_NONE;
+
+               /* Set pixelformat to the format in which MFC
+                  outputs the decoded frame */
+               pix_mp->pixelformat = ctx->dst_fmt->fourcc;
+               for (i = 0; i < ctx->dst_fmt->mem_planes; i++) {
+                       pix_mp->plane_fmt[i].bytesperline = raw->stride[i];
+                       if (ctx->dst_fmt->mem_planes == 1) {
+                               pix_mp->plane_fmt[i].sizeimage = raw->total_plane_size;
+                       } else {
+                               if (ctx->is_10bit)
+                                       pix_mp->plane_fmt[i].sizeimage = raw->plane_size[i]
+                                               + raw->plane_size_2bits[i];
+                               else
+                                       pix_mp->plane_fmt[i].sizeimage = raw->plane_size[i];
+                       }
+               }
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec;
+       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+
+       mfc_debug_enter();
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+       mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+
+       /* This is run on OUTPUT
+          The buffer contains compressed image
+          so width and height have no meaning */
+       pix_mp->width = 0;
+       pix_mp->height = 0;
+       pix_mp->field = V4L2_FIELD_NONE;
+       pix_mp->plane_fmt[0].bytesperline = dec->src_buf_size;
+       pix_mp->plane_fmt[0].sizeimage = dec->src_buf_size;
+       pix_mp->pixelformat = ctx->src_fmt->fourcc;
+       pix_mp->num_planes = ctx->src_fmt->mem_planes;
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_fmt *fmt;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       mfc_debug(2, "Type is %d\n", f->type);
+       if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               fmt = mfc_dec_find_format(f, MFC_FMT_DEC);
+               if (!fmt) {
+                       mfc_err_dev("Unsupported format for source.\n");
+                       return -EINVAL;
+               }
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               fmt = mfc_dec_find_format(f, MFC_FMT_RAW);
+
+               if (!fmt) {
+                       mfc_err_dev("Unsupported format for destination.\n");
+                       return -EINVAL;
+               }
+
+               if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+                       mfc_err_dev("Not supported format: NV12MT\n");
+                       return -EINVAL;
+               }
+               else if(fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
+                       mfc_err_dev("Not supported format: NV12MT_16X16\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                                       struct v4l2_format *f)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->vq_dst.streaming) {
+               mfc_err_ctx("queue busy\n");
+               return -EBUSY;
+       }
+
+       ret = vidioc_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       ctx->dst_fmt = mfc_dec_find_format(f, MFC_FMT_RAW);
+       if (!ctx->dst_fmt) {
+               mfc_err_ctx("Unsupported format for destination.\n");
+               return -EINVAL;
+       }
+       ctx->raw_buf.num_planes = ctx->dst_fmt->num_planes;
+       mfc_info_ctx("Dec output pixelformat : %s\n", ctx->dst_fmt->name);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int mfc_force_close_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       if (ctx->inst_no == MFC_NO_INSTANCE_SET)
+               return 0;
+
+       s5p_mfc_change_state(ctx, MFCINST_RETURN_INST);
+       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       if (s5p_mfc_just_run(dev, ctx->num)) {
+               mfc_err_ctx("Failed to run MFC.\n");
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               return -EIO;
+       }
+
+       /* Wait until instance is returned or timeout occured */
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET)) {
+               mfc_err_ctx("Waiting for CLOSE_INSTANCE timed out\n");
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               return -EIO;
+       }
+
+       /* Free resources */
+       s5p_mfc_release_instance_context(ctx);
+       s5p_mfc_change_state(ctx, MFCINST_INIT);
+
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,
+                                                       struct v4l2_format *f)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec;
+       int ret = 0;
+       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->vq_src.streaming) {
+               mfc_err_ctx("queue busy\n");
+               return -EBUSY;
+       }
+
+       ret = vidioc_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       ctx->src_fmt = mfc_dec_find_format(f, MFC_FMT_DEC);
+       ctx->codec_mode = ctx->src_fmt->codec_mode;
+       mfc_info_ctx("Dec input codec(%d): %s\n",
+                       ctx->codec_mode, ctx->src_fmt->name);
+       ctx->pix_format = pix_mp->pixelformat;
+       if ((pix_mp->width > 0) && (pix_mp->height > 0)) {
+               ctx->img_height = pix_mp->height;
+               ctx->img_width = pix_mp->width;
+       }
+       /* As this buffer will contain compressed data, the size is set
+        * to the maximum size. */
+       if (pix_mp->plane_fmt[0].sizeimage)
+               dec->src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+       else
+               dec->src_buf_size = MAX_FRAME_SIZE;
+       mfc_debug(2, "sizeimage: %d\n", pix_mp->plane_fmt[0].sizeimage);
+       pix_mp->plane_fmt[0].bytesperline = 0;
+       MFC_TRACE_CTX_HWLOCK("**DEC s_fmt\n");
+       ret = s5p_mfc_get_hwlock_ctx(ctx);
+       if (ret < 0) {
+               mfc_err_ctx("Failed to get hwlock.\n");
+               return -EBUSY;
+       }
+
+       /* In case of calling s_fmt twice or more */
+       ret = mfc_force_close_inst(dev, ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to close already opening instance\n");
+               return -EIO;
+       }
+
+       ret = s5p_mfc_alloc_instance_context(ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to allocate dec instance[%d] buffers.\n",
+                               ctx->num);
+               s5p_mfc_release_hwlock_ctx(ctx);
+               return -ENOMEM;
+       }
+
+       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       ret = s5p_mfc_just_run(dev, ctx->num);
+       if (ret) {
+               mfc_err_ctx("Failed to run MFC.\n");
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               s5p_mfc_release_instance_context(ctx);
+               return -EIO;
+       }
+
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+                       S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET)) {
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               s5p_mfc_release_instance_context(ctx);
+               return -EIO;
+       }
+
+       s5p_mfc_release_hwlock_ctx(ctx);
+
+       mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+
+       if (s5p_mfc_dec_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+               struct v4l2_requestbuffers *reqbufs)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec;
+       int ret = 0;
+
+       mfc_debug_enter();
+       mfc_info_ctx("Memory type: %d\n", reqbufs->memory);
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       if (reqbufs->memory == V4L2_MEMORY_MMAP) {
+               mfc_err_ctx("Not supported memory type (%d).\n", reqbufs->memory);
+               return -EINVAL;
+       }
+
+       if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               /* Can only request buffers after
+                  an instance has been opened.*/
+               if (ctx->state == MFCINST_GOT_INST) {
+                       if (reqbufs->count == 0) {
+                               mfc_debug(2, "Freeing buffers.\n");
+                               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+                               ctx->output_state = QUEUE_FREE;
+                               return ret;
+                       }
+
+                       /* Decoding */
+                       if (ctx->output_state != QUEUE_FREE) {
+                               mfc_err_ctx("Bufs have already been requested.\n");
+                               return -EINVAL;
+                       }
+
+                       ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+                       if (ret) {
+                               mfc_err_ctx("vb2_reqbufs on output failed.\n");
+                               return ret;
+                       }
+
+                       ctx->output_state = QUEUE_BUFS_REQUESTED;
+               }
+       } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (reqbufs->count == 0) {
+                       mfc_debug(2, "Freeing buffers.\n");
+                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       s5p_mfc_release_codec_buffers(ctx);
+                       ctx->capture_state = QUEUE_FREE;
+                       return ret;
+               }
+
+               dec->dst_memtype = reqbufs->memory;
+
+               if (ctx->capture_state != QUEUE_FREE) {
+                       mfc_err_ctx("Bufs have already been requested.\n");
+                       return -EINVAL;
+               }
+
+               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+               if (ret) {
+                       mfc_err_ctx("vb2_reqbufs on capture failed.\n");
+                       return ret;
+               }
+
+               if (reqbufs->count < ctx->dpb_count) {
+                       mfc_err_ctx("Not enough buffers allocated.\n");
+                       reqbufs->count = 0;
+                       vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       return -ENOMEM;
+               }
+
+               dec->total_dpb_count = reqbufs->count;
+
+               ret = s5p_mfc_alloc_codec_buffers(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to allocate decoding buffers.\n");
+                       reqbufs->count = 0;
+                       vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       return -ENOMEM;
+               }
+
+               ctx->capture_state = QUEUE_BUFS_REQUESTED;
+
+               if (s5p_mfc_dec_ctx_ready(ctx))
+                       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+
+               s5p_mfc_try_run(dev);
+       }
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+                                                  struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret;
+
+       mfc_debug_enter();
+
+       mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = vb2_querybuf(&ctx->vq_dst, buf);
+               if (ret != 0) {
+                       mfc_err_dev("dec dst: error in vb2_querybuf()\n");
+                       return ret;
+               }
+       } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = vb2_querybuf(&ctx->vq_src, buf);
+               if (ret != 0) {
+                       mfc_err_dev("dec src: error in vb2_querybuf()\n");
+                       return ret;
+               }
+       } else {
+               mfc_err_dev("invalid buf type (%d)\n", buf->type);
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = -EINVAL;
+
+       mfc_debug_enter();
+
+       mfc_debug(2, "Enqueued buf: %d, (type = %d)\n", buf->index, buf->type);
+       if (ctx->state == MFCINST_ERROR) {
+               mfc_err_ctx("Call on QBUF after unrecoverable error.\n");
+               return -EIO;
+       }
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && !buf->length) {
+               mfc_err_ctx("multiplanar but length is zero\n");
+               return -EIO;
+       }
+
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (buf->m.planes[0].bytesused > buf->m.planes[0].length) {
+                       mfc_err_ctx("data size (%d) must be less than "
+                                       "buffer size(%d)\n",
+                                       buf->m.planes[0].bytesused,
+                                       buf->m.planes[0].length);
+                       return -EIO;
+               }
+
+               s5p_mfc_qos_update_framerate(ctx);
+
+               if (!buf->m.planes[0].bytesused) {
+                       buf->m.planes[0].bytesused = buf->m.planes[0].length;
+                       mfc_debug(2, "Src input size zero, changed to buf size %d\n",
+                                       buf->m.planes[0].bytesused);
+               } else {
+                       mfc_debug(2, "Src input size = %d\n", buf->m.planes[0].bytesused);
+               }
+               ret = vb2_qbuf(&ctx->vq_src, buf);
+       } else {
+               ret = vb2_qbuf(&ctx->vq_dst, buf);
+               mfc_debug(2, "End of enqueue(%d) : %d\n", buf->index, ret);
+       }
+
+       mfc_debug_leave();
+       return ret;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct dec_dpb_ref_info *dstBuf, *srcBuf;
+       int ret;
+       int ncount = 0;
+
+       mfc_debug_enter();
+       mfc_debug(2, "Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf->m.planes,
+                                                               buf->type);
+       if (ctx->state == MFCINST_ERROR) {
+               mfc_err_ctx("Call on DQBUF after unrecoverable error.\n");
+               return -EIO;
+       }
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+       } else {
+               ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+
+               if (buf->index >= MFC_MAX_DPBS) {
+                       mfc_err_ctx("buffer index[%d] range over\n", buf->index);
+                       return -EINVAL;
+               }
+
+               /* Memcpy from dec->ref_info to shared memory */
+               srcBuf = &dec->ref_info[buf->index];
+               for (ncount = 0; ncount < MFC_MAX_DPBS; ncount++) {
+                       if (srcBuf->dpb[ncount].fd[0] == MFC_INFO_INIT_FD)
+                               break;
+                       mfc_debug(2, "DQ index[%d] Released FD = %d\n",
+                                       buf->index, srcBuf->dpb[ncount].fd[0]);
+               }
+
+               if (dec->sh_handle.vaddr != NULL) {
+                       dstBuf = (struct dec_dpb_ref_info *)
+                                       dec->sh_handle.vaddr + buf->index;
+                       memcpy(dstBuf, srcBuf, sizeof(struct dec_dpb_ref_info));
+                       dstBuf->index = buf->index;
+               }
+       }
+       mfc_debug_leave();
+       return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+                          enum v4l2_buf_type type)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = -EINVAL;
+
+       mfc_debug_enter();
+
+       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = vb2_streamon(&ctx->vq_src, type);
+       } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = vb2_streamon(&ctx->vq_dst, type);
+
+               if (!ret)
+                       s5p_mfc_qos_on(ctx);
+       } else {
+               mfc_err_ctx("unknown v4l2 buffer type\n");
+       }
+
+       mfc_debug(2, "src: %d, dst: %d,  state = %d, dpb_count = %d\n",
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+                 ctx->state, ctx->dpb_count);
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+                           enum v4l2_buf_type type)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = -EINVAL;
+
+       mfc_debug_enter();
+
+       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               s5p_mfc_qos_reset_last_framerate(ctx);
+
+               ret = vb2_streamoff(&ctx->vq_src, type);
+       } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = vb2_streamoff(&ctx->vq_dst, type);
+               if (!ret)
+                       s5p_mfc_qos_off(ctx);
+       } else {
+               mfc_err_ctx("unknown v4l2 buffer type\n");
+       }
+
+       mfc_debug(2, "streamoff\n");
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Query a ctrl */
+static int vidioc_queryctrl(struct file *file, void *priv,
+                           struct v4l2_queryctrl *qc)
+{
+       struct v4l2_queryctrl *c;
+
+       c = mfc_dec_get_ctrl(qc->id);
+       if (!c)
+               return -EINVAL;
+       *qc = *c;
+       return 0;
+}
+
+static int mfc_dec_ext_info(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int val = 0;
+
+       val |= DEC_SET_DYNAMIC_DPB;
+       if (FW_SUPPORT_SKYPE(dev))
+               val |= DEC_SET_SKYPE_FLAG;
+
+       return val;
+}
+
+/* Get ctrl */
+static int mfc_dec_get_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       int found = 0;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       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_CACHEABLE:
+               mfc_debug(5, "it is supported only V4L2_MEMORY_MMAP\n");
+               break;
+       case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+               if (ctx->state >= MFCINST_HEAD_PARSED &&
+                   ctx->state < MFCINST_ABORT) {
+                       ctrl->value = ctx->dpb_count;
+                       break;
+               } else if (ctx->state != MFCINST_INIT) {
+                       mfc_err_ctx("Decoding not initialised.\n");
+                       return -EINVAL;
+               }
+
+               /* Should wait for the header to be parsed */
+               if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_FIMV_R2H_CMD_SEQ_DONE_RET)) {
+                       s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+                       return -EIO;
+               }
+
+               if (ctx->state >= MFCINST_HEAD_PARSED &&
+                   ctx->state < MFCINST_ABORT) {
+                       ctrl->value = ctx->dpb_count;
+               } else {
+                       mfc_err_ctx("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:
+               if (ctx->is_dpb_realloc && ctx->state == MFCINST_HEAD_PARSED)
+                       ctrl->value = MFCSTATE_DEC_S3D_REALLOC;
+               else if (ctx->state == MFCINST_RES_CHANGE_FLUSH
+                               || ctx->state == MFCINST_RES_CHANGE_END
+                               || ctx->state == MFCINST_HEAD_PARSED)
+                       ctrl->value = MFCSTATE_DEC_RES_DETECT;
+               else if (ctx->state == 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 = s5p_mfc_qos_get_framerate(ctx);
+               break;
+       case V4L2_CID_MPEG_MFC_GET_VERSION_INFO:
+               ctrl->value = s5p_mfc_version(dev);
+               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_10BIT_INFO:
+               ctrl->value = ctx->is_10bit;
+               break;
+       case V4L2_CID_MPEG_MFC_GET_DRIVER_INFO:
+               ctrl->value = MFC_DRIVER_INFO;
+               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->has_new) {
+                                       ctx_ctrl->has_new = 0;
+                                       ctrl->value = ctx_ctrl->val;
+                               } else {
+                                       mfc_debug(8, "Control value "\
+                                                       "is not up to date: "\
+                                                       "0x%08x\n", ctrl->id);
+                                       return -EINVAL;
+                               }
+
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+                       return -EINVAL;
+               }
+               break;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Get a ctrl */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+                       struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+       ret = mfc_dec_get_ctrl_val(ctx, ctrl);
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Set a ctrl */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       int ret = 0;
+       int found = 0;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       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_CACHEABLE:
+               mfc_debug(5, "it is supported only V4L2_MEMORY_MMAP\n");
+               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:
+               ctx->wait_state = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE:
+               mfc_err_dev("not supported CID: 0x%x\n",ctrl->id);
+               break;
+       case V4L2_CID_MPEG_VIDEO_QOS_RATIO:
+               ctx->qos_ratio = ctrl->value;
+               mfc_info_ctx("set %d qos_ratio.\n", ctrl->value);
+               break;
+       case V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE:
+               dec->is_dynamic_dpb = ctrl->value;
+               if (dec->is_dynamic_dpb == 0)
+                       mfc_err_dev("is_dynamic_dpb is 0. it has to be enabled.\n");
+               break;
+       case V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE:
+               dec->sh_handle.fd = ctrl->value;
+               if (s5p_mfc_mem_get_user_shared_handle(ctx, &dec->sh_handle)) {
+                       dec->sh_handle.fd = -1;
+                       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;
+               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->has_new = 1;
+                               ctx_ctrl->val = ctrl->value;
+
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+                       return -EINVAL;
+               }
+               break;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+               struct v4l2_crop *cr)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+
+       mfc_debug_enter();
+
+       if (!ready_to_get_crop(ctx)) {
+               mfc_debug(2, "ready to get crop failed\n");
+               return -EINVAL;
+       }
+
+       if (ctx->state == MFCINST_RUNNING && dec->detect_black_bar
+                       && dec->black_bar_updated) {
+               cr->c.left = dec->black_bar.left;
+               cr->c.top = dec->black_bar.top;
+               cr->c.width = dec->black_bar.width;
+               cr->c.height = dec->black_bar.height;
+               mfc_debug(2, "black bar 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 ||
+                       ctx->src_fmt->fourcc == V4L2_PIX_FMT_BPG) {
+                       cr->c.left = dec->cr_left;
+                       cr->c.top = dec->cr_top;
+                       cr->c.width = ctx->img_width - dec->cr_left - dec->cr_right;
+                       cr->c.height = ctx->img_height - dec->cr_top - dec->cr_bot;
+                       mfc_debug(2, "Cropping info: l=%d t=%d " \
+                                       "w=%d h=%d (r=%d b=%d fw=%d fh=%d)\n",
+                                       dec->cr_left, dec->cr_top,
+                                       cr->c.width, cr->c.height,
+                                       dec->cr_right, dec->cr_bot,
+                                       ctx->img_width, ctx->img_height);
+               } else {
+                       cr->c.left = 0;
+                       cr->c.top = 0;
+                       cr->c.width = ctx->img_width;
+                       cr->c.height = ctx->img_height;
+                       mfc_debug(2, "Cropping info: w=%d h=%d fw=%d fh=%d\n",
+                                       cr->c.width, cr->c.height,
+                                       ctx->img_width, ctx->img_height);
+               }
+       }
+
+       mfc_debug_leave();
+       return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                       struct v4l2_ext_controls *f)
+{
+       struct s5p_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_MPEG)
+               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_debug(2, "[%d] id: 0x%08x, value: %d\n", i, ext_ctrl->id, ext_ctrl->value);
+       }
+
+       return ret;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
+       .vidioc_querycap                = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+       .vidioc_enum_fmt_vid_out        = vidioc_enum_fmt_vid_out,
+       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+       .vidioc_g_fmt_vid_cap_mplane    = vidioc_g_fmt_vid_cap_mplane,
+       .vidioc_g_fmt_vid_out_mplane    = vidioc_g_fmt_vid_out_mplane,
+       .vidioc_try_fmt_vid_cap_mplane  = vidioc_try_fmt,
+       .vidioc_try_fmt_vid_out_mplane  = vidioc_try_fmt,
+       .vidioc_s_fmt_vid_cap_mplane    = vidioc_s_fmt_vid_cap_mplane,
+       .vidioc_s_fmt_vid_out_mplane    = vidioc_s_fmt_vid_out_mplane,
+       .vidioc_reqbufs                 = vidioc_reqbufs,
+       .vidioc_querybuf                = vidioc_querybuf,
+       .vidioc_qbuf                    = vidioc_qbuf,
+       .vidioc_dqbuf                   = vidioc_dqbuf,
+       .vidioc_streamon                = vidioc_streamon,
+       .vidioc_streamoff               = vidioc_streamoff,
+       .vidioc_queryctrl               = vidioc_queryctrl,
+       .vidioc_g_ctrl                  = vidioc_g_ctrl,
+       .vidioc_s_ctrl                  = vidioc_s_ctrl,
+       .vidioc_g_crop                  = vidioc_g_crop,
+       .vidioc_g_ext_ctrls             = vidioc_g_ext_ctrls,
+};
+
+const struct v4l2_ioctl_ops *s5p_mfc_get_dec_v4l2_ioctl_ops(void)
+{
+       return &s5p_mfc_dec_ioctl_ops;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_dec.h b/drivers/media/platform/exynos/mfc/s5p_mfc_dec.h
new file mode 100644 (file)
index 0000000..24e48f7
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_dec.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 __S5P_MFC_DEC_H
+#define __S5P_MFC_DEC_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+const struct v4l2_ioctl_ops *s5p_mfc_get_dec_v4l2_ioctl_ops(void);
+
+#endif /* __S5P_MFC_DEC_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_dec_internal.h b/drivers/media/platform/exynos/mfc/s5p_mfc_dec_internal.h
new file mode 100644 (file)
index 0000000..4fb5aa0
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_dec_internal.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 __S5P_MFC_DEC_INTERNAL_H
+#define __S5P_MFC_DEC_INTERNAL_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+struct s5p_mfc_fmt dec_formats[] = {
+       {
+               .name = "4:2:0 3 Planes Y/Cb/Cr",
+               .fourcc = V4L2_PIX_FMT_YUV420M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 3,
+       },
+       {
+               .name = "4:2:0 3 Planes Y/Cb/Cr single",
+               .fourcc = V4L2_PIX_FMT_YUV420N,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 1,
+       },
+       {
+               .name = "4:2:0 3 Planes Y/Cr/Cb",
+               .fourcc = V4L2_PIX_FMT_YVU420M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 3,
+       },
+       {
+               .name = "4:2:0 2 Planes 16x16 Tiles",
+               .fourcc = V4L2_PIX_FMT_NV12MT_16X16,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes 64x32 Tiles",
+               .fourcc = V4L2_PIX_FMT_NV12MT,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV12M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr single",
+               .fourcc = V4L2_PIX_FMT_NV12N,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 1,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr 10bit",
+               .fourcc = V4L2_PIX_FMT_NV12M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr 10bit single",
+               .fourcc = V4L2_PIX_FMT_NV12N_10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 1,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr P010 10bit",
+               .fourcc = V4L2_PIX_FMT_NV12M_P010,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb",
+               .fourcc = V4L2_PIX_FMT_NV21M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb 10bit",
+               .fourcc = V4L2_PIX_FMT_NV21M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb P010 10bit",
+               .fourcc = V4L2_PIX_FMT_NV21M_P010,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV16M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr 10bit",
+               .fourcc = V4L2_PIX_FMT_NV16M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr P210 10bit",
+               .fourcc = V4L2_PIX_FMT_NV16M_P210,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb",
+               .fourcc = V4L2_PIX_FMT_NV61M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb 10bit",
+               .fourcc = V4L2_PIX_FMT_NV61M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb P210 10bit",
+               .fourcc = V4L2_PIX_FMT_NV61M_P210,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "H264 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264,
+               .codec_mode = S5P_FIMV_CODEC_H264_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "H264/MVC Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264_MVC,
+               .codec_mode = S5P_FIMV_CODEC_H264_MVC_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "H263 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H263,
+               .codec_mode = S5P_FIMV_CODEC_H263_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "MPEG1 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_MPEG1,
+               .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "MPEG2 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_MPEG2,
+               .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "MPEG4 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_MPEG4,
+               .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "FIMV Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_FIMV,
+               .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "FIMV1 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_FIMV1,
+               .codec_mode = S5P_FIMV_CODEC_FIMV1_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "FIMV2 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_FIMV2,
+               .codec_mode = S5P_FIMV_CODEC_FIMV2_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "FIMV3 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_FIMV3,
+               .codec_mode = S5P_FIMV_CODEC_FIMV3_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "FIMV4 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_FIMV4,
+               .codec_mode = S5P_FIMV_CODEC_FIMV4_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "XviD Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_XVID,
+               .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VC1 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+               .codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VC1 RCV Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+               .codec_mode = S5P_FIMV_CODEC_VC1_RCV_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VP8 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VP8,
+               .codec_mode = S5P_FIMV_CODEC_VP8_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VP9 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VP9,
+               .codec_mode = S5P_FIMV_CODEC_VP9_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "HEVC Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_HEVC,
+               .codec_mode = S5P_FIMV_CODEC_HEVC_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "BPG Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_BPG,
+               .codec_mode = S5P_FIMV_CODEC_BPG_DEC,
+               .type = MFC_FMT_DEC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(dec_formats)
+
+static struct v4l2_queryctrl 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_CACHEABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Cacheable flag",
+               .minimum = 0,
+               .maximum = 3,
+               .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 = 1,
+               .maximum = 480000,
+               .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 = 1000,
+               .step = 10,
+               .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 = 65535,
+               .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,
+       },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+#endif /* __S5P_MFC_DEC_INTERNAL_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_dec_ops.c b/drivers/media/platform/exynos/mfc/s5p_mfc_dec_ops.c
new file mode 100644 (file)
index 0000000..0f8f8f0
--- /dev/null
@@ -0,0 +1,950 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_dec_ops.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  "s5p_mfc_common.h"
+
+#include "s5p_mfc_reg.h"
+
+#define NUM_CTRL_CFGS ARRAY_SIZE(mfc_ctrl_list)
+
+struct s5p_mfc_ctrl_cfg mfc_ctrl_list[] = {
+       {
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_PICTURE_TAG,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_RET_PICTURE_TAG_TOP,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_STATUS,
+               .mask = 0x7,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       /* CRC related definitions are based on non-H.264 type */
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_FIRST_PLANE_CRC,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_SECOND_PLANE_CRC,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA1,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_THIRD_PLANE_CRC,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_LUMA,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_FIRST_PLANE_2BIT_CRC,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_CHROMA,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_SECOND_PLANE_2BIT_CRC,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CRC_GENERATED,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_DISPLAY_STATUS,
+               .mask = 0x1,
+               .shft = 6,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_SEI_AVAIL,
+               .mask = 0x1,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_FRAME_PACK_ARRGMENT_ID,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_FRAME_PACK_SEI_INFO,
+               .mask = 0x3FFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_FRAME_PACK_GRID_POS,
+               .mask = 0xFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MVC_VIEW_ID,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MVC_VIEW_ID,
+               .mask = 0xFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_CONTENT_LIGHT_LEVEL_INFO_SEI,
+               .mask = 0xFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_CONTENT_LIGHT_LEVEL_INFO_SEI,
+               .mask = 0xFFFF,
+               .shft = 16,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_VIDEO_SIGNAL_TYPE,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_FORMAT,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_VIDEO_SIGNAL_TYPE,
+               .mask = 0x7,
+               .shft = 26,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_VIDEO_SIGNAL_TYPE,
+               .mask = 0x1,
+               .shft = 25,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_VIDEO_SIGNAL_TYPE,
+               .mask = 0xFF,
+               .shft = 16,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_VIDEO_SIGNAL_TYPE,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+};
+
+static int s5p_mfc_dec_cleanup_ctx_ctrls(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+
+       while (!list_empty(&ctx->ctrls)) {
+               ctx_ctrl = list_entry((&ctx->ctrls)->next,
+                                     struct s5p_mfc_ctx_ctrl, list);
+
+               mfc_debug(7, "Cleanup context control "\
+                               "id: 0x%08x, type: %d\n",
+                               ctx_ctrl->id, ctx_ctrl->type);
+
+               list_del(&ctx_ctrl->list);
+               kfree(ctx_ctrl);
+       }
+
+       INIT_LIST_HEAD(&ctx->ctrls);
+
+       return 0;
+}
+
+static int s5p_mfc_dec_init_ctx_ctrls(struct s5p_mfc_ctx *ctx)
+{
+       unsigned long i;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+
+       INIT_LIST_HEAD(&ctx->ctrls);
+
+       for (i = 0; i < NUM_CTRL_CFGS; i++) {
+               ctx_ctrl = kzalloc(sizeof(struct s5p_mfc_ctx_ctrl), GFP_KERNEL);
+               if (ctx_ctrl == NULL) {
+                       mfc_err_dev("Failed to allocate context control "\
+                                       "id: 0x%08x, type: %d\n",
+                                       mfc_ctrl_list[i].id,
+                                       mfc_ctrl_list[i].type);
+
+                       s5p_mfc_dec_cleanup_ctx_ctrls(ctx);
+
+                       return -ENOMEM;
+               }
+
+               ctx_ctrl->type = mfc_ctrl_list[i].type;
+               ctx_ctrl->id = mfc_ctrl_list[i].id;
+               ctx_ctrl->addr = mfc_ctrl_list[i].addr;
+               ctx_ctrl->has_new = 0;
+               ctx_ctrl->val = 0;
+
+               list_add_tail(&ctx_ctrl->list, &ctx->ctrls);
+
+               mfc_debug(7, "Add context control id: 0x%08x, type : %d\n",
+                               ctx_ctrl->id, ctx_ctrl->type);
+       }
+
+       return 0;
+}
+
+static void mfc_dec_remove_buf_ctrls(struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       while (!list_empty(head)) {
+               buf_ctrl = list_entry(head->next,
+                               struct s5p_mfc_buf_ctrl, list);
+
+               mfc_debug(7, "Cleanup buffer control "\
+                               "id: 0x%08x, type: %d\n",
+                               buf_ctrl->id, buf_ctrl->type);
+
+               list_del(&buf_ctrl->list);
+               kfree(buf_ctrl);
+       }
+
+       INIT_LIST_HEAD(head);
+}
+
+static void s5p_mfc_dec_reset_buf_ctrls(struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               mfc_debug(8, "Reset buffer control value "\
+                               "id: 0x%08x, type: %d\n",
+                               buf_ctrl->id, buf_ctrl->type);
+
+               buf_ctrl->has_new = 0;
+               buf_ctrl->val = 0;
+               buf_ctrl->old_val = 0;
+               buf_ctrl->updated = 0;
+       }
+}
+
+static int s5p_mfc_dec_init_buf_ctrls(struct s5p_mfc_ctx *ctx,
+       enum s5p_mfc_ctrl_type type, unsigned int index)
+{
+       unsigned long i;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct list_head *head;
+
+       if (index >= MFC_MAX_BUFFERS) {
+               mfc_err_dev("Per-buffer control index is out of range\n");
+               return -EINVAL;
+       }
+
+       if (type & MFC_CTRL_TYPE_SRC) {
+               if (test_bit(index, &ctx->src_ctrls_avail)) {
+                       mfc_debug(7, "Source per-buffer control is already "\
+                                       "initialized [%d]\n", index);
+
+                       s5p_mfc_dec_reset_buf_ctrls(&ctx->src_ctrls[index]);
+
+                       return 0;
+               }
+
+               head = &ctx->src_ctrls[index];
+       } else if (type & MFC_CTRL_TYPE_DST) {
+               if (test_bit(index, &ctx->dst_ctrls_avail)) {
+                       mfc_debug(7, "Dest. per-buffer control is already "\
+                                       "initialized [%d]\n", index);
+
+                       s5p_mfc_dec_reset_buf_ctrls(&ctx->dst_ctrls[index]);
+
+                       return 0;
+               }
+
+               head = &ctx->dst_ctrls[index];
+       } else {
+               mfc_err_dev("Control type mismatch. type : %d\n", type);
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(head);
+
+       list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+               if (!(type & ctx_ctrl->type))
+                       continue;
+
+               /* find matched control configuration index */
+               for (i = 0; i < NUM_CTRL_CFGS; i++) {
+                       if (ctx_ctrl->id == mfc_ctrl_list[i].id)
+                               break;
+               }
+
+               if (i == NUM_CTRL_CFGS) {
+                       mfc_err_dev("Failed to find buffer control "\
+                                       "id: 0x%08x, type: %d\n",
+                                       ctx_ctrl->id, ctx_ctrl->type);
+                       continue;
+               }
+
+               buf_ctrl = kzalloc(sizeof(struct s5p_mfc_buf_ctrl), GFP_KERNEL);
+               if (buf_ctrl == NULL) {
+                       mfc_err_dev("Failed to allocate buffer control "\
+                                       "id: 0x%08x, type: %d\n",
+                                       mfc_ctrl_list[i].id,
+                                       mfc_ctrl_list[i].type);
+
+                       mfc_dec_remove_buf_ctrls(head);
+
+                       return -ENOMEM;
+               }
+
+               buf_ctrl->id = ctx_ctrl->id;
+               buf_ctrl->type = ctx_ctrl->type;
+               buf_ctrl->addr = ctx_ctrl->addr;
+
+               buf_ctrl->is_volatile = mfc_ctrl_list[i].is_volatile;
+               buf_ctrl->mode = mfc_ctrl_list[i].mode;
+               buf_ctrl->mask = mfc_ctrl_list[i].mask;
+               buf_ctrl->shft = mfc_ctrl_list[i].shft;
+               buf_ctrl->flag_mode = mfc_ctrl_list[i].flag_mode;
+               buf_ctrl->flag_addr = mfc_ctrl_list[i].flag_addr;
+               buf_ctrl->flag_shft = mfc_ctrl_list[i].flag_shft;
+
+               list_add_tail(&buf_ctrl->list, head);
+
+               mfc_debug(7, "Add buffer control id: 0x%08x, type : %d\n",\
+                               buf_ctrl->id, buf_ctrl->type);
+       }
+
+       s5p_mfc_dec_reset_buf_ctrls(head);
+
+       if (type & MFC_CTRL_TYPE_SRC)
+               set_bit(index, &ctx->src_ctrls_avail);
+       else
+               set_bit(index, &ctx->dst_ctrls_avail);
+
+       return 0;
+}
+
+static int s5p_mfc_dec_cleanup_buf_ctrls(struct s5p_mfc_ctx *ctx,
+       enum s5p_mfc_ctrl_type type, unsigned int index)
+{
+       struct list_head *head;
+
+       if (index >= MFC_MAX_BUFFERS) {
+               mfc_err_dev("Per-buffer control index is out of range\n");
+               return -EINVAL;
+       }
+
+       if (type & MFC_CTRL_TYPE_SRC) {
+               if (!(test_and_clear_bit(index, &ctx->src_ctrls_avail))) {
+                       mfc_debug(7, "Source per-buffer control is "\
+                                       "not available [%d]\n", index);
+                       return 0;
+               }
+
+               head = &ctx->src_ctrls[index];
+       } else if (type & MFC_CTRL_TYPE_DST) {
+               if (!(test_and_clear_bit(index, &ctx->dst_ctrls_avail))) {
+                       mfc_debug(7, "Dest. per-buffer Control is "\
+                                       "not available [%d]\n", index);
+                       return 0;
+               }
+
+               head = &ctx->dst_ctrls[index];
+       } else {
+               mfc_err_dev("Control type mismatch. type : %d\n", type);
+               return -EINVAL;
+       }
+
+       mfc_dec_remove_buf_ctrls(head);
+
+       return 0;
+}
+
+static int s5p_mfc_dec_to_buf_ctrls(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+               if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET) || !ctx_ctrl->has_new)
+                       continue;
+
+               list_for_each_entry(buf_ctrl, head, list) {
+                       if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET))
+                               continue;
+
+                       if (buf_ctrl->id == ctx_ctrl->id) {
+                               buf_ctrl->has_new = 1;
+                               buf_ctrl->val = ctx_ctrl->val;
+                               if (buf_ctrl->is_volatile)
+                                       buf_ctrl->updated = 0;
+
+                               ctx_ctrl->has_new = 0;
+                               break;
+                       }
+               }
+       }
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (buf_ctrl->has_new)
+                       mfc_debug(8, "Updated buffer control "\
+                                       "id: 0x%08x val: %d\n",
+                                       buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_to_ctx_ctrls(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET) || !buf_ctrl->has_new)
+                       continue;
+
+               list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+                       if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET))
+                               continue;
+
+                       if (ctx_ctrl->id == buf_ctrl->id) {
+                               if (ctx_ctrl->has_new)
+                                       mfc_debug(8,
+                                       "Overwrite context control "\
+                                       "value id: 0x%08x, val: %d\n",
+                                               ctx_ctrl->id, ctx_ctrl->val);
+
+                               ctx_ctrl->has_new = 1;
+                               ctx_ctrl->val = buf_ctrl->val;
+
+                               buf_ctrl->has_new = 0;
+                       }
+               }
+       }
+
+       list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+               if (ctx_ctrl->has_new)
+                       mfc_debug(8, "Updated context control "\
+                                       "id: 0x%08x val: %d\n",
+                                       ctx_ctrl->id, ctx_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_set_buf_ctrls_val(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int value = 0;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || !buf_ctrl->has_new)
+                       continue;
+
+               /* read old vlaue */
+               value = MFC_READL(buf_ctrl->addr);
+
+               /* save old vlaue for recovery */
+               if (buf_ctrl->is_volatile)
+                       buf_ctrl->old_val = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               /* write new value */
+               value &= ~(buf_ctrl->mask << buf_ctrl->shft);
+               value |= ((buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft);
+               MFC_WRITEL(value, buf_ctrl->addr);
+
+               /* set change flag bit */
+               if (buf_ctrl->flag_mode == MFC_CTRL_MODE_SFR) {
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value |= (1 << buf_ctrl->flag_shft);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+               }
+
+               buf_ctrl->has_new = 0;
+               buf_ctrl->updated = 1;
+
+               if (buf_ctrl->id == V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG)
+                       dec->stored_tag = buf_ctrl->val;
+
+               mfc_debug(8, "Set buffer control "\
+                               "id: 0x%08x val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_get_buf_ctrls_val(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int value = 0;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET))
+                       continue;
+
+               value = MFC_READL(buf_ctrl->addr);
+               value = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               buf_ctrl->val = value;
+               buf_ctrl->has_new = 1;
+
+               if (IS_VP9_DEC(ctx)) {
+                       if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG)
+                               buf_ctrl->val = ctx->color_range;
+                       else if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES)
+                               buf_ctrl->val = ctx->color_space;
+               }
+               mfc_debug(8, "Get buffer control "\
+                               "id: 0x%08x val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_set_buf_ctrls_val_nal_q_dec(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, DecoderInputStr *pInStr)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+
+       mfc_debug_enter();
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || !buf_ctrl->has_new)
+                       continue;
+               switch (buf_ctrl->id) {
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+                       pInStr->PictureTag &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->PictureTag |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       dec->stored_tag = buf_ctrl->val;
+                       break;
+               /* If new dynamic controls are added, insert here */
+               default:
+                       mfc_info_ctx("NAL Q: can't find control, id: 0x%x\n",
+                                       buf_ctrl->id);
+               }
+               buf_ctrl->has_new = 0;
+               buf_ctrl->updated = 1;
+
+               mfc_debug(3, "NAL Q: dec_set_buf_ctrls, ctrl id: 0x%x, val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_dec_get_buf_ctrls_val_nal_q_dec(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       unsigned int value = 0;
+
+       mfc_debug_enter();
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET))
+                       continue;
+               switch (buf_ctrl->id) {
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+                       value = pOutStr->PictureTagTop;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS:
+                       value = pOutStr->DisplayStatus;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA:
+                       value = pOutStr->DisplayFirstCrc;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA:
+                       value = pOutStr->DisplaySecondCrc;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA1:
+                       value = pOutStr->DisplayThirdCrc;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_LUMA:
+                       value = pOutStr->DisplayFirst2BitCrc;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_CHROMA:
+                       value = pOutStr->DisplaySecond2BitCrc;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CRC_GENERATED:
+                       value = pOutStr->DisplayStatus;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL:
+                       value = pOutStr->SeiAvail;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID:
+                       value = pOutStr->FramePackArrgmentId;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO:
+                       value = pOutStr->FramePackSeiInfo;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS:
+                       value = pOutStr->FramePackGridPos;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_MVC_VIEW_ID:
+                       value = 0;
+                       break;
+               /* TO-DO: SEI information has to be added in NAL-Q */
+               case V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT:
+               case V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT:
+                       value = pOutStr->ContentLightLevelInfoSei;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE:
+                       value = pOutStr->MasteringDisplayColourVolumeSei0;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE:
+                       value = pOutStr->MasteringDisplayColourVolumeSei1;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG:
+                       if (IS_VP9_DEC(ctx)) {
+                               buf_ctrl->val = ctx->color_range;
+                               buf_ctrl->has_new = 1;
+                               continue;
+                       }
+               case V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES:
+                       if (IS_VP9_DEC(ctx)) {
+                               buf_ctrl->val = ctx->color_space;
+                               buf_ctrl->has_new = 1;
+                               continue;
+                       }
+               case V4L2_CID_MPEG_VIDEO_FORMAT:
+               case V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS:
+               case V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS:
+                       value = pOutStr->VideoSignalType;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT:
+                       value = pOutStr->MasteringDisplayColourVolumeSei2;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0:
+                       value = pOutStr->MasteringDisplayColourVolumeSei3;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1:
+                       value = pOutStr->MasteringDisplayColourVolumeSei4;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2:
+                       value = pOutStr->MasteringDisplayColourVolumeSei5;
+                       break;
+                       /* If new dynamic controls are added, insert here */
+               default:
+                       mfc_info_ctx("NAL Q: can't find control, id: 0x%x\n",
+                                       buf_ctrl->id);
+               }
+               value = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               buf_ctrl->val = value;
+               buf_ctrl->has_new = 1;
+
+               mfc_debug(3, "NAL Q: dec_get_buf_ctrls, ctrl id: 0x%x, val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_dec_recover_buf_ctrls_val(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int value = 0;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET)
+                       || !buf_ctrl->is_volatile
+                       || !buf_ctrl->updated)
+                       continue;
+
+               value = MFC_READL(buf_ctrl->addr);
+               value &= ~(buf_ctrl->mask << buf_ctrl->shft);
+               value |= ((buf_ctrl->old_val & buf_ctrl->mask) << buf_ctrl->shft);
+               MFC_WRITEL(value, buf_ctrl->addr);
+
+               /* clear change flag bit */
+               if (buf_ctrl->flag_mode == MFC_CTRL_MODE_SFR) {
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value &= ~(1 << buf_ctrl->flag_shft);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+               }
+
+               buf_ctrl->updated = 0;
+
+               mfc_debug(8, "Recover buffer control "\
+                               "id: 0x%08x old val: %d\n",
+                               buf_ctrl->id, buf_ctrl->old_val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_get_buf_update_val(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, unsigned int id, int value)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if ((buf_ctrl->id == id)) {
+                       buf_ctrl->val = value;
+                       mfc_debug(5, "++id: 0x%08x val: %d\n",
+                                       buf_ctrl->id, buf_ctrl->val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_dec_recover_buf_ctrls_nal_q(struct s5p_mfc_ctx *ctx,
+               struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET)
+                               || !(buf_ctrl->updated))
+                       continue;
+
+               buf_ctrl->has_new = 1;
+               buf_ctrl->updated = 0;
+               mfc_debug(5, "recover has_new, id: 0x%08x val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+struct s5p_mfc_ctrls_ops decoder_ctrls_ops = {
+       .init_ctx_ctrls                 = s5p_mfc_dec_init_ctx_ctrls,
+       .cleanup_ctx_ctrls              = s5p_mfc_dec_cleanup_ctx_ctrls,
+       .init_buf_ctrls                 = s5p_mfc_dec_init_buf_ctrls,
+       .reset_buf_ctrls                = s5p_mfc_dec_reset_buf_ctrls,
+       .cleanup_buf_ctrls              = s5p_mfc_dec_cleanup_buf_ctrls,
+       .to_buf_ctrls                   = s5p_mfc_dec_to_buf_ctrls,
+       .to_ctx_ctrls                   = s5p_mfc_dec_to_ctx_ctrls,
+       .set_buf_ctrls_val              = s5p_mfc_dec_set_buf_ctrls_val,
+       .get_buf_ctrls_val              = s5p_mfc_dec_get_buf_ctrls_val,
+       .set_buf_ctrls_val_nal_q_dec    = s5p_mfc_dec_set_buf_ctrls_val_nal_q_dec,
+       .get_buf_ctrls_val_nal_q_dec    = s5p_mfc_dec_get_buf_ctrls_val_nal_q_dec,
+       .recover_buf_ctrls_val          = s5p_mfc_dec_recover_buf_ctrls_val,
+       .get_buf_update_val             = s5p_mfc_dec_get_buf_update_val,
+       .recover_buf_ctrls_nal_q        = s5p_mfc_dec_recover_buf_ctrls_nal_q,
+};
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_dec_vb2_ops.c b/drivers/media/platform/exynos/mfc/s5p_mfc_dec_vb2_ops.c
new file mode 100644 (file)
index 0000000..7932a97
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_dec_vb2_ops.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  "s5p_mfc_common.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+static int s5p_mfc_dec_queue_setup(struct vb2_queue *vq,
+                               unsigned int *buf_count, unsigned int *plane_count,
+                               unsigned int psize[], struct device *alloc_devs[])
+{
+       struct s5p_mfc_ctx *ctx;
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_raw_info *raw;
+       int i;
+
+       mfc_debug_enter();
+
+       if (!vq) {
+               mfc_err_dev("no vb2_queue info\n");
+               return -EINVAL;
+       }
+
+       ctx = vq->drv_priv;
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       raw = &ctx->raw_buf;
+
+       /* Video output for decoding (source)
+        * this can be set after getting an instance */
+       if (ctx->state == MFCINST_GOT_INST &&
+           vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mfc_debug(2, "setting for VIDEO output\n");
+               /* A single plane is required for input */
+               *plane_count = 1;
+               if (*buf_count < 1)
+                       *buf_count = 1;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               psize[0] = dec->src_buf_size;
+               alloc_devs[0] = dev->device;
+       /* Video capture for decoding (destination)
+        * this can be set after the header was parsed */
+       } else if (ctx->state == MFCINST_HEAD_PARSED &&
+                  vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               mfc_debug(2, "setting for VIDEO capture\n");
+               /* Output plane count is different by the pixel format */
+               *plane_count = ctx->dst_fmt->mem_planes;
+               /* Setup buffer count */
+               if (*buf_count < ctx->dpb_count)
+                       *buf_count = ctx->dpb_count;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               if (ctx->dst_fmt->mem_planes == 1) {
+                       psize[0] = raw->total_plane_size;
+                       alloc_devs[0] = dev->device;
+               } else {
+                       for (i = 0; i < ctx->dst_fmt->num_planes; i++) {
+                               psize[i] = raw->plane_size[i];
+                               alloc_devs[i] = dev->device;
+                       }
+               }
+       } else {
+               mfc_err_ctx("State seems invalid. State = %d, vq->type = %d\n",
+                                                       ctx->state, vq->type);
+               return -EINVAL;
+       }
+       mfc_debug(2, "buffer count=%d, plane count=%d type=0x%x\n",
+                                       *buf_count, *plane_count, vq->type);
+
+       mfc_debug(2, "plane=0, size=%d\n", psize[0]);
+       mfc_debug(2, "plane=1, size=%d\n", psize[1]);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void s5p_mfc_dec_unlock(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_dec_lock(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_dec_buf_init(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *buf = vb_to_mfc_buf(vb);
+       dma_addr_t start_raw;
+       int i, ret;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->dst_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               start_raw = s5p_mfc_mem_get_daddr_vb(vb, 0);
+               if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12N) {
+                       buf->planes.raw[0] = start_raw;
+                       buf->planes.raw[1] = NV12N_CBCR_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+               } else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12N_10B) {
+                       buf->planes.raw[0] = start_raw;
+                       buf->planes.raw[1] = NV12N_10B_CBCR_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+               } else if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_YUV420N) {
+                       buf->planes.raw[0] = start_raw;
+                       buf->planes.raw[1] = YUV420N_CB_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+                       buf->planes.raw[2] = YUV420N_CR_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+               } else {
+                       for (i = 0; i < ctx->dst_fmt->mem_planes; i++)
+                               buf->planes.raw[i] = s5p_mfc_mem_get_daddr_vb(vb, i);
+               }
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_DST,
+                                       vb->index) < 0)
+                       mfc_err_ctx("failed in init_buf_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->src_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               buf->planes.stream = s5p_mfc_mem_get_daddr_vb(vb, 0);
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC,
+                                       vb->index) < 0)
+                       mfc_err_ctx("failed in init_buf_ctrls\n");
+       } else {
+               mfc_err_ctx("s5p_mfc_dec_buf_init: unknown queue type.\n");
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_dec_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_raw_info *raw;
+       unsigned int index = vb->index;
+       int i;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               raw = &ctx->raw_buf;
+               /* check the size per plane */
+               if (ctx->dst_fmt->mem_planes == 1) {
+                       mfc_debug(2, "Plane size = %lu, dst size:%d\n",
+                                       vb2_plane_size(vb, 0),
+                                       raw->total_plane_size);
+                       if (vb2_plane_size(vb, 0) < raw->total_plane_size) {
+                               mfc_err_ctx("Capture plane is too small\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       for (i = 0; i < ctx->dst_fmt->mem_planes; i++) {
+                               mfc_debug(2, "Plane[%d] size: %lu, dst[%d] size: %d\n",
+                                               i, vb2_plane_size(vb, i),
+                                               i, raw->plane_size[i]);
+                               if (vb2_plane_size(vb, i) < raw->plane_size[i]) {
+                                       mfc_err_ctx("Capture plane[%d] is too small\n", i);
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mfc_debug(2, "Plane size: %lu, dec->src_buf_size: %u\n",
+                               vb2_plane_size(vb, 0), dec->src_buf_size);
+
+               if (vb2_plane_size(vb, 0) < dec->src_buf_size) {
+                       mfc_err_ctx("Plane buffer (OUTPUT) is too small.\n");
+                       return -EINVAL;
+               }
+
+               if (call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_buf_ctrls\n");
+       }
+
+       s5p_mfc_mem_buf_prepare(vb);
+
+       return 0;
+}
+
+static void s5p_mfc_dec_buf_finish(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       unsigned int index = vb->index;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (call_cop(ctx, to_ctx_ctrls, ctx, &ctx->dst_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_ctx_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (call_cop(ctx, to_ctx_ctrls, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_ctx_ctrls\n");
+       }
+
+       s5p_mfc_mem_buf_finish(vb);
+}
+
+static void s5p_mfc_dec_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       unsigned int index = vb->index;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                                       MFC_CTRL_TYPE_DST, index) < 0)
+                       mfc_err_ctx("failed in cleanup_buf_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                                       MFC_CTRL_TYPE_SRC, index) < 0)
+                       mfc_err_ctx("failed in cleanup_buf_ctrls\n");
+       } else {
+               mfc_err_ctx("s5p_mfc_dec_buf_cleanup: unknown queue type.\n");
+       }
+
+       mfc_debug_leave();
+}
+
+static int s5p_mfc_dec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->state == MFCINST_FINISHING)
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+
+       /* If context is ready then dev = work->data;schedule it to run */
+       if (s5p_mfc_dec_ctx_ready(ctx)) {
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       }
+
+       s5p_mfc_try_run(dev);
+
+       return 0;
+}
+
+static void mfc_dec_src_stop_streaming(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *src_mb;
+       int index, csd, condition = 0;
+       int ret = 0;
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       while (1) {
+               csd = s5p_mfc_peek_buf_csd(&ctx->buf_queue_lock, &ctx->src_buf_queue);
+
+               if (FW_HAS_SPECIAL_PARSING(dev) && (csd == 1)) {
+                       s5p_mfc_clean_ctx_int_flags(ctx);
+                       if (need_to_special_parsing(ctx)) {
+                               s5p_mfc_change_state(ctx, MFCINST_SPECIAL_PARSING);
+                               condition = S5P_FIMV_R2H_CMD_SEQ_DONE_RET;
+                               mfc_info_ctx("try to special parsing! (before NAL_START)\n");
+                       } else if (need_to_special_parsing_nal(ctx)) {
+                               s5p_mfc_change_state(ctx, MFCINST_SPECIAL_PARSING_NAL);
+                               condition = S5P_FIMV_R2H_CMD_FRAME_DONE_RET;
+                               mfc_info_ctx("try to special parsing! (after NAL_START)\n");
+                       } else {
+                               mfc_info_ctx("can't parsing CSD!, state = %d\n", ctx->state);
+                       }
+
+                       if (condition) {
+                               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+
+                               ret = s5p_mfc_just_run(dev, ctx->num);
+                               if (ret) {
+                                       mfc_err_ctx("Failed to run MFC.\n");
+                               } else {
+                                       if (s5p_mfc_wait_for_done_ctx(ctx, condition))
+                                               mfc_err_ctx("special parsing time out\n");
+                               }
+                       }
+               }
+
+               src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!src_mb)
+                       break;
+
+               index = src_mb->vb.vb2_buf.index;
+
+               if (ctx->is_drm)
+                       s5p_mfc_stream_unprotect(ctx, src_mb, index);
+               vb2_set_plane_payload(&src_mb->vb.vb2_buf, 0, 0);
+               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+       }
+
+       dec->consumed = 0;
+       dec->remained_size = 0;
+
+       s5p_mfc_init_queue(&ctx->src_buf_queue);
+
+       index = 0;
+       while (index < MFC_MAX_BUFFERS) {
+               index = find_next_bit(&ctx->src_ctrls_avail,
+                               MFC_MAX_BUFFERS, index);
+               if (index < MFC_MAX_BUFFERS)
+                       call_cop(ctx, reset_buf_ctrls, &ctx->src_ctrls[index]);
+               index++;
+       }
+}
+
+static void mfc_dec_dst_stop_streaming(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       int index = 0;
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       s5p_mfc_cleanup_assigned_fd(ctx);
+       s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->ref_buf_queue);
+
+       dec->dynamic_used = 0;
+       dec->err_reuse_flag = 0;
+
+       s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue);
+
+       ctx->is_dpb_realloc = 0;
+       dec->available_dpb = 0;
+
+       dec->y_addr_for_pb = 0;
+
+       s5p_mfc_cleanup_assigned_dpb(ctx);
+
+       while (index < MFC_MAX_BUFFERS) {
+               index = find_next_bit(&ctx->dst_ctrls_avail,
+                               MFC_MAX_BUFFERS, index);
+               if (index < MFC_MAX_BUFFERS)
+                       call_cop(ctx, reset_buf_ctrls, &ctx->dst_ctrls[index]);
+               index++;
+       }
+
+       if (ctx->wait_state == WAIT_INITBUF_DONE ||
+                       ctx->wait_state == WAIT_DECODING) {
+               ctx->wait_state = WAIT_NONE;
+               mfc_debug(2, "Decoding can be started now\n");
+       }
+}
+
+static void s5p_mfc_dec_stop_streaming(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev;
+       int ret = 0;
+       int prev_state;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       mfc_info_ctx("dec stop_streaming is called, hwlock : %d, type : %d\n",
+                               test_bit(ctx->num, &dev->hwlock.bits), q->type);
+       MFC_TRACE_CTX("** DEC streamoff(type:%d)\n", q->type);
+
+       MFC_TRACE_CTX_HWLOCK("**DEC streamoff(type:%d)\n", q->type);
+       /* If a H/W operation is in progress, wait for it complete */
+       ret = s5p_mfc_get_hwlock_ctx(ctx);
+       if (ret < 0) {
+               mfc_err_ctx("Failed to get hwlock.\n");
+               return;
+       }
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+               mfc_dec_dst_stop_streaming(ctx);
+       else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               mfc_dec_src_stop_streaming(ctx);
+
+       if (ctx->state == MFCINST_FINISHING)
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && need_to_dpb_flush(ctx)) {
+               prev_state = ctx->state;
+               s5p_mfc_change_state(ctx, MFCINST_DPB_FLUSHING);
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+               s5p_mfc_clean_ctx_int_flags(ctx);
+               mfc_info_ctx("try to DPB flush\n");
+               ret = s5p_mfc_just_run(dev, ctx->num);
+               if (ret) {
+                       mfc_err_ctx("Failed to run MFC.\n");
+                       s5p_mfc_release_hwlock_ctx(ctx);
+                       s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+                       return;
+               }
+
+               if (s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_DPB_FLUSH_RET)) {
+                       mfc_err_ctx("time out during DPB flush\n");
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_DPB_FLUSH);
+                       s5p_mfc_dump_info_and_stop_hw(dev);
+               }
+
+               s5p_mfc_change_state(ctx, prev_state);
+       }
+
+       mfc_debug(2, "buffer cleanup & flush is done in stop_streaming, type : %d\n", q->type);
+
+       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+       s5p_mfc_release_hwlock_ctx(ctx);
+
+       if (s5p_mfc_dec_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+}
+
+static void s5p_mfc_dec_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *buf = vb_to_mfc_buf(vb);
+       int index, i;
+       unsigned char *stream_vir = NULL;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mfc_debug(2, "Src queue: %p\n", &ctx->src_buf_queue);
+               mfc_debug(2, "Adding to src: %p (0x%08llx, 0x%08llx)\n", vb,
+                               s5p_mfc_mem_get_daddr_vb(vb, 0),
+                               buf->planes.stream);
+               if (dec->dst_memtype == V4L2_MEMORY_DMABUF &&
+                               ctx->state < MFCINST_HEAD_PARSED && !ctx->is_drm) {
+                       stream_vir = vb2_plane_vaddr(vb, 0);
+                       s5p_mfc_mem_inv_vb(vb, 1);
+               }
+               buf->vir_addr = stream_vir;
+
+               s5p_mfc_add_tail_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, buf);
+
+               MFC_TRACE_CTX("Q src[%d] fd: %d, %#llx\n",
+                               vb->index, vb->planes[0].m.fd, buf->planes.stream);
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               index = vb->index;
+               mfc_debug(2, "Dst queue: %p\n", &ctx->dst_buf_queue);
+               mfc_debug(2, "Adding to dst: %p (0x%08llx)\n", vb,
+                               s5p_mfc_mem_get_daddr_vb(vb, 0));
+               for (i = 0; i < ctx->dst_fmt->num_planes; i++)
+                       mfc_debug(2, "dec dst plane[%d]: %08llx\n",
+                                       i, buf->planes.raw[i]);
+               s5p_mfc_store_dpb(ctx, vb);
+
+               if ((dec->dst_memtype == V4L2_MEMORY_USERPTR || dec->dst_memtype == V4L2_MEMORY_DMABUF) &&
+                               s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock,
+                                       &ctx->dst_buf_queue, dec->total_dpb_count))
+                       ctx->capture_state = QUEUE_BUFS_MMAPED;
+
+               MFC_TRACE_CTX("Q dst[%d] fd: %d, %#llx / avail %#lx used %#x\n",
+                               index, vb->planes[0].m.fd, buf->planes.raw[0],
+                               dec->available_dpb, dec->dynamic_used);
+       } else {
+               mfc_err_ctx("Unsupported buffer type (%d)\n", vq->type);
+       }
+
+       if (s5p_mfc_dec_ctx_ready(ctx)) {
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+               s5p_mfc_try_run(dev);
+       }
+
+       mfc_debug_leave();
+}
+
+struct vb2_ops s5p_mfc_dec_qops = {
+       .queue_setup            = s5p_mfc_dec_queue_setup,
+       .wait_prepare           = s5p_mfc_dec_unlock,
+       .wait_finish            = s5p_mfc_dec_lock,
+       .buf_init               = s5p_mfc_dec_buf_init,
+       .buf_prepare            = s5p_mfc_dec_buf_prepare,
+       .buf_finish             = s5p_mfc_dec_buf_finish,
+       .buf_cleanup            = s5p_mfc_dec_buf_cleanup,
+       .start_streaming        = s5p_mfc_dec_start_streaming,
+       .stop_streaming         = s5p_mfc_dec_stop_streaming,
+       .buf_queue              = s5p_mfc_dec_buf_queue,
+};
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc.c b/drivers/media/platform/exynos/mfc/s5p_mfc_enc.c
new file mode 100644 (file)
index 0000000..c1728c2
--- /dev/null
@@ -0,0 +1,1877 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc.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 "s5p_mfc_enc.h"
+#include "s5p_mfc_enc_internal.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_otf.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+static struct s5p_mfc_fmt *mfc_enc_find_format(struct v4l2_format *f, unsigned int t)
+{
+       unsigned long i;
+
+       for (i = 0; i < NUM_FORMATS; i++) {
+               if (enc_formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+                   enc_formats[i].type == t)
+                       return (struct s5p_mfc_fmt *)&enc_formats[i];
+       }
+
+       return NULL;
+}
+
+static struct v4l2_queryctrl *mfc_enc_get_ctrl(int id)
+{
+       unsigned long i;
+
+       for (i = 0; i < NUM_CTRLS; ++i)
+               if (id == controls[i].id)
+                       return &controls[i];
+       return NULL;
+}
+
+static int mfc_enc_check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct v4l2_queryctrl *c;
+
+       c = mfc_enc_get_ctrl(ctrl->id);
+       if (!c)
+               return -EINVAL;
+
+       if (ctrl->id == V4L2_CID_MPEG_VIDEO_GOP_SIZE
+           && ctrl->value > c->maximum) {
+               mfc_info_ctx("GOP_SIZE is changed to max(%d -> %d)\n",
+                                ctrl->value, c->maximum);
+               ctrl->value = c->maximum;
+       }
+
+       if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER) {
+               if ((ctrl->value & ~(1 << 16)) < c->minimum || (ctrl->value & ~(1 << 16)) > c->maximum
+                   || (c->step != 0 && (ctrl->value & ~(1 << 16)) % c->step != 0)) {
+                       mfc_err_ctx("Invalid control value for %#x (%#x)\n", ctrl->id, ctrl->value);
+                       return -ERANGE;
+               } else {
+                       return 0;
+               }
+       }
+
+       if (ctrl->value < c->minimum || ctrl->value > c->maximum
+           || (c->step != 0 && ctrl->value % c->step != 0)) {
+               mfc_err_ctx("Invalid control value for %#x (%#x)\n", ctrl->id, ctrl->value);
+               return -ERANGE;
+       }
+
+       if (!FW_HAS_ROI_CONTROL(dev) && ctrl->id == \
+                       V4L2_CID_MPEG_VIDEO_ROI_CONTROL) {
+               mfc_err_ctx("Not support feature(0x%x) for F/W\n", ctrl->id);
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+static inline int mfc_enc_h264_profile(struct s5p_mfc_ctx *ctx, int profile)
+{
+       int ret = 0;
+
+       switch (profile) {
+       case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+               ret = S5P_FIMV_E_PROFILE_H264_MAIN;
+               break;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+               ret = S5P_FIMV_E_PROFILE_H264_HIGH;
+               break;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+               ret = S5P_FIMV_E_PROFILE_H264_BASELINE;
+               break;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+               ret = S5P_FIMV_E_PROFILE_H264_CONSTRAINED_BASELINE;
+               break;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH:
+               ret = S5P_FIMV_E_PROFILE_H264_CONSTRAINED_HIGH;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+                          struct v4l2_capability *cap)
+{
+       strncpy(cap->driver, "MFC", sizeof(cap->driver) - 1);
+       strncpy(cap->card, "encoder", sizeof(cap->card) - 1);
+       cap->bus_info[0] = 0;
+       cap->version = KERNEL_VERSION(1, 0, 0);
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
+                       | V4L2_CAP_VIDEO_OUTPUT
+                       | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+                       | V4L2_CAP_VIDEO_OUTPUT_MPLANE
+                       | V4L2_CAP_STREAMING
+                       | V4L2_CAP_DEVICE_CAPS;
+
+       cap->capabilities = cap->device_caps;
+
+       return 0;
+}
+
+static int mfc_enc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+       struct s5p_mfc_fmt *fmt;
+       unsigned long i;
+       int j = 0;
+
+       for (i = 0; i < NUM_FORMATS; ++i) {
+               if (mplane && enc_formats[i].mem_planes == 1)
+                       continue;
+               else if (!mplane && enc_formats[i].mem_planes > 1)
+                       continue;
+               if (out && enc_formats[i].type != MFC_FMT_RAW)
+                       continue;
+               else if (!out && enc_formats[i].type != MFC_FMT_ENC)
+                       continue;
+
+               if (j == f->index) {
+                       fmt = &enc_formats[i];
+                       strlcpy(f->description, fmt->name,
+                               sizeof(f->description));
+                       f->pixelformat = fmt->fourcc;
+
+                       return 0;
+               }
+
+               ++j;
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+                                  struct v4l2_fmtdesc *f)
+{
+       return mfc_enc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+                                         struct v4l2_fmtdesc *f)
+{
+       return mfc_enc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+                                  struct v4l2_fmtdesc *f)
+{
+       return mfc_enc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+                                         struct v4l2_fmtdesc *f)
+{
+       return mfc_enc_enum_fmt(f, true, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       struct s5p_mfc_raw_info *raw;
+       int i;
+
+       mfc_debug_enter();
+
+       mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+
+       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               /* This is run on output (encoder dest) */
+               pix_fmt_mp->width = 0;
+               pix_fmt_mp->height = 0;
+               pix_fmt_mp->field = V4L2_FIELD_NONE;
+               pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+               pix_fmt_mp->num_planes = ctx->dst_fmt->mem_planes;
+
+               pix_fmt_mp->plane_fmt[0].bytesperline = enc->dst_buf_size;
+               pix_fmt_mp->plane_fmt[0].sizeimage = enc->dst_buf_size;
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               /* This is run on capture (encoder src) */
+               raw = &ctx->raw_buf;
+
+               pix_fmt_mp->width = ctx->img_width;
+               pix_fmt_mp->height = ctx->img_height;
+               pix_fmt_mp->field = V4L2_FIELD_NONE;
+               pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+               pix_fmt_mp->num_planes = ctx->src_fmt->mem_planes;
+               for (i = 0; i < ctx->src_fmt->mem_planes; i++) {
+                       pix_fmt_mp->plane_fmt[i].bytesperline = raw->stride[i];
+                       pix_fmt_mp->plane_fmt[i].sizeimage = raw->plane_size[i];
+               }
+       } else {
+               mfc_err_dev("invalid buf type (%d)\n", f->type);
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct s5p_mfc_fmt *fmt;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               fmt = mfc_enc_find_format(f, MFC_FMT_ENC);
+               if (!fmt) {
+                       mfc_err_dev("failed to try capture format\n");
+                       return -EINVAL;
+               }
+
+               if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+                       mfc_err_dev("must be set encoding dst size\n");
+                       return -EINVAL;
+               }
+
+               pix_fmt_mp->plane_fmt[0].bytesperline =
+                       pix_fmt_mp->plane_fmt[0].sizeimage;
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               fmt = mfc_enc_find_format(f, MFC_FMT_RAW);
+               if (!fmt) {
+                       mfc_err_dev("failed to try output format\n");
+                       return -EINVAL;
+               }
+
+               if (fmt->mem_planes != pix_fmt_mp->num_planes) {
+                       mfc_err_dev("plane number is different (%d != %d)\n",
+                               fmt->mem_planes, pix_fmt_mp->num_planes);
+                       return -EINVAL;
+               }
+       } else {
+               mfc_err_dev("invalid buf type (%d)\n", f->type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void mfc_enc_check_format(struct s5p_mfc_ctx *ctx)
+{
+       switch (ctx->src_fmt->fourcc) {
+       case V4L2_PIX_FMT_NV16M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+       case V4L2_PIX_FMT_NV16M_P210:
+       case V4L2_PIX_FMT_NV61M_P210:
+               mfc_debug(2, "is 422 and 10bit format\n");
+               ctx->is_10bit = 1;
+               ctx->is_422format = 1;
+               break;
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               mfc_debug(2, "is 422 format\n");
+               ctx->is_10bit = 0;
+               ctx->is_422format = 1;
+               break;
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV21M_S10B:
+       case V4L2_PIX_FMT_NV21M_P010:
+               mfc_debug(2, "is 10bit format\n");
+               ctx->is_10bit = 1;
+               ctx->is_422format = 0;
+               break;
+       default:
+               ctx->is_10bit = 0;
+               ctx->is_422format = 0;
+               break;
+       }
+       mfc_debug(2, "10bit: %d, 422: %d\n", ctx->is_10bit, ctx->is_422format);
+}
+
+static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                                       struct v4l2_format *f)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_fmt *fmt;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       ret = vidioc_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       if (ctx->vq_dst.streaming) {
+               mfc_err_ctx("dst queue busy\n");
+               return -EBUSY;
+       }
+
+       fmt = mfc_enc_find_format(f, MFC_FMT_ENC);
+       if (!fmt) {
+               mfc_err_ctx("failed to set capture format\n");
+               return -EINVAL;
+       }
+
+       ctx->dst_fmt = fmt;
+       ctx->codec_mode = ctx->dst_fmt->codec_mode;
+       mfc_info_ctx("Enc output codec(%d) : %s\n",
+                       ctx->dst_fmt->codec_mode, ctx->dst_fmt->name);
+
+       if (ctx->otf_handle && ctx->dst_fmt->fourcc != V4L2_PIX_FMT_H264 &&
+                       ctx->dst_fmt->fourcc != V4L2_PIX_FMT_HEVC) {
+               mfc_err_ctx("OTF: only H.264 and HEVC is supported\n");
+               return -EINVAL;
+       }
+
+       enc->dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
+       pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+
+       ret = s5p_mfc_alloc_instance_context(ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to allocate enc instance[%d] buffers.\n",
+                               ctx->num);
+               return -ENOMEM;
+       }
+
+       s5p_mfc_change_state(ctx, MFCINST_INIT);
+
+       ctx->capture_state = QUEUE_FREE;
+
+       if (FW_HAS_ROI_CONTROL(dev)) {
+               ret = s5p_mfc_alloc_enc_roi_buffer(ctx);
+               if (ret) {
+                       mfc_err_ctx("Failed to allocate ROI buffers.\n");
+                       s5p_mfc_release_instance_context(ctx);
+                       return -ENOMEM;
+               }
+       }
+       MFC_TRACE_CTX_HWLOCK("**ENC s_fmt\n");
+
+       ret = s5p_mfc_get_hwlock_ctx(ctx);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get hwlock.\n");
+               s5p_mfc_release_instance_context(ctx);
+               s5p_mfc_release_enc_roi_buffer(ctx);
+               return -EBUSY;
+       }
+
+       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       ret = s5p_mfc_just_run(dev, ctx->num);
+       if (ret) {
+               mfc_err_ctx("Failed to run MFC.\n");
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               s5p_mfc_release_instance_context(ctx);
+               s5p_mfc_release_enc_roi_buffer(ctx);
+               return -EIO;
+       }
+
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET)) {
+               mfc_err_ctx("time out during open instance\n");
+               s5p_mfc_release_hwlock_ctx(ctx);
+               s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               s5p_mfc_release_instance_context(ctx);
+               s5p_mfc_release_enc_roi_buffer(ctx);
+               return -EIO;
+       }
+       s5p_mfc_release_hwlock_ctx(ctx);
+
+       mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+
+       if (s5p_mfc_enc_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (ctx->otf_handle && s5p_mfc_otf_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       mfc_debug_leave();
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,
+                                                       struct v4l2_format *f)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       struct s5p_mfc_fmt *fmt;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       ret = vidioc_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       if (ctx->vq_src.streaming) {
+               mfc_err_ctx("src queue busy\n");
+               return -EBUSY;
+       }
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip source s_fmt\n");
+               return 0;
+       }
+
+       fmt = mfc_enc_find_format(f, MFC_FMT_RAW);
+       if (!fmt) {
+               mfc_err_ctx("failed to set output format\n");
+               return -EINVAL;
+       }
+       if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+               mfc_err_ctx("Not supported format: NV12MT\n");
+               return -EINVAL;
+       } else if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
+               mfc_err_ctx("Not supported format: NV12MT_16X16\n");
+               return -EINVAL;
+       }
+
+       if (fmt->mem_planes != pix_fmt_mp->num_planes) {
+               mfc_err_ctx("plane number is different (%d != %d)\n",
+                               fmt->mem_planes, pix_fmt_mp->num_planes);
+               return -EINVAL;
+       }
+
+       ctx->src_fmt = fmt;
+       ctx->raw_buf.num_planes = ctx->src_fmt->num_planes;
+       ctx->img_width = pix_fmt_mp->width;
+       ctx->img_height = pix_fmt_mp->height;
+       ctx->buf_stride = pix_fmt_mp->plane_fmt[0].bytesperline;
+
+       mfc_enc_check_format(ctx);
+
+       if ((ctx->state == MFCINST_RUNNING)
+               && (((ctx->old_img_width != 0) && (ctx->old_img_width != ctx->img_width))
+               || ((ctx->old_img_height != 0) && (ctx->old_img_height != ctx->img_height)))) {
+               ctx->enc_drc_flag = 1;
+       }
+
+       mfc_info_ctx("Enc input pixelformat : %s\n", ctx->src_fmt->name);
+       mfc_info_ctx("fmt - w: %d, h: %d, stride: %d\n",
+                       pix_fmt_mp->width, pix_fmt_mp->height, ctx->buf_stride);
+
+       s5p_mfc_enc_calc_src_size(ctx);
+
+       ctx->output_state = QUEUE_FREE;
+
+       mfc_debug_leave();
+       return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+                                         struct v4l2_requestbuffers *reqbufs)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       mfc_debug(2, "type: %d\n", reqbufs->memory);
+
+       if (reqbufs->memory == V4L2_MEMORY_MMAP) {
+               mfc_err_ctx("Not supported memory type (%d).\n", reqbufs->memory);
+               return -EINVAL;
+       }
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip reqbufs\n");
+               return 0;
+       }
+
+       if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (reqbufs->count == 0) {
+                       mfc_debug(2, "Freeing buffers.\n");
+                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       ctx->capture_state = QUEUE_FREE;
+                       return ret;
+               }
+
+               if (ctx->capture_state != QUEUE_FREE) {
+                       mfc_err_ctx("invalid capture state: %d\n", ctx->capture_state);
+                       return -EINVAL;
+               }
+
+               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+               if (ret) {
+                       mfc_err_ctx("error in vb2_reqbufs() for E(D)\n");
+                       return ret;
+               }
+
+               ctx->capture_state = QUEUE_BUFS_REQUESTED;
+       } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (reqbufs->count == 0) {
+                       mfc_debug(2, "Freeing buffers.\n");
+                       ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+                       ctx->output_state = QUEUE_FREE;
+                       return ret;
+               }
+
+               if (ctx->output_state != QUEUE_FREE) {
+                       mfc_err_ctx("invalid output state: %d\n", ctx->output_state);
+                       return -EINVAL;
+               }
+
+               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+               if (ret) {
+                       mfc_err_ctx("error in vb2_reqbufs() for E(S)\n");
+                       return ret;
+               }
+
+               ctx->output_state = QUEUE_BUFS_REQUESTED;
+       } else {
+               mfc_err_ctx("invalid buf type (%d)\n", reqbufs->type);
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+                                                  struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip source querybuf\n");
+               return 0;
+       }
+
+       mfc_debug(2, "state: %d, buf->type: %d\n", ctx->state, buf->type);
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = vb2_querybuf(&ctx->vq_dst, buf);
+               if (ret != 0) {
+                       mfc_err_dev("enc dst: error in vb2_querybuf()\n");
+                       return ret;
+               }
+       } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = vb2_querybuf(&ctx->vq_src, buf);
+               if (ret != 0) {
+                       mfc_err_dev("enc src: error in vb2_querybuf()\n");
+                       return ret;
+               }
+       } else {
+               mfc_err_dev("invalid buf type (%d)\n", buf->type);
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int i, ret = -EINVAL;
+
+       mfc_debug_enter();
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip qbuf\n");
+               return 0;
+       }
+
+       mfc_debug(2, "Enqueued buf: %d (type = %d)\n", buf->index, buf->type);
+       if (ctx->state == MFCINST_ERROR) {
+               mfc_err_ctx("Call on QBUF after unrecoverable error.\n");
+               return -EIO;
+       }
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && !buf->length) {
+               mfc_err_ctx("multiplanar but length is zero\n");
+               return -EIO;
+       }
+
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               for (i = 0; i < ctx->src_fmt->num_planes; i++) {
+                       if (!buf->m.planes[i].bytesused) {
+                               mfc_debug(2, "Src input[%d] size zero, "
+                                               "changed to buf size %d\n",
+                                               i, buf->m.planes[i].length);
+                               buf->m.planes[i].bytesused = buf->m.planes[i].length;
+                       } else {
+                               mfc_debug(2, "Src input[%d] size %d\n",
+                                               i, buf->m.planes[i].bytesused);
+                       }
+               }
+               ret = vb2_qbuf(&ctx->vq_src, buf);
+       } else {
+               ret = vb2_qbuf(&ctx->vq_dst, buf);
+       }
+
+       mfc_debug_leave();
+       return ret;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret;
+
+       mfc_debug_enter();
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip dqbuf\n");
+               return 0;
+       }
+
+       mfc_debug(2, "Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf->m.planes,
+                                                               buf->type);
+       if (ctx->state == MFCINST_ERROR) {
+               mfc_err_ctx("Call on DQBUF after unrecoverable error.\n");
+               return -EIO;
+       }
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+       else
+               ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+       mfc_debug_leave();
+       return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+                          enum v4l2_buf_type type)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = -EINVAL;
+
+       mfc_debug_enter();
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip streamon\n");
+               return 0;
+       }
+
+       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = vb2_streamon(&ctx->vq_src, type);
+
+               if (!ret) {
+                       s5p_mfc_qos_on(ctx);
+               }
+       } else {
+               ret = vb2_streamon(&ctx->vq_dst, type);
+       }
+       mfc_debug(2, "src: %d, dst: %d, state = %d, dpb_count = %d\n",
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+                 ctx->state, ctx->dpb_count);
+       mfc_debug_leave();
+       return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+                           enum v4l2_buf_type type)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret;
+
+       mfc_debug_enter();
+
+       if (ctx->otf_handle) {
+               mfc_info_ctx("OTF: skip streamoff\n");
+               return 0;
+       }
+
+       ret = -EINVAL;
+       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               s5p_mfc_qos_reset_last_framerate(ctx);
+
+               ctx->old_img_width = ctx->img_width;
+               ctx->old_img_height = ctx->img_height;
+
+               mfc_debug(2, "vidioc_streamoff ctx->old_img_width = %d\n", ctx->old_img_width);
+               mfc_debug(2, "vidioc_streamoff ctx->old_img_height = %d\n", ctx->old_img_height);
+
+               ret = vb2_streamoff(&ctx->vq_src, type);
+               if (!ret)
+                       s5p_mfc_qos_off(ctx);
+       } else {
+               ret = vb2_streamoff(&ctx->vq_dst, type);
+       }
+
+       mfc_debug(2, "streamoff\n");
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/* Query a ctrl */
+static int vidioc_queryctrl(struct file *file, void *priv,
+                           struct v4l2_queryctrl *qc)
+{
+       struct v4l2_queryctrl *c;
+
+       c = mfc_enc_get_ctrl(qc->id);
+       if (!c)
+               return -EINVAL;
+       *qc = *c;
+       return 0;
+}
+
+static int mfc_enc_ext_info(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int val = 0;
+
+       val |= ENC_SET_SPARE_SIZE;
+       val |= ENC_SET_TEMP_SVC_CH;
+
+       if (FW_SUPPORT_SKYPE(dev))
+               val |= ENC_SET_SKYPE_FLAG;
+
+       if (FW_HAS_ROI_CONTROL(dev))
+               val |= ENC_SET_ROI_CONTROL;
+
+       val |= ENC_SET_QP_BOUND_PB;
+       val |= ENC_SET_FIXED_SLICE;
+       val |= ENC_SET_PVC_MODE;
+
+       if (FW_HAS_RATIO_INTRA_CTRL(dev))
+               val |= ENC_SET_RATIO_OF_INTRA;
+
+       return val;
+}
+
+static int mfc_enc_get_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       int ret = 0;
+       int found = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_CACHEABLE:
+               mfc_debug(5, "it is supported only V4L2_MEMORY_MMAP\n");
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE:
+               ctrl->value = enc->dst_buf_size;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE:
+               ctrl->value = enc->frame_type;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE:
+               if (ctx->state == MFCINST_RUNNING_NO_OUTPUT)
+                       ctrl->value = MFCSTATE_ENC_NO_OUTPUT;
+               else
+                       ctrl->value = MFCSTATE_PROCESSING;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+       case V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR:
+       case V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR:
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS:
+               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->has_new) {
+                                       ctx_ctrl->has_new = 0;
+                                       ctrl->value = ctx_ctrl->val;
+                               } else {
+                                       mfc_debug(8, "Control value "\
+                                                       "is not up to date: "\
+                                                       "0x%08x\n", ctrl->id);
+                                       return -EINVAL;
+                               }
+
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+                       return -EINVAL;
+               }
+               break;
+       case V4L2_CID_MPEG_MFC_GET_VERSION_INFO:
+               ctrl->value = s5p_mfc_version(dev);
+               break;
+       case V4L2_CID_MPEG_MFC_GET_DRIVER_INFO:
+               ctrl->value = MFC_DRIVER_INFO;
+               break;
+       case V4L2_CID_MPEG_MFC_GET_EXTRA_BUFFER_SIZE:
+               ctrl->value = MFC_LINEAR_BUF_SIZE;
+               break;
+       case V4L2_CID_MPEG_VIDEO_QOS_RATIO:
+               ctrl->value = ctx->qos_ratio;
+               break;
+       case V4L2_CID_MPEG_MFC_GET_EXT_INFO:
+               ctrl->value = mfc_enc_ext_info(ctx);
+               break;
+       case V4L2_CID_MPEG_VIDEO_BPG_HEADER_SIZE:
+               ctrl->value = enc->header_size;
+               break;
+       default:
+               mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+       ret = mfc_enc_get_ctrl_val(ctx, ctrl);
+       mfc_debug_leave();
+
+       return ret;
+}
+
+static inline int mfc_enc_h264_level(enum v4l2_mpeg_video_h264_level lvl)
+{
+       static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_5_1 + 1] = {
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0   */ 10,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_1B    */ 9,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1   */ 11,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2   */ 12,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3   */ 13,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0   */ 20,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1   */ 21,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2   */ 22,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0   */ 30,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1   */ 31,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2   */ 32,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0   */ 40,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_4_1   */ 41,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_4_2   */ 42,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_5_0   */ 50,
+               /* V4L2_MPEG_VIDEO_H264_LEVEL_5_1   */ 51,
+       };
+       return t[lvl];
+}
+
+static inline int mfc_enc_mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
+{
+       static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_6 + 1] = {
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0             */ 0,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B, Simple    */ 9,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1             */ 1,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2             */ 2,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3             */ 3,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B, Advanced  */ 7,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4             */ 4,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5             */ 5,
+               /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_6,  Simple    */ 6,
+       };
+       return t[lvl];
+}
+
+static inline int mfc_enc_vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
+{
+       static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED     */ 0,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1             */ 1,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11           */ 2,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11           */ 3,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11           */ 4,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33           */ 5,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11           */ 6,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11           */ 7,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11           */ 8,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33           */ 9,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11           */ 10,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11           */ 11,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33           */ 12,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99          */ 13,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3             */ 14,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2             */ 15,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1             */ 16,
+               /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED        */ 255,
+       };
+       return t[sar];
+}
+
+static int mfc_enc_set_param(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+               p->gop_size = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+               p->slice_mode =
+                       (enum v4l2_mpeg_video_multi_slice_mode)ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+               p->slice_mb = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+               p->slice_bit = ctrl->value * 8;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW:
+               p->slice_mb_row = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+               p->intra_refresh_mb = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
+               p->pad = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
+               p->pad_luma = (ctrl->value >> 16) & 0xff;
+               p->pad_cb = (ctrl->value >> 8) & 0xff;
+               p->pad_cr = (ctrl->value >> 0) & 0xff;
+               break;
+       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+               p->rc_frame = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+               p->rc_bitrate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
+               p->rc_reaction_coeff = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+               enc->force_frame_type = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+               p->vbv_buf_size = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+               p->seq_hdr_mode =
+                       (enum v4l2_mpeg_video_header_mode)(ctrl->value);
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+               p->frame_skip_mode =
+                       (enum v4l2_mpeg_mfc51_video_frame_skip_mode)
+                       (ctrl->value);
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
+               p->fixed_target_bit = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+               p->num_b_frame = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+               p->codec.h264.profile =
+               mfc_enc_h264_profile(ctx, (enum v4l2_mpeg_video_h264_profile)(ctrl->value));
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+               p->codec.h264.level =
+               mfc_enc_h264_level((enum v4l2_mpeg_video_h264_level)(ctrl->value));
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE:
+               p->codec.h264.interlace = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+               p->codec.h264.loop_filter_mode =
+               (enum v4l2_mpeg_video_h264_loop_filter_mode)(ctrl->value);
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+               p->codec.h264.loop_filter_alpha = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+               p->codec.h264.loop_filter_beta = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+               p->codec.h264.entropy_mode =
+                       (enum v4l2_mpeg_video_h264_entropy_mode)(ctrl->value);
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
+               p->num_refs_for_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+               p->codec.h264._8x8_transform = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+               p->rc_mb = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE:
+               p->codec.h264.rc_framerate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+               p->codec.h264.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+               p->codec.h264.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+               p->codec.h264.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P:
+               p->codec.h264.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P:
+               p->codec.h264.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B:
+               p->codec.h264.rc_min_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B:
+               p->codec.h264.rc_max_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
+               p->codec.h264.rc_mb_dark = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
+               p->codec.h264.rc_mb_smooth = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
+               p->codec.h264.rc_mb_static = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
+               p->codec.h264.rc_mb_activity = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+               p->codec.h264.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+               p->codec.h264.rc_b_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+               p->codec.h264.ar_vui = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+               p->codec.h264.ar_vui_idc =
+               mfc_enc_vui_sar_idc((enum v4l2_mpeg_video_h264_vui_sar_idc)(ctrl->value));
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+               p->codec.h264.ext_sar_width = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+               p->codec.h264.ext_sar_height = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+               p->codec.h264.open_gop = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+               p->codec.h264.open_gop_size = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING:
+               p->codec.h264.hier_qp_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE:
+               p->codec.h264.hier_qp_type =
+               (enum v4l2_mpeg_video_h264_hierarchical_coding_type)(ctrl->value);
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:
+               p->codec.h264.num_hier_layer = ctrl->value & 0x7;
+               p->codec.h264.hier_ref_type = (ctrl->value >> 16) & 0x1;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP:
+               p->codec.h264.hier_qp_layer[(ctrl->value >> 16) & 0x7]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0:
+               p->codec.h264.hier_bit_layer[0] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1:
+               p->codec.h264.hier_bit_layer[1] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2:
+               p->codec.h264.hier_bit_layer[2] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3:
+               p->codec.h264.hier_bit_layer[3] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4:
+               p->codec.h264.hier_bit_layer[4] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5:
+               p->codec.h264.hier_bit_layer[5] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6:
+               p->codec.h264.hier_bit_layer[6] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING:
+               p->codec.h264.sei_gen_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0:
+               p->codec.h264.sei_fp_curr_frame_0 = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE:
+               p->codec.h264.sei_fp_arrangement_type = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO:
+               p->codec.h264.fmo_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE:
+               switch ((enum v4l2_mpeg_video_h264_fmo_map_type)(ctrl->value)) {
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES:
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES:
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN:
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN:
+                       p->codec.h264.fmo_slice_map_type = ctrl->value;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP:
+               p->codec.h264.fmo_slice_num_grp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH:
+               p->codec.h264.fmo_run_length[(ctrl->value >> 30) & 0x3]
+                       = ctrl->value & 0x3FFFFFFF;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION:
+               p->codec.h264.fmo_sg_dir = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE:
+               p->codec.h264.fmo_sg_rate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_ASO:
+               p->codec.h264.aso_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER:
+               p->codec.h264.aso_slice_order[(ctrl->value >> 18) & 0x7]
+                       &= ~(0xFF << (((ctrl->value >> 16) & 0x3) << 3));
+               p->codec.h264.aso_slice_order[(ctrl->value >> 18) & 0x7]
+                       |= (ctrl->value & 0xFF) << \
+                               (((ctrl->value >> 16) & 0x3) << 3);
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR:
+               p->codec.h264.prepend_sps_pps_to_idr = ctrl->value ? 1 : 0;
+               break;
+       case V4L2_CID_MPEG_MFC_H264_ENABLE_LTR:
+               p->codec.h264.enable_ltr = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR:
+               p->codec.h264.num_of_ltr = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY:
+               p->codec.h264.base_priority = ctrl->value;
+               p->codec.h264.set_priority = 1;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+               switch ((enum v4l2_mpeg_video_mpeg4_profile)(ctrl->value)) {
+               case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+                       p->codec.mpeg4.profile =
+                               S5P_FIMV_E_PROFILE_MPEG4_SIMPLE;
+                       break;
+               case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+                       p->codec.mpeg4.profile =
+                       S5P_FIMV_E_PROFILE_MPEG4_ADVANCED_SIMPLE;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+               p->codec.mpeg4.level =
+               mfc_enc_mpeg4_level((enum v4l2_mpeg_video_mpeg4_level)(ctrl->value));
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+               p->codec.mpeg4.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+               p->codec.mpeg4.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+               p->codec.mpeg4.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P:
+               p->codec.mpeg4.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P:
+               p->codec.mpeg4.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B:
+               p->codec.mpeg4.rc_min_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B:
+               p->codec.mpeg4.rc_max_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+               p->codec.mpeg4.quarter_pixel = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+               p->codec.mpeg4.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+               p->codec.mpeg4.rc_b_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES:
+               p->codec.mpeg4.vop_time_res = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA:
+               p->codec.mpeg4.vop_frm_delta = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE:
+               p->codec.mpeg4.rc_framerate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+               p->codec.mpeg4.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+               p->codec.mpeg4.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+               p->codec.mpeg4.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P:
+               p->codec.mpeg4.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P:
+               p->codec.mpeg4.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+               p->codec.mpeg4.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION:
+               p->codec.vp8.vp8_version = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE:
+               p->codec.vp8.rc_framerate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP:
+               p->codec.vp8.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP:
+               p->codec.vp8.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P:
+               p->codec.vp8.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P:
+               p->codec.vp8.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP:
+               p->codec.vp8.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP:
+               p->codec.vp8.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS:
+               p->codec.vp8.vp8_numberofpartitions = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL:
+               p->codec.vp8.vp8_filterlevel = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS:
+               p->codec.vp8.vp8_filtersharpness = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL:
+               p->codec.vp8.vp8_goldenframesel = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD:
+               p->codec.vp8.vp8_gfrefreshperiod = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE:
+               p->codec.vp8.hier_qp_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0:
+               p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1:
+               p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2:
+               p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0:
+               p->codec.vp8.hier_bit_layer[0] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1:
+               p->codec.vp8.hier_bit_layer[1] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2:
+               p->codec.vp8.hier_bit_layer[2] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES:
+               p->num_refs_for_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4:
+               p->codec.vp8.intra_4x4mode_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER:
+               p->codec.vp8.num_hier_layer = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_VERSION:
+               p->codec.vp9.vp9_version = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE:
+               p->codec.vp9.rc_framerate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP:
+               p->codec.vp9.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP:
+               p->codec.vp9.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P:
+               p->codec.vp9.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P:
+               p->codec.vp9.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP:
+               p->codec.vp9.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP:
+               p->codec.vp9.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL:
+               p->codec.vp9.vp9_goldenframesel = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD:
+               p->codec.vp9.vp9_gfrefreshperiod = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE:
+               p->codec.vp9.hier_qp_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP:
+               p->codec.vp9.hier_qp_layer[(ctrl->value >> 16) & 0x3]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0:
+               p->codec.vp9.hier_bit_layer[0] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1:
+               p->codec.vp9.hier_bit_layer[1] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2:
+               p->codec.vp9.hier_bit_layer[2] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES:
+               p->num_refs_for_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER:
+               p->codec.vp9.num_hier_layer = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH:
+               p->codec.vp9.max_partition_depth = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT:
+               p->codec.vp9.intra_pu_split_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER:
+               p->ivf_header_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+               p->codec.vp9.profile = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
+               p->codec.hevc.rc_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP:
+               p->codec.hevc.rc_p_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP:
+               p->codec.hevc.rc_b_frame_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE:
+               p->codec.hevc.rc_framerate = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+               p->codec.hevc.rc_min_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+               p->codec.hevc.rc_max_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P:
+               p->codec.hevc.rc_min_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P:
+               p->codec.hevc.rc_max_qp_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B:
+               p->codec.hevc.rc_min_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B:
+               p->codec.hevc.rc_max_qp_b = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+               p->codec.hevc.level = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+               p->codec.hevc.profile = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK:
+               p->codec.hevc.rc_lcu_dark = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH:
+               p->codec.hevc.rc_lcu_smooth = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC:
+               p->codec.hevc.rc_lcu_static = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY:
+               p->codec.hevc.rc_lcu_activity = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG:
+               p->codec.hevc.tier_flag = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH:
+               p->codec.hevc.max_partition_depth = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES:
+               p->num_refs_for_p = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE:
+               p->codec.hevc.refreshtype = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE:
+               p->codec.hevc.const_intra_period_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE:
+               p->codec.hevc.lossless_cu_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE:
+               p->codec.hevc.wavefront_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE:
+               p->codec.hevc.loopfilter_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY:
+               p->codec.hevc.loopfilter_across = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE:
+               p->codec.hevc.enable_ltr = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE:
+               p->codec.hevc.hier_qp_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE:
+               p->codec.hevc.hier_qp_type =
+               (enum v4l2_mpeg_video_hevc_hierarchical_coding_type)(ctrl->value);
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER:
+               p->codec.hevc.num_hier_layer = ctrl->value & 0x7;
+               p->codec.hevc.hier_ref_type = (ctrl->value >> 16) & 0x1;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP:
+               p->codec.hevc.hier_qp_layer[(ctrl->value >> 16) & 0x7]
+                       = ctrl->value & 0xFF;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0:
+               p->codec.hevc.hier_bit_layer[0] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1:
+               p->codec.hevc.hier_bit_layer[1] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2:
+               p->codec.hevc.hier_bit_layer[2] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3:
+               p->codec.hevc.hier_bit_layer[3] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4:
+               p->codec.hevc.hier_bit_layer[4] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5:
+               p->codec.hevc.hier_bit_layer[5] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6:
+               p->codec.hevc.hier_bit_layer[6] = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE:
+               p->codec.hevc.general_pb_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE:
+               p->codec.hevc.temporal_id_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG:
+               p->codec.hevc.strong_intra_smooth = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT:
+               p->codec.hevc.intra_pu_split_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION:
+               p->codec.hevc.tmv_prediction_disable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1:
+               p->codec.hevc.max_num_merge_mv = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE:
+               p->codec.hevc.encoding_nostartcode_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD:
+               p->codec.hevc.refreshperiod = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2:
+               p->codec.hevc.lf_beta_offset_div2 = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2:
+               p->codec.hevc.lf_tc_offset_div2 = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+               p->codec.hevc.size_of_length_field = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF:
+               p->codec.hevc.user_ref = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF:
+               p->codec.hevc.store_ref = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_ROI_ENABLE:
+               p->roi_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE:
+               p->codec.h264.vui_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR:
+               p->codec.hevc.prepend_sps_pps_to_idr = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE:
+               p->dynamic_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC_CONFIG_QP:
+               p->config_qp = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE:
+               /* It is valid for H.264, HEVC, VP8, VP9 */
+               p->rc_pvc = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER:
+               p->num_hier_max_layer = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING:
+               break;
+       case V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE:
+               p->weighted_enable = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BPG_THUMBNAIL_SIZE:
+               /* It should be included when NAL_START */
+               p->codec.bpg.thumb_size = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BPG_EXIF_SIZE:
+               /* It should be included when NAL_START */
+               p->codec.bpg.exif_size = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA:
+               p->ratio_intra = ctrl->value;
+               break;
+       default:
+               mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int mfc_enc_set_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       int ret = 0;
+       int found = 0;
+       int index = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_CACHEABLE:
+               mfc_debug(5, "it is supported only V4L2_MEMORY_MMAP\n");
+               break;
+       case V4L2_CID_MPEG_VIDEO_QOS_RATIO:
+               ctx->qos_ratio = ctrl->value;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P:
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P:
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B:
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B:
+       case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B:
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+       case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+       case V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH:
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH:
+       case V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH:
+       case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH:
+       case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH:
+       case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH:
+       case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH:
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+       case V4L2_CID_MPEG_MFC_H264_MARK_LTR:
+       case V4L2_CID_MPEG_MFC_H264_USE_LTR:
+       case V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY:
+       case V4L2_CID_MPEG_MFC_CONFIG_QP:
+       case V4L2_CID_MPEG_VIDEO_ROI_CONTROL:
+       case V4L2_CID_MPEG_VIDEO_YSUM:
+       case V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA:
+               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->has_new = 1;
+                               ctx_ctrl->val = ctrl->value;
+                               if (ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH) {
+                                       ctx_ctrl->val &= ~(0xFFFF << 16);
+                                       ctx_ctrl->val |= ctx_ctrl->val << 16;
+                                       ctx_ctrl->val &= ~(0xFFFF);
+                                       ctx_ctrl->val |= p->rc_frame_delta & 0xFFFF;
+                               }
+                               if (((ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) ||
+                                       (ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH) ||
+                                       (ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH) ||
+                                       (ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH)) &&
+                                       (enc->sh_handle_svc.fd == -1)) {
+                                               enc->sh_handle_svc.fd = ctrl->value;
+                                               if (s5p_mfc_mem_get_user_shared_handle(ctx,
+                                                                       &enc->sh_handle_svc)) {
+                                                       enc->sh_handle_svc.fd = -1;
+                                                       return -EINVAL;
+                                               }
+                               }
+                               if (ctx_ctrl->id == V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH &&
+                                               p->i_frm_ctrl_mode) {
+                                       ctx_ctrl->val = ctx_ctrl->val * (p->num_b_frame + 1);
+                                       if (ctx_ctrl->val >= 0x3FFFFFFF) {
+                                               mfc_info_ctx("I frame interval is bigger than max: %d\n",
+                                                               ctx_ctrl->val);
+                                               ctx_ctrl->val = 0x3FFFFFFF;
+                                       }
+                               }
+                               if (ctx_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL)
+                                       ctx_ctrl->val = mfc_enc_h264_level((enum v4l2_mpeg_video_h264_level)(ctrl->value));
+                               if (ctx_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE)
+                                       ctx_ctrl->val = mfc_enc_h264_profile(ctx, (enum v4l2_mpeg_video_h264_profile)(ctrl->value));
+                               if (ctx_ctrl->id == \
+                                       V4L2_CID_MPEG_VIDEO_ROI_CONTROL) {
+                                       enc->sh_handle_roi.fd = ctrl->value;
+                                       if (s5p_mfc_mem_get_user_shared_handle(ctx,
+                                                               &enc->sh_handle_roi)) {
+                                               enc->sh_handle_roi.fd = -1;
+                                               return -EINVAL;
+                                       }
+                                       index = enc->roi_index;
+                                       memcpy(&enc->roi_info[index],
+                                                       enc->sh_handle_roi.vaddr,
+                                                       sizeof(struct mfc_enc_roi_info));
+                                       if (copy_from_user(enc->roi_buf[index].vaddr,
+                                                       enc->roi_info[index].addr,
+                                                       enc->roi_info[index].size))
+                                               return -EFAULT;
+                               }
+
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       mfc_err_ctx("Invalid control: 0x%08x\n", ctrl->id);
+                       return -EINVAL;
+               }
+               break;
+       default:
+               ret = mfc_enc_set_param(ctx, ctrl);
+               break;
+       }
+
+       return ret;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct s5p_mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data);
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       ret = mfc_enc_check_ctrl_val(ctx, ctrl);
+       if (ret != 0)
+               return ret;
+
+       ret = mfc_enc_set_ctrl_val(ctx, ctrl);
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                             struct v4l2_ext_controls *f)
+{
+       struct s5p_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_MPEG)
+               return -EINVAL;
+
+       for (i = 0; i < f->count; i++) {
+               ext_ctrl = (f->controls + i);
+
+               ctrl.id = ext_ctrl->id;
+
+               ret = mfc_enc_get_ctrl_val(ctx, &ctrl);
+               if (ret == 0) {
+                       ext_ctrl->value = ctrl.value;
+               } else {
+                       f->error_idx = i;
+                       break;
+               }
+
+               mfc_debug(2, "[%d] id: 0x%08x, value: %d\n", i, ext_ctrl->id, ext_ctrl->value);
+       }
+
+       return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *f)
+{
+       struct s5p_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_MPEG)
+               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_enc_check_ctrl_val(ctx, &ctrl);
+               if (ret != 0) {
+                       f->error_idx = i;
+                       break;
+               }
+
+               ret = mfc_enc_set_param(ctx, &ctrl);
+               if (ret != 0) {
+                       f->error_idx = i;
+                       break;
+               }
+
+               mfc_debug(2, "[%d] id: 0x%08x, value: %d\n", i, ext_ctrl->id, ext_ctrl->value);
+       }
+
+       return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *f)
+{
+       struct s5p_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_MPEG)
+               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_enc_check_ctrl_val(ctx, &ctrl);
+               if (ret != 0) {
+                       f->error_idx = i;
+                       break;
+               }
+
+               mfc_debug(2, "[%d] id: 0x%08x, value: %d\n", i, ext_ctrl->id, ext_ctrl->value);
+       }
+
+       return ret;
+}
+
+static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
+       .vidioc_querycap                = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+       .vidioc_enum_fmt_vid_out        = vidioc_enum_fmt_vid_out,
+       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+       .vidioc_g_fmt_vid_cap_mplane    = vidioc_g_fmt,
+       .vidioc_g_fmt_vid_out_mplane    = vidioc_g_fmt,
+       .vidioc_try_fmt_vid_cap_mplane  = vidioc_try_fmt,
+       .vidioc_try_fmt_vid_out_mplane  = vidioc_try_fmt,
+       .vidioc_s_fmt_vid_cap_mplane    = vidioc_s_fmt_vid_cap_mplane,
+       .vidioc_s_fmt_vid_out_mplane    = vidioc_s_fmt_vid_out_mplane,
+       .vidioc_reqbufs                 = vidioc_reqbufs,
+       .vidioc_querybuf                = vidioc_querybuf,
+       .vidioc_qbuf                    = vidioc_qbuf,
+       .vidioc_dqbuf                   = vidioc_dqbuf,
+       .vidioc_streamon                = vidioc_streamon,
+       .vidioc_streamoff               = vidioc_streamoff,
+       .vidioc_queryctrl               = vidioc_queryctrl,
+       .vidioc_g_ctrl                  = vidioc_g_ctrl,
+       .vidioc_s_ctrl                  = vidioc_s_ctrl,
+       .vidioc_g_ext_ctrls             = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls             = vidioc_s_ext_ctrls,
+       .vidioc_try_ext_ctrls           = vidioc_try_ext_ctrls,
+};
+
+const struct v4l2_ioctl_ops *s5p_mfc_get_enc_v4l2_ioctl_ops(void)
+{
+       return &s5p_mfc_enc_ioctl_ops;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc.h b/drivers/media/platform/exynos/mfc/s5p_mfc_enc.h
new file mode 100644 (file)
index 0000000..4cfbc2a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc.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 __S5P_MFC_ENC_H
+#define __S5P_MFC_ENC_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+const struct v4l2_ioctl_ops *s5p_mfc_get_enc_v4l2_ioctl_ops(void);
+
+#endif /* __S5P_MFC_ENC_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc_internal.h b/drivers/media/platform/exynos/mfc/s5p_mfc_enc_internal.h
new file mode 100644 (file)
index 0000000..6907d41
--- /dev/null
@@ -0,0 +1,2244 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc_internal.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 __S5P_MFC_ENC_INTERNAL_H
+#define __S5P_MFC_ENC_INTERNAL_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+/*
+ * RGB encoding information to avoid confusion.
+ *
+ * V4L2_PIX_FMT_ARGB32 takes ARGB data like below.
+ * MSB                              LSB
+ * 3       2       1
+ * 2       4       6       8       0
+ * |B......BG......GR......RA......A|
+ */
+struct s5p_mfc_fmt enc_formats[] = {
+       {
+               .name = "4:2:0 3 Planes Y/Cb/Cr",
+               .fourcc = V4L2_PIX_FMT_YUV420M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 3,
+       },
+       {
+               .name = "4:2:0 3 Planes Y/Cb/Cr single",
+               .fourcc = V4L2_PIX_FMT_YUV420N,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 1,
+       },
+       {
+               .name = "4:2:0 3 Planes Y/Cr/Cb",
+               .fourcc = V4L2_PIX_FMT_YVU420M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 3,
+               .mem_planes = 3,
+       },
+       {
+               .name = "4:2:0 2 Planes 16x16 Tiles",
+               .fourcc = V4L2_PIX_FMT_NV12MT_16X16,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes 64x32 Tiles",
+               .fourcc = V4L2_PIX_FMT_NV12MT,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes",
+               .fourcc = V4L2_PIX_FMT_NV12M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr single",
+               .fourcc = V4L2_PIX_FMT_NV12N,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 1,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr 10bit",
+               .fourcc = V4L2_PIX_FMT_NV12M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CbCr P010 10bit",
+               .fourcc = V4L2_PIX_FMT_NV12M_P010,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb",
+               .fourcc = V4L2_PIX_FMT_NV21M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb 10bit",
+               .fourcc = V4L2_PIX_FMT_NV21M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:0 2 Planes Y/CrCb P010 10bit",
+               .fourcc = V4L2_PIX_FMT_NV21M_P010,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV16M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr 10bit",
+               .fourcc = V4L2_PIX_FMT_NV16M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb 10bit",
+               .fourcc = V4L2_PIX_FMT_NV61M_S10B,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CbCr P210 10bit",
+               .fourcc = V4L2_PIX_FMT_NV16M_P210,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb P210 10bit",
+               .fourcc = V4L2_PIX_FMT_NV61M_P210,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "4:2:2 2 Planes Y/CrCb",
+               .fourcc = V4L2_PIX_FMT_NV61M,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 2,
+       },
+       {
+               .name = "RGB888 1 Plane 24bpp",
+               .fourcc = V4L2_PIX_FMT_RGB24,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "RGB565 1 Plane 16bpp",
+               .fourcc = V4L2_PIX_FMT_RGB565,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "RGBX8888 1 Plane 32bpp",
+               .fourcc = V4L2_PIX_FMT_RGB32X,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "BGRA8888 1 Plane 32bpp",
+               .fourcc = V4L2_PIX_FMT_BGR32,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "ARGB8888 1 Plane 32bpp",
+               .fourcc = V4L2_PIX_FMT_ARGB32,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "H264 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264,
+               .codec_mode = S5P_FIMV_CODEC_H264_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "MPEG4 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_MPEG4,
+               .codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "H263 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H263,
+               .codec_mode = S5P_FIMV_CODEC_H263_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VP8 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VP8,
+               .codec_mode = S5P_FIMV_CODEC_VP8_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "VP9 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_VP9,
+               .codec_mode = S5P_FIMV_CODEC_VP9_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "HEVC Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_HEVC,
+               .codec_mode = S5P_FIMV_CODEC_HEVC_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "BPG Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_BPG,
+               .codec_mode = S5P_FIMV_CODEC_BPG_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(enc_formats)
+
+static struct v4l2_queryctrl controls[] = {
+       {
+               .id = V4L2_CID_CACHEABLE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Cacheable flag",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The period of intra frame",
+               .minimum = 0,
+               .maximum = (1 << 30) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The slice partitioning method",
+               .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+               .maximum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The number of MB in a slice",
+               .minimum = 1,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The maximum bits per slices",
+               .minimum = 350,
+               .maximum = INT_MAX / 8,
+               .step = 1,
+               .default_value = 350,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The number of MB row in a slice",
+               .minimum = 1,
+               .maximum = INT_MAX / 256,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The number of intra refresh MBs",
+               .minimum = 0,
+               .maximum = (1 << 18) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Padding control enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Padding Color YUV Value",
+               .minimum = 0,
+               .maximum = (1 << 24) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Frame level rate control enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Target bit rate rate-control",
+               .minimum = 1,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Rate control reaction coeff.",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Encoded stream size",
+               .minimum = 0,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+               .flags = V4L2_CTRL_FLAG_READ_ONLY,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_COUNT,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Encoded frame count",
+               .minimum = 0,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+               .flags = V4L2_CTRL_FLAG_READ_ONLY,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Encoded frame type",
+               .minimum = 0,
+               .maximum = 5,
+               .step = 1,
+               .default_value = 0,
+               .flags = V4L2_CTRL_FLAG_READ_ONLY,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Force frame type",
+               .minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+               .maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
+               .step = 1,
+               .default_value = \
+                       V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VBV buffer size (1Kbits)",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Sequence header mode",
+               .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+               .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Frame skip enable",
+               .minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+               .maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+               .step = 1,
+               .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Fixed target bit enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The number of B frames",
+               .minimum = 0,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 profile",
+               .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+               .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 level",
+               .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+               .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 interlace mode",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 loop filter mode",
+               .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+               .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_S_B,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 loop filter alpha offset",
+               .minimum = -12,
+               .maximum = 12,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 loop filter beta offset",
+               .minimum = -12,
+               .maximum = 12,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 entorpy mode",
+               .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+               .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "The number of ref. picture of P",
+               .minimum = 1,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 8x8 transform enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "MB level rate control",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Frame rate",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Frame QP value",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 dark region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 smooth region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 static region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "H264 MB activity adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 P frame QP value",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 B frame QP value",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Aspect ratio VUI enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VUI aspect ratio IDC",
+               .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+               .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Horizontal size of SAR",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Vertical size of SAR",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "GOP closure",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 I period",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Hierarchical Coding",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Type",
+               .minimum = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B,
+               .maximum = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer",
+               .minimum = 0,
+               .maximum = 7,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer QP",
+               .minimum = INT_MIN,
+               .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 generation flag",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Current frame is frame 0 flag",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Frame packing arrangement type",
+               .minimum = V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE,
+               .maximum = V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Flexible Macroblock Order",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Map type for FMO",
+               .minimum = V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES,
+               .maximum = V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN,
+               .step = 1,
+               .default_value = \
+                       V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Number of slice groups for FMO",
+               .minimum = 1,
+               .maximum = 4,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "FMO Run Length",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Direction of the slice group",
+               .minimum = V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT,
+               .maximum = V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Size of the first slice group",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_ASO,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Arbitrary Slice Order",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "ASO Slice order",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Prepend SPS/PPS to every IDR",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 profile",
+               .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+               .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 level",
+               .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+               .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_6,
+               .step = 1,
+               .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Frame QP value",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Quarter pixel search enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 P frame QP value",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 B frame QP value",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 vop time resolution",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 frame delta",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Frame rate",
+               .minimum = 1,
+               .maximum = (1 << 8) - 1,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Frame QP value",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 P frame QP value",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .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_FRAME_STATUS,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Frame Status",
+               .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 = 1000,
+               .step = 10,
+               .default_value = 100,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 version",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Frame QP value",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Frame QP value",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Frame rate",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 number of partitions",
+               .minimum = 0,
+               .maximum = 8,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 loop filter level",
+               .minimum = 0,
+               .maximum = 63,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 loop filter sharpness",
+               .minimum = 0,
+               .maximum = 7,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 indication of golden frame",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 indication of golden frame",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP8 hierarchy QP enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 layer0 QP value",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 layer1 QP value",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 layer2 QP value",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Number of reference picture",
+               .minimum = 1,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP8 intra 4x4 mode disable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP8 number of hierarchical layer",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_VERSION,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 version",
+               .minimum = 0,
+               .maximum = 0,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Frame QP value",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Frame QP value",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Frame rate",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 indication of golden frame",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 indication of golden frame",
+               .minimum = 0,
+               .maximum = ((1 << 16) - 1),
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP9 hierarchy QP enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 layer0 QP value",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Number of reference picture",
+               .minimum = 1,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP9 number of hierarchical layer",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP9 Maximum coding unit depth",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VP9 disable intra pu split",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit0",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit1",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit2",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Change",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "IVF header generation",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Frame QP value",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC P frame QP value",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC B frame QP value",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC dark region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC smooth region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC static region adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC activity adaptive",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Profile",
+               .minimum = 0,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC level",
+               .minimum = 10,
+               .maximum = 62,
+               .step = 1,
+               .default_value = 10,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC tier_flag default is Main",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Frame rate",
+               .minimum = 1,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Maximum coding unit depth",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Number of reference picture",
+               .minimum = 1,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Number of reference picture",
+               .minimum = 0,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC refresh type",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC lossless encoding select",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Wavefront enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Filter disable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "across or not slice boundary",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "long term reference enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "QP values for temporal layer",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Hierarchical Coding Type",
+               .minimum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B,
+               .maximum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer",
+               .minimum = 0,
+               .maximum = 7,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer QP",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT0",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT1",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT2",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT3",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT4",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT5",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer BIT6",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Change",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Sign data hiding",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC General pb enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Temporal id enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC Strong intra smoothing flag",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC disable intra pu split",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "HEVC disable tmv prediction",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "max number of candidate MVs",
+               .minimum = 0,
+               .maximum = 4,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "ENC without startcode enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Number of reference picture",
+               .minimum = 0,
+               .maximum = (1 << 16) - 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC loop filter beta offset",
+               .minimum = -6,
+               .maximum = 6,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC loop filter tc offset",
+               .minimum = -6,
+               .maximum = 6,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC size of length field",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "user long term reference frame",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "store long term reference frame",
+               .minimum = 0,
+               .maximum = 2,
+               .step = 1,
+               .default_value = 0, /* need to check defualt value */
+       },
+       {
+               .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_GET_EXTRA_BUFFER_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Get extra buffer size",
+               .minimum = 0,
+               .maximum = (2 << 31) - 1,
+               .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_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit0",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit1",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit2",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit3",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit4",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit5",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit6",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Change",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit0",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit1",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Bit2",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding Layer Change",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_ENABLE_LTR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Enable LTR",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Number of LTR",
+               .minimum = 0,
+               .maximum = 4,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_MARK_LTR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Set the frame as a LTRP",
+               .minimum = 0,
+               .maximum = 4,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_USE_LTR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Specify a LTRP for encoding",
+               .minimum = 0,
+               .maximum = 4,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Base Layer Priority",
+               .minimum = 0,
+               .maximum = (1 << 6) - 1 - 6,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_CONFIG_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "QP control per each frame",
+               .minimum = -12,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 vui generation enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Prepend SPS/PPS to every IDR",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "set dynamic qp controls",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_ROI_CONTROL,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Region-Of-Interest control",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_ROI_ENABLE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Region-Of-Interest enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Min QP value for I frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Max QP value for I frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Min QP value for I frame",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Max QP value for I frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Min QP value for I frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Max QP value for I frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Min QP value for I frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Max QP value for I frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Min QP value for I frame",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Max QP value for I frame",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MIN_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Min QP value for I frame",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MAX_QP,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Max QP value for I frame",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Min QP value for P frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Max QP value for P frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Min QP value for P frame",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Max QP value for P frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Min QP value for P frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Max QP value for P frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Min QP value for P frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H263 Max QP value for P frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Min QP value for P frame",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP8 Max QP value for P frame",
+               .minimum = 0,
+               .maximum = 127,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Min QP value for P frame",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "VP9 Max QP value for P frame",
+               .minimum = 1,
+               .maximum = 255,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Min QP value for B frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "H264 Max QP value for B frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Min QP value for B frame",
+               .minimum = -12,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HEVC Max QP value for B frame",
+               .minimum = 0,
+               .maximum = 51,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Min QP value for B frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "MPEG4 Max QP value for B frame",
+               .minimum = 1,
+               .maximum = 31,
+               .step = 1,
+               .default_value = 1,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Perceptual Video Coding enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hierarchical Coding max layer",
+               .minimum = 0,
+               .maximum = 3,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Weighted Prediction enable",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_YSUM,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "YSUM for weighted Prediction",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_BPG_THUMBNAIL_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "BPG thumbnail size",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_BPG_EXIF_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "BPG EXIF data size",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_BPG_HEADER_SIZE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "BPG header size",
+               .minimum = INT_MIN,
+               .maximum = INT_MAX,
+               .step = 1,
+               .default_value = 0,
+       },
+       {
+               .id = V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "ratio of intra encoded size",
+               .minimum = 15,
+               .maximum = 50,
+               .step = 1,
+               .default_value = 0,
+       },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+#endif /* __S5P_MFC_ENC_INTERNAL_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc_ops.c b/drivers/media/platform/exynos/mfc/s5p_mfc_enc_ops.c
new file mode 100644 (file)
index 0000000..4f8e213
--- /dev/null
@@ -0,0 +1,1664 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc_ops.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 "s5p_mfc_common.h"
+
+#include "s5p_mfc_reg.h"
+
+static int mfc_enc_ctrl_read_cst(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf_ctrl *buf_ctrl)
+{
+       int ret;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       switch (buf_ctrl->id) {
+       case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS:
+               ret = !enc->in_slice;
+               break;
+       default:
+               mfc_err_ctx("not support custom per-buffer control\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static struct s5p_mfc_ctrl_cfg mfc_ctrl_list[] = {
+       {       /* set frame tag */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_PICTURE_TAG,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* get frame tag */
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RET_PICTURE_TAG,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* encoded y physical addr */
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* encoded c physical addr */
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* I, not coded frame insertion */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_FRAME_INSERTION,
+               .mask = 0x3,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* I period change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_GOP_CONFIG,
+               .mask = 0xFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 0,
+       },
+       {       /* frame rate change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_FRAME_RATE,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 1,
+       },
+       {       /* bit rate change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_BIT_RATE,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 2,
+       },
+       {       /* frame status (in slice or not) */
+               .type = MFC_CTRL_TYPE_GET_DST,
+               .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS,
+               .is_volatile = 0,
+               .mode = MFC_CTRL_MODE_CST,
+               .addr = 0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+               .read_cst = mfc_enc_ctrl_read_cst,
+               .write_cst = NULL,
+       },
+       {       /* H.264 I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.263 I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.263 I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP8 I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP8 I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP9 I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP9 I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC I frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC I frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.263 P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.263 P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP8 P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP8 P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP9 P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* VP9 P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC P frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC P frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 B frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 24,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 B frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 16,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 B frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 24,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* MPEG4 B frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 16,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC B frame QP Max change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 24,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* HEVC B frame QP Min change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_QP_BOUND_PB,
+               .mask = 0xFF,
+               .shft = 16,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 4,
+       },
+       {       /* H.264 Dynamic Temporal Layer & bitrate change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 10,
+       },
+       {       /* HEVC Dynamic Temporal Layer & bitrate change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 10,
+       },
+       {       /* VP8 Dynamic Temporal Layer change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 10,
+       },
+       {       /* VP9 Dynamic Temporal Layer change */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 10,
+       },
+       {       /* set level */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_PICTURE_PROFILE,
+               .mask = 0x000000FF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 5,
+       },
+       {       /* set profile */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_PICTURE_PROFILE,
+               .mask = 0x0000000F,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 5,
+       },
+       {       /* set store LTR */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC_H264_MARK_LTR,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_H264_NAL_CONTROL,
+               .mask = 0x00000003,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* set use LTR */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC_H264_USE_LTR,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_H264_NAL_CONTROL,
+               .mask = 0x00000003,
+               .shft = 2,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* set base layer priority */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_H264_HD_SVC_EXTENSION_0,
+               .mask = 0x0000003F,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 12,
+       },
+       {       /* set QP per each frame */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_MFC_CONFIG_QP,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_FIXED_PICTURE_QP,
+               .mask = 0x000000FF,
+               .shft = 24,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* Region-Of-Interest control */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_ROI_CONTROL,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_ROI_CTRL,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_addr = 0,
+               .flag_shft = 0,
+       },
+       {       /* set YSUM for weighted prediction */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_YSUM,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_WEIGHT_FOR_WEIGHTED_PREDICTION,
+               .mask = 0xFFFFFFFF,
+               .shft = 0,
+               .flag_mode = MFC_CTRL_MODE_NONE,
+               .flag_mode = 0,
+               .flag_shft = 0,
+       },
+       {       /* set base layer priority */
+               .type = MFC_CTRL_TYPE_SET,
+               .id = V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA,
+               .is_volatile = 1,
+               .mode = MFC_CTRL_MODE_SFR,
+               .addr = S5P_FIMV_E_RC_MODE,
+               .mask = 0x000000FF,
+               .shft = 8,
+               .flag_mode = MFC_CTRL_MODE_SFR,
+               .flag_addr = S5P_FIMV_E_PARAM_CHANGE,
+               .flag_shft = 13,
+       }
+};
+
+#define NUM_CTRL_CFGS ARRAY_SIZE(mfc_ctrl_list)
+
+static int s5p_mfc_enc_cleanup_ctx_ctrls(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+
+       while (!list_empty(&ctx->ctrls)) {
+               ctx_ctrl = list_entry((&ctx->ctrls)->next,
+                                     struct s5p_mfc_ctx_ctrl, list);
+
+               mfc_debug(7, "Cleanup context control "\
+                               "id: 0x%08x, type: %d\n",
+                               ctx_ctrl->id, ctx_ctrl->type);
+
+               list_del(&ctx_ctrl->list);
+               kfree(ctx_ctrl);
+       }
+
+       INIT_LIST_HEAD(&ctx->ctrls);
+
+       return 0;
+}
+
+static int s5p_mfc_enc_get_buf_update_val(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, unsigned int id, int value)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if ((buf_ctrl->id == id)) {
+                       buf_ctrl->val = value;
+                       mfc_debug(5, "++id: 0x%08x val: %d\n",
+                                       buf_ctrl->id, buf_ctrl->val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_enc_init_ctx_ctrls(struct s5p_mfc_ctx *ctx)
+{
+       unsigned long i;
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+
+       INIT_LIST_HEAD(&ctx->ctrls);
+
+       for (i = 0; i < NUM_CTRL_CFGS; i++) {
+               ctx_ctrl = kzalloc(sizeof(struct s5p_mfc_ctx_ctrl), GFP_KERNEL);
+               if (ctx_ctrl == NULL) {
+                       mfc_err_dev("Failed to allocate context control "\
+                                       "id: 0x%08x, type: %d\n",
+                                       mfc_ctrl_list[i].id,
+                                       mfc_ctrl_list[i].type);
+
+                       s5p_mfc_enc_cleanup_ctx_ctrls(ctx);
+
+                       return -ENOMEM;
+               }
+
+               ctx_ctrl->type = mfc_ctrl_list[i].type;
+               ctx_ctrl->id = mfc_ctrl_list[i].id;
+               ctx_ctrl->has_new = 0;
+               ctx_ctrl->val = 0;
+
+               list_add_tail(&ctx_ctrl->list, &ctx->ctrls);
+
+               mfc_debug(7, "Add context control id: 0x%08x, type : %d\n",
+                               ctx_ctrl->id, ctx_ctrl->type);
+       }
+
+       return 0;
+}
+
+static void s5p_mfc_enc_reset_buf_ctrls(struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               mfc_debug(8, "Reset buffer control value "\
+                               "id: 0x%08x, type: %d\n",
+                               buf_ctrl->id, buf_ctrl->type);
+
+               buf_ctrl->has_new = 0;
+               buf_ctrl->val = 0;
+               buf_ctrl->old_val = 0;
+               buf_ctrl->updated = 0;
+       }
+}
+
+static void mfc_enc_remove_buf_ctrls(struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       while (!list_empty(head)) {
+               buf_ctrl = list_entry(head->next,
+                               struct s5p_mfc_buf_ctrl, list);
+
+               mfc_debug(7, "Cleanup buffer control "\
+                               "id: 0x%08x, type: %d\n",
+                               buf_ctrl->id, buf_ctrl->type);
+
+               list_del(&buf_ctrl->list);
+               kfree(buf_ctrl);
+       }
+
+       INIT_LIST_HEAD(head);
+}
+
+static int s5p_mfc_enc_init_buf_ctrls(struct s5p_mfc_ctx *ctx,
+       enum s5p_mfc_ctrl_type type, unsigned int index)
+{
+       unsigned long i;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct list_head *head;
+
+       if (index >= MFC_MAX_BUFFERS) {
+               mfc_err_dev("Per-buffer control index is out of range\n");
+               return -EINVAL;
+       }
+
+       if (type & MFC_CTRL_TYPE_SRC) {
+               if (test_bit(index, &ctx->src_ctrls_avail)) {
+                       mfc_debug(7, "Source per-buffer control is already "\
+                                       "initialized [%d]\n", index);
+
+                       s5p_mfc_enc_reset_buf_ctrls(&ctx->src_ctrls[index]);
+
+                       return 0;
+               }
+
+               head = &ctx->src_ctrls[index];
+       } else if (type & MFC_CTRL_TYPE_DST) {
+               if (test_bit(index, &ctx->dst_ctrls_avail)) {
+                       mfc_debug(7, "Dest. per-buffer control is already "\
+                                       "initialized [%d]\n", index);
+
+                       s5p_mfc_enc_reset_buf_ctrls(&ctx->dst_ctrls[index]);
+
+                       return 0;
+               }
+
+               head = &ctx->dst_ctrls[index];
+       } else {
+               mfc_err_dev("Control type mismatch. type : %d\n", type);
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(head);
+
+       for (i = 0; i < NUM_CTRL_CFGS; i++) {
+               if (!(type & mfc_ctrl_list[i].type))
+                       continue;
+
+               buf_ctrl = kzalloc(sizeof(struct s5p_mfc_buf_ctrl), GFP_KERNEL);
+               if (buf_ctrl == NULL) {
+                       mfc_err_dev("Failed to allocate buffer control "\
+                                       "id: 0x%08x, type: %d\n",
+                                       mfc_ctrl_list[i].id,
+                                       mfc_ctrl_list[i].type);
+
+                       mfc_enc_remove_buf_ctrls(head);
+
+                       return -ENOMEM;
+               }
+
+               buf_ctrl->type = mfc_ctrl_list[i].type;
+               buf_ctrl->id = mfc_ctrl_list[i].id;
+               buf_ctrl->is_volatile = mfc_ctrl_list[i].is_volatile;
+               buf_ctrl->mode = mfc_ctrl_list[i].mode;
+               buf_ctrl->addr = mfc_ctrl_list[i].addr;
+               buf_ctrl->mask = mfc_ctrl_list[i].mask;
+               buf_ctrl->shft = mfc_ctrl_list[i].shft;
+               buf_ctrl->flag_mode = mfc_ctrl_list[i].flag_mode;
+               buf_ctrl->flag_addr = mfc_ctrl_list[i].flag_addr;
+               buf_ctrl->flag_shft = mfc_ctrl_list[i].flag_shft;
+               if (buf_ctrl->mode == MFC_CTRL_MODE_CST) {
+                       buf_ctrl->read_cst = mfc_ctrl_list[i].read_cst;
+                       buf_ctrl->write_cst = mfc_ctrl_list[i].write_cst;
+               }
+
+               list_add_tail(&buf_ctrl->list, head);
+
+               mfc_debug(7, "Add buffer control id: 0x%08x, type : %d\n",
+                               buf_ctrl->id, buf_ctrl->type);
+       }
+
+       s5p_mfc_enc_reset_buf_ctrls(head);
+
+       if (type & MFC_CTRL_TYPE_SRC)
+               set_bit(index, &ctx->src_ctrls_avail);
+       else
+               set_bit(index, &ctx->dst_ctrls_avail);
+
+       return 0;
+}
+
+static int s5p_mfc_enc_cleanup_buf_ctrls(struct s5p_mfc_ctx *ctx,
+       enum s5p_mfc_ctrl_type type, unsigned int index)
+{
+       struct list_head *head;
+
+       if (index >= MFC_MAX_BUFFERS) {
+               mfc_err_dev("Per-buffer control index is out of range\n");
+               return -EINVAL;
+       }
+
+       if (type & MFC_CTRL_TYPE_SRC) {
+               if (!(test_and_clear_bit(index, &ctx->src_ctrls_avail))) {
+                       mfc_debug(7, "Source per-buffer control is "\
+                                       "not available [%d]\n", index);
+                       return 0;
+               }
+
+               head = &ctx->src_ctrls[index];
+       } else if (type & MFC_CTRL_TYPE_DST) {
+               if (!(test_and_clear_bit(index, &ctx->dst_ctrls_avail))) {
+                       mfc_debug(7, "Dest. per-buffer Control is "\
+                                       "not available [%d]\n", index);
+                       return 0;
+               }
+
+               head = &ctx->dst_ctrls[index];
+       } else {
+               mfc_err_dev("Control type mismatch. type : %d\n", type);
+               return -EINVAL;
+       }
+
+       mfc_enc_remove_buf_ctrls(head);
+
+       return 0;
+}
+
+static int s5p_mfc_enc_to_buf_ctrls(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       int index = 0;
+       unsigned int reg = 0;
+
+       list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+               if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET) || !ctx_ctrl->has_new)
+                       continue;
+
+               list_for_each_entry(buf_ctrl, head, list) {
+                       if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET))
+                               continue;
+
+                       if (buf_ctrl->id == ctx_ctrl->id) {
+                               buf_ctrl->has_new = 1;
+                               buf_ctrl->val = ctx_ctrl->val;
+                               if (buf_ctrl->is_volatile)
+                                       buf_ctrl->updated = 0;
+
+                               ctx_ctrl->has_new = 0;
+                               if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_ROI_CONTROL) {
+                                       index = enc->roi_index;
+                                       if (enc->roi_info[index].enable) {
+                                               enc->roi_index =
+                                                       (index + 1) % MFC_MAX_EXTRA_BUF;
+                                               reg |= enc->roi_info[index].enable;
+                                               reg &= ~(0xFF << 8);
+                                               reg |= (enc->roi_info[index].lower_qp << 8);
+                                               reg &= ~(0xFFFF << 16);
+                                               reg |= (enc->roi_info[index].upper_qp << 16);
+                                               mfc_debug(3, "ROI: [%d] en %d, "\
+                                                               "QP lower %d upper %d reg %#x\n",
+                                                               index, enc->roi_info[index].enable,
+                                                               enc->roi_info[index].lower_qp,
+                                                               enc->roi_info[index].upper_qp,
+                                                               reg);
+                                       } else {
+                                               mfc_debug(3, "ROI: [%d] not enabled\n", index);
+                                       }
+                                       buf_ctrl->val = reg;
+                                       buf_ctrl->old_val2 = index;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (buf_ctrl->has_new)
+                       mfc_debug(8, "Updated buffer control "\
+                                       "id: 0x%08x val: %d\n",
+                                       buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_enc_to_ctx_ctrls(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_ctx_ctrl *ctx_ctrl;
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET) || !buf_ctrl->has_new)
+                       continue;
+
+               list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+                       if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET))
+                               continue;
+
+                       if (ctx_ctrl->id == buf_ctrl->id) {
+                               if (ctx_ctrl->has_new)
+                                       mfc_debug(8,
+                                       "Overwrite context control "\
+                                       "value id: 0x%08x, val: %d\n",
+                                               ctx_ctrl->id, ctx_ctrl->val);
+
+                               ctx_ctrl->has_new = 1;
+                               ctx_ctrl->val = buf_ctrl->val;
+
+                               buf_ctrl->has_new = 0;
+                       }
+               }
+       }
+
+       list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) {
+               if (ctx_ctrl->has_new)
+                       mfc_debug(8, "Updated context control "\
+                                       "id: 0x%08x val: %d\n",
+                                       ctx_ctrl->id, ctx_ctrl->val);
+       }
+
+       return 0;
+}
+
+static void mfc_enc_set_buf_ctrls_temporal_svc(struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       unsigned int value = 0, value2 = 0;
+       struct temporal_layer_info temporal_LC;
+       unsigned int i;
+       struct s5p_mfc_enc_params *p = &enc->params;
+
+       if (buf_ctrl->id
+               == V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH ||
+               buf_ctrl->id
+               == V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH ||
+               buf_ctrl->id
+               == V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH ||
+               buf_ctrl->id
+               == V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) {
+               memcpy(&temporal_LC,
+                       enc->sh_handle_svc.vaddr, sizeof(struct temporal_layer_info));
+
+               if(((temporal_LC.temporal_layer_count & 0x7) < 1) ||
+                       ((temporal_LC.temporal_layer_count > 3) && IS_VP8_ENC(ctx)) ||
+                       ((temporal_LC.temporal_layer_count > 3) && IS_VP9_ENC(ctx))) {
+                       /* clear NUM_T_LAYER_CHANGE */
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value &= ~(1 << 10);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+                       mfc_err_ctx("Temporal SVC: layer count is invalid : %d\n",
+                                       temporal_LC.temporal_layer_count);
+                       return;
+               }
+
+               if (IS_H264_ENC(ctx))
+                       p->codec.h264.num_hier_layer = temporal_LC.temporal_layer_count & 0x7;
+
+               /* enable RC_BIT_RATE_CHANGE */
+               value = MFC_READL(buf_ctrl->flag_addr);
+               if (temporal_LC.temporal_layer_bitrate[0] > 0)
+                       /* set RC_BIT_RATE_CHANGE */
+                       value |= (1 << 2);
+               else
+                       /* clear RC_BIT_RATE_CHANGE */
+                       value &= ~(1 << 2);
+               MFC_WRITEL(value, buf_ctrl->flag_addr);
+
+               mfc_debug(3, "Temporal SVC: layer count %d, E_PARAM_CHANGE %#x\n",
+                               temporal_LC.temporal_layer_count & 0x7, value);
+
+               value = MFC_READL(S5P_FIMV_E_NUM_T_LAYER);
+               buf_ctrl->old_val2 = value;
+               value &= ~(0x7);
+               value |= (temporal_LC.temporal_layer_count & 0x7);
+               MFC_WRITEL(value, S5P_FIMV_E_NUM_T_LAYER);
+               mfc_debug(3, "Temporal SVC: E_NUM_T_LAYER %#x\n", value);
+               for (i = 0; i < (temporal_LC.temporal_layer_count & 0x7); i++) {
+                       mfc_debug(3, "Temporal SVC: layer bitrate[%d] %d\n",
+                                       i, temporal_LC.temporal_layer_bitrate[i]);
+                       MFC_WRITEL(temporal_LC.temporal_layer_bitrate[i],
+                                       buf_ctrl->addr + i * 4);
+               }
+               /* priority change */
+               if (IS_H264_ENC(ctx)) {
+                       value = 0;
+                       value2 = 0;
+                       for (i = 0; i < (p->codec.h264.num_hier_layer & 0x07); i++) {
+                               if (i <= 4)
+                                       value |= ((p->codec.h264.base_priority & 0x3F) + i)
+                                               << (6 * i);
+                               else
+                                       value2 |= ((p->codec.h264.base_priority & 0x3F) + i)
+                                               << (6 * (i - 5));
+                       }
+                       MFC_WRITEL(value, S5P_FIMV_E_H264_HD_SVC_EXTENSION_0);
+                       MFC_WRITEL(value2, S5P_FIMV_E_H264_HD_SVC_EXTENSION_1);
+                       mfc_debug(3, "Temporal SVC: EXTENSION0 %#x, EXTENSION1 %#x\n",
+                                       value, value2);
+
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value |= (1 << 12);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+                       mfc_debug(3, "Temporal SVC: E_PARAM_CHANGE %#x\n", value);
+               }
+
+       }
+
+       /* temproral layer priority */
+       if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY) {
+               value = MFC_READL(S5P_FIMV_E_H264_HD_SVC_EXTENSION_0);
+               buf_ctrl->old_val |= value & 0x3FFFFFC0;
+               value &= ~(0x3FFFFFC0);
+               value2 = MFC_READL(S5P_FIMV_E_H264_HD_SVC_EXTENSION_1);
+               buf_ctrl->old_val2 = value2 & 0x0FFF;
+               value2 &= ~(0x0FFF);
+               for (i = 0; i < (p->codec.h264.num_hier_layer & 0x07); i++) {
+                       if (i <= 4)
+                               value |= ((buf_ctrl->val & 0x3F) + i) << (6 * i);
+                       else
+                               value2 |= ((buf_ctrl->val & 0x3F) + i) << (6 * (i - 5));
+               }
+               MFC_WRITEL(value, S5P_FIMV_E_H264_HD_SVC_EXTENSION_0);
+               MFC_WRITEL(value2, S5P_FIMV_E_H264_HD_SVC_EXTENSION_1);
+               p->codec.h264.base_priority = buf_ctrl->val;
+               mfc_debug(3, "Temporal SVC: EXTENSION0 %#x, EXTENSION1 %#x\n",
+                               value, value2);
+       }
+}
+
+static void mfc_enc_set_buf_ctrls_exception(struct s5p_mfc_ctx *ctx,
+                       struct s5p_mfc_buf_ctrl *buf_ctrl)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       unsigned int value = 0;
+
+       if (buf_ctrl->id == V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG)
+               enc->stored_tag = buf_ctrl->val;
+
+       /* temporal layer setting */
+       mfc_enc_set_buf_ctrls_temporal_svc(ctx, buf_ctrl);
+
+       if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_MARK_LTR) {
+               value = MFC_READL(S5P_FIMV_E_H264_NAL_CONTROL);
+               buf_ctrl->old_val2 = (value >> 8) & 0x7;
+               value &= ~(0x7 << 8);
+               value |= (buf_ctrl->val & 0x7) << 8;
+               MFC_WRITEL(value, S5P_FIMV_E_H264_NAL_CONTROL);
+       }
+       if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_USE_LTR) {
+               value = MFC_READL(S5P_FIMV_E_H264_NAL_CONTROL);
+               buf_ctrl->old_val2 = (value >> 11) & 0x7;
+               value &= ~(0x7 << 11);
+               value |= (buf_ctrl->val & 0x7) << 11;
+               MFC_WRITEL(value, S5P_FIMV_E_H264_NAL_CONTROL);
+       }
+
+       if ((buf_ctrl->id == V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH)) {
+               value = MFC_READL(S5P_FIMV_E_GOP_CONFIG2);
+               buf_ctrl->old_val |= (value << 16) & 0x3FFF0000;
+               value &= ~(0x3FFF);
+               value |= (buf_ctrl->val >> 16) & 0x3FFF;
+               MFC_WRITEL(value, S5P_FIMV_E_GOP_CONFIG2);
+       }
+
+       /* PROFILE & LEVEL have to be set up together */
+       if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) {
+               value = MFC_READL(S5P_FIMV_E_PICTURE_PROFILE);
+               buf_ctrl->old_val |= (value & 0x000F) << 8;
+               value &= ~(0x000F);
+               value |= p->codec.h264.profile & 0x000F;
+               MFC_WRITEL(value, S5P_FIMV_E_PICTURE_PROFILE);
+               p->codec.h264.level = buf_ctrl->val;
+       }
+
+       if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) {
+               value = MFC_READL(S5P_FIMV_E_PICTURE_PROFILE);
+               buf_ctrl->old_val |= value & 0xFF00;
+               value &= ~(0x00FF << 8);
+               value |= (p->codec.h264.level << 8) & 0xFF00;
+               MFC_WRITEL(value, S5P_FIMV_E_PICTURE_PROFILE);
+               p->codec.h264.profile = buf_ctrl->val;
+       }
+
+       /* per buffer QP setting change */
+       if (buf_ctrl->id == V4L2_CID_MPEG_MFC_CONFIG_QP)
+               p->config_qp = buf_ctrl->val;
+
+       /* set the ROI buffer DVA */
+       if ((buf_ctrl->id == V4L2_CID_MPEG_VIDEO_ROI_CONTROL) &&
+                       FW_HAS_ROI_CONTROL(dev)) {
+               MFC_WRITEL(enc->roi_buf[buf_ctrl->old_val2].daddr,
+                               S5P_FIMV_E_ROI_BUFFER_ADDR);
+               mfc_debug(3, "ROI: buffer[%d] addr %#llx, QP val: %#x\n",
+                               buf_ctrl->old_val2,
+                               enc->roi_buf[buf_ctrl->old_val2].daddr,
+                               buf_ctrl->val);
+       }
+}
+
+static int s5p_mfc_enc_set_buf_ctrls_val(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       unsigned int value = 0;
+       struct s5p_mfc_enc_params *p = &enc->params;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || !buf_ctrl->has_new)
+                       continue;
+
+               /* read old vlaue */
+               value = MFC_READL(buf_ctrl->addr);
+
+               /* save old value for recovery */
+               if (buf_ctrl->is_volatile)
+                       buf_ctrl->old_val = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               /* write new value */
+               value &= ~(buf_ctrl->mask << buf_ctrl->shft);
+               value |= ((buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft);
+               MFC_WRITEL(value, buf_ctrl->addr);
+
+               /* set change flag bit */
+               if (buf_ctrl->flag_mode == MFC_CTRL_MODE_SFR) {
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value |= (1 << buf_ctrl->flag_shft);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+               }
+
+               buf_ctrl->has_new = 0;
+               buf_ctrl->updated = 1;
+
+               mfc_enc_set_buf_ctrls_exception(ctx, buf_ctrl);
+
+               mfc_debug(8, "Set buffer control "\
+                               "id: 0x%x, val: %d (%#x)\n",
+                               buf_ctrl->id, buf_ctrl->val,
+                               MFC_READL(buf_ctrl->addr));
+       }
+
+       if (!p->rc_frame && !p->rc_mb && p->dynamic_qp) {
+               value = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+               value &= ~(0xFF000000);
+               value |= (p->config_qp & 0xFF) << 24;
+               MFC_WRITEL(value, S5P_FIMV_E_FIXED_PICTURE_QP);
+               mfc_debug(8, "Dynamic QP changed %#x\n",
+                               MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP));
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_enc_get_buf_ctrls_val(struct s5p_mfc_ctx *ctx, struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int value = 0;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET))
+                       continue;
+
+               if (buf_ctrl->mode == MFC_CTRL_MODE_SFR)
+                       value = MFC_READL(buf_ctrl->addr);
+               else if (buf_ctrl->mode == MFC_CTRL_MODE_CST)
+                       value = call_bop(buf_ctrl, read_cst, ctx, buf_ctrl);
+
+               value = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               buf_ctrl->val = value;
+               buf_ctrl->has_new = 1;
+
+               mfc_debug(8, "Get buffer control "\
+                               "id: 0x%08x val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_enc_set_buf_ctrls_val_nal_q_enc(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, EncoderInputStr *pInStr)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct temporal_layer_info temporal_LC;
+       unsigned int i, param_change;
+       struct s5p_mfc_enc_params *p = &enc->params;
+
+       mfc_debug_enter();
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || !buf_ctrl->has_new)
+                       continue;
+               param_change = 0;
+               switch (buf_ctrl->id) {
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+                       pInStr->PictureTag &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->PictureTag |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       enc->stored_tag = buf_ctrl->val;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+                       pInStr->FrameInsertion &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->FrameInsertion |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH:
+                       pInStr->GopConfig &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->GopConfig |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->GopConfig2 &= ~(0x3FFF);
+                       pInStr->GopConfig2 |= (buf_ctrl->val >> 16) & 0x3FFF;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH:
+                       pInStr->RcFrameRate &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcFrameRate |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH:
+                       pInStr->RcBitRate &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcBitRate |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP:
+               case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+               case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+               case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP:
+               case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP:
+                       pInStr->RcQpBound &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcQpBound |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P:
+               case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P:
+               case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B:
+               case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B:
+               case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B:
+               case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B:
+                       pInStr->RcQpBoundPb &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcQpBoundPb |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH:
+               case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH:
+               case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH:
+               case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH:
+                       memcpy(&temporal_LC,
+                               enc->sh_handle_svc.vaddr, sizeof(struct temporal_layer_info));
+
+                       if (((temporal_LC.temporal_layer_count & 0x7) < 1) ||
+                               ((temporal_LC.temporal_layer_count > 3) && IS_VP8_ENC(ctx)) ||
+                               ((temporal_LC.temporal_layer_count > 3) && IS_VP9_ENC(ctx))) {
+                               /* claer NUM_T_LAYER_CHANGE */
+                               mfc_err_ctx("Temporal SVC: layer count(%d) is invalid\n",
+                                               temporal_LC.temporal_layer_count);
+                               return 0;
+                       }
+
+                       if (IS_H264_ENC(ctx))
+                               p->codec.h264.num_hier_layer =
+                                       temporal_LC.temporal_layer_count & 0x7;
+
+                       /* enable RC_BIT_RATE_CHANGE */
+                       if (temporal_LC.temporal_layer_bitrate[0] > 0)
+                               pInStr->ParamChange |= (1 << 2);
+                       else
+                               pInStr->ParamChange &= ~(1 << 2);
+
+                       /* enalbe NUM_T_LAYER_CHANGE */
+                       if (temporal_LC.temporal_layer_count & 0x7)
+                               pInStr->ParamChange |= (1 << 10);
+                       else
+                               pInStr->ParamChange &= ~(1 << 10);
+                       mfc_debug(3, "Temporal SVC layer count %d\n",
+                                       temporal_LC.temporal_layer_count & 0x7);
+
+                       pInStr->NumTLayer &= ~(0x7);
+                       pInStr->NumTLayer |= (temporal_LC.temporal_layer_count & 0x7);
+                       for (i = 0; i < (temporal_LC.temporal_layer_count & 0x7); i++) {
+                               mfc_debug(3, "Temporal SVC: layer bitrate[%d] %d\n",
+                                       i, temporal_LC.temporal_layer_bitrate[i]);
+                               pInStr->HierarchicalBitRateLayer[i] =
+                                       temporal_LC.temporal_layer_bitrate[i];
+                       }
+
+                       /* priority change */
+                       if (IS_H264_ENC(ctx)) {
+                               for (i = 0; i < (temporal_LC.temporal_layer_count & 0x7); i++) {
+                                       if (i <= 4)
+                                               pInStr->H264HDSvcExtension0 |=
+                                                       ((p->codec.h264.base_priority & 0x3f) + i) << (6 * i);
+                                       else
+                                               pInStr->H264HDSvcExtension1 |=
+                                                       ((p->codec.h264.base_priority & 0x3f) + i) << (6 * (i - 5));
+                               }
+                               mfc_debug(3, "NAL-Q: Temporal SVC: EXTENSION0 %#x, EXTENSION1 %#x\n",
+                                               pInStr->H264HDSvcExtension0, pInStr->H264HDSvcExtension1);
+
+                               pInStr->ParamChange |= (1 << 12);
+                       }
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+                       pInStr->PictureProfile &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->PictureProfile |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->PictureProfile &= ~(0xf);
+                       pInStr->PictureProfile |= p->codec.h264.profile & 0xf;
+                       p->codec.h264.level = buf_ctrl->val;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+                       pInStr->PictureProfile &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->PictureProfile |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->PictureProfile &= ~(0xff << 8);
+                       pInStr->PictureProfile |= (p->codec.h264.level << 8) & 0xff00;
+                       p->codec.h264.profile = buf_ctrl->val;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_MFC_H264_MARK_LTR:
+                       pInStr->H264NalControl &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->H264NalControl |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->H264NalControl &= ~(0x7 << 8);
+                       pInStr->H264NalControl |= (buf_ctrl->val & 0x7) << 8;
+                       break;
+               case V4L2_CID_MPEG_MFC_H264_USE_LTR:
+                       pInStr->H264NalControl &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->H264NalControl |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->H264NalControl &= ~(0x7 << 11);
+                       pInStr->H264NalControl |= (buf_ctrl->val & 0x7) << 11;
+                       break;
+               case V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY:
+                       for (i = 0; i < (p->codec.h264.num_hier_layer & 0x7); i++)
+                               if (i <= 4)
+                                       pInStr->H264HDSvcExtension0 |=
+                                               ((buf_ctrl->val & 0x3f) + i) << (6 * i);
+                               else
+                                       pInStr->H264HDSvcExtension1 |=
+                                               ((buf_ctrl->val & 0x3f) + i) << (6 * (i - 5));
+                       p->codec.h264.base_priority = buf_ctrl->val;
+                       param_change = 1;
+                       break;
+               case V4L2_CID_MPEG_MFC_CONFIG_QP:
+                       pInStr->FixedPictureQp &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->FixedPictureQp |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       p->config_qp = buf_ctrl->val;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_ROI_CONTROL:
+                       pInStr->RcRoiCtrl &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcRoiCtrl |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       pInStr->RoiBufferAddr = enc->roi_buf[buf_ctrl->old_val2].daddr;
+                       mfc_debug(3, "NAL-Q: ROI: buffer[%d] addr %#llx, QP val: %#x\n",
+                                       buf_ctrl->old_val2,
+                                       enc->roi_buf[buf_ctrl->old_val2].daddr,
+                                       buf_ctrl->val);
+                       break;
+               case V4L2_CID_MPEG_VIDEO_YSUM:
+                       pInStr->Weight &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->Weight |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       break;
+               case V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA:
+                       pInStr->RcMode &= ~(buf_ctrl->mask << buf_ctrl->shft);
+                       pInStr->RcMode |=
+                               (buf_ctrl->val & buf_ctrl->mask) << buf_ctrl->shft;
+                       param_change = 1;
+                       break;
+               /* If new dynamic controls are added, insert here */
+               default:
+                       mfc_info_ctx("NAL Q: can't find control, id: 0x%x\n",
+                                       buf_ctrl->id);
+               }
+
+               if (param_change)
+                       pInStr->ParamChange |= (1 << buf_ctrl->flag_shft);
+
+               buf_ctrl->has_new = 0;
+               buf_ctrl->updated = 1;
+               mfc_debug(8, "NAL Q: Set buffer control id: 0x%x, val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       if (!p->rc_frame && !p->rc_mb && p->dynamic_qp) {
+               pInStr->FixedPictureQp &= ~(0xFF000000);
+               pInStr->FixedPictureQp |= (p->config_qp & 0xFF) << 24;
+               mfc_debug(8, "NAL Q: Dynamic QP changed %#x\n",
+                               pInStr->FixedPictureQp);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_enc_get_buf_ctrls_val_nal_q_enc(struct s5p_mfc_ctx *ctx,
+                       struct list_head *head, EncoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       unsigned int value = 0;
+
+       mfc_debug_enter();
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET))
+                       continue;
+               switch (buf_ctrl->id) {
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG:
+                       value = pOutStr->PictureTag;
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR:
+                       value = pOutStr->EncodedFrameAddr[0];
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR:
+                       value = pOutStr->EncodedFrameAddr[1];
+                       break;
+               case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS:
+                       value = !enc->in_slice;
+                       break;
+               /* If new dynamic controls are added, insert here */
+               default:
+                       mfc_info_ctx("NAL Q: can't find control, id: 0x%x\n",
+                                       buf_ctrl->id);
+               }
+               value = (value >> buf_ctrl->shft) & buf_ctrl->mask;
+
+               buf_ctrl->val = value;
+               buf_ctrl->has_new = 1;
+
+               mfc_debug(2, "NAL Q: enc_get_buf_ctrls, ctrl id: 0x%x, val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_enc_recover_buf_ctrls_val(struct s5p_mfc_ctx *ctx,
+                                               struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int value = 0;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET)
+                       || !buf_ctrl->is_volatile
+                       || !buf_ctrl->updated)
+                       continue;
+
+               if (buf_ctrl->mode == MFC_CTRL_MODE_SFR)
+                       value = MFC_READL(buf_ctrl->addr);
+
+               value &= ~(buf_ctrl->mask << buf_ctrl->shft);
+               value |= ((buf_ctrl->old_val & buf_ctrl->mask)
+                                                       << buf_ctrl->shft);
+
+               if (buf_ctrl->mode == MFC_CTRL_MODE_SFR)
+                       MFC_WRITEL(value, buf_ctrl->addr);
+
+               /* clear change flag bit */
+               if (buf_ctrl->flag_mode == MFC_CTRL_MODE_SFR) {
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value &= ~(1 << buf_ctrl->flag_shft);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+               }
+
+               mfc_debug(8, "Recover buffer control "\
+                               "id: 0x%08x old val: %#x old val2: %#x\n",
+                               buf_ctrl->id, buf_ctrl->old_val, buf_ctrl->old_val2);
+               if (buf_ctrl->id == V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH) {
+                       value = MFC_READL(S5P_FIMV_E_GOP_CONFIG2);
+                       value &= ~(0x3FFF);
+                       value |= (buf_ctrl->old_val >> 16) & 0x3FFF;
+                       MFC_WRITEL(value, S5P_FIMV_E_GOP_CONFIG2);
+               }
+               if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) {
+                       value = MFC_READL(S5P_FIMV_E_PICTURE_PROFILE);
+                       value &= ~(0x000F);
+                       value |= (buf_ctrl->old_val >> 8) & 0x000F;
+                       MFC_WRITEL(value, S5P_FIMV_E_PICTURE_PROFILE);
+               }
+               if (buf_ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) {
+                       value = MFC_READL(S5P_FIMV_E_PICTURE_PROFILE);
+                       value &= ~(0xFF00);
+                       value |= buf_ctrl->old_val & 0xFF00;
+                       MFC_WRITEL(value, S5P_FIMV_E_PICTURE_PROFILE);
+               }
+               if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY) {
+                       MFC_WRITEL(buf_ctrl->old_val, S5P_FIMV_E_H264_HD_SVC_EXTENSION_0);
+                       MFC_WRITEL(buf_ctrl->old_val2, S5P_FIMV_E_H264_HD_SVC_EXTENSION_1);
+               }
+               if (buf_ctrl->id
+                       == V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH ||
+                       buf_ctrl->id
+                       == V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH ||
+                       buf_ctrl->id
+                       == V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) {
+                       MFC_WRITEL(buf_ctrl->old_val2, S5P_FIMV_E_NUM_T_LAYER);
+                       /* clear RC_BIT_RATE_CHANGE */
+                       value = MFC_READL(buf_ctrl->flag_addr);
+                       value &= ~(1 << 2);
+                       MFC_WRITEL(value, buf_ctrl->flag_addr);
+               }
+               if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_MARK_LTR) {
+                       value = MFC_READL(S5P_FIMV_E_H264_NAL_CONTROL);
+                       value &= ~(0x7 << 8);
+                       value |= (buf_ctrl->old_val2 & 0x7) << 8;
+                       MFC_WRITEL(value, S5P_FIMV_E_H264_NAL_CONTROL);
+               }
+               if (buf_ctrl->id == V4L2_CID_MPEG_MFC_H264_USE_LTR) {
+                       value = MFC_READL(S5P_FIMV_E_H264_NAL_CONTROL);
+                       value &= ~(0x7 << 11);
+                       value |= (buf_ctrl->old_val2 & 0x7) << 11;
+                       MFC_WRITEL(value, S5P_FIMV_E_H264_NAL_CONTROL);
+               }
+               buf_ctrl->updated = 0;
+       }
+
+       return 0;
+}
+
+static int s5p_mfc_enc_recover_buf_ctrls_nal_q(struct s5p_mfc_ctx *ctx,
+               struct list_head *head)
+{
+       struct s5p_mfc_buf_ctrl *buf_ctrl;
+
+       list_for_each_entry(buf_ctrl, head, list) {
+               if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET)
+                               || !(buf_ctrl->updated))
+                       continue;
+
+               buf_ctrl->has_new = 1;
+               buf_ctrl->updated = 0;
+               mfc_debug(5, "recover has_new, id: 0x%08x val: %d\n",
+                               buf_ctrl->id, buf_ctrl->val);
+       }
+
+       return 0;
+}
+
+struct s5p_mfc_ctrls_ops encoder_ctrls_ops = {
+       .init_ctx_ctrls                 = s5p_mfc_enc_init_ctx_ctrls,
+       .cleanup_ctx_ctrls              = s5p_mfc_enc_cleanup_ctx_ctrls,
+       .init_buf_ctrls                 = s5p_mfc_enc_init_buf_ctrls,
+       .reset_buf_ctrls                = s5p_mfc_enc_reset_buf_ctrls,
+       .cleanup_buf_ctrls              = s5p_mfc_enc_cleanup_buf_ctrls,
+       .to_buf_ctrls                   = s5p_mfc_enc_to_buf_ctrls,
+       .to_ctx_ctrls                   = s5p_mfc_enc_to_ctx_ctrls,
+       .set_buf_ctrls_val              = s5p_mfc_enc_set_buf_ctrls_val,
+       .get_buf_ctrls_val              = s5p_mfc_enc_get_buf_ctrls_val,
+       .recover_buf_ctrls_val          = s5p_mfc_enc_recover_buf_ctrls_val,
+       .get_buf_update_val             = s5p_mfc_enc_get_buf_update_val,
+       .set_buf_ctrls_val_nal_q_enc    = s5p_mfc_enc_set_buf_ctrls_val_nal_q_enc,
+       .get_buf_ctrls_val_nal_q_enc    = s5p_mfc_enc_get_buf_ctrls_val_nal_q_enc,
+       .recover_buf_ctrls_nal_q        = s5p_mfc_enc_recover_buf_ctrls_nal_q,
+};
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.c b/drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.c
new file mode 100644 (file)
index 0000000..4f615f0
--- /dev/null
@@ -0,0 +1,1317 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.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 "s5p_mfc_enc_param.h"
+
+#include "s5p_mfc_reg.h"
+
+/* Definition */
+#define FRAME_DELTA_DEFAULT            1
+#define CBR_FIX_MAX                    10
+#define CBR_I_LIMIT_MAX                        5
+#define BPG_EXTENSION_TAG_SIZE         5
+
+void s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       /* multi-slice control */
+       if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES)
+               MFC_WRITEL((enc->slice_mode + 0x4), S5P_FIMV_E_MSLICE_MODE);
+       else if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW)
+               MFC_WRITEL((enc->slice_mode - 0x2), S5P_FIMV_E_MSLICE_MODE);
+       else if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES)
+               MFC_WRITEL((enc->slice_mode + 0x3), S5P_FIMV_E_MSLICE_MODE);
+       else
+               MFC_WRITEL(enc->slice_mode, S5P_FIMV_E_MSLICE_MODE);
+
+       /* multi-slice MB number or bit size */
+       if ((enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) ||
+                       (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW)) {
+               MFC_WRITEL(enc->slice_size.mb, S5P_FIMV_E_MSLICE_SIZE_MB);
+       } else if ((enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) ||
+                       (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES)){
+               MFC_WRITEL(enc->slice_size.bits, S5P_FIMV_E_MSLICE_SIZE_BITS);
+       } else {
+               MFC_WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_MB);
+               MFC_WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_BITS);
+       }
+}
+
+static void mfc_set_gop_size(struct s5p_mfc_ctx *ctx, int ctrl_mode)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       unsigned int reg = 0;
+
+       if (ctrl_mode) {
+               p->i_frm_ctrl_mode = 1;
+               p->i_frm_ctrl = p->gop_size * (p->num_b_frame + 1);
+               if (p->i_frm_ctrl >= 0x3FFFFFFF) {
+                       mfc_info_ctx("I frame interval is bigger than max: %d\n",
+                                       p->i_frm_ctrl);
+                       p->i_frm_ctrl = 0x3FFFFFFF;
+               }
+       } else {
+               p->i_frm_ctrl_mode = 0;
+               p->i_frm_ctrl = p->gop_size;
+       }
+
+       mfc_debug(2, "I frame interval: %d, (P: %d, B: %d), ctrl mode: %d\n",
+                       p->i_frm_ctrl, p->gop_size,
+                       p->num_b_frame, p->i_frm_ctrl_mode);
+
+       /* pictype : IDR period, number of B */
+       reg = MFC_READL(S5P_FIMV_E_GOP_CONFIG);
+       reg &= ~(0xFFFF);
+       reg |= p->i_frm_ctrl & 0xFFFF;
+       reg &= ~(0x1 << 19);
+       reg |= p->i_frm_ctrl_mode << 19;
+       reg &= ~(0x3 << 16);
+       /* if B frame is used, the performance falls by half */
+       reg |= (p->num_b_frame << 16);
+       MFC_WRITEL(reg, S5P_FIMV_E_GOP_CONFIG);
+
+       reg = MFC_READL(S5P_FIMV_E_GOP_CONFIG2);
+       reg &= ~(0x3FFF);
+       reg |= (p->i_frm_ctrl >> 16) & 0x3FFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_GOP_CONFIG2);
+}
+
+static void mfc_set_default_params(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       /* Default setting for quality */
+       /* Common Registers */
+       MFC_WRITEL(0x0, S5P_FIMV_E_ENC_OPTIONS);
+       MFC_WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP);
+       MFC_WRITEL(0x100, S5P_FIMV_E_MV_HOR_RANGE);
+       MFC_WRITEL(0x100, S5P_FIMV_E_MV_VER_RANGE);
+       MFC_WRITEL(0x0, S5P_FIMV_E_IR_SIZE);
+       MFC_WRITEL(0x0, S5P_FIMV_E_AIR_THRESHOLD);
+       MFC_WRITEL(0x1E, S5P_FIMV_E_GOP_CONFIG); /* I frame period: 30 */
+       MFC_WRITEL(0x0, S5P_FIMV_E_GOP_CONFIG2);
+       MFC_WRITEL(0x0, S5P_FIMV_E_MSLICE_MODE);
+
+       /* Hierarchical Coding */
+       MFC_WRITEL(0x8, S5P_FIMV_E_NUM_T_LAYER);
+
+       /* Rate Control */
+       MFC_WRITEL(0x4000, S5P_FIMV_E_RC_CONFIG);
+       MFC_WRITEL(0x0, S5P_FIMV_E_RC_QP_BOUND);
+       MFC_WRITEL(0x0, S5P_FIMV_E_RC_QP_BOUND_PB);
+       MFC_WRITEL(0x12, S5P_FIMV_E_RC_MODE);
+       MFC_WRITEL(0x1E0001, S5P_FIMV_E_RC_FRAME_RATE); /* framerate: 30 fps */
+       MFC_WRITEL(0xF4240, S5P_FIMV_E_RC_BIT_RATE); /* bitrate: 1000000 */
+       MFC_WRITEL(0x3FD00, S5P_FIMV_E_RC_ROI_CTRL);
+       MFC_WRITEL(0x0, S5P_FIMV_E_VBV_BUFFER_SIZE);
+       MFC_WRITEL(0x0, S5P_FIMV_E_VBV_INIT_DELAY);
+       MFC_WRITEL(0x0, S5P_FIMV_E_BIT_COUNT_ENABLE);
+       MFC_WRITEL(0x2710, S5P_FIMV_E_MAX_BIT_COUNT); /* max bit count: 10000 */
+       MFC_WRITEL(0x3E8, S5P_FIMV_E_MIN_BIT_COUNT); /* min bit count: 1000 */
+       /*
+        * If the high quality mode is used, the performance falls by half
+        * If the high quality mode is used, NAL-Q is not supported
+        */
+       MFC_WRITEL(0x0, S5P_FIMV_E_HIGH_QUALITY_MODE);
+       MFC_WRITEL(0x0, S5P_FIMV_E_WEIGHT_FOR_WEIGHTED_PREDICTION);
+
+       /* HEVC */
+       MFC_WRITEL(0x8050F215, S5P_FIMV_E_HEVC_OPTIONS);
+       MFC_WRITEL(0x0, S5P_FIMV_E_HEVC_REFRESH_PERIOD);
+       MFC_WRITEL(0x0, S5P_FIMV_E_HEVC_CHROMA_QP_OFFSET);
+       MFC_WRITEL(0x0, S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2);
+       MFC_WRITEL(0x0, S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2);
+       MFC_WRITEL(0x0, S5P_FIMV_E_SAO_WEIGHT0);
+       MFC_WRITEL(0x0, S5P_FIMV_E_SAO_WEIGHT1);
+
+       /* H.264 */
+       MFC_WRITEL(0x3011, S5P_FIMV_E_H264_OPTIONS);
+       MFC_WRITEL(0x0, S5P_FIMV_E_H264_OPTIONS_2);
+       MFC_WRITEL(0x0, S5P_FIMV_E_H264_LF_ALPHA_OFFSET);
+       MFC_WRITEL(0x0, S5P_FIMV_E_H264_LF_BETA_OFFSET);
+       MFC_WRITEL(0x0, S5P_FIMV_E_H264_REFRESH_PERIOD);
+       MFC_WRITEL(0x0, S5P_FIMV_E_H264_CHROMA_QP_OFFSET);
+
+       /* VP8 */
+       MFC_WRITEL(0x0, S5P_FIMV_E_VP8_OPTION);
+       MFC_WRITEL(0x0, S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION);
+
+       /* VP9 */
+       MFC_WRITEL(0x2D, S5P_FIMV_E_VP9_OPTION);
+       MFC_WRITEL(0xA00, S5P_FIMV_E_VP9_FILTER_OPTION);
+       MFC_WRITEL(0x3C, S5P_FIMV_E_VP9_GOLDEN_FRAME_OPTION);
+
+       /* BPG */
+       MFC_WRITEL(0x961, S5P_FIMV_E_BPG_OPTIONS);
+       MFC_WRITEL(0x6FA00, S5P_FIMV_E_BPG_EXT_CTB_QP_CTRL);
+       MFC_WRITEL(0x0, S5P_FIMV_E_BPG_CHROMA_QP_OFFSET);
+
+       /* MVC */
+       MFC_WRITEL(0x1D, S5P_FIMV_E_MVC_FRAME_QP_VIEW1); /* QP: 29 */
+       MFC_WRITEL(0xF4240, S5P_FIMV_E_MVC_RC_BIT_RATE_VIEW1); /* bitrate: 1000000 */
+       MFC_WRITEL(0x33003300, S5P_FIMV_E_MVC_RC_QBOUND_VIEW1); /* max I, P QP: 51 */
+       MFC_WRITEL(0x2, S5P_FIMV_E_MVC_RC_MODE_VIEW1);
+       MFC_WRITEL(0x1, S5P_FIMV_E_MVC_INTER_VIEW_PREDICTION_ON);
+
+       /* Additional initialization: NAL start only */
+       MFC_WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION);
+       MFC_WRITEL(0x0, S5P_FIMV_E_ROI_BUFFER_ADDR);
+       MFC_WRITEL(0x0, S5P_FIMV_E_PARAM_CHANGE);
+       MFC_WRITEL(0x0, S5P_FIMV_E_PICTURE_TAG);
+       MFC_WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_ADDR);
+       MFC_WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_SIZE);
+}
+
+static void mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       unsigned int reg = 0;
+
+       mfc_debug_enter();
+
+       mfc_set_default_params(ctx);
+
+       /* width */
+       MFC_WRITEL(ctx->img_width, S5P_FIMV_E_FRAME_WIDTH); /* 16 align */
+       /* height */
+       MFC_WRITEL(ctx->img_height, S5P_FIMV_E_FRAME_HEIGHT); /* 16 align */
+       /** cropped width */
+       MFC_WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH);
+       /** cropped height */
+       MFC_WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT);
+       /** cropped offset */
+       MFC_WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET);
+
+       /* multi-slice control */
+       /* multi-slice MB number or bit size */
+       enc->slice_mode = p->slice_mode;
+
+       if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+               enc->slice_size.mb = p->slice_mb;
+       } else if ((p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) ||
+                       (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES)){
+               enc->slice_size.bits = p->slice_bit;
+       } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW) {
+               enc->slice_size.mb = p->slice_mb_row * ((ctx->img_width + 15) / 16);
+       } else {
+               enc->slice_size.mb = 0;
+               enc->slice_size.bits = 0;
+       }
+
+       s5p_mfc_set_slice_mode(ctx);
+
+       /* cyclic intra refresh */
+       MFC_WRITEL(p->intra_refresh_mb, S5P_FIMV_E_IR_SIZE);
+
+       reg = MFC_READL(S5P_FIMV_E_ENC_OPTIONS);
+       /* frame skip mode */
+       reg |= (p->frame_skip_mode & S5P_FIMV_E_ENC_OPT_FRAME_SKIP_EN_MASK);
+       /* seq header ctrl */
+       reg |= ((p->seq_hdr_mode & S5P_FIMV_E_ENC_OPT_SEQ_HEADER_CONTROL_MASK)
+                       << S5P_FIMV_E_ENC_OPT_SEQ_HEADER_CONTROL_SHIFT);
+       /* enable seq header generation */
+       reg &= ~(0x1 << S5P_FIMV_E_ENC_OPT_DISABLE_SEQ_HEADER_SHIFT);
+       /* disable seq header generation */
+       if (ctx->otf_handle) {
+               reg |= (0x1 << S5P_FIMV_E_ENC_OPT_DISABLE_SEQ_HEADER_SHIFT);
+               mfc_debug(2, "OTF: SEQ_HEADER_GENERATION is disabled\n");
+       }
+       /* cyclic intra refresh */
+       if (p->intra_refresh_mb)
+               reg |= (0x1 << S5P_FIMV_E_ENC_OPT_IR_MODE_SHIFT);
+       /* 'NON_REFERENCE_STORE_ENABLE' for debugging */
+       reg &= ~(0x1 << S5P_FIMV_E_ENC_OPT_NON_REFERENCE_EN_SHIFT);
+       /* Disable parallel processing if nal_q_parallel_disable was set */
+       if (nal_q_parallel_disable)
+               reg |= 0x1 << S5P_FIMV_E_ENC_OPT_PARALLEL_DISABLE_SHIFT;
+       MFC_WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS);
+
+       s5p_mfc_set_pixel_format(dev, ctx->src_fmt->fourcc);
+
+       /* padding control & value */
+       MFC_WRITEL(0x0, S5P_FIMV_E_PADDING_CTRL);
+       if (p->pad) {
+               reg = 0;
+               /** enable */
+               reg |= (1 << 31);
+               /** cr value */
+               reg &= ~(0xFF << 16);
+               reg |= (p->pad_cr << 16);
+               /** cb value */
+               reg &= ~(0xFF << 8);
+               reg |= (p->pad_cb << 8);
+               /** y value */
+               reg &= ~(0xFF);
+               reg |= (p->pad_luma);
+               MFC_WRITEL(reg, S5P_FIMV_E_PADDING_CTRL);
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /* macroblock level rate control */
+       reg &= ~(0x1 << 8);
+       reg |= ((p->rc_mb & 0x1) << 8);
+       /* frame-level rate control */
+       reg &= ~(0x1 << 9);
+       reg |= ((p->rc_frame & 0x1) << 9);
+       /* 'DROP_CONTROL_ENABLE', disable */
+       reg &= ~(0x1 << 10);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* bit rate */
+       if (p->rc_bitrate)
+               MFC_WRITEL(p->rc_bitrate, S5P_FIMV_E_RC_BIT_RATE);
+
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_MODE);
+               reg &= ~(0x3);
+
+               if (p->rc_reaction_coeff <= CBR_I_LIMIT_MAX) {
+                       reg |= S5P_FIMV_E_RC_CBR_I_LIMIT;
+                       /*
+                        * Ratio of intra for max frame size
+                        * is controled when only CBR_I_LIMIT mode.
+                        * And CBR_I_LIMIT mode is valid for H.264, HEVC codec
+                        */
+                       if (p->ratio_intra) {
+                               reg &= ~(0xFF << 8);
+                               reg |= ((p->ratio_intra & 0xff) << 8);
+                       }
+               } else if (p->rc_reaction_coeff <= CBR_FIX_MAX) {
+                       reg |= S5P_FIMV_E_RC_CBR_FIX;
+               } else {
+                       reg |= S5P_FIMV_E_RC_VBR;
+               }
+
+               if (p->rc_mb) {
+                       reg &= ~(0x3 << 4);
+                       reg |= ((p->rc_pvc & 0x3) << 4);
+               }
+
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_MODE);
+       }
+
+       /* extended encoder ctrl */
+       /** vbv buffer size */
+       if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+               reg = MFC_READL(S5P_FIMV_E_VBV_BUFFER_SIZE);
+               reg &= ~(0xFF);
+               reg |= p->vbv_buf_size & 0xFF;
+               MFC_WRITEL(reg, S5P_FIMV_E_VBV_BUFFER_SIZE);
+       }
+
+       mfc_debug_leave();
+}
+
+static void mfc_set_temporal_svc_h264(struct s5p_mfc_ctx *ctx, struct s5p_mfc_h264_enc_params *p_264)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       unsigned int reg = 0, reg2 = 0;
+       int i;
+
+       reg = MFC_READL(S5P_FIMV_E_H264_OPTIONS_2);
+       /* pic_order_cnt_type = 0 for backward compatibilities */
+       reg &= ~(0x3);
+       /* Enable LTR */
+       reg &= ~(0x1 << 2);
+       if ((p_264->enable_ltr & 0x1) || (p_264->num_of_ltr > 0))
+               reg |= (0x1 << 2);
+       /* Number of LTR */
+       reg &= ~(0x3 << 7);
+       if (p_264->num_of_ltr > 2)
+               reg |= (((p_264->num_of_ltr - 2) & 0x3) << 7);
+       MFC_WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_2);
+
+       /* Temporal SVC - qp type, layer number */
+       reg = MFC_READL(S5P_FIMV_E_NUM_T_LAYER);
+       reg &= ~(0x1 << 3);
+       reg |= (p_264->hier_qp_type & 0x1) << 3;
+       reg &= ~(0x7);
+       reg |= p_264->num_hier_layer & 0x7;
+       reg &= ~(0x7 << 4);
+       if (p_264->hier_ref_type) {
+               reg |= 0x1 << 7;
+               reg |= (p->num_hier_max_layer & 0x7) << 4;
+       } else {
+               reg |= 0x7 << 4;
+       }
+       MFC_WRITEL(reg, S5P_FIMV_E_NUM_T_LAYER);
+       mfc_debug(3, "Temporal SVC: hier_qp_enable %d, enable_ltr %d, "
+               "num_hier_layer %d, max_layer %d, hier_ref_type %d, NUM_T_LAYER 0x%x\n",
+               p_264->hier_qp_enable, p_264->enable_ltr, p_264->num_hier_layer,
+               p->num_hier_max_layer, p_264->hier_ref_type, reg);
+       /* QP & Bitrate for each layer */
+       for (i = 0; i < 7; i++) {
+               MFC_WRITEL(p_264->hier_qp_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_QP_LAYER0 + i * 4);
+               MFC_WRITEL(p_264->hier_bit_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4);
+               mfc_debug(3, "Temporal SVC: layer[%d] QP: %#x, bitrate: %d\n",
+                                       i, p_264->hier_qp_layer[i],
+                                       p_264->hier_bit_layer[i]);
+       }
+       if (p_264->set_priority) {
+               reg = 0;
+               reg2 = 0;
+               for (i = 0; i < (p_264->num_hier_layer & 0x7); i++) {
+                       if (i <= 4)
+                               reg |= ((p_264->base_priority & 0x3F) + i) << (6 * i);
+                       else
+                               reg2 |= ((p_264->base_priority & 0x3F) + i) << (6 * (i - 5));
+               }
+               MFC_WRITEL(reg, S5P_FIMV_E_H264_HD_SVC_EXTENSION_0);
+               MFC_WRITEL(reg2, S5P_FIMV_E_H264_HD_SVC_EXTENSION_1);
+               mfc_debug(3, "Temporal SVC: priority EXTENSION0: %#x, EXTENSION1: %#x\n",
+                                                       reg, reg2);
+       }
+}
+
+static void mfc_set_fmo_slice_map_h264(struct s5p_mfc_ctx *ctx, struct s5p_mfc_h264_enc_params *p_264)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int i;
+
+       if (p_264->fmo_enable) {
+               switch (p_264->fmo_slice_map_type) {
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES:
+                       if (p_264->fmo_slice_num_grp > 4)
+                               p_264->fmo_slice_num_grp = 4;
+                       for (i = 0; i < (p_264->fmo_slice_num_grp & 0xF); i++)
+                               MFC_WRITEL(p_264->fmo_run_length[i] - 1,
+                               S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0 + i*4);
+                       break;
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES:
+                       if (p_264->fmo_slice_num_grp > 4)
+                               p_264->fmo_slice_num_grp = 4;
+                       break;
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN:
+               case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN:
+                       if (p_264->fmo_slice_num_grp > 2)
+                               p_264->fmo_slice_num_grp = 2;
+                       MFC_WRITEL(p_264->fmo_sg_dir & 0x1,
+                               S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR);
+                       /* the valid range is 0 ~ number of macroblocks -1 */
+                       MFC_WRITEL(p_264->fmo_sg_rate, S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1);
+                       break;
+               default:
+                       mfc_err_ctx("Unsupported map type for FMO: %d\n",
+                                       p_264->fmo_slice_map_type);
+                       p_264->fmo_slice_map_type = 0;
+                       p_264->fmo_slice_num_grp = 1;
+                       break;
+               }
+
+               MFC_WRITEL(p_264->fmo_slice_map_type, S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE);
+               MFC_WRITEL(p_264->fmo_slice_num_grp - 1, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1);
+       } else {
+               MFC_WRITEL(0, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1);
+       }
+}
+
+void s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+       unsigned int reg = 0;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       if (p_264->num_hier_layer & 0x7) {
+               /* set gop_size without i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 0);
+       } else {
+               /* set gop_size with i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 1);
+       }
+
+       /* UHD encoding case */
+       if(IS_UHD_RES(ctx)) {
+               if (p_264->level < 51) {
+                       mfc_info_ctx("Set Level 5.1 for UHD\n");
+                       p_264->level = 51;
+               }
+               if (p_264->profile != 0x2) {
+                       mfc_info_ctx("Set High profile for UHD\n");
+                       p_264->profile = 0x2;
+               }
+       }
+
+       /* profile & level */
+       reg = 0;
+       /** level */
+       reg &= ~(0xFF << 8);
+       reg |= (p_264->level << 8);
+       /** profile - 0 ~ 3 */
+       reg &= ~(0x3F);
+       reg |= p_264->profile;
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+
+       reg = MFC_READL(S5P_FIMV_E_H264_OPTIONS);
+       /* entropy coding mode */
+       reg &= ~(0x1);
+       reg |= (p_264->entropy_mode & 0x1);
+       /* loop filter ctrl */
+       reg &= ~(0x3 << 1);
+       reg |= ((p_264->loop_filter_mode & 0x3) << 1);
+       /* interlace */
+       reg &= ~(0x1 << 3);
+       reg |= ((p_264->interlace & 0x1) << 3);
+       /* intra picture period for H.264 open GOP */
+       reg &= ~(0x1 << 4);
+       reg |= ((p_264->open_gop & 0x1) << 4);
+       /* extended encoder ctrl */
+       reg &= ~(0x1 << 5);
+       reg |= ((p_264->ar_vui & 0x1) << 5);
+       /* ASO enable */
+       reg &= ~(0x1 << 6);
+       reg |= ((p_264->aso_enable & 0x1) << 6);
+       /* if num_refs_for_p is 2, the performance falls by half */
+       reg &= ~(0x1 << 7);
+       reg |= (((p->num_refs_for_p - 1) & 0x1) << 7);
+       /* Temporal SVC - hier qp enable */
+       reg &= ~(0x1 << 8);
+       reg |= ((p_264->hier_qp_enable & 0x1) << 8);
+       /* Weighted Prediction enable */
+       reg &= ~(0x3 << 9);
+       reg |= ((p->weighted_enable & 0x1) << 9);
+       /* 8x8 transform enable */
+       reg &= ~(0x1 << 12);
+       reg &= ~(0x1 << 13);
+       reg |= ((p_264->_8x8_transform & 0x1) << 12);
+       reg |= ((p_264->_8x8_transform & 0x1) << 13);
+       /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */
+       reg &= ~(0x1 << 14);
+       /*
+        * CONSTRAINT_SET0_FLAG: all constraints specified in
+        * Baseline Profile
+        */
+       reg |= (0x1 << 26);
+       /* sps pps control */
+       reg &= ~(0x1 << 29);
+       reg |= ((p_264->prepend_sps_pps_to_idr & 0x1) << 29);
+       /* enable sps pps control in OTF scenario */
+       if (ctx->otf_handle) {
+               reg |= (0x1 << 29);
+               mfc_debug(2, "OTF: SPS_PPS_CONTROL enabled\n");
+       }
+       /* VUI parameter disable */
+       reg &= ~(0x1 << 30);
+       reg |= ((p_264->vui_enable & 0x1) << 30);
+       MFC_WRITEL(reg, S5P_FIMV_E_H264_OPTIONS);
+
+       /** height */
+       if (p_264->interlace) {
+               MFC_WRITEL(ctx->img_height >> 1, S5P_FIMV_E_FRAME_HEIGHT); /* 32 align */
+               /** cropped height */
+               MFC_WRITEL(ctx->img_height >> 1, S5P_FIMV_E_CROPPED_FRAME_HEIGHT);
+       }
+
+       /* loopfilter alpha offset */
+       reg = MFC_READL(S5P_FIMV_E_H264_LF_ALPHA_OFFSET);
+       reg &= ~(0x1F);
+       reg |= (p_264->loop_filter_alpha & 0x1F);
+       MFC_WRITEL(reg, S5P_FIMV_E_H264_LF_ALPHA_OFFSET);
+
+       /* loopfilter beta offset */
+       reg = MFC_READL(S5P_FIMV_E_H264_LF_BETA_OFFSET);
+       reg &= ~(0x1F);
+       reg |= (p_264->loop_filter_beta & 0x1F);
+       MFC_WRITEL(reg, S5P_FIMV_E_H264_LF_BETA_OFFSET);
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_264->rc_frame_qp & 0xFF);
+       if (!p->rc_frame && !p->rc_mb && p->dynamic_qp)
+               reg |= (0x1 << 11);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* frame rate */
+       /* Fix value for H.264, H.263 in the driver */
+       p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= p_264->rc_framerate << 16;
+               reg &= ~(0xFFFF);
+               reg |= p->rc_frame_delta & 0xFFFF;
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       }
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_264->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= p_264->rc_min_qp & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max B frame QP */
+       reg &= ~(0xFF << 24);
+       reg |= ((p_264->rc_max_qp_b & 0xFF) << 24);
+       /** min B frame QP */
+       reg &= ~(0xFF << 16);
+       reg |= ((p_264->rc_min_qp_b & 0xFF) << 16);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_264->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_264->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 16);
+       reg |= ((p_264->rc_b_frame_qp & 0xFF) << 16);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_264->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_264->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       MFC_WRITEL(0x0, S5P_FIMV_E_ASPECT_RATIO);
+       MFC_WRITEL(0x0, S5P_FIMV_E_EXTENDED_SAR);
+       if (p_264->ar_vui) {
+               /* aspect ration IDC */
+               reg = 0;
+               reg &= ~(0xff);
+               reg |= p_264->ar_vui_idc;
+               MFC_WRITEL(reg, S5P_FIMV_E_ASPECT_RATIO);
+               if (p_264->ar_vui_idc == 0xFF) {
+                       /* sample  AR info. */
+                       reg = 0;
+                       reg &= ~(0xffffffff);
+                       reg |= p_264->ext_sar_width << 16;
+                       reg |= p_264->ext_sar_height;
+                       MFC_WRITEL(reg, S5P_FIMV_E_EXTENDED_SAR);
+               }
+       }
+       /* intra picture period for H.264 open GOP, value */
+       if (p_264->open_gop) {
+               reg = MFC_READL(S5P_FIMV_E_H264_REFRESH_PERIOD);
+               reg &= ~(0xFFFF);
+               reg |= (p_264->open_gop_size & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_H264_REFRESH_PERIOD);
+       }
+
+       /* Temporal SVC */
+       mfc_set_temporal_svc_h264(ctx, p_264);
+
+       /* set frame pack sei generation */
+       if (p_264->sei_gen_enable) {
+               /* frame packing enable */
+               reg = MFC_READL(S5P_FIMV_E_H264_OPTIONS);
+               reg |= (1 << 25);
+               MFC_WRITEL(reg, S5P_FIMV_E_H264_OPTIONS);
+
+               /* set current frame0 flag & arrangement type */
+               reg = 0;
+               /** current frame0 flag */
+               reg |= ((p_264->sei_fp_curr_frame_0 & 0x1) << 2);
+               /** arrangement type */
+               reg |= (p_264->sei_fp_arrangement_type - 3) & 0x3;
+               MFC_WRITEL(reg, S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO);
+       }
+
+       mfc_set_fmo_slice_map_h264(ctx, p_264);
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+       unsigned int reg = 0;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       /* set gop_size with I_FRM_CTRL mode */
+       mfc_set_gop_size(ctx, 1);
+
+       /* profile & level */
+       reg = 0;
+       /** level */
+       reg &= ~(0xFF << 8);
+       reg |= (p_mpeg4->level << 8);
+       /** profile - 0 ~ 1 */
+       reg &= ~(0x3F);
+       reg |= p_mpeg4->profile;
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+
+       /* quarter_pixel */
+       /* MFC_WRITEL(p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL); */
+
+       /* qp */
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 16);
+       reg |= ((p_mpeg4->rc_b_frame_qp & 0xFF) << 16);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       /* frame rate */
+       if (p->rc_frame) {
+               p->rc_frame_delta = p_mpeg4->vop_frm_delta;
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= (p_mpeg4->vop_time_res << 16);
+               reg &= ~(0xFFFF);
+               reg |= (p_mpeg4->vop_frm_delta & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       } else {
+               p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_min_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max B frame QP */
+       reg &= ~(0xFF << 24);
+       reg |= ((p_mpeg4->rc_max_qp_b & 0xFF) << 24);
+       /** min B frame QP */
+       reg &= ~(0xFF << 16);
+       reg |= ((p_mpeg4->rc_min_qp_b & 0xFF) << 16);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_mpeg4->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       /* initialize for '0' only setting*/
+       MFC_WRITEL(0x0, S5P_FIMV_E_MPEG4_OPTIONS); /* SEQ_start only */
+       MFC_WRITEL(0x0, S5P_FIMV_E_MPEG4_HEC_PERIOD); /* SEQ_start only */
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+       unsigned int reg = 0;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       /* set gop_size with I_FRM_CTRL mode */
+       mfc_set_gop_size(ctx, 1);
+
+       /* profile & level: supports only baseline profile Level 70 */
+
+       /* qp */
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       /* frame rate */
+       /* Fix value for H.264, H.263 in the driver */
+       p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= (p_mpeg4->rc_framerate << 16);
+               reg &= ~(0xFFFF);
+               reg |= (p->rc_frame_delta & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_mpeg4->rc_min_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_mpeg4->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_mpeg4->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;
+       unsigned int reg = 0;
+       int i;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       if (p_vp8->num_hier_layer & 0x3) {
+               /* set gop_size without i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 0);
+       } else {
+               /* set gop_size with i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 1);
+       }
+
+       /* profile*/
+       reg = 0;
+       reg |= (p_vp8->vp8_version) ;
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+
+       reg = MFC_READL(S5P_FIMV_E_VP8_OPTION);
+       /* if num_refs_for_p is 2, the performance falls by half */
+       reg &= ~(0x1);
+       reg |= (p->num_refs_for_p - 1) & 0x1;
+       /* vp8 partition is possible as below value: 1/2/4/8 */
+       if (p_vp8->vp8_numberofpartitions & 0x1) {
+               if (p_vp8->vp8_numberofpartitions > 1)
+                       mfc_err_ctx("partition should be even num (%d)\n",
+                                       p_vp8->vp8_numberofpartitions);
+               p_vp8->vp8_numberofpartitions = (p_vp8->vp8_numberofpartitions & ~0x1);
+       }
+       reg &= ~(0xF << 3);
+       reg |= (p_vp8->vp8_numberofpartitions & 0xF) << 3;
+       reg &= ~(0x1 << 10);
+       reg |= (p_vp8->intra_4x4mode_disable & 0x1) << 10;
+       /* Temporal SVC - hier qp enable */
+       reg &= ~(0x1 << 11);
+       reg |= (p_vp8->hier_qp_enable & 0x1) << 11;
+       /* Disable IVF header */
+       reg &= ~(0x1 << 12);
+       reg |= ((p->ivf_header_disable & 0x1) << 12);
+       MFC_WRITEL(reg, S5P_FIMV_E_VP8_OPTION);
+
+       reg = MFC_READL(S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION);
+       reg &= ~(0x1);
+       reg |= (p_vp8->vp8_goldenframesel & 0x1);
+       reg &= ~(0xFFFF << 1);
+       reg |= (p_vp8->vp8_gfrefreshperiod & 0xFFFF) << 1;
+       MFC_WRITEL(reg, S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION);
+
+       /* Temporal SVC - layer number */
+       reg = MFC_READL(S5P_FIMV_E_NUM_T_LAYER);
+       reg &= ~(0x7);
+       reg |= p_vp8->num_hier_layer & 0x3;
+       reg &= ~(0x7 << 4);
+       reg |= 0x3 << 4;
+       MFC_WRITEL(reg, S5P_FIMV_E_NUM_T_LAYER);
+       mfc_debug(3, "Temporal SVC: hier_qp_enable %d, num_hier_layer %d, NUM_T_LAYER 0x%x\n",
+                       p_vp8->hier_qp_enable, p_vp8->num_hier_layer, reg);
+
+       /* QP & Bitrate for each layer */
+       for (i = 0; i < 3; i++) {
+               MFC_WRITEL(p_vp8->hier_qp_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_QP_LAYER0 + i * 4);
+               MFC_WRITEL(p_vp8->hier_bit_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4);
+               mfc_debug(3, "Temporal SVC: layer[%d] QP: %#x, bitrate: %#x\n",
+                                       i, p_vp8->hier_qp_layer[i],
+                                       p_vp8->hier_bit_layer[i]);
+       }
+
+       reg = 0;
+       reg |= (p_vp8->vp8_filtersharpness & 0x7);
+       reg |= (p_vp8->vp8_filterlevel & 0x3f) << 8;
+       MFC_WRITEL(reg, S5P_FIMV_E_VP8_FILTER_OPTION);
+
+       /* qp */
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp8->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_vp8->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       /* frame rate */
+       p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= (p_vp8->rc_framerate << 16);
+               reg &= ~(0xFFFF);
+               reg |= (p->rc_frame_delta & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_vp8->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp8->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_vp8->rc_min_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp8->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_vp8->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_vp9(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_vp9_enc_params *p_vp9 = &p->codec.vp9;
+       unsigned int reg = 0;
+       int i;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       if (p_vp9->num_hier_layer & 0x3) {
+               /* set gop_size without i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 0);
+       } else {
+               /* set gop_size with i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 1);
+       }
+
+       /* profile*/
+       reg = 0;
+       reg |= (p_vp9->vp9_version) ;
+       /* bit depth minus8 */
+       if (ctx->is_10bit) {
+               reg &= ~(0x3F << 17);
+               reg |= (0x2 << 17);
+               reg |= (0x2 << 20);
+               /* fixed profile */
+               reg |= 0x2;
+       }
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+
+       reg = MFC_READL(S5P_FIMV_E_VP9_OPTION);
+       /* if num_refs_for_p is 2, the performance falls by half */
+       reg &= ~(0x1);
+       reg |= (p->num_refs_for_p - 1) & 0x1;
+       reg &= ~(0x1 << 1);
+       reg |= (p_vp9->intra_pu_split_disable & 0x1) << 1;
+       reg &= ~(0x1 << 3);
+       reg |= (p_vp9->max_partition_depth & 0x1) << 3;
+       /* Temporal SVC - hier qp enable */
+       reg &= ~(0x1 << 11);
+       reg |= ((p_vp9->hier_qp_enable & 0x1) << 11);
+       /* Disable IVF header */
+       reg &= ~(0x1 << 12);
+       reg |= ((p->ivf_header_disable & 0x1) << 12);
+       MFC_WRITEL(reg, S5P_FIMV_E_VP9_OPTION);
+
+       reg = MFC_READL(S5P_FIMV_E_VP9_GOLDEN_FRAME_OPTION);
+       reg &= ~(0x1);
+       reg |= (p_vp9->vp9_goldenframesel & 0x1);
+       reg &= ~(0xFFFF << 1);
+       reg |= (p_vp9->vp9_gfrefreshperiod & 0xFFFF) << 1;
+       MFC_WRITEL(reg, S5P_FIMV_E_VP9_GOLDEN_FRAME_OPTION);
+
+       /* Temporal SVC - layer number */
+       reg = MFC_READL(S5P_FIMV_E_NUM_T_LAYER);
+       reg &= ~(0x7);
+       reg |= p_vp9->num_hier_layer & 0x3;
+       reg &= ~(0x7 << 4);
+       reg |= 0x3 << 4;
+       MFC_WRITEL(reg, S5P_FIMV_E_NUM_T_LAYER);
+       mfc_debug(3, "Temporal SVC: hier_qp_enable %d, num_hier_layer %d, NUM_T_LAYER 0x%x\n",
+                       p_vp9->hier_qp_enable, p_vp9->num_hier_layer, reg);
+
+       /* QP & Bitrate for each layer */
+       for (i = 0; i < 3; i++) {
+               MFC_WRITEL(p_vp9->hier_qp_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_QP_LAYER0 + i * 4);
+               MFC_WRITEL(p_vp9->hier_bit_layer[i],
+                               S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4);
+               mfc_debug(3, "Temporal SVC: layer[%d] QP: %#x, bitrate: %#x\n",
+                                       i, p_vp9->hier_qp_layer[i],
+                                       p_vp9->hier_bit_layer[i]);
+       }
+
+       /* qp */
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp9->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_vp9->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       /* frame rate */
+       p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= (p_vp9->rc_framerate << 16);
+               reg &= ~(0xFFFF);
+               reg |= (p->rc_frame_delta & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_vp9->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp9->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_vp9->rc_min_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_vp9->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_vp9->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_hevc(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_hevc_enc_params *p_hevc = &p->codec.hevc;
+       unsigned int reg = 0;
+       int i;
+
+       mfc_debug_enter();
+
+       mfc_set_enc_params(ctx);
+
+       if (p_hevc->num_hier_layer & 0x7) {
+               /* set gop_size without i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 0);
+       } else {
+               /* set gop_size with i_frm_ctrl mode */
+               mfc_set_gop_size(ctx, 1);
+       }
+
+       /* UHD encoding case */
+       if ((ctx->img_width == 3840) && (ctx->img_height == 2160)) {
+               p_hevc->level = 51;
+               p_hevc->tier_flag = 0;
+       /* this tier_flag can be changed */
+       }
+
+       /* tier_flag & level & profile */
+       reg = 0;
+       /* profile */
+       reg |= p_hevc->profile & 0x3;
+       /* level */
+       reg &= ~(0xFF << 8);
+       reg |= (p_hevc->level << 8);
+       /* tier_flag - 0 ~ 1 */
+       reg |= (p_hevc->tier_flag << 16);
+       /* bit depth minus8 */
+       if (ctx->is_10bit) {
+               reg &= ~(0x3F << 17);
+               reg |= (0x2 << 17);
+               reg |= (0x2 << 20);
+               /* fixed profile */
+               if (ctx->is_422format)
+                       reg |= 0x2;
+               else
+                       reg |= 0x3;
+       }
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+
+       /* max partition depth */
+       reg = MFC_READL(S5P_FIMV_E_HEVC_OPTIONS);
+       reg &= ~(0x3);
+       reg |= (p_hevc->max_partition_depth & 0x1);
+       /* if num_refs_for_p is 2, the performance falls by half */
+       reg &= ~(0x1 << 2);
+       reg |= (p->num_refs_for_p - 1) << 2;
+       reg &= ~(0x3 << 3);
+       reg |= (p_hevc->refreshtype & 0x3) << 3;
+       reg &= ~(0x1 << 5);
+       reg |= (p_hevc->const_intra_period_enable & 0x1) << 5;
+       reg &= ~(0x1 << 6);
+       reg |= (p_hevc->lossless_cu_enable & 0x1) << 6;
+       reg &= ~(0x1 << 7);
+       reg |= (p_hevc->wavefront_enable & 0x1) << 7;
+       reg &= ~(0x1 << 8);
+       reg |= (p_hevc->loopfilter_disable & 0x1) << 8;
+       reg &= ~(0x1 << 9);
+       reg |= (p_hevc->loopfilter_across & 0x1) << 9;
+       reg &= ~(0x1 << 10);
+       reg |= (p_hevc->enable_ltr & 0x1) << 10;
+       reg &= ~(0x1 << 11);
+       reg |= (p_hevc->hier_qp_enable & 0x1) << 11;
+       reg &= ~(0x1 << 13);
+       reg |= (p_hevc->general_pb_enable & 0x1) << 13;
+       reg &= ~(0x1 << 14);
+       reg |= (p_hevc->temporal_id_enable & 0x1) << 14;
+       reg &= ~(0x1 << 15);
+       reg |= (p_hevc->strong_intra_smooth & 0x1) << 15;
+       reg &= ~(0x1 << 16);
+       reg |= (p_hevc->intra_pu_split_disable & 0x1) << 16;
+       reg &= ~(0x1 << 17);
+       reg |= (p_hevc->tmv_prediction_disable & 0x1) << 17;
+       reg &= ~(0x7 << 18);
+       reg |= (p_hevc->max_num_merge_mv & 0x7) << 18;
+       reg &= ~(0x1 << 23);
+       reg |= (p_hevc->encoding_nostartcode_enable & 0x1) << 23;
+       reg &= ~(0x1 << 26);
+       reg |= (p_hevc->prepend_sps_pps_to_idr & 0x1) << 26;
+       /* enable sps pps control in OTF scenario */
+       if (ctx->otf_handle) {
+               reg |= (0x1 << 26);
+               mfc_debug(2, "OTF: SPS_PPS_CONTROL enabled\n");
+       }
+       /* Weighted Prediction enable */
+       reg &= ~(0x1 << 28);
+       reg |= ((p->weighted_enable & 0x1) << 28);
+       /* 30bit is 32x32 transform. If it is enabled, the performance falls by half */
+       reg &= ~(0x1 << 30);
+       MFC_WRITEL(reg, S5P_FIMV_E_HEVC_OPTIONS);
+       /* refresh period */
+       reg = MFC_READL(S5P_FIMV_E_HEVC_REFRESH_PERIOD);
+       reg &= ~(0xFFFF);
+       reg |= (p_hevc->refreshperiod & 0xFFFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_HEVC_REFRESH_PERIOD);
+       /* loop filter setting */
+       if (!p_hevc->loopfilter_disable) {
+               MFC_WRITEL(p_hevc->lf_beta_offset_div2, S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2);
+               MFC_WRITEL(p_hevc->lf_tc_offset_div2, S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2);
+       }
+       /* long term reference */
+       if (p_hevc->enable_ltr) {
+               reg = 0;
+               reg |= (p_hevc->store_ref & 0x3);
+               reg &= ~(0x3 << 2);
+               reg |= (p_hevc->user_ref & 0x3) << 2;
+               MFC_WRITEL(reg, S5P_FIMV_E_HEVC_NAL_CONTROL);
+       }
+
+       /* Temporal SVC - qp type, layer number */
+       reg = MFC_READL(S5P_FIMV_E_NUM_T_LAYER);
+       reg &= ~(0x1 << 3);
+       reg |= (p_hevc->hier_qp_type & 0x1) << 3;
+       reg &= ~(0x7);
+       reg |= p_hevc->num_hier_layer & 0x7;
+       reg &= ~(0x7 << 4);
+       if (p_hevc->hier_ref_type) {
+               reg |= 0x1 << 7;
+               reg |= (p->num_hier_max_layer & 0x7) << 4;
+       } else {
+               reg |= 0x7 << 4;
+       }
+       MFC_WRITEL(reg, S5P_FIMV_E_NUM_T_LAYER);
+       mfc_debug(2, "Temporal SVC: hier_qp_enable %d, enable_ltr %d, "
+               "num_hier_layer %d, max_layer %d, hier_ref_type %d, NUM_T_LAYER 0x%x\n",
+               p_hevc->hier_qp_enable, p_hevc->enable_ltr, p_hevc->num_hier_layer,
+               p->num_hier_max_layer, p_hevc->hier_ref_type, reg);
+
+       /* QP & Bitrate for each layer */
+       for (i = 0; i < 7; i++) {
+               MFC_WRITEL(p_hevc->hier_qp_layer[i],
+                       S5P_FIMV_E_HIERARCHICAL_QP_LAYER0 + i * 4);
+               MFC_WRITEL(p_hevc->hier_bit_layer[i],
+                       S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4);
+               mfc_debug(3, "Temporal SVC: layer[%d] QP: %#x, bitrate: %#x\n",
+                                       i, p_hevc->hier_qp_layer[i],
+                                       p_hevc->hier_bit_layer[i]);
+       }
+
+       /* rate control config. */
+       reg = MFC_READL(S5P_FIMV_E_RC_CONFIG);
+       /** frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_hevc->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_CONFIG);
+
+       /* frame rate */
+       p->rc_frame_delta = FRAME_DELTA_DEFAULT;
+       if (p->rc_frame) {
+               reg = MFC_READL(S5P_FIMV_E_RC_FRAME_RATE);
+               reg &= ~(0xFFFF << 16);
+               reg |= (p_hevc->rc_framerate << 16);
+               reg &= ~(0xFFFF);
+               reg |= (p->rc_frame_delta & 0xFFFF);
+               MFC_WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE);
+       }
+
+       /* max & min value of QP for I frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND);
+       /** max I frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_hevc->rc_max_qp & 0xFF) << 8);
+       /** min I frame QP */
+       reg &= ~(0xFF);
+       reg |= (p_hevc->rc_min_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND);
+
+       /* max & min value of QP for P/B frame */
+       reg = MFC_READL(S5P_FIMV_E_RC_QP_BOUND_PB);
+       /** max B frame QP */
+       reg &= ~(0xFF << 24);
+       reg |= ((p_hevc->rc_max_qp_b & 0xFF) << 24);
+       /** min B frame QP */
+       reg &= ~(0xFF << 16);
+       reg |= ((p_hevc->rc_min_qp_b & 0xFF) << 16);
+       /** max P frame QP */
+       reg &= ~(0xFF << 8);
+       reg |= ((p_hevc->rc_max_qp_p & 0xFF) << 8);
+       /** min P frame QP */
+       reg &= ~(0xFF);
+       reg |= p_hevc->rc_min_qp_p & 0xFF;
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_PB);
+
+       reg = MFC_READL(S5P_FIMV_E_FIXED_PICTURE_QP);
+       reg &= ~(0xFF << 24);
+       reg |= ((p->config_qp & 0xFF) << 24);
+       reg &= ~(0xFF << 16);
+       reg |= ((p_hevc->rc_b_frame_qp & 0xFF) << 16);
+       reg &= ~(0xFF << 8);
+       reg |= ((p_hevc->rc_p_frame_qp & 0xFF) << 8);
+       reg &= ~(0xFF);
+       reg |= (p_hevc->rc_frame_qp & 0xFF);
+       MFC_WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP);
+
+       /* ROI enable: it must set on SEQ_START only for HEVC encoder */
+       reg = MFC_READL(S5P_FIMV_E_RC_ROI_CTRL);
+       reg &= ~(0x1);
+       reg |= (p->roi_enable);
+       MFC_WRITEL(reg, S5P_FIMV_E_RC_ROI_CTRL);
+       mfc_debug(3, "ROI: HEVC ROI enable\n");
+
+       mfc_debug_leave();
+}
+
+void s5p_mfc_set_enc_params_bpg(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_bpg_enc_params *p_bpg = &p->codec.bpg;
+       unsigned int reg = 0;
+
+       mfc_set_enc_params(ctx);
+
+       /* extension tag */
+       reg = p_bpg->thumb_size + p_bpg->exif_size;
+       MFC_WRITEL(reg, S5P_FIMV_E_BPG_EXTENSION_DATA_SIZE);
+       mfc_debug(3, "main image extension size %d (thumbnail: %d, exif: %d)\n",
+                       reg, p_bpg->thumb_size, p_bpg->exif_size);
+
+       /* profile & level */
+       reg = 0;
+       /** profile */
+       reg &= ~(0xF);
+       /* bit depth minus8 */
+       if (ctx->is_10bit) {
+               reg &= ~(0x3F << 17);
+               reg |= (0x2 << 17);
+               reg |= (0x2 << 20);
+               /* fixed profile */
+               if (ctx->is_422format)
+                       reg |= 0x1;
+       }
+       MFC_WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.h b/drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.h
new file mode 100644 (file)
index 0000000..86afc7e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc_param.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 __S5P_MFC_ENC_PARAM_H
+#define __S5P_MFC_ENC_PARAM_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+void s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_vp9(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_hevc(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_enc_params_bpg(struct s5p_mfc_ctx *ctx);
+
+#endif /* __S5P_MFC_ENC_PARAM_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_enc_vb2_ops.c b/drivers/media/platform/exynos/mfc/s5p_mfc_enc_vb2_ops.c
new file mode 100644 (file)
index 0000000..3503370
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_enc_vb2_ops.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 "s5p_mfc_common.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+static int s5p_mfc_enc_queue_setup(struct vb2_queue *vq,
+                               unsigned int *buf_count, unsigned int *plane_count,
+                               unsigned int psize[], struct device *alloc_devs[])
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_raw_info *raw;
+       int i;
+
+       mfc_debug_enter();
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->state != MFCINST_GOT_INST &&
+           vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               mfc_err_ctx("invalid state: %d\n", ctx->state);
+               return -EINVAL;
+       }
+       if (ctx->state >= MFCINST_FINISHING &&
+           vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               mfc_err_ctx("invalid state: %d\n", ctx->state);
+               return -EINVAL;
+       }
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (ctx->dst_fmt)
+                       *plane_count = ctx->dst_fmt->mem_planes;
+               else
+                       *plane_count = MFC_ENC_CAP_PLANE_COUNT;
+
+               if (*buf_count < 1)
+                       *buf_count = 1;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               psize[0] = enc->dst_buf_size;
+               alloc_devs[0] = dev->device;
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               raw = &ctx->raw_buf;
+
+               if (ctx->src_fmt)
+                       *plane_count = ctx->src_fmt->mem_planes;
+               else
+                       *plane_count = MFC_ENC_OUT_PLANE_COUNT;
+
+               if (*buf_count < 1)
+                       *buf_count = 1;
+               if (*buf_count > MFC_MAX_BUFFERS)
+                       *buf_count = MFC_MAX_BUFFERS;
+
+               if (*plane_count == 1) {
+                       psize[0] = raw->total_plane_size;
+                       alloc_devs[0] = dev->device;
+               } else {
+                       for (i = 0; i < *plane_count; i++) {
+                               psize[i] = raw->plane_size[i];
+                               alloc_devs[i] = dev->device;
+                       }
+               }
+       } else {
+               mfc_err_ctx("invalid queue type: %d\n", vq->type);
+               return -EINVAL;
+       }
+
+       mfc_debug(2, "buf_count: %d, plane_count: %d\n", *buf_count, *plane_count);
+       for (i = 0; i < *plane_count; i++)
+               mfc_debug(2, "plane[%d] size=%d\n", i, psize[i]);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void s5p_mfc_enc_unlock(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_enc_lock(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_enc_buf_init(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_buf *buf = vb_to_mfc_buf(vb);
+       dma_addr_t start_raw;
+       int i, ret;
+
+       mfc_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->dst_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               buf->planes.stream = s5p_mfc_mem_get_daddr_vb(vb, 0);
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_DST,
+                                       vb->index) < 0)
+                       mfc_err_ctx("failed in init_buf_ctrls\n");
+
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->src_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               start_raw = s5p_mfc_mem_get_daddr_vb(vb, 0);
+               if (start_raw == 0) {
+                       mfc_err_ctx("Plane mem not allocated.\n");
+                       return -ENOMEM;
+               }
+               if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12N) {
+                       buf->planes.raw[0] = start_raw;
+                       buf->planes.raw[1] = NV12N_CBCR_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+               } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_YUV420N) {
+                       buf->planes.raw[0] = start_raw;
+                       buf->planes.raw[1] = YUV420N_CB_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+                       buf->planes.raw[2] = YUV420N_CR_BASE(start_raw,
+                                                       ctx->img_width,
+                                                       ctx->img_height);
+               } else {
+                       for (i = 0; i < ctx->src_fmt->num_planes; i++)
+                               buf->planes.raw[i] = s5p_mfc_mem_get_daddr_vb(vb, i);
+               }
+
+               if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC,
+                                       vb->index) < 0)
+                       mfc_err_ctx("failed in init_buf_ctrls\n");
+
+       } else {
+               mfc_err_ctx("inavlid queue type: %d\n", vq->type);
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int s5p_mfc_enc_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_raw_info *raw;
+       unsigned int index = vb->index;
+       int ret, i;
+
+       mfc_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->dst_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               mfc_debug(2, "plane size: %lu, dst size: %u\n",
+                       vb2_plane_size(vb, 0), enc->dst_buf_size);
+
+               if (vb2_plane_size(vb, 0) < enc->dst_buf_size) {
+                       mfc_err_ctx("plane size is too small for capture\n");
+                       return -EINVAL;
+               }
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ret = s5p_mfc_check_vb_with_fmt(ctx->src_fmt, vb);
+               if (ret < 0)
+                       return ret;
+
+               raw = &ctx->raw_buf;
+               if (ctx->src_fmt->mem_planes == 1) {
+                       mfc_debug(2, "Plane size = %lu, src size:%d\n",
+                                       vb2_plane_size(vb, 0),
+                                       raw->total_plane_size);
+                       if (vb2_plane_size(vb, 0) < raw->total_plane_size) {
+                               mfc_err_ctx("Output plane is too small\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       for (i = 0; i < ctx->src_fmt->mem_planes; i++) {
+                               mfc_debug(2, "plane[%d] size: %lu, src[%d] size: %d\n",
+                                               i, vb2_plane_size(vb, i),
+                                               i, raw->plane_size[i]);
+                               if (vb2_plane_size(vb, i) < raw->plane_size[i]) {
+                                       mfc_err_ctx("Output plane[%d] is too smalli\n", i);
+                                       return -EINVAL;
+                               }
+                       }
+               }
+
+               if (call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_buf_ctrls\n");
+       } else {
+               mfc_err_ctx("inavlid queue type: %d\n", vq->type);
+               return -EINVAL;
+       }
+
+       s5p_mfc_mem_buf_prepare(vb);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void s5p_mfc_enc_buf_finish(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       unsigned int index = vb->index;
+
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (call_cop(ctx, to_ctx_ctrls, ctx, &ctx->dst_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_ctx_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (call_cop(ctx, to_ctx_ctrls, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in to_ctx_ctrls\n");
+       }
+
+       s5p_mfc_mem_buf_finish(vb);
+}
+
+static void s5p_mfc_enc_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       unsigned int index = vb->index;
+
+       mfc_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                                       MFC_CTRL_TYPE_DST, index) < 0)
+                       mfc_err_ctx("failed in cleanup_buf_ctrls\n");
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                                       MFC_CTRL_TYPE_SRC, index) < 0)
+                       mfc_err_ctx("failed in cleanup_buf_ctrls\n");
+       } else {
+               mfc_err_ctx("s5p_mfc_enc_buf_cleanup: unknown queue type.\n");
+       }
+
+       mfc_debug_leave();
+}
+
+static int s5p_mfc_enc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       /* If context is ready then dev = work->data;schedule it to run */
+       if (s5p_mfc_enc_ctx_ready(ctx)) {
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       }
+
+       s5p_mfc_try_run(dev);
+
+       return 0;
+}
+
+static void s5p_mfc_enc_stop_streaming(struct vb2_queue *q)
+{
+       struct s5p_mfc_ctx *ctx = q->drv_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int index = 0;
+       int aborted = 0;
+       int ret = 0;
+
+       mfc_info_ctx("enc stop_streaming is called, hwlock : %d, type : %d\n",
+                               test_bit(ctx->num, &dev->hwlock.bits), q->type);
+       MFC_TRACE_CTX("** ENC streamoff(type:%d)\n", q->type);
+
+       /* If a H/W operation is in progress, wait for it complete */
+       if (need_to_wait_nal_abort(ctx)) {
+               if (s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_NAL_ABORT_RET)) {
+                       mfc_err_ctx("time out during nal abort\n");
+                       s5p_mfc_cleanup_work_bit_and_try_run(ctx);
+               }
+               aborted = 1;
+       }
+       MFC_TRACE_CTX_HWLOCK("**ENC streamoff(type:%d)\n", q->type);
+       ret = s5p_mfc_get_hwlock_ctx(ctx);
+       if (ret < 0) {
+               mfc_err_ctx("Failed to get hwlock.\n");
+               return;
+       }
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               s5p_mfc_cleanup_enc_dst_queue(ctx);
+
+               while (index < MFC_MAX_BUFFERS) {
+                       index = find_next_bit(&ctx->dst_ctrls_avail,
+                                       MFC_MAX_BUFFERS, index);
+                       if (index < MFC_MAX_BUFFERS)
+                               call_cop(ctx, reset_buf_ctrls, &ctx->dst_ctrls[index]);
+                       index++;
+               }
+       } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               s5p_mfc_move_all_bufs(&ctx->buf_queue_lock, &ctx->src_buf_queue,
+                               &ctx->ref_buf_queue, MFC_QUEUE_ADD_BOTTOM);
+               s5p_mfc_cleanup_enc_src_queue(ctx);
+
+               while (index < MFC_MAX_BUFFERS) {
+                       index = find_next_bit(&ctx->src_ctrls_avail,
+                                       MFC_MAX_BUFFERS, index);
+                       if (index < MFC_MAX_BUFFERS)
+                               call_cop(ctx, reset_buf_ctrls, &ctx->src_ctrls[index]);
+                       index++;
+               }
+       }
+
+       if (aborted || ctx->state == MFCINST_FINISHING)
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+
+       mfc_debug(2, "buffer cleanup is done in stop_streaming, type : %d\n", q->type);
+
+       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+       s5p_mfc_release_hwlock_ctx(ctx);
+
+       if (s5p_mfc_enc_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+}
+
+static void s5p_mfc_enc_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct s5p_mfc_ctx *ctx = vq->drv_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_buf *buf = vb_to_mfc_buf(vb);
+
+       mfc_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               mfc_debug(2, "dst queue: %p\n", &ctx->dst_buf_queue);
+               mfc_debug(2, "Adding to dst: %p (%08llx, %08llx)\n", vb,
+                               s5p_mfc_mem_get_daddr_vb(vb, 0),
+                               buf->planes.stream);
+
+               /* Mark destination as available for use by MFC */
+               s5p_mfc_add_tail_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, buf);
+       } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               s5p_mfc_add_tail_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, buf);
+       } else {
+               mfc_err_ctx("unsupported buffer type (%d)\n", vq->type);
+       }
+
+       mfc_debug(7, "timestamp: %lld\n", buf->vb.vb2_buf.timestamp);
+       mfc_debug(7, "qos ratio: %d\n", ctx->qos_ratio);
+
+       s5p_mfc_qos_update_last_framerate(ctx, buf->vb.vb2_buf.timestamp);
+       s5p_mfc_qos_update_framerate(ctx);
+
+       if (s5p_mfc_enc_ctx_ready(ctx)) {
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       }
+       s5p_mfc_try_run(dev);
+
+       mfc_debug_leave();
+}
+
+struct vb2_ops s5p_mfc_enc_qops = {
+       .queue_setup            = s5p_mfc_enc_queue_setup,
+       .wait_prepare           = s5p_mfc_enc_unlock,
+       .wait_finish            = s5p_mfc_enc_lock,
+       .buf_init               = s5p_mfc_enc_buf_init,
+       .buf_prepare            = s5p_mfc_enc_buf_prepare,
+       .buf_finish             = s5p_mfc_enc_buf_finish,
+       .buf_cleanup            = s5p_mfc_enc_buf_cleanup,
+       .start_streaming        = s5p_mfc_enc_start_streaming,
+       .stop_streaming         = s5p_mfc_enc_stop_streaming,
+       .buf_queue              = s5p_mfc_enc_buf_queue,
+};
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_hwfc_internal.h b/drivers/media/platform/exynos/mfc/s5p_mfc_hwfc_internal.h
new file mode 100644 (file)
index 0000000..ba63cf2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_hwfc_internal.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 __S5P_MFC_HWFC_INTERNAL_H
+#define __S5P_MFC_HWFC_INTERNAL_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+/*
+ * RGB encoding information to avoid confusion.
+ *
+ * V4L2_PIX_FMT_ARGB32 takes ARGB data like below.
+ * MSB                              LSB
+ * 3       2       1
+ * 2       4       6       8       0
+ * |B......BG......GR......RA......A|
+ */
+struct s5p_mfc_fmt enc_hwfc_formats[] = {
+       {
+               .name = "4:2:0 2 Planes Y/CbCr single",
+               .fourcc = V4L2_PIX_FMT_NV12N,
+               .codec_mode = MFC_FORMATS_NO_CODEC,
+               .type = MFC_FMT_RAW,
+               .num_planes = 2,
+               .mem_planes = 1,
+       },
+       {
+               .name = "H264 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264,
+               .codec_mode = S5P_FIMV_CODEC_H264_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+       {
+               .name = "HEVC Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_HEVC,
+               .codec_mode = S5P_FIMV_CODEC_HEVC_ENC,
+               .type = MFC_FMT_ENC,
+               .num_planes = 1,
+               .mem_planes = 1,
+       },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(enc_hwfc_formats)
+
+#endif /* __S5P_MFC_HWFC_INTERNAL_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.c b/drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.c
new file mode 100644 (file)
index 0000000..0cedccc
--- /dev/null
@@ -0,0 +1,1033 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.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 "s5p_mfc_hwlock.h"
+
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_otf.h"
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_inst.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+
+static inline void mfc_print_hwlock(struct s5p_mfc_dev *dev)
+{
+       mfc_debug(2, "dev.hwlock.dev = 0x%lx, bits = 0x%lx, owned_by_irq = %d, wl_count = %d, transfer_owner = %d\n",
+               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+}
+
+void s5p_mfc_init_hwlock(struct s5p_mfc_dev *dev)
+{
+       unsigned long flags;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       spin_lock_init(&dev->hwlock.lock);
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+
+       INIT_LIST_HEAD(&dev->hwlock.waiting_list);
+       dev->hwlock.wl_count = 0;
+       dev->hwlock.bits = 0;
+       dev->hwlock.dev = 0;
+       dev->hwlock.owned_by_irq = 0;
+       dev->hwlock.transfer_owner = 0;
+
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+}
+
+static int mfc_remove_listable_wq_dev(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_listable_wq *listable_wq;
+       unsigned long flags;
+       int ret = -1;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       list_for_each_entry(listable_wq, &dev->hwlock.waiting_list, list) {
+               if (!listable_wq->dev)
+                       continue;
+
+               mfc_debug(2, "Found dev and will delete it!\n");
+
+               list_del(&listable_wq->list);
+               dev->hwlock.wl_count--;
+
+               ret = 0;
+               break;
+       }
+
+       mfc_print_hwlock(dev);
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+       return ret;
+}
+
+static int mfc_remove_listable_wq_ctx(struct s5p_mfc_ctx *curr_ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_listable_wq *listable_wq;
+       unsigned long flags;
+       int ret = -1;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = curr_ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       list_for_each_entry(listable_wq, &dev->hwlock.waiting_list, list) {
+               if (!listable_wq->ctx)
+                       continue;
+
+               if (listable_wq->ctx->num == curr_ctx->num) {
+                       mfc_debug(2, "Found ctx and will delete it (%d)!\n", curr_ctx->num);
+
+                       list_del(&listable_wq->list);
+                       dev->hwlock.wl_count--;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       mfc_print_hwlock(dev);
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+       return ret;
+}
+
+/*
+ * Return value description
+ *    0: succeeded to get hwlock
+ * -EIO: failed to get hwlock (time out)
+ */
+int s5p_mfc_get_hwlock_dev(struct s5p_mfc_dev *dev)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&dev->hwlock_wq.wait_mutex);
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       if (dev->shutdown) {
+               mfc_info_dev("Couldn't lock HW. Shutdown was called.\n");
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+               mutex_unlock(&dev->hwlock_wq.wait_mutex);
+               return -EINVAL;
+       }
+
+       if ((dev->hwlock.bits != 0) || (dev->hwlock.dev != 0)) {
+               list_add_tail(&dev->hwlock_wq.list, &dev->hwlock.waiting_list);
+               dev->hwlock.wl_count++;
+
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+               mfc_debug(2, "Waiting for hwlock to be released.\n");
+
+               ret = wait_event_timeout(dev->hwlock_wq.wait_queue,
+                       ((dev->hwlock.transfer_owner == 1) && (dev->hwlock.dev == 1)),
+                       msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+               MFC_TRACE_DEV_HWLOCK("get_hwlock_dev: before waiting\n");
+               MFC_TRACE_DEV_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               dev->hwlock.transfer_owner = 0;
+               mfc_remove_listable_wq_dev(dev);
+               if (ret == 0) {
+                       mfc_err_dev("Woken up but timed out\n");
+                       mfc_print_hwlock(dev);
+                       mutex_unlock(&dev->hwlock_wq.wait_mutex);
+                       return -EIO;
+               } else {
+                       mfc_debug(2, "Woken up and got hwlock\n");
+                       mfc_print_hwlock(dev);
+                       mutex_unlock(&dev->hwlock_wq.wait_mutex);
+               }
+       } else {
+               dev->hwlock.bits = 0;
+               dev->hwlock.dev = 1;
+               dev->hwlock.owned_by_irq = 0;
+
+               MFC_TRACE_DEV_HWLOCK("get_hwlock_dev: no waiting\n");
+               MFC_TRACE_DEV_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               mfc_print_hwlock(dev);
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+               mutex_unlock(&dev->hwlock_wq.wait_mutex);
+       }
+
+#ifdef NAL_Q_ENABLE
+       /* Stop NAL-Q after getting hwlock */
+       if (dev->nal_q_handle)
+               s5p_mfc_nal_q_stop_if_started(dev);
+#endif
+       return 0;
+}
+
+/*
+ * Return value description
+ *    0: succeeded to get hwlock
+ * -EIO: failed to get hwlock (time out)
+ */
+int s5p_mfc_get_hwlock_ctx(struct s5p_mfc_ctx *curr_ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_ctx *ctx = curr_ctx;
+       int ret = 0;
+       unsigned long flags;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = curr_ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&curr_ctx->hwlock_wq.wait_mutex);
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       if (dev->shutdown) {
+               mfc_info_dev("Couldn't lock HW. Shutdown was called.\n");
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+               mutex_unlock(&curr_ctx->hwlock_wq.wait_mutex);
+               return -EINVAL;
+       }
+
+       if ((dev->hwlock.bits != 0) || (dev->hwlock.dev != 0)) {
+               list_add_tail(&curr_ctx->hwlock_wq.list, &dev->hwlock.waiting_list);
+               dev->hwlock.wl_count++;
+
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+               MFC_TRACE_CTX_HWLOCK("get_hwlock_ctx: before waiting\n");
+               MFC_TRACE_CTX_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               mfc_debug(2, "Waiting for hwlock to be released.\n");
+
+               ret = wait_event_timeout(curr_ctx->hwlock_wq.wait_queue,
+                       ((dev->hwlock.transfer_owner == 1) && (test_bit(curr_ctx->num, &dev->hwlock.bits))),
+                       msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+               MFC_TRACE_CTX_HWLOCK("get_hwlock_ctx: after waiting, ret:%d\n", ret);
+               MFC_TRACE_CTX_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               dev->hwlock.transfer_owner = 0;
+               mfc_remove_listable_wq_ctx(curr_ctx);
+               if (ret == 0) {
+                       mfc_err_dev("Woken up but timed out\n");
+                       mfc_print_hwlock(dev);
+                       mutex_unlock(&curr_ctx->hwlock_wq.wait_mutex);
+                       return -EIO;
+               } else {
+                       mfc_debug(2, "Woken up and got hwlock\n");
+                       mfc_print_hwlock(dev);
+                       mutex_unlock(&curr_ctx->hwlock_wq.wait_mutex);
+               }
+       } else {
+               dev->hwlock.bits = 0;
+               dev->hwlock.dev = 0;
+               set_bit(curr_ctx->num, &dev->hwlock.bits);
+               dev->hwlock.owned_by_irq = 0;
+
+               MFC_TRACE_CTX_HWLOCK("get_hwlock_ctx: no waiting\n");
+               MFC_TRACE_CTX_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               mfc_print_hwlock(dev);
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+               mutex_unlock(&curr_ctx->hwlock_wq.wait_mutex);
+       }
+
+#ifdef NAL_Q_ENABLE
+       /* Stop NAL-Q after getting hwlock */
+       if (dev->nal_q_handle)
+               s5p_mfc_nal_q_stop_if_started(dev);
+#endif
+       return 0;
+}
+
+/*
+ * Return value description
+ *  0: succeeded to release hwlock
+ *  1: succeeded to release hwlock, hwlock is captured by another module
+ * -1: error since device is waiting again.
+ */
+int s5p_mfc_release_hwlock_dev(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_listable_wq *listable_wq;
+       unsigned long flags;
+       int ret = -1;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       dev->hwlock.dev = 0;
+       dev->hwlock.owned_by_irq = 0;
+
+       if (dev->shutdown) {
+               mfc_debug(2, "Couldn't wakeup module. Shutdown was called.\n");
+               ret = 0;
+       } else if (list_empty(&dev->hwlock.waiting_list)) {
+               mfc_debug(2, "No waiting module.\n");
+               ret = 0;
+       } else {
+               mfc_debug(2, "There is a waiting module.\n");
+               listable_wq = list_entry(dev->hwlock.waiting_list.next, struct s5p_mfc_listable_wq, list);
+               list_del(&listable_wq->list);
+               dev->hwlock.wl_count--;
+
+               if (listable_wq->dev) {
+                       mfc_debug(2, "Waking up dev\n");
+                       dev->hwlock.dev = 1;
+               } else {
+                       mfc_debug(2, "Waking up another ctx\n");
+                       set_bit(listable_wq->ctx->num, &dev->hwlock.bits);
+               }
+
+               dev->hwlock.transfer_owner = 1;
+
+               MFC_TRACE_DEV_HWLOCK("release_hwlock_dev: wakeup.\n");
+               MFC_TRACE_DEV_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               wake_up(&listable_wq->wait_queue);
+               ret = 1;
+       }
+
+       mfc_print_hwlock(dev);
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+       return ret;
+}
+
+/*
+ * Should be called with hwlock.lock
+ *
+ * Return value description
+ * 0: succeeded to release hwlock
+ * 1: succeeded to release hwlock, hwlock is captured by another module
+ */
+static int mfc_release_hwlock_ctx_protected(struct s5p_mfc_ctx *curr_ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_ctx *ctx = curr_ctx;
+       struct s5p_mfc_listable_wq *listable_wq;
+       int ret = -1;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = curr_ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       mfc_print_hwlock(dev);
+       clear_bit(curr_ctx->num, &dev->hwlock.bits);
+       dev->hwlock.owned_by_irq = 0;
+
+       if (dev->shutdown) {
+               mfc_debug(2, "Couldn't wakeup module. Shutdown was called.\n");
+               ret = 0;
+       } else if (list_empty(&dev->hwlock.waiting_list)) {
+               mfc_debug(2, "No waiting module.\n");
+               ret = 0;
+       } else {
+               mfc_debug(2, "There is a waiting module.\n");
+               listable_wq = list_entry(dev->hwlock.waiting_list.next, struct s5p_mfc_listable_wq, list);
+               list_del(&listable_wq->list);
+               dev->hwlock.wl_count--;
+
+               if (listable_wq->dev) {
+                       mfc_debug(2, "Waking up dev\n");
+                       dev->hwlock.dev = 1;
+               } else {
+                       mfc_debug(2, "Waking up another ctx\n");
+                       set_bit(listable_wq->ctx->num, &dev->hwlock.bits);
+               }
+
+               dev->hwlock.transfer_owner = 1;
+
+               MFC_TRACE_CTX_HWLOCK("release_hwlock_ctx: wakeup.\n");
+               MFC_TRACE_CTX_HWLOCK(">>dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n",
+                               dev->hwlock.dev, dev->hwlock.bits, dev->hwlock.owned_by_irq,
+                               dev->hwlock.wl_count, dev->hwlock.transfer_owner);
+
+               wake_up(&listable_wq->wait_queue);
+               ret = 1;
+       }
+
+       mfc_print_hwlock(dev);
+       return ret;
+}
+
+/*
+ * Return value description
+ * 0: succeeded to release hwlock
+ * 1: succeeded to release hwlock, hwlock is captured by another module
+ */
+int s5p_mfc_release_hwlock_ctx(struct s5p_mfc_ctx *curr_ctx)
+{
+       struct s5p_mfc_dev *dev;
+       unsigned long flags;
+       int ret = -1;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = curr_ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       ret = mfc_release_hwlock_ctx_protected(curr_ctx);
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+       return ret;
+}
+
+static inline int mfc_yield_hwlock(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *curr_ctx)
+{
+       unsigned long flags;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+
+       mfc_release_hwlock_ctx_protected(curr_ctx);
+
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+       /* Trigger again if other instance's work is waiting */
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       return 0;
+}
+
+/*
+ * Should be called with hwlock.lock
+ */
+static inline void mfc_transfer_hwlock_ctx_protected(struct s5p_mfc_dev *dev, int curr_ctx_index)
+{
+       dev->hwlock.dev = 0;
+       dev->hwlock.bits = 0;
+       set_bit(curr_ctx_index, &dev->hwlock.bits);
+}
+
+/*
+ * Should be called with hwlock.lock
+ *
+ * Return value description
+ *   >=0: succeeded to get hwlock_bit for the context, index of new context
+ *   -1, -EINVAL: failed to get hwlock_bit for a context
+ */
+static int mfc_try_to_get_new_ctx_protected(struct s5p_mfc_dev *dev)
+{
+       int ret = 0;
+       int index;
+       struct s5p_mfc_ctx *new_ctx;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (dev->shutdown) {
+               mfc_info_dev("Couldn't lock HW. Shutdown was called.\n");
+               return -EINVAL;
+       }
+
+       if (dev->sleep) {
+               mfc_info_dev("Couldn't lock HW. Sleep was called.\n");
+               return -EINVAL;
+       }
+
+       /* Check whether hardware is not running */
+       if ((dev->hwlock.bits != 0) || (dev->hwlock.dev != 0)) {
+               /* This is perfectly ok, the scheduled ctx should wait */
+               mfc_debug(2, "Couldn't lock HW.\n");
+               return -1;
+       }
+
+       /* Choose the context to run */
+       index = s5p_mfc_get_new_ctx(dev);
+       if (index < 0) {
+               /* This is perfectly ok, the scheduled ctx should wait
+                * No contexts to run
+                */
+               mfc_debug(2, "No ctx is scheduled to be run.\n");
+               ret = -1;
+               return ret;
+       }
+
+       new_ctx = dev->ctx[index];
+       if (!new_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               ret = -1;
+               return ret;
+       }
+
+       set_bit(new_ctx->num, &dev->hwlock.bits);
+       ret = index;
+
+       return ret;
+}
+
+/*
+ * Should be called without hwlock holding
+ *
+ * Try to run an operation on hardware
+ */
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
+{
+       int new_ctx_index;
+       int ret;
+       unsigned long flags;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+       mfc_debug(2, "Try run dev: %p\n", dev);
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       new_ctx_index = mfc_try_to_get_new_ctx_protected(dev);
+       if (new_ctx_index < 0) {
+               mfc_debug(2, "Failed to get new context to run.\n");
+               mfc_print_hwlock(dev);
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+               return;
+       }
+
+       dev->hwlock.owned_by_irq = 1;
+
+       mfc_print_hwlock(dev);
+       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+       ret = s5p_mfc_just_run(dev, new_ctx_index);
+       if (ret)
+               mfc_yield_hwlock(dev, dev->ctx[new_ctx_index]);
+}
+
+/*
+ * Should be called without hwlock holding
+ *
+ */
+void s5p_mfc_cleanup_work_bit_and_try_run(struct s5p_mfc_ctx *curr_ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = curr_ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       s5p_mfc_clear_bit(curr_ctx->num, &dev->work_bits);
+
+       s5p_mfc_try_run(dev);
+}
+
+void s5p_mfc_cache_flush(struct s5p_mfc_dev *dev, int is_drm)
+{
+       s5p_mfc_cmd_cache_flush(dev);
+       if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_CACHE_FLUSH_RET)) {
+               mfc_err_dev("Failed to CACHE_FLUSH\n");
+               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_CHACHE_FLUSH);
+               s5p_mfc_dump_info_and_stop_hw(dev);
+       }
+
+       s5p_mfc_pm_clock_off(dev);
+       dev->curr_ctx_is_drm = is_drm;
+       s5p_mfc_pm_clock_on_with_base(dev, (is_drm ? MFCBUF_DRM : MFCBUF_NORMAL));
+}
+
+#ifdef NAL_Q_ENABLE
+/*
+ * Return value description
+ *  0: NAL-Q is handled successfully
+ *  1: NAL_START command should be handled
+ * -1: Error
+*/
+static int mfc_nal_q_just_run(struct s5p_mfc_ctx *ctx, int need_cache_flush)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int ret = -1;
+
+       nal_queue_handle *nal_q_handle = dev->nal_q_handle;
+
+       if (!nal_q_handle) {
+               mfc_err_dev("nal_q_handle is NULL\n");
+               return ret;
+       }
+
+       if (nal_q_handle->nal_q_state != NAL_Q_STATE_STARTED
+                       && nal_q_handle->nal_q_state != NAL_Q_STATE_STOPPED) {
+               mfc_debug(2, "continue_clock_on = %d\n", dev->continue_clock_on);
+               if (!dev->continue_clock_on) {
+                       s5p_mfc_pm_clock_on(dev);
+               } else {
+                       dev->continue_clock_on = false;
+               }
+       }
+
+       switch (nal_q_handle->nal_q_state) {
+       case NAL_Q_STATE_CREATED:
+               s5p_mfc_nal_q_init(dev, nal_q_handle);
+       case NAL_Q_STATE_INITIALIZED:
+               if (s5p_mfc_nal_q_check_enable(dev) == 0) {
+                       /* NAL START */
+                       ret = 1;
+               } else {
+                       /* enable NAL QUEUE */
+                       if (need_cache_flush)
+                               s5p_mfc_cache_flush(dev, ctx->is_drm);
+
+                       mfc_info_ctx("NAL Q: start NAL QUEUE\n");
+                       s5p_mfc_nal_q_start(dev, nal_q_handle);
+
+                       if (s5p_mfc_nal_q_enqueue_in_buf(dev, ctx, nal_q_handle->nal_q_in_handle)) {
+                               mfc_debug(2, "NAL Q: Failed to enqueue input data\n");
+                               if (ctx->clear_work_bit) {
+                                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+                                       ctx->clear_work_bit = 0;
+                               }
+                               s5p_mfc_release_hwlock_ctx(ctx);
+                               ret = 0;
+                               break;
+                       }
+
+                       if (!nal_q_handle->nal_q_exception)
+                               s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+
+                       s5p_mfc_release_hwlock_ctx(ctx);
+
+                       if (s5p_mfc_ctx_ready(ctx))
+                               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+                       if (s5p_mfc_is_work_to_do(dev))
+                               queue_work(dev->butler_wq, &dev->butler_work);
+
+                       ret = 0;
+               }
+               break;
+       case NAL_Q_STATE_STARTED:
+               if (s5p_mfc_nal_q_check_enable(dev) == 0 ||
+                               nal_q_handle->nal_q_exception) {
+                       /* disable NAL QUEUE */
+                       s5p_mfc_nal_q_stop(dev, nal_q_handle);
+                       mfc_info_ctx("NAL Q: stop NAL QUEUE\n");
+                       if (s5p_mfc_wait_for_done_dev(dev,
+                                       S5P_FIMV_R2H_CMD_COMPLETE_QUEUE_RET)) {
+                               mfc_err_dev("NAL Q: Failed to stop queue.\n");
+                               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_STOP_NAL_Q);
+                               s5p_mfc_dump_info_and_stop_hw(dev);
+                       }
+                       nal_q_handle->nal_q_exception = 0;
+                       ret = 1;
+                       s5p_mfc_pm_clock_on(dev);
+                       break;
+               } else {
+                       /* NAL QUEUE */
+                       if (s5p_mfc_nal_q_enqueue_in_buf(dev, ctx, nal_q_handle->nal_q_in_handle)) {
+                               mfc_debug(2, "NAL Q: Failed to enqueue input data\n");
+                               if (ctx->clear_work_bit) {
+                                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+                                       ctx->clear_work_bit = 0;
+                               }
+                               s5p_mfc_release_hwlock_ctx(ctx);
+                               ret = 0;
+                               break;
+                       }
+
+                       if (!nal_q_handle->nal_q_exception)
+                               s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+
+                       s5p_mfc_release_hwlock_ctx(ctx);
+
+                       if (s5p_mfc_ctx_ready(ctx))
+                               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+                       if (s5p_mfc_is_work_to_do(dev))
+                               queue_work(dev->butler_wq, &dev->butler_work);
+                       ret = 0;
+               }
+               break;
+       default:
+               mfc_info_ctx("NAL Q: can't try command, nal_q_state : %d\n",
+                               nal_q_handle->nal_q_state);
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
+#endif
+
+static int mfc_just_run_dec(struct s5p_mfc_ctx *ctx)
+{
+       int ret = 0;
+
+       switch (ctx->state) {
+       case MFCINST_FINISHING:
+               ret = s5p_mfc_run_dec_last_frames(ctx);
+               break;
+       case MFCINST_RUNNING:
+       case MFCINST_SPECIAL_PARSING_NAL:
+               ret = s5p_mfc_run_dec_frame(ctx);
+               break;
+       case MFCINST_INIT:
+               ret = s5p_mfc_open_inst(ctx);
+               break;
+       case MFCINST_RETURN_INST:
+               ret = s5p_mfc_close_inst(ctx);
+               break;
+       case MFCINST_GOT_INST:
+       case MFCINST_SPECIAL_PARSING:
+               ret = s5p_mfc_run_dec_init(ctx);
+               break;
+       case MFCINST_HEAD_PARSED:
+               if (ctx->codec_buffer_allocated == 0) {
+                       ctx->clear_work_bit = 1;
+                       mfc_err_ctx("codec buffer is not allocated\n");
+                       ret = -EAGAIN;
+                       break;
+               }
+               ret = s5p_mfc_cmd_dec_init_buffers(ctx);
+               break;
+       case MFCINST_RES_CHANGE_INIT:
+               ret = s5p_mfc_run_dec_last_frames(ctx);
+               break;
+       case MFCINST_RES_CHANGE_FLUSH:
+               ret = s5p_mfc_run_dec_last_frames(ctx);
+               break;
+       case MFCINST_RES_CHANGE_END:
+               mfc_debug(2, "Finished remaining frames after resolution change.\n");
+               ctx->capture_state = QUEUE_FREE;
+               mfc_debug(2, "Will re-init the codec.\n");
+               ret = s5p_mfc_run_dec_init(ctx);
+               break;
+       case MFCINST_DPB_FLUSHING:
+               ret = s5p_mfc_cmd_dpb_flush(ctx);
+               break;
+       default:
+               mfc_info_ctx("can't try command(decoder just_run), state : %d\n", ctx->state);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
+static int mfc_just_run_enc(struct s5p_mfc_ctx *ctx)
+{
+       int ret = 0;
+
+       switch (ctx->state) {
+               case MFCINST_FINISHING:
+                       ret = s5p_mfc_run_enc_last_frames(ctx);
+                       break;
+               case MFCINST_RUNNING:
+               case MFCINST_RUNNING_NO_OUTPUT:
+                       if (ctx->otf_handle) {
+                               ret = s5p_mfc_otf_run_enc_frame(ctx);
+                               break;
+                       }
+                       ret = s5p_mfc_run_enc_frame(ctx);
+                       break;
+               case MFCINST_INIT:
+                       ret = s5p_mfc_open_inst(ctx);
+                       break;
+               case MFCINST_RETURN_INST:
+                       ret = s5p_mfc_close_inst(ctx);
+                       break;
+               case MFCINST_GOT_INST:
+                       if (ctx->otf_handle) {
+                               ret = s5p_mfc_otf_run_enc_init(ctx);
+                               break;
+                       }
+                       ret = s5p_mfc_run_enc_init(ctx);
+                       break;
+               case MFCINST_HEAD_PARSED:
+                       ret = s5p_mfc_cmd_enc_init_buffers(ctx);
+                       break;
+               case MFCINST_ABORT_INST:
+                       ret = s5p_mfc_abort_inst(ctx);
+                       break;
+               default:
+                       mfc_info_ctx("can't try command(encoder just_run), state : %d\n", ctx->state);
+                       ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
+/* Run an operation on hardware */
+int s5p_mfc_just_run(struct s5p_mfc_dev *dev, int new_ctx_index)
+{
+       struct s5p_mfc_ctx *ctx;
+       unsigned int ret = 0;
+       int need_cache_flush = 0;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       ctx = dev->ctx[new_ctx_index];
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       if (ctx->state == MFCINST_RUNNING)
+               s5p_mfc_clean_ctx_int_flags(ctx);
+
+       mfc_debug(2, "New context: %d\n", new_ctx_index);
+       dev->curr_ctx = ctx->num;
+
+       /* Got context to run in ctx */
+       mfc_debug(2, "src: %d, dst: %d, state: %d, dpb_count = %d\n",
+               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+               s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+               ctx->state, ctx->dpb_count);
+       mfc_debug(2, "ctx->state=%d\n", ctx->state);
+       /* Last frame has already been sent to MFC
+        * Now obtaining frames from MFC buffer */
+
+       /* Check if cache flush command is needed */
+       if (dev->curr_ctx_is_drm != ctx->is_drm)
+               need_cache_flush = 1;
+       else
+               dev->curr_ctx_is_drm = ctx->is_drm;
+
+       mfc_debug(2, "need_cache_flush = %d, is_drm = %d\n", need_cache_flush, ctx->is_drm);
+
+#ifdef NAL_Q_ENABLE
+       if (dev->nal_q_handle) {
+               ret = mfc_nal_q_just_run(ctx, need_cache_flush);
+               if (ret == 0) {
+                       mfc_debug(2, "NAL_Q was handled\n");
+                       return ret;
+               } else if (ret == 1){
+                       /* Path through */
+                       mfc_debug(2, "NAL_START will be handled\n");
+               } else {
+                       return ret;
+               }
+       }
+#else
+       mfc_debug(2, "continue_clock_on = %d\n", dev->continue_clock_on);
+       if (!dev->continue_clock_on) {
+               s5p_mfc_pm_clock_on(dev);
+       } else {
+               dev->continue_clock_on = false;
+       }
+#endif
+
+       if (need_cache_flush)
+               s5p_mfc_cache_flush(dev, ctx->is_drm);
+
+       if (ctx->type == MFCINST_DECODER) {
+               ret = mfc_just_run_dec(ctx);
+       } else if (ctx->type == MFCINST_ENCODER) {
+               ret = mfc_just_run_enc(ctx);
+       } else {
+               mfc_err_ctx("invalid context type: %d\n", ctx->type);
+               ret = -EAGAIN;
+       }
+
+       if (ret) {
+               /* Check again the ctx condition and clear work bits
+                * if ctx is not available. */
+               if (s5p_mfc_ctx_ready(ctx) == 0 || ctx->clear_work_bit) {
+                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+                       ctx->clear_work_bit = 0;
+               }
+
+               s5p_mfc_pm_clock_off(dev);
+       }
+
+       return ret;
+}
+
+void s5p_mfc_hwlock_handler_irq(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *curr_ctx,
+               unsigned int reason, unsigned int err)
+{
+       int new_ctx_index;
+       unsigned long flags;
+       int ret;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       spin_lock_irqsave(&dev->hwlock.lock, flags);
+       mfc_print_hwlock(dev);
+
+       if (dev->hwlock.owned_by_irq) {
+               if (dev->preempt_ctx > MFC_NO_INSTANCE_SET) {
+                       mfc_debug(2, "There is a preempt_ctx\n");
+                       dev->continue_clock_on = true;
+                       s5p_mfc_wake_up_ctx(curr_ctx, reason, err);
+                       new_ctx_index = dev->preempt_ctx;
+                       mfc_debug(2, "preempt_ctx is : %d\n", new_ctx_index);
+
+                       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+                       ret = s5p_mfc_just_run(dev, new_ctx_index);
+                       if (ret) {
+                               dev->continue_clock_on = false;
+                               mfc_yield_hwlock(dev, dev->ctx[new_ctx_index]);
+                       }
+               } else if (!list_empty(&dev->hwlock.waiting_list)) {
+                       mfc_debug(2, "There is a waiting module for hwlock\n");
+                       dev->continue_clock_on = false;
+                       s5p_mfc_pm_clock_off(dev);
+
+                       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+                       s5p_mfc_release_hwlock_ctx(curr_ctx);
+                       s5p_mfc_wake_up_ctx(curr_ctx, reason, err);
+                       queue_work(dev->butler_wq, &dev->butler_work);
+               } else {
+                       mfc_debug(2, "No preempt_ctx and no waiting module\n");
+                       new_ctx_index = s5p_mfc_get_new_ctx(dev);
+                       if (new_ctx_index < 0) {
+                               mfc_debug(2, "No ctx to run\n");
+                               /* No contexts to run */
+                               dev->continue_clock_on = false;
+                               s5p_mfc_pm_clock_off(dev);
+
+                               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+                               s5p_mfc_release_hwlock_ctx(curr_ctx);
+                               s5p_mfc_wake_up_ctx(curr_ctx, reason, err);
+                               queue_work(dev->butler_wq, &dev->butler_work);
+                       } else {
+                               mfc_debug(2, "There is a ctx to run\n");
+                               dev->continue_clock_on = true;
+                               s5p_mfc_wake_up_ctx(curr_ctx, reason, err);
+
+                               /* If cache flush command is needed or there is OTF handle, handler should stop */
+                               if ((dev->curr_ctx_is_drm != dev->ctx[new_ctx_index]->is_drm) ||
+                                               dev->ctx[new_ctx_index]->otf_handle) {
+                                       mfc_debug(2, "Secure and nomal switching or OTF mode\n");
+                                       mfc_debug(2, "DRM attribute %d->%d\n",
+                                                       dev->curr_ctx_is_drm, dev->ctx[new_ctx_index]->is_drm);
+
+                                       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+                                       s5p_mfc_release_hwlock_ctx(curr_ctx);
+                                       queue_work(dev->butler_wq, &dev->butler_work);
+                               } else {
+                                       mfc_debug(2, "Work to do successively (next ctx: %d)\n", new_ctx_index);
+                                       mfc_transfer_hwlock_ctx_protected(dev, new_ctx_index);
+
+                                       spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+
+                                       ret = s5p_mfc_just_run(dev, new_ctx_index);
+                                       if (ret) {
+                                               dev->continue_clock_on = false;
+                                               mfc_yield_hwlock(dev, dev->ctx[new_ctx_index]);
+                                       }
+                               }
+                       }
+               }
+       } else {
+               mfc_debug(2, "hwlock is NOT owned by irq\n");
+               dev->continue_clock_on = false;
+               s5p_mfc_pm_clock_off(dev);
+               s5p_mfc_wake_up_ctx(curr_ctx, reason, err);
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+               spin_unlock_irqrestore(&dev->hwlock.lock, flags);
+       }
+       mfc_print_hwlock(dev);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.h b/drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.h
new file mode 100644 (file)
index 0000000..a9616a8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_hwlock.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 __S5P_MFC_HWLOCK_H
+#define __S5P_MFC_HWLOCK_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+static inline void s5p_mfc_init_listable_wq_dev(struct s5p_mfc_dev *dev)
+{
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       INIT_LIST_HEAD(&dev->hwlock_wq.list);
+       init_waitqueue_head(&dev->hwlock_wq.wait_queue);
+       mutex_init(&dev->hwlock_wq.wait_mutex);
+       dev->hwlock_wq.ctx = NULL;
+       dev->hwlock_wq.dev = dev;
+}
+
+static inline void s5p_mfc_init_listable_wq_ctx(struct s5p_mfc_ctx *curr_ctx)
+{
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       INIT_LIST_HEAD(&curr_ctx->hwlock_wq.list);
+       init_waitqueue_head(&curr_ctx->hwlock_wq.wait_queue);
+       mutex_init(&curr_ctx->hwlock_wq.wait_mutex);
+       curr_ctx->hwlock_wq.ctx = curr_ctx;
+       curr_ctx->hwlock_wq.dev = NULL;
+}
+
+static inline void s5p_mfc_destroy_listable_wq_dev(struct s5p_mfc_dev *dev)
+{
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       mutex_destroy(&dev->hwlock_wq.wait_mutex);
+}
+
+static inline void s5p_mfc_destroy_listable_wq_ctx(struct s5p_mfc_ctx *curr_ctx)
+{
+       if (!curr_ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       mutex_destroy(&curr_ctx->hwlock_wq.wait_mutex);
+}
+
+void s5p_mfc_init_hwlock(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_get_hwlock_dev(struct s5p_mfc_dev *dev);
+int s5p_mfc_get_hwlock_ctx(struct s5p_mfc_ctx *curr_ctx);
+
+int s5p_mfc_release_hwlock_dev(struct s5p_mfc_dev *dev);
+int s5p_mfc_release_hwlock_ctx(struct s5p_mfc_ctx *curr_ctx);
+
+void s5p_mfc_cache_flush(struct s5p_mfc_dev *dev, int is_drm);
+
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_work_bit_and_try_run(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_just_run(struct s5p_mfc_dev *dev, int new_ctx_index);
+
+void s5p_mfc_hwlock_handler_irq(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+               unsigned int reason, unsigned int err);
+
+#endif /* __S5P_MFC_HWLOCK_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_inst.c b/drivers/media/platform/exynos/mfc/s5p_mfc_inst.c
new file mode 100644 (file)
index 0000000..72f39b3
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_inst.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 "s5p_mfc_inst.h"
+
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_enc_param.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_utils.h"
+
+int s5p_mfc_open_inst(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int reg;
+       int ret;
+
+       /* Preparing decoding - getting instance number */
+       mfc_debug(2, "Getting instance number\n");
+       s5p_mfc_clean_ctx_int_flags(ctx);
+
+       reg = MFC_READL(S5P_FIMV_CODEC_CONTROL);
+       /* Clear OTF_CONTROL[2:1] & OTF_DEBUG[3] */
+       reg &= ~(0x7 << 1);
+       if (ctx->otf_handle) {
+               /* Set OTF_CONTROL[2:1], 0: Non-OTF, 1: OTF+HWFC, 2: OTF only */
+               reg |= (0x1 << 1);
+               mfc_info_ctx("HWFC + OTF enabled\n");
+               if (otf_dump && !ctx->is_drm) {
+                       /* Set OTF_DEBUG[3] for OTF path dump */
+                       reg |= (0x1 << 3);
+                       mfc_info_ctx("Debugging mode enabled\n");
+               }
+       }
+       MFC_WRITEL(reg, S5P_FIMV_CODEC_CONTROL);
+
+
+       ret = s5p_mfc_cmd_open_inst(ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to create a new instance.\n");
+               s5p_mfc_change_state(ctx, MFCINST_ERROR);
+       }
+
+       return ret;
+}
+
+int s5p_mfc_close_inst(struct s5p_mfc_ctx *ctx)
+{
+       int ret = -EINVAL;
+
+       /* Closing decoding instance  */
+       mfc_debug(2, "Returning instance number\n");
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       if (ctx->state == MFCINST_FREE) {
+               mfc_err_ctx("ctx already free status\n");
+               return ret;
+       }
+
+       ret = s5p_mfc_cmd_close_inst(ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to return an instance.\n");
+               s5p_mfc_change_state(ctx, MFCINST_ERROR);
+       }
+
+       return ret;
+}
+
+int s5p_mfc_abort_inst(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_NAL_ABORT);
+
+       return 0;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       unsigned int reg = 0;
+       int fmo_aso_ctrl = 0;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+       mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no, S5P_FIMV_H2R_CMD_SEQ_HEADER);
+       mfc_debug(2, "BUFs: %08x\n", MFC_READL(S5P_FIMV_D_CPB_BUFFER_ADDR));
+
+       /* When user sets desplay_delay to 0,
+        * It works as "display_delay enable" and delay set to 0.
+        * If user wants display_delay disable, It should be
+        * set to negative value. */
+       if (dec->display_delay >= 0) {
+               reg |= (0x1 << S5P_FIMV_D_DEC_OPT_DISPLAY_DELAY_EN_SHIFT);
+               MFC_WRITEL(dec->display_delay, S5P_FIMV_D_DISPLAY_DELAY);
+       }
+
+       /* FMO_ASO_CTRL - 0: Enable, 1: Disable */
+       reg |= ((fmo_aso_ctrl & S5P_FIMV_D_DEC_OPT_FMO_ASO_CTRL_MASK)
+                       << S5P_FIMV_D_DEC_OPT_FMO_ASO_CTRL_SHIFT);
+
+       reg |= ((dec->idr_decoding & S5P_FIMV_D_DEC_OPT_IDR_DECODING_MASK)
+                       << S5P_FIMV_D_DEC_OPT_IDR_DECODING_SHIFT);
+
+       /* VC1 RCV: Discard to parse additional header as default */
+       if (IS_VC1_RCV_DEC(ctx))
+               reg |= (0x1 << S5P_FIMV_D_DEC_OPT_DISCARD_RCV_HEADER_SHIFT);
+
+       /* conceal control to specific color */
+       if (FW_HAS_CONCEAL_CONTROL(dev))
+               reg |= (0x4 << S5P_FIMV_D_DEC_OPT_CONCEAL_CONTROL_SHIFT);
+
+       /* Disable parallel processing if nal_q_parallel_disable was set */
+       if (nal_q_parallel_disable)
+               reg |= (0x2 << S5P_FIMV_D_DEC_OPT_PARALLEL_DISABLE_SHIFT);
+
+       /* Realloc buffer for resolution decrease case in NAL QUEUE mode */
+       reg |= (0x1 << S5P_FIMV_D_DEC_OPT_REALLOC_CONTROL_SHIFT);
+
+       /* Parsing all including PPS */
+       reg |= (0x1 << S5P_FIMV_D_DEC_OPT_SPECIAL_PARSING_SHIFT);
+
+       MFC_WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS);
+
+       if (FW_HAS_CONCEAL_CONTROL(dev))
+               MFC_WRITEL(MFC_CONCEAL_COLOR, S5P_FIMV_D_FORCE_PIXEL_VAL);
+
+       if (IS_FIMV1_DEC(ctx)) {
+               mfc_debug(2, "Setting FIMV1 resolution to %dx%d\n",
+                                       ctx->img_width, ctx->img_height);
+               MFC_WRITEL(ctx->img_width, S5P_FIMV_D_SET_FRAME_WIDTH);
+               MFC_WRITEL(ctx->img_height, S5P_FIMV_D_SET_FRAME_HEIGHT);
+       }
+
+       s5p_mfc_set_pixel_format(dev, ctx->dst_fmt->fourcc);
+
+       reg = 0;
+       /* Enable realloc interface if SEI is enabled */
+       if (dec->sei_parse)
+               reg |= (0x1 << S5P_FIMV_D_SEI_ENABLE_NEED_INIT_BUFFER_SHIFT);
+       if (FW_HAS_SEI_INFO_FOR_HDR(dev)) {
+               reg |= (0x1 << S5P_FIMV_D_SEI_ENABLE_CONTENT_LIGHT_SHIFT);
+               reg |= (0x1 << S5P_FIMV_D_SEI_ENABLE_MASTERING_DISPLAY_SHIFT);
+       }
+       reg |= (0x1 << S5P_FIMV_D_SEI_ENABLE_RECOVERY_PARSING_SHIFT);
+
+       MFC_WRITEL(reg, S5P_FIMV_D_SEI_ENABLE);
+       mfc_debug(2, "SEI enable was set, 0x%x\n", MFC_READL(S5P_FIMV_D_SEI_ENABLE));
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SEQ_HEADER);
+
+       mfc_debug_leave();
+       return 0;
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       u32 reg = 0;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       mfc_debug(2, "Dynamic:0x%08x, Available:0x%lx\n",
+                       dec->dynamic_set, dec->available_dpb);
+
+       reg = MFC_READL(S5P_FIMV_D_NAL_START_OPTIONS);
+       reg &= ~(0x1 << S5P_FIMV_D_NAL_START_OPT_BLACK_BAR_SHIFT);
+       reg |= ((dec->detect_black_bar & 0x1) << S5P_FIMV_D_NAL_START_OPT_BLACK_BAR_SHIFT);
+       MFC_WRITEL(reg, S5P_FIMV_D_NAL_START_OPTIONS);
+       mfc_debug(3, "black bar detect set: %#x\n", reg);
+
+       MFC_WRITEL(dec->dynamic_set, S5P_FIMV_D_DYNAMIC_DPB_FLAG_LOWER);
+       MFC_WRITEL(0x0, S5P_FIMV_D_DYNAMIC_DPB_FLAG_UPPER);
+       MFC_WRITEL(dec->available_dpb, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER);
+       MFC_WRITEL(0x0, S5P_FIMV_D_AVAILABLE_DPB_FLAG_UPPER);
+       MFC_WRITEL(dec->slice_enable, S5P_FIMV_D_SLICE_IF_ENABLE);
+       MFC_WRITEL(MFC_TIMEOUT_VALUE, S5P_FIMV_DEC_TIMEOUT_VALUE);
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       /* Issue different commands to instance basing on whether it
+        * is the last frame or not. */
+       switch (last_frame) {
+       case 0:
+               s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_NAL_START);
+               break;
+       case 1:
+               s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_LAST_FRAME);
+               break;
+       }
+
+       mfc_debug(2, "Decoding a usual frame.\n");
+       return 0;
+}
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       mfc_debug(2, "++\n");
+
+       if (IS_H264_ENC(ctx))
+               s5p_mfc_set_enc_params_h264(ctx);
+       else if (IS_MPEG4_ENC(ctx))
+               s5p_mfc_set_enc_params_mpeg4(ctx);
+       else if (IS_H263_ENC(ctx))
+               s5p_mfc_set_enc_params_h263(ctx);
+       else if (IS_VP8_ENC(ctx))
+               s5p_mfc_set_enc_params_vp8(ctx);
+       else if (IS_VP9_ENC(ctx))
+               s5p_mfc_set_enc_params_vp9(ctx);
+       else if (IS_HEVC_ENC(ctx))
+               s5p_mfc_set_enc_params_hevc(ctx);
+       else if (IS_BPG_ENC(ctx))
+               s5p_mfc_set_enc_params_bpg(ctx);
+       else {
+               mfc_err_ctx("Unknown codec for encoding (%x).\n",
+                       ctx->codec_mode);
+               return -EINVAL;
+       }
+
+       mfc_debug(5, "RC) Bitrate: %d / framerate: %#x / config %#x / mode %#x\n",
+                       MFC_READL(S5P_FIMV_E_RC_BIT_RATE),
+                       MFC_READL(S5P_FIMV_E_RC_FRAME_RATE),
+                       MFC_READL(S5P_FIMV_E_RC_CONFIG),
+                       MFC_READL(S5P_FIMV_E_RC_MODE));
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SEQ_HEADER);
+
+       mfc_debug(2, "--\n");
+
+       return 0;
+}
+
+static int mfc_h264_set_aso_slice_order(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+       int i;
+
+       if (p_264->aso_enable) {
+               for (i = 0; i < 8; i++)
+                       MFC_WRITEL(p_264->aso_slice_order[i],
+                               S5P_FIMV_E_H264_ASO_SLICE_ORDER_0 + i * 4);
+       }
+       return 0;
+}
+
+/*
+       When the resolution is changed,
+       s5p_mfc_start_change_resol_enc() should be called right before NAL_START.
+       return value
+       0: no resolution change
+       1: resolution swap
+       2: resolution change
+*/
+static int mfc_start_change_resol_enc(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       unsigned int reg = 0;
+       int old_img_width;
+       int old_img_height;
+       int new_img_width;
+       int new_img_height;
+       int ret = 0;
+
+       if ((ctx->img_width == 0) || (ctx->img_height == 0)) {
+               mfc_err_dev("new_img_width = %d, new_img_height = %d\n",
+                       ctx->img_width, ctx->img_height);
+               return 0;
+       }
+
+       old_img_width = ctx->old_img_width;
+       old_img_height = ctx->old_img_height;
+
+       new_img_width = ctx->img_width;
+       new_img_height = ctx->img_height;
+
+       if ((old_img_width == new_img_width) && (old_img_height == new_img_height)) {
+               mfc_err_dev("Resolution is not changed. new_img_width = %d, new_img_height = %d\n",
+                       new_img_width, new_img_height);
+               return 0;
+       }
+
+       mfc_info_ctx("Resolution Change : (%d x %d) -> (%d x %d)\n",
+               old_img_width, old_img_height, new_img_width, new_img_height);
+
+       if ((old_img_width == new_img_height) && (old_img_height == new_img_width)) {
+               reg = MFC_READL(S5P_FIMV_E_PARAM_CHANGE);
+               reg &= ~(0x1 << 6);
+               reg |= (0x1 << 6); /* resolution swap */
+               MFC_WRITEL(reg, S5P_FIMV_E_PARAM_CHANGE);
+               ret = 1;
+       } else {
+               reg = MFC_READL(S5P_FIMV_E_PARAM_CHANGE);
+               reg &= ~(0x3 << 7);
+               /* For now, FW does not care S5P_FIMV_E_PARAM_CHANGE is 1 or 2.
+                * It cares S5P_FIMV_E_PARAM_CHANGE is NOT zero.
+                */
+               if ((old_img_width*old_img_height) < (new_img_width*new_img_height)) {
+                       reg |= (0x1 << 7); /* resolution increased */
+                       mfc_info_ctx("Resolution Increased\n");
+               } else {
+                       reg |= (0x2 << 7); /* resolution decreased */
+                       mfc_info_ctx("Resolution Decreased\n");
+               }
+               MFC_WRITEL(reg, S5P_FIMV_E_PARAM_CHANGE);
+
+               /** set cropped width */
+               MFC_WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH);
+               /** set cropped height */
+               MFC_WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT);
+
+               /* bit rate */
+               if (p->rc_frame)
+                       MFC_WRITEL(p->rc_bitrate, S5P_FIMV_E_RC_BIT_RATE);
+               else
+                       MFC_WRITEL(1, S5P_FIMV_E_RC_BIT_RATE);
+               ret = 2;
+       }
+
+       /** set new stride */
+       s5p_mfc_set_enc_stride(ctx);
+
+       /** set cropped offset */
+       MFC_WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET);
+
+       return ret;
+}
+
+/* Encode a single frame */
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       mfc_debug(2, "++\n");
+
+       if (IS_H264_ENC(ctx))
+               mfc_h264_set_aso_slice_order(ctx);
+
+       s5p_mfc_set_slice_mode(ctx);
+
+       if (ctx->enc_drc_flag) {
+               ctx->enc_res_change = mfc_start_change_resol_enc(ctx);
+               ctx->enc_drc_flag = 0;
+       }
+
+       MFC_WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID);
+       /* Issue different commands to instance basing on whether it
+        * is the last frame or not. */
+       switch (last_frame) {
+       case 0:
+               s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_NAL_START);
+               break;
+       case 1:
+               s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_LAST_FRAME);
+               break;
+       }
+
+       mfc_debug(2, "--\n");
+
+       return 0;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_inst.h b/drivers/media/platform/exynos/mfc/s5p_mfc_inst.h
new file mode 100644 (file)
index 0000000..a82135e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_inst.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 __S5P_MFC_INST_H
+#define __S5P_MFC_INST_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_open_inst(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_close_inst(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_abort_inst(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx, int last_frame);
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx, int last_frame);
+
+#endif /* __S5P_MFC_INST_H  */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_irq.c b/drivers/media/platform/exynos/mfc/s5p_mfc_irq.c
new file mode 100644 (file)
index 0000000..8cb8001
--- /dev/null
@@ -0,0 +1,1510 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_irq.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 "s5p_mfc_irq.h"
+
+#include "s5p_mfc_hwlock.h"
+#include "s5p_mfc_nal_q.h"
+#include "s5p_mfc_otf.h"
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+static void mfc_handle_black_bar_info(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       struct v4l2_rect new_black_bar;
+       int black_bar_info;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+
+       black_bar_info = s5p_mfc_get_black_bar_detection();
+       mfc_debug(3, "black bar type: %#x\n", black_bar_info);
+
+       if (black_bar_info == S5P_FIMV_DISP_STATUS_BLACK_BAR) {
+               new_black_bar.left = s5p_mfc_get_black_bar_pos_x();
+               new_black_bar.top = s5p_mfc_get_black_bar_pos_y();
+               new_black_bar.width = s5p_mfc_get_black_bar_image_w();
+               new_black_bar.height = s5p_mfc_get_black_bar_image_h();
+       } else if (black_bar_info == S5P_FIMV_DISP_STATUS_BLACK_SCREEN) {
+               new_black_bar.left = -1;
+               new_black_bar.top = -1;
+               new_black_bar.width = ctx->img_width;
+               new_black_bar.height = ctx->img_height;
+       } else if (black_bar_info == S5P_FIMV_DISP_STATUS_NOT_DETECTED) {
+               new_black_bar.left = 0;
+               new_black_bar.top = 0;
+               new_black_bar.width = ctx->img_width;
+               new_black_bar.height = ctx->img_height;
+       } else {
+               mfc_err_ctx("Not supported black bar type: %#x\n", black_bar_info);
+               dec->black_bar_updated = 0;
+               return;
+       }
+
+       if ((new_black_bar.left == dec->black_bar.left) &&
+                       (new_black_bar.top == dec->black_bar.top) &&
+                       (new_black_bar.width == dec->black_bar.width) &&
+                       (new_black_bar.height == dec->black_bar.height)) {
+               mfc_debug(3, "black bar info was not changed\n");
+               dec->black_bar_updated = 0;
+               return;
+       }
+
+       dec->black_bar = new_black_bar;
+       dec->black_bar_updated = 1;
+}
+
+static unsigned int mfc_handle_frame_field(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int interlace_type = 0, is_interlace = 0;
+       unsigned int field;
+
+       if (CODEC_INTERLACED(ctx))
+               is_interlace = s5p_mfc_is_interlace_picture();
+
+       if (is_interlace) {
+               interlace_type = s5p_mfc_get_interlace_type();
+               if (interlace_type)
+                       field = V4L2_FIELD_INTERLACED_TB;
+               else
+                       field = V4L2_FIELD_INTERLACED_BT;
+       } else {
+               field = V4L2_FIELD_NONE;
+       }
+
+       mfc_debug(2, "is_interlace : %d interlace_type : %d, field: 0x%#x\n",
+                       is_interlace, interlace_type, field);
+
+       return field;
+}
+
+static void mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *dst_mb;
+       int index, i, is_first = 1;
+
+       mfc_debug(2, "Decided to finish\n");
+       ctx->sequence++;
+
+       while (1) {
+               dst_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!dst_mb)
+                       break;
+
+               mfc_debug(2, "Cleaning up buffer: %d\n",
+                                         dst_mb->vb.vb2_buf.index);
+
+               index = dst_mb->vb.vb2_buf.index;
+
+               for (i = 0; i < ctx->dst_fmt->mem_planes; i++)
+                       vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0);
+
+               dst_mb->vb.sequence = (ctx->sequence++);
+               dst_mb->vb.field = mfc_handle_frame_field(ctx);
+
+               clear_bit(dst_mb->vb.vb2_buf.index, &dec->available_dpb);
+
+               if (call_cop(ctx, get_buf_ctrls_val, ctx, &ctx->dst_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in get_buf_ctrls_val\n");
+
+               if (is_first) {
+                       call_cop(ctx, get_buf_update_val, ctx,
+                               &ctx->dst_ctrls[index],
+                               V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                               dec->stored_tag);
+                       is_first = 0;
+               } else {
+                       call_cop(ctx, get_buf_update_val, ctx,
+                               &ctx->dst_ctrls[index],
+                               V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                               DEFAULT_TAG);
+                       call_cop(ctx, get_buf_update_val, ctx,
+                               &ctx->dst_ctrls[index],
+                               V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL,
+                               0);
+               }
+
+               vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+               /* decoder dst buffer CFW UNPROT */
+               if (ctx->is_drm)
+                       s5p_mfc_raw_unprotect(ctx, dst_mb, index);
+
+               mfc_debug(2, "Cleaned up buffer: %d\n", index);
+       }
+
+       s5p_mfc_handle_force_change_status(ctx);
+       mfc_debug(2, "After cleanup\n");
+}
+
+static void mfc_handle_frame_copy_timestamp(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_buf *ref_mb, *src_mb;
+       dma_addr_t dec_y_addr;
+
+       dec_y_addr = (dma_addr_t)s5p_mfc_get_dec_y_addr();
+
+       /* Get the source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+               return;
+       }
+
+       ref_mb = s5p_mfc_find_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, dec_y_addr);
+       if (ref_mb)
+               ref_mb->vb.vb2_buf.timestamp = src_mb->vb.vb2_buf.timestamp;
+}
+
+static void mfc_handle_frame_output_move(struct s5p_mfc_ctx *ctx,
+               dma_addr_t dspl_y_addr, unsigned int released_flag)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *ref_mb;
+       int index;
+
+       ref_mb = s5p_mfc_find_move_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->dst_buf_queue, &ctx->ref_buf_queue, dspl_y_addr, released_flag);
+       if (ref_mb) {
+               mfc_debug(2, "Listing: %d\n", ref_mb->vb.vb2_buf.index);
+               /* Check if this is the buffer we're looking for */
+               mfc_debug(2, "Found 0x%08llx, looking for 0x%08llx\n",
+                               s5p_mfc_mem_get_daddr_vb(&ref_mb->vb.vb2_buf, 0), dspl_y_addr);
+
+               index = ref_mb->vb.vb2_buf.index;
+
+               if (released_flag & (1 << index)) {
+                       dec->available_dpb &= ~(1 << index);
+                       released_flag &= ~(1 << index);
+                       mfc_debug(2, "Corrupted frame(%d), it will be re-used(release)\n",
+                                       s5p_mfc_get_warn(s5p_mfc_get_int_err()));
+               } else {
+                       dec->err_reuse_flag |= 1 << index;
+                       mfc_debug(2, "Corrupted frame(%d), it will be re-used(not released)\n",
+                                       s5p_mfc_get_warn(s5p_mfc_get_int_err()));
+               }
+               dec->dynamic_used |= released_flag;
+       }
+}
+
+static void mfc_handle_frame_output_del(struct s5p_mfc_ctx *ctx,
+               unsigned int err, unsigned int released_flag)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       struct s5p_mfc_buf *ref_mb;
+       dma_addr_t dspl_y_addr;
+       unsigned int frame_type;
+       unsigned int dst_frame_status;
+       unsigned int is_video_signal_type = 0, is_colour_description = 0;
+       unsigned int is_content_light = 0, is_display_colour = 0;
+       unsigned int i, index;
+
+       if (FW_HAS_VIDEO_SIGNAL_TYPE(dev)) {
+               is_video_signal_type = s5p_mfc_get_video_signal_type();
+               is_colour_description = s5p_mfc_get_colour_description();
+       }
+
+       if (FW_HAS_SEI_INFO_FOR_HDR(dev)) {
+               is_content_light = s5p_mfc_get_sei_avail_content_light();
+               is_display_colour = s5p_mfc_get_sei_avail_mastering_display();
+       }
+
+       if (FW_HAS_BLACK_BAR_DETECT(dev) && dec->detect_black_bar)
+               mfc_handle_black_bar_info(dev, ctx);
+       else
+               dec->black_bar_updated = 0;
+
+       if (dec->immediate_display == 1) {
+               dspl_y_addr = (dma_addr_t)s5p_mfc_get_dec_y_addr();
+               frame_type = s5p_mfc_get_dec_frame_type();
+       } else {
+               dspl_y_addr = (dma_addr_t)s5p_mfc_get_disp_y_addr();
+               frame_type = s5p_mfc_get_disp_frame_type();
+       }
+
+       ref_mb = s5p_mfc_find_del_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, dspl_y_addr);
+       if (ref_mb) {
+               mfc_debug(2, "Listing: %d\n", ref_mb->vb.vb2_buf.index);
+               /* Check if this is the buffer we're looking for */
+               mfc_debug(2, "Found 0x%08llx, looking for 0x%08llx\n",
+                               s5p_mfc_mem_get_daddr_vb(&ref_mb->vb.vb2_buf, 0), dspl_y_addr);
+
+               index = ref_mb->vb.vb2_buf.index;
+
+               ref_mb->vb.sequence = ctx->sequence;
+               ref_mb->vb.field = mfc_handle_frame_field(ctx);
+
+               /* Set reserved2 bits in order to inform SEI information */
+               ref_mb->vb.reserved2 = 0;
+
+               if (is_content_light) {
+                       ref_mb->vb.reserved2 |= (1 << 0);
+                       mfc_debug(2, "content light level parsed\n");
+               }
+
+               if (is_display_colour) {
+                       ref_mb->vb.reserved2 |= (1 << 1);
+                       mfc_debug(2, "mastering display colour parsed\n");
+               }
+
+               if (is_video_signal_type) {
+                       ref_mb->vb.reserved2 |= (1 << 4);
+                       mfc_debug(2, "video signal type parsed\n");
+                       if (is_colour_description) {
+                               ref_mb->vb.reserved2 |= (1 << 2);
+                               mfc_debug(5, "matrix coefficients parsed\n");
+                               ref_mb->vb.reserved2 |= (1 << 3);
+                               mfc_debug(2, "colour description parsed\n");
+                       }
+               }
+
+               if (IS_VP9_DEC(ctx) && FW_HAS_VP9_HDR(dev)) {
+                       if (ctx->color_space != S5P_FIMV_D_COLOR_UNKNOWN) {
+                               ref_mb->vb.reserved2 |= (1 << 3);
+                               mfc_debug(2, "color space parsed\n");
+                       }
+                       ref_mb->vb.reserved2 |= (1 << 4);
+                       mfc_debug(2, "color range parsed\n");
+               }
+
+               if (dec->black_bar_updated) {
+                       ref_mb->vb.reserved2 |= (1 << 5);
+                       mfc_debug(3, "black bar detected\n");
+               }
+
+               if (ctx->src_fmt->mem_planes == 1) {
+                       vb2_set_plane_payload(&ref_mb->vb.vb2_buf, 0,
+                                       raw->total_plane_size);
+                       mfc_debug(5, "single plane payload: %d\n",
+                                       raw->total_plane_size);
+               } else {
+                       for (i = 0; i < ctx->src_fmt->mem_planes; i++) {
+                               vb2_set_plane_payload(&ref_mb->vb.vb2_buf, i,
+                                               raw->plane_size[i]);
+                       }
+               }
+
+               clear_bit(index, &dec->available_dpb);
+
+               ref_mb->vb.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
+                                       V4L2_BUF_FLAG_PFRAME |
+                                       V4L2_BUF_FLAG_BFRAME |
+                                       V4L2_BUF_FLAG_ERROR);
+
+               switch (frame_type) {
+                       case S5P_FIMV_DISPLAY_FRAME_I:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_KEYFRAME;
+                               break;
+                       case S5P_FIMV_DISPLAY_FRAME_P:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_PFRAME;
+                               break;
+                       case S5P_FIMV_DISPLAY_FRAME_B:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_BFRAME;
+                               break;
+                       default:
+                               break;
+               }
+
+               if (s5p_mfc_get_warn(err)) {
+                       mfc_err_ctx("Warning for displayed frame: %d\n",
+                                       s5p_mfc_get_warn(err));
+                       ref_mb->vb.flags |= V4L2_BUF_FLAG_ERROR;
+               }
+
+               if (call_cop(ctx, get_buf_ctrls_val, ctx, &ctx->dst_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in get_buf_ctrls_val\n");
+
+               s5p_mfc_handle_released_info(ctx, released_flag, index);
+
+               if (dec->immediate_display == 1) {
+                       dst_frame_status = s5p_mfc_get_dec_status();
+
+                       call_cop(ctx, get_buf_update_val, ctx,
+                                       &ctx->dst_ctrls[index],
+                                       V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS,
+                                       dst_frame_status);
+
+                       call_cop(ctx, get_buf_update_val, ctx,
+                                       &ctx->dst_ctrls[index],
+                                       V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                                       dec->stored_tag);
+
+                       dec->immediate_display = 0;
+               }
+
+               /* Update frame tag for packed PB */
+               if (CODEC_MULTIFRAME(ctx) && (dec->y_addr_for_pb == dspl_y_addr)) {
+                       call_cop(ctx, get_buf_update_val, ctx,
+                                       &ctx->dst_ctrls[index],
+                                       V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                                       dec->stored_tag);
+                       dec->y_addr_for_pb = 0;
+               }
+
+               s5p_mfc_qos_update_last_framerate(ctx, ref_mb->vb.vb2_buf.timestamp);
+               vb2_buffer_done(&ref_mb->vb.vb2_buf, s5p_mfc_get_warn(err) ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+       }
+}
+
+static void mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       dma_addr_t dspl_y_addr;
+       unsigned int frame_type;
+       int mvc_view_id;
+       unsigned int prev_flag, released_flag = 0;
+
+       frame_type = s5p_mfc_get_disp_frame_type();
+       mvc_view_id = s5p_mfc_get_mvc_disp_view_id();
+
+       mfc_debug(2, "frame_type : %d\n", frame_type);
+
+       if (IS_H264_MVC_DEC(ctx)) {
+               if (mvc_view_id == 0)
+                       ctx->sequence++;
+       } else {
+               ctx->sequence++;
+       }
+
+       dspl_y_addr = s5p_mfc_get_disp_y_addr();
+
+       if (dec->immediate_display == 1) {
+               dspl_y_addr = (dma_addr_t)s5p_mfc_get_dec_y_addr();
+               frame_type = s5p_mfc_get_dec_frame_type();
+       }
+
+       /* If frame is same as previous then skip and do not dequeue */
+       if (frame_type == S5P_FIMV_DISPLAY_FRAME_NOT_CODED)
+               if (!CODEC_NOT_CODED(ctx))
+                       return;
+
+       prev_flag = dec->dynamic_used;
+       dec->dynamic_used = s5p_mfc_get_dec_used_flag();
+       released_flag = prev_flag & (~dec->dynamic_used);
+
+       mfc_debug(2, "Used flag = %08x, Released Buffer = %08x\n",
+                       dec->dynamic_used, released_flag);
+
+       /* decoder dst buffer CFW UNPROT */
+       s5p_mfc_unprotect_released_dpb(ctx, released_flag);
+
+       if ((IS_VC1_RCV_DEC(ctx) &&
+               s5p_mfc_get_warn(err) == S5P_FIMV_ERR_SYNC_POINT_NOT_RECEIVED) ||
+               (s5p_mfc_get_warn(err) == S5P_FIMV_ERR_BROKEN_LINK))
+               mfc_handle_frame_output_move(ctx, dspl_y_addr, released_flag);
+       else
+               mfc_handle_frame_output_del(ctx, err, released_flag);
+}
+
+static void mfc_handle_frame_error(struct s5p_mfc_ctx *ctx,
+               unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *src_mb;
+       unsigned int index;
+
+       if (ctx->type == MFCINST_ENCODER) {
+               mfc_err_ctx("Encoder Interrupt Error: %d\n", err);
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       mfc_err_ctx("Interrupt Error: %d\n", err);
+
+       /* Get the source buffer */
+       src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+       } else {
+               index = src_mb->vb.vb2_buf.index;
+               if (call_cop(ctx, recover_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in recover_buf_ctrls_val\n");
+
+               mfc_debug(2, "MFC needs next buffer.\n");
+               dec->consumed = 0;
+
+               if (call_cop(ctx, get_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in get_buf_ctrls_val\n");
+
+               /* decoder src buffer CFW UNPROT */
+               if (ctx->is_drm)
+                       s5p_mfc_stream_unprotect(ctx, src_mb, index);
+
+               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+       }
+
+       mfc_debug(2, "Assesing whether this context should be run again.\n");
+}
+
+static void mfc_handle_ref_frame(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *dst_mb;
+       dma_addr_t dec_addr;
+
+       dec_addr = (dma_addr_t)s5p_mfc_get_dec_y_addr();
+
+       /* Try to search decoded address in whole dst queue */
+       dst_mb = s5p_mfc_find_move_buf_vb_used(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, &ctx->dst_buf_queue, dec_addr);
+       if (dst_mb) {
+               mfc_debug(2, "Found in dst queue = 0x%08llx, buf = 0x%08llx\n",
+                               dec_addr, s5p_mfc_mem_get_daddr_vb(&dst_mb->vb.vb2_buf, 0));
+
+               if (!(dec->dynamic_set & s5p_mfc_get_dec_used_flag()))
+                       dec->dynamic_used |= dec->dynamic_set;
+       } else {
+               mfc_debug(2, "Can't find buffer for addr = 0x%08llx\n", dec_addr);
+       }
+}
+
+static void mfc_handle_reuse_buffer(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       unsigned int prev_flag, released_flag = 0;
+       int i;
+
+       prev_flag = dec->dynamic_used;
+       dec->dynamic_used = s5p_mfc_get_dec_used_flag();
+       released_flag = prev_flag & (~dec->dynamic_used);
+
+       if (!released_flag)
+               return;
+
+       /* reuse not referenced buf anymore */
+       for (i = 0; i < MFC_MAX_DPBS; i++)
+               if (released_flag & (1 << i))
+                       s5p_mfc_move_reuse_buffer(ctx, i);
+}
+
+static void mfc_handle_frame_input(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *src_mb;
+       unsigned int index;
+       int deleted = 0;
+       unsigned long consumed;
+
+       consumed = dec->consumed + s5p_mfc_get_consumed_stream();
+
+       if (s5p_mfc_get_err(err) == S5P_FIMV_ERR_NON_PAIRED_FIELD) {
+               /*
+                * For non-paired field, the same buffer need to be
+                * resubmitted and the consumed stream will be 0
+                */
+               mfc_debug(2, "Not paired field. Running again the same buffer.\n");
+               return;
+       }
+
+       /* Get the source buffer */
+       src_mb = s5p_mfc_get_del_if_consumed(&ctx->buf_queue_lock, &ctx->src_buf_queue,
+                       consumed, STUFF_BYTE, err, &deleted);
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+               return;
+       }
+
+       if (!deleted) {
+               /* Run MFC again on the same buffer */
+               mfc_debug(2, "Running again the same buffer.\n");
+
+               if (CODEC_MULTIFRAME(ctx))
+                       dec->y_addr_for_pb = (dma_addr_t)s5p_mfc_get_dec_y_addr();
+
+               dec->consumed = consumed;
+               dec->remained_size = src_mb->vb.vb2_buf.planes[0].bytesused
+                                       - dec->consumed;
+               dec->has_multiframe = 1;
+               /* Do not move src buffer to done_list */
+               return;
+       }
+
+       index = src_mb->vb.vb2_buf.index;
+       if (call_cop(ctx, recover_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+               mfc_err_ctx("failed in recover_buf_ctrls_val\n");
+
+       mfc_debug(2, "MFC needs next buffer.\n");
+       dec->consumed = 0;
+       dec->remained_size = 0;
+
+       if (call_cop(ctx, get_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+               mfc_err_ctx("failed in get_buf_ctrls_val\n");
+
+       /* decoder src buffer CFW UNPROT */
+       if (ctx->is_drm)
+               s5p_mfc_stream_unprotect(ctx, src_mb, index);
+
+       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+/* Handle frame decoding interrupt */
+static void mfc_handle_frame(struct s5p_mfc_ctx *ctx,
+                       unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       unsigned int dst_frame_status, sei_avail_frame_pack;
+       unsigned int res_change, need_dpb_change, need_scratch_change;
+
+       dst_frame_status = s5p_mfc_get_disp_status();
+       res_change = s5p_mfc_get_res_change();
+       need_dpb_change = s5p_mfc_get_dpb_change();
+       need_scratch_change = s5p_mfc_get_scratch_change();
+       sei_avail_frame_pack = s5p_mfc_get_sei_avail_frame_pack();
+
+       if (dec->immediate_display == 1)
+               dst_frame_status = s5p_mfc_get_dec_status();
+
+       mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
+       mfc_debug(5, "SEI available status: 0x%08x\n", s5p_mfc_get_sei_avail());
+       mfc_debug(5, "SEI content light: 0x%08x\n", s5p_mfc_get_sei_content_light());
+       mfc_debug(5, "SEI luminance: 0x%08x, 0x%08x white point: 0x%08x\n",
+                       s5p_mfc_get_sei_mastering0(), s5p_mfc_get_sei_mastering1(),
+                       s5p_mfc_get_sei_mastering2());
+       mfc_debug(5, "SEI display primaries: 0x%08x, 0x%08x, 0x%08x\n",
+                       s5p_mfc_get_sei_mastering3(), s5p_mfc_get_sei_mastering4(),
+                       s5p_mfc_get_sei_mastering5());
+       mfc_debug(2, "Used flag: old = %08x, new = %08x\n",
+                               dec->dynamic_used, s5p_mfc_get_dec_used_flag());
+
+       if (ctx->state == MFCINST_RES_CHANGE_INIT)
+               s5p_mfc_change_state(ctx, MFCINST_RES_CHANGE_FLUSH);
+
+       if (res_change) {
+               mfc_debug(2, "Resolution change set to %d\n", res_change);
+               s5p_mfc_change_state(ctx, MFCINST_RES_CHANGE_INIT);
+               ctx->wait_state = WAIT_DECODING;
+               mfc_debug(2, "Decoding waiting! : %d\n", ctx->wait_state);
+               return;
+       }
+
+       if (need_dpb_change || need_scratch_change)
+               mfc_debug(2, "Interframe resolution change is not supported\n");
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->src_buf_queue, 0) &&
+               s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)) {
+               mfc_err_dev("Queue count is zero for src and dst\n");
+               goto leave_handle_frame;
+       }
+
+       if (IS_H264_DEC(ctx) && sei_avail_frame_pack &&
+               dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY) {
+               mfc_debug(2, "Frame packing SEI exists for a frame.\n");
+               mfc_debug(2, "Reallocate DPBs and issue init_buffer.\n");
+               ctx->is_dpb_realloc = 1;
+               s5p_mfc_change_state(ctx, MFCINST_HEAD_PARSED);
+               ctx->capture_state = QUEUE_FREE;
+               ctx->wait_state = WAIT_DECODING;
+               mfc_handle_frame_all_extracted(ctx);
+               goto leave_handle_frame;
+       }
+
+       /* All frames remaining in the buffer have been extracted  */
+       if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+               if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+                       struct mfc_timestamp *temp_ts = NULL;
+
+                       mfc_debug(2, "Last frame received after resolution change.\n");
+                       mfc_handle_frame_all_extracted(ctx);
+                       s5p_mfc_change_state(ctx, MFCINST_RES_CHANGE_END);
+                       /* If there is no display frame after resolution change,
+                        * Some released frames can't be unprotected.
+                        * So, check and request unprotection in the end of DRC.
+                        */
+                       s5p_mfc_cleanup_assigned_dpb(ctx);
+
+                       /* empty the timestamp queue */
+                       while (!list_empty(&ctx->ts_list)) {
+                               temp_ts = list_entry((&ctx->ts_list)->next,
+                                               struct mfc_timestamp, list);
+                               list_del(&temp_ts->list);
+                       }
+                       ctx->ts_count = 0;
+                       ctx->ts_is_full = 0;
+                       s5p_mfc_qos_reset_last_framerate(ctx);
+                       s5p_mfc_qos_set_framerate(ctx, DEC_DEFAULT_FPS);
+
+                       goto leave_handle_frame;
+               } else {
+                       mfc_handle_frame_all_extracted(ctx);
+               }
+       }
+
+       if (s5p_mfc_get_num_of_tile() >= 4)
+               dec->num_of_tile_over_4 = 1;
+
+       switch (dst_frame_status) {
+       case S5P_FIMV_DEC_STATUS_DECODING_DISPLAY:
+               mfc_handle_ref_frame(ctx);
+               break;
+       case S5P_FIMV_DEC_STATUS_DECODING_ONLY:
+               mfc_handle_ref_frame(ctx);
+               /*
+                * Some cases can have many decoding only frames like VP9
+                * alt-ref frame. So need handling release buffer
+                * because of DPB full.
+                */
+               mfc_handle_reuse_buffer(ctx);
+               break;
+       default:
+               break;
+       }
+
+       if (s5p_mfc_dec_status_decoding(dst_frame_status))
+               mfc_handle_frame_copy_timestamp(ctx);
+
+       /* A frame has been decoded and is in the buffer  */
+       if (s5p_mfc_dec_status_display(dst_frame_status))
+               mfc_handle_frame_new(ctx, err);
+       else
+               mfc_debug(2, "No frame decode.\n");
+
+       /* Mark source buffer as complete */
+       if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY)
+               mfc_handle_frame_input(ctx, err);
+
+leave_handle_frame:
+       mfc_debug(2, "Assesing whether this context should be run again.\n");
+}
+
+/* Handle encoder resolution change */
+static void mfc_enc_res_change(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned int reg = 0;
+
+       /*
+        * Right after the first NAL_START finished with the new resolution,
+        * We need to reset the fields
+        */
+       if (ctx->enc_res_change) {
+               /* clear resolution change bits */
+               s5p_mfc_clear_enc_res_change(dev);
+
+               ctx->enc_res_change_state = ctx->enc_res_change;
+               ctx->enc_res_change = 0;
+       }
+
+       if (ctx->enc_res_change_state == 1) { /* resolution swap */
+               ctx->enc_res_change_state = 0;
+       } else if (ctx->enc_res_change_state == 2) { /* resolution change */
+               reg = s5p_mfc_get_enc_nal_done_info();
+
+               /*
+                * Encoding resolution status
+                * 0: Normal encoding
+                * 1: Resolution Change for B-frame
+                *    (Encode with previous resolution)
+                * 2: Resolution Change for B-frame
+                *    (Last encoding with previous resolution)
+                * 3: Resolution Change for only P-frame
+                *    (No encode, as all frames with previous resolution are encoded)
+                */
+               mfc_debug(2, "Encoding Resolution Status : %d\n", reg);
+
+               if (reg == 2 || reg == 3) {
+                       s5p_mfc_release_codec_buffers(ctx);
+                       /* for INIT_BUFFER cmd */
+                       s5p_mfc_change_state(ctx, MFCINST_HEAD_PARSED);
+
+                       ctx->enc_res_change_state = 0;
+                       ctx->min_scratch_buf_size = s5p_mfc_get_enc_scratch_size();
+                       mfc_debug(2, "S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE = 0x%x\n",
+                                       (unsigned int)ctx->min_scratch_buf_size);
+                       if (reg == 3)
+                               ctx->enc_res_change_re_input = 1;
+               }
+       }
+}
+
+static void mfc_handle_stream_input(struct s5p_mfc_ctx *ctx, int slice_type)
+{
+       struct s5p_mfc_raw_info *raw;
+       struct s5p_mfc_buf *ref_mb, *src_mb;
+       dma_addr_t enc_addr[3] = { 0, 0, 0 };
+       int i;
+       unsigned int index;
+
+       raw = &ctx->raw_buf;
+
+       if (!ctx->enc_res_change_re_input && slice_type >= 0 &&
+                       ctx->state != MFCINST_FINISHING) {
+               if (ctx->state == MFCINST_RUNNING_NO_OUTPUT ||
+                       ctx->state == MFCINST_RUNNING_BUF_FULL)
+                       s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+
+               s5p_mfc_get_enc_frame_buffer(ctx, &enc_addr[0], raw->num_planes);
+
+               for (i = 0; i < raw->num_planes; i++)
+                       mfc_debug(2, "encoded[%d] addr: 0x%08llx\n",
+                                               i, enc_addr[i]);
+
+               src_mb = s5p_mfc_find_del_buf_raw(&ctx->buf_queue_lock,
+                       &ctx->src_buf_queue, enc_addr[0]);
+               if (src_mb) {
+                       index = src_mb->vb.vb2_buf.index;
+                       if (call_cop(ctx, recover_buf_ctrls_val, ctx,
+                                       &ctx->src_ctrls[index]) < 0)
+                               mfc_err_ctx("failed in recover_buf_ctrls_val\n");
+
+                       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+                       /* encoder src buffer CFW UNPROT */
+                       if (ctx->is_drm)
+                               s5p_mfc_raw_unprotect(ctx, src_mb, index);
+               }
+
+               ref_mb = s5p_mfc_find_del_buf_raw(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, enc_addr[0]);
+               if (ref_mb) {
+                       vb2_buffer_done(&ref_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+                       /* encoder src buffer CFW UNPROT */
+                       if (ctx->is_drm) {
+                               index = ref_mb->vb.vb2_buf.index;
+                               s5p_mfc_raw_unprotect(ctx, ref_mb, index);
+                       }
+               }
+       } else if (ctx->state == MFCINST_FINISHING) {
+               src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock,
+                               &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!src_mb) {
+                       mfc_err_dev("no src buffers.\n");
+                       return;
+               }
+
+               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+               /* encoder src buffer CFW UNPROT */
+               if (ctx->is_drm) {
+                       index = src_mb->vb.vb2_buf.index;
+                       s5p_mfc_raw_unprotect(ctx, src_mb, index);
+               }
+       }
+}
+
+static void mfc_handle_stream_output(struct s5p_mfc_ctx *ctx, int slice_type,
+                                       unsigned int strm_size)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_buf *dst_mb;
+       unsigned int index;
+
+       if (strm_size > 0 || ctx->state == MFCINST_FINISHING
+                         || ctx->state == MFCINST_RUNNING_BUF_FULL) {
+               /* at least one more dest. buffers exist always  */
+               dst_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock,
+                               &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!dst_mb) {
+                       mfc_err_dev("no dst buffers.\n");
+                       return;
+               }
+
+               dst_mb->vb.flags &=
+                       ~(V4L2_BUF_FLAG_KEYFRAME |
+                         V4L2_BUF_FLAG_PFRAME |
+                         V4L2_BUF_FLAG_BFRAME);
+
+               switch (slice_type) {
+               case S5P_FIMV_E_SLICE_TYPE_I:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_KEYFRAME;
+                       break;
+               case S5P_FIMV_E_SLICE_TYPE_P:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_PFRAME;
+                       break;
+               case S5P_FIMV_E_SLICE_TYPE_B:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_BFRAME;
+                       break;
+               default:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_KEYFRAME;
+                       break;
+               }
+               mfc_debug(2, "Slice type : %d\n", dst_mb->vb.flags);
+
+               if (IS_BPG_ENC(ctx)) {
+                       strm_size += enc->header_size;
+                       mfc_debug(2, "bpg total stream size: %d\n", strm_size);
+               }
+               vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, strm_size);
+
+               index = dst_mb->vb.vb2_buf.index;
+               if (call_cop(ctx, get_buf_ctrls_val, ctx, &ctx->dst_ctrls[index]) < 0)
+                       mfc_err_ctx("failed in get_buf_ctrls_val\n");
+
+               if (strm_size == 0 && ctx->state == MFCINST_FINISHING)
+                       call_cop(ctx, get_buf_update_val, ctx,
+                               &ctx->dst_ctrls[index],
+                               V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                               enc->stored_tag);
+
+               vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+               /* encoder dst buffer CFW UNPROT */
+               if (ctx->is_drm)
+                       s5p_mfc_stream_unprotect(ctx, dst_mb, index);
+       } else if (strm_size == 0 && slice_type == S5P_FIMV_E_SLICE_TYPE_SKIPPED) {
+               /* TO-DO: skipped frame should be handled */
+       }
+}
+
+/* Handle frame encoding interrupt */
+static int mfc_handle_stream(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_raw_info *raw;
+       int slice_type;
+       unsigned int strm_size;
+       unsigned int pic_count;
+
+       slice_type = s5p_mfc_get_enc_slice_type();
+       strm_size = s5p_mfc_get_enc_strm_size();
+       pic_count = s5p_mfc_get_enc_pic_count();
+
+       mfc_debug(2, "encoded slice type: %d\n", slice_type);
+       mfc_debug(2, "encoded stream size: %d\n", strm_size);
+       mfc_debug(2, "display order: %d\n", pic_count);
+
+       if (enc->buf_full) {
+               s5p_mfc_change_state(ctx, MFCINST_ABORT_INST);
+               return 0;
+       }
+
+       if (ctx->enc_res_change || ctx->enc_res_change_state)
+               mfc_enc_res_change(ctx);
+
+       /* set encoded frame type */
+       enc->frame_type = slice_type;
+       raw = &ctx->raw_buf;
+
+       ctx->sequence++;
+
+       /* handle destination buffer */
+       mfc_handle_stream_output(ctx, slice_type, strm_size);
+
+       if (enc->in_slice) {
+               if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)) {
+                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+               }
+               return 0;
+       }
+
+       /* handle source buffer */
+       mfc_handle_stream_input(ctx, slice_type);
+
+       if (ctx->enc_res_change_re_input)
+               ctx->enc_res_change_re_input = 0;
+
+       if (s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->src_buf_queue, 0) &&
+               ((ctx->state == MFCINST_RUNNING) ||
+                (ctx->state == MFCINST_RUNNING_NO_OUTPUT) ||
+                (ctx->state == MFCINST_RUNNING_BUF_FULL))) {
+
+               s5p_mfc_move_first_buf_used(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, &ctx->src_buf_queue, MFC_QUEUE_ADD_BOTTOM);
+
+               /* slice_type = 4 && strm_size = 0, skipped enable
+                  should be considered */
+               if ((slice_type == -1) && (strm_size == 0))
+                       s5p_mfc_change_state(ctx, MFCINST_RUNNING_NO_OUTPUT);
+
+               mfc_debug(2, "slice_type: %d, ctx->state: %d\n", slice_type, ctx->state);
+               mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+                         s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                         s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue));
+       }
+
+       return 0;
+}
+
+/* Error handling for interrupt */
+static inline void mfc_handle_error(struct s5p_mfc_ctx *ctx,
+       unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_buf *src_mb;
+       int index;
+
+       mfc_err_ctx("Interrupt Error: display: %d, decoded: %d\n",
+                       s5p_mfc_get_warn(err), s5p_mfc_get_err(err));
+       err = s5p_mfc_get_err(err);
+
+       /* Error recovery is dependent on the state of context */
+       switch (ctx->state) {
+       case MFCINST_RES_CHANGE_END:
+       case MFCINST_GOT_INST:
+               /* This error had to happen while parsing the header */
+               if (!ctx->is_drm) {
+                       unsigned char *stream_vir = NULL;
+                       unsigned int strm_size = 0;
+
+                       src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+                       if (src_mb) {
+                               stream_vir = src_mb->vir_addr;
+                               strm_size = src_mb->vb.vb2_buf.planes[0].bytesused;
+                               if (strm_size > 32)
+                                       strm_size = 32;
+
+                               if (stream_vir && strm_size)
+                                       print_hex_dump(KERN_ERR, "No header: ",
+                                                       DUMP_PREFIX_ADDRESS, strm_size, 0,
+                                                       stream_vir, strm_size, false);
+
+                               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+                       }
+               } else {
+                       src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+                       if (src_mb) {
+                               index = src_mb->vb.vb2_buf.index;
+                               /* decoder src buffer CFW UNPROT */
+                               s5p_mfc_stream_unprotect(ctx, src_mb, index);
+                               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+                       }
+               }
+               break;
+       case MFCINST_INIT:
+               /* This error had to happen while acquireing instance */
+       case MFCINST_RETURN_INST:
+               /* This error had to happen while releasing instance */
+       case MFCINST_DPB_FLUSHING:
+               /* This error had to happen while flushing DPB */
+       case MFCINST_SPECIAL_PARSING:
+       case MFCINST_SPECIAL_PARSING_NAL:
+               /* This error had to happen while special parsing */
+               break;
+       case MFCINST_HEAD_PARSED:
+               /* This error had to happen while setting dst buffers */
+       case MFCINST_RES_CHANGE_INIT:
+       case MFCINST_RES_CHANGE_FLUSH:
+               /* This error has to happen while resolution change */
+       case MFCINST_ABORT_INST:
+               /* This error has to happen while buffer full handling */
+       case MFCINST_FINISHING:
+               /* It is higly probable that an error occured
+                * while decoding a frame */
+               s5p_mfc_change_state(ctx, MFCINST_ERROR);
+               /* Mark all dst buffers as having an error */
+               s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue);
+               /* Mark all src buffers as having an error */
+               s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_queue);
+               break;
+       default:
+               mfc_err_ctx("Encountered an error interrupt which had not been handled.\n");
+               mfc_err_ctx("ctx->state = %d, ctx->inst_no = %d\n",
+                                               ctx->state, ctx->inst_no);
+               break;
+       }
+
+       s5p_mfc_wake_up_dev(dev, reason, err);
+
+       return;
+}
+
+/* Handle header decoder interrupt */
+static int mfc_handle_seq_dec(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       int i;
+
+       if (ctx->src_fmt->fourcc != V4L2_PIX_FMT_FIMV1) {
+               ctx->img_width = s5p_mfc_get_img_width();
+               ctx->img_height = s5p_mfc_get_img_height();
+               mfc_info_ctx("width: %d, height: %d\n", ctx->img_width, ctx->img_height);
+       }
+
+       ctx->dpb_count = s5p_mfc_get_dpb_count();
+       ctx->scratch_buf_size = s5p_mfc_get_scratch_size();
+       for (i = 0; i < ctx->dst_fmt->num_planes; i++)
+               ctx->min_dpb_size[i] = s5p_mfc_get_min_dpb_size(i);
+
+       s5p_mfc_dec_store_crop_info(ctx);
+       dec->mv_count = s5p_mfc_get_mv_count();
+       if (CODEC_10BIT(ctx)) {
+               if (s5p_mfc_get_luma_bit_depth_minus8() ||
+                       s5p_mfc_get_chroma_bit_depth_minus8() ||
+                       s5p_mfc_get_profile() == S5P_FIMV_D_PROFILE_HEVC_MAIN_10) {
+                       ctx->is_10bit = 1;
+                       mfc_info_ctx("10bit contents, profile: %d, depth: %d/%d\n",
+                                       s5p_mfc_get_profile(),
+                                       s5p_mfc_get_luma_bit_depth_minus8() + 8,
+                                       s5p_mfc_get_chroma_bit_depth_minus8() + 8);
+               }
+       }
+       if (CODEC_422FORMAT(ctx)) {
+               if (s5p_mfc_get_chroma_format() == S5P_FIMV_D_CHROMA_422) {
+                       ctx->is_422format = 1;
+                       mfc_info_ctx("422 chroma format\n");
+               }
+       }
+
+       if (ctx->img_width == 0 || ctx->img_height == 0)
+               s5p_mfc_change_state(ctx, MFCINST_ERROR);
+       else
+               s5p_mfc_change_state(ctx, MFCINST_HEAD_PARSED);
+
+       if (ctx->state == MFCINST_HEAD_PARSED)
+               dec->is_interlaced =
+                       s5p_mfc_is_interlace_picture();
+
+       if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx)) {
+               struct s5p_mfc_buf *src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (src_mb) {
+                       dec->consumed += s5p_mfc_get_consumed_stream();
+                       mfc_debug(2, "Check consumed size of header. ");
+                       mfc_debug(2, "total size : %d, consumed : %lu\n",
+                                       src_mb->vb.vb2_buf.planes[0].bytesused, dec->consumed);
+                       if ((dec->consumed > 0) &&
+                                       (src_mb->vb.vb2_buf.planes[0].bytesused > dec->consumed)) {
+                               dec->remained_size = src_mb->vb.vb2_buf.planes[0].bytesused -
+                                       dec->consumed;
+                               mfc_debug(2, "there is remained bytes after header parsing\n");
+                               mfc_debug(2, "remained_size: %lu\n", dec->remained_size);
+                       } else {
+                               dec->consumed = 0;
+                               dec->remained_size = 0;
+                       }
+               }
+       }
+
+       if (IS_VP9_DEC(ctx)) {
+               ctx->color_range = s5p_mfc_get_color_range();
+               ctx->color_space = s5p_mfc_get_color_space();
+               mfc_debug(2, "color range: %d, color space: %d, It's valid for VP9\n",
+                               ctx->color_range, ctx->color_space);
+       }
+
+       return 0;
+}
+
+/* Handle header encoder interrupt */
+static int mfc_handle_seq_enc(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       struct s5p_mfc_buf *dst_mb;
+       int ret;
+
+       enc->header_size = s5p_mfc_get_enc_strm_size();
+       mfc_debug(2, "seq header size: %d\n", enc->header_size);
+
+       if (IS_BPG_ENC(ctx)) {
+               dst_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!dst_mb) {
+                       mfc_err_dev("no dst buffers.\n");
+                       return -EAGAIN;
+               }
+
+               dst_mb->vb.vb2_buf.planes[0].data_offset += (enc->header_size +
+                               p->codec.bpg.thumb_size + p->codec.bpg.exif_size);
+               mfc_debug(2, "offset for NAL_START: %d (header: %d + thumb: %d + exif: %d)\n",
+                               dst_mb->vb.vb2_buf.planes[0].data_offset,
+                               enc->header_size,
+                               p->codec.bpg.thumb_size,
+                               p->codec.bpg.exif_size);
+       } else {
+               /* TODO: is there other case? */
+               if ((p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) ||
+                       (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY)) {
+                       dst_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock,
+                                       &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED);
+                       if (!dst_mb) {
+                               mfc_err_dev("no dst buffers.\n");
+                               return -EAGAIN;
+                       }
+
+                       vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, s5p_mfc_get_enc_strm_size());
+                       vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+                       /* encoder dst buffer CFW UNPROT */
+                       if (ctx->is_drm) {
+                               int index = dst_mb->vb.vb2_buf.index;
+
+                               s5p_mfc_stream_unprotect(ctx, dst_mb, index);
+                       }
+
+               }
+       }
+
+       ctx->dpb_count = s5p_mfc_get_enc_dpb_count();
+       ctx->scratch_buf_size = s5p_mfc_get_enc_scratch_size();
+
+       ret = s5p_mfc_alloc_codec_buffers(ctx);
+       if (ret) {
+               mfc_err_ctx("Failed to allocate encoding buffers.\n");
+               return ret;
+       }
+
+       s5p_mfc_change_state(ctx, MFCINST_HEAD_PARSED);
+
+       return 0;
+}
+
+irqreturn_t s5p_mfc_top_half_irq(int irq, void *priv)
+{
+       struct s5p_mfc_dev *dev = priv;
+       struct s5p_mfc_ctx *ctx;
+       unsigned int err;
+       unsigned int reason;
+
+       ctx = dev->ctx[dev->curr_ctx];
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return IRQ_WAKE_THREAD;
+       }
+
+       reason = s5p_mfc_get_int_reason();
+       err = s5p_mfc_get_int_err();
+       mfc_debug(2, "[c:%d] Int reason: %d (err: %d)\n",
+                       dev->curr_ctx, reason, err);
+       MFC_TRACE_CTX("<< INT(top): %d\n", reason);
+
+       return IRQ_WAKE_THREAD;
+}
+
+#ifdef NAL_Q_ENABLE
+/*
+ * Return value description
+ *  0: NAL-Q is handled successfully
+ *  1: NAL_START command
+ * -1: Error
+*/
+static inline int mfc_nal_q_irq(struct s5p_mfc_dev *dev,
+               unsigned int reason, unsigned int err)
+{
+       int ret = -1;
+       unsigned int errcode;
+
+       nal_queue_handle *nal_q_handle = dev->nal_q_handle;
+       EncoderOutputStr *pOutStr;
+
+       switch (reason) {
+       case S5P_FIMV_R2H_CMD_QUEUE_DONE_RET:
+               pOutStr = s5p_mfc_nal_q_dequeue_out_buf(dev,
+                       nal_q_handle->nal_q_out_handle, &errcode);
+               if (pOutStr) {
+                       if (s5p_mfc_nal_q_handle_out_buf(dev, pOutStr))
+                               mfc_err_dev("NAL Q: Failed to handle out buf\n");
+               } else {
+                       mfc_err_dev("NAL Q: pOutStr is NULL\n");
+               }
+
+               if (nal_q_handle->nal_q_exception)
+                       s5p_mfc_set_bit(nal_q_handle->nal_q_out_handle->nal_q_ctx,
+                                       &dev->work_bits);
+               s5p_mfc_clear_int_sfr();
+
+               ret = 0;
+               break;
+       case S5P_FIMV_R2H_CMD_COMPLETE_QUEUE_RET:
+               s5p_mfc_watchdog_stop_tick(dev);
+               s5p_mfc_nal_q_cleanup_queue(dev);
+               nal_q_handle->nal_q_state = NAL_Q_STATE_CREATED;
+               MFC_TRACE_DEV("** NAL Q state : %d\n", nal_q_handle->nal_q_state);
+               mfc_debug(2, "NAL Q: return to created state\n");
+               s5p_mfc_clear_int_sfr();
+               s5p_mfc_pm_clock_off(dev);
+               s5p_mfc_wake_up_dev(dev, reason, err);
+
+               ret = 0;
+               break;
+       default:
+               if (nal_q_handle->nal_q_state == NAL_Q_STATE_STARTED ||
+                       nal_q_handle->nal_q_state == NAL_Q_STATE_STOPPED) {
+                       mfc_err_dev("NAL Q: Should not be here! state: %d, int reason : %d\n",
+                               nal_q_handle->nal_q_state, reason);
+                       s5p_mfc_clear_int_sfr();
+
+                       ret = -1;
+               } else {
+                       /* NAL START */
+                       ret = 1;
+               }
+
+               break;
+       }
+
+       if (ret == 0)
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       return ret;
+}
+#endif
+
+static inline int mfc_handle_done_frame(struct s5p_mfc_ctx *ctx,
+                               unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = NULL;
+
+       if (ctx->type == MFCINST_DECODER) {
+               if (ctx->state == MFCINST_SPECIAL_PARSING_NAL) {
+                       s5p_mfc_clear_int_sfr();
+                       s5p_mfc_pm_clock_off(dev);
+                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+                       s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+                       s5p_mfc_wake_up_ctx(ctx, reason, err);
+                       return 0;
+               }
+               mfc_handle_frame(ctx, reason, err);
+       } else if (ctx->type == MFCINST_ENCODER) {
+               if (ctx->otf_handle) {
+                       s5p_mfc_otf_handle_stream(ctx);
+                       return 1;
+               }
+               enc = ctx->enc_priv;
+               if (reason == S5P_FIMV_R2H_CMD_SLICE_DONE_RET) {
+                       dev->preempt_ctx = ctx->num;
+                       enc->buf_full = 0;
+                       enc->in_slice = 1;
+               } else if (reason == S5P_FIMV_R2H_CMD_ENC_BUFFER_FULL_RET) {
+                       mfc_err_ctx("stream buffer size(%d) isn't enough\n",
+                                       s5p_mfc_get_enc_strm_size());
+                       dev->preempt_ctx = ctx->num;
+                       enc->buf_full = 1;
+                       enc->in_slice = 0;
+               } else {
+                       enc->buf_full = 0;
+                       enc->in_slice = 0;
+               }
+               mfc_handle_stream(ctx);
+       }
+
+       return 1;
+}
+
+static inline void mfc_handle_nal_abort(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       if (ctx->type == MFCINST_ENCODER) {
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING_BUF_FULL);
+               enc->buf_full = 0;
+               if (IS_VP8_ENC(ctx))
+                       mfc_err_ctx("stream buffer size isn't enough\n");
+               mfc_handle_stream(ctx);
+       } else {
+               s5p_mfc_change_state(ctx, MFCINST_ABORT);
+       }
+}
+
+static int mfc_irq_dev(struct s5p_mfc_dev *dev, unsigned int reason, unsigned int err)
+{
+       /* Stop the timeout watchdog */
+       if (reason != S5P_FIMV_R2H_CMD_FW_STATUS_RET)
+               s5p_mfc_watchdog_stop_tick(dev);
+
+       switch (reason) {
+       case S5P_FIMV_R2H_CMD_CACHE_FLUSH_RET:
+       case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+       case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+       case S5P_FIMV_R2H_CMD_SLEEP_RET:
+       case S5P_FIMV_R2H_CMD_WAKEUP_RET:
+               s5p_mfc_clear_int_sfr();
+               s5p_mfc_wake_up_dev(dev, reason, err);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int mfc_irq_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       switch (reason) {
+       case S5P_FIMV_R2H_CMD_ERR_RET:
+               if (ctx->otf_handle) {
+                       s5p_mfc_otf_handle_error(ctx, reason, err);
+                       break;
+               }
+               /* An error has occured */
+               if (ctx->state == MFCINST_RUNNING || ctx->state == MFCINST_ABORT ||
+                               ctx->state == MFCINST_RUNNING_NO_OUTPUT) {
+                       if ((s5p_mfc_get_err(err) >= S5P_FIMV_ERR_WARNINGS_START) &&
+                               (s5p_mfc_get_err(err) <= S5P_FIMV_ERR_WARNINGS_END))
+                               mfc_handle_frame(ctx, reason, err);
+                       else
+                               mfc_handle_frame_error(ctx, reason, err);
+               } else {
+                       mfc_handle_error(ctx, reason, err);
+               }
+               break;
+       case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+       case S5P_FIMV_R2H_CMD_FIELD_DONE_RET:
+       case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+       case S5P_FIMV_R2H_CMD_COMPLETE_SEQ_RET:
+       case S5P_FIMV_R2H_CMD_ENC_BUFFER_FULL_RET:
+               return mfc_handle_done_frame(ctx, reason, err);
+       case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+               if (ctx->type == MFCINST_ENCODER) {
+                       if (ctx->otf_handle) {
+                               s5p_mfc_otf_handle_seq(ctx);
+                               break;
+                       }
+                       mfc_handle_seq_enc(ctx);
+               } else if (ctx->type == MFCINST_DECODER) {
+                       mfc_handle_seq_dec(ctx);
+               }
+               break;
+       case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+               ctx->inst_no = s5p_mfc_get_inst_no();
+               s5p_mfc_change_state(ctx, MFCINST_GOT_INST);
+               break;
+       case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+               s5p_mfc_change_state(ctx, MFCINST_FREE);
+               break;
+       case S5P_FIMV_R2H_CMD_NAL_ABORT_RET:
+               mfc_handle_nal_abort(ctx);
+               break;
+       case S5P_FIMV_R2H_CMD_DPB_FLUSH_RET:
+               s5p_mfc_change_state(ctx, MFCINST_ABORT);
+               break;
+       case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+               if (err != 0) {
+                       mfc_err_ctx("INIT_BUFFERS_RET error: %d\n", err);
+                       break;
+               }
+
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+               if (ctx->type == MFCINST_DECODER) {
+                       if (ctx->wait_state == WAIT_DECODING) {
+                               ctx->wait_state = WAIT_INITBUF_DONE;
+                               mfc_debug(2, "INIT_BUFFER has done, but can't start decoding\n");
+                       }
+                       if (ctx->is_dpb_realloc)
+                               ctx->is_dpb_realloc = 0;
+               }
+               break;
+       default:
+               mfc_err_ctx("Unknown int reason: %d\n", reason);
+       }
+
+       return 1;
+}
+
+/* Interrupt processing */
+irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+       struct s5p_mfc_dev *dev = priv;
+       struct s5p_mfc_ctx *ctx;
+       unsigned int reason;
+       unsigned int err;
+       int ret = -1;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               goto irq_end;
+       }
+
+       if (s5p_mfc_pm_get_pwr_ref_cnt(dev) == 0) {
+               mfc_err_dev("no mfc power on\n");
+               goto irq_end;
+       }
+
+       /* Get the reason of interrupt and the error code */
+       reason = s5p_mfc_get_int_reason();
+       err = s5p_mfc_get_int_err();
+       mfc_debug(1, "Int reason: %d (err: %d)\n", reason, err);
+       MFC_TRACE_DEV("<< INT: %d\n", reason);
+
+       dev->preempt_ctx = MFC_NO_INSTANCE_SET;
+
+       if (dbg_enable && (reason != S5P_FIMV_R2H_CMD_QUEUE_DONE_RET))
+               s5p_mfc_dbg_disable(dev);
+
+#ifdef NAL_Q_ENABLE
+       if (dev->nal_q_handle) {
+               ret = mfc_nal_q_irq(dev, reason, err);
+               if (ret == 0) {
+                       mfc_debug(2, "NAL_Q command was handled\n");
+                       goto irq_end;
+               } else if (ret == 1){
+                       /* Path through */
+                       mfc_debug(2, "NAL_START command will be handled\n");
+               } else {
+                       mfc_debug(2, "Error.\n");
+                       goto irq_end;
+               }
+       }
+#endif
+
+       ret = mfc_irq_dev(dev, reason, err);
+       if (!ret)
+               goto irq_end;
+
+       ctx = dev->ctx[dev->curr_ctx];
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               s5p_mfc_clear_int_sfr();
+               s5p_mfc_pm_clock_off(dev);
+               goto irq_end;
+       }
+
+       ret = mfc_irq_ctx(ctx, reason, err);
+       if (!ret)
+               goto irq_end;
+
+       /* clean-up interrupt */
+       s5p_mfc_clear_int_sfr();
+
+       if ((ctx->state != MFCINST_RES_CHANGE_INIT) && (s5p_mfc_ctx_ready(ctx) == 0))
+               s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+
+       if (ctx->otf_handle) {
+               if (s5p_mfc_otf_ctx_ready(ctx))
+                       s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+               else
+                       s5p_mfc_clear_bit(ctx->num, &dev->work_bits);
+       }
+
+       s5p_mfc_hwlock_handler_irq(dev, ctx, reason, err);
+
+irq_end:
+       mfc_debug_leave();
+       return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_irq.h b/drivers/media/platform/exynos/mfc/s5p_mfc_irq.h
new file mode 100644 (file)
index 0000000..1aa1799
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_irq.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 __S5P_MFC_IRQ_H
+#define __S5P_MFC_IRQ_H __FILE__
+
+#include <linux/interrupt.h>
+
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_reg.h"
+
+irqreturn_t s5p_mfc_top_half_irq(int irq, void *priv);
+irqreturn_t s5p_mfc_irq(int irq, void *priv);
+
+static inline int s5p_mfc_dec_status_decoding(unsigned int dst_frame_status)
+{
+       if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
+           dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+               return 1;
+       return 0;
+}
+
+static inline int s5p_mfc_dec_status_display(unsigned int dst_frame_status)
+{
+       if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+           dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
+               return 1;
+
+       return 0;
+}
+
+static inline void s5p_mfc_handle_force_change_status(struct s5p_mfc_ctx *ctx)
+{
+       if (ctx->state != MFCINST_ABORT && ctx->state != MFCINST_HEAD_PARSED &&
+                       ctx->state != MFCINST_RES_CHANGE_FLUSH)
+               s5p_mfc_change_state(ctx, MFCINST_RUNNING);
+}
+#endif /* __S5P_MFC_IRQ_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_macros.h b/drivers/media/platform/exynos/mfc/s5p_mfc_macros.h
new file mode 100644 (file)
index 0000000..7ab2085
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_macros.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 __S5P_MFC_MACROS_H
+#define __S5P_MFC_MACROS_H __FILE__
+
+#define WIDTH_MB(x_size)       ((x_size + 15) / 16)
+#define HEIGHT_MB(y_size)      ((y_size + 15) / 16)
+
+/*
+   Note that lcu_width and lcu_height are defined as follows :
+   lcu_width = (frame_width + lcu_size - 1)/lcu_size
+   lcu_height = (frame_height + lcu_size - 1)/lcu_size.
+   (lcu_size is 32(encoder) or 64(decoder))
+*/
+#define DEC_LCU_WIDTH(x_size)  ((x_size + 63) / 64)
+#define ENC_LCU_WIDTH(x_size)  ((x_size + 31) / 32)
+#define DEC_LCU_HEIGHT(y_size) ((y_size + 63) / 64)
+#define ENC_LCU_HEIGHT(y_size) ((y_size + 31) / 32)
+
+#define STREAM_BUF_ALIGN               512
+#define MFC_LINEAR_BUF_SIZE            256
+#define set_strm_size_max(cpb_max)     ((cpb_max) - STREAM_BUF_ALIGN)
+
+#define DEC_STATIC_BUFFER_SIZE 20480
+
+#define DEC_MV_SIZE_MB(x, y)   (WIDTH_MB(x) * (((HEIGHT_MB(y)+1)/2)*2) * 64 + 1024)
+#define DEC_HEVC_MV_SIZE(x, y) (DEC_LCU_WIDTH(x) * DEC_LCU_HEIGHT(y) * 256 + 512)
+
+#define ENC_HEVC_LUMA_DPB_10B_SIZE(x, y)                               \
+       ((x + 63) / 64) * 64 * ((y + 31) / 32 ) * 32 +                  \
+       (((((ENC_LCU_WIDTH(x) * 32 + 3) / 4) + 15) / 16) * 16) *        \
+       ((y + 31) / 32 ) * 32 + 64
+#define ENC_HEVC_CHROMA_DPB_10B_SIZE(x, y)                             \
+       ((x + 63) / 64) * 64 * ((y + 31) / 32 ) * 32 +                  \
+       (((((ENC_LCU_WIDTH(x) * 32 + 3) / 4) + 15) / 16) * 16) *        \
+       ((y + 31) / 32 ) * 32 + 64
+#define ENC_VP9_LUMA_DPB_10B_SIZE(x, y)                                        \
+       (((x * 2 + 128) / 128) * 128) *                                 \
+       ((y + 31) / 32 ) * 32 + 64
+#define ENC_VP9_CHROMA_DPB_10B_SIZE(x, y)                              \
+       (((x * 2 + 128) / 128) * 128) *                                 \
+       ((((y + 31) / 32 ) * 32) / 2) + 64
+#define ENC_LUMA_DPB_SIZE(x, y)                                                \
+       ((x + 63) / 64) * 64 * ((y + 31) / 32 ) * 32 + 64
+#define ENC_CHROMA_DPB_SIZE(x, y)                                      \
+       ((x + 63) / 64) * 64 * ((((y + 31) / 32 ) * 32) / 2) + 64
+
+#define ENC_V100_H264_ME_SIZE(x, y)                            \
+       (((x + 3) * (y + 3) * 8) + ((((x * y) + 63) / 64) * 32) + (((y * 64) + 2304) * (x + 7) / 8))
+#define ENC_V100_MPEG4_ME_SIZE(x, y)                           \
+       (((x + 3) * (y + 3) * 8) + ((((x * y) + 127) / 128) * 16) + (((y * 64) + 2304) * (x + 7) / 8))
+#define ENC_V100_VP8_ME_SIZE(x, y)                             \
+       (((x + 3) * (y + 3) * 8) + (((y * 64) + 2304) * (x + 7) / 8))
+#define ENC_V100_VP9_ME_SIZE(x, y)                             \
+       ((((x * 2) + 3) * ((y * 2) + 3) * 128) + (((y * 256) + 2304) * (x + 1) / 2))
+#define ENC_V100_HEVC_ME_SIZE(x, y)                            \
+       (((x + 3) * (y + 3) * 32) + (((y * 128) + 2304) * (x + 3) / 4))
+
+#endif /* __S5P_MFC_MACROS_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_mem.c b/drivers/media/platform/exynos/mfc/s5p_mfc_mem.c
new file mode 100644 (file)
index 0000000..ace5b48
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_mem.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 "s5p_mfc_mem.h"
+
+struct vb2_mem_ops *s5p_mfc_mem_ops(void)
+{
+       return (struct vb2_mem_ops *)&vb2_ion_memops;
+}
+
+void s5p_mfc_mem_clean(struct s5p_mfc_dev *dev,
+                       struct s5p_mfc_special_buf *special_buf,
+                       off_t offset, size_t size)
+{
+       exynos_ion_sync_vaddr_for_device(dev->device, special_buf->dma_buf,
+                       special_buf->vaddr, size, offset, DMA_TO_DEVICE);
+       return;
+}
+
+void s5p_mfc_mem_invalidate(struct s5p_mfc_dev *dev,
+                       struct s5p_mfc_special_buf *special_buf,
+                       off_t offset, size_t size)
+{
+       exynos_ion_sync_vaddr_for_device(dev->device, special_buf->dma_buf,
+                       special_buf->vaddr, size, offset, DMA_FROM_DEVICE);
+       return;
+}
+
+int s5p_mfc_mem_clean_vb(struct vb2_buffer *vb, u32 num_planes)
+{
+       struct vb2_ion_cookie *cookie;
+       int i;
+       size_t size;
+
+       for (i = 0; i < num_planes; i++) {
+               cookie = vb2_plane_cookie(vb, i);
+               if (!cookie)
+                       continue;
+
+               size = vb->planes[i].length;
+               vb2_ion_sync_for_device(cookie, 0, size, DMA_TO_DEVICE);
+       }
+
+       return 0;
+}
+
+int s5p_mfc_mem_inv_vb(struct vb2_buffer *vb, u32 num_planes)
+{
+       struct vb2_ion_cookie *cookie;
+       int i;
+       size_t size;
+
+       for (i = 0; i < num_planes; i++) {
+               cookie = vb2_plane_cookie(vb, i);
+               if (!cookie)
+                       continue;
+
+               size = vb->planes[i].length;
+               vb2_ion_sync_for_device(cookie, 0, size, DMA_FROM_DEVICE);
+       }
+
+       return 0;
+}
+
+int s5p_mfc_mem_get_user_shared_handle(struct s5p_mfc_ctx *ctx,
+       struct mfc_user_shared_handle *handle)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int ret = 0;
+
+       handle->ion_handle =
+               ion_import_dma_buf_fd(dev->mfc_ion_client, handle->fd);
+       if (IS_ERR(handle->ion_handle)) {
+               mfc_err_ctx("Failed to import fd\n");
+               ret = PTR_ERR(handle->ion_handle);
+               goto import_dma_fail;
+       }
+
+       handle->vaddr =
+               ion_map_kernel(dev->mfc_ion_client, handle->ion_handle);
+       if (handle->vaddr == NULL) {
+               mfc_err_ctx("Failed to get kernel virtual address\n");
+               ret = -EINVAL;
+               goto map_kernel_fail;
+       }
+
+       mfc_debug(2, "User Handle: fd = %d, virtual addr = 0x%p\n",
+                               handle->fd, handle->vaddr);
+
+       return 0;
+
+map_kernel_fail:
+       ion_free(dev->mfc_ion_client, handle->ion_handle);
+
+import_dma_fail:
+       return ret;
+}
+
+int s5p_mfc_mem_cleanup_user_shared_handle(struct s5p_mfc_ctx *ctx,
+               struct mfc_user_shared_handle *handle)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       if (handle->fd == -1)
+               return 0;
+
+       if (handle->vaddr)
+               ion_unmap_kernel(dev->mfc_ion_client,
+                                       handle->ion_handle);
+
+       ion_free(dev->mfc_ion_client, handle->ion_handle);
+
+       return 0;
+}
+
+void *s5p_mfc_mem_get_vaddr(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf)
+{
+       return ion_map_kernel(dev->mfc_ion_client, special_buf->handle);
+}
+
+void s5p_mfc_mem_ion_free(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf)
+{
+       if (!special_buf->handle)
+               return;
+
+       if (special_buf->daddr)
+               ion_iovmm_unmap(special_buf->attachment, special_buf->daddr);
+
+       if (special_buf->handle) {
+               dma_buf_unmap_attachment(special_buf->attachment,
+                               special_buf->sgt, DMA_BIDIRECTIONAL);
+               dma_buf_detach(special_buf->dma_buf, special_buf->attachment);
+               dma_buf_put(special_buf->dma_buf);
+               ion_free(dev->mfc_ion_client, special_buf->handle);
+       }
+
+       memset(&special_buf->handle, 0, sizeof(struct ion_handle *));
+       memset(&special_buf->daddr, 0, sizeof(special_buf->daddr));
+
+       special_buf->handle = NULL;
+       special_buf->dma_buf = NULL;
+       special_buf->attachment = NULL;
+       special_buf->sgt = NULL;
+       special_buf->daddr = 0;
+       special_buf->vaddr = NULL;
+}
+
+int s5p_mfc_mem_ion_alloc(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf)
+{
+       struct s5p_mfc_ctx *ctx = dev->ctx[dev->curr_ctx];
+       int ion_mask, flag;
+
+       switch (special_buf->buftype) {
+       case MFCBUF_NORMAL:
+               ion_mask = EXYNOS_ION_HEAP_SYSTEM_MASK;
+               flag = 0;
+               break;
+       case MFCBUF_NORMAL_FW:
+               ion_mask = EXYNOS_ION_HEAP_VIDEO_NFW_MASK;
+               flag = 0;
+               break;
+       case MFCBUF_DRM:
+               ion_mask = EXYNOS_ION_HEAP_VIDEO_FRAME_MASK;
+               flag = ION_FLAG_PROTECTED;
+               break;
+       case MFCBUF_DRM_FW:
+               ion_mask = EXYNOS_ION_HEAP_VIDEO_FW_MASK;
+               flag = ION_FLAG_PROTECTED;
+               break;
+       default:
+               mfc_err_ctx("not supported mfc mem type: %d\n",
+                               special_buf->buftype);
+               return -EINVAL;
+       }
+       special_buf->handle = ion_alloc(dev->mfc_ion_client,
+                       special_buf->size, 0, ion_mask, flag);
+       if (IS_ERR(special_buf->handle)) {
+               mfc_err_ctx("Failed to allocate buffer (err %ld)",
+                               PTR_ERR(special_buf->handle));
+               special_buf->handle = NULL;
+               goto err_ion_alloc;
+       }
+
+       special_buf->dma_buf = ion_share_dma_buf(dev->mfc_ion_client,
+                       special_buf->handle);
+       if (IS_ERR(special_buf->dma_buf)) {
+               mfc_err_ctx("Failed to get dma_buf (err %ld)",
+                               PTR_ERR(special_buf->dma_buf));
+               special_buf->dma_buf = NULL;
+               goto err_ion_alloc;
+       }
+
+       special_buf->attachment = dma_buf_attach(special_buf->dma_buf, dev->device);
+       if (IS_ERR(special_buf->attachment)) {
+               mfc_err_ctx("Failed to get dma_buf_attach (err %ld)",
+                               PTR_ERR(special_buf->attachment));
+               special_buf->attachment = NULL;
+               goto err_ion_alloc;
+       }
+
+       special_buf->sgt = dma_buf_map_attachment(special_buf->attachment,
+                       DMA_BIDIRECTIONAL);
+       if (IS_ERR(special_buf->sgt)) {
+               mfc_err_ctx("Failed to get sgt (err %ld)",
+                               PTR_ERR(special_buf->sgt));
+               special_buf->sgt = NULL;
+               goto err_ion_alloc;
+       }
+
+       special_buf->daddr = ion_iovmm_map(special_buf->attachment, 0,
+                       special_buf->size, DMA_BIDIRECTIONAL, 0);
+       if (IS_ERR_VALUE(special_buf->daddr)) {
+               mfc_err_ctx("Failed to allocate iova (err %pa)",
+                               &special_buf->daddr);
+               special_buf->daddr = 0;
+               goto err_ion_alloc;
+       }
+
+       return 0;
+
+err_ion_alloc:
+       s5p_mfc_mem_ion_free(dev, special_buf);
+       return -ENOMEM;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_mem.h b/drivers/media/platform/exynos/mfc/s5p_mfc_mem.h
new file mode 100644 (file)
index 0000000..18f0020
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_mem.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 __S5P_MFC_MEM_H
+#define __S5P_MFC_MEM_H __FILE__
+
+#include <media/videobuf2-ion.h>
+
+#include "s5p_mfc_common.h"
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+*  while mmaping */
+#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
+
+static inline dma_addr_t s5p_mfc_mem_get_daddr_vb(
+       struct vb2_buffer *v, unsigned int n)
+{
+       void *cookie = vb2_plane_cookie(v, n);
+       dma_addr_t addr = 0;
+
+       WARN_ON(vb2_ion_dma_address(cookie, &addr) != 0);
+
+       return addr;
+}
+
+static inline int s5p_mfc_mem_buf_prepare(struct vb2_buffer *vb)
+{
+       return vb2_ion_buf_prepare(vb);
+}
+
+static inline void s5p_mfc_mem_buf_finish(struct vb2_buffer *vb)
+{
+       vb2_ion_buf_finish(vb);
+}
+
+struct vb2_mem_ops *s5p_mfc_mem_ops(void);
+
+void s5p_mfc_mem_set_cacheable(bool cacheable);
+void s5p_mfc_mem_clean(struct s5p_mfc_dev *dev,
+                       struct s5p_mfc_special_buf *specail_buf,
+                       off_t offset, size_t size);
+void s5p_mfc_mem_invalidate(struct s5p_mfc_dev *dev,
+                       struct s5p_mfc_special_buf *specail_buf,
+                       off_t offset, size_t size);
+int s5p_mfc_mem_clean_vb(struct vb2_buffer *vb, u32 num_planes);
+int s5p_mfc_mem_inv_vb(struct vb2_buffer *vb, u32 num_planes);
+
+int s5p_mfc_mem_get_user_shared_handle(struct s5p_mfc_ctx *ctx,
+               struct mfc_user_shared_handle *handle);
+int s5p_mfc_mem_cleanup_user_shared_handle(struct s5p_mfc_ctx *ctx,
+               struct mfc_user_shared_handle *handle);
+
+void *s5p_mfc_mem_get_vaddr(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf);
+void s5p_mfc_mem_ion_free(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf);
+int s5p_mfc_mem_ion_alloc(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_special_buf *special_buf);
+
+#endif /* __S5P_MFC_MEM_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.c b/drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.c
new file mode 100644 (file)
index 0000000..0b38b6d
--- /dev/null
@@ -0,0 +1,1796 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.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 "s5p_mfc_nal_q.h"
+
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+#include "s5p_mfc_irq.h"
+
+#ifdef NAL_Q_ENABLE
+#define CBR_I_LIMIT_MAX                        5
+int s5p_mfc_nal_q_check_enable(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_ctx *temp_ctx;
+       struct s5p_mfc_dec *dec = NULL;
+       struct s5p_mfc_enc *enc = NULL;
+       struct s5p_mfc_enc_params *p = NULL;
+       int i;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (nal_q_disable)
+               return 0;
+
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               temp_ctx = dev->ctx[i];
+               if (temp_ctx) {
+                       /* NAL-Q doesn't support drm */
+                       if (temp_ctx->is_drm) {
+                               mfc_debug(2, "There is a drm ctx. Can't start NAL-Q\n");
+                               return 0;
+                       }
+                       /* NAL-Q can be enabled when all ctx are in running state */
+                       if (temp_ctx->state != MFCINST_RUNNING &&
+                                       temp_ctx->state != MFCINST_RUNNING_NO_OUTPUT) {
+                               mfc_debug(2, "There is a ctx which is not in running state. "
+                                               "index: %d, state: %d\n", i, temp_ctx->state);
+                               return 0;
+                       }
+                       /* NAL-Q can't use the command about last frame */
+                       if (s5p_mfc_is_last_frame(temp_ctx) == 1) {
+                               mfc_debug(2, "There is a last frame. index: %d\n", i);
+                               return 0;
+                       }
+                       /* NAL-Q doesn't support OTF mode */
+                       if (temp_ctx->otf_handle) {
+                               mfc_debug(2, "There is a OTF node.\n");
+                               return 0;
+                       }
+                       /* NAL-Q doesn't support BPG */
+                       if (IS_BPG_DEC(temp_ctx) || IS_BPG_ENC(temp_ctx)) {
+                               mfc_debug(2, "BPG codec type\n");
+                               return 0;
+                       }
+                       /* NAL-Q doesn't support multi-frame, interlaced, black bar */
+                       if (temp_ctx->type == MFCINST_DECODER) {
+                               dec = temp_ctx->dec_priv;
+                               if (!dec) {
+                                       mfc_debug(2, "There is no dec\n");
+                                       return 0;
+                               }
+                               if ((dec->has_multiframe && CODEC_MULTIFRAME(temp_ctx)) || dec->consumed) {
+                                       mfc_debug(2, "There is a multi frame or consumed header.\n");
+                                       return 0;
+                               }
+                               if (dec->is_dpb_full) {
+                                       mfc_debug(2, "All buffers are referenced\n");
+                                       return 0;
+                               }
+                               if (dec->is_interlaced) {
+                                       mfc_debug(2, "There is a interlaced stream\n");
+                                       return 0;
+                               }
+                               if (dec->detect_black_bar) {
+                                       mfc_debug(2, "black bar detection is enabled\n");
+                                       return 0;
+                               }
+                       /* NAL-Q doesn't support fixed byte(slice mode), CBR_VT(rc mode) */
+                       } else if (temp_ctx->type == MFCINST_ENCODER) {
+                               enc = temp_ctx->enc_priv;
+                               if (!enc) {
+                                       mfc_debug(2, "There is no enc\n");
+                                       return 0;
+                               }
+                               if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES) {
+                                       mfc_debug(2, "There is fixed bytes option(slice mode)\n");
+                                       return 0;
+                               }
+                               p = &enc->params;
+                               if (p->rc_reaction_coeff <= CBR_I_LIMIT_MAX) {
+                                       mfc_debug(2, "There is CBR_VT option(rc mode)\n");
+                                       return 0;
+                               }
+                       }
+                       mfc_debug(2, "There is a ctx in running state. index: %d\n", i);
+               }
+       }
+
+       mfc_debug(2, "All working ctx are in running state!\n");
+
+       mfc_debug_leave();
+
+       return 1;
+}
+
+static int mfc_nal_q_find_ctx(struct s5p_mfc_dev *dev, EncoderOutputStr *pOutputStr)
+{
+       int i;
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       for(i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               if (dev->ctx[i] && dev->ctx[i]->inst_no == pOutputStr->InstanceId)
+                       return i;
+       }
+       return -1;
+}
+
+static nal_queue_in_handle* mfc_nal_q_create_in_q(struct s5p_mfc_dev *dev,
+               nal_queue_handle *nal_q_handle)
+{
+       nal_queue_in_handle *nal_q_in_handle;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return NULL;
+       }
+
+       nal_q_in_handle = kzalloc(sizeof(*nal_q_in_handle), GFP_KERNEL);
+       if (!nal_q_in_handle) {
+               mfc_err_dev("NAL Q: Failed to get memory for nal_queue_in_handle\n");
+               return NULL;
+       }
+
+       nal_q_in_handle->nal_q_handle = nal_q_handle;
+       nal_q_in_handle->in_buf.buftype = MFCBUF_NORMAL;
+       nal_q_in_handle->in_buf.size = NAL_Q_IN_ENTRY_SIZE * (NAL_Q_IN_QUEUE_SIZE + 2);
+       if (s5p_mfc_mem_ion_alloc(dev, &nal_q_in_handle->in_buf)) {
+               mfc_err_dev("NAL Q: failed to get memory\n");
+               kfree(nal_q_in_handle);
+               return NULL;
+       }
+
+       nal_q_in_handle->nal_q_in_addr
+               = (nal_in_queue *)s5p_mfc_mem_get_vaddr(dev, &nal_q_in_handle->in_buf);
+       if (!nal_q_in_handle->nal_q_in_addr) {
+               mfc_err_dev("NAL Q: failed to get vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &nal_q_in_handle->in_buf);
+               kfree(nal_q_in_handle);
+               return NULL;
+       }
+
+       mfc_debug_leave();
+
+       return nal_q_in_handle;
+}
+
+static nal_queue_out_handle* mfc_nal_q_create_out_q(struct s5p_mfc_dev *dev,
+               nal_queue_handle *nal_q_handle)
+{
+       nal_queue_out_handle *nal_q_out_handle;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return NULL;
+       }
+
+       nal_q_out_handle = kzalloc(sizeof(*nal_q_out_handle), GFP_KERNEL);
+       if (!nal_q_out_handle) {
+               mfc_err_dev("NAL Q: failed to get memory for nal_queue_out_handle\n");
+               return NULL;
+       }
+
+       nal_q_out_handle->nal_q_handle = nal_q_handle;
+       nal_q_out_handle->out_buf.buftype = MFCBUF_NORMAL;
+       nal_q_out_handle->out_buf.size = NAL_Q_OUT_ENTRY_SIZE * (NAL_Q_OUT_QUEUE_SIZE + 2);
+       if (s5p_mfc_mem_ion_alloc(dev, &nal_q_out_handle->out_buf)) {
+               mfc_err_dev("NAL Q: failed to get memory\n");
+               kfree(nal_q_out_handle);
+               return NULL;
+       }
+
+       nal_q_out_handle->nal_q_out_addr
+               = (nal_out_queue *)s5p_mfc_mem_get_vaddr(dev, &nal_q_out_handle->out_buf);
+       if (!nal_q_out_handle->nal_q_out_addr) {
+               mfc_err_dev("NAL Q : failed to get vaddr\n");
+               s5p_mfc_mem_ion_free(dev, &nal_q_out_handle->out_buf);
+               kfree(nal_q_out_handle);
+               return NULL;
+       }
+
+       mfc_debug_leave();
+
+       return nal_q_out_handle;
+}
+
+static int mfc_nal_q_destroy_in_q(struct s5p_mfc_dev *dev,
+                       nal_queue_in_handle *nal_q_in_handle)
+{
+       mfc_debug_enter();
+
+       if (!nal_q_in_handle)
+               return -EINVAL;
+
+       s5p_mfc_mem_ion_free(dev, &nal_q_in_handle->in_buf);
+       if (nal_q_in_handle)
+               kfree(nal_q_in_handle);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int mfc_nal_q_destroy_out_q(struct s5p_mfc_dev *dev,
+                       nal_queue_out_handle *nal_q_out_handle)
+{
+       mfc_debug_enter();
+
+       if (!nal_q_out_handle)
+               return -EINVAL;
+
+       s5p_mfc_mem_ion_free(dev, &nal_q_out_handle->out_buf);
+       if (nal_q_out_handle)
+               kfree(nal_q_out_handle);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/*
+  * This function should be called after s5p_mfc_alloc_firmware() being called.
+  */
+nal_queue_handle *s5p_mfc_nal_q_create(struct s5p_mfc_dev *dev)
+{
+       nal_queue_handle *nal_q_handle;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return NULL;
+       }
+
+       nal_q_handle = kzalloc(sizeof(*nal_q_handle), GFP_KERNEL);
+       if (!nal_q_handle) {
+               mfc_err_dev("NAL Q: no nal_q_handle\n");
+               return NULL;
+       }
+
+       nal_q_handle->nal_q_in_handle = mfc_nal_q_create_in_q(dev, nal_q_handle);
+       if (!nal_q_handle->nal_q_in_handle) {
+               kfree(nal_q_handle);
+               mfc_err_dev("NAL Q: no nal_q_in_handle\n");
+               return NULL;
+       }
+
+       spin_lock_init(&nal_q_handle->nal_q_in_handle->lock);
+
+       nal_q_handle->nal_q_out_handle = mfc_nal_q_create_out_q(dev, nal_q_handle);
+       if (!nal_q_handle->nal_q_out_handle) {
+               mfc_nal_q_destroy_in_q(dev, nal_q_handle->nal_q_in_handle);
+               kfree(nal_q_handle);
+               mfc_err_dev("NAL Q: no nal_q_out_handle\n");
+               return NULL;
+       }
+
+       nal_q_handle->nal_q_state = NAL_Q_STATE_CREATED;
+       MFC_TRACE_DEV("** NAL Q state : %d\n", nal_q_handle->nal_q_state);
+       mfc_debug(2, "NAL Q: handle created, state = %d\n", nal_q_handle->nal_q_state);
+
+       mfc_debug_leave();
+
+       return nal_q_handle;
+}
+
+int s5p_mfc_nal_q_destroy(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle)
+{
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (!nal_q_handle) {
+               mfc_err_dev("there isn't nal_q_handle\n");
+               return -EINVAL;
+       }
+
+       ret = mfc_nal_q_destroy_out_q(dev, nal_q_handle->nal_q_out_handle);
+       if (ret) {
+               mfc_err_dev("failed nal_q_out_handle destroy\n");
+               return ret;
+       }
+
+       ret = mfc_nal_q_destroy_in_q(dev, nal_q_handle->nal_q_in_handle);
+       if (ret) {
+               mfc_err_dev("failed nal_q_in_handle destroy\n");
+               return ret;
+       }
+
+       kfree(nal_q_handle);
+       dev->nal_q_handle = NULL;
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+void s5p_mfc_nal_q_init(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle)
+{
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       if (!nal_q_handle) {
+               mfc_err_dev("NAL Q: There is no nal_q_handle\n");
+               return;
+       }
+
+       if ((nal_q_handle->nal_q_state != NAL_Q_STATE_CREATED)
+               && (nal_q_handle->nal_q_state != NAL_Q_STATE_STOPPED)) {
+               mfc_err_dev("NAL Q: State is wrong, state: %d\n", nal_q_handle->nal_q_state);
+               return;
+       }
+
+       s5p_mfc_reset_nal_queue_registers(dev);
+
+       nal_q_handle->nal_q_in_handle->in_exe_count = 0;
+       nal_q_handle->nal_q_out_handle->out_exe_count = 0;
+
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_INPUT_COUNT=%d\n",
+               s5p_mfc_get_nal_q_input_count());
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_OUTPUT_COUNT=%d\n",
+               s5p_mfc_get_nal_q_output_count());
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_INPUT_EXE_COUNT=%d\n",
+               s5p_mfc_get_nal_q_input_exe_count());
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_INFO=%d\n",
+               s5p_mfc_get_nal_q_info());
+
+       nal_q_handle->nal_q_exception = 0;
+       nal_q_handle->nal_q_state = NAL_Q_STATE_INITIALIZED;
+       MFC_TRACE_DEV("** NAL Q state : %d\n", nal_q_handle->nal_q_state);
+       mfc_debug(2, "NAL Q: initialized, state = %d\n", nal_q_handle->nal_q_state);
+
+       mfc_debug_leave();
+
+       return;
+}
+
+void s5p_mfc_nal_q_start(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle)
+{
+       dma_addr_t addr;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       if (!nal_q_handle) {
+               mfc_err_dev("NAL Q: There is no nal_q_handle\n");
+               return;
+       }
+
+       if (nal_q_handle->nal_q_state != NAL_Q_STATE_INITIALIZED) {
+               mfc_err_dev("NAL Q: State is wrong, state: %d\n", nal_q_handle->nal_q_state);
+               return;
+       }
+
+       addr = nal_q_handle->nal_q_in_handle->in_buf.daddr;
+
+       s5p_mfc_update_nal_queue_input(dev, addr, NAL_Q_IN_ENTRY_SIZE * NAL_Q_IN_QUEUE_SIZE);
+
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_INPUT_ADDR=0x%x\n",
+               s5p_mfc_get_nal_q_input_addr());
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_INPUT_SIZE=%d\n",
+               s5p_mfc_get_nal_q_input_size());
+
+       addr = nal_q_handle->nal_q_out_handle->out_buf.daddr;
+
+       s5p_mfc_update_nal_queue_output(dev, addr, NAL_Q_OUT_ENTRY_SIZE * NAL_Q_OUT_QUEUE_SIZE);
+
+       mfc_debug(2, "NAL Q: S5P_FIMV_NAL_QUEUE_OUTPUT_ADDR=0x%x\n",
+               s5p_mfc_get_nal_q_output_addr());
+       mfc_debug(2, "S5P_FIMV_NAL_QUEUE_OUTPUT_SIZE=%d\n",
+               s5p_mfc_get_nal_q_output_ize());
+
+       nal_q_handle->nal_q_state = NAL_Q_STATE_STARTED;
+       MFC_TRACE_DEV("** NAL Q state : %d\n", nal_q_handle->nal_q_state);
+       mfc_debug(2, "NAL Q: started, state = %d\n", nal_q_handle->nal_q_state);
+
+       MFC_WRITEL(MFC_TIMEOUT_VALUE, S5P_FIMV_DEC_TIMEOUT_VALUE);
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_NAL_QUEUE);
+
+       mfc_debug_leave();
+
+       return;
+}
+
+void s5p_mfc_nal_q_stop(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle)
+{
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       if (!nal_q_handle) {
+               mfc_err_dev("NAL Q: There is no nal_q_handle\n");
+               return;
+       }
+
+       if (nal_q_handle->nal_q_state != NAL_Q_STATE_STARTED) {
+               mfc_err_dev("NAL Q: State is wrong, state: %d\n", nal_q_handle->nal_q_state);
+               return;
+       }
+
+       nal_q_handle->nal_q_state = NAL_Q_STATE_STOPPED;
+       MFC_TRACE_DEV("** NAL Q state : %d\n", nal_q_handle->nal_q_state);
+       mfc_debug(2, "NAL Q: stopped, state = %d\n", nal_q_handle->nal_q_state);
+
+       s5p_mfc_clean_dev_int_flags(dev);
+
+       s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_STOP_QUEUE);
+
+       mfc_debug_leave();
+
+       return;
+}
+
+void s5p_mfc_nal_q_stop_if_started(struct s5p_mfc_dev *dev)
+{
+       nal_queue_handle *nal_q_handle;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       nal_q_handle = dev->nal_q_handle;
+       if (!nal_q_handle) {
+               mfc_err_dev("NAL Q: There is no nal_q_handle\n");
+               return;
+       }
+
+       if (nal_q_handle->nal_q_state != NAL_Q_STATE_STARTED) {
+               mfc_debug(2, "NAL Q: it is not running, state: %d\n",
+                               nal_q_handle->nal_q_state);
+               return;
+       }
+
+       s5p_mfc_nal_q_stop(dev, nal_q_handle);
+       mfc_info_dev("NAL Q: stop NAL QUEUE during get hwlock\n");
+       if (s5p_mfc_wait_for_done_dev(dev,
+                               S5P_FIMV_R2H_CMD_COMPLETE_QUEUE_RET)) {
+               mfc_err_dev("NAL Q: Failed to stop qeueue during get hwlock\n");
+               dev->logging_data->cause |= (1 << MFC_CAUSE_FAIL_STOP_NAL_Q_FOR_OTHER);
+               s5p_mfc_dump_info_and_stop_hw(dev);
+       }
+
+       mfc_debug_leave();
+       return;
+}
+
+void s5p_mfc_nal_q_cleanup_queue(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_ctx *ctx;
+       int i;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       for(i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               ctx = dev->ctx[i];
+               if (ctx) {
+                       s5p_mfc_cleanup_nal_queue(ctx);
+                       if (s5p_mfc_ctx_ready(ctx)) {
+                               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+                               mfc_debug(2, "NAL Q: set work_bits after cleanup,"
+                                               " ctx: %d\n", ctx->num);
+                       }
+               }
+       }
+
+       mfc_debug_leave();
+
+       return;
+}
+
+static void mfc_nal_q_set_slice_mode(struct s5p_mfc_ctx *ctx, EncoderInputStr *pInStr)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       /* multi-slice control */
+       if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES)
+               pInStr->MsliceMode = enc->slice_mode + 0x4;
+       else if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW)
+               pInStr->MsliceMode = enc->slice_mode - 0x2;
+       else if (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES)
+               pInStr->MsliceMode = enc->slice_mode + 0x3;
+       else
+               pInStr->MsliceMode = enc->slice_mode;
+
+       /* multi-slice MB number or bit size */
+       if ((enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) ||
+                       (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW)) {
+               pInStr->MsliceSizeMb = enc->slice_size.mb;
+       } else if ((enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) ||
+                       (enc->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES)){
+               pInStr->MsliceSizeBits = enc->slice_size.bits;
+       } else {
+               pInStr->MsliceSizeMb = 0;
+               pInStr->MsliceSizeBits = 0;
+       }
+}
+
+static int mfc_nal_q_run_in_buf_enc(struct s5p_mfc_ctx *ctx, EncoderInputStr *pInStr)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       struct s5p_mfc_raw_info *raw = NULL;
+       dma_addr_t src_addr[3] = {0, 0, 0};
+       dma_addr_t addr_2bit[2];
+       unsigned int index, i;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("NAL Q: no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       pInStr->StartCode = 0xBBBBBBBB;
+       pInStr->CommandId = s5p_mfc_get_nal_q_input_count();
+       pInStr->InstanceId = ctx->inst_no;
+
+       raw = &ctx->raw_buf;
+
+       /* move src_queue -> src_queue_nal_q */
+       src_mb = s5p_mfc_get_move_buf(&ctx->buf_queue_lock,
+               &ctx->src_buf_nal_queue, &ctx->src_buf_queue, MFC_BUF_SET_USED, MFC_QUEUE_ADD_BOTTOM);
+       if (!src_mb) {
+               mfc_err_dev("NAL Q: no src buffers\n");
+               return -EAGAIN;
+       }
+
+       for (i = 0; i < raw->num_planes; i++) {
+               src_addr[i] = s5p_mfc_mem_get_daddr_vb(&src_mb->vb.vb2_buf, i);
+               mfc_debug(2, "NAL Q: enc src[%d] addr: 0x%08llx\n",
+                               i, src_addr[i]);
+       }
+
+       for (i = 0; i < raw->num_planes; i++)
+               pInStr->FrameAddr[i] = src_addr[i];
+
+       if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M_S10B ||
+               ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M_S10B) {
+               addr_2bit[0] = src_addr[0] + NV12N_Y_SIZE(ctx->img_width, ctx->img_height);
+               addr_2bit[1] = src_addr[1] + NV12N_CBCR_SIZE(ctx->img_width, ctx->img_height);
+
+               for (i = 0; i < raw->num_planes; i++)
+                       pInStr->Frame2bitAddr[i] = addr_2bit[i];
+       } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV16M_S10B ||
+               ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV61M_S10B) {
+               addr_2bit[0] = src_addr[0] + NV16M_Y_SIZE(ctx->img_width, ctx->img_height);
+               addr_2bit[1] = src_addr[1] + NV16M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+
+               for (i = 0; i < raw->num_planes; i++)
+                       pInStr->Frame2bitAddr[i] = addr_2bit[i];
+       }
+
+       /* move dst_queue -> dst_queue_nal_q */
+       dst_mb = s5p_mfc_get_move_buf(&ctx->buf_queue_lock,
+               &ctx->dst_buf_nal_queue, &ctx->dst_buf_queue, MFC_BUF_SET_USED, MFC_QUEUE_ADD_BOTTOM);
+       if (!dst_mb) {
+               mfc_err_dev("NAL Q: no dst buffers\n");
+               return -EAGAIN;
+       }
+
+       pInStr->StreamBufferAddr = s5p_mfc_mem_get_daddr_vb(&dst_mb->vb.vb2_buf, 0);
+       pInStr->StreamBufferSize = (unsigned int)vb2_plane_size(&dst_mb->vb.vb2_buf, 0);
+       pInStr->StreamBufferSize = ALIGN(pInStr->StreamBufferSize, 512);
+
+       index = src_mb->vb.vb2_buf.index;
+       if (call_cop(ctx, set_buf_ctrls_val_nal_q_enc, ctx, &ctx->src_ctrls[index], pInStr) < 0)
+               mfc_err_ctx("NAL Q: failed in set_buf_ctrals_val in nal q\n");
+
+       mfc_debug(2, "NAL Q: input queue, src_queue -> src_queue_nal_q, index:%d\n",
+                       src_mb->vb.vb2_buf.index);
+       mfc_debug(2, "NAL Q: input queue, dst_buf_queue -> dst_buf_nal_queue, index:%d\n",
+                       dst_mb->vb.vb2_buf.index);
+
+       mfc_nal_q_set_slice_mode(ctx, pInStr);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int mfc_nal_q_run_in_buf_dec(struct s5p_mfc_ctx *ctx, DecoderInputStr *pInStr)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       dma_addr_t buf_addr;
+       unsigned int strm_size;
+       unsigned int cpb_buf_size;
+       int src_index, dst_index;
+       int i;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("NAL Q: no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("NAL Q: no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0) &&
+                       s5p_mfc_is_queue_count_smaller(&ctx->buf_queue_lock,
+                               &ctx->ref_buf_queue, (ctx->dpb_count + 5))) {
+               mfc_err_dev("NAL Q: no dst buffers\n");
+               return -EAGAIN;
+       }
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->src_buf_queue, 0)) {
+               mfc_err_dev("NAL Q: no src buffers\n");
+               return -EAGAIN;
+       }
+
+       pInStr->StartCode = 0xAAAAAAAA;
+       pInStr->CommandId = s5p_mfc_get_nal_q_input_count();
+       pInStr->InstanceId = ctx->inst_no;
+       pInStr->NalStartOptions = 0;
+
+       /* Try to use the non-referenced DPB on dst-queue */
+       dst_mb = s5p_mfc_search_move_dpb_nal_q(ctx, dec->dynamic_used);
+       if (!dst_mb) {
+               mfc_debug(2, "NAL Q: no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       /* move src_queue -> src_queue_nal_q */
+       src_mb = s5p_mfc_get_move_buf(&ctx->buf_queue_lock,
+                       &ctx->src_buf_nal_queue, &ctx->src_buf_queue,
+                       MFC_BUF_SET_USED, MFC_QUEUE_ADD_BOTTOM);
+       if (!src_mb) {
+               mfc_err_dev("NAL Q: no src buffers\n");
+               return -EAGAIN;
+       }
+
+       /* src buffer setting */
+       buf_addr = s5p_mfc_mem_get_daddr_vb(&src_mb->vb.vb2_buf, 0);
+       strm_size = src_mb->vb.vb2_buf.planes[0].bytesused;
+       cpb_buf_size = ALIGN(dec->src_buf_size, STREAM_BUF_ALIGN);
+       mfc_debug(2, "NAL Q: Src addr: 0x%08llx, size: %d\n", buf_addr, strm_size);
+
+       if (strm_size > set_strm_size_max(cpb_buf_size)) {
+               mfc_info_ctx("NAL Q: Decrease strm_size : %u -> %u, gap : %d\n",
+                               strm_size, set_strm_size_max(cpb_buf_size), STREAM_BUF_ALIGN);
+               strm_size = set_strm_size_max(cpb_buf_size);
+               src_mb->vb.vb2_buf.planes[0].bytesused = strm_size;
+       }
+
+       if (strm_size == 0)
+               mfc_info_ctx("stream size is 0\n");
+
+       pInStr->StreamDataSize = strm_size;
+       pInStr->CpbBufferAddr = buf_addr;
+       pInStr->CpbBufferSize = cpb_buf_size;
+       pInStr->CpbBufferOffset = 0;
+
+       MFC_TRACE_CTX("Set src[%d] fd: %d, %#llx\n",
+                       src_mb->vb.vb2_buf.index,
+                       src_mb->vb.vb2_buf.planes[0].m.fd,
+                       buf_addr);
+
+       /* dst buffer setting */
+       dst_index = dst_mb->vb.vb2_buf.index;
+       set_bit(dst_index, &dec->available_dpb);
+       dec->dynamic_set = 1 << dst_index;
+
+       for (i = 0; i < raw->num_planes; i++) {
+               pInStr->FrameSize[i] = raw->plane_size[i];
+               pInStr->FrameAddr[i] = dst_mb->planes.raw[i];
+       }
+       mfc_debug(2, "NAL Q: dst addr[0]: 0x%08llx\n",
+                       dst_mb->planes.raw[0]);
+
+       pInStr->ScratchBufAddr = ctx->codec_buf.daddr;
+       pInStr->ScratchBufSize = ctx->scratch_buf_size;
+
+       src_index = src_mb->vb.vb2_buf.index;
+       if (call_cop(ctx, set_buf_ctrls_val_nal_q_dec, ctx,
+                               &ctx->src_ctrls[src_index], pInStr) < 0)
+               mfc_err_ctx("NAL Q: failed in set_buf_ctrls_val\n");
+
+       pInStr->DynamicDpbFlagLower = dec->dynamic_set;
+
+       /* use dynamic_set value to available dpb in NAL Q */
+       // pInStr->AvailableDpbFlagLower = dec->available_dpb;
+       pInStr->AvailableDpbFlagLower = dec->dynamic_set;
+
+       MFC_TRACE_CTX("Set dst[%d] fd: %d, %#llx / avail %#lx used %#x\n",
+                       dst_index, dst_mb->vb.vb2_buf.planes[0].m.fd, dst_mb->planes.raw[0],
+                       dec->available_dpb, dec->dynamic_used);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void mfc_nal_q_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+               dma_addr_t addr[], int num_planes, EncoderOutputStr *pOutStr)
+{
+       unsigned long enc_recon_y_addr, enc_recon_c_addr;
+       int i;
+
+       for (i = 0; i < num_planes; i++)
+               addr[i] = pOutStr->EncodedFrameAddr[i];
+
+       enc_recon_y_addr = pOutStr->ReconLumaDpbAddr;
+       enc_recon_c_addr = pOutStr->ReconChromaDpbAddr;
+
+       mfc_debug(2, "NAL Q: recon y addr: 0x%08lx\n", enc_recon_y_addr);
+       mfc_debug(2, "NAL Q: recon c addr: 0x%08lx\n", enc_recon_c_addr);
+}
+
+static void mfc_nal_q_handle_stream(struct s5p_mfc_ctx *ctx, EncoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_buf *src_mb, *dst_mb, *ref_mb;
+       struct s5p_mfc_raw_info *raw;
+       dma_addr_t enc_addr[3] = { 0, 0, 0 };
+       int slice_type, i;
+       unsigned int strm_size;
+       unsigned int pic_count;
+       unsigned int index;
+
+       mfc_debug_enter();
+
+       slice_type = pOutStr->SliceType;
+       strm_size = pOutStr->StreamSize;
+       pic_count = pOutStr->EncCnt;
+
+       mfc_debug(2, "NAL Q: encoded slice type: %d\n", slice_type);
+       mfc_debug(2, "NAL Q: encoded stream size: %d\n", strm_size);
+       mfc_debug(2, "NAL Q: display order: %d\n", pic_count);
+/*
+       if (enc->buf_full) {
+               ctx->state = MFCINST_ABORT_INST;
+               return 0;
+       }
+*/
+       /* set encoded frame type */
+       enc->frame_type = slice_type;
+       raw = &ctx->raw_buf;
+
+       ctx->sequence++;
+       if (strm_size > 0) {
+               /* at least one more dest. buffers exist always  */
+               dst_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!dst_mb) {
+                       mfc_err_dev("NAL Q: no dst buffers\n");
+                       return;
+               }
+
+               dst_mb->vb.flags &=
+                       ~(V4L2_BUF_FLAG_KEYFRAME |
+                         V4L2_BUF_FLAG_PFRAME |
+                         V4L2_BUF_FLAG_BFRAME);
+
+               switch (slice_type) {
+               case S5P_FIMV_E_SLICE_TYPE_I:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_KEYFRAME;
+                               break;
+               case S5P_FIMV_E_SLICE_TYPE_P:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_PFRAME;
+                       break;
+               case S5P_FIMV_E_SLICE_TYPE_B:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_BFRAME;
+                       break;
+               default:
+                       dst_mb->vb.flags |=
+                               V4L2_BUF_FLAG_KEYFRAME;
+                       break;
+               }
+               mfc_debug(2, "NAL Q: Slice type : %d\n", dst_mb->vb.flags);
+
+               vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, strm_size);
+
+               index = dst_mb->vb.vb2_buf.index;
+               if (call_cop(ctx, get_buf_ctrls_val_nal_q_enc, ctx,
+                               &ctx->dst_ctrls[index], pOutStr) < 0)
+                       mfc_err_ctx("NAL Q: failed in get_buf_ctrls_val in nal q\n");
+
+               vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+       } else if (strm_size == 0) {
+               dst_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue, MFC_BUF_NO_TOUCH_USED);
+               if (!dst_mb)
+                       mfc_err_dev("NAL Q: no dst buffers\n");
+               else
+                       vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+       }
+
+       if (slice_type >= 0) {
+               if (ctx->state == MFCINST_RUNNING_NO_OUTPUT ||
+                       ctx->state == MFCINST_RUNNING_BUF_FULL)
+                       ctx->state = MFCINST_RUNNING;
+
+               mfc_nal_q_get_enc_frame_buffer(ctx, &enc_addr[0],
+                                       raw->num_planes, pOutStr);
+
+               for (i = 0; i < raw->num_planes; i++)
+                       mfc_debug(2, "NAL Q: encoded[%d] addr: 0x%08llx\n", i,
+                                       enc_addr[i]);
+
+               src_mb = s5p_mfc_find_del_buf_vb(&ctx->buf_queue_lock,
+                               &ctx->src_buf_nal_queue, enc_addr[0]);
+               if (!src_mb) {
+                       mfc_err_dev("NAL Q: no src buffers\n");
+                       return;
+               }
+
+               vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+               ref_mb = s5p_mfc_find_del_buf_vb(&ctx->buf_queue_lock,
+                               &ctx->ref_buf_queue, enc_addr[0]);
+               if (ref_mb)
+                       vb2_buffer_done(&ref_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+       } else if (s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue, 0)) {
+               src_mb = s5p_mfc_get_move_buf_used(&ctx->buf_queue_lock,
+                               &ctx->ref_buf_queue, &ctx->src_buf_nal_queue);
+               if (!src_mb) {
+                       mfc_err_dev("NAL Q: no src buffers\n");
+                       return;
+               }
+
+               if (src_mb->used) {
+                       mfc_debug(2, "NAL Q: no output, src_queue_nal_q -> ref_queue, index:%d\n",
+                                       src_mb->vb.vb2_buf.index);
+               }
+
+               /* slice_type = 4 && strm_size = 0, skipped enable
+                  should be considered */
+               if ((slice_type == -1) && (strm_size == 0)) {
+                       ctx->state = MFCINST_RUNNING_NO_OUTPUT;
+
+                       dst_mb = s5p_mfc_get_move_buf(&ctx->buf_queue_lock,
+                               &ctx->dst_buf_queue, &ctx->dst_buf_nal_queue, MFC_BUF_RESET_USED, MFC_QUEUE_ADD_TOP);
+                       if (!dst_mb) {
+                               mfc_err_dev("NAL Q: no dst buffers\n");
+                               return;
+                       }
+
+                       mfc_debug(2, "NAL Q: no output, dst_buf_nal_queue -> dst_buf_queue, index:%d\n",
+                                       dst_mb->vb.vb2_buf.index);
+               }
+
+               mfc_debug(2, "NAL Q: slice_type: %d, ctx->state: %d\n", slice_type, ctx->state);
+               mfc_debug(2, "NAL Q: enc src count: %d, enc ref count: %d\n",
+                         s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                         s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue));
+       }
+
+       mfc_debug_leave();
+
+       return;
+}
+
+static void mfc_nal_q_handle_reuse_buffer(struct s5p_mfc_ctx *ctx, DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       unsigned int prev_flag, released_flag = 0;
+       struct s5p_mfc_buf *dst_mb;
+       dma_addr_t disp_addr;
+       int i;
+
+       /* reuse not used buf: dst_buf_nal_queue -> dst_queue */
+       disp_addr = pOutStr->DisplayAddr[0];
+       if (disp_addr) {
+               mfc_debug(2, "NAL Q: decoding only but there is disp addr: 0x%llx\n", disp_addr);
+               dst_mb = s5p_mfc_get_move_buf_addr(&ctx->buf_queue_lock,
+                               &ctx->dst_buf_queue, &ctx->dst_buf_nal_queue,
+                               disp_addr);
+               if (dst_mb) {
+                       mfc_debug(2, "NAL Q: buf[%d] will reused. addr: 0x%08llx\n",
+                                       dst_mb->vb.vb2_buf.index, disp_addr);
+                       dst_mb->used = 0;
+                       clear_bit(dst_mb->vb.vb2_buf.index, &dec->available_dpb);
+               }
+       }
+
+       /* reuse not referenced buf anymore: ref_queue -> dst_queue */
+       prev_flag = dec->dynamic_used;
+       dec->dynamic_used = pOutStr->UsedDpbFlagLower;
+       released_flag = prev_flag & (~dec->dynamic_used);
+
+       if (!released_flag)
+               return;
+
+       for (i = 0; i < MFC_MAX_DPBS; i++)
+               if (released_flag & (1 << i))
+                       s5p_mfc_move_reuse_buffer(ctx, i);
+
+}
+
+static void mfc_nal_q_handle_ref_frame(struct s5p_mfc_ctx *ctx, DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *dst_mb;
+       dma_addr_t dec_addr, buf_addr, disp_addr;
+       int index = 0;
+
+       mfc_debug_enter();
+
+       dec_addr = pOutStr->DecodedAddr[0];
+       disp_addr = pOutStr->DisplayAddr[0];
+       mfc_debug(2, "NAL Q: dec addr: 0x%08llx, disp addr: 0x%08llx\n",
+                       dec_addr, disp_addr);
+
+       dst_mb = s5p_mfc_find_move_buf_vb_used(&ctx->buf_queue_lock,
+               &ctx->ref_buf_queue, &ctx->dst_buf_nal_queue, dec_addr);
+       if (dst_mb) {
+               buf_addr = s5p_mfc_mem_get_daddr_vb(&dst_mb->vb.vb2_buf, 0);
+               mfc_debug(2, "NAL Q: Found in dst queue, "
+                               "dec addr: 0x%08llx, buf addr: 0x%08llx, used: %d\n",
+                               dec_addr, buf_addr, dst_mb->used);
+
+               index = dst_mb->vb.vb2_buf.index;
+               if (!((1 << index) & pOutStr->UsedDpbFlagLower))
+                       dec->dynamic_used |= 1 << index;
+       } else {
+               mfc_debug(2, "NAL Q: Can't find buffer for addr: 0x%08llx\n", dec_addr);
+       }
+
+       mfc_debug_leave();
+}
+
+static void mfc_nal_q_handle_frame_copy_timestamp(struct s5p_mfc_ctx *ctx, DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *ref_mb, *src_mb;
+       dma_addr_t dec_y_addr;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       dev = ctx->dev;
+
+       dec_y_addr = pOutStr->DecodedAddr[0];
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue, MFC_BUF_NO_TOUCH_USED);
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+               return;
+       }
+
+       ref_mb = s5p_mfc_find_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, dec_y_addr);
+       if (ref_mb)
+               ref_mb->vb.vb2_buf.timestamp = src_mb->vb.vb2_buf.timestamp;
+
+       mfc_debug_leave();
+}
+
+static void mfc_nal_q_handle_frame_output_move(struct s5p_mfc_ctx *ctx,
+                       dma_addr_t dspl_y_addr, unsigned int released_flag)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_buf *dst_mb;
+       unsigned int index;
+
+       dst_mb = s5p_mfc_find_move_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->dst_buf_queue, &ctx->ref_buf_queue, dspl_y_addr, released_flag);
+       if (dst_mb) {
+               mfc_debug(2, "NAL Q: find display buf, index: %d\n", dst_mb->vb.vb2_buf.index);
+               /* Check if this is the buffer we're looking for */
+               mfc_debug(2, "NAL Q: buf addr: 0x%08llx, disp addr: 0x%08llx\n",
+                               s5p_mfc_mem_get_daddr_vb(&dst_mb->vb.vb2_buf, 0), dspl_y_addr);
+
+               index = dst_mb->vb.vb2_buf.index;
+
+               if (released_flag & (1 << index)) {
+                       dec->available_dpb &= ~(1 << index);
+                       released_flag &= ~(1 << index);
+                       mfc_debug(2, "NAL Q: Corrupted frame(%d), it will be re-used(release)\n",
+                                       s5p_mfc_get_warn(s5p_mfc_get_int_err()));
+               } else {
+                       dec->err_reuse_flag |= 1 << index;
+                       mfc_debug(2, "NAL Q: Corrupted frame(%d), it will be re-used(not released)\n",
+                                       s5p_mfc_get_warn(s5p_mfc_get_int_err()));
+               }
+               dec->dynamic_used |= released_flag;
+       }
+}
+
+static void mfc_nal_q_handle_frame_output_del(struct s5p_mfc_ctx *ctx,
+               DecoderOutputStr *pOutStr, unsigned int err, unsigned int released_flag)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       struct s5p_mfc_buf *ref_mb;
+       dma_addr_t dspl_y_addr;
+       unsigned int frame_type;
+       unsigned int dst_frame_status;
+       unsigned int is_video_signal_type = 0, is_colour_description = 0;
+       unsigned int is_content_light = 0, is_display_colour = 0;
+       unsigned int disp_err;
+       int i, index;
+
+       if (FW_HAS_VIDEO_SIGNAL_TYPE(dev)) {
+               is_video_signal_type = ((pOutStr->VideoSignalType
+                                       >> S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT)
+                                       & S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_MASK);
+               is_colour_description = ((pOutStr->VideoSignalType
+                                       >> S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_SHIFT)
+                                       & S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_MASK);
+       }
+
+       if (FW_HAS_SEI_INFO_FOR_HDR(dev)) {
+               is_content_light = ((pOutStr->SeiAvail >> S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_SHIFT)
+                                       & S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_MASK);
+               is_display_colour = ((pOutStr->SeiAvail >> S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT)
+                                       & S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_MASK);
+       }
+
+       if (dec->immediate_display == 1) {
+               dspl_y_addr = pOutStr->DecodedAddr[0];
+               frame_type = pOutStr->DecodedFrameType & S5P_FIMV_DECODED_FRAME_MASK;
+       } else {
+               dspl_y_addr = pOutStr->DisplayAddr[0];
+               frame_type = pOutStr->DisplayFrameType & S5P_FIMV_DISPLAY_FRAME_MASK;
+       }
+
+       ref_mb = s5p_mfc_find_del_buf_vb(&ctx->buf_queue_lock,
+                       &ctx->ref_buf_queue, dspl_y_addr);
+       if (ref_mb) {
+               mfc_debug(2, "NAL Q: find display buf, index: %d\n", ref_mb->vb.vb2_buf.index);
+               /* Check if this is the buffer we're looking for */
+               mfc_debug(2, "NAL Q: buf addr: 0x%08llx, disp addr: 0x%08llx\n",
+                               s5p_mfc_mem_get_daddr_vb(&ref_mb->vb.vb2_buf, 0), dspl_y_addr);
+
+               index = ref_mb->vb.vb2_buf.index;
+
+               ref_mb->vb.sequence = ctx->sequence;
+
+               /* Set reserved2 bits in order to inform SEI information */
+               ref_mb->vb.reserved2 = 0;
+
+               if (is_content_light) {
+                       ref_mb->vb.reserved2 |= (1 << 0);
+                       mfc_debug(2, "NAL Q: content light level parsed\n");
+               }
+               if (is_display_colour) {
+                       ref_mb->vb.reserved2 |= (1 << 1);
+                       mfc_debug(2, "NAL Q: mastering display colour parsed\n");
+               }
+               if (is_video_signal_type) {
+                       ref_mb->vb.reserved2 |= (1 << 4);
+                       mfc_debug(2, "NAL Q: video signal type parsed\n");
+                       if (is_colour_description) {
+                               ref_mb->vb.reserved2 |= (1 << 2);
+                               mfc_debug(2, "NAL Q: matrix coefficients parsed\n");
+                               ref_mb->vb.reserved2 |= (1 << 3);
+                               mfc_debug(2, "NAL Q: colour description parsed\n");
+                       }
+               }
+
+               if (IS_VP9_DEC(ctx) && FW_HAS_VP9_HDR(dev)) {
+                       if (ctx->color_space != S5P_FIMV_D_COLOR_UNKNOWN) {
+                               ref_mb->vb.reserved2 |= (1 << 3);
+                               mfc_debug(2, "NAL Q: color space parsed\n");
+                       }
+                       ref_mb->vb.reserved2 |= (1 << 4);
+                       mfc_debug(2, "NAL Q: color range parsed\n");
+               }
+
+               for (i = 0; i < raw->num_planes; i++)
+                       vb2_set_plane_payload(&ref_mb->vb.vb2_buf, i,
+                                       raw->plane_size[i]);
+
+               clear_bit(index, &dec->available_dpb);
+
+               ref_mb->vb.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
+                                       V4L2_BUF_FLAG_PFRAME |
+                                       V4L2_BUF_FLAG_BFRAME |
+                                       V4L2_BUF_FLAG_ERROR);
+
+               switch (frame_type) {
+                       case S5P_FIMV_DISPLAY_FRAME_I:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_KEYFRAME;
+                               break;
+                       case S5P_FIMV_DISPLAY_FRAME_P:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_PFRAME;
+                               break;
+                       case S5P_FIMV_DISPLAY_FRAME_B:
+                               ref_mb->vb.flags |= V4L2_BUF_FLAG_BFRAME;
+                               break;
+                       default:
+                               break;
+               }
+
+               disp_err = s5p_mfc_get_warn(pOutStr->ErrorCode);
+               if (disp_err) {
+                       mfc_err_ctx("NAL Q: Warning for displayed frame: %d\n",
+                                       disp_err);
+                       ref_mb->vb.flags |= V4L2_BUF_FLAG_ERROR;
+               }
+
+               if (call_cop(ctx, get_buf_ctrls_val_nal_q_dec, ctx,
+                                       &ctx->dst_ctrls[index], pOutStr) < 0)
+                       mfc_err_ctx("NAL Q: failed in get_buf_ctrls_val\n");
+
+               s5p_mfc_handle_released_info(ctx, released_flag, index);
+
+               if (dec->immediate_display == 1) {
+                       dst_frame_status = pOutStr->DecodedStatus
+                               & S5P_FIMV_DEC_STATUS_DECODED_STATUS_MASK;
+
+                       call_cop(ctx, get_buf_update_val, ctx,
+                                       &ctx->dst_ctrls[index],
+                                       V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS,
+                                       dst_frame_status);
+
+                       call_cop(ctx, get_buf_update_val, ctx,
+                                       &ctx->dst_ctrls[index],
+                                       V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG,
+                                       dec->stored_tag);
+
+                       dec->immediate_display = 0;
+               }
+
+               s5p_mfc_qos_update_last_framerate(ctx, ref_mb->vb.vb2_buf.timestamp);
+               vb2_buffer_done(&ref_mb->vb.vb2_buf, disp_err ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+       }
+}
+
+static void mfc_nal_q_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err,
+                                       DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       dma_addr_t dspl_y_addr;
+       unsigned int frame_type;
+       unsigned int prev_flag, released_flag = 0;
+       unsigned int disp_err;
+
+       mfc_debug_enter();
+
+       frame_type = pOutStr->DisplayFrameType & S5P_FIMV_DISPLAY_FRAME_MASK;
+       disp_err = s5p_mfc_get_warn(pOutStr->ErrorCode);
+
+       mfc_debug(2, "NAL_Q: frame_type in nal q : %d\n", frame_type);
+
+       ctx->sequence++;
+
+       dspl_y_addr = pOutStr->DisplayAddr[0];
+
+       if (dec->immediate_display == 1) {
+               dspl_y_addr = pOutStr->DecodedAddr[0];
+               frame_type = pOutStr->DecodedFrameType & S5P_FIMV_DECODED_FRAME_MASK;
+       }
+
+       /* If frame is same as previous then skip and do not dequeue */
+       if (frame_type == S5P_FIMV_DISPLAY_FRAME_NOT_CODED) {
+               if (!CODEC_NOT_CODED(ctx))
+                       return;
+       }
+
+       prev_flag = dec->dynamic_used;
+       dec->dynamic_used = pOutStr->UsedDpbFlagLower;
+       released_flag = prev_flag & (~dec->dynamic_used);
+
+       mfc_debug(2, "NAL Q: Used flag = %08x, Released Buffer = %08x\n",
+                       dec->dynamic_used, released_flag);
+
+       if ((IS_VC1_RCV_DEC(ctx) &&
+                       (disp_err == S5P_FIMV_ERR_SYNC_POINT_NOT_RECEIVED)) ||
+                       (disp_err == S5P_FIMV_ERR_BROKEN_LINK))
+               mfc_nal_q_handle_frame_output_move(ctx, dspl_y_addr, released_flag);
+       else
+               mfc_nal_q_handle_frame_output_del(ctx, pOutStr, err, released_flag);
+
+       mfc_debug_leave();
+}
+
+static void mfc_nal_q_handle_frame_input(struct s5p_mfc_ctx *ctx, unsigned int err,
+                                       DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *src_mb;
+       unsigned int index;
+       int deleted = 0;
+       unsigned long consumed;
+
+       /* If there is consumed byte, it is abnormal status,
+        * We have to return remained stream buffer
+        */
+       if (dec->consumed) {
+               mfc_err_dev("NAL Q: previous buffer was not fully consumed\n");
+               src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue,
+                               MFC_BUF_NO_TOUCH_USED);
+               if (src_mb)
+                       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+       }
+
+       /* Check multi-frame */
+       consumed = pOutStr->DecodedNalSize;
+       src_mb = s5p_mfc_get_del_if_consumed(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue,
+                       consumed, STUFF_BYTE, err, &deleted);
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+               return;
+       }
+
+       if (!deleted) {
+               /* Run MFC again on the same buffer */
+               mfc_debug(2, "NAL Q: Running again the same buffer.\n");
+
+               if (CODEC_MULTIFRAME(ctx))
+                       dec->y_addr_for_pb = (dma_addr_t)pOutStr->DecodedAddr[0];
+
+               dec->consumed = consumed;
+               dec->remained_size = src_mb->vb.vb2_buf.planes[0].bytesused
+                       - dec->consumed;
+               dec->has_multiframe = 1;
+               /* Do not move src buffer to done_list */
+               return;
+       }
+
+       index = src_mb->vb.vb2_buf.index;
+       if (call_cop(ctx, get_buf_ctrls_val_nal_q_dec, ctx,
+                               &ctx->src_ctrls[index], pOutStr) < 0)
+               mfc_err_ctx("NAL Q: failed in get_buf_ctrls_val\n");
+
+       mfc_debug(2, "NAL Q: MFC needs next buffer.\n");
+       dec->consumed = 0;
+       dec->remained_size = 0;
+
+       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void mfc_nal_q_handle_frame(struct s5p_mfc_ctx *ctx, DecoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       unsigned int dst_frame_status, sei_avail_status, need_empty_dpb;
+       unsigned int res_change, need_dpb_change, need_scratch_change;
+       unsigned int is_interlaced, err;
+
+       mfc_debug_enter();
+
+       dst_frame_status = pOutStr->DisplayStatus
+                               & S5P_FIMV_DISP_STATUS_DISPLAY_STATUS_MASK;
+       need_empty_dpb = (pOutStr->DisplayStatus
+                               >> S5P_FIMV_DISP_STATUS_NEED_EMPTY_DPB_SHIFT)
+                               & S5P_FIMV_DISP_STATUS_NEED_EMPTY_DPB_MASK;
+       res_change = (pOutStr->DisplayStatus
+                               >> S5P_FIMV_DISP_STATUS_RES_CHANGE_SHIFT)
+                               & S5P_FIMV_DISP_STATUS_RES_CHANGE_MASK;
+       need_dpb_change = (pOutStr->DisplayStatus
+                               >> S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_SHIFT)
+                               & S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_MASK;
+       need_scratch_change = (pOutStr->DisplayStatus
+                                >> S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_SHIFT)
+                               & S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_MASK;
+       is_interlaced = (pOutStr->DisplayStatus
+                               >> S5P_FIMV_DISP_STATUS_INTERLACE_SHIFT)
+                               & S5P_FIMV_DISP_STATUS_INTERLACE_MASK;
+       sei_avail_status = pOutStr->SeiAvail;
+       err = pOutStr->ErrorCode;
+
+       if (dec->immediate_display == 1)
+               dst_frame_status = pOutStr->DecodedStatus
+                               & S5P_FIMV_DEC_STATUS_DECODED_STATUS_MASK;
+
+       mfc_debug(2, "NAL Q: Frame Status: %x\n", dst_frame_status);
+       mfc_debug(2, "NAL Q: SEI available status: %x\n", sei_avail_status);
+       mfc_debug(2, "NAL Q: Used flag: old = %08x, new = %08x\n",
+                               dec->dynamic_used, pOutStr->UsedDpbFlagLower);
+
+       if (ctx->state == MFCINST_RES_CHANGE_INIT) {
+               mfc_debug(2, "NAL Q: return until NAL-Q stopped in try_run\n");
+               goto leave_handle_frame;
+       }
+       if (res_change) {
+               mfc_debug(2, "NAL Q: Resolution change set to %d\n", res_change);
+               s5p_mfc_change_state(ctx, MFCINST_RES_CHANGE_INIT);
+               ctx->wait_state = WAIT_DECODING;
+               dev->nal_q_handle->nal_q_exception = 1;
+               mfc_info_ctx("NAL Q: nal_q_exception is set (res change)\n");
+               goto leave_handle_frame;
+       }
+       if (need_empty_dpb) {
+               mfc_debug(2, "NAL Q: There is multi-frame. consumed:%ld\n", dec->consumed);
+               dec->has_multiframe = 1;
+               dev->nal_q_handle->nal_q_exception = 1;
+               mfc_info_ctx("NAL Q: nal_q_exception is set (multi-frame)\n");
+               goto leave_handle_frame;
+       }
+       if (need_dpb_change || need_scratch_change) {
+               mfc_debug(2, "NAL Q: Interframe resolution change is not supported\n");
+               dev->nal_q_handle->nal_q_exception = 1;
+               mfc_info_ctx("NAL Q: nal_q_exception is set (interframe res change)\n");
+               goto leave_handle_frame;
+       }
+       if (is_interlaced) {
+               mfc_debug(2, "NAL Q: Progressive -> Interlaced\n");
+               dec->is_interlaced = is_interlaced;
+               dev->nal_q_handle->nal_q_exception = 1;
+               mfc_info_ctx("NAL Q: nal_q_exception is set (interlaced)\n");
+               goto leave_handle_frame;
+       }
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue, 0) &&
+               s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue, 0)) {
+               mfc_err_dev("NAL Q: Queue count is zero for src/dst\n");
+               goto leave_handle_frame;
+       }
+
+       switch (dst_frame_status) {
+       case S5P_FIMV_DEC_STATUS_DECODING_DISPLAY:
+               mfc_nal_q_handle_ref_frame(ctx, pOutStr);
+               mfc_nal_q_handle_frame_copy_timestamp(ctx, pOutStr);
+               break;
+       case S5P_FIMV_DEC_STATUS_DECODING_ONLY:
+               mfc_nal_q_handle_ref_frame(ctx, pOutStr);
+               mfc_nal_q_handle_reuse_buffer(ctx, pOutStr);
+               break;
+       default:
+               break;
+       }
+
+       /* A frame has been decoded and is in the buffer  */
+       if (s5p_mfc_dec_status_display(dst_frame_status))
+               mfc_nal_q_handle_frame_new(ctx, err, pOutStr);
+       else
+               mfc_debug(2, "NAL Q: No display frame.\n");
+
+       /* Mark source buffer as complete */
+       if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY)
+               mfc_nal_q_handle_frame_input(ctx, err, pOutStr);
+       else
+               mfc_debug(2, "NAL Q: can't support display only in NAL-Q, is_dpb_full: %d\n",
+                               dec->is_dpb_full);
+
+leave_handle_frame:
+       mfc_debug_leave();
+}
+
+void mfc_nal_q_handle_error(struct s5p_mfc_ctx *ctx, EncoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *src_mb;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("NAL Q: no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return;
+       }
+
+       mfc_err_ctx("NAL Q: Interrupt Error: %d\n", pOutStr->ErrorCode);
+
+       dev->nal_q_handle->nal_q_exception = 1;
+       mfc_info_ctx("NAl Q: nal_q_exception is set (error)\n");
+
+       if (ctx->type == MFCINST_DECODER) {
+               dec = ctx->dec_priv;
+               if (!dec) {
+                       mfc_err_dev("NAL Q: no mfc decoder to run\n");
+                       return;
+               }
+
+               src_mb = s5p_mfc_get_del_buf(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue, MFC_BUF_NO_TOUCH_USED);
+
+               if (!src_mb) {
+                       mfc_err_dev("NAL Q: no src buffers.\n");
+               } else {
+                       dec->consumed = 0;
+                       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+               }
+       }
+
+       mfc_debug_leave();
+}
+
+int s5p_mfc_nal_q_handle_out_buf(struct s5p_mfc_dev *dev, EncoderOutputStr *pOutStr)
+{
+       struct s5p_mfc_ctx *ctx;
+       struct s5p_mfc_enc *enc;
+       struct s5p_mfc_dec *dec;
+       int ctx_num;
+
+       mfc_debug_enter();
+
+       if (!dev) {
+               mfc_err_dev("NAL Q: no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       ctx_num = dev->nal_q_handle->nal_q_out_handle->nal_q_ctx;
+       if (ctx_num < 0) {
+               mfc_err_dev("NAL Q: Can't find ctx in nal q\n");
+               return -EINVAL;
+       }
+
+       ctx = dev->ctx[ctx_num];
+       if (!ctx) {
+               mfc_err_dev("NAL Q: no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       mfc_debug(2, "NAL Q: Int ctx is %d(%s)\n", ctx_num,
+                        ctx->type == MFCINST_ENCODER ? "enc" : "dec");
+
+       if (s5p_mfc_get_err(pOutStr->ErrorCode) &&
+                       (s5p_mfc_get_err(pOutStr->ErrorCode) < S5P_FIMV_ERR_FRAME_CONCEAL)) {
+               mfc_nal_q_handle_error(ctx, pOutStr);
+               return 0;
+       }
+
+       if (ctx->type == MFCINST_ENCODER) {
+               enc = ctx->enc_priv;
+               if (!enc) {
+                       mfc_err_dev("NAL Q: no mfc encoder to run\n");
+                       return -EINVAL;
+               }
+               mfc_nal_q_handle_stream(ctx, pOutStr);
+       } else if (ctx->type == MFCINST_DECODER) {
+               dec = ctx->dec_priv;
+               if (!dec) {
+                       mfc_err_dev("NAL Q: no mfc decoder to run\n");
+                       return -EINVAL;
+               }
+               mfc_nal_q_handle_frame(ctx, (DecoderOutputStr *)pOutStr);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/*
+  * This function should be called in NAL_Q_STATE_INITIALIZED or NAL_Q_STATE_STARTED state.
+  */
+int s5p_mfc_nal_q_enqueue_in_buf(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+       nal_queue_in_handle *nal_q_in_handle)
+{
+       unsigned long flags;
+       unsigned int input_count = 0;
+       unsigned int input_exe_count = 0;
+       int input_diff = 0;
+       unsigned int index = 0;
+       EncoderInputStr *pStr = NULL;
+       int ret = 0;
+
+       mfc_debug_enter();
+
+       if (!nal_q_in_handle) {
+               mfc_err_dev("NAL Q: There is no nal_q_handle\n");
+               return -EINVAL;
+       }
+
+       if ((nal_q_in_handle->nal_q_handle->nal_q_state != NAL_Q_STATE_INITIALIZED)
+               && (nal_q_in_handle->nal_q_handle->nal_q_state != NAL_Q_STATE_STARTED)) {
+               mfc_err_dev("NAL Q: State is wrong, state: %d\n",
+                               nal_q_in_handle->nal_q_handle->nal_q_state);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&nal_q_in_handle->lock, flags);
+
+       input_count = s5p_mfc_get_nal_q_input_count();
+       input_exe_count = s5p_mfc_get_nal_q_input_exe_count();
+       input_diff = input_count - input_exe_count;
+
+       /*
+        * meaning of the variable input_diff
+        * 0:                           number of available slots = NAL_Q_IN_QUEUE_SIZE
+        * 1:                           number of available slots = NAL_Q_IN_QUEUE_SIZE - 1
+        * ...
+        * NAL_Q_IN_QUEUE_SIZE-1:       number of available slots = 1
+        * NAL_Q_IN_QUEUE_SIZE:         number of available slots = 0
+        */
+
+       mfc_debug(2, "NAL Q: input_diff = %d(in: %d, exe: %d)\n",
+                       input_diff, input_count, input_exe_count);
+
+       if ((input_diff < 0) || (input_diff >= NAL_Q_IN_QUEUE_SIZE)) {
+               mfc_err_dev("NAL Q: No available input slot(%d)\n", input_diff);
+               spin_unlock_irqrestore(&nal_q_in_handle->lock, flags);
+               return -EINVAL;
+       }
+
+       index = input_count % NAL_Q_IN_QUEUE_SIZE;
+       pStr = &(nal_q_in_handle->nal_q_in_addr->entry[index].enc);
+
+       memset(pStr, 0, NAL_Q_IN_ENTRY_SIZE);
+
+       if (ctx->type == MFCINST_ENCODER)
+               ret = mfc_nal_q_run_in_buf_enc(ctx, pStr);
+       else if (ctx->type == MFCINST_DECODER)
+               ret = mfc_nal_q_run_in_buf_dec(ctx, (DecoderInputStr *)pStr);
+
+       if (ret != 0) {
+               mfc_debug(2, "NAL Q: Failed to set input queue\n");
+               spin_unlock_irqrestore(&nal_q_in_handle->lock, flags);
+               return ret;
+       }
+
+       if (nal_q_dump == 1) {
+               mfc_err_dev("[NAL-Q][DUMP][%s INPUT][c: %d] diff: %d, count: %d, exe: %d\n",
+                               ctx->type == MFCINST_ENCODER ? "ENC" : "DEC", dev->curr_ctx,
+                               input_diff, input_count, input_exe_count);
+               print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4, (int *)pStr, 256, false);
+               printk("...\n");
+       }
+       input_count++;
+
+       s5p_mfc_update_nal_queue_input_count(dev, input_count);
+
+       if (input_diff == 0)
+               s5p_mfc_watchdog_start_tick(dev);
+
+       spin_unlock_irqrestore(&nal_q_in_handle->lock, flags);
+
+       MFC_TRACE_CTX("NAL %s in: diff %d count %d exe %d\n",
+                       ctx->type == MFCINST_ENCODER ? "ENC" : "DEC",
+                       input_diff, input_count, input_exe_count);
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+/*
+  * This function should be called in NAL_Q_STATE_STARTED state.
+  */
+EncoderOutputStr *s5p_mfc_nal_q_dequeue_out_buf(struct s5p_mfc_dev *dev,
+       nal_queue_out_handle *nal_q_out_handle, unsigned int *reason)
+{
+       struct s5p_mfc_ctx *ctx;
+       unsigned long flags;
+       unsigned int output_count = 0;
+       unsigned int output_exe_count = 0;
+       int output_diff = 0;
+       unsigned int index = 0;
+       EncoderOutputStr *pStr = NULL;
+
+       int input_diff = 0;
+
+       mfc_debug_enter();
+
+       if (!nal_q_out_handle || !nal_q_out_handle->nal_q_out_addr) {
+               mfc_err_dev("NAL Q: There is no handle\n");
+               return pStr;
+       }
+
+       spin_lock_irqsave(&nal_q_out_handle->nal_q_handle->nal_q_in_handle->lock, flags);
+
+       output_count = s5p_mfc_get_nal_q_output_count();
+       output_exe_count = nal_q_out_handle->out_exe_count;
+       output_diff = output_count - output_exe_count;
+
+       /*
+        * meaning of the variable output_diff
+        * 0:                           number of output slots = 0
+        * 1:                           number of output slots = 1
+        * ...
+        * NAL_Q_OUT_QUEUE_SIZE-1:      number of output slots = NAL_Q_OUT_QUEUE_SIZE - 1
+        * NAL_Q_OUT_QUEUE_SIZE:        number of output slots = NAL_Q_OUT_QUEUE_SIZE
+        */
+
+       mfc_debug(2, "NAL Q: output_diff = %d(out: %d, exe: %d)\n",
+                       output_diff, output_count, output_exe_count);
+       if ((output_diff <= 0) || (output_diff > NAL_Q_OUT_QUEUE_SIZE)) {
+               spin_unlock_irqrestore(&nal_q_out_handle->nal_q_handle->nal_q_in_handle->lock, flags);
+               mfc_debug(2, "NAL Q: No available output slot(%d)\n", output_diff);
+               return pStr;
+       }
+
+       index = output_exe_count % NAL_Q_OUT_QUEUE_SIZE;
+       pStr = &(nal_q_out_handle->nal_q_out_addr->entry[index].enc);
+
+       nal_q_out_handle->nal_q_ctx = mfc_nal_q_find_ctx(dev, pStr);
+       if (nal_q_out_handle->nal_q_ctx < 0) {
+               mfc_err_dev("NAl Q: Can't find ctx in nal q\n");
+               pStr = NULL;
+               return pStr;
+       }
+
+       ctx = dev->ctx[nal_q_out_handle->nal_q_ctx];
+       if (nal_q_dump == 1) {
+               mfc_err_dev("[NAL-Q][DUMP][%s OUTPUT][c: %d] diff: %d, count: %d, exe: %d\n",
+                               ctx->type == MFCINST_ENCODER ? "ENC" : "DEC",
+                               nal_q_out_handle->nal_q_ctx,
+                               output_diff, output_count, output_exe_count);
+               print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4, (int *)pStr, 256, false);
+               printk("...\n");
+       }
+       nal_q_out_handle->out_exe_count++;
+
+       if (pStr->ErrorCode) {
+               *reason = S5P_FIMV_R2H_CMD_ERR_RET;
+               mfc_err_dev("NAL Q: Error : %d\n", pStr->ErrorCode);
+       }
+
+       input_diff = s5p_mfc_get_nal_q_input_count() - s5p_mfc_get_nal_q_input_exe_count();
+       if (input_diff == 0) {
+               s5p_mfc_watchdog_stop_tick(dev);
+       } else if (input_diff > 0) {
+               s5p_mfc_watchdog_reset_tick(dev);
+       }
+
+       spin_unlock_irqrestore(&nal_q_out_handle->nal_q_handle->nal_q_in_handle->lock, flags);
+
+       MFC_TRACE_CTX("NAL %s out: diff %d count %d exe %d\n",
+                       ctx->type == MFCINST_ENCODER ? "ENC" : "DEC",
+                       output_diff, output_count, output_exe_count);
+
+       mfc_debug_leave();
+
+       return pStr;
+}
+
+#if 0
+/* not used function - only for reference sfr <-> structure */
+void s5p_mfc_nal_q_fill_DecoderInputStr(struct s5p_mfc_dev *dev, DecoderInputStr *pStr)
+{
+       pStr->StartCode                 = 0xAAAAAAAA; // Decoder input start
+//     pStr->CommandId                 = MFC_READL(S5P_FIMV_HOST2RISC_CMD);            // 0x1100
+       pStr->InstanceId                = MFC_READL(S5P_FIMV_INSTANCE_ID);              // 0xF008
+       pStr->PictureTag                = MFC_READL(S5P_FIMV_D_PICTURE_TAG);            // 0xF5C8
+       pStr->CpbBufferAddr             = MFC_READL(S5P_FIMV_D_CPB_BUFFER_ADDR);        // 0xF5B0
+       pStr->CpbBufferSize             = MFC_READL(S5P_FIMV_D_CPB_BUFFER_SIZE);        // 0xF5B4
+       pStr->CpbBufferOffset           = MFC_READL(S5P_FIMV_D_CPB_BUFFER_OFFSET);      // 0xF5C0
+       pStr->StreamDataSize            = MFC_READL(S5P_FIMV_D_STREAM_DATA_SIZE);       // 0xF5D0
+       pStr->AvailableDpbFlagUpper     = MFC_READL(S5P_FIMV_D_AVAILABLE_DPB_FLAG_UPPER);// 0xF5B8
+       pStr->AvailableDpbFlagLower     = MFC_READL(S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER);// 0xF5BC
+       pStr->DynamicDpbFlagUpper       = MFC_READL(S5P_FIMV_D_DYNAMIC_DPB_FLAG_UPPER); // 0xF5D4
+       pStr->DynamicDpbFlagLower       = MFC_READL(S5P_FIMV_D_DYNAMIC_DPB_FLAG_LOWER); // 0xF5D8
+       pStr->FirstPlaneDpb             = MFC_READL(S5P_FIMV_D_FIRST_PLANE_DPB0);       // 0xF160+(index*4)
+       pStr->SecondPlaneDpb            = MFC_READL(S5P_FIMV_D_SECOND_PLANE_DPB0);      // 0xF260+(index*4)
+       pStr->ThirdPlaneDpb             = MFC_READL(S5P_FIMV_D_THIRD_PLANE_DPB0);       // 0xF360+(index*4)
+       pStr->FirstPlaneDpbSize         = MFC_READL(S5P_FIMV_D_FIRST_PLANE_DPB_SIZE);   // 0xF144
+       pStr->SecondPlaneDpbSize        = MFC_READL(S5P_FIMV_D_SECOND_PLANE_DPB_SIZE);  // 0xF148
+       pStr->ThirdPlaneDpbSize         = MFC_READL(S5P_FIMV_D_THIRD_PLANE_DPB_SIZE);   // 0xF14C
+       pStr->NalStartOptions           = MFC_READL(0xF5AC);// S5P_FIMV_D_NAL_START_OPTIONS 0xF5AC
+       pStr->FirstPlaneStrideSize      = MFC_READL(S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE);// 0xF138
+       pStr->SecondPlaneStrideSize     = MFC_READL(S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE);// 0xF13C
+       pStr->ThirdPlaneStrideSize      = MFC_READL(S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE);// 0xF140
+       pStr->FirstPlane2BitDpbSize     = MFC_READL(S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_SIZE);// 0xF578
+       pStr->SecondPlane2BitDpbSize    = MFC_READL(S5P_FIMV_D_SECOND_PLANE_2BIT_DPB_SIZE);// 0xF57C
+       pStr->FirstPlane2BitStrideSize  = MFC_READL(S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE);// 0xF580
+       pStr->SecondPlane2BitStrideSize = MFC_READL(S5P_FIMV_D_SECOND_PLANE_2BIT_DPB_STRIDE_SIZE);// 0xF584
+       pStr->ScratchBufAddr            = MFC_READL(S5P_FIMV_D_SCRATCH_BUFFER_ADDR);    // 0xF560
+       pStr->ScratchBufSize            = MFC_READL(S5P_FIMV_D_SCRATCH_BUFFER_SIZE);    // 0xF564
+}
+
+void s5p_mfc_nal_q_flush_DecoderOutputStr(struct s5p_mfc_dev *dev, DecoderOutputStr *pStr)
+{
+       //pStr->StartCode; // 0xAAAAAAAA; // Decoder output start
+//     MFC_WRITEL(pStr->CommandId, S5P_FIMV_RISC2HOST_CMD);                            // 0x1104
+       MFC_WRITEL(pStr->InstanceId, S5P_FIMV_RET_INSTANCE_ID);                         // 0xF070
+       MFC_WRITEL(pStr->ErrorCode, S5P_FIMV_ERROR_CODE);                               // 0xF074
+       MFC_WRITEL(pStr->PictureTagTop, S5P_FIMV_D_RET_PICTURE_TAG_TOP);                // 0xF674
+       MFC_WRITEL(pStr->PictureTimeTop, S5P_FIMV_D_RET_PICTURE_TIME_TOP);              // 0xF67C
+       MFC_WRITEL(pStr->DisplayFrameWidth, S5P_FIMV_D_DISPLAY_FRAME_WIDTH);            // 0xF600
+       MFC_WRITEL(pStr->DisplayFrameHeight, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT);          // 0xF604
+       MFC_WRITEL(pStr->DisplayStatus, S5P_FIMV_D_DISPLAY_STATUS);                     // 0xF608
+       MFC_WRITEL(pStr->DisplayFirstPlaneAddr, S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR);   // 0xF60C
+       MFC_WRITEL(pStr->DisplaySecondPlaneAddr, S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR); // 0xF610
+       MFC_WRITEL(pStr->DisplayThirdPlaneAddr,S5P_FIMV_D_DISPLAY_THIRD_PLANE_ADDR);    // 0xF614
+       MFC_WRITEL(pStr->DisplayFrameType, S5P_FIMV_D_DISPLAY_FRAME_TYPE);              // 0xF618
+       MFC_WRITEL(pStr->DisplayCropInfo1, S5P_FIMV_D_DISPLAY_CROP_INFO1);              // 0xF61C
+       MFC_WRITEL(pStr->DisplayCropInfo2, S5P_FIMV_D_DISPLAY_CROP_INFO2);              // 0xF620
+       MFC_WRITEL(pStr->DisplayPictureProfile, S5P_FIMV_D_DISPLAY_PICTURE_PROFILE);    // 0xF624
+       MFC_WRITEL(pStr->DisplayAspectRatio, S5P_FIMV_D_DISPLAY_ASPECT_RATIO);          // 0xF634
+       MFC_WRITEL(pStr->DisplayExtendedAr, S5P_FIMV_D_DISPLAY_EXTENDED_AR);            // 0xF638
+       MFC_WRITEL(pStr->DecodedNalSize, S5P_FIMV_D_DECODED_NAL_SIZE);                  // 0xF664
+       MFC_WRITEL(pStr->UsedDpbFlagUpper, S5P_FIMV_D_USED_DPB_FLAG_UPPER);             // 0xF720
+       MFC_WRITEL(pStr->UsedDpbFlagLower, S5P_FIMV_D_USED_DPB_FLAG_LOWER);             // 0xF724
+       MFC_WRITEL(pStr->SeiAvail, S5P_FIMV_D_SEI_AVAIL);                               // 0xF6DC
+       MFC_WRITEL(pStr->FramePackArrgmentId, S5P_FIMV_D_FRAME_PACK_ARRGMENT_ID);       // 0xF6E0
+       MFC_WRITEL(pStr->FramePackSeiInfo, S5P_FIMV_D_FRAME_PACK_SEI_INFO);             // 0xF6E4
+       MFC_WRITEL(pStr->FramePackGridPos, S5P_FIMV_D_FRAME_PACK_GRID_POS);             // 0xF6E8
+       MFC_WRITEL(pStr->DisplayRecoverySeiInfo, S5P_FIMV_D_DISPLAY_RECOVERY_SEI_INFO); // 0xF6EC
+       MFC_WRITEL(pStr->H264Info, S5P_FIMV_D_H264_INFO);                               // 0xF690
+       MFC_WRITEL(pStr->DisplayFirstCrc, S5P_FIMV_D_DISPLAY_FIRST_PLANE_CRC);          // 0xF628
+       MFC_WRITEL(pStr->DisplaySecondCrc, S5P_FIMV_D_DISPLAY_SECOND_PLANE_CRC);        // 0xF62C
+       MFC_WRITEL(pStr->DisplayThirdCrc, S5P_FIMV_D_DISPLAY_THIRD_PLANE_CRC);          // 0xF630
+       MFC_WRITEL(pStr->DisplayFirst2BitCrc, S5P_FIMV_D_DISPLAY_FIRST_PLANE_2BIT_CRC); // 0xF6FC
+       MFC_WRITEL(pStr->DisplaySecond2BitCrc, S5P_FIMV_D_DISPLAY_SECOND_PLANE_2BIT_CRC);// 0xF700
+       MFC_WRITEL(pStr->DecodedFrameWidth, S5P_FIMV_D_DECODED_FRAME_WIDTH);            // 0xF63C
+       MFC_WRITEL(pStr->DecodedFrameHeight, S5P_FIMV_D_DECODED_FRAME_HEIGHT);          // 0xF640
+       MFC_WRITEL(pStr->DecodedStatus, S5P_FIMV_D_DECODED_STATUS);                     // 0xF644
+       MFC_WRITEL(pStr->DecodedFirstPlaneAddr, S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR);   // 0xF648
+       MFC_WRITEL(pStr->DecodedSecondPlaneAddr, S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR); // 0xF64C
+       MFC_WRITEL(pStr->DecodedThirdPlaneAddr, S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR);   // 0xF650
+       MFC_WRITEL(pStr->DecodedFrameType, S5P_FIMV_D_DECODED_FRAME_TYPE);              // 0xF654
+       MFC_WRITEL(pStr->DecodedCropInfo1, S5P_FIMV_D_DECODED_CROP_INFO1);              // 0xF658
+       MFC_WRITEL(pStr->DecodedCropInfo2, S5P_FIMV_D_DECODED_CROP_INFO2);              // 0xF65C
+       MFC_WRITEL(pStr->DecodedPictureProfile, S5P_FIMV_D_DECODED_PICTURE_PROFILE);    // 0xF660
+       MFC_WRITEL(pStr->DecodedRecoverySeiInfo, S5P_FIMV_D_DECODED_RECOVERY_SEI_INFO); // 0xF6F0
+       MFC_WRITEL(pStr->DecodedFirstCrc, S5P_FIMV_D_DECODED_FIRST_PLANE_CRC);          // 0xF668
+       MFC_WRITEL(pStr->DecodedSecondCrc, S5P_FIMV_D_DECODED_SECOND_PLANE_CRC);        // 0xF66C
+       MFC_WRITEL(pStr->DecodedThirdCrc, S5P_FIMV_D_DECODED_THIRD_PLANE_CRC);          // 0xF670
+       MFC_WRITEL(pStr->DecodedFirst2BitCrc, S5P_FIMV_D_DECODED_FIRST_PLANE_2BIT_CRC); // 0xF704
+       MFC_WRITEL(pStr->DecodedSecond2BitCrc, S5P_FIMV_D_DECODED_SECOND_PLANE_2BIT_CRC);// 0xF708
+       MFC_WRITEL(pStr->PictureTagBot, S5P_FIMV_D_RET_PICTURE_TAG_BOT);                // 0xF678
+       MFC_WRITEL(pStr->PictureTimeBot, S5P_FIMV_D_RET_PICTURE_TIME_BOT);              // 0xF680
+}
+#endif
+#endif
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.h b/drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.h
new file mode 100644 (file)
index 0000000..656896f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_nal_q.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 __S5P_MFC_NAL_Q_H
+#define __S5P_MFC_NAL_Q_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_nal_q_check_enable(struct s5p_mfc_dev *dev);
+
+nal_queue_handle *s5p_mfc_nal_q_create(struct s5p_mfc_dev *dev);
+int s5p_mfc_nal_q_destroy(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle);
+
+void s5p_mfc_nal_q_init(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle);
+void s5p_mfc_nal_q_start(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle);
+void s5p_mfc_nal_q_stop(struct s5p_mfc_dev *dev, nal_queue_handle *nal_q_handle);
+void s5p_mfc_nal_q_stop_if_started(struct s5p_mfc_dev *dev);
+void s5p_mfc_nal_q_cleanup_queue(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_nal_q_handle_out_buf(struct s5p_mfc_dev *dev, EncoderOutputStr *pOutStr);
+int s5p_mfc_nal_q_enqueue_in_buf(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+                       nal_queue_in_handle *nal_q_in_handle);
+EncoderOutputStr *s5p_mfc_nal_q_dequeue_out_buf(struct s5p_mfc_dev *dev,
+                       nal_queue_out_handle *nal_q_out_handle, unsigned int *reason);
+
+#endif /* __S5P_MFC_NAL_Q_H  */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_opr.c b/drivers/media/platform/exynos/mfc/s5p_mfc_opr.c
new file mode 100644 (file)
index 0000000..994b3c2
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_opr.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 "s5p_mfc_opr.h"
+
+#include "s5p_mfc_inst.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_mem.h"
+
+int s5p_mfc_run_dec_init(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *src_mb;
+       struct s5p_mfc_dec *dec = NULL;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       /* Initializing decoding - parsing header */
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+       if (!src_mb) {
+               mfc_err_dev("no src buffers.\n");
+               return -EAGAIN;
+       }
+
+       mfc_debug(2, "Preparing to init decoding.\n");
+       mfc_debug(2, "Header size: %d, (offset: %lu)\n",
+               src_mb->vb.vb2_buf.planes[0].bytesused, dec->consumed);
+
+       if (dec->consumed) {
+               s5p_mfc_set_dec_stream_buffer(ctx, src_mb, dec->consumed, dec->remained_size);
+       } else {
+               /* decoder src buffer CFW PROT */
+               if (ctx->is_drm) {
+                       int index = src_mb->vb.vb2_buf.index;
+
+                       s5p_mfc_stream_protect(ctx, src_mb, index);
+               }
+
+               s5p_mfc_set_dec_stream_buffer(ctx, src_mb,
+                       0, src_mb->vb.vb2_buf.planes[0].bytesused);
+       }
+
+       mfc_debug(2, "Header addr: 0x%08llx\n", s5p_mfc_mem_get_daddr_vb(&src_mb->vb.vb2_buf, 0));
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_init_decode(ctx);
+
+       return 0;
+}
+
+static int mfc_check_last_frame(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf)
+{
+       if (mfc_buf->vb.reserved2 & FLAG_LAST_FRAME) {
+               mfc_debug(2, "Setting ctx->state to FINISHING\n");
+               s5p_mfc_change_state(ctx, MFCINST_FINISHING);
+               return 1;
+       }
+
+       return 0;
+}
+
+int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       struct s5p_mfc_dec *dec;
+       int last_frame = 0;
+       unsigned int index;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0) &&
+                       s5p_mfc_is_queue_count_smaller(&ctx->buf_queue_lock,
+                               &ctx->ref_buf_queue, (ctx->dpb_count + 5))) {
+               return -EAGAIN;
+       }
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_SET_USED);
+       if (!src_mb) {
+               mfc_debug(2, "no src buffers.\n");
+               return -EAGAIN;
+       }
+
+       /* decoder src buffer CFW PROT */
+       if (ctx->is_drm) {
+               if (!dec->consumed) {
+                       index = src_mb->vb.vb2_buf.index;
+                       s5p_mfc_stream_protect(ctx, src_mb, index);
+               }
+       }
+
+       if (src_mb->vb.reserved2 & FLAG_EMPTY_DATA)
+               src_mb->vb.vb2_buf.planes[0].bytesused = 0;
+
+       if (dec->consumed)
+               s5p_mfc_set_dec_stream_buffer(ctx, src_mb, dec->consumed, dec->remained_size);
+       else
+               s5p_mfc_set_dec_stream_buffer(ctx, src_mb, 0, src_mb->vb.vb2_buf.planes[0].bytesused);
+
+       /* Try to use the non-referenced DPB on dst-queue */
+       dst_mb = s5p_mfc_search_for_dpb(ctx, dec->dynamic_used);
+       if (!dst_mb) {
+               mfc_debug(2, "no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       index = src_mb->vb.vb2_buf.index;
+       if (call_cop(ctx, set_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+               mfc_err_ctx("failed in set_buf_ctrls_val\n");
+
+       s5p_mfc_set_dynamic_dpb(ctx, dst_mb);
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+
+       last_frame = mfc_check_last_frame(ctx, src_mb);
+       s5p_mfc_decode_one_frame(ctx, last_frame);
+
+       return 0;
+}
+
+int s5p_mfc_run_dec_last_frames(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       struct s5p_mfc_dec *dec;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no decoder context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       if (s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)) {
+               mfc_debug(2, "no dst buffer\n");
+               return -EAGAIN;
+       }
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_SET_USED);
+
+       /* Frames are being decoded */
+       if (!src_mb) {
+               mfc_debug(2, "no src buffers.\n");
+               s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+       } else {
+               if (dec->consumed) {
+                       s5p_mfc_set_dec_stream_buffer(ctx, src_mb, dec->consumed, dec->remained_size);
+               } else {
+                       /* decoder src buffer CFW PROT */
+                       if (ctx->is_drm) {
+                               int index = src_mb->vb.vb2_buf.index;
+
+                               s5p_mfc_stream_protect(ctx, src_mb, index);
+                       }
+
+                       s5p_mfc_set_dec_stream_buffer(ctx, src_mb, 0, 0);
+               }
+       }
+
+       /* Try to use the non-referenced DPB on dst-queue */
+       dst_mb = s5p_mfc_search_for_dpb(ctx, dec->dynamic_used);
+       if (!dst_mb) {
+               mfc_debug(2, "no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       s5p_mfc_set_dynamic_dpb(ctx, dst_mb);
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_decode_one_frame(ctx, 1);
+
+       return 0;
+}
+
+int s5p_mfc_run_enc_init(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_buf *dst_mb;
+       int ret;
+
+       dst_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED);
+       if (!dst_mb) {
+               mfc_debug(2, "no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       /* encoder dst buffer CFW PROT */
+       if (ctx->is_drm) {
+               int index = dst_mb->vb.vb2_buf.index;
+
+               s5p_mfc_stream_protect(ctx, dst_mb, index);
+       }
+       s5p_mfc_set_enc_stream_buffer(ctx, dst_mb);
+
+       s5p_mfc_set_enc_stride(ctx);
+
+       mfc_debug(2, "Header addr: 0x%08llx\n", s5p_mfc_mem_get_daddr_vb(&dst_mb->vb.vb2_buf, 0));
+       s5p_mfc_clean_ctx_int_flags(ctx);
+
+       ret = s5p_mfc_init_encode(ctx);
+       return ret;
+}
+
+int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_buf *dst_mb;
+       struct s5p_mfc_buf *src_mb;
+       struct s5p_mfc_raw_info *raw;
+       unsigned int index, i;
+       int last_frame = 0;
+
+       raw = &ctx->raw_buf;
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_SET_USED);
+       if (!src_mb) {
+               mfc_debug(2, "no src buffers.\n");
+               return -EAGAIN;
+       }
+
+       last_frame = mfc_check_last_frame(ctx, src_mb);
+
+       index = src_mb->vb.vb2_buf.index;
+
+       /* encoder src buffer CFW PROT */
+       if (ctx->is_drm)
+               s5p_mfc_raw_protect(ctx, src_mb, index);
+
+       s5p_mfc_set_enc_frame_buffer(ctx, src_mb, raw->num_planes);
+
+       dst_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, MFC_BUF_SET_USED);
+       if (!dst_mb) {
+               mfc_debug(2, "no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       /* encoder dst buffer CFW PROT */
+       if (ctx->is_drm) {
+               i = dst_mb->vb.vb2_buf.index;
+               s5p_mfc_stream_protect(ctx, dst_mb, i);
+       }
+       mfc_debug(2, "nal start : src index from src_buf_queue:%d\n",
+               src_mb->vb.vb2_buf.index);
+       mfc_debug(2, "nal start : dst index from dst_buf_queue:%d\n",
+               dst_mb->vb.vb2_buf.index);
+
+       s5p_mfc_set_enc_stream_buffer(ctx, dst_mb);
+
+       if (call_cop(ctx, set_buf_ctrls_val, ctx, &ctx->src_ctrls[index]) < 0)
+               mfc_err_ctx("failed in set_buf_ctrls_val\n");
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_encode_one_frame(ctx, last_frame);
+
+       return 0;
+}
+
+int s5p_mfc_run_enc_last_frames(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       struct s5p_mfc_raw_info *raw;
+
+       raw = &ctx->raw_buf;
+
+       /* Get the next source buffer */
+       src_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED);
+       if (!src_mb) {
+               mfc_debug(2, "no src buffers.\n");
+               return -EAGAIN;
+       }
+
+       dst_mb = s5p_mfc_get_buf(&ctx->buf_queue_lock, &ctx->dst_buf_queue, MFC_BUF_SET_USED);
+       if (!dst_mb) {
+               mfc_debug(2, "no dst buffers.\n");
+               return -EAGAIN;
+       }
+
+       mfc_debug(2, "Set address zero for all planes\n");
+       s5p_mfc_set_enc_frame_buffer(ctx, 0, raw->num_planes);
+
+       /* encoder dst buffer CFW PROT */
+       if (ctx->is_drm) {
+               int index = dst_mb->vb.vb2_buf.index;
+
+               s5p_mfc_stream_protect(ctx, dst_mb, index);
+       }
+
+       s5p_mfc_set_enc_stream_buffer(ctx, dst_mb);
+
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_encode_one_frame(ctx, 1);
+
+       return 0;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_opr.h b/drivers/media/platform/exynos/mfc/s5p_mfc_opr.h
new file mode 100644 (file)
index 0000000..76c6e35
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_opr.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 __S5P_MFC_OPR_H
+#define __S5P_MFC_OPR_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_run_dec_init(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_run_dec_last_frames(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_run_enc_init(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_run_enc_last_frames(struct s5p_mfc_ctx *ctx);
+
+#endif /* __S5P_MFC_OPR_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_otf.c b/drivers/media/platform/exynos/mfc/s5p_mfc_otf.c
new file mode 100644 (file)
index 0000000..1660122
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_otf.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.
+ */
+
+#ifdef CONFIG_VIDEO_EXYNOS_REPEATER
+#include <media/exynos_repeater.h>
+#endif
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+#include <media/exynos_tsmux.h>
+#endif
+#include <media/s5p_mfc_hwfc.h>
+
+#include "s5p_mfc_otf.h"
+#include "s5p_mfc_hwfc_internal.h"
+
+#include "s5p_mfc_watchdog.h"
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_inst.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_qos.h"
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_buf.h"
+#include "s5p_mfc_mem.h"
+
+static struct s5p_mfc_fmt *mfc_enc_hwfc_find_format(unsigned int format, unsigned int t)
+{
+       unsigned long i;
+
+       mfc_debug_enter();
+
+       for (i = 0; i < NUM_FORMATS; i++) {
+               if (enc_hwfc_formats[i].fourcc == format &&
+                               enc_hwfc_formats[i].type == t)
+                       return (struct s5p_mfc_fmt *)&enc_hwfc_formats[i];
+       }
+
+       mfc_debug_leave();
+
+       return NULL;
+}
+
+static int mfc_otf_set_buf_info(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_info *buf_info = &handle->otf_buf_info;
+
+       mfc_debug_enter();
+
+       ctx->src_fmt = mfc_enc_hwfc_find_format(buf_info->pixel_format, MFC_FMT_RAW);
+       if (!ctx->src_fmt) {
+               mfc_err_ctx("OTF: failed to set source format\n");
+               return -EINVAL;
+       }
+
+       /* set source information */
+       ctx->raw_buf.num_planes = ctx->src_fmt->num_planes;
+       ctx->img_width = buf_info->width;
+       ctx->img_height = buf_info->height;
+       ctx->buf_stride = ALIGN(ctx->img_width, 16);
+
+       /* calculate source size */
+       s5p_mfc_enc_calc_src_size(ctx);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static int mfc_otf_map_buf(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_addr *buf_addr = &handle->otf_buf_addr;
+       struct _otf_buf_info *buf_info = &handle->otf_buf_info;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       int i;
+
+       mfc_debug_enter();
+
+       mfc_debug(2, "OTF: buffer count: %d\n", buf_info->buffer_count);
+       /* map buffers */
+       for (i = 0; i < buf_info->buffer_count; i++) {
+               mfc_debug(2, "OTF: dma_buf: %p\n", buf_info->bufs[i]);
+               buf_addr->otf_buf_attach[i] = dma_buf_attach(buf_info->bufs[i], dev->device);
+               if (IS_ERR(buf_addr->otf_buf_attach[i])) {
+                       mfc_err_ctx("OTF: Failed to get attachment (err %ld)",
+                               PTR_ERR(buf_addr->otf_buf_attach[i]));
+                       buf_addr->otf_buf_attach[i] = 0;
+                       return -EINVAL;
+               }
+               buf_addr->otf_daddr[i][0] = ion_iovmm_map(buf_addr->otf_buf_attach[i], 0,
+                               raw->total_plane_size, DMA_BIDIRECTIONAL, 0);
+               if (IS_ERR_VALUE(buf_addr->otf_daddr[i][0])) {
+                       mfc_err_ctx("OTF: Failed to get daddr (0x%08llx)",
+                                       buf_addr->otf_daddr[i][0]);
+                       buf_addr->otf_daddr[i][0] = 0;
+                       return -EINVAL;
+               }
+               if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12N) {
+                       buf_addr->otf_daddr[i][1] = NV12N_CBCR_BASE(buf_addr->otf_daddr[i][0],
+                                       ctx->img_width, ctx->img_height);
+               } else {
+                       mfc_err_ctx("OTF: not supported format(0x%x)\n", ctx->src_fmt->fourcc);
+                       return -EINVAL;
+               }
+               mfc_debug(2, "OTF: index: %d, addr[0]: 0x%08llx, addr[1]: 0x%08llx\n",
+                               i, buf_addr->otf_daddr[i][0], buf_addr->otf_daddr[i][1]);
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void mfc_otf_unmap_buf(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_addr *buf_addr = &handle->otf_buf_addr;
+       struct _otf_buf_info *buf_info = &handle->otf_buf_info;
+       int i;
+
+       mfc_debug_enter();
+
+       for (i = 0; i < buf_info->buffer_count; i++) {
+               if (buf_addr->otf_daddr[i][0]) {
+                       ion_iovmm_unmap(buf_addr->otf_buf_attach[i], buf_addr->otf_daddr[i][0]);
+                       buf_addr->otf_daddr[i][0] = 0;
+               }
+               if (buf_addr->otf_buf_attach[i]) {
+                       dma_buf_detach(buf_info->bufs[i], buf_addr->otf_buf_attach[i]);
+                       buf_addr->otf_buf_attach[i] = 0;
+               }
+       }
+
+       mfc_debug_leave();
+}
+
+static void mfc_otf_put_buf(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_info *buf_info = &handle->otf_buf_info;
+       int i;
+
+       mfc_debug_enter();
+
+       for (i = 0; i < buf_info->buffer_count; i++) {
+               if (buf_info->bufs[i]) {
+                       dma_buf_put(buf_info->bufs[i]);
+                       buf_info->bufs[i] = NULL;
+               }
+       }
+
+       mfc_debug_leave();
+
+}
+
+static int mfc_otf_init_hwfc_buf(struct s5p_mfc_ctx *ctx)
+{
+#ifdef CONFIG_VIDEO_EXYNOS_REPEATER
+       struct shared_buffer_info *shared_buf_info;
+#endif
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_info *buf_info = &handle->otf_buf_info;
+
+       mfc_debug_enter();
+
+#ifdef CONFIG_VIDEO_EXYNOS_REPEATER
+       shared_buf_info = (struct shared_buffer_info *)buf_info;
+       /* request buffers */
+       if (hwfc_request_buffer(shared_buf_info, 1)) {
+               mfc_err_dev("OTF: request_buffer failed\n");
+               return -EINVAL;
+       }
+#endif
+       mfc_debug(2, "OTF: recieved buffer information\n");
+       mfc_debug(2, "OTF: w(%d), h(%d), format(0x%x), bufcnt(%d)\n",
+                       buf_info->width, buf_info->height,
+                       buf_info->pixel_format, buf_info->buffer_count);
+
+       /* set buffer information to ctx, and calculate buffer size */
+       if (mfc_otf_set_buf_info(ctx)) {
+               mfc_err_ctx("OTF: failed to set buffer information\n");
+               mfc_otf_put_buf(ctx);
+               return -EINVAL;
+       }
+
+       if (mfc_otf_map_buf(ctx)) {
+               mfc_err_ctx("OTF: failed to map buffers\n");
+               mfc_otf_unmap_buf(ctx);
+               mfc_otf_put_buf(ctx);
+               return -EINVAL;
+       }
+       mfc_debug(2, "OTF: HWFC buffer initialized\n");
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void mfc_otf_deinit_hwfc_buf(struct s5p_mfc_ctx *ctx)
+{
+       mfc_debug_enter();
+
+       mfc_otf_unmap_buf(ctx);
+       mfc_otf_put_buf(ctx);
+       mfc_debug(2, "OTF: HWFC buffer de-initialized\n");
+
+       mfc_debug_leave();
+}
+
+static int mfc_otf_create_handle(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *otf_handle;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("OTF: no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       ctx->otf_handle = kzalloc(sizeof(*otf_handle), GFP_KERNEL);
+       if (!ctx->otf_handle) {
+               mfc_err_dev("OTF: no otf_handle\n");
+               return -EINVAL;
+       }
+       mfc_debug(2, "OTF: otf_handle created\n");
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void mfc_otf_destroy_handle(struct s5p_mfc_ctx *ctx)
+{
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("OTF: no mfc context to run\n");
+               return;
+       }
+
+       kfree(ctx->otf_handle);
+       ctx->otf_handle = NULL;
+       mfc_debug(2, "OTF: otf_handle destroyed\n");
+
+       mfc_debug_leave();
+}
+
+int s5p_mfc_otf_init(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       int i;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("OTF: no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("OTF: no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               if (dev->ctx[i] && dev->ctx[i]->otf_handle) {
+                       mfc_err_dev("OTF: otf_handle is already created, ctx: %d\n", i);
+                       return -EINVAL;
+               }
+       }
+
+       if (mfc_otf_create_handle(ctx)) {
+               mfc_err_dev("OTF: otf_handle is not created\n");
+               return -EINVAL;
+       }
+
+       if (mfc_otf_init_hwfc_buf(ctx)) {
+               mfc_err_dev("OTF: HWFC init failed\n");
+               mfc_otf_destroy_handle(ctx);
+               return -EINVAL;
+       }
+
+       if (otf_dump) {
+               /* It is for debugging. Do not return error */
+               if (s5p_mfc_otf_alloc_stream_buf(ctx)) {
+                       mfc_err_dev("OTF: stream buffer allocation failed\n");
+                       s5p_mfc_otf_release_stream_buf(ctx);
+               }
+       }
+
+       mfc_debug(2, "OTF: otf_init is completed\n");
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void s5p_mfc_otf_deinit(struct s5p_mfc_ctx *ctx)
+{
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("OTF: no mfc context to run\n");
+               return;
+       }
+
+       s5p_mfc_otf_release_stream_buf(ctx);
+       mfc_otf_deinit_hwfc_buf(ctx);
+       mfc_otf_destroy_handle(ctx);
+       s5p_mfc_qos_off(ctx);
+       mfc_debug(2, "OTF: deinit_otf is completed\n");
+
+       mfc_debug_leave();
+}
+
+int s5p_mfc_otf_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle;
+
+       mfc_debug_enter();
+
+       if (!ctx->otf_handle)
+               return 0;
+
+       handle = ctx->otf_handle;
+
+       mfc_debug(1, "OTF: [c:%d] state = %d, otf_work_bit = %d\n",
+                       ctx->num, ctx->state, handle->otf_work_bit);
+       /* If shutdown is called, do not try any cmd */
+       if (dev->shutdown)
+               return 0;
+
+       /* Context is to parse header */
+       if (ctx->state == MFCINST_GOT_INST)
+               return 1;
+
+       /* Context is to set buffers */
+       if (ctx->state == MFCINST_HEAD_PARSED)
+               return 1;
+
+       if (ctx->state == MFCINST_RUNNING && handle->otf_work_bit)
+               return 1;
+       mfc_debug(2, "OTF: ctx is not ready.\n");
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+int s5p_mfc_otf_run_enc_init(struct s5p_mfc_ctx *ctx)
+{
+       int ret;
+
+       mfc_debug_enter();
+
+       s5p_mfc_set_enc_stride(ctx);
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       ret = s5p_mfc_init_encode(ctx);
+
+       mfc_debug_leave();
+
+       return ret;
+}
+
+int s5p_mfc_otf_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct s5p_mfc_raw_info *raw;
+
+       mfc_debug_enter();
+
+       raw = &ctx->raw_buf;
+
+       if (!handle) {
+               mfc_err_ctx("OTF: There is no otf_handle, handle: %p\n", handle);
+               return -EINVAL;
+       }
+
+       if (!handle->otf_work_bit) {
+               mfc_err_ctx("OTF: Can't run OTF encoder, otf_work_bit: %d\n",
+                               handle->otf_work_bit);
+               return -EINVAL;
+       }
+
+       s5p_mfc_otf_set_frame_addr(ctx, raw->num_planes);
+       s5p_mfc_otf_set_stream_size(ctx, raw->total_plane_size);
+       s5p_mfc_otf_set_hwfc_index(ctx, handle->otf_job_id);
+
+       if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC, handle->otf_buf_index) < 0)
+               mfc_err_ctx("failed in init_buf_ctrls\n");
+       if (call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[handle->otf_buf_index]) < 0)
+               mfc_err_ctx("failed in to_buf_ctrls\n");
+       if (call_cop(ctx, set_buf_ctrls_val, ctx, &ctx->src_ctrls[handle->otf_buf_index]) < 0)
+               mfc_err_ctx("OTF: failed in set_buf_ctrls_val\n");
+
+       /* Change timestamp usec -> nsec */
+       s5p_mfc_qos_update_last_framerate(ctx, handle->otf_time_stamp * 1000);
+       s5p_mfc_qos_update_framerate(ctx);
+
+       /* Set stream buffer size to handle buffer full */
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_encode_one_frame(ctx, 0);
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+int s5p_mfc_otf_handle_seq(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+
+       mfc_debug_enter();
+
+       enc->header_size = s5p_mfc_get_enc_strm_size();
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       if (enc->header_size)
+               set_es_size(enc->header_size);
+#endif
+
+       ctx->dpb_count = s5p_mfc_get_enc_dpb_count();
+       ctx->scratch_buf_size = s5p_mfc_get_enc_scratch_size();
+       mfc_debug(2, "OTF: header size: %d, cpb_count: %d, scratch size: %zu\n",
+                       enc->header_size, ctx->dpb_count, ctx->scratch_buf_size);
+
+       s5p_mfc_change_state(ctx, MFCINST_HEAD_PARSED);
+
+       if (s5p_mfc_alloc_codec_buffers(ctx)) {
+               mfc_err_ctx("OTF: Failed to allocate encoding buffers.\n");
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+int s5p_mfc_otf_handle_stream(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_debug *debug = &handle->otf_debug;
+       struct s5p_mfc_special_buf *buf;
+       struct _otf_buf_addr *buf_addr = &handle->otf_buf_addr;
+       struct s5p_mfc_raw_info *raw;
+       dma_addr_t enc_addr[3] = { 0, 0, 0 };
+       int slice_type, i;
+       unsigned int strm_size;
+       unsigned int pic_count;
+       int enc_ret = HWFC_ERR_NONE;
+       unsigned int print_size;
+
+       mfc_debug_enter();
+
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       mfc_encoding_end();
+#endif
+
+       slice_type = s5p_mfc_get_enc_slice_type();
+       pic_count = s5p_mfc_get_enc_pic_count();
+       strm_size = s5p_mfc_get_enc_strm_size();
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       if (strm_size)
+               set_es_size(strm_size);
+#endif
+
+       mfc_debug(2, "OTF: encoded slice type: %d\n", slice_type);
+       mfc_debug(2, "OTF: encoded stream size: %d\n", strm_size);
+       mfc_debug(2, "OTF: display order: %d\n", pic_count);
+
+       /* set encoded frame type */
+       enc->frame_type = slice_type;
+       raw = &ctx->raw_buf;
+
+       if (strm_size > 0) {
+               s5p_mfc_get_enc_frame_buffer(ctx, &enc_addr[0], raw->num_planes);
+
+               for (i = 0; i < raw->num_planes; i++)
+                       mfc_debug(2, "OTF: encoded[%d] addr: 0x%08llx\n",
+                                               i, enc_addr[i]);
+               if (enc_addr[0] !=  buf_addr->otf_daddr[handle->otf_buf_index][0]) {
+                       mfc_err_ctx("OTF: address is not matched. 0x%08llx != 0x%08llx\n",
+                                       enc_addr[0], buf_addr->otf_daddr[handle->otf_buf_index][0]);
+                       enc_ret = -HWFC_ERR_MFC;
+               }
+       } else {
+               mfc_err_ctx("OTF: stream size is zero\n");
+               enc_ret = -HWFC_ERR_MFC;
+       }
+
+       if (otf_dump && !ctx->is_drm) {
+               buf = &debug->stream_buf[debug->frame_cnt];
+               debug->stream_size[debug->frame_cnt] = strm_size;
+               debug->frame_cnt++;
+               if (debug->frame_cnt >= OTF_MAX_BUF)
+                       debug->frame_cnt = 0;
+               /* print stream dump */
+               print_size = (strm_size * 2) + 64;
+               if (print_size > 512 && otf_dump == 1)
+                       print_size = 512;
+
+               if (buf->vaddr)
+                       print_hex_dump(KERN_ERR, "OTF dump: ",
+                                       DUMP_PREFIX_ADDRESS, print_size, 0,
+                                       buf->vaddr, print_size, false);
+       }
+
+       if (call_cop(ctx, recover_buf_ctrls_val, ctx,
+                               &ctx->src_ctrls[handle->otf_buf_index]) < 0)
+               mfc_err_ctx("OTF: failed in recover_buf_ctrls_val\n");
+       if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                               MFC_CTRL_TYPE_SRC, handle->otf_buf_index) < 0)
+               mfc_err_ctx("OTF: failed in cleanup_buf_ctrls\n");
+
+       handle->otf_work_bit = 0;
+       handle->otf_buf_index = 0;
+       handle->otf_job_id = 0;
+
+#ifdef CONFIG_VIDEO_EXYNOS_REPEATER
+       hwfc_encoding_done(enc_ret);
+#endif
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void s5p_mfc_otf_handle_error(struct s5p_mfc_ctx *ctx,
+               unsigned int reason, unsigned int err)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       int enc_ret = -HWFC_ERR_MFC;
+
+       mfc_debug_enter();
+
+       mfc_err_ctx("OTF: Interrupt Error: display: %d, decoded: %d\n",
+                       s5p_mfc_get_warn(err), s5p_mfc_get_err(err));
+       err = s5p_mfc_get_err(err);
+
+       /* Error recovery is dependent on the state of context */
+       switch (ctx->state) {
+       case MFCINST_GOT_INST:
+       case MFCINST_INIT:
+       case MFCINST_RETURN_INST:
+       case MFCINST_HEAD_PARSED:
+               mfc_err_ctx("OTF: error happened during init/de-init\n");
+               break;
+       case MFCINST_RUNNING:
+               if (err == S5P_FIMV_ERR_MFC_TIMEOUT) {
+                       mfc_err_ctx("OTF: MFC TIMEOUT. go to error state\n");
+                       s5p_mfc_change_state(ctx, MFCINST_ERROR);
+                       enc_ret = -HWFC_ERR_MFC_TIMEOUT;
+               } else if (err == S5P_FIMV_ERR_TS_MUX_TIMEOUT ||
+                               err == S5P_FIMV_ERR_G2D_TIMEOUT) {
+                       mfc_err_ctx("OTF: TS-MUX or G2D TIMEOUT. skip this frame\n");
+                       enc_ret = -HWFC_ERR_MFC_TIMEOUT;
+               } else {
+                       mfc_err_ctx("OTF: MFC ERROR. skip this frame\n");
+                       enc_ret = -HWFC_ERR_MFC;
+               }
+
+               handle->otf_work_bit = 0;
+               handle->otf_buf_index = 0;
+               handle->otf_job_id = 0;
+
+               if (call_cop(ctx, recover_buf_ctrls_val, ctx,
+                                       &ctx->src_ctrls[handle->otf_buf_index]) < 0)
+                       mfc_err_ctx("OTF: failed in recover_buf_ctrls_val\n");
+               if (call_cop(ctx, cleanup_buf_ctrls, ctx,
+                                       MFC_CTRL_TYPE_SRC, handle->otf_buf_index) < 0)
+                       mfc_err_ctx("OTF: failed in cleanup_buf_ctrls\n");
+
+#ifdef CONFIG_VIDEO_EXYNOS_REPEATER
+               hwfc_encoding_done(enc_ret);
+#endif
+               break;
+       default:
+               mfc_err_ctx("Encountered an error interrupt which had not been handled.\n");
+               mfc_err_ctx("ctx->state = %d, ctx->inst_no = %d\n",
+                                               ctx->state, ctx->inst_no);
+               break;
+       }
+
+       s5p_mfc_wake_up_dev(dev, reason, err);
+
+       mfc_debug_leave();
+}
+
+int mfc_hwfc_check_run(struct s5p_mfc_ctx *ctx)
+{
+       struct _otf_handle *handle = ctx->otf_handle;
+
+       mfc_debug_enter();
+
+       if (!handle) {
+               mfc_err_ctx("OTF: there is no handle for OTF\n");
+               return -EINVAL;
+       }
+       if (handle->otf_work_bit) {
+               mfc_err_ctx("OTF: OTF is already working\n");
+               return -EINVAL;
+       }
+       if (ctx->state != MFCINST_RUNNING) {
+               mfc_err_ctx("OTF: mfc is not running state\n");
+               return -EINVAL;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+int s5p_mfc_hwfc_encode(int buf_index, int job_id, struct encoding_param *param)
+{
+       struct s5p_mfc_dev *dev = g_mfc_dev;
+       struct _otf_handle *handle;
+       struct s5p_mfc_ctx *ctx = NULL;
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       struct packetizing_param packet_param;
+#endif
+       int i;
+
+       mfc_debug_enter();
+
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       mfc_encoding_start(buf_index);
+#endif
+
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               if (dev->ctx[i] && dev->ctx[i]->otf_handle) {
+                       ctx = dev->ctx[i];
+                       break;
+               }
+       }
+
+       if (!ctx) {
+               mfc_err_dev("OTF: there is no context to run\n");
+               return -HWFC_ERR_MFC_NOT_PREPARED;
+       }
+
+       if (mfc_hwfc_check_run(ctx)) {
+               mfc_err_dev("OTF: mfc is not prepared\n");
+               return -HWFC_ERR_MFC_NOT_PREPARED;
+       }
+
+#ifdef CONFIG_VIDEO_EXYNOS_TSMUX
+       packet_param.time_stamp = param->time_stamp;
+       mfc_debug(2, "OTF: timestamp: %llu\n", param->time_stamp);
+       if (packetize(&packet_param)) {
+               mfc_err_dev("OTF: packetize failed\n");
+               return -HWFC_ERR_TSMUX;
+       }
+#endif
+
+       handle = ctx->otf_handle;
+       handle->otf_work_bit = 1;
+       handle->otf_buf_index = buf_index;
+       handle->otf_job_id = job_id;
+       handle->otf_time_stamp = param->time_stamp;
+
+       if (s5p_mfc_otf_ctx_ready(ctx))
+               s5p_mfc_set_bit(ctx->num, &dev->work_bits);
+       if (s5p_mfc_is_work_to_do(dev))
+               queue_work(dev->butler_wq, &dev->butler_work);
+
+       mfc_debug_leave();
+
+       return HWFC_ERR_NONE;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_otf.h b/drivers/media/platform/exynos/mfc/s5p_mfc_otf.h
new file mode 100644 (file)
index 0000000..1167b23
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_otf.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 __S5P_MFC_OTF_H
+#define __S5P_MFC_OTF_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+extern struct s5p_mfc_dev *g_mfc_dev;
+
+int s5p_mfc_otf_init(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_otf_deinit(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_otf_ctx_ready(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_otf_run_enc_init(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_otf_run_enc_frame(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_otf_handle_seq(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_otf_handle_stream(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_otf_handle_error(struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err);
+
+#endif /* __S5P_MFC_OTF_H  */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_pm.c b/drivers/media/platform/exynos/mfc/s5p_mfc_pm.c
new file mode 100644 (file)
index 0000000..2937f67
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_pm.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/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/smc.h>
+
+#include "s5p_mfc_pm.h"
+
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+void s5p_mfc_pm_init(struct s5p_mfc_dev *dev)
+{
+       spin_lock_init(&dev->pm.clklock);
+       atomic_set(&dev->pm.pwr_ref, 0);
+       atomic_set(&dev->clk_ref, 0);
+
+       dev->pm.device = dev->device;
+       dev->pm.clock_on_steps = 0;
+       dev->pm.clock_off_steps = 0;
+       pm_runtime_enable(dev->pm.device);
+}
+
+void s5p_mfc_pm_final(struct s5p_mfc_dev *dev)
+{
+       pm_runtime_disable(dev->pm.device);
+}
+
+int s5p_mfc_pm_clock_on(struct s5p_mfc_dev *dev)
+{
+       int ret = 0;
+       int state;
+       unsigned long flags;
+
+       dev->pm.clock_on_steps = 1;
+       state = atomic_read(&dev->clk_ref);
+
+       MFC_TRACE_DEV("** clock_on start: ref state(%d)\n", state);
+       ret = clk_enable(dev->pm.clock);
+       if (ret < 0) {
+               mfc_err_dev("clk_enable failed (%d)\n", ret);
+               return ret;
+       }
+       dev->pm.clock_on_steps |= 0x1 << 1;
+
+       if (dev->pm.base_type != MFCBUF_INVALID)
+               s5p_mfc_set_risc_base_addr(dev, dev->pm.base_type);
+
+       dev->pm.clock_on_steps |= 0x1 << 2;
+       if (dev->curr_ctx_is_drm) {
+               spin_lock_irqsave(&dev->pm.clklock, flags);
+               mfc_debug(3, "Begin: enable protection\n");
+               ret = exynos_smc(SMC_PROTECTION_SET, 0,
+                                       dev->id, SMC_PROTECTION_ENABLE);
+               dev->pm.clock_on_steps |= 0x1 << 3;
+               if (ret != DRMDRV_OK) {
+                       mfc_err_dev("Protection Enable failed! ret(%u)\n", ret);
+                       spin_unlock_irqrestore(&dev->pm.clklock, flags);
+                       clk_disable(dev->pm.clock);
+                       return -EACCES;
+               }
+               mfc_debug(3, "End: enable protection\n");
+               spin_unlock_irqrestore(&dev->pm.clklock, flags);
+       }
+
+       dev->pm.clock_on_steps |= 0x1 << 4;
+       atomic_inc_return(&dev->clk_ref);
+
+       dev->pm.clock_on_steps |= 0x1 << 6;
+       state = atomic_read(&dev->clk_ref);
+       mfc_debug(2, "+ %d\n", state);
+       MFC_TRACE_DEV("** clock_on end: ref state(%d)\n", state);
+
+       return 0;
+}
+
+/* Use only in functions that first instance is guaranteed, like mfc_init_hw() */
+int s5p_mfc_pm_clock_on_with_base(struct s5p_mfc_dev *dev,
+                               enum mfc_buf_usage_type buf_type)
+{
+       int ret;
+       dev->pm.base_type = buf_type;
+       ret = s5p_mfc_pm_clock_on(dev);
+       dev->pm.base_type = MFCBUF_INVALID;
+
+       return ret;
+}
+
+void s5p_mfc_pm_clock_off(struct s5p_mfc_dev *dev)
+{
+       int state;
+       unsigned long flags;
+       int ret = 0;
+
+       dev->pm.clock_off_steps = 1;
+       atomic_dec_return(&dev->clk_ref);
+
+       dev->pm.clock_off_steps |= 0x1 << 1;
+       state = atomic_read(&dev->clk_ref);
+       MFC_TRACE_DEV("** clock_off start: ref state(%d)\n", state);
+       if (state < 0) {
+               mfc_err_dev("Clock state is wrong(%d)\n", state);
+               atomic_set(&dev->clk_ref, 0);
+               dev->pm.clock_off_steps |= 0x1 << 2;
+       } else {
+               if (dev->curr_ctx_is_drm) {
+                       mfc_debug(3, "Begin: disable protection\n");
+                       spin_lock_irqsave(&dev->pm.clklock, flags);
+                       dev->pm.clock_off_steps |= 0x1 << 3;
+                       ret = exynos_smc(SMC_PROTECTION_SET, 0,
+                                       dev->id, SMC_PROTECTION_DISABLE);
+                       if (ret != DRMDRV_OK) {
+                               mfc_err_dev("Protection Disable failed! ret(%u)\n", ret);
+                               spin_unlock_irqrestore(&dev->pm.clklock, flags);
+                               clk_disable(dev->pm.clock);
+                               return;
+                       }
+                       mfc_debug(3, "End: disable protection\n");
+                       dev->pm.clock_off_steps |= 0x1 << 4;
+                       spin_unlock_irqrestore(&dev->pm.clklock, flags);
+               }
+               dev->pm.clock_off_steps |= 0x1 << 5;
+               clk_disable(dev->pm.clock);
+       }
+
+       dev->pm.clock_off_steps |= 0x1 << 6;
+       state = atomic_read(&dev->clk_ref);
+       mfc_debug(2, "- %d\n", state);
+       MFC_TRACE_DEV("** clock_off end: ref state(%d)\n", state);
+}
+
+int s5p_mfc_pm_power_on(struct s5p_mfc_dev *dev)
+{
+       int ret;
+
+       MFC_TRACE_DEV("++ Power on\n");
+       ret = pm_runtime_get_sync(dev->pm.device);
+       if (ret < 0) {
+               mfc_err_dev("Failed to get power: ret(%d)\n", ret);
+               goto err_power_on;
+       }
+
+       dev->pm.clock = clk_get(dev->device, "aclk_mfc");
+       if (IS_ERR(dev->pm.clock)) {
+               mfc_err_dev("failed to get parent clock: ret(%d)\n", ret);
+               ret = -ENOENT;
+               goto err_clk_get;
+       }
+
+       ret = clk_prepare(dev->pm.clock);
+       if (ret) {
+               mfc_err_dev("clk_prepare() failed: ret(%d)\n", ret);
+               goto err_clk_prepare;
+       }
+
+       atomic_inc(&dev->pm.pwr_ref);
+
+       MFC_TRACE_DEV("-- Power on: ret(%d)\n", ret);
+
+       return 0;
+
+err_clk_prepare:
+       clk_put(dev->pm.clock);
+
+err_clk_get:
+       pm_runtime_put_sync(dev->pm.device);
+
+err_power_on:
+       return ret;
+}
+
+int s5p_mfc_pm_power_off(struct s5p_mfc_dev *dev)
+{
+       int ret;
+
+       MFC_TRACE_DEV("++ Power off\n");
+
+       clk_unprepare(dev->pm.clock);
+       clk_put(dev->pm.clock);
+
+       ret = pm_runtime_put_sync(dev->pm.device);
+       if (ret < 0) {
+               mfc_err_dev("Failed to put power: ret(%d)\n", ret);
+               return ret;
+       }
+
+       atomic_dec(&dev->pm.pwr_ref);
+
+       MFC_TRACE_DEV("-- Power off: ret(%d)\n", ret);
+
+       return ret;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_pm.h b/drivers/media/platform/exynos/mfc/s5p_mfc_pm.h
new file mode 100644 (file)
index 0000000..d185b57
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_pm.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 __S5P_MFC_PM_H
+#define __S5P_MFC_PM_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+static inline int s5p_mfc_pm_get_pwr_ref_cnt(struct s5p_mfc_dev *dev)
+{
+       return atomic_read(&dev->pm.pwr_ref);
+}
+
+static inline int s5p_mfc_pm_get_clk_ref_cnt(struct s5p_mfc_dev *dev)
+{
+       return atomic_read(&dev->clk_ref);
+}
+
+void s5p_mfc_pm_init(struct s5p_mfc_dev *dev);
+void s5p_mfc_pm_final(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_pm_clock_on(struct s5p_mfc_dev *dev);
+int s5p_mfc_pm_clock_on_with_base(struct s5p_mfc_dev *dev,
+                       enum mfc_buf_usage_type buf_type);
+void s5p_mfc_pm_clock_off(struct s5p_mfc_dev *dev);
+int s5p_mfc_pm_power_on(struct s5p_mfc_dev *dev);
+int s5p_mfc_pm_power_off(struct s5p_mfc_dev *dev);
+
+#endif /* __S5P_MFC_PM_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_qos.c b/drivers/media/platform/exynos/mfc/s5p_mfc_qos.c
new file mode 100644 (file)
index 0000000..c92fe7b
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_qos.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/err.h>
+#include <soc/samsung/bts.h>
+
+#include "s5p_mfc_qos.h"
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+enum {
+       MFC_QOS_ADD,
+       MFC_QOS_UPDATE,
+       MFC_QOS_REMOVE,
+       MFC_QOS_BW,
+};
+
+static void mfc_qos_operate(struct s5p_mfc_ctx *ctx, int opr_type, int idx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_platdata *pdata = dev->pdata;
+       struct s5p_mfc_qos *qos_table = pdata->qos_table;
+
+       switch (opr_type) {
+       case MFC_QOS_ADD:
+               MFC_TRACE_CTX("++ QOS add[%d] (int:%d, mif:%d)\n",
+                       idx, qos_table[idx].freq_int, qos_table[idx].freq_mif);
+
+               pm_qos_add_request(&dev->qos_req_int,
+                               PM_QOS_DEVICE_THROUGHPUT,
+                               qos_table[idx].freq_int);
+               pm_qos_add_request(&dev->qos_req_mif,
+                               PM_QOS_BUS_THROUGHPUT,
+                               qos_table[idx].freq_mif);
+
+#ifdef CONFIG_ARM_EXYNOS_MP_CPUFREQ
+               pm_qos_add_request(&dev->qos_req_cluster1,
+                               PM_QOS_CLUSTER1_FREQ_MIN,
+                               qos_table[idx].freq_cpu);
+               pm_qos_add_request(&dev->qos_req_cluster0,
+                               PM_QOS_CLUSTER0_FREQ_MIN,
+                               qos_table[idx].freq_kfc);
+#endif
+
+               bts_update_scen(BS_MFC_UHD_ENC60, qos_table[idx].mo_uhd_enc60_value);
+               bts_update_scen(BS_MFC_UHD_10BIT, qos_table[idx].mo_10bit_value);
+               bts_update_scen(BS_MFC_UHD, qos_table[idx].mo_value);
+
+               atomic_set(&dev->qos_req_cur, idx + 1);
+               MFC_TRACE_CTX("-- QOS add[%d] (int:%d, mif:%d, mo:%d, mo_10bit:%d, mo_uhd_enc:%d)\n",
+                               idx, qos_table[idx].freq_int, qos_table[idx].freq_mif,
+                               qos_table[idx].mo_value, qos_table[idx].mo_10bit_value,
+                               qos_table[idx].mo_uhd_enc60_value);
+               mfc_debug(2, "QOS add[%d] (int:%d, mif:%d, mo:%d, mo_10bit:%d, mo_uhd_enc:%d)\n",
+                               idx, qos_table[idx].freq_int, qos_table[idx].freq_mif,
+                               qos_table[idx].mo_value, qos_table[idx].mo_10bit_value,
+                               qos_table[idx].mo_uhd_enc60_value);
+               break;
+       case MFC_QOS_UPDATE:
+               MFC_TRACE_CTX("++ QOS update[%d] (int:%d, mif:%d)\n",
+                               idx, qos_table[idx].freq_int, qos_table[idx].freq_mif);
+
+               pm_qos_update_request(&dev->qos_req_int,
+                               qos_table[idx].freq_int);
+               pm_qos_update_request(&dev->qos_req_mif,
+                               qos_table[idx].freq_mif);
+
+#ifdef CONFIG_ARM_EXYNOS_MP_CPUFREQ
+               pm_qos_update_request(&dev->qos_req_cluster1,
+                               qos_table[idx].freq_cpu);
+               pm_qos_update_request(&dev->qos_req_cluster0,
+                               qos_table[idx].freq_kfc);
+#endif
+
+               bts_update_scen(BS_MFC_UHD_ENC60, qos_table[idx].mo_uhd_enc60_value);
+               bts_update_scen(BS_MFC_UHD_10BIT, qos_table[idx].mo_10bit_value);
+               bts_update_scen(BS_MFC_UHD, qos_table[idx].mo_value);
+
+               atomic_set(&dev->qos_req_cur, idx + 1);
+               MFC_TRACE_CTX("-- QOS update[%d] (int:%d, mif:%d, mo:%d, mo_10bit:%d, mo_uhd_enc:%d)\n",
+                               idx, qos_table[idx].freq_int, qos_table[idx].freq_mif,
+                               qos_table[idx].mo_value, qos_table[idx].mo_10bit_value,
+                               qos_table[idx].mo_uhd_enc60_value);
+               mfc_debug(2, "QOS update[%d] (int:%d, mif:%d, mo:%d, mo_10bit:%d, mo_uhd_enc:%d)\n",
+                               idx, qos_table[idx].freq_int, qos_table[idx].freq_mif,
+                               qos_table[idx].mo_value, qos_table[idx].mo_10bit_value,
+                               qos_table[idx].mo_uhd_enc60_value);
+               break;
+       case MFC_QOS_REMOVE:
+               MFC_TRACE_CTX("++ QOS remove\n");
+
+               pm_qos_remove_request(&dev->qos_req_int);
+               pm_qos_remove_request(&dev->qos_req_mif);
+
+#ifdef CONFIG_ARM_EXYNOS_MP_CPUFREQ
+               pm_qos_remove_request(&dev->qos_req_cluster1);
+               pm_qos_remove_request(&dev->qos_req_cluster0);
+#endif
+
+               bts_update_scen(BS_MFC_UHD_ENC60, 0);
+               bts_update_scen(BS_MFC_UHD_10BIT, 0);
+               bts_update_scen(BS_MFC_UHD, 0);
+
+               dev->mfc_bw.peak = 0;
+               dev->mfc_bw.read = 0;
+               dev->mfc_bw.write = 0;
+               bts_update_bw(BTS_BW_MFC, dev->mfc_bw);
+
+               atomic_set(&dev->qos_req_cur, 0);
+               MFC_TRACE_CTX("-- QOS remove\n");
+               mfc_debug(2, "QoS remove\n");
+               break;
+       case MFC_QOS_BW:
+               MFC_TRACE_CTX("++ QOS BW (peak: %d, read: %d, write: %d)\n",
+                               dev->mfc_bw.peak, dev->mfc_bw.read, dev->mfc_bw.write);
+
+               bts_update_bw(BTS_BW_MFC, dev->mfc_bw);
+
+               MFC_TRACE_CTX("-- QOS BW (peak: %d, read: %d, write: %d)\n",
+                               dev->mfc_bw.peak, dev->mfc_bw.read, dev->mfc_bw.write);
+               mfc_debug(2, "QoS BW, (peak: %d, read: %d, write: %d)\n",
+                               dev->mfc_bw.peak, dev->mfc_bw.read, dev->mfc_bw.write);
+               break;
+       default:
+               mfc_err_ctx("Unknown request for opr [%d]\n", opr_type);
+               break;
+       }
+}
+
+static void mfc_qos_set(struct s5p_mfc_ctx *ctx, struct bts_bw *mfc_bw, int i)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_platdata *pdata = dev->pdata;
+       struct s5p_mfc_qos *qos_table = pdata->qos_table;
+
+       mfc_debug(2, "QoS table[%d] covered mb %d ~ %d (int:%d, mif:%d)\n",
+                       i, qos_table[i].threshold_mb,
+                       i == pdata->num_qos_steps - 1 ?
+                       pdata->max_mb : qos_table[i + 1].threshold_mb,
+                       qos_table[i].freq_int, qos_table[i].freq_mif);
+
+       if (mfc_bw->peak != dev->mfc_bw.peak) {
+               dev->mfc_bw.peak = mfc_bw->peak;
+               dev->mfc_bw.read = mfc_bw->read;
+               dev->mfc_bw.write = mfc_bw->write;
+               mfc_qos_operate(ctx, MFC_QOS_BW, i);
+       }
+
+       if (atomic_read(&dev->qos_req_cur) == 0)
+               mfc_qos_operate(ctx, MFC_QOS_ADD, i);
+       else if (atomic_read(&dev->qos_req_cur) != (i + 1))
+               mfc_qos_operate(ctx, MFC_QOS_UPDATE, i);
+}
+
+static inline unsigned long mfc_qos_get_weighted_mb(struct s5p_mfc_ctx *ctx,
+                                               unsigned long mb)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_enc_params *p;
+       u32 num_planes = ctx->dst_fmt->num_planes;
+       int weight = 1000;
+       unsigned long weighted_mb;
+
+       switch (ctx->codec_mode) {
+       case S5P_FIMV_CODEC_H264_DEC:
+       case S5P_FIMV_CODEC_H264_MVC_DEC:
+       case S5P_FIMV_CODEC_H264_ENC:
+       case S5P_FIMV_CODEC_H264_MVC_ENC:
+       case S5P_FIMV_CODEC_VP8_DEC:
+       case S5P_FIMV_CODEC_VP8_ENC:
+               if (num_planes == 3)
+                       weight = (weight * 100) / MFC_QOS_WEIGHT_3PLANE;
+               break;
+
+       case S5P_FIMV_CODEC_HEVC_DEC:
+       case S5P_FIMV_CODEC_HEVC_ENC:
+       case S5P_FIMV_CODEC_VP9_DEC:
+       case S5P_FIMV_CODEC_VP9_ENC:
+       case S5P_FIMV_CODEC_BPG_DEC:
+       case S5P_FIMV_CODEC_BPG_ENC:
+               if (num_planes == 3) {
+                       weight = (weight * 100) / MFC_QOS_WEIGHT_3PLANE;
+               } else {
+                       if (ctx->is_10bit)
+                               weight = (weight * 100) / MFC_QOS_WEIGHT_10BIT;
+                       else if (ctx->is_422format)
+                               weight = (weight * 100) / MFC_QOS_WEIGHT_422_10INTRA;
+               }
+               break;
+
+       case S5P_FIMV_CODEC_MPEG4_DEC:
+       case S5P_FIMV_CODEC_FIMV1_DEC:
+       case S5P_FIMV_CODEC_FIMV2_DEC:
+       case S5P_FIMV_CODEC_FIMV3_DEC:
+       case S5P_FIMV_CODEC_FIMV4_DEC:
+       case S5P_FIMV_CODEC_H263_DEC:
+       case S5P_FIMV_CODEC_VC1_RCV_DEC:
+       case S5P_FIMV_CODEC_VC1_DEC:
+       case S5P_FIMV_CODEC_MPEG2_DEC:
+       case S5P_FIMV_CODEC_MPEG4_ENC:
+       case S5P_FIMV_CODEC_H263_ENC:
+               weight = (weight * 100) / MFC_QOS_WEIGHT_OTHER_CODEC;
+               break;
+
+       default:
+               mfc_err_ctx("wrong codec_mode (%d), no weight\n", ctx->codec_mode);
+       }
+
+       if (enc) {
+               p = &enc->params;
+               if ((IS_H264_ENC(ctx) || IS_HEVC_ENC(ctx)) && p->num_b_frame) {
+                       weight = (weight * 100) / MFC_QOS_WEIGHT_BFRAME;
+                       mfc_debug(3, "QoS weight: B frame encoding\n");
+               }
+               if ((IS_H264_ENC(ctx) || IS_HEVC_ENC(ctx) || IS_VP8_ENC(ctx) ||
+                                       IS_VP9_ENC(ctx)) && (p->num_refs_for_p >= 2)) {
+                       weight = (weight * 100) / MFC_QOS_WEIGHT_NUM_OF_REF;
+                       mfc_debug(3, "QoS weight: num of ref >= 2\n");
+               }
+       }
+       if (dec) {
+               if (dec->num_of_tile_over_4) {
+                       weight = (weight * 100) / MFC_QOS_WEIGHT_NUM_OF_TILE;
+                       mfc_debug(3, "QoS weight: num of tile >= 4\n");
+               }
+       }
+
+       weighted_mb = (mb * weight) / 1000;
+       mfc_debug(3, "QoS weight: %d.%03d, codec: %d, num planes: %d, "
+                       "10bit: %d, 422format: %d (mb: %ld)\n",
+                       weight / 1000, weight % 1000, ctx->codec_mode,
+                       num_planes, ctx->is_10bit, ctx->is_422format,
+                       weighted_mb);
+
+
+       return weighted_mb;
+}
+
+static inline unsigned long mfc_qos_get_mb_per_second(struct s5p_mfc_ctx *ctx)
+{
+       unsigned long mb_width, mb_height, fps, mb;
+
+       mb_width = (ctx->img_width + 15) / 16;
+       mb_height = (ctx->img_height + 15) / 16;
+       fps = ctx->framerate / 1000;
+
+       mb = mb_width * mb_height * fps;
+       mfc_debug(4, "QoS ctx[%d:%s] %d x %d @ %ld fps (mb: %ld)\n",
+                       ctx->num, ctx->type == MFCINST_ENCODER ? "ENC" : "DEC",
+                       ctx->img_width, ctx->img_height, fps, mb);
+
+       return mfc_qos_get_weighted_mb(ctx, mb);
+}
+
+static struct s5p_mfc_qos_bw mfc_bw_info = {
+       /*                                peak   read   write   (KB/UHD frame) */
+       .h264_dec_uhd_bw        =       { 38131, 40206, 24870 },
+       .hevc_dec_uhd_bw        =       { 35055, 33741, 20511 },
+       .hevc_dec_uhd_10bit_bw  =       { 38643, 36428, 25491 },
+       .vp8_dec_uhd_bw         =       { 28693, 30464, 22331 },
+       .vp9_dec_uhd_bw         =       { 21464, 22160, 19747 },
+       .mpeg4_dec_uhd_bw       =       { 31567, 25191, 15961 },
+       .h264_enc_uhd_bw        =       { 62543, 75230, 13080 },
+       .hevc_enc_uhd_bw        =       { 54863, 65417, 11422 },
+       .hevc_enc_uhd_10bit_bw  =       { 68011, 79367, 14688 },
+       .vp8_enc_uhd_bw         =       { 63970, 67281, 22508 },
+       .vp9_enc_uhd_bw         =       { 84443, 71588, 19337 },
+       .mpeg4_enc_uhd_bw       =       { 44633, 55310, 9599  },
+};
+
+static void mfc_qos_get_bw_per_second(struct s5p_mfc_ctx *ctx, struct bts_bw *mfc_bw)
+{
+       struct mfc_qos_bw_data bw_data;
+       unsigned long mb_width, mb_height, fps, mb;
+       unsigned long peak_bw_per_sec;
+       unsigned long read_bw_per_sec;
+       unsigned long write_bw_per_sec;
+       unsigned long mb_count_per_uhd_frame = MB_COUNT_PER_UHD_FRAME;
+       unsigned long max_fps_per_uhd_frame = MAX_FPS_PER_UHD_FRAME;
+
+       mb_width = (ctx->img_width + 15) / 16;
+       mb_height = (ctx->img_height + 15) / 16;
+       fps = ctx->framerate / 1000;
+
+       mb = mb_width * mb_height * fps;
+
+       switch (ctx->codec_mode) {
+       case S5P_FIMV_CODEC_H264_DEC:
+       case S5P_FIMV_CODEC_H264_MVC_DEC:
+               bw_data = mfc_bw_info.h264_dec_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_H264_ENC:
+       case S5P_FIMV_CODEC_H264_MVC_ENC:
+               bw_data = mfc_bw_info.h264_enc_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_HEVC_DEC:
+       case S5P_FIMV_CODEC_BPG_DEC:
+               if (ctx->is_10bit)
+                       bw_data = mfc_bw_info.hevc_dec_uhd_10bit_bw;
+               else
+                       bw_data = mfc_bw_info.hevc_dec_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_HEVC_ENC:
+       case S5P_FIMV_CODEC_BPG_ENC:
+               if (ctx->is_10bit)
+                       bw_data = mfc_bw_info.hevc_enc_uhd_10bit_bw;
+               else
+                       bw_data = mfc_bw_info.hevc_enc_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_MPEG4_DEC:
+       case S5P_FIMV_CODEC_FIMV1_DEC:
+       case S5P_FIMV_CODEC_FIMV2_DEC:
+       case S5P_FIMV_CODEC_FIMV3_DEC:
+       case S5P_FIMV_CODEC_FIMV4_DEC:
+       case S5P_FIMV_CODEC_H263_DEC:
+       case S5P_FIMV_CODEC_VC1_RCV_DEC:
+       case S5P_FIMV_CODEC_VC1_DEC:
+       case S5P_FIMV_CODEC_MPEG2_DEC:
+               bw_data = mfc_bw_info.mpeg4_dec_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_VP8_DEC:
+               bw_data = mfc_bw_info.vp8_dec_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_VP9_DEC:
+               bw_data = mfc_bw_info.vp9_dec_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_MPEG4_ENC:
+       case S5P_FIMV_CODEC_H263_ENC:
+               bw_data = mfc_bw_info.mpeg4_enc_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_VP8_ENC:
+               bw_data = mfc_bw_info.vp8_enc_uhd_bw;
+               break;
+       case S5P_FIMV_CODEC_VP9_ENC:
+               bw_data = mfc_bw_info.vp9_enc_uhd_bw;
+               break;
+       default:
+               bw_data.peak = 0;
+               bw_data.read = 0;
+               bw_data.write = 0;
+               mfc_err_ctx("wrong codec_mode (%d)\n", ctx->codec_mode);
+       }
+
+       if (mb > (mb_count_per_uhd_frame * max_fps_per_uhd_frame)) {
+               mfc_debug(2, "fix upper mb bound (mb: %ld, fps: %ld)\n", mb, fps);
+               mb = mb_count_per_uhd_frame * max_fps_per_uhd_frame;
+       }
+
+       peak_bw_per_sec = (bw_data.peak * mb) / mb_count_per_uhd_frame;
+       read_bw_per_sec = (bw_data.read * mb) / mb_count_per_uhd_frame;
+       write_bw_per_sec = (bw_data.write * mb) / mb_count_per_uhd_frame;
+
+       if (peak_bw_per_sec == 0) {
+               mfc_debug(2, "fix lower peak bound (mb: %ld, fps: %ld)\n", mb, fps);
+               peak_bw_per_sec = MIN_BW_PER_SEC;
+       }
+       if (read_bw_per_sec == 0) {
+               mfc_debug(2, "fix lower read bound (mb: %ld, fps: %ld)\n", mb, fps);
+               read_bw_per_sec = MIN_BW_PER_SEC;
+       }
+       if (write_bw_per_sec == 0) {
+               mfc_debug(2, "fix lower write bound (mb: %ld, fps: %ld)\n", mb, fps);
+               write_bw_per_sec = MIN_BW_PER_SEC;
+       }
+
+       mfc_bw->peak = (unsigned int)peak_bw_per_sec;
+       mfc_bw->read = (unsigned int)read_bw_per_sec;
+       mfc_bw->write = (unsigned int)write_bw_per_sec;
+}
+
+void s5p_mfc_qos_on(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_platdata *pdata = dev->pdata;
+       struct s5p_mfc_qos *qos_table = pdata->qos_table;
+       struct s5p_mfc_ctx *qos_ctx;
+       struct bts_bw mfc_bw, mfc_bw_ctx;
+       unsigned long hw_mb = 0, total_mb = 0;
+       unsigned int fw_time, sw_time, total_fps = 0;
+       int i, found = 0, enc_found = 0;
+       int start_qos_step;
+
+       list_for_each_entry(qos_ctx, &dev->qos_queue, qos_list)
+               if (qos_ctx == ctx)
+                       found = 1;
+
+       if (!found)
+               list_add_tail(&ctx->qos_list, &dev->qos_queue);
+
+       mfc_bw.peak = 0;
+       mfc_bw.read = 0;
+       mfc_bw.write = 0;
+       /* get the hw macroblock */
+       list_for_each_entry(qos_ctx, &dev->qos_queue, qos_list) {
+               if (OVER_UHD_ENC60(qos_ctx))
+                       enc_found = 1;
+               hw_mb += mfc_qos_get_mb_per_second(qos_ctx);
+               mfc_qos_get_bw_per_second(qos_ctx, &mfc_bw_ctx);
+               mfc_bw.peak += mfc_bw_ctx.peak;
+               mfc_bw.read += mfc_bw_ctx.read;
+               mfc_bw.write += mfc_bw_ctx.write;
+               total_fps += (qos_ctx->framerate / 1000);
+       }
+
+       start_qos_step = pdata->num_qos_steps;
+       if (enc_found)
+               start_qos_step = pdata->max_qos_steps;
+
+       /* search the suitable qos table */
+       for (i = start_qos_step - 1; i >= 0; i--) {
+               fw_time = qos_table[i].time_fw;
+               sw_time = (MFC_DRV_TIME + fw_time);
+
+               total_mb = ((1000000 * hw_mb) / (1000000 - (total_fps * sw_time)));
+               mfc_debug(4, "QoS table[%d] fw_time: %dus, hw_mb: %ld, "
+                               "sw_time: %d, total_fps: %d, total_mb: %ld\n",
+                               i, fw_time, hw_mb, sw_time, total_fps, total_mb);
+
+               if ((total_mb > qos_table[i].threshold_mb) || (i == 0))
+                       break;
+       }
+
+       if (total_mb > pdata->max_mb)
+               mfc_debug(4, "QoS overspec mb %ld > %d\n", total_mb, pdata->max_mb);
+
+       mfc_qos_set(ctx, &mfc_bw, i);
+}
+
+void s5p_mfc_qos_off(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_platdata *pdata = dev->pdata;
+       struct s5p_mfc_qos *qos_table = pdata->qos_table;
+       struct s5p_mfc_ctx *qos_ctx;
+       struct bts_bw mfc_bw, mfc_bw_ctx;
+       unsigned long hw_mb = 0, total_mb = 0;
+       unsigned int fw_time, sw_time, total_fps = 0;
+       int i, found = 0, enc_found = 0;
+       int start_qos_step;
+
+       if (list_empty(&dev->qos_queue)) {
+               if (atomic_read(&dev->qos_req_cur) != 0) {
+                       mfc_err_ctx("MFC request count is wrong!\n");
+                       mfc_qos_operate(ctx, MFC_QOS_REMOVE, 0);
+               }
+               return;
+       }
+
+       mfc_bw.peak = 0;
+       mfc_bw.read = 0;
+       mfc_bw.write = 0;
+       /* get the hw macroblock */
+       list_for_each_entry(qos_ctx, &dev->qos_queue, qos_list) {
+               if (qos_ctx == ctx) {
+                       found = 1;
+                       continue;
+               }
+
+               if (OVER_UHD_ENC60(qos_ctx))
+                       enc_found = 1;
+               hw_mb += mfc_qos_get_mb_per_second(qos_ctx);
+               mfc_qos_get_bw_per_second(qos_ctx, &mfc_bw_ctx);
+               mfc_bw.peak += mfc_bw_ctx.peak;
+               mfc_bw.read += mfc_bw_ctx.read;
+               mfc_bw.write += mfc_bw_ctx.write;
+               total_fps += (qos_ctx->framerate / 1000);
+       }
+
+       start_qos_step = pdata->num_qos_steps;
+       if (enc_found)
+               start_qos_step = pdata->max_qos_steps;
+
+       /* search the suitable qos table */
+       for (i = start_qos_step - 1; i >= 0; i--) {
+               fw_time = qos_table[i].time_fw;
+               sw_time = (MFC_DRV_TIME + fw_time);
+
+               total_mb = ((1000000 * hw_mb) / (1000000 - (total_fps * sw_time)));
+               mfc_debug(4, "QoS table[%d] fw_time: %dus, hw_mb: %ld, "
+                               "sw_time: %d, total_fps: %d, total_mb: %ld\n",
+                               i, fw_time, hw_mb, sw_time, total_fps, total_mb);
+
+               if ((total_mb > qos_table[i].threshold_mb) || (total_mb == 0) || (i == 0))
+                       break;
+       }
+
+       if (total_mb > pdata->max_mb)
+               mfc_debug(4, "QoS overspec mb %ld > %d\n", total_mb, pdata->max_mb);
+
+       if (found)
+               list_del(&ctx->qos_list);
+
+       if (list_empty(&dev->qos_queue) || total_mb == 0)
+               mfc_qos_operate(ctx, MFC_QOS_REMOVE, 0);
+       else
+               mfc_qos_set(ctx, &mfc_bw, i);
+}
+#endif
+
+#define COL_FRAME_RATE         0
+#define COL_FRAME_INTERVAL     1
+
+#define MFC_MAX_INTERVAL       (2 * USEC_PER_SEC)
+
+/*
+ * A framerate table determines framerate by the interval(us) of each frame.
+ * Framerate is not accurate, just rough value to seperate overload section.
+ * Base line of each section are selected from middle value.
+ * 40fps(25000us), 80fps(12500us), 144fps(6940us)
+ * 205fps(4860us), 320fps(3125us)
+ *
+ * interval(us) | 0         3125          4860          6940          12500         25000          |
+ * framerate    |    480fps   |    240fps   |    180fps   |    120fps   |    60fps    |    30fps   |
+ */
+static unsigned long framerate_table[][2] = {
+       {  30000, 25000 },
+       {  60000, 12500 },
+       { 120000,  6940 },
+       { 180000,  4860 },
+       { 240000,  3125 },
+       { 480000,     0 },
+};
+
+static inline unsigned long mfc_qos_timeval_diff(struct timeval *to,
+                                       struct timeval *from)
+{
+       return (to->tv_sec * USEC_PER_SEC + to->tv_usec)
+               - (from->tv_sec * USEC_PER_SEC + from->tv_usec);
+}
+
+static unsigned long mfc_qos_get_framerate_by_interval(int interval)
+{
+       unsigned long i;
+
+       /* if the interval is too big (2sec), framerate set to 0 */
+       if (interval > MFC_MAX_INTERVAL)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(framerate_table); i++) {
+               if (interval > framerate_table[i][COL_FRAME_INTERVAL])
+                       return framerate_table[i][COL_FRAME_RATE];
+       }
+
+       return 0;
+}
+
+/* Return the minimum interval between previous and next entry */
+static int mfc_qos_get_interval(struct list_head *head, struct list_head *entry)
+{
+       int prev_interval = MFC_MAX_INTERVAL, next_interval = MFC_MAX_INTERVAL;
+       struct mfc_timestamp *prev_ts, *next_ts, *curr_ts;
+
+       curr_ts = list_entry(entry, struct mfc_timestamp, list);
+
+       if (entry->prev != head) {
+               prev_ts = list_entry(entry->prev, struct mfc_timestamp, list);
+               prev_interval = mfc_qos_timeval_diff(&curr_ts->timestamp, &prev_ts->timestamp);
+       }
+
+       if (entry->next != head) {
+               next_ts = list_entry(entry->next, struct mfc_timestamp, list);
+               next_interval = mfc_qos_timeval_diff(&next_ts->timestamp, &curr_ts->timestamp);
+       }
+
+       return (prev_interval < next_interval ? prev_interval : next_interval);
+}
+
+static int mfc_qos_dec_add_timestamp(struct s5p_mfc_ctx *ctx,
+                       struct timeval *time, struct list_head *head)
+{
+       int replace_entry = 0;
+       struct mfc_timestamp *curr_ts = &ctx->ts_array[ctx->ts_count];
+
+       if (ctx->ts_is_full) {
+               /* Replace the entry if list of array[ts_count] is same as entry */
+               if (&curr_ts->list == head)
+                       replace_entry = 1;
+               else
+                       list_del(&curr_ts->list);
+       }
+
+       memcpy(&curr_ts->timestamp, time, sizeof(struct timeval));
+       if (!replace_entry)
+               list_add(&curr_ts->list, head);
+       curr_ts->interval =
+               mfc_qos_get_interval(&ctx->ts_list, &curr_ts->list);
+       curr_ts->index = ctx->ts_count;
+       ctx->ts_count++;
+
+       if (ctx->ts_count == MFC_TIME_INDEX) {
+               ctx->ts_is_full = 1;
+               ctx->ts_count %= MFC_TIME_INDEX;
+       }
+
+       return 0;
+}
+
+static unsigned long mfc_qos_get_fps_by_timestamp(struct s5p_mfc_ctx *ctx, struct timeval *time)
+{
+       struct mfc_timestamp *temp_ts;
+       int found;
+       int index = 0;
+       int min_interval = MFC_MAX_INTERVAL;
+       int time_diff;
+       unsigned long max_framerate;
+
+       if (debug_ts == 1) {
+               /* Debug info */
+               mfc_info_ctx("======================================\n");
+               mfc_info_ctx("New timestamp = %ld.%06ld, count = %d \n",
+                       time->tv_sec, time->tv_usec, ctx->ts_count);
+       }
+
+       if (list_empty(&ctx->ts_list)) {
+               mfc_qos_dec_add_timestamp(ctx, time, &ctx->ts_list);
+               return mfc_qos_get_framerate_by_interval(0);
+       } else {
+               found = 0;
+               list_for_each_entry_reverse(temp_ts, &ctx->ts_list, list) {
+                       time_diff = timeval_compare(time, &temp_ts->timestamp);
+                       if (time_diff == 0) {
+                               /* Do not add if same timestamp already exists */
+                               found = 1;
+                               break;
+                       } else if (time_diff > 0) {
+                               /* Add this after temp_ts */
+                               mfc_qos_dec_add_timestamp(ctx, time, &temp_ts->list);
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)     /* Add this at first entry */
+                       mfc_qos_dec_add_timestamp(ctx, time, &ctx->ts_list);
+       }
+
+       list_for_each_entry(temp_ts, &ctx->ts_list, list) {
+               if (temp_ts->interval < min_interval)
+                       min_interval = temp_ts->interval;
+       }
+
+       max_framerate = mfc_qos_get_framerate_by_interval(min_interval);
+
+       if (debug_ts == 1) {
+               /* Debug info */
+               index = 0;
+               list_for_each_entry(temp_ts, &ctx->ts_list, list) {
+                       mfc_info_ctx("[%d] timestamp [i:%d]: %ld.%06ld\n",
+                                       index, temp_ts->index,
+                                       temp_ts->timestamp.tv_sec,
+                                       temp_ts->timestamp.tv_usec);
+                       index++;
+               }
+               mfc_info_ctx("Min interval = %d, It is %ld fps\n",
+                               min_interval, max_framerate);
+       } else if (debug_ts == 2) {
+               mfc_info_ctx("Min interval = %d, It is %ld fps\n",
+                               min_interval, max_framerate);
+       }
+
+       if (!ctx->ts_is_full) {
+               if (debug_ts == 1)
+                       mfc_info_ctx("ts doesn't full, keep %ld fps\n", ctx->framerate);
+               return ctx->framerate;
+       }
+
+       return max_framerate;
+}
+
+void s5p_mfc_qos_update_framerate(struct s5p_mfc_ctx *ctx)
+{
+       if (ctx->last_framerate != 0 && ctx->last_framerate != ctx->framerate) {
+               mfc_debug(2, "fps changed: %ld -> %ld\n",
+                               ctx->framerate, ctx->last_framerate);
+               ctx->framerate = ctx->last_framerate;
+               s5p_mfc_qos_on(ctx);
+       }
+}
+
+void s5p_mfc_qos_update_last_framerate(struct s5p_mfc_ctx *ctx, u64 timestamp)
+{
+       struct timeval time;
+
+       time.tv_sec = timestamp / NSEC_PER_SEC;
+       time.tv_usec = (timestamp - (time.tv_sec * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+       ctx->last_framerate = mfc_qos_get_fps_by_timestamp(ctx, &time);
+       if (ctx->last_framerate > MFC_MAX_FPS)
+               ctx->last_framerate = MFC_MAX_FPS;
+       ctx->last_framerate = (ctx->qos_ratio * ctx->last_framerate) / 100;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_qos.h b/drivers/media/platform/exynos/mfc/s5p_mfc_qos.h
new file mode 100644 (file)
index 0000000..521af89
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_qos.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 __S5P_MFC_QOS_H
+#define __S5P_MFC_QOS_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+#define MFC_MAX_FPS            (480000)
+#define DEC_DEFAULT_FPS                (240000)
+#define ENC_DEFAULT_FPS                (240000)
+
+#define MB_COUNT_PER_UHD_FRAME 32400
+#define MAX_FPS_PER_UHD_FRAME  120
+#define MIN_BW_PER_SEC         1
+
+#define MFC_DRV_TIME           500
+
+#define MFC_QOS_WEIGHT_3PLANE          80
+#define MFC_QOS_WEIGHT_OTHER_CODEC     25
+#define MFC_QOS_WEIGHT_10BIT           75
+#define MFC_QOS_WEIGHT_422_10INTRA     70
+#define MFC_QOS_WEIGHT_BFRAME          50
+#define MFC_QOS_WEIGHT_NUM_OF_REF      50
+#define MFC_QOS_WEIGHT_NUM_OF_TILE     75
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+void s5p_mfc_qos_on(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_qos_off(struct s5p_mfc_ctx *ctx);
+#else
+#define s5p_mfc_qos_on(ctx)    do {} while (0)
+#define s5p_mfc_qos_off(ctx)   do {} while (0)
+#endif
+
+void s5p_mfc_qos_update_framerate(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_qos_update_last_framerate(struct s5p_mfc_ctx *ctx, u64 timestamp);
+
+static inline void s5p_mfc_qos_reset_framerate(struct s5p_mfc_ctx *ctx)
+{
+       if (ctx->type == MFCINST_DECODER)
+               ctx->framerate = DEC_DEFAULT_FPS;
+       else if (ctx->type == MFCINST_ENCODER)
+               ctx->framerate = ENC_DEFAULT_FPS;
+}
+
+static inline void s5p_mfc_qos_reset_last_framerate(struct s5p_mfc_ctx *ctx)
+{
+       ctx->last_framerate = 0;
+}
+
+static inline void s5p_mfc_qos_set_framerate(struct s5p_mfc_ctx *ctx, int rate)
+{
+       ctx->framerate = rate;
+}
+
+static inline int s5p_mfc_qos_get_framerate(struct s5p_mfc_ctx *ctx)
+{
+       return ctx->framerate;
+}
+
+#endif /* __S5P_MFC_QOS_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_queue.c b/drivers/media/platform/exynos/mfc/s5p_mfc_queue.c
new file mode 100644 (file)
index 0000000..a045496
--- /dev/null
@@ -0,0 +1,958 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_queue.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 "s5p_mfc_queue.h"
+
+#include "s5p_mfc_utils.h"
+#include "s5p_mfc_mem.h"
+
+void s5p_mfc_add_tail_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               struct s5p_mfc_buf *mfc_buf)
+{
+       unsigned long flags;
+
+       if (!mfc_buf) {
+               mfc_err_dev("mfc_buf is NULL!\n");
+               return;
+       }
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "queue address: %p\n", queue);
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+
+       mfc_buf->used = 0;
+       list_add_tail(&mfc_buf->list, &queue->head);
+       queue->count++;
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+int s5p_mfc_peek_buf_csd(spinlock_t *plock, struct s5p_mfc_buf_queue *queue)
+{
+       unsigned long flags;
+       int csd = -1;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&queue->head)) {
+               mfc_debug(2, "queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return csd;
+       }
+
+       mfc_buf = list_entry(queue->head.next, struct s5p_mfc_buf, list);
+
+       csd = mfc_buf->vb.reserved2 & FLAG_CSD ? 1 : 0;
+
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+       spin_unlock_irqrestore(plock, flags);
+       return csd;
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               enum s5p_mfc_queue_used_type used)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&queue->head)) {
+               mfc_debug(2, "queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(queue->head.next, struct s5p_mfc_buf, list);
+
+       if ((used == MFC_BUF_RESET_USED) || (used == MFC_BUF_SET_USED))
+               mfc_buf->used = used;
+
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+       spin_unlock_irqrestore(plock, flags);
+       return mfc_buf;
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_del_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               enum s5p_mfc_queue_used_type used)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&queue->head)) {
+               mfc_debug(2, "queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(queue->head.next, struct s5p_mfc_buf, list);
+
+       if ((used == MFC_BUF_RESET_USED) || (used == MFC_BUF_SET_USED))
+               mfc_buf->used = used;
+
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+       list_del(&mfc_buf->list);
+       queue->count--;
+
+       spin_unlock_irqrestore(plock, flags);
+       return mfc_buf;
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_del_if_consumed(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               unsigned long consumed, unsigned int min_bytes, int error, int *deleted)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       unsigned int remained;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&queue->head)) {
+               mfc_debug(2, "queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(queue->head.next, struct s5p_mfc_buf, list);
+
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+       remained = (unsigned int)(mfc_buf->vb.vb2_buf.planes[0].bytesused - consumed);
+
+       mfc_debug(2, "Packed PB test. Total Size: %d, consumed: %ld, remained: %d\n",
+               mfc_buf->vb.vb2_buf.planes[0].bytesused, consumed, remained);
+
+       if ((consumed > 0) && (remained > min_bytes) && (error == 0) &&
+                       (mfc_buf->vb.vb2_buf.planes[0].bytesused > consumed)){
+               /* do not delete from queue */
+               *deleted = 0;
+       } else {
+               list_del(&mfc_buf->list);
+               queue->count--;
+
+               *deleted = 1;
+       }
+
+       spin_unlock_irqrestore(plock, flags);
+       return mfc_buf;
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_move_buf(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               enum s5p_mfc_queue_used_type used, enum s5p_mfc_queue_top_type top)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&from_queue->head)) {
+               mfc_debug(2, "from_queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(from_queue->head.next, struct s5p_mfc_buf, list);
+
+       if ((used == MFC_BUF_RESET_USED) || (used == MFC_BUF_SET_USED))
+               mfc_buf->used = used;
+
+       mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+       list_del(&mfc_buf->list);
+       from_queue->count--;
+
+       if (top == MFC_QUEUE_ADD_TOP)
+               list_add(&mfc_buf->list, &to_queue->head);
+       else
+               list_add_tail(&mfc_buf->list, &to_queue->head);
+
+       to_queue->count++;
+
+       spin_unlock_irqrestore(plock, flags);
+       return mfc_buf;
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_move_buf_used(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&from_queue->head)) {
+               mfc_debug(2, "from_queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(from_queue->head.next, struct s5p_mfc_buf, list);
+
+       if (mfc_buf->used) {
+               mfc_debug(2, "mfc_buf: %p\n", mfc_buf);
+               mfc_debug(2, "First plane address: 0x%08llx\n",
+                       s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+               list_del(&mfc_buf->list);
+               from_queue->count--;
+
+               list_add_tail(&mfc_buf->list, &to_queue->head);
+               to_queue->count++;
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+struct s5p_mfc_buf *s5p_mfc_get_move_buf_addr(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&from_queue->head)) {
+               mfc_debug(2, "from_queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+
+       mfc_buf = list_entry(from_queue->head.next, struct s5p_mfc_buf, list);
+
+       if (s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0) == addr) {
+               mfc_debug(2, "First plane address: 0x%08llx\n",
+                               s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0));
+
+               list_del(&mfc_buf->list);
+               from_queue->count--;
+
+               list_add_tail(&mfc_buf->list, &to_queue->head);
+               to_queue->count++;
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+struct s5p_mfc_buf *s5p_mfc_find_buf_vb(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       dma_addr_t mb_addr;
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "Looking for this address: 0x%08llx\n", addr);
+       list_for_each_entry(mfc_buf, &queue->head, list) {
+               mb_addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+               mfc_debug(2, "plane[0] addr: 0x%08llx\n", mb_addr);
+
+               if (addr == mb_addr) {
+                       spin_unlock_irqrestore(plock, flags);
+                       return mfc_buf;
+               }
+       }
+
+       spin_unlock_irqrestore(plock, flags);
+       return NULL;
+}
+
+struct s5p_mfc_buf *s5p_mfc_find_del_buf_raw(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       dma_addr_t mb_addr;
+       int found = 0;
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "Looking for this address: 0x%08llx\n", addr);
+       list_for_each_entry(mfc_buf, &queue->head, list) {
+               mb_addr = mfc_buf->planes.raw[0];
+               mfc_debug(2, "plane[0] addr: 0x%08llx\n", mb_addr);
+
+               if (addr == mb_addr) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found == 1) {
+               list_del(&mfc_buf->list);
+               queue->count--;
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+struct s5p_mfc_buf *s5p_mfc_find_del_buf_vb(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       dma_addr_t mb_addr;
+       int found = 0;
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "Looking for this address: 0x%08llx\n", addr);
+       list_for_each_entry(mfc_buf, &queue->head, list) {
+               mb_addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+               mfc_debug(2, "plane[0] addr: 0x%08llx\n", mb_addr);
+
+               if (addr == mb_addr) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found == 1) {
+               list_del(&mfc_buf->list);
+               queue->count--;
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+struct s5p_mfc_buf *s5p_mfc_find_move_buf_vb(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr, unsigned int released_flag)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       dma_addr_t mb_addr;
+       int found = 0;
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "Looking for this address: 0x%08llx\n", addr);
+       list_for_each_entry(mfc_buf, &from_queue->head, list) {
+               mb_addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+               mfc_debug(2, "plane[0] addr: 0x%08llx\n", mb_addr);
+
+               if (addr == mb_addr) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found == 1) {
+               if (released_flag & (1 << mfc_buf->vb.vb2_buf.index)) {
+                       list_del(&mfc_buf->list);
+                       from_queue->count--;
+
+                       list_add_tail(&mfc_buf->list, &to_queue->head);
+                       to_queue->count++;
+               }
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+struct s5p_mfc_buf *s5p_mfc_find_move_buf_vb_used(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       dma_addr_t mb_addr;
+       int found = 0;
+
+       spin_lock_irqsave(plock, flags);
+
+       mfc_debug(2, "Looking for this address: 0x%08llx\n", addr);
+       list_for_each_entry(mfc_buf, &from_queue->head, list) {
+               mb_addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+               mfc_debug(2, "plane[0] addr: 0x%08llx, used: %d\n",
+                               mb_addr, mfc_buf->used);
+
+               if ((addr == mb_addr) && (mfc_buf->used == 1)) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found == 1) {
+               list_del(&mfc_buf->list);
+               from_queue->count--;
+
+               list_add_tail(&mfc_buf->list, &to_queue->head);
+               to_queue->count++;
+
+               spin_unlock_irqrestore(plock, flags);
+               return mfc_buf;
+       } else {
+               spin_unlock_irqrestore(plock, flags);
+               return NULL;
+       }
+}
+
+void s5p_mfc_move_first_buf_used(spinlock_t *plock, struct s5p_mfc_buf_queue *to_queue,
+               struct s5p_mfc_buf_queue *from_queue, enum s5p_mfc_queue_top_type top)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (list_empty(&from_queue->head)) {
+               mfc_err_dev("from_queue is empty\n");
+               spin_unlock_irqrestore(plock, flags);
+               return;
+       }
+       mfc_buf = list_entry(from_queue->head.next, struct s5p_mfc_buf, list);
+
+       if (mfc_buf->used) {
+               list_del(&mfc_buf->list);
+               from_queue->count--;
+
+               if (top == MFC_QUEUE_ADD_TOP)
+                       list_add(&mfc_buf->list, &to_queue->head);
+               else
+                       list_add_tail(&mfc_buf->list, &to_queue->head);
+
+               to_queue->count++;
+       }
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+void s5p_mfc_move_all_bufs(spinlock_t *plock, struct s5p_mfc_buf_queue *to_queue,
+               struct s5p_mfc_buf_queue *from_queue, enum s5p_mfc_queue_top_type top)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(plock, flags);
+
+       if (top == MFC_QUEUE_ADD_TOP) {
+               while (!list_empty(&from_queue->head)) {
+                       mfc_buf = list_entry(from_queue->head.prev, struct s5p_mfc_buf, list);
+
+                       list_del(&mfc_buf->list);
+                       from_queue->count--;
+
+                       list_add(&mfc_buf->list, &to_queue->head);
+                       to_queue->count++;
+               }
+       } else {
+               while (!list_empty(&from_queue->head)) {
+                       mfc_buf = list_entry(from_queue->head.next, struct s5p_mfc_buf, list);
+
+                       list_del(&mfc_buf->list);
+                       from_queue->count--;
+
+                       list_add_tail(&mfc_buf->list, &to_queue->head);
+                       to_queue->count++;
+               }
+       }
+
+       INIT_LIST_HEAD(&from_queue->head);
+       from_queue->count = 0;
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+void s5p_mfc_cleanup_queue(spinlock_t *plock, struct s5p_mfc_buf_queue *queue)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       int i;
+
+       spin_lock_irqsave(plock, flags);
+
+       while (!list_empty(&queue->head)) {
+               mfc_buf = list_entry(queue->head.next, struct s5p_mfc_buf, list);
+
+               for (i = 0; i < mfc_buf->vb.vb2_buf.num_planes; i++)
+                       vb2_set_plane_payload(&mfc_buf->vb.vb2_buf, i, 0);
+               vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+               list_del(&mfc_buf->list);
+               queue->count--;
+       }
+
+       INIT_LIST_HEAD(&queue->head);
+       queue->count = 0;
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+/*
+ * Check released buffers are enqueued again.
+ * s5p_mfc_dec.assigned_fd
+ * s5p_mfc_dec.available_dpb
+ */
+static void mfc_check_ref_frame(spinlock_t *plock, struct s5p_mfc_buf_queue *dst_queue,
+               struct s5p_mfc_buf_queue *ref_queue,
+               struct s5p_mfc_ctx *ctx, int ref_index)
+{
+       unsigned long flags;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *ref_mb, *tmp_mb;
+       int index;
+       int found = 0;
+
+       spin_lock_irqsave(plock, flags);
+
+       list_for_each_entry_safe(ref_mb, tmp_mb, &ref_queue->head, list) {
+               index = ref_mb->vb.vb2_buf.index;
+               if (index == ref_index) {
+                       list_del(&ref_mb->list);
+                       ref_queue->count--;
+
+                       list_add_tail(&ref_mb->list, &dst_queue->head);
+                       dst_queue->count++;
+
+                       dec->assigned_fd[index] =
+                                       ref_mb->vb.vb2_buf.planes[0].m.fd;
+                       clear_bit(index, &dec->available_dpb);
+                       mfc_debug(2, "Move buffer[%d], fd[%d] to dst queue\n",
+                                       index, dec->assigned_fd[index]);
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               list_for_each_entry_safe(ref_mb, tmp_mb, &dst_queue->head, list) {
+                       index = ref_mb->vb.vb2_buf.index;
+                       if (index == ref_index) {
+                               dec->assigned_fd[index] =
+                                       ref_mb->vb.vb2_buf.planes[0].m.fd;
+                               clear_bit(index, &dec->available_dpb);
+                               mfc_debug(2, "re-assigned buffer[%d], fd[%d]\n",
+                                               index, dec->assigned_fd[index]);
+                               break;
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+/* Process the released reference information */
+void s5p_mfc_handle_released_info(struct s5p_mfc_ctx *ctx,
+               unsigned int released_flag, int index)
+{
+       struct s5p_mfc_dec *dec;
+       struct dec_dpb_ref_info *refBuf;
+       int t, ncount = 0;
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no decoder context to run\n");
+               return;
+       }
+       refBuf = &dec->ref_info[index];
+
+       if (released_flag) {
+               for (t = 0; t < MFC_MAX_DPBS; t++) {
+                       if (released_flag & (1 << t)) {
+                               if (dec->err_reuse_flag & (1 << t)) {
+                                       mfc_debug(2, "Released, but reuse. FD[%d] = %03d\n",
+                                                       t, dec->assigned_fd[t]);
+                                       dec->err_reuse_flag &= ~(1 << t);
+                               } else {
+                                       mfc_debug(2, "Release FD[%d] = %03d\n",
+                                                       t, dec->assigned_fd[t]);
+                                       refBuf->dpb[ncount].fd[0] = dec->assigned_fd[t];
+                                       ncount++;
+                               }
+                               dec->assigned_fd[t] = MFC_INFO_INIT_FD;
+                               mfc_check_ref_frame(&ctx->buf_queue_lock, &ctx->dst_buf_queue,
+                                               &ctx->ref_buf_queue, ctx, t);
+                       }
+               }
+       }
+
+       if (ncount != MFC_MAX_DPBS)
+               refBuf->dpb[ncount].fd[0] = MFC_INFO_INIT_FD;
+}
+
+void s5p_mfc_move_reuse_buffer(struct s5p_mfc_ctx *ctx, int release_index)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf_queue *dst_queue = &ctx->dst_buf_queue;
+       struct s5p_mfc_buf_queue *ref_queue = &ctx->ref_buf_queue;
+       struct s5p_mfc_buf *ref_mb, *tmp_mb;
+       spinlock_t *plock = &ctx->buf_queue_lock;
+       unsigned long flags;
+       int index;
+
+       spin_lock_irqsave(plock, flags);
+
+       list_for_each_entry_safe(ref_mb, tmp_mb, &ref_queue->head, list) {
+               index = ref_mb->vb.vb2_buf.index;
+               if (index == release_index) {
+                       s5p_mfc_raw_unprotect(ctx, ref_mb, index);
+
+                       ref_mb->used = 0;
+
+                       list_del(&ref_mb->list);
+                       ref_queue->count--;
+
+                       list_add_tail(&ref_mb->list, &dst_queue->head);
+                       dst_queue->count++;
+
+                       clear_bit(index, &dec->available_dpb);
+                       mfc_debug(2, "buffer[%d] is moved to dst queue for reuse\n", index);
+               }
+       }
+
+       spin_unlock_irqrestore(plock, flags);
+}
+
+void s5p_mfc_cleanup_enc_src_queue(struct s5p_mfc_ctx *ctx)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *src_mb, *tmp_mb;
+       int i;
+
+       if (ctx->is_drm && ctx->raw_protect_flag) {
+               spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+               mfc_debug(2, "raw_protect_flag(%#lx) will be released\n",
+                               ctx->raw_protect_flag);
+
+               list_for_each_entry_safe(src_mb, tmp_mb, &ctx->src_buf_queue.head, list) {
+                       i = src_mb->vb.vb2_buf.index;
+
+                       s5p_mfc_raw_unprotect(ctx, src_mb, i);
+
+                       for (i = 0; i < src_mb->vb.vb2_buf.num_planes; i++)
+                               vb2_set_plane_payload(&src_mb->vb.vb2_buf, i, 0);
+
+                       vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+                       list_del(&src_mb->list);
+                       ctx->src_buf_queue.count--;
+               }
+
+               INIT_LIST_HEAD(&ctx->src_buf_queue.head);
+               ctx->src_buf_queue.count = 0;
+
+               spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+       } else {
+               s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_queue);
+       }
+}
+
+
+void s5p_mfc_cleanup_enc_dst_queue(struct s5p_mfc_ctx *ctx)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *dst_mb, *tmp_mb;
+       int i;
+
+       if (ctx->is_drm && ctx->stream_protect_flag) {
+               spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+               mfc_debug(2, "stream_protect_flag(%#lx) will be released\n",
+                               ctx->stream_protect_flag);
+
+               list_for_each_entry_safe(dst_mb, tmp_mb, &ctx->dst_buf_queue.head, list) {
+                       i = dst_mb->vb.vb2_buf.index;
+
+                       s5p_mfc_stream_unprotect(ctx, dst_mb, i);
+
+                       for (i = 0; i < dst_mb->vb.vb2_buf.num_planes; i++)
+                               vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0);
+                       vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+                       list_del(&dst_mb->list);
+                       ctx->dst_buf_queue.count--;
+               }
+
+               INIT_LIST_HEAD(&ctx->dst_buf_queue.head);
+               ctx->dst_buf_queue.count = 0;
+
+               spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+       } else {
+               s5p_mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue);
+       }
+}
+
+/* Check all buffers are referenced or not */
+struct s5p_mfc_buf *mfc_check_full_refered_dpb(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec = NULL;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       int sum_dpb;
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no decoder context to run\n");
+               return NULL;
+       }
+
+       sum_dpb = ctx->dst_buf_queue.count + ctx->ref_buf_queue.count;
+
+       if ((ctx->dst_buf_queue.count > 1) && (sum_dpb >= (ctx->dpb_count + 5))) {
+               if (list_empty(&ctx->dst_buf_queue.head)) {
+                       mfc_err_ctx("dst_buf_queue is empty\n");
+                       return NULL;
+               }
+               mfc_debug(2, "We should use this buffer.\n");
+               mfc_buf = list_entry(ctx->dst_buf_queue.head.next,
+                               struct s5p_mfc_buf, list);
+       } else if ((ctx->dst_buf_queue.count == 0)
+                       && ((ctx->ref_buf_queue.count) == (ctx->dpb_count + 5))) {
+               if (list_empty(&ctx->ref_buf_queue.head)) {
+                       mfc_err_ctx("ref_buf_queue is empty\n");
+                       return NULL;
+               }
+               mfc_debug(2, "All buffers are referenced.\n");
+               mfc_buf = list_entry(ctx->ref_buf_queue.head.next,
+                               struct s5p_mfc_buf, list);
+       } else {
+               mfc_debug(2, "waiting for dst buffer, ref = %d, dst = %d\n",
+                               ctx->ref_buf_queue.count, ctx->dst_buf_queue.count);
+               ctx->clear_work_bit = 1;
+       }
+
+       if (mfc_buf)
+               mfc_buf->used = 1;
+
+       return mfc_buf;
+}
+
+/* Try to search non-referenced DPB on dst-queue */
+struct s5p_mfc_buf *s5p_mfc_search_for_dpb(struct s5p_mfc_ctx *ctx, unsigned int dynamic_used)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+
+       spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+       mfc_debug(2, "Try to find non-referenced DPB. dynamic_used: 0x%x\n", dynamic_used);
+       list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) {
+               if ((dynamic_used & (1 << mfc_buf->vb.vb2_buf.index)) == 0) {
+                       mfc_buf->used = 1;
+                       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+                       return mfc_buf;
+               }
+       }
+
+       mfc_buf = mfc_check_full_refered_dpb(ctx);
+
+       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+
+       return mfc_buf;
+}
+
+struct s5p_mfc_buf *s5p_mfc_search_move_dpb_nal_q(struct s5p_mfc_ctx *ctx, unsigned int dynamic_used)
+{
+       unsigned long flags;
+       struct s5p_mfc_buf *mfc_buf = NULL;
+       struct s5p_mfc_dec *dec = NULL;
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return mfc_buf;
+       }
+
+       spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+       mfc_debug(2, "Try to find non-referenced DPB. dynamic_used: 0x%x\n", dynamic_used);
+       list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) {
+               if ((dynamic_used & (1 << mfc_buf->vb.vb2_buf.index)) == 0) {
+                       mfc_buf->used = 1;
+
+                       list_del(&mfc_buf->list);
+                       ctx->dst_buf_queue.count--;
+
+                       list_add_tail(&mfc_buf->list, &ctx->dst_buf_nal_queue.head);
+                       ctx->dst_buf_nal_queue.count++;
+
+                       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+                       return mfc_buf;
+               }
+       }
+
+       mfc_buf = mfc_check_full_refered_dpb(ctx);
+
+       if (mfc_buf) {
+               mfc_debug(2, "DPB is full. stop NAL-Q if started\n");
+               dec->is_dpb_full = 1;
+       }
+
+       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+
+       return mfc_buf;
+}
+
+/* Add dst buffer in dst_buf_queue */
+void s5p_mfc_store_dpb(struct s5p_mfc_ctx *ctx, struct vb2_buffer *vb)
+{
+       unsigned long flags;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *mfc_buf;
+       int index;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+       mfc_buf = vb_to_mfc_buf(vb);
+       mfc_buf->used = 0;
+       index = vb->index;
+
+       dec->assigned_fd[index] = vb->planes[0].m.fd;
+       mfc_debug(2, "Assigned FD[%d] = %d (%s)\n", index, dec->assigned_fd[index],
+                       (dec->dynamic_used & (1 << index) ? "used" : "non-used"));
+
+       list_add_tail(&mfc_buf->list, &ctx->dst_buf_queue.head);
+       ctx->dst_buf_queue.count++;
+
+       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+}
+
+void s5p_mfc_cleanup_nal_queue(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_buf *src_mb, *dst_mb;
+       unsigned long flags;
+       unsigned int index;
+
+       spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+       while (!list_empty(&ctx->src_buf_nal_queue.head)) {
+               src_mb = list_entry(ctx->src_buf_nal_queue.head.prev, struct s5p_mfc_buf, list);
+
+               index = src_mb->vb.vb2_buf.index;
+               call_cop(ctx, recover_buf_ctrls_nal_q, ctx, &ctx->src_ctrls[index]);
+
+               src_mb->used = 0;
+
+               list_del(&src_mb->list);
+               ctx->src_buf_nal_queue.count--;
+
+               list_add(&src_mb->list, &ctx->src_buf_queue.head);
+               ctx->src_buf_queue.count++;
+
+               mfc_debug(2, "NAL Q: cleanup, src_buf_nal_queue -> src_buf_queue, index:%d\n",
+                               src_mb->vb.vb2_buf.index);
+       }
+
+       while (!list_empty(&ctx->dst_buf_nal_queue.head)) {
+               dst_mb = list_entry(ctx->dst_buf_nal_queue.head.prev, struct s5p_mfc_buf, list);
+
+               dst_mb->used = 0;
+               if (ctx->type == MFCINST_DECODER)
+                       clear_bit(dst_mb->vb.vb2_buf.index, &dec->available_dpb);
+               list_del(&dst_mb->list);
+               ctx->dst_buf_nal_queue.count--;
+
+               list_add(&dst_mb->list, &ctx->dst_buf_queue.head);
+               ctx->dst_buf_queue.count++;
+
+               mfc_debug(2, "NAL Q: cleanup, dst_buf_nal_queue -> dst_buf_queue, index:%d\n",
+                               dst_mb->vb.vb2_buf.index);
+       }
+
+       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+}
+
+int s5p_mfc_is_last_frame(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_buf *src_mb;
+       struct s5p_mfc_dev *dev;
+       unsigned long flags;
+
+       mfc_debug_enter();
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ctx->buf_queue_lock, flags);
+
+       if (list_empty(&ctx->src_buf_queue.head)) {
+               mfc_debug(2, "src_buf_queue is empty\n");
+               spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+               return -EINVAL;
+       }
+
+       src_mb = list_entry(ctx->src_buf_queue.head.next, struct s5p_mfc_buf, list);
+
+       mfc_debug(2, "mfc_buf: %p\n", src_mb);
+       mfc_debug(2, "First plane address: 0x%08llx\n",
+               s5p_mfc_mem_get_daddr_vb(&src_mb->vb.vb2_buf, 0));
+
+       if (src_mb->vb.reserved2 & FLAG_LAST_FRAME) {
+               mfc_debug(2, "last frame!\n");
+               spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+               return 1;
+       }
+
+       mfc_debug(2, "not last frame!\n");
+       spin_unlock_irqrestore(&ctx->buf_queue_lock, flags);
+
+       mfc_debug_leave();
+
+       return 0;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_queue.h b/drivers/media/platform/exynos/mfc/s5p_mfc_queue.h
new file mode 100644 (file)
index 0000000..0c7fde4
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_queue.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 __S5P_MFC_QUEUE_H
+#define __S5P_MFC_QUEUE_H __FILE__
+
+#include  "s5p_mfc_common.h"
+
+/**
+ * enum s5p_mfc_queue_used_type
+ */
+enum s5p_mfc_queue_used_type {
+       MFC_BUF_NO_TOUCH_USED   = -1,
+       MFC_BUF_RESET_USED      = 0,
+       MFC_BUF_SET_USED        = 1,
+};
+
+/**
+ * enum s5p_mfc_queue_top_type
+ */
+enum s5p_mfc_queue_top_type {
+       MFC_QUEUE_ADD_BOTTOM    = 0,
+       MFC_QUEUE_ADD_TOP       = 1,
+};
+
+static inline unsigned int s5p_mfc_get_queue_count(spinlock_t *plock, struct s5p_mfc_buf_queue *queue)
+{
+       unsigned long flags;
+       unsigned int ret = 0;
+
+       spin_lock_irqsave(plock, flags);
+       ret = queue->count;
+       spin_unlock_irqrestore(plock, flags);
+
+       return ret;
+}
+
+static inline int s5p_mfc_is_queue_count_same(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               unsigned int value)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(plock, flags);
+       if (queue->count == value)
+               ret = 1;
+       spin_unlock_irqrestore(plock, flags);
+
+       return ret;
+}
+
+static inline int s5p_mfc_is_queue_count_greater(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               unsigned int value)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(plock, flags);
+       if (queue->count > value)
+               ret = 1;
+       spin_unlock_irqrestore(plock, flags);
+
+       return ret;
+}
+
+static inline int s5p_mfc_is_queue_count_smaller(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               unsigned int value)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(plock, flags);
+       if (queue->count < value)
+               ret = 1;
+       spin_unlock_irqrestore(plock, flags);
+
+       return ret;
+}
+
+static inline void s5p_mfc_init_queue(struct s5p_mfc_buf_queue *queue)
+{
+       INIT_LIST_HEAD(&queue->head);
+       queue->count = 0;
+}
+
+static inline void s5p_mfc_create_queue(struct s5p_mfc_buf_queue *queue)
+{
+       s5p_mfc_init_queue(queue);
+}
+
+static inline void s5p_mfc_delete_queue(struct s5p_mfc_buf_queue *queue)
+{
+       s5p_mfc_init_queue(queue);
+}
+
+void s5p_mfc_add_tail_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               struct s5p_mfc_buf *mfc_buf);
+
+int s5p_mfc_peek_buf_csd(spinlock_t *plock, struct s5p_mfc_buf_queue *queue);
+
+struct s5p_mfc_buf *s5p_mfc_get_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               enum s5p_mfc_queue_used_type used);
+struct s5p_mfc_buf *s5p_mfc_get_del_buf(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               enum s5p_mfc_queue_used_type used);
+struct s5p_mfc_buf *s5p_mfc_get_del_if_consumed(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               unsigned long consumed, unsigned int min_bytes, int err, int *deleted);
+struct s5p_mfc_buf *s5p_mfc_get_move_buf(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               enum s5p_mfc_queue_used_type used, enum s5p_mfc_queue_top_type top);
+struct s5p_mfc_buf *s5p_mfc_get_move_buf_used(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue);
+struct s5p_mfc_buf *s5p_mfc_get_move_buf_addr(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr);
+
+struct s5p_mfc_buf *s5p_mfc_find_buf_vb(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr);
+struct s5p_mfc_buf *s5p_mfc_find_del_buf_raw(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr);
+struct s5p_mfc_buf *s5p_mfc_find_del_buf_vb(spinlock_t *plock, struct s5p_mfc_buf_queue *queue,
+               dma_addr_t addr);
+struct s5p_mfc_buf *s5p_mfc_find_move_buf_vb(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr, unsigned int released_flag);
+struct s5p_mfc_buf *s5p_mfc_find_move_buf_vb_used(spinlock_t *plock,
+               struct s5p_mfc_buf_queue *to_queue, struct s5p_mfc_buf_queue *from_queue,
+               dma_addr_t addr);
+
+void s5p_mfc_move_first_buf_used(spinlock_t *plock, struct s5p_mfc_buf_queue *to_queue,
+               struct s5p_mfc_buf_queue *from_queue, enum s5p_mfc_queue_top_type top);
+void s5p_mfc_move_all_bufs(spinlock_t *plock, struct s5p_mfc_buf_queue *to_queue,
+               struct s5p_mfc_buf_queue *from_queue, enum s5p_mfc_queue_top_type top);
+
+void s5p_mfc_cleanup_queue(spinlock_t *plock, struct s5p_mfc_buf_queue *queue);
+
+void s5p_mfc_handle_released_info(struct s5p_mfc_ctx *ctx,
+               unsigned int released_flag, int index);
+
+void s5p_mfc_move_reuse_buffer(struct s5p_mfc_ctx *ctx, int release_index);
+
+void s5p_mfc_cleanup_enc_src_queue(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_cleanup_enc_dst_queue(struct s5p_mfc_ctx *ctx);
+
+struct s5p_mfc_buf *s5p_mfc_search_for_dpb(struct s5p_mfc_ctx *ctx, unsigned int dynamic_used);
+struct s5p_mfc_buf *s5p_mfc_search_move_dpb_nal_q(struct s5p_mfc_ctx *ctx, unsigned int dynamic_used);
+void s5p_mfc_store_dpb(struct s5p_mfc_ctx *ctx, struct vb2_buffer *vb);
+
+void s5p_mfc_cleanup_nal_queue(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_is_last_frame(struct s5p_mfc_ctx *ctx);
+
+#endif /* __S5P_MFC_QUEUE_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_reg.c b/drivers/media/platform/exynos/mfc/s5p_mfc_reg.c
new file mode 100644 (file)
index 0000000..113e9de
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_reg.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/delay.h>
+
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_mem.h"
+
+void s5p_mfc_dbg_enable(struct s5p_mfc_dev *dev)
+{
+       mfc_debug(2, "MFC debug info enable\n");
+       MFC_WRITEL(0x1, S5P_FIMV_DBG_INFO_ENABLE);
+}
+
+void s5p_mfc_dbg_disable(struct s5p_mfc_dev *dev)
+{
+       mfc_debug(2, "MFC debug info disable\n");
+       MFC_WRITEL(0x0, S5P_FIMV_DBG_INFO_ENABLE);
+}
+
+void s5p_mfc_dbg_set_addr(struct s5p_mfc_dev *dev)
+{
+       struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->buf;
+
+       memset((void *)dev->dbg_info_buf.vaddr, 0, buf_size->dbg_info_buf);
+
+       mfc_debug(2, "MFC debug info set addr(0x%08llx), size(0x%08lx)\n",
+                       dev->dbg_info_buf.daddr, buf_size->dbg_info_buf);
+       MFC_WRITEL(dev->dbg_info_buf.daddr, S5P_FIMV_DBG_BUFFER_ADDR);
+       MFC_WRITEL(buf_size->dbg_info_buf, S5P_FIMV_DBG_BUFFER_SIZE);
+}
+
+void s5p_mfc_otf_set_frame_addr(struct s5p_mfc_ctx *ctx, int num_planes)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_buf_addr *buf_addr = &handle->otf_buf_addr;
+       int index = handle->otf_buf_index;
+       int i;
+
+       for (i = 0; i < num_planes; i++) {
+               mfc_debug(2, "OTF: set frame buffer[i], 0x%08llx)\n",
+                               buf_addr->otf_daddr[index][i]);
+               MFC_WRITEL(buf_addr->otf_daddr[index][i],
+                               S5P_FIMV_E_SOURCE_FIRST_ADDR + (i * 4));
+       }
+}
+
+void s5p_mfc_otf_set_stream_size(struct s5p_mfc_ctx *ctx, unsigned int size)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct _otf_handle *handle = ctx->otf_handle;
+       struct _otf_debug *debug = &handle->otf_debug;
+       struct s5p_mfc_special_buf *buf;
+
+       mfc_debug(2, "OTF: set stream buffer full size, %u\n", size);
+       MFC_WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE);
+
+       if (otf_dump && !ctx->is_drm) {
+               buf = &debug->stream_buf[debug->frame_cnt];
+               mfc_debug(2, "OTF: set stream addr for debugging\n");
+               mfc_debug(2, "OTF: buf[%d] daddr: 0x%08llx\n",
+                               debug->frame_cnt, buf->daddr);
+               MFC_WRITEL(buf->daddr, S5P_FIMV_E_STREAM_BUFFER_ADDR);
+       }
+}
+
+void s5p_mfc_otf_set_hwfc_index(struct s5p_mfc_ctx *ctx, int job_id)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       mfc_debug(2, "OTF: set hwfc index, %d\n", job_id);
+       MFC_WRITEL_HWFC(job_id, HWFC_ENCODING_IDX);
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       unsigned int i;
+       size_t frame_size_mv;
+       dma_addr_t buf_addr1;
+       int buf_size1;
+       int align_gap;
+       struct s5p_mfc_raw_info *raw;
+       unsigned int reg = 0;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       raw = &ctx->raw_buf;
+       buf_addr1 = ctx->codec_buf.daddr;
+       buf_size1 = ctx->codec_buf.size;
+
+       mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
+       mfc_debug(2, "Total DPB COUNT: %d\n", dec->total_dpb_count);
+       mfc_debug(2, "Setting display delay to %d\n", dec->display_delay);
+
+       MFC_WRITEL(dec->total_dpb_count, S5P_FIMV_D_NUM_DPB);
+       mfc_debug(2, "raw->num_planes %d\n", raw->num_planes);
+       for (i = 0; i < raw->num_planes; i++) {
+               mfc_debug(2, "raw->plane_size[%d]= %d\n", i, raw->plane_size[i]);
+               MFC_WRITEL(raw->plane_size[i], S5P_FIMV_D_FIRST_PLANE_DPB_SIZE + (i * 4));
+       }
+
+       MFC_WRITEL(buf_addr1, S5P_FIMV_D_SCRATCH_BUFFER_ADDR);
+       MFC_WRITEL(ctx->scratch_buf_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE);
+       buf_addr1 += ctx->scratch_buf_size;
+       buf_size1 -= ctx->scratch_buf_size;
+
+       if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx) || IS_BPG_DEC(ctx))
+               MFC_WRITEL(ctx->mv_size, S5P_FIMV_D_MV_BUFFER_SIZE);
+
+       if (IS_VP9_DEC(ctx)){
+               MFC_WRITEL(buf_addr1, S5P_FIMV_D_STATIC_BUFFER_ADDR);
+               MFC_WRITEL(DEC_STATIC_BUFFER_SIZE, S5P_FIMV_D_STATIC_BUFFER_SIZE);
+               buf_addr1 += DEC_STATIC_BUFFER_SIZE;
+               buf_size1 -= DEC_STATIC_BUFFER_SIZE;
+       }
+
+       if (IS_MPEG4_DEC(ctx) && dec->loop_filter_mpeg4) {
+               mfc_debug(2, "Add DPB for loop filter of MPEG4\n");
+               for (i = 0; i < NUM_MPEG4_LF_BUF; i++) {
+                       MFC_WRITEL(buf_addr1, S5P_FIMV_D_POST_FILTER_LUMA_DPB0 + (4 * i));
+                       buf_addr1 += ctx->loopfilter_luma_size;
+                       buf_size1 -= ctx->loopfilter_luma_size;
+
+                       MFC_WRITEL(buf_addr1, S5P_FIMV_D_POST_FILTER_CHROMA_DPB0 + (4 * i));
+                       buf_addr1 += ctx->loopfilter_chroma_size;
+                       buf_size1 -= ctx->loopfilter_chroma_size;
+               }
+               reg |= ((dec->loop_filter_mpeg4 & S5P_FIMV_D_INIT_BUF_OPT_LF_CTRL_MASK)
+                               << S5P_FIMV_D_INIT_BUF_OPT_LF_CTRL_SHIFT);
+       }
+
+       reg |= (0x1 << S5P_FIMV_D_INIT_BUF_OPT_DYNAMIC_DPB_SET_SHIFT);
+
+       if (CODEC_NOT_CODED(ctx)) {
+               reg |= (0x1 << S5P_FIMV_D_INIT_BUF_OPT_COPY_NOT_CODED_SHIFT);
+               mfc_debug(2, "Notcoded frame copy mode start\n");
+       }
+       /* Enable 10bit Dithering */
+       if (ctx->is_10bit) {
+               reg |= (0x1 << S5P_FIMV_D_INIT_BUF_OPT_DITHERING_EN_SHIFT);
+               /* 64byte align, It is vaid only for VP9 */
+               reg |= (0x1 << S5P_FIMV_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN);
+       } else {
+               /* 16byte align, It is vaid only for VP9 */
+               reg &= ~(0x1 << S5P_FIMV_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN);
+       }
+
+       MFC_WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS);
+
+       frame_size_mv = ctx->mv_size;
+       mfc_debug(2, "Frame size: %d, %d, %d, mv: %ld, scratch: %ld\n",
+                       raw->plane_size[0], raw->plane_size[1],
+                       raw->plane_size[2], frame_size_mv,
+                       ctx->scratch_buf_size);
+
+       /* set decoder stride size */
+       for (i = 0; i < ctx->raw_buf.num_planes; i++) {
+               MFC_WRITEL(ctx->raw_buf.stride[i],
+                       S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE + (i * 4));
+               mfc_debug(2, "# plane%d.size = %d, stride = %d\n", i,
+                       ctx->raw_buf.plane_size[i], ctx->raw_buf.stride[i]);
+       }
+
+       if (ctx->is_10bit) {
+               for (i = 0; i < ctx->raw_buf.num_planes; i++) {
+                       MFC_WRITEL(raw->stride_2bits[i], S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE + (i * 4));
+                       MFC_WRITEL(raw->plane_size_2bits[i], S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_SIZE + (i * 4));
+                       mfc_debug(2, "# HEVC 10bit : 2bits plane%d.size = %d, stride = %d\n", i,
+                               ctx->raw_buf.plane_size_2bits[i], ctx->raw_buf.stride_2bits[i]);
+               }
+       }
+
+       MFC_WRITEL(dec->mv_count, S5P_FIMV_D_NUM_MV);
+       if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx) || IS_BPG_DEC(ctx)) {
+               for (i = 0; i < dec->mv_count; i++) {
+                       /* To test alignment */
+                       align_gap = buf_addr1;
+                       buf_addr1 = ALIGN(buf_addr1, 16);
+                       align_gap = buf_addr1 - align_gap;
+                       buf_size1 -= align_gap;
+
+                       mfc_debug(2, "\tBuf1: %p, size: %d\n", (void *)buf_addr1, buf_size1);
+                       MFC_WRITEL(buf_addr1, S5P_FIMV_D_MV_BUFFER0 + i * 4);
+                       buf_addr1 += frame_size_mv;
+                       buf_size1 -= frame_size_mv;
+               }
+       }
+
+       mfc_debug(2, "Buf1: %p, buf_size1: %d (frames %d)\n",
+                       (void *)buf_addr1, buf_size1, dec->total_dpb_count);
+       if (buf_size1 < 0) {
+               mfc_debug(2, "Not enough memory has been allocated.\n");
+               return -ENOMEM;
+       }
+
+       mfc_debug(2, "After setting buffers.\n");
+       return 0;
+}
+
+/* Set encoding ref & codec buffer */
+int s5p_mfc_set_enc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       dma_addr_t buf_addr1;
+       int buf_size1;
+       int i;
+
+       mfc_debug_enter();
+
+       buf_addr1 = ctx->codec_buf.daddr;
+       buf_size1 = ctx->codec_buf.size;
+
+       mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
+
+       MFC_WRITEL(buf_addr1, S5P_FIMV_E_SCRATCH_BUFFER_ADDR);
+       MFC_WRITEL(ctx->scratch_buf_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE);
+       buf_addr1 += ctx->scratch_buf_size;
+       buf_size1 -= ctx->scratch_buf_size;
+
+       /* start address of per buffer is aligned */
+       for (i = 0; i < ctx->dpb_count; i++) {
+               MFC_WRITEL(buf_addr1, S5P_FIMV_E_LUMA_DPB + (4 * i));
+               buf_addr1 += enc->luma_dpb_size;
+               buf_size1 -= enc->luma_dpb_size;
+       }
+       for (i = 0; i < ctx->dpb_count; i++) {
+               MFC_WRITEL(buf_addr1, S5P_FIMV_E_CHROMA_DPB + (4 * i));
+               buf_addr1 += enc->chroma_dpb_size;
+               buf_size1 -= enc->chroma_dpb_size;
+       }
+       for (i = 0; i < ctx->dpb_count; i++) {
+               MFC_WRITEL(buf_addr1, S5P_FIMV_E_ME_BUFFER + (4 * i));
+               buf_addr1 += enc->me_buffer_size;
+               buf_size1 -= enc->me_buffer_size;
+       }
+
+       MFC_WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER0);
+       buf_addr1 += enc->tmv_buffer_size >> 1;
+       MFC_WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER1);
+       buf_addr1 += enc->tmv_buffer_size >> 1;
+       buf_size1 -= enc->tmv_buffer_size;
+
+       mfc_debug(2, "Buf1: %p, buf_size1: %d (ref frames %d)\n",
+                       (void *)buf_addr1, buf_size1, ctx->dpb_count);
+       if (buf_size1 < 0) {
+               mfc_debug(2, "Not enough memory has been allocated.\n");
+               return -ENOMEM;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                 unsigned int start_num_byte, unsigned int strm_size)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       unsigned int cpb_buf_size;
+       dma_addr_t addr;
+
+       mfc_debug_enter();
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return -EINVAL;
+       }
+       dev = ctx->dev;
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return -EINVAL;
+       }
+
+       cpb_buf_size = ALIGN(dec->src_buf_size, STREAM_BUF_ALIGN);
+
+       if (mfc_buf) {
+               addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+               if (strm_size > set_strm_size_max(cpb_buf_size)) {
+                       mfc_info_ctx("Decrease strm_size because of %d align: %u -> %u\n",
+                               STREAM_BUF_ALIGN, strm_size, set_strm_size_max(cpb_buf_size));
+                       strm_size = set_strm_size_max(cpb_buf_size);
+                       mfc_buf->vb.vb2_buf.planes[0].bytesused = strm_size;
+               }
+       } else {
+               addr = 0;
+       }
+
+       mfc_debug(2, "inst_no: %d, buf_addr: 0x%08llx\n", ctx->inst_no, addr);
+       mfc_debug(2, "strm_size: %u cpb_buf_size: %u offset: %u\n",
+                       strm_size, cpb_buf_size, start_num_byte);
+
+       if (strm_size == 0)
+               mfc_info_ctx("stream size is 0\n");
+
+       MFC_WRITEL(strm_size, S5P_FIMV_D_STREAM_DATA_SIZE);
+       MFC_WRITEL(addr, S5P_FIMV_D_CPB_BUFFER_ADDR);
+       MFC_WRITEL(cpb_buf_size, S5P_FIMV_D_CPB_BUFFER_SIZE);
+       MFC_WRITEL(start_num_byte, S5P_FIMV_D_CPB_BUFFER_OFFSET);
+
+       if (mfc_buf)
+               MFC_TRACE_CTX("Set src[%d] fd: %d, %#llx\n",
+                               mfc_buf->vb.vb2_buf.index,
+                               mfc_buf->vb.vb2_buf.planes[0].m.fd,
+                               addr);
+
+       mfc_debug_leave();
+       return 0;
+}
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf *mfc_buf, int num_planes)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       dma_addr_t addr[3] = { 0, 0, 0 };
+       dma_addr_t addr_2bit[2];
+       int i;
+
+       if (mfc_buf) {
+               for (i = 0; i < num_planes; i++) {
+                       addr[i] = mfc_buf->planes.raw[i];
+                       mfc_debug(2, "enc src[%d] addr: 0x%08llx\n", i, addr[i]);
+               }
+
+               if (mfc_buf->planes.raw[0] != s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0))
+                       mfc_err_ctx("enc src yaddr: 0x%08llx != vb2 yaddr: 0x%08llx\n",
+                                       mfc_buf->planes.raw[i],
+                                       s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, i));
+       }
+
+       for (i = 0; i < num_planes; i++)
+               MFC_WRITEL(addr[i], S5P_FIMV_E_SOURCE_FIRST_ADDR + (i * 4));
+
+       if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M_S10B ||
+               ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M_S10B) {
+               addr_2bit[0] = addr[0] + NV12N_10B_Y_8B_SIZE(ctx->img_width, ctx->img_height);
+               addr_2bit[1] = addr[1] + NV12N_10B_CBCR_8B_SIZE(ctx->img_width, ctx->img_height);
+
+               for (i = 0; i < num_planes; i++)
+                       MFC_WRITEL(addr_2bit[i], S5P_FIMV_E_SOURCE_FIRST_2BIT_ADDR + (i * 4));
+       } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV16M_S10B ||
+               ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV61M_S10B) {
+               addr_2bit[0] = addr[0] + NV16M_Y_SIZE(ctx->img_width, ctx->img_height);
+               addr_2bit[1] = addr[1] + NV16M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+
+               for (i = 0; i < num_planes; i++)
+                       MFC_WRITEL(addr_2bit[i], S5P_FIMV_E_SOURCE_FIRST_2BIT_ADDR + (i * 4));
+       }
+}
+
+/* Set registers for encoding stream buffer */
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf *mfc_buf)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       dma_addr_t addr;
+       unsigned int size, offset;
+
+       addr = s5p_mfc_mem_get_daddr_vb(&mfc_buf->vb.vb2_buf, 0);
+       offset = mfc_buf->vb.vb2_buf.planes[0].data_offset;
+       size = (unsigned int)vb2_plane_size(&mfc_buf->vb.vb2_buf, 0);
+       size = ALIGN(size, 512);
+
+       MFC_WRITEL(addr, S5P_FIMV_E_STREAM_BUFFER_ADDR); /* 16B align */
+       MFC_WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE);
+       MFC_WRITEL(offset, S5P_FIMV_E_STREAM_BUFFER_OFFSET);
+
+       mfc_debug(2, "stream buf addr: 0x%08llx, size: 0x%08x(%d), offset: %d\n",
+                               addr, size, size, offset);
+
+       return 0;
+}
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+               dma_addr_t addr[], int num_planes)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       unsigned long enc_recon_y_addr, enc_recon_c_addr;
+       int i, addr_offset;
+
+       addr_offset = S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR;
+
+       for (i = 0; i < num_planes; i++)
+               addr[i] = MFC_READL(addr_offset + (i * 4));
+
+       enc_recon_y_addr = MFC_READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR);
+       enc_recon_c_addr = MFC_READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR);
+
+       mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);
+       mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr);
+}
+
+void s5p_mfc_set_enc_stride(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int i;
+
+       for (i = 0; i < ctx->raw_buf.num_planes; i++) {
+               MFC_WRITEL(ctx->raw_buf.stride[i],
+                               S5P_FIMV_E_SOURCE_FIRST_STRIDE + (i * 4));
+               mfc_debug(2, "enc src[%d] stride: 0x%08lx\n",
+                               i, (unsigned long)ctx->raw_buf.stride[i]);
+       }
+}
+
+int s5p_mfc_set_dynamic_dpb(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *dst_mb)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       struct s5p_mfc_raw_info *raw = &ctx->raw_buf;
+       int dst_index;
+       int i;
+
+       dst_index = dst_mb->vb.vb2_buf.index;
+       set_bit(dst_index, &dec->available_dpb);
+       dec->dynamic_set = 1 << dst_index;
+       mfc_debug(2, "ADDING Flag after: 0x%lx\n", dec->available_dpb);
+       mfc_debug(2, "Dst addr [%d] = 0x%08llx\n", dst_index, dst_mb->planes.raw[0]);
+
+       /* for debugging about black bar detection */
+       if (FW_HAS_BLACK_BAR_DETECT(dev) && dec->detect_black_bar) {
+               for (i = 0; i < raw->num_planes; i++) {
+                       dec->frame_vaddr[i][dec->frame_cnt] = vb2_plane_vaddr(&dst_mb->vb.vb2_buf, i);
+                       dec->frame_daddr[i][dec->frame_cnt] = dst_mb->planes.raw[i];
+                       dec->frame_size[i][dec->frame_cnt] = raw->plane_size[i];
+                       dec->index[i][dec->frame_cnt] = dst_index;
+                       dec->fd[i][dec->frame_cnt] = dst_mb->vb.vb2_buf.planes[0].m.fd;
+               }
+               dec->frame_cnt++;
+               if (dec->frame_cnt >= 30)
+                       dec->frame_cnt = 0;
+       }
+
+       /* decoder dst buffer CFW PROT */
+       s5p_mfc_protect_dpb(ctx, dst_mb);
+
+       for (i = 0; i < raw->num_planes; i++) {
+               MFC_WRITEL(raw->plane_size[i],
+                               S5P_FIMV_D_FIRST_PLANE_DPB_SIZE + i*4);
+               MFC_WRITEL(dst_mb->planes.raw[i],
+                               S5P_FIMV_D_FIRST_PLANE_DPB0 + (i*0x100 + dst_index*4));
+       }
+
+       MFC_TRACE_CTX("Set dst[%d] fd: %d, %#llx / avail %#lx used %#x\n",
+                       dst_index, dst_mb->vb.vb2_buf.planes[0].m.fd, dst_mb->planes.raw[0],
+                       dec->available_dpb, dec->dynamic_used);
+
+       return 0;
+}
+
+void s5p_mfc_set_pixel_format(struct s5p_mfc_dev *dev, unsigned int format)
+{
+       unsigned int reg = 0;
+       unsigned int pix_val, mem_type = 0;
+
+       switch (format) {
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV12N:
+       case V4L2_PIX_FMT_NV12MT_16X16:
+       case V4L2_PIX_FMT_NV16M:
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV21M:
+       case V4L2_PIX_FMT_NV61M:
+               pix_val = 1;
+               break;
+       case V4L2_PIX_FMT_YVU420M:
+               pix_val = 2;
+               break;
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YUV420N:
+               pix_val = 3;
+               break;
+       /* 10bit */
+       case V4L2_PIX_FMT_NV12N_10B:
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV16M_S10B:
+               mem_type = 0;
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV16M_P210:
+               mem_type = 1;
+               pix_val = 0;
+               break;
+       case V4L2_PIX_FMT_NV21M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+               mem_type = 0;
+               pix_val = 1;
+               break;
+       case V4L2_PIX_FMT_NV21M_P010:
+       case V4L2_PIX_FMT_NV61M_P210:
+               mem_type = 1;
+               pix_val = 1;
+               break;
+       default:
+               pix_val = 0;
+               break;
+       }
+       reg |= pix_val;
+       reg |= (mem_type << 4);
+       MFC_WRITEL(reg, S5P_FIMV_PIXEL_FORMAT);
+       mfc_debug(2, "pixel format: %d, mem_type for 10bit: %d (reg: %#x)\n",
+                       pix_val, mem_type, reg);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_reg.h b/drivers/media/platform/exynos/mfc/s5p_mfc_reg.h
new file mode 100644 (file)
index 0000000..592bf99
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_reg.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 __S5P_MFC_REG_H
+#define __S5P_MFC_REG_H __FILE__
+
+#include <linux/io.h>
+
+#include "s5p_mfc_common.h"
+
+#include "s5p_mfc_utils.h"
+
+#define MFC_READL(offset)              readl(dev->regs_base + (offset))
+#define MFC_WRITEL(data, offset)       writel((data), dev->regs_base + (offset))
+
+#define MFC_MMU0_READL(offset)         readl(dev->sysmmu0_base + (offset))
+#define MFC_MMU1_READL(offset)         readl(dev->sysmmu1_base + (offset))
+
+#define MFC_WRITEL_HWFC(data, offset)  writel((data), dev->hwfc_base + (offset))
+
+/* version */
+#define s5p_mfc_get_fimv_info()                ((MFC_READL(S5P_FIMV_FW_VERSION)                \
+                                               >> S5P_FIMV_FW_VER_INFO_SHFT)           \
+                                               & S5P_FIMV_FW_VER_INFO_MASK)
+#define s5p_mfc_get_fw_ver_year()      ((MFC_READL(S5P_FIMV_FW_VERSION)                \
+                                               >> S5P_FIMV_FW_VER_YEAR_SHFT)           \
+                                               & S5P_FIMV_FW_VER_YEAR_MASK)
+#define s5p_mfc_get_fw_ver_month()     ((MFC_READL(S5P_FIMV_FW_VERSION)                \
+                                               >> S5P_FIMV_FW_VER_MONTH_SHFT)          \
+                                               & S5P_FIMV_FW_VER_MONTH_MASK)
+#define s5p_mfc_get_fw_ver_date()      ((MFC_READL(S5P_FIMV_FW_VERSION)                \
+                                               >> S5P_FIMV_FW_VER_DATE_SHFT)           \
+                                               & S5P_FIMV_FW_VER_DATE_MASK)
+#define s5p_mfc_get_fw_ver_all()       ((MFC_READL(S5P_FIMV_FW_VERSION)                \
+                                               >> S5P_FIMV_FW_VER_ALL_SHFT)            \
+                                               & S5P_FIMV_FW_VER_ALL_MASK)
+#define s5p_mfc_get_mfc_version()      ((MFC_READL(S5P_FIMV_MFC_VERSION)               \
+                                               >> S5P_FIMV_MFC_VER_SHFT)               \
+                                               & S5P_FIMV_MFC_VER_MASK)
+
+
+/* decoding & display information */
+#define s5p_mfc_get_dec_status()       (MFC_READL(S5P_FIMV_D_DECODED_STATUS)           \
+                                               & S5P_FIMV_DEC_STATUS_DECODED_STATUS_MASK)
+#define s5p_mfc_get_disp_status()      (MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)           \
+                                               & S5P_FIMV_DISP_STATUS_DISPLAY_STATUS_MASK)
+#define s5p_mfc_get_res_change()       ((MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)          \
+                                               >> S5P_FIMV_DISP_STATUS_RES_CHANGE_SHIFT)       \
+                                               & S5P_FIMV_DISP_STATUS_RES_CHANGE_MASK)
+#define s5p_mfc_get_black_bar_detection()      ((MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)          \
+                                               >> S5P_FIMV_DISP_STATUS_BLACK_BAR_DETECT_SHIFT) \
+                                               & S5P_FIMV_DISP_STATUS_BLACK_BAR_DETECT_MASK)
+#define s5p_mfc_get_dpb_change()       ((MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)          \
+                                               >> S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_SHIFT)  \
+                                               & S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_MASK)
+#define s5p_mfc_get_scratch_change()   ((MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)          \
+                                               >> S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_SHIFT)      \
+                                               & S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_MASK)
+#define s5p_mfc_get_disp_frame_type()  (MFC_READL(S5P_FIMV_D_DISPLAY_FRAME_TYPE)       \
+                                               & S5P_FIMV_DISPLAY_FRAME_MASK)
+#define s5p_mfc_get_dec_frame_type()   (MFC_READL(S5P_FIMV_D_DECODED_FRAME_TYPE)       \
+                                               & S5P_FIMV_DECODED_FRAME_MASK)
+#define s5p_mfc_get_interlace_type()   ((MFC_READL(S5P_FIMV_D_DISPLAY_FRAME_TYPE)      \
+                                               >> S5P_FIMV_DISPLAY_TEMP_INFO_SHIFT)    \
+                                               & S5P_FIMV_DISPLAY_TEMP_INFO_MASK)
+#define s5p_mfc_is_interlace_picture() ((MFC_READL(S5P_FIMV_D_DISPLAY_STATUS)          \
+                                               >> S5P_FIMV_DISP_STATUS_INTERLACE_SHIFT)\
+                                               & S5P_FIMV_DISP_STATUS_INTERLACE_MASK)
+#define s5p_mfc_get_img_width()                MFC_READL(S5P_FIMV_D_DISPLAY_FRAME_WIDTH)
+#define s5p_mfc_get_img_height()       MFC_READL(S5P_FIMV_D_DISPLAY_FRAME_HEIGHT)
+#define s5p_mfc_get_disp_y_addr()      MFC_READL(S5P_FIMV_D_DISPLAY_LUMA_ADDR)
+#define s5p_mfc_get_dec_y_addr()       MFC_READL(S5P_FIMV_D_DECODED_LUMA_ADDR)
+
+
+/* kind of interrupt */
+#define s5p_mfc_get_int_err()          MFC_READL(S5P_FIMV_ERROR_CODE)
+#define s5p_mfc_get_err(x)             (((x) >> S5P_FIMV_ERR_STATUS_SHIFT)             \
+                                               & S5P_FIMV_ERR_STATUS_MASK)
+#define s5p_mfc_get_warn(x)            (((x) >> S5P_FIMV_WARN_STATUS_SHIFT)            \
+                                               & S5P_FIMV_WARN_STATUS_MASK)
+
+
+/* additional information */
+#define s5p_mfc_get_consumed_stream()          MFC_READL(S5P_FIMV_D_DECODED_NAL_SIZE)
+#define s5p_mfc_get_dpb_count()                        MFC_READL(S5P_FIMV_D_MIN_NUM_DPB)
+#define s5p_mfc_get_min_dpb_size(x)            MFC_READL(S5P_FIMV_D_MIN_FIRST_PLANE_DPB_SIZE + (x * 4))
+#define s5p_mfc_get_scratch_size()             MFC_READL(S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE)
+#define s5p_mfc_get_mv_count()                 MFC_READL(S5P_FIMV_D_MIN_NUM_MV)
+#define s5p_mfc_get_inst_no()                  MFC_READL(S5P_FIMV_RET_INSTANCE_ID)
+#define s5p_mfc_get_enc_dpb_count()            MFC_READL(S5P_FIMV_E_NUM_DPB)
+#define s5p_mfc_get_enc_scratch_size()         MFC_READL(S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE)
+#define s5p_mfc_get_enc_strm_size()            MFC_READL(S5P_FIMV_E_STREAM_SIZE)
+#define s5p_mfc_get_enc_slice_type()           MFC_READL(S5P_FIMV_E_SLICE_TYPE)
+#define s5p_mfc_get_enc_pic_count()            MFC_READL(S5P_FIMV_E_PICTURE_COUNT)
+#define s5p_mfc_get_sei_avail()                        MFC_READL(S5P_FIMV_D_SEI_AVAIL)
+#define s5p_mfc_get_sei_content_light()                MFC_READL(S5P_FIMV_D_CONTENT_LIGHT_LEVEL_INFO_SEI)
+#define s5p_mfc_get_sei_mastering0()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0)
+#define s5p_mfc_get_sei_mastering1()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1)
+#define s5p_mfc_get_sei_mastering2()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2)
+#define s5p_mfc_get_sei_mastering3()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3)
+#define s5p_mfc_get_sei_mastering4()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4)
+#define s5p_mfc_get_sei_mastering5()           MFC_READL(S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5)
+#define s5p_mfc_get_sei_avail_frame_pack()     (MFC_READL(S5P_FIMV_D_SEI_AVAIL)        \
+                                               & S5P_FIMV_D_SEI_AVAIL_FRAME_PACK_MASK)
+#define s5p_mfc_get_sei_avail_content_light()  ((MFC_READL(S5P_FIMV_D_SEI_AVAIL)       \
+                                               >> S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_SHIFT)    \
+                                               & S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_MASK)
+#define s5p_mfc_get_sei_avail_mastering_display()      ((MFC_READL(S5P_FIMV_D_SEI_AVAIL)       \
+                                               >> S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT)        \
+                                               & S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_MASK)
+#define s5p_mfc_get_video_signal_type()                ((MFC_READL(S5P_FIMV_D_VIDEO_SIGNAL_TYPE)       \
+                                               >> S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT)     \
+                                               & S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_MASK)
+#define s5p_mfc_get_colour_description()       ((MFC_READL(S5P_FIMV_D_VIDEO_SIGNAL_TYPE)       \
+                                               >> S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_SHIFT)    \
+                                               & S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_MASK)
+#define s5p_mfc_get_black_bar_pos_x()          ((MFC_READL(S5P_FIMV_D_BLACK_BAR_START_POS)     \
+                                               >> S5P_FIMV_D_BLACK_BAR_START_X_SHIFT)          \
+                                               & S5P_FIMV_D_BLACK_BAR_START_X_MASK)
+#define s5p_mfc_get_black_bar_pos_y()          ((MFC_READL(S5P_FIMV_D_BLACK_BAR_START_POS)     \
+                                               >> S5P_FIMV_D_BLACK_BAR_START_Y_SHIFT)          \
+                                               & S5P_FIMV_D_BLACK_BAR_START_Y_MASK)
+#define s5p_mfc_get_black_bar_image_w()                ((MFC_READL(S5P_FIMV_D_BLACK_BAR_IMAGE_SIZE)    \
+                                               >> S5P_FIMV_D_BLACK_BAR_IMAGE_W_SHIFT)  \
+                                               & S5P_FIMV_D_BLACK_BAR_IMAGE_W_MASK)
+#define s5p_mfc_get_black_bar_image_h()                ((MFC_READL(S5P_FIMV_D_BLACK_BAR_IMAGE_SIZE)    \
+                                               >> S5P_FIMV_D_BLACK_BAR_IMAGE_H_SHIFT)  \
+                                               & S5P_FIMV_D_BLACK_BAR_IMAGE_H_MASK)
+#define s5p_mfc_get_mvc_disp_view_id()         (MFC_READL(S5P_FIMV_D_MVC_VIEW_ID)              \
+                                               & S5P_FIMV_D_MVC_VIEW_ID_DISP_MASK)
+#define s5p_mfc_get_profile()                  (MFC_READL(S5P_FIMV_D_DECODED_PICTURE_PROFILE)  \
+                                               & S5P_FIMV_D_DECODED_PIC_PROFILE_MASK)
+#define s5p_mfc_get_luma_bit_depth_minus8()    ((MFC_READL(S5P_FIMV_D_DECODED_PICTURE_PROFILE) \
+                                               >> S5P_FIMV_D_BIT_DEPTH_LUMA_MINUS8_SHIFT)      \
+                                               & S5P_FIMV_D_BIT_DEPTH_LUMA_MINUS8_MASK)
+#define s5p_mfc_get_chroma_bit_depth_minus8()  ((MFC_READL(S5P_FIMV_D_DECODED_PICTURE_PROFILE) \
+                                               >> S5P_FIMV_D_BIT_DEPTH_CHROMA_MINUS8_SHIFT)    \
+                                               & S5P_FIMV_D_BIT_DEPTH_CHROMA_MINUS8_MASK)
+#define s5p_mfc_get_dec_used_flag()            MFC_READL(S5P_FIMV_D_USED_DPB_FLAG_LOWER)
+#define s5p_mfc_get_enc_nal_done_info()                ((MFC_READL(S5P_FIMV_E_NAL_DONE_INFO) & (0x3 << 4)) >> 4)
+#define s5p_mfc_get_chroma_format()            (MFC_READL(S5P_FIMV_D_CHROMA_FORMAT)            \
+                                               & S5P_FIMV_D_CHROMA_FORMAT_MASK)
+#define s5p_mfc_get_color_range()              ((MFC_READL(S5P_FIMV_D_CHROMA_FORMAT)   \
+                                               >> S5P_FIMV_D_COLOR_RANGE_SHIFT)        \
+                                               & S5P_FIMV_D_COLOR_RANGE_MASK)
+#define s5p_mfc_get_color_space()              ((MFC_READL(S5P_FIMV_D_CHROMA_FORMAT)   \
+                                               >> S5P_FIMV_D_COLOR_SPACE_SHIFT)        \
+                                               & S5P_FIMV_D_COLOR_SPACE_MASK)
+#define s5p_mfc_get_num_of_tile()              ((MFC_READL(S5P_FIMV_D_DECODED_STATUS)          \
+                                               >> S5P_FIMV_DEC_STATUS_NUM_OF_TILE_SHIFT)       \
+                                               & S5P_FIMV_DEC_STATUS_NUM_OF_TILE_MASK)
+
+
+/* nal queue information */
+#define s5p_mfc_get_nal_q_input_count()                MFC_READL(S5P_FIMV_NAL_QUEUE_INPUT_COUNT)
+#define s5p_mfc_get_nal_q_output_count()       MFC_READL(S5P_FIMV_NAL_QUEUE_OUTPUT_COUNT)
+#define s5p_mfc_get_nal_q_input_exe_count()    MFC_READL(S5P_FIMV_NAL_QUEUE_INPUT_EXE_COUNT)
+#define s5p_mfc_get_nal_q_info()               MFC_READL(S5P_FIMV_NAL_QUEUE_INFO)
+#define s5p_mfc_get_nal_q_input_addr()         MFC_READL(S5P_FIMV_NAL_QUEUE_INPUT_ADDR)
+#define s5p_mfc_get_nal_q_input_size()         MFC_READL(S5P_FIMV_NAL_QUEUE_INPUT_SIZE)
+#define s5p_mfc_get_nal_q_output_addr()                MFC_READL(S5P_FIMV_NAL_QUEUE_OUTPUT_ADDR)
+#define s5p_mfc_get_nal_q_output_ize()         MFC_READL(S5P_FIMV_NAL_QUEUE_OUTPUT_SIZE)
+
+static inline void s5p_mfc_reset_nal_queue_registers(struct s5p_mfc_dev *dev)
+{
+       MFC_WRITEL(0x0, S5P_FIMV_NAL_QUEUE_INPUT_COUNT);
+       MFC_WRITEL(0x0, S5P_FIMV_NAL_QUEUE_OUTPUT_COUNT);
+       MFC_WRITEL(0x0, S5P_FIMV_NAL_QUEUE_INPUT_EXE_COUNT);
+       MFC_WRITEL(0x0, S5P_FIMV_NAL_QUEUE_INFO);
+}
+
+static inline void s5p_mfc_update_nal_queue_input(struct s5p_mfc_dev *dev,
+       dma_addr_t addr, unsigned int size)
+{
+       MFC_WRITEL(addr, S5P_FIMV_NAL_QUEUE_INPUT_ADDR);
+       MFC_WRITEL(size, S5P_FIMV_NAL_QUEUE_INPUT_SIZE);
+}
+
+static inline void s5p_mfc_update_nal_queue_output(struct s5p_mfc_dev *dev,
+       dma_addr_t addr, unsigned int size)
+{
+       MFC_WRITEL(addr, S5P_FIMV_NAL_QUEUE_OUTPUT_ADDR);
+       MFC_WRITEL(size, S5P_FIMV_NAL_QUEUE_OUTPUT_SIZE);
+}
+
+static inline void s5p_mfc_update_nal_queue_input_count(struct s5p_mfc_dev *dev,
+       unsigned int input_count)
+{
+       MFC_WRITEL(input_count, S5P_FIMV_NAL_QUEUE_INPUT_COUNT);
+}
+
+static inline void s5p_mfc_dec_store_crop_info(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_dec *dec = ctx->dec_priv;
+       u32 left, right, top, bottom;
+
+       left = MFC_READL(S5P_FIMV_D_DISPLAY_CROP_INFO1);
+       right = left >> S5P_FIMV_D_SHARED_CROP_RIGHT_SHIFT;
+       left = left & S5P_FIMV_D_SHARED_CROP_LEFT_MASK;
+       top = MFC_READL(S5P_FIMV_D_DISPLAY_CROP_INFO2);
+       bottom = top >> S5P_FIMV_D_SHARED_CROP_BOTTOM_SHIFT;
+       top = top & S5P_FIMV_D_SHARED_CROP_TOP_MASK;
+
+       dec->cr_left = left;
+       dec->cr_right = right;
+       dec->cr_top = top;
+       dec->cr_bot = bottom;
+}
+
+static inline void s5p_mfc_clear_enc_res_change(struct s5p_mfc_dev *dev)
+{
+       unsigned int reg = 0;
+
+       reg = MFC_READL(S5P_FIMV_E_PARAM_CHANGE);
+       reg &= ~(0x7 << 6);
+       MFC_WRITEL(reg, S5P_FIMV_E_PARAM_CHANGE);
+}
+
+void s5p_mfc_dbg_enable(struct s5p_mfc_dev *dev);
+void s5p_mfc_dbg_disable(struct s5p_mfc_dev *dev);
+void s5p_mfc_dbg_set_addr(struct s5p_mfc_dev *dev);
+
+void s5p_mfc_otf_set_frame_addr(struct s5p_mfc_ctx *ctx, int num_planes);
+void s5p_mfc_otf_set_stream_size(struct s5p_mfc_ctx *ctx, unsigned int size);
+void s5p_mfc_otf_set_hwfc_index(struct s5p_mfc_ctx *ctx, int job_id);
+
+int s5p_mfc_set_dec_codec_buffers(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_set_enc_codec_buffers(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf *mfc_buf,
+               unsigned int start_num_byte,
+               unsigned int buf_size);
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf *mfc_buf, int num_planes);
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+               struct s5p_mfc_buf *mfc_buf);
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+               dma_addr_t addr[], int num_planes);
+void s5p_mfc_set_enc_stride(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_set_dynamic_dpb(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *dst_vb);
+
+void s5p_mfc_set_pixel_format(struct s5p_mfc_dev *dev, unsigned int format);
+
+#endif /* __S5P_MFC_REG_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_regs_v10.h b/drivers/media/platform/exynos/mfc/s5p_mfc_regs_v10.h
new file mode 100644 (file)
index 0000000..ed07e9a
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * drivers/media/platform/exynos/mfc/regs-mfc-v10.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 __REGS_MFC_V10_H
+#define __REGS_MFC_V10_H __FILE__
+
+/* SYSMMU Register */
+#define MFC_MMU0_BASE_ADDR                                     0x17880000
+#define MFC_MMU1_BASE_ADDR                                     0x178A0000
+#define MFC_MMU_SIZE                                           0x9000
+
+#define MFC_MMU_INTERRUPT_STATUS                               0x0060
+#define MFC_MMU_FAULT_TRANS_INFO                               0x0078
+#define MFC_MMU_FAULT_TRANS_INFO_RW_MASK                       0x100000
+#define MFC_MMU_FAULT_TRANS_INFO_AXID_MASK                     0xFFFF
+
+#define HWFC_BASE_ADDR                                         0x17628000
+#define HWFC_SIZE                                              0x100
+
+#define HWFC_ENCODING_IDX                                      0x4
+
+/* Codec Common Registers */
+#define S5P_FIMV_RISC_ON                                       0x0000
+#define S5P_FIMV_RISC2HOST_INT                                 0x003C
+#define S5P_FIMV_HOST2RISC_INT                                 0x0044
+#define S5P_FIMV_RISC_BASE_ADDRESS                             0x0054
+
+#define S5P_FIMV_MFC_FW_CLOCK                                  0x1060
+#define S5P_FIMV_MFC_RESET                                     0x1070
+
+#define S5P_FIMV_HOST2RISC_CMD                                 0x1100
+#define S5P_FIMV_RISC2HOST_CMD                                 0x1104
+
+#define S5P_FIMV_MFC_BUS_STATUS                                        0x7018
+#define S5P_FIMV_MFC_RPEND                                     0x7028
+#define S5P_FIMV_MFC_WPEND                                     0x702C
+#define S5P_FIMV_MFC_BUS_RESET_CTRL                            0x7110
+#define S5P_FIMV_MFC_OFF                                       0x7120
+#define S5P_FIMV_MFC_STATE                                     0x7124
+
+#define S5P_FIMV_FW_VERSION                                    0xF000
+#define S5P_FIMV_INSTANCE_ID                                   0xF008
+#define S5P_FIMV_CODEC_TYPE                                    0xF00C
+#define S5P_FIMV_CONTEXT_MEM_ADDR                              0xF014
+#define S5P_FIMV_CONTEXT_MEM_SIZE                              0xF018
+#define S5P_FIMV_SHARED_MEM_ADDR                               0xF01C
+#define S5P_FIMV_PIXEL_FORMAT                                  0xF020
+
+#define S5P_FIMV_METADATA_ENABLE                               0xF024
+#define S5P_FIMV_MFC_VERSION                                   0xF028
+#define S5P_FIMV_DBG_INFO_ENABLE                               0xF02C
+#define S5P_FIMV_DBG_BUFFER_ADDR                               0xF030
+#define S5P_FIMV_DBG_BUFFER_SIZE                               0xF034
+
+#define S5P_FIMV_CODEC_CONTROL                                 0xF038
+#define S5P_FIMV_DEC_TIMEOUT_VALUE                             0xF03C
+#define S5P_FIMV_HED_SHARED_MEM_ADDR                           0xF040
+
+/* NAL QUEUE */
+#define S5P_FIMV_NAL_QUEUE_INPUT_ADDR                          0xF044
+#define S5P_FIMV_NAL_QUEUE_INPUT_SIZE                          0xF048
+#define S5P_FIMV_NAL_QUEUE_OUTPUT_ADDR                         0xF04C
+#define S5P_FIMV_NAL_QUEUE_OUTPUT_SIZE                         0xF050
+#define S5P_FIMV_NAL_QUEUE_INPUT_COUNT                         0xF054
+
+#define S5P_FIMV_RET_INSTANCE_ID                               0xF070
+#define S5P_FIMV_ERROR_CODE                                    0xF074
+#define S5P_FIMV_DBG_BUFFER_OUTPUT_SIZE                                0xF078
+#define S5P_FIMV_METADATA_STATUS                               0xF07C
+
+#define S5P_FIMV_DBG_INFO_STAGE_COUNTER                                0xF088
+
+/* NAL QUEUE */
+#define S5P_FIMV_NAL_QUEUE_OUTPUT_COUNT                                0xF08C
+#define S5P_FIMV_NAL_QUEUE_INPUT_EXE_COUNT                     0xF090
+#define S5P_FIMV_NAL_QUEUE_INFO                                        0xF094
+
+/* Decoder Registers */
+#define S5P_FIMV_D_CRC_CTRL                                    0xF0B0
+#define S5P_FIMV_D_DEC_OPTIONS                                 0xF0B4
+
+#define S5P_FIMV_D_DISPLAY_DELAY                               0xF0B8
+
+#define S5P_FIMV_D_SET_FRAME_WIDTH                             0xF0BC
+#define S5P_FIMV_D_SET_FRAME_HEIGHT                            0xF0C0
+
+#define S5P_FIMV_D_SEI_ENABLE                                  0xF0C4
+
+#define S5P_FIMV_D_FORCE_PIXEL_VAL                             0xF0C8
+
+/* Buffer setting registers */
+/* Session return */
+#define S5P_FIMV_D_MIN_NUM_DPB                                 0xF0F0
+#define S5P_FIMV_D_MIN_FIRST_PLANE_DPB_SIZE                    0xF0F4
+#define S5P_FIMV_D_MIN_SECOND_PLANE_DPB_SIZE                   0xF0F8
+#define S5P_FIMV_D_MIN_THIRD_PLANE_DPB_SIZE                    0xF0FC
+#define S5P_FIMV_D_MIN_NUM_MV                                  0xF100
+#define S5P_FIMV_D_MVC_NUM_VIEWS                               0xF104
+#define S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE                     0xF108
+#define S5P_FIMV_D_MIN_FIRST_PLANE_2BIT_DPB_SIZE               0xF10C
+#define S5P_FIMV_D_MIN_SECOND_PLANE_2BIT_DPB_SIZE              0xF110
+#define S5P_FIMV_D_POST_FILTER_LUMA_DPB0                       0xF120
+#define S5P_FIMV_D_POST_FILTER_LUMA_DPB1                       0xF124
+#define S5P_FIMV_D_POST_FILTER_CHROMA_DPB0                     0xF128
+#define S5P_FIMV_D_POST_FILTER_CHROMA_DPB1                     0xF12C
+
+/* Buffers */
+#define S5P_FIMV_D_NUM_DPB                                     0xF130
+#define S5P_FIMV_D_NUM_MV                                      0xF134
+#define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE                 0xF138
+#define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE                        0xF13C
+#define S5P_FIMV_D_THIRD_PLANE_DPB_STRIDE_SIZE                 0xF140
+#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE                                0xF144
+#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE                       0xF148
+#define S5P_FIMV_D_THIRD_PLANE_DPB_SIZE                                0xF14C
+#define S5P_FIMV_D_MV_BUFFER_SIZE                              0xF150
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS                         0xF154
+#define S5P_FIMV_D_FIRST_PLANE_DPB0                            0xF160
+#define S5P_FIMV_D_SECOND_PLANE_DPB0                           0xF260
+#define S5P_FIMV_D_THIRD_PLANE_DPB0                            0xF360
+#define S5P_FIMV_D_MV_BUFFER0                                  0xF460
+#define S5P_FIMV_D_SCRATCH_BUFFER_ADDR                         0xF560
+#define S5P_FIMV_D_SCRATCH_BUFFER_SIZE                         0xF564
+#define S5P_FIMV_D_METADATA_BUFFER_ADDR                                0xF568
+#define S5P_FIMV_D_METADATA_BUFFER_SIZE                                0xF56C
+
+#define S5P_FIMV_D_STATIC_BUFFER_ADDR                          0xF570
+#define S5P_FIMV_D_STATIC_BUFFER_SIZE                          0xF574
+#define S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_SIZE                   0xF578
+#define S5P_FIMV_D_SECOND_PLANE_2BIT_DPB_SIZE                  0xF57C
+#define S5P_FIMV_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE            0xF580
+#define S5P_FIMV_D_SECOND_PLANE_2BIT_DPB_STRIDE_SIZE           0xF584
+
+#define S5P_FIMV_D_NAL_START_OPTIONS                           0xF5AC
+
+/* Nal cmd */
+#define S5P_FIMV_D_CPB_BUFFER_ADDR                             0xF5B0
+#define S5P_FIMV_D_CPB_BUFFER_SIZE                             0xF5B4
+#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_UPPER                    0xF5B8
+#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER                    0xF5BC
+#define S5P_FIMV_D_CPB_BUFFER_OFFSET                           0xF5C0
+#define S5P_FIMV_D_SLICE_IF_ENABLE                             0xF5C4
+#define S5P_FIMV_D_PICTURE_TAG                                 0xF5C8
+#define S5P_FIMV_D_STREAM_DATA_SIZE                            0xF5D0
+#define S5P_FIMV_D_DYNAMIC_DPB_FLAG_UPPER                      0xF5D4
+#define S5P_FIMV_D_DYNAMIC_DPB_FLAG_LOWER                      0xF5D8
+
+/* Nal return */
+#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH                         0xF600
+#define S5P_FIMV_D_DISPLAY_FRAME_HEIGHT                                0xF604
+#define S5P_FIMV_D_DISPLAY_STATUS                              0xF608
+#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR                    0xF60C
+#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR                   0xF610
+#define S5P_FIMV_D_DISPLAY_THIRD_PLANE_ADDR                    0xF614
+#define S5P_FIMV_D_DISPLAY_FRAME_TYPE                          0xF618
+#define S5P_FIMV_D_DISPLAY_CROP_INFO1                          0xF61C
+#define S5P_FIMV_D_DISPLAY_CROP_INFO2                          0xF620
+#define S5P_FIMV_D_DISPLAY_PICTURE_PROFILE                     0xF624
+#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_CRC                     0xF628
+#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_CRC                    0xF62C
+#define S5P_FIMV_D_DISPLAY_THIRD_PLANE_CRC                     0xF630
+#define S5P_FIMV_D_DISPLAY_ASPECT_RATIO                                0xF634
+#define S5P_FIMV_D_DISPLAY_EXTENDED_AR                         0xF638
+#define S5P_FIMV_D_DECODED_FRAME_WIDTH                         0xF63C
+#define S5P_FIMV_D_DECODED_FRAME_HEIGHT                                0xF640
+#define S5P_FIMV_D_DECODED_STATUS                              0xF644
+#define S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR                    0xF648
+#define S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR                   0xF64C
+#define S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR                    0xF650
+#define S5P_FIMV_D_DECODED_FRAME_TYPE                          0xF654
+#define S5P_FIMV_D_DECODED_CROP_INFO1                          0xF658
+#define S5P_FIMV_D_DECODED_CROP_INFO2                          0xF65C
+#define S5P_FIMV_D_DECODED_PICTURE_PROFILE                     0xF660
+#define S5P_FIMV_D_DECODED_NAL_SIZE                            0xF664
+#define S5P_FIMV_D_DECODED_FIRST_PLANE_CRC                     0xF668
+#define S5P_FIMV_D_DECODED_SECOND_PLANE_CRC                    0xF66C
+#define S5P_FIMV_D_DECODED_THIRD_PLANE_CRC                     0xF670
+#define S5P_FIMV_D_RET_PICTURE_TAG_TOP                         0xF674
+#define S5P_FIMV_D_RET_PICTURE_TAG_BOT                         0xF678
+#define S5P_FIMV_D_RET_PICTURE_TIME_TOP                                0xF67C
+#define S5P_FIMV_D_RET_PICTURE_TIME_BOT                                0xF680
+#define S5P_FIMV_D_CHROMA_FORMAT                               0xF684
+
+#define S5P_FIMV_D_VC1_INFO                                    0xF688
+#define S5P_FIMV_D_MPEG4_INFO                                  0xF68C
+#define S5P_FIMV_D_H264_INFO                                   0xF690
+#define S5P_FIMV_D_HEVC_INFO                                   0xF6A0
+#define S5P_FIMV_D_BPG_INFO                                    0xF6A8
+
+#define S5P_FIMV_D_METADATA_ADDR_CONCEALED_MB                  0xF6B0
+#define S5P_FIMV_D_METADATA_SIZE_CONCEALED_MB                  0xF6B4
+#define S5P_FIMV_D_METADATA_ADDR_VC1_PARAM                     0xF6B8
+#define S5P_FIMV_D_METADATA_SIZE_VC1_PARAM                     0xF6BC
+#define S5P_FIMV_D_METADATA_ADDR_SEI_NAL                       0xF6C0
+#define S5P_FIMV_D_METADATA_SIZE_SEI_NAL                       0xF6C4
+#define S5P_FIMV_D_METADATA_ADDR_VUI                           0xF6C8
+#define S5P_FIMV_D_METADATA_SIZE_VUI                           0xF6CC
+#define S5P_FIMV_D_METADATA_ADDR_MVCVUI                                0xF6D0
+#define S5P_FIMV_D_METADATA_SIZE_MVCVUI                                0xF6D4
+
+#define S5P_FIMV_D_MVC_VIEW_ID                                 0xF6D8
+
+#define S5P_FIMV_D_SEI_AVAIL                                   0xF6DC
+#define S5P_FIMV_D_FRAME_PACK_ARRGMENT_ID                      0xF6E0
+#define S5P_FIMV_D_FRAME_PACK_SEI_INFO                         0xF6E4
+#define S5P_FIMV_D_FRAME_PACK_GRID_POS                         0xF6E8
+
+#define S5P_FIMV_D_DISPLAY_RECOVERY_SEI_INFO                   0xF6EC
+#define S5P_FIMV_D_DECODED_RECOVERY_SEI_INFO                   0xF6F0
+
+#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_2BIT_CRC                        0xF6FC
+#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_2BIT_CRC               0xF700
+#define S5P_FIMV_D_DECODED_FIRST_PLANE_2BIT_CRC                        0xF704
+#define S5P_FIMV_D_DECODED_SECOND_PLANE_2BIT_CRC               0xF708
+
+#define S5P_FIMV_D_VIDEO_SIGNAL_TYPE                           0xF70C
+#define S5P_FIMV_D_CONTENT_LIGHT_LEVEL_INFO_SEI                        0xF710
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0       0xF714
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1       0xF718
+
+#define S5P_FIMV_D_USED_DPB_FLAG_UPPER                         0xF720
+#define S5P_FIMV_D_USED_DPB_FLAG_LOWER                         0xF724
+
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2       0xF728
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3       0xF72C
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4       0xF730
+#define S5P_FIMV_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5       0xF734
+
+#define S5P_FIMV_D_BLACK_BAR_START_POS                         0xF738
+#define S5P_FIMV_D_BLACK_BAR_IMAGE_SIZE                                0xF73C
+
+#define S5P_FIMV_D_DISPLAY_LUMA_ADDR                           0xF60C
+#define S5P_FIMV_D_DISPLAY_CHROMA_ADDR                         0xF610
+
+#define S5P_FIMV_D_DECODED_LUMA_ADDR                           0xF648
+#define S5P_FIMV_D_DECODED_CHROMA_ADDR                         0xF64C
+
+/* Encoder Registers */
+#define S5P_FIMV_E_FRAME_WIDTH                                 0xF770
+#define S5P_FIMV_E_FRAME_HEIGHT                                        0xF774
+#define S5P_FIMV_E_CROPPED_FRAME_WIDTH                         0xF778
+#define S5P_FIMV_E_CROPPED_FRAME_HEIGHT                                0xF77C
+#define S5P_FIMV_E_FRAME_CROP_OFFSET                           0xF780
+#define S5P_FIMV_E_ENC_OPTIONS                                 0xF784
+#define S5P_FIMV_E_PICTURE_PROFILE                             0xF788
+#define S5P_FIMV_E_VBV_BUFFER_SIZE                             0xF78C
+#define S5P_FIMV_E_VBV_INIT_DELAY                              0xF790
+#define S5P_FIMV_E_FIXED_PICTURE_QP                            0xF794
+#define S5P_FIMV_E_RC_CONFIG                                   0xF798
+#define S5P_FIMV_E_RC_QP_BOUND                                 0xF79C
+#define S5P_FIMV_E_RC_QP_BOUND_PB                              0xF7A0
+#define S5P_FIMV_E_RC_MODE                                     0xF7A4
+
+#define S5P_FIMV_E_MB_RC_CONFIG                                        0xF7A8
+#define S5P_FIMV_E_PADDING_CTRL                                        0xF7AC
+#define S5P_FIMV_E_AIR_THRESHOLD                               0xF7B0
+
+#define S5P_FIMV_E_MV_HOR_RANGE                                        0xF7B4
+#define S5P_FIMV_E_MV_VER_RANGE                                        0xF7B8
+
+#define S5P_FIMV_E_HIGH_QUALITY_MODE                           0xF7C0
+
+#define S5P_FIMV_E_SAO_WEIGHT0                                 0xF7C8
+#define S5P_FIMV_E_SAO_WEIGHT1                                 0xF7CC
+
+#define S5P_FIMV_E_NUM_DPB                                     0xF890
+#define S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE                     0xF894
+
+#define S5P_FIMV_E_LUMA_DPB                                    0xF8C0
+#define S5P_FIMV_E_CHROMA_DPB                                  0xF904
+#define S5P_FIMV_E_ME_BUFFER                                   0xF948
+
+#define S5P_FIMV_E_SCRATCH_BUFFER_ADDR                         0xF98C
+#define S5P_FIMV_E_SCRATCH_BUFFER_SIZE                         0xF990
+#define S5P_FIMV_E_TMV_BUFFER0                                 0xF994
+#define S5P_FIMV_E_TMV_BUFFER1                                 0xF998
+#define S5P_FIMV_E_IR_BUFFER_ADDR                              0xF99C
+#define S5P_FIMV_E_SOURCE_FIRST_2BIT_ADDR                      0xF9D0
+#define S5P_FIMV_E_SOURCE_SECOND_2BIT_ADDR                     0xF9D4
+#define S5P_FIMV_E_SOURCE_FIRST_2BIT_STRIDE                    0xF9D8
+#define S5P_FIMV_E_SOURCE_SECOND_2BIT_STRIDE                   0xF9DC
+#define S5P_FIMV_E_SOURCE_FIRST_ADDR                           0xF9E0
+#define S5P_FIMV_E_SOURCE_SECOND_ADDR                          0xF9E4
+#define S5P_FIMV_E_SOURCE_THIRD_ADDR                           0xF9E8
+#define S5P_FIMV_E_SOURCE_FIRST_STRIDE                         0xF9EC
+#define S5P_FIMV_E_SOURCE_SECOND_STRIDE                                0xF9F0
+#define S5P_FIMV_E_SOURCE_THIRD_STRIDE                         0xF9F4
+#define S5P_FIMV_E_STREAM_BUFFER_ADDR                          0xF9F8
+#define S5P_FIMV_E_STREAM_BUFFER_SIZE                          0xF9FC
+#define S5P_FIMV_E_ROI_BUFFER_ADDR                             0xFA00
+
+#define S5P_FIMV_E_PARAM_CHANGE                                        0xFA04
+#define S5P_FIMV_E_IR_SIZE                                     0xFA08
+#define S5P_FIMV_E_GOP_CONFIG                                  0xFA0C
+#define S5P_FIMV_E_MSLICE_MODE                                 0xFA10
+#define S5P_FIMV_E_MSLICE_SIZE_MB                              0xFA14
+#define S5P_FIMV_E_MSLICE_SIZE_BITS                            0xFA18
+#define S5P_FIMV_E_FRAME_INSERTION                             0xFA1C
+
+#define S5P_FIMV_E_RC_FRAME_RATE                               0xFA20
+#define S5P_FIMV_E_RC_BIT_RATE                                 0xFA24
+#define S5P_FIMV_E_RC_ROI_CTRL                                 0xFA2C
+#define S5P_FIMV_E_PICTURE_TAG                                 0xFA30
+#define S5P_FIMV_E_BIT_COUNT_ENABLE                            0xFA34
+#define S5P_FIMV_E_MAX_BIT_COUNT                               0xFA38
+#define S5P_FIMV_E_MIN_BIT_COUNT                               0xFA3C
+
+#define S5P_FIMV_E_METADATA_BUFFER_ADDR                                0xFA40
+#define S5P_FIMV_E_METADATA_BUFFER_SIZE                                0xFA44
+
+#define S5P_FIMV_E_ENCODING_ORDER_TIME_INFO                    0xFA50
+#define S5P_FIMV_E_ENCODING_ORDER_INFO                         0xFA54
+#define S5P_FIMV_E_STREAM_BUFFER_OFFSET                                0xFA58
+#define S5P_FIMV_E_GOP_CONFIG2                                 0xFA5C
+#define S5P_FIMV_E_WEIGHT_FOR_WEIGHTED_PREDICTION              0xFA60
+
+#define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR                   0xFA70
+#define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR                  0xFA74
+#define S5P_FIMV_E_ENCODED_SOURCE_THIRD_ADDR                   0xFA78
+
+#define S5P_FIMV_E_STREAM_SIZE                                 0xFA80
+#define S5P_FIMV_E_SLICE_TYPE                                  0xFA84
+#define S5P_FIMV_E_PICTURE_COUNT                               0xFA88
+#define S5P_FIMV_E_RET_PICTURE_TAG                             0xFA8C
+
+#define S5P_FIMV_E_RECON_LUMA_DPB_ADDR                         0xFA9C
+#define S5P_FIMV_E_RECON_CHROMA_DPB_ADDR                       0xFAA0
+#define S5P_FIMV_E_METADATA_ADDR_ENC_SLICE                     0xFAA4
+#define S5P_FIMV_E_METADATA_SIZE_ENC_SLICE                     0xFAA8
+
+#define S5P_FIMV_E_NAL_DONE_INFO                               0xFAEC
+
+#define S5P_FIMV_E_MPEG4_OPTIONS                               0xFB10
+#define S5P_FIMV_E_MPEG4_HEC_PERIOD                            0xFB14
+
+#define S5P_FIMV_E_BPG_OPTIONS                                 0xFB1C
+#define S5P_FIMV_E_BPG_EXT_CTB_QP_CTRL                         0xFB20
+#define S5P_FIMV_E_BPG_CHROMA_QP_OFFSET                                0xFB24
+#define S5P_FIMV_E_BPG_EXTENSION_DATA_SIZE                     0xFB28
+
+#define S5P_FIMV_E_H264_HD_SVC_EXTENSION_0                     0xFB44
+#define S5P_FIMV_E_H264_HD_SVC_EXTENSION_1                     0xFB48
+#define S5P_FIMV_E_ASPECT_RATIO                                        0xFB4C
+#define S5P_FIMV_E_EXTENDED_SAR                                        0xFB50
+
+#define S5P_FIMV_E_H264_OPTIONS                                        0xFB54
+#define S5P_FIMV_E_H264_OPTIONS_2                              0xFB58
+#define S5P_FIMV_E_H264_LF_ALPHA_OFFSET                                0xFB5C
+#define S5P_FIMV_E_H264_LF_BETA_OFFSET                         0xFB60
+#define S5P_FIMV_E_H264_REFRESH_PERIOD                         0xFB64
+
+#define S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE                 0xFB68
+#define S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1               0xFB6C
+#define S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR               0xFB70
+#define S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1       0xFB74
+#define S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0                        0xFB78
+#define S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_1                        0xFB7C
+#define S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_2                        0xFB80
+#define S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_3                        0xFB84
+
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_0                      0xFB88
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_1                      0xFB8C
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_2                      0xFB90
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_3                      0xFB94
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_4                      0xFB98
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_5                      0xFB9C
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_6                      0xFBA0
+#define S5P_FIMV_E_H264_ASO_SLICE_ORDER_7                      0xFBA4
+#define S5P_FIMV_E_H264_CHROMA_QP_OFFSET                       0xFBA8
+
+#define S5P_FIMV_E_NUM_T_LAYER                                 0xFBAC
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER0                      0xFBB0
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER1                      0xFBB4
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER2                      0xFBB8
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER3                      0xFBBC
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER4                      0xFBC0
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER5                      0xFBC4
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER6                      0xFBC8
+
+/* For backward compatibility */
+#define S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO                 0xFC4C
+
+#define S5P_FIMV_E_H264_NAL_CONTROL                            0xFD14
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0                        0xFD18
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER1                        0xFD1C
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER2                        0xFD20
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER3                        0xFD24
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER4                        0xFD28
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER5                        0xFD2C
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER6                        0xFD30
+
+#define S5P_FIMV_E_MVC_FRAME_QP_VIEW1                          0xFD40
+#define S5P_FIMV_E_MVC_RC_FRAME_RATE_VIEW1                     0xFD44
+#define S5P_FIMV_E_MVC_RC_BIT_RATE_VIEW1                       0xFD48
+#define S5P_FIMV_E_MVC_RC_QBOUND_VIEW1                         0xFD4C
+#define S5P_FIMV_E_MVC_RC_MODE_VIEW1                           0xFD50
+#define S5P_FIMV_E_MVC_INTER_VIEW_PREDICTION_ON                        0xFD80
+
+#define S5P_FIMV_E_VP9_OPTION                                  0xFD90
+#define S5P_FIMV_E_VP9_FILTER_OPTION                           0xFD94
+#define S5P_FIMV_E_VP9_GOLDEN_FRAME_OPTION                     0xFD98
+#define S5P_FIMV_E_VP8_OPTION                                  0xFDB0
+#define S5P_FIMV_E_VP8_FILTER_OPTION                           0xFDB4
+#define S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION                     0xFDB8
+
+#define S5P_FIMV_E_HEVC_OPTIONS                                        0xFDD4
+#define S5P_FIMV_E_HEVC_REFRESH_PERIOD                         0xFDD8
+#define S5P_FIMV_E_HEVC_CHROMA_QP_OFFSET                       0xFDDC
+#define S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2                    0xFDE0
+#define S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2                      0xFDE4
+#define S5P_FIMV_E_HEVC_NAL_CONTROL                            0xFDE8
+
+#define S5P_FIMV_E_VP8_NAL_CONTROL                             0xFDF0
+#define S5P_FIMV_E_VP9_NAL_CONTROL                             0xFDF4
+
+
+#define S5P_FIMV_REG_CLEAR_BEGIN                       0xf000
+#define S5P_FIMV_REG_CLEAR_COUNT                       1024
+
+
+/* Bit Definitions */
+/* 0x1100: S5P_FIMV_HOST2RISC_CMD */
+#define S5P_FIMV_H2R_CMD_EMPTY                         0
+#define S5P_FIMV_H2R_CMD_SYS_INIT                      1
+#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE                 2
+#define S5P_FIMV_H2R_CMD_SEQ_HEADER                    3
+#define S5P_FIMV_H2R_CMD_INIT_BUFFERS                  4
+#define S5P_FIMV_H2R_CMD_NAL_START                     5
+#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE                        6
+#define S5P_FIMV_H2R_CMD_SLEEP                         7
+#define S5P_FIMV_H2R_CMD_WAKEUP                                8
+#define S5P_FIMV_H2R_CMD_LAST_FRAME                    9
+#define S5P_FIMV_H2R_CMD_DPB_FLUSH                     10
+#define S5P_FIMV_H2R_CMD_NAL_ABORT                     11
+#define S5P_FIMV_H2R_CMD_CACHE_FLUSH                   12
+#define S5P_FIMV_H2R_CMD_NAL_QUEUE                     13
+#define S5P_FIMV_H2R_CMD_STOP_QUEUE                    14
+
+
+/* 0x1104: S5P_FIMV_RISC2HOST_CMD */
+#define S5P_FIMV_RISC2HOST_CMD_MASK                    0x1FFFF
+#define S5P_FIMV_R2H_CMD_EMPTY                         0
+#define S5P_FIMV_R2H_CMD_SYS_INIT_RET                  1
+#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET             2
+#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET                  3
+#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET              4
+#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET            6
+#define S5P_FIMV_R2H_CMD_SLEEP_RET                     7
+#define S5P_FIMV_R2H_CMD_WAKEUP_RET                    8
+#define S5P_FIMV_R2H_CMD_COMPLETE_SEQ_RET              9
+#define S5P_FIMV_R2H_CMD_DPB_FLUSH_RET                 10
+#define S5P_FIMV_R2H_CMD_NAL_ABORT_RET                 11
+#define S5P_FIMV_R2H_CMD_FW_STATUS_RET                 12
+#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET                        13
+#define S5P_FIMV_R2H_CMD_FIELD_DONE_RET                        14
+#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET                        15
+#define S5P_FIMV_R2H_CMD_ENC_BUFFER_FULL_RET           16
+#define S5P_FIMV_R2H_CMD_QUEUE_DONE_RET                        17
+#define S5P_FIMV_R2H_CMD_COMPLETE_QUEUE_RET            18
+#define S5P_FIMV_R2H_CMD_CACHE_FLUSH_RET               20
+#define S5P_FIMV_R2H_CMD_ERR_RET                       32
+
+
+/* 0xF000: S5P_FIMV_FW_VERSION */
+#define S5P_FIMV_FW_VER_INFO_MASK                      0xFF
+#define S5P_FIMV_FW_VER_INFO_SHFT                      24
+#define S5P_FIMV_FW_VER_YEAR_MASK                      0xFF
+#define S5P_FIMV_FW_VER_YEAR_SHFT                      16
+#define S5P_FIMV_FW_VER_MONTH_MASK                     0xFF
+#define S5P_FIMV_FW_VER_MONTH_SHFT                     8
+#define S5P_FIMV_FW_VER_DATE_MASK                      0xFF
+#define S5P_FIMV_FW_VER_DATE_SHFT                      0
+#define S5P_FIMV_FW_VER_ALL_MASK                       0xFFFFFF
+#define S5P_FIMV_FW_VER_ALL_SHFT                       0
+
+
+/* 0xF00C: S5P_FIMV_CODEC_TYPE */
+#define MFC_FORMATS_NO_CODEC                           -1
+/* Decoder */
+#define S5P_FIMV_CODEC_H264_DEC                                0
+#define S5P_FIMV_CODEC_H264_MVC_DEC                    1
+#define S5P_FIMV_CODEC_MPEG4_DEC                       3
+#define S5P_FIMV_CODEC_FIMV1_DEC                       4
+#define S5P_FIMV_CODEC_FIMV2_DEC                       5
+#define S5P_FIMV_CODEC_FIMV3_DEC                       6
+#define S5P_FIMV_CODEC_FIMV4_DEC                       7
+#define S5P_FIMV_CODEC_H263_DEC                                8
+#define S5P_FIMV_CODEC_VC1_RCV_DEC                     9
+#define S5P_FIMV_CODEC_VC1_DEC                         10
+#define S5P_FIMV_CODEC_MPEG2_DEC                       13
+#define S5P_FIMV_CODEC_VP8_DEC                         14
+#define S5P_FIMV_CODEC_HEVC_DEC                                17
+#define S5P_FIMV_CODEC_VP9_DEC                         18
+/* Encoder */
+#define S5P_FIMV_CODEC_H264_ENC                                20
+#define S5P_FIMV_CODEC_H264_MVC_ENC                    21
+#define S5P_FIMV_CODEC_MPEG4_ENC                       23
+#define S5P_FIMV_CODEC_H263_ENC                                24
+#define S5P_FIMV_CODEC_VP8_ENC                         25
+#define S5P_FIMV_CODEC_HEVC_ENC                                26
+#define S5P_FIMV_CODEC_VP9_ENC                         27
+
+#define S5P_FIMV_CODEC_BPG_DEC                         32
+#define S5P_FIMV_CODEC_BPG_ENC                         33
+
+/* 0xF028: S5P_FIMV_MFC_VERSION */
+#define S5P_FIMV_MFC_VER_MASK                          0xFFFFFFFF
+#define S5P_FIMV_MFC_VER_SHFT                          0
+
+
+/* 0xF074: S5P_FIMV_ERROR_CODE */
+#define S5P_FIMV_ERR_STATUS_MASK                       0xFFFF
+#define S5P_FIMV_ERR_STATUS_SHIFT                      0
+#define S5P_FIMV_WARN_STATUS_MASK                      0xFFFF
+#define S5P_FIMV_WARN_STATUS_SHIFT                     16
+/* Error number */
+#define S5P_FIMV_ERR_NO_KEY_FRAME                      34
+#define S5P_FIMV_ERR_VPS_ONLY_ERROR                    42
+#define S5P_FIMV_ERR_NULL_SCRATCH                      61
+
+#define S5P_FIMV_ERR_UNSUPPORTED_FEATURE               100
+#define S5P_FIMV_ERR_UNSUPPORTED_RESOLUTION            101
+#define S5P_FIMV_ERR_HEADER_NOT_FOUND                  102
+#define S5P_FIMV_ERR_INVAILD_NAL_TYPE                  103
+#define S5P_FIMV_ERR_SEQUENCE_HEADER                   104
+#define S5P_FIMV_ERR_MFC_TIMEOUT                       140
+#define S5P_FIMV_ERR_TS_MUX_TIMEOUT                    141
+#define S5P_FIMV_ERR_G2D_TIMEOUT                       142
+#define S5P_FIMV_ERR_FRAME_CONCEAL                     150
+#define S5P_FIMV_ERR_WARNINGS_START                    160
+#define S5P_FIMV_ERR_BROKEN_LINK                       161
+#define S5P_FIMV_ERR_SYNC_POINT_NOT_RECEIVED           190
+#define S5P_FIMV_ERR_NON_PAIRED_FIELD                  191
+#define S5P_FIMV_ERR_WARNINGS_END                      222
+
+
+/* 0xF0B4: S5P_FIMV_D_DEC_OPTIONS */
+#define S5P_FIMV_D_DEC_OPT_DISPLAY_DELAY_EN_SHIFT      3
+#define S5P_FIMV_D_DEC_OPT_FMO_ASO_CTRL_MASK           0x1
+#define S5P_FIMV_D_DEC_OPT_FMO_ASO_CTRL_SHIFT          4
+#define S5P_FIMV_D_DEC_OPT_IDR_DECODING_MASK           0x1
+#define S5P_FIMV_D_DEC_OPT_IDR_DECODING_SHIFT          6
+#define S5P_FIMV_D_DEC_OPT_DISCARD_RCV_HEADER_SHIFT    7
+#define S5P_FIMV_D_DEC_OPT_CONCEAL_CONTROL_SHIFT       8
+#define S5P_FIMV_D_DEC_OPT_PARALLEL_DISABLE_SHIFT      11
+#define S5P_FIMV_D_DEC_OPT_REALLOC_CONTROL_SHIFT       13
+#define S5P_FIMV_D_DEC_OPT_SPECIAL_PARSING_SHIFT       15
+#define S5P_FIMV_D_DEC_OPT_THUMBNAIL_DECODING          16
+
+
+/* 0xF0C4: S5P_FIMV_D_SEI_ENABLE */
+#define S5P_FIMV_D_SEI_ENABLE_NEED_INIT_BUFFER_SHIFT   1
+#define S5P_FIMV_D_SEI_ENABLE_RECOVERY_PARSING_SHIFT   2
+#define S5P_FIMV_D_SEI_ENABLE_CONTENT_LIGHT_SHIFT      4
+#define S5P_FIMV_D_SEI_ENABLE_MASTERING_DISPLAY_SHIFT  5
+
+
+/* 0xF154: S5P_FIMV_D_INIT_BUFFER_OPTIONS */
+#define S5P_FIMV_D_INIT_BUF_OPT_LF_CTRL_MASK           0x3
+#define S5P_FIMV_D_INIT_BUF_OPT_LF_CTRL_SHIFT          1
+#define S5P_FIMV_D_INIT_BUF_OPT_DYNAMIC_DPB_SET_SHIFT  3
+#define S5P_FIMV_D_INIT_BUF_OPT_COPY_NOT_CODED_SHIFT   4
+#define S5P_FIMV_D_INIT_BUF_OPT_DITHERING_EN_SHIFT     6
+#define S5P_FIMV_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN      7
+
+
+/* 0xF5AC: S5P_FIMV_D_NAL_START_OPTIONS */
+#define S5P_FIMV_D_NAL_START_OPT_BLACK_BAR_SHIFT       3
+
+
+/* 0xF608: S5P_FIMV_D_DISPLAY_STATUS */
+#define S5P_FIMV_DISP_STATUS_DISPLAY_STATUS_MASK       0x7
+#define S5P_FIMV_DISP_STATUS_INTERLACE_MASK            0x1
+#define S5P_FIMV_DISP_STATUS_INTERLACE_SHIFT           3
+#define S5P_FIMV_DISP_STATUS_RES_CHANGE_MASK           0x3
+#define S5P_FIMV_DISP_STATUS_RES_CHANGE_SHIFT          4
+#define S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_MASK      0x1
+#define S5P_FIMV_DISP_STATUS_NEED_DPB_CHANGE_SHIFT     9
+#define S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_MASK  0x1
+#define S5P_FIMV_DISP_STATUS_NEED_SCRATCH_CHANGE_SHIFT 10
+#define S5P_FIMV_DISP_STATUS_NEED_EMPTY_DPB_MASK       0x1
+#define S5P_FIMV_DISP_STATUS_NEED_EMPTY_DPB_SHIFT      12
+#define S5P_FIMV_DISP_STATUS_BLACK_BAR_DETECT_MASK     0x3
+#define S5P_FIMV_DISP_STATUS_BLACK_BAR_DETECT_SHIFT    13
+#define S5P_FIMV_DISP_STATUS_NOT_DETECTED              0x0
+#define S5P_FIMV_DISP_STATUS_BLACK_BAR                 0x1
+#define S5P_FIMV_DISP_STATUS_BLACK_SCREEN              0x2
+
+
+/* 0xF618: S5P_FIMV_D_DISPLAY_FRAME_TYPE */
+#define S5P_FIMV_DISPLAY_FRAME_MASK                    0x7
+#define S5P_FIMV_DISPLAY_TEMP_INFO_MASK                        0x1
+#define S5P_FIMV_DISPLAY_TEMP_INFO_SHIFT               7
+#define S5P_FIMV_DISPLAY_FRAME_NOT_CODED               0
+#define S5P_FIMV_DISPLAY_FRAME_I                       1
+#define S5P_FIMV_DISPLAY_FRAME_P                       2
+#define S5P_FIMV_DISPLAY_FRAME_B                       3
+
+
+/* 0xF61C: S5P_FIMV_D_DISPLAY_CROP_INFO1 */
+#define S5P_FIMV_D_SHARED_CROP_LEFT_MASK               0xFFFF
+#define S5P_FIMV_D_SHARED_CROP_RIGHT_SHIFT             16
+
+
+/* 0xF620: S5P_FIMV_D_DISPLAY_CROP_INFO2 */
+#define S5P_FIMV_D_SHARED_CROP_TOP_MASK                        0xFFFF
+#define S5P_FIMV_D_SHARED_CROP_BOTTOM_SHIFT            16
+
+
+/* 0xF644: S5P_FIMV_D_DECODED_STATUS */
+#define S5P_FIMV_DEC_STATUS_DECODED_STATUS_MASK                0x7
+#define S5P_FIMV_DEC_STATUS_DECODING_ONLY              0
+#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY           1
+#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY               2
+#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY             3
+#define S5P_FIMV_DEC_STATUS_NUM_OF_TILE_MASK           0xF
+#define S5P_FIMV_DEC_STATUS_NUM_OF_TILE_SHIFT          15
+
+
+/* 0xF654: S5P_FIMV_D_DECODED_FRAME_TYPE */
+#define S5P_FIMV_DECODED_FRAME_MASK                    0x7
+#define S5P_FIMV_DECODED_FRAME_NOT_CODED               0
+#define S5P_FIMV_DECODED_FRAME_I                       1
+#define S5P_FIMV_DECODED_FRAME_P                       2
+#define S5P_FIMV_DECODED_FRAME_B                       3
+
+
+/* 0xF660: S5P_FIMV_D_DECODED_PICTURE_PROFILE */
+#define S5P_FIMV_D_DECODED_PIC_PROFILE_MASK            0x1F
+#define S5P_FIMV_D_BIT_DEPTH_CHROMA_MINUS8_MASK                0x7
+#define S5P_FIMV_D_BIT_DEPTH_CHROMA_MINUS8_SHIFT       19
+#define S5P_FIMV_D_BIT_DEPTH_LUMA_MINUS8_MASK          0x7
+#define S5P_FIMV_D_BIT_DEPTH_LUMA_MINUS8_SHIFT         16
+#define S5P_FIMV_D_PROFILE_HEVC_MAIN                   1
+#define S5P_FIMV_D_PROFILE_HEVC_MAIN_10                        2
+#define S5P_FIMV_D_PROFILE_HEVC_RANGE_EXT              4
+
+
+/* 0xF684: S5P_FIMV_D_CHROMA_FORMAT */
+#define S5P_FIMV_D_CHROMA_FORMAT_MASK                  0x3
+#define S5P_FIMV_D_COLOR_RANGE_MASK                    0x1
+#define S5P_FIMV_D_COLOR_RANGE_SHIFT                   3
+#define S5P_FIMV_D_COLOR_SPACE_MASK                    0xF
+#define S5P_FIMV_D_COLOR_SPACE_SHIFT                   4
+#define S5P_FIMV_D_COLOR_UNKNOWN                       0
+#define S5P_FIMV_D_CHROMA_400                          0
+#define S5P_FIMV_D_CHROMA_420                          1
+#define S5P_FIMV_D_CHROMA_422                          2
+#define S5P_FIMV_D_CHROMA_444                          3
+
+
+/* 0xF6D8: S5P_FIMV_D_MVC_VIEW_ID */
+#define S5P_FIMV_D_MVC_VIEW_ID_DISP_MASK               0xFFFF
+
+
+/* 0xF6DC: S5P_FIMV_D_SEI_AVAIL */
+#define S5P_FIMV_D_SEI_AVAIL_FRAME_PACK_MASK           0x1
+#define S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_MASK                0x1
+#define S5P_FIMV_D_SEI_AVAIL_CONTENT_LIGHT_SHIFT       1
+#define S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_MASK    0x1
+#define S5P_FIMV_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT   2
+
+
+/* 0xF70C: S5P_FIMV_D_VIDEO_SIGNAL_TYPE */
+#define S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_MASK         0x1
+#define S5P_FIMV_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT                29
+#define S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_MASK                0x1
+#define S5P_FIMV_D_COLOUR_DESCRIPTION_FLAG_SHIFT       24
+
+
+/* 0xF738: S5P_FIMV_D_BLACK_BAR_START_POS */
+#define S5P_FIMV_D_BLACK_BAR_START_X_SHIFT             0
+#define S5P_FIMV_D_BLACK_BAR_START_X_MASK              0xFFFF
+#define S5P_FIMV_D_BLACK_BAR_START_Y_SHIFT             16
+#define S5P_FIMV_D_BLACK_BAR_START_Y_MASK              0xFFFF
+
+
+/* 0xF73C: S5P_FIMV_D_BLACK_BAR_IMAGE_SIZE */
+#define S5P_FIMV_D_BLACK_BAR_IMAGE_W_SHIFT             0
+#define S5P_FIMV_D_BLACK_BAR_IMAGE_W_MASK              0xFFFF
+#define S5P_FIMV_D_BLACK_BAR_IMAGE_H_SHIFT             16
+#define S5P_FIMV_D_BLACK_BAR_IMAGE_H_MASK              0xFFFF
+
+
+/* 0xF784: S5P_FIMV_E_ENC_OPTIONS */
+#define S5P_FIMV_E_ENC_OPT_FRAME_SKIP_EN_MASK          0x3
+#define S5P_FIMV_E_ENC_OPT_SEQ_HEADER_CONTROL_MASK     0x1
+#define S5P_FIMV_E_ENC_OPT_SEQ_HEADER_CONTROL_SHIFT    2
+#define S5P_FIMV_E_ENC_OPT_IR_MODE_SHIFT               4
+#define S5P_FIMV_E_ENC_OPT_DISABLE_SEQ_HEADER_SHIFT    6
+#define S5P_FIMV_E_ENC_OPT_NON_REFERENCE_EN_SHIFT      9
+#define S5P_FIMV_E_ENC_OPT_PARALLEL_DISABLE_SHIFT      18
+
+
+/* 0xF788: S5P_FIMV_E_PICTURE_PROFILE */
+#define S5P_FIMV_E_PROFILE_H264_BASELINE               0
+#define S5P_FIMV_E_PROFILE_H264_MAIN                   1
+#define S5P_FIMV_E_PROFILE_H264_HIGH                   2
+#define S5P_FIMV_E_PROFILE_H264_CONSTRAINED_BASELINE   3
+#define S5P_FIMV_E_PROFILE_H264_CONSTRAINED_HIGH       5
+#define S5P_FIMV_E_PROFILE_MPEG4_SIMPLE                        0
+#define S5P_FIMV_E_PROFILE_MPEG4_ADVANCED_SIMPLE       1
+#define S5P_FIMV_E_PROFILE_HEVC_422_10_INTRA           2
+
+
+/* 0xF7A4: S5P_FIMV_E_RC_MODE */
+#define S5P_FIMV_E_RC_CBR_FIX                          0
+#define S5P_FIMV_E_RC_CBR_VAR                          1
+#define S5P_FIMV_E_RC_VBR                              2
+#define S5P_FIMV_E_RC_CBR_I_LIMIT                      3
+
+
+/* 0xFA84: S5P_FIMV_E_SLICE_TYPE */
+#define S5P_FIMV_E_SLICE_TYPE_NOT_CODED                        0
+#define S5P_FIMV_E_SLICE_TYPE_I                                1
+#define S5P_FIMV_E_SLICE_TYPE_P                                2
+#define S5P_FIMV_E_SLICE_TYPE_B                                3
+#define S5P_FIMV_E_SLICE_TYPE_SKIPPED                  4
+
+
+#endif /* __REGS_MFC_V10_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_sync.c b/drivers/media/platform/exynos/mfc/s5p_mfc_sync.c
new file mode 100644 (file)
index 0000000..a4ecf11
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_intr.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 "s5p_mfc_sync.h"
+
+#include "s5p_mfc_queue.h"
+
+#define R2H_BIT(x)     (((x) > 0) ? (1 << ((x) - 1)) : 0)
+
+static inline unsigned int mfc_r2h_bit_mask(int cmd)
+{
+       unsigned int mask = R2H_BIT(cmd);
+
+       if (cmd == S5P_FIMV_R2H_CMD_FRAME_DONE_RET)
+               mask |= (R2H_BIT(S5P_FIMV_R2H_CMD_FIELD_DONE_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_COMPLETE_SEQ_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_SLICE_DONE_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_ENC_BUFFER_FULL_RET));
+       /* FIXME: Temporal mask for S3D SEI processing */
+       else if (cmd == S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET)
+               mask |= (R2H_BIT(S5P_FIMV_R2H_CMD_FIELD_DONE_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_SLICE_DONE_RET) |
+                        R2H_BIT(S5P_FIMV_R2H_CMD_FRAME_DONE_RET));
+
+       return (mask |= R2H_BIT(S5P_FIMV_R2H_CMD_ERR_RET));
+}
+
+#define wait_condition(x, c) (x->int_condition &&              \
+               (R2H_BIT(x->int_reason) & mfc_r2h_bit_mask(c)))
+#define is_err_cond(x) ((x->int_condition) && (x->int_reason == S5P_FIMV_R2H_CMD_ERR_RET))
+
+/*
+ * Return value description
+ * 0: waked up before timeout
+ * 1: failed to get the response for the command before timeout
+*/
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+       int ret;
+
+       ret = wait_event_timeout(dev->cmd_wq,
+                       wait_condition(dev, command),
+                       msecs_to_jiffies(MFC_INT_TIMEOUT));
+       if (ret == 0) {
+               mfc_err_dev("Interrupt (dev->int_reason:%d, command:%d) timed out.\n",
+                                                       dev->int_reason, command);
+               return 1;
+       }
+       mfc_debug(2, "Finished waiting (dev->int_reason:%d, command: %d).\n",
+                                                       dev->int_reason, command);
+       return 0;
+}
+
+/*
+ * Return value description
+ *  0: waked up before timeout
+ *  1: failed to get the response for the command before timeout
+ * -1: got the error response for the command before timeout
+*/
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx, int command)
+{
+       int ret;
+       unsigned int timeout = MFC_INT_TIMEOUT;
+
+       if (command == S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET)
+               timeout = MFC_INT_SHORT_TIMEOUT;
+
+       ret = wait_event_timeout(ctx->cmd_wq,
+                       wait_condition(ctx, command),
+                       msecs_to_jiffies(timeout));
+       if (ret == 0) {
+               mfc_err_ctx("Interrupt (ctx->int_reason:%d, command:%d) timed out.\n",
+                                                       ctx->int_reason, command);
+               return 1;
+       } else if (ret > 0) {
+               if (is_err_cond(ctx)) {
+                       mfc_err_ctx("Finished (ctx->int_reason:%d, command: %d).\n",
+                                       ctx->int_reason, command);
+                       mfc_err_ctx("But error (ctx->int_err:%d).\n", ctx->int_err);
+                       return -1;
+               }
+       }
+       mfc_debug(2, "Finished waiting (ctx->int_reason:%d, command: %d).\n",
+                                                       ctx->int_reason, command);
+       return 0;
+}
+
+/* Wake up device wait_queue */
+void s5p_mfc_wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+               unsigned int err)
+{
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       dev->int_condition = 1;
+       dev->int_reason = reason;
+       dev->int_err = err;
+       wake_up(&dev->cmd_wq);
+}
+
+/* Wake up context wait_queue */
+void s5p_mfc_wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+               unsigned int err)
+{
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       ctx->int_condition = 1;
+       ctx->int_reason = reason;
+       ctx->int_err = err;
+       wake_up(&ctx->cmd_wq);
+}
+
+int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+       unsigned long wflags;
+       int new_ctx_index = 0;
+       int cnt = 0;
+       int i;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->work_bits.lock, wflags);
+
+       mfc_debug(2, "Previous context: %d (bits %08lx)\n", dev->curr_ctx,
+                                                       dev->work_bits.bits);
+
+       if (dev->preempt_ctx > MFC_NO_INSTANCE_SET) {
+               new_ctx_index = dev->preempt_ctx;
+               mfc_debug(2, "preempt_ctx is : %d\n", new_ctx_index);
+       } else {
+               for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+                       if (dev->ctx[i] && dev->ctx[i]->otf_handle) {
+                               if (test_bit(i, &dev->work_bits.bits)) {
+                                       spin_unlock_irqrestore(&dev->work_bits.lock, wflags);
+                                       return i;
+                               }
+                               break;
+                       }
+               }
+
+               new_ctx_index = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+               while (!test_bit(new_ctx_index, &dev->work_bits.bits)) {
+                       new_ctx_index = (new_ctx_index + 1) % MFC_NUM_CONTEXTS;
+                       cnt++;
+                       if (cnt > MFC_NUM_CONTEXTS) {
+                               /* No contexts to run */
+                               spin_unlock_irqrestore(&dev->work_bits.lock, wflags);
+                               return -EAGAIN;
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->work_bits.lock, wflags);
+       return new_ctx_index;
+}
+
+/* Check whether a context should be run on hardware */
+int s5p_mfc_dec_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+       int src_buf_queue_greater_than_0 = 0;
+       int dst_buf_queue_greater_than_0 = 0;
+       int ref_buf_queue_same_dpb_count_plus_5 = 0;
+
+       mfc_debug(1, "[c:%d] src = %d, dst = %d, src_nal = %d, dst_nal = %d, ref = %d, state = %d, capstat = %d\n",
+                 ctx->num, s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue),
+                 ctx->state, ctx->capture_state);
+       mfc_debug(2, "wait_state = %d\n", ctx->wait_state);
+
+       src_buf_queue_greater_than_0
+               = s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->src_buf_queue, 0);
+       dst_buf_queue_greater_than_0
+               = s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0);
+       ref_buf_queue_same_dpb_count_plus_5
+               = s5p_mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->ref_buf_queue, (ctx->dpb_count + 5));
+
+       /* If shutdown is called, do not try any cmd */
+       if (dev->shutdown)
+               return 0;
+
+       /* Context is to parse header */
+       if (ctx->state == MFCINST_GOT_INST &&
+               src_buf_queue_greater_than_0)
+               return 1;
+
+       /* Context is to decode a frame */
+       if (ctx->state == MFCINST_RUNNING &&
+               ctx->wait_state == WAIT_NONE && src_buf_queue_greater_than_0 &&
+               (dst_buf_queue_greater_than_0 || ref_buf_queue_same_dpb_count_plus_5))
+               return 1;
+
+       /* Context is to return last frame */
+       if (ctx->state == MFCINST_FINISHING &&
+               (dst_buf_queue_greater_than_0 || ref_buf_queue_same_dpb_count_plus_5))
+               return 1;
+
+       /* Context is to set buffers */
+       if (ctx->state == MFCINST_HEAD_PARSED &&
+               (dst_buf_queue_greater_than_0 && ctx->wait_state == WAIT_NONE))
+               return 1;
+
+       /* Resolution change */
+       if ((ctx->state == MFCINST_RES_CHANGE_INIT || ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
+               dst_buf_queue_greater_than_0)
+               return 1;
+
+       if (ctx->state == MFCINST_RES_CHANGE_END &&
+               src_buf_queue_greater_than_0)
+               return 1;
+
+       mfc_debug(2, "ctx is not ready.\n");
+
+       return 0;
+}
+
+int s5p_mfc_enc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_enc *enc = ctx->enc_priv;
+       struct s5p_mfc_dev *dev = ctx->dev;
+       struct s5p_mfc_enc_params *p = &enc->params;
+       int src_buf_queue_greater_than_0 = 0;
+       int dst_buf_queue_greater_than_0 = 0;
+
+       mfc_debug(1, "[c:%d] src = %d, dst = %d, src_nal = %d, dst_nal = %d, ref = %d, state = %d\n",
+                 ctx->num, s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_nal_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_nal_queue),
+                 s5p_mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue),
+                 ctx->state);
+
+       src_buf_queue_greater_than_0
+               = s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->src_buf_queue, 0);
+       dst_buf_queue_greater_than_0
+               = s5p_mfc_is_queue_count_greater(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0);
+
+       /* If shutdown is called, do not try any cmd */
+       if (dev->shutdown)
+               return 0;
+
+       /* context is ready to make header */
+       if (ctx->state == MFCINST_GOT_INST &&
+               dst_buf_queue_greater_than_0) {
+               if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY) {
+                       if (src_buf_queue_greater_than_0)
+                               return 1;
+               } else {
+                       return 1;
+               }
+       }
+
+       /* context is ready to allocate DPB */
+       if (ctx->state == MFCINST_HEAD_PARSED &&
+               dst_buf_queue_greater_than_0)
+               return 1;
+
+       /* context is ready to encode a frame */
+       if (ctx->state == MFCINST_RUNNING &&
+               src_buf_queue_greater_than_0 && dst_buf_queue_greater_than_0)
+               return 1;
+
+       /* context is ready to encode a frame in case of B frame */
+       if (ctx->state == MFCINST_RUNNING_NO_OUTPUT &&
+               src_buf_queue_greater_than_0 && dst_buf_queue_greater_than_0)
+               return 1;
+
+       /* context is ready to encode a frame for NAL_ABORT command */
+       if (ctx->state == MFCINST_ABORT_INST &&
+               src_buf_queue_greater_than_0 && dst_buf_queue_greater_than_0)
+               return 1;
+
+       /* context is ready to encode remain frames */
+       if (ctx->state == MFCINST_FINISHING &&
+               src_buf_queue_greater_than_0 && dst_buf_queue_greater_than_0)
+               return 1;
+
+       mfc_debug(2, "ctx is not ready.\n");
+
+       return 0;
+}
+
+int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+       if (ctx->type == MFCINST_DECODER)
+               return s5p_mfc_dec_ctx_ready(ctx);
+       else if (ctx->type == MFCINST_ENCODER)
+               return s5p_mfc_enc_ctx_ready(ctx);
+
+       return 0;
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_sync.h b/drivers/media/platform/exynos/mfc/s5p_mfc_sync.h
new file mode 100644 (file)
index 0000000..c1cce5b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_intr.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 __S5P_MFC_INTR_H
+#define __S5P_MFC_INTR_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+#define need_to_dpb_flush(ctx)         \
+       ((ctx->state == MFCINST_FINISHING) ||   \
+         (ctx->state == MFCINST_RUNNING))
+#define need_to_wait_nal_abort(ctx)             \
+       (ctx->state == MFCINST_ABORT_INST)
+#define need_to_continue(ctx)                  \
+       ((ctx->state == MFCINST_DPB_FLUSHING) ||\
+       (ctx->state == MFCINST_ABORT_INST) ||   \
+       (ctx->state == MFCINST_RETURN_INST) ||  \
+       (ctx->state == MFCINST_SPECIAL_PARSING) ||      \
+       (ctx->state == MFCINST_SPECIAL_PARSING_NAL))
+#define need_to_special_parsing(ctx)           \
+       ((ctx->state == MFCINST_GOT_INST) ||    \
+        (ctx->state == MFCINST_HEAD_PARSED))
+#define need_to_special_parsing_nal(ctx)       \
+       (ctx->state == MFCINST_RUNNING)
+#define ready_to_get_crop(ctx)                 \
+       ((ctx->state == MFCINST_HEAD_PARSED) || \
+        (ctx->state == MFCINST_RUNNING) || \
+        (ctx->state == MFCINST_FINISHING))
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx, int command);
+void s5p_mfc_wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+               unsigned int err);
+void s5p_mfc_wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+               unsigned int err);
+
+int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev);
+int s5p_mfc_dec_ctx_ready(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_enc_ctx_ready(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx);
+
+
+static inline void s5p_mfc_set_bit(int num, struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&data->lock, flags);
+       __set_bit(num, &data->bits);
+       spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static inline void s5p_mfc_clear_bit(int num, struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&data->lock, flags);
+       __clear_bit(num, &data->bits);
+       spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static inline int s5p_mfc_test_bit(int num, struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       int ret;
+       spin_lock_irqsave(&data->lock, flags);
+       ret = test_bit(num, &data->bits);
+       spin_unlock_irqrestore(&data->lock, flags);
+       return ret;
+}
+
+static inline int s5p_mfc_is_all_bits_cleared(struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       int ret;
+       spin_lock_irqsave(&data->lock, flags);
+       ret = ((data->bits) == 0) ? 1 : 0;
+       spin_unlock_irqrestore(&data->lock, flags);
+       return ret;
+}
+
+static inline void s5p_mfc_clear_all_bits(struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&data->lock, flags);
+       data->bits = 0;
+       spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static inline unsigned long s5p_mfc_get_bits(struct s5p_mfc_bits *data)
+{
+       unsigned long flags;
+       unsigned long ret;
+       spin_lock_irqsave(&data->lock, flags);
+       ret = data->bits;
+       spin_unlock_irqrestore(&data->lock, flags);
+       return ret;
+}
+
+static inline void s5p_mfc_create_bits(struct s5p_mfc_bits *data)
+{
+       spin_lock_init(&data->lock);
+       s5p_mfc_clear_all_bits(data);
+}
+
+static inline void s5p_mfc_delete_bits(struct s5p_mfc_bits *data)
+{
+       s5p_mfc_clear_all_bits(data);
+}
+
+static inline int s5p_mfc_is_work_to_do(struct s5p_mfc_dev *dev)
+{
+       return (!s5p_mfc_is_all_bits_cleared(&dev->work_bits));
+}
+
+#endif /* __S5P_MFC_INTR_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_utils.c b/drivers/media/platform/exynos/mfc/s5p_mfc_utils.c
new file mode 100644 (file)
index 0000000..304b689
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_utils.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/smc.h>
+
+#include "s5p_mfc_utils.h"
+
+#include "s5p_mfc_mem.h"
+
+int s5p_mfc_check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
+{
+       int i;
+
+       if (!fmt)
+               return -EINVAL;
+
+       if (fmt->mem_planes != vb->num_planes) {
+               mfc_err_dev("plane number is different (%d != %d)\n",
+                               fmt->mem_planes, vb->num_planes);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < vb->num_planes; i++) {
+               if (!s5p_mfc_mem_get_daddr_vb(vb, i)) {
+                       mfc_err_dev("failed to get plane cookie\n");
+                       return -ENOMEM;
+               }
+
+               mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08llx\n",
+                               vb->index, i,
+                               s5p_mfc_mem_get_daddr_vb(vb, i));
+       }
+
+       return 0;
+}
+
+int mfc_stream_buf_prot(struct s5p_mfc_ctx *ctx,
+                               struct s5p_mfc_buf *buf, bool en)
+{
+       return 0;
+}
+
+int mfc_raw_buf_prot(struct s5p_mfc_ctx *ctx,
+                               struct s5p_mfc_buf *buf, bool en)
+{
+       return 0;
+}
+
+void s5p_mfc_raw_protect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index)
+{
+       if (!test_bit(index, &ctx->raw_protect_flag)) {
+               if (mfc_raw_buf_prot(ctx, mfc_buf, true)) {
+                       mfc_err_ctx("failed to CFW_PROT\n");
+               } else {
+                       set_bit(index, &ctx->raw_protect_flag);
+                       mfc_debug(2, "[index:%d] raw protect, flag: %#lx\n",
+                                       index, ctx->raw_protect_flag);
+               }
+       }
+}
+
+void s5p_mfc_raw_unprotect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index)
+{
+       if (test_bit(index, &ctx->raw_protect_flag)) {
+               if (mfc_raw_buf_prot(ctx, mfc_buf, false)) {
+                       mfc_err_ctx("failed to CFW_UNPROT\n");
+               } else {
+                       clear_bit(index, &ctx->raw_protect_flag);
+                       mfc_debug(2, "[index:%d] raw unprotect, flag: %#lx\n",
+                                       index, ctx->raw_protect_flag);
+               }
+       }
+}
+
+void s5p_mfc_stream_protect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index)
+{
+       if (!test_bit(index, &ctx->stream_protect_flag)) {
+               if (mfc_stream_buf_prot(ctx, mfc_buf, true)) {
+                       mfc_err_ctx("failed to CFW_PROT\n");
+               } else {
+                       set_bit(index, &ctx->stream_protect_flag);
+                       mfc_debug(2, "[index:%d] stream protect, flag: %#lx\n",
+                                       index, ctx->stream_protect_flag);
+               }
+       }
+}
+
+void s5p_mfc_stream_unprotect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index)
+{
+       if (test_bit(index, &ctx->stream_protect_flag)) {
+               if (mfc_stream_buf_prot(ctx, mfc_buf, false)) {
+                       mfc_err_ctx("failed to CFW_UNPROT\n");
+               } else {
+                       clear_bit(index, &ctx->stream_protect_flag);
+                       mfc_debug(2, "[index:%d] stream unprotect, flag: %#lx\n",
+                                       index, ctx->stream_protect_flag);
+               }
+       }
+}
+
+static int mfc_calc_plane(int width, int height, int is_tiled)
+{
+       int mbX, mbY;
+
+       mbX = (width + 15)/16;
+       mbY = (height + 15)/16;
+
+       /* Alignment for interlaced processing */
+       if (is_tiled)
+               mbY = (mbY + 1) / 2 * 2;
+
+       return (mbX * 16) * (mbY * 16);
+}
+
+static void mfc_set_linear_stride_size(struct s5p_mfc_ctx *ctx,
+                               struct s5p_mfc_fmt *fmt)
+{
+       struct s5p_mfc_raw_info *raw;
+       int i;
+
+       raw = &ctx->raw_buf;
+
+       switch (fmt->fourcc) {
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YUV420N:
+       case V4L2_PIX_FMT_YVU420M:
+               raw->stride[0] = ALIGN(ctx->img_width, 16);
+               raw->stride[1] = ALIGN(raw->stride[0] >> 1, 16);
+               raw->stride[2] = ALIGN(raw->stride[0] >> 1, 16);
+               break;
+       case V4L2_PIX_FMT_NV12MT_16X16:
+       case V4L2_PIX_FMT_NV12MT:
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV12N:
+       case V4L2_PIX_FMT_NV21M:
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               raw->stride[0] = ALIGN(ctx->img_width, 16);
+               raw->stride[1] = ALIGN(ctx->img_width, 16);
+               raw->stride[2] = 0;
+               break;
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV12N_10B:
+       case V4L2_PIX_FMT_NV21M_S10B:
+       case V4L2_PIX_FMT_NV16M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+               raw->stride[0] = S10B_8B_STRIDE(ctx->img_width);
+               raw->stride[1] = S10B_8B_STRIDE(ctx->img_width);
+               raw->stride[2] = 0;
+               raw->stride_2bits[0] = S10B_2B_STRIDE(ctx->img_width);
+               raw->stride_2bits[1] = S10B_2B_STRIDE(ctx->img_width);
+               raw->stride_2bits[2] = 0;
+               break;
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV21M_P010:
+       case V4L2_PIX_FMT_NV61M_P210:
+       case V4L2_PIX_FMT_NV16M_P210:
+               raw->stride[0] = ALIGN(ctx->img_width, 16) * 2;
+               raw->stride[1] = ALIGN(ctx->img_width, 16) * 2;
+               raw->stride[2] = 0;
+               raw->stride_2bits[0] = 0;
+               raw->stride_2bits[1] = 0;
+               raw->stride_2bits[2] = 0;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               ctx->raw_buf.stride[0] = ctx->img_width * 3;
+               ctx->raw_buf.stride[1] = 0;
+               ctx->raw_buf.stride[2] = 0;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               ctx->raw_buf.stride[0] = ctx->img_width * 2;
+               ctx->raw_buf.stride[1] = 0;
+               ctx->raw_buf.stride[2] = 0;
+               break;
+       case V4L2_PIX_FMT_RGB32X:
+       case V4L2_PIX_FMT_BGR32:
+       case V4L2_PIX_FMT_ARGB32:
+               ctx->raw_buf.stride[0] = (ctx->buf_stride > ctx->img_width) ?
+                       (ALIGN(ctx->img_width, 16) * 4) : (ctx->img_width * 4);
+               ctx->raw_buf.stride[1] = 0;
+               ctx->raw_buf.stride[2] = 0;
+               break;
+       default:
+               break;
+       }
+
+       /* Decoder needs multiple of 16 alignment for stride */
+       if (ctx->type == MFCINST_DECODER) {
+               for (i = 0; i < 3; i++)
+                       ctx->raw_buf.stride[i] =
+                               ALIGN(ctx->raw_buf.stride[i], 16);
+       }
+}
+
+void s5p_mfc_dec_calc_dpb_size(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_raw_info *raw;
+       int i;
+       int extra = MFC_LINEAR_BUF_SIZE;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       raw = &ctx->raw_buf;
+       raw->total_plane_size = 0;
+
+       dec = ctx->dec_priv;
+
+       for (i = 0; i < raw->num_planes; i++) {
+               raw->plane_size[i] = 0;
+               raw->plane_size_2bits[i] = 0;
+       }
+
+       switch (ctx->dst_fmt->fourcc) {
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV21M_S10B:
+               raw->plane_size[0] = NV12M_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV12M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[0] = NV12M_Y_2B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[1] = NV12M_CBCR_2B_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV21M:
+               raw->plane_size[0] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) + extra;
+               raw->plane_size[1] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) / 2 + extra;
+               break;
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV21M_P010:
+               raw->plane_size[0] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) * 2 + extra;
+               raw->plane_size[1] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) + extra;
+               break;
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YVU420M:
+               raw->plane_size[0] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) + extra;
+               raw->plane_size[1] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) / 2 + extra;
+               raw->plane_size[2] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) / 2 + extra;
+               break;
+       case V4L2_PIX_FMT_NV16M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+               raw->plane_size[0] = NV16M_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV16M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[0] = NV16M_Y_2B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[1] = NV16M_CBCR_2B_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               raw->plane_size[0] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) + extra;
+               raw->plane_size[1] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) + extra;
+               break;
+       case V4L2_PIX_FMT_NV16M_P210:
+       case V4L2_PIX_FMT_NV61M_P210:
+               raw->plane_size[0] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) * 2 + extra;
+               raw->plane_size[1] = mfc_calc_plane(ctx->img_width, ctx->img_height, 0) * 2 + extra;
+               break;
+       /* non-contiguous single fd format */
+       case V4L2_PIX_FMT_NV12N_10B:
+               raw->plane_size[0] = NV12N_10B_Y_8B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV12N_10B_CBCR_8B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[0] = NV12N_10B_Y_2B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[1] = NV12N_10B_CBCR_2B_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_NV12N:
+               raw->plane_size[0] = NV12N_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV12N_CBCR_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_YUV420N:
+               raw->plane_size[0] = YUV420N_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = YUV420N_CB_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[2] = YUV420N_CR_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       default:
+               mfc_err_ctx("Invalid pixelformat : %s\n", ctx->dst_fmt->name);
+               break;
+       }
+
+       mfc_set_linear_stride_size(ctx, ctx->dst_fmt);
+
+       for (i = 0; i < raw->num_planes; i++) {
+               if (raw->plane_size[i] < ctx->min_dpb_size[i]) {
+                       mfc_info_dev("plane[%d] size is changed %d -> %d\n",
+                                       i, raw->plane_size[i], ctx->min_dpb_size[i]);
+                       raw->plane_size[i] = ctx->min_dpb_size[i];
+               }
+       }
+
+       for (i = 0; i < raw->num_planes; i++) {
+               raw->total_plane_size += raw->plane_size[i];
+               mfc_debug(2, "Plane[%d] size = %d, stride = %d\n",
+                       i, raw->plane_size[i], raw->stride[i]);
+       }
+       if (ctx->is_10bit) {
+               for (i = 0; i < raw->num_planes; i++) {
+                       raw->total_plane_size += raw->plane_size_2bits[i];
+                       mfc_debug(2, "Plane[%d] 2bit size = %d, stride = %d\n",
+                                       i, raw->plane_size_2bits[i],
+                                       raw->stride_2bits[i]);
+               }
+       }
+       mfc_debug(2, "total plane size: %d\n", raw->total_plane_size);
+
+       if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx)) {
+               ctx->mv_size = DEC_MV_SIZE_MB(ctx->img_width, ctx->img_height);
+               ctx->mv_size = ALIGN(ctx->mv_size, 32);
+       } else if (IS_HEVC_DEC(ctx) || IS_BPG_DEC(ctx)) {
+               ctx->mv_size = DEC_HEVC_MV_SIZE(ctx->img_width, ctx->img_height);
+               ctx->mv_size = ALIGN(ctx->mv_size, 32);
+       } else {
+               ctx->mv_size = 0;
+       }
+}
+
+void s5p_mfc_enc_calc_src_size(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dev *dev;
+       struct s5p_mfc_raw_info *raw;
+       unsigned int mb_width, mb_height, default_size;
+       int i, extra;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dev = ctx->dev;
+       raw = &ctx->raw_buf;
+       raw->total_plane_size = 0;
+       mb_width = WIDTH_MB(ctx->img_width);
+       mb_height = HEIGHT_MB(ctx->img_height);
+       extra = MFC_LINEAR_BUF_SIZE;
+       default_size = mb_width * mb_height * 256;
+
+       for (i = 0; i < raw->num_planes; i++) {
+               raw->plane_size[i] = 0;
+               raw->plane_size_2bits[i] = 0;
+       }
+
+       switch (ctx->src_fmt->fourcc) {
+       case V4L2_PIX_FMT_YUV420M:
+       case V4L2_PIX_FMT_YUV420N:
+       case V4L2_PIX_FMT_YVU420M:
+               raw->plane_size[0] = ALIGN(default_size, 256) + extra;
+               raw->plane_size[1] = ALIGN(default_size >> 2, 256) + extra;
+               raw->plane_size[2] = ALIGN(default_size >> 2, 256) + extra;
+               break;
+       case V4L2_PIX_FMT_NV12M_S10B:
+       case V4L2_PIX_FMT_NV21M_S10B:
+               raw->plane_size[0] = NV12M_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV12M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[0] = NV12M_Y_2B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[1] = NV12M_CBCR_2B_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_NV12MT_16X16:
+       case V4L2_PIX_FMT_NV12M:
+       case V4L2_PIX_FMT_NV12N:
+       case V4L2_PIX_FMT_NV21M:
+               raw->plane_size[0] = ALIGN(default_size, 256) + extra;
+               raw->plane_size[1] = ALIGN(default_size / 2, 256) + extra;
+               break;
+       case V4L2_PIX_FMT_NV12M_P010:
+       case V4L2_PIX_FMT_NV21M_P010:
+               raw->plane_size[0] = ALIGN(default_size, 256) * 2 + extra;
+               raw->plane_size[1] = ALIGN(default_size, 256) + extra;
+               break;
+       case V4L2_PIX_FMT_NV16M_S10B:
+       case V4L2_PIX_FMT_NV61M_S10B:
+               raw->plane_size[0] = NV16M_Y_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size[1] = NV16M_CBCR_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[0] = NV16M_Y_2B_SIZE(ctx->img_width, ctx->img_height);
+               raw->plane_size_2bits[1] = NV16M_CBCR_2B_SIZE(ctx->img_width, ctx->img_height);
+               break;
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               raw->plane_size[0] = ALIGN(default_size, 256) + extra;
+               raw->plane_size[1] = ALIGN(default_size, 256) + extra;
+               break;
+       case V4L2_PIX_FMT_NV16M_P210:
+       case V4L2_PIX_FMT_NV61M_P210:
+               raw->plane_size[0] = ALIGN(default_size, 256) * 2 + extra;
+               raw->plane_size[1] = ALIGN(default_size, 256) * 2 + extra;
+               break;
+       default:
+               mfc_err_ctx("Invalid pixel format(%d)\n", ctx->src_fmt->fourcc);
+               break;
+       }
+
+       mfc_set_linear_stride_size(ctx, ctx->src_fmt);
+
+       for (i = 0; i < raw->num_planes; i++) {
+               raw->total_plane_size += raw->plane_size[i];
+               mfc_debug(2, "Plane[%d] size = %d, stride = %d\n",
+                       i, raw->plane_size[i], raw->stride[i]);
+       }
+       if (ctx->is_10bit) {
+               for (i = 0; i < raw->num_planes; i++) {
+                       raw->total_plane_size += raw->plane_size_2bits[i];
+                       mfc_debug(2, "Plane[%d] 2bit size = %d, stride = %d\n",
+                                       i, raw->plane_size_2bits[i],
+                                       raw->stride_2bits[i]);
+               }
+       }
+       mfc_debug(2, "total plane size: %d\n", raw->total_plane_size);
+}
+
+void s5p_mfc_cleanup_assigned_dpb(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *dst_mb;
+       int i;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       if (ctx->is_drm && ctx->raw_protect_flag) {
+               mfc_debug(2, "raw_protect_flag(%#lx) will be released\n",
+                               ctx->raw_protect_flag);
+               for (i = 0; i < MFC_MAX_DPBS; i++) {
+                       dst_mb = dec->assigned_dpb[i];
+
+                       s5p_mfc_raw_unprotect(ctx, dst_mb, i);
+               }
+               s5p_mfc_clear_assigned_dpb(ctx);
+       }
+}
+
+void s5p_mfc_unprotect_released_dpb(struct s5p_mfc_ctx *ctx, unsigned int released_flag)
+{
+       struct s5p_mfc_dec *dec;
+       struct s5p_mfc_buf *dst_mb;
+       int i;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       if (ctx->is_drm) {
+               for (i = 0; i < MFC_MAX_DPBS; i++) {
+                       if (released_flag & (1 << i)) {
+                               dst_mb = dec->assigned_dpb[i];
+                               s5p_mfc_raw_unprotect(ctx, dst_mb, i);
+                       }
+               }
+       }
+
+}
+
+void s5p_mfc_protect_dpb(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *dst_mb)
+{
+       struct s5p_mfc_dec *dec;
+       int dst_index;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       dst_index = dst_mb->vb.vb2_buf.index;
+
+       if (ctx->is_drm) {
+               dec->assigned_dpb[dst_index] = dst_mb;
+               s5p_mfc_raw_protect(ctx, dst_mb, dst_index);
+       }
+}
+
+void s5p_mfc_watchdog_tick(unsigned long arg)
+{
+       struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
+
+       if (!dev) {
+               mfc_err_dev("no mfc device to run\n");
+               return;
+       }
+
+       mfc_debug(5, "watchdog is ticking!\n");
+
+       if (atomic_read(&dev->watchdog_tick_running))
+               atomic_inc(&dev->watchdog_tick_cnt);
+       else
+               atomic_set(&dev->watchdog_tick_cnt, 0);
+
+       if (atomic_read(&dev->watchdog_tick_cnt) >= WATCHDOG_TICK_CNT_TO_START_WATCHDOG) {
+               /* This means that hw is busy and no interrupts were
+                * generated by hw for the Nth time of running this
+                * watchdog timer. This usually means a serious hw
+                * error. Now it is time to kill all instances and
+                * reset the MFC. */
+               mfc_err_dev("[%d] Time out during waiting for HW\n",
+                               atomic_read(&dev->watchdog_tick_cnt));
+               queue_work(dev->watchdog_wq, &dev->watchdog_work);
+       }
+
+       dev->watchdog_timer.expires = jiffies +
+                                       msecs_to_jiffies(WATCHDOG_TICK_INTERVAL);
+       add_timer(&dev->watchdog_timer);
+}
+
+void s5p_mfc_watchdog_start_tick(struct s5p_mfc_dev *dev)
+{
+       if (atomic_read(&dev->watchdog_tick_running)) {
+               mfc_debug(2, "watchdog timer was already started!\n");
+       } else {
+               mfc_debug(2, "watchdog timer is now started!\n");
+               atomic_set(&dev->watchdog_tick_running, 1);
+       }
+
+       /* Reset the timeout watchdog */
+       atomic_set(&dev->watchdog_tick_cnt, 0);
+}
+
+void s5p_mfc_watchdog_stop_tick(struct s5p_mfc_dev *dev)
+{
+       if (atomic_read(&dev->watchdog_tick_running)) {
+               mfc_debug(2, "watchdog timer is now stopped!\n");
+               atomic_set(&dev->watchdog_tick_running, 0);
+       } else {
+               mfc_debug(2, "watchdog timer was already stopped!\n");
+       }
+
+       /* Reset the timeout watchdog */
+       atomic_set(&dev->watchdog_tick_cnt, 0);
+}
+
+void s5p_mfc_watchdog_reset_tick(struct s5p_mfc_dev *dev)
+{
+       mfc_debug(2, "watchdog timer reset!\n");
+
+       /* Reset the timeout watchdog */
+       atomic_set(&dev->watchdog_tick_cnt, 0);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_utils.h b/drivers/media/platform/exynos/mfc/s5p_mfc_utils.h
new file mode 100644 (file)
index 0000000..cebcb2d
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_utils.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 __S5P_MFC_UTILS_H
+#define __S5P_MFC_UTILS_H __FILE__
+
+#include <linux/time.h>
+
+#include "s5p_mfc_common.h"
+
+static inline void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+       dev->int_condition = 0;
+       dev->int_reason = 0;
+       dev->int_err = 0;
+}
+
+static inline void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+       ctx->int_condition = 0;
+       ctx->int_reason = 0;
+       ctx->int_err = 0;
+}
+
+static inline void s5p_mfc_change_state(struct s5p_mfc_ctx *ctx, enum s5p_mfc_inst_state state)
+{
+       struct s5p_mfc_dev *dev = ctx->dev;
+
+       MFC_TRACE_CTX("** state : %d\n", state);
+       ctx->state = state;
+}
+
+static inline enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       enum s5p_mfc_node_type node_type;
+
+       if (!vdev) {
+               mfc_err_dev("failed to get video_device\n");
+               return MFCNODE_INVALID;
+       }
+
+       mfc_debug(2, "video_device index: %d\n", vdev->index);
+
+       switch (vdev->index) {
+       case 0:
+               node_type = MFCNODE_DECODER;
+               break;
+       case 1:
+               node_type = MFCNODE_ENCODER;
+               break;
+       case 2:
+               node_type = MFCNODE_DECODER_DRM;
+               break;
+       case 3:
+               node_type = MFCNODE_ENCODER_DRM;
+               break;
+       case 4:
+               node_type = MFCNODE_ENCODER_OTF;
+               break;
+       case 5:
+               node_type = MFCNODE_ENCODER_OTF_DRM;
+               break;
+       default:
+               node_type = MFCNODE_INVALID;
+               break;
+       }
+
+       return node_type;
+}
+
+static inline int s5p_mfc_is_decoder_node(enum s5p_mfc_node_type node)
+{
+       if (node == MFCNODE_DECODER || node == MFCNODE_DECODER_DRM)
+               return 1;
+
+       return 0;
+}
+
+static inline int s5p_mfc_is_drm_node(enum s5p_mfc_node_type node)
+{
+       if (node == MFCNODE_DECODER_DRM || node == MFCNODE_ENCODER_DRM ||
+                       node == MFCNODE_ENCODER_OTF_DRM)
+               return 1;
+
+       return 0;
+}
+
+static inline int s5p_mfc_is_encoder_otf_node(enum s5p_mfc_node_type node)
+{
+       if (node == MFCNODE_ENCODER_OTF || node == MFCNODE_ENCODER_OTF_DRM)
+               return 1;
+
+       return 0;
+}
+
+int s5p_mfc_check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb);
+
+void s5p_mfc_raw_protect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index);
+void s5p_mfc_raw_unprotect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index);
+void s5p_mfc_stream_protect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index);
+void s5p_mfc_stream_unprotect(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *mfc_buf,
+                                       int index);
+
+void s5p_mfc_dec_calc_dpb_size(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_enc_calc_src_size(struct s5p_mfc_ctx *ctx);
+
+static inline void s5p_mfc_cleanup_assigned_fd(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       int i;
+
+       dec = ctx->dec_priv;
+
+       for (i = 0; i < MFC_MAX_DPBS; i++)
+               dec->assigned_fd[i] = MFC_INFO_INIT_FD;
+}
+
+static inline void s5p_mfc_clear_assigned_dpb(struct s5p_mfc_ctx *ctx)
+{
+       struct s5p_mfc_dec *dec;
+       int i;
+
+       if (!ctx) {
+               mfc_err_dev("no mfc context to run\n");
+               return;
+       }
+
+       dec = ctx->dec_priv;
+       if (!dec) {
+               mfc_err_dev("no mfc decoder to run\n");
+               return;
+       }
+
+       for (i = 0; i < MFC_MAX_DPBS; i++)
+               dec->assigned_dpb[i] = NULL;
+}
+
+void s5p_mfc_cleanup_assigned_dpb(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_unprotect_released_dpb(struct s5p_mfc_ctx *ctx, unsigned int released_flag);
+void s5p_mfc_protect_dpb(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf *dst_mb);
+
+/* Watchdog interval */
+#define WATCHDOG_TICK_INTERVAL   1000
+/* After how many executions watchdog should assume lock up */
+#define WATCHDOG_TICK_CNT_TO_START_WATCHDOG        5
+
+void s5p_mfc_watchdog_tick(unsigned long arg);
+void s5p_mfc_watchdog_start_tick(struct s5p_mfc_dev *dev);
+void s5p_mfc_watchdog_stop_tick(struct s5p_mfc_dev *dev);
+void s5p_mfc_watchdog_reset_tick(struct s5p_mfc_dev *dev);
+
+#endif /* __S5P_MFC_UTILS_H */
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.c b/drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.c
new file mode 100644 (file)
index 0000000..7dbd69c
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.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.
+ */
+#ifdef BIGDATA_LOGGING_ENABLE
+#include <linux/sec_debug.h>
+#endif
+
+#include "s5p_mfc_watchdog.h"
+
+#include "s5p_mfc_sync.h"
+
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_cal.h"
+#include "s5p_mfc_reg.h"
+
+#include "s5p_mfc_queue.h"
+#include "s5p_mfc_utils.h"
+
+#define MFC_SFR_AREA_COUNT     22
+static void mfc_dump_regs(struct s5p_mfc_dev *dev)
+{
+       int i;
+       struct s5p_mfc_buf_size_v6 *buf_size = NULL;
+       int addr[MFC_SFR_AREA_COUNT][2] = {
+               { 0x0, 0x80 },
+               { 0x1000, 0xCD0 },
+               { 0xF000, 0xFF8 },
+               { 0x2000, 0xA00 },
+               { 0x2f00, 0x6C },
+               { 0x3000, 0x40 },
+               { 0x3094, 0x4 },
+               { 0x30b4, 0x8 },
+               { 0x3110, 0x10 },
+               { 0x5000, 0x100 },
+               { 0x5200, 0x300 },
+               { 0x5600, 0x100 },
+               { 0x5800, 0x100 },
+               { 0x5A00, 0x100 },
+               { 0x6000, 0xC4 },
+               { 0x7000, 0x21C },
+               { 0x8000, 0x20C },
+               { 0x9000, 0x10C },
+               { 0xA000, 0x20C },
+               { 0xB000, 0x444 },
+               { 0xC000, 0x84 },
+               { 0xD000, 0x74 },
+       };
+
+       pr_err("-----------dumping MFC registers (SFR base = %p, dev = %p)\n",
+                               dev->regs_base, dev);
+
+       s5p_mfc_enable_all_clocks(dev);
+
+       for (i = 0; i < MFC_SFR_AREA_COUNT; i++) {
+               printk("[%04X .. %04X]\n", addr[i][0], addr[i][0] + addr[i][1]);
+               print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4, dev->regs_base + addr[i][0],
+                               addr[i][1], false);
+               printk("...\n");
+       }
+
+       if (dbg_enable) {
+               buf_size = dev->variant->buf_size->buf;
+               printk("[DBG INFO dump]\n");
+               print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 32, 4, dev->dbg_info_buf.vaddr,
+                       buf_size->dbg_info_buf, false);
+               printk("...\n");
+       }
+}
+
+const u32 s5p_mfc_logging_sfr_set1[MFC_SFR_LOGGING_COUNT_SET1] = {
+       0x1000, 0x1004, 0x100C, 0x1010
+};
+
+const u32 s5p_mfc_logging_sfr_set2[MFC_SFR_LOGGING_COUNT_SET2] = {
+       0x0070, 0x10B4, 0x2020, 0x2028,
+       0x204C, 0x20B4, 0x3000, 0x3004,
+       0x3010, 0x301C, 0x3110, 0x5A54,
+       0x5A80, 0x5A88, 0x5A94, 0x6038,
+       0x6050, 0x6168, 0x7018, 0x7110,
+       0x7114, 0xF088, 0xFFD0
+};
+
+int mfc_change_hex_to_ascii(u32 hex, u32 byte, char *ascii, int idx)
+{
+       int i;
+       char tmp;
+
+       for (i = 0; i < byte; i++) {
+               if (idx >= MFC_LOGGING_DATA_SIZE) {
+                       pr_err("logging data size exceed: %d\n", idx);
+                       return idx;
+               }
+
+               tmp = (hex >> ((byte - 1 - i) * 4)) & 0xF;
+               if (tmp > 9)
+                       ascii[idx] = tmp + 'a' - 0xa;
+               else if (tmp <= 9)
+                       ascii[idx] = tmp + '0';
+               idx++;
+       }
+
+       /* space */
+       if (idx < MFC_LOGGING_DATA_SIZE)
+               ascii[idx] = ' ';
+
+       return ++idx;
+}
+
+static void mfc_save_logging_sfr(struct s5p_mfc_dev *dev)
+{
+       char *errorinfo;
+       int i, idx = 0;
+
+       pr_err("-----------logging MFC error info-----------\n");
+       errorinfo = dev->logging_data->errorinfo;
+       for (i = 0; i < MFC_SFR_LOGGING_COUNT_SET1; i++)
+               dev->logging_data->SFRs_set1[i] = MFC_READL(s5p_mfc_logging_sfr_set1[i]);
+       for (i = 0; i < MFC_SFR_LOGGING_COUNT_SET2; i++)
+               dev->logging_data->SFRs_set2[i] = MFC_READL(s5p_mfc_logging_sfr_set2[i]);
+
+       idx = mfc_change_hex_to_ascii(dev->logging_data->cause, 8, errorinfo, idx);
+       idx = mfc_change_hex_to_ascii(dev->logging_data->fault_status, 2, errorinfo, idx);
+       idx = mfc_change_hex_to_ascii(dev->logging_data->fault_trans_info, 8, errorinfo, idx);
+       idx = mfc_change_hex_to_ascii(dev->logging_data->fault_addr, 8, errorinfo, idx);
+       for (i = 0; i < MFC_SFR_LOGGING_COUNT_SET1; i++)
+               idx = mfc_change_hex_to_ascii(dev->logging_data->SFRs_set1[i], 2, errorinfo, idx);
+       for (i = 0; i < MFC_SFR_LOGGING_COUNT_SET2; i++)
+               idx = mfc_change_hex_to_ascii(dev->logging_data->SFRs_set2[i], 8, errorinfo, idx);
+
+       pr_err("%s\n", errorinfo);
+
+#ifdef BIGDATA_LOGGING_ENABLE
+       sec_debug_set_extra_info_mfc_error(errorinfo);
+#endif
+}
+
+static void mfc_display_state(struct s5p_mfc_dev *dev)
+{
+       int i;
+
+       pr_err("-----------dumping MFC device info-----------\n");
+       pr_err("power:%d, clock:%d, num_inst:%d, num_drm_inst:%d, fw_status:%d\n",
+                       s5p_mfc_pm_get_pwr_ref_cnt(dev), s5p_mfc_pm_get_clk_ref_cnt(dev),
+                       dev->num_inst, dev->num_drm_inst, dev->fw.status);
+       pr_err("hwlock bits:%#lx / dev:%#lx, curr_ctx:%d (is_drm:%d),"
+                       " preempt_ctx:%d, work_bits:%#lx\n",
+                       dev->hwlock.bits, dev->hwlock.dev,
+                       dev->curr_ctx, dev->curr_ctx_is_drm,
+                       dev->preempt_ctx, s5p_mfc_get_bits(&dev->work_bits));
+
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++)
+               if (dev->ctx[i])
+                       pr_err("MFC ctx[%d] %s(%d) state:%d, queue_cnt(src:%d, dst:%d),"
+                               " interrupt(cond:%d, type:%d, err:%d)\n",
+                               dev->ctx[i]->num,
+                               dev->ctx[i]->type == MFCINST_DECODER ? "DEC" : "ENC",
+                               dev->ctx[i]->codec_mode, dev->ctx[i]->state,
+                               s5p_mfc_get_queue_count(&dev->ctx[i]->buf_queue_lock, &dev->ctx[i]->src_buf_queue),
+                               s5p_mfc_get_queue_count(&dev->ctx[i]->buf_queue_lock, &dev->ctx[i]->dst_buf_queue),
+                               dev->ctx[i]->int_condition, dev->ctx[i]->int_reason,
+                               dev->ctx[i]->int_err);
+}
+
+static void mfc_print_trace(struct s5p_mfc_dev *dev)
+{
+       int i, cnt, trace_cnt;
+
+       pr_err("-----------dumping MFC trace info-----------\n");
+
+       trace_cnt = atomic_read(&dev->trace_ref);
+       for (i = MFC_TRACE_COUNT_PRINT - 1; i >= 0; i--) {
+               cnt = ((trace_cnt + MFC_TRACE_COUNT_MAX) - i) % MFC_TRACE_COUNT_MAX;
+               pr_err("MFC trace[%d]: time=%llu, str=%s", cnt,
+                               dev->mfc_trace[cnt].time, dev->mfc_trace[cnt].str);
+       }
+}
+
+void s5p_mfc_dump_buffer_info(struct s5p_mfc_dev *dev, unsigned long addr)
+{
+       struct s5p_mfc_ctx *ctx;
+
+       ctx = dev->ctx[dev->curr_ctx];
+       if (ctx) {
+               pr_err("-----------dumping MFC buffer info (fault at: %#lx)\n", addr);
+               pr_err("common:%#llx~%#llx, instance:%#llx~%#llx, codec:%#llx~%#llx\n",
+                               dev->common_ctx_buf.daddr,
+                               dev->common_ctx_buf.daddr + PAGE_ALIGN(0x7800),
+                               ctx->instance_ctx_buf.daddr,
+                               ctx->instance_ctx_buf.daddr + ctx->instance_ctx_buf.size,
+                               ctx->codec_buf.daddr,
+                               ctx->codec_buf.daddr + ctx->codec_buf.size);
+               if (ctx->type == MFCINST_DECODER) {
+                       pr_err("Decoder CPB:%#x++%#x, scratch:%#x++%#x, static(vp9):%#x++%#x\n",
+                                       MFC_READL(S5P_FIMV_D_CPB_BUFFER_ADDR),
+                                       MFC_READL(S5P_FIMV_D_CPB_BUFFER_SIZE),
+                                       MFC_READL(S5P_FIMV_D_SCRATCH_BUFFER_ADDR),
+                                       MFC_READL(S5P_FIMV_D_SCRATCH_BUFFER_SIZE),
+                                       MFC_READL(S5P_FIMV_D_STATIC_BUFFER_ADDR),
+                                       MFC_READL(S5P_FIMV_D_STATIC_BUFFER_SIZE));
+                       pr_err("DPB [0]plane:++%#x, [1]plane:++%#x, [2]plane:++%#x, MV buffer:++%#lx\n",
+                                       ctx->raw_buf.plane_size[0],
+                                       ctx->raw_buf.plane_size[1],
+                                       ctx->raw_buf.plane_size[2],
+                                       ctx->mv_size);
+                       print_hex_dump(KERN_ERR, "[0] plane ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_D_FIRST_PLANE_DPB0,
+                                       0x100, false);
+                       print_hex_dump(KERN_ERR, "[1] plane ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_D_SECOND_PLANE_DPB0,
+                                       0x100, false);
+                       if (ctx->dst_fmt->num_planes == 3)
+                               print_hex_dump(KERN_ERR, "[2] plane ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                               dev->regs_base + S5P_FIMV_D_THIRD_PLANE_DPB0,
+                                               0x100, false);
+                       print_hex_dump(KERN_ERR, "MV buffer ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_D_MV_BUFFER0,
+                                       0x100, false);
+               } else if (ctx->type == MFCINST_ENCODER) {
+                       pr_err("Encoder SRC %dplane, [0]:%#x++%#x, [1]:%#x++%#x, [2]:%#x++%#x\n",
+                                       ctx->src_fmt->num_planes,
+                                       MFC_READL(S5P_FIMV_E_SOURCE_FIRST_ADDR),
+                                       ctx->raw_buf.plane_size[0],
+                                       MFC_READL(S5P_FIMV_E_SOURCE_SECOND_ADDR),
+                                       ctx->raw_buf.plane_size[1],
+                                       MFC_READL(S5P_FIMV_E_SOURCE_THIRD_ADDR),
+                                       ctx->raw_buf.plane_size[2]);
+                       pr_err("DST:%#x++%#x, scratch:%#x++%#x\n",
+                                       MFC_READL(S5P_FIMV_E_STREAM_BUFFER_ADDR),
+                                       MFC_READL(S5P_FIMV_E_STREAM_BUFFER_SIZE),
+                                       MFC_READL(S5P_FIMV_E_SCRATCH_BUFFER_ADDR),
+                                       MFC_READL(S5P_FIMV_E_SCRATCH_BUFFER_SIZE));
+                       pr_err("DPB [0] plane:++%#lx, [1] plane:++%#lx, ME buffer:++%#lx\n",
+                                       ctx->enc_priv->luma_dpb_size,
+                                       ctx->enc_priv->chroma_dpb_size,
+                                       ctx->enc_priv->me_buffer_size);
+                       print_hex_dump(KERN_ERR, "[0] plane ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_E_LUMA_DPB,
+                                       0x44, false);
+                       print_hex_dump(KERN_ERR, "[1] plane ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_E_CHROMA_DPB,
+                                       0x44, false);
+                       print_hex_dump(KERN_ERR, "ME buffer ", DUMP_PREFIX_ADDRESS, 32, 4,
+                                       dev->regs_base + S5P_FIMV_E_ME_BUFFER,
+                                       0x44, false);
+               } else {
+                       pr_err("invalid MFC instnace type(%d)\n", ctx->type);
+               }
+       }
+}
+
+void s5p_mfc_dump_info_and_stop_hw(struct s5p_mfc_dev *dev)
+{
+       MFC_TRACE_DEV("** mfc will stop!!!\n");
+       mfc_display_state(dev);
+       mfc_print_trace(dev);
+       mfc_save_logging_sfr(dev);
+       mfc_dump_regs(dev);
+       exynos_sysmmu_show_status(dev->device);
+       BUG();
+}
+
+void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+       struct s5p_mfc_dev *dev;
+       int cmd = 0;
+
+       dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
+
+       if (atomic_read(&dev->watchdog_run)) {
+               mfc_err_dev("watchdog already running???\n");
+               return;
+       }
+
+       if (!atomic_read(&dev->watchdog_tick_cnt)) {
+               mfc_err_dev("interrupt handler is called\n");
+               return;
+       }
+
+       /* Check whether HW interrupt has occured or not */
+       if (s5p_mfc_pm_get_pwr_ref_cnt(dev) && s5p_mfc_pm_get_clk_ref_cnt(dev))
+               cmd = s5p_mfc_check_int_cmd(dev);
+       if (cmd) {
+               if (atomic_read(&dev->watchdog_tick_cnt) == (3 * WATCHDOG_TICK_CNT_TO_START_WATCHDOG)) {
+                       mfc_err_dev("MFC driver waited for upward of %dsec\n",
+                                               3 * WATCHDOG_TICK_CNT_TO_START_WATCHDOG);
+                       dev->logging_data->cause |= (1 << MFC_CAUSE_NO_SCHEDULING);
+               } else {
+                       mfc_err_dev("interrupt(%d) is occured, wait scheduling\n", cmd);
+                       return;
+               }
+       } else {
+               dev->logging_data->cause |= (1 << MFC_CAUSE_NO_INTERRUPT);
+               mfc_err_dev("Driver timeout error handling\n");
+       }
+
+       /* Run watchdog worker */
+       atomic_set(&dev->watchdog_run, 1);
+
+       /* Reset the timeout watchdog */
+       atomic_set(&dev->watchdog_tick_cnt, 0);
+
+       /* Stop after dumping information */
+       s5p_mfc_dump_info_and_stop_hw(dev);
+}
diff --git a/drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.h b/drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.h
new file mode 100644 (file)
index 0000000..7867d12
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * drivers/media/platform/exynos/mfc/s5p_mfc_watchdog.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 __S5P_MFC_WATCHDOG_H
+#define __S5P_MFC_WATCHDOG_H __FILE__
+
+#include "s5p_mfc_common.h"
+
+void s5p_mfc_dump_buffer_info(struct s5p_mfc_dev *dev, unsigned long addr);
+void s5p_mfc_dump_info_and_stop_hw(struct s5p_mfc_dev *dev);
+void s5p_mfc_watchdog_worker(struct work_struct *work);
+
+#endif /* __S5P_MFC_WATCHDOG_H */
diff --git a/include/media/s5p_mfc_hwfc.h b/include/media/s5p_mfc_hwfc.h
new file mode 100644 (file)
index 0000000..39b5585
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for mfc driver
+ *
+ * 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 _S5P_MFC_HWFC_H
+#define _S5P_MFC_HWFC_H
+
+#include <linux/types.h>
+
+#define HWFC_ERR_NONE                  0
+#define HWFC_ERR_TSMUX                 1
+#define HWFC_ERR_MFC                   2
+#define HWFC_ERR_MFC_NOT_PREPARED      3
+#define HWFC_ERR_MFC_TIMEOUT           4
+#define HWFC_ERR_MFC_NOT_ENABLED       5
+
+/*
+ * struct encoding_param
+ * @time_stamp : timestamp value
+ */
+struct encoding_param {
+       u64 time_stamp;
+};
+
+/*
+ * s5p_mfc_hwfc_encode - Request encoding
+ * @encoding_param : parameters for encoding
+ *
+ * repeater calls it to start encoding
+ *
+ */
+#ifdef CONFIG_VIDEO_EXYNOS_MFC
+int s5p_mfc_hwfc_encode(int buf_index, int job_id, struct encoding_param *param);
+#else
+static inline int s5p_mfc_hwfc_encode(int buf_index, int job_id, struct encoding_param *param)
+{
+       return -HWFC_ERR_MFC_NOT_ENABLED;
+}
+#endif
+
+#endif /* _S5P_MFC_HWFC_H */
diff --git a/include/trace/events/mfc.h b/include/trace/events/mfc.h
new file mode 100644 (file)
index 0000000..38326b7
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mfc
+
+#if !defined(_TRACE_MFC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MFC_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(mfc_node,
+
+       TP_PROTO(int ctx_num,
+                       int num_inst,
+                       unsigned int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, num_inst, type, is_drm),
+
+       TP_STRUCT__entry(
+               __field(        int,            ctx_num         )
+               __field(        int,            num_inst        )
+               __field(        unsigned int,   type            )
+               __field(        int,            is_drm          )
+       ),
+
+       TP_fast_assign(
+               __entry->ctx_num        = ctx_num;
+               __entry->num_inst       = num_inst;
+               __entry->type           = type;
+               __entry->is_drm         = is_drm;
+       ),
+
+       TP_printk("ctx[%d] total inst=%d, type=%d, %s",
+                       __entry->ctx_num,
+                       __entry->num_inst,
+                       __entry->type,
+                       __entry->is_drm ? "drm" : "normal"
+       )
+);
+
+DEFINE_EVENT(mfc_node, mfc_node_open,
+
+       TP_PROTO(int ctx_num,
+                       int num_inst,
+                       unsigned int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, num_inst, type, is_drm)
+);
+
+DEFINE_EVENT(mfc_node, mfc_node_close,
+
+       TP_PROTO(int ctx_num,
+                       int num_inst,
+                       unsigned int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, num_inst, type, is_drm)
+);
+
+DECLARE_EVENT_CLASS(mfc_loadfw,
+
+       TP_PROTO(size_t fw_region_size,
+                       size_t fw_size),
+
+       TP_ARGS(fw_region_size, fw_size),
+
+       TP_STRUCT__entry(
+               __field(size_t, fw_region_size)
+               __field(size_t, fw_size)
+       ),
+
+       TP_fast_assign(
+               __entry->fw_region_size = fw_region_size;
+               __entry->fw_size        = fw_size;
+       ),
+
+       TP_printk("FW region: %ld, size: %ld",
+                       __entry->fw_region_size,
+                       __entry->fw_size
+       )
+);
+
+DEFINE_EVENT(mfc_loadfw, mfc_loadfw_start,
+
+       TP_PROTO(size_t fw_region_size,
+                       size_t fw_size),
+
+       TP_ARGS(fw_region_size, fw_size)
+);
+
+DEFINE_EVENT(mfc_loadfw, mfc_loadfw_end,
+
+       TP_PROTO(size_t fw_region_size,
+                       size_t fw_size),
+
+       TP_ARGS(fw_region_size, fw_size)
+);
+
+DECLARE_EVENT_CLASS(mfc_dcpp,
+
+       TP_PROTO(int ctx_num,
+                       int is_support_smc,
+                       int drm_fw_status),
+
+       TP_ARGS(ctx_num, is_support_smc, drm_fw_status),
+
+       TP_STRUCT__entry(
+               __field(        int,            ctx_num         )
+               __field(        int,            is_support_smc  )
+               __field(        int,            drm_fw_status   )
+       ),
+
+       TP_fast_assign(
+               __entry->ctx_num        = ctx_num;
+               __entry->is_support_smc = is_support_smc;
+               __entry->drm_fw_status  = drm_fw_status;
+       ),
+
+       TP_printk("ctx[%d] support drm=%d, drm fw %s",
+                       __entry->ctx_num,
+                       __entry->is_support_smc,
+                       __entry->drm_fw_status ? "loaded" : "not-loaded"
+       )
+);
+
+DEFINE_EVENT(mfc_dcpp, mfc_dcpp_start,
+
+       TP_PROTO(int ctx_num,
+                       int is_support_smc,
+                       int drm_fw_status),
+
+       TP_ARGS(ctx_num, is_support_smc, drm_fw_status)
+);
+
+DEFINE_EVENT(mfc_dcpp, mfc_dcpp_end,
+
+       TP_PROTO(int ctx_num,
+                       int is_support_smc,
+                       int drm_fw_status),
+
+       TP_ARGS(ctx_num, is_support_smc, drm_fw_status)
+);
+
+DECLARE_EVENT_CLASS(mfc_frame,
+
+       TP_PROTO(int ctx_num,
+                       int reason,
+                       int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, reason, type, is_drm),
+
+       TP_STRUCT__entry(
+               __field(        int,            ctx_num )
+               __field(        int,            reason  )
+               __field(        int,            type    )
+               __field(        int,            is_drm  )
+       ),
+
+       TP_fast_assign(
+               __entry->ctx_num        = ctx_num;
+               __entry->reason         = reason;
+               __entry->type           = type;
+               __entry->is_drm         = is_drm;
+       ),
+
+       TP_printk("ctx[%d] reason=%d, type=%d, %s",
+                       __entry->ctx_num,
+                       __entry->reason,
+                       __entry->type,
+                       __entry->is_drm ? "drm" : "normal"
+       )
+);
+
+DEFINE_EVENT(mfc_frame, mfc_frame_start,
+
+       TP_PROTO(int ctx_num,
+                       int reason,
+                       int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, reason, type, is_drm)
+);
+
+DEFINE_EVENT(mfc_frame, mfc_frame_top,
+
+       TP_PROTO(int ctx_num,
+                       int reason,
+                       int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, reason, type, is_drm)
+);
+
+DEFINE_EVENT(mfc_frame, mfc_frame_bottom,
+
+       TP_PROTO(int ctx_num,
+                       int reason,
+                       int type,
+                       int is_drm),
+
+       TP_ARGS(ctx_num, reason, type, is_drm)
+);
+
+#endif /* _TRACE_MFC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>