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

#include <linux/module.h>
#include <linux/file.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/math64.h>
#include <linux/kthread.h>

#include <linux/dvb/dmx2_mtk.h>
#include <media/dvb_event.h>

#include "mtk_dmx_common.h"
#include "mtk_dmx_log.h"
#include "mtk_dmx_dev.h"
#include "mtk_dmx_riu.h"
#include "mtk_dmx_rm.h"
#include "mtk_dmx_pm.h"
#include "mtk_dmx_bufctrl.h"
#include "mtk_dmx_drv.h"
#include "mtk_dmx_m2ts.h"
#include "mtk_dmx_isdb3.h"
#include "mtk_dmx_atsc3.h"
#include "mtk_mvd_parser.h"
#include "mtk_mad_parser.h"
#include "mtk_dscmb.h"
#include "mtk_scrambler.h"

#define IS_DMX_TAP(out)				\
	((out == DMX2_OUT_TAP) ||		\
	 (out == DMX2_OUT_DEMUX_TAP))

struct filter_list_info {
	s32 id;
	struct dvb_fh *fh;
	enum filter_sub_type sub;

	struct list_head list;
};

static bool is_filter_need_ringbuf(struct dmx2_filter *filter);
static bool is_video_filter_params(struct dmx2_filter *filter);

static void _set_pid_filter_info(struct mtk_demux2_fh_ctx *fh, struct pid_filter_cfg *info);
static void _get_pid_filter_info(struct mtk_demux2_fh_ctx *fh, struct pid_filter_cfg *info);
static void _set_sec_filter_info(struct mtk_demux2_fh_ctx *fh, struct sec_filter_cfg *info);
static void _get_sec_filter_info(struct mtk_demux2_fh_ctx *fh, struct sec_filter_cfg *info);
static void _set_pcr_filter_info(struct mtk_demux2_fh_ctx *fh, struct pcr_filter_cfg *info);
static void _get_pcr_filter_info(struct mtk_demux2_fh_ctx *fh, struct pcr_filter_cfg *info);
static void _set_sec_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event event);
static void _get_sec_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event *event);

static void _set_pid_filter_info(struct mtk_demux2_fh_ctx *fh, struct pid_filter_cfg *info)
{
	fh->cfg.pid_cfg.parent = info->parent;
	fh->cfg.pid_cfg.filter_dst_type = info->filter_dst_type;
	fh->cfg.pid_cfg.pid = info->pid;
	fh->cfg.pid_cfg.src_path = info->src_path;
	fh->cfg.pid_cfg.dst_eng_id = info->dst_eng_id;
}

static void _get_pid_filter_info(struct mtk_demux2_fh_ctx *fh, struct pid_filter_cfg *info)
{
	info->parent = fh->cfg.pid_cfg.parent;
	info->filter_dst_type = fh->cfg.pid_cfg.filter_dst_type;
	info->pid = fh->cfg.pid_cfg.pid;
	info->src_path = fh->cfg.pid_cfg.src_path;
	info->dst_eng_id = fh->cfg.pid_cfg.dst_eng_id;
}

static void _set_sec_filter_info(struct mtk_demux2_fh_ctx *fh, struct sec_filter_cfg *info)
{
	fh->cfg.sec_cfg.cfg.parent = info->cfg.parent;
	fh->cfg.sec_cfg.cfg.pid = info->cfg.pid;
	fh->cfg.sec_cfg.cfg.src_path = info->cfg.src_path;
	fh->cfg.sec_cfg.cfg.dst_eng_id = info->cfg.dst_eng_id;
	fh->cfg.sec_cfg.cfg.filter_dst_type = info->cfg.filter_dst_type;

	fh->cfg.sec_cfg.si_type = info->si_type;
	fh->cfg.sec_cfg.si_mode = info->si_mode;
	fh->cfg.sec_cfg.demux = info->demux;
}

static void _get_sec_filter_info(struct mtk_demux2_fh_ctx *fh, struct sec_filter_cfg *info)
{
	info->cfg.parent = fh->cfg.sec_cfg.cfg.parent;
	info->cfg.pid = fh->cfg.sec_cfg.cfg.pid;
	info->cfg.src_path = fh->cfg.sec_cfg.cfg.src_path;
	info->cfg.dst_eng_id = fh->cfg.sec_cfg.cfg.dst_eng_id;
	info->cfg.filter_dst_type = fh->cfg.sec_cfg.cfg.filter_dst_type;

	info->si_type = fh->cfg.sec_cfg.si_type;
	info->si_mode = fh->cfg.sec_cfg.si_mode;
	info->demux = fh->cfg.sec_cfg.demux;
}

static void _set_pcr_filter_info(struct mtk_demux2_fh_ctx *fh, struct pcr_filter_cfg *info)
{
	fh->cfg.pcr_cfg.cfg.parent = info->cfg.parent;
	fh->cfg.pcr_cfg.cfg.pid = info->cfg.pid;
	fh->cfg.pcr_cfg.cfg.src_path = info->cfg.src_path;
	fh->cfg.pcr_cfg.cfg.dst_eng_id = info->cfg.dst_eng_id;
	fh->cfg.pcr_cfg.int_cnt = info->int_cnt;
	fh->cfg.pcr_cfg.pcr = info->pcr;
	fh->cfg.pcr_cfg.pcr_ctrl.reset_pcr = info->pcr_ctrl.reset_pcr;
	fh->cfg.pcr_cfg.pcr_ctrl.prev_pll_ctrl = info->pcr_ctrl.prev_pll_ctrl;
	fh->cfg.pcr_cfg.pcr_ctrl.prev_stc_base = info->pcr_ctrl.prev_stc_base;
	fh->cfg.pcr_cfg.pcr_ctrl.prev_leap_ind = info->pcr_ctrl.prev_leap_ind;
	fh->cfg.pcr_cfg.pcr_ctrl.prev_sys_time = info->pcr_ctrl.prev_sys_time;
	fh->cfg.pcr_cfg.pcr_ctrl.leap_second_mode = info->pcr_ctrl.leap_second_mode;
}

