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

#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <media/dmx2dev.h>
#include <asm-generic/div64.h>

#include "mtk_dmx_common.h"
#include "mtk_dmx_log.h"
#include "mtk_dmx_dev.h"
#include "mtk_dmx_m2ts.h"
#include "mtk_dmx_pvr.h"
#include "mtk_dmx_rm.h"
#include "mtk_dmx_tm.h"
#include "mtk_dmx_io.h"
#include "mtk_dmx_filter.h"
#include "mtk_dmx_ctrl.h"
#include "mtk_dmx_drv.h"
#include "mtk_dmx_tee_if.h"
#include "mtk_dmx_mediactrl.h"
#include "mtk_mvd_parser.h"
#include "mtk_mad_parser.h"

#include "mdrv_adapter.h"
#include "mtk_dscmb.h"
#include "mtk_scrambler.h"

#define SEC_MATCH_OFFSET			(2)
#define FIQ_SEARCH_RANGE_NUM			(2)

#define TS_TIMESTAMP_LEN			(4)
#define TS_TIMESTAMP_HZ				(90000)

#define TS_HEADER_LEN				(4)
#define TS_SYNC_BYTE				(0x47)
#define NULL_PAYLOAD				(0xFF)
#define TS_PAYLOAD_ONLY				(0x10)
#define WRITE_WAIT				(1000)

#define DEFAULT_MEDIA_TIMESTAMP			(0xFFFFFFFFFFFFFFFFULL)

#define BOOL_2_STR(val) \
	((val)? "Y": "N")

#define GET_BYTE(data, position) \
	(((data) >> (8 * (position))) & 0xFF)

#define GET_BITMASK_OFFSET(var, FIELDNAME) \
	({ \
		((var) & FIELDNAME ## _BITMASK) >> FIELDNAME ## _OFFSET; \
	})

#define GET_TIMESTAMP(p) \
	( \
		(((u32)(p)[0]) << (8 * 3)) | \
		(((u32)(p)[1]) << (8 * 2)) | \
		(((u32)(p)[2]) << (8 * 1)) | \
		(((u32)(p)[3]) << (8 * 0)) | \
		0 \
	)
#define GET_TIMESTAMP_U64(p) \
	( \
		(((u64)(p)[0]) << (8 * 7)) | \
		(((u64)(p)[1]) << (8 * 6)) | \
		(((u64)(p)[2]) << (8 * 5)) | \
		(((u64)(p)[3]) << (8 * 4)) | \
		(((u64)(p)[4]) << (8 * 3)) | \
		(((u64)(p)[5]) << (8 * 2)) | \
		(((u64)(p)[6]) << (8 * 1)) | \
		(((u64)(p)[7]) << (8 * 0)) | \
		0 \
	)

#define GET_TIMESCALE(p) \
	GET_TIMESTAMP(p)

#define TEMI_TIMELINE_CURRENT(mtk_demux2, pid_type) \
	(mtk_demux2)->temi.timeline[TEMI_TIMELINE_CURRENT][pid_type]
#define TEMI_TIMELINE_PREV(mtk_demux2, pid_type) \
	(mtk_demux2)->temi.timeline[TEMI_TIMELINE_PREV][pid_type]

#define IS_SECTION_TYPE(type)	\
	((type == SIGNAL_INFO_TYPE_MP2_SECTION) || \
	 (type == SIGNAL_INFO_TYPE_TLV_SECTION) || \
	 (type == SIGNAL_INFO_TYPE_MMTP_SECTION))

#define IS_RINGBUF_TYPE(type)	\
	(type == DMX2_MEMORY_RINGBUF)

#define IS_PUSI_PACKET(ts_pkt)	\
	((ts_pkt[1] >> 6) & 0x1)

#define IS_AF_PACKET(ts_pkt)	\
	((ts_pkt[3] >> 4) & 0x02)

#define IS_PAYLOAD_PACKET(ts_pkt)	\
	((ts_pkt[3] >> 4) & 0x01)

#define TS_PACKET_SIZE (188)
#define TS_TIMESTAMP_SIZE (4)
#define FILEIN_MODULUS(is_timestamp) \
	((is_timestamp) ? (TS_PACKET_SIZE + TS_TIMESTAMP_SIZE) : TS_PACKET_SIZE)
#define FILEIN_STEP(is_timestamp) (FILEIN_MODULUS(is_timestamp))
#define FILEIN_ALIGN_SIZE(is_timestamp, size) \
	(div64_u64(size, FILEIN_STEP(is_timestamp)) * FILEIN_STEP(is_timestamp))

enum pid_filter_type {
	PID_FILTER_TYPE_AUDIO,
	PID_FILTER_TYPE_VIDEO,
	PID_FILTER_TYPE_SECTION,
	PID_FILTER_TYPE_PVR,
	PID_FILTER_TYPE_MAX,
	PID_FILTER_TYPE_NULL = -1
};

typedef enum {
	AF_FLAGS_PCR			= BIT(4),
	AF_FLAGS_OPCR			= BIT(3),
	AF_FLAGS_SPLICING_POINT		= BIT(2),
	AF_FLAGS_TRANSPORT_PRIVATE_DATA = BIT(1),
	AF_FLAGS_EXTENSION		= BIT(0)
} af_flags_mask;

typedef enum {
	AF_PCR_LEN		= 6,
	AF_OPCR_LEN		= 6,
	AF_SPLICING_POINT_LEN	= 1
} af_optional_field_len;

typedef enum {
	AF_EXT_FLAGS_LTW		= BIT(7),
	AF_EXT_FLAGS_PIECEWISE_RATE	= BIT(6),
	AF_EXT_FLAGS_SEAMLESS_SPLICE	= BIT(5),
	AF_EXT_FLAGS_DESCR_NOT_PRESENT	= BIT(4)
} af_extension_flags_mask;

typedef enum {
	AF_EXT_LTW_LEN			= 2,
	AF_EXT_PIECEWISE_RATE_LEN	= 3,
	AF_EXT_SEAMLESS_SPLICE_LEN	= 5
} af_extension_optional_field_len;

typedef enum {
	AF_DESCR_TAG_TIMELINE = 0x4,
	AF_DESCR_TAG_LOCATION = 0x5,
	AF_DESCR_TAG_BASEURL  = 0x6
} af_descr_tag;

typedef enum {
	PES_START_CODE_OFFSET	= 0,
	PES_STREAM_ID_OFFSET	= 3,
	PES_PACKET_LEN_OFFSET	= 4,
	PES_INDICATOR_OFFSET	= 6,
	PES_FLAGS_OFFSET	= 7,
	PES_HDR_DATA_LEN_OFFSET = 8,
	PES_PTS_OFFSET		= 9,
} pes_packet_offset;

typedef enum {
	PES_FLAGS_PTS	= BIT(7),
	PES_FLAGS_DTS	= BIT(6)
} pes_flags_mask;

typedef enum {
	PTS_BYTE0_OFFSET = 30,
	PTS_BYTE1_OFFSET = 22,
	PTS_BYTE2_OFFSET = 15,
	PTS_BYTE3_OFFSET = 7,
	PTS_BYTE4_OFFSET = 0
} pts_byte_offset;

typedef enum {
	PTS_BYTE0_BITMASK = 0x7,	// {4'b0010', bit[32:30], marker_bit}
	PTS_BYTE1_BITMASK = 0xFF,	// bit[29:22]
	PTS_BYTE2_BITMASK = 0x7F,	// {bit[21:15], marker_bit}
	PTS_BYTE3_BITMASK = 0xFF,	// bit[14:7]
	PTS_BYTE4_BITMASK = 0x7F	// {bit[6:0], marker_bit}
} pts_byte_bitmask;

typedef enum {
	TIMELINE_DESCR_BYTE2_OFFSET = 2,
	TIMELINE_DESCR_BYTE3_OFFSET = 3,
	TIMELINE_DESCR_TIMELINE_ID_OFFSET = 4,
	TIMELINE_DESCR_TIMESCALE_OFFSET = 5,
	TIMELINE_DESCR_MEDIA_TIMESTAMP_OFFSET = 9,
} timeline_descr_offset;

typedef enum {
	TIMELINE_DESCR_BYTE2_HAS_TIMESTAMP_OFFSET = 6,
	TIMELINE_DESCR_BYTE2_HAS_NTP_OFFSET = 5,
	TIMELINE_DESCR_BYTE2_HAS_PTP_OFFSET = 4,
	TIMELINE_DESCR_BYTE2_HAS_TIMECODE_OFFSET = 2,
	TIMELINE_DESCR_BYTE2_FORCE_RELOAD_OFFSET = 1,
	TIMELINE_DESCR_BYTE2_PAUSED_OFFSET = 0,
} timeline_descr_byte2_offset;

typedef enum {
	TIMELINE_DESCR_BYTE2_HAS_TIMESTAMP_BITMASK = BIT(7) | BIT(6),
	TIMELINE_DESCR_BYTE2_HAS_NTP_BITMASK = BIT(5),
	TIMELINE_DESCR_BYTE2_HAS_PTP_BITMASK = BIT(4),
	TIMELINE_DESCR_BYTE2_HAS_TIMECODE_BITMASK = BIT(3) | BIT(2),
	TIMELINE_DESCR_BYTE2_FORCE_RELOAD_BITMASK = BIT(1),
	TIMELINE_DESCR_BYTE2_PAUSED_BITMASK = BIT(0),
} timeline_descr_byte2_bitmask;

typedef enum {
	TIMELINE_DESCR_BYTE3_DISCONTINUITY_OFFSET = 7,
} timeline_descr_byte3_offset;

typedef enum {
	TIMELINE_DESCR_BYTE3_DISCONTINUITY_BITMASK = BIT(7),
} timeline_descr_byte3_bitmask;

typedef enum {
	TIMELINE_HAS_TIMESTAMP_NONE = 0,
	TIMELINE_HAS_TIMESTAMP_U32 = 1,
	TIMELINE_HAS_TIMESTAMP_U64 = 2,
} timeline_has_timestamp;

static int m2ts_temi_callback(
	const u8 *buf1, size_t len1,
	const u8 *buf2, size_t len2,
	struct dmx2_feed *src,
	u32 *buf_flags);

static int mtk_dmx_m2ts_start(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_flush(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed);

static int mtk_dmx_m2ts_start_av_filter(struct dmx2_filter *filter,
	struct mtk_m2ts_feed *feed, bool esPayloadMode);
static int mtk_dmx_m2ts_start_pcr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_start_sec_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_start_pvr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_start_pes_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_start_ts_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_start_temi_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);

static int mtk_dmx_m2ts_stop_av_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_pcr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_sec_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_pvr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_pes_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_ts_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_stop_temi_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);

static int mtk_dmx_m2ts_flush_sec_buf(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int mtk_dmx_m2ts_flush_av_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed);
static int m2ts_dvr_finalize_callback(struct mtk_dvr2 *dvr, int timeout);

static inline int total_filter_num(struct dmx2_filter_caps *caps)
{
	int i, ret = 0;

	for (i = 0; i < DMX2_FILTER_TYPE_MAX; i++)
		ret += caps[i].number;

	return ret;
}

static inline bool _check_fiq_range(u64 start, u64 read, u64 write, u64 end)
{
	return ((read >= start) && (read < end) && (write >= start) && (write < end));
}

static inline bool _check_fiq_search_range(u64 begin, u64 end)
{
	return ((end - begin) >= FQ_PACKET_UNIT_LENGTH);
}

static int _search_fiq_rush_addr(u16 eng_id, struct mem_info *buf, u32 delta, u64 *rush_addr)
{
	int i;
	u8 *begin[FIQ_SEARCH_RANGE_NUM] = { 0 }, *end[FIQ_SEARCH_RANGE_NUM] = { 0 }, *cur;
	bool valid[FIQ_SEARCH_RANGE_NUM] = { false };
	u32 target_time, cur_time, prev_time = 0;
	u64 start, read, write, tail;
	u32 size;

	start = buf->paddr;
	size = rounddown64(buf->size, FQ_PACKET_UNIT_LENGTH);
	tail = start + size;

	read = mtk_dmx_io_get_fiq_rptr(eng_id);
	write = mtk_dmx_io_get_fiq_wptr(eng_id);

	MTK_DMX_DEBUG("(s,r,w,e) = (0x%llx,0x%llx,0x%llx,0x%llx)\n", start, read, write, tail);

	if (!_check_fiq_range(start, read, write, tail)) {
		MTK_DMX_ERR("out-of-range !!!\n");
		return -ERANGE;
	}

	// #### 1st search range (S <---> R) ####
	if (_check_fiq_search_range(start, read)) {
		valid[0] = true;
		begin[0] = buf->vaddr;
		end[0] = buf->vaddr + (rounddown64((read - start), FQ_PACKET_UNIT_LENGTH) -
			FQ_PACKET_UNIT_LENGTH);
	}

	// #### 2nd search range (W <---> E) ####
	if (_check_fiq_search_range(write, tail)) {
		valid[1] = true;
		begin[1] = buf->vaddr + roundup64((write - start), FQ_PACKET_UNIT_LENGTH);
		end[1] = buf->vaddr + (size - FQ_PACKET_UNIT_LENGTH);
	}

	if (valid[0]) {
		target_time = GET_TIMESTAMP(end[0]) - (delta * TS_TIMESTAMP_HZ);
		MTK_DMX_DEBUG("end[0] time = %x\n", GET_TIMESTAMP(end[0]));
		MTK_DMX_DEBUG("target_time = %x\n", target_time);
	} else if (valid[1]) {
		target_time = GET_TIMESTAMP(end[1]) - (delta * TS_TIMESTAMP_HZ);
		MTK_DMX_DEBUG("end[1] time = %x\n", GET_TIMESTAMP(end[1]));
		MTK_DMX_DEBUG("target_time = %x\n", target_time);
	} else {
		MTK_DMX_ERR("can't calculate target time !!!\n");
		return -ERANGE;
	}

	if (!target_time)	// don't let the target time to be zero
		target_time += 1;

	for (i = 0; i < FIQ_SEARCH_RANGE_NUM; i++) {
		if (!valid[i]) {
			MTK_DMX_ERR("search range(%d) fail\n", i);
			continue;
		}

		cur = end[i];

		while (cur >= begin[i]) {
			// @TOOD: currently not support merge stream case, need to modify
			if (cur[TS_TIMESTAMP_LEN] != TS_SYNC_BYTE) {
				MTK_DMX_ERR("lost sync\n");
				break;
			}

			cur_time = GET_TIMESTAMP(cur);
			if (cur_time < target_time) {
				MTK_DMX_DEBUG("success !!!\n");
				*rush_addr = start + (cur - (u8*)buf->vaddr);

				return 0;
			} else if ((prev_time != 0) && (cur_time > prev_time)) {
				MTK_DMX_ERR("cur(0x%x) shall not be larger then prev(0x%x)\n",
					cur_time, prev_time);
				prev_time = 0;
				break;
			}

			prev_time = cur_time;
			cur -= FQ_PACKET_UNIT_LENGTH;
		}
	}

	MTK_DMX_DEBUG("not found !!!\n");

	return -ENODATA;
}

static int _trigger_fast_zapping(struct mtk_demux2 *demux, u16 path_id, u32 search_time, u64 *rush_addr)
{
	struct mem_info *pfiq_buffer = &demux->rm->fiqmap;

	// search rush_addr
	if (_search_fiq_rush_addr(0, pfiq_buffer, search_time, rush_addr) == -ERANGE) {
		MTK_DMX_DEBUG("search rush addr fail...\n");
		return -ERANGE;
	}

	MTK_DMX_DEBUG("rush_addr = %llx\n", *rush_addr);

	return 0;
}

static bool _is_filein_input(struct mtk_demux2 *demux)
{
	struct mtk_dvr2 *dvr_playback = &demux->path->dvr_playback;

	return dvr_playback->base.fh != NULL;
}

static int video_parser_callback(parser_id_enum parser_id,
	enum parser_event event, void *data)
{
	struct dmx2_filter *filter = (struct dmx2_filter *)data;
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2 *demux = fh_to_demux(fh);
	u16 filter_id = filter->feed->index;
	u64 rush_addr = 0;

	switch (event) {
	case PARSER_START:
		mtk_dmx_pid_filter_rush_pass(filter_id, true);
		mtk_dmx_reset_vfifo(parser_id, false);

		MTK_DMX_FTRACE(MTK_DMX_FTAG3, "trigger zapping\n");
		mutex_lock(&demux->rm->fiq_mutex);
		if (demux->rm->fiq_rush_en && demux->rm->fiq_rush_path == demux->path_id) {
			if (!_is_filein_input(demux)) {
				MTK_DMX_FTRACE(MTK_DMX_FTAG3, "fast zapping\n");
				_trigger_fast_zapping(demux
					, demux->path_id, 1, &rush_addr); // 1 sec

				mtk_dmx_io_rush_fiq(0, rush_addr);
			} else {
				MTK_DMX_INFO("path %d is configured as filein, ignore FIQ trigger\n"
					, demux->path_id);
			}
		} else {
			MTK_DMX_INFO("[path %dx][flt %d][vpas %d]"
				"fiq mmap was released by media controller disconenct event\n"
				, demux->path_id, filter_id, parser_id);
		}
		mutex_unlock(&demux->rm->fiq_mutex);
		break;
	case PARSER_STOP:
		mtk_dmx_reset_vfifo(parser_id, true);
		break;
	default:
		MTK_DMX_ERR("invalid event %d\n", event);
		return -EINVAL;
	}

	return 0;
}

