// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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.
 */

#include "msm_vidc_common.h"
#include "vidc_hfi_api.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_dcvs.h"

#define IS_VALID_DCVS_SESSION(__cur_mbpf, __min_mbpf) \
		((__cur_mbpf) >= (__min_mbpf))

static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst);
static bool msm_dcvs_enc_check(struct msm_vidc_inst *inst);
static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst);
static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd);

static inline int msm_dcvs_get_mbs_per_frame(struct msm_vidc_inst *inst)
{
	int height, width;

	if (!inst->in_reconfig) {
		height = max(inst->prop.height[CAPTURE_PORT],
				inst->prop.height[OUTPUT_PORT]);
		width = max(inst->prop.width[CAPTURE_PORT],
				inst->prop.width[OUTPUT_PORT]);
	} else {
		height = inst->reconfig_height;
		width = inst->reconfig_width;
	}

	return NUM_MBS_PER_FRAME(height, width);
}

static inline int msm_dcvs_count_active_instances(struct msm_vidc_core *core)
{
	int active_instances = 0;
	struct msm_vidc_inst *inst = NULL;

	if (!core) {
		dprintk(VIDC_ERR, "%s: Invalid args: %pK\n", __func__, core);
		return -EINVAL;
	}

	mutex_lock(&core->lock);
	list_for_each_entry(inst, &core->instances, list) {
		if (inst->state >= MSM_VIDC_OPEN_DONE &&
			inst->state < MSM_VIDC_STOP_DONE)
			active_instances++;
	}
	mutex_unlock(&core->lock);
	return active_instances;
}

static bool msm_dcvs_check_codec_supported(int fourcc,
		unsigned long codecs_supported, enum session_type type)
{
	int codec_bit, session_type_bit;
	bool codec_type, session_type;
	unsigned long session;

	session = VIDC_VOTE_DATA_SESSION_VAL(get_hal_codec(fourcc),
		get_hal_domain(type));

	if (!codecs_supported || !session)
		return false;

	/* ffs returns a 1 indexed, test_bit takes a 0 indexed...index */
	codec_bit = ffs(session) - 1;
	session_type_bit = codec_bit + 1;

	codec_type =
		test_bit(codec_bit, &codecs_supported) ==
		test_bit(codec_bit, &session);
	session_type =
		test_bit(session_type_bit, &codecs_supported) ==
		test_bit(session_type_bit, &session);

	return codec_type && session_type;
}

static void msm_dcvs_update_dcvs_params(int idx, struct msm_vidc_inst *inst)
{
	struct dcvs_stats *dcvs = NULL;
	struct msm_vidc_platform_resources *res = NULL;
	struct dcvs_table *table = NULL;

	if (!inst || !inst->core) {
		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
		return;
	}

	dcvs = &inst->dcvs;
	res = &inst->core->resources;
	table = res->dcvs_tbl;

	dcvs->load_low = table[idx].load_low;
	dcvs->load_high = table[idx].load_high;
	dcvs->supported_codecs = table[idx].supported_codecs;
}

static void msm_dcvs_enc_check_and_scale_clocks(struct msm_vidc_inst *inst)
{
	int rc = 0;

	if (inst->session_type == MSM_VIDC_ENCODER && msm_vidc_enc_dcvs_mode) {
		inst->dcvs_mode = msm_dcvs_check_supported(inst);
		dprintk(VIDC_DBG, "%s: session DCVS %s supported\n",
				__func__, inst->dcvs_mode ? "" : "not");

		if (inst->dcvs_mode) {
			rc = msm_dcvs_enc_scale_clocks(inst);
			if (rc) {
				dprintk(VIDC_DBG,
				"ENC_DCVS: error while scaling clocks\n");
			}
		}
	}
}