static void _get_pcr_filter_info(struct mtk_demux2_fh_ctx *fh, struct pcr_filter_cfg *info)
{
	info->cfg.pid = fh->cfg.pcr_cfg.cfg.pid;
	info->cfg.src_path = fh->cfg.pcr_cfg.cfg.src_path;
	info->cfg.dst_eng_id = fh->cfg.pcr_cfg.cfg.dst_eng_id;
	info->int_cnt = fh->cfg.pcr_cfg.int_cnt;
	info->pcr = fh->cfg.pcr_cfg.pcr;
	info->pcr_ctrl.reset_pcr = fh->cfg.pcr_cfg.pcr_ctrl.reset_pcr;
	info->pcr_ctrl.prev_pll_ctrl = fh->cfg.pcr_cfg.pcr_ctrl.prev_pll_ctrl;
	info->pcr_ctrl.prev_stc_base = fh->cfg.pcr_cfg.pcr_ctrl.prev_stc_base;
	info->pcr_ctrl.prev_leap_ind = fh->cfg.pcr_cfg.pcr_ctrl.prev_leap_ind;
	info->pcr_ctrl.prev_sys_time = fh->cfg.pcr_cfg.pcr_ctrl.prev_sys_time;
	info->pcr_ctrl.leap_second_mode = fh->cfg.pcr_cfg.pcr_ctrl.leap_second_mode;
}

static void _set_alp_filter_info(struct mtk_demux2_fh_ctx *fh, struct alp_filter_cfg *info)
{
	memcpy(&fh->cfg.alp_cfg, info, sizeof(fh->cfg.alp_cfg));
}

static void _get_alp_filter_info(struct mtk_demux2_fh_ctx *fh, struct alp_filter_cfg *info)
{
	memcpy(info, &fh->cfg.alp_cfg, sizeof(fh->cfg.alp_cfg));
}

static void _set_ptp_filter_info(struct mtk_demux2_fh_ctx *fh, struct ptp_filter_cfg *info)
{
	memcpy(&fh->cfg.ptp_cfg, info, sizeof(fh->cfg.ptp_cfg));
}

static void _get_ptp_filter_info(struct mtk_demux2_fh_ctx *fh, struct ptp_filter_cfg *info)
{
	memcpy(info, &fh->cfg.ptp_cfg, sizeof(fh->cfg.ptp_cfg));
}

static void _set_sec_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event event)
{
	fh->cfg.sec_cfg.event = event;
}

static void _get_sec_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event *event)
{
	*event = fh->cfg.sec_cfg.event;
}

static enum dmx2_protocol_format get_demux_protocol(struct mtk_demux2 *demux)
{
	int path_id = demux->path_id;
	u16 ts_num = mtk_dmx_rm_get_hwcap_by_id(demux->rm, HWCAPS_TYPE_LIVEIN);
	u16 tlv_num = mtk_dmx_rm_get_hwcap_by_id(demux->rm, HWCAPS_TYPE_TLV);
	u16 alp_num = mtk_dmx_rm_get_hwcap_by_id(demux->rm, HWCAPS_TYPE_ALP);

	if (path_id < ts_num)
		return DMX2_FORMAT_M2TS;
	else if (path_id < (ts_num + tlv_num))
		return DMX2_FORMAT_ISDB3;
	else if (path_id < (ts_num + tlv_num + alp_num))
		return DMX2_FORMAT_ATSC3;

	return DMX2_FORMAT_NONE;
}

static int _init(struct mtk_demux2 *demux)
{
	int ret;
	enum dmx2_protocol_format protocol = get_demux_protocol(demux);

	switch (protocol) {
	case DMX2_FORMAT_M2TS:
		ret = mtk_dmx_m2ts_init(demux);
		break;
	case DMX2_FORMAT_ISDB3:
		ret = mtk_dmx_isdb3_init(demux);
		break;
	case DMX2_FORMAT_ATSC3:
		ret = mtk_dmx_atsc3_init(demux);
		break;
	default:
		return -ENOTTY;
	}

	INIT_LIST_HEAD(&demux->filter_list);
	spin_lock_init(&demux->lock);

	if (ret < 0) {
		MTK_DMX_ERR("init demux fail (protocol = %x)\n", protocol);
		return -EFAULT;
	}

	return 0;
}

static int _deinit(struct mtk_demux2 *demux)
{
	int ret;
	enum dmx2_protocol_format protocol = get_demux_protocol(demux);

	switch (protocol) {
	case DMX2_FORMAT_M2TS:
		ret = mtk_dmx_m2ts_exit(demux);
		break;
	case DMX2_FORMAT_ISDB3:
		ret = mtk_dmx_isdb3_exit(demux);
		break;
	case DMX2_FORMAT_ATSC3:
		ret = mtk_dmx_atsc3_exit(demux);
		break;
	default:
		return -ENOTTY;
	}

	if (ret < 0) {
		MTK_DMX_ERR("de-init demux fail (protocol = %x)\n", protocol);
		return -EFAULT;
	}

	return 0;
}

static inline struct dvb_fh *fd_to_fh(int fd)
{
	struct file *file = fget(fd);
	struct dvb_fh *fh;

	if (!file)
		return NULL;

	fh = file->private_data;
	fput(file);

	return fh;
}

static int create_event_monitor(struct dvb_fh *fh, u32 type)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct event_ctx *ev_ctx = fh_dmxctx_get_ev_ctx(ctx);
	unsigned long duration;
	timer_fn_t fn;
	struct event_ctx_task *p, *q;
	bool exist = false;
	int i;

	switch (type) {
	case DMX2_EVENT_SCRAMBLING_STATUS:
		for (i = 0; i < SCRAMBLING_STATUS_POS_MAX; i++) {
			ev_ctx->prev.scrambling_status[i].status = DMX2_UNKNOWN;
			ev_ctx->prev.scrambling_status[i].pos = i;
		}
		fn = mtk_dmx_tm_scmb_monitor;
		duration = SCMB_MON_TIMEOUT;
		break;

	case DMX2_EVENT_IP_CID_CHANGE:
		fn = mtk_dmx_tm_cid_monitor;
		duration = CID_MON_TIMEOUT;
		break;

	case DMX2_EVENT_STC_DATA_READY:
		fn = mtk_dmx_tm_time_int_monitor;
		duration = PCR_INT_MON_TIMEOUT;
		break;
	default:
		MTK_DMX_ERR("create event monitor fail, (type = %x)\n", type);
		return -EINVAL;
	}

	spin_lock(&ev_ctx->ev_ctx_lock);
	list_for_each_entry_safe(p, q, &ev_ctx->ev_list_head, list) {
		if (p->type == type) {
			exist = true;
			break;
		}
	}
	if (!exist) {
		p = kzalloc(sizeof(struct event_ctx_task), GFP_ATOMIC);
		if (p) {
			p->fh_ctx = ctx;
			p->type = type;
			p->duration = duration;
			p->ev_ctx = ev_ctx;
			list_add_tail(&p->list, &ev_ctx->ev_list_head);
		}
	}
	spin_unlock(&ev_ctx->ev_ctx_lock);

	if (exist)
		return -EEXIST;

	if (p) {
		p->task = kthread_run(fn, p, "dmx_mon_%s",
			(type == DMX2_EVENT_SCRAMBLING_STATUS) ? "scrmb_status" :
			(type == DMX2_EVENT_IP_CID_CHANGE) ? "cid_change" :
			(type == DMX2_EVENT_STC_DATA_READY) ? "stc_dat_rdy" :
			"unknown");
		if (IS_ERR(p->task)) {
			MTK_DMX_ERR("create event monitor fail, (type = %x)\n", type);
			p->task = NULL;
			spin_lock(&ev_ctx->ev_ctx_lock);
			list_del(&p->list);
			spin_unlock(&ev_ctx->ev_ctx_lock);
			return -ENOMEM;
		}
	} else {
		MTK_DMX_ERR("create event fail - kalloc fail\n");
		return -ENOMEM;
	}
	return 0;
}

