// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */
#include "mtk_tv_drm_vrr.h"
#include "mtk_tv_drm_video_frc.h"
#include "../mtk_tv_drm_log.h"
#include "../mtk_tv_drm_kms.h"

#include "mtk_tv_drm_video.h"

#include "hwreg_pq_display_vrr.h"
#include "hwreg_render_video_pnl.h"

#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/component.h>
#include <drm/mtk_tv_drm.h>
#include "hwreg_render_stub.h"
//mdelay
#include <linux/delay.h>
//pm
#include <soc/mediatek/mtk-pm.h>
//LEDS
#include <linux/leds_mtk_control.h>
#include "mtk-reserved-memory.h"

//-------------------------------------------------------------------------------------------------
// Prototype
//-------------------------------------------------------------------------------------------------
#define MTK_DRM_MODEL MTK_DRM_MODEL_VRR

#define QMS_MIN_TFR_IDX (1)
#define QMS_MAX_TFR_IDX (13)
#define QMS_TFR_NUM (14)

#define QMS_OUTPUT_FRC_EARLY_FRAME_NUM 1
#define CLK_TOLERANCEX1000 (5UL) //from HDMI spec: 0.5%
#define QMS_FIFO_MUX_OUTPUT (1) //1:output 2:input

#define DEC_100 (100UL)
#define DEC_1000 (1000UL)
#define SHIFT_BITS_4 (4)
#define SHIFT_BITS_8 (8)

#define CLK_TOLERANCEX1000_LOWER_BOUND (DEC_1000 - CLK_TOLERANCEX1000)
#define CLK_TOLERANCEX1000_UPPER_BOUND (DEC_1000 + CLK_TOLERANCEX1000)

#define IS_NEED_QMS_CORRECTION(x, y) ((x == EN_XC_QMS_PROJECT_PERFECT_DELAY_HW) && (y == 0))
#define IS_TFR_IN_RANGE(x) ((x >= QMS_MIN_TFR_IDX) && (x <= QMS_MAX_TFR_IDX))

static enum EN_XC_QMS_SETTING_STATUS _eQmsSettingStatus = E_XC_QMS_SETTING_NONE;
static enum video_crtc_frame_sync_mode eQmsPreferFrameSyncMode;
static __u8 _u8TotalDelayFrame;
static __u8 _u8MaxFrameInfoLen;

static struct vrr_qms_buf_mem_info _stVrrQmsBufMemInfo = {0};
static struct vrr_qms_base_info *_stVRRQMSBaseInfo;
static struct vrr_init_info vrr_info;
//-------------------------------------------------------------------------------------------------
// Structure
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
// Function
//-------------------------------------------------------------------------------------------------

void _mtk_tv_panel_vrr_clear_shm(void)
{
	uint64_t *addr = NULL;

	addr = (uint64_t *)_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr;
	memset(addr, 0, _stVrrQmsBufMemInfo.u32VrrQmsBufSize);
}

void _mtk_tv_panel_vrr_set_qms_off(void)
{
	_eQmsSettingStatus = E_XC_QMS_SETTING_NONE;
	_u8TotalDelayFrame = 0;
	_u8MaxFrameInfoLen = 0;
	_mtk_tv_panel_vrr_clear_shm();

	DRM_ERROR("[%s:%d][VRR] turn off QMS\n", __func__, __LINE__);
}

struct vrr_qms_per_frame_info *_mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(__u8 idx)
{
	struct vrr_qms_per_frame_info *stPerFrameInfo = NULL;

	if (idx > _u8MaxFrameInfoLen)
		idx = _u8MaxFrameInfoLen;

	stPerFrameInfo = (struct vrr_qms_per_frame_info *)(_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr
		+ ((idx + 1) * sizeof(struct vrr_qms_per_frame_info)));

	return stPerFrameInfo;
}

