// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */

#include <linux/module.h>
#include <linux/sysfs.h>
#include <drm/drm_print.h>

#include "mtk_tv_drm_kms.h"
#include "mtk_tv_drm_log.h"
#include "mtk_tv_drm_video_display.h"
#include "mtk_tv_drm_video_display_sysfs.h"

#include "hwreg_render_video_display.h"

//--------------------------------------------------------------------------------------------------
//  Local Defines
//--------------------------------------------------------------------------------------------------
#define MTK_DRM_MODEL MTK_DRM_MODEL_DISPLAY
#define ERR(fmt, args...) DRM_ERROR("[%s][%d] " fmt, __func__, __LINE__, ##args)
#define WRN(fmt, args...) DRM_WARN("[%s][%d] " fmt, __func__, __LINE__, ##args)
#define LOG(fmt, args...) DRM_INFO("[%s][%d] " fmt, __func__, __LINE__, ##args)


#define MTK_SYSFS_RO(__name)									\
	static ssize_t __name##_show(struct device *, struct device_attribute *,		\
					char *buf);						\
	static DEVICE_ATTR_RO(__name)

#define MTK_SYSFS_RW(__name)									\
	static ssize_t __name##_show(struct device *dev, struct device_attribute *attr,		\
					char *buf);						\
	static ssize_t __name##_store(struct device *dev, struct device_attribute *attr,	\
					const char *buf, size_t count);				\
	static DEVICE_ATTR_RW(__name)


//--------------------------------------------------------------------------------------------------
//  Local Structures
//--------------------------------------------------------------------------------------------------
struct mtk_video_dbg_info_plane {
	uint8_t  hvspbypass[MTK_VPLANE_TYPE_MAX];
	struct  window *scl_inputWin;
	struct  window *scl_outputWin;
	uint8_t  zpos[MTK_VPLANE_TYPE_MAX];
	uint8_t  layerEn[MTK_VPLANE_TYPE_MAX];
	uint8_t  thinReadFactor[MTK_VPLANE_TYPE_MAX];
	uint8_t  mgwPlaneNum;
	uint8_t  preInsert_en[MTK_VPLANE_TYPE_MAX];
	struct  windowRect preInsert_win[MTK_VPLANE_TYPE_MAX];
	uint64_t preInsert_w[MTK_VPLANE_TYPE_MAX];
	uint64_t preInsert_h[MTK_VPLANE_TYPE_MAX];
	uint16_t preInsert_htt[MTK_VPLANE_TYPE_MAX];
	uint16_t preInsert_vtt[MTK_VPLANE_TYPE_MAX];
	uint8_t  postFill_en[MTK_VPLANE_TYPE_MAX];
	uint16_t postFill_htt[MTK_VPLANE_TYPE_MAX];
	uint16_t postFill_vtt[MTK_VPLANE_TYPE_MAX];
	struct ARGB postFill_argb[MTK_VPLANE_TYPE_MAX];
};

struct mtk_video_dbg_info {
	struct window *crop_win;
	struct window *disp_win;
	enum drm_mtk_video_plane_type planeType[MAX_WINDOW_NUM];
	uint16_t rwdiff[MAX_WINDOW_NUM];
	uint8_t mute_en[MAX_WINDOW_NUM];
};

struct mtk_video_dbg_mute_info {
	enum drm_mtk_video_plane_type planeType[MAX_WINDOW_NUM];
	uint8_t  src_sel[NUM_OF_LAYER];
	uint8_t  layer_sel[NUM_OF_LAYER];
	uint8_t  layer_en[NUM_OF_LAYER];
	uint8_t  blend_out_mute[MTK_VPLANE_TYPE_MAX];
	uint8_t mute_en[MAX_WINDOW_NUM];
	uint8_t blend_out_mute_en;
};

struct video_vb_info {
	uint8_t  zpos[MTK_VPLANE_TYPE_MAX];
	uint8_t  layer_en[MTK_VPLANE_TYPE_MAX];
	uint8_t  line_buffer_en[MTK_VPLANE_TYPE_MAX];
	uint8_t  line_buffer_h_size[MTK_VPLANE_TYPE_MAX];
	uint8_t  clip_en[MTK_VPLANE_TYPE_MAX];
	struct  windowRect clip_win[MTK_VPLANE_TYPE_MAX];
	uint8_t clip_w[MTK_VPLANE_TYPE_MAX];
	uint8_t clip_h[MTK_VPLANE_TYPE_MAX];
	uint8_t  pre_insert_en[MTK_VPLANE_TYPE_MAX];
	struct  windowRect pre_insert_win[MTK_VPLANE_TYPE_MAX];
	uint8_t pre_insert_w[MTK_VPLANE_TYPE_MAX];
	uint8_t pre_insert_h[MTK_VPLANE_TYPE_MAX];
	uint16_t pre_insert_htt[MTK_VPLANE_TYPE_MAX];
	uint16_t pre_insert_vtt[MTK_VPLANE_TYPE_MAX];
	uint8_t  post_fill_en[MTK_VPLANE_TYPE_MAX];
	struct  windowRect post_fill_win[MTK_VPLANE_TYPE_MAX];
	uint8_t post_fill_w[MTK_VPLANE_TYPE_MAX];
	uint8_t post_fill_h[MTK_VPLANE_TYPE_MAX];
	uint8_t post_fill_htt[MTK_VPLANE_TYPE_MAX];
	uint8_t post_fill_vtt[MTK_VPLANE_TYPE_MAX];
	uint8_t vb_out_mute;
	uint8_t vb_out_h_size;
	uint8_t vb_mux;
};

//--------------------------------------------------------------------------------------------------
//  Local Variables
//--------------------------------------------------------------------------------------------------
MTK_SYSFS_RO(debug_info);
MTK_SYSFS_RO(debug_info_plane_type);
MTK_SYSFS_RO(mute_info);
MTK_SYSFS_RW(mute_enable);
MTK_SYSFS_RW(mute_color);
MTK_SYSFS_RW(default_background_color);
MTK_SYSFS_RO(cfd_setting_info);
MTK_SYSFS_RO(vb_color_space);
MTK_SYSFS_RW(enable_expect_pqout_size);
MTK_SYSFS_RW(enable_pqu_ds_size_ctl);
MTK_SYSFS_RO(mgw_version);
MTK_SYSFS_RO(vb_version);
MTK_SYSFS_RO(mute_version);
MTK_SYSFS_RO(disp_version);
MTK_SYSFS_RO(irq_version);
MTK_SYSFS_RO(pattern_version);
MTK_SYSFS_RO(dma_num);
MTK_SYSFS_RO(mgw_num);
MTK_SYSFS_RO(memc_num);
MTK_SYSFS_RO(window_num);
MTK_SYSFS_RO(panel_gamma_bit_num);
MTK_SYSFS_RO(support_od);
MTK_SYSFS_RO(support_oled_demura_in);
MTK_SYSFS_RO(support_oled_boosting);
MTK_SYSFS_RO(support_hwmode_planetype);
MTK_SYSFS_RO(support_swmode_planetype);
MTK_SYSFS_RO(support_bypassmode_planetype);
MTK_SYSFS_RO(support_h_scl_down);
MTK_SYSFS_RO(support_render_scaling);
MTK_SYSFS_RO(support_live_tone);
MTK_SYSFS_RO(support_i_view);
MTK_SYSFS_RO(support_i_view_pip);
MTK_SYSFS_RO(render_p_engine);
MTK_SYSFS_RO(byte_per_word);
MTK_SYSFS_RO(disp_x_align);
MTK_SYSFS_RO(disp_y_align);
MTK_SYSFS_RO(disp_w_align);
MTK_SYSFS_RO(disp_h_align);
MTK_SYSFS_RO(crop_x_align_420_422);
MTK_SYSFS_RO(crop_x_align_444);
MTK_SYSFS_RO(crop_y_align_420);
MTK_SYSFS_RO(crop_y_align_444_422);
MTK_SYSFS_RO(crop_w_align);
MTK_SYSFS_RO(crop_h_align);
MTK_SYSFS_RO(video_mute_color_bits_num);
MTK_SYSFS_RO(video_plane_prop_info);
MTK_SYSFS_RO(render_size_report);
MTK_SYSFS_RO(render_memory_info);
MTK_SYSFS_RO(render_latency_info);


static struct attribute *mtk_drm_video_display_attrs[] = {
	&dev_attr_debug_info.attr,
	&dev_attr_debug_info_plane_type.attr,
	&dev_attr_mute_info.attr,
	&dev_attr_mute_enable.attr,
	&dev_attr_mute_color.attr,
	&dev_attr_default_background_color.attr,
	&dev_attr_cfd_setting_info.attr,
	&dev_attr_vb_color_space.attr,
	&dev_attr_video_plane_prop_info.attr,
	&dev_attr_render_size_report.attr,
	&dev_attr_render_memory_info.attr,
	&dev_attr_render_latency_info.attr,
	NULL
};

static struct attribute *mtk_drm_video_display_attrs_version[] = {
	&dev_attr_mgw_version.attr,
	&dev_attr_vb_version.attr,
	&dev_attr_mute_version.attr,
	&dev_attr_disp_version.attr,
	&dev_attr_irq_version.attr,
	&dev_attr_pattern_version.attr,
	NULL
};

static struct attribute *mtk_drm_video_display_attrs_capability[] = {
	&dev_attr_dma_num.attr,
	&dev_attr_mgw_num.attr,
	&dev_attr_memc_num.attr,
	&dev_attr_window_num.attr,
	&dev_attr_panel_gamma_bit_num.attr,
	&dev_attr_support_od.attr,
	&dev_attr_support_oled_demura_in.attr,
	&dev_attr_support_oled_boosting.attr,
	&dev_attr_support_hwmode_planetype.attr,
	&dev_attr_support_swmode_planetype.attr,
	&dev_attr_support_bypassmode_planetype.attr,
	&dev_attr_support_h_scl_down.attr,
	&dev_attr_support_render_scaling.attr,
	&dev_attr_enable_expect_pqout_size.attr,
	&dev_attr_enable_pqu_ds_size_ctl.attr,
	&dev_attr_support_live_tone.attr,
	&dev_attr_support_i_view.attr,
	&dev_attr_support_i_view_pip.attr,
	&dev_attr_render_p_engine.attr,
	&dev_attr_byte_per_word.attr,
	&dev_attr_disp_x_align.attr,
	&dev_attr_disp_y_align.attr,
	&dev_attr_disp_w_align.attr,
	&dev_attr_disp_h_align.attr,
	&dev_attr_crop_x_align_420_422.attr,
	&dev_attr_crop_x_align_444.attr,
	&dev_attr_crop_y_align_420.attr,
	&dev_attr_crop_y_align_444_422.attr,
	&dev_attr_crop_w_align.attr,
	&dev_attr_crop_h_align.attr,
	&dev_attr_video_mute_color_bits_num.attr,
	NULL
};

static const struct attribute_group mtk_drm_video_display_attr_group = {
	.name = "video_display",
	.attrs = mtk_drm_video_display_attrs
};

static const struct attribute_group mtk_drm_video_display_attrs_version_group = {
	.name = "version",
	.attrs = mtk_drm_video_display_attrs_version
};

static const struct attribute_group mtk_drm_video_display_capability_group = {
	.name = "capability",
	.attrs = mtk_drm_video_display_attrs_capability
};

