// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
 */

#include <linux/slab.h>
#include <soc/qcom/scm.h>
#include "msm_vdec.h"
#include "msm_vidc_internal.h"
#include "msm_vidc_common.h"
#include "vidc_hfi.h"
#include "vidc_hfi_helper.h"
#include "vidc_hfi_api.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_clocks.h"
#include "msm_vidc_buffer_calculations.h"

#define MIN_NUM_DEC_OUTPUT_BUFFERS 4
#define MIN_NUM_DEC_CAPTURE_BUFFERS 4
/* Y=16(0-9bits), Cb(10-19bits)=Cr(20-29bits)=128, black by default */
#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8020010
#define MAX_VP9D_INST_COUNT 6

static const char *const vp8_profile_level[] = {
	"Unused",
	"0.0",
	"1.0",
	"2.0",
	"3.0",
	NULL
};

static const char *const vp9_level[] = {
	"Unused",
	"1.0",
	"1.1",
	"2.0",
	"2.1",
	"3.0",
	"3.1",
	"4.0",
	"4.1",
	"5.0",
	"5.1",
	"6.0",
	"6.1",
	NULL
};

static const char *const mpeg2_profile[] = {
	"Simple",
	"Main",
	"High",
	NULL
};

static const char *const mpeg2_level[] = {
	"0",
	"1",
	"2",
	"3",
	NULL
};

static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
	{
		.id = V4L2_CID_MPEG_VIDEO_UNKNOWN,
		.name = "Invalid control",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = 0,
		.maximum = 0,
		.default_value = 0,
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_DECODE_ORDER,
		.name = "Decode Order",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_DISABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE,
		.name = "Sync Frame Decode",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_DISABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
		.name = "Secure mode",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_DISABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
		.name = "Extradata Type",
		.type = V4L2_CTRL_TYPE_BITMASK,
		.minimum = EXTRADATA_NONE,
		.maximum = EXTRADATA_DEFAULT | EXTRADATA_ADVANCED,
		.default_value = EXTRADATA_DEFAULT,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE,
		.name = "Video decoder multi stream",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum =
			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
		.maximum =
			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY,
		.default_value =
			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
		.name = "H264 Profile",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
		.maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
		.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
		(1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
		(1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
		(1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
		(1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
		.name = "H264 Level",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
		.maximum = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
		.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_5_0,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
		(1 << V4L2_MPEG_VIDEO_H264_LEVEL_6_2)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
		.name = "HEVC Profile",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
		.maximum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
		.default_value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
		(1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
		(1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
		.name = "HEVC Level",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
		.maximum = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
		.default_value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
		(1 << V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_HEVC_TIER,
		.name = "HEVC Tier",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
		.maximum = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
		.default_value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) |
		(1 << V4L2_MPEG_VIDEO_HEVC_TIER_HIGH)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
		.name = "VP8 Profile",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
		.maximum = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
		.default_value = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
		.menu_skip_mask = ~(1 << V4L2_MPEG_VIDEO_VP8_PROFILE_0),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
		.name = "VP8 Profile Level",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
		.maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3,
		.default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3)
		),
		.qmenu = vp8_profile_level,
	},
	{
		.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
		.name = "VP9 Profile",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
		.maximum = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
		.default_value = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDEO_VP9_PROFILE_0) |
		(1 << V4L2_MPEG_VIDEO_VP9_PROFILE_1) |
		(1 << V4L2_MPEG_VIDEO_VP9_PROFILE_2)
		),
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL,
		.name = "VP9 Level",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED,
		.maximum = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61,
		.default_value = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_1) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_11) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_2) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_21) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_3) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_31) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_4) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_41) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_5) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6) |
		(1 << V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61)
		),
		.qmenu = vp9_level,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE,
		.name = "MPEG2 Profile",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE,
		.maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN,
		.default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN,
		.menu_skip_mask = ~(
		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE) |
		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN)
		),
		.qmenu = mpeg2_profile,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL,
		.name = "MPEG2 Level",
		.type = V4L2_CTRL_TYPE_MENU,
		.minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0,
		.maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2,
		.default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2,
		.menu_skip_mask = ~(
			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0) |
			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1) |
			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2)
		),
		.qmenu = mpeg2_level,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT,
		.name = "Picture concealed color 8bit",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = 0x0,
		.maximum = 0xff3fcff,
		.default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT,
		.name = "Picture concealed color 10bit",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = 0x0,
		.maximum = 0x3fffffff,
		.default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT,
		.name = "Buffer size limit",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = 0,
		.maximum = INT_MAX,
		.default_value = 0,
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
		.name = "CAPTURE Count",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = SINGLE_OUTPUT_BUFFER,
		.maximum = MAX_NUM_OUTPUT_BUFFERS,
		.default_value = SINGLE_OUTPUT_BUFFER,
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
		.name = "OUTPUT Count",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = SINGLE_INPUT_BUFFER,
		.maximum = MAX_NUM_INPUT_BUFFERS,
		.default_value = SINGLE_INPUT_BUFFER,
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
		.name = "Frame Rate",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = (MINIMUM_FPS << 16),
		.maximum = (MAXIMUM_FPS << 16),
		.default_value = (DEFAULT_FPS << 16),
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY,
		.name = "Session Priority",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_ENABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE,
		.name = "Decoder Operating rate",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = (DEFAULT_FPS << 16),/* Power Vote min fps */
		.maximum = INT_MAX,
		.default_value =  (DEFAULT_FPS << 16),
		.step = 1,
		.qmenu = NULL,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE,
		.name = "Low Latency Mode",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_DISABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_HINT,
		.name = "Low Latency Hint",
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.minimum = V4L2_MPEG_MSM_VIDC_DISABLE,
		.maximum = V4L2_MPEG_MSM_VIDC_ENABLE,
		.default_value = V4L2_MPEG_MSM_VIDC_DISABLE,
		.step = 1,
	},
	{
		.id = V4L2_CID_MPEG_VIDC_SUPERFRAME,
		.name = "Superframe",
		.type = V4L2_CTRL_TYPE_INTEGER,
		.minimum = 0,
		.maximum = 0,
		.default_value = 0,
		.step = 1,
	},
};