static int delete_event_monitor(struct dvb_fh *fh, u32 type)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct event_ctx *ev_ctx = fh_dmxctx_get_ev_ctx(ctx);
	struct event_ctx_task *p, *q;
	bool exist = false;

	switch (type) {
	case DMX2_EVENT_SCRAMBLING_STATUS:
	case DMX2_EVENT_IP_CID_CHANGE:
	case DMX2_EVENT_STC_DATA_READY:
		spin_lock(&ev_ctx->ev_ctx_lock);
		list_for_each_entry_safe(p, q, &ev_ctx->ev_list_head, list) {
			if (p->type == type) {
				exist = true;
				list_del(&p->list);
				break;
			}
		}
		spin_unlock(&ev_ctx->ev_ctx_lock);
		if (exist) {
			kthread_stop(p->task);
			kfree(p);
		}
		break;
	default:
		MTK_DMX_ERR("delete event monitor fail, (type = %x)\n", type);
		return -EINVAL;
	}

	return 0;
}

static void restore_subscribed_event(struct dvb_fh *fh)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct event_ctx *ev_ctx = fh_dmxctx_get_ev_ctx(ctx);

	if (ev_ctx->scrambling_status_monitored)
		create_event_monitor(fh, DMX2_EVENT_SCRAMBLING_STATUS);
	if (ev_ctx->ip_cid_change_monitored)
		create_event_monitor(fh, DMX2_EVENT_IP_CID_CHANGE);
	if (ev_ctx->stc_data_ready_monitored)
		create_event_monitor(fh, DMX2_EVENT_STC_DATA_READY);
}

int mtk_demux2_open_filter(
	struct mtk_demux2 *demux,
	struct dvb_fh *fh,
	struct dmx2_filter_settings *setting)
{
	int ret;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct dmx2_filter *filter;
	struct dmx2_filter_params param = { 0 };

	param.input = setting->input;
	param.output = setting->output;

	if (setting->input == DMX2_IN_FILTER) {
		struct dvb_fh *prev_fh = fd_to_fh(setting->fd);

		param.prev_filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), prev_fh);
		if (!param.prev_filter) {
			MTK_DMX_ERR("can't find previous filter\n");
			return -EINVAL;
		}
	}

	switch (fh_dmxctx_get_flt_type(ctx)) {
	case FILTER_MAIN_TYPE_TS:
		param.type = DMX2_FILTER_TYPE_TS;
		memcpy(&param.ts, &setting->ts, sizeof(param.ts));
		break;
	case FILTER_MAIN_TYPE_TLV:
		param.type = DMX2_FILTER_TYPE_TLV;
		memcpy(&param.tlv, &setting->tlv, sizeof(param.tlv));
		break;
	case FILTER_MAIN_TYPE_ALP:
		param.type = DMX2_FILTER_TYPE_ALP;
		memcpy(&param.alp, &setting->alp, sizeof(param.alp));
		break;
	case FILTER_MAIN_TYPE_IP:
		param.type = DMX2_FILTER_TYPE_IP;
		memcpy(&param.ip, &setting->ip, sizeof(param.ip));
		break;
	case FILTER_MAIN_TYPE_MMTP:
		param.type = DMX2_FILTER_TYPE_MMTP;
		memcpy(&param.mmtp, &setting->mmtp, sizeof(param.mmtp));
		break;
	default:
		MTK_DMX_ERR("No support\n");
		return -EFAULT;
	}

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (filter == NULL) {
	ret = call_op(demux, flt, allocate_filter, &filter);
	if (ret < 0) {
		MTK_DMX_ERR("alloc filter fail\n");
		return -EFAULT;
	}
	}

	ret = dvb_dmx2_filter_open(cast_to_dmx_parent(demux), fh, &param, filter);
	if (ret < 0) {
		MTK_DMX_ERR("open filter fail\n");
		return -EFAULT;
	}

	restore_subscribed_event(fh);

	return 0;
}

static int set_ringbuf_rptr(struct mtk_bc_ctx *ctx, u64 rptr)
{
	int ret;
	struct mtk_demux2_fh_ctx *fh_ctx = container_of(ctx, struct mtk_demux2_fh_ctx, bc_ctx);
	struct mtk_demux2 *demux = fh_dmxctx_get_demux(fh_ctx);
	struct dvb_fh *fh = fh_dmxctx_get_fh(fh_ctx);

	ret = call_op(demux, buf, set_rptr, fh, rptr);

	return ret;
}

static u64 get_input_keytoken(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	u64 keytoken = 0;

	keytoken = mtk_dscmb_get_keytoken(demux->path_id, PURPOSE_PLAYBACK);
	if (keytoken) {
		MTK_DMX_INFO("got keytoken %llx from descrambler, with path id %d\n",
			keytoken, demux->path_id);
		return keytoken;
	}

	keytoken = mdrv_scrambler_get_keytoken(demux->path_id, KEY_PLAYBACK);
	if (keytoken) {
		MTK_DMX_INFO("got keytoken %llx from scrambler, with path id %d\n",
			keytoken, demux->path_id);
		return keytoken;
	}

	MTK_DMX_INFO("did not get any keytoken from scrambler or descrambler\n");
	return 0;
}