//--------------------------------------------------------------------------------------------------
//  Local Functions
//--------------------------------------------------------------------------------------------------
// *************************************************************************************************
const char *DispModeToString(uint8_t u8disMode)
{
	switch (u8disMode) {
	case MTK_VPLANE_DISP_MODE_NONE:
		return "DISP MODE NONE";
	case MTK_VPLANE_DISP_MODE_TIME_SHARING:
		return "DISP MODE Time_Sharing";
	case MTK_VPLANE_DISP_MODE_LEGACY:
		return "DISP MODE Legacy";
	default:
		return "Not Defined DISP MODE";
	}
}
const char *BuffModeToString(uint8_t u8bufmode)
{
	switch (u8bufmode) {
	case MTK_VPLANE_BUF_MODE_NONE:
		return "BUFF MODE NONE";
	case MTK_VPLANE_BUF_MODE_SW:
		return "BUFF MODE SW";
	case MTK_VPLANE_BUF_MODE_HW:
		return "BUFF MODE HW";
	case MTK_VPLANE_BUF_MODE_BYPASSS:
		return "BUFF MODE BYPASS";
	default:
		return "Not Defined BUFF MODE";
	}
}

static ssize_t render_latency_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	int len = 0;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	enum drm_mtk_video_plane_type planeType[MAX_WINDOW_NUM];
	uint32_t output_freq = 0;
	uint32_t input_freq = 0;
	int i = 0;
	uint16_t hw_rwdiff = 0;
	uint16_t hw_delay_ms = 0;

	if ((dev == NULL) || (attr == NULL) || (buf == NULL)) {
		ERR("Pointer is NULL!");
		return -ENODEV;
	}

	pctx = dev_get_drvdata(dev);

	if (pctx == NULL) {
		ERR("Pointer is NULL!");
		return -ENODEV;
	}

	pctx_video = &pctx->video_priv;
	if (pctx_video == NULL) {
		ERR("Pointer is NULL!");
		return -ENODEV;
	}

	pctx_pnl = &pctx->panel_priv;
	if (pctx_pnl == NULL) {
		ERR("Pointer is NULL!");
		return -ENODEV;
	}
	output_freq = (pctx_pnl->outputTiming_info.u32OutputVfreq/VFRQRATIO_1000);
	input_freq = pctx_video->report_latency.memc_pre_input_vfreq;

	len += snprintf(buf + len, PAGE_SIZE - len,
		"===============[RWdiff Info]==============\n");
	len += snprintf(buf + len, PAGE_SIZE - len,
		"input_freq = %d\n", input_freq);
	len += snprintf(buf + len, PAGE_SIZE - len,
		"output_freq = %d\n", output_freq);
	len += snprintf(buf + len, PAGE_SIZE - len,
		"disp mode = %d (%s)\n",
		pctx_video->report_latency.disp_mode,
		DispModeToString(pctx_video->report_latency.disp_mode));
	len += snprintf(buf + len, PAGE_SIZE - len,
		"buff mode = %d (%s)\n",
		pctx_video->report_latency.buf_mode,
		BuffModeToString(pctx_video->report_latency.buf_mode));

	if (pctx->chip_series == CHIP_SERIES_MAIN_TS) {
		for (i = 0; i < pctx->hw_caps.window_num; i++) {
			planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);

			if (IS_DRM_PLANE_TYPE_VALID(planeType[i])) {
				drv_hwreg_render_display_get_rw_diff(i, &hw_rwdiff);

				if (hw_rwdiff)
					hw_delay_ms =
						_1_SECOND / (input_freq*hw_rwdiff);
				else
					hw_delay_ms = 0;

				len += snprintf(buf + len, PAGE_SIZE - len,
					"[win %2d]\n", i);

				len += snprintf(buf + len, PAGE_SIZE - len,
					"(mdgw, sw status) report rw diff = %d\n",
					pctx_video->report_latency.mdgw_rw_diff);

				len += snprintf(buf + len, PAGE_SIZE - len,
					"(mdgw, hw status) real rw diff = %d\n", hw_rwdiff);

				len += snprintf(buf + len, PAGE_SIZE - len,
					"(mdgw, sw status) report latency = %d\n",
					pctx_video->report_latency.mdgw_delay_ms);

				len += snprintf(buf + len, PAGE_SIZE - len,
					"(mdgw, hw status) real latency = %d\n", hw_delay_ms);

				len += snprintf(buf + len, PAGE_SIZE - len,
					"render latency status = %s\n",
					(pctx_video->report_latency.mdgw_delay_ms ==
					hw_delay_ms) ? "pass" : "fail");
			}
		}
	} else
		len += snprintf(buf + len, PAGE_SIZE - len, "Not Support!!!\n");

	return ((len < 0) || (len > PAGE_SIZE)) ? -EINVAL : len;
}

static ssize_t render_memory_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct hwreg_memory_info mem_hw_info;
	int len = 0, index = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	memset(&mem_hw_info, 0, sizeof(struct hwreg_memory_info));

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;

	drv_hwreg_render_display_get_memory_hw_info(&mem_hw_info);

	if (pctx->chip_series == CHIP_SERIES_MAIN_TS) {/*1.0*/
		SYSFS_PRINT(len, buf, "=====  [Client ID Info]  =====");
		SYSFS_PRINT(len, buf, "(MGW, DMA0, DMA1):(0x%x,0x%x,0x%x)",
			pctx_video->cid_table.mgw0,
			pctx_video->cid_table.dma0,
			pctx_video->cid_table.dma1);

		SYSFS_PRINT(len, buf, "\n=== [Log] ===");
		mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_FRAME_BUFFER, buf, &len);

		SYSFS_PRINT(len, buf, "\n=====  [HW Status]  =====");

		SYSFS_PRINT(len, buf, "-----     (DMA Enable)     -----");
		SYSFS_PRINT(len, buf, "MGW:%d DMA0:%d DMA1:%d",
			mem_hw_info.dma_en[DMA_TYPE_MGW],
			mem_hw_info.dma_en[DMA_TYPE_DMA0],
			mem_hw_info.dma_en[DMA_TYPE_DMA1]);

		SYSFS_PRINT(len, buf, "\n-----  (DMA Window Enable) -----");
		SYSFS_PRINT(len, buf, "(MGW, DMA0, DMA1):(0x%x,0x%x,0x%x)",
			(mem_hw_info.dma_win_en[DMA_TYPE_MGW][WIN_ID_0]|
			SHIFT_LEFT_1(mem_hw_info.dma_win_en[DMA_TYPE_MGW][WIN_ID_1])|
			SHIFT_LEFT_2(mem_hw_info.dma_win_en[DMA_TYPE_MGW][WIN_ID_2])|
			SHIFT_LEFT_3(mem_hw_info.dma_win_en[DMA_TYPE_MGW][WIN_ID_3])|
			SHIFT_LEFT_4(mem_hw_info.dma_win_en[DMA_TYPE_MGW][WIN_ID_4])),
			(mem_hw_info.dma_win_en[DMA_TYPE_DMA0][WIN_ID_0]|
			SHIFT_LEFT_1(mem_hw_info.dma_win_en[DMA_TYPE_DMA0][WIN_ID_1])|
			SHIFT_LEFT_2(mem_hw_info.dma_win_en[DMA_TYPE_DMA0][WIN_ID_2])|
			SHIFT_LEFT_3(mem_hw_info.dma_win_en[DMA_TYPE_DMA0][WIN_ID_3])|
			SHIFT_LEFT_4(mem_hw_info.dma_win_en[DMA_TYPE_DMA0][WIN_ID_4])),
			(mem_hw_info.dma_win_en[DMA_TYPE_DMA1][WIN_ID_0]|
			SHIFT_LEFT_1(mem_hw_info.dma_win_en[DMA_TYPE_DMA1][WIN_ID_1])|
			SHIFT_LEFT_2(mem_hw_info.dma_win_en[DMA_TYPE_DMA1][WIN_ID_2])|
			SHIFT_LEFT_3(mem_hw_info.dma_win_en[DMA_TYPE_DMA1][WIN_ID_3])|
			SHIFT_LEFT_4(mem_hw_info.dma_win_en[DMA_TYPE_DMA1][WIN_ID_4])));

		SYSFS_PRINT(len, buf, "\n-----  (Ext Window Enable) -----");
		SYSFS_PRINT(len, buf, "(MGW, DMA0, DMA1):(%d,%d,%d)",
			mem_hw_info.ext_win_en[DMA_TYPE_MGW],
			mem_hw_info.ext_win_en[DMA_TYPE_DMA0],
			mem_hw_info.ext_win_en[DMA_TYPE_DMA1]);

		SYSFS_PRINT(len, buf, "\n-----  (buf mode sw/hw status) -----");
		SYSFS_PRINT(len, buf, "(MGW, DMA0, DMA1):(%s, %s, %s)",
			mem_hw_info.ext_win_auto_en[DMA_TYPE_MGW] == true ? "hw mode" : "sw mode",
			mem_hw_info.ext_win_auto_en[DMA_TYPE_DMA0] == true ? "hw mode" : "sw mode",
			mem_hw_info.ext_win_auto_en[DMA_TYPE_DMA1] == true ? "hw mode" : "sw mode");

		SYSFS_PRINT(len, buf, "\n-----    (Base Address)    -----");
		SYSFS_PRINT(len, buf, "[Ext](MGW)  plane(0,1,2):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.addr_ext[DMA_TYPE_MGW].plane0,
			mem_hw_info.addr_ext[DMA_TYPE_MGW].plane1,
			mem_hw_info.addr_ext[DMA_TYPE_MGW].plane2);

		SYSFS_PRINT(len, buf, "[Ext](DMA0) plane(0,1,2):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.addr_ext[DMA_TYPE_DMA0].plane0,
			mem_hw_info.addr_ext[DMA_TYPE_DMA0].plane1,
			mem_hw_info.addr_ext[DMA_TYPE_DMA0].plane2);

		SYSFS_PRINT(len, buf, "[Ext](DMA1) plane(0,1,2):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.addr_ext[DMA_TYPE_DMA1].plane0,
			mem_hw_info.addr_ext[DMA_TYPE_DMA1].plane1,
			mem_hw_info.addr_ext[DMA_TYPE_DMA1].plane2);

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf,
				"[Multi](MGW) window id(%d) plane(0,1,2):(0x%x, 0x%x, 0x%x)",
				index,
				mem_hw_info.addr_multi[index].plane0,
				mem_hw_info.addr_multi[index].plane1,
				mem_hw_info.addr_multi[index].plane2);
		}

		SYSFS_PRINT(len, buf, "\n-----    (Memory Config)   -----");
		SYSFS_PRINT(len, buf, "[Ext](MGW, DMA0, DMA1):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.mem_config_ext[DMA_TYPE_MGW],
			mem_hw_info.mem_config_ext[DMA_TYPE_DMA0],
			mem_hw_info.mem_config_ext[DMA_TYPE_DMA1]);

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf, "[Multi](MGW) window id(%d):(0x%x)",
				index, mem_hw_info.mem_config_multi[index]);
		}

		SYSFS_PRINT(len, buf, "\n-----     (Line Offset)    -----");
		SYSFS_PRINT(len, buf, "[Ext](MGW, DMA0, DMA1):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.line_offset_ext[DMA_TYPE_MGW],
			mem_hw_info.line_offset_ext[DMA_TYPE_DMA0],
			mem_hw_info.line_offset_ext[DMA_TYPE_DMA1]);

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf, "[Multi](MGW) window id(%d):(0x%x)",
				index, mem_hw_info.line_offset_multi[index]);
		}

		SYSFS_PRINT(len, buf, "\n-----    (Frame Offset)    -----");
		SYSFS_PRINT(len, buf, "[Ext](MGW, DMA0, DMA1):(0x%x, 0x%x, 0x%x)",
			mem_hw_info.frame_offset_ext[DMA_TYPE_MGW],
			mem_hw_info.frame_offset_ext[DMA_TYPE_DMA0],
			mem_hw_info.frame_offset_ext[DMA_TYPE_DMA1]);

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf, "[Multi](MGW) window id(%d):(0x%x)",
				index, mem_hw_info.frame_offset_multi[index]);
		}

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf, "rw diff window id(%d):(%d)",
				index, mem_hw_info.rw_diff[index]);
		}

		for (index = 0; index < WINDOW_NUM_MAX; index++) {
			SYSFS_PRINT(len, buf, "read pointer: window id(%d):(%d)",
				index, mem_hw_info.read_ptr[index]);
		}

	} else if (pctx->chip_series == CHIP_SERIES_LEGACY) {/*2.0*/
		SYSFS_PRINT(len, buf, "It's Legacy Chip!");
	} else
		ERR("invalid chip version:%d\n", pctx->chip_series);

	return len;
}