#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)


struct msm_vidc_format_desc vdec_output_formats[] = {
	{
		.name = "YCbCr Semiplanar 4:2:0",
		.description = "Y/CbCr 4:2:0",
		.fourcc = V4L2_PIX_FMT_NV12,
	},
	{
		.name = "YCbCr Semiplanar 4:2:0 10bit",
		.description = "Y/CbCr 4:2:0 10bit",
		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS,
	},
	{
		.name = "UBWC YCbCr Semiplanar 4:2:0",
		.description = "UBWC Y/CbCr 4:2:0",
		.fourcc = V4L2_PIX_FMT_NV12_UBWC,
	},
	{
		.name = "UBWC YCbCr Semiplanar 4:2:0 10bit",
		.description = "UBWC Y/CbCr 4:2:0 10bit",
		.fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC,
	},
};

struct msm_vidc_format_desc vdec_input_formats[] = {
	{
		.name = "Mpeg2",
		.description = "Mpeg2 compressed format",
		.fourcc = V4L2_PIX_FMT_MPEG2,
	},
	{
		.name = "H264",
		.description = "H264 compressed format",
		.fourcc = V4L2_PIX_FMT_H264,
	},
	{
		.name = "HEVC",
		.description = "HEVC compressed format",
		.fourcc = V4L2_PIX_FMT_HEVC,
	},
	{
		.name = "VP8",
		.description = "VP8 compressed format",
		.fourcc = V4L2_PIX_FMT_VP8,
	},
	{
		.name = "VP9",
		.description = "VP9 compressed format",
		.fourcc = V4L2_PIX_FMT_VP9,
	},
};

struct msm_vidc_format_constraint dec_pix_format_constraints[] = {
	{
		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS,
		.num_planes = 2,
		.y_max_stride = 8192,
		.y_buffer_alignment = 256,
		.uv_max_stride = 8192,
		.uv_buffer_alignment = 256,
	},
	{
		.fourcc = V4L2_PIX_FMT_NV12,
		.num_planes = 2,
		.y_max_stride = 8192,
		.y_buffer_alignment = 512,
		.uv_max_stride = 8192,
		.uv_buffer_alignment = 256,
	},
	{
		.fourcc = V4L2_PIX_FMT_NV21,
		.num_planes = 2,
		.y_max_stride = 8192,
		.y_buffer_alignment = 512,
		.uv_max_stride = 8192,
		.uv_buffer_alignment = 256,
	},
};

static bool msm_vidc_check_for_vp9d_overload(struct msm_vidc_core *core)
{
	u32 vp9d_instance_count = 0;
	struct msm_vidc_inst *inst = NULL;

	mutex_lock(&core->lock);
	list_for_each_entry(inst, &core->instances, list) {
		if (inst->session_type == MSM_VIDC_DECODER &&
			get_v4l2_codec(inst) == V4L2_PIX_FMT_VP9)
			vp9d_instance_count++;
	}
	mutex_unlock(&core->lock);

	if (vp9d_instance_count > MAX_VP9D_INST_COUNT)
		return true;
	return false;
}

int msm_vdec_update_stream_output_mode(struct msm_vidc_inst *inst)
{
	struct v4l2_format *f;
	u32 format;
	u32 stream_output_mode;
	u32 fourcc;

	if (!inst) {
		d_vpr_e("%s: invalid parameters\n", __func__);
		return -EINVAL;
	}

	f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
	format = f->fmt.pix_mp.pixelformat;
	stream_output_mode = HAL_VIDEO_DECODER_PRIMARY;
	if ((format == V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS) ||
		(format == V4L2_PIX_FMT_NV12)) {
		stream_output_mode = HAL_VIDEO_DECODER_SECONDARY;
	}

	msm_comm_set_stream_output_mode(inst,
		stream_output_mode);

	fourcc = V4L2_PIX_FMT_NV12_UBWC;
	if (inst->bit_depth == MSM_VIDC_BIT_DEPTH_10)
		fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC;

	inst->clk_data.dpb_fourcc = fourcc;

	return 0;
}