int mtk_demux2_start_filter(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	int ret;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
	struct dmx2_filter *filter;
	struct dmxta_pipeline_id pipeline = {0};
	u64 input_keytoken = 0;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (!filter) {
		MTK_DMX_ERR("find filter fail\n");
		return -EFAULT;
	}

	ret = dvb_dmx2_filter_set_buffer(filter, &buf_ctx->d_ctx.buf);
	if (ret < 0) {
		MTK_DMX_ERR("set filter buffer fail\n");
		return -EFAULT;
	}

	if (is_filter_need_ringbuf(filter)) {
		ret = mtk_bc_ringbuf_set_rptr_cb(buf_ctx, &set_ringbuf_rptr);
		if (ret < 0) {
			MTK_DMX_ERR("set rptr cb fail\n");
			return -EFAULT;
		}
	} else if (mtk_bc_vb2_is_ringbuf_mode(buf_ctx)) {
		ret = mtk_bc_vb2_set_rptr_cb(buf_ctx, &set_ringbuf_rptr);
		if (ret < 0) {
			MTK_DMX_ERR("set rptr cb fail\n");
			return -EFAULT;
		}
	}

	ret = dvb_dmx2_filter_start(filter);
	if (ret < 0) {
		MTK_DMX_ERR("start filter fail\n");
		return -EFAULT;
	}

	if (is_video_filter_params(filter)) {
		input_keytoken = get_input_keytoken(demux, fh);
		if (input_keytoken != 0 || ctx->cfg.pipeline_id.need_lock == true) {
			//always secure mode
			if (ctx->cfg.pipeline_id.already_lock == false) {
				MTK_DMX_DEBUG("start filter always secure mode "
					"allocate pipeline id\n");
				pipeline.lock = true;
				pipeline.input_keytoken = input_keytoken;
				mtk_dmx_tee_if_manage_pipeline(&pipeline);
				mtk_dmx_lock_filter(fh, &pipeline);
				ctx->cfg.pipeline_id.keytoken = pipeline.keytoken;
				ctx->cfg.pipeline_id.pipelinid = pipeline.pipelinid;
				ctx->cfg.es_r = 0;
				ctx->cfg.es_w = 0;
				MTK_DMX_INFO("keytoken = 0x%llx  pipelineid= 0x%x\n",
					ctx->cfg.pipeline_id.keytoken,
					ctx->cfg.pipeline_id.pipelinid);

				ctx->cfg.pipeline_id.already_lock = true;
			} else {
				//reconfigure case, don't create new pipeline id
				//don't set es r/w = 0
				MTK_DMX_DEBUG("start filter always secure mode "
					"skip new pipeline id\n");
			}
		} else {
			//not always secure mode
			ctx->cfg.es_r = 0;
			ctx->cfg.es_w = 0;
		}
	}

	return 0;
}

int mtk_demux2_stop_filter(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	int ret;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
	struct dmx2_filter *filter, *f;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (!filter) {
		MTK_DMX_INFO("filter is already closed\n");
		return 0;
	}

	if (filter->state != DMX2_STATE_GO) {
		MTK_DMX_INFO("filter is already stopped, id: %d\n", filter->feed->index);
		return 0;
	}

	for (f = filter->next; f; f = f->sibling) {
		MTK_DMX_ERR("stop sibling filter id %d\n", f->feed->index);
		mtk_demux2_stop_filter(demux, f->fh);
	}

	ret = dvb_dmx2_filter_stop(filter);
	if (ret < 0) {
		MTK_DMX_ERR("stop filter fail\n");
		return -EFAULT;
	}

	ret = mtk_bc_ringbuf_reset(buf_ctx, true);
	if (ret < 0) {
		MTK_DMX_ERR("reset ringbuffer fail\n");
		return -EFAULT;
	}

	ret = mtk_bc_ringbuf_set_rptr_cb(buf_ctx, NULL);
	if (ret < 0) {
		MTK_DMX_ERR("set rptr cb fail\n");
		return -EFAULT;
	}

	return 0;
}

int mtk_demux2_flush_filter(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	int ret;
	struct dmx2_filter *filter;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (!filter) {
		MTK_DMX_ERR("find filter fail\n");
		return -EFAULT;
	}

	ret = dvb_dmx2_filter_flush(filter);
	if (ret < 0) {
		MTK_DMX_ERR("flush filter fail\n");
		return -EFAULT;
	}

	return 0;
}

int mtk_demux2_release_pipeline_id(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct dmxta_pipeline_id pipeline = {0};
	struct pid_filter_cfg info = {0};
	vedio_parser_ctrl p_ctrl;

	fh_dmxctx_get_filter_info(ctx, (void *)&info, sizeof(struct pid_filter_cfg));

	if (ctx->cfg.pipeline_id.already_lock == true) {
		MTK_DMX_DEBUG("video eng id: %x VPAS_CMD_SET_CLOSE_SECURE\n",
								info.dst_eng_id);
		memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
		p_ctrl.size = sizeof(u32);
		p_ctrl.value = ctx->cfg.pipeline_id.pipelinid;
		video_parser_set_control(info.dst_eng_id, VPAS_CMD_SET_CLOSE_SECURE,
									&p_ctrl);

		MTK_DMX_INFO("release keytoken = 0x%llx, pipeline.pipelinid= 0x%x\n",
			ctx->cfg.pipeline_id.keytoken, ctx->cfg.pipeline_id.pipelinid);

		pipeline.lock = false;
		pipeline.keytoken = ctx->cfg.pipeline_id.keytoken;
		pipeline.pipelinid = ctx->cfg.pipeline_id.pipelinid;
		mtk_dmx_tee_if_manage_pipeline(&pipeline);

		ctx->cfg.pipeline_id.already_lock = false;
	}

	return 0;
}