static int _start_video_parser(struct dmx2_filter *filter,
	struct mtk_m2ts_feed *feed, u16 fifo_id)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);

	struct dmx2_record_parser_params rec_pas;
	parser_init_config stParserInitParam = {0};

	memset(&rec_pas, 0, sizeof(struct dmx2_record_parser_params));
	stParserInitParam.es_buf_tag = VDEC_ES_BUF_ID;
	if (fifo_id == 0)
		stParserInitParam.parser_id = PARSER_ID_0;
	else if (fifo_id == 1)
		stParserInitParam.parser_id = PARSER_ID_1;
	else
		MTK_DMX_ERR("[%s][%d] error video fifo_id = %d\n",
		__func__, __LINE__, fifo_id);

	if (((u16) fifo_id) < MAX_VPAS_NUM) {
		struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
		struct dvb2_vb2_ctx *vb2_ctx = &buf_ctx->d_ctx.buf.vb2;

		struct mtk_dmx2dev *mtkdev =
			container_of(demux->rm, struct mtk_dmx2dev, rm);
		struct platform_device *pdev = to_platform_device(mtkdev->dev);
		u32 pipeline_id = 0;
		u8 bEnableParseSlice = _is_filein_input(demux);

		fh_dmxctx_get_rec_parser_info(fh_to_dmxctx(fh), &rec_pas);

		stParserInitParam.ctx = vb2_ctx;
		if(stParserInitParam.ctx == NULL)
			MTK_DMX_ERR("[%s][%d] ctx = NULL\n", __func__, __LINE__);

		if (ctx->cfg.pipeline_id.already_lock) {
			MTK_DMX_INFO("start video filter keytoken = 0x%llx  pipelineid= 0x%x\n",
				ctx->cfg.pipeline_id.keytoken, ctx->cfg.pipeline_id.pipelinid);

			pipeline_id = ctx->cfg.pipeline_id.pipelinid;
		}

		if (video_parser_init(&stParserInitParam, pdev,
				buf_ctx, filter, rec_pas.codec_type,
				&video_parser_callback, (void *)filter,
				ctx->cfg.es_r, ctx->cfg.es_w, pipeline_id,
				bEnableParseSlice) == 0)
			MTK_DMX_ERR("video_parser_init fail\n");
	}

	return 0;
}

static int mtk_dmx_m2ts_start_av_filter(struct dmx2_filter *filter,
	struct mtk_m2ts_feed *feed, bool esPayloadMode)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = { 0 };
	struct pid_filter_cfg info = { 0 };
	enum filter_dst_type dst_type;
	int filter_id;
	int fifo_id;
	struct dscmb_connect_info dsc_info = { 0 };

	MTK_DMX_DEBUG("type: %d, payload mode: %d\n", filter->params.ts.data_type, esPayloadMode);

	switch (filter->params.ts.data_type) {
	case DMX2_TS_DATA_AU_VIDEO:
		dst_type = FILTER_DST_TYPE_VIDEO;
		fifo_id = mtk_dmx_rm_get_vfifo_idx(demux->rm);
		if ((fifo_id < 0) || (fifo_id >= MAX_VPAS_NUM)) {
			MTK_DMX_ERR("no free video fifo\n");
			return -EBUSY;
		}
		break;
	case DMX2_TS_DATA_AU_AUDIO:
	{
		struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
		struct dvb2_vb2_ctx *vb2_ctx = &buf_ctx->d_ctx.buf.vb2;
		struct mtk_dmx2dev *mtkdev = container_of(demux->rm, struct mtk_dmx2dev, rm);
		struct platform_device *pdev = to_platform_device(mtkdev->dev);

		dst_type = FILTER_DST_TYPE_AUDIO;
		fifo_id = mtk_dmx_rm_get_afifo_idx(demux->rm);
		if ((fifo_id < 0) || (fifo_id >= MAX_MAD_NUM)) {
			MTK_DMX_ERR("no free audio fifo\n");
			return -EBUSY;
		}

		if (esPayloadMode) {
			mtk_bc_metabuf_init(buf_ctx, sizeof(struct dmx2_ts_audio_metadata), MAD_META_COUNT);

			if (mdrv_pes_parser_mad_init((u16) fifo_id, pdev, mtkdev, vb2_ctx, buf_ctx,
				filter->params.type, filter) < 0) {
				mtk_bc_metabuf_release(buf_ctx);
				MTK_DMX_ERR("[%s] pes parser mad init fail\n", __func__);
				return -EFAULT;
			}
		}
	}
		break;
	default:
		MTK_DMX_ERR("pes type (%d) not support\n", filter->params.ts.data_type);
		return -EINVAL;
	}

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;
	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free pid filter\n");
		return -ENODEV;
	}
	feed->base.index = filter_id;
	fh_dmxctx_set_filter_type(ctx, type);

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_PID)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	//@TODO: MVD Parser owner need to imple it
#ifndef VPAS_PASS_THROUGH_MODE
	if (dst_type == FILTER_DST_TYPE_VIDEO) {
		struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
		mtk_bc_metabuf_init(buf_ctx, sizeof(struct dmx2_ts_video_metadata), MAX_META_Q_NUM);
		MTK_DMX_INFO("prepare for calling _start_video_parser(eng: %d, pid %x)\n",
			info.dst_eng_id, info.pid);
		_start_video_parser(filter, feed, fifo_id);
		MTK_DMX_INFO("done for calling _start_video_parser(eng: %d, pid %x)\n",
			info.dst_eng_id, info.pid);

		if (_is_filein_input(demux)) {
			MTK_DMX_INFO("path %d is configured as filein input, reset directly\n"
				, demux->path_id);
			mtk_dmx_reset_vfifo(fifo_id, false);
		}
	}
#endif
	mtk_dmx_pid_filter_init(filter_id);
	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);
	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, dst_type, (u16) fifo_id, demux->rm);

	mtk_dmx_set_fifo_src(dst_type, (u16) fifo_id, demux->path_id);

	if (dst_type == FILTER_DST_TYPE_AUDIO) {
		mtk_dmx_pid_filter_rush_pass(filter_id, false);
		mtk_dmx_reset_afifo((u16) fifo_id, false);
	}

	info.filter_dst_type = dst_type;
	info.pid = filter->params.ts.pid;
	info.src_path = demux->path_id;
	info.dst_eng_id = (u16) fifo_id;
	fh_dmxctx_set_filter_info(ctx, (void *)&info,
		sizeof(struct pid_filter_cfg));

	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.ts.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_PLAYBACK;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_connect_pid(dsc_info);

	return 0;
}

static int mtk_dmx_m2ts_start_pcr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct dvb_demux2 *base = cast_to_dmx_parent(demux);
	struct mtk_m2ts_dmx_private *priv = base->priv;
	struct filter_type type = {0};
	struct pcr_filter_cfg info;
	struct stc_info stc_info;
	int filter_idx;
	int eng_id;
	u32 cw;
	bool bypass_3_2;

	memset(&info, 0, sizeof(struct pcr_filter_cfg));
	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PCR;

	filter_idx = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_idx < 0) {
		MTK_DMX_ERR("no free pcr filter\n");
		return -ENODEV;
	}

	eng_id = mtk_dmx_rm_stc_alloc_engine(demux->rm, demux->path_id, filter_idx);
	if (eng_id < 0) {
		MTK_DMX_ERR("alloc stc resource fail\n");
		return -ENODEV;
	}

	stc_info.is_filter_use_stc = true;
	stc_info.eng_id = eng_id;
	fh_dmxctx_set_stc_info(ctx, (void *)&stc_info);
	//init cw
	bypass_3_2 = (priv->stc_bypass_3_2 != 0);
	mtk_dmx_stc_bypass_3_2(stc_info.eng_id, bypass_3_2);
	cw = priv->stc_cw;
	mtk_dmx_stc_cw(stc_info.eng_id, &cw, bypass_3_2, true);
	mtk_dmx_stc_set_mode(stc_info.eng_id, STC_MODE_TS);

	fh_dmxctx_set_filter_type(ctx, type);

	if (mtk_demux2_add_filter_list(demux, fh, filter_idx
			, FILTER_SUB_TYPE_PCR)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	feed->base.index = filter_idx;

	info.cfg.pid  = filter->params.ts.pid;
	info.cfg.src_path = demux->path_id;
	info.cfg.dst_eng_id = eng_id;
	info.pcr_ctrl.reset_pcr = 3;
	info.pcr_ctrl.prev_stc_base = 0;
	info.pcr_ctrl.prev_pll_ctrl = 0;

	fh_dmxctx_set_filter_info(ctx, (void *)&info,
		sizeof(struct pcr_filter_cfg));

	mtk_dmx_pcr_filter_sel_src(filter_idx, demux->path_id);
	mtk_dmx_pcr_filter_pid(filter_idx, &filter->params.ts.pid, true);
	mtk_dmx_pcr_filter_set_srcid(filter_idx, 0);	// @TODO: support merge stream
	mtk_dmx_pcr_filter_enable(filter_idx, true);
	return 0;
}

static int mtk_dmx_m2ts_start_pes_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mem_info mem_info = { 0 };
	struct mtk_demux2 *demux = fh_to_demux(fh);

	int filter_id;
	struct sec_filter_cfg sec_info;
	struct filter_type type = { 0 };
	enum filter_si_mode si_mode = FILTER_SI_MODE_CONTINUE;
	u64 start_addr, end_addr;
	struct dscmb_connect_info dsc_info = { 0 };

	memset(&sec_info, 0, sizeof(struct sec_filter_cfg));
	if (mtk_bc_get_mem_info(
		fh_dmxctx_get_bc_ctx(ctx),
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;

	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free section filter\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_type(ctx, type);

	cast_to_dmx2_feed(feed)->index = filter_id;

	//TODO: alloc filter without dst, and store param
	// section filter
	sec_info.si_mode.oneshot = false;
	sec_info.si_mode.crc_check = false;
	sec_info.si_type = SIGNAL_INFO_TYPE_MP2_SUBTITLE;
	sec_info.demux = cast_to_dmx_parent(demux);
	sec_info.cfg.filter_dst_type = FILTER_DST_TYPE_SEC;
	sec_info.cfg.pid = filter->params.ts.pid;
	sec_info.cfg.src_path = demux->path_id;
	sec_info.cfg.dst_eng_id = 0;

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);

	mtk_dmx_sec_filter_set_type(filter_id, FILTER_SI_TYPE_PES);
	mtk_dmx_sec_filter_set_mode(filter_id, si_mode);

	start_addr = mem_info.paddr;
	end_addr = mem_info.paddr + mem_info.size;

	mtk_dmx_sec_filter_start_addr(filter_id, &start_addr, true);
	mtk_dmx_sec_filter_end_addr(filter_id, &end_addr, true);
	mtk_dmx_sec_filter_reset_buffer(filter_id, ctx->demux->rm);
	mtk_dmx_sec_filter_dma_ctrl(filter_id, true);

	// pid filter
	mtk_dmx_pid_filter_init(filter_id);
	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);
	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, FILTER_DST_TYPE_SEC, 0, demux->rm);
	mtk_dmx_pid_filter_rush_pass(filter_id, false);
	mtk_dmx_pid_filter_sel_secflt(filter_id, filter_id);

	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.ts.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_PLAYBACK;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_connect_pid(dsc_info);

	return 0;
}

static int mtk_dmx_m2ts_start_ts_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_bc_ctx *bc_ctx = fh_dmxctx_get_bc_ctx(ctx);
	struct mem_info mem_info = { 0 };
	struct mtk_demux2 *demux = fh_to_demux(fh);

	int filter_id;
	struct sec_filter_cfg sec_info;
	struct filter_type type = { 0 };
	enum filter_si_mode si_mode = FILTER_SI_MODE_CONTINUE;
	u64 start_addr, end_addr;

	memset(&sec_info, 0, sizeof(struct sec_filter_cfg));
	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;

	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free section filter\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_type(ctx, type);

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	cast_to_dmx2_feed(feed)->index = filter_id;

	//TODO: alloc filter without dst, and store param
	// section filter
	sec_info.si_mode.oneshot = false;
	sec_info.si_mode.crc_check = false;
	sec_info.si_type = SIGNAL_INFO_TYPE_MP2_PACKET;
	sec_info.demux = cast_to_dmx_parent(demux);
	sec_info.cfg.filter_dst_type = FILTER_DST_TYPE_SEC;
	sec_info.cfg.pid = filter->params.ts.pid;
	sec_info.cfg.src_path = demux->path_id;
	sec_info.cfg.dst_eng_id = 0;

	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	if (mtk_bc_metabuf_init(bc_ctx,
		MTK_BC_METADATA_BUF_SIZE,
		MTK_BC_METADATA_BUF_NUM) < 0) {
		MTK_DMX_ERR("init metadata buffer fail\n");
		return -EFAULT;
	}

	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);

	mtk_dmx_sec_filter_set_type(filter_id, FILTER_SI_TYPE_PKT);
	mtk_dmx_sec_filter_set_mode(filter_id, si_mode);

	start_addr = mem_info.paddr;
	end_addr = mem_info.paddr + mem_info.size;

	mtk_dmx_sec_filter_start_addr(filter_id, &start_addr, true);
	mtk_dmx_sec_filter_end_addr(filter_id, &end_addr, true);
	mtk_dmx_sec_filter_reset_buffer(filter_id, ctx->demux->rm);
	mtk_dmx_sec_filter_dma_ctrl(filter_id, true);

	// pid filter
	mtk_dmx_pid_filter_init(filter_id);
	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);
	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, FILTER_DST_TYPE_SEC, 0, demux->rm);
	mtk_dmx_pid_filter_rush_pass(filter_id, false);
	mtk_dmx_pid_filter_sel_secflt(filter_id, filter_id);

	return 0;
}

static int mtk_dmx_m2ts_start_ts_undefined_filter(struct dmx2_filter *filter,
					struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct pid_filter_cfg info = { 0 };

	int filter_id;
	struct filter_type type = { 0 };

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;

	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free section filter\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_type(ctx, type);

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	cast_to_dmx2_feed(feed)->index = filter_id;

	info.filter_dst_type = FILTER_DST_TYPE_NULL;
	info.pid = filter->params.ts.pid;
	info.src_path = demux->path_id;

	fh_dmxctx_set_filter_info(ctx, (void *)&info,
		sizeof(struct sec_filter_cfg));

	// pid filter
	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);
	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, FILTER_DST_TYPE_NULL, 0, demux->rm);
	mtk_dmx_pid_filter_rush_pass(filter_id, false);
	mtk_dmx_pid_filter_sel_secflt(filter_id, filter_id);

	return 0;
}

static int m2ts_temi_timeline_exit(struct mtk_demux2 *mtk_demux2)
{
	int i;
	int j;

	if (mtk_demux2 == NULL)
		return -EINVAL;

	vfree(mtk_demux2->temi.meta);
	mtk_demux2->temi.meta = NULL;

	for (i = 0; i < TEMI_TIMELINE_MAX; i++) {
		for (j = 0; j < M2TS_PID_TYPE_MAX; j++) {
			// NULL is checked by vfree
			vfree(mtk_demux2->temi.timeline[i][j]);
			mtk_demux2->temi.timeline[i][j] = NULL;
		}
	}

	mtk_demux2->temi.timeline_size = 0;

	return 0;
}

static int m2ts_temi_timeline_init(struct mtk_demux2 *mtk_demux2)
{
	int i;
	int j;
	int k;
	bool allocated = true;

	if (mtk_demux2 == NULL)
		return -EINVAL;

	mtk_demux2->temi.timeline_size = TIMELINE_DESCR_MAX;
	mtk_demux2->temi.meta = vzalloc(
		sizeof(struct dmx2_temi_metadata) *
		mtk_demux2->temi.timeline_size);
	if (mtk_demux2->temi.meta == NULL) {
		MTK_DMX_ERR("Allocate memory for meta is failed\n");

		if (m2ts_temi_timeline_exit(mtk_demux2) < 0)
			MTK_DMX_DEBUG("m2ts_temi_timeline_exit is failed\n");

		return -ENOMEM;
	}

	// timeline is reset as NULL in advance.
	for (i = 0; i < TEMI_TIMELINE_MAX; i++) {
		for (j = 0; j < M2TS_PID_TYPE_MAX; j++) {
			mtk_demux2->temi.timeline[i][j] =
				vzalloc(sizeof(struct timeline) *
				mtk_demux2->temi.timeline_size);
			allocated = (mtk_demux2->temi.timeline[i][j] != NULL);
			if (!allocated)
				break;
		}
		if (!allocated)
			break;
	}
	if (!allocated) {
		MTK_DMX_ERR("%s is failed\n", __func__);

		if (m2ts_temi_timeline_exit(mtk_demux2) < 0)
			MTK_DMX_DEBUG("m2ts_temi_timeline_exit is failed\n");

		return -ENOMEM;
	}

	for (i = 0; i < TEMI_TIMELINE_MAX; i++) {
		for (j = 0; j < M2TS_PID_TYPE_MAX; j++) {
			for (k = 0; k < mtk_demux2->temi.timeline_size; k++) {
				mtk_demux2->temi.timeline[i][j][k].timeline_descr.media_timestamp =
					DEFAULT_MEDIA_TIMESTAMP;
				mtk_demux2->temi.timeline[i][j][k].metadata.pts =
					DEFAULT_MEDIA_TIMESTAMP;
			}
		}
	}

	return 0;
}

static int m2ts_timeline_id_exit(struct mtk_demux2 *mtk_demux2)
{
	int i;

	if (mtk_demux2 == NULL)
		return -EINVAL;

	for (i = 0; i < M2TS_PID_TYPE_MAX; i++) {
		// NULL is checked by vfree
		vfree(mtk_demux2->temi.timeline_id[i]);
		mtk_demux2->temi.timeline_id[i] = NULL;
	}

	mtk_demux2->temi.timeline_id_size = 0;

	return 0;
}

static int m2ts_timeline_id_init(struct mtk_demux2 *mtk_demux2)
{
	int i;
	bool allocated = true;

	if (mtk_demux2 == NULL)
		return -EINVAL;

	mtk_demux2->temi.timeline_id_size = TIMELINE_DESCR_MAX;

	// timeline_id is reset as NULL in advance.
	for (i = 0; i < M2TS_PID_TYPE_MAX; i++) {
		mtk_demux2->temi.timeline_id[i] =
			vzalloc(sizeof(u8) *
			mtk_demux2->temi.timeline_id_size);
		allocated = (mtk_demux2->temi.timeline_id[i] != NULL);
		if (!allocated)
			break;
	}
	if (!allocated) {
		MTK_DMX_ERR("%s is failed\n", __func__);

		if (m2ts_timeline_id_exit(mtk_demux2) < 0)
			MTK_DMX_DEBUG("m2ts_timeline_id_exit is failed\n");

		return -ENOMEM;
	}

	return 0;
}