static int window_info_2_0_show(
	struct mtk_video_context *pctx_video,
	char *buf, int len)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_dbg_info win_dbg_info;
	int i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = pctx_video_to_kms(pctx_video);
	memset(&win_dbg_info, 0, sizeof(win_dbg_info));

	win_dbg_info.disp_win = ZALLOC(sizeof(struct window));
	if (!win_dbg_info.disp_win)
		return -ENOMEM;

	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		win_dbg_info.planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);
		drv_hwreg_render_display_get_disp_window(i, &win_dbg_info.disp_win[i]);
		drv_hwreg_render_display_get_video_mute(&win_dbg_info.mute_en[i]);
	}

	len += snprintf(buf + len, PAGE_SIZE - len, "\n=== window base size report ===\n");
#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	for (i = 0; i < pctx->hw_caps.window_num; i++) \
		len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO("video window", "%23d  | ", i);
	SHOW_WIN_INFO("mute status", "%23d  | ", win_dbg_info.mute_en[i]);
	SHOW_WIN_INFO("disp window", "(%4lld, %4lld, %4lld, %4lld) | ",
		win_dbg_info.disp_win[i].x, win_dbg_info.disp_win[i].y,
		win_dbg_info.disp_win[i].w, win_dbg_info.disp_win[i].h);
#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", "video_plane_type");
	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		if (IS_DRM_PLANE_TYPE_VALID(win_dbg_info.planeType[i])) {
			switch (win_dbg_info.planeType[i]) {
			case MTK_VPLANE_TYPE_MEMC:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%24s | ", "MEMC(SRC0)");
			break;
			case MTK_VPLANE_TYPE_NONE:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%24s | ", "BYPASS(SRC0)");
			break;
			default:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "unknown source");
			}
		}
	}
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");

	FREE(win_dbg_info.disp_win);
	return len;
}

static int video_blending_info_legacy_v1_show(
	struct mtk_video_context *pctx_video,
	char *buf, int len)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct hwreg_vb_info vb_info;
	struct hwreg_vb_pre_info *vb_pre_info;
	struct hwreg_vb_out_info vb_out_info;
	char *layer_name[NUMBER_2] = {"Layer 0", "Layer 1"};
	char *source_name[NUMBER_2] = {"SRC 0", "SRC 1"};
	int i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = pctx_video_to_kms(pctx_video);
	vb_pre_info = ZALLOC(sizeof(struct hwreg_vb_pre_info) * NUMBER_2);
	memset(&vb_info, 0, sizeof(struct hwreg_vb_info));
	memset(&vb_out_info, 0, sizeof(struct hwreg_vb_out_info));
	if (vb_pre_info == NULL)
		return -ENOMEM;

	// get 2-source info
	if (vb_pre_info != NULL) {
		for (i = 0; i < NUMBER_2; i++) {
			vb_pre_info[i].source = i;
			drv_hwreg_render_display_get_vb_pre_info(&vb_pre_info[i]);
		}
	}

	drv_hwreg_render_display_get_vb_info(&vb_info);
	drv_hwreg_render_display_get_vb_out_info(&vb_out_info);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending pre layer info ===\n"
		"%22s | %23s  | %23s  |\n",
		"layer idx", "Layer 0", "Layer 1");

#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	for (i = 0; i < NUMBER_2; i++) \
		len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)

	SHOW_WIN_INFO("layer enable", "%23d  | ", (!vb_pre_info[i].layer_off));
	SHOW_WIN_INFO("src idx", "%23s  | ", source_name[vb_pre_info[i].source_sel]);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending pre size report ===\n"
		"%22s | %23d\n"
		"%22s | %23s  | %23s  |\n",
		"vb pre enable", (!vb_pre_info[0].is_bypass),
		"source idx", "SRC0", "SRC1");

	SHOW_WIN_INFO("layer idx", "%23s  | ", layer_name[vb_pre_info[i].layer_sel]);
	SHOW_WIN_INFO("pre_insert enable", "%23d  | ", vb_pre_info[i].pre_insert.pre_insert_en);
	SHOW_WIN_INFO("pre_insert disp size", "(%4lld, %4lld, %4lld, %4lld) | ",
			vb_pre_info[i].pre_insert.pre_insert_win_rect.h_start,
			vb_pre_info[i].pre_insert.pre_insert_win_rect.h_end,
			vb_pre_info[i].pre_insert.pre_insert_win_rect.v_start,
			vb_pre_info[i].pre_insert.pre_insert_win_rect.v_end);

	SHOW_WIN_INFO("pre_insert frame size", "            (%4d, %4d) | ",
			vb_pre_info[i].pre_insert.frame_h_size,
			vb_pre_info[i].pre_insert.frame_v_size);
	SHOW_WIN_INFO("lb enable", "%23d  | ", vb_pre_info[i].lb_info.lb_en);
	len += snprintf(buf + len, PAGE_SIZE - len,
		"%22s | %23d  |              No support  |\n", "lb before hvsp",
		vb_pre_info[0].lb_info.lb_before_hvsp);

	SHOW_WIN_INFO("lb h_protect en", "%23d  | ", vb_pre_info[i].lb_info.lb_h_protect_en);
	SHOW_WIN_INFO("lb h_protect size", "%23llu  | ", vb_pre_info[i].lb_info.lb_size);

#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending layer info ===\n"
		"%22s | %23s  |\n"
		"%22s | %23d  |\n"
		"%22s | %23s  |\n"
		"\n=== video blending size report ===\n"
		"%22s | %23s  |\n"
		"%22s | %23s  |\n"
		"%22s | %23d  |\n"
		"%22s | (%4lld, %4lld, %4lld, %4lld) |\n"
		"%22s | %23d  |\n"
		"%22s | (%4lld, %4lld, %4lld, %4lld) |\n"
		"%22s |             (%4d, %4d) |\n",
		"layer idx", "Layer 0",
		"layer enable", (!vb_info.layer_off),
		"src idx", source_name[vb_info.source_sel],
		"src idx", "SRC0",
		"layer idx", layer_name[vb_info.layer_sel],
		"clip enable", vb_info.clip.clip_en,
		"clip size",
		vb_info.clip.clip_win_rect.h_start, vb_info.clip.clip_win_rect.h_end,
		vb_info.clip.clip_win_rect.v_start, vb_info.clip.clip_win_rect.v_end,
		"pre_insert enable", vb_info.pre_insert.pre_insert_en,
		"pre_insert disp size",
		vb_info.pre_insert.pre_insert_win_rect.h_start,
		vb_info.pre_insert.pre_insert_win_rect.h_end,
		vb_info.pre_insert.pre_insert_win_rect.v_start,
		vb_info.pre_insert.pre_insert_win_rect.v_end,
		"pre_insert frame size",
		vb_info.pre_insert.frame_h_size, vb_info.pre_insert.frame_v_size);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== after blending size report ===\n"
		"%22s | %23d  |\n"
		"%22s | %23d  |\n"
		"%22s | %23d  |\n"
		"%22s | (%4d, %4d, %4d, %4d) |\n"
		"%22s |             (%4d, %4d) |\n",
		"blend out mute enable", vb_out_info.blend_out.mute_en,
		"blend out h_size", vb_out_info.blend_out.h_size,
		"fohvsp bypass", vb_out_info.fohvsp_info.is_bypass,
		"fohvsp scl size",
		vb_out_info.fohvsp_info.input_w, vb_out_info.fohvsp_info.input_h,
		vb_out_info.fohvsp_info.output_w, vb_out_info.fohvsp_info.output_h,
		"disp lb size",
		vb_out_info.disp_lb_h_size, vb_out_info.disp_lb_v_size);

	len += snprintf(buf + len, PAGE_SIZE - len, "\n");

	FREE(vb_pre_info);
	return len;
}

static int video_blending_info_legacy_v0_show(
	struct mtk_video_context *pctx_video,
	char *buf, int len)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct hwreg_vb_info *vb_info;
	struct hwreg_vb_out_info vb_out_info;
	char *layer_name[NUMBER_2] = {"Layer 0", "Layer 1"};
	char *source_name[NUMBER_2] = {"SRC 0", "SRC 1"};
	int i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = pctx_video_to_kms(pctx_video);
	vb_info = ZALLOC(sizeof(struct hwreg_vb_info) * NUMBER_2);
	memset(&vb_out_info, 0, sizeof(struct hwreg_vb_out_info));
	if (vb_info == NULL)
		return -ENOMEM;

	// get 2-source info
	for (i = 0; i < NUMBER_2; i++) {
		vb_info[i].source = i;
		drv_hwreg_render_display_get_vb_info(&vb_info[i]);
	}

	drv_hwreg_render_display_get_vb_out_info(&vb_out_info);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending layer info ===\n"
		"%22s | %23s  | %23s  |\n",
		"layer idx", "Layer 0", "Layer 1");

#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	for (i = 0; i < NUMBER_2; i++) \
		len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)

	SHOW_WIN_INFO("layer enable", "%23d  | ", (!vb_info[i].layer_off));
	SHOW_WIN_INFO("src idx", "%23s  | ", source_name[vb_info[i].source_sel]);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending size report ===\n"
		"%22s | %23s  | %23s  |\n",
		"source idx", "SRC0", "SRC1");

	SHOW_WIN_INFO("layer idx", "%23s  | ", layer_name[vb_info[i].layer_sel]);

	SHOW_WIN_INFO("clip enable", "%23d  | ", vb_info[i].pre_insert.pre_insert_en);
	SHOW_WIN_INFO("clip size", "(%4lld, %4lld, %4lld, %4lld) | ",
			vb_info[i].clip.clip_win_rect.h_start,
			vb_info[i].clip.clip_win_rect.h_end,
			vb_info[i].clip.clip_win_rect.v_start,
			vb_info[i].clip.clip_win_rect.v_end);
	SHOW_WIN_INFO("pre_insert enable", "%23d  | ", vb_info[i].pre_insert.pre_insert_en);
	SHOW_WIN_INFO("pre_insert disp size", "(%4lld, %4lld, %4lld, %4lld) | ",
			vb_info[i].pre_insert.pre_insert_win_rect.h_start,
			vb_info[i].pre_insert.pre_insert_win_rect.h_end,
			vb_info[i].pre_insert.pre_insert_win_rect.v_start,
			vb_info[i].pre_insert.pre_insert_win_rect.v_end);

	SHOW_WIN_INFO("pre_insert frame size", "            (%4d, %4d) | ",
			vb_info[i].pre_insert.frame_h_size,
			vb_info[i].pre_insert.frame_v_size);