int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
	struct msm_vidc_format *fmt = NULL;
	struct msm_vidc_format_desc *fmt_desc = NULL;
	struct v4l2_pix_format_mplane *mplane = NULL;
	int rc = 0;
	u32 color_format;

	if (!inst || !f) {
		d_vpr_e("%s: invalid parameters %pK %pK\n", __func__, inst, f);
		return -EINVAL;
	}

	/*
	 * First update inst format with new width/height/format
	 * Recalculate sizes/strides etc
	 * Perform necessary checks to continue with session
	 * Copy recalculated info into user format
	 */
	if (f->type == OUTPUT_MPLANE) {
		fmt = &inst->fmts[OUTPUT_PORT];
		fmt_desc = msm_comm_get_pixel_fmt_fourcc(vdec_output_formats,
			ARRAY_SIZE(vdec_output_formats),
			f->fmt.pix_mp.pixelformat, inst->sid);
		if (!fmt_desc) {
			s_vpr_e(inst->sid, "Invalid fmt set : %x\n",
				f->fmt.pix_mp.pixelformat);
			return -EINVAL;
		}
		strlcpy(fmt->name, fmt_desc->name, sizeof(fmt->name));
		strlcpy(fmt->description, fmt_desc->description,
			sizeof(fmt->description));

		inst->clk_data.opb_fourcc = f->fmt.pix_mp.pixelformat;

		fmt->v4l2_fmt.type = f->type;
		mplane = &fmt->v4l2_fmt.fmt.pix_mp;
		mplane->width = f->fmt.pix_mp.width;
		mplane->height = f->fmt.pix_mp.height;
		mplane->pixelformat = f->fmt.pix_mp.pixelformat;
		mplane->plane_fmt[0].sizeimage =
			msm_vidc_calculate_dec_output_frame_size(inst);

		if (mplane->num_planes > 1)
			mplane->plane_fmt[1].sizeimage =
				msm_vidc_calculate_dec_output_extra_size(inst);
		color_format = msm_comm_convert_color_fmt(
			f->fmt.pix_mp.pixelformat, inst->sid);
		mplane->plane_fmt[0].bytesperline =
			VENUS_Y_STRIDE(color_format, f->fmt.pix_mp.width);
		mplane->plane_fmt[0].reserved[0] =
			VENUS_Y_SCANLINES(color_format, f->fmt.pix_mp.height);
		inst->bit_depth = MSM_VIDC_BIT_DEPTH_8;
		if ((f->fmt.pix_mp.pixelformat ==
			V4L2_PIX_FMT_NV12_TP10_UBWC) ||
			(f->fmt.pix_mp.pixelformat ==
			V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS)) {
			inst->bit_depth = MSM_VIDC_BIT_DEPTH_10;
		}

		rc = msm_vidc_check_session_supported(inst);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: session not supported\n", __func__);
			goto err_invalid_fmt;
		}

		rc = msm_vdec_update_stream_output_mode(inst);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: failed to update output stream mode\n",
				__func__);
			goto err_invalid_fmt;
		}

		memcpy(f, &fmt->v4l2_fmt, sizeof(struct v4l2_format));
	} else if (f->type == INPUT_MPLANE) {
		fmt = &inst->fmts[INPUT_PORT];
		fmt_desc = msm_comm_get_pixel_fmt_fourcc(vdec_input_formats,
			ARRAY_SIZE(vdec_input_formats),
			f->fmt.pix_mp.pixelformat, inst->sid);
		if (!fmt_desc) {
			s_vpr_e(inst->sid, "Invalid fmt set : %x\n",
				f->fmt.pix_mp.pixelformat);
			return -EINVAL;
		}
		strlcpy(fmt->name, fmt_desc->name, sizeof(fmt->name));
		strlcpy(fmt->description, fmt_desc->description,
			sizeof(fmt->description));

		if (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_VP9) {
			if (msm_vidc_check_for_vp9d_overload(inst->core)) {
				s_vpr_e(inst->sid, "VP9 Decode overload\n");
				rc = -ENOMEM;
				goto err_invalid_fmt;
			}
		}

		fmt->v4l2_fmt.type = f->type;
		mplane = &fmt->v4l2_fmt.fmt.pix_mp;
		mplane->width = f->fmt.pix_mp.width;
		mplane->height = f->fmt.pix_mp.height;
		mplane->pixelformat = f->fmt.pix_mp.pixelformat;
		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
		if (rc) {
			s_vpr_e(inst->sid, "Failed to open instance\n");
			goto err_invalid_fmt;
		}

		mplane->plane_fmt[0].sizeimage =
			msm_vidc_calculate_dec_input_frame_size(inst);

		/* Driver can recalculate buffer count only for
		 * only for bitstream port. Decoder YUV port reconfig
		 * should not overwrite the FW calculated buffer
		 * count.
		 */
		rc = msm_vidc_calculate_buffer_counts(inst);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s failed to calculate buffer count\n",
				__func__);
			return rc;
		}

		rc = msm_vidc_check_session_supported(inst);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: session not supported\n", __func__);
			goto err_invalid_fmt;
		}
		update_log_ctxt(inst->sid, inst->session_type,
			mplane->pixelformat);
		memcpy(f, &fmt->v4l2_fmt, sizeof(struct v4l2_format));
	}

	inst->batch.enable = is_batching_allowed(inst);
	msm_dcvs_try_enable(inst);

