// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */
/*
 * Copyright (c) 2024 Sony Corporation
 */
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/component.h>
#include <linux/delay.h>
#include <drm/mtk_tv_drm.h>

#include "mtk_tv_drm_plane.h"
#include "mtk_tv_drm_gem.h"
#include "mtk_tv_drm_drv.h"
#include "mtk_tv_drm_crtc.h"
#include "mtk_tv_drm_connector.h"
#include "mtk_tv_drm_video.h"
#include "mtk_tv_drm_encoder.h"
#include "mtk_tv_drm_kms.h"
#include "mtk_tv_drm_common.h"
#include "drv_scriptmgt.h"
#include "mtk_tv_drm_demura.h"
#include "mtk_tv_drm_backlight.h"
#include "mtk_tv_drm_log.h"
#include "pqu_msg.h"
#include "ext_command_render_if.h"
#include "hwreg_render_stub.h"
#include "mtk_tv_drm_video_frc.h"
#include "mtk_tv_drm_video_frc_cmd.h"
#include "pixelmonitor.h"

#define MTK_DRM_MODEL MTK_DRM_MODEL_VIDEO
#define SW_DELAY (20) // for LTP test
#define SW_FB_NUM (10) //for LTP test
#define MEMC_FB_NUM (10)
#define PXM_SLEEP_DIFF_TIME (1000) // 1ms=1000us
#define R_G_B_MAX (0x3FF) /* 10 bit */

#define TO_PTR(v)		     ((void *)(long)(v))
#define TO_PTR_BACKLIGHT_RANGE(v)    ((struct drm_mtk_range_value *)(long)(v))
#define TO_PTR_PATTERN_GENERATE(v)    ((struct drm_mtk_pattern_config *)(long)(v))
#define TO_PTR_DEMURA_CONFIG(v)    ((struct drm_mtk_demura_config *)(long)(v))



#define PANEL_PROP(i) \
		((i >= E_EXT_CRTC_PROP_FRAMESYNC_PLANE_ID) && \
			(i <= E_EXT_CRTC_PROP_NO_SIGNAL)) \

#define PIXELSHIFT_PROP(i) \
		((i >= E_EXT_CRTC_PROP_PIXELSHIFT_ENABLE) && \
			(i <= E_EXT_CRTC_PROP_PIXELSHIFT_V)) \

#define LDM_PROP(i) \
		((i >= E_EXT_CRTC_PROP_LDM_STATUS) && \
			(i <= E_EXT_CRTC_PROP_LDM_SUSPEND_RESUME_TEST))\

#define VIDEO_GOP_PROP(i) \
		((i >= E_EXT_CRTC_PROP_GOP_VIDEO_ORDER) && \
			(i <= E_EXT_CRTC_PROP_GOP_VIDEO_ORDER))\

static void _mtk_tv_drm_video_set_live_tone(
	struct mtk_tv_kms_context *pctx,
	void *buffer_addr)
{
	struct drm_mtk_range_value *range = (struct drm_mtk_range_value *)buffer_addr;

	if (pctx == NULL || range == NULL) {
		DRM_ERROR("[%s][%d] Invalid input", __func__, __LINE__);
		return;
	}
	DRM_INFO("[%s][%d] valid     = %u", __func__, __LINE__, range->valid);
	DRM_INFO("[%s][%d] max_value = %u", __func__, __LINE__, range->max_value);
	DRM_INFO("[%s][%d] min_value = %u", __func__, __LINE__, range->min_value);
	DRM_INFO("[%s][%d] value     = %u", __func__, __LINE__, range->value);
	drv_STUB("LiveTone_valid",     range->valid);
	drv_STUB("LiveTone_max_value", range->max_value);
	drv_STUB("LiveTone_min_value", range->min_value);
	drv_STUB("LiveTone_value",     range->value);

	if (bPquEnable) { // RV55
		struct pqu_render_live_tone backlight;

		memset(&backlight, 0, sizeof(struct pqu_render_live_tone));
		backlight.usr_valid     = range->valid;
		backlight.usr_max_value = range->max_value;
		backlight.usr_min_value = range->min_value;
		backlight.usr_value     = range->value;
		pqu_render_live_tone((const struct pqu_render_live_tone *)&backlight, NULL);
	} else { // PQU
		struct msg_render_live_tone backlight;

		memset(&backlight, 0, sizeof(struct msg_render_live_tone));
		backlight.usr_valid     = range->valid;
		backlight.usr_max_value = range->max_value;
		backlight.usr_min_value = range->min_value;
		backlight.usr_value     = range->value;
		pqu_msg_send(PQU_MSG_SEND_RENDER_LIVE_TONE, (void *)&backlight);
	}
}

static void _mtk_tv_drm_video_set_background_color(
	struct mtk_tv_kms_context *pctx,
	void *buffer_addr)
{
	struct mtk_video_context *pctx_video = NULL;
	struct drm_mtk_bg_color *bg_color = (struct drm_mtk_bg_color *)buffer_addr;
	struct reg_info reg[HWREG_VIDEO_REG_NUM_BG_RGB];
	struct hwreg_post_fill_bg_argb post_fill_bg_argb_info;
	struct hwregOut reg_out;
	int i = 0;
	struct mtk_hw_capability *hw_caps = NULL;
	u8 mute_color_bit_num_caps = 0;

	struct hwreg_post_fill_tgen_frame_rgb post_fill_tgen_frame_rgb = {0};

	if (buffer_addr == NULL) {
		DRM_ERROR("[%s][%d] set background color fail\n", __func__, __LINE__);
		return;
	}

	pctx_video = &pctx->video_priv;
	hw_caps = &pctx->hw_caps;

	/* mute color bit num*/
	mute_color_bit_num_caps = hw_caps->video_mute_color_bits_num;

	pr_info("[%s][%d] background GBR/YUV:[%u, %u, %u]\n", __func__, __LINE__,
		bg_color->green_Y,	bg_color->blue_Cb, bg_color->red_Cr);

	for (i = MTK_VPLANE_TYPE_MEMC; i < MTK_VPLANE_TYPE_MAX; ++i) {
		memset(reg, 0, sizeof(reg));
		memset(&post_fill_bg_argb_info, 0, sizeof(struct hwreg_post_fill_bg_argb));
		memset(&reg_out, 0, sizeof(struct hwregOut));

		post_fill_bg_argb_info.RIU = 0;
		post_fill_bg_argb_info.planeType = (enum drm_hwreg_video_plane_type)i;
		post_fill_bg_argb_info.bgARGB.R = bg_color->red_Cr;
		post_fill_bg_argb_info.bgARGB.G = bg_color->green_Y;
		post_fill_bg_argb_info.bgARGB.B = bg_color->blue_Cb;
		reg_out.reg = reg;

		drv_hwreg_render_display_set_post_fill_bg_rgb(post_fill_bg_argb_info, &reg_out);

		//8bit transfer
		if (mute_color_bit_num_caps == DIGIT_8) {
			post_fill_bg_argb_info.bgARGB.R &= MASK_8BITS;
			post_fill_bg_argb_info.bgARGB.G &= MASK_8BITS;
			post_fill_bg_argb_info.bgARGB.B &= MASK_8BITS;
		} else if (mute_color_bit_num_caps == DIGIT_10) {

			if (pctx_video->window_mute_color.user_control_bit_num == true) {

				if (pctx_video->window_mute_color.bit_num ==
					EN_MUTE_COLOR_BIT_NUM_8) {

					/* re-mmaping 8bits (user) to 10bits (caps)*/
					post_fill_bg_argb_info.bgARGB.R =
						INTERPOLATE(MAX_8BITS, 0,
						post_fill_bg_argb_info.bgARGB.R, MAX_10BITS, 0);
					post_fill_bg_argb_info.bgARGB.G =
						INTERPOLATE(MAX_8BITS, 0,
						post_fill_bg_argb_info.bgARGB.G, MAX_10BITS, 0);
					post_fill_bg_argb_info.bgARGB.B =
						INTERPOLATE(MAX_8BITS, 0,
						post_fill_bg_argb_info.bgARGB.B, MAX_10BITS, 0);
				} else if (pctx_video->window_mute_color.bit_num ==
					EN_MUTE_COLOR_BIT_NUM_10) {
					post_fill_bg_argb_info.bgARGB.R &= MASK_10BITS;
					post_fill_bg_argb_info.bgARGB.G  &= MASK_10BITS;
					post_fill_bg_argb_info.bgARGB.B &= MASK_10BITS;
				}
			} else {
				/* re-mmaping 8bits (default) to 10bits (caps)*/
				post_fill_bg_argb_info.bgARGB.R =
					INTERPOLATE(MAX_8BITS, 0,
					post_fill_bg_argb_info.bgARGB.R, MAX_10BITS, 0);
				post_fill_bg_argb_info.bgARGB.G =
					INTERPOLATE(MAX_8BITS, 0,
					post_fill_bg_argb_info.bgARGB.G, MAX_10BITS, 0);
				post_fill_bg_argb_info.bgARGB.B =
					INTERPOLATE(MAX_8BITS, 0,
					post_fill_bg_argb_info.bgARGB.B, MAX_10BITS, 0);
			}

		}
		//Merak 2.0 get framae color setting

		post_fill_tgen_frame_rgb.tgenFrameRGB.r = post_fill_bg_argb_info.bgARGB.R;
		post_fill_tgen_frame_rgb.tgenFrameRGB.g = post_fill_bg_argb_info.bgARGB.G;
		post_fill_tgen_frame_rgb.tgenFrameRGB.b = post_fill_bg_argb_info.bgARGB.B;

		drv_hwreg_render_display_set_post_fill_tgen_frame_rgb(
			post_fill_tgen_frame_rgb,
			&reg_out);

		DRM_INFO("[%s][%d]: tgen_frame_r:%llu, tgen_frame_g:%llu, tgen_frame_b:%llu",
			__func__, __LINE__,
			post_fill_tgen_frame_rgb.tgenFrameRGB.r,
			post_fill_tgen_frame_rgb.tgenFrameRGB.g,
			post_fill_tgen_frame_rgb.tgenFrameRGB.b);

		if (!post_fill_bg_argb_info.RIU) {
			mtk_tv_sm_write_cmd(&pctx_video->disp_ml,
				reg_out.regCount, (struct sm_reg *)&reg);
		}

		DRM_INFO("[%s][%d]: plane_type: %d, RIU %d, regCount %d",
			__func__, __LINE__,
			i, post_fill_bg_argb_info.RIU, reg_out.regCount);
	}
}