#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== after blending size report ===\n"
		"%22s | %23d  |\n"
		"%22s | %23d  |\n"
		"%22s |             (%4d, %4d) |\n",
		"blend out mute enable", vb_out_info.blend_out.mute_en,
		"blend out h_size", vb_out_info.blend_out.h_size,
		"disp lb size", vb_out_info.disp_lb_h_size, vb_out_info.disp_lb_v_size);

	len += snprintf(buf + len, PAGE_SIZE - len, "\n");

	FREE(vb_info);
	return len;
}

static int window_info_1_0_show(
	struct mtk_video_context *pctx_video,
	char *buf, int len)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_dbg_info win_dbg_info;
	int i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = pctx_video_to_kms(pctx_video);
	memset(&win_dbg_info, 0, sizeof(win_dbg_info));

	win_dbg_info.crop_win =
		ZALLOC(sizeof(struct window) * MAX_WINDOW_NUM);
	win_dbg_info.disp_win =
		ZALLOC(sizeof(struct window) * MAX_WINDOW_NUM);

	if (!win_dbg_info.crop_win || !win_dbg_info.disp_win) {
		if (win_dbg_info.crop_win) {
			FREE(win_dbg_info.crop_win);
			win_dbg_info.crop_win = NULL;
		}
		if (win_dbg_info.disp_win) {
			FREE(win_dbg_info.disp_win);
			win_dbg_info.disp_win = NULL;
		}
		return -ENOMEM;
	}

	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		win_dbg_info.planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);
		drv_hwreg_render_display_get_rw_diff(i, &win_dbg_info.rwdiff[i]);
		drv_hwreg_render_display_get_disp_window(i, &win_dbg_info.disp_win[i]);
		drv_hwreg_render_display_get_crop_window(
				win_dbg_info.planeType[i],
				pctx_video->plane_num[MTK_VPLANE_TYPE_MULTI_WIN],
				i, &win_dbg_info.crop_win[i]);

		if ((win_dbg_info.planeType[i] == MTK_VPLANE_TYPE_MULTI_WIN) &&
			(pctx_video->plane_num[MTK_VPLANE_TYPE_MULTI_WIN] > 1)) {
			// w/h of cropWin is the same as dispWin
			win_dbg_info.crop_win[i].w = win_dbg_info.disp_win[i].w;
			win_dbg_info.crop_win[i].h = win_dbg_info.disp_win[i].h;
		}

		drv_hwreg_render_display_get_post_fill_mute_screen(
				win_dbg_info.planeType[i], i, &win_dbg_info.mute_en[i]);
	}

	SYSFS_PRINT(len, buf, "=== window base size report ===");
#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	for (i = 0; i < pctx->hw_caps.window_num; i++) { \
		if (IS_DRM_PLANE_TYPE_VALID(win_dbg_info.planeType[i])) \
			len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	} \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO("video window", "%23d  | ", i);
	SHOW_WIN_INFO("mute status", "%23d  | ", win_dbg_info.mute_en[i]);
	SHOW_WIN_INFO("rwdiff", "%23d  | ", win_dbg_info.rwdiff[i]);
	SHOW_WIN_INFO("crop window", "(%4lld, %4lld, %4lld, %4lld) | ",
		win_dbg_info.crop_win[i].x, win_dbg_info.crop_win[i].y,
		win_dbg_info.crop_win[i].w, win_dbg_info.crop_win[i].h);
	SHOW_WIN_INFO("disp window", "(%4lld, %4lld, %4lld, %4lld) | ",
		win_dbg_info.disp_win[i].x, win_dbg_info.disp_win[i].y,
		win_dbg_info.disp_win[i].w, win_dbg_info.disp_win[i].h);
#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", "video_plane_type");
	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		if (IS_DRM_PLANE_TYPE_VALID(win_dbg_info.planeType[i])) {
			switch (win_dbg_info.planeType[i]) {
			case MTK_VPLANE_TYPE_NONE:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "MEMC(SRC0)");
			break;
			case MTK_VPLANE_TYPE_MEMC:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "MEMC(SRC0)");
			break;
			case MTK_VPLANE_TYPE_MULTI_WIN:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "MGW(SRC1)");
			break;
			case MTK_VPLANE_TYPE_SINGLE_WIN1:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "DMA0(SRC2)");
			break;
			case MTK_VPLANE_TYPE_SINGLE_WIN2:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "DMA1(SRC3)");
			break;
			default:
				len += snprintf(buf + len, PAGE_SIZE - len,
					"%23s  | ", "NONE");
			}
		}
	}
	len += snprintf(buf + len, PAGE_SIZE - len, "\n\n");

	FREE(win_dbg_info.crop_win);
	FREE(win_dbg_info.disp_win);

	return len;
}

static int video_blending_info_main_ts_show(
	struct mtk_video_context *pctx_video,
	char *buf, int len)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct hwreg_vb_info *vb_info;
	struct hwreg_vb_out_info vb_out_info;
	char *layer_name[NUMBER_4] = {"Layer 0", "Layer 1", "Layer 2", "Layer 3"};
	char *source_name[NUMBER_4] = {"SRC 0", "SRC 1", "SRC 2", "SRC 3"};
	int i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = pctx_video_to_kms(pctx_video);
	vb_info = ZALLOC(sizeof(struct hwreg_vb_info) * NUMBER_4);
	if (vb_info == NULL)
		return -ENOMEM;

	memset(&vb_out_info, 0, sizeof(struct hwreg_vb_out_info));

	// get 4-source info
	for (i = 0; i < NUMBER_4; i++) {
		vb_info[i].source = i;
		drv_hwreg_render_display_get_vb_info(&vb_info[i]);
	}
	drv_hwreg_render_display_get_vb_out_info(&vb_out_info);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n=== video blending layer info ===\n"
		"%22s | %23s  | %23s  | %23s  | %23s  |\n",
		"layer idx", "Layer 0", "Layer 1", "Layer 2", "Layer 3");

#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	for (i = 0; i < NUMBER_4; i++) { \
		len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	} \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO("layer enable", "%23d  | ", (!vb_info[i].layer_off));
	SHOW_WIN_INFO("src idx", "%23s  | ", source_name[vb_info[i].source_sel]);

	SYSFS_PRINT(len, buf, "\n=== video blending size report ===");

	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | %23s  | %23s  | %23s  | %23s  |\n",
		"src idx", "SRC0", "SRC1", "SRC2", "SRC3");

	SHOW_WIN_INFO("layer idx", "%23s  | ", layer_name[vb_info[i].layer_sel]);
	SHOW_WIN_INFO("lb enable", "%23d  | ", vb_info[i].lb_info.lb_en);
	SHOW_WIN_INFO("lb size", "%23llu  | ", vb_info[i].lb_info.lb_size);
	SHOW_WIN_INFO("clip enable", "%23d  | ", vb_info[i].lb_info.lb_en);
	SHOW_WIN_INFO("clip size", "(%4lld, %4lld, %4lld, %4lld) | ",
		vb_info[i].clip.clip_win_rect.h_start, vb_info[i].clip.clip_win_rect.h_end,
		vb_info[i].clip.clip_win_rect.v_start, vb_info[i].clip.clip_win_rect.v_end);
	SHOW_WIN_INFO("pre_insert enable", "%23d  | ", vb_info[i].pre_insert.pre_insert_en);
	SHOW_WIN_INFO("pre_insert disp size", "(%4lld, %4lld, %4lld, %4lld) | ",
		vb_info[i].pre_insert.pre_insert_win_rect.h_start,
		vb_info[i].pre_insert.pre_insert_win_rect.h_end,
		vb_info[i].pre_insert.pre_insert_win_rect.v_start,
		vb_info[i].pre_insert.pre_insert_win_rect.v_end);
	SHOW_WIN_INFO("pre_insert frame size", "            (%4d, %4d) | ",
		vb_info[i].pre_insert.frame_h_size,
		vb_info[i].pre_insert.frame_v_size);
	SHOW_WIN_INFO("post_fill enable", "%23d  | ", vb_info[i].post_fill.post_fill_en);
	SHOW_WIN_INFO("post_fill win enable", "%#23x  | ", vb_info[i].post_fill.post_fill_win_en);
	SHOW_WIN_INFO("post_fill frame size", "            (%4d, %4d) | ",
		vb_info[i].post_fill.frame_h_size,
		vb_info[i].post_fill.frame_v_size);
#undef SHOW_WIN_INFO

	SYSFS_PRINT(len, buf, "\n=== after blending size report ===");
#define SHOW_WIN_INFO(title, fmt, ...) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%22s | ", title); \
	len += snprintf(buf + len, PAGE_SIZE - len, fmt, ##__VA_ARGS__); \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO("blend out mute enable", "%23d  | ", vb_out_info.blend_out.mute_en);
	SHOW_WIN_INFO("blend out h_size", "%23d  | ", vb_out_info.blend_out.h_size);
	SHOW_WIN_INFO("fohvsp bypass", "%23d  | ", vb_out_info.fohvsp_info.is_bypass);
	SHOW_WIN_INFO("fohvsp scl size", "(%4d, %4d, %4d, %4d) | ",
		vb_out_info.fohvsp_info.input_w,
		vb_out_info.fohvsp_info.input_h,
		vb_out_info.fohvsp_info.output_w,
		vb_out_info.fohvsp_info.output_h);
	SHOW_WIN_INFO("disp lb size", "%23d  | ", vb_out_info.disp_lb_h_size);
#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len, "\n\n");

	FREE(vb_info);
	return len;
}

static ssize_t render_size_report_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version;
	enum DRM_VB_VERSION_E vb_version = 0;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;
	vb_version = (enum DRM_VB_VERSION_E)
			video_hw_version->video_blending;

	if (pctx->chip_series == CHIP_SERIES_MAIN_TS) {/*1.0*/
		len = window_info_1_0_show(pctx_video, buf, len);
		len = video_blending_info_main_ts_show(pctx_video, buf, len);
	} else if (pctx->chip_series == CHIP_SERIES_LEGACY) {/*2.0*/

		len = window_info_2_0_show(pctx_video, buf, len);

		if (vb_version == EN_VB_VERSION_LEGACY_V1)
			len = video_blending_info_legacy_v1_show(pctx_video, buf, len);
		else if (vb_version == EN_VB_VERSION_LEGACY_V0)
			len = video_blending_info_legacy_v0_show(pctx_video, buf, len);
	} else
		ERR("invalid chip version:%d\n", pctx->chip_series);

	return ((len < 0) || (len > PAGE_SIZE)) ? -EINVAL : len;
}

static ssize_t video_plane_prop_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	int len = 0;

	SYSFS_PRINT(len, buf, "=== video plane prop input record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_PLANE_PROP_INPUT, buf, &len);

	return len;
}

static ssize_t enable_pqu_ds_size_ctl_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int ret = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	ret = snprintf(buf, PAGE_SIZE, "%d\n", pctx->sw_caps.enable_pqu_ds_size_ctl);

	return ((ret < 0) || (ret > PAGE_SIZE)) ? -EINVAL : ret;
}