err_invalid_fmt:
	return rc;
}

int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
	struct v4l2_format *fmt;

	if (f->type == OUTPUT_MPLANE) {
		fmt = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
		fmt->fmt.pix_mp.plane_fmt[0].sizeimage =
			msm_vidc_calculate_dec_output_frame_size(inst);
		if (fmt->fmt.pix_mp.num_planes > 1)
			fmt->fmt.pix_mp.plane_fmt[1].sizeimage =
				msm_vidc_calculate_dec_output_extra_size(inst);
		memcpy(f, fmt, sizeof(struct v4l2_format));
	} else if (f->type == INPUT_MPLANE) {
		fmt = &inst->fmts[INPUT_PORT].v4l2_fmt;
		fmt->fmt.pix_mp.plane_fmt[0].sizeimage =
			msm_vidc_calculate_dec_input_frame_size(inst);
		memcpy(f, fmt, sizeof(struct v4l2_format));
	} else {
		s_vpr_e(inst->sid, "%s: Unsupported buf type: %d\n",
			__func__, f->type);
		return -EINVAL;
	}

	return 0;
}

int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
{
	const struct msm_vidc_format_desc *fmt_desc = NULL;
	int rc = 0;

	if (!inst || !f) {
		d_vpr_e("Invalid input, inst = %pK, f = %pK\n", inst, f);
		return -EINVAL;
	}
	if (f->type == OUTPUT_MPLANE) {
		fmt_desc = msm_comm_get_pixel_fmt_index(vdec_output_formats,
			ARRAY_SIZE(vdec_output_formats), f->index, inst->sid);
	} else if (f->type == INPUT_MPLANE) {
		fmt_desc = msm_comm_get_pixel_fmt_index(vdec_input_formats,
			ARRAY_SIZE(vdec_input_formats), f->index, inst->sid);
		f->flags = V4L2_FMT_FLAG_COMPRESSED;
	}

	memset(f->reserved, 0, sizeof(f->reserved));
	if (fmt_desc) {
		strlcpy(f->description, fmt_desc->description,
				sizeof(f->description));
		f->pixelformat = fmt_desc->fourcc;
	} else {
		s_vpr_h(inst->sid, "No more formats found\n");
		rc = -EINVAL;
	}
	return rc;
}