int mtk_demux2_close_filter(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	int ret = 0;
	struct dmx2_filter *filter, *f;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (!filter) {
		MTK_DMX_INFO("filter is already closed\n");
		return 0;
	}

	while (filter->next != NULL) {
		f = filter->next;
		MTK_DMX_ERR("close sibling filter id %d\n", f->feed->index);
		mtk_demux2_close_filter(demux, f->fh);
	}

	ret |= delete_event_monitor(fh, DMX2_EVENT_SCRAMBLING_STATUS);
	ret |= delete_event_monitor(fh, DMX2_EVENT_STC_DATA_READY);
	ret |= delete_event_monitor(fh, DMX2_EVENT_IP_CID_CHANGE);
	if (ret < 0)
		MTK_DMX_ERR("delete event monitor fail\n");

	ret = dvb_dmx2_filter_close(filter);
	if (ret < 0) {
		MTK_DMX_ERR("close filter fail\n");
		return -EFAULT;
	}

	ret = call_op(demux, flt, release_filter, filter);
	if (ret < 0) {
		MTK_DMX_ERR("release filter fail\n");
		return -EFAULT;
	}

	return 0;
}

int mtk_demux2_subscribe_event(struct dvb_fh *fh, const struct dmx2_event_subscription *sub)
{
	int ret;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct event_ctx *event_ctx = &ctx->ev_ctx;

	ret = dvb_event_subscribe(fh, sub, DVB_DEFAULT_EVENTQ_NUM);
	if (ret < 0) {
		MTK_DMX_ERR("subscribe event fail, (type = %x)\n", sub->type);
		return -EFAULT;
	}

	ret = create_event_monitor(fh, sub->type);
	if (ret < 0) {
		if (ret == -EEXIST) {
			MTK_DMX_DEBUG("event monitor is existed, (type = %x)\n", sub->type);
		} else {
			MTK_DMX_ERR("create event monitor fail, (type = %x)\n", sub->type);
			return -EFAULT;
		}
	}

	switch (sub->type) {
	case DMX2_EVENT_SCRAMBLING_STATUS:
		event_ctx->scrambling_status_monitored = true;
		break;
	case DMX2_EVENT_STC_DATA_READY:
		event_ctx->stc_data_ready_monitored = true;
		break;
	case DMX2_EVENT_IP_CID_CHANGE:
		event_ctx->ip_cid_change_monitored = true;
		break;
	default:
		break;
	}

	return 0;
}

int mtk_demux2_unsubscribe_event(struct dvb_fh *fh, const struct dmx2_event_subscription *sub)
{
	int ret;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct event_ctx *event_ctx = &ctx->ev_ctx;

	switch (sub->type) {
	case DMX2_EVENT_SCRAMBLING_STATUS:
		event_ctx->scrambling_status_monitored = false;
		break;
	case DMX2_EVENT_STC_DATA_READY:
		event_ctx->stc_data_ready_monitored = false;
		break;
	case DMX2_EVENT_IP_CID_CHANGE:
		event_ctx->ip_cid_change_monitored = false;
		break;
	default:
		break;
	}

	ret = delete_event_monitor(fh, sub->type);
	if (ret < 0) {
		MTK_DMX_ERR("delete event monitor fail, (type = %x)\n", sub->type);
		return -EFAULT;
	}

	ret = dvb_event_unsubscribe(fh, sub);
	if (ret < 0) {
		MTK_DMX_ERR("un-subscribe event fail, (type = %x)\n", sub->type);
		return -EFAULT;
	}

	return 0;
}


bool mtk_demux2_is_filter_running(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	struct dmx2_filter *filter;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);

	return (filter && filter->state == DMX2_STATE_GO);
}

static bool is_filter_need_ringbuf(struct dmx2_filter *filter)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(filter->fh);
	struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
	enum mtk_bc_memtype type;

	if (mtk_bc_get_data_buf_type(buf_ctx, &type) < 0) {
		MTK_DMX_ERR("get data buffer type fail\n");
		return false;
	}

	return (type == MTK_BC_MEMTYPE_RINGBUF && IS_DMX_TAP(filter->params.output));
}

static bool is_video_filter_params(struct dmx2_filter *filter)
{
	if ((filter->params.type == DMX2_FILTER_TYPE_TS)
		&& (filter->params.ts.data_type == DMX2_TS_DATA_AU_VIDEO)) {
		return true;
	}

	if ((filter->params.type == DMX2_FILTER_TYPE_MMTP)
		&& (filter->params.mmtp.data_type == DMX2_MMTP_DATA_AU_VIDEO)) {
		return true;
	}

	return false;
}

bool mtk_demux2_is_filter_need_ringbuf(struct mtk_demux2 *demux, struct dvb_fh *fh)
{
	struct dmx2_filter *filter;

	filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	if (!filter) {
		MTK_DMX_ERR("find filter fail\n");
		return false;
	}

	return is_filter_need_ringbuf(filter);
}

static int mtk_demux2_set_stc(struct mtk_demux2 *demux, struct dvb_fh *fh, struct dmx2_stc *info)
{
	int ret;

	ret = call_op(demux, stc, set_stc, fh, info);
	if (ret < 0) {
		MTK_DMX_ERR("set stc fail\n");
		return -EFAULT;
	}

	return 0;
}

int mtk_demux2_get_stc(struct mtk_demux2 *demux, struct dvb_fh *fh, struct dmx2_stc *info)
{
	int ret;

	ret = call_op(demux, stc, get_stc, fh, info);
	if (ret < 0) {
		MTK_DMX_ERR("get stc fail\n");
		return -EFAULT;
	}

	return 0;
}

int mtk_demux2_set_ext_ctrl(struct dvb_fh *fh, u32 ctrl_id, void *arg)
{
	int ret = 0;

	if (fh == NULL || arg == NULL) {
		MTK_DMX_ERR("input source param invalid\n");
		return -EINVAL;
	}

	switch (ctrl_id) {
	case DMX2_CID_SET_STC:
		{
			struct mtk_demux2 *dmx2 = fh_to_demux(fh);

			ret = mtk_demux2_set_stc(dmx2, fh, (struct dmx2_stc *)arg);
		}
		break;
	case DMX2_CID_RECORD_PARSER:
		{
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);

			fh_dmxctx_set_rec_parser_info(ctx, arg);
		}
		break;
	case DMX2_CID_SET_PID_SECURE:
		{
			mtk_dmx_set_filter_secure(fh, arg);
		}
		break;
	case DMX2_CID_QBUF_DMA:
		{
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct pid_filter_cfg info = {0};
			fh_dmxctx_get_filter_info(ctx, (void *)&info,
			sizeof(struct pid_filter_cfg));
			if (info.filter_dst_type == FILTER_DST_TYPE_VIDEO) {
				vedio_parser_ctrl p_ctrl;

				memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
				fh_dmxctx_get_filter_info(ctx, (void *)&info,
				sizeof(struct pid_filter_cfg));

				p_ctrl.size = sizeof(struct dmx2_buffer);
				ret = video_parser_qbuf(info.dst_eng_id, arg);
			} else if (info.filter_dst_type == FILTER_DST_TYPE_AUDIO) {
				struct audio_parser_ctrl p_ctrl;

				memset(&p_ctrl, 0, sizeof(struct audio_parser_ctrl));
				fh_dmxctx_get_filter_info(ctx, (void *)&info,
				sizeof(struct pid_filter_cfg));

				p_ctrl.size = sizeof(struct dmx2_buffer);
				ret = audio_parser_qbuf(info.dst_eng_id, arg);
			}
		}
		break;
	default:
		MTK_DMX_ERR("ctrl id (%x) not support\n", ctrl_id);
		return -EINVAL;
	}

	return ret;
}

int mtk_demux2_get_ext_ctrl(struct dvb_fh *fh, u32 ctrl_id, void *arg)
{
	int ret = 0;

	if (fh == NULL || arg == NULL) {
		MTK_DMX_ERR("input source param invalid\n");
		return -EINVAL;
	}

	switch (ctrl_id) {
	case DMX2_CID_GET_METADATA:
		{
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct mtk_bc_ctx *bc_ctx = fh_dmxctx_get_bc_ctx(ctx);

			// @NOTE: release mutex if adopt blocking mode
			if (mtk_bc_metabuf_dequeue(bc_ctx, arg, true) < 0) {
				MTK_DMX_DEBUG("dequeue meta fail\n");
				ret = -EFAULT;
			}
		}
		break;
	case DMX2_CID_DQBUF_DMA:
		{
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct pid_filter_cfg info = {0};
			fh_dmxctx_get_filter_info(ctx, (void *)&info,
			sizeof(struct pid_filter_cfg));
			if (info.filter_dst_type == FILTER_DST_TYPE_VIDEO) {
				vedio_parser_ctrl p_ctrl;
				struct dmx2_buffer *pBuf = static_cast(struct dmx2_buffer *, arg);

				memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
				fh_dmxctx_get_filter_info(ctx, (void *)&info,
				sizeof(struct pid_filter_cfg));

				p_ctrl.size = sizeof(struct dmx2_buffer);
				ret = video_parser_dqbuf(info.dst_eng_id, pBuf);
			} else if (info.filter_dst_type == FILTER_DST_TYPE_AUDIO) {
				struct audio_parser_ctrl p_ctrl;
				struct dmx2_buffer *pBuf = static_cast(struct dmx2_buffer *, arg);

				memset(&p_ctrl, 0, sizeof(struct audio_parser_ctrl));
				fh_dmxctx_get_filter_info(ctx, (void *)&info,
				sizeof(struct pid_filter_cfg));

				p_ctrl.size = sizeof(struct dmx2_buffer);
				ret = audio_parser_dqbuf(info.dst_eng_id, pBuf);
			}
		}
		break;
	case DMX2_CID_GET_HW_FILTER_ID:
		{
			u32 *index = static_cast(u32 *, arg);
			struct mtk_demux2 *dmx2 = fh_to_demux(fh);
			struct dmx2_filter *filter;

			if (!mtk_demux2_is_filter_running(dmx2, fh)) {
				MTK_DMX_ERR("filter is not started\n");
				ret = -EFAULT;
				break;
			}

			filter = dvb_dmx2_filter_find(cast_to_dmx_parent(dmx2), fh);
			if (!filter) {
				MTK_DMX_ERR("can't find filter\n");
				ret = -EFAULT;
				break;
			}
			*index = filter->feed->index;
		}
		break;
	case DMX2_CID_GET_FIFO_ID:
		{
			u32 *index = static_cast(u32 *, arg);
			struct mtk_demux2 *dmx2 = fh_to_demux(fh);
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct pid_filter_cfg info = { 0 };

			if (!mtk_demux2_is_filter_running(dmx2, fh)) {
				MTK_DMX_ERR("filter is not started\n");
				ret = -EFAULT;
				break;
			}

			fh_dmxctx_get_filter_info(ctx, (void *)&info, sizeof(struct pid_filter_cfg));
			if (info.filter_dst_type != FILTER_DST_TYPE_VIDEO
				&& info.filter_dst_type != FILTER_DST_TYPE_AUDIO) {
				MTK_DMX_ERR("filter is not media type\n");
				ret = -EFAULT;
				break;
			}
			*index = info.dst_eng_id;
		}
		break;
	case DMX2_CID_GET_STC_ID:
		{
			u32 *index = static_cast(u32 *, arg);
			struct mtk_demux2 *dmx2 = fh_to_demux(fh);
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct stc_info stc_info;

			if (!mtk_demux2_is_filter_running(dmx2, fh)) {
				MTK_DMX_ERR("filter is not started\n");
				ret = -EFAULT;
				break;
			}

			if (ctx->cfg.type.sub != FILTER_SUB_TYPE_PCR
				&& ctx->cfg.type.sub != FILTER_SUB_TYPE_NTP
				&& ctx->cfg.type.sub != FILTER_SUB_TYPE_PTP) {
				MTK_DMX_ERR("filter is not time type\n");
				ret = -EFAULT;
				break;
			}

			fh_dmxctx_get_stc_info(ctx, (void *)&stc_info);
			if (!stc_info.is_filter_use_stc) {
				MTK_DMX_ERR("filter is not using stc\n");
				ret = -EFAULT;
				break;
			}
			*index = stc_info.eng_id;
		}
		break;
	case DMX2_CID_GET_REMAINING_SIZE:
		{
			u32 *index = static_cast(u32 *, arg);
			struct mtk_demux2 *dmx2 = fh_to_demux(fh);
			struct dmx2_filter *filter = NULL;
			struct dmx2_feed *feed = NULL;
			ssize_t avail_size = 0;

			filter = dvb_dmx2_filter_find(&dmx2->base, fh);
			if (filter) {
				feed = filter->feed;
				if (feed)
					avail_size = dvb_ringbuffer_avail(&feed->buf_ctx->ring);
			}
			*index = avail_size;
		}
		break;
	case DMX2_CID_GET_DMABUF_DATA:
		{
			struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
			struct mtk_bc_ctx *bc_ctx = fh_dmxctx_get_bc_ctx(ctx);

			u32 *index = static_cast(u32 *, arg);

			struct file *file = NULL;
			u32 fnt = 0;

			if (bc_ctx->d_ctx.vb2_fd <= 0) {
				MTK_DMX_ERR("media fd(%d) is invalid integer\n",
					bc_ctx->d_ctx.vb2_fd);
				ret = -EFAULT;
				break;
			}
			file = fget(bc_ctx->d_ctx.vb2_fd);
			if (!file) {
				MTK_DMX_ERR("fget fail, fd %d\n",
					bc_ctx->d_ctx.vb2_fd);
				ret = -EFAULT;
				break;
			}
			fnt = file_count(file) - 1;
			MTK_DMX_INFO("avfd(%d) with refCnt(%d) iNode(%ld)\n",
				bc_ctx->d_ctx.vb2_fd, fnt, file_inode(file)->i_ino);

			*index = fnt;
			fput(file);
		}
		break;
	default:
		MTK_DMX_ERR("ctrl id (%x) not support\n", ctrl_id);
		return -EINVAL;
	}

	return ret;
}