static ssize_t enable_pqu_ds_size_ctl_store(
	struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct mtk_tv_kms_context *pctx = dev_get_drvdata(dev);
	unsigned long val = 0;
	int err;

	err = kstrtoul(buf, 0, &val);
	if (err)
		return err;

	pctx->sw_caps.enable_pqu_ds_size_ctl = val;

	return count;
}

static int _debug_info_1_0_show(
	struct mtk_video_context *pctx_video,
	struct mtk_video_dbg_info win_dbg_info,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0, i = 0;

	if ((pctx_video == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = pctx_video_to_kms(pctx_video);

	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		win_dbg_info.planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);
		drv_hwreg_render_display_get_rw_diff(i, &win_dbg_info.rwdiff[i]);
		drv_hwreg_render_display_get_disp_window(i, &win_dbg_info.disp_win[i]);
		drv_hwreg_render_display_get_crop_window(
				win_dbg_info.planeType[i],
				pctx_video->plane_num[MTK_VPLANE_TYPE_MULTI_WIN],
				i, &win_dbg_info.crop_win[i]);

		if ((win_dbg_info.planeType[i] == MTK_VPLANE_TYPE_MULTI_WIN) &&
			(pctx_video->plane_num[MTK_VPLANE_TYPE_MULTI_WIN] > 1)) {
			// w/h of cropWin is the same as dispWin
			win_dbg_info.crop_win[i].w = win_dbg_info.disp_win[i].w;
			win_dbg_info.crop_win[i].h = win_dbg_info.disp_win[i].h;
		}

		drv_hwreg_render_display_get_post_fill_mute_screen(
				win_dbg_info.planeType[i], i, &win_dbg_info.mute_en[i]);
	}

#define SHOW_WIN_INFO(title, elem) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%s |", title); \
	for (i = 0; i < pctx->hw_caps.window_num; i++) { \
		len += snprintf(buf + len, PAGE_SIZE - len, " %5lld |", (uint64_t)(elem)); \
	} \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO(" video window          ", i);
	SHOW_WIN_INFO(" crop_x           ", win_dbg_info.crop_win[i].x);
	SHOW_WIN_INFO(" crop_y           ", win_dbg_info.crop_win[i].y);
	SHOW_WIN_INFO(" crop_w           ", win_dbg_info.crop_win[i].w);
	SHOW_WIN_INFO(" crop_h           ", win_dbg_info.crop_win[i].h);
	SHOW_WIN_INFO(" disp_x           ", win_dbg_info.disp_win[i].x);
	SHOW_WIN_INFO(" disp_y           ", win_dbg_info.disp_win[i].y);
	SHOW_WIN_INFO(" disp_w           ", win_dbg_info.disp_win[i].w);
	SHOW_WIN_INFO(" disp_h           ", win_dbg_info.disp_win[i].h);
	SHOW_WIN_INFO(" rwdiff           ", win_dbg_info.rwdiff[i]);
	SHOW_WIN_INFO(" mute_en          ", win_dbg_info.mute_en[i]);
#undef SHOW_WIN_INFO

	len += snprintf(buf + len, PAGE_SIZE - len, " video_plane_type       |");
	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		switch (win_dbg_info.planeType[i]) {
		case MTK_VPLANE_TYPE_MEMC:
			len += snprintf(buf + len, PAGE_SIZE - len, " MEMC |");
		break;
		case MTK_VPLANE_TYPE_NONE:
			len += snprintf(buf + len, PAGE_SIZE - len, " NONE |");
		break;
		case MTK_VPLANE_TYPE_MULTI_WIN:
			len += snprintf(buf + len, PAGE_SIZE - len, " MGW  |");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN1:
			len += snprintf(buf + len, PAGE_SIZE - len, " DMA0 |");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN2:
			len += snprintf(buf + len, PAGE_SIZE - len, " DMA1 |");
		break;
		default:
			len += snprintf(buf + len, PAGE_SIZE - len, " %4d |",
					win_dbg_info.planeType[i]);
		}
	}
	len += snprintf(buf + len, PAGE_SIZE - len, "\n\n");

	return len;
}

static int  _vb_info_2_0_show(
	struct mtk_video_context *pctx_video,
	char *buf,
	int at)
{
	struct video_vb_info *vb_info;

	vb_info = ZALLOC(sizeof(struct video_vb_info));
	if (vb_info == NULL)
		return -EINVAL;

	//drv_hwreg_render_display_get_layer_control(
		//MTK_VPLANE_TYPE_MEMC, &(vb_info->zpos[MTK_VPLANE_TYPE_MEMC]),
		//&(vb_info->layer_en[MTK_VPLANE_TYPE_MEMC]));
	drv_hwreg_render_display_get_pre_insert_enable(
		0, &(vb_info->pre_insert_en[0]));
	drv_hwreg_render_display_get_pre_insert_image_size(
		0, &(vb_info->pre_insert_win[0]));
	vb_info->pre_insert_w[0] =
		(&(vb_info->pre_insert_win[0].h_end) -
		&(vb_info->pre_insert_win[0].h_start)) + 1;
	vb_info->pre_insert_h[0] =
		(&(vb_info->pre_insert_win[0].v_end) -
		&(vb_info->pre_insert_win[0].v_start)) + 1;
	drv_hwreg_render_display_get_pre_insert_frame_size(
		0, &(vb_info->pre_insert_htt[0]), &(vb_info->pre_insert_vtt[0]));

	drv_hwreg_render_display_get_blend_out_mute(&(vb_info->vb_out_mute));

	at += sysfs_emit_at(buf, at, "\t%s\n", "----video blending info----");
	//at += sysfs_emit_at(buf, at, "\tzpos : %d\n",
		//	vb_info->zpos[MTK_VPLANE_TYPE_MEMC]);
	//at += sysfs_emit_at(buf, at, "\tlayer_en : %d\n",
			//vb_info->layer_en[MTK_VPLANE_TYPE_MEMC]);
	at += sysfs_emit_at(buf, at, "\tpre_insert_en : %d\n",
			vb_info->pre_insert_en[0]);
	at += sysfs_emit_at(buf, at, "\tpre_insert_w : %d\n",
			vb_info->pre_insert_w[0]);
	at += sysfs_emit_at(buf, at, "\tpre_insert_h : %d\n",
			vb_info->pre_insert_h[0]);
	at += sysfs_emit_at(buf, at, "\tpre_insert_frame_size_h : %d\n",
			vb_info->pre_insert_htt[0]);
	at += sysfs_emit_at(buf, at, "\tpre_insert_frame_size_v : %d\n",
			vb_info->pre_insert_vtt[0]);
	//at += sysfs_emit_at(buf, at, "\tvb_out_mute : %d\n",
			//vb_info->vb_out_mute);

	FREE(vb_info);
	return at;
}

static int _debug_info_2_0_show(
	struct mtk_video_context *pctx_video,
	struct mtk_video_hw_version *video_hw_version,
	struct mtk_video_dbg_info win_dbg_info,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	enum DRM_VB_VERSION_E vb_version = 0;
	int idx = 0, at = 0;

	if ((pctx_video == NULL) || (video_hw_version == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = pctx_video_to_kms(pctx_video);

	vb_version = (enum DRM_VB_VERSION_E)
		video_hw_version->video_blending;

	for (idx = 0; idx < pctx->hw_caps.window_num; idx++) {
		win_dbg_info.planeType[idx] = VIDEO_CUR_PLANE_TYPE(pctx_video, idx);
		drv_hwreg_render_display_get_disp_window(idx, &win_dbg_info.disp_win[idx]);
		drv_hwreg_render_display_get_video_mute(&win_dbg_info.mute_en[idx]);
	}

	for (idx = 0; idx < pctx->hw_caps.window_num; idx++) {
		at += sysfs_emit_at(buf, at, "\tvideo window: %d\n", idx);

		at += sysfs_emit_at(buf, at, "\tdisp_x : %llu\n",
			win_dbg_info.disp_win[idx].x);
		at += sysfs_emit_at(buf, at, "\tdisp_y : %llu\n",
			win_dbg_info.disp_win[idx].y);
		at += sysfs_emit_at(buf, at, "\tdisp_w : %llu\n",
			win_dbg_info.disp_win[idx].w);
		at += sysfs_emit_at(buf, at, "\tdisp_h : %llu\n",
			win_dbg_info.disp_win[idx].h);
		at += sysfs_emit_at(buf, at, "\tmute_en : %d\n",
			win_dbg_info.mute_en[idx]);

		switch (win_dbg_info.planeType[idx]) {
		case MTK_VPLANE_TYPE_NONE:
			at += sysfs_emit_at(buf, at, "\tvideo_play_type %s\n",
				"NONE");
		break;
		case MTK_VPLANE_TYPE_MEMC:
			at += sysfs_emit_at(buf, at, "\tvideo_play_type %s\n",
				"MEMC");
		break;
		default:
			at += sysfs_emit_at(buf, at, "\tvideo_play_type %s\n",
				"NONE");
		}
	}

	if (IS_VB_TS(vb_version)) {
		// need implement.
	} else if (vb_version == EN_VB_VERSION_LEGACY_V0) {
		at = _vb_info_2_0_show(pctx_video, buf, at);
	} else if (vb_version == EN_VB_VERSION_LEGACY_V1) {
		// pre-vb
		//vb
		at = _vb_info_2_0_show(pctx_video, buf, at);
	}

	return at;
}

static ssize_t debug_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	//struct mtk_video_hw_version *video_hw_version = NULL;
	struct mtk_video_dbg_info win_dbg_info;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_mute_hw_ver = (&pctx_video->video_hw_ver)->video_mute;

	memset(&win_dbg_info, 0, sizeof(win_dbg_info));

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB) {/*1.0*/
		win_dbg_info.crop_win =
			vmalloc(sizeof(struct window) * MAX_WINDOW_NUM);
		win_dbg_info.disp_win =
			vmalloc(sizeof(struct window) * MAX_WINDOW_NUM);
	} else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB) {/*2.0*/
		win_dbg_info.disp_win = vmalloc(sizeof(struct window));
	}

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB)
		len = _debug_info_1_0_show(pctx_video, win_dbg_info, buf);
	else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB) {
		len = _debug_info_2_0_show(pctx_video, &pctx_video->video_hw_ver,
			win_dbg_info, buf);
	} else
		ERR("invalid mute version:%d\n", video_mute_hw_ver);

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB)/*1.0*/
		vfree(win_dbg_info.crop_win);
	vfree(win_dbg_info.disp_win);

	return ((len < 0) || (len > PAGE_SIZE)) ? -EINVAL : len;
}