int msm_vdec_inst_init(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct msm_vidc_core *core;
	struct msm_vidc_format_desc *fmt_desc = NULL;
	struct v4l2_format *f = NULL;

	if (!inst || !inst->core) {
		d_vpr_e("Invalid input = %pK\n", inst);
		return -EINVAL;
	}
	core = inst->core;

	inst->prop.extradata_ctrls = EXTRADATA_DEFAULT;
	f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	f->fmt.pix_mp.height = DEFAULT_HEIGHT;
	f->fmt.pix_mp.width = DEFAULT_WIDTH;
	f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12_UBWC;
	f->fmt.pix_mp.num_planes = 2;
	f->fmt.pix_mp.plane_fmt[0].sizeimage =
		msm_vidc_calculate_dec_output_frame_size(inst);
	f->fmt.pix_mp.plane_fmt[1].sizeimage =
		msm_vidc_calculate_dec_output_extra_size(inst);
	fmt_desc = msm_comm_get_pixel_fmt_fourcc(vdec_output_formats,
		ARRAY_SIZE(vdec_output_formats),
		f->fmt.pix_mp.pixelformat, inst->sid);
	if (!fmt_desc) {
		s_vpr_e(inst->sid, "Invalid fmt set: %x\n",
			f->fmt.pix_mp.pixelformat);
		return -EINVAL;
	}
	strlcpy(inst->fmts[OUTPUT_PORT].name, fmt_desc->name,
		sizeof(inst->fmts[OUTPUT_PORT].name));
	strlcpy(inst->fmts[OUTPUT_PORT].description, fmt_desc->description,
		sizeof(inst->fmts[OUTPUT_PORT].description));
	f = &inst->fmts[INPUT_PORT].v4l2_fmt;
	f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
	f->fmt.pix_mp.height = DEFAULT_HEIGHT;
	f->fmt.pix_mp.width = DEFAULT_WIDTH;
	f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
	f->fmt.pix_mp.num_planes = 1;
	f->fmt.pix_mp.plane_fmt[0].sizeimage =
		msm_vidc_calculate_dec_input_frame_size(inst);
	fmt_desc = msm_comm_get_pixel_fmt_fourcc(vdec_input_formats,
		ARRAY_SIZE(vdec_input_formats), f->fmt.pix_mp.pixelformat,
		inst->sid);
	if (!fmt_desc) {
		s_vpr_e(inst->sid, "Invalid fmt set: %x\n",
			f->fmt.pix_mp.pixelformat);
		return -EINVAL;
	}
	strlcpy(inst->fmts[INPUT_PORT].name, fmt_desc->name,
		sizeof(inst->fmts[INPUT_PORT].name));
	strlcpy(inst->fmts[INPUT_PORT].description, fmt_desc->description,
		sizeof(inst->fmts[INPUT_PORT].description));
	inst->buffer_mode_set[INPUT_PORT] = HAL_BUFFER_MODE_STATIC;
	inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_DYNAMIC;
	inst->stream_output_mode = HAL_VIDEO_DECODER_PRIMARY;


	inst->clk_data.frame_rate = (DEFAULT_FPS << 16);
	inst->clk_data.operating_rate = (DEFAULT_FPS << 16);
	if (core->resources.decode_batching) {
		inst->batch.enable = true;
		inst->batch.size = MAX_DEC_BATCH_SIZE;
	}

	inst->buff_req.buffer[1].buffer_type = HAL_BUFFER_INPUT;
	inst->buff_req.buffer[1].buffer_count_min_host =
	inst->buff_req.buffer[1].buffer_count_actual =
		MIN_NUM_DEC_OUTPUT_BUFFERS;
	inst->buff_req.buffer[2].buffer_type = HAL_BUFFER_OUTPUT;
	inst->buff_req.buffer[2].buffer_count_min_host =
	inst->buff_req.buffer[2].buffer_count_actual =
		MIN_NUM_DEC_CAPTURE_BUFFERS;
	inst->buff_req.buffer[3].buffer_type = HAL_BUFFER_OUTPUT2;
	inst->buff_req.buffer[3].buffer_count_min_host =
	inst->buff_req.buffer[3].buffer_count_actual =
		MIN_NUM_DEC_CAPTURE_BUFFERS;
	inst->buff_req.buffer[4].buffer_type = HAL_BUFFER_EXTRADATA_INPUT;
	inst->buff_req.buffer[5].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT;
	inst->buff_req.buffer[6].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT2;
	inst->buff_req.buffer[7].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
	inst->buff_req.buffer[8].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_1;
	inst->buff_req.buffer[9].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_2;
	inst->buff_req.buffer[10].buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
	inst->buff_req.buffer[11].buffer_type = HAL_BUFFER_INTERNAL_PERSIST_1;
	inst->buff_req.buffer[12].buffer_type = HAL_BUFFER_INTERNAL_CMD_QUEUE;
	inst->buff_req.buffer[13].buffer_type = HAL_BUFFER_INTERNAL_RECON;
	msm_vidc_init_buffer_size_calculators(inst);

	return rc;
}

int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
	int rc = 0;

	if (!inst || !inst->core || !inst->core->device) {
		d_vpr_e("%s: invalid parameters\n", __func__);
		return -EINVAL;
	}

	s_vpr_h(inst->sid, "%s: control name = %s, id = 0x%x value = %d\n",
		__func__, ctrl->name, ctrl->id, ctrl->val);

	switch (ctrl->id) {
	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
	case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
	case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
	case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
		inst->profile = msm_comm_v4l2_to_hfi(ctrl->id, ctrl->val,
							inst->sid);
		break;
	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
	case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
	case V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL:
	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL:
		inst->level = msm_comm_v4l2_to_hfi(ctrl->id, ctrl->val,
							inst->sid);
		break;
	case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
		inst->level |=
			(msm_comm_v4l2_to_hfi(ctrl->id, ctrl->val,
				inst->sid) << 28);
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_DECODE_ORDER:
	case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT:
	case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT:
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE:
		inst->flags &= ~VIDC_THUMBNAIL;
		if (ctrl->val)
			inst->flags |= VIDC_THUMBNAIL;

		inst->batch.enable = is_batching_allowed(inst);
		rc = msm_vidc_calculate_buffer_counts(inst);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: failed to calculate thumbnail buffer count\n",
				__func__);
			return rc;
		}
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
		inst->flags &= ~VIDC_SECURE;
		if (ctrl->val)
			inst->flags |= VIDC_SECURE;
		if (msm_comm_check_for_inst_overload(inst->core)) {
			s_vpr_e(inst->sid,
				"%s: Instance count reached Max limit, rejecting session",
				__func__);
			return -ENOTSUPP;
		}
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE:
		inst->clk_data.frame_rate = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
		if (ctrl->val == EXTRADATA_NONE)
			inst->prop.extradata_ctrls = 0;
		else
			inst->prop.extradata_ctrls |= ctrl->val;
		/*
		 * nothing to do here as inst->bufq[OUTPUT_PORT].num_planes
		 * and inst->bufq[OUTPUT_PORT].plane_sizes[1] are already
		 * initialized to proper values
		 */
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT:
		inst->buffer_size_limit = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY:
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
		if (!is_valid_operating_rate(inst, ctrl->val))
			break;
		/*
		 * reset the resources like clock and bus as per the updated
		 * flag. When switch from TURBO to normal, need not wait for
		 * next qbuf to scale down the resources.
		 */
		if ((inst->flags & VIDC_TURBO) && (ctrl->val != INT_MAX)) {
			inst->flags &= ~VIDC_TURBO;
			msm_comm_reset_clocks_and_bus(inst);
		}
		inst->flags &= ~VIDC_TURBO;
		if (ctrl->val == INT_MAX)
			inst->flags |= VIDC_TURBO;
		else
			inst->clk_data.operating_rate = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE:
		inst->clk_data.low_latency_mode = !!ctrl->val;
		inst->batch.enable = is_batching_allowed(inst);
		break;
	case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_HINT:
		break;
	default:
		s_vpr_e(inst->sid, "Unknown control %#x\n", ctrl->id);
		break;
	}

	return rc;
}