struct vrr_qms_per_frame_info *_mtk_tv_panel_vrr_hw_correction(__u8 *CurFrameIdx,
	__u8 *CurFrcIdx, __u8 *CurFrcOpCnt)
{
	int ret = 0;
	struct vrr_qms_per_frame_info *stPerFrameInfo = NULL;
	bool mapping = false;
	__u8 u8Idx = 0;
	__u8 u8FrameIdx = 0;
	__u8 u8IdxUpperBound = 0;
	__u32 u32FifoOutCounter = 0;

	if ((!CurFrameIdx) || (!CurFrcIdx) || (!CurFrcOpCnt)) {
		DRM_ERROR("[%s, %d]: null ptr!\n", __func__, __LINE__);
		return NULL;
	}

	ret = drv_hwreg_pq_display_vrr_get_qms_fifo_counter(&u32FifoOutCounter,
		QMS_FIFO_MUX_OUTPUT);
	if (ret) {
		DRM_ERROR("QMS get counter failed.\n");
		return _mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(u8FrameIdx);
	}

	if (_u8TotalDelayFrame == 0)
		u8IdxUpperBound = _u8MaxFrameInfoLen;

	for (u8Idx = 0; u8Idx <= u8IdxUpperBound; u8Idx++) {
		u8FrameIdx =
			(*CurFrameIdx + _u8MaxFrameInfoLen - u8Idx) % _u8MaxFrameInfoLen;
		stPerFrameInfo = _mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(u8FrameIdx);
		if (stPerFrameInfo->u32FifoCounter == u32FifoOutCounter) {
			mapping = true;
			break;
		}
	}

	if (!mapping)
		return stPerFrameInfo;

	if (_u8TotalDelayFrame > 0)
		_u8TotalDelayFrame = 0;

	if (*CurFrameIdx != u8FrameIdx)
		DRM_INFO("[VRR][QMS][Output] SW adjust: idx(%d -> %d), counter(%d)\n",
			*CurFrameIdx, u8FrameIdx, u32FifoOutCounter);

	*CurFrameIdx = u8FrameIdx;
	if (stPerFrameInfo->u8OutRatio > QMS_OUTPUT_FRC_EARLY_FRAME_NUM) {
		*CurFrcIdx = u8FrameIdx;
		*CurFrcOpCnt = QMS_OUTPUT_FRC_EARLY_FRAME_NUM;
	} else {
		*CurFrcIdx =
			(u8FrameIdx + _u8MaxFrameInfoLen + QMS_OUTPUT_FRC_EARLY_FRAME_NUM)
			% _u8MaxFrameInfoLen;
		*CurFrcOpCnt = 0;
	}

	return stPerFrameInfo;
}
void _mtk_tv_panel_vrr_control_sw_mode(
	struct mtk_panel_context *pctx_pnl,
	struct vrr_qms_per_frame_info *stNextFrameInfo,
	struct vrr_qms_per_frame_info *stFRCFrameInfo,
	__u16 *u16CurTotalOutputvtt,
	__u8 u8CurIdxOpcnt, __u8 u8CurFrcOpCnt)
{

	struct hwregOut paramOut;
	struct reg_info reg[HWREG_VIDEO_REG_NUM_PANEL];

	bool bRIU = true;
	static __u8 _u8CurTfr;
	__u8 u8Tfr = 0;
	__u8 u8FrcRdNextMode = 0;
	__u16 u16Typvtt = 0;
	__u16 u16TypFreq = 0;
	__u32 u32TotalOutputvtt = 0;
	__u16 u16PerframeOutputvtt = 0;
	__u16 u16Outputvtt = 0;
	__u32 u32VttUpperBound = 0;
	__u32 u32VttLowerBound = 0;

	memset(&reg, 0, sizeof(reg));
	memset(&paramOut, 0, sizeof(struct hwregOut));
	paramOut.reg = reg;

	if ((!pctx_pnl) || (!stNextFrameInfo) || (!u16CurTotalOutputvtt)) {
		DRM_ERROR("[%s, %d]: null ptr!\n", __func__, __LINE__);
		return;
	}

	u16Typvtt = pctx_pnl->info.typ_vtt;
	u16TypFreq = (__u16)(pctx_pnl->outputTiming_info.u32OutputVfreq/DEC_1000);

	if (u8CurIdxOpcnt == 0)
		*u16CurTotalOutputvtt = 0;

	//1. get frame info of (N+1) from shm
	//1.1 set vtt register:
	if ((stNextFrameInfo->u16InputVFreqX100 > 0) && (_u8TotalDelayFrame == 0)) {
		//1.1.1 calculate output vtt by input vfreq
		u32TotalOutputvtt = (__u32)u16Typvtt * (__u32)u16TypFreq * DEC_100 /
				(__u32)(stNextFrameInfo->u16InputVFreqX100);
		u32VttUpperBound = u32TotalOutputvtt * CLK_TOLERANCEX1000_UPPER_BOUND / DEC_1000;
		u32VttLowerBound = u32TotalOutputvtt * CLK_TOLERANCEX1000_LOWER_BOUND / DEC_1000;

		// 1.1.2 use HW report tgen input vtt if the value is correct
		// in order to cover input/output clk diff.
		// Trigger gen dummy 0 value will affect its update time,
		// so its just a error-handling to prevent we get the info of  previous frame
		if ((stNextFrameInfo->u16InputVtt > u32VttLowerBound) &&
			(stNextFrameInfo->u16InputVtt < u32VttUpperBound))
			u32TotalOutputvtt = (__u32)stNextFrameInfo->u16InputVtt;

		u16PerframeOutputvtt =
			(__u16)(u32TotalOutputvtt)/(__u16)(stNextFrameInfo->u8OutRatio);

		if (u8CurIdxOpcnt == (stNextFrameInfo->u8OutRatio - 1))
			u16Outputvtt = (__u16)(u32TotalOutputvtt) - *u16CurTotalOutputvtt;
		else
			u16Outputvtt = u16PerframeOutputvtt;

		if (u16Outputvtt < pctx_pnl->info.min_vtt_panelprotect) {
			u16Outputvtt = pctx_pnl->info.min_vtt_panelprotect;
			DRM_ERROR("[VRR][QMS] Frame[%d]: vtt = %d (%d Hz), outof lower boud!\n",
				stNextFrameInfo->u8FrameIdx,
				u16Outputvtt,
				stNextFrameInfo->u16InputVFreqX100);
		} else if (u16Outputvtt > pctx_pnl->info.max_vtt_panelprotect) {
			u16Outputvtt = pctx_pnl->info.max_vtt_panelprotect;
			DRM_ERROR("[VRR][QMS] Frame[%d]: vtt = %d (%d Hz), outof upper boud!\n",
				stNextFrameInfo->u8FrameIdx,
				u16Outputvtt,
				stNextFrameInfo->u16InputVFreqX100);
		}

		if (stNextFrameInfo->bUsrTfrMode)
			u8Tfr = stNextFrameInfo->u8UsrTfr;
		else
			u8Tfr = stNextFrameInfo->u8InputTfr;

		*u16CurTotalOutputvtt += u16Outputvtt;
		u8FrcRdNextMode = 1;
	} else
		u16Outputvtt = u16Typvtt;

	drv_hwreg_render_pnl_set_vtt(
		&paramOut,
		bRIU,
		u16Outputvtt);

	if (_u8CurTfr != u8Tfr) {
		DRM_INFO("[VRR][QMS][Output][TFR will changed!] TFR(%d), vtt(%d)\n",
			u8Tfr, u16Outputvtt);
		_u8CurTfr = u8Tfr;
	}

	//2. set R2 shm
	if (stFRCFrameInfo != NULL)
		mtk_video_display_frc_set_qms_vrr_info(stFRCFrameInfo->u8FrcOpmRIdx,
			u8CurFrcOpCnt, u8FrcRdNextMode);

	DRM_INFO("[VRR][QMS][Output][NEXT] Frame[%d %d/%d], Counter(%u), vtt(%d), user(%d, %d)",
		stNextFrameInfo->u8FrameIdx,
		u8CurIdxOpcnt,
		stNextFrameInfo->u8OutRatio,
		stNextFrameInfo->u32FifoCounter,
		stNextFrameInfo->u16InputVtt,
		stNextFrameInfo->bUsrTfrMode,
		stNextFrameInfo->u8UsrTfr);
}