static int _debug_info_plane_type_1_0_show(
	struct mtk_video_context *pctx_video,
	char *buf)
{
	int i = 0, len = 0;
	struct mtk_video_dbg_info_plane dbgInfo;

	memset(&dbgInfo, 0, sizeof(dbgInfo));
	dbgInfo.scl_inputWin = ZALLOC(sizeof(struct window) * MTK_VPLANE_TYPE_MAX);
	dbgInfo.scl_outputWin = ZALLOC(sizeof(struct window) * MTK_VPLANE_TYPE_MAX);
	if (!dbgInfo.scl_inputWin || !dbgInfo.scl_outputWin) {
		if (dbgInfo.scl_inputWin) {
			FREE(dbgInfo.scl_inputWin);
			dbgInfo.scl_inputWin = NULL;
		}
		if (dbgInfo.scl_outputWin) {
			FREE(dbgInfo.scl_outputWin);
			dbgInfo.scl_outputWin = NULL;
		}
		return -EINVAL;
	}

	for (i = 0; i < MTK_VPLANE_TYPE_MAX; i++) {
		drv_hwreg_render_display_get_scaling_info(
			i, &dbgInfo.hvspbypass[i],
			&dbgInfo.scl_inputWin[i], &dbgInfo.scl_outputWin[i]);
		//drv_hwreg_render_display_get_layer_control(
			//i, &dbgInfo.zpos[i], &dbgInfo.layerEn[i]);

		dbgInfo.mgwPlaneNum = pctx_video->plane_num[MTK_VPLANE_TYPE_MULTI_WIN];
		drv_hwreg_render_display_get_thin_read_factor(
			i, dbgInfo.mgwPlaneNum, &dbgInfo.thinReadFactor[i]);
		drv_hwreg_render_display_get_pre_insert_enable(
			i, &dbgInfo.preInsert_en[i]);
		drv_hwreg_render_display_get_pre_insert_image_size(
			i, &dbgInfo.preInsert_win[i]);
		dbgInfo.preInsert_w[i] =
			((dbgInfo.preInsert_win[i].h_end) -
			(dbgInfo.preInsert_win[i].h_start)) + 1;
		dbgInfo.preInsert_h[i] =
			((dbgInfo.preInsert_win[i].v_end) -
			(dbgInfo.preInsert_win[i].v_start)) + 1;
		drv_hwreg_render_display_get_pre_insert_frame_size(
			i, &dbgInfo.preInsert_htt[i], &dbgInfo.preInsert_vtt[i]);
		drv_hwreg_render_display_get_post_fill_enable(
			i, &dbgInfo.postFill_en[i]);
		drv_hwreg_render_display_get_post_fill_frame_size(
			i, &dbgInfo.postFill_htt[i], &dbgInfo.postFill_vtt[i]);
		drv_hwreg_render_display_get_post_fill_window_argb(
			i, &dbgInfo.postFill_argb[i]);
	}

#define SHOW_PLANE_TYPE_INFO(title, elem) do {							\
	len += snprintf(buf + len, PAGE_SIZE - len, "%s |", title);				\
	for (i = MTK_VPLANE_TYPE_MEMC; i < MTK_VPLANE_TYPE_MAX; i++) {\
		len += snprintf(buf + len, PAGE_SIZE - len, " %5lld |", (uint64_t)(elem));	\
	}											\
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");					\
} while (0)
	len += snprintf(buf + len, PAGE_SIZE - len,
			 "    video plane type    |  MEMC |   MGW |  DMA0 |  DMA1 |\n");
	//SHOW_PLANE_TYPE_INFO(" zpos                  ", dbgInfo.zpos[i]);
	//SHOW_PLANE_TYPE_INFO(" layer_en              ", dbgInfo.layerEn[i]);
	SHOW_PLANE_TYPE_INFO(" thin read factor        ", dbgInfo.thinReadFactor[i]);
	SHOW_PLANE_TYPE_INFO(" pre insert enanle          ", dbgInfo.preInsert_en[i]);
	SHOW_PLANE_TYPE_INFO(" pre insert h start     ", dbgInfo.preInsert_win[i].h_start);
	SHOW_PLANE_TYPE_INFO(" pre insert h end       ", dbgInfo.preInsert_win[i].h_end);
	SHOW_PLANE_TYPE_INFO(" pre insert v start     ", dbgInfo.preInsert_win[i].v_start);
	SHOW_PLANE_TYPE_INFO(" pre insert v end       ", dbgInfo.preInsert_win[i].v_end);
	SHOW_PLANE_TYPE_INFO(" pre insert width       ", dbgInfo.preInsert_w[i]);
	SHOW_PLANE_TYPE_INFO(" pre insert height      ", dbgInfo.preInsert_h[i]);
	SHOW_PLANE_TYPE_INFO(" pre insert htt         ", dbgInfo.preInsert_htt[i]);
	SHOW_PLANE_TYPE_INFO(" pre insert vtt       ", dbgInfo.preInsert_vtt[i]);
	SHOW_PLANE_TYPE_INFO(" post fill enable     ", dbgInfo.postFill_en[i]);
	SHOW_PLANE_TYPE_INFO(" post fill htt        ", dbgInfo.postFill_htt[i]);
	SHOW_PLANE_TYPE_INFO(" post fill vtt        ", dbgInfo.postFill_vtt[i]);
	SHOW_PLANE_TYPE_INFO(" post fill alpha      ", dbgInfo.postFill_argb[i].alpha);
	SHOW_PLANE_TYPE_INFO(" post fill red         ", dbgInfo.postFill_argb[i].R);
	SHOW_PLANE_TYPE_INFO(" post fill green       ", dbgInfo.postFill_argb[i].G);
	SHOW_PLANE_TYPE_INFO(" post fill blue        ", dbgInfo.postFill_argb[i].B);
	SHOW_PLANE_TYPE_INFO(" hvsp bypass           ", dbgInfo.hvspbypass[i]);
	SHOW_PLANE_TYPE_INFO(" hvsp input width           ", dbgInfo.scl_inputWin[i].w);
	SHOW_PLANE_TYPE_INFO(" hvsp input height          ", dbgInfo.scl_inputWin[i].h);
	SHOW_PLANE_TYPE_INFO(" hvsp output width          ", dbgInfo.scl_outputWin[i].w);
	SHOW_PLANE_TYPE_INFO(" hvsp output height         ", dbgInfo.scl_outputWin[i].h);
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
#undef SHOW_PLANE_TYPE_INFO

	FREE(dbgInfo.scl_inputWin);
	FREE(dbgInfo.scl_outputWin);

	return len;
}

static ssize_t debug_info_plane_type_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;

	video_hw_version = &pctx_video->video_hw_ver;
	video_mute_hw_ver = video_hw_version->video_mute;

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB)
		len = _debug_info_plane_type_1_0_show(pctx_video, buf);
	else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB)
		len += snprintf(buf + len, PAGE_SIZE - len, "not support this cmd!\n");
	else
		DRM_ERROR("[%s, %d]: invalid mute version\n", __func__, __LINE__);

	return ((len < 0) || (len > PAGE_SIZE)) ? -EINVAL : len;
}

static int _mute_info_1_0_show(
	struct mtk_video_context *pctx_video,
	struct mtk_panel_context *pctx_pnl,
	uint8_t window_num,
	char *buf)
{
	struct mtk_video_dbg_mute_info mute_info;
	uint8_t blend_out_mute_en, backlight_mute_en = 0;
	bool video_mod_mute_en = 0, gfx_mod_mute_en = 0;
	bool video_tx_mute_en = 0, gfx_tx_mute_en = 0;
	int len = 0, i = 0;

	memset(&mute_info, 0, sizeof(mute_info));

	/* get info*/
	video_tx_mute_en = drv_hwreg_render_pnl_get_tx_mute_en_video();
	gfx_tx_mute_en   = drv_hwreg_render_pnl_get_tx_mute_en_gfx();

	video_mod_mute_en = drv_hwreg_render_pnl_get_mod_pattern_video();
	gfx_mod_mute_en = drv_hwreg_render_pnl_get_mod_pattern_gfx();

	for (i = 0; i < NUM_OF_LAYER; i++) {
		drv_hwreg_render_display_get_layer_control(
		i,
		&mute_info.src_sel[i],
		&mute_info.layer_sel[i],
		&mute_info.layer_en[i]);
	}

	for (i = 0; i < window_num; i++) {
		mute_info.planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);
		drv_hwreg_render_display_get_post_fill_mute_screen(
				mute_info.planeType[i], i, &mute_info.mute_en[i]);
	}

	drv_hwreg_render_display_get_blend_out_mute(&blend_out_mute_en);
	backlight_mute_en = !gpiod_get_value(pctx_pnl->gpio_backlight);

	SYSFS_PRINT(len, buf, "=== video mute user record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_USER, buf, &len);

	SYSFS_PRINT(len, buf, "=== video mute dummy record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_DUMMY, buf, &len);

	SYSFS_PRINT(len, buf, "=== video mute hwreg record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_HWREG, buf, &len);

	/* show info*/
	len += snprintf(buf + len, PAGE_SIZE - len,
		"\n");
	len += snprintf(buf + len, PAGE_SIZE - len,
		"===========================================================\n");
	len += snprintf(buf + len, PAGE_SIZE - len,
			 "arch: video mute (win) -> vb out mute -> pixel mute -> backlight mute -> tx mute\n");
	len += snprintf(buf + len, PAGE_SIZE - len,
		"===========================================================\n");
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");

	// 1. tx mute info
	len += snprintf(buf + len, PAGE_SIZE - len, " tx mute video enable:%d\n",
		video_tx_mute_en);
	len += snprintf(buf + len, PAGE_SIZE - len, " tx mute gfx enable:%d\n",
		gfx_tx_mute_en);
	// 2. backlight mute info
	len += snprintf(buf + len, PAGE_SIZE - len, "backlight mute enable:%d\n",
		backlight_mute_en);
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
	// 3. pixel mute info
	len += snprintf(buf + len, PAGE_SIZE - len, "pixel mute video enable:%d\n",
		video_mod_mute_en);
	len += snprintf(buf + len, PAGE_SIZE - len, "pixel mute gfx enable:%d\n",
		gfx_mod_mute_en);
	// 4. vb out mute info
	len += snprintf(buf + len, PAGE_SIZE - len, "vb out mute enable:%d\n",
		blend_out_mute_en);
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");

	// 5. video mute info
#define SHOW_WIN_INFO(title, elem) do { \
	len += snprintf(buf + len, PAGE_SIZE - len, "%s |", title); \
	for (i = 0; i < window_num; i++) { \
		len += snprintf(buf + len, PAGE_SIZE - len, " %5lld |", (uint64_t)(elem)); \
	} \
	len += snprintf(buf + len, PAGE_SIZE - len, "\n"); \
} while (0)
	SHOW_WIN_INFO("window ID       ", i);
	SHOW_WIN_INFO("mute_en         ", mute_info.mute_en[i]);
#undef SHOW_WIN_INFO
	len += snprintf(buf + len, PAGE_SIZE - len, "video_plane_type |");
	for (i = 0; i < window_num; i++) {
		switch (mute_info.planeType[i]) {
		case MTK_VPLANE_TYPE_NONE:
			len += snprintf(buf + len, PAGE_SIZE - len, "  NONE |");
		break;
		case MTK_VPLANE_TYPE_MEMC:
			len += snprintf(buf + len, PAGE_SIZE - len, "  MEMC |");
		break;
		case MTK_VPLANE_TYPE_MULTI_WIN:
			len += snprintf(buf + len, PAGE_SIZE - len, "   MGW |");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN1:
			len += snprintf(buf + len, PAGE_SIZE - len, "  DMA0 |");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN2:
			len += snprintf(buf + len, PAGE_SIZE - len, "  DMA1 |");
		break;
		default:
			len += snprintf(buf + len, PAGE_SIZE - len, "  NONE |");
		}
	}
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
	len += snprintf(buf + len, PAGE_SIZE - len,
		"===========================================================\n");
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
/*
#define SHOW_PLANE_TYPE_INFO(title, elem) do {							\
	len += snprintf(buf + len, PAGE_SIZE - len, "%s |", title);				\
	for (i = MTK_VPLANE_TYPE_MEMC; i < MTK_VPLANE_TYPE_MAX; i++) {\
		len += snprintf(buf + len, PAGE_SIZE - len, " %5lld |", (uint64_t)(elem));	\
	}											\
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");					\
} while (0)

	len += snprintf(buf + len, PAGE_SIZE - len,
			 " video plane type       |  MEMC |   MGW |  DMA0 |  DMA1 |\n");
	SHOW_PLANE_TYPE_INFO(" layer_en              ", mute_info.layer_en[i]);
	SHOW_PLANE_TYPE_INFO(" zpos                  ", mute_info.zpos[i]);

	len += snprintf(buf + len, PAGE_SIZE - len,
		"===========================================================\n");
#undef SHOW_PLANE_TYPE_INFO
*/
	return len;
}

static int _mute_info_2_0_show(
	struct mtk_video_context *pctx_video,
	struct mtk_panel_context *pctx_pnl,
	uint8_t window_num,
	char *buf)
{
	struct mtk_video_dbg_mute_info mute_info;
	uint8_t video_mute_en = 0, backlight_mute_en = 0;
	bool video_mod_mute_en = 0, gfx_mod_mute_en = 0;
	bool video_tx_mute_en = 0, gfx_tx_mute_en = 0;
	int len = 0;

	memset(&mute_info, 0, sizeof(mute_info));

	/* get info*/
	video_tx_mute_en = drv_hwreg_render_pnl_get_tx_mute_en_video();
	gfx_tx_mute_en   = drv_hwreg_render_pnl_get_tx_mute_en_gfx();

	video_mod_mute_en = drv_hwreg_render_pnl_get_mod_pattern_video();
	gfx_mod_mute_en = drv_hwreg_render_pnl_get_mod_pattern_gfx();

	drv_hwreg_render_display_get_layer_control(
		0,
		&mute_info.src_sel[0],
		&mute_info.layer_sel[0],
		&mute_info.layer_en[0]);

	drv_hwreg_render_display_get_video_mute(&video_mute_en);
	backlight_mute_en = !gpiod_get_value(pctx_pnl->gpio_backlight);

	SYSFS_PRINT(len, buf, "=== video mute user record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_USER, buf, &len);
	SYSFS_PRINT(len, buf, "\n");
	SYSFS_PRINT(len, buf, "=== video mute dummy record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_DUMMY, buf, &len);
	SYSFS_PRINT(len, buf, "\n");
	SYSFS_PRINT(len, buf, "=== video mute hwreg record ===");
	mtk_tv_drm_logrec_read(MTK_TV_DRM_LOGREC_VIDEO_MUTE_HWREG, buf, &len);

	/* show info*/
	SYSFS_PRINT(len, buf, "\n");
	SYSFS_PRINT(len, buf, "tx mute video enable:%d",
		video_tx_mute_en);
	SYSFS_PRINT(len, buf, "tx mute gfx enable:%d",
		gfx_tx_mute_en);
	SYSFS_PRINT(len, buf, "backlight enable:%d",
		backlight_mute_en);
	SYSFS_PRINT(len, buf, "pixel mute video enable:%d",
		video_mod_mute_en);
	SYSFS_PRINT(len, buf, "pixel mute gfx enable:%d",
		gfx_mod_mute_en);
	SYSFS_PRINT(len, buf, "pixel mute gfx enable:%d",
		gfx_mod_mute_en);
	SYSFS_PRINT(len, buf, "video mute enable:%d",
		video_mute_en);
	SYSFS_PRINT(len, buf, "\n");

	SYSFS_PRINT(len, buf, "video plane type  SRC0");
	SYSFS_PRINT(len, buf, "layer_en         %u",
		mute_info.layer_en[0]);
	SYSFS_PRINT(len, buf, "src select       %u",
		mute_info.src_sel[0]);
	SYSFS_PRINT(len, buf, "layer select     %u",
		mute_info.layer_sel[0]);
	SYSFS_PRINT(len, buf, "\n");

	return len;
}

static ssize_t mute_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_panel_context *pctx_pnl = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	uint8_t window_num = 0;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	pctx_pnl = &pctx->panel_priv;

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

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB)
		len = _mute_info_1_0_show(pctx_video, pctx_pnl, window_num, buf);
	else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB)
		len = _mute_info_2_0_show(pctx_video, pctx_pnl, window_num, buf);
	else
		ERR("invalid mute version:%d\n", video_mute_hw_ver);

	return len;
}