static void _mtk_tv_drm_video_set_video_mute_color(
	struct mtk_tv_kms_context *pctx,
	void *buffer_addr)
{
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	struct mtk_hw_capability *hw_caps = NULL;
	struct drm_mtk_mute_color *mute_color = (struct drm_mtk_mute_color *)buffer_addr;
	struct reg_info reg[HWREG_VIDEO_REG_NUM_WINDOW_RGB];
	struct hwreg_mute_color_argb mute_color_info;
	struct hwregOut reg_out;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	int i = 0;
	u8 mute_color_bit_num_caps = 0;

	if (buffer_addr == NULL) {
		DRM_ERROR("[%s][%d] set mute color fail\n", __func__, __LINE__);
		return;
	}

	pr_info("[%s][%d] (ori) video mute color GBR/YUV:[%u, %u, %u]\n",
		__func__, __LINE__,
		mute_color->green_Y, mute_color->blue_Cb, mute_color->red_Cr);

	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;
	video_mute_hw_ver = video_hw_version->video_mute;
	hw_caps = &pctx->hw_caps;

	/* mute color bit num*/
	mute_color_bit_num_caps = hw_caps->video_mute_color_bits_num;

	if (mute_color_bit_num_caps == DIGIT_8) {
		mute_color->red_Cr &= MASK_8BITS;
		mute_color->green_Y &= MASK_8BITS;
		mute_color->blue_Cb &= MASK_8BITS;
	} else if (mute_color_bit_num_caps == DIGIT_10) {

		if (pctx_video->window_mute_color.user_control_bit_num == true) {

			if (pctx_video->window_mute_color.bit_num ==
				EN_MUTE_COLOR_BIT_NUM_8) {

				/* re-mmaping 8bits (user) to 10bits (caps)*/
				mute_color->red_Cr =
					INTERPOLATE(MAX_8BITS, 0,
					mute_color->red_Cr, MAX_10BITS, 0);
				mute_color->green_Y =
					INTERPOLATE(MAX_8BITS, 0,
					mute_color->green_Y, MAX_10BITS, 0);
				mute_color->blue_Cb =
					INTERPOLATE(MAX_8BITS, 0,
					mute_color->blue_Cb, MAX_10BITS, 0);
			} else if (pctx_video->window_mute_color.bit_num ==
				EN_MUTE_COLOR_BIT_NUM_10) {
				mute_color->red_Cr &= MASK_10BITS;
				mute_color->green_Y &= MASK_10BITS;
				mute_color->blue_Cb &= MASK_10BITS;
			}
		} else {
			/* re-mmaping 8bits (default) to 10bits (caps)*/
			mute_color->red_Cr =
				INTERPOLATE(MAX_8BITS, 0,
				mute_color->red_Cr, MAX_10BITS, 0);
			mute_color->green_Y =
				INTERPOLATE(MAX_8BITS, 0,
				mute_color->green_Y, MAX_10BITS, 0);
			mute_color->blue_Cb =
				INTERPOLATE(MAX_8BITS, 0,
				mute_color->blue_Cb, MAX_10BITS, 0);
		}
	}

	pr_info("[%s][%d] (bit_num) cap:%d user_control:%d user_bit_num:%d\n",
		__func__, __LINE__,
		mute_color_bit_num_caps,
		pctx_video->window_mute_color.user_control_bit_num,
		pctx_video->window_mute_color.bit_num);

	pr_info("[%s][%d] (final) video mute color GBR/YUV:[%u, %u, %u]\n",
		__func__, __LINE__,
		mute_color->green_Y, mute_color->blue_Cb, mute_color->red_Cr);


	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB) {
		memset(&mute_color_info, 0, sizeof(struct hwreg_mute_color_argb));
		mute_color_info.RIU = 0;
		mute_color_info.windowARGB.R = mute_color->red_Cr;
		mute_color_info.windowARGB.G = mute_color->green_Y;
		mute_color_info.windowARGB.B = mute_color->blue_Cb;

		for (i = MTK_VPLANE_TYPE_MEMC; i < MTK_VPLANE_TYPE_MAX; ++i) {
			memset(reg, 0, sizeof(reg));
			memset(&reg_out, 0, sizeof(struct hwregOut));

			mute_color_info.planeType = (enum drm_hwreg_video_plane_type)i;
			reg_out.reg = reg;

			drv_hwreg_render_display_set_post_fill_window_rgb(
				mute_color_info, &reg_out);
			DRM_INFO("[%s][%d]: plane_type: %d, RIU %d, regCount %d",
				__func__, __LINE__,
				i,
				mute_color_info.RIU,
				reg_out.regCount);

			if (!mute_color_info.RIU)
				mtk_tv_sm_write_cmd(&pctx_video->disp_ml,
					reg_out.regCount, (struct sm_reg *)&reg);
		}
	} else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB) {
		memset(&mute_color_info, 0, sizeof(struct hwreg_mute_color_argb));
		memset(&reg_out, 0, sizeof(struct hwregOut));

		mute_color_info.RIU = 0;
		mute_color_info.planeType = 0;//do not care
		mute_color_info.windowARGB.R = mute_color->red_Cr;
		mute_color_info.windowARGB.G = mute_color->green_Y;
		mute_color_info.windowARGB.B = mute_color->blue_Cb;
		reg_out.reg = reg;
		drv_hwreg_render_display_set_mute_color(
			mute_color_info, &reg_out);

		if (!mute_color_info.RIU)
			mtk_tv_sm_write_cmd(&pctx_video->disp_ml,
				reg_out.regCount, (struct sm_reg *)&reg);
	} else {
		DRM_ERROR("[%s, %d]: invalid mute version\n", __func__, __LINE__);
		return;
	}
}