static int mt2s_temi_exit(struct mtk_demux2 *mtk_demux2)
{
	int ret = 0;
	int tmp_ret = 0;

	tmp_ret = m2ts_timeline_id_exit(mtk_demux2);
	if (tmp_ret < 0) {
		MTK_DMX_DEBUG("m2ts_timeline_id_exit is failed\n");
		ret = tmp_ret;
	}

	tmp_ret = m2ts_temi_timeline_exit(mtk_demux2);
	if (tmp_ret < 0) {
		MTK_DMX_DEBUG("m2ts_temi_timeline_exit is failed\n");
		if (ret == 0)
			ret = tmp_ret;
	}

	return ret;
}

static int m2ts_temi_reset(struct mtk_demux2 *mtk_demux2)
{
	int i;
	int j;

	if (mtk_demux2 == NULL)
		return -EINVAL;

	mtk_demux2->temi.timeline_size = 0;
	for (i = 0; i < TEMI_TIMELINE_MAX; i++) {
		for (j = 0; j < M2TS_PID_TYPE_MAX; j++) {
			mtk_demux2->temi.timeline[i][j] = NULL;
		}
	}

	mtk_demux2->temi.timeline_id_size = 0;
	for (i = 0; i < M2TS_PID_TYPE_MAX; i++) {
		mtk_demux2->temi.timeline_id[i] = NULL;
		mtk_demux2->temi.timeline_descr_num[i] = 0;
	}

	mtk_demux2->temi.meta = NULL;

	return 0;
}

static int m2ts_temi_init(struct mtk_demux2 *mtk_demux2)
{
	int ret = 0;

	ret = m2ts_temi_reset(mtk_demux2);
	if (ret < 0) {
		MTK_DMX_ERR("m2ts_temi_filter_reset is failed\n");

		return ret;
	}

	ret = m2ts_temi_timeline_init(mtk_demux2);
	if (ret < 0) {
		MTK_DMX_ERR("m2ts_temi_timeline_init is failed\n");

		return ret;
	}

	ret = m2ts_timeline_id_init(mtk_demux2);
	if (ret < 0) {
		MTK_DMX_DEBUG("m2ts_timeline_id_init is failed\n");

		if (mt2s_temi_exit(mtk_demux2) < 0)
			MTK_DMX_DEBUG("mt2s_temi_filter_exit is failed\n");

		return ret;
	}

	return ret;
}

static int m2ts_get_pid_type(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
#ifdef TEMI_PID_TYPE_ENABLE
	enum m2ts_pid_type pid_type = M2TS_PID_TYPE_OTHER;

	struct mtk_demux2 *mtk_demux2 = NULL;
	struct pid_filter_cfg *pid_filter_cfg = NULL;

	if (filter == NULL)
		return M2TS_PID_TYPE_OTHER;

	mtk_demux2 = mtk_demux2_get_mtk_demux2(filter);
	if (mtk_demux2 == NULL)
		return M2TS_PID_TYPE_OTHER;

	pid_filter_cfg =
		mtk_demux2_query_pid_filter_cfg(
			mtk_demux2,
			filter->params.ts.pid);
	if (pid_filter_cfg == NULL)
		return M2TS_PID_TYPE_OTHER;

	switch (pid_filter_cfg->filter_dst_type) {
	case FILTER_DST_TYPE_VIDEO:
		pid_type = M2TS_PID_TYPE_ES_VIDEO;
		break;

	case FILTER_DST_TYPE_AUDIO:
		pid_type = M2TS_PID_TYPE_ES_AUDIO;
		break;

	default:
		break;
	}

	return pid_type;
#else
	return M2TS_PID_TYPE_OTHER;
#endif //#ifdef TEMI_PID_TYPE_ENABLE
}

static int mtk_dmx_m2ts_start_temi_filter(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
	if (filter == NULL)
		return -EINVAL;
	if (feed == NULL)
		return -EINVAL;

	// replace default callback
	cast_to_dmx2_feed(feed)->callback = &m2ts_temi_callback;
	cast_to_m2ts_filter(filter)->pid_type = m2ts_get_pid_type(filter, feed);

	return mtk_dmx_m2ts_start_ts_filter(filter, feed);
}

static int mtk_dmx_m2ts_start_pvr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = {0};
	struct pid_filter_cfg info = { 0 };
	int filter_id;
	int pvr_eng_id = mtk_dmx_rm_pvr_alloc_engine(demux->rm, demux->path_id);
	struct dscmb_connect_info dsc_info= { 0 };
	struct scrambler_connect_info scramb_info = {0};

	if (pvr_eng_id < 0) {
		MTK_DMX_ERR("no free pvr eng\n");
		return -ENODEV;
	}

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;

	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free pid filter\n");
		return -ENODEV;
	}

	feed->base.index = filter_id;

	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);

	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, FILTER_DST_TYPE_PVR, (u16)pvr_eng_id, demux->rm);
	mtk_dmx_pid_filter_rush_pass(filter_id, false);

	info.filter_dst_type = FILTER_DST_TYPE_PVR;
	info.pid = filter->params.ts.pid;
	info.src_path = demux->path_id;
	info.dst_eng_id = (u16)pvr_eng_id;
	fh_dmxctx_set_filter_info(ctx, (void *)&info,
		sizeof(struct pid_filter_cfg));

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_PID)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.ts.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_RECORD;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_connect_pid(dsc_info);

	scramb_info.path_id = demux->path_id;
	scramb_info.pid = filter->params.ts.pid;
	scramb_info.pid_flt_id = filter_id;
	scramb_info.purpose = PURPOSE_RECORD;
	scramb_info.set_tlv_avsrc = false;
	mdrv_scrambler_connect_pid(scramb_info);
	return 0;
}

static int mtk_dmx_m2ts_stop_av_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct pid_filter_cfg info = { 0 };
	struct filter_type type = {0};
	struct mtk_bc_ctx *bc_ctx = fh_dmxctx_get_bc_ctx(ctx);
	u16 filter_id = cast_to_dmx2_feed(feed)->index;
	struct dscmb_connect_info dsc_info = { 0 };

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;

	fh_dmxctx_get_filter_info(ctx, (void *)&info,
		sizeof(struct pid_filter_cfg));
	MTK_DMX_DEBUG("engine id: %d, dst type: %d\n"
		, info.dst_eng_id, info.filter_dst_type);

	if (mtk_demux2_remove_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_PID)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}

	switch (info.filter_dst_type) {
	case FILTER_DST_TYPE_AUDIO:
	{
		struct mtk_dmx2dev *mtkdev = container_of(demux->rm, struct mtk_dmx2dev, rm);
		struct platform_device *pdev = to_platform_device(mtkdev->dev);

		mtk_dmx_reset_afifo(info.dst_eng_id, true);
		mdrv_pes_parser_mad_exit(info.dst_eng_id, pdev, mtkdev);
		mtk_bc_metabuf_reset(bc_ctx);
		mtk_dmx_rm_put_afifo_idx(demux->rm, info.dst_eng_id);
		mtk_bc_metabuf_release(bc_ctx);
	}
		break;
	case FILTER_DST_TYPE_VIDEO:
		mtk_dmx_reset_vfifo(info.dst_eng_id, true);
#ifndef VPAS_PASS_THROUGH_MODE
	{
		vedio_parser_ctrl p_ctrl;


		MTK_DMX_INFO("prepare for calling video_parser_stop(eng: %d, pid: %x)\n",
			info.dst_eng_id, info.pid);
		video_parser_stop(info.dst_eng_id);
		MTK_DMX_INFO("done for calling video_parser_stop(eng: %d, pid: %x)\n",
			info.dst_eng_id, info.pid);

		p_ctrl.size = sizeof(u32);
		p_ctrl.value = 0;
		video_parser_get_control(info.dst_eng_id, VPAS_CMD_GET_START_OFFSET, &p_ctrl);
		ctx->cfg.es_w = p_ctrl.value;
		video_parser_get_control(info.dst_eng_id, VPAS_CMD_GET_READ_OFFSET, &p_ctrl);
		ctx->cfg.es_r = p_ctrl.value;

		MTK_DMX_INFO("prepare for calling video_parser_deinit(eng: %d, pid: %x)\n",
			info.dst_eng_id, info.pid);
		video_parser_deinit(info.dst_eng_id);
		MTK_DMX_INFO("done for calling video_parser_deinit(eng: %d, pid: %x)\n",
			info.dst_eng_id, info.pid);

	}
#endif
		mtk_bc_metabuf_reset(bc_ctx);
		mtk_dmx_rm_put_vfifo_idx(demux->rm, info.dst_eng_id);
#ifndef VPAS_PASS_THROUGH_MODE
		mtk_bc_metabuf_release(bc_ctx);
#endif
		break;
	default:
		MTK_DMX_ERR("filter type is not audio or video\n");
		return -EINVAL;
	}

	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.mmtp.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_PLAYBACK;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_disconnect_pid(dsc_info);

	mtk_dmx_set_fifo_src(info.filter_dst_type, info.dst_eng_id, DEMUX_PATH_NULL);
	mtk_dmx_pid_filter_init(filter_id);

	mtk_dmx_rm_put_filter_idx(demux->rm, type, filter_id);

	return 0;
}

static int mtk_dmx_m2ts_stop_pcr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct filter_type type = { 0 };
	struct stc_info stc_info;
	u32 pcr_base, pcr_base_32;
	u16 eng_id = cast_to_dmx2_feed(feed)->index;

	struct pcr_filter_cfg pcr_info = {
		.cfg = {.dst_eng_id = 0, .pid = NULL_PID },
		.int_cnt = 0,
		.pcr = 0,
		.pcr_ctrl = {.reset_pcr = 0, .prev_stc_base = 0, .prev_pll_ctrl = 0 },
	};

	if (mtk_demux2_remove_filter_list(demux, fh, eng_id
			, FILTER_SUB_TYPE_PCR)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}

	mtk_dmx_pcr_filter_init(eng_id);
	//always read PCR after PCR interrupt has been disabled
	//or cleared to avoid that interrupt's not trggering again.
	mtk_dmx_pcr_filter_get_PCR(eng_id, &pcr_base, &pcr_base_32);

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PCR;

	mtk_dmx_rm_put_filter_idx(demux->rm, type, eng_id);

	fh_dmxctx_get_stc_info(ctx, (void *)&stc_info);

	mtk_dmx_rm_stc_free_engine(demux->rm, stc_info.eng_id);

	fh_dmxctx_set_filter_info(ctx, (void *)&pcr_info,
		sizeof(struct pcr_filter_cfg));

	return 0;
}

static int mtk_dmx_m2ts_start_sec_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct mem_info mem_info = { 0 };

	int filter_id, i;
	u8 match[SECFLT_PATTERN_DEPTH] = { 0 };
	u8 mask[SECFLT_PATTERN_DEPTH] = { [0 ... (SECFLT_PATTERN_DEPTH-1)] = 0xFF };
	u8 nmask[SECFLT_PATTERN_DEPTH] = { 0 };
	struct sec_filter_cfg sec_info;
	struct filter_type type = { 0 };
	enum filter_si_mode si_mode = FILTER_SI_MODE_CONTINUE;
	u64 start_addr, end_addr;
	struct dscmb_connect_info dsc_info = { 0 };

	memset(&sec_info, 0, sizeof(struct sec_filter_cfg));
	if (mtk_bc_get_mem_info(
		fh_dmxctx_get_bc_ctx(ctx),
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;

	filter_id = mtk_dmx_rm_get_filter_idx(demux->rm, type);
	if (filter_id < 0) {
		MTK_DMX_ERR("no free section filter\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_type(ctx, type);

	MTK_DMX_DEBUG("Open section filter id:%d\n", filter_id);

	feed->base.index = filter_id;

	//TODO: alloc filter without dst, and store param
	// section filter
	if (filter->params.ts.section.flags & DMX2_ONESHOT) {
		si_mode = FILTER_SI_MODE_ONESHOT;
		sec_info.si_mode.oneshot = true;
	}
	if (filter->params.ts.section.flags & DMX2_CHECK_CRC) {
		si_mode |= FILTER_SI_MODE_CRC_CHECK;
		sec_info.si_mode.crc_check = true;
	}

	sec_info.si_type = SIGNAL_INFO_TYPE_MP2_SECTION;
	sec_info.demux = &demux->base;
	sec_info.cfg.filter_dst_type = FILTER_DST_TYPE_SEC;
	sec_info.cfg.pid = filter->params.ts.pid;
	sec_info.cfg.src_path = demux->path_id;
	sec_info.cfg.dst_eng_id = 0;

	if (mtk_demux2_add_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("add filter list fail\n");
		return -ENODEV;
	}

	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);

	mtk_dmx_sec_filter_set_type(filter_id, FILTER_SI_TYPE_SECTION);
	mtk_dmx_sec_filter_set_mode(filter_id, si_mode);

	match[0] = filter->params.ts.section.section_bits.filter[0];
	mask[0] = ~filter->params.ts.section.section_bits.mask[0];
	nmask[0] = filter->params.ts.section.section_bits.mode[0];

	for (i = 1; i < (SECFLT_PATTERN_DEPTH - SEC_MATCH_OFFSET); i++) {
		match[i+SEC_MATCH_OFFSET] = filter->params.ts.section.section_bits.filter[i];
		// need to invert mask (for our HW)
		mask[i+SEC_MATCH_OFFSET] = ~filter->params.ts.section.section_bits.mask[i];
		nmask[i+SEC_MATCH_OFFSET] = filter->params.ts.section.section_bits.mode[i];
	}

	mtk_dmx_sec_filter_set_pattern(filter_id, mask, nmask, match);

	start_addr = mem_info.paddr;
	end_addr = mem_info.paddr + mem_info.size;

	mtk_dmx_sec_filter_start_addr(filter_id, &start_addr, true);
	mtk_dmx_sec_filter_end_addr(filter_id, &end_addr, true);
	mtk_dmx_sec_filter_reset_buffer(filter_id, ctx->demux->rm);
	mtk_dmx_sec_filter_dma_ctrl(filter_id, true);

	// pid filter
	mtk_dmx_pid_filter_init(filter_id);
	mtk_dmx_pid_filter_sel_src(filter_id, demux->path_id);
	mtk_dmx_pid_filter_set_pid(filter_id, filter->params.ts.pid);
	mtk_dmx_pid_filter_sel_dst(filter_id, FILTER_DST_TYPE_SEC, 0, demux->rm);
	mtk_dmx_pid_filter_rush_pass(filter_id, false);
	mtk_dmx_pid_filter_sel_secflt(filter_id, filter_id);

	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.ts.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_PLAYBACK;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_connect_pid(dsc_info);

	return 0;
}

static int mtk_dmx_m2ts_stop_sec_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = { 0 };
	int filter_id = 0;
	u16 pid_filter_num = mtk_dmx_rm_get_hwcap_by_id(demux->rm, HWCAPS_TYPE_PID_FILTER);

	struct sec_filter_cfg sec_info = {
		.cfg = {.filter_dst_type = FILTER_DST_TYPE_NULL, .pid = 0xFFFF },
		.si_type = SIGNAL_INFO_TYPE_NULL,
		.si_mode = {.oneshot = false, .crc_check = false},
		.demux = NULL,
	};

	// pid filter
	filter_id = cast_to_dmx2_feed(feed)->index;
	if (WARN_ON((filter_id < 0) || (filter_id >= pid_filter_num)))
		return -EINVAL;

	if (mtk_demux2_remove_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}
	// stop AEON FW
	mtk_dmx_sec_filter_dma_ctrl(filter_id, false);

	// stop pid filter
	mtk_dmx_pid_filter_init(filter_id);
	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;
	mtk_dmx_rm_put_filter_idx(demux->rm, type, filter_id);

	// sec filter
	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);
	fh_dmxctx_set_filter_event(ctx, SW_INT_EVENT_NULL);
	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	return 0;
}

static int mtk_dmx_m2ts_stop_pvr_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = { 0 };
	struct scrambler_connect_info scramb_info = {0};
	u16 filter_id = cast_to_dmx2_feed(feed)->index;

	scramb_info.path_id = demux->path_id;
	scramb_info.pid = filter->params.ts.pid;
	scramb_info.pid_flt_id = filter_id;
	scramb_info.purpose = PURPOSE_RECORD;
	scramb_info.set_tlv_avsrc = false;
	mdrv_scrambler_disconnect_pid(scramb_info);

	if (mtk_demux2_remove_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_PID)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}

	mtk_dmx_pid_filter_init(filter_id);

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;

	mtk_dmx_rm_put_filter_idx(demux->rm, type, filter_id);

	return 0;
}

static int mtk_dmx_m2ts_stop_pes_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = { 0 };
	u16 filter_id = 0;
	struct dscmb_connect_info dsc_info = { 0 };

	struct sec_filter_cfg sec_info = {
		.cfg = {.filter_dst_type = FILTER_DST_TYPE_NULL, .pid = 0xFFFF },
		.si_type = SIGNAL_INFO_TYPE_NULL,
		.si_mode = {.oneshot = false, .crc_check = false},
		.demux = NULL,
	};
	filter_id = cast_to_dmx2_feed(feed)->index;

	if (mtk_demux2_remove_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}
	// stop AEON FW
	mtk_dmx_sec_filter_dma_ctrl(filter_id, false);

	// disconnect from dsc
	dsc_info.path_id = demux->path_id;
	dsc_info.pid = filter->params.mmtp.pid;
	dsc_info.pid_flt_id = filter_id;
	dsc_info.purpose = PURPOSE_PLAYBACK;
	dsc_info.set_tlv_avsrc = false;
	mtk_dscmb_disconnect_pid(dsc_info);

	// pid filter
	mtk_dmx_pid_filter_init(filter_id);
	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;
	mtk_dmx_rm_put_filter_idx(demux->rm, type, filter_id);

	// sec filter
	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);
	fh_dmxctx_set_filter_event(ctx, SW_INT_EVENT_NULL);
	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	return 0;
}

static int mtk_dmx_m2ts_stop_ts_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_bc_ctx *bc_ctx = fh_dmxctx_get_bc_ctx(ctx);
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct filter_type type = { 0 };
	u16 filter_id = 0;

	struct sec_filter_cfg sec_info = {
		.cfg = {.filter_dst_type = FILTER_DST_TYPE_NULL, .pid = 0xFFFF },
		.si_type = SIGNAL_INFO_TYPE_NULL,
		.si_mode = {.oneshot = false, .crc_check = false},
		.demux = NULL,
	};
	filter_id = cast_to_dmx2_feed(feed)->index;

	if (mtk_demux2_remove_filter_list(demux, fh, filter_id
			, FILTER_SUB_TYPE_SEC)) {
		MTK_DMX_ERR("remove filter list fail\n");
		return -ENODEV;
	}
	// stop AEON FW
	mtk_dmx_sec_filter_dma_ctrl(filter_id, false);


	// pid filter
	mtk_dmx_pid_filter_init(filter_id);
	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_SEC;
	mtk_dmx_rm_put_filter_idx(demux->rm, type, filter_id);

	// sec filter
	mtk_dmx_sec_filter_dma_ctrl(filter_id, false);
	mtk_dmx_sec_filter_init(filter_id, ctx->demux->rm);
	fh_dmxctx_set_filter_event(ctx, SW_INT_EVENT_NULL);
	fh_dmxctx_set_filter_info(ctx, (void *)&sec_info,
		sizeof(struct sec_filter_cfg));

	if (mtk_bc_metabuf_release(bc_ctx) < 0) {
		MTK_DMX_ERR("release metadata buffer fail\n");
		return -EFAULT;
	}

	return 0;
}

static int mtk_dmx_m2ts_stop_temi_filter(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
	return mtk_dmx_m2ts_stop_ts_filter(filter, feed);
}

static int mtk_dmx_m2ts_flush_sec_buf(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	u16 filter_id = cast_to_dmx2_feed(feed)->index;
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);

	mtk_dmx_sec_filter_reset_buffer(filter_id, ctx->demux->rm);
	return 0;
}

static int mtk_dmx_m2ts_flush_av_filter(struct dmx2_filter *filter,
									struct mtk_m2ts_feed *feed)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mtk_bc_ctx *buf_ctx = fh_dmxctx_get_bc_ctx(ctx);
	struct pid_filter_cfg info = { 0 };
	struct filter_type type = {0};

	type.main = filter->params.type;
	type.sub = FILTER_SUB_TYPE_PID;

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

	switch (info.filter_dst_type) {
	case FILTER_DST_TYPE_AUDIO:
	{
		mtk_dmx_reset_afifo(info.dst_eng_id, true);
		usleep_range(WRITE_WAIT, WRITE_WAIT);

		if (filter->params.output == DMX2_OUT_TAP) {
			struct mtk_demux2 *demux = fh_to_demux(fh);
			struct mtk_dmx2dev *mtkdev = container_of(demux->rm, struct mtk_dmx2dev, rm);
			struct platform_device *pdev = to_platform_device(mtkdev->dev);
			struct dvb2_vb2_ctx *vb2_ctx = &buf_ctx->d_ctx.buf.vb2;

			mdrv_pes_parser_mad_exit(info.dst_eng_id, pdev, mtkdev);

			mtk_bc_metabuf_reset(buf_ctx);

			if (mdrv_pes_parser_mad_init((u16) info.dst_eng_id, pdev, mtkdev, vb2_ctx
				, buf_ctx, filter->params.type, filter) < 0)
				MTK_DMX_ERR("[%s] pes parser mad init fail\n", __func__);
		}

		mtk_dmx_reset_afifo(info.dst_eng_id, false);
		break;
	}
	case FILTER_DST_TYPE_VIDEO:
	{
		int pipeline_id;
		vedio_parser_ctrl p_ctrl;

		pipeline_id = video_parser_get_pipeline_id(info.dst_eng_id);

		usleep_range(WRITE_WAIT, WRITE_WAIT);

		video_parser_stop(info.dst_eng_id);

		p_ctrl.size = sizeof(u32);
		p_ctrl.value = 0;
		video_parser_get_control(info.dst_eng_id, VPAS_CMD_GET_START_OFFSET, &p_ctrl);
		ctx->cfg.es_w = p_ctrl.value;
		video_parser_get_control(info.dst_eng_id, VPAS_CMD_GET_READ_OFFSET, &p_ctrl);
		ctx->cfg.es_r = p_ctrl.value;

		video_parser_deinit(info.dst_eng_id);

		mtk_bc_metabuf_reset(buf_ctx);

		_start_video_parser(filter, feed, info.dst_eng_id);

		if (pipeline_id != 0) {
			vedio_parser_ctrl p_ctrl;
			memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
			p_ctrl.size = sizeof(u32);
			p_ctrl.value = pipeline_id;
			video_parser_set_control(info.dst_eng_id, VPAS_CMD_SET_SECURE, &p_ctrl);
		}


		mtk_dmx_reset_vfifo(info.dst_eng_id, false);
		break;
	}
	default:
		break;
	}

	return 0;
}

int mtk_dmx_m2ts_start(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
	int ret = -1;

	switch (filter->params.ts.data_type) {
	case DMX2_TS_DATA_SECTION:
		ret = mtk_dmx_m2ts_start_sec_filter(filter, feed);
		break;
	case DMX2_TS_DATA_PES:
		ret = mtk_dmx_m2ts_start_pes_filter(filter, feed);
		break;
	case DMX2_TS_DATA_PCR:
		ret = mtk_dmx_m2ts_start_pcr_filter(filter, feed);
		break;
	case DMX2_TS_DATA_AU_VIDEO:
	case DMX2_TS_DATA_AU_AUDIO:
		if (filter->params.output == DMX2_OUT_TAP) // es payload mode
			ret = mtk_dmx_m2ts_start_av_filter(filter, feed, true);
		else if (filter->params.output == DMX2_OUT_DECODER) // pass through mode
			ret = mtk_dmx_m2ts_start_av_filter(filter, feed, false);
		break;
	case DMX2_TS_DATA_ALL:
		if (filter->params.output == DMX2_OUT_NONE)
			ret = mtk_dmx_m2ts_start_ts_undefined_filter(filter, feed);
		else if (filter->params.output == DMX2_OUT_DVR_TAP)
			ret = mtk_dmx_m2ts_start_pvr_filter(filter, feed);
		else
			ret = mtk_dmx_m2ts_start_ts_filter(filter, feed);
		break;
	default:
		if ((u32)(filter->params.ts.data_type) == DMX2_TS_PRIV_DATA_TEMI_AF) {
			ret = mtk_dmx_m2ts_start_temi_filter(filter, feed);
			break;
		} else
			return -1;
	}

	return ret;
}

int mtk_dmx_m2ts_stop(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
	int ret = -1;

	switch (filter->params.ts.data_type) {
	case DMX2_TS_DATA_SECTION:
		ret = mtk_dmx_m2ts_stop_sec_filter(filter, feed);
		break;
	case DMX2_TS_DATA_PES:
		ret = mtk_dmx_m2ts_stop_pes_filter(filter, feed);
		break;
	case DMX2_TS_DATA_PCR:
		ret = mtk_dmx_m2ts_stop_pcr_filter(filter, feed);
		break;
	case DMX2_TS_DATA_AU_VIDEO:
	case DMX2_TS_DATA_AU_AUDIO:
		if (filter->params.output == DMX2_OUT_TAP) // es payload mode
			ret = mtk_dmx_m2ts_stop_av_filter(filter, feed);
		else if (filter->params.output == DMX2_OUT_DECODER) // pass through mode
			ret = mtk_dmx_m2ts_stop_av_filter(filter, feed);
		break;
	case DMX2_TS_DATA_ALL:
		if (filter->params.output == DMX2_OUT_DVR_TAP)
			ret = mtk_dmx_m2ts_stop_pvr_filter(filter, feed);
		else
			ret = mtk_dmx_m2ts_stop_ts_filter(filter, feed);
		break;
	default:
		if ((u32)(filter->params.ts.data_type) == DMX2_TS_PRIV_DATA_TEMI_AF) {
			ret = mtk_dmx_m2ts_stop_temi_filter(filter, feed);
			break;
		} else
			return -1;
	}
	return ret;
}

int mtk_dmx_m2ts_flush(struct dmx2_filter *filter, struct mtk_m2ts_feed *feed)
{
	int ret;

	switch (filter->params.ts.data_type) {
	case DMX2_TS_DATA_SECTION:
	case DMX2_TS_DATA_PES:
		ret = mtk_dmx_m2ts_flush_sec_buf(filter, feed);
		break;
	case DMX2_TS_DATA_AU_VIDEO:
	case DMX2_TS_DATA_AU_AUDIO:
		ret = mtk_dmx_m2ts_flush_av_filter(filter, feed);
		break;
	default:
		return -1;
	}

	return ret;
}

static int m2ts_feed_start(struct dmx2_feed *feed)
{
	struct mtk_m2ts_feed *m2ts_feed = cast_to_m2ts_feed(feed);
	struct dmx2_filter *filter = feed->filter;
	int ret = -1;

	switch (filter->params.type) {
	case DMX2_FILTER_TYPE_TS:
		ret = mtk_dmx_m2ts_start(filter, m2ts_feed);
		break;
	default:
		return -1;
	}

	return ret;
}

static int m2ts_feed_stop(struct dmx2_feed *feed)
{
	struct mtk_m2ts_feed *m2ts_feed = cast_to_m2ts_feed(feed);
	struct dmx2_filter *filter = feed->filter;

	switch (filter->params.type) {
	case DMX2_FILTER_TYPE_TS:
		mtk_dmx_m2ts_stop(filter, m2ts_feed);
		break;
	default:
		return -1;
	}

	return 0;
}

static int m2ts_feed_flush(struct dmx2_feed *feed)
{
	struct mtk_m2ts_feed *m2ts_feed = cast_to_m2ts_feed(feed);
	struct dmx2_filter *filter = feed->filter;

	switch (filter->params.type) {
	case DMX2_FILTER_TYPE_TS:
		mtk_dmx_m2ts_flush(filter, m2ts_feed);
		break;
	default:
		return -1;
	}

	return 0;
}

static struct dmx2_feed_operations m2ts_feed_ops = {
	.start = m2ts_feed_start,
	.stop = m2ts_feed_stop,
	.flush = m2ts_feed_flush,
};

static int m2ts_feed_callback(
	const u8 *buf1, size_t len1,
	const u8 *buf2, size_t len2,
	struct dmx2_feed *src,
	u32 *buf_flags)
{
	int ret;
	u64 start, wptr, rptr, end;
	u64 use_size;
	enum mtk_bc_memtype memtype;
	struct mem_info mem_info = { 0 };
	struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, src->buf_ctx);

	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}
	start = mem_info.paddr;
	end = mem_info.paddr + mem_info.size;
	wptr = *((u64 *)buf1);
	rptr = *((u64 *)buf2);

	if (wptr < start || wptr >= end)
		return -EINVAL;

	if (wptr >= rptr)
		use_size = wptr - rptr;
	else
		use_size = (end - rptr) + (wptr - start);
	MTK_DMX_DEBUG("Used %d / %d\n", (int)use_size, mem_info.size);

	mtk_bc_get_data_buf_type(bc_ctx, &memtype);

	if (memtype == MTK_BC_MEMTYPE_RINGBUF)
		ret = mtk_bc_ringbuf_set_wptr(bc_ctx, (wptr - start));
	else if (mtk_bc_vb2_is_ringbuf_mode(bc_ctx))
		ret = mtk_bc_vb2_set_wptr(bc_ctx, (wptr - start));
	else {
		u8 *buf = mem_info.vaddr + (rptr - start);
		if (wptr >= rptr)
			ret = mtk_bc_vb2_fill_buffer(bc_ctx, buf, (wptr - rptr));
		else {
			ret = mtk_bc_vb2_fill_buffer(bc_ctx, buf, (end - rptr));
			ret += mtk_bc_vb2_fill_buffer(bc_ctx, mem_info.vaddr, (wptr - start));
		}
	}

	return ret;
}

static void timeline_descr_dump(struct timeline_descr *timeline_descr)
{
	if (timeline_descr == NULL)
		return;

	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"has_timestamp %d(0x%x)\n",
		timeline_descr->has_timestamp,
		timeline_descr->has_timestamp);
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"force_reload %s\n",
		BOOL_2_STR(timeline_descr->force_reload));
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"paused %s\n",
		BOOL_2_STR(timeline_descr->paused));
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"discontinuity %s\n",
		BOOL_2_STR(timeline_descr->discontinuity));
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"timeline_id %d(0x%x)\n",
		timeline_descr->timeline_id,
		timeline_descr->timeline_id);
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"media_timestamp 0x%llx\n",
		(unsigned long long) timeline_descr->media_timestamp);
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"timescale %d(0x%x)\n",
		timeline_descr->timescale,
		timeline_descr->timescale);
}

static int timeline_descr_parser(u8 *buf, struct timeline_descr *timeline_descr)
{
	int ret = 0;

	if (buf == NULL)
		return -ENODATA;
	if (timeline_descr == NULL)
		return -ENODATA;

	memset(timeline_descr, 0, sizeof(*timeline_descr));

	timeline_descr->has_timestamp =
		GET_BITMASK_OFFSET(
			buf[TIMELINE_DESCR_BYTE2_OFFSET],
			TIMELINE_DESCR_BYTE2_HAS_TIMESTAMP);
	timeline_descr->force_reload =
		GET_BITMASK_OFFSET(
			buf[TIMELINE_DESCR_BYTE2_OFFSET],
			TIMELINE_DESCR_BYTE2_FORCE_RELOAD);
	timeline_descr->paused =
		GET_BITMASK_OFFSET(
			buf[TIMELINE_DESCR_BYTE2_OFFSET],
			TIMELINE_DESCR_BYTE2_PAUSED);
	timeline_descr->discontinuity =
		GET_BITMASK_OFFSET(
			buf[TIMELINE_DESCR_BYTE3_OFFSET],
			TIMELINE_DESCR_BYTE3_DISCONTINUITY);
	timeline_descr->timeline_id =
		buf[TIMELINE_DESCR_TIMELINE_ID_OFFSET];

	if (timeline_descr->has_timestamp == TIMELINE_HAS_TIMESTAMP_NONE) {
		timeline_descr->media_timestamp = DEFAULT_MEDIA_TIMESTAMP;
	} else {
		timeline_descr->timescale =
			GET_TIMESCALE(&buf[TIMELINE_DESCR_TIMESCALE_OFFSET]);

		if (timeline_descr->has_timestamp == TIMELINE_HAS_TIMESTAMP_U32)
			timeline_descr->media_timestamp =
				GET_TIMESTAMP(
					&buf[TIMELINE_DESCR_MEDIA_TIMESTAMP_OFFSET]);
		else if (timeline_descr->has_timestamp == TIMELINE_HAS_TIMESTAMP_U64)
			timeline_descr->media_timestamp =
				GET_TIMESTAMP_U64(
					&buf[TIMELINE_DESCR_MEDIA_TIMESTAMP_OFFSET]);
	}

	timeline_descr_dump(timeline_descr);

	return ret;
}

static int af_descr_parser(u8 *buf, u8 len, struct dmx2_temi_metadata *meta, int size, u32 *num)
{
	const u8 af_descr_comm_hdr_len = 2; // af_descr_tag, af_descr_len
	int ret = -ENODATA;
	u8 offset = 0;
	u8 tag, descriptor_len;
	u32 timeline_descr_num;

	timeline_descr_num = 0;
	while (offset < len) {
		tag = buf[offset];
		descriptor_len = buf[offset + 1] + af_descr_comm_hdr_len;

		if ((offset + descriptor_len) > len) {
			MTK_DMX_ERR("#### out-of-range ####\n");
			MTK_DMX_ERR("(offset, descriptor_len, len) = (%u, %u, %u)\n",
				offset, descriptor_len, len);
			ret = -EFAULT;
			// Don't continue
			break;
		}

		switch (tag) {
		case AF_DESCR_TAG_TIMELINE:
			if (timeline_descr_num < size) {
				meta[timeline_descr_num].descr_tag = tag;
				memcpy(meta[timeline_descr_num].descr_data, &buf[offset], descriptor_len);
				timeline_descr_num++;
				ret = 0;
			} else {
				MTK_DMX_ERR(
					"timeline_descr_num %d >= size %d\n",
					timeline_descr_num,
					size);
				// Continue even though error is happened.
				ret = -ENOSPC;
			}
			break;
		default:
			break;
		}

		offset += descriptor_len;
	}
	*num = timeline_descr_num;

	return ret;
}