static ssize_t mute_enable_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_dbg_mute_info mute_info;
	int len = 0, i = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;

	memset(&mute_info, 0, sizeof(mute_info));

	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		mute_info.planeType[i] = VIDEO_CUR_PLANE_TYPE(pctx_video, i);
		drv_hwreg_render_display_get_post_fill_mute_screen(
				mute_info.planeType[i], i, &mute_info.mute_en[i]);
	}

	for (i = 0; i < pctx->hw_caps.window_num; i++) {
		SYSFS_PRINT(len, buf, "window id:  %u", i);
		SYSFS_PRINT(len, buf, "mute_en:  %u", mute_info.mute_en[i]);
		SYSFS_PRINT(len, buf, "video_plane_type:  ");
		switch (mute_info.planeType[i]) {
		case MTK_VPLANE_TYPE_NONE:
			SYSFS_PRINT(len, buf, "NONE");
		break;
		case MTK_VPLANE_TYPE_MEMC:
			SYSFS_PRINT(len, buf, "MEMC");
		break;
		case MTK_VPLANE_TYPE_MULTI_WIN:
			SYSFS_PRINT(len, buf, "MGW");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN1:
			SYSFS_PRINT(len, buf, "DMA0");
		break;
		case MTK_VPLANE_TYPE_SINGLE_WIN2:
			SYSFS_PRINT(len, buf, "DMA1");
		break;
		default:
			SYSFS_PRINT(len, buf, "NONE");
		}
	}

	return len;
}

static uint8_t _mtk_video_display_get_memory_mode(
	struct mtk_video_context *pctx_video,
	unsigned int plane_idx)
{
	struct mtk_video_plane_ctx *plane_ctx = NULL;
	uint8_t mem_hw_mode = 0;

	plane_ctx = (pctx_video->plane_ctx + plane_idx);
	mem_hw_mode = plane_ctx->mem_hw_mode;

	return mem_hw_mode;
}

static ssize_t mute_enable_store(
	struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct reg_info reg[HWREG_VIDEO_REG_NUM_VB_POST_FILL_WINDOW_RGB];
	struct hwreg_post_fill_mute_screen vb_mute_info;
	struct hwreg_post_fill_mute_sw_mode_en mute_sw_mode_en = { };
	struct hwreg_video_mute osdb_mute_info;
	struct hwregOut reg_out;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	uint64_t mute_enable = 0, window_id = 0, cmd_control = 0;
	uint8_t mem_hw_mode = 0;
	int ret = 0, i = 0;

	if (!dev || !buf) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;
	video_mute_hw_ver = video_hw_version->video_mute;

	LOG("cmd: '%s'", buf);

	ret = mtk_render_common_parser_cmd_int(buf, "enable", &mute_enable);
	if (ret) {
		ERR("fail parser enable, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "window_id", &window_id);
	if (ret) {
		ERR("fail parser win_id, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "cmd_control", &cmd_control);
	if (ret) {
		ERR("fail parser cmd_control, ret=%d\n", ret);
		goto EXIT;
	}

	LOG("mute_enable:%lld, window_id:%lld cmd_control:%lld",
		mute_enable, window_id, cmd_control);

	if (cmd_control) { /*by sysfs cmd*/
		gMtkDrmDbg.mute_enable->bEn[window_id] = TRUE;
		gMtkDrmDbg.mute_enable->bMuteEn[window_id] = mute_enable;
	} else { /*by sw code*/
		gMtkDrmDbg.mute_enable->bEn[window_id] = FALSE;
		gMtkDrmDbg.mute_enable->bMuteEn[window_id] = mute_enable;
	}

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB) {
		for (i = 0; i < VPLANE_TYPE_MAX; ++i) {

			memset(&vb_mute_info, 0, sizeof(struct hwreg_post_fill_mute_screen));
			memset(&reg_out, 0, sizeof(struct hwregOut));

			mem_hw_mode = _mtk_video_display_get_memory_mode(pctx_video, window_id);

			vb_mute_info.RIU = true;
			vb_mute_info.planeType = i;
			vb_mute_info.windowID = window_id;
			vb_mute_info.muteEn = mute_enable;

			mute_sw_mode_en.RIU = true;
			mute_sw_mode_en.planeType = i;
			mute_sw_mode_en.windowID = window_id;
			mute_sw_mode_en.mute_sw_mode_enable = (bool)!mem_hw_mode;

			reg_out.reg = reg;
			drv_hwreg_render_display_set_post_fill_mute_screen(vb_mute_info, &reg_out);
			drv_hwreg_render_display_set_post_fill_mute_sw_mode_en(mute_sw_mode_en,
											&reg_out);
		}
	} else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB) {
		memset(&osdb_mute_info, 0, sizeof(struct hwreg_video_mute));
		memset(&reg_out, 0, sizeof(struct hwregOut));
		osdb_mute_info.RIU = true;
		osdb_mute_info.muteEn = mute_enable;
		reg_out.reg = reg;
		drv_hwreg_render_display_set_video_mute(osdb_mute_info, &reg_out);
	} else {
		ERR("invalid mute version:%d\n", video_mute_hw_ver);
		return -EINVAL;
	}

EXIT:
	return count;
}

static int mute_color_1_0_show(char *buf)
{
	struct ARGB color[MTK_VPLANE_TYPE_MAX] = {0};
	enum drm_mtk_video_plane_type plane_type = 0;
	int len = 0;

	// all planes have same mute color
	for (plane_type = MTK_VPLANE_TYPE_MEMC;
		plane_type < MTK_VPLANE_TYPE_MAX; ++plane_type) {
		drv_hwreg_render_display_get_post_fill_window_argb(
		plane_type, &color[plane_type]);
	}

#define SHOW_VPLANE_TYPE_INFO(title, elem) do {							\
	len += snprintf(buf + len, PAGE_SIZE - len, "%s |", title);				\
	for (plane_type = MTK_VPLANE_TYPE_MEMC; plane_type < MTK_VPLANE_TYPE_MAX; plane_type++) {\
		len += snprintf(buf + len, PAGE_SIZE - len, " %5lld |", (uint64_t)(elem));	\
	}											\
	len += snprintf(buf + len, PAGE_SIZE - len, "\n");					\
} while (0)
	len += snprintf(buf + len, PAGE_SIZE - len,
			      " video plane type: |  MEMC |    MGW|   DMA0|   DMA1|\n");
	SHOW_VPLANE_TYPE_INFO("        R        :", color[plane_type].R);
	SHOW_VPLANE_TYPE_INFO("        G        :", color[plane_type].G);
	SHOW_VPLANE_TYPE_INFO("        B        :", color[plane_type].B);

	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
#undef SHOW_VPLANE_TYPE_INFO

	return len;
}

static int mute_color_2_0_show(char *buf)
{
	struct ARGB color[MTK_VPLANE_TYPE_MAX] = {0};
	int len = 0;

	drv_hwreg_render_display_get_mute_color(&color[MTK_VPLANE_TYPE_NONE]);

	SYSFS_PRINT(len, buf, "'video mute' mute color");
	SYSFS_PRINT(len, buf, "R : %lld", color[MTK_VPLANE_TYPE_NONE].R);
	SYSFS_PRINT(len, buf, "G : %lld", color[MTK_VPLANE_TYPE_NONE].G);
	SYSFS_PRINT(len, buf, "B : %lld", color[MTK_VPLANE_TYPE_NONE].B);

	return len;
}

static ssize_t mute_color_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;
	video_mute_hw_ver = video_hw_version->video_mute;

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB)
		len = mute_color_1_0_show(buf);
	else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB)
		len = mute_color_2_0_show(buf);
	else
		ERR("invalid mute version:%d\n", video_mute_hw_ver);

	return len;
}