static inline int _mtk_tv_drm_video_crtc_check_blob_prop_size(
	uint32_t propy_idx,
	void *buf,
	uint32_t size)
{
	int ret = 0; // default, we don't need to check BLOB

	switch (propy_idx) {
	case E_EXT_CRTC_PROP_PQMAP_NODE_ARRAY:
		ret = mtk_tv_drm_pqmap_check_buffer(buf, size);
		break;
	case E_EXT_CRTC_PROP_GLOBAL_PQ:
		ret = mtk_tv_drm_global_pq_check(buf, size);
		break;
	case E_EXT_CRTC_PROP_DEMURA_CONFIG:
		ret = mtk_tv_drm_demura_check_buffer(buf, size);
		break;
	default:
		break;
	}
	return ret;
}

static int _mtk_tv_drm_video_get_pixel_report(
	struct mtk_tv_kms_context *pctx,
	struct drm_mtk_pxm_get_report_size *pxm_report)
{
	int ret = 0;
	__u16 instance = 0;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct pxm_res_info info;
	struct pxm_report_info report_info;
	enum pxm_return pxm_ret = E_PXM_RET_OK;

	if (!pctx || !pxm_report)
		return -EINVAL;

	pctx_pnl = &(pctx->panel_priv);

	memset(&info, 0, sizeof(struct pxm_res_info));
	memset(&report_info, 0, sizeof(struct pxm_report_info));

	//assign resource information value
	info.win_id = pxm_report->win_id;
	info.display_en = pxm_report->display_en;
	info.point = (enum pxm_point)pxm_report->point;
	info.pos.x = pxm_report->pos_x;
	info.pos.y = pxm_report->pos_y;
	info.display_color.color_r = pxm_report->display_r;
	info.display_color.color_g = pxm_report->display_g;
	info.display_color.color_b = pxm_report->display_b;
	info.display_color.alpha_val = pxm_report->display_alpha;
	info.report_en = pxm_report->report_en;
	info.report_once = 0;
	info.h_size = pctx_pnl->info.de_width;
	info.v_size = pctx_pnl->info.de_height;

	DRM_INFO("[%s][%d]report_en=%d, report_once=%d, win id(%u)\n",
		__func__, __LINE__,
		info.report_en, info.report_once, info.win_id);
	DRM_INFO("[%s][%d]point(%d), pos(x:%u, y:%u), size(h:%u, v:%u)\n",
		__func__, __LINE__,
		info.point, info.pos.x, info.pos.y, info.h_size, info.v_size);
	DRM_INFO("[%s][%d]display_en=%d, display(r:%u, g:%u, b:%u, alpha:%u\n",
		__func__, __LINE__,
		info.display_en, info.display_color.color_r, info.display_color.color_g,
		info.display_color.color_b, info.display_color.alpha_val);

	//Create pixel monitor resource
	pxm_ret = pxm_create_resource(&instance, &info);
	if (pxm_ret != E_PXM_RET_OK) {
		DRM_ERROR("[%s][%d]Fail to create pixel monitor resource, ret=%d\n",
			__func__, __LINE__, pxm_ret);
		ret = -EINVAL;
		goto exit;
	}

	//Fire pixel monitor setting
	pxm_ret = pxm_fire(instance);
	if (pxm_ret != E_PXM_RET_OK) {
		DRM_ERROR("[%s][%d]Fail to fire pixel monitor, ret=%d\n",
			__func__, __LINE__, pxm_ret);
		ret = -EINVAL;
		goto exit;
	}

	//If report enable. Get pixel value
	if (info.report_en == 1) {

		//After update position, wait a frame time to update pixel value
		usleep_range(pxm_report->frame_us, (pxm_report->frame_us + PXM_SLEEP_DIFF_TIME));

		report_info.times = 1;
		report_info.mode = E_PXM_REPORT_MODE_LEGACY;
		report_info.color = kzalloc(sizeof(struct pxm_color), GFP_KERNEL);
		if (report_info.color == NULL) {
			DRM_ERROR("[%s][%d]Fail to allocate report color info memory\n",
				__func__, __LINE__);
			goto exit;
		}

		pxm_ret = pxm_get_report(instance, &report_info);
		if (pxm_ret != E_PXM_RET_OK) {
			DRM_ERROR("[%s][%d]Fail to get pixel monitor report, ret=%d\n",
				__func__, __LINE__, pxm_ret);
			ret = -EINVAL;
			goto exit;
		}

		pxm_report->r = report_info.color->color_r;
		pxm_report->g = report_info.color->color_g;
		pxm_report->b = report_info.color->color_b;
		pxm_report->alpha = report_info.color->alpha_val;
		DRM_INFO("[%s][%d]color (r:%u, g:%u, b:%u, alpha:%u)\n",
			__func__, __LINE__,
			pxm_report->r, pxm_report->g, pxm_report->b,
			pxm_report->alpha);
	}

exit:
	kfree(report_info.color);

	pxm_ret = pxm_destroy_resource(instance);
	if (pxm_ret != E_PXM_RET_OK) {
		DRM_ERROR("[%s][%d]Fail to destroy pixel monitor resource, ret=%d\n",
			__func__, __LINE__, pxm_ret);
		ret = -EINVAL;
	}

	return ret;
}