int msm_vdec_set_frame_size(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct hfi_frame_size frame_size;
	struct v4l2_format *f;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	f = &inst->fmts[INPUT_PORT].v4l2_fmt;
	frame_size.buffer_type = HFI_BUFFER_INPUT;
	frame_size.width = f->fmt.pix_mp.width;
	frame_size.height = f->fmt.pix_mp.height;
	s_vpr_h(inst->sid, "%s: input wxh %dx%d\n", __func__,
		frame_size.width, frame_size.height);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_FRAME_SIZE, &frame_size, sizeof(frame_size));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_color_format(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct msm_vidc_format_constraint *fmt_constraint;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	rc = msm_comm_set_color_format(inst,
			msm_comm_get_hal_output_buffer(inst),
			inst->clk_data.opb_fourcc);
	if (rc) {
		s_vpr_e(inst->sid, "%s: set color format (%#x) failed\n",
			__func__, inst->clk_data.opb_fourcc);
		return rc;
	}
	fmt_constraint = msm_comm_get_pixel_fmt_constraints(
			dec_pix_format_constraints,
			ARRAY_SIZE(dec_pix_format_constraints),
			inst->clk_data.opb_fourcc, inst->sid);
	if (fmt_constraint) {
		rc = msm_comm_set_color_format_constraints(inst,
				msm_comm_get_hal_output_buffer(inst),
				fmt_constraint);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: Set constraints for color format %#x failed\n",
				__func__, inst->clk_data.opb_fourcc);
			return rc;
		}
	}

	return rc;
}

int msm_vdec_set_input_buffer_counts(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct msm_vidc_format *fmt;
	enum hal_buffer buffer_type;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	buffer_type = HAL_BUFFER_INPUT;
	fmt = &inst->fmts[INPUT_PORT];
	rc = msm_comm_set_buffer_count(inst,
			fmt->count_min,
			fmt->count_actual,
			buffer_type);
	if (rc) {
		s_vpr_e(inst->sid, "%s: failed to set bufreqs(%#x)\n",
			__func__, buffer_type);
		return -EINVAL;
	}

	return rc;
}

int msm_vdec_set_output_buffer_counts(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct msm_vidc_format *fmt;
	enum hal_buffer buffer_type;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	buffer_type = msm_comm_get_hal_output_buffer(inst);
	/* Correct buffer counts is always stored in HAL_BUFFER_OUTPUT */
	fmt = &inst->fmts[OUTPUT_PORT];
	if (buffer_type == HAL_BUFFER_OUTPUT2) {
		/*
		 * For split mode set DPB count as well
		 * For DPB actual count is same as min output count
		 */
		rc = msm_comm_set_buffer_count(inst,
			fmt->count_min,
			fmt->count_min,
			HAL_BUFFER_OUTPUT);
		if (rc) {
			s_vpr_e(inst->sid,
				"%s: failed to set buffer count(%#x)\n",
				__func__, buffer_type);
			return -EINVAL;
		}
	}
	rc = msm_comm_set_buffer_count(inst,
			fmt->count_min,
			fmt->count_actual,
			buffer_type);
	if (rc) {
		s_vpr_e(inst->sid, "%s: failed to set bufreqs(%#x)\n",
			__func__, buffer_type);
		return -EINVAL;
	}

	return rc;
}