static ssize_t mute_color_store(
	struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct reg_info reg[HWREG_VIDEO_REG_NUM_VB_POST_FILL_WINDOW_RGB];
	struct hwreg_mute_color_argb paramIn;
	struct hwregOut reg_out;
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	enum VIDEO_MUTE_VERSION_E video_mute_hw_ver = 0;
	uint64_t R_Cr = 0, G_Y = 0, B_Cb = 0;
	int ret = 0, i = 0;

	if (!dev || !buf) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;
	video_mute_hw_ver = video_hw_version->video_mute;

	LOG("cmd: '%s'", buf);

	ret = mtk_render_common_parser_cmd_int(buf, "r", &R_Cr);
	if (ret) {
		ERR("fail parser red, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "g", &G_Y);
	if (ret) {
		ERR("fail parser green, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "b", &B_Cb);
	if (ret) {
		ERR("fail parser blue, ret=%d\n", ret);
		goto EXIT;
	}

	LOG("R_V:0x%llx,G_Y:0x%llx,B_U:0x%llx", R_Cr, G_Y, B_Cb);

	if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_VB) {
		memset(&paramIn, 0, sizeof(struct hwreg_mute_color_argb));
		paramIn.RIU = true;
		paramIn.windowARGB.R = R_Cr;
		paramIn.windowARGB.G = G_Y;
		paramIn.windowARGB.B = B_Cb;

		for (i = 0; i < VPLANE_TYPE_MAX; ++i) {
			memset(reg, 0, sizeof(reg));
			memset(&reg_out, 0, sizeof(struct hwregOut));
			paramIn.planeType = i;
			reg_out.reg = reg;
			drv_hwreg_render_display_set_post_fill_window_rgb(paramIn, &reg_out);
		}

		// set for legacy mode
		memset(reg, 0, sizeof(reg));
		memset(&reg_out, 0, sizeof(struct hwregOut));
		reg_out.reg = reg;

		drv_hwreg_render_display_set_mute_color(paramIn, &reg_out);
	} else if (video_mute_hw_ver == EN_VIDEO_MUTE_VERSION_OSDB) {
		memset(&paramIn, 0, sizeof(struct hwreg_mute_color_argb));
		memset(&reg_out, 0, sizeof(struct hwregOut));
		paramIn.RIU = true;
		paramIn.planeType = 0;	// don't care
		paramIn.windowARGB.R = R_Cr;
		paramIn.windowARGB.G = G_Y;
		paramIn.windowARGB.B = B_Cb;
		reg_out.reg = reg;

		drv_hwreg_render_display_set_mute_color(paramIn, &reg_out);
	} else {
		ERR("invalid mute version:%d\n", video_mute_hw_ver);
		return -EINVAL;
	}
EXIT:
	return count;
}

static ssize_t default_background_color_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	//not implement yet
	return 0;
}

static ssize_t default_background_color_store(
	struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct reg_info reg[HWREG_VIDEO_REG_NUM_VB_POST_FILL_WINDOW_RGB];
	struct hwreg_default_background_rgb paramIn;
	struct hwregOut reg_out;
	uint64_t R_Cr = 0, G_Y = 0, B_Cb = 0;
	int ret = 0;

	memset(reg, 0, sizeof(reg));

	LOG("cmd: '%s'", buf);

	ret = mtk_render_common_parser_cmd_int(buf, "r", &R_Cr);
	if (ret) {
		ERR("fail parser red, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "g", &G_Y);
	if (ret) {
		ERR("fail parser green, ret=%d\n", ret);
		goto EXIT;
	}
	ret = mtk_render_common_parser_cmd_int(buf, "b", &B_Cb);
	if (ret) {
		ERR("fail parser blue, ret=%d\n", ret);
		goto EXIT;
	}

	LOG("R_V:0x%llx,G_Y:0x%llx,B_U:0x%llx", R_Cr, G_Y, B_Cb);

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

	reg_out.reg = reg;

	paramIn.RIU = true;
	paramIn.RGB.R = R_Cr;
	paramIn.RGB.G = G_Y;
	paramIn.RGB.B = B_Cb;

	drv_hwreg_render_display_set_default_background_rgb(paramIn, &reg_out);

EXIT:
	return count;
}

static ssize_t cfd_setting_info_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	ssize_t pos = 0;
	struct mtk_tv_kms_context *pctx = NULL;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	pctx = dev_get_drvdata(dev);
	if (pos == 0)
		pos = mtk_video_display_cfd_show_dbg_infor(buf, pctx);

	return ((pos < 0) || (pos > PAGE_SIZE)) ? -EINVAL : pos;
}

static ssize_t vb_color_space_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	uint8_t OutputDataFormat = 0;
	int ret = 0, len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}

	ret = mtk_video_display_cfd_get_format(&OutputDataFormat);

	SYSFS_PRINT(len, buf, "OutputDataFormat:%u\n",
		OutputDataFormat);

	return len;
}

static ssize_t mgw_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->mgw);

	return len;
}

static ssize_t vb_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->video_blending);

	return len;
}

static ssize_t mute_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->video_mute);

	return len;
}

static ssize_t disp_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->disp_win);

	return len;
}

static ssize_t irq_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->irq);

	return len;
}

static ssize_t pattern_version_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	struct mtk_video_context *pctx_video = NULL;
	struct mtk_video_hw_version *video_hw_version = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);
	pctx_video = &pctx->video_priv;
	video_hw_version = &pctx_video->video_hw_ver;

	SYSFS_PRINT(len, buf, "%u\n",
		video_hw_version->pattern);

	return len;
}

static ssize_t enable_expect_pqout_size_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u\n",
		pctx->sw_caps.enable_expect_pqout_size);

	return len;
}

static ssize_t enable_expect_pqout_size_store(
	struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct mtk_tv_kms_context *pctx = dev_get_drvdata(dev);
	unsigned long val = 0;
	int err;

	err = kstrtoul(buf, 0, &val);
	if (err)
		return err;

	pctx->sw_caps.enable_expect_pqout_size = val;

	return count;
}

static ssize_t dma_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.dma_num);

	return len;
}

static ssize_t mgw_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.mgw_num);

	return len;
}

static ssize_t memc_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.memc_num);

	return len;
}

static ssize_t window_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.window_num);

	return len;
}

static ssize_t panel_gamma_bit_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.panel_gamma_bit);

	return len;
}

static ssize_t support_od_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_OD);

	return len;
}

static ssize_t support_oled_demura_in_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len  = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u\n",
		pctx->hw_caps.support_OLED_demura_in);

	return len;
}

static ssize_t support_oled_boosting_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_OLED_boosting);

	return len;
}

static ssize_t support_hwmode_planetype_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_hwmode_planetype);

	return len;
}

static ssize_t support_swmode_planetype_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_swmode_planetype);

	return len;
}

static ssize_t support_bypassmode_planetype_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_bypassmode_planetype);

	return len;
}

static ssize_t support_h_scl_down_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_h_scl_down);

	return len;
}

static ssize_t support_render_scaling_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u\n",
		pctx->hw_caps.support_render_scaling);

	return len;
}

static ssize_t support_live_tone_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_live_tone);

	return len;
}

static ssize_t support_i_view_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u",
		pctx->hw_caps.support_i_view);

	return len;
}

static ssize_t support_i_view_pip_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "%u\n",
		pctx->hw_caps.support_i_view_pip);

	return len;
}

static ssize_t render_p_engine_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "p engine:%u",
		pctx->hw_caps.render_p_engine);
	SYSFS_PRINT(len, buf, "post p engine:%u",
		pctx->hw_caps.render_post_p_engine);

	return len;
}

static ssize_t byte_per_word_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "byte_per_word:%u",
		pctx->hw_caps.byte_per_word);

	return len;
}

static ssize_t disp_x_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "disp_x_align:%u",
		pctx->hw_caps.disp_x_align);

	return len;
}

static ssize_t disp_y_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "disp_y_align:%u",
		pctx->hw_caps.disp_y_align);

	return len;
}

static ssize_t disp_w_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "disp_w_align:%u",
		pctx->hw_caps.disp_w_align);

	return len;
}

static ssize_t disp_h_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "disp_h_align:%u",
		pctx->hw_caps.disp_h_align);

	return len;
}

static ssize_t crop_x_align_420_422_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_x_align_420_422:%u",
		pctx->hw_caps.crop_x_align_420_422);

	return len;
}

static ssize_t crop_x_align_444_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_x_align_444:%u",
		pctx->hw_caps.crop_x_align_444);

	return len;
}

static ssize_t crop_y_align_420_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_y_align_420:%u",
		pctx->hw_caps.crop_y_align_420);

	return len;
}

static ssize_t crop_y_align_444_422_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_y_align_444_422:%u",
		pctx->hw_caps.crop_y_align_444_422);

	return len;
}

static ssize_t crop_w_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_w_align:%u",
		pctx->hw_caps.crop_w_align);

	return len;
}

static ssize_t crop_h_align_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "crop_h_align:%u",
		pctx->hw_caps.crop_h_align);

	return len;
}

static ssize_t video_mute_color_bits_num_show(
	struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	struct mtk_tv_kms_context *pctx = NULL;
	int len = 0;

	if ((dev == NULL) || (buf == NULL)) {
		ERR("pointer is null\n");
		return -EINVAL;
	}
	pctx = dev_get_drvdata(dev);

	SYSFS_PRINT(len, buf, "video_mute_color_bits_num:%u",
		pctx->hw_caps.video_mute_color_bits_num);

	return len;
}

//-------------------------------------------------------------------------------------------------
//  Global Functions
//-------------------------------------------------------------------------------------------------
void mtk_video_display_create_sysfs(struct platform_device *pdev)
{
	int ret = 0;

	ret = sysfs_create_group(&pdev->dev.kobj, &mtk_drm_video_display_attr_group);
	if (ret) {
		dev_err(&pdev->dev, "[%s, %d]Fail to create sysfs files: %d\n",
			__func__, __LINE__, ret);
	}

	ret = sysfs_create_group(&pdev->dev.kobj, &mtk_drm_video_display_attrs_version_group);
	if (ret) {
		dev_err(&pdev->dev, "[%s, %d]Fail to create sysfs files: %d\n",
			__func__, __LINE__, ret);
	}

	ret = sysfs_update_group(&pdev->dev.kobj, &mtk_drm_video_display_capability_group);
	if (ret) {
		dev_err(&pdev->dev, "[%s, %d]Fail to create sysfs files: %d\n",
			__func__, __LINE__, ret);
	}
}

void mtk_video_display_remove_sysfs(struct platform_device *pdev)
{
	dev_info(&pdev->dev, "Remove device attribute files");
	sysfs_remove_group(&pdev->dev.kobj, &mtk_drm_video_display_attr_group);
	sysfs_remove_group(&pdev->dev.kobj, &mtk_drm_video_display_attrs_version_group);
	sysfs_remove_group(&pdev->dev.kobj, &mtk_drm_video_display_capability_group);
}