static void msm_dcvs_dec_check_and_scale_clocks(struct msm_vidc_inst *inst)
{
	int rc = 0;

	if (inst->session_type != MSM_VIDC_DECODER || !msm_vidc_dec_dcvs_mode)
		return;

	if (msm_dcvs_check_supported(inst)) {
		inst->dcvs_mode = true;
		dprintk(VIDC_DBG,
			"%s: session DCVS supported, decode_dcvs_mode = %d\n",
			__func__, inst->dcvs_mode);
	} else {
		inst->dcvs_mode = false;
		dprintk(VIDC_DBG,
			"%s: session DCVS not supported, decode_dcvs_mode = %d\n",
			__func__, inst->dcvs_mode);
	}

	if (msm_vidc_dec_dcvs_mode && inst->dcvs_mode) {
		msm_dcvs_monitor_buffer(inst);
		rc = msm_dcvs_dec_scale_clocks(inst, false);
		if (rc) {
			dprintk(VIDC_ERR,
				"%s: Failed to scale clocks in DCVS: %d\n",
				__func__, rc);
		}
	}
}

void msm_dcvs_check_and_scale_clocks(struct msm_vidc_inst *inst, bool is_etb)
{
	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
		return;
	}

	if (is_etb)
		msm_dcvs_enc_check_and_scale_clocks(inst);
	else
		msm_dcvs_dec_check_and_scale_clocks(inst);
}

static inline int get_pending_bufs_fw(struct msm_vidc_inst *inst)
{
	int fw_out_qsize = 0, buffers_in_driver = 0;

	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
		return -EINVAL;
	}

	if (inst->state >= MSM_VIDC_OPEN_DONE &&
		inst->state < MSM_VIDC_STOP_DONE) {
		fw_out_qsize = inst->count.ftb - inst->count.fbd;
		buffers_in_driver = inst->buffers_held_in_driver;
	}

	return fw_out_qsize + buffers_in_driver;
}

static inline void msm_dcvs_print_dcvs_stats(struct dcvs_stats *dcvs)
{
	dprintk(VIDC_DBG,
		"DCVS: Load_Low %d, Load High %d\n",
		dcvs->load_low,
		dcvs->load_high);

	dprintk(VIDC_DBG,
		"DCVS: ThrDispBufLow %d, ThrDispBufHigh %d\n",
		dcvs->threshold_disp_buf_low,
		dcvs->threshold_disp_buf_high);

	dprintk(VIDC_DBG,
		"DCVS: min_threshold %d, max_threshold %d\n",
		dcvs->min_threshold, dcvs->max_threshold);
}

void msm_dcvs_init_load(struct msm_vidc_inst *inst)
{
	struct msm_vidc_core *core;
	struct hal_buffer_requirements *output_buf_req;
	struct dcvs_stats *dcvs;
	struct dcvs_table *table;
	struct msm_vidc_platform_resources *res = NULL;
	int i, num_rows, fourcc;

	dprintk(VIDC_DBG, "Init DCVS Load\n");

	if (!inst || !inst->core) {
		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
		return;
	}

	core = inst->core;
	dcvs = &inst->dcvs;
	res = &core->resources;
	dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);

	num_rows = res->dcvs_tbl_size;
	table = res->dcvs_tbl;

	if (!num_rows || !table) {
		dprintk(VIDC_ERR,
				"%s: Dcvs table entry not found.\n", __func__);
		return;
	}

	fourcc = inst->session_type == MSM_VIDC_DECODER ?
				inst->fmts[OUTPUT_PORT].fourcc :
				inst->fmts[CAPTURE_PORT].fourcc;

	for (i = 0; i < num_rows; i++) {
		bool matches = msm_dcvs_check_codec_supported(
					fourcc,
					table[i].supported_codecs,
					inst->session_type);
		if (!matches)
			continue;

		if (dcvs->load > table[i].load) {
			msm_dcvs_update_dcvs_params(i, inst);
			break;
		}
	}

	if (inst->session_type == MSM_VIDC_ENCODER)
		goto print_stats;

	output_buf_req = get_buff_req_buffer(inst,
		msm_comm_get_hal_output_buffer(inst));

	if (!output_buf_req) {
		dprintk(VIDC_ERR,
			"%s: No buffer requirement for buffer type %x\n",
			__func__, HAL_BUFFER_OUTPUT);
		return;
	}

	dcvs->transition_turbo = false;

	/* calculating the min and max threshold */
	if (output_buf_req->buffer_count_actual) {
		dcvs->min_threshold = output_buf_req->buffer_count_actual -
			output_buf_req->buffer_count_min -
			msm_dcvs_get_extra_buff_count(inst) + 1;
		dcvs->max_threshold = output_buf_req->buffer_count_actual;
		if (dcvs->max_threshold <= dcvs->min_threshold)
			dcvs->max_threshold =
				dcvs->min_threshold + DCVS_BUFFER_SAFEGUARD;
		dcvs->threshold_disp_buf_low = dcvs->min_threshold;
		dcvs->threshold_disp_buf_high = dcvs->max_threshold;
	}