int msm_vdec_set_profile_level(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct hfi_profile_level profile_level;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	profile_level.profile = inst->profile;
	profile_level.level = inst->level;

	s_vpr_h(inst->sid, "%s: %#x %#x\n", __func__,
		profile_level.profile, profile_level.level);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT, &profile_level,
		sizeof(profile_level));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_output_order(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct v4l2_ctrl *ctrl;
	u32 output_order;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	ctrl = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_DECODE_ORDER);
	s_vpr_h(inst->sid, "%s: %d\n", __func__, ctrl->val);
	if (ctrl->val == V4L2_MPEG_MSM_VIDC_ENABLE)
		output_order = HFI_OUTPUT_ORDER_DECODE;
	else
		output_order = HFI_OUTPUT_ORDER_DISPLAY;

	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER, &output_order,
		sizeof(u32));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_sync_frame_mode(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct v4l2_ctrl *ctrl;
	struct hfi_enable hfi_property;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	ctrl = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE);
	hfi_property.enable = (bool)ctrl->val;

	s_vpr_h(inst->sid, "%s: %#x\n", __func__, hfi_property.enable);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE, &hfi_property,
		sizeof(hfi_property));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_secure_mode(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct v4l2_ctrl *ctrl;
	u32 codec;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	ctrl = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_SECURE);

	codec = get_v4l2_codec(inst);
	if (ctrl->val) {
		if (!(codec == V4L2_PIX_FMT_HEVC ||
			codec == V4L2_PIX_FMT_H264 ||
			codec == V4L2_PIX_FMT_VP9 ||
			codec == V4L2_PIX_FMT_MPEG2)) {
			s_vpr_e(inst->sid,
				"%s: Secure allowed for HEVC/H264/VP9/MPEG2\n",
				__func__);
			return -EINVAL;
		}
	}

	s_vpr_h(inst->sid, "%s: %#x\n", __func__, ctrl->val);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_SECURE_SESSION, &ctrl->val, sizeof(u32));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_output_stream_mode(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct hfi_multi_stream multi_stream;
	struct hfi_frame_size frame_sz;
	struct v4l2_format *f;
	u32 sid;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;
	sid = inst->sid;

	if (is_primary_output_mode(inst)) {
		multi_stream.buffer_type = HFI_BUFFER_OUTPUT;
		multi_stream.enable = true;
		rc = call_hfi_op(hdev, session_set_property, inst->session,
			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM, &multi_stream,
			sizeof(multi_stream));
		if (rc) {
			s_vpr_e(sid,
				"%s: set prop multistream primary failed: %d\n",
				__func__, rc);
			return rc;
		}
		multi_stream.buffer_type = HFI_BUFFER_OUTPUT2;
		multi_stream.enable = false;
		rc = call_hfi_op(hdev, session_set_property, inst->session,
			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM, &multi_stream,
			sizeof(multi_stream));
		if (rc) {
			s_vpr_e(sid,
				"%s: set prop multistream primary2 failed : %d\n",
				__func__, rc);
			return rc;
		}
	} else {
		rc = msm_comm_set_color_format(inst,
			HAL_BUFFER_OUTPUT, inst->clk_data.dpb_fourcc);
		if (rc)
			return rc;

		multi_stream.buffer_type = HFI_BUFFER_OUTPUT2;
		multi_stream.enable = true;
		rc = call_hfi_op(hdev, session_set_property, inst->session,
			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM, &multi_stream,
			sizeof(multi_stream));
		if (rc) {
			s_vpr_e(sid,
				"%s: set prop multistream secondary failed : %d\n",
				__func__, rc);
			return rc;
		}
		multi_stream.buffer_type = HFI_BUFFER_OUTPUT;
		multi_stream.enable = false;
		rc = call_hfi_op(hdev, session_set_property, inst->session,
			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM, &multi_stream,
			sizeof(multi_stream));
		if (rc) {
			s_vpr_e(sid,
				"%s: set prop multistream secondary2 failed: %d\n",
				__func__, rc);
			return rc;
		}
		frame_sz.buffer_type = HFI_BUFFER_OUTPUT2;
		f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
		frame_sz.width = f->fmt.pix_mp.width;
		frame_sz.height = f->fmt.pix_mp.height;
		s_vpr_h(sid,
			"frame_size: hal buffer type %d, width %d, height %d\n",
			frame_sz.buffer_type, frame_sz.width, frame_sz.height);
		rc = call_hfi_op(hdev, session_set_property, inst->session,
			HFI_PROPERTY_PARAM_FRAME_SIZE, &frame_sz,
			sizeof(frame_sz));
		if (rc) {
			s_vpr_e(sid, "%s: set prop frame_size failed\n",
				__func__);
			return rc;
		}
	}

	return rc;
}