void _mtk_tv_panel_vrr_control_hw_mode(
	struct mtk_panel_context *pctx_pnl,
	struct vrr_qms_per_frame_info *stNextFrameInfo,
	__u16 *u16CurTotalOutputvtt,
	__u8 u8CurIdxOpcnt)
{
	static bool _bTfrUsrMode;
	bool bFreerunClr = TRUE;
	__u8 u8FrcRdNextMode = 0; //0:repeat, 1:update, 2:drop
	__u8 u8CurTfr = 0;
	__u16 u16Phase = 0;
	__u16 u16OutputVtt = 0;
	__u8 u8FreerunStatus = 0;

	if ((!pctx_pnl) || (!stNextFrameInfo) || (!u16CurTotalOutputvtt)) {
		DRM_ERROR("[%s, %d]: null ptr!\n", __func__, __LINE__);
		return;
	}

	drv_hwreg_pq_display_vrr_get_qms_tgen_tfr(&u8CurTfr);
	if ((!vrr_info.vrr_qms_hw_support_error_handling) ||
		(IS_TFR_IN_RANGE(u8CurTfr)))
		drv_hwreg_pq_display_vrr_set_qms_force_freerun_clr(bFreerunClr);

	if (_u8TotalDelayFrame > 0) {
		_bTfrUsrMode = TRUE;
		drv_hwreg_pq_display_vrr_set_qms_fifo_usr_tfr_mode(_bTfrUsrMode, 0);
		return;
	}

	//if TFR user mode on, set hw reg only in the locked frame
	if ((u8CurIdxOpcnt == 0) && (_u8TotalDelayFrame == 0)) {
		if (stNextFrameInfo->bUsrTfrMode) {
			_bTfrUsrMode = TRUE;
			drv_hwreg_pq_display_vrr_set_qms_fifo_usr_tfr_mode(_bTfrUsrMode,
				stNextFrameInfo->u8UsrTfr);
			DRM_ERROR("[VRR][QMS][Render] Enable TFR usr mode\n");
		} else if (_bTfrUsrMode) {
			//once freerun is triggered need to cleared by sw.
			_bTfrUsrMode = FALSE;
			drv_hwreg_pq_display_vrr_set_qms_fifo_usr_tfr_mode(_bTfrUsrMode,
				stNextFrameInfo->u8UsrTfr);
			DRM_ERROR("[VRR][QMS][Render] Turn off TFR usr mode\n");
		}
	}

	if ((stNextFrameInfo->u8UsrTfr == 0) && (_bTfrUsrMode))
		_mtk_tv_panel_vrr_control_sw_mode(pctx_pnl, stNextFrameInfo, NULL,
			u16CurTotalOutputvtt, u8CurIdxOpcnt, 0);

	//2. set R2 shm
	mtk_video_display_frc_set_qms_vrr_info(0, 0, u8FrcRdNextMode);

	//3. get debug info
	drv_hwreg_pq_display_vrr_get_qms_tgen_sample_vcnt(&u16Phase);
	drv_hwreg_pq_display_vrr_get_qms_tgen_vrr_output_vtt(&u16OutputVtt);
	drv_hwreg_pq_display_vrr_get_qms_tgen_freerun_status(&u8FreerunStatus);

	DRM_INFO("[VRR][QMS][Output][PRE] vtt(%d), phase(%d), freerun(%d)\n", u16OutputVtt,
		u16Phase, u8FreerunStatus);
	DRM_INFO("[VRR][QMS][Output][CUR] TFR(%d)\n", u8CurTfr);
	DRM_INFO("[VRR][QMS][Output][NEXT] Frame[%d %d/%d], Counter(%u), vtt(%d), user(%d, %d)",
		stNextFrameInfo->u8FrameIdx,
		u8CurIdxOpcnt,
		stNextFrameInfo->u8OutRatio,
		stNextFrameInfo->u32FifoCounter,
		stNextFrameInfo->u16InputVtt,
		stNextFrameInfo->bUsrTfrMode,
		stNextFrameInfo->u8UsrTfr);
}