print_stats:
	msm_dcvs_print_dcvs_stats(dcvs);
}

void msm_dcvs_init(struct msm_vidc_inst *inst)
{
	dprintk(VIDC_DBG, "Init DCVS Struct\n");

	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
		return;
	}

	inst->dcvs = (struct dcvs_stats){ {0} };
	inst->dcvs.threshold_disp_buf_high = DCVS_NOMINAL_THRESHOLD;
	inst->dcvs.threshold_disp_buf_low = DCVS_TURBO_THRESHOLD;
}

void msm_dcvs_monitor_buffer(struct msm_vidc_inst *inst)
{
	int new_ftb, i, prev_buf_count;
	int fw_pending_bufs, total_output_buf, buffers_outside_fw;
	struct dcvs_stats *dcvs;
	struct hal_buffer_requirements *output_buf_req;

	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
		return;
	}
	dcvs = &inst->dcvs;

	mutex_lock(&inst->lock);
	output_buf_req = get_buff_req_buffer(inst,
				msm_comm_get_hal_output_buffer(inst));
	if (!output_buf_req) {
		dprintk(VIDC_ERR, "%s : Get output buffer req failed %pK\n",
			__func__, inst);
		mutex_unlock(&inst->lock);
		return;
	}

	total_output_buf = output_buf_req->buffer_count_actual;
	fw_pending_bufs = get_pending_bufs_fw(inst);
	mutex_unlock(&inst->lock);

	buffers_outside_fw = total_output_buf - fw_pending_bufs;
	dcvs->num_ftb[dcvs->ftb_index] = buffers_outside_fw;
	dcvs->ftb_index = (dcvs->ftb_index + 1) % DCVS_FTB_WINDOW;

	if (dcvs->ftb_counter < DCVS_FTB_WINDOW)
		dcvs->ftb_counter++;

	dprintk(VIDC_PROF,
		"DCVS: ftb_counter %d\n", dcvs->ftb_counter);

	if (dcvs->ftb_counter == DCVS_FTB_WINDOW) {
		new_ftb = 0;
		for (i = 0; i < dcvs->ftb_counter; i++) {
			if (dcvs->num_ftb[i] > new_ftb)
				new_ftb = dcvs->num_ftb[i];
		}

		dcvs->threshold_disp_buf_high = new_ftb;
		if (dcvs->threshold_disp_buf_high <=
			dcvs->threshold_disp_buf_low +
			DCVS_BUFFER_SAFEGUARD) {
			dcvs->threshold_disp_buf_high =
				dcvs->threshold_disp_buf_low +
				DCVS_BUFFER_SAFEGUARD
				+ (DCVS_BUFFER_SAFEGUARD == 0);
		}

		dcvs->threshold_disp_buf_high =
			clamp(dcvs->threshold_disp_buf_high,
				dcvs->min_threshold,
				dcvs->max_threshold);
	}

	if (dcvs->ftb_counter == DCVS_FTB_WINDOW &&
			dcvs->load == dcvs->load_low) {
		prev_buf_count =
			dcvs->num_ftb[((dcvs->ftb_index - 2 +
				DCVS_FTB_WINDOW) % DCVS_FTB_WINDOW)];
		if (prev_buf_count == dcvs->threshold_disp_buf_low &&
			buffers_outside_fw <= dcvs->threshold_disp_buf_low) {
			dcvs->transition_turbo = true;
		} else if (buffers_outside_fw > dcvs->threshold_disp_buf_low &&
			(buffers_outside_fw -
			 (prev_buf_count - buffers_outside_fw))
			< dcvs->threshold_disp_buf_low){
			dcvs->transition_turbo = true;
		}
	}

	dprintk(VIDC_PROF,
		"DCVS: total_output_buf %d buffers_outside_fw %d load %d transition_turbo %d\n",
		total_output_buf, buffers_outside_fw, dcvs->load_low,
		dcvs->transition_turbo);
}

