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

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>

#include "mdrv_demux_common.h"
#include "mdrv_demux_resource.h"
#include "mdrv_demux_debug.h"
#include "mdrv_demux_ctrl.h"
#include "mdrv_demux_tee_if.h"

#include "mhal_demux_ctrl.h"

#define MHZ					(1000 * 1000)
#define NTP_BASEPOINT_CNT	(16 * 1024 * 1024)
#define STR_LEN				(40)

static ssize_t kobj_attr_get_video_drop_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						char *buf);
static ssize_t kobj_attr_get_audio_drop_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						char *buf);
static ssize_t kobj_attr_get_video_dis_conti_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf);
static ssize_t kobj_attr_get_audio_dis_conti_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf);
static ssize_t kobj_attr_get_video_pkt_cnt(struct kobject *kobj, struct kobj_attribute *attr,
					   char *buf);
static ssize_t kobj_attr_get_audio_pkt_cnt(struct kobject *kobj, struct kobj_attribute *attr,
					   char *buf);
static ssize_t kobj_attr_get_scramble_status(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf);
static ssize_t kobj_attr_get_video_fifo_status(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf);
static ssize_t kobj_attr_get_audio_fifo_status(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);

static struct kobj_attribute kobj_attr_ctrl_video_drop_cnt =
__ATTR(video_drop_cnt, 0444, kobj_attr_get_video_drop_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_audio_drop_cnt =
__ATTR(audio_drop_cnt, 0444, kobj_attr_get_audio_drop_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_video_dis_conti_cnt =
__ATTR(video_dis_conti_cnt, 0444, kobj_attr_get_video_dis_conti_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_audio_dis_conti_cnt =
__ATTR(audio_dis_conti_cnt, 0444, kobj_attr_get_audio_dis_conti_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_video_pkt_cnt =
__ATTR(video_pkt_cnt, 0444, kobj_attr_get_video_pkt_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_audio_pkt_cnt =
__ATTR(audio_pkt_cnt, 0444, kobj_attr_get_audio_pkt_cnt, NULL);

static struct kobj_attribute kobj_attr_ctrl_scramble_status =
__ATTR(scramble_cnt, 0444, kobj_attr_get_scramble_status, NULL);

static struct kobj_attribute kobj_attr_ctrl_video_fifo_status =
__ATTR(video_fifo_status, 0444, kobj_attr_get_video_fifo_status, NULL);

static struct kobj_attribute kobj_attr_ctrl_audio_fifo_status =
__ATTR(audio_fifo_status, 0444, kobj_attr_get_audio_fifo_status, NULL);

static struct attribute *ctrl_attrs[] = {
	&kobj_attr_ctrl_video_drop_cnt.attr,
	&kobj_attr_ctrl_audio_drop_cnt.attr,
	&kobj_attr_ctrl_video_dis_conti_cnt.attr,
	&kobj_attr_ctrl_audio_dis_conti_cnt.attr,
	&kobj_attr_ctrl_video_pkt_cnt.attr,
	&kobj_attr_ctrl_audio_pkt_cnt.attr,
	&kobj_attr_ctrl_scramble_status.attr,
	&kobj_attr_ctrl_video_fifo_status.attr,
	&kobj_attr_ctrl_audio_fifo_status.attr,
	NULL
};

static struct attribute_group ctrl_attrs_group = {
	.attrs = ctrl_attrs,
};

static int populate_fw_buffer(struct platform_device *pdev, void __iomem *vaddr, u32 size)
{
	int ret = 0;
	const struct firmware *tsp_fw = NULL;

	ret = request_firmware(&tsp_fw, TSP_FW_NAME, &pdev->dev);
	if (ret == 0) {
		if (size >= tsp_fw->size) {
			memcpy((u8 *) vaddr, tsp_fw->data, tsp_fw->size);
		} else {
			DEMUX_DEBUG_ERR("[%s][%d] firmware buffer is NOT enough\n",
				__func__, __LINE__);
			ret = -ENOMEM;
		}
	} else {
		DEMUX_DEBUG_ERR("[%s][%d] request TSP firmware fail\n",
			__func__, __LINE__);
		return ret;
	}

	release_firmware(tsp_fw);
	return ret;
}

static inline int mdrv_demux_clk_get(struct clk **clk,
	struct platform_device *pdev, const char *name)
{
	*clk = devm_clk_get(&pdev->dev, name);
	if ((IS_ERR(*clk) && PTR_ERR(*clk) != -ENOENT) || *clk == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get %s handle, error: %d\n",
			__func__, __LINE__, name, PTR_ERR(*clk));
		return 1;
	}
	return 0;
}

static int mdrv_demux_pm_init(struct platform_device *pdev, struct mdrv_demux_ctrl *pctrl)
{
	int i, ret = 0;
	char name[32] = "";
	u16 stc_num = pctrl->res->hwcaps->stc_num;

	// always on
	ret |= mdrv_demux_clk_get(&pctrl->tsp_clk,
		pdev, "clk_tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_mcu_nonpm2tsp,
		pdev, "clk_tsp_en_mcu_nonpm2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_smi_tsp2tsp,
		pdev, "clk_tsp_en_smi_tsp2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->top_mg_smi_ck,
		pdev, "clk_top_mg_smi_ck");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_smi_tsp,
		pdev, "clk_smi_tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsp2tsp,
		pdev, "clk_tsp_en_tsp2tsp");
	// tsp
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_imi2tsp,
		pdev, "clk_tsp_en_imi2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_smi2tsp,
		pdev, "clk_tsp_en_smi2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsp2dscrmb,
		pdev, "clk_tsp_en_tsp2dscrmb");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsp2tso,
		pdev, "clk_tsp_en_tsp2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsp2tso2,
		pdev, "clk_tsp_en_tsp2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts02tsp,
		pdev, "clk_tsp_en_ts02tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts12tsp,
		pdev, "clk_tsp_en_ts12tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts22tsp,
		pdev, "clk_tsp_en_ts22tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsfi2tsp,
		pdev, "clk_tsp_en_tsfi2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tsif0_27m2tsp,
		pdev, "clk_tsp_en_tsif0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_fiq0_27m2tsp,
		pdev, "clk_tsp_en_fiq0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_mm0_27m2tsp,
		pdev, "clk_tsp_en_mm0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_mm1_27m2tsp,
		pdev, "clk_tsp_en_mm1_27m2tsp");
	// time stamp
	ret |= mdrv_demux_clk_get(&pctrl->timestamp_clk,
		pdev, "clk_stamp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_stamp2tso,
		pdev, "clk_tsp_en_stamp2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_stamp2tso2,
		pdev, "clk_tsp_en_stamp2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_stamp2tsp,
		pdev, "clk_tsp_en_stamp2tsp");
	// ts sample
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_sample2ts_sample,
		pdev, "clk_tsp_en_ts_sample2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts0_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts0_in_dft2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts1_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts1_in_dft2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts2_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts2_in_dft2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts3_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts3_in_dft2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts4_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts4_in_dft2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts5_in_dft2ts_sample,
		pdev, "clk_tsp_en_ts5_in_dft2ts_sample");
	// path
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_path0_27m2tsp,
		pdev, "clk_tsp_en_path0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_path1_27m2tsp,
		pdev, "clk_tsp_en_path1_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_path2_27m2tsp,
		pdev, "clk_tsp_en_path2_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_path_fi_27m2tsp,
		pdev, "clk_tsp_en_path_fi_27m2tsp");
	// parser
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_parser2tsp,
		pdev, "clk_tsp_en_parser2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->parser_clk,
		pdev, "clk_parser");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_parser2vd_pes_parser,
		pdev, "clk_tsp_en_parser2vd_pes_parser");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_viva_parser2parser,
		pdev, "clk_tsp_en_viva_parser2parser");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_smi2vd_pes_parser,
		pdev, "clk_tsp_en_smi2vd_pes_parser");
	// stc
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_syn_stc02ts_sample,
		pdev, "clk_tsp_en_syn_stc02ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_syn_stc12ts_sample,
		pdev, "clk_tsp_en_syn_stc12ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_syn_stc22ts_sample,
		pdev, "clk_tsp_en_syn_stc22ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_syn_stc32ts_sample,
		pdev, "clk_tsp_en_syn_stc32ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_sys_time0_27m2tsp,
		pdev, "clk_tsp_en_sys_time0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_sys_time1_27m2tsp,
		pdev, "clk_tsp_en_sys_time1_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_sys_time2_27m2tsp,
		pdev, "clk_tsp_en_sys_time2_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_sys_time3_27m2tsp,
		pdev, "clk_tsp_en_sys_time3_27m2tsp");
	pctrl->stc_synth_clk =
		devm_kzalloc(&pdev->dev, sizeof(struct clk *) * stc_num, GFP_KERNEL);
	if (!pctrl->stc_synth_clk) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate clk_sync_stc handle fail\n", __func__,
				__LINE__);
		return -ENOMEM;
	}
	for (i = 0; i < stc_num; i++) {
		snprintf(name, sizeof(name), "clk_sync_stc%d", i);
		ret |= mdrv_demux_clk_get(&pctrl->stc_synth_clk[i],
			pdev, name);
	}
	pctrl->stc_clk =
		devm_kzalloc(&pdev->dev, sizeof(struct clk *) * stc_num, GFP_KERNEL);
	if (!pctrl->stc_clk) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate clk_stc handle fail\n", __func__,
				__LINE__);
		return -ENOMEM;
	}
	for (i = 0; i < stc_num; i++) {
		snprintf(name, sizeof(name), "clk_stc%d", i);
		ret |= mdrv_demux_clk_get(&pctrl->stc_clk[i],
			pdev, name);
	}
	// pvr
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_pvr1_27m2tsp,
		pdev, "clk_tsp_en_pvr1_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_pvr2_27m2tsp,
		pdev, "clk_tsp_en_pvr2_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_pvr3_27m2tsp,
		pdev, "clk_tsp_en_pvr3_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_pvr4_27m2tsp,
		pdev, "clk_tsp_en_pvr4_27m2tsp");
	// tlv
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_tlv02tsp,
		pdev, "clk_tsp_en_ts_tlv02tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_tlv12tsp,
		pdev, "clk_tsp_en_ts_tlv12tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_tlv22tsp,
		pdev, "clk_tsp_en_ts_tlv22tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tlv0_27m2tsp,
		pdev, "clk_tsp_en_tlv0_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tlv1_27m2tsp,
		pdev, "clk_tsp_en_tlv1_27m2tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tlv2_27m2tsp,
		pdev, "clk_tsp_en_tlv2_27m2tsp");
	// alp
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_alp02tsp,
		pdev, "clk_tsp_en_ts_alp02tsp");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_alp12tsp,
		pdev, "clk_tsp_en_ts_alp12tsp");
	// tso
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch1_27m2tso,
		pdev, "clk_tsp_en_tso_ch1_27m2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch12tso,
		pdev, "clk_tsp_en_tso_ch12tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch5_27m2tso,
		pdev, "clk_tsp_en_tso_ch5_27m2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch52tso,
		pdev, "clk_tsp_en_tso_ch52tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch6_27m2tso,
		pdev, "clk_tsp_en_tso_ch6_27m2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_ch62tso,
		pdev, "clk_tsp_en_tso_ch62tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_out_div_mn_src2ts_sample,
		pdev, "clk_tsp_en_tso_out_div_mn_src2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_out2tso,
		pdev, "clk_tsp_en_tso_out2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_s2p12tso,
		pdev, "clk_tsp_en_tso_s2p12tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_s2p22tso,
		pdev, "clk_tsp_en_tso_s2p22tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_trace_in2tso,
		pdev, "clk_tsp_en_tso_trace_in2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso_trace2tso,
		pdev, "clk_tsp_en_tso_trace2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch1_27m2tso2,
		pdev, "clk_tsp_en_tso2_ch1_27m2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch12tso2,
		pdev, "clk_tsp_en_tso2_ch12tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch5_27m2tso2,
		pdev, "clk_tsp_en_tso2_ch5_27m2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch52tso2,
		pdev, "clk_tsp_en_tso2_ch52tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch6_27m2tso2,
		pdev, "clk_tsp_en_tso2_ch6_27m2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_ch62tso2,
		pdev, "clk_tsp_en_tso2_ch62tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_out_div_mn_src2ts_sample,
		pdev, "clk_tsp_en_tso2_out_div_mn_src2ts_sample");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_out2tso2,
		pdev, "clk_tsp_en_tso2_out2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_s2p12tso2,
		pdev, "clk_tsp_en_tso2_s2p12tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_s2p22tso2,
		pdev, "clk_tsp_en_tso2_s2p22tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_trace_in2tso2,
		pdev, "clk_tsp_en_tso2_trace_in2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_tso2_trace2tso2,
		pdev, "clk_tsp_en_tso2_trace2tso2");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_smi2tso,
		pdev, "clk_tsp_en_smi2tso");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_smi2tso2,
		pdev, "clk_tsp_en_smi2tso2");
	// cilink
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_tx_c12cilink,
		pdev, "clk_tsp_en_ts_tx_c12cilink");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_ts_tx_c22cilink,
		pdev, "clk_tsp_en_ts_tx_c22cilink");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_cilink_2b_rx2cilink,
		pdev, "clk_tsp_en_cilink_2b_rx2cilink");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_cilink_2b_tx2cilink,
		pdev, "clk_tsp_en_cilink_2b_tx2cilink");
	ret |= mdrv_demux_clk_get(&pctrl->tsp_en_cilink_if2cilink,
		pdev, "clk_tsp_en_cilink_if2cilink");
	return (ret == 0) ? 0 : -ENODEV;
}

