--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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>");
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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>