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

#include <linux/delay.h>
#include <linux/printk.h>

#include "mtk_dmx_dev.h"
#include "mtk_dmx_common.h"
#include "mtk_dmx_log.h"
#include "mtk_dmx_log_alp.h"
#include "mtk_dmx_alp.h"
#include "mtk_dmx_riu.h"
#include "mtk_dmx_regs.h"
#include "mtk_dmx_io.h"
#include "mtk_dmx_io.inc"
#include "mtk_dmx_ca.h"
#include "mtk_dmx_rm.h"
#include "mtk_dmx_ctrl.h"
#include "mtk_dmx_pvr.h"

#ifdef pr_fmt
#undef pr_fmt
#endif //pr_fmt
#define pr_fmt(fmt) "mtk_dmx_io: " fmt

#define MTK_DMX_IO_FILEIN_BYTE_DELAY_DEFAULT 0xA
#define MTK_DMX_IO_ALP_FILEIN_BYTE_DELAY_DEFAULT	0x21

#define FILEIN_RESET_RETRY_COUNT 100
#define FILEIN_RESET_SLEEP_RANGE_L 10000
#define FILEIN_RESET_SLEEP_RANGE_H 20000
#define CLK_27M		(27000000)
#define CLK_172M	(172000000)
#define CLK_288M	(288000000)
#define CLK_216M	(216000000)
#define CLK_432M	(432000000)
#define D_1		1
#define D_2		2
#define D_3		3
#define IS_PHASE_ADJUSTMENT_FOR_90_DEGREE(out_clk) ((out_clk) <= CLK_27M)
// This formular(432/u16OutClk) comes from HW RD
// Divided by 4 due to 90 degree is quarter of a cycle
#define PHASE_FOR_90_DEGREE(ts_sample_clk, out_clk) ((ts_sample_clk) / (out_clk) / 4)

#define FIFO_OUTPUT_LENGTH		(256)
#define FIFO_NUMBER_LENGTH		(32)
#define FIFO_SHIFT_LENGTH		(2)
#define AFIFO_NUMBER			(4)
#define VFIFO_NUMBER			(2)

#define REG_FLD_ENG_DEFAULT (-1)

#define TSP0_BASE (0)
#define TSP1_BASE (1)
#define TSP2_BASE (2)
#define TSP3_BASE (3)
#define TSP4_BASE (4)
#define TSP5_BASE (5)
#define TSP6_BASE (6)
#define TSP7_BASE (7)
#define TSP8_BASE (8)

#define REG_SHIFT 4
#define MIU_ADDR(addr) ((u32) ((addr) >> MIU_BUS))

#define TLV_FILEIN_MUX_0		(0)
#define TLV_FILEIN_MUX_1		(1)

typedef enum {
	LOCKED_CNT_SRC_FROM_TSIF_0 = 0,
	LOCKED_CNT_SRC_FROM_TSIF_1 = 1,
	LOCKED_CNT_SRC_FROM_TSIF_2 = 2,
	LOCKED_CNT_SRC_FROM_TSIF_FI = 4,
	LOCKED_CNT_SRC_FROM_FILEIN_MAIN = 5,
	LOCKED_CNT_SRC_FROM_FILEIN_MM0 = 6,
	LOCKED_CNT_SRC_FROM_FILEIN_MM1 = 7,
} locked_cnt_src_t;

struct eng_reg_fld {
	u16 eng;
	int base;
	u16 offset;
	REG16 *reg32;
	uint32_t fld;
};

struct mtk_dmx_io_wait_params {
	u8 retry_count;
	unsigned long min;
	unsigned long max;
};

static DEFINE_SPINLOCK(locked_pkt_lock);

static void mtk_dmx_io_set_tsif_fi_mux(bool bLivein);
static void mtk_dmx_io_filein_en(u16 path, bool bEnable);
static void mtk_dmx_io_filein_en_v2(u16 path, u16 eng, bool bEnable);
static void mtk_dmx_io_sel_tsif_src(u16 path, struct demux_io_cfg *pcfg);
static void mtk_dmx_io_tso_tsif_en(u16 eng, u16 tsif_id, bool bEnable);
static void mtk_dmx_io_tso_sel_tsif_src(u16 eng, u16 tsif_id, struct demux_io_cfg cfg);
static void mtk_dmx_io_filein_byte_delay(u16 path, u32 delay);
static void mtk_dmx_io_filein_byte_delay_v2(int eng, u32 delay);

static struct eng_reg_fld filein_done_eng_reg_flds[] = {
	// Put default in [0] always
	{
		.eng = REG_FLD_ENG_DEFAULT, // main filein
		.base = TSP0_BASE,
		.offset = REG_00F8_TSP0,
		.fld = TSP0_REG_TSP_FILEIN_DONE_00F8,
	},
	{
		.eng = FILEIN_MM0,
		.base = TSP2_BASE,
		.offset = REG_0030_TSP2,
		.fld = TSP2_REG_TSP_FILEIN_DONE_MMFI0_0030,
	},
	{
		.eng = FILEIN_MM1,
		.base = TSP2_BASE,
		.offset = REG_00B0_TSP2,
		.fld = TSP2_REG_TSP_FILEIN_DONE_MMFI1_00B0,
	},
};

static struct eng_reg_fld filein_fms_reset_finished_eng_reg_flds[] = {
	// Put default in [0] always
	{
		.eng = REG_FLD_ENG_DEFAULT, // main filein
		.base = TSP5_BASE,
		.offset = REG_0010_TSP5,
		.fld = TSP5_REG_WB_FSM_RESET_FINISHED_0010,
	},
};

static void mtk_dmx_io_tlv_filein_byte_delay(u16 path, u32 delay)
{
	u16 eng = 0;
	u32 fld_timer_en = 0, fld_timer = 0;
	REG16 __iomem *reg_timer_en = NULL, *reg_timer = NULL;

	switch (path) {
	case TLV_TSIF_0:
		eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0000_TSP_TLV,
			TSP_TLV_REG_TLV_FILEIN_SEL_0000);
		break;
	case TLV_TSIF_1:
		eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0040_TSP_TLV,
			TSP_TLV_REG_PATH1_TLV_FILEIN_SEL_0040);
		break;
	case TLV_TSIF_2:
		eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0080_TSP_TLV,
			TSP_TLV_REG_PATH2_TLV_FILEIN_SEL_0080);
		break;
	default:
		return;
	}

	switch (eng) {
	case TLV_FILEIN_0:
		reg_timer_en = REG_TLV1_BASE + REG_0000_TSP_TLV2;
		fld_timer_en = TSP_TLV2_REG_TIMER_EN_0000;
		reg_timer = REG_TLV1_BASE + REG_0004_TSP_TLV2;
		fld_timer = TSP_TLV2_REG_TIMER_BYTE_0004;
		break;
	case TLV_FILEIN_1:
		reg_timer_en = REG_TLV1_BASE + REG_0040_TSP_TLV2;
		fld_timer_en = TSP_TLV2_REG_FI1_TIMER_EN_0040;
		reg_timer = REG_TLV1_BASE + REG_0044_TSP_TLV2;
		fld_timer = TSP_TLV2_REG_FI1_TIMER_BYTE_0044;
		break;
	default:
		MTK_DMX_ERR("Please enable tlv path %u file in first\n", path);
		return;
	}

	if (delay == 0) {
		vIO32WriteFld(reg_timer_en, 0, fld_timer_en);
	} else {
		vIO32WriteFld(reg_timer, delay, fld_timer);
		vIO32WriteFld(reg_timer_en, 1, fld_timer_en);
	}
}

void mtk_dmx_io_set_tsif_en(u16 path, bool bEnable)
{
	REG16 __iomem *tsif_en_offset;
	u32  tsif_en_nr;

	switch (path) {
	case TSIF_0:
		tsif_en_offset = REG_TSP0_BASE + REG_0110_TSP0;
		tsif_en_nr = TSP0_REG_TS_IF0_EN_0110;
		break;
	case TSIF_FI:
		tsif_en_offset = REG_TSP5_BASE + REG_0008_TSP5;
		tsif_en_nr = TSP5_REG_TS_IF_FI_EN_0008;
		break;
	case TSIF_1:
		tsif_en_offset = REG_TSP0_BASE + REG_0110_TSP0;
		tsif_en_nr = TSP0_REG_TS_IF1_EN_0110;
		break;
	case TSIF_2:
		tsif_en_offset = REG_TSP0_BASE + REG_003C_TSP0;
		tsif_en_nr = TSP0_REG_TS_IF2_EN_003C;
		break;
	case TLV_TSIF_0:
		tsif_en_offset = REG_TLV0_BASE + REG_0000_TSP_TLV;
		tsif_en_nr = TSP_TLV_REG_TLV_IF_EN_0000;
		break;
	case TLV_TSIF_1:
		tsif_en_offset = REG_TLV0_BASE + REG_0040_TSP_TLV;
		tsif_en_nr = TSP_TLV_REG_PATH1_TLV_IF_EN_0040;
		break;
	case TLV_TSIF_2:
		tsif_en_offset = REG_TLV0_BASE + REG_0080_TSP_TLV;
		tsif_en_nr = TSP_TLV_REG_PATH2_TLV_IF_EN_0080;
		break;
	case TS_ALP_0:
		tsif_en_offset = REG_TSP6_BASE + REG_00B8_TSP6;
		tsif_en_nr = TSP6_REG_TSIF_ALP0_EN_00B8;
		break;
	case TS_ALP_FI:
		tsif_en_offset = REG_TSP6_BASE + REG_00C0_TSP6;
		tsif_en_nr = TSP6_REG_TSIF_ALP1_EN_00C0;
		break;
	default:
		return;
	}

	if (bEnable)
		vIO32WriteFld(tsif_en_offset, 1, tsif_en_nr);
	else
		vIO32WriteFld(tsif_en_offset, 0, tsif_en_nr);
}

void mtk_dmx_io_sync_byte(u16 path, u8 idx, u8 *psync_byte, bool bSet)
{
	REG16 __iomem *offset;
	u32 mask;

	switch (path) {
	case TSIF_0:
		offset = REG_TSP3_BASE + REG_00A0_TSP3;
		mask = (idx & 1) ? TSP3_REG_SYNC_BYTE01_00A0 : TSP3_REG_SYNC_BYTE00_00A0;
		break;
	case TSIF_FI:
		offset = REG_TSP3_BASE + REG_00B8_TSP3;
		mask = (idx & 1) ? TSP3_REG_SYNC_BYTE_F1_00B8 : TSP3_REG_SYNC_BYTE_F0_00B8;
		break;
	case TSIF_1:
		offset = REG_TSP3_BASE + REG_00D0_TSP3;
		mask = (idx & 1) ? TSP3_REG_SYNC_BYTE11_00D0 : TSP3_REG_SYNC_BYTE10_00D0;
		break;
	default:
		return;
	}

	offset += ((idx >> 1) * 4);

	if (bSet)
		vIO32WriteFld(offset, *psync_byte, mask);
	else
		*psync_byte = u4IO32ReadFld(offset, mask);
}

void mtk_dmx_io_sync_byte_en(u16 path, u8 idx, bool bEnable)
{
	REG16 __iomem *offset = REG_TSP6_BASE + REG_0114_TSP6 + ((path >> 1) << 2);
	//u8 nr = idx + ((path & 1) ? 8 : 0);
	u8 val;
	u32 fld;

	switch (path) {
	case 0:
		fld = TSP6_REG_SYNC_BYTE_ENABLE_CONFIG0_0114_7_0;
		val = u4IO32ReadFld(offset, fld);
		break;
	case 1:
		fld = TSP6_REG_SYNC_BYTE_ENABLE_CONFIG0_0114_15_8;
		val = u4IO32ReadFld(offset, fld);
		break;
	case 2:
		fld = TSP6_REG_SYNC_BYTE_ENABLE_CONFIG1_0118_7_0;
		val = u4IO32ReadFld(offset, fld);
		break;
	case 3:
		fld = TSP6_REG_SYNC_BYTE_ENABLE_CONFIG1_0118_15_8;
		val = u4IO32ReadFld(offset, fld);
		break;
	default:
		return;
	}
	if (bEnable) {
		//reg16_set_bit(offset, nr);
		val |= (1 << idx);
		vIO32WriteFld(offset, val, fld);
	} else {
		//reg16_clear_bit(offset, nr);
		val &= ~(1 << idx);
		vIO32WriteFld(offset, val, fld);
	}
}

void mtk_dmx_io_src_id(u16 path, u8 idx, u8 *psrc_id, bool bSet)
{
	REG16 __iomem *offset;
	u32 mask;

	switch (path) {
	case TSIF_0:
		offset = REG_TSP3_BASE + REG_00B0_TSP3;
		switch (idx % 4) {
		case 0:
			mask = TSP3_REG_SOURCE_ID00_00B0;
			break;
		case 1:
			mask = TSP3_REG_SOURCE_ID01_00B0;
			break;
		case 2:
			mask = TSP3_REG_SOURCE_ID02_00B0;
			break;
		case 3:
			mask = TSP3_REG_SOURCE_ID03_00B0;
			break;
		default:
			return;
		}
		break;
	case TSIF_FI:
		offset = REG_TSP3_BASE + REG_00C8_TSP3;
		switch (idx % 4) {
		case 0:
			mask = TSP3_REG_SOURCE_ID_F0_00C8;
			break;
		case 1:
			mask = TSP3_REG_SOURCE_ID_F1_00C8;
			break;
		case 2:
			mask = TSP3_REG_SOURCE_ID_F2_00C8;
			break;
		case 3:
			mask = TSP3_REG_SOURCE_ID_F3_00C8;
			break;
		default:
			return;
		}
		break;
	case TSIF_1:
		offset = REG_TSP3_BASE + REG_00E0_TSP3;
		switch (idx % 4) {
		case 0:
			mask = TSP3_REG_SOURCE_ID10_00E0;
			break;
		case 1:
			mask = TSP3_REG_SOURCE_ID11_00E0;
			break;
		case 2:
			mask = TSP3_REG_SOURCE_ID12_00E0;
			break;
		case 3:
			mask = TSP3_REG_SOURCE_ID13_00E0;
			break;
		default:
			return;
		}
		break;
	default:
		return;
	}
	offset += ((idx >> 2) << 2);
	if (bSet)
		vIO32WriteFld(offset, *psrc_id, mask);
	else
		*psrc_id = u4IO32ReadFld(offset, mask);
}

static void mtk_dmx_io_set_tlv_input_port(int path, int port)
{
	switch (path) {
	case TLV_TSIF_0:
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D4_TS_SAMPLE, port
			, TS_SAMPLE_REG_CKG_TS_TLV0_00D4_5_2);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D4_TS_SAMPLE, 0
			, TS_SAMPLE_REG_CKG_TS_TLV0_00D4_0);
		break;
	case TLV_TSIF_1:
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D4_TS_SAMPLE, port
			, TS_SAMPLE_REG_CKG_TS_TLV1_00D4_13_10);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D4_TS_SAMPLE, 0
			, TS_SAMPLE_REG_CKG_TS_TLV1_00D4_8);
		break;
	case TLV_TSIF_2:
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D8_TS_SAMPLE, port
			, TS_SAMPLE_REG_CKG_TS_TLV2_00D8_5_2);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_00D8_TS_SAMPLE, 0
			, TS_SAMPLE_REG_CKG_TS_TLV2_00D8_0);
		break;
	default:
		break;
	}
}

static void mtk_dmx_io_set_tlv_input_port_type(int path, enum demux_port_type type)
{
	int parallel, serial_2bit;

	parallel = (type == DEMUX_PORT_TYPE_PARALLEL) ? 1 : 0;
	serial_2bit = (type == DEMUX_PORT_TYPE_SERIAL_2BITS) ? 1 : 0;

	switch (path) {
	case TLV_TSIF_0:
		vIO32WriteFld(REG_TLV0_BASE + REG_0000_TSP_TLV, parallel
			, TSP_TLV_REG_P_SEL_0000);
		vIO32WriteFld(REG_TLV0_BASE + REG_0000_TSP_TLV, 1
			, TSP_TLV_REG_EXT_SYNC_SEL_0000);
		vIO32WriteFld(REG_TLV0_BASE + REG_0000_TSP_TLV, parallel == 1 ? 0 : 1
			, TSP_TLV_REG_SERIAL_EXT_SYNC_1T_0000);
		vIO32WriteFld(REG_TLV0_BASE + REG_0004_TSP_TLV, serial_2bit
			, TSP_TLV_REG_2BIT_SERIAL_IN_0004);
		break;
	case TLV_TSIF_1:
		vIO32WriteFld(REG_TLV0_BASE + REG_0040_TSP_TLV, parallel
			, TSP_TLV_REG_PATH1_P_SEL_0040);
		vIO32WriteFld(REG_TLV0_BASE + REG_0040_TSP_TLV, 1
			, TSP_TLV_REG_PATH1_EXT_SYNC_SEL_0040);
		vIO32WriteFld(REG_TLV0_BASE + REG_0040_TSP_TLV, parallel == 1 ? 0 : 1
			, TSP_TLV_REG_PATH1_SERIAL_EXT_SYNC_1T_0040);
		vIO32WriteFld(REG_TLV0_BASE + REG_0044_TSP_TLV, serial_2bit
			, TSP_TLV_REG_PATH1_2BIT_SERIAL_IN_0044);
		break;
	case TLV_TSIF_2:
		vIO32WriteFld(REG_TLV0_BASE + REG_0080_TSP_TLV, parallel
			, TSP_TLV_REG_PATH2_P_SEL_0080);
		vIO32WriteFld(REG_TLV0_BASE + REG_0080_TSP_TLV, 1
			, TSP_TLV_REG_PATH2_EXT_SYNC_SEL_0080);
		vIO32WriteFld(REG_TLV0_BASE + REG_0080_TSP_TLV, parallel == 1 ? 0 : 1
			, TSP_TLV_REG_PATH2_SERIAL_EXT_SYNC_1T_0080);
		vIO32WriteFld(REG_TLV0_BASE + REG_0084_TSP_TLV, serial_2bit
			, TSP_TLV_REG_PATH2_2BIT_SERIAL_IN_0084);
		break;
	}
}

static void check_tlv_path_mux_sel(int path, int mux)
{
	u32 eng;
	int mux_set;

	switch (mux) {
	case TLV_FILEIN_MUX_0:
		mux_set = TLV_FILEIN_0;
		break;
	case TLV_FILEIN_MUX_1:
		mux_set = TLV_FILEIN_1;
		break;
	default:
		MTK_DMX_ERR("invalid mux %d\n", mux);
		return;
	}

	eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0000_TSP_TLV,
		TSP_TLV_REG_TLV_FILEIN_SEL_0000);
	if (eng == mux_set && path != TLV_TSIF_0) {
		MTK_DMX_ERR("path %d mux is same as current setting, disable\n", path);
		vIO32WriteFld(REG_TLV0_BASE + REG_0000_TSP_TLV, 0,
			TSP_TLV_REG_TLV_FILEIN_SEL_0000);
	}

	eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0040_TSP_TLV,
		TSP_TLV_REG_PATH1_TLV_FILEIN_SEL_0040);
	if (eng == mux_set && path != TLV_TSIF_1) {
		MTK_DMX_ERR("path %d mux is same as current setting, disable\n", path);
		vIO32WriteFld(REG_TLV0_BASE + REG_0040_TSP_TLV, 0,
			TSP_TLV_REG_PATH1_TLV_FILEIN_SEL_0040);
	}

	eng = u4IO32ReadFld(REG_TLV0_BASE + REG_0080_TSP_TLV,
		TSP_TLV_REG_PATH2_TLV_FILEIN_SEL_0080);
	if (eng == mux_set && path != TLV_TSIF_2) {
		MTK_DMX_ERR("path %d mux is same as current setting, disable\n", path);
		vIO32WriteFld(REG_TLV0_BASE + REG_0080_TSP_TLV, 0,
			TSP_TLV_REG_PATH2_TLV_FILEIN_SEL_0080);
	}
}

void mtk_dmx_io_set_tlv_input_type(int path, bool live, int eng, int mux)
{
#define TLV_FILEIN_PATH_MAIN 1
#define TLV_FILEIN_PATH_MM0  2
#define TLV_FILEIN_PATH_MM1  3
	REG16 __iomem *reg;
	u32 fld, val = 0, mux_set = 1;

	MTK_DMX_INFO("tlv path %d set live: %d, filein: %d, mux: %d\n", path, live, eng, mux);

	if (!live) {
		switch (mux) {
		case TLV_FILEIN_MUX_0:
			mux_set = TLV_FILEIN_0;
			break;
		case TLV_FILEIN_MUX_1:
			mux_set = TLV_FILEIN_1;
			break;
		default:
			MTK_DMX_ERR("invalid mux %d\n", mux);
			return;
		}

		check_tlv_path_mux_sel(path, mux);

		switch (mux) {
		case TLV_FILEIN_MUX_0:
			reg = REG_TLV1_BASE + REG_0000_TSP_TLV2;
			fld = TSP_TLV2_REG_FILEIN_DMA_SEL_0000;
			break;
		case TLV_FILEIN_MUX_1:
			reg = REG_TLV1_BASE + REG_0040_TSP_TLV2;
			fld = TSP_TLV2_REG_FI1_FILEIN_DMA_SEL_0040;
			break;
		default:
			MTK_DMX_ERR("invalid mux %d\n", mux);
			return;
		}

		switch (eng) {
		default:
			MTK_DMX_ERR("Invalid filein eng %d for tlv\n", eng);
			break;
		case FILEIN_MAIN:
			val = TLV_FILEIN_PATH_MAIN;
			break;
		case FILEIN_MM0:
			val = TLV_FILEIN_PATH_MM0;
			break;
		case FILEIN_MM1:
			val = TLV_FILEIN_PATH_MM1;
			break;
		}
		vIO32WriteFld(reg, val, fld);
	}

	switch (path) {
	case TLV_TSIF_0:
		vIO32WriteFld(REG_TLV0_BASE + REG_0000_TSP_TLV,
			live ? 0 : mux_set,
			TSP_TLV_REG_TLV_FILEIN_SEL_0000);
		break;
	case TLV_TSIF_1:
		vIO32WriteFld(REG_TLV0_BASE + REG_0040_TSP_TLV,
			live ? 0 : mux_set,
			TSP_TLV_REG_PATH1_TLV_FILEIN_SEL_0040);
		break;
	case TLV_TSIF_2:
		vIO32WriteFld(REG_TLV0_BASE + REG_0080_TSP_TLV,
			live ? 0 : mux_set,
			TSP_TLV_REG_PATH2_TLV_FILEIN_SEL_0080);
		break;
	}
}

static int mtk_dmx_io_set_tlv_cfg(int path, struct demux_io_cfg *pcfg, int eng, int mux)
{
	switch (pcfg->type) {
	case DEMUX_IO_TYPE_EXT_DEMOD:
		mtk_dmx_io_set_tlv_input_port(path, pcfg->port_num + 1);
		mtk_dmx_io_set_tlv_input_port_type(path, pcfg->port_type);
		mtk_dmx_io_set_tlv_input_type(path, true, -1, 0);
		break;
	case DEMUX_IO_TYPE_MEM:
		mtk_dmx_io_set_tlv_input_type(path, false, eng, mux);
		break;
	default:
		MTK_DMX_ERR("not supported type %d\n", pcfg->type);
		return -EINVAL;
	}

	return 0;
}

static int mtk_dmx_io_demux_io_cfg_2_alp_src(struct demux_io_cfg *pcfg)
{
	demux_alp_path_type_e alp_path_type = DEMUX_ALP_PATH_TYPE_NULL;

	switch (pcfg->type) {
	case DEMUX_IO_TYPE_IN_DEMOD:
		alp_path_type = DEMUX_ALP_PATH_TYPE_LIVE_IN_FROM_DEMOD;
		break;
	case DEMUX_IO_TYPE_EXT_DEMOD:
		alp_path_type = DEMUX_ALP_PATH_TYPE_LIVE_IN_FROM_TS0 + pcfg->port_num;
		break;
	case DEMUX_IO_TYPE_MEM:
		switch (pcfg->port_num) {
		case 0:
			alp_path_type = DEMUX_ALP_PATH_TYPE_FILEIN_FROM_FILEIN0;
			break;

		case 1:
			alp_path_type = DEMUX_ALP_PATH_TYPE_FILEIN_FROM_MMFI0;
			break;

		case 2:
			alp_path_type = DEMUX_ALP_PATH_TYPE_FILEIN_FROM_MMFI1;
			break;

		default:
			MTK_DMX_ERR(
				"pcfg->port_num(%d) is not supported.\n",
				pcfg->port_num);
			break;
		}
		break;

	default:
		MTK_DMX_ERR(
			"pcfg->type(%d) is not supported.\n",
			pcfg->type);
		break;
	}

	return alp_path_type;
}

static int mtk_dmx_io_alp_set_src_and_path(
		u16 path,
		struct demux_io_cfg *pcfg)
{
	int ret = 0;
	demux_alp_instance_e alp_instance = DEMUX_ALP_INSTANCE_NULL;
	demux_alp_path_type_e alp_src = DEMUX_ALP_PATH_TYPE_NULL;
	demux_alp_path_type_e mtk_alp_top_src = DEMUX_ALP_PATH_TYPE_NULL;

	alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(path);
	if (alp_instance == DEMUX_ALP_INSTANCE_NULL)
		return -EINVAL;

	alp_src = mtk_dmx_io_demux_io_cfg_2_alp_src(pcfg);
	if (alp_src == DEMUX_ALP_PATH_TYPE_NULL)
		return -EINVAL;

	switch (alp_src) {
	case DEMUX_ALP_PATH_TYPE_LIVE_IN_MIN ... (DEMUX_ALP_PATH_TYPE_LIVE_IN_MAX - 1):
		mtk_alp_top_src = DEMUX_ALP_PATH_TYPE_MTK_ALP_TOP_FROM_TSIF_ALP;
		break;
	case DEMUX_ALP_PATH_TYPE_FILEIN_MIN ... (DEMUX_ALP_PATH_TYPE_FILEIN_MAX - 1):
		mtk_alp_top_src = DEMUX_ALP_PATH_TYPE_MTK_ALP_TOP_FROM_ALP_FILEIN_TOP;
		break;
	default:
		break;
	}
	if (mtk_alp_top_src == DEMUX_ALP_PATH_TYPE_NULL) {
		MTK_DMX_ERR(
			"alp_src(0x%x) is not supported.\n",
			alp_src);

		return -EINVAL;
	}

	ret = mtk_dmx_alp_set_path(alp_instance, mtk_alp_top_src);
	if (ret < 0) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_set_path(%d, 0x%x) failed\n",
			alp_instance,
			mtk_alp_top_src);

		return -EIO;
	}

	if (pcfg->type == DEMUX_IO_TYPE_MEM) {
		ret = mtk_dmx_alp_set_path(alp_instance, alp_src);
		if (ret < 0) {
			MTK_DMX_ERR(
				"mtk_dmx_alp_set_path(%d, 0x%x) failed\n",
				alp_instance,
				alp_src);

			return -EIO;
		}
	} else {
		mtk_dmx_io_sel_tsif_src(path, pcfg);
	}

	mtk_dmx_log_alp_graph_print(alp_instance);

	return ret;
}

static void mtk_dmx_io_alp_set_input_src(
		u16 path,
		struct demux_io_cfg *pcfg)
{
	if (pcfg->type == DEMUX_IO_TYPE_MEM) {
		switch (pcfg->port_num) {
		case 0: // filein
			mtk_dmx_io_set_tsif_fi_mux(false);
			break;

		case 1: // mmfi0
			// TODO
			break;

		case 2: // mmfi0
			// TODO
			break;

		default:
			break;
		}

		mtk_dmx_io_filein_en(path, true);
		mtk_dmx_io_filein_byte_delay(
			path,
			MTK_DMX_IO_ALP_FILEIN_BYTE_DELAY_DEFAULT);
	} else {
		mtk_dmx_io_set_tsif_fi_mux(true);
		mtk_dmx_io_filein_en(path, false);
	}

	mtk_dmx_io_alp_set_src_and_path(path, pcfg);
}

void mtk_dmx_io_set_input_src(u16 path, struct demux_io_cfg *pcfg,
	struct demux_filein_engine *pFileinEng)
{
	int filein_eng = (pFileinEng) ? pFileinEng->engine_id : -1;
	int tlv_mux = (pFileinEng) ? pFileinEng->tlv_mux : -1;
	bool filein_en = (pcfg->type == DEMUX_IO_TYPE_MEM) ? true : false;

	if (IS_TSIF_ALP(path)) { /* old mode */
		mtk_dmx_io_alp_set_input_src(path, pcfg);
	} else { /* new mode, using filein mux */
		if (filein_en) {
			mtk_dmx_io_filein_x_mux_sel(path, filein_eng, filein_en);
			mtk_dmx_io_filein_byte_delay_v2(filein_eng, 0xA);
		}
		else if (IS_TSIF_TS(path)) {
			mtk_dmx_io_set_tsif_fi_mux(true);
			mtk_dmx_io_sel_tsif_src(path, pcfg);
		}
		if (IS_TSIF_TLV(path)) {
			mtk_dmx_io_set_tlv_cfg(path, pcfg, filein_eng, tlv_mux);
			if (filein_en)
				mtk_dmx_io_tlv_filein_byte_delay(
					path,
					MTK_DMX_IO_FILEIN_BYTE_DELAY_DEFAULT);
		}
	}

	mtk_dmx_io_set_tsif_en(path, (filein_en) ? false : true);
}

void mtk_dmx_io_flt_null_pkt(u16 path, bool bEnable)
{
	switch (path) {
	case TSIF_0:
		vIO32WriteFld(REG_TSP5_BASE + REG_0018_TSP5,
			bEnable, TSP5_REG_FILTER_NULL_PKT0_0018);
		break;
	case TSIF_FI:
		vIO32WriteFld(REG_TSP5_BASE + REG_0018_TSP5,
			bEnable, TSP5_REG_FILTER_NULL_PKT_FILE_0018);
		break;
	case TSIF_1:
		vIO32WriteFld(REG_TSP5_BASE + REG_0018_TSP5,
			bEnable, TSP5_REG_FILTER_NULL_PKT1_0018);
		break;
	case TSIF_2:
		vIO32WriteFld(REG_TSP5_BASE + REG_0018_TSP5,
			bEnable, TSP5_REG_FILTER_NULL_PKT2_0018);
		break;
	default:
		break;
	}
}

static void mtk_dmx_io_filein_byte_delay_v2(int eng, u32 delay)
{
	switch (eng) {
	case FILEIN_MAIN:
		if (delay == 0) {
			vIO32WriteFld(REG_TSP0_BASE + REG_0168_TSP0, 0,
				TSP0_REG_BYTE_TIMER_EN_0168);
		} else {
			RegWrite4B(REG_TSP0_BASE + REG_01C0_TSP0,
				REG_TSP0_BASE + REG_01C4_TSP0,
				(delay & BYTE_TIMER_THRESHOLD_MASK));
			vIO32WriteFld(REG_TSP0_BASE + REG_0168_TSP0, 1,
				TSP0_REG_BYTE_TIMER_EN_0168);
		}
		break;
	case FILEIN_MM0: // mmfi0
		if (delay == 0) {
			vIO32WriteFld(REG_TSP2_BASE + REG_003C_TSP2, 0,
				TSP2_REG_MMFI0_CONFIG1_003C_3);
		} else {
			vIO32WriteFld(REG_TSP2_BASE + REG_0030_TSP2,
				(delay & BYTE_TIMER_THRESHOLD_MASK),
				TSP2_REG_TSP_FILE_TIMER_MMFI0_0030_15_8);
			vIO32WriteFld(REG_TSP2_BASE + REG_003C_TSP2, 1,
				TSP2_REG_MMFI0_CONFIG1_003C_3);
		}
		break;
	case FILEIN_MM1: // mmfi1
		if (delay == 0) {
			vIO32WriteFld(REG_TSP2_BASE + REG_00BC_TSP2, 0,
				TSP2_REG_MMFI1_CONFIG1_00BC_3);
		} else {
			vIO32WriteFld(REG_TSP2_BASE + REG_00B0_TSP2,
				(delay & BYTE_TIMER_THRESHOLD_MASK),
				TSP2_REG_TSP_FILE_TIMER_MMFI1_00B0_15_8);
			vIO32WriteFld(REG_TSP2_BASE + REG_00BC_TSP2, 1,
				TSP2_REG_MMFI1_CONFIG1_00BC_3);
		}
		break;
	default:
		return;
	}
}

static void mtk_dmx_io_filein_byte_delay(u16 path, u32 delay)
{
	int eng;

	switch (mtk_dmx_dev_get_predefined_path_id(path)) {
	case TSIF_0:
	case TSIF_FI:
	case TS_ALP_0:
	case TS_ALP_FI:
		eng = FILEIN_MAIN;
		break;
	case TSIF_1: // mmfi0
		eng = FILEIN_MM0;
		break;
	case TSIF_2: // mmfi1
		eng = FILEIN_MM1;
		break;
	default:
		return;
	}
	mtk_dmx_io_filein_byte_delay_v2(eng, delay);
}

static inline void mtk_dmx_io_set_tsif_fi_mux(bool bLivein)
{
	vIO32WriteFld(REG_TSP5_BASE + REG_0008_TSP5, (bLivein) ? 1 : 0, TSP5_REG_MUX_LIVE_FILE_PATH_0008);
}


void mtk_dmx_io_filein_x_mux_sel(u16 path, int eng, bool bEn)
{
	REG16 __iomem *reg;
	u32 fld, val = 0;

	mtk_dmx_io_filein_en_v2(path, eng, bEn);

	switch (path) {
	case TSIF_0:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC0_MUX_01E8;
		break;
	case TSIF_FI:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PCFI_MUX_01E8;
		break;
	case TSIF_1:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC1_MUX_01E8;
		break;
	case TSIF_2:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC2_MUX_01E8;
		break;
	default:
		return;
	}

	switch (eng) {
	case FILEIN_MAIN:
		if (IS_TSIF_TS(path))
			val = (bEn) ? 1 : 0;
		break;
	case FILEIN_MM0:
		if (IS_TSIF_TS(path))
			val = (bEn) ? 2 : 0;
		break;
	case FILEIN_MM1:
		if (IS_TSIF_TS(path))
			val = (bEn) ? 3 : 0;
		break;
	default:
		break;
	}

	vIO32WriteFld(reg, val, fld);

	MTK_DMX_DEBUG("set FILE eng:%x enable:%x path:%x\n", eng, bEn, path);
}

void mtk_dmx_io_get_filein_eng_sel(u16 path, int *eng, bool *bEn)
{
	REG16 __iomem *reg;
	u32 fld, val = 0;

	switch (path) {
	case TSIF_0:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC0_MUX_01E8;
		break;
	case TSIF_FI:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PCFI_MUX_01E8;
		break;
	case TSIF_1:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC1_MUX_01E8;
		break;
	case TSIF_2:
		reg = REG_TSP3_BASE + REG_01E8_TSP3;
		fld = TSP3_REG_PATH_PC2_MUX_01E8;
		break;
	default:
		*eng = FILEIN_MAIN;
		*bEn = false;
		return;
	}

	val = u4IO32ReadFld(reg, fld);

	*bEn = true;
	switch (val) {
	case D_1:
		*eng = FILEIN_MAIN;
		break;
	case D_2:
		*eng = FILEIN_MM0;
		break;
	case D_3:
		*eng = FILEIN_MM1;
		break;
	default:
		*eng = FILEIN_MAIN;
		*bEn = false;
		break;
	}

	MTK_DMX_DEBUG("get FILE eng:%x enable:%x path:%x\n", *eng, *bEn, path);
}

void mtk_dmx_io_filein_en_v2(u16 path, u16 eng, bool bEnable)
{
	u32 val = (bEnable) ? 1 : 0;
	enum dmx2_protocol_format fmt = DMX2_FORMAT_NONE;

	if (IS_TSIF_TS(path))
		fmt = DMX2_FORMAT_M2TS;
	else if (IS_TSIF_TLV(path))
		fmt = DMX2_FORMAT_ISDB3;
	else if (IS_TSIF_ALP(path))
		fmt = DMX2_FORMAT_ATSC3;

	switch (eng) {
	case FILEIN_MAIN:
		vIO32WriteFld(REG_TSP3_BASE + REG_0054_TSP3,
			(fmt == DMX2_FORMAT_ISDB3) ? 1 : 0, TSP3_REG_HW3_CONFIG1_0054_15);
		vIO32WriteFld(REG_TSP0_BASE + REG_01E8_TSP0, val,
			TSP0_REG_FILE_INPUT_01E8);
		vIO32WriteFld(REG_TSP0_BASE + REG_01FC_TSP0, val,
			TSP0_REG_TSP_FILE_SEGMENT_01FC);
		break;
	case FILEIN_MM0:
		vIO32WriteFld(REG_TSP2_BASE + REG_0140_TSP2,
			(fmt == DMX2_FORMAT_ISDB3) ? 1 : 0, TSP2_REG_MMFI0_CONFIG2_0140_12);
		break;
	case FILEIN_MM1:
		vIO32WriteFld(REG_TSP2_BASE + REG_0144_TSP2,
			(fmt == DMX2_FORMAT_ISDB3) ? 1 : 0, TSP2_REG_MMFI1_CONFIG2_0144_12);
		break;
	default:
		break;
	}
}

static void mtk_dmx_io_filein_en(u16 path, bool bEnable)
{
	u16 eng;

	switch (mtk_dmx_dev_get_predefined_path_id(path)) {
	case TSIF_0:
	case TSIF_FI:
	case TS_ALP_0:
	case TS_ALP_FI:
	case TLV_TSIF_0:
	case TLV_TSIF_1:
	case TLV_TSIF_2:
		eng = FILEIN_MAIN;
		break;
	case TSIF_1: // mmfi0
		eng = FILEIN_MM0;
		break;
	case TSIF_2: // mmfi1
		eng = FILEIN_MM1;
		break;
	default:
		return;
	}
	mtk_dmx_io_filein_en_v2(path, eng, bEnable);
}

void mtk_dmx_io_sel_input_clock_src(u16 path, u16 input_pad)
{
	REG16 __iomem *clk_src_offset;
	u32 clk_src_mask;
	u32 clk_disable_nr;

	switch (mtk_dmx_dev_get_predefined_path_id(path)) {
	case TSIF_0:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_0138_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TS0_0138_5_2;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TS0_0138_0;
		break;
	case TSIF_FI:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_0140_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TSFI_0140_13_10;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TSFI_0140_8;
		break;
	case TSIF_1:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_0138_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TS1_0138_13_10;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TS1_0138_8;
		break;
	case TSIF_2:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_013C_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TS2_013C_5_2;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TS2_013C_0;
		break;
	case TS_ALP_0:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_0144_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TS_ALP0_0144_5_2;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TS_ALP0_0144_0;
		break;
	case TS_ALP_FI:
		clk_src_offset = REG_TS_SAMPLE_BASE + REG_0148_TS_SAMPLE;
		clk_src_mask = TS_SAMPLE_REG_CKG_TS_ALP_FI_0148_13_10;
		clk_disable_nr = TS_SAMPLE_REG_CKG_TS_ALP_FI_0148_8;
		break;
	default:
		return;
	}

	// @TODO: use struct clk handle to control
	vIO32WriteFld(clk_src_offset, input_pad, clk_src_mask);
	vIO32WriteFld(clk_src_offset, 0, clk_disable_nr);
}


static void mtk_dmx_io_sel_tsif_src(u16 path, struct demux_io_cfg *pcfg)
{
	u16 input_pad;
	REG16 __iomem *tsif_ctrl_offset, *serial_ctrl_offset;
	REG16 __iomem *serial_2bits_offset;
	u32 ext_sync_ur, parallel_ur, enable_ur;
	u32 serial_ext_sync_1t, serial_2bit_ur;

	switch (pcfg->type) {
	case DEMUX_IO_TYPE_IN_DEMOD:
		input_pad = TSIF_CLK_DEMOD;
		break;
	case DEMUX_IO_TYPE_EXT_DEMOD:
		input_pad = TSIF_CLK_PAD0 + pcfg->port_num + 1;
		break;
	case DEMUX_IO_TYPE_CILINK:
		input_pad = TSIF_CLK_CILINK0 + pcfg->port_num;
		break;
	case DEMUX_IO_TYPE_TSO:
		input_pad = TSIF_CLK_TSO + pcfg->port_num;
		break;
	default:
		return;
	}

	switch (mtk_dmx_dev_get_predefined_path_id(path)) {
	case TSIF_0:
		tsif_ctrl_offset = REG_TSP0_BASE + REG_00B0_TSP0;
		parallel_ur = TSP0_REG_PARALLEL_SEL_00B0;
		ext_sync_ur = TSP0_REG_EXT_SYNC_SEL_00B0;
		enable_ur = 0;
		serial_ctrl_offset = 0;
		serial_ext_sync_1t = 0;
		serial_2bits_offset = REG_TSP5_BASE + REG_004C_TSP5;
		serial_2bit_ur = TSP5_REG_TSIF0_CONFIG_004C_0;
		break;
	case TSIF_FI:
		tsif_ctrl_offset = REG_TSP5_BASE + REG_0008_TSP5;
		parallel_ur = TSP5_REG_P_SEL_FI_0008;
		ext_sync_ur = TSP5_REG_EXT_SYNC_SEL_FI_0008;
		enable_ur = 0;
		serial_ctrl_offset = 0;
		serial_ext_sync_1t = 0;
		serial_2bits_offset = REG_TSP5_BASE + REG_004C_TSP5;
		serial_2bit_ur = TSP5_REG_TSIF_FI_CONFIG_004C_8;
		break;
	case TSIF_1:
		tsif_ctrl_offset = REG_TSP0_BASE + REG_010C_TSP0;
		parallel_ur = TSP0_REG_PARALLEL_SEL1_010C;
		ext_sync_ur = TSP0_REG_EXT_SYNC_SEL1_010C;
		enable_ur = 0;
		serial_ctrl_offset = 0;
		serial_ext_sync_1t = 0;
		serial_2bits_offset = REG_TSP5_BASE + REG_0050_TSP5;
		serial_2bit_ur = TSP5_REG_TSIF1_CONFIG_0050_0;
		break;
	case TSIF_2:
		tsif_ctrl_offset = REG_TSP0_BASE + REG_003C_TSP0;
		parallel_ur = TSP0_REG_P_SEL2_003C;
		ext_sync_ur = TSP0_REG_EXT_SYNC_SEL2_003C;
		enable_ur = 0;
		serial_ctrl_offset = 0;
		serial_ext_sync_1t = 0;
		serial_2bits_offset = REG_TSP5_BASE + REG_0050_TSP5;
		serial_2bit_ur = TSP5_REG_TSIF2_CONFIG_0050_8;
		break;
	case TS_ALP_0:
		tsif_ctrl_offset = REG_TSP6_BASE + REG_00B8_TSP6;
		parallel_ur = TSP6_REG_P_SEL_ALP0_00B8;
		ext_sync_ur = TSP6_REG_EXT_SYNC_SEL_ALP0_00B8;
		enable_ur = TSP6_REG_TSIF_ALP0_EN_00B8;
		serial_ctrl_offset = REG_TSP0_BASE + REG_016C_TSP0;
		serial_ext_sync_1t = TSP0_REG_SERIAL_EXT_SYNC_1T_016C;
		serial_2bits_offset = 0;
		serial_2bit_ur = 0;
		break;
	case TS_ALP_FI:
		tsif_ctrl_offset = REG_TSP6_BASE + REG_00BC_TSP6;
		parallel_ur = TSP6_REG_P_SEL_ALP_FI_00BC;
		ext_sync_ur = TSP6_REG_EXT_SYNC_SEL_ALP_FI_00BC;
		enable_ur = TSP6_REG_TSIF_ALP_FI_EN_00BC;
		serial_ctrl_offset = REG_TSP0_BASE + REG_016C_TSP0;
		serial_ext_sync_1t = TSP0_REG_SERIAL_EXT_SYNC_1T_016C;
		serial_2bits_offset = 0;
		serial_2bit_ur = 0;
		break;
	default:
		return;
	}

	mtk_dmx_io_sel_input_clock_src(path, input_pad);

	if (pcfg->port_type == DEMUX_PORT_TYPE_PARALLEL)
		vIO32WriteFld(tsif_ctrl_offset, 1, parallel_ur);
	else {
		vIO32WriteFld(tsif_ctrl_offset, 0, parallel_ur);

		// TODO: It is always enabled.
		if (serial_ext_sync_1t != 0)
			vIO32WriteFld(serial_ctrl_offset, 1, serial_ext_sync_1t);

		if (serial_2bits_offset != 0) {
			if (pcfg->port_type == DEMUX_PORT_TYPE_SERIAL_2BITS)
				vIO32WriteFld(serial_2bits_offset, 1, serial_2bit_ur);
			else
				vIO32WriteFld(serial_2bits_offset, 0, serial_2bit_ur);
		}
	}

	// TODO: It is always enabled.
	vIO32WriteFld(tsif_ctrl_offset, 1, ext_sync_ur);

	if (enable_ur != 0)
		vIO32WriteFld(tsif_ctrl_offset, 1, enable_ur);
}

void mtk_dmx_io_init_mmfi(void)
{
	// rst all
	vIO32Write2B(REG_TSP2_BASE + REG_0068_TSP2, 0xFFFE);
	vIO32Write2B(REG_TSP2_BASE + REG_0068_TSP2, 0x0001);
}

void mtk_dmx_io_get_video_fifo_status(void)
{
	u16 i;
	u32 status;
	char output[FIFO_OUTPUT_LENGTH] = { 0 };
	char number[FIFO_NUMBER_LENGTH] = { 0 };
	int ret = 0;
	u16 vfifo_num = VFIFO_NUMBER;
	bool Is_fifo_block = false;

	for (i = 0; i < vfifo_num; i++) {
		status = (mtk_dmx_get_video_fifo_level(i) << FIFO_SHIFT_LENGTH) |
				(mtk_dmx_get_video_fifo_full_bit(i) << 1) |
				(mtk_dmx_get_video_fifo_empty_bit(i));

		ret = snprintf(number, FIFO_NUMBER_LENGTH, "%x", status);
		if (ret < 0)
			MTK_DMX_ERR("snprintf error err code: %d\n", ret);

		if (status != 1)
			Is_fifo_block = true;

		strncat(output, number, sizeof(output) - 1);

		if (i != (vfifo_num - 1))
			strncat(output, " ", sizeof(output) - 1);
	}

	if (Is_fifo_block)
		MTK_DMX_ERR("Video fifo ststus unusual!: %s\n", output);
}

void mtk_dmx_io_get_audio_fifo_status(void)
{
	u16 i;
	u32 status;
	char output[FIFO_OUTPUT_LENGTH] = { 0 };
	char number[FIFO_NUMBER_LENGTH] = { 0 };
	int ret = 0;
	u16 afifo_num = AFIFO_NUMBER;
	bool Is_fifo_block = false;

	for (i = 0; i < afifo_num; i++) {
		status = (mtk_dmx_get_audio_fifo_level(i) << FIFO_SHIFT_LENGTH) |
				(mtk_dmx_get_audio_fifo_full_bit(i) << 1) |
				(mtk_dmx_get_audio_fifo_empty_bit(i));

		ret = snprintf(number, FIFO_NUMBER_LENGTH, "%x", status);
		if (ret < 0)
			MTK_DMX_ERR("snprintf error err code: %d\n", ret);

		if (status != 1)
			Is_fifo_block = true;

		strncat(output, number, sizeof(output) - 1);

		if (i != (afifo_num - 1))
			strncat(output, " ", sizeof(output) - 1);
	}

	if (Is_fifo_block)
		MTK_DMX_ERR("Audio fifo ststus unusual! : %s\n", output);
}

static bool mtk_dmx_io_feed_filein_v2(u16 eng, u64 paddr, u32 size, bool bInitTrust)
{
	u32 addr_l;
	u16 addr_h;
	bool fill_addr = true;

	if (paddr == 0 && size == 0) {
		fill_addr = false;
	} else {
		addr_l = (u32)paddr;
		addr_h = (u16)(paddr >> 32);
	}

	switch (eng) {
	case FILEIN_MM0: // mmfi0
		if (fill_addr) {
		// addr
			RegWrite4B(REG_TSP2_BASE + REG_0020_TSP2,
				REG_TSP2_BASE + REG_0024_TSP2, addr_l);
			vIO32WriteFldMulti(REG_TSP2_BASE + REG_00E8_TSP2, 2,
				(addr_h & 1), TSP2_REG_TSP_FILEIN_RADDR_MMFI0_EXT_00E8_0,
				((addr_h >> 1) & 1), TSP2_REG_TSP_FILEIN_RADDR_MMFI0_EXT_00E8_RESERVED_1);
		// size
			RegWrite4B(REG_TSP2_BASE + REG_0028_TSP2, REG_TSP2_BASE + REG_002C_TSP2, size);
		}
		// ctrl
		if (bInitTrust == true)
			vIO32WriteFldMulti(REG_TSP2_BASE + REG_0030_TSP2, 2,
				1, TSP2_REG_TSP_FILEIN_RSTART_MMFI0_0030,
				1, TSP2_REG_TSP_FILEIN_INIT_TRUST_MMFI0_0030);
		else
			vIO32WriteFld(REG_TSP2_BASE + REG_0030_TSP2, 1,
				TSP2_REG_TSP_FILEIN_RSTART_MMFI0_0030);
		break;
	case FILEIN_MM1: // mmfi1
		if (fill_addr) {
		// addr
			RegWrite4B(REG_TSP2_BASE + REG_00A0_TSP2,
				REG_TSP2_BASE + REG_00A4_TSP2, addr_l);
			vIO32WriteFldMulti(REG_TSP2_BASE + REG_00EC_TSP2, 2,
				(addr_h & 1), TSP2_REG_TSP_FILEIN_RADDR_MMFI1_EXT_00EC_0,
				((addr_h >> 1) & 1), TSP2_REG_TSP_FILEIN_RADDR_MMFI1_EXT_00EC_RESERVED_1);
		// size
			RegWrite4B(REG_TSP2_BASE + REG_00A8_TSP2, REG_TSP2_BASE + REG_00AC_TSP2, size);
		}
		// ctrl
		if (bInitTrust == true)
			vIO32WriteFldMulti(REG_TSP2_BASE + REG_00B0_TSP2, 2,
				1, TSP2_REG_TSP_FILEIN_RSTART_MMFI1_00B0,
				1, TSP2_REG_TSP_FILEIN_INIT_TRUST_MMFI1_00B0);
		else
			vIO32WriteFld(REG_TSP2_BASE + REG_00B0_TSP2, 1,
				TSP2_REG_TSP_FILEIN_RSTART_MMFI1_00B0);
		break;
	default: // main filein
		if (fill_addr) {
		// addr
			RegWrite4B(REG_TSP0_BASE + REG_00E8_TSP0,
				REG_TSP0_BASE + REG_00EC_TSP0, addr_l);
			vIO32WriteFldMulti(REG_TSP0_BASE + REG_0030_TSP0, 2,
				(addr_h & 1), TSP0_REG_TSP_FILEIN_RADDR_EXT_0030_0,
				((addr_h >> 1) & 1), TSP0_REG_TSP_FILEIN_RADDR_EXT_0030_RESERVED_1);
		// size
			RegWrite4B(REG_TSP0_BASE + REG_00F0_TSP0, REG_TSP0_BASE + REG_00F4_TSP0, size);
		}
		// ctrl
		if (bInitTrust == true)
			vIO32WriteFldMulti(REG_TSP0_BASE + REG_00F8_TSP0, 2,
				1, TSP0_REG_TSP_FILEIN_RSTART_00F8,
				1, TSP0_REG_TSP_FILEIN_INIT_TRUST_00F8);
		else
			vIO32WriteFld(REG_TSP0_BASE + REG_00F8_TSP0, 1,
				TSP0_REG_TSP_FILEIN_RSTART_00F8);
		break;
	}

	return true;
}

bool mtk_dmx_io_feed_filein(u16 eng, u64 paddr, u32 size, bool bInitTrust)
{
	if (size == 0) {
		MTK_DMX_DEBUG("paddr 0x%llx size %d\n", paddr, size);
		return true;
	}

	return mtk_dmx_io_feed_filein_v2(eng, paddr, size, bInitTrust);
}

bool mtk_dmx_io_start_filein(u16 eng, bool bInitTrust)
{
	return mtk_dmx_io_feed_filein_v2(eng, 0, 0, bInitTrust);
}

enum demux_filein_state mtk_dmx_io_get_filein_state(u16 eng, u16 *cmdq_cnt)
{
	switch (eng) {
	case FILEIN_MM0: // mmfi0
		vIO32WriteFld(REG_TSP2_BASE + REG_0038_TSP2, 1, TSP2_REG_MMFI0_CONFIG0_0038_15);
		vIO32WriteFld(REG_TSP2_BASE + REG_0038_TSP2, 0, TSP2_REG_MMFI0_CONFIG0_0038_15);
		*cmdq_cnt = u4IO32ReadFld(REG_TSP2_BASE + REG_0034_TSP2,
			TSP2_REG_CMD_QUEUE_STATUS_MMFI0_0034_4_0);
		if (u4IO32ReadFld(REG_TSP0_BASE + REG_01FC_TSP0, TSP0_REG_TSP_FILE_PAUSE_01FC))
			return DEMUX_FILEIN_STATE_PAUSE;
		else if (u4IO32ReadFld(REG_TSP2_BASE + REG_0030_TSP2, TSP2_REG_TSP_FILEIN_RSTART_MMFI0_0030) ||
				*cmdq_cnt)
			return DEMUX_FILEIN_STATE_BUSY;
		else
			return DEMUX_FILEIN_STATE_IDLE;
	case FILEIN_MM1: // mmfi1
		vIO32WriteFld(REG_TSP2_BASE + REG_00B8_TSP2, 1, TSP2_REG_MMFI1_CONFIG0_00B8_15);
		vIO32WriteFld(REG_TSP2_BASE + REG_00B8_TSP2, 0, TSP2_REG_MMFI1_CONFIG0_00B8_15);
		*cmdq_cnt = u4IO32ReadFld(REG_TSP2_BASE + REG_00B4_TSP2,
			TSP2_REG_CMD_QUEUE_STATUS_MMFI1_00B4_4_0);
		if (u4IO32ReadFld(REG_TSP0_BASE + REG_01FC_TSP0, TSP0_REG_TSP_FILE_PAUSE_01FC))
			return DEMUX_FILEIN_STATE_PAUSE;
		else if (u4IO32ReadFld(REG_TSP2_BASE + REG_00B0_TSP2, TSP2_REG_TSP_FILEIN_RSTART_MMFI1_00B0) ||
				*cmdq_cnt)
			return DEMUX_FILEIN_STATE_BUSY;
		else
			return DEMUX_FILEIN_STATE_IDLE;
	default: // main filein
		*cmdq_cnt = u4IO32ReadFld(REG_TSP0_BASE + REG_00FC_TSP0,
			TSP0_REG_CMDQ_FIFO_STATUS_00FC_4_0);
		if (u4IO32ReadFld(REG_TSP0_BASE + REG_01FC_TSP0, TSP0_REG_TSP_FILE_PAUSE_01FC))
			return DEMUX_FILEIN_STATE_PAUSE;
		else if (u4IO32ReadFld(REG_TSP0_BASE + REG_00F8_TSP0, TSP0_REG_TSP_FILEIN_RSTART_00F8) ||
				*cmdq_cnt)
			return DEMUX_FILEIN_STATE_BUSY;
		else
			return DEMUX_FILEIN_STATE_IDLE;
	}
}
EXPORT_SYMBOL(mtk_dmx_io_get_filein_state);

void mtk_dmx_filein_dmaw_protect(int eng, u64 lbound, u64 ubound)
{
#define FILEIN_DMAW_WADDR_ERR (0b1001)

	REG16 __iomem *lbound_offset, *ubound_offset;

	MTK_DMX_INFO("lbound 0x%llx ubound 0x%llx\n", lbound, ubound);

	switch (eng) {
	case FILEIN_MAIN:
		lbound_offset = REG_TSP5_BASE + REG_0080_TSP5;
		ubound_offset = REG_TSP5_BASE + REG_0088_TSP5;
		break;

	case FILEIN_MM0: // mmfi0
		lbound_offset = REG_TSP5_BASE + REG_0090_TSP5;
		ubound_offset = REG_TSP5_BASE + REG_0098_TSP5;
		break;

	case FILEIN_MM1: // mmfi1
		lbound_offset = REG_TSP5_BASE + REG_00A0_TSP5;
		ubound_offset = REG_TSP5_BASE + REG_00A8_TSP5;
		break;

	default:
		MTK_DMX_ERR("eng %d is not supported\n", eng);
		return;
	}

	RegWrite4B(lbound_offset, lbound_offset + REG_SHIFT, MIU_ADDR(lbound));
	RegWrite4B(ubound_offset, ubound_offset + REG_SHIFT, MIU_ADDR(ubound));

	vIO32WriteFld(REG_TSP1_BASE + REG_0070_TSP1, FILEIN_DMAW_WADDR_ERR,
		TSP1_REG_DMAW_WADDR_ERR_SRC_SEL_0070);

#undef FILEIN_DMAW_WADDR_ERR
}

static bool mtk_dmx_io_wait_done(
		REG16 *reg32,
		uint32_t fld,
		struct mtk_dmx_io_wait_params wait_params)
{
	u8 i = 0;

	while (i < wait_params.retry_count) {
		if (u4IO32ReadFld(reg32, fld))
			break;
		usleep_range(wait_params.min, wait_params.max);
		i++;
	}

	if (i >= wait_params.retry_count)
		return false;

	return true;
}

static int mtk_dmx_io_get_tsp_reg(struct eng_reg_fld *eng_reg_fld)
{
	if (eng_reg_fld == NULL)
		return -EINVAL;

	switch (eng_reg_fld->base) {
	case TSP0_BASE ... TSP8_BASE:
		eng_reg_fld->reg32 =
			dmx_bank_offsets[RIU_BANK_TYPE_TS].ptr[eng_reg_fld->base] +
			eng_reg_fld->offset;
		break;
	default:
		MTK_DMX_ERR(
				"eng_reg_fld->base %d is not supported\n",
				eng_reg_fld->base);
		return -EINVAL;
	}

	return 0;
}

static void mtk_dmx_io_print_eng_reg_fld(struct eng_reg_fld *eng_reg_fld)
{
	if (eng_reg_fld == NULL)
		return;

	MTK_DMX_DEBUG(
		"eng %d base %d fld 0x%x reg32 0x%llx offset 0x%x\n",
		eng_reg_fld->eng,
		eng_reg_fld->base,
		eng_reg_fld->fld,
		(u64)eng_reg_fld->reg32,
		eng_reg_fld->offset);
}

static int mtk_dmx_io_get_eng_reg_fld(
		u16 eng,
		struct eng_reg_fld *eng_reg_flds,
		size_t array_size,
		struct eng_reg_fld *eng_reg_fld)
{
	int i;
	int found = 0; // Put default in [0] always

	if (eng_reg_flds == NULL)
		return -EINVAL;
	if (eng_reg_fld == NULL)
		return -EINVAL;
	if (array_size <= 0)
		return -EINVAL;

	for (i = 1; i < array_size; i++) {
		if (eng_reg_flds[i].eng == eng) {
			found = i;
			break;
		}
	}

	memcpy(eng_reg_fld, &eng_reg_flds[found], sizeof(eng_reg_flds[found]));
	if (mtk_dmx_io_get_tsp_reg(eng_reg_fld) != 0) {
		MTK_DMX_ERR(
				"mtk_dmx_io_get_tsp_reg is failed\n");
		return -EINVAL;
	}
	mtk_dmx_io_print_eng_reg_fld(eng_reg_fld);

	return 0;
}

bool mtk_dmx_io_is_filein_done(u16 eng)
{
	struct eng_reg_fld eng_reg_fld;

	if (mtk_dmx_io_get_eng_reg_fld(
			eng,
			filein_done_eng_reg_flds,
			ARRAY_SIZE(filein_done_eng_reg_flds),
			&eng_reg_fld) != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_io_get_eng_reg_fld is failed\n");
		return false;
	}

	return u4IO32ReadFld(eng_reg_fld.reg32, eng_reg_fld.fld);
}

bool mtk_dmx_io_wait_filein_done(u16 eng)
{
	struct eng_reg_fld eng_reg_fld;
	struct mtk_dmx_io_wait_params wait_params = {
		.retry_count = FILEIN_RESET_RETRY_COUNT,
		.min = FILEIN_RESET_SLEEP_RANGE_L,
		.max = FILEIN_RESET_SLEEP_RANGE_H,
	};
	bool is_done = false;

	if (mtk_dmx_io_get_eng_reg_fld(
			eng,
			filein_done_eng_reg_flds,
			ARRAY_SIZE(filein_done_eng_reg_flds),
			&eng_reg_fld) != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_io_get_eng_reg_fld is failed\n");
		return false;
	}

	is_done = mtk_dmx_io_wait_done(eng_reg_fld.reg32, eng_reg_fld.fld, wait_params);
	if (!is_done)
		MTK_DMX_ERR("Waiting filein %d done is failed\n", eng);

	return is_done;
}

bool mtk_dmx_io_is_filein_fms_reset_finished(u16 eng)
{
	struct eng_reg_fld eng_reg_fld;

	if (mtk_dmx_io_get_eng_reg_fld(
			eng,
			filein_fms_reset_finished_eng_reg_flds,
			ARRAY_SIZE(filein_fms_reset_finished_eng_reg_flds),
			&eng_reg_fld) != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_io_get_eng_reg_fld is failed\n");
		return false;
	}

	return u4IO32ReadFld(eng_reg_fld.reg32, eng_reg_fld.fld);
}

bool mtk_dmx_io_wait_filein_fms_reset_finished(u16 eng)
{
	struct eng_reg_fld eng_reg_fld;
	struct mtk_dmx_io_wait_params wait_params = {
		.retry_count = FILEIN_RESET_RETRY_COUNT,
		.min = FILEIN_RESET_SLEEP_RANGE_L,
		.max = FILEIN_RESET_SLEEP_RANGE_H,
	};
	bool is_done = false;

	if (mtk_dmx_io_get_eng_reg_fld(
			eng,
			filein_fms_reset_finished_eng_reg_flds,
			ARRAY_SIZE(filein_fms_reset_finished_eng_reg_flds),
			&eng_reg_fld) != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_io_get_eng_reg_fld is failed\n");
		return false;
	}

	is_done = mtk_dmx_io_wait_done(eng_reg_fld.reg32, eng_reg_fld.fld, wait_params);
	if (!is_done)
		MTK_DMX_ERR("Waiting filein %d fms reset finished is failed\n", eng);

	return is_done;
}

bool mtk_dmx_io_wait_filein_status_idle(u16 eng)
{
	bool is_done = true;
	int retry_cnt = FILEIN_RESET_RETRY_COUNT;
	u16 cmdq_cnt;
	enum demux_filein_state demux_filein_state;

	while ((demux_filein_state = mtk_dmx_io_get_filein_state(eng, &cmdq_cnt))
			!= DEMUX_FILEIN_STATE_IDLE) {
		if (--retry_cnt < 0) {
			MTK_DMX_ERR("Warning! timeout\n");
			mtk_dmx_io_get_audio_fifo_status();
			mtk_dmx_io_get_video_fifo_status();
			is_done = false;
			break;
		}
		usleep_range(FILEIN_RESET_SLEEP_RANGE_L, FILEIN_RESET_SLEEP_RANGE_H);
	}
	if (!is_done)
		MTK_DMX_ERR(
				"Waiting filein %d idle is failed. status %d cmdq_cnt %d\n",
				eng,
				demux_filein_state,
				cmdq_cnt);

	return is_done;
}

void mtk_dmx_io_miup_debug(void)
{
	int i;
	enum demux_filein_state demux_filein_state;
	u16 cmdq_cnt;

	for (i = 0; i < ARRAY_SIZE(filein_done_eng_reg_flds); i++) {
		if (!mtk_dmx_io_is_filein_done(
				filein_done_eng_reg_flds[i].eng))
			pr_warn_ratelimited(
				"eng %d is not filein done\n",
				filein_done_eng_reg_flds[i].eng);
	}

	for (i = 0; i < ARRAY_SIZE(filein_fms_reset_finished_eng_reg_flds); i++) {
		if (!mtk_dmx_io_is_filein_fms_reset_finished(
				filein_fms_reset_finished_eng_reg_flds[i].eng))
			pr_warn_ratelimited(
				"eng %d is not filein fms reset finished\n",
				filein_fms_reset_finished_eng_reg_flds[i].eng);
	}

	for (i = 0; i < FILEIN_MAX_NUM; i++) {
		demux_filein_state = mtk_dmx_io_get_filein_state(i, &cmdq_cnt);
		if (demux_filein_state != DEMUX_FILEIN_STATE_IDLE)
			pr_warn_ratelimited(
				"eng %d demux_filein_state %d cmdq_cnt %d\n",
				i,
				demux_filein_state,
				cmdq_cnt);
	}
}
EXPORT_SYMBOL(mtk_dmx_io_miup_debug);

bool mtk_dmx_io_filein_reset(u16 eng)
{
#define FILEIN_DONE (0)
#define FILEIN_FMS_RESET_FINISHED (1)
#define FILEIN_STATUS_IDLE (2)
#define FILEIN_IS_DONE_MAX (3)
	bool is_done[FILEIN_IS_DONE_MAX] = { false };

	switch (eng) {
	case FILEIN_MM0: // mmfi0
		// reset filein cmdQ
		vIO32WriteFld(REG_TSP2_BASE + REG_0068_TSP2, 1, TSP2_REG_SW_RSTZ_0068_2);
		vIO32WriteFld(REG_TSP2_BASE + REG_0068_TSP2, 0, TSP2_REG_SW_RSTZ_0068_2);
		// reset the last data that hw is excuting
		vIO32WriteFld(REG_TSP2_BASE + REG_0140_TSP2, 1, TSP2_REG_MMFI0_CONFIG2_0140_10);
		is_done[FILEIN_FMS_RESET_FINISHED] = true;
		is_done[FILEIN_DONE] = mtk_dmx_io_wait_filein_done(eng);
		vIO32WriteFld(REG_TSP2_BASE + REG_0140_TSP2, 0, TSP2_REG_MMFI0_CONFIG2_0140_10);
		is_done[FILEIN_STATUS_IDLE] = mtk_dmx_io_wait_filein_status_idle(eng);
		// reset filein status
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 1, TSP6_REG_RESET_CTRL2_0098_10);
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 0, TSP6_REG_RESET_CTRL2_0098_10);
		// init file-in time-stamp
		RegWrite4B(REG_TSP5_BASE + REG_0194_TSP5, REG_TSP5_BASE + REG_0198_TSP5, 0);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 1, TSP5_REG_INIT_TIMESTAMP_001C_1);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 0, TSP5_REG_INIT_TIMESTAMP_001C_1);
		break;
	case FILEIN_MM1: // mmfi1
		// reset filein cmdQ
		vIO32WriteFld(REG_TSP2_BASE + REG_0068_TSP2, 1, TSP2_REG_SW_RSTZ_0068_6);
		vIO32WriteFld(REG_TSP2_BASE + REG_0068_TSP2, 0, TSP2_REG_SW_RSTZ_0068_6);
		// reset the last data that hw is excuting
		vIO32WriteFld(REG_TSP2_BASE + REG_0144_TSP2, 1, TSP2_REG_MMFI1_CONFIG2_0144_10);
		is_done[FILEIN_FMS_RESET_FINISHED] = true;
		is_done[FILEIN_DONE] = mtk_dmx_io_wait_filein_done(eng);
		vIO32WriteFld(REG_TSP2_BASE + REG_0144_TSP2, 0, TSP2_REG_MMFI1_CONFIG2_0144_10);
		is_done[FILEIN_STATUS_IDLE] = mtk_dmx_io_wait_filein_status_idle(eng);
		// reset filein status
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 1, TSP6_REG_RESET_CTRL2_0098_11);
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 0, TSP6_REG_RESET_CTRL2_0098_11);
		// init file-in time-stamp
		RegWrite4B(REG_TSP5_BASE + REG_019C_TSP5, REG_TSP5_BASE + REG_01A0_TSP5, 0);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 1, TSP5_REG_INIT_TIMESTAMP_001C_2);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 0, TSP5_REG_INIT_TIMESTAMP_001C_2);
		break;
	default: // main filein
		// reset filein cmdQ
		vIO32WriteFld(REG_TSP0_BASE + REG_01FC_TSP0, 1, TSP0_REG_CMDQ_RSTZ_01FC);
		vIO32WriteFld(REG_TSP0_BASE + REG_01FC_TSP0, 0, TSP0_REG_CMDQ_RSTZ_01FC);
		// reset the last data that hw is excuting
		vIO32WriteFld(REG_TSP5_BASE + REG_0010_TSP5, 1, TSP5_REG_WB_FSM_RESET_0010);
		is_done[FILEIN_FMS_RESET_FINISHED] = mtk_dmx_io_wait_filein_fms_reset_finished(eng);
		is_done[FILEIN_DONE] = mtk_dmx_io_wait_filein_done(eng);
		vIO32WriteFld(REG_TSP5_BASE + REG_0010_TSP5, 0, TSP5_REG_WB_FSM_RESET_0010);
		is_done[FILEIN_STATUS_IDLE] = mtk_dmx_io_wait_filein_status_idle(eng);

		// reset filein status
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 1, TSP6_REG_RESET_CTRL2_0098_8);
		vIO32WriteFld(REG_TSP6_BASE + REG_0098_TSP6, 0, TSP6_REG_RESET_CTRL2_0098_8);
		// init file-in time-stamp
		RegWrite4B(REG_TSP5_BASE + REG_018C_TSP5, REG_TSP5_BASE + REG_0190_TSP5, 0);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 1, TSP5_REG_INIT_TIMESTAMP_001C_0);
		vIO32WriteFld(REG_TSP5_BASE + REG_001C_TSP5, 0, TSP5_REG_INIT_TIMESTAMP_001C_0);
		break;
	}

	return is_done[FILEIN_DONE] &&
			is_done[FILEIN_FMS_RESET_FINISHED] &&
			is_done[FILEIN_STATUS_IDLE];

#undef FILEIN_DONE
#undef FILEIN_FMS_RESET_FINISHED
#undef FILEIN_STATUS_IDLE
#undef FILEIN_IS_DONE_MAX
}

void mtk_dmx_io_set_filein_cfg(u16 eng, enum demux_in_format format, bool timestamp_en)
{
#define PKT_192_MODE 0xBF
#define PKT_188_MODE 0xBB
	bool timestamp_blk_disable, timestamp_enable;
	u16	 pkt_size;

	switch (format) {
	case DEMUX_IN_FORMAT_M2TS_TIMESTAMP:
		pkt_size = PKT_192_MODE;
		break;
	case DEMUX_IN_FORMAT_M2TS:
	case DEMUX_IN_FORMAT_TLV:
	case DEMUX_IN_FORMAT_ALP:
		pkt_size = PKT_188_MODE;
		break;
	default:
		return;
	}

	timestamp_enable = (timestamp_en) ? true : false;
	timestamp_blk_disable = (timestamp_en) ? true : false;

	switch (eng) {
	default: // main filein
		vIO32WriteFld(REG_TSP1_BASE + REG_0020_TSP1, pkt_size,
			TSP1_REG_PKT_CHK_SIZE_FIN_0020);
		vIO32WriteFld(REG_TSP1_BASE + REG_0018_TSP1, timestamp_enable ? 1 : 0,
			TSP1_REG_PKT192_EN_FILEIN_0018);
		vIO32WriteFld(REG_TSP1_BASE + REG_0020_TSP1,
			timestamp_blk_disable ? 1 : 0,
			TSP1_REG_PKT192_BLK_DIS_FIN_0020);
		break;
	case FILEIN_MM0: // mmfi0
		vIO32WriteFld(REG_TSP2_BASE + REG_0070_TSP2, pkt_size,
			TSP2_REG_PKT_CHK_SIZE_MMFI0_0070_7_0);
		vIO32WriteFld(REG_TSP2_BASE + REG_003C_TSP2, timestamp_enable ? 1 : 0,
			TSP2_REG_MMFI0_CONFIG1_003C_14);
		vIO32WriteFld(REG_TSP2_BASE + REG_003C_TSP2,
			timestamp_blk_disable ? 1 : 0,
			TSP2_REG_MMFI0_CONFIG1_003C_15);
		break;
	case FILEIN_MM1: // mmfi1
		vIO32WriteFld(REG_TSP2_BASE + REG_00F0_TSP2, pkt_size,
			TSP2_REG_PKT_CHK_SIZE_MMFI1_00F0_7_0);
		vIO32WriteFld(REG_TSP2_BASE + REG_00BC_TSP2, timestamp_enable ? 1 : 0,
			TSP2_REG_MMFI1_CONFIG1_00BC_14);
		vIO32WriteFld(REG_TSP2_BASE + REG_00BC_TSP2,
			timestamp_blk_disable ? 1 : 0,
			TSP2_REG_MMFI1_CONFIG1_00BC_15);
		break;
	}
	mtk_dmx_io_filein_reset(eng);
#undef PKT_188_MODE
#undef PKT_192_MODE
}

static void mtk_dmx_io_tso_tsif_en(u16 eng, u16 tsif_id, bool bEnable)
{
	u32 tsif_en_nr;
	REG16 __iomem *base;

	switch (eng) {
	case 0:
		switch (tsif_id) {
		case TSO_TSIF_1:
			base = REG_TSO0_BASE + REG_0074_TSO;
			tsif_en_nr = TSO0_REG_TSO_CONFIG1_0074_1;
			break;
		case TSO_TSIF_5:
			base = REG_TSO0_BASE + REG_0074_TSO;
			tsif_en_nr = TSO0_REG_TSO_CONFIG1_0074_5;
			break;
		case TSO_TSIF_6:
			base = REG_TSO0_BASE + REG_0074_TSO;
			tsif_en_nr = TSO0_REG_TSO_CONFIG1_0074_6;
			break;
		default:
			return;
		}
		break;
	case 1:
		switch (tsif_id) {
		case TSO_TSIF_1:
			base = REG_TSOSEC0_BASE + REG_0074_TSO_SEC0;
			tsif_en_nr = TSO_SEC0_REG_TSO_CONFIG1_0074_1;
			break;
		case TSO_TSIF_5:
			base = REG_TSOSEC0_BASE + REG_0074_TSO_SEC0;
			tsif_en_nr = TSO_SEC0_REG_TSO_CONFIG1_0074_5;
			break;
		case TSO_TSIF_6:
			base = REG_TSOSEC0_BASE + REG_0074_TSO_SEC0;
			tsif_en_nr = TSO_SEC0_REG_TSO_CONFIG1_0074_6;
			break;
		default:
			return;
		}
		break;
	default:
		return;
	}

	if (bEnable)
		vIO32WriteFld(base, 1, tsif_en_nr);
	else
		vIO32WriteFld(base, 0, tsif_en_nr);
}

static void mtk_dmx_io_tso_sel_tsif_src(u16 eng, u16 tsif_id, struct demux_io_cfg cfg)
{
	REG16 __iomem *clk_src_offset, *tsif_ctrl_offset, *file_cfg_offset;

	u16 input_pad;
	u32 clk_src_mask;
	u32 clk_disable_nr;
	u32 tsif_ctrl_p_sel;
	u32 tsif_ctrl_sin_c0, tsif_ctrl_sin_c1;
	u32 tsif_ctrl_ext_sync_sel;
	u32 tsif_ctrl_pdflt_rec_all, tsif_ctrl_pdflt_rec_null;
	u32 file_cfg_port_sel, file_cfg_en, file_cfg_timer_en, file_cfg_tsp_file_seg;

	switch (cfg.type) {
	case DEMUX_IO_TYPE_IN_DEMOD:
		input_pad = TSIF_CLK_DEMOD;
		break;
	case DEMUX_IO_TYPE_MEM:
		input_pad = 0xFF;
		break;
	case DEMUX_IO_TYPE_EXT_DEMOD:
		input_pad = TSIF_CLK_PAD0 + cfg.port_num + 1;
		break;
	default:
		return;
	}

	switch (eng) {
	case 0:
		switch (tsif_id) {
		case TSO_TSIF_1:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_014C_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO_CH1_014C_13_10;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO_CH1_014C_8;
			tsif_ctrl_offset = REG_TSO0_BASE + REG_0018_TSO;
			tsif_ctrl_p_sel = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_0;
			tsif_ctrl_sin_c0 = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_2;
			tsif_ctrl_sin_c1 = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_3;
			tsif_ctrl_ext_sync_sel = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_1;
			tsif_ctrl_pdflt_rec_all = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_4;
			tsif_ctrl_pdflt_rec_null = TSO0_REG_CHANNEL0_IF1_CONFIG2_0018_5;
			file_cfg_offset = 0;
			break;
		case TSO_TSIF_5:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_0150_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO_CH2_0150_5_2;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO_CH2_0150_0;
			tsif_ctrl_offset = REG_TSO0_BASE + REG_0058_TSO;
			tsif_ctrl_p_sel = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_0;
			tsif_ctrl_sin_c0 = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_2;
			tsif_ctrl_sin_c1 = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_3;
			tsif_ctrl_ext_sync_sel = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_1;
			tsif_ctrl_pdflt_rec_all = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_4;
			tsif_ctrl_pdflt_rec_null = TSO0_REG_CHANNEL0_IF5_CONFIG2_0058_5;
			file_cfg_offset = REG_TSO0_BASE + REG_01E4_TSO;
			file_cfg_port_sel = TSO0_REG_TSO_FILE_CONFIG_01E4_7;
			file_cfg_en = TSO0_REG_TSO_FILE_CONFIG_01E4_8;
			file_cfg_timer_en = TSO0_REG_TSO_FILE_CONFIG_01E4_9;
			file_cfg_tsp_file_seg = TSO0_REG_TSO_FILE_CONFIG_01E4_12;
			break;
		case TSO_TSIF_6:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_0150_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO_CH3_0150_13_10;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO_CH3_0150_8;
			tsif_ctrl_offset = REG_TSO0_BASE + REG_0068_TSO;
			tsif_ctrl_p_sel = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_0;
			tsif_ctrl_sin_c0 = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_2;
			tsif_ctrl_sin_c1 = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_3;
			tsif_ctrl_ext_sync_sel = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_1;
			tsif_ctrl_pdflt_rec_all = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_4;
			tsif_ctrl_pdflt_rec_null = TSO0_REG_CHANNEL0_IF6_CONFIG2_0068_5;
			file_cfg_offset = REG_TSO0_BASE + REG_01E8_TSO;
			file_cfg_port_sel = TSO0_REG_TSO_FILE_CONFIG1_01E8_7;
			file_cfg_en = TSO0_REG_TSO_FILE_CONFIG1_01E8_8;
			file_cfg_timer_en = TSO0_REG_TSO_FILE_CONFIG1_01E8_9;
			file_cfg_tsp_file_seg = TSO0_REG_TSO_FILE_CONFIG1_01E8_12;
			break;
		default:
			return;
		}
		break;
	case 1:
		switch (tsif_id) {
		case TSO_TSIF_1:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_00C0_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO2_CH1_00C0_13_10;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO2_CH1_00C0_8;
			tsif_ctrl_offset = REG_TSOSEC0_BASE + REG_0018_TSO_SEC0;
			tsif_ctrl_p_sel = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_0;
			tsif_ctrl_sin_c0 = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_2;
			tsif_ctrl_sin_c1 = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_3;
			tsif_ctrl_ext_sync_sel = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_1;
			tsif_ctrl_pdflt_rec_all = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_4;
			tsif_ctrl_pdflt_rec_null = TSO_SEC0_REG_CHANNEL0_IF1_CONFIG2_0018_5;
			file_cfg_offset = 0;
			break;
		case TSO_TSIF_5:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_00C4_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO2_CH2_00C4_5_2;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO2_CH2_00C4_0;
			tsif_ctrl_offset = REG_TSOSEC0_BASE + REG_0058_TSO_SEC0;
			tsif_ctrl_p_sel = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_0;
			tsif_ctrl_sin_c0 = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_2;
			tsif_ctrl_sin_c1 = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_3;
			tsif_ctrl_ext_sync_sel = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_1;
			tsif_ctrl_pdflt_rec_all = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_4;
			tsif_ctrl_pdflt_rec_null = TSO_SEC0_REG_CHANNEL0_IF5_CONFIG2_0058_5;
			file_cfg_offset = REG_TSOSEC0_BASE + REG_01E4_TSO_SEC0;
			file_cfg_port_sel = TSO_SEC0_REG_TSO_FILE_CONFIG_01E4_7;
			file_cfg_en = TSO_SEC0_REG_TSO_FILE_CONFIG_01E4_8;
			file_cfg_timer_en = TSO_SEC0_REG_TSO_FILE_CONFIG_01E4_9;
			file_cfg_tsp_file_seg = TSO_SEC0_REG_TSO_FILE_CONFIG_01E4_12;
			break;
		case TSO_TSIF_6:
			clk_src_offset = REG_TS_SAMPLE_BASE + REG_00C4_TS_SAMPLE;
			clk_src_mask = TS_SAMPLE_REG_CKG_TSO2_CH3_00C4_13_10;
			clk_disable_nr = TS_SAMPLE_REG_CKG_TSO2_CH3_00C4_8;
			tsif_ctrl_offset = REG_TSOSEC0_BASE + REG_0068_TSO_SEC0;
			tsif_ctrl_p_sel = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_0;
			tsif_ctrl_sin_c0 = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_2;
			tsif_ctrl_sin_c1 = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_3;
			tsif_ctrl_ext_sync_sel = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_1;
			tsif_ctrl_pdflt_rec_all = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_4;
			tsif_ctrl_pdflt_rec_null = TSO_SEC0_REG_CHANNEL0_IF6_CONFIG2_0068_5;
			file_cfg_offset = REG_TSOSEC0_BASE + REG_01E8_TSO_SEC0;
			file_cfg_port_sel = TSO_SEC0_REG_TSO_FILE_CONFIG1_01E8_7;
			file_cfg_en = TSO_SEC0_REG_TSO_FILE_CONFIG1_01E8_8;
			file_cfg_timer_en = TSO_SEC0_REG_TSO_FILE_CONFIG1_01E8_9;
			file_cfg_tsp_file_seg = TSO_SEC0_REG_TSO_FILE_CONFIG1_01E8_12;
			break;
		default:
			return;
		}
		break;
	default:
		return;
	}

	if (input_pad == 0xFF && file_cfg_offset) { // file-in
		vIO32WriteFldMulti(file_cfg_offset, 4,
			1, file_cfg_port_sel, 1, file_cfg_en,
			1, file_cfg_timer_en, 1, file_cfg_tsp_file_seg);
	} else {
		vIO32WriteFld(clk_src_offset, input_pad, clk_src_mask);
		vIO32WriteFld(clk_src_offset, 0, clk_disable_nr);
	}

	if (cfg.port_type == DEMUX_PORT_TYPE_PARALLEL) {
		vIO32WriteFld(tsif_ctrl_offset, 1, tsif_ctrl_p_sel);
	} else {
		vIO32WriteFld(tsif_ctrl_offset, 0, tsif_ctrl_p_sel);
		vIO32WriteFld(tsif_ctrl_offset, 0, tsif_ctrl_sin_c0);
		vIO32WriteFld(tsif_ctrl_offset, 1, tsif_ctrl_sin_c1);
	}

	vIO32WriteFld(tsif_ctrl_offset, 1, tsif_ctrl_ext_sync_sel);

	// default: NOT use tso pid filter
	vIO32WriteFld(tsif_ctrl_offset, 1, tsif_ctrl_pdflt_rec_all);
	vIO32WriteFld(tsif_ctrl_offset, 1, tsif_ctrl_pdflt_rec_null);
}

static void mtk_dmx_io_set_tso_out_mode(u16 eng, enum demux_out_format out_format)
{
	u16 out_mode, out_pkt_size;

	switch (out_format) {
	case DEMUX_OUT_FORMAT_CIPLUS:
		out_mode = SVQ_RX_MODE_CIPLUS;
		out_pkt_size = 0xBB;
		break;
	case DEMUX_OUT_FORMAT_192PKT:
		out_mode = SVQ_RX_MODE_192_PKT;
		out_pkt_size = 0xBF;
		break;
	case DEMUX_OUT_FORMAT_OPENCABLE:
		out_mode = SVQ_RX_MODE_OPENCABLE;
		out_pkt_size = 0xC7;
		break;
	default:
		return;
	}

	switch (eng) {
	case 0:
		vIO32WriteFld(REG_TSO1_BASE + REG_00C0_TSO1, out_mode, TSO1_REG_SVQ_RX_CONFIG_00C0_1_0);
		vIO32Write2B(REG_TSO0_BASE + REG_007C_TSO, out_pkt_size);
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, 1, TSO0_REG_TSO_CONFIG1_0074_15);
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, 0, TSO0_REG_TSO_CONFIG1_0074_15);
		// file-in settings
		vIO32WriteFld(REG_TSO0_BASE + REG_01AC_TSO,
			out_pkt_size, TSO0_REG_PKT_CHK_SIZE_FIN_01AC);
		vIO32WriteFld(REG_TSO0_BASE + REG_01AC_TSO,
			out_pkt_size, TSO0_REG_PKT_CHK_SIZE_FIN1_01AC);
		break;
	case 1:
		vIO32WriteFld(REG_TSOSEC1_BASE + REG_00C0_TSO_SEC1, out_mode, TSO_SEC1_REG_SVQ_RX_CONFIG_00C0_1_0);
		vIO32Write2B(REG_TSOSEC0_BASE + REG_007C_TSO_SEC0, out_pkt_size);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, 1, TSO_SEC0_REG_TSO_CONFIG1_0074_15);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, 0, TSO_SEC0_REG_TSO_CONFIG1_0074_15);
		// file-in settings
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_01AC_TSO_SEC0,
			out_pkt_size, TSO_SEC0_REG_PKT_CHK_SIZE_FIN_01AC);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_01AC_TSO_SEC0,
			out_pkt_size, TSO_SEC0_REG_PKT_CHK_SIZE_FIN1_01AC);
		break;
	default:
		return;
	}
}

static void mtk_dmx_io_set_tso_out_clk(
		u16 eng,
		struct tso_out_clk *ptso_out_clk,
		bool bEnable,
		bool bClk_sample_inv)
{
	bool bAdjustPhase = false;
	u32 div_clk_src = (ptso_out_clk->div_src == TSO_OUT_DIV_SEL_SRC_172MHZ)
									 ? CLK_172M : CLK_288M;
	u32 out_clk = div_clk_src / (D_2 * (ptso_out_clk->div_num + 1));
	u16 ts_sample_clk_src = u4IO32ReadFld(REG_TS_SAMPLE_BASE + REG_018C_TS_SAMPLE,
							TS_SAMPLE_REG_CKG_TS_SAMPLE_018C_11_10);
	u32 ts_sample_clk = (ts_sample_clk_src == TSO_TS_SAMPLE_CLK_216MHz) ? CLK_216M : CLK_432M;

	REG16 __iomem *reg_div_sel;
	u32 fld_div_sel;
	REG16 __iomem *reg_div_num;
	u32 fld_div_num_4_0, fld_div_num_5, fld_div_num_6, fld_div_num_7;
	REG16 __iomem *reg_out_cfg;
	u32 fld_out_cfg_0, fld_out_cfg_2_4;
	u32 fld_out_cfg_8, fld_out_cfg_9, fld_out_cfg_12_10;
	REG16 __iomem *reg_out_inv;
	u32 fld_out_inv;
	REG16 __iomem *reg_out_sample;
	u32 fld_out_sample_0_4, fld_out_sample_5, fld_out_sample_7;
	u16 phase_num = 0;

	MTK_DMX_INFO("ts_sample_clk_src=%u\n", ts_sample_clk_src);
	MTK_DMX_INFO("ts_sample_clk=%u\n", ts_sample_clk);
	MTK_DMX_INFO("div_clk_src=%u\n", div_clk_src);
	MTK_DMX_INFO("out_clk=%u\n", out_clk);
	MTK_DMX_INFO("ptso_out_clk->div_src=%u\n", ptso_out_clk->div_src);
	MTK_DMX_INFO("ptso_out_clk->div_num=%u\n", ptso_out_clk->div_num);

	switch (eng) {
	case 0:
		reg_div_sel = REG_TS_SAMPLE_BASE + REG_0180_TS_SAMPLE;
		fld_div_sel = TS_SAMPLE_REG_CLK_TSO_OUT_DIV_SEL_0180;
		reg_div_num = REG_TS_SAMPLE_BASE + REG_017C_TS_SAMPLE;
		fld_div_num_4_0 = TS_SAMPLE_REG_CLK_TSO_OUT_DIVNUM_017C_4_0;
		fld_div_num_5 = TS_SAMPLE_REG_CLK_TSO_OUT_DIVNUM_017C_RESERVED_5;
		fld_div_num_6 = TS_SAMPLE_REG_CLK_TSO_OUT_DIVNUM_017C_RESERVED_6;
		fld_div_num_7 = TS_SAMPLE_REG_CLK_TSO_OUT_DIVNUM_017C_RESERVED_7;
		reg_out_cfg = REG_TS_SAMPLE_BASE + REG_0178_TS_SAMPLE;
		fld_out_cfg_0 = TS_SAMPLE_REG_CKG_TSO_OUT_0178_0;
		fld_out_cfg_2_4 = TS_SAMPLE_REG_CKG_TSO_OUT_0178_4_2;
		fld_out_cfg_8 = TS_SAMPLE_REG_CKG_P_TSO_OUT_0178_8;
		fld_out_cfg_9 = TS_SAMPLE_REG_CKG_P_TSO_OUT_0178_9;
		fld_out_cfg_12_10 = TS_SAMPLE_REG_CKG_P_TSO_OUT_0178_12_10;
		reg_out_inv = REG_TS_SAMPLE_BASE + REG_018C_TS_SAMPLE;
		fld_out_inv = TS_SAMPLE_REG_CLK_TSO_TOPAD_INV_018C;
		reg_out_sample = REG_TS_SAMPLE_BASE + REG_000C_TS_SAMPLE;
		fld_out_sample_0_4 = TS_SAMPLE_REG_TSO_OUT_CLK_SAMPLE_000C_4_0;
		fld_out_sample_5 = TS_SAMPLE_REG_TSO_OUT_CLK_SAMPLE_000C_5;
		fld_out_sample_7 = TS_SAMPLE_REG_TSO_OUT_CLK_SAMPLE_000C_7;
		break;
	case 1:
		reg_div_sel = REG_TS_SAMPLE_BASE + REG_019C_TS_SAMPLE;
		fld_div_sel = TS_SAMPLE_REG_CLK_TSO2_OUT_DIV_SEL_019C;
		reg_div_num = REG_TS_SAMPLE_BASE + REG_0198_TS_SAMPLE;
		fld_div_num_4_0 = TS_SAMPLE_REG_CLK_TSO2_OUT_DIVNUM_0198_4_0;
		fld_div_num_5 = TS_SAMPLE_REG_CLK_TSO2_OUT_DIVNUM_0198_RESERVED_5;
		fld_div_num_6 = TS_SAMPLE_REG_CLK_TSO2_OUT_DIVNUM_0198_RESERVED_6;
		fld_div_num_7 = TS_SAMPLE_REG_CLK_TSO2_OUT_DIVNUM_0198_RESERVED_7;
		reg_out_cfg = REG_TS_SAMPLE_BASE + REG_0194_TS_SAMPLE;
		fld_out_cfg_0 = TS_SAMPLE_REG_CKG_TSO2_OUT_0194_0;
		fld_out_cfg_2_4 = TS_SAMPLE_REG_CKG_TSO2_OUT_0194_4_2;
		fld_out_cfg_8 = TS_SAMPLE_REG_CKG_P_TSO2_OUT_0194_8;
		fld_out_cfg_9 = TS_SAMPLE_REG_CKG_P_TSO2_OUT_0194_9;
		fld_out_cfg_12_10 = TS_SAMPLE_REG_CKG_P_TSO2_OUT_0194_12_10;
		reg_out_inv = REG_TS_SAMPLE_BASE + REG_01A8_TS_SAMPLE;
		fld_out_inv = TS_SAMPLE_REG_CLK_TSO2_TOPAD_INV_01A8;
		reg_out_sample = REG_TS_SAMPLE_BASE + REG_0014_TS_SAMPLE;
		fld_out_sample_0_4 = TS_SAMPLE_REG_TSO2_OUT_CLK_SAMPLE_0014_4_0;
		fld_out_sample_5 = TS_SAMPLE_REG_TSO2_OUT_CLK_SAMPLE_0014_5;
		fld_out_sample_7 = TS_SAMPLE_REG_TSO2_OUT_CLK_SAMPLE_0014_7;
		break;
	default:
		return;
	}

	// TODO: move to mtk_dmx_pm_clock_ctrl
	vIO32WriteFld(
		reg_out_inv,
		0,
		TS_SAMPLE_REG_CKG_TS_SAMPLE_018C_8);

	if (bEnable) {
		switch (ptso_out_clk->out_clk_sel) {
		case TSO_OUT_CLK_27MHZ:
		case TSO_OUT_CLK_INT_DEMOD:
			break;
		case TSO_OUT_CLK_DIV_2N:
			vIO32WriteFld(reg_div_sel,
				(ptso_out_clk->div_src), fld_div_sel);
			vIO32WriteFldMulti(reg_div_num, 4,
				((ptso_out_clk->div_num) & 0x1F), fld_div_num_4_0,
				(((ptso_out_clk->div_num) >> 5) & 0x1), fld_div_num_5,
				(((ptso_out_clk->div_num) >> 6) & 0x1), fld_div_num_6,
				(((ptso_out_clk->div_num) >> 7) & 0x1), fld_div_num_7);
			bAdjustPhase = true;
			break;
		case TSO_OUT_CLK_P_TSO_OUT:
		case TSO_OUT_CLK_P_TSO_OUT_DIV8:
			vIO32WriteFld(reg_out_cfg,
				(ptso_out_clk->p_tso_out_clk_sel), fld_out_cfg_12_10);
			vIO32WriteFld(reg_out_cfg,
				0, fld_out_cfg_8);
			vIO32WriteFld(reg_out_cfg,
				0, fld_out_cfg_9);
			break;
		/*
		 *case TSO_OUT_CLK_DIV_MN:
		 *	break;
		 */
		default:
			return;
		}

		vIO32WriteFld(
			reg_out_inv,
			ptso_out_clk->bClk_invert ? 1 : 0,
			fld_out_inv);

		// if output clock <= 27MHz and not fixed frequency,
		// adjust phase for 90 degrees
		MTK_DMX_INFO("out_clk=%u\n", out_clk);
		if (IS_PHASE_ADJUSTMENT_FOR_90_DEGREE(out_clk)) {
			phase_num = PHASE_FOR_90_DEGREE(ts_sample_clk, out_clk);
			MTK_DMX_INFO("phase_num=%u, ts_sample_clk=%u, out_clk=%u\n",
			phase_num, ts_sample_clk, out_clk);
		}

		if (bAdjustPhase && (phase_num != 0)) {
			vIO32WriteFld(
				reg_out_sample,
				phase_num,
				fld_out_sample_0_4);
			MTK_DMX_INFO("phase_num=%u\n", phase_num);
			vIO32WriteFld(
				reg_out_sample,
				1,
				fld_out_sample_5);
			MTK_DMX_INFO("adjust enable\n");
		} else {
			vIO32WriteFld(
				reg_out_sample,
				0,
				fld_out_sample_5);
			MTK_DMX_INFO("adjust disable\n");
		}
		vIO32WriteFld(reg_out_cfg,
			(ptso_out_clk->out_clk_sel), fld_out_cfg_2_4);
		vIO32WriteFld(reg_out_cfg,
			0, fld_out_cfg_0);
	} else {
		vIO32WriteFld(reg_out_cfg,
		1, fld_out_cfg_0);
	}
	vIO32WriteFld(
		reg_out_sample,
		bClk_sample_inv ? 1 : 0,
		fld_out_sample_7);
}

void mtk_dmx_io_tso_input_cfg(struct demux_tso_input_cfg *pcfg)
{
	// disable tsif_en
	mtk_dmx_io_tso_tsif_en(pcfg->tso_eng_id, pcfg->tso_tsif_id, false);

	// sel input pad & clk
	mtk_dmx_io_tso_sel_tsif_src(pcfg->tso_eng_id, pcfg->tso_tsif_id, pcfg->src_cfg);

	// enable tsif_en
	mtk_dmx_io_tso_tsif_en(pcfg->tso_eng_id, pcfg->tso_tsif_id, true);
}

void mtk_dmx_io_tso_out_en(u16 eng, bool bEnable)
{
	u32 v = (bEnable) ? 1 : 0;

	switch (eng) {
	case 0:
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, v,
			TSO0_REG_TSO_CONFIG1_0074_0);
		break;
	case 1:
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, v,
			TSO_SEC0_REG_TSO_CONFIG1_0074_0);
		break;
	default:
		break;
	}
}

void mtk_dmx_io_tso_set_serial_out(u16 eng, bool bEnable)
{
	u32 v = (bEnable) ? 1 : 0;

	switch (eng) {
	case 0:
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, v,
			TSO0_REG_TSO_CONFIG1_0074_12);
		break;
	case 1:
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, v,
			TSO_SEC0_REG_TSO_CONFIG1_0074_12);
		break;
	default:
		break;
	}
}

void mtk_dmx_io_tso_output_cfg(struct demux_tso_output_cfg *pcfg)
{
	struct tso_out_clk tso_out_clk = { // default, use 8.6MHZ
		.bClk_invert = true,
		.out_clk_sel = ((pcfg->isUsbCam) ? tso_ck_ci20_tbl[0].out_clk_sel :
						tso_ck_ci14_tbl[0].out_clk_sel),
		.div_src = ((pcfg->isUsbCam) ? tso_ck_ci20_tbl[0].div_src :
						tso_ck_ci14_tbl[0].div_src),
		.div_num = ((pcfg->isUsbCam) ? tso_ck_ci20_tbl[0].div_num :
						tso_ck_ci14_tbl[0].div_num),
	};
	u32 i, p;
	bool found = false;
	const struct mtk_tso_ck_settings *tso_ck = ((pcfg->isUsbCam) ? tso_ck_ci20_tbl :
				tso_ck_ci14_tbl);
	u32 ck_num = ((pcfg->isUsbCam) ?
				(sizeof(tso_ck_ci20_tbl) / sizeof(struct mtk_tso_ck_settings)) :
				(sizeof(tso_ck_ci14_tbl) / sizeof(struct mtk_tso_ck_settings)));

	// output mode
	mtk_dmx_io_set_tso_out_mode(pcfg->tso_eng_id, pcfg->out_format);

	// TSO out clk
	if (pcfg->clk_cfg.bDefault == false) {
		tso_out_clk.bClk_invert = true;

		for (i = 0; i < ck_num; i++) {
			if ((tso_ck[i].rateKHz) >= (pcfg->clk_cfg.out_clk)) {
				MTK_DMX_INFO("set tso clk %d, isCi2 %d, index %d\n",
					tso_ck[i].rateKHz,
					pcfg->isUsbCam,
					i);
				found = true;
				break;
			}
		}
		p = (found) ? i : ck_num - 1;
		tso_out_clk.out_clk_sel = tso_ck[p].out_clk_sel;
		tso_out_clk.div_src = tso_ck[p].div_src;
		tso_out_clk.div_num = tso_ck[p].div_num;
	}
	vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_018C_TS_SAMPLE,
		TSO_TS_SAMPLE_CLK_216MHz, TS_SAMPLE_REG_CKG_TS_SAMPLE_018C_11_10);
	MTK_DMX_INFO("ts_sample set 216M");

	mtk_dmx_io_set_tso_out_clk(
		pcfg->tso_eng_id,
		&tso_out_clk,
		true,
		pcfg->bClk_sample_inv);
	// set serial mode
	if (pcfg->port_type == DEMUX_PORT_TYPE_SERIAL)
		mtk_dmx_io_tso_set_serial_out(pcfg->tso_eng_id, true);

	// enable out_en
	mtk_dmx_io_tso_out_en(pcfg->tso_eng_id, true);
}

void mtk_dmx_io_tso_feed_filein(u16 eng, u16 tsif, u64 paddr, u32 size)
{
	u8 i = 0;
	u32 addr_l;
	REG16 __iomem *addr;
	REG16 __iomem *sz_l, *sz_h;
	u32 sz_l_fld, sz_h_fld;
	REG16 __iomem *ctrl;
	u32 start_fld;

	addr_l = (u32) paddr;

	switch (eng) {
	case 0:
		switch (tsif) {
		case TSO_TSIF_5:
			addr = REG_TSO0_BASE + REG_0180_TSO;
			sz_l = REG_TSO0_BASE + REG_0188_TSO;
			sz_h = REG_TSO0_BASE + REG_018C_TSO;
			sz_l_fld = TSO0_REG_TSO_FILEIN_RNUM_0_0188_15_0;
			sz_h_fld = TSO0_REG_TSO_FILEIN_RNUM_1_018C_23_16;
			ctrl = REG_TSO0_BASE + REG_0190_TSO;
			start_fld = TSO0_REG_TSO_FILEIN_CTRL_0190_0;
			break;
		default:
			return;
		}
		break;
	case 1:
		switch (tsif) {
		case TSO_TSIF_5:
			addr = REG_TSOSEC0_BASE + REG_0180_TSO_SEC0;
			sz_l = REG_TSOSEC0_BASE + REG_0188_TSO_SEC0;
			sz_h = REG_TSOSEC0_BASE + REG_018C_TSO_SEC0;
			sz_l_fld = TSO_SEC0_REG_TSO_FILEIN_RNUM_0_0188_15_0;
			sz_h_fld = TSO_SEC0_REG_TSO_FILEIN_RNUM_1_018C_23_16;
			ctrl = REG_TSOSEC0_BASE + REG_0190_TSO_SEC0;
			start_fld = TSO_SEC0_REG_TSO_FILEIN_CTRL_0190_0;
			break;
		default:
			return;
		}
		break;
	default:
		return;
	}
	RegWrite4B(addr, addr + 4, addr_l);
	vIO32WriteFld(sz_l, (size & 0xFFFF), sz_l_fld);
	vIO32WriteFld(sz_h, (size >> 16), sz_h_fld);
	// start
	vIO32WriteFld(ctrl, 1, start_fld);
	while (i < FILEIN_RESET_RETRY_COUNT) {
		if (!u4IO32ReadFld(ctrl, start_fld))
			break;
		usleep_range(FILEIN_RESET_SLEEP_RANGE_L, FILEIN_RESET_SLEEP_RANGE_H);
		i++;
	}
	if (i >= FILEIN_RESET_RETRY_COUNT)
		MTK_DMX_ERR("tso filein error\n");
}

int mtk_dmx_io_get_tlv_ca_dst(u16 path)
{
	u32 dsc2tlv, value;
	u16 predefined_path_id = mtk_dmx_dev_get_predefined_path_id(path);

	if (!IS_TSIF_TLV(path))
		return 0;
	value = (predefined_path_id - TLV_TSIF_0 + 1);

	if (value == u4IO32ReadFld(REG_TLV0_BASE + REG_0170_TSP_TLV
		, TSP_TLV_REG_TLV2DSCRMB0_SRC_0170))
		dsc2tlv = u4IO32ReadFld(REG_TLV0_BASE + REG_0174_TSP_TLV
			, TSP_TLV_REG_DSCRMB02TLV_SRC_0174);
	else if (value == u4IO32ReadFld(REG_TLV0_BASE + REG_0170_TSP_TLV
		, TSP_TLV_REG_TLV2DSCRMB1_SRC_0170))
		dsc2tlv = u4IO32ReadFld(REG_TLV0_BASE + REG_0174_TSP_TLV
			, TSP_TLV_REG_DSCRMB12TLV_SRC_0174);
	else if (value == u4IO32ReadFld(REG_TLV0_BASE + REG_0170_TSP_TLV
		, TSP_TLV_REG_TLV2DSCRMB2_SRC_0170))
		dsc2tlv = u4IO32ReadFld(REG_TLV0_BASE + REG_0174_TSP_TLV
			, TSP_TLV_REG_DSCRMB22TLV_SRC_0174);
	else
		return 0;

	return dsc2tlv + TLV_TSIF_0 - 1;
}

int mtk_dmx_io_ca_ctrl(u16 path, struct dmx_ca_info info, bool bThrough)
{
	reg_fld_t *in, *out, *ca_en;
	u32 value = (bThrough) ? 1 : 0;
	u32 tlv2dsc, dsc2tlv;
	bool bTlv = false, set_src;
	u16 predefined_path_id = mtk_dmx_dev_get_predefined_path_id(path);
	int i;

	MTK_DMX_DEBUG("path %d predefined_path_id %d bThrough %d\n",
		path,
		predefined_path_id,
		bThrough);

	MTK_DMX_DEBUG("dmx_ca_info: set_tlv_avsrc %d pass_through %d eng_id %d path_id %d\n",
		info.set_tlv_avsrc,
		info.pass_through,
		info.eng_id,
		info.path_id);
	for (i = 0; i < FILTER_DST_TYPE_MAX; i++) {
		MTK_DMX_DEBUG("dmx_ca_info: info.fifo_id_used[%d] %d fifo_id[%d] %d\n",
			i, info.fifo_id_used[i],
			i, info.fifo_id[i]);
	}

	// clean old bit before set
	if (bThrough && IS_TSIF_TS(path)) {
		switch (path) {
		case TSIF_0:
			in = &ca_in_tsif0[info.eng_id];
			out = &ca_out_tsif0[info.eng_id];
			break;
		case TSIF_FI:
			in = &ca_in_tsiffi[info.eng_id];
			out = &ca_out_tsiffi[info.eng_id];
			break;
		case TSIF_1:
			in = &ca_in_tsif1[info.eng_id];
			out = &ca_out_tsif1[info.eng_id];
			break;
		case TSIF_2:
			in = &ca_in_tsif2[info.eng_id];
			out = &ca_out_tsif2[info.eng_id];
			break;
		default:
			return -EINVAL;
		}
		vIO32WriteFld(REG_TSP1_BASE + in->offset, 0, in->fld);
		vIO32WriteFld(REG_TSP1_BASE + out->offset, 0, out->fld);
	}

	switch (predefined_path_id) {
	case TSIF_0:
		in = &ca_in_tsif0[info.eng_id];
		out = &ca_out_tsif0[info.eng_id];
		break;
	case TSIF_FI:
		in = &ca_in_tsiffi[info.eng_id];
		out = &ca_out_tsiffi[info.eng_id];
		break;
	case TSIF_1:
		in = &ca_in_tsif1[info.eng_id];
		out = &ca_out_tsif1[info.eng_id];
		break;
	case TSIF_2:
		in = &ca_in_tsif2[info.eng_id];
		out = &ca_out_tsif2[info.eng_id];
		break;
	case TLV_TSIF_0:
	case TLV_TSIF_1:
	case TLV_TSIF_2:
		bTlv = true;
		tlv2dsc = (bThrough) ? (predefined_path_id - TLV_TSIF_0 + 1) : 0;
		dsc2tlv = (bThrough) ? (info.eng_id + 1) : 0;
		set_src = bThrough;
		in = &ca_in_tlv[info.eng_id];
		out = &ca_out_tlv[info.eng_id];
		ca_en = &ca_en_tlv[info.eng_id];
		break;
	default:
		return -EINVAL;
	}

	if (bTlv) {
		vIO32WriteFld(REG_TLV0_BASE + in->offset, tlv2dsc, in->fld);
		vIO32WriteFld(REG_TLV0_BASE + out->offset, dsc2tlv, out->fld);
		vIO32WriteFld(REG_TLV0_BASE + ca_en->offset, value, ca_en->fld);
		mtk_dmx_set_sec_src_tlv(path, set_src);
	} else {
		vIO32WriteFld(REG_TSP1_BASE + in->offset, value, in->fld);
		vIO32WriteFld(REG_TSP1_BASE + out->offset, value, out->fld);
	}

	if (info.pvr_id != PVR_INVALID) {
		mtk_pvr_set_src(info.pvr_id, path);
	}

	if (info.set_tlv_avsrc) {
		for (i = 0; i < FILTER_DST_TYPE_MAX; i++) {
			if (info.fifo_id_used[i]) {
				mtk_dmx_set_fifo_src(
					i,
					info.fifo_id[i],
					info.path_id);
			}
		}
	}

	return 0;
}

void mtk_dmx_io_init_svq(u16 eng, u16 tsif, u64 addr, u32 size, bool tx_en)
{
	REG16 __iomem *base_offset, *size_offset, *ctrl_offset;
	u32 load, nr;

	switch (eng) {
	case 0:
		switch (tsif) {
		case TSO_TSIF_1:
			base_offset = REG_TSO1_BASE + REG_0060_TSO1;
			size_offset = REG_TSO1_BASE + REG_0068_TSO1;
			ctrl_offset = REG_TSO1_BASE + REG_006C_TSO1;
			load = TSO1_REG_SVQ1_TX_CONFIG_006C_12;
			nr = TSO1_REG_SVQ1_TX_CONFIG_006C_15;
			break;
		case TSO_TSIF_5:
			base_offset = REG_TSO1_BASE + REG_00A0_TSO1;
			size_offset = REG_TSO1_BASE + REG_00A8_TSO1;
			ctrl_offset = REG_TSO1_BASE + REG_00AC_TSO1;
			load = TSO1_REG_SVQ5_TX_CONFIG_00AC_12;
			nr = TSO1_REG_SVQ5_TX_CONFIG_00AC_15;
			break;
		case TSO_TSIF_6:
			base_offset = REG_TSO1_BASE + REG_00B0_TSO1;
			size_offset = REG_TSO1_BASE + REG_00B8_TSO1;
			ctrl_offset = REG_TSO1_BASE + REG_00BC_TSO1;
			load = TSO1_REG_SVQ6_TX_CONFIG_00BC_12;
			nr = TSO1_REG_SVQ6_TX_CONFIG_00BC_15;
			break;
		default:
			return;
		}
		break;
	case 1:
		switch (tsif) {
		case TSO_TSIF_1:
			base_offset = REG_TSOSEC1_BASE + REG_0060_TSO_SEC1;
			size_offset = REG_TSOSEC1_BASE + REG_0068_TSO_SEC1;
			ctrl_offset = REG_TSOSEC1_BASE + REG_006C_TSO_SEC1;
			load = TSO_SEC1_REG_SVQ1_TX_CONFIG_006C_12;
			nr = TSO_SEC1_REG_SVQ1_TX_CONFIG_006C_15;
			break;
		case TSO_TSIF_5:
			base_offset = REG_TSOSEC1_BASE + REG_00A0_TSO_SEC1;
			size_offset = REG_TSOSEC1_BASE + REG_00A8_TSO_SEC1;
			ctrl_offset = REG_TSOSEC1_BASE + REG_00AC_TSO_SEC1;
			load = TSO_SEC1_REG_SVQ5_TX_CONFIG_00AC_12;
			nr = TSO_SEC1_REG_SVQ5_TX_CONFIG_00AC_15;
			break;
		case TSO_TSIF_6:
			base_offset = REG_TSOSEC1_BASE + REG_00B0_TSO_SEC1;
			size_offset = REG_TSOSEC1_BASE + REG_00B8_TSO_SEC1;
			ctrl_offset = REG_TSOSEC1_BASE + REG_00BC_TSO_SEC1;
			load = TSO_SEC1_REG_SVQ6_TX_CONFIG_00BC_12;
			nr = TSO_SEC1_REG_SVQ6_TX_CONFIG_00BC_15;
			break;
		default:
			return;
		}
		break;
	default:
		return;
	}
	vIO32WriteFld(ctrl_offset, 0, nr);
	if (tx_en == false)
		return;
	RegWrite4B(base_offset, base_offset + 4, (u32)(addr >> 4));
	vIO32Write2B(size_offset, (u16)(size / 208));
	vIO32WriteFld(ctrl_offset, 1, load);
	vIO32WriteFld(ctrl_offset, 0, load);
	vIO32WriteFld(ctrl_offset, 1, nr);
}

void mtk_dmx_io_init_fiq(void)
{
	// sw reset
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 0, TSP3_REG_FIQ0_CONFIG0_0100_0);
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 1, TSP3_REG_FIQ0_CONFIG0_0100_0);

	// skip rush data to audio/PVR/SEC/PCR
	vIO32WriteFldMulti(REG_TSP3_BASE + REG_012C_TSP3, 7,
	1, TSP3_REG_FIQ0_CONFIG1_012C_3,
	1, TSP3_REG_FIQ0_CONFIG1_012C_4,
	1, TSP3_REG_FIQ0_CONFIG1_012C_6,
	1, TSP3_REG_FIQ0_CONFIG1_012C_7,
	1, TSP3_REG_FIQ0_CONFIG1_012C_8,
	1, TSP3_REG_FIQ0_CONFIG1_012C_9,
	1, TSP3_REG_FIQ0_CONFIG1_012C_12);

	vIO32WriteFldMulti(REG_TSP3_BASE + REG_01AC_TSP3, 5,
	1, TSP3_REG_FIQ1_CONFIG1_01AC_3,
	1, TSP3_REG_FIQ1_CONFIG1_01AC_4,
	1, TSP3_REG_FIQ1_CONFIG1_01AC_6,
	1, TSP3_REG_FIQ1_CONFIG1_01AC_7,
	1, TSP3_REG_FIQ1_CONFIG1_01AC_9);
}

void mtk_dmx_io_deinit_fiq(void)
{
	// disable sw reset
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 1, TSP3_REG_FIQ0_CONFIG0_0100_0);
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 0, TSP3_REG_FIQ0_CONFIG0_0100_0);
}

// @NOTE: TV chips not use eng_id
void mtk_dmx_io_set_fiq_buf(u16 eng_id, u64 paddr, u32 size)
{
	u64 start_addr, end_addr;
	u32 aligned_size = rounddown64(size, FQ_PACKET_UNIT_LENGTH);

	start_addr = paddr;
	end_addr = start_addr + aligned_size;

	RegWrite4B(REG_TSP3_BASE + REG_0104_TSP3, REG_TSP3_BASE + REG_0108_TSP3,
		(u32)(start_addr >> MIU_BUS));
	RegWrite4B(REG_TSP3_BASE + REG_010C_TSP3, REG_TSP3_BASE + REG_0110_TSP3,
		(u32)(end_addr >> MIU_BUS));
	RegWrite4B(REG_TSP3_BASE + REG_0114_TSP3, REG_TSP3_BASE + REG_0118_TSP3,
		(u32)(start_addr >> MIU_BUS));
}

// @NOTE: TV chips not use eng_id
void mtk_dmx_io_enable_fiq(u16 eng_id, u16 path_id, bool bEnable)
{
	if (bEnable) {
		vIO32WriteFld(REG_TSP3_BASE + REG_012C_TSP3, 0, TSP3_REG_FIQ0_CONFIG1_012C_0);

		vIO32WriteFld(REG_TSP5_BASE + REG_002C_TSP5,
					  path_id, TSP5_REG_FIQ0_MUX_SRC_SEL_002C);

		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
					  1, TSP3_REG_FIQ0_CONFIG0_0100_2);
		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
					  0, TSP3_REG_FIQ0_CONFIG0_0100_2);

		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
					  1, TSP3_REG_FIQ0_CONFIG0_0100_1);
	} else {
		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
					  0, TSP3_REG_FIQ0_CONFIG0_0100_1);
		vIO32WriteFld(REG_TSP3_BASE + REG_012C_TSP3,
					  1, TSP3_REG_FIQ0_CONFIG1_012C_0);
		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
					  0, TSP3_REG_FIQ0_CONFIG0_0100_15);
	}
}

// @NOTE: TV chips not use eng_id
void mtk_dmx_io_reset_fiq_buffer(u16 eng_id, u16 path_id)
{
#define RESET_FIQ_BUFFER_RETRY_COUNT 10
#define RESET_FIQ_BUFFER_DELAY_MIN 1000
#define RESET_FIQ_BUFFER_DELAY_MAX 1100
	int retry = RESET_FIQ_BUFFER_RETRY_COUNT;
	u8 wr_level;

	mtk_dmx_io_set_tsif_en(path_id, false);
	mtk_dmx_io_enable_fiq(eng_id, path_id, false);

	while (retry > 0) {
		if (u4IO32ReadFld(REG_TSP3_BASE + REG_0154_TSP3
			, TSP3_REG_FIQ0_STATUS_0154_0) == 1)
			break;

		wr_level = u4IO32ReadFld(REG_TSP3_BASE + REG_0154_TSP3
			, TSP3_REG_FIQ0_STATUS_0154_4_2);
		usleep_range(RESET_FIQ_BUFFER_DELAY_MIN, RESET_FIQ_BUFFER_DELAY_MAX);
		if (wr_level == u4IO32ReadFld(REG_TSP3_BASE + REG_0154_TSP3
			, TSP3_REG_FIQ0_STATUS_0154_4_2))
			break;

		MTK_DMX_INFO("Not empty or wr_level is not keep. retry %d\n", retry);
		retry--;
	}

	if (retry > 0) {
		// reset lpcr
		RegWrite4B(REG_TSP3_BASE + REG_0158_TSP3, REG_TSP3_BASE + REG_015C_TSP3, 0);
		vIO32WriteFld(REG_TSP3_BASE + REG_012C_TSP3,
						1, TSP3_REG_FIQ0_CONFIG1_012C_10);
		vIO32WriteFld(REG_TSP3_BASE + REG_012C_TSP3,
						0, TSP3_REG_FIQ0_CONFIG1_012C_10);

		// reset waddr
		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
						1, TSP3_REG_FIQ0_CONFIG0_0100_2);
		vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3,
						0, TSP3_REG_FIQ0_CONFIG0_0100_2);
	} else {
		MTK_DMX_ERR("Reset fiq buffer failed.\n");
	}

	mtk_dmx_io_enable_fiq(eng_id, path_id, true);
	mtk_dmx_io_set_tsif_en(path_id, true);
}

// @NOTE: TV chips not use eng_id
void mtk_dmx_io_rush_fiq(u16 eng_id, u64 rush_addr)
{
	// set rush mode
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, !!rush_addr, TSP3_REG_FIQ0_CONFIG0_0100_15);

	// set rush addr
	RegWrite4B(REG_TSP3_BASE + REG_011C_TSP3, REG_TSP3_BASE + REG_0120_TSP3, (u32)(rush_addr >> MIU_BUS));

	// rush enable
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 1, TSP3_REG_FIQ0_CONFIG0_0100_14);
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 0, TSP3_REG_FIQ0_CONFIG0_0100_14);
}

// @NOTE: TV chips not use eng_id
u64 mtk_dmx_io_get_fiq_rptr(u16 eng_id)
{
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 1, TSP3_REG_FIQ0_CONFIG0_0100_6);
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 0, TSP3_REG_FIQ0_CONFIG0_0100_6);

	return (u64)(RegRead4B(REG_TSP3_BASE + REG_014C_TSP3,
		REG_TSP3_BASE + REG_0150_TSP3)) << MIU_BUS;
}

// @NOTE: TV chips not use eng_id
u64 mtk_dmx_io_get_fiq_wptr(u16 eng_id)
{
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 1, TSP3_REG_FIQ0_CONFIG0_0100_6);
	vIO32WriteFld(REG_TSP3_BASE + REG_0100_TSP3, 0, TSP3_REG_FIQ0_CONFIG0_0100_6);

	return (u64)(RegRead4B(REG_TSP3_BASE + REG_0144_TSP3,
		REG_TSP3_BASE + REG_0148_TSP3)) << MIU_BUS;
}

void mtk_dmx_io_init_tso(u16 num)
{
	switch (num) {
	case 0:
		// SW reset
		vIO32Write2B(REG_TSO0_BASE + REG_0000_TSO, 0x00FE);
		vIO32Write2B(REG_TSO0_BASE + REG_0000_TSO, 0x0001);
		vIO32Write2B(REG_TSO0_BASE + REG_0004_TSO, 0x0031);
		vIO32Write2B(REG_TSO0_BASE + REG_0004_TSO, 0);
		// local stream ID = 0x47
		vIO32Write2B(REG_TSO1_BASE + REG_0000_TSO1, 0x47);
		vIO32Write2B(REG_TSO1_BASE + REG_0040_TSO1, 0x47);
		vIO32Write2B(REG_TSO1_BASE + REG_0050_TSO1, 0x47);
		// SVQ TX forece fire cnt (0xC)
		vIO32WriteFld(REG_TSO1_BASE + REG_006C_TSO1, 0xC,
			TSO1_REG_SVQ1_TX_CONFIG_006C_11_8);
		vIO32WriteFld(REG_TSO1_BASE + REG_00AC_TSO1, 0xC,
			TSO1_REG_SVQ5_TX_CONFIG_00AC_11_8);
		vIO32WriteFld(REG_TSO1_BASE + REG_00BC_TSO1, 0xC,
			TSO1_REG_SVQ6_TX_CONFIG_00BC_11_8);
		// SVQ ECO bits
		vIO32WriteFld(REG_TSO0_BASE + REG_010C_TSO, 1,
			TSO0_REG_TSO_CONFIG4_010C_5);
		vIO32WriteFld(REG_TSO0_BASE + REG_010C_TSO, 1,
			TSO0_REG_TSO_CONFIG4_010C_6);
		vIO32WriteFld(REG_TSO0_BASE + REG_010C_TSO, 1,
			TSO0_REG_TSO_CONFIG4_010C_7);
		// cmd queue ld latch bits
		vIO32WriteFld(REG_TSO0_BASE + REG_0070_TSO, 1,
			TSO0_REG_TSO_CONFIG0_0070_5);
		vIO32WriteFld(REG_TSO0_BASE + REG_0070_TSO, 1,
			TSO0_REG_TSO_CONFIG0_0070_13);
		// valid / invalid count
		vIO32WriteFld(REG_TSO0_BASE + REG_0078_TSO, 188,
			TSO0_REG_TSO_CONFIG2_0078_7_0);
		vIO32WriteFld(REG_TSO0_BASE + REG_0078_TSO, 0,
			TSO0_REG_TSO_CONFIG2_0078_15_8);
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, 1,
			TSO0_REG_TSO_CONFIG1_0074_15);
		vIO32WriteFld(REG_TSO0_BASE + REG_0074_TSO, 0,
			TSO0_REG_TSO_CONFIG1_0074_15);
		// enable sys timestamp
		vIO32WriteFld(REG_TSO0_BASE + REG_010C_TSO, 1,
			TSO0_REG_TSO_CONFIG4_010C_1);
		// default: no phase tuning
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0180_TS_SAMPLE, 0,
			TS_SAMPLE_REG_CLK_TSO_OUT_PH_TUN_EN_0180);
		break;
	case 1:
		// SW reset
		vIO32Write2B(REG_TSOSEC0_BASE + REG_0000_TSO_SEC0, 0x00FE);
		vIO32Write2B(REG_TSOSEC0_BASE + REG_0000_TSO_SEC0, 0x0001);
		vIO32Write2B(REG_TSOSEC0_BASE + REG_0004_TSO_SEC0, 0x0031);
		vIO32Write2B(REG_TSOSEC0_BASE + REG_0004_TSO_SEC0, 0);
		// local stream ID = 0x47
		vIO32Write2B(REG_TSOSEC1_BASE + REG_0000_TSO_SEC1, 0x47);
		vIO32Write2B(REG_TSOSEC1_BASE + REG_0040_TSO_SEC1, 0x47);
		vIO32Write2B(REG_TSOSEC1_BASE + REG_0050_TSO_SEC1, 0x47);
		// SVQ TX forece fire cnt (0xC)
		vIO32WriteFld(REG_TSOSEC1_BASE + REG_006C_TSO_SEC1, 0xC,
			TSO_SEC1_REG_SVQ1_TX_CONFIG_006C_11_8);
		vIO32WriteFld(REG_TSOSEC1_BASE + REG_00AC_TSO_SEC1, 0xC,
			TSO_SEC1_REG_SVQ5_TX_CONFIG_00AC_11_8);
		vIO32WriteFld(REG_TSOSEC1_BASE + REG_00BC_TSO_SEC1, 0xC,
			TSO_SEC1_REG_SVQ6_TX_CONFIG_00BC_11_8);
		// SVQ ECO bits
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_010C_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG4_010C_5);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_010C_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG4_010C_6);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_010C_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG4_010C_7);
		// cmd queue ld latch bits
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0070_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG0_0070_5);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0070_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG0_0070_13);
		// valid / invalid count
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0078_TSO_SEC0, 188,
			TSO_SEC0_REG_TSO_CONFIG2_0078_7_0);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0078_TSO_SEC0, 0,
			TSO_SEC0_REG_TSO_CONFIG2_0078_15_8);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG1_0074_15);
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_0074_TSO_SEC0, 0,
			TSO_SEC0_REG_TSO_CONFIG1_0074_15);
		// enable sys timestamp
		vIO32WriteFld(REG_TSOSEC0_BASE + REG_010C_TSO_SEC0, 1,
			TSO_SEC0_REG_TSO_CONFIG4_010C_1);
		// default: no phase tuning
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_019C_TS_SAMPLE,
			0, TS_SAMPLE_REG_CLK_TSO2_OUT_PH_TUN_EN_019C);
		break;
	default:
		break;
	}

	// disable cilink1
	vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
					1, TS_SAMPLE_REG_CKG_TS_TX_C1_0164_0);

	// disable cilink2
	vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
					1, TS_SAMPLE_REG_CKG_TS_TX_C2_0164_8);
}

void mtk_dmx_io_set_cilink_input_cfg(struct demux_cilink_input_cfg *pcfg)
{
	u16 input_pad;
	u32 pktgen = 0;

	if (pcfg->bEnable) {
		switch (pcfg->type) {
		case DEMUX_IO_TYPE_IN_DEMOD:
			input_pad = TSIF_CLK_DEMOD;
			break;
		case DEMUX_IO_TYPE_TSO:
			switch (pcfg->port_num) {
			case 0:
				input_pad = TSIF_CLK_TSO;
				break;
			case 1:
				input_pad = TSIF_CLK_TSO2;
				break;
			default:
				return;
			}
			break;
		default:
			return;
		}
		// @TODO: clk interface
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_016C_TS_SAMPLE,
			CLK_CILINK_IF_27M, TS_SAMPLE_REG_CKG_CILINK_IF_016C_3_2);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_016C_TS_SAMPLE,
			CLK_CILINK_IF_DISABLE, TS_SAMPLE_REG_CKG_CILINK_IF_016C_0);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			CLK_CILINK_2B_RX_PAD, TS_SAMPLE_REG_CKG_2B_RX_INT_0168_11_10);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			CLK_CILINK_2B_RX_INVERT, TS_SAMPLE_REG_CKG_2B_RX_INT_0168_9);
		MTK_DMX_INFO("Set Cilink CLK_CILINK_2B_RX_INVERT\n");
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			CLK_CILINK_2B_RX_DISABLE, TS_SAMPLE_REG_CKG_2B_RX_INT_0168_8);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			CLK_CILINK_2B_TX_PAD, TS_SAMPLE_REG_CKG_2B_TX_0168_15_14);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			CLK_CILINK_2B_TX_DISABLE, TS_SAMPLE_REG_CKG_2B_TX_0168_12);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0170_TS_SAMPLE,
			0, TS_SAMPLE_REG_CLK_2B_TX_EXT_INV_0170);
		MTK_DMX_INFO("UnSet Cilink CLK_2B_TX_EXT_INV\n");
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_018C_TS_SAMPLE,
			TSO_TS_SAMPLE_CLK_432MHz, TS_SAMPLE_REG_CKG_TS_SAMPLE_018C_11_10);
		switch (pcfg->port_num) {
		case 0:
			pktgen = u4IO32ReadFld(REG_CILINK_BASE + REG_0014_CILINK_SOC_IF,
				REG_C1_TS_GENERATOR_START);
			vIO32WriteFld(REG_CILINK_BASE + REG_0014_CILINK_SOC_IF,
				((pktgen == 1) ? 2 : 0), REG_C1_TS_SRC_SEL);
			vIO32WriteFld(REG_CILINK_BASE + REG_0014_CILINK_SOC_IF,
				1, REG_C1_FULL_TO_TSO_EN);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				input_pad, TS_SAMPLE_REG_CKG_TS_TX_C1_0164_5_2);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				TS_TX_C1_CLK_DISABLE, TS_SAMPLE_REG_CKG_TS_TX_C1_0164_0);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				TS_TX_CLCLK_TS_TX_C1, TS_SAMPLE_REG_CLK_TS_TX_C1_EXT_DIV8_0164);
			break;
		case 1:
			pktgen = u4IO32ReadFld(REG_CILINK_BASE + REG_0020_CILINK_SOC_IF,
				REG_C2_TS_GENERATOR_START);
			vIO32WriteFld(REG_CILINK_BASE + REG_0020_CILINK_SOC_IF,
			((pktgen == 1) ? 2 : 0), REG_C2_TS_SRC_SEL);
			vIO32WriteFld(REG_CILINK_BASE + REG_0020_CILINK_SOC_IF,
				1, REG_C2_FULL_TO_TSO_EN);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				input_pad, TS_SAMPLE_REG_CKG_TS_TX_C2_0164_13_10);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				TS_TX_C2_CLK_DISABLE, TS_SAMPLE_REG_CKG_TS_TX_C2_0164_8);
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				TS_TX_CLCLK_TS_TX_C2, TS_SAMPLE_REG_CLK_TS_TX_C2_EXT_DIV8_0164);
			break;
		default:
			return;
		}
	} else {
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_016C_TS_SAMPLE,
			1, TS_SAMPLE_REG_CKG_CILINK_IF_016C_0);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			1, TS_SAMPLE_REG_CKG_2B_RX_INT_0168_8);
		vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0168_TS_SAMPLE,
			1, TS_SAMPLE_REG_CKG_2B_TX_0168_12);
		switch (pcfg->port_num) {
		case 0:
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				1, TS_SAMPLE_REG_CKG_TS_TX_C1_0164_0);
			vIO32WriteFld(REG_CILINK_BASE + REG_0014_CILINK_SOC_IF,
				0, REG_C1_FULL_TO_TSO_EN);
			break;
		case 1:
			vIO32WriteFld(REG_TS_SAMPLE_BASE + REG_0164_TS_SAMPLE,
				1, TS_SAMPLE_REG_CKG_TS_TX_C2_0164_8);
			vIO32WriteFld(REG_CILINK_BASE + REG_0020_CILINK_SOC_IF,
				0, REG_C2_FULL_TO_TSO_EN);
			break;
		default:
			return;
		}
	}
}

u32 mtk_dmx_io_get_cilink_loopback_status(u16 eng_id)
{
	u32 reg, offset;

	switch (eng_id) {
	case 0:
		offset = REG_0014_CILINK_SOC_IF;
		reg = REG_C1_2B_LOOPBACK;
		break;
	case 1:
		offset = REG_0020_CILINK_SOC_IF;
		reg = REG_C2_2B_LOOPBACK;
		break;
	default:
		return 0;
	}
	return u4IO32ReadFld(REG_CILINK_BASE + offset, reg);
}

void mtk_dmx_io_set_cilink_loopback_status(u16 eng_id, bool bSet)
{
	u32 reg, offset;

	switch (eng_id) {
	case 0:
		offset = REG_0014_CILINK_SOC_IF;
		reg = REG_C1_2B_LOOPBACK;
		break;
	case 1:
		offset = REG_0020_CILINK_SOC_IF;
		reg = REG_C2_2B_LOOPBACK;
		break;
	default:
		return;
	}
	vIO32WriteFld(REG_CILINK_BASE + offset, bSet ? 1 : 0, reg);
}

u32 mtk_dmx_io_get_locked_cnt(u16 path)
{
	REG16 __iomem *load_bit_offset, *load_buf_offset;
	u32 load_bit;
	u32 enable_pkt_cnt;
	u16 src;
	unsigned long flags;
	u32 ret_cnt;

	if (IS_TSIF_TS(path)) {
		switch (path) {
		case TSIF_0:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_0_LOAD_01E8;
			src = LOCKED_CNT_SRC_FROM_TSIF_0;
			break;
		case TSIF_FI:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_FI_LOAD_01E8;
			src = LOCKED_CNT_SRC_FROM_TSIF_FI;
			break;
		case TSIF_1:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_1_LOAD_01E8;
			src = LOCKED_CNT_SRC_FROM_TSIF_1;
			break;
		case TSIF_2:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_2_LOAD_01E8;
			src = LOCKED_CNT_SRC_FROM_TSIF_2;
			break;
		default:
			return 0;
		}

		spin_lock_irqsave(&locked_pkt_lock, flags);

		vIO32WriteFld(REG_TSP4_BASE + REG_01E8_TSP4, 1, load_bit);
		vIO32WriteFld(REG_TSP4_BASE + REG_01FC_TSP4, src,
			TSP4_REG_TSIF_SRC_SEL_01FC);

		ret_cnt = u2IO32Read2B(REG_TSP4_BASE + REG_0034_TSP4);

		spin_unlock_irqrestore(&locked_pkt_lock, flags);

		return ret_cnt;
	} else if (IS_TSIF_TLV(path)) {
		switch (path) {
		case TLV_TSIF_0:
			load_bit_offset = REG_TLV0_BASE + REG_0008_TSP_TLV;
			load_bit = TSP_TLV_REG_LOCKED_PKT_CNT_LOAD_0008;
			load_buf_offset = REG_TLV0_BASE + REG_000C_TSP_TLV;
			enable_pkt_cnt = TSP_TLV_REG_TLV_LOCKED_PKT_CNT_EN_0008;
			break;
		case TLV_TSIF_1:
			load_bit_offset = REG_TLV0_BASE + REG_0048_TSP_TLV;
			load_bit = TSP_TLV_REG_PATH1_LOCKED_PKT_CNT_LOAD_0048;
			load_buf_offset = REG_TLV0_BASE + REG_004C_TSP_TLV;
			enable_pkt_cnt = TSP_TLV_REG_PATH1_TLV_LOCKED_PKT_CNT_EN_0048;
			break;
		case TLV_TSIF_2:
			load_bit_offset = REG_TLV0_BASE + REG_0088_TSP_TLV;
			load_bit = TSP_TLV_REG_PATH2_LOCKED_PKT_CNT_LOAD_0088;
			load_buf_offset = REG_TLV0_BASE + REG_008C_TSP_TLV;
			enable_pkt_cnt = TSP_TLV_REG_PATH2_TLV_LOCKED_PKT_CNT_EN_0088;
			break;
		default:
			return 0;
		}

		spin_lock_irqsave(&locked_pkt_lock, flags);

		vIO32WriteFld(load_bit_offset, 1, enable_pkt_cnt);
		vIO32WriteFld(load_bit_offset, 1, load_bit);
		ret_cnt = u2IO32Read2B(load_buf_offset);

		spin_unlock_irqrestore(&locked_pkt_lock, flags);

		return ret_cnt;
	} else
		return 0;
}

u32 mtk_dmx_io_get_filein_locked_cnt(u16 path)
{
	u32 load_bit;
	u16 src;
	unsigned long flags;
	u32 ret_cnt;
	int filein_eng = FILEIN_MAIN;
	bool filein_enable = false;

	mtk_dmx_io_get_filein_eng_sel(path, &filein_eng, &filein_enable);

	if (IS_TSIF_TS(path)) {
		switch (path) {
		case TSIF_0:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_0_LOAD_01E8;
			break;
		case TSIF_FI:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_FI_LOAD_01E8;
			break;
		case TSIF_1:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_1_LOAD_01E8;
			break;
		case TSIF_2:
			load_bit = TSP4_REG_LOCKED_PKT_CNT_2_LOAD_01E8;
			break;
		default:
			return 0;
		}

		switch (filein_eng) {
		case FILEIN_MAIN:
			src = LOCKED_CNT_SRC_FROM_FILEIN_MAIN;
			break;
		case FILEIN_MM0:
			src = LOCKED_CNT_SRC_FROM_FILEIN_MM0;
			break;
		case FILEIN_MM1:
			src = LOCKED_CNT_SRC_FROM_FILEIN_MM1;
			break;
		default:
			return 0;
		}

		spin_lock_irqsave(&locked_pkt_lock, flags);

		vIO32WriteFld(REG_TSP4_BASE + REG_01E8_TSP4, 1, load_bit);
		vIO32WriteFld(REG_TSP4_BASE + REG_01FC_TSP4, src,
					  TSP4_REG_TSIF_SRC_SEL_01FC);

		ret_cnt = u2IO32Read2B(REG_TSP4_BASE + REG_0034_TSP4);

		spin_unlock_irqrestore(&locked_pkt_lock, flags);

		return ret_cnt;
	} else if (IS_TSIF_TLV(path)) {
		//todo: support TLV file in locked packet count
		return 0;
	} else
		return 0;
}

int mtk_dmx_io_suspend(bool suspend, void *parg)
{
	struct mtk_dmx2dev *dmxdev = (struct mtk_dmx2dev *) parg;
	u16 i = 0;
	u16 livein_num = mtk_dmx_rm_get_hwcap_by_id(&dmxdev->rm, HWCAPS_TYPE_LIVEIN);
	u16 tlv_num = mtk_dmx_rm_get_hwcap_by_id(&dmxdev->rm, HWCAPS_TYPE_TLV);
	u16 tso_num = mtk_dmx_rm_get_hwcap_by_id(&dmxdev->rm, HWCAPS_TYPE_TSO);

	if (suspend) {
		// disable fiq
		if (dmxdev->rm.fiq_rush_path != -1) {
			MTK_DMX_INFO("Fiq is still enabled on path %d, disable it.\n"
				, dmxdev->rm.fiq_rush_path);
			mtk_dmx_io_enable_fiq(0, dmxdev->rm.fiq_rush_path, false);
			mtk_dmx_rm_put_buf(&dmxdev->rm, &dmxdev->rm.fiqmap);
			dmxdev->rm.fiq_rush_path = -1;
		}

		// disable live-in
		for (i = 0; i < livein_num + tlv_num; i++)
			mtk_dmx_io_set_tsif_en(i, false);
		// disable TSO output
		for (i = 0; i < tso_num; i++)
			mtk_dmx_io_tso_out_en(i, false);
	} else {
	}
	return 0;
}