static inline void mdrv_demux_clk_enable(struct clk *target)
{
	if (!IS_ERR_OR_NULL(target))
		if (clk_prepare_enable(target) < 0)
			DEMUX_DEBUG_ERR("[%s][%d] %s prepare & enable fail\n",
				__func__, __LINE__, __clk_get_name(target));
}

static inline void mdrv_demux_clk_disable(struct clk *target)
{
	if (!IS_ERR_OR_NULL(target) && __clk_is_enabled(target))
		clk_disable_unprepare(target);
}

static inline void mdrv_demux_clk_set_rate(struct clk *target, unsigned long rate)
{
	unsigned long old_rate;
	long round_rate;

	if (!IS_ERR_OR_NULL(target)) {
		old_rate = clk_get_rate(target);
		round_rate = clk_round_rate(target, rate);
		if (round_rate < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] %s set round rate fail\n",
				__func__, __LINE__, __clk_get_name(target));
			return;
		}
		if (old_rate != round_rate)
			if (clk_set_rate(target, round_rate) < 0) {
				DEMUX_DEBUG_ERR("[%s][%d] %s set rate fail\n",
					__func__, __LINE__, __clk_get_name(target));
				return;
			}
	}
}

static inline void mdrv_demux_clk_set_parent(struct clk *target, struct clk *parent)
{
	if (!IS_ERR_OR_NULL(target) && !IS_ERR_OR_NULL(parent))
		if (clk_set_parent(target, parent) < 0)
			DEMUX_DEBUG_ERR("[%s][%d] %s set parent fail\n",
				__func__, __LINE__, __clk_get_name(target));
}

static int mdrv_demux_pm_ctrl(struct mdrv_demux_ctrl *pctrl, bool bEnable, bool bInitSW)
{
	int ret = 0;

	mhal_demux_mcm_ctrl(bEnable);

	if (bEnable) {
		mdrv_demux_clk_set_rate(pctrl->tsp_clk, ULONG_MAX);
		mdrv_demux_clk_enable(pctrl->tsp_clk);
		mdrv_demux_clk_set_parent(pctrl->tsp_smi_tsp, pctrl->top_mg_smi_ck);
		mdrv_demux_clk_enable(pctrl->tsp_smi_tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_mcu_nonpm2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_smi_tsp2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tsp2tsp);
		if (pctrl->bRpmEn == false)
			mdrv_demux_ctrl_runtime(pctrl, DEMUX_RPM_RESUME);
	} else {
		if (pctrl->bRpmEn == false)
			mdrv_demux_ctrl_runtime(pctrl, DEMUX_RPM_SUSPEND);
		mdrv_demux_clk_disable(pctrl->tsp_smi_tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_mcu_nonpm2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_smi_tsp2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tsp2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_clk);
	}
	return ret;
}

int mdrv_demux_ctrl_runtime(struct mdrv_demux_ctrl *pctrl, demux_rpm_status eStatus)
{
	int i;
	u16 stc_num = pctrl->res->hwcaps->stc_num;
	u32 parser_ck_rate = pctrl->res->hwcaps->parser_ck_rate;

	switch (eStatus) {
	case DEMUX_RPM_SUSPEND:
		// tsp
		mdrv_demux_clk_disable(pctrl->tsp_en_tsp2dscrmb);
		mdrv_demux_clk_disable(pctrl->tsp_en_tsp2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tsp2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts02tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts12tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts22tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tsfi2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tsif0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_fiq0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_mm0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_mm1_27m2tsp);
		// time stamp
		mdrv_demux_clk_disable(pctrl->timestamp_clk);
		mdrv_demux_clk_disable(pctrl->tsp_en_stamp2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_stamp2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_stamp2tsp);
		// ts sample
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_sample2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts0_in_dft2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts1_in_dft2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts2_in_dft2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts3_in_dft2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts4_in_dft2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts5_in_dft2ts_sample);
		// path
		mdrv_demux_clk_disable(pctrl->tsp_en_path0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_path1_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_path2_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_path_fi_27m2tsp);
		// parser
		mdrv_demux_clk_disable(pctrl->tsp_en_parser2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_parser2vd_pes_parser);
		mdrv_demux_clk_disable(pctrl->tsp_en_viva_parser2parser);
		mdrv_demux_clk_disable(pctrl->tsp_en_smi2vd_pes_parser);
		mdrv_demux_clk_disable(pctrl->parser_clk);
		// stc
		mdrv_demux_clk_disable(pctrl->tsp_en_syn_stc02ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_syn_stc12ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_syn_stc22ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_syn_stc32ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_sys_time0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_sys_time1_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_sys_time2_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_sys_time3_27m2tsp);
		for (i = 0; i < stc_num; i++) {
			mdrv_demux_clk_disable(pctrl->stc_synth_clk[i]);
			mdrv_demux_clk_disable(pctrl->stc_clk[i]);
		}
		// pvr
		mdrv_demux_clk_disable(pctrl->tsp_en_pvr1_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_pvr2_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_pvr3_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_pvr4_27m2tsp);
		// tlv
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_tlv02tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_tlv12tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_tlv22tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tlv0_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tlv1_27m2tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_tlv2_27m2tsp);
		// alp
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_alp02tsp);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_alp12tsp);
		// tso
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch1_27m2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch12tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch5_27m2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch52tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch6_27m2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_ch62tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_out_div_mn_src2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_out2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_s2p12tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_s2p22tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_trace_in2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso_trace2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch1_27m2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch12tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch5_27m2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch52tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch6_27m2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_ch62tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_out_div_mn_src2ts_sample);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_out2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_s2p12tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_s2p22tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_trace_in2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_tso2_trace2tso2);
		mdrv_demux_clk_disable(pctrl->tsp_en_smi2tso);
		mdrv_demux_clk_disable(pctrl->tsp_en_smi2tso2);
		// cilink
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_tx_c12cilink);
		mdrv_demux_clk_disable(pctrl->tsp_en_ts_tx_c22cilink);
		mdrv_demux_clk_disable(pctrl->tsp_en_cilink_2b_rx2cilink);
		mdrv_demux_clk_disable(pctrl->tsp_en_cilink_2b_tx2cilink);
		mdrv_demux_clk_disable(pctrl->tsp_en_cilink_if2cilink);
		break;
	case DEMUX_RPM_RESUME:
		// tsp
		mdrv_demux_clk_enable(pctrl->tsp_en_tsp2dscrmb);
		mdrv_demux_clk_enable(pctrl->tsp_en_tsp2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tsp2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts02tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts12tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts22tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tsfi2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tsif0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_fiq0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_mm0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_mm1_27m2tsp);
		// time stamp
		mdrv_demux_clk_set_rate(pctrl->timestamp_clk, ULONG_MAX);
		mdrv_demux_clk_enable(pctrl->timestamp_clk);
		mdrv_demux_clk_enable(pctrl->tsp_en_stamp2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_stamp2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_stamp2tsp);
		// ts sample
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_sample2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts0_in_dft2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts1_in_dft2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts2_in_dft2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts3_in_dft2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts4_in_dft2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts5_in_dft2ts_sample);
		// path
		mdrv_demux_clk_enable(pctrl->tsp_en_path0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_path1_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_path2_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_path_fi_27m2tsp);
		// parser
		mdrv_demux_clk_enable(pctrl->tsp_en_parser2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_parser2vd_pes_parser);
		mdrv_demux_clk_enable(pctrl->tsp_en_viva_parser2parser);
		mdrv_demux_clk_enable(pctrl->tsp_en_smi2vd_pes_parser);
		mdrv_demux_clk_set_rate(pctrl->parser_clk, (parser_ck_rate * MHZ));
		mdrv_demux_clk_enable(pctrl->parser_clk);
		// stc
		mdrv_demux_clk_enable(pctrl->tsp_en_syn_stc02ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_syn_stc12ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_syn_stc22ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_syn_stc32ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_sys_time0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_sys_time1_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_sys_time2_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_sys_time3_27m2tsp);
		for (i = 0; i < stc_num; i++) {
			mdrv_demux_clk_set_rate(pctrl->stc_synth_clk[i], (432 * MHZ));
			mdrv_demux_clk_enable(pctrl->stc_synth_clk[i]);
			mdrv_demux_clk_set_parent(pctrl->stc_clk[i], pctrl->stc_synth_clk[i]);
			mdrv_demux_clk_enable(pctrl->stc_clk[i]);
		}
		// pvr
		mdrv_demux_clk_enable(pctrl->tsp_en_pvr1_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_pvr2_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_pvr3_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_pvr4_27m2tsp);
		// tlv
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_tlv02tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_tlv12tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_tlv22tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tlv0_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tlv1_27m2tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_tlv2_27m2tsp);
		// alp
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_alp02tsp);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_alp12tsp);
		// tso
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch1_27m2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch12tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch5_27m2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch52tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch6_27m2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_ch62tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_out_div_mn_src2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_out2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_s2p12tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_s2p22tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_trace_in2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso_trace2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch1_27m2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch12tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch5_27m2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch52tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch6_27m2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_ch62tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_out_div_mn_src2ts_sample);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_out2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_s2p12tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_s2p22tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_trace_in2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_tso2_trace2tso2);
		mdrv_demux_clk_enable(pctrl->tsp_en_smi2tso);
		mdrv_demux_clk_enable(pctrl->tsp_en_smi2tso2);
		// cilink
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_tx_c12cilink);
		mdrv_demux_clk_enable(pctrl->tsp_en_ts_tx_c22cilink);
		mdrv_demux_clk_enable(pctrl->tsp_en_cilink_2b_rx2cilink);
		mdrv_demux_clk_enable(pctrl->tsp_en_cilink_2b_tx2cilink);
		mdrv_demux_clk_enable(pctrl->tsp_en_cilink_if2cilink);
		// tso sw rst
		mhal_demux_reset_tso();
		break;
	case DEMUX_RPM_IDLE:
		break;
	default:
		break;
	}
	return 0;
}

int mdrv_demux_ctrl_init(struct platform_device *pdev, struct mdrv_demux_ctrl *pctrl, bool bInitSW)
{
	bool bDmxTeeInited;
	struct mem_info *pfw_buffer = NULL, *pvq_buffer = NULL;
	u16 vq_num = pctrl->res->hwcaps->vq_num;

	// SW part
	if (bInitSW) {
		spin_lock_init(&pctrl->interrupt_lock);
		mutex_init(&pctrl->ctrl_mutex);
		pctrl->bIsSuspend = false;
		DEMUX_KOBJ_INIT_ADD(&pctrl->kobj, &ctrl_attrs_group,
						mdrv_demux_debug_get_root_kobj(), "ctrl");
		if (mdrv_demux_pm_init(pdev, pctrl) != 0)
			return -ENODEV;

		mhal_demux_common_init(&pctrl->res->regmaps->riu_banks);
	}
	// HW part
	mhal_demux_ctrl_init(&bDmxTeeInited);
	DEMUX_DEBUG_INFO("[%s][%d] bDmxTeeInited = %u\n",
		__func__, __LINE__, bDmxTeeInited);

	mdrv_demux_pm_ctrl(pctrl, true, bInitSW);

	mhal_demux_init_hw();

	if (!bDmxTeeInited) {
		if (mdrv_demux_get_memory(pdev, MEMMAP_FW_BUF, 1, &pfw_buffer, 0) < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] get FW buf fail\n",
				__func__, __LINE__);
			return -ENOMEM;
		}

		pctrl->res->memmap->fw_buf = pfw_buffer;

		if (populate_fw_buffer(pdev, pfw_buffer->vaddr, pfw_buffer->size) < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] populate firmware buffer fail\n",
				__func__, __LINE__);
			return -EFAULT;
		}

		if (!mhal_demux_load_fw(pfw_buffer->paddr, pfw_buffer->size)) {
			DEMUX_DEBUG_ERR("[%s][%d] (REE) load TSP firmware fail\n",
				__func__, __LINE__);
			return -EFAULT;
		}

		if (mdrv_demux_get_memory(pdev, MEMMAP_VQ_BUF, 1, &pvq_buffer, 0) < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] get VQ buf fail\n",
				__func__, __LINE__);
			return -ENOMEM;
		}

		pctrl->res->memmap->vq_buf = pvq_buffer;

		mhal_demux_set_vq_buffers(pvq_buffer->paddr, pvq_buffer->size, vq_num);
	} else {
		if (mdrv_demux_tee_load_fw() < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] (TEE) load TSP firmware fail\n",
				__func__, __LINE__);
			return -EFAULT;
		}

		if (!mhal_demux_check_fw_alive()) {
			DEMUX_DEBUG_ERR("[%s][%d] TSP firmware is NOT alive\n",
				__func__, __LINE__);
			return -EFAULT;
		}

		if (mdrv_demux_tee_set_vq() < 0) {
			DEMUX_DEBUG_ERR("[%s][%d] (TEE) set VQ buffer fail\n",
				__func__, __LINE__);
			return -EFAULT;
		}
	}

	mhal_demux_enable_vq_buffers(vq_num);

	mhal_demux_interrupt_ctrl(true);

	mhal_demux_set_ntp_base_point(NTP_BASEPOINT_CNT); // 1 second
	mhal_demux_set_leap_ind_eq_mode(1, true, false);
	mhal_demux_set_leap_ind_eq_mode(2, false, true);

	return 0;
}

int mdrv_demux_ctrl_exit(struct platform_device *pdev, struct mdrv_demux_ctrl *pctrl)
{
	mdrv_demux_debug_remove_attr_group(&pctrl->kobj, &ctrl_attrs_group);
	mhal_demux_common_exit();

	return 0;
}

int mdrv_demux_ctrl_suspend(struct mdrv_demux_ctrl *pctrl)
{
	u16 eng_id;

	mhal_demux_interrupt_ctrl(false);

	mhal_demux_clear_sw_interrupt_status();
	for (eng_id = PCR_0; eng_id < pctrl->res->hwcaps->pcr_filter_num; eng_id++)
		mhal_demux_clear_pcr_interrupt_status(eng_id);

	mdrv_demux_pm_ctrl(pctrl, false, false);

	return 0;
}

int mdrv_demux_ctrl_resume(struct mdrv_demux_ctrl *pctrl)
{
	// reset + enable
	mhal_demux_enable_vq_buffers(pctrl->res->hwcaps->vq_num);

	return 0;
}

bool mdrv_demux_ctrl_get_sw_int_status(struct mdrv_demux_ctrl *pctrl, u16 *pfilter_id,
					   enum sw_interrupt_event *pevent)
{
	unsigned long flags;
	u32 status;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	status = mhal_demux_get_sw_interrupt_status();
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);

	*pfilter_id = SW_INT_STATUS_GET_FLT_ID(status);

	switch (SW_INT_STATUS_GET_EVENT(status)) {
	case SW_INT_STATUS_EVENT_SEC_RDY:
		*pevent = SW_INT_EVENT_SEC_READY;
		break;
	case SW_INT_STATUS_EVENT_SEC_CRCERR:
		*pevent = SW_INT_EVENT_SEC_CRC_ERR;
		break;
	case SW_INT_STATUS_EVENT_SEC_OVERFLOW:
		*pevent = SW_INT_EVENT_SEC_BUF_OVERFLOW;
		break;
	default:
		DEMUX_DEBUG_DEBUG("[%s][%d] wrong event: %x\n",
			__func__, __LINE__, SW_INT_STATUS_GET_EVENT(status));
		return false;
	}

	return SW_INT_STATUS_CHK_STATE(status);
}

void mdrv_demux_ctrl_clear_sw_int_status(struct mdrv_demux_ctrl *pctrl)
{
	unsigned long flags;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	mhal_demux_clear_sw_interrupt_status();
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);
}

bool mdrv_demux_ctrl_get_pcr_int_status(struct mdrv_demux_ctrl *pctrl, u16 filter_id)
{
	unsigned long flags;
	bool status;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	status = mhal_demux_get_pcr_interrupt_status(filter_id);
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);

	return status;
}

void mdrv_demux_ctrl_clear_pcr_int_status(struct mdrv_demux_ctrl *pctrl, u16 filter_id)
{
	unsigned long flags;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	mhal_demux_clear_pcr_interrupt_status(filter_id);
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);
}

bool mdrv_demux_ctrl_get_ntp_int_status(struct mdrv_demux_ctrl *pctrl, u16 filter_id)
{
	unsigned long flags;
	bool status;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	status = mhal_demux_get_ntp_interrupt_status(filter_id);
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);

	return status;
}

void mdrv_demux_ctrl_clear_ntp_int_status(struct mdrv_demux_ctrl *pctrl, u16 filter_id)
{
	unsigned long flags;

	spin_lock_irqsave(&pctrl->interrupt_lock, flags);
	mhal_demux_clear_ntp_interrupt_status(filter_id);
	spin_unlock_irqrestore(&pctrl->interrupt_lock, flags);
}