static int m2ts_af_parser(u8 *buf, struct dmx2_temi_metadata *meta, int size, int *num)
{
	const u8 af_comm_hdr_len = 2; // af_len, af_indicators, af_flags
	int ret = -ENODATA;
	u8 af_len;

	if (buf == NULL)
		return ret;
	if (meta == NULL)
		return ret;
	if (num == NULL)
		return ret;

	af_len = buf[0];
	*num = 0;
	if (af_len > 1) {
		bool pcr_flag = buf[1] & AF_FLAGS_PCR;
		bool opcr_flag = buf[1] & AF_FLAGS_OPCR;
		bool splicing_point_flag = buf[1] & AF_FLAGS_SPLICING_POINT;
		bool transport_private_data_flag = buf[1] & AF_FLAGS_TRANSPORT_PRIVATE_DATA;
		bool af_ext_flag = buf[1] & AF_FLAGS_EXTENSION;
		u8 offset = 0;

		MTK_DMX_DEBUG("pcr_flag = %d\n", pcr_flag);
		MTK_DMX_DEBUG("af_ext_flag = %d\n", af_ext_flag);

		offset += af_comm_hdr_len;

		if (pcr_flag)
			offset += AF_PCR_LEN;

		if (opcr_flag)
			offset += AF_OPCR_LEN;

		if (splicing_point_flag)
			offset += AF_SPLICING_POINT_LEN;

		if (transport_private_data_flag) {
			u8 transport_private_data_length = buf[offset];

			offset += (1 + transport_private_data_length);
		}

		if (af_ext_flag) {
			u8 af_ext_length = buf[offset++];
			u8 flags = buf[offset++];

			if (af_ext_length--) {
				bool ltw_flag = flags & AF_EXT_FLAGS_LTW;
				bool piecewise_rate_flag = flags & AF_EXT_FLAGS_PIECEWISE_RATE;
				bool seamless_splice_flag = flags & AF_EXT_FLAGS_SEAMLESS_SPLICE;
				bool af_descriptor_not_present_flag = flags & AF_EXT_FLAGS_DESCR_NOT_PRESENT;

				MTK_DMX_DEBUG("af_descriptor_not_present_flag = %d\n", af_descriptor_not_present_flag);

				if (ltw_flag) {
					offset += AF_EXT_LTW_LEN;
					af_ext_length -= AF_EXT_LTW_LEN;
				}

				if (piecewise_rate_flag) {
					offset += AF_EXT_PIECEWISE_RATE_LEN;
					af_ext_length -= AF_EXT_PIECEWISE_RATE_LEN;
				}

				if (seamless_splice_flag) {
					offset += AF_EXT_SEAMLESS_SPLICE_LEN;
					af_ext_length -= AF_EXT_SEAMLESS_SPLICE_LEN;
				}

				if (!af_descriptor_not_present_flag)
					ret = af_descr_parser(
						&buf[offset],
						af_ext_length,
						meta,
						size,
						num);
			}
		}
	}

	return ret;
}

static u8 m2ts_search_pes_head(u8 *buf)
{
	u8 offset = TS_HEADER_LEN;

	if (IS_AF_PACKET(buf)) {
		u8 af_len = buf[offset];

		offset += (1 + af_len);
	}

	return offset;
}

static int m2ts_compute_pts(u8 *buf, u64 *pts)
{
	u8 pts_dts_flags = buf[PES_FLAGS_OFFSET] & PES_FLAGS_PTS;

	if (pts_dts_flags) {
		int i = 0;
		u8 *p = &buf[PES_PTS_OFFSET];

		*pts = (((u64)p[i++] >> 1) & PTS_BYTE0_BITMASK) << PTS_BYTE0_OFFSET;
		*pts |= ((u64)p[i++] & PTS_BYTE1_BITMASK) << PTS_BYTE1_OFFSET;
		*pts |= (((u64)p[i++] >> 1) & PTS_BYTE2_BITMASK) << PTS_BYTE2_OFFSET;
		*pts |= ((u64)p[i++] & PTS_BYTE3_BITMASK) << PTS_BYTE3_OFFSET;
		*pts |= (((u64)p[i++] >> 1) & PTS_BYTE4_BITMASK) << PTS_BYTE4_OFFSET;

		return 0;
	}

	return -ENODATA;
}

static void m2ts_temi_timeline_update_current_media_timestamp(
	struct timeline *temi_timeline_current,
	struct timeline *temi_timeline_prev)
{
#define PTS_MAX (2)

	u64 pts[PTS_MAX] = { 0 };
	u64 pts1;
	u64 pts2;
	u32 timescale = 0;
	u64 media_timestamp_prev;
	u64 media_timestamp_current;
	u8 *descr_data = NULL;
	int i;

	if (temi_timeline_current == NULL)
		return;
	if (temi_timeline_prev == NULL)
		return;

	pts1 = temi_timeline_prev->metadata.pts;
	pts2 = temi_timeline_current->metadata.pts;
	// It is expected that
	// timescale of current one should be the same with previous one.
	timescale = temi_timeline_current->timeline_descr.timescale;
	media_timestamp_prev = temi_timeline_prev->timeline_descr.media_timestamp;
	descr_data = temi_timeline_current->metadata.descr_data;

	if (pts1 >= pts2) {
		pts[0] = pts1;
		pts[1] = pts2;
	} else {
		pts[0] = pts2;
		pts[1] = pts1;
	}
	media_timestamp_current =
		(pts[0] - pts[1]) * timescale / TS_TIMESTAMP_HZ + media_timestamp_prev;
	temi_timeline_current->timeline_descr.media_timestamp = media_timestamp_current;

	memcpy(descr_data,
		temi_timeline_prev->metadata.descr_data,
		sizeof(temi_timeline_prev->metadata.descr_data));
	switch (temi_timeline_current->timeline_descr.has_timestamp) {
	case TIMELINE_HAS_TIMESTAMP_U32:
		for (i = 0; i < sizeof(u32); i++)
			descr_data[TIMELINE_DESCR_MEDIA_TIMESTAMP_OFFSET + i] =
				GET_BYTE(media_timestamp_current, sizeof(u32) - 1 - i);
		break;

	case TIMELINE_HAS_TIMESTAMP_U64:
		for (i = 0; i < sizeof(u64); i++)
			descr_data[TIMELINE_DESCR_MEDIA_TIMESTAMP_OFFSET + i] =
				GET_BYTE(media_timestamp_current, sizeof(u64) - 1 - i);
		break;

	default:
		break;
	}

#undef PTS_MAX
}

static void dmx2_temi_metadata_dump(struct dmx2_temi_metadata *metadata)
{
#define BYTE_0 (0)
#define BYTE_1 (1)
#define BYTE_2 (2)
#define BYTE_3 (3)
#define BYTE_4 (4)
#define BYTE_5 (5)
#define BYTE_6 (6)
#define BYTE_7 (7)
#define BYTES_PER_LINE (BYTE_7 + 1)

	int i;

	if (metadata == NULL)
		return;

	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"pts 0x%llx\n",
		(unsigned long long) metadata->pts);
	MTK_DMX_FTRACE(MTK_DMX_FTAG2,
		"descr_tag %d\n",
		metadata->descr_tag);
	MTK_DMX_FTRACE(MTK_DMX_FTAG2, "descr_data\n");
	for (i = 0; i < sizeof(metadata->descr_data); i += BYTES_PER_LINE)
		MTK_DMX_FTRACE(MTK_DMX_FTAG2,
			"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x0%2x 0x0%2x\n",
			metadata->descr_data[i + BYTE_0],
			metadata->descr_data[i + BYTE_1],
			metadata->descr_data[i + BYTE_2],
			metadata->descr_data[i + BYTE_3],
			metadata->descr_data[i + BYTE_4],
			metadata->descr_data[i + BYTE_5],
			metadata->descr_data[i + BYTE_6],
			metadata->descr_data[i + BYTE_7]);

#undef BYTE_0
#undef BYTE_1
#undef BYTE_2
#undef BYTE_3
#undef BYTE_4
#undef BYTE_5
#undef BYTE_6
#undef BYTE_7
#undef BYTES_PER_LINE
}

static int m2ts_get_temi_timeline(
	struct timeline *temi_timeline_head,
	struct timeline **temi_timeline,
	u8 timeline_id,
	u32 timeline_id_max)
{
	if (temi_timeline_head == NULL)
		return -EINVAL;
	if (temi_timeline == NULL)
		return -EINVAL;

	*temi_timeline = NULL;

	if (((u32) timeline_id) >= timeline_id_max) {
		MTK_DMX_INFO(
			"timeline_id %d >= timeline_id_size %d\n",
			timeline_id,
			timeline_id_max);
		return -EINVAL;
	}

	*temi_timeline = &temi_timeline_head[timeline_id];

	return 0;
}

static int m2ts_multi_timeline_descr_parser(
	struct dmx2_feed *feed,
	struct dmx2_temi_metadata *meta,
	u32 num)
{
	int ret = 0;
	struct mtk_m2ts_feed *mtk_m2ts_feed = cast_to_m2ts_feed(feed);
	struct mtk_m2ts_filter *mtk_m2ts_filter = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct dmx2_temi_metadata *meta_current = NULL;
	struct timeline_descr timeline_descr = { 0 };
	u8 timeline_id = 0;
	struct timeline *temi_timeline = NULL;
	int i;

	if (mtk_m2ts_feed == NULL)
		return -EINVAL;
	mtk_m2ts_filter = cast_to_m2ts_filter(feed->filter);
	if (mtk_m2ts_filter == NULL)
		return -EINVAL;
	if (meta == NULL)
		return -EINVAL;
	mtk_demux2 = mtk_demux2_get_mtk_demux2(feed->filter);
	if (mtk_demux2 == NULL)
		return -EINVAL;

	mtk_demux2->temi.timeline_descr_num[mtk_m2ts_filter->pid_type] = num;
	if (num > mtk_demux2->temi.timeline_id_size) {
		MTK_DMX_INFO(
			"num %d > timeline_id_size %d\n",
			num,
			mtk_demux2->temi.timeline_id_size);
		MTK_DMX_INFO("timeline_descr_num is set as timeline_id_size\n");

		mtk_demux2->temi.timeline_descr_num[mtk_m2ts_filter->pid_type] = mtk_demux2->temi.timeline_id_size;
	}

	for (i = 0; i < mtk_demux2->temi.timeline_descr_num[mtk_m2ts_filter->pid_type]; i++) {
		meta_current = &meta[i];
		MTK_DMX_FTRACE(MTK_DMX_FTAG2,
			"meta[%d].descr_tag = %x\n",
			i,
			meta_current->descr_tag);
		if (meta_current->descr_tag == AF_DESCR_TAG_TIMELINE) {
			if (timeline_descr_parser(
				meta_current->descr_data,
				&timeline_descr) < 0) {
				MTK_DMX_ERR("timeline_descr_parser is failed\n");

				ret = -EFAULT;
				// Continue even though error is happened.
				continue;
			}

			timeline_id = timeline_descr.timeline_id;
			if (m2ts_get_temi_timeline(
				TEMI_TIMELINE_CURRENT(mtk_demux2, mtk_m2ts_filter->pid_type),
				&temi_timeline,
				timeline_id,
				mtk_demux2->temi.timeline_size) < 0) {
				MTK_DMX_INFO(
					"m2ts_get_temi_timeline for timeline_id %d is failed\n",
					timeline_id);

				ret = -EFAULT;
				// Continue even though error is happened.
				continue;
			}

			mtk_demux2->temi.timeline_id[mtk_m2ts_filter->pid_type][i] = timeline_id;
			temi_timeline->metadata.descr_tag = meta_current->descr_tag;
			memcpy(temi_timeline->metadata.descr_data,
				meta_current->descr_data,
				sizeof(meta_current->descr_data));
			memcpy(&temi_timeline->timeline_descr,
				&timeline_descr,
				sizeof(timeline_descr));
		}
	}

	return ret;
}

static void m2ts_temi_timeline_update_pts(struct dmx2_feed *feed, u64 pts)
{
	struct mtk_m2ts_filter *mtk_m2ts_filter = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	int i = 0;
	u8 timeline_id = 0;
	struct timeline *temi_timeline = NULL;

	if (feed == NULL)
		return;
	mtk_m2ts_filter = cast_to_m2ts_filter(feed->filter);
	if (mtk_m2ts_filter == NULL)
		return;
	mtk_demux2 = mtk_demux2_get_mtk_demux2(feed->filter);
	if (mtk_demux2 == NULL)
		return;

	for (i = 0; i < mtk_demux2->temi.timeline_descr_num[mtk_m2ts_filter->pid_type]; i++) {
		timeline_id = mtk_demux2->temi.timeline_id[mtk_m2ts_filter->pid_type][i];
		if (m2ts_get_temi_timeline(
			TEMI_TIMELINE_CURRENT(mtk_demux2, mtk_m2ts_filter->pid_type),
			&temi_timeline,
			timeline_id,
			mtk_demux2->temi.timeline_size) < 0) {
			MTK_DMX_INFO(
				"m2ts_get_temi_timeline for timeline_id %d is failed\n",
				timeline_id);

			// Continue even though error is happened.
			continue;
		}

		temi_timeline->metadata.pts = pts;
	}
}

static int m2ts_notify_timeline_descr(
	struct dmx2_feed *feed,
	struct timeline *temi_timeline_current,
	struct timeline *temi_timeline_prev)
{
	int ret = 0;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct dmx2_temi_metadata meta = { 0 };

	if (feed == NULL)
		return -EINVAL;
	if (temi_timeline_current == NULL)
		return -EINVAL;
	if (temi_timeline_prev == NULL)
		return -EINVAL;
	bc_ctx = dynamic_cast(struct mtk_bc_ctx *, feed->buf_ctx);

	if (temi_timeline_current->timeline_descr.media_timestamp != DEFAULT_MEDIA_TIMESTAMP) {
		memcpy(temi_timeline_prev,
			temi_timeline_current,
			sizeof(*temi_timeline_current));

		memcpy(&meta,
			&temi_timeline_current->metadata,
			sizeof(meta));
	} else if (temi_timeline_prev->timeline_descr.media_timestamp != DEFAULT_MEDIA_TIMESTAMP) {
		m2ts_temi_timeline_update_current_media_timestamp(
			temi_timeline_current,
			temi_timeline_prev);

		memcpy(&meta,
			&temi_timeline_current->metadata,
			sizeof(meta));
	} else {
		// Don't send metadata if only PTS is available.
	}

	if (meta.descr_tag == AF_DESCR_TAG_TIMELINE) {
		MTK_DMX_FTRACE(MTK_DMX_FTAG2,
			"#### queue metedata ####\n");
		dmx2_temi_metadata_dump(&meta);

		ret = mtk_bc_metabuf_queue(bc_ctx, &meta);
		if (ret < 0)
			MTK_DMX_ERR("queue metadata fail\n");

		temi_timeline_current->timeline_descr.media_timestamp = DEFAULT_MEDIA_TIMESTAMP;
	}

	return ret;
}

static int m2ts_notify_multi_timeline_descr(struct dmx2_feed *feed)
{
	int ret = 0;
	int tmp_ret = 0;
	struct mtk_m2ts_filter *mtk_m2ts_filter = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	u8 timeline_id = 0;
	struct timeline *temi_timeline_current = NULL;
	struct timeline *temi_timeline_prev = NULL;
	int i = 0;

	if (feed == NULL)
		return -EINVAL;
	mtk_m2ts_filter = cast_to_m2ts_filter(feed->filter);
	if (mtk_m2ts_filter == NULL)
		return -EINVAL;
	mtk_demux2 = mtk_demux2_get_mtk_demux2(feed->filter);
	if (mtk_demux2 == NULL)
		return -EINVAL;

	for (i = 0; i < mtk_demux2->temi.timeline_descr_num[mtk_m2ts_filter->pid_type]; i++) {
		timeline_id = mtk_demux2->temi.timeline_id[mtk_m2ts_filter->pid_type][i];

		if (m2ts_get_temi_timeline(
			TEMI_TIMELINE_CURRENT(mtk_demux2, mtk_m2ts_filter->pid_type),
			&temi_timeline_current,
			timeline_id,
			mtk_demux2->temi.timeline_size) < 0) {
			MTK_DMX_INFO(
				"m2ts_get_temi_timeline for timeline_id %d is failed\n",
				timeline_id);

			if (ret == 0)
				ret = -EFAULT;
			// Continue even though error is happened.
			continue;
		}
		if (m2ts_get_temi_timeline(
			TEMI_TIMELINE_PREV(mtk_demux2, mtk_m2ts_filter->pid_type),
			&temi_timeline_prev,
			timeline_id,
			mtk_demux2->temi.timeline_size) < 0) {
			MTK_DMX_INFO(
				"m2ts_get_temi_timeline for timeline_id %d is failed\n",
				timeline_id);

			if (ret == 0)
				ret = -EFAULT;
			// Continue even though error is happened.
			continue;
		}

		tmp_ret = m2ts_notify_timeline_descr(
				feed,
				temi_timeline_current,
				temi_timeline_prev);
		if (tmp_ret < 0) {
			MTK_DMX_ERR(
				"m2ts_notify_timeline_descr for timeline_id %d is failed\n",
				timeline_id);

			// Continue even though error is happened.
			if (ret == 0)
				ret = tmp_ret;
		}
	}

	return ret;
}

static int m2ts_temi_parser(struct dmx2_feed *feed, u8 *buf, u32 size)
{
	int i;
	bool found;
	struct dmx2_temi_metadata *meta = NULL;
	size_t meta_memory_size;
	struct mtk_demux2 *mtk_demux2 = NULL;
	u8 *ts_packet = buf;
	int num;
	u64 pts;

	if (feed == NULL)
		return -EINVAL;
	if (buf == NULL)
		return -EINVAL;
	mtk_demux2 = mtk_demux2_get_mtk_demux2(feed->filter);
	if (mtk_demux2 == NULL)
		return -EINVAL;

	if (size < M2TS_PACKET_TYPE_NORMAL) {
		MTK_DMX_ERR("size not enough: 0x%x\n", size);
		return -EINVAL;
	}

	if (size % M2TS_PACKET_TYPE_NORMAL) {
		MTK_DMX_ERR("size not align to %u, size: %u\n", M2TS_PACKET_TYPE_NORMAL, size);
		return -EINVAL;
	}

	meta_memory_size =
		sizeof(struct dmx2_temi_metadata) *
		mtk_demux2->temi.timeline_size;
	meta = mtk_demux2->temi.meta;
	if (meta == NULL) {
		MTK_DMX_ERR("memory for meta is NULL\n");

		return -ENOMEM;
	}

	for (i = 0; i < size; i += M2TS_PACKET_TYPE_NORMAL) {
		found = false;

		if (IS_AF_PACKET(ts_packet)) {
			memset(meta, 0, meta_memory_size);
			num = 0;
			if (m2ts_af_parser(&ts_packet[TS_HEADER_LEN], meta, mtk_demux2->temi.timeline_size, &num)) {
				MTK_DMX_ERR("m2ts_af_parser is failed\n");
			}
			// Parse #num of timeline_descriptor even though error is happened.
			if (num > 0 && m2ts_multi_timeline_descr_parser(feed, meta, num)) {
				// Continue even though error is happened.
				MTK_DMX_ERR("m2ts_multi_timeline_descr_parser is failed\n");
			}
		}

		if (IS_PUSI_PACKET(ts_packet) && IS_PAYLOAD_PACKET(ts_packet)) {
			u8 offset = m2ts_search_pes_head(ts_packet);

			if (!m2ts_compute_pts(&ts_packet[offset], &pts)) {
				found = true;
				m2ts_temi_timeline_update_pts(feed, pts);
			}
		}

		if (found) {
			int ret;
			ret = m2ts_notify_multi_timeline_descr(feed);
			if (ret < 0) {
				MTK_DMX_ERR("m2ts_notify_multi_timeline_descr is failed\n");
				// Continue even though error is happened.
			}
		}

		ts_packet += M2TS_PACKET_TYPE_NORMAL;
	}

	return 0;
}