int mtk_demux2_query_caps(struct mtk_demux2 *demux, struct dmx2_capability *info)
{
	int i;
	u16 num;
	int ret = 0;
	struct dmx2_device_caps *entry = NULL;

	info->capabilities = demux->capabilities;

	for (i = 0, entry = info->device_info; i < info->count; i++, entry++) {
		num = mtk_dmx_rm_get_hwcap_by_name(demux->rm, entry->match);

		if (!num)
			entry->supported = false;
		else {
			entry->supported = true;
			ret = snprintf(entry->cap, sizeof(entry->cap), "%u", num);
			if (ret < 0)
				MTK_DMX_ERR("snprintf error err code: %d \n", ret);
		}
	}

	return 0;
}

int mtk_demux2_add_filter_list(struct mtk_demux2 *demux
	, struct dvb_fh *fh, int filter_id, enum filter_sub_type type)
{
	struct filter_list_info *info;

	info = kzalloc(sizeof(struct filter_list_info), GFP_KERNEL);
	if (info == 0) {
		MTK_DMX_ERR("alloc filter map list fail\n");
		return -ENODEV;
	}
	info->fh = fh;
	info->id = filter_id;
	info->sub = type;
	spin_lock(&demux->lock);
	list_add(&info->list, &demux->filter_list);
	spin_unlock(&demux->lock);
	return 0;
}

int mtk_demux2_remove_filter_list(struct mtk_demux2 *demux
	, struct dvb_fh *fh, int filter_id, enum filter_sub_type type)
{
	struct filter_list_info *info, *q;

	spin_lock(&demux->lock);
	list_for_each_entry_safe(info, q, &demux->filter_list, list)
		if (info->sub == type && info->id == filter_id
				&& info->fh == fh) {
			list_del(&info->list);
			kfree(info);
			spin_unlock(&demux->lock);
			return 0;
		}
	spin_unlock(&demux->lock);
	return -ENODEV;
}

struct dvb_fh *mtk_demux2_query_filter_list(struct mtk_demux2 *demux
	, int filter_id, enum filter_sub_type type)
{
	struct filter_list_info *info;

	spin_lock(&demux->lock);
	list_for_each_entry(info, &demux->filter_list, list)
		if (info->sub == type && info->id == filter_id) {
			spin_unlock(&demux->lock);
			return info->fh;
		}
	spin_unlock(&demux->lock);
	return NULL;
}

struct pid_filter_cfg *mtk_demux2_query_pid_filter_cfg(struct mtk_demux2 *mtk_demux2, u16 pid)
{
	struct filter_list_info *filter_list_info;
	struct mtk_demux2_fh_ctx *ctx;
	struct pid_filter_cfg *pid_filter_cfg = NULL;

	spin_lock(&mtk_demux2->lock);
	list_for_each_entry(filter_list_info, &mtk_demux2->filter_list, list) {
		ctx = fh_to_dmxctx(filter_list_info->fh);
		switch (ctx->cfg.type.main) {
		case DMX2_FILTER_TYPE_TS:
		case DMX2_FILTER_TYPE_TLV:
		case DMX2_FILTER_TYPE_MMTP:
			switch (ctx->cfg.type.sub) {
			case FILTER_SUB_TYPE_PID:
				if (ctx->cfg.pid_cfg.pid == pid)
					pid_filter_cfg = &ctx->cfg.pid_cfg;
				break;
			default:
				break;
			}
			break;

		default:
			break;
		}

		if (pid_filter_cfg != NULL)
			break;
	}
	spin_unlock(&mtk_demux2->lock);
	return pid_filter_cfg;
}

int mtk_demux2_get_filter_num(struct mtk_demux2 *demux)
{
	struct dvb_demux2 *dvb_demux2;
	struct dmx2_filter *filter;
	unsigned long flags;
	int count = 0;

	if (demux == NULL)
		return 0;
	dvb_demux2 = cast_to_dmx_parent(demux);

	spin_lock_irqsave(&dvb_demux2->lock, flags);

	list_for_each_entry(filter, &dvb_demux2->filter_list, list)
		if (filter != NULL)
			count++;

	spin_unlock_irqrestore(&dvb_demux2->lock, flags);

	return count;
}

void fh_dmxctx_set_filter_type(struct mtk_demux2_fh_ctx *fh, struct filter_type type)
{
	memcpy(&fh->cfg.type, &type, sizeof(struct filter_type));
}

#define ST_SIZE_CHK(__ST, __SZ, __FUNC) \
({ \
	int r = (sizeof(struct __ST) == __SZ) ? 0 : -EINVAL; \
	if (r == 0) \
		__FUNC; \
	r; \
})