int mtk_tv_drm_video_atomic_set_crtc_property(
	struct mtk_tv_drm_crtc *crtc,
	struct drm_crtc_state *state,
	struct drm_property *property,
	uint64_t val)
{
	int ret = -EINVAL;
	int i = 0;
	struct mtk_tv_kms_context *pctx = (struct mtk_tv_kms_context *)crtc->crtc_private;
	struct ext_crtc_prop_info *crtc_props = pctx->ext_crtc_properties;

	for (i = 0; i < E_EXT_CRTC_PROP_MAX; i++) {
		if (property == pctx->drm_crtc_prop[i]) {
			ret = 0x0;
			break;
		}
	}
	if (ret != 0) {
		DRM_ERROR("[%s, %d]: unknown CRTC property %s!!\n",
			__func__, __LINE__, property->name);
		return ret;
	}

	if (PANEL_PROP(i)) { //pnl property
		crtc_props->prop_val[i] = val;
		crtc_props->prop_update[i] = 1;
		ret = mtk_video_panel_atomic_set_crtc_property(
			crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:set crtc PNL prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (PIXELSHIFT_PROP(i)) { //pixel shift property
		crtc_props->prop_val[i] = val;
		crtc_props->prop_update[i] = 1;
		ret = mtk_video_pixelshift_atomic_set_crtc_property(
			crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:set crtc pixelshift prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (LDM_PROP(i)) { //ldm property
		crtc_props->prop_val[i] = val;
		crtc_props->prop_update[i] = 1;
		ret = mtk_ld_atomic_set_crtc_property(crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:set crtc LDM prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (IS_BLOB_PROP(property)) {
		// Avoid non-block mode maybe free the BLOB before commit_tail.
		// Alloc a memory to store the BLOB and update at mtk_tv_drm_video_update_crtc.
		struct drm_property_blob *property_blob = NULL;
		void *buffer = NULL;

		if (crtc_props->prop_update[i] == 1) {
			// DRM_INFO("[%s][%d]prop '%s' is not completion last commit\n",
			//     __func__, __LINE__, property->name);
			return 0;
		}
		// free the previous drm commit blob buffer
		if (crtc_props->prop_val[i])
			kvfree(TO_PTR(crtc_props->prop_val[i])); // free old buffer

		crtc_props->prop_val[i] = 0; // reset

		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob == NULL) {
			DRM_ERROR("[%s][%d]prop '%s' has invalid blob id: 0x%llx\n",
				__func__, __LINE__, property->name, val);
			return -EINVAL;
		}
		if (_mtk_tv_drm_video_crtc_check_blob_prop_size(i,
				property_blob->data, property_blob->length)) {
			DRM_ERROR("[%s][%d] prop %s check buffer fail",
				__func__, __LINE__, property->name);
			drm_property_blob_put(property_blob);
			return -EINVAL;
		}
		buffer = kvmalloc(property_blob->length, GFP_KERNEL);
		if (IS_ERR_OR_NULL(buffer)) {
			DRM_ERROR("[%s][%d] prop '%s' alloc buffer fail, size = %zd\n",
				__func__, __LINE__, property->name, property_blob->length);
			drm_property_blob_put(property_blob);
			return -ENOMEM;
		}
		memcpy(buffer, property_blob->data, property_blob->length);
		crtc_props->prop_val[i] = (uintptr_t)buffer;
		crtc_props->prop_update[i] = 1;
		drm_property_blob_put(property_blob);
		DRM_INFO("[%s][%d] prop '%s': blob id = %llu, blob length = %zd\n",
			__func__, __LINE__, property->name, val, property_blob->length);
	} else { // remained property
		crtc_props->prop_val[i] = val;
		crtc_props->prop_update[i] = 1;
	}

	return ret;
}

int mtk_tv_drm_video_atomic_get_crtc_property(
	struct mtk_tv_drm_crtc *crtc,
	const struct drm_crtc_state *state,
	struct drm_property *property,
	uint64_t *val)
{
	int ret = 0;
	int i = 0;
	struct mtk_tv_kms_context *pctx = (struct mtk_tv_kms_context *)crtc->crtc_private;
	struct ext_crtc_prop_info *crtc_props = pctx->ext_crtc_properties;

	for (i = 0; i < E_EXT_CRTC_PROP_MAX; i++) {
		if (property == pctx->drm_crtc_prop[i]) {
			*val = crtc_props->prop_val[i];
			ret = 0x0;
			break;
		}
	}

	if (PANEL_PROP(i)) {
		//pnl property
		ret = mtk_video_panel_atomic_get_crtc_property(
			crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:get crtc PNL prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (PIXELSHIFT_PROP(i)) {
		//pixel shift property
		ret = mtk_video_pixelshift_atomic_get_crtc_property(
			crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:get crtc pixelshift prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (LDM_PROP(i)) {
		//ldm property
		ret = mtk_ld_atomic_get_crtc_property(crtc,
			state,
			property,
			val,
			i);
		if (ret != 0) {
			DRM_ERROR("[%s, %d]:get crtc LDM prop fail!!\n",
				__func__, __LINE__);
			return ret;
		}
	} else if (IS_BLOB_PROP(property)) {
		// TODO
		// If the property is BLOB, create a blob and return the blob id.
		// Otherwise, set @val from @prop_val directly.
	} else {
		switch (i) {
		case E_EXT_CRTC_PROP_DEMURA_CONFIG:
			DRM_INFO("[%s][%d] E_EXT_CRTC_PROP_DEMURA_CUC, %td!!\n",
				__func__, __LINE__, (ptrdiff_t)*val);
			break;
		case E_EXT_CRTC_PROP_PATTERN_GENERATE:
			DRM_INFO("[%s][%d] E_EXT_CRTC_PROP_PATTERN_GENERATE, %td!!\n",
				__func__, __LINE__, (ptrdiff_t)*val);
			break;
		case E_EXT_CRTC_PROP_MAX:
			DRM_ERROR("[%s][%d] invalid property!!\n", __func__, __LINE__);
			ret = -EINVAL;
			break;
		default:
			//DRM_INFO("[DRM][VIDEO][%s][%d]default\n", __func__, __LINE__);
			break;
		}
	}

	return ret;
}

void mtk_tv_drm_video_update_crtc(
	struct mtk_tv_drm_crtc *crtc)
{
	int i = 0;
	struct mtk_tv_kms_context *pctx = NULL;
	struct ext_crtc_prop_info *crtc_prop = NULL;
	/* Sony modification start */
	struct drm_mode_object *obj = NULL;
	struct drm_plane *plane = NULL;
	struct mtk_drm_plane *mplane = NULL;
	uint64_t framesync_plane_id = 0;
	/* Sony modification end */
	uint64_t framesync_mode = 0;

	if (!crtc) {
		DRM_ERROR("[%s, %d]: null ptr! crtc=%p\n", __func__, __LINE__, crtc);
		return;
	}

	pctx = (struct mtk_tv_kms_context *)crtc->plane[MTK_DRM_PLANE_TYPE_VIDEO]->crtc_private;
	crtc_prop = pctx->ext_crtc_properties;

	for (i = 0; i < E_EXT_CRTC_PROP_MAX; i++) {
		if (crtc_prop->prop_update[i] == 0) {
			// no update, no work
			continue;
		}
		switch (i) {
		case E_EXT_CRTC_PROP_FRAMESYNC_PLANE_ID:
			//stTimingInfo.u32HighAccurateInputVFreq = (uint32_t)val;
			//stTimingInfo.u16InputVFreq = (uint32_t)val /100;
			DRM_INFO("[%s][%d] E_EXT_CRTC_PROP_FRAMESYNC_PLANE_ID= %td\n",
				__func__, __LINE__, (ptrdiff_t)crtc_prop->prop_val[i]);
			framesync_mode =
				crtc_prop->prop_val[E_EXT_CRTC_PROP_SET_FRAMESYNC_MODE];
			/* Sony modification start */
			framesync_plane_id = crtc_prop->prop_val[E_EXT_CRTC_PROP_FRAMESYNC_PLANE_ID];
			obj = drm_mode_object_find(pctx->drm_dev, NULL, framesync_plane_id, DRM_MODE_OBJECT_ANY);
			plane = obj_to_plane(obj);
			mplane = to_mtk_plane(plane);
			set_main_window_id(mplane->video_index);
			/* Sony modification end */

			if (framesync_mode == VIDEO_CRTC_FRAME_SYNC_VTTV) {
				pctx->panel_priv.outputTiming_info.eFrameSyncState =
					E_PNL_FRAMESYNC_STATE_PROP_IN;
				_mtk_video_set_framesync_mode(pctx);
				mtk_tv_drm_logrec_write(MTK_TV_DRM_LOGREC_FRAMESYNC,
				"[FRAMESYNC_PLANE_ID] %td",
				(ptrdiff_t)crtc_prop->prop_val[i]);
			}
			break;
		case E_EXT_CRTC_PROP_SET_FRAMESYNC_MODE:
			DRM_INFO("[%s][%d] E_EXT_CRTC_PROP_SET_FRAMESYNC_MODE= %td\n",
			__func__, __LINE__, (ptrdiff_t)crtc_prop->prop_val[i]);
			pctx->panel_priv.outputTiming_info.eFrameSyncState =
				E_PNL_FRAMESYNC_STATE_PROP_IN;
			_mtk_video_set_framesync_mode(pctx);
			mtk_tv_drm_logrec_write(MTK_TV_DRM_LOGREC_FRAMESYNC,
			"[SET_FRAMESYNC] %td",
			(ptrdiff_t)crtc_prop->prop_val[i]);
			break;
		case E_EXT_CRTC_PROP_LOW_LATENCY_MODE:
			DRM_INFO("[%s][%d] E_EXT_CRTC_PROP_LOW_LATENCY_MODE= %td\n",
				__func__, __LINE__, (ptrdiff_t)crtc_prop->prop_val[i]);
			framesync_mode =
				crtc_prop->prop_val[E_EXT_CRTC_PROP_SET_FRAMESYNC_MODE];
			if (framesync_mode == VIDEO_CRTC_FRAME_SYNC_VTTV) {
				pctx->panel_priv.outputTiming_info.eFrameSyncState =
					E_PNL_FRAMESYNC_STATE_PROP_IN;
				_mtk_video_set_low_latency_mode(
					pctx,
					crtc_prop->prop_val[i]);
			}
			break;
		case E_EXT_CRTC_PROP_DEMURA_CONFIG:
			if (crtc_prop->prop_val[i] != 0) {
				mtk_tv_drm_demura_set_config(&pctx->demura_priv,
				TO_PTR_DEMURA_CONFIG(crtc_prop->prop_val[i]));
			}
			break;
		case E_EXT_CRTC_PROP_PATTERN_GENERATE:
			if (crtc_prop->prop_val[i] != 0) {
				mtk_drm_pattern_set_param(&pctx->pattern_status,
				TO_PTR_PATTERN_GENERATE(crtc_prop->prop_val[i]));
			}
			break;
		case E_EXT_CRTC_PROP_MUTE_COLOR:
			if (crtc_prop->prop_val[i] != 0) {
				_mtk_tv_drm_video_set_video_mute_color(pctx,
					TO_PTR(crtc_prop->prop_val[i]));
			}
			break;
		case E_EXT_CRTC_PROP_BACKGROUND_COLOR:
			_mtk_tv_drm_video_set_background_color(
				pctx,
				TO_PTR(crtc_prop->prop_val[i]));
			break;
		case E_EXT_CRTC_PROP_GLOBAL_PQ:
			mtk_tv_drm_global_pq_flush(
				&pctx->global_pq_priv,
				TO_PTR(crtc_prop->prop_val[i]));
			break;
		case E_EXT_CRTC_PROP_BACKLIGHT_RANGE:
			mtk_tv_drm_backlight_set_property(&pctx->panel_priv,
				TO_PTR_BACKLIGHT_RANGE(crtc_prop->prop_val[i]));
			break;
		case E_EXT_CRTC_PROP_LIVETONE_RANGE:
			_mtk_tv_drm_video_set_live_tone(pctx, TO_PTR(crtc_prop->prop_val[i]));
			break;
		case E_EXT_CRTC_PROP_PQMAP_NODE_ARRAY:
			mtk_tv_drm_pqmap_update(&pctx->pqmap_priv, &pctx->video_priv.disp_ml,
				(struct drm_mtk_tv_pqmap_node_array *)crtc_prop->prop_val[i]);
			break;
		case E_EXT_CRTC_PROP_CFD_CSC_Render_In:
			mtk_video_display_cfd_set_render_in_csc_setting(
				pctx, TO_PTR(crtc_prop->prop_val[i]));
			break;
		case E_EXT_CRTC_PROP_NO_SIGNAL:
			if (crtc_prop->prop_val[i] != 0)
				pctx->panel_priv.outputTiming_info.FrameSyncModeChgBypass = false;
			break;
		default:
			break;
		}
		crtc_prop->prop_update[i] = 0;
	}
}

/*Connector*/
int mtk_tv_drm_video_atomic_set_connector_property(
	struct mtk_tv_drm_connector *connector,
	struct drm_connector_state *state,
	struct drm_property *property,
	uint64_t val)
{
	int ret = -EINVAL;
	int i;
	struct drm_property_blob *property_blob;
	struct mtk_tv_kms_context *pctx =
		(struct mtk_tv_kms_context *)connector->connector_private;
	struct ext_connector_prop_info *connector_props = pctx->ext_connector_properties;
	struct mtk_panel_context *pctx_pnl = NULL;

	for (i = 0; i < E_EXT_CONNECTOR_PROP_MAX; i++) {
		if (property == pctx->drm_connector_prop[i]) {
			connector_props->prop_val[i] = val;
			ret = 0x0;
			break;
		}
	}
	switch (i) {
	case E_EXT_CONNECTOR_PROP_PNL_DBG_LEVEL:
		//MApi_PNL_SetDbgLevel(val);
		DRM_INFO("[%s][%d] PNL_DBG_LEVEL = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_OUTPUT:
		//MApi_PNL_SetOutput(val);
		DRM_INFO("[%s][%d] PNL_OUTPUT = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SWING_LEVEL:
		//MApi_PNL_Control_Out_Swing(val);
		DRM_INFO("[%s][%d] PNL_SWING_LEVEL = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_FORCE_PANEL_DCLK:
		//MApi_PNL_ForceSetPanelDCLK(val , TRUE);
		DRM_INFO("[%s][%d] PNL_FORCE_PANEL_DCLK = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_PNL_FORCE_PANEL_HSTART:
		//MApi_PNL_ForceSetPanelHStart(val , TRUE);
		DRM_INFO("[%s][%d] PNL_PNL_FORCE_PANEL_HSTART = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_PNL_OUTPUT_PATTERN:
		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob != NULL) {
			drm_st_pnl_output_pattern stOutputPattern;

			memset(&stOutputPattern, 0, sizeof(drm_st_pnl_output_pattern));
			memcpy(&stOutputPattern,
				property_blob->data,
				sizeof(drm_st_pnl_output_pattern));
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_OUTPUT_PATTERN = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}

		break;
	case E_EXT_CONNECTOR_PROP_PNL_MIRROR_STATUS:
		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob != NULL) {
			drm_st_pnl_mirror_info stMirrorInfo;

			memset(&stMirrorInfo, 0, sizeof(drm_st_pnl_mirror_info));
			memcpy(&stMirrorInfo, property_blob->data, sizeof(drm_st_pnl_mirror_info));

			//MApi_PNL_GetMirrorStatus(&stMirrorInfo);
			//Update blob id memory
			memcpy(property_blob->data, &stMirrorInfo, sizeof(drm_st_pnl_mirror_info));
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_MIRROR_STATUS = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SSC_EN:
		//MApi_PNL_SetSSC_En(val);
		DRM_INFO("[%s][%d] PNL_SSC_EN = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SSC_FMODULATION:
		//MApi_PNL_SetSSC_Fmodulation(val);
		DRM_INFO("[%s][%d] PNL_SSC_FMODULATION = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SSC_RDEVIATION:
		//MApi_PNL_SetSSC_Rdeviation(val);
		DRM_INFO("[[%s][%d] PNL_SSC_RDEVIATION = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_OVERDRIVER_ENABLE:
		//MApi_PNL_OverDriver_Enable(val);
		DRM_INFO("[%s][%d] PNL_OVERDRIVER_ENABLE = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		break;
	case E_EXT_CONNECTOR_PROP_PNL_PANEL_SETTING:
		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob != NULL) {
			drm_st_pnl_ini_para stPnlSetting;

			memset(&stPnlSetting, 0, sizeof(drm_st_pnl_ini_para));

			memcpy(&stPnlSetting, property_blob->data, sizeof(drm_st_pnl_ini_para));
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_PANEL_SETTING = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PARSE_OUT_INFO_FROM_DTS:
		/*
		DRM_INFO("[%s][%d] E_EXT_CONNECTOR_PROP_PARSE_OUT_INFO_FROM_DTS = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob == NULL) {
			DRM_INFO("[%s][%d] unknown output info status = %td!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			drm_parse_dts_info param;
			drm_parse_dts_info *drmParseDtsInfo
					= ((drm_parse_dts_info *)(property_blob->data));
			drm_en_pnl_output_model output_model_cal;

			memset(&param, 0, sizeof(drm_parse_dts_info));
			//memcpy(&param, property_blob->data, sizeof(drm_parse_dts_info));

			param.src_info.linkIF = drmParseDtsInfo->src_info.linkIF;
			param.src_info.div_section = drmParseDtsInfo->src_info.div_section;
			param.src_info.hsync_st = drmParseDtsInfo->src_info.hsync_st;
			param.src_info.hsync_w = drmParseDtsInfo->src_info.hsync_w;
			param.src_info.vsync_st = drmParseDtsInfo->src_info.vsync_st;
			param.src_info.vsync_w = drmParseDtsInfo->src_info.vsync_w;

			param.src_info.de_hstart = drmParseDtsInfo->src_info.de_hstart;
			param.src_info.de_vstart = drmParseDtsInfo->src_info.de_vstart;
			param.src_info.de_width = drmParseDtsInfo->src_info.de_width;
			param.src_info.de_height = drmParseDtsInfo->src_info.de_height;
			param.src_info.typ_htt = drmParseDtsInfo->src_info.typ_htt;

			param.src_info.typ_vtt = drmParseDtsInfo->src_info.typ_vtt;
			param.src_info.typ_dclk = drmParseDtsInfo->src_info.typ_dclk;

			param.src_info.op_format = drmParseDtsInfo->src_info.op_format;

			param.out_cfg.linkIF = drmParseDtsInfo->out_cfg.linkIF;
			param.out_cfg.lanes = drmParseDtsInfo->out_cfg.lanes;
			param.out_cfg.div_sec = drmParseDtsInfo->out_cfg.div_sec;
			param.out_cfg.timing = drmParseDtsInfo->out_cfg.timing;
			param.out_cfg.format = drmParseDtsInfo->out_cfg.format;

			param.v_path_en = drmParseDtsInfo->v_path_en;
			param.extdev_path_en = drmParseDtsInfo->extdev_path_en;
			param.gfx_path_en = drmParseDtsInfo->gfx_path_en;
			param.output_model_ref = drmParseDtsInfo->output_model_ref;

			param.video_hprotect_info.hsync_w =
				drmParseDtsInfo->video_hprotect_info.hsync_w;
			param.video_hprotect_info.de_hstart =
				drmParseDtsInfo->video_hprotect_info.de_hstart;
			param.video_hprotect_info.lane_numbers =
				drmParseDtsInfo->video_hprotect_info.lane_numbers;
			param.delta_hprotect_info.hsync_w =
				drmParseDtsInfo->delta_hprotect_info.hsync_w;
			param.delta_hprotect_info.de_hstart =
				drmParseDtsInfo->delta_hprotect_info.de_hstart;
			param.delta_hprotect_info.lane_numbers =
				drmParseDtsInfo->delta_hprotect_info.lane_numbers;
			param.gfx_hprotect_info.hsync_w =
				drmParseDtsInfo->gfx_hprotect_info.hsync_w;
			param.gfx_hprotect_info.de_hstart =
				drmParseDtsInfo->gfx_hprotect_info.de_hstart;
			param.gfx_hprotect_info.lane_numbers =
				drmParseDtsInfo->gfx_hprotect_info.lane_numbers;

			ret |= mtk_tv_drm_check_out_mod_cfg(&pctx->panel_priv, &param);
			ret |= mtk_tv_drm_check_out_lane_cfg(&pctx->panel_priv);
			ret |= mtk_tv_drm_check_video_hbproch_protect(&param);

			output_model_cal = mtk_tv_drm_set_output_model(param.v_path_en,
				param.extdev_path_en, param.gfx_path_en);

			DRM_INFO("[%s][%d] param.output_model_ref = %d!!\n",
				__func__, __LINE__, param.output_model_ref);

			if (output_model_cal == param.output_model_ref)
				ret |= 0;
			else
				ret |= -1;
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] set out mod cfg return = %td!!\n",
				__func__, __LINE__, ret);
		}
		*/
		break;
	case E_EXT_CONNECTOR_PROP_PNL_VBO_CTRLBIT:
		property_blob = drm_property_lookup_blob(property->dev, val);
		if ((property_blob != NULL) && (val != 0)) {
			struct drm_st_ctrlbits stCtrlbits;

			memset(&stCtrlbits, 0, sizeof(struct drm_st_ctrlbits));
			memcpy(&stCtrlbits, property_blob->data, sizeof(struct drm_st_ctrlbits));

			mtk_render_set_vbo_ctrlbit(&pctx->panel_priv, &stCtrlbits);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_VBO_CTRLBIT = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_VBO_TX_MUTE_EN:
		DRM_INFO("[%s][%d] E_EXT_CONNECTOR_PROP_PNL_VBO_TX_MUTE_EN = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		property_blob = drm_property_lookup_blob(property->dev, val);

		if (property_blob == NULL) {
			DRM_INFO("[%s][%d] unknown tx_mute_info status = %td!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			drm_st_tx_mute_info param;

			memset(&param, 0, sizeof(drm_st_tx_mute_info));
			memcpy(&param, property_blob->data, sizeof(drm_st_tx_mute_info));

			ret = mtk_render_set_tx_mute(&pctx->panel_priv, param);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] set TX mute return = %d!!\n",
				__func__, __LINE__, ret);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SWING_VREG:
		DRM_INFO("[%s][%d] E_EXT_CONNECTOR_PROP_PNL_SWING_VREG = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		property_blob = drm_property_lookup_blob(property->dev, val);

		if (property_blob != NULL) {
			struct drm_st_out_swing_level stSwing;

			memset(&stSwing, 0, sizeof(struct drm_st_out_swing_level));
			memcpy(&stSwing, property_blob->data,
				sizeof(struct drm_st_out_swing_level));

			mtk_render_set_swing_vreg(&stSwing);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_SWING_VREG = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_PRE_EMPHASIS:
		DRM_INFO("[%s][%d] E_EXT_CONNECTOR_PROP_PNL_PRE_EMPHASIS = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);
		property_blob = drm_property_lookup_blob(property->dev, val);

		if (property_blob != NULL) {
			struct drm_st_out_pe_level stPE;

			memset(&stPE, 0, sizeof(struct drm_st_out_pe_level));
			memcpy(&stPE, property_blob->data, sizeof(struct drm_st_out_pe_level));

			mtk_render_set_pre_emphasis(&stPE);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_SE = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SSC:
		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob != NULL) {
			struct drm_st_out_ssc stSSC;

			memset(&stSSC, 0, sizeof(struct drm_st_out_ssc));
			memcpy(&stSSC, property_blob->data, sizeof(struct drm_st_out_ssc));

			pctx_pnl = &pctx->panel_priv;

			if (!pctx_pnl) {
				DRM_ERROR("[%s, %d]: pctx_pnl is NULL.\n",
				__func__, __LINE__);
				return -ENODEV;
			}

			memcpy(&(pctx_pnl->stdrmSSC.ssc_setting), &stSSC,
				sizeof(struct drm_st_out_ssc));
			// modulation: 1Khz for property, 0.1Khz in render DD
			// deviation: 0.01% for property, 0.01% in render DD
			pctx_pnl->stdrmSSC.ssc_setting.ssc_modulation *= 10;
			mtk_render_set_ssc(pctx_pnl);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] PNL_SSC = %td\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			DRM_INFO("[%s][%d]blob id= 0x%tx, blob is NULL!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_CHECK_DTS_OUTPUT_TIMING:
		DRM_INFO("[%s][%d] E_EXT_CONNECTOR_PROP_PNL_CHECK_DTS_OUTPUT_TIMING = %td\n",
			__func__, __LINE__, (ptrdiff_t)val);

		property_blob = drm_property_lookup_blob(property->dev, val);
		if (property_blob == NULL) {
			DRM_INFO("[%s][%d] unknown output info status = %td!!\n",
				__func__, __LINE__, (ptrdiff_t)val);
		} else {
			drm_st_pnl_info *pStPnlInfo
					= ((drm_st_pnl_info *)(property_blob->data));

			ret = mtk_tv_drm_panel_checkDTSPara(pStPnlInfo);
			drm_property_blob_put(property_blob);
			DRM_INFO("[%s][%d] mtk_tv_drm_panel_checkDTSPara return = %d!!\n",
				__func__, __LINE__, ret);
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_FRAMESYNC_MLOAD:
		break;
	case E_EXT_CONNECTOR_PROP_MAX:
		DRM_INFO("[%s][%d] invalid property!!\n",
			__func__, __LINE__);
		break;
	default:
		break;
	}

	return ret;
}

int mtk_tv_drm_video_atomic_get_connector_property(
	struct mtk_tv_drm_connector *connector,
	const struct drm_connector_state *state,
	struct drm_property *property,
	uint64_t *val)
{
	int ret = -EINVAL;
	int i;
	drm_pnl_info stPanelInfo;
	struct mtk_tv_kms_context *pctx =
		(struct mtk_tv_kms_context *)connector->connector_private;
	struct ext_connector_prop_info *connector_props = pctx->ext_connector_properties;
	struct drm_property_blob *property_blob;

	memset(&stPanelInfo, 0, sizeof(drm_pnl_info));
	property_blob = NULL;

	for (i = 0; i < E_EXT_CONNECTOR_PROP_MAX; i++) {
		if (property == pctx->drm_connector_prop[i]) {
			*val = connector_props->prop_val[i];
			ret = 0x0;
			break;
		}
	}

	switch (i) {
	case E_EXT_CONNECTOR_PROP_PNL_PANEL_INFO:
		//stPanelInfo.u16PnlHstart = MAPI_PNL_GetPNLHstart_U2();
		//stPanelInfo.u16PnlVstart = MAPI_PNL_GetPNLVstart_U2();
		//stPanelInfo.u16PnlWidth = MAPI_PNL_GetPNLWidth_U2();
		//stPanelInfo.u16PnlHeight = MAPI_PNL_GetPNLHeight_U2();
		//stPanelInfo.u16PnlHtotal = MAPI_PNL_GetPNLHtotal_U2();
		//stPanelInfo.u16PnlVtotal = MAPI_PNL_GetPNLVtotal_U2();
		//stPanelInfo.u16PnlHsyncWidth = MAPI_PNL_GetPNLHsyncWidth_U2();
		//stPanelInfo.u16PnlHsyncBackPorch = MAPI_PNL_GetPNLHsyncBackPorch_U2();
		//stPanelInfo.u16PnlVsyncBackPorch = MAPI_PNL_GetPNLVsyncBackPorch_U2();
		//stPanelInfo.u16PnlDefVfreq = MApi_PNL_GetDefVFreq();
		//stPanelInfo.u16PnlLPLLMode = MApi_PNL_GetLPLLMode();
		//stPanelInfo.u16PnlLPLLType = MApi_PNL_GetLPLLType_U2();
		//stPanelInfo.u16PnlMinSET = MApi_PNL_GetMinSET_U2();
		//stPanelInfo.u16PnlMaxSET = MApi_PNL_GetMaxSET_U2();
		break;
	case E_EXT_CONNECTOR_PROP_PNL_VBO_CTRLBIT:
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SWING_VREG:
		if ((property_blob == NULL) && (val != NULL)) {
			property_blob = drm_property_lookup_blob(property->dev, *val);
			if (property_blob == NULL) {
				DRM_INFO("[%s][%d] unknown swing vreg status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			} else {
				struct drm_st_out_swing_level stSwing;
				size_t len;

				len = min(sizeof(struct drm_st_out_swing_level),
						property_blob->length);
				memset(&stSwing, 0, sizeof(struct drm_st_out_swing_level));
				memcpy(&stSwing, property_blob->data, len);
				mtk_render_get_swing_vreg(&stSwing);
				drm_property_blob_put(property_blob);
				DRM_INFO("[%s][%d] get swing verg status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			}
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_PRE_EMPHASIS:
		if ((property_blob == NULL) && (val != NULL)) {
			property_blob = drm_property_lookup_blob(property->dev, *val);
			if (property_blob == NULL) {
				DRM_INFO("[%s][%d] unknown pre emphasis status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			} else {
				struct drm_st_out_pe_level stPE;
				size_t len;

				len = min(sizeof(struct drm_st_out_pe_level),
						property_blob->length);
				memset(&stPE, 0, sizeof(struct drm_st_out_pe_level));
				memcpy(&stPE, property_blob->data, len);
				mtk_render_get_pre_emphasis(&stPE);
				drm_property_blob_put(property_blob);
				DRM_INFO("[%s][%d] get pre emphasis status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			}
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_SSC:
		if ((property_blob == NULL) && (val != NULL)) {
			property_blob = drm_property_lookup_blob(property->dev, *val);
			if (property_blob == NULL) {
				DRM_INFO("[%s][%d] unknown ssc status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			} else {
				struct drm_st_out_ssc stSSC;
				size_t len;

				len = min(sizeof(struct drm_st_out_ssc),
						property_blob->length);
				memset(&stSSC, 0, sizeof(struct drm_st_out_ssc));
				memcpy(&stSSC, property_blob->data, len);
				mtk_render_get_ssc(&stSSC);
				drm_property_blob_put(property_blob);
				DRM_INFO("[%s][%d] get ssc status = %td!!\n",
					__func__, __LINE__, (ptrdiff_t)*val);
			}
		}
		break;
	case E_EXT_CONNECTOR_PROP_PNL_OUT_MODEL:
		*val = pctx->out_model;
		break;
	case E_EXT_CONNECTOR_PROP_PNL_FRAMESYNC_MLOAD:
		 *val = pctx->panel_priv.outputTiming_info.eFrameSyncState;
		break;
	case E_EXT_CONNECTOR_PROP_MAX:
		DRM_INFO("[%s][%d] invalid property!!\n",
			__func__, __LINE__);
		break;
	default:
		break;
	}

	return ret;
}

int mtk_tv_drm_video_get_delay_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_video_delay *delay = (struct drm_mtk_tv_video_delay *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_frc_context *pctx_frc = NULL;
	struct mtk_frc_info_for_latency frc_info_latency = {0};

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;
	pctx_frc = &pctx->frc_priv;
	pctx_pnl = &pctx->panel_priv;

	if (pctx->stub)
		delay->sw_delay_ms = SW_DELAY;
	else
		delay->sw_delay_ms = pctx_video->sw_delay;

	DRM_INFO("\n[%s]frc_latency=%d", __func__, pctx_frc->frc_latency);
	frc_info_latency.input_freq = delay->memc_pre_input_vfreq;
	frc_info_latency.contentW = delay->content_width;
	frc_info_latency.contentH = delay->content_height;
	frc_info_latency.srcW = delay->src_width;
	frc_info_latency.srcH = delay->src_height;
	frc_info_latency.srcFps = delay->src_fps;
	frc_info_latency.maxFps = delay->max_fps;
	frc_info_latency.IsHbbtv = delay->IsHbbtv;
	frc_info_latency.IsInputSrcDD = delay->IsInputSrcDD;
	frc_info_latency.IsAdaptive = delay->IsAdaptive;
	frc_info_latency.IsLowLatency = delay->IsLowLatency;
	frc_info_latency.disp_mode = delay->disp_mode;
	_mtk_video_frc_get_latency_preplay(&frc_info_latency, pctx);

	delay->memc_delay_ms = pctx_frc->frc_latency;
	delay->memc_rw_diff  = pctx_frc->frc_rwdiff;

	mtk_video_display_mgw_get_latency(delay, pctx);

	DRM_INFO("\n[%s]frc_latency=%d frc_rwdiff=%d", __func__, pctx_frc->frc_latency,
		pctx_frc->frc_rwdiff);

	return 0;
}

int mtk_tv_drm_video_get_fb_num_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_video_fb_num *fb = (struct drm_mtk_tv_video_fb_num *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct mtk_video_context *pctx_video = NULL;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;
	pctx_pnl = &pctx->panel_priv;

	if (pctx->stub) {
		fb->memc_fb_num = SW_FB_NUM;
		fb->mdgw_fb_num = SW_FB_NUM;
	}	else {
		if (fb->mdgw_fb_mode == MTK_VPLANE_BUF_MODE_SW)
			fb->mdgw_fb_num = 1;
		else if (fb->mdgw_fb_mode == MTK_VPLANE_BUF_MODE_HW)
			fb->mdgw_fb_num = 1;
		else
			fb->mdgw_fb_num = 0;

		fb->memc_fb_num = 0;
	}

	return 0;
}

int mtk_tv_drm_video_set_frc_mode_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_video_frc_mode *frc_mode = (struct drm_mtk_tv_video_frc_mode *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct mtk_frc_context *pctx_frc = NULL;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_frc = &pctx->frc_priv;
	pctx_pnl = &pctx->panel_priv;

	pctx_frc->video_memc_mode = frc_mode->video_memc_mode;
	pctx_frc->video_frc_mode = frc_mode->video_frc_mode;

	return 0;
}

int mtk_tv_drm_video_set_extra_video_latency_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_video_extra_latency *latency =
		(struct drm_mtk_tv_video_extra_latency *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	int ret = 0;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}
	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;

	ret = mtk_video_display_frc_set_extra_latency(pctx, latency);
	if (ret < 0) {
		DRM_INFO("[%s][%d] frc_set_extra_latency failed!!\n",
			__func__, __LINE__);
		return ret;
	}

	return 0;
}

int mtk_tv_drm_video_get_expect_pq_out_size_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_video_expect_pq_out_size *expect_pq_out_size =
		(struct drm_mtk_video_expect_pq_out_size *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct mtk_video_context *pctx_video = NULL;
	enum DRM_VIDEO_MEM_MODE_E mem_mode = EN_VIDEO_MEM_MODE_SW;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;
	pctx_pnl = &pctx->panel_priv;

	if (pctx->sw_caps.enable_expect_pqout_size == 1) {
		mem_mode = expect_pq_out_size->mem_mode;

		if (mem_mode == EN_VIDEO_MEM_MODE_MAX) {
			DRM_WARN("Invalid mem_mode:%d\n", mem_mode);
			return -EINVAL;
		}

		if (mem_mode == EN_VIDEO_MEM_MODE_HW) {
			expect_pq_out_size->width = pctx_pnl->info.de_width;
			expect_pq_out_size->height = pctx_pnl->info.de_height;
		} else {
			expect_pq_out_size->width = 0;
			expect_pq_out_size->height = 0;
		}
	} else {
		expect_pq_out_size->width = 0;
		expect_pq_out_size->height = 0;
	}


	pr_info("[%s][%d] expect_pq_out_size:[%u,%u]\n",
			__func__, __LINE__,
			expect_pq_out_size->width,
			expect_pq_out_size->height);

	return 0;
}

int mtk_tv_drm_video_set_live_tone_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct drm_mtk_range_value *range = (struct drm_mtk_range_value *)data;

	if (!drm_dev || !data || !file_priv)
		return -EINVAL;

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}
	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	_mtk_tv_drm_video_set_live_tone(pctx, range);
	return 0;
}



int mtk_tv_drm_video_get_pxm_report_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct drm_mtk_pxm_get_report_size *pxm_report = (struct drm_mtk_pxm_get_report_size *)data;
	int ret = 0;

	if (!drm_dev || !data || !file_priv)
		return -EINVAL;

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}
	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;

	ret = _mtk_tv_drm_video_get_pixel_report(pctx, pxm_report);
	if (ret < 0) {
		DRM_ERROR("[%s][%d] Get pixel report failed!!\n",
			__func__, __LINE__);
		return ret;
	}

	return 0;
}

int mtk_tv_drm_video_set_factory_mode_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct drm_mtk_video_factory_mode  *factory_mode_ctrl =
		(struct drm_mtk_video_factory_mode  *)data;
	struct reg_info reg[HWREG_VIDEO_REG_NUM_FACTORY_MODE];
	struct hwreg_factory_mode paramIn;
	struct hwregOut reg_out;
	int ret = 0;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;

	memset(reg, 0, sizeof(reg));
	memset(&paramIn, 0, sizeof(struct hwreg_factory_mode));
	memset(&reg_out, 0, sizeof(struct hwregOut));

	paramIn.RIU = 0;
	paramIn.factory_mode =
		(enum drm_hwreg_factory_mode) factory_mode_ctrl->factory_mode;
	reg_out.reg = reg;

	pr_info("[%s][%d] factory_mode:%d\n",
			__func__, __LINE__, paramIn.factory_mode);

	drv_hwreg_render_display_set_factory_mode(paramIn, &reg_out);

	if (!paramIn.RIU) {
		ret = mtk_tv_sm_write_cmd(&pctx_video->disp_irq_ml,
			reg_out.regCount, (struct sm_reg *)reg);
		if (ret) {
			DRM_ERROR("[%s, %d]: disp_ml append cmd fail (%d)",
				__func__, __LINE__, ret);
		}
	}
	return 0;
}

int mtk_tv_drm_video_set_frc_input_hdr_type_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_video_frc_input_hdr_type *input_hdr_type =
		(struct drm_mtk_tv_video_frc_input_hdr_type *)data;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_frc_context *pctx_frc = NULL;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_frc = &pctx->frc_priv;

	pctx_frc->frc_input_hdr_type =
		(enum mtk_video_frc_input_hdr_type)input_hdr_type->input_hdr_type;

	return 0;
}

int mtk_tv_drm_video_set_per_vsync_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	int ret = 0;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}

	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;

	if (mtk_tv_sm_get_mem_index(&pctx_video->disp_ml))
		DRM_WARN("[%s, %d]: disp_ml get mem index fail", __func__, __LINE__);
	if (mtk_tv_sm_get_mem_index(&pctx_video->vgs_ml))
		DRM_WARN("[%s, %d]: vgs_ml get mem index fail", __func__, __LINE__);

	ret = mtk_video_display_frc_set_per_vsync(mtk_tv_crtc);
	ret |= mtk_video_display_mgw_set_per_vsync(mtk_tv_crtc);
	ret |= mtk_video_display_mute_set_per_vsync(mtk_tv_crtc);
	if (ret)
		DRM_WARN("[%s] sub function fail", __func__);

	if (mtk_tv_sm_fire(&pctx_video->disp_ml) < 0)
		DRM_WARN("[%s, %d]: disp ml fire fail", __func__, __LINE__);

	if (mtk_tv_sm_fire(&pctx_video->vgs_ml) < 0)
		DRM_WARN("[%s, %d]: vgs ml fire fail", __func__, __LINE__);

	return ret;
}

int mtk_tv_drm_video_set_test_pattern_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	int ret = 0;
	struct drm_mtk_tv_render_testpattern *test_pattern_info =
		(struct drm_mtk_tv_render_testpattern *)data;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct drm_pattern_status *pattern_status = NULL;
	struct drm_mtk_pattern_config pattern_config;
	struct drm_mtk_pattern_pure_color pure_color;

	memset(&pure_color, 0, sizeof(struct drm_mtk_pattern_pure_color));

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}
	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;

	if (test_pattern_info->pattern_position == EN_VIDEO_PATTERN_POSITION_MOD) {
		ret = mtk_tv_drm_set_mod_pattern(test_pattern_info);
	} else if (test_pattern_info->pattern_position == EN_VIDEO_PATTERN_POSITION_PQ_GAMMA) {
		pure_color.color.red = R_G_B_MAX;
		pure_color.color.green = R_G_B_MAX;
		pure_color.color.blue = R_G_B_MAX;

		pattern_status = &pctx->pattern_status;

		pattern_config.pattern_type = MTK_PATTERN_PURE_COLOR;
		pattern_config.pattern_position = MTK_PATTERN_POSITION_PQGA;
		pattern_config.u.pure_color = pure_color;

		ret = mtk_drm_pattern_set_param(pattern_status, &pattern_config);
	}

	return ret;
}

int mtk_tv_drm_video_get_frc_algo_version_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	uint32_t *u32pVersion = (uint32_t *)data;
	int ret = 0;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	ret = mtk_video_display_frc_get_algo_version(u32pVersion);
	DRM_INFO("MEMC Version INFO: %d\n", *u32pVersion);

	return ret;
}

int mtk_tv_drm_video_set_video_mute_color_capability_ioctl(
	struct drm_device *drm_dev,
	void *data,
	struct drm_file *file_priv)
{
	struct drm_mtk_tv_mute_color_cap *mute_color_info =
		(struct drm_mtk_tv_mute_color_cap *)data;
	struct drm_crtc *crtc = NULL;
	struct mtk_tv_drm_crtc *mtk_tv_crtc = NULL;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	int ret = 0;

	if (!drm_dev || !data || !file_priv) {
		DRM_WARN("[%s][%d] Invalid input null ptr\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	drm_for_each_crtc(crtc, drm_dev) {
		mtk_tv_crtc = to_mtk_tv_crtc(crtc);
		break;
	}
	pctx = (struct mtk_tv_kms_context *)mtk_tv_crtc->crtc_private;
	pctx_video = &pctx->video_priv;

	pctx_video->window_mute_color.user_control_bit_num =
		mute_color_info->control;

	if (mute_color_info->control) {
		pctx_video->window_mute_color.bit_num =
			mute_color_info->bit_num;
	} else {/* use default*/
		pctx_video->window_mute_color.bit_num =
			EN_MUTE_COLOR_BIT_NUM_8;
	}

	pr_info("[%s][%d] control:%d final_mute_color_bit_num:%d\n",
		__func__, __LINE__,
		mute_color_info->control,
		pctx_video->window_mute_color.bit_num);

	return ret;
}