static int m2ts_temi_callback(
	const u8 *buf1, size_t len1,
	const u8 *buf2, size_t len2,
	struct dmx2_feed *src,
	u32 *buf_flags)
{
	int ret = 0;
	u64 start, wptr, rptr, end;
	u8 *buf;
	struct mem_info mem_info = { 0 };
	struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, src->buf_ctx);
	u16 filter_id = src->index;
	u32 sz1, sz2;
	u8 *tmp = NULL;
	struct dvb_fh *fh = src->filter->fh;
	struct mtk_demux2 *demux = fh_to_demux(fh);
	bool sec_event_dbg_en = demux->rm->sec_event_dbg_en;

	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}
	start = mem_info.paddr;
	end = mem_info.paddr + mem_info.size;
	wptr = *((u64 *)buf1);
	rptr = *((u64 *)buf2);

	if (wptr < start || wptr >= end)
		return -EINVAL;

	buf = mem_info.vaddr + (rptr - start);

	if (wptr >= rptr) {
		if (unlikely(sec_event_dbg_en))
			MTK_DMX_FTRACE(MTK_DMX_FTAG2, "pkt: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
				buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
		ret = m2ts_temi_parser(src, buf, (u32)(wptr - rptr));
	} else {
		sz1 = (u32)(end - rptr);
		sz2 = (u32)(wptr - start);

		tmp = kzalloc(sizeof(u8) * (sz1 + sz2), GFP_KERNEL);
		if (tmp) {
			memcpy(tmp, buf, sz1);
			memcpy(tmp + sz1, mem_info.vaddr, sz2);
			if (unlikely(sec_event_dbg_en))
				MTK_DMX_FTRACE(MTK_DMX_FTAG2, "pkt: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
					tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
			ret = m2ts_temi_parser(src, tmp, (sz1 + sz2));
			kfree(tmp);
		} else
			ret = -ENOMEM;
	}

	if (!ret)
		mtk_dmx_sec_filter_read_addr(filter_id, &wptr, true);

	return ret;
}

void m2ts_handle_section_transfer(struct dmx2_feed *feed)
{
	u16 filter_id;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;
	struct mtk_demux2 *mtk_dmx_filter = NULL;
	enum sw_interrupt_event event = SW_INT_EVENT_NULL;
	u64 read_ptr, write_ptr;
	bool sec_event_dbg_en = false;
	struct mem_info mem_info = { 0 };

	if ((!feed->parent) || (feed->state != DMX2_STATE_GO)) {
		MTK_DMX_ERR("filter not active\n");
		return;
	}

	fh = feed->filter->fh;
	mtk_dmx_filter = fh_to_demux(fh);
	sec_event_dbg_en = mtk_dmx_filter->rm->sec_event_dbg_en;
	ctx = fh_to_dmxctx(fh);
	filter_id = feed->index;

	if (mtk_bc_get_mem_info(
		fh_dmxctx_get_bc_ctx(ctx),
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return;
	}

	fh_dmxctx_get_filter_event(ctx, &event);

	if (event == SW_INT_EVENT_SEC_BUF_OVERFLOW) {
		MTK_DMX_ERR("SW_INT_EVENT_SEC_BUF_OVERFLOW filter_id:%d\n", filter_id);
		if (IS_RINGBUF_TYPE(feed->buf_ctx->type))
			feed->buf_ctx->ring.error = -EOVERFLOW;
	} else if (event == SW_INT_EVENT_SEC_READY
				|| event == SW_INT_EVENT_SEC_CRC_ERR) {
		mtk_dmx_sec_filter_read_addr(filter_id, &read_ptr, false);
		mtk_dmx_sec_filter_write_addr(filter_id, &write_ptr, false);

		/* start = mem_info.vaddr;
		end = start + mem_info.size;
		read = start + (read_ptr - mem_info.paddr);
		write = start + (write_ptr - mem_info.paddr); */

		// buffer control will be intergrated in buf_ctrl
		feed->callback((const u8 *)&write_ptr, 0, (const u8 *)&read_ptr, 0, feed, 0);
		if (event == SW_INT_EVENT_SEC_CRC_ERR)
			MTK_DMX_ERR("SW_INT_EVENT_SEC_CRC_ERR filter_id:%d\n", filter_id);
	}

	if (sec_event_dbg_en)
		MTK_DMX_FTRACE(MTK_DMX_FTAG1, "filter id: %d event: %s\n", filter_id,
			(event == SW_INT_EVENT_SEC_READY) ? "ready" :
			(event == SW_INT_EVENT_SEC_CRC_ERR) ? "crc err" :
			(event == SW_INT_EVENT_SEC_BUF_OVERFLOW) ? "buf overflow" :
			"unknown");
}

static int m2ts_filter_alloc(struct dmx2_filter **filter)
{
	struct mtk_m2ts_filter *m2ts_flt;
	struct mtk_m2ts_feed *m2ts_feed;


	m2ts_flt = kzalloc(sizeof(*m2ts_flt), GFP_KERNEL);
	if (!m2ts_flt) {
		MTK_DMX_ERR("m2ts filter alloc fail\n");
		return -ENOMEM;
	}

	m2ts_feed = kzalloc(sizeof(*m2ts_feed), GFP_KERNEL);
	if (!m2ts_feed) {
		MTK_DMX_ERR("m2ts feed alloc fail\n");
		kfree(m2ts_flt);
		return -ENOMEM;
	}

	cast_to_dmx2_feed(m2ts_feed)->callback = &m2ts_feed_callback;
	cast_to_dmx2_filter(m2ts_flt)->feed = cast_to_dmx2_feed(m2ts_feed);

	*filter = cast_to_dmx2_filter(m2ts_flt);
	return 0;
}

static int m2ts_filter_release(struct dmx2_filter *filter)
{
	struct mtk_m2ts_filter *m2ts_flt = cast_to_m2ts_filter(filter);
	struct mtk_m2ts_feed *m2ts_feed = cast_to_m2ts_feed(filter->feed);

	kfree(m2ts_feed);
	kfree(m2ts_flt);
	return 0;
}

static int m2ts_filter_packet_valid(struct dmx2_filter *filter)
{
	struct dvb_fh *fh = filter->fh;
	struct mtk_demux2 *demux = fh_to_demux(fh);

	if (filter->state != DMX2_STATE_GO)
		return 0;

	return mtk_dmx_pid_filter_packet_valid(demux->rm, filter->feed->index) ? 1 : 0;
}

static struct mtk_demux2_flt_ops m2ts_flt_ops = {
	.allocate_filter = m2ts_filter_alloc,
	.release_filter = m2ts_filter_release,
	.packet_valid = m2ts_filter_packet_valid,
};

static int m2ts_stc_set(struct dvb_fh *fh, struct dmx2_stc *info)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct pcr_filter_cfg filter_info = {0};
	struct stc_info stc_info;
	u32 stc_l, stc_h;

	if (ctx->cfg.type.sub != FILTER_SUB_TYPE_PCR) {
		MTK_DMX_ERR("it's not pcr filter to set stc\n");
		return -ENODEV;
	}

	fh_dmxctx_get_filter_info(ctx, (void *)&filter_info, sizeof(struct pcr_filter_cfg));

	// @TODO: refine the using of stc_info
	stc_info.is_filter_use_stc = false;
	stc_info.eng_id = filter_info.cfg.dst_eng_id;
	fh_dmxctx_set_stc_info(ctx, (void *)&stc_info);

	if (info->base != 1)
		do_div(info->stc, info->base);

	stc_l = (u32) info->stc;
	stc_h = (u32) (info->stc >> 32);

	mtk_dmx_stc(filter_info.cfg.dst_eng_id, &stc_l, &stc_h, true);

	return 0;
}

static int m2ts_stc_get(struct dvb_fh *fh, struct dmx2_stc *info)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct stc_info stc_info;
	struct pcr_filter_cfg filter_info = {0};
	u32 stc_l = 0, stc_h = 0;

	if (ctx->cfg.type.sub != FILTER_SUB_TYPE_PCR) {
		MTK_DMX_ERR("it's not pcr filter to get stc\n");
		return -ENODEV;
	}

	fh_dmxctx_get_filter_info(ctx, (void *)&filter_info, sizeof(struct pcr_filter_cfg));

	// @TODO: refine the using of stc_info
	stc_info.is_filter_use_stc = false;
	stc_info.eng_id = filter_info.cfg.dst_eng_id;
	fh_dmxctx_set_stc_info(ctx, (void *)&stc_info);

	mtk_dmx_stc(filter_info.cfg.dst_eng_id, &stc_l, &stc_h, false);
	info->stc = (u64) stc_l | ((u64) stc_h << 32);
	info->base = 1; /* output: divisor for stc to get 90 kHz clock */

	return 0;
}