void mtk_tv_panel_vrr_control(struct mtk_tv_kms_context *pctx)
{
	struct mtk_panel_context *pctx_pnl = NULL;
	static __u8 _u8FrcOpCnt;
	static __u8 _u8IdxOpCnt;
	static __u8 _u8Idx;
	static __u8 _u8FrcReadScriptIdx;
	static __u8 _u8FrcDelayFrame;
	static __u16 u16CurTotalOutputvtt;
	static __u32 u32FirstInputFrame;

	static enum drm_render_vrr_mode_type _eVRR = E_RENDER_VRR_MODE_TYPE_NVRR;
	struct vrr_qms_per_frame_info *stPerFrameInfo[E_XC_QMS_FRAME_MAX];

	if (!pctx) {
		DRM_ERROR("[%s, %d]: null ptr! pctx=0x%llx\n",
			__func__, __LINE__, (unsigned long long)pctx);
		return;
	}

	pctx_pnl = &pctx->panel_priv;

	//0. check if vrr type change
	if (pctx->trigger_gen_info.eVrr != _eVRR) {
		if ((_eVRR == E_RENDER_VRR_MODE_TYPE_QMSVRR)
			&& (_eQmsSettingStatus > E_XC_QMS_SETTING_NONE))
			_mtk_tv_panel_vrr_set_qms_off();
		_eVRR = pctx->trigger_gen_info.eVrr;
	} else if (_eVRR != E_RENDER_VRR_MODE_TYPE_QMSVRR)
		return;

	if (_eQmsSettingStatus == E_XC_QMS_SETTING_NONE) {
		//0. check if framesync mode is correct
		if ((vrr_info.vrr_qms_suport) &&
			(pctx_pnl->outputTiming_info.eFrameSyncMode == eQmsPreferFrameSyncMode))
			_eQmsSettingStatus = E_XC_QMS_SETTING_READY;

		return;
	}

	if (_eQmsSettingStatus == E_XC_QMS_SETTING_READY) {
		//get Base Info
		_stVRRQMSBaseInfo =
			(struct vrr_qms_base_info *)(_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr);
		_u8TotalDelayFrame = _stVRRQMSBaseInfo->u8TotalDelayFrame;
		_u8MaxFrameInfoLen = _stVRRQMSBaseInfo->u8MaxFrameInfoLen;
		if (_u8TotalDelayFrame == 0)
			return;

		_u8FrcReadScriptIdx = 0;
		_u8Idx = 0;
		_u8FrcOpCnt = 0;
		_u8IdxOpCnt = 0;
		stPerFrameInfo[E_XC_QMS_FRAME_NEXT]
			= _mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(_u8Idx);
		if (stPerFrameInfo[E_XC_QMS_FRAME_NEXT]->bQms == FALSE)
			return;

		_eQmsSettingStatus = E_XC_QMS_SETTING_START;
		DRM_INFO("[%s:%d][VRR] QMS start: TotalDelay(%d), FrameInfoLen(%d), addr(%lld)\n",
			__func__, __LINE__,
			_u8TotalDelayFrame, _u8MaxFrameInfoLen,
			u32FirstInputFrame, (__u64)_stVRRQMSBaseInfo);
	}

	drv_hwreg_pq_display_vrr_set_qms_fifo_report_mux(QMS_FIFO_MUX_OUTPUT);


	if (IS_NEED_QMS_CORRECTION(vrr_info.vrr_qms_project, _u8IdxOpCnt))
		stPerFrameInfo[E_XC_QMS_FRAME_NEXT]
			= _mtk_tv_panel_vrr_hw_correction(&_u8Idx,
				&_u8FrcReadScriptIdx, &_u8FrcOpCnt);
	else
		stPerFrameInfo[E_XC_QMS_FRAME_NEXT]
			= _mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(_u8Idx);

	stPerFrameInfo[E_XC_QMS_FRAME_FRC_READ] =
		_mtk_tv_panel_vrr_get_qms_per_frame_shm_addr(_u8FrcReadScriptIdx);

	if (stPerFrameInfo[E_XC_QMS_FRAME_FRC_READ]->bQms == FALSE) {
		_mtk_tv_panel_vrr_set_qms_off();
		return;
	}

	switch (vrr_info.vrr_qms_project) {
	case EN_XC_QMS_PROJECT_PERFECT_DELAY_HW:
		_mtk_tv_panel_vrr_control_hw_mode(pctx_pnl,
			stPerFrameInfo[E_XC_QMS_FRAME_NEXT],
			 &u16CurTotalOutputvtt, _u8IdxOpCnt);
		break;
	case EN_XC_QMS_PROJECT_PERFECT_DELAY_SW_MLOAD:
		_mtk_tv_panel_vrr_control_sw_mode(pctx_pnl,
			stPerFrameInfo[E_XC_QMS_FRAME_NEXT],
			stPerFrameInfo[E_XC_QMS_FRAME_FRC_READ],
			&u16CurTotalOutputvtt, _u8IdxOpCnt, _u8FrcOpCnt);
		break;
	default:
		return;
	}

	_u8FrcDelayFrame = _u8TotalDelayFrame;
	if (_u8TotalDelayFrame > 0) {
		_u8FrcDelayFrame =
			(_u8TotalDelayFrame > QMS_OUTPUT_FRC_EARLY_FRAME_NUM) ?
			(_u8TotalDelayFrame - QMS_OUTPUT_FRC_EARLY_FRAME_NUM):0;
		_u8TotalDelayFrame--;
		DRM_INFO("[QMS][Output][Wait] Delay frame(%d)\n", _u8TotalDelayFrame);
	} else if (_u8IdxOpCnt < (stPerFrameInfo[E_XC_QMS_FRAME_NEXT]->u8OutRatio - 1))
		_u8IdxOpCnt++;
	else {
		_u8IdxOpCnt = 0;
		_u8Idx = (_u8Idx + 1) % _u8MaxFrameInfoLen;
		u16CurTotalOutputvtt = 0;
	}

	if (_u8FrcDelayFrame > 0)
		_u8FrcDelayFrame--;
	else if (_u8FrcOpCnt < (stPerFrameInfo[E_XC_QMS_FRAME_FRC_READ]->u8OutRatio - 1))
		_u8FrcOpCnt++;
	else {
		_u8FrcOpCnt = 0;
		_u8FrcReadScriptIdx =
			(_u8FrcReadScriptIdx + 1) % _u8MaxFrameInfoLen;
	}

}