int fh_dmxctx_set_filter_info(struct mtk_demux2_fh_ctx *fh, void *data, u32 size)
{
	int ret = 0;

	switch (fh->cfg.type.main) {
	case DMX2_FILTER_TYPE_TS:
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_PID:
			ret = ST_SIZE_CHK(pid_filter_cfg, size,
				_set_pid_filter_info(fh, (struct pid_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_SEC:
			ret = ST_SIZE_CHK(sec_filter_cfg, size,
				_set_sec_filter_info(fh, (struct sec_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_PCR:
			ret = ST_SIZE_CHK(pcr_filter_cfg, size,
				_set_pcr_filter_info(fh, (struct pcr_filter_cfg *)data));
			break;
		default:
			ret = -EINVAL;
			break;
		}
		break;

	case DMX2_FILTER_TYPE_IP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_NTP:
			ret = ST_SIZE_CHK(pcr_filter_cfg, size,
				_set_pcr_filter_info(fh, (struct pcr_filter_cfg *)data));
			break;
		default:
			break;
		}
		break;

	case DMX2_FILTER_TYPE_ALP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_ALP:
			ret = ST_SIZE_CHK(alp_filter_cfg, size,
				_set_alp_filter_info(fh, (struct alp_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_PTP:
			ret = ST_SIZE_CHK(ptp_filter_cfg, size,
				_set_ptp_filter_info(fh, (struct ptp_filter_cfg *)data));
			break;
		default:
			break;
		}
		break;

	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

int fh_dmxctx_get_filter_info(struct mtk_demux2_fh_ctx *fh, void *data, u32 size)
{
	int ret = 0;

	switch (fh->cfg.type.main) {
	case DMX2_FILTER_TYPE_TS:
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_PID:
			ret = ST_SIZE_CHK(pid_filter_cfg, size,
				_get_pid_filter_info(fh, (struct pid_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_SEC:
			ret = ST_SIZE_CHK(sec_filter_cfg, size,
				_get_sec_filter_info(fh, (struct sec_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_PCR:
			ret = ST_SIZE_CHK(pcr_filter_cfg, size,
				_get_pcr_filter_info(fh, (struct pcr_filter_cfg *)data));
			break;
		default:
			ret = -EINVAL;
			break;
		}
		break;

	case DMX2_FILTER_TYPE_IP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_NTP:
			ret = ST_SIZE_CHK(pcr_filter_cfg, size,
				_get_pcr_filter_info(fh, (struct pcr_filter_cfg *)data));
			break;
		default:
			break;
		}
		break;

	case DMX2_FILTER_TYPE_ALP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_ALP:
			ret = ST_SIZE_CHK(alp_filter_cfg, size,
				_get_alp_filter_info(fh, (struct alp_filter_cfg *)data));
			break;
		case FILTER_SUB_TYPE_PTP:
			ret = ST_SIZE_CHK(ptp_filter_cfg, size,
				_get_ptp_filter_info(fh, (struct ptp_filter_cfg *)data));
			break;
		default:
			break;
		}
		break;

	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

int fh_dmxctx_set_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event event)
{
	switch (fh->cfg.type.main) {
	case DMX2_FILTER_TYPE_TS:
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_SEC:
			_set_sec_filter_event(fh, event);
			return 0;
		default:
			break;
		}
		break;
	default:
		break;
	}
	return -EINVAL;
}

int fh_dmxctx_get_filter_event(struct mtk_demux2_fh_ctx *fh, enum sw_interrupt_event *event)
{
	switch (fh->cfg.type.main) {
	case DMX2_FILTER_TYPE_TS:
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		switch (fh->cfg.type.sub) {
		case FILTER_SUB_TYPE_SEC:
			_get_sec_filter_event(fh, event);
			return 0;
		default:
			break;
		}
		break;
	default:
		break;
	}
	return -EINVAL;
}

void fh_dmxctx_set_stc_info(struct mtk_demux2_fh_ctx *fh, void *stc)
{
	struct stc_info *info = (struct stc_info *)stc;

	fh->stc.eng_id = info->eng_id;
	fh->stc.is_filter_use_stc = info->is_filter_use_stc;
}

void fh_dmxctx_get_stc_info(struct mtk_demux2_fh_ctx *fh, void *stc)
{
	struct stc_info *info = (struct stc_info *)stc;

	info->eng_id = fh->stc.eng_id;
	info->is_filter_use_stc = fh->stc.is_filter_use_stc;
}

void fh_dmxctx_set_rec_parser_info(struct mtk_demux2_fh_ctx *fh, void *info)
{
	struct dmx2_record_parser_params *param = (struct dmx2_record_parser_params *)info;

	memcpy(&fh->rec_pas, param, sizeof(*param));
}

void fh_dmxctx_get_rec_parser_info(struct mtk_demux2_fh_ctx *fh, void *info)
{
	struct dmx2_record_parser_params *param = (struct dmx2_record_parser_params *)info;

	memcpy(param, &fh->rec_pas, sizeof(*param));
}

int mtk_demux2_init(struct mtk_demux2 *demux)
{
	int ret;

	if (!demux || !demux->rm || !demux->pm) {
		MTK_DMX_ERR("invalid params\n");
		return -EINVAL;
	}

	ret = _init(demux);
	if (ret < 0) {
		MTK_DMX_ERR("_init fail\n");
		return -EFAULT;
	}

	return dvb_dmx2_init(cast_to_dmx_parent(demux));
}

int mtk_demux2_exit(struct mtk_demux2 *demux)
{
	int ret;

	ret = _deinit(demux);
	if (ret < 0) {
		MTK_DMX_ERR("__deinit fail\n");
		return -EFAULT;
	}

	return dvb_dmx2_exit(cast_to_dmx_parent(demux));
}

void mtk_dmx_set_filter_secure(struct dvb_fh *fh, void *arg)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	bool *lock = (bool *)arg;
	ctx->cfg.pipeline_id.need_lock = *lock;
}

void mtk_dmx_lock_filter(struct dvb_fh *fh, struct dmxta_pipeline_id *pipeline)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct pid_filter_cfg info = {0};
	vedio_parser_ctrl p_ctrl;

	if (pipeline->pipelinid == 0 || pipeline->lock == false)
		return;

	memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
	fh_dmxctx_get_filter_info(ctx, (void *)&info,
		sizeof(struct pid_filter_cfg));

	switch (info.filter_dst_type) {
	case FILTER_DST_TYPE_VIDEO:
		p_ctrl.size = sizeof(u32);
		p_ctrl.value = pipeline->pipelinid;
		video_parser_set_control(info.dst_eng_id, VPAS_CMD_SET_SECURE, &p_ctrl);
		break;

	default:
		break;
	}
}

struct mtk_demux2 *mtk_demux2_get_mtk_demux2(struct dmx2_filter *dmx2_filter)
{
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return NULL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return NULL;
	mtk_demux2 = fh_to_demux(fh);

	return mtk_demux2;
}