static int m2ts_dmx_set_buf_rptr(struct dvb_fh *fh, u64 rptr)
{
	struct mtk_demux2_fh_ctx *ctx = fh_to_dmxctx(fh);
	struct mem_info mem_info = { 0 };
	struct mtk_demux2 *demux = fh_to_demux(fh);
	struct dmx2_filter *filter = dvb_dmx2_filter_find(cast_to_dmx_parent(demux), fh);
	int filter_id = 0;

	if (!(ctx->cfg.type.main == DMX2_FILTER_TYPE_TS && ctx->cfg.type.sub == FILTER_SUB_TYPE_SEC))
		return 0;

	if (filter == NULL) {
		MTK_DMX_ERR("null access\n");
		return -ENODEV;
	}
	filter_id = filter->feed->index;

	if (mtk_bc_get_mem_info(
		fh_dmxctx_get_bc_ctx(ctx),
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}
	// rptr is 'offset', need to add 'start'
	if (rptr >= mem_info.size)
		return -EINVAL;

	rptr += mem_info.paddr;
	mtk_dmx_sec_filter_read_addr(filter_id, &rptr, true);
	return 0;
}

static int m2ts_notify_event(struct mtk_demux2_notify_args *args)
{
	struct mtk_dmx_path *path = args->path;
	struct mtk_dmx_rm *rm = path->demux2.rm;
	link_state_t state = args->state;

	if (state == LINK_STATE_CONNECT) {
		mutex_lock(&rm->fiq_mutex);
		if (rm->fiq_rush_path == -1) {
			if (mtk_dmx_rm_get_buf(rm, MMAP_BUF_TYPE_FQ,
					0, &rm->fiqmap) < 0) {
				MTK_DMX_ERR("get FQ buf fail\n");
				mutex_unlock(&rm->fiq_mutex);
				return 0;
			}
			mtk_dmx_io_init_fiq();
			mtk_dmx_io_set_fiq_buf(0, rm->fiqmap.paddr,
				rm->fiqmap.size);
			mtk_dmx_io_enable_fiq(0, path->path_id, true);
			rm->fiq_rush_path = path->path_id;
			MTK_DMX_INFO("Fiq enabled on path %d\n", rm->fiq_rush_path);
		}
		mutex_unlock(&rm->fiq_mutex);

		if (!path->input_monitor_task) {
			path->input_monitor_task = kthread_run(mtk_dmx_tm_path_input_monitor,
				(void *)path,
				"path%d_input_monitor_task",
				path->path_id);

			if (IS_ERR(path->input_monitor_task)) {
				MTK_DMX_ERR("create input monitor task for path %d failed\n"
					, path->path_id);
				path->input_monitor_task = NULL;
				return -ENODEV;
			}

			MTK_DMX_INFO("start input monitor task for path %d\n", path->path_id);
		}
	} else if (state == LINK_STATE_DISCONNECT) {
		if (path->input_monitor_task) {
			MTK_DMX_INFO("stop input monitor task for path %d\n", path->path_id);
			kthread_stop(path->input_monitor_task);
			path->input_monitor_task = NULL;
		}
		mtk_dmx_io_set_tsif_en(path->path_id, false);

		mutex_lock(&rm->fiq_mutex);
		if (rm->fiq_rush_path == path->path_id) {
			MTK_DMX_INFO("Fiq disabled on path %d\n", rm->fiq_rush_path);
			mtk_dmx_io_enable_fiq(0, rm->fiq_rush_path, false);
			mtk_dmx_io_deinit_fiq();
			mtk_dmx_rm_put_buf(rm, &rm->fiqmap);
			rm->fiq_rush_path = -1;
		}
		mutex_unlock(&rm->fiq_mutex);
	}

	return 0;
}

static int handle_ci2_streaming_sink_event(
	bool streaming, u16 path_id, struct mtk_dvr2 *dvr, struct mtk_dmx_rm *rm)
{
	struct dvb_dvr2 *base = cast_to_dvr_parent(dvr);
	struct mtk_m2ts_dvr_private *priv = base->priv;
	int ret = 0;
	struct demux_io_cfg cfg = { 0 };
	struct demux_filein_engine filein_eng = {0};

	if (streaming) {
		priv->filein_ctx = kzalloc(sizeof(struct mtk_dvr2_fh_ctx), GFP_KERNEL);
		if (priv->filein_ctx) {
			ret = mtk_dvr2_fh_ctx_init(priv->filein_ctx, dvr,
				DVR_DATA_TO_DEVICE, DVB_DVR2_BUFFER_SIZE);
			if (ret < 0) {
				kfree(priv->filein_ctx);
				priv->filein_ctx = NULL;
				return -ENXIO;
			}
		}
		cfg.type = DEMUX_IO_TYPE_MEM;
		filein_eng.engine_id = mtk_dmx_rm_filein_find_engine_by_path(rm, path_id);
		if (filein_eng.engine_id >= 0) {
			mtk_dmx_io_set_filein_cfg(filein_eng.engine_id,
				DEMUX_IN_FORMAT_M2TS, false);
			mtk_dmx_io_set_input_src(path_id, &cfg, &filein_eng);
		} else
			return -ENODEV;
	} else {
		if (priv->filein_ctx) {
			mtk_dvr2_fh_ctx_exit(priv->filein_ctx);
			kfree(priv->filein_ctx);
			priv->filein_ctx = NULL;
		}
	}

	return 0;
}

static int m2ts_query_event(struct mtk_demux2_query_args *args)
{
	struct dvbdev_properties *prop = args->prop;
	u32 i, num = prop->num;
	struct mtk_dvr2 *dvr;
	u64 wptr = 0, start = 0, end = 0, prev = 0;
	void *buf0 = NULL, *buf1 = NULL;
	u32 len0 = 0, len1 = 0;
	int ret = 0;
	bool streaming = false;
	struct dvb_dvr2 *base;
	struct mtk_m2ts_dvr_private *priv;
	bool status = false;
	u16 path_id = args->path->path_id;
	struct mtk_dmx_rm *rm = args->path->demux2.rm;
	bool config_streaming = false;

	for (i = 0; i < num; i++) {
		switch (prop->props[i].cmd) {
		case ADAP_CMD_FILEIN_STREAMING_ON:
			dvr = &args->path->dvr_playback;
			base = cast_to_dvr_parent(dvr);
			priv = base->priv;
			status = priv->status;
			if (status)
				prop->props[i].result = 1;
			else
				prop->props[i].result = 0;
			break;
		case ADAP_CMD_CI2_STREAMING_STATUS:
			if (config_streaming == true)
				break;
			streaming = (prop->props[0].data[0] == ADAP_CI2_STREAMING_ON) ? true : false;
			switch (prop->props[0].data[1]) {
			case ADAP_CI2_STREAMING_SOURCE:
				dvr = &args->path->dvr_record;
				base = cast_to_dvr_parent(dvr);
				if (streaming)
					dvr->rec_cfg.free_run = true;
				else
					dvr->rec_cfg.free_run = false;
				break;
			case ADAP_CI2_STREAMING_SINK:
				dvr = &args->path->dvr_playback;
				base = cast_to_dvr_parent(dvr);
				ret = handle_ci2_streaming_sink_event(streaming, path_id, dvr, rm);
				if (ret)
					return ret;
				break;
			}
			config_streaming = true;
			break;
		case ADAP_CMD_CI2_BUFFER_INFO:
			dvr = &args->path->dvr_record;
			base = cast_to_dvr_parent(dvr);
			if (i == 0) {
				prev = *(u64 *)(prop->props[0].buffer.buf);
				MTK_DMX_DEBUG("prev@ 0x%llx\n", prev);
				wptr = call_op(dvr, buf, get_wptr_v2, dvr, true, &start, &end);
				if (wptr <= 0)
					return -EFAULT;
				*(u64 *)(prop->props[0].buffer.buf) = wptr;

				MTK_DMX_DEBUG("wptr@ 0x%llx start@ 0x%llx end@ 0x%llx \n",
					wptr, start, end);
				if (prev > end || prev < start) {
					buf0 = (void *)(size_t)start;
					len0 = (wptr - start);
				} else {
					if (wptr < prev) {
						buf0 = (void *)(size_t)prev;
						len0 = (end - prev);
						buf1 = (void *)(size_t)start;
						len1 = (wptr - start);
					} else {
						buf0 = (void *)(size_t)prev;
						len0 = (wptr - prev);
					}
				}
				if (prev == 0)
					len0 = len1 = 0;
				MTK_DMX_DEBUG("query[%d]: 0x%llx 0x%x, 0x%llx 0x%x\n", args->path->path_id,
					(u64)(size_t)buf0, len0, (u64)(size_t)buf1, len1);

				prop->props[i].buffer.buf = buf0;
				prop->props[i].buffer.len = len0;
			} else if (i == 1) {
				prop->props[i].buffer.buf = buf1;
				prop->props[i].buffer.len = len1;
			}
			ret = (wptr == 0) ? -ENXIO : 0;
			break;
		default:
			return -1;
		}
	}
	return ret;
}

static int m2ts_streaming_event_find_tso(struct mtk_dmx_rm *rm,
	struct media_entity *remote)
{
	int i;
	u16 ts_path_num = mtk_dmx_rm_get_hwcap_by_id(rm, HWCAPS_TYPE_LIVEIN) +
		mtk_dmx_rm_get_hwcap_by_id(rm, HWCAPS_TYPE_TLV) +
		mtk_dmx_rm_get_hwcap_by_id(rm, HWCAPS_TYPE_ALP);
	u16 tso_num = mtk_dmx_rm_get_hwcap_by_id(rm, HWCAPS_TYPE_TSO);
	struct dvb_device *tso_dvbdev;
	struct media_entity *tso_entity;
	struct media_link *link;

	for (i = 0; i < tso_num; i++) {
		tso_dvbdev = mdrv_adapter_find_device(DVB_DEVICE_DEMUX2, (ts_path_num + i));
		tso_entity = tso_dvbdev->entity;
		link = media_entity_find_link(&tso_entity->pads[0], &remote->pads[1]);
		if (link && (link->flags == MEDIA_LNK_FL_ENABLED)) {
			return i;
		}
	}
	return -1;
}

static void _m2ts_check_locked_fiq_buf_reset(struct mtk_dmx_rm *rm)
{
	if (rm == NULL)
		return;

	if ((rm->fiq_rush_path != -1) && (rm->fiq_rush_path != rm->fiq_pre_rush_path)) {
		mtk_dmx_io_reset_fiq_buffer(0, rm->fiq_rush_path);
		rm->fiq_pre_rush_path = rm->fiq_rush_path;
	}
}

static void _m2ts_check_unlocked_fiq(struct mtk_dmx_rm *rm, int path_id)
{
	if (rm == NULL)
		return;

	if ((rm->fiq_rush_path != -1) && (rm->fiq_rush_path == path_id)) {
		mtk_dmx_io_enable_fiq(0, path_id, false);
	}
}

static int m2ts_streaming_event(struct mtk_demux2_streaming_args *args)
{
	int i, ret;
	struct mtk_dmx_path *path = args->path;
	struct dvb_frontend *fe = args->fe;
	struct cfm_properties *props = args->props;
	struct dvb_device *dvbdev = args->dvbdev;
	u32 data, bits = 0;
	struct cfm_properties p;
	struct mtk_msource_settings src;
	struct mtk_dmx_rm *rm = path->demux2.rm;
	u8 pin[32] = { 0 };
	bool ext_ts = false, filein = false;
	struct media_pad *pad;

	memset(&src, 0, sizeof(struct mtk_msource_settings));
	for (i = 0; i < props->num; i++)
		switch (props->props[i].cmd) {
		case CFM_STREAM_CLOCK_RATE:
			break;
		case CFM_STREAMING_STATUS:
			data = props->props[i].u.data;
			if (data) {
				if (!fe) {
					MTK_DMX_INFO("file-in start\n");
					bits = 8;
					filein = true;
				} else {
					MTK_DMX_INFO("demod locked: %d\n", path->path_id);
					/*
					 *	reset fiq read/write to head to aviod
					 *	video garbage on difference freq
					 */
					_m2ts_check_locked_fiq_buf_reset(rm);

					p.num = 2;
					p.props = kzalloc(sizeof(struct cfm_property) * p.num, GFP_KERNEL);
					if (p.props) {
						p.props[0].cmd = CFM_STREAM_DATA_BITS_NUM;
						p.props[1].cmd = CFM_STREAM_PIN_MUX_INFO;
						cfm_get_demod_property(fe, &p);
						bits = p.props[0].u.data;
						memset(&pin, 0, sizeof(pin));
						memcpy(&pin, p.props[1].u.buffer.data, (sizeof(pin) - 1));
						kfree(p.props);
						if (!strcmp(pin, "internal_pad")) {
							ext_ts = false;
							src.port_num = 0;
						} else if (!strncmp(pin, "ext_ts_", strlen("ext_ts_"))) {
						/* we only compare prefix of pmux string
						 * parallel or serial can be found by #bits = p.props[0].u.data;
						 */
							ext_ts = true;
							src.port_num = 0;
						} else if (!strncmp(pin, "ext1_ts_", strlen("ext1_ts_"))) {
							ext_ts = true;
							src.port_num = 1;
						} else if (!strncmp(pin, "ext2_ts_", strlen("ext2_ts_"))) {
							ext_ts = true;
							src.port_num = 2;
						} else if (!strncmp(pin, "ext3_ts_", strlen("ext3_ts_"))) {
							ext_ts = true;
							src.port_num = 3;
						} else {
							MTK_DMX_ERR("cannot find pin: %s\n", pin);
							return -EINVAL;
						}
						MTK_DMX_INFO("bits: %d pin: %s\n", bits, pin);
					} else
						return -ENOMEM;
				}
				src.cicam = path->cicam;
				src.cifpga = path->cifpga;
				if (src.cifpga) {
					ext_ts = true;
					src.port_type = DMX_MC_PORT_SERIAL;
				} else if (src.cicam) {
					src.port_type = DMX_MC_PORT_PARALLEL;
				} else {
					src.port_type = (bits == 2) ? DMX_MC_PORT_2BITS_SERIAL :
					(bits == 1) ? DMX_MC_PORT_SERIAL : DMX_MC_PORT_PARALLEL;
				}
				if (filein)
					src.input_type = DMX_MC_INPUT_DRAM;
				else
					src.input_type = (ext_ts) ? DMX_MC_INPUT_EXTERNAL_FE :
						DMX_MC_INPUT_INTERNAL_FE;

				// find which TSO
				pad = mtk_mc_remote_pad(&dvbdev->entity->pads[1]);
				if (src.cicam) {
					if (pad && is_ci_device(pad->entity))
						src.port_num = m2ts_streaming_event_find_tso(rm, pad->entity);
				} else if (src.cifpga) {
					src.port_num = 2;
				} else {
					if (pad && is_tso_device(pad->entity)) {
						memset(&pin, 0, sizeof(pin));
						memcpy(&pin, "tso_pad", strlen("tso_pad"));
						src.port_num = m2ts_streaming_event_find_tso(rm, dvbdev->entity);
					} else if (pad && is_ci2_device(pad->entity)) {
						MTK_DMX_INFO("usbcam linked\n");
						return 0;
					}
				}

				ret = mtk_mc_set_tsp_input_source(rm, path->path_id, &src, pin);
				if (ret < 0)
					MTK_DMX_ERR("set input source fail\n");
			} else {
				MTK_DMX_INFO("demod unlocked: %d\n", path->path_id);
				mtk_dmx_io_sel_input_clock_src(path->path_id, 0);
				mtk_dmx_io_set_tsif_en(path->path_id, false);

				/**
				 * reset fiq read/write to head to aviod
				 * video garbage on difference freq
				 */
				_m2ts_check_unlocked_fiq(rm, path->path_id);
			}
			break;
		}

	return 0;
}

static int m2ts_data_ready_event(struct mtk_demux2_data_ready_args *args)
{
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct mtk_dvr2 *dvr = NULL;
	int ret, i;

	if ((args == NULL) || (args->addr == NULL) || (args->size == 0))
		return 0;

	dvr = &args->path->dvr_playback;
	MTK_DMX_DEBUG("data_ready[%d]: 0x%llx 0x%x\n", args->path->path_id,
		(u64)(size_t)(args->addr), args->size);

	dvb_dvr2 = cast_to_dvr_parent(dvr);
	buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
	bc_ctx = (struct mtk_bc_ctx *)buffer_ctx;
	if (bc_ctx) {
		ret = mtk_bc_ringbuf_write(bc_ctx, args->addr, args->size);

		MTK_DMX_DEBUG("size 0x%x\n", args->size);
		for (i = 0; i < 4; i++)
			MTK_DMX_DEBUG("0x%x\n", *(u8 *)(args->addr + i));

		if (ret == -EOVERFLOW)
			MTK_DMX_ERR("data_ready[%d]: overflow\n", args->path->path_id);

		mtk_dvr2_write(dvr, false); // blocking
	}
	return 0;
}

static struct mtk_demux2_stc_ops m2ts_stc_ops = {
	.set_stc = m2ts_stc_set,
	.get_stc = m2ts_stc_get,
};

static struct mtk_demux2_buf_ops m2ts_dmx_buf_ops = {
	.set_rptr = m2ts_dmx_set_buf_rptr,
};

static struct mtk_demux2_media_ops m2ts_media_ops = {
	.notify = m2ts_notify_event,
	.query = m2ts_query_event,
	.streaming = m2ts_streaming_event,
	.data_ready = m2ts_data_ready_event,
};

int mtk_dmx_m2ts_init(struct mtk_demux2 *demux)
{
	int ret = 0;
	struct dvb_demux2 *base = cast_to_dmx_parent(demux);
	u16 pid_flt_num = mtk_dmx_rm_get_hwcap_by_id(demux->rm, HWCAPS_TYPE_PID_FILTER);
	struct mtk_m2ts_dmx_private *priv = NULL;
	struct mtk_dmx2dev *mtkdev = container_of(demux->rm, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);
	struct device_node *np, *np2;

	/* base part */
	base->protocol = DMX2_FORMAT_M2TS;

	memset(base->filter_caps, 0, sizeof(base->filter_caps));
	base->filter_caps[DMX2_FILTER_TYPE_TS].number = pid_flt_num;
	base->filternum = base->feednum = total_filter_num(base->filter_caps);

	base->priv = kzalloc(sizeof(struct mtk_m2ts_dmx_private), GFP_KERNEL);
	if (!base->priv) {
		MTK_DMX_ERR("allocate private data fail\n");
		return -ENOMEM;
	}

	base->feed_ops = &m2ts_feed_ops;

	/* derived part */
	demux->capabilities =
		DMX2_CAP_IN_FRONTEND |
		DMX2_CAP_OUT_MEMORY |
		DMX2_CAP_STREAMING |
		DMX2_CAP_TS_FILTER;

	demux->flt_ops = &m2ts_flt_ops;
	demux->stc_ops = &m2ts_stc_ops;
	demux->buf_ops = &m2ts_dmx_buf_ops;
	demux->media_ops = &m2ts_media_ops;

	priv = base->priv;
	// stc control word
	np = of_find_node_by_name(pdev->dev.of_node, "ip_configs");
	if (np == NULL) {
		MTK_DMX_ERR("Failed to get hwcaps node\n");
		return -ENOENT;
	}
	np2 = of_find_node_by_name(pdev->dev.of_node, "m2ts");
	if (np2) {
		of_property_read_u32(np2, "stc_cw", &priv->stc_cw);
		of_property_read_u32(np2, "stc_bypass_3_2", &priv->stc_bypass_3_2);
	}
	of_node_put(np2);
	of_node_put(np);

	ret = m2ts_temi_init(demux);
	if (ret < 0) {
		MTK_DMX_ERR("m2ts_temi_init is failed\n");

		return ret;
	}

	return 0;
}

int mtk_dmx_m2ts_exit(struct mtk_demux2 *demux)
{
	if (mt2s_temi_exit(demux) < 0) {
		MTK_DMX_DEBUG("mt2s_temi_exit is failed\n");

		// Continue even though error is happened.
	}

	return 0;
}

static int m2ts_dvr_start(struct dvb_dvr2 *dvr)
{
	int ret = 0;
	int eng_id = dvr->index;
	enum pvr_packet_type type;
	struct dvr_rec_config rec_cfg;
	struct mtk_dvr2 *mtk_dvr = dynamic_cast(struct mtk_dvr2 *, dvr);

	rec_cfg = mtk_dvr->rec_cfg;

	if (rec_cfg.pre_hdr_type == DVR_INSERT_TIMESTAMP)
		type = PVR_PACKET_TYPE_M2TS_WITH_TIMESTAMP;
	else
		type = PVR_PACKET_TYPE_M2TS;

	mtk_pvr_set_record_type(eng_id, type);
	mtk_pvr_set_record_mode(eng_id, ((rec_cfg.record_all == true) ? true : false));
	mtk_dvr->record_task_info.pause_callback = m2ts_dvr_finalize_callback;
	mtk_dvr->record_task_info.pause_state = DVR_PAUSE_STATE_INIT;

	if (mtk_dvr2_is_tee_mode(mtk_dvr)) {
		struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(dvr->fh);
		struct mtk_bc_ctx *bc_ctx = fh_dvrctx_get_bc_ctx(ctx);
		struct mem_info mem_info = { 0 };

		if (mtk_bc_get_mem_info(
			bc_ctx,
			MEM_INFO_TYPE_NORMAL,
			&mem_info) < 0) {
			MTK_DMX_ERR("get meminfo fail\n");
			return -EFAULT;
		}
		if (mem_info.size == 0) {
			MTK_DMX_ERR("meminfo's size is 0\n");
			return -EFAULT;
		}

		if (mtk_bc_metabuf_init(bc_ctx,
				sizeof(struct dmx2_record_parser_ts_metadata),
				MTK_BC_METADATA_BUF_NUM) < 0) {
			MTK_DMX_ERR("init metadata buffer fail\n");
			ret = -EFAULT;
			goto exit;
		}

		MTK_DMX_INFO("[PVR][secure_mode] lock pvr buffer %llx size %d\n",
			mem_info.paddr, mem_info.size);
		if (mtk_dmx_tee_if_lock_pvr_buffer(eng_id, rec_cfg.pipeline_id, &mem_info) < 0) {
			MTK_DMX_ERR("lock pvr buffer fail\n");
			ret = -EFAULT;
			goto exit;
		}
	}

exit:
	return ret;
}

static int m2ts_dvr_stop(struct dvb_dvr2 *dvr)
{
	int eng_id = dvr->index;
	struct mtk_dvr2 *mtk_dvr = dynamic_cast(struct mtk_dvr2 *, dvr);

	if (mtk_dvr2_is_tee_mode(mtk_dvr)) {
		struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(dvr->fh);
		struct mtk_bc_ctx *bc_ctx = fh_dvrctx_get_bc_ctx(ctx);
		struct mtk_dvr2 *mtk_dvr2 = cast_to_mtk_dvr(dvr);
		struct mem_info mem_info = { 0 };
		struct pvr_encrypt_param param = { 0 };

		param.key_token = mtk_dvr2->rec_cfg.key_token;

		if (mtk_bc_get_mem_info(
			bc_ctx,
			MEM_INFO_TYPE_NORMAL,
			&mem_info) < 0) {
			MTK_DMX_ERR("get meminfo fail\n");
			return -EFAULT;
		}
		if (mem_info.size == 0) {
			MTK_DMX_ERR("meminfo's size is 0\n");
			return -EFAULT;
		}

		if (mtk_dmx_tee_if_pvr_release(eng_id, &param) < 0) {
			MTK_DMX_ERR("release rec ta : %d resource fail\n", eng_id);
			return -EFAULT;
		}

		MTK_DMX_INFO("[PVR][secure_mode] unlock pvr buffer %llx size %d\n",
			mem_info.paddr, mem_info.size);

		if (mtk_dmx_tee_if_unlock_pvr_buffer(eng_id, &mem_info) < 0) {
			MTK_DMX_ERR("unlock pvr buffer fail\n");
			return -EFAULT;
		}

		if (mtk_bc_metabuf_release(bc_ctx) < 0) {
			MTK_DMX_ERR("release metadata buffer fail\n");
			return -EFAULT;
		}
		mtk_dvr2_set_secure_mode(mtk_dvr2, 0, 0);
	}

	mtk_dvr->record_task_info.pause_state = DVR_PAUSE_STATE_INIT;

	return 0;
}

static int m2ts_dvr_flush(struct dvb_dvr2 *dvr)
{
	struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(dvr->fh);

	if (ctx == NULL) {
		MTK_DMX_ERR("translate to mtk_dvr2_fh_ctx fail\n");
		return -EFAULT;
	}

	if (IS_INPUT_DEVICE(ctx->dir)) {
		struct dmx2_buffer_ctx *buf_ctx = dvr->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
		struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buf_ctx);
		struct mtk_m2ts_dvr_private *dvr_priv = cast_to_m2ts_dvr_private(dvr->priv);
		int eng;
		struct mtk_dvr2 *mtk_dvr;
		mtk_bc_ringbuf_reset(bc_ctx, true);
		dvr_priv->dvr_m2ts_filein_state = DVR_M2TS_FILEIN_INIT_STATE;
		MTK_DMX_INFO("set m2ts filein status to init\n");
		mtk_dvr = ctx->dvr;
		mtk_dvr->filein_task_info.write_offset = INVALID_FILEIN_WRITE_OFFSET;
		eng = mtk_dmx_rm_filein_find_engine_by_path(mtk_dvr->rm, mtk_dvr->path_id);
		mtk_dmx_io_filein_reset(eng);
		return 0;
	}
	else {
		mtk_pvr_flush_data(dvr->index);
	}
	return 0;
}

static u64 m2ts_filein_get_aligned_size(struct dvb_dvr2 *dvr, u64 size)
{
#define PWRITE_SAME_COUNT_MAX (5)

	struct dmx2_buffer_ctx *buf_ctx = NULL;
	struct mtk_dvr2 *mtk_dvr = dynamic_cast(struct mtk_dvr2 *, dvr);
	struct mtk_m2ts_dvr_private *dvr_priv = NULL;
	u64 count;

	if (dvr == NULL)
		return 0;
	dvr_priv = cast_to_m2ts_dvr_private(dvr->priv);
	if (dvr_priv == NULL)
		return 0;
	buf_ctx = dvr->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
	if (buf_ctx == NULL)
		return 0;
	if (size == 0)
		return 0;

	if (mtk_dvr->filein_task_info.write_offset == buf_ctx->ring.pwrite)
		dvr_priv->pwrite_same_count++;
	else
		dvr_priv->pwrite_same_count = 0;

	// Guess it is the last one if pwrite is the same for PWRITE_SAME_COUNT_MAX times
	if (dvr_priv->pwrite_same_count > PWRITE_SAME_COUNT_MAX)
		count = size;
	else
		count = FILEIN_ALIGN_SIZE(mtk_dvr->filein_cfg.timestamp_en, size);

	if (count == 0)
		MTK_DMX_DEBUG("size %lld\n", size);

	return count;

#undef PWRITE_SAME_COUNT_MAX

}