static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst)
{
	int rc = 0, fw_pending_bufs = 0, total_input_buf = 0;
	struct msm_vidc_core *core;
	struct dcvs_stats *dcvs;

	if (!inst || !inst->core || !inst->core->device) {
		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
		return -EINVAL;
	}

	core = inst->core;
	dcvs = &inst->dcvs;

	mutex_lock(&inst->lock);
	total_input_buf = inst->buff_req.buffer[0].buffer_count_actual;
	fw_pending_bufs = (inst->count.etb - inst->count.ebd);
	mutex_unlock(&inst->lock);

	dprintk(VIDC_PROF,
		"DCVS: total_input_buf %d, fw_pending_bufs %d\n",
		total_input_buf, fw_pending_bufs);

	if (dcvs->etb_counter < total_input_buf) {
		dcvs->etb_counter++;
		if (dcvs->etb_counter != total_input_buf)
			return rc;
	}

	dprintk(VIDC_PROF,
		"DCVS: total_input_buf %d, fw_pending_bufs %d etb_counter %d  dcvs->load %d\n",
		total_input_buf, fw_pending_bufs,
		dcvs->etb_counter, dcvs->load);

	if (fw_pending_bufs <= DCVS_ENC_LOW_THR &&
		dcvs->load > dcvs->load_low) {
		dcvs->load = dcvs->load_low;
		dcvs->prev_freq_lowered = true;
	} else {
		dcvs->prev_freq_lowered = false;
	}

	if (fw_pending_bufs >= DCVS_ENC_HIGH_THR &&
		dcvs->load <= dcvs->load_low) {
		dcvs->load = dcvs->load_high;
		dcvs->prev_freq_increased = true;
	} else {
		dcvs->prev_freq_increased = false;
	}

	if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) {
		dprintk(VIDC_PROF,
			"DCVS: (Scaling Clock %s)  etb clock set = %d total_input_buf = %d fw_pending_bufs %d\n",
			dcvs->prev_freq_lowered ? "Lower" : "Higher",
			dcvs->load, total_input_buf, fw_pending_bufs);

		rc = msm_comm_scale_clocks_load(core, dcvs->load,
				LOAD_CALC_NO_QUIRKS);
		if (rc) {
			dprintk(VIDC_PROF,
				"Failed to set clock rate in FBD: %d\n", rc);
		}
	} else {
		dprintk(VIDC_PROF,
			"DCVS: etb clock load_old = %d total_input_buf = %d fw_pending_bufs %d\n",
			dcvs->load, total_input_buf, fw_pending_bufs);
	}

	return rc;
}


/*
 * In DCVS scale_clocks will be done both in qbuf and FBD
 * 1 indicates call made from fbd that lowers clock
 * 0 indicates call made from qbuf that increases clock
 * based on DCVS algorithm
 */