int mdrv_demux_ctrl_reset_afifo(struct mdrv_demux_ctrl *pctrl, u16 eng_id, bool bReset)
{
	u16 afifo_num = pctrl->res->hwcaps->afifo_num;

	if (eng_id >= afifo_num) {
		DEMUX_DEBUG_ERR("[%s][%d] end_id (%u) is out-of-range\n",
			__func__, __LINE__, eng_id);
		return -EINVAL;
	}

	mhal_demux_reset_afifo(eng_id, bReset);

	return 0;
}
EXPORT_SYMBOL(mdrv_demux_ctrl_reset_afifo);

int mdrv_demux_ctrl_reset_vfifo(struct mdrv_demux_ctrl *pctrl, u16 eng_id, bool bReset)
{
	u16 vfifo_num = pctrl->res->hwcaps->vfifo_num;

	if (eng_id >= vfifo_num) {
		DEMUX_DEBUG_ERR("[%s][%d] end_id (%u) is out-of-range\n",
			__func__, __LINE__, eng_id);
		return -EINVAL;
	}

	mhal_demux_reset_vfifo(eng_id, bReset);

	return 0;
}
EXPORT_SYMBOL(mdrv_demux_ctrl_reset_vfifo);

static ssize_t kobj_attr_get_video_drop_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 vfifo_num = pctrl->res->hwcaps->vfifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_video_drop_cnt(0));
	for (i = 1; i < vfifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_video_drop_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_audio_drop_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 afifo_num = pctrl->res->hwcaps->afifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_audio_drop_cnt(0));
	for (i = 1; i < afifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_audio_drop_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_video_dis_conti_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 vfifo_num = pctrl->res->hwcaps->vfifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_video_dis_conti_cnt(0));
	for (i = 1; i < vfifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_video_dis_conti_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_audio_dis_conti_cnt(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 afifo_num = pctrl->res->hwcaps->afifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_audio_dis_conti_cnt(0));
	for (i = 1; i < afifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_audio_dis_conti_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_video_pkt_cnt(struct kobject *kobj, struct kobj_attribute *attr,
					   char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 vfifo_num = pctrl->res->hwcaps->vfifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_video_pkt_cnt(0));
	for (i = 1; i < vfifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_video_pkt_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_audio_pkt_cnt(struct kobject *kobj, struct kobj_attribute *attr,
					   char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 afifo_num = pctrl->res->hwcaps->afifo_num;
	u16 i;
	char output[40] = { 0 }, cur[10] = {
	0};
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	snprintf(output, 40, "%u", mhal_demux_get_audio_pkt_cnt(0));
	for (i = 1; i < afifo_num; i++) {
		snprintf(cur, 10, " %u", mhal_demux_get_audio_pkt_cnt(i));
		strncat(output, cur, sizeof(output) - 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_scramble_status(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	// @TODO: need to modify (not yet implement)
	len = scnprintf(buf, PAGE_SIZE, "%u\n", 0);	// 0: clear, 1: scramble

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_video_fifo_status(struct kobject *kobj, struct kobj_attribute *attr,
						 char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj, struct mdrv_demux_ctrl, kobj);
	u16 vfifo_num = pctrl->res->hwcaps->vfifo_num;
	u16 i;
	u32 status;
	char output[STR_LEN] = { 0 }, cur[STR_LEN] = { 0 };
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	for (i = 0; i < vfifo_num; i++) {
		status =
			(mhal_demux_get_video_fifo_level(i) << 2) |
			(mhal_demux_get_video_fifo_full_bit(i) << 1) |
			(mhal_demux_get_video_fifo_empty_bit(i));
		snprintf(cur, STR_LEN, "%x ", status);
		if ((strlen(output) + strlen(cur)) < STR_LEN)
			strncat(output, cur, strlen(cur));
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}

static ssize_t kobj_attr_get_audio_fifo_status(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_ctrl *pctrl = container_of(kobj,
		struct mdrv_demux_ctrl, kobj);
	u16 afifo_num = pctrl->res->hwcaps->afifo_num;
	u16 i;
	u32 status;
	char output[STR_LEN] = { 0 }, cur[STR_LEN] = { 0 };
	int len;

	mutex_lock(&pctrl->ctrl_mutex);

	for (i = 0; i < afifo_num; i++) {
		status =
			(mhal_demux_get_audio_fifo_level(i) << 2) |
			(mhal_demux_get_audio_fifo_full_bit(i) << 1) |
			(mhal_demux_get_audio_fifo_empty_bit(i));
		snprintf(cur, STR_LEN, "%x ", status);
		if ((strlen(output) + strlen(cur)) < STR_LEN)
			strncat(output, cur, strlen(cur));
	}

	len = scnprintf(buf, PAGE_SIZE, "%s\n", output);

	mutex_unlock(&pctrl->ctrl_mutex);

	return len;
}