static int m2ts_dvr_write(struct dvb_dvr2 *dvr)
{
#define TS_HEADER_MAX (4)

	struct dmx2_buffer_ctx *buf_ctx = dvr->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
	struct mtk_dvr2 *mtk_dvr = dynamic_cast(struct mtk_dvr2 *, dvr);
	struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buf_ctx);
	struct mem_info mem_info = { 0 };
	struct mtk_m2ts_dvr_private *dvr_priv = NULL;

	ssize_t read_offset = 0;
	ssize_t write_offset = buf_ctx->ring.pwrite;

	u64 pstart = 0;
	u64 pwrite = 0;
	u64 pread = 0;
	u32 buf_size = buf_ctx->ring.size;
	u64 count = 0, size1 = 0;
	bool bInitTrust = false;
	int i, eng;

	dvr_priv = cast_to_m2ts_dvr_private(dvr->priv);
	if (dvr_priv == NULL) {
		MTK_DMX_ERR("get dvr priv handle invalid\n");
		return -EFAULT;
	}
	bInitTrust = dvr_priv->dvr_m2ts_filein_state != DVR_M2TS_FILEIN_NORMAL_STATE;
	eng = mtk_dmx_rm_filein_find_engine_by_path(mtk_dvr->rm, mtk_dvr->path_id);
	if (eng < 0)
		return -ENODEV;

	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&mem_info) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (mem_info.size == 0) {
		MTK_DMX_ERR("meminfo's size is 0\n");
		return -EFAULT;
	}

	read_offset = buf_ctx->ring.pread;
	if (mtk_dvr->filein_task_info.write_offset != INVALID_FILEIN_WRITE_OFFSET)
		read_offset = mtk_dvr->filein_task_info.write_offset;

	pstart = mem_info.paddr;
	pwrite = pstart + write_offset;
	pread = pstart + read_offset;

	MTK_DMX_DEBUG("S @ 0x%llx R @ 0x%llx LW @ 0x%x W @ 0x%llx size: 0x%x\n",
		pstart, pread, mtk_dvr->filein_task_info.write_offset, pwrite, mem_info.size);

	dvr_priv->buf_ctx = buf_ctx;
	if (pwrite != pread) {
		if (pwrite > pread)
			size1 = pwrite - pread;
		else
			size1 = buf_size - read_offset;

		count = m2ts_filein_get_aligned_size(dvr, size1);
		if (!mtk_dmx_io_feed_filein(eng, pread, count, bInitTrust)) {
			MTK_DMX_ERR("mtk_dmx_io_feed_filein(%d, 0x%llx, %lld, %s)\n",
				eng,
				pread,
				count,
				bInitTrust ? "true" : "false");
			return -EINVAL;
		}
		for (i = 0; i < min_t(u64, count, TS_HEADER_MAX); i++)
			MTK_DMX_DEBUG("0x%x\n",
				*(u8 *)(mem_info.vaddr + read_offset + i));
	} else {
		MTK_DMX_ERR("buffer is empty fail.\n");
		return -EINVAL;
	}

	mtk_dvr->filein_task_info.write_offset = (buf_ctx->ring.pread + (ssize_t)count) % buf_ctx->ring.size;

	dvr_priv->dvr_m2ts_filein_state = DVR_M2TS_FILEIN_NORMAL_STATE;
	return count;

#undef TS_HEADER_MAX
}

static struct dvr2_operations m2ts_dvr_ops = {
	.start = m2ts_dvr_start,
	.stop = m2ts_dvr_stop,
	.flush = m2ts_dvr_flush,
	.write = m2ts_dvr_write,
};

static int m2ts_dvr_set_buf_rptr(struct mtk_dvr2 *dvr, u64 rptr)
{
	return 0;
}

static u64 m2ts_dvr_get_buf_wptr_v2(struct mtk_dvr2 *dvr, bool is_va,
	u64 *pstart, u64 *pend)
{
	struct dvb_dvr2 *dvbdvr = cast_to_dvr_parent(dvr);
	u64 wptr = 0;

	if (dvbdvr->state == DVR2_STATE_GO) {
		struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(dvbdvr->fh);
		struct mtk_bc_ctx *bc_ctx = fh_dvrctx_get_bc_ctx(ctx);
		struct mem_info mem_info = { 0 };
		u64 start, diff, packet_len, va;

		if (mtk_bc_get_mem_info(
			bc_ctx,
			MEM_INFO_TYPE_NORMAL,
			&mem_info) < 0) {
			MTK_DMX_ERR("get meminfo fail\n");
			return -EFAULT;
		}
		if (mem_info.size == 0) {
			MTK_DMX_ERR("meminfo's size is 0\n");
			return -EFAULT;
		}

		va = (u64)(size_t)(mem_info.vaddr);
		start = mem_info.paddr;
		diff = mtk_pvr_get_wptr(dvbdvr->index) - start;

		// aligned wptr
		packet_len = (dvr->rec_cfg.pre_hdr_type == DVR_INSERT_NONE) ?
			M2TS_PACKET_TYPE_NORMAL : M2TS_PACKET_TYPE_WITH_STAMP;
		if (is_va) {
			wptr = va + rounddown64(diff, packet_len);
			if (pstart)
				*(u64 *)pstart = va;
			if (pend)
				*(u64 *)pend = va + mem_info.size;
		} else {
			wptr = start + rounddown64(diff, packet_len);
			if (pstart)
				*(u64 *)pstart = start;
			if (pend)
				*(u64 *)pend = start + mem_info.size;
		}
	}

	return wptr;
}

static u64 m2ts_dvr_get_buf_wptr(struct mtk_dvr2 *dvr)
{
	return m2ts_dvr_get_buf_wptr_v2(dvr, false, NULL, NULL);
}

static struct mtk_dvr2_buf_ops m2ts_dvr_buf_ops = {
	.set_rptr = m2ts_dvr_set_buf_rptr,
	.get_wptr = m2ts_dvr_get_buf_wptr,
	.get_wptr_v2 = m2ts_dvr_get_buf_wptr_v2,
};

static int m2ts_dvr_prewrite(struct mtk_dvr2 *dvr)
{
#define FEED_NULLPKT_COUNT 3
#define FEED_NULLPKT_BUF_SIZE 1024

	u8 nullPkt[TS_PACKET_SIZE] = {TS_SYNC_BYTE, GET_BYTE(NULL_PID, 1),
		GET_BYTE(NULL_PID, 0), TS_PAYLOAD_ONLY,
		[TS_HEADER_LEN ... (TS_PACKET_SIZE-1)] = NULL_PAYLOAD};
	u8 data[FEED_NULLPKT_BUF_SIZE] = {0};

	struct dmx2_buffer_ctx *buf_ctx = dvr->base.buf_ctx[DVR2_BUF_CTX_TYPE_IN];
	struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buf_ctx);
	struct mtk_m2ts_dvr_private *dvr_priv = NULL;
	int i, len = 0;

	dvr_priv = cast_to_m2ts_dvr_private(dvr->base.priv);
	if (dvr_priv == NULL) {
		MTK_DMX_ERR("get dvr priv handle invalid\n");
		return -EFAULT;
	}

	if (dvr_priv->dvr_m2ts_filein_state != DVR_M2TS_FILEIN_INIT_STATE)
		return 0;

	dvr_priv->dvr_m2ts_filein_state = DVR_M2TS_FILEIN_NORMAL_STATE;

	for (i = 0; i < FEED_NULLPKT_COUNT; i++) {
		if (dvr->filein_cfg.timestamp_en)
			len += TS_TIMESTAMP_LEN;

		memcpy(&data[len], nullPkt, TS_PACKET_SIZE);
		len += TS_PACKET_SIZE;
	}

	mtk_bc_ringbuf_write(bc_ctx, data, len);
	return 0;
}

static struct mtk_dvr2_playback_ops m2ts_dvr_playback_ops = {
	.prewrite = m2ts_dvr_prewrite,
};

static int _m2ts_ca_ctrl(struct mtk_dvr2 *dvr, struct dmx_ca_info info, bool bThrough)
{
	int ret;
	u16 ca_num = mtk_dmx_rm_get_hwcap_by_id(dvr->rm, HWCPAS_TYPE_CA);

	if (info.eng_id >= ca_num) {
		MTK_DMX_ERR("out-of-range (eng_id: %u)\n", info.eng_id);
		return -EINVAL;
	}

	ret = mtk_dmx_io_ca_ctrl(dvr->path_id, info, bThrough);
	if (ret < 0) {
		MTK_DMX_ERR("CA ctrl fail (eng_id: %u, bThrough: %u)\n", info.eng_id, bThrough);
		return -EFAULT;
	}

	return 0;
}

static int m2ts_dvr_attach(struct mtk_dvr2 *dvr, struct dmx_ca_info info)
{
	return _m2ts_ca_ctrl(dvr, info, true);
}

static int m2ts_dvr_detach(struct mtk_dvr2 *dvr, struct dmx_ca_info info)
{
	return _m2ts_ca_ctrl(dvr, info, false);
}

static void _queue_pvr_metadata(struct mtk_bc_ctx *bc_ctx, struct pvr_ts_metadata *meta, u32 num)
{
	u32 i;
	struct dmx2_record_parser_ts_metadata rec_meta;

	for (i = 0; i < num; i++) {
		if (meta[i].pid == 0)
			break;

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

		// @TODO: need to modify
		rec_meta.pid = meta[i].pid;
		rec_meta.sc_index = (meta[i].mask & PVR_TAG_MASK_I_FRAME) ? DMX2_I_FRAME : 0;
		rec_meta.timestamp = meta[i].pts;
		rec_meta.byte_number = meta[i].offset;

		MTK_DMX_DEBUG("meta[%u]:\n", i);
		MTK_DMX_DEBUG("rec_meta.pid 		= 0x%x\n",	 rec_meta.pid);
		MTK_DMX_DEBUG("rec_meta.sc_index	= 0x%x\n",	 rec_meta.sc_index );
		MTK_DMX_DEBUG("rec_meta.timestamp	= 0x%llx\n", rec_meta.timestamp);
		MTK_DMX_DEBUG("rec_meta.byte_number = 0x%llx\n", rec_meta.byte_number);

		mtk_bc_metabuf_queue(bc_ctx, &rec_meta);
	}

	memset(meta, 0x00, i*sizeof(struct pvr_ts_metadata));
}

static int m2ts_dvr_encrypt(struct mtk_dvr2 *dvr, u64 offset, u32 len)
{
	struct pvr_ts_metadata *metadata;
	int ret;
	u64 src_addr, dst_addr;
	u32 buf_size, split;
	struct pvr_encrypt_param param = { 0 };
	struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(cast_to_dvr_parent(dvr)->fh);
	struct mtk_bc_ctx *bc_ctx = fh_dvrctx_get_bc_ctx(ctx);
	struct mem_info pvr_meminfo = { 0 };
	struct mem_info aes_meminfo = { 0 };

	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&pvr_meminfo) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (pvr_meminfo.size == 0) {
		MTK_DMX_ERR("pvr_meminfo's size is 0\n");
		return -EFAULT;
	}
	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_CRYPTO,
		&aes_meminfo) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (aes_meminfo.size == 0) {
		MTK_DMX_ERR("aes_meminfo's size is 0\n");
		return -EFAULT;
	}

	metadata = kmalloc(sizeof(struct pvr_ts_metadata)*PVR_METADATA_MAX_NUM, GFP_ATOMIC);
	if (!metadata) {
		MTK_DMX_ERR("out-of-memory\n");
		return -ENOMEM;
	}

	src_addr = pvr_meminfo.paddr + offset;
	dst_addr = aes_meminfo.paddr + offset;
	buf_size = pvr_meminfo.size;

	memset(metadata, 0x00, sizeof(struct pvr_ts_metadata)*PVR_METADATA_MAX_NUM);

	split = (offset + len > buf_size) ? (buf_size - offset) : 0;
	if (split > 0) {
		param.key_token = dvr->rec_cfg.key_token;
		param.src_addr = src_addr;
		param.dst_addr = dst_addr;
		param.size = split;
		param.index_num = PVR_METADATA_MAX_NUM;
		param.metadata = metadata;
		param.flag = 0;

		ret = mtk_dmx_tee_if_pvr_ts_encrypt_buffer(
			cast_to_dvr_parent(dvr)->index,
			&param);
		if (ret < 0) {
			MTK_DMX_ERR("pvr encrypt buffer fail\n");
			kfree(metadata);
			return -EFAULT;
		}

		_queue_pvr_metadata(bc_ctx, metadata, PVR_METADATA_MAX_NUM);

		src_addr = pvr_meminfo.paddr;
		dst_addr = aes_meminfo.paddr;
		len -= split;
	}

	param.key_token = dvr->rec_cfg.key_token;
	param.src_addr = src_addr;
	param.dst_addr = dst_addr;
	param.size = len;
	param.index_num = PVR_METADATA_MAX_NUM;
	param.metadata = metadata;
	param.flag = 0;

	ret = mtk_dmx_tee_if_pvr_ts_encrypt_buffer(
		cast_to_dvr_parent(dvr)->index,
		&param);
	if (ret < 0) {
		MTK_DMX_ERR("pvr encrypt buffer fail\n");
		kfree(metadata);
		return -EFAULT;
	}

	_queue_pvr_metadata(bc_ctx, metadata, PVR_METADATA_MAX_NUM);

	kfree(metadata);

	return ret;
}

static struct mtk_dvr2_crypto_ops m2ts_dvr_crypto_ops = {
	.attach = m2ts_dvr_attach,
	.detach = m2ts_dvr_detach,
	.encrypt = m2ts_dvr_encrypt,
};

static inline void _init_dvr_rec_cfg(struct mtk_dvr2 *dvr)
{
	struct dvr_rec_config *cfg = &dvr->rec_cfg;

	memset(cfg, 0, sizeof(struct dvr_rec_config));
}

static int m2ts_dvr_finalize_callback(struct mtk_dvr2 *dvr, int timeout)
{
	int ret;
	static struct pvr_ts_metadata metadata[PVR_METADATA_MAX_NUM];
	struct pvr_encrypt_param param = { 0 };
	struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(cast_to_dvr_parent(dvr)->fh);
	struct mtk_bc_ctx *bc_ctx = fh_dvrctx_get_bc_ctx(ctx);
	struct mem_info pvr_meminfo = { 0 };
	struct mem_info aes_meminfo = { 0 };
	u64 src_addr = 0, dst_addr = 0;
	u32 buf_size;

	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_NORMAL,
		&pvr_meminfo) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (pvr_meminfo.size == 0) {
		MTK_DMX_ERR("pvr_meminfo's size is 0\n");
		return -EFAULT;
	}
	if (mtk_bc_get_mem_info(
		bc_ctx,
		MEM_INFO_TYPE_CRYPTO,
		&aes_meminfo) < 0) {
		MTK_DMX_ERR("get meminfo fail\n");
		return -EFAULT;
	}
	if (aes_meminfo.size == 0) {
		MTK_DMX_ERR("aes_meminfo's size is 0\n");
		return -EFAULT;
	}

	src_addr = pvr_meminfo.paddr;
	dst_addr = aes_meminfo.paddr;
	buf_size = pvr_meminfo.size;

	param.key_token = dvr->rec_cfg.key_token;
	param.src_addr = src_addr;
	param.dst_addr = dst_addr;
	param.size = buf_size;
	param.index_num = PVR_METADATA_MAX_NUM;
	param.metadata = metadata;
	param.flag = M2TS_TYPE_ENABLE_RECORD_PAUSE;

	ret = mtk_dmx_tee_if_pvr_ts_encrypt_buffer(cast_to_dvr_parent(dvr)->index, &param);
	if (ret < 0) {
		MTK_DMX_ERR("pvr notify pause fail\n");
		return -EFAULT;
	}

	return ret;
}

int mtk_dvr_m2ts_init(struct mtk_dvr2 *dvr)
{
	struct dvb_dvr2 *base = cast_to_dvr_parent(dvr);
	struct mtk_m2ts_dvr_private *pm2ts_dvr_priv = NULL;

	/* base part */
	base->protocol = DVR2_FORMAT_M2TS;
	base->state = DVR2_STATE_FREE;
	base->priv = kzalloc(sizeof(struct mtk_m2ts_dvr_private), GFP_KERNEL);
	if (!base->priv) {
		MTK_DMX_ERR("allocate private data fail\n");
		return -EFAULT;
	}

	base->ops = &m2ts_dvr_ops;

	/* derived part */
	dvr->capabilities =
		DMX2_CAP_IN_FRONTEND |
		DMX2_CAP_OUT_MEMORY |
		DMX2_CAP_STREAMING |
		DMX2_CAP_IN_MEMORY;

	dvr->buf_ops = &m2ts_dvr_buf_ops;
	dvr->playback_ops = &m2ts_dvr_playback_ops;
	dvr->crypto_ops = &m2ts_dvr_crypto_ops;

	_init_dvr_rec_cfg(dvr);

	pm2ts_dvr_priv = cast_to_m2ts_dvr_private(base->priv);
	pm2ts_dvr_priv->dvr_m2ts_filein_state = DVR_M2TS_FILEIN_INIT_STATE;

	return 0;
}

int mtk_dvr_m2ts_exit(struct mtk_dvr2 *dvr)
{
	struct dvb_dvr2 *base = cast_to_dvr_parent(dvr);

	base->state = DVR2_STATE_FREE;
	dvr->record_task_info.pause_state = DVR_PAUSE_STATE_INIT;
	kfree(base->priv);

	return 0;
}