static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd)
{
	int rc = 0;
	int fw_pending_bufs = 0;
	int total_output_buf = 0;
	int buffers_outside_fw = 0;
	struct msm_vidc_core *core;
	struct hal_buffer_requirements *output_buf_req;
	struct dcvs_stats *dcvs;

	if (!inst || !inst->core || !inst->core->device) {
		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
		return -EINVAL;
	}
	core = inst->core;
	dcvs = &inst->dcvs;
	mutex_lock(&inst->lock);
	fw_pending_bufs = get_pending_bufs_fw(inst);

	output_buf_req = get_buff_req_buffer(inst,
		msm_comm_get_hal_output_buffer(inst));
	mutex_unlock(&inst->lock);
	if (!output_buf_req) {
		dprintk(VIDC_ERR,
			"%s: No buffer requirement for buffer type %x\n",
			__func__, HAL_BUFFER_OUTPUT);
		return -EINVAL;
	}

	/* Total number of output buffers */
	total_output_buf = output_buf_req->buffer_count_actual;

	/* Buffers outside FW are with display */
	buffers_outside_fw = total_output_buf - fw_pending_bufs;

	if (buffers_outside_fw >= dcvs->threshold_disp_buf_high &&
		!dcvs->prev_freq_increased &&
		dcvs->load > dcvs->load_low) {
		dcvs->load = dcvs->load_low;
		dcvs->prev_freq_lowered = true;
		dcvs->prev_freq_increased = false;
	} else if (dcvs->transition_turbo && dcvs->load == dcvs->load_low) {
		dcvs->load = dcvs->load_high;
		dcvs->prev_freq_increased = true;
		dcvs->prev_freq_lowered = false;
		dcvs->transition_turbo = false;
	} else {
		dcvs->prev_freq_increased = false;
		dcvs->prev_freq_lowered = false;
	}

	if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) {
		dprintk(VIDC_PROF,
			"DCVS: clock set = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n",
			dcvs->load, total_output_buf, buffers_outside_fw,
			dcvs->threshold_disp_buf_high, dcvs->transition_turbo);

		rc = msm_comm_scale_clocks_load(core, dcvs->load,
				LOAD_CALC_NO_QUIRKS);
		if (rc) {
			dprintk(VIDC_ERR,
				"Failed to set clock rate in FBD: %d\n", rc);
		}
	} else {
		dprintk(VIDC_PROF,
			"DCVS: clock old = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n",
			dcvs->load, total_output_buf, buffers_outside_fw,
			dcvs->threshold_disp_buf_high, dcvs->transition_turbo);
	}
	return rc;
}

static bool msm_dcvs_enc_check(struct msm_vidc_inst *inst)
{
	int num_mbs_per_frame = 0;
	long instance_load = 0;
	long dcvs_limit = 0;
	bool dcvs_check_passed = false, is_codec_supported  = false;
	struct msm_vidc_platform_resources *res = NULL;

	if (!inst || !inst->core) {
		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
		return dcvs_check_passed;
	}

	res = &inst->core->resources;
	if (!res->dcvs_limit) {
		dprintk(VIDC_ERR,
			"%s Dcvs limit table uninitialized\n", __func__);
		return false;
	}

	is_codec_supported =
		msm_dcvs_check_codec_supported(
				inst->fmts[CAPTURE_PORT].fourcc,
				inst->dcvs.supported_codecs,
				inst->session_type);

	num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst);
	instance_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);
	dcvs_limit =
		(long)res->dcvs_limit[inst->session_type].min_mbpf *
		res->dcvs_limit[inst->session_type].fps;

	if (msm_vidc_enc_dcvs_mode && is_codec_supported &&
		inst->dcvs.is_power_save_mode &&
		IS_VALID_DCVS_SESSION(num_mbs_per_frame,
			res->dcvs_limit[inst->session_type].min_mbpf) &&
		IS_VALID_DCVS_SESSION(instance_load, dcvs_limit)) {
		dcvs_check_passed = true;
	}
	return dcvs_check_passed;
}