int msm_vdec_set_priority(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct hfi_enable hfi_property;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	hfi_property.enable = is_realtime_session(inst);

	s_vpr_h(inst->sid, "%s: %#x\n", __func__, hfi_property.enable);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_CONFIG_REALTIME, &hfi_property,
		sizeof(hfi_property));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_seqchng_at_syncframe(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct hfi_enable hfi_property;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;
	hfi_property.enable = is_low_latency_hint(inst);

	if (!hfi_property.enable)
		return 0;

	s_vpr_h(inst->sid, "%s: %#x\n", __func__, hfi_property.enable);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_VDEC_SEQCHNG_AT_SYNCFRM, &hfi_property,
		sizeof(hfi_property));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}

int msm_vdec_set_conceal_color(struct msm_vidc_inst *inst)
{
	int rc = 0;
	struct hfi_device *hdev;
	struct v4l2_ctrl *ctrl_8b;
	struct v4l2_ctrl *ctrl_10b;
	struct hfi_conceal_color conceal_color;

	if (!inst || !inst->core) {
		d_vpr_e("%s: invalid params %pK\n", __func__, inst);
		return -EINVAL;
	}
	hdev = inst->core->device;

	ctrl_8b = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT);
	ctrl_10b = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT);
	conceal_color.conceal_color_8bit = ctrl_8b->val;
	conceal_color.conceal_color_10bit = ctrl_10b->val;

	s_vpr_h(inst->sid, "%s: %#x %#x\n", __func__,
		conceal_color.conceal_color_8bit,
		conceal_color.conceal_color_10bit);
	rc = call_hfi_op(hdev, session_set_property, inst->session,
		HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR, &conceal_color,
		sizeof(conceal_color));
	if (rc)
		s_vpr_e(inst->sid, "%s: set property failed\n", __func__);

	return rc;
}


int msm_vdec_set_extradata(struct msm_vidc_inst *inst)
{
	uint32_t display_info = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA;
	u32 value = 0x0;
	u32 codec;

	codec = get_v4l2_codec(inst);
	switch (codec) {
	case V4L2_PIX_FMT_H264:
	case V4L2_PIX_FMT_HEVC:
		display_info = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA;
		break;
	case V4L2_PIX_FMT_VP8:
	case V4L2_PIX_FMT_VP9:
		display_info =
			HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA;
		break;
	case V4L2_PIX_FMT_MPEG2:
		display_info = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA;
		break;
	}

	/* Enable Default Extradata */
	msm_comm_set_index_extradata(inst,
		MSM_VIDC_EXTRADATA_OUTPUT_CROP, 0x1);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA, 0x1);
	msm_comm_set_extradata(inst, display_info, 0x1);

	if (codec == V4L2_PIX_FMT_VP9 || codec == V4L2_PIX_FMT_HEVC) {
		msm_comm_set_extradata(inst,
			HFI_PROPERTY_PARAM_VDEC_HDR10_HIST_EXTRADATA, 0x1);
	}

	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB, 0x1);
	if (codec == V4L2_PIX_FMT_HEVC) {
		msm_comm_set_extradata(inst,
			HFI_PROPERTY_PARAM_VDEC_MASTER_DISP_COL_SEI_EXTRADATA,
			0x1);
		msm_comm_set_extradata(inst,
			HFI_PROPERTY_PARAM_VDEC_CLL_SEI_EXTRADATA, 0x1);
		msm_comm_set_extradata(inst,
			HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA,
			0x1);
	}

	/* Enable / Disable Advanced Extradata */
	if (inst->prop.extradata_ctrls & EXTRADATA_ADVANCED)
		value = 0x1;
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA, value);
	msm_comm_set_index_extradata(inst,
		MSM_VIDC_EXTRADATA_ASPECT_RATIO, value);
	msm_comm_set_extradata(inst,
		HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA, value);

	return 0;
}

int msm_vdec_set_properties(struct msm_vidc_inst *inst)
{
	int rc = 0;

	if (!in_port_reconfig(inst)) {
		/* do not allow these settings in port reconfiration */
		rc = msm_vdec_set_frame_size(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_input_buffer_counts(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_profile_level(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_output_order(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_sync_frame_mode(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_secure_mode(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_extradata(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_priority(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_conceal_color(inst);
		if (rc)
			goto exit;
		rc = msm_vdec_set_seqchng_at_syncframe(inst);
		if (rc)
			goto exit;
	}

	rc = msm_vdec_set_color_format(inst);
	if (rc)
		goto exit;
	rc = msm_vdec_set_output_stream_mode(inst);
	if (rc)
		goto exit;
	rc = msm_vdec_set_output_buffer_counts(inst);
	if (rc)
		goto exit;

exit:
	if (rc)
		s_vpr_e(inst->sid, "%s: failed with %d\n", __func__, rc);
	else
		s_vpr_h(inst->sid, "%s: set properties successful\n", __func__);

	return rc;
}

int msm_vdec_ctrl_init(struct msm_vidc_inst *inst,
	const struct v4l2_ctrl_ops *ctrl_ops)
{
	return msm_comm_ctrl_init(inst, msm_vdec_ctrls,
		ARRAY_SIZE(msm_vdec_ctrls), ctrl_ops);
}