void mtk_tv_panel_vrr_get_shm_info(void)
{
#define VRR_SHM_INDEX 0
#define CPU_BASE_ADR (0x20000000)

	struct device_node *np;
	struct of_mmap_info_data of_mmap_info;
	int ret = 0;
	uint64_t *addr = NULL;
	__u32 project = 0;
	__u32 sw_mode = 0;
	__u32 hw_support = 0;

	np = of_find_compatible_node(NULL, NULL, "mediatek,vrr");

	if (np == NULL) {
		DRM_ERROR("[%s, %d] Failed to get VRR compatible\r\n",
			__func__, __LINE__);
		return;
	}
	memset(&of_mmap_info, 0, sizeof(struct of_mmap_info_data));
	ret = of_mtk_get_reserved_memory_info_by_idx(np, VRR_SHM_INDEX, &of_mmap_info);
	if (ret) {
		DRM_ERROR("[%s, %d][VRR] Failed to get VRR_SHM_INDEX MEMORY resource\r\n",
			__func__, __LINE__);
	} else {
		_stVrrQmsBufMemInfo.u64VrrQmsBufPhyAddr =
			(uint64_t)of_mmap_info.start_bus_address - CPU_BASE_ADR;
		_stVrrQmsBufMemInfo.u32VrrQmsBufSize =
			(uint32_t)of_mmap_info.buffer_size;

		_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr =
			(uint64_t)ioremap_wc(of_mmap_info.start_bus_address,
				of_mmap_info.buffer_size);

		if (_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr == 0) {
			_stVrrQmsBufMemInfo.u64VrrQmsBufPhyAddr = 0;
			_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr = 0;
			_stVrrQmsBufMemInfo.u32VrrQmsBufSize = 0;
		} else {
			addr = (uint64_t *)_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr;
			memset(addr, 0, _stVrrQmsBufMemInfo.u32VrrQmsBufSize);
		}

		DRM_INFO("[%s, %d][VRR] VrrQmsBufVirAddr(%lld), VrrQmsBufPhyAddr(%lld)\n",
			 __func__, __LINE__,
			_stVrrQmsBufMemInfo.u64VrrQmsBufVirAddr,
			_stVrrQmsBufMemInfo.u64VrrQmsBufPhyAddr);
	}

	ret = of_property_read_u32(np, "vrr_qms_support",
		&vrr_info.vrr_qms_suport);

	ret = of_property_read_u32(np, "vrr_qms_project",
		&project);

	ret = of_property_read_u32(np, "vrr_qms_project_sw_mode",
		&sw_mode);

	ret = of_property_read_u32(np, "vrr_qms_project_hw_support",
		&hw_support);
	vrr_info.vrr_qms_project
		= (project << SHIFT_BITS_8) | (sw_mode << SHIFT_BITS_4) | (hw_support);

	ret = of_property_read_u32(np, "vrr_qms_hw_support_error_handling",
		&vrr_info.vrr_qms_hw_support_error_handling);

	drv_hwreg_render_pnl_set_QMS_error_hanlding_hw_support(
		vrr_info.vrr_qms_hw_support_error_handling);

	if (vrr_info.vrr_qms_project == EN_XC_QMS_PROJECT_PERFECT_DELAY_HW)
		eQmsPreferFrameSyncMode = VIDEO_CRTC_FRAME_SYNC_FRAMELOCK_NM;
	else if (vrr_info.vrr_qms_project == EN_XC_QMS_PROJECT_PERFECT_DELAY_SW_MLOAD)
		eQmsPreferFrameSyncMode = VIDEO_CRTC_FRAME_SYNC_FREERUN;
	else
		vrr_info.vrr_qms_suport = 0;

}

MODULE_AUTHOR("Mediatek TV group");
MODULE_DESCRIPTION("Mediatek SoC DRM driver");
MODULE_LICENSE("GPL v2");