static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
{
	int num_mbs_per_frame = 0, instance_count = 0;
	long instance_load = 0;
	long dcvs_limit = 0;
	struct msm_vidc_inst *temp = NULL;
	struct msm_vidc_core *core;
	struct hal_buffer_requirements *output_buf_req;
	struct dcvs_stats *dcvs;
	bool is_codec_supported = false;
	struct msm_vidc_platform_resources *res = NULL;

	if (!inst || !inst->core || !inst->core->device) {
		dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
		return -EINVAL;
	}

	core = inst->core;
	dcvs = &inst->dcvs;
	res = &core->resources;

	if (!res->dcvs_limit) {
		dprintk(VIDC_INFO,
				"%s: dcvs limit table not found\n", __func__);
		return false;
	}
	instance_count = msm_dcvs_count_active_instances(core);

	if (instance_count == 1 && inst->session_type == MSM_VIDC_DECODER &&
		!msm_comm_turbo_session(inst)) {
		num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst);
		instance_load = msm_comm_get_inst_load(inst,
			LOAD_CALC_NO_QUIRKS);
		output_buf_req = get_buff_req_buffer(inst,
			msm_comm_get_hal_output_buffer(inst));
		dcvs_limit =
			(long)res->dcvs_limit[inst->session_type].min_mbpf *
			res->dcvs_limit[inst->session_type].fps;
		is_codec_supported =
			msm_dcvs_check_codec_supported(
					inst->fmts[OUTPUT_PORT].fourcc,
					inst->dcvs.supported_codecs,
					inst->session_type);
		if (!is_codec_supported ||
			!IS_VALID_DCVS_SESSION(num_mbs_per_frame,
				res->dcvs_limit[inst->session_type].min_mbpf) ||
			!IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) ||
			inst->seqchanged_count > 1)
			return false;

		if (!output_buf_req) {
			dprintk(VIDC_ERR,
				"%s: No buffer requirement for buffer type %x\n",
				__func__, HAL_BUFFER_OUTPUT);
			return false;
		}
	} else if (instance_count == 1 &&
			inst->session_type == MSM_VIDC_ENCODER &&
			!msm_comm_turbo_session(inst)) {
		if (!msm_dcvs_enc_check(inst))
			return false;
	} else {
		/*
		 * For multiple instance use case with 4K, clocks will be scaled
		 * as per load in streamon, but the clocks may be scaled
		 * down as DCVS is running for first playback instance
		 * Rescaling the core clock for multiple instance use case
		 */
		if (!dcvs->is_clock_scaled) {
			if (!msm_comm_scale_clocks(core)) {
				dcvs->is_clock_scaled = true;
				dprintk(VIDC_DBG,
					"%s: Scaled clocks = %d\n",
					__func__, dcvs->is_clock_scaled);
			} else {
				dprintk(VIDC_DBG,
					"%s: Failed to Scale clocks. Perf might be impacted\n",
					__func__);
			}
		}
		/*
		 * For multiple instance use case turn OFF DCVS algorithm
		 * immediately
		 */
		if (instance_count > 1) {
			mutex_lock(&core->lock);
			list_for_each_entry(temp, &core->instances, list)
				temp->dcvs_mode = false;
			mutex_unlock(&core->lock);
		}

		return false;
	}

	return true;
}

int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst)
{
	int extra_buffer = 0;

	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
		return 0;
	}

	if (inst->session_type == MSM_VIDC_ENCODER) {
		if (msm_dcvs_enc_check(inst))
			extra_buffer = DCVS_ENC_EXTRA_OUTPUT_BUFFERS;
	} else if (inst->session_type == MSM_VIDC_DECODER) {
		if (msm_dcvs_check_supported(inst))
			extra_buffer = DCVS_DEC_EXTRA_OUTPUT_BUFFERS;
	}
	return extra_buffer;
}


void msm_dcvs_enc_set_power_save_mode(struct msm_vidc_inst *inst,
					bool is_power_save_mode)
{
	if (!inst) {
		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
		return;
	}

	inst->dcvs.is_power_save_mode = is_power_save_mode;
}
