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

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <media/dvb_frontend.h>

#include "mdrv_adapter.h"
#include "cfm.h"

#include "mtk_dmx_common.h"
#include "mtk_dmx_log.h"
#include "mtk_dmx_log_alp.h"
#include "mtk_dmx_atsc3.h"
#include "mtk_dmx_alp.h"
#include "mtk_dmx_rm.h"
#include "mtk_dmx_dev.h"
#include "mtk_dmx_drv.h"
#include "mtk_dmx_pvr.h"
#include "mtk_dmx_io.h"
#include "mtk_dmx_mediactrl.h"

#include "network_packet.h"
#include "alp_lmt.h"
#include "alp_parser_if.h"
#include "alp_priv_packet_custom_0.h"
#include "alp_priv_packet_custom_1.h"

#include "mdrv_atsc30_if.h"
#include "mdrv_atsc30_alp_si.h"
#include "mdrv_atsc30_udp_ip.h"
#include "mdrv_atsc30_ptp.h"

// Put it in the end of head includes
#define ENUM_GENERATE_STRINGS  // Start string generation
#include "mtk_dmx_enum2str.h"
#undef ENUM_GENERATE_STRINGS   // Stop string generation

#if (LOG_TO_FILE)
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/syscalls.h>

static struct file *pfd_ORI;
#endif
//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------
#ifdef pr_fmt
#undef pr_fmt
#endif //pr_fmt
#define pr_fmt(fmt) "mtk_dmx_atsc3: " fmt

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------
#define ATSC3_PVR_POLICY (SCHED_RR)
#define ATSC3_PVR_PRIORITY (1) // MIN:1 MAX:99
#define ATSC3_PVR_WAIT_TIME1 (25000)
#define ATSC3_PVR_WAIT_TIME2 (25500)
#define ATSC3_PVR_FINALIZE_TIMEOUT (100)
#define ATSC3_FILEIN_WRITE_MAX_RETRY_COUNT (100)

// TODO: DMX ring buffer
#define ATSC3_META_COUNT (1024)

#define ATSC3_IP_ADDR_MASK_IGNORE (0xFFFFFFFF)
#define ATSC3_UDP_PORT_MASK_IGNORE (0xFFFF)

#define ATSC3_STC_RESET_RETRY (3)

//TODO: This is a temporary solution before it is available in cfm.h
#ifndef CFM_DECOUPLE_DMD
#define CFM_DECOUPLE_DMD (12)
#endif //#ifndef CFM_DECOUPLE_DMD

#define ATSC3_EXTERNAL_DEMOD_ID (1)
#define ATSC3_AUSTIN_EXTERNAL_DEMOD_ID (0)
#define ATSC3_CFM_PROPERTYIES_NUM (1)

#define ATSC3_ALP_PRIV_DTS "ip_configs"
#define ATSC3_ALP_PRIV_IPS "atsc3"
#define ATSC3_ALP_PRIV_PACKET_CUSTOMS "packet_customs"
#define ATSC3_ALP_PRIV_PACKET_CUSTOMS_PROPNAME_MAX (32)
#define ATSC3_ALP_PRIV_PACKET_HW_BUF "hw_buf"
#define ATSC3_ALP_PRIV_PACKET_OPERATIONS_DEFAULT (0)
#define ATSC3_ALP_PRIV_PACKET_OPERATIONS_AUSTIN (1)

#define MHZ (1000 * 1000)

#define STC_CW_INTEGER_SHIFT (27)
#define STC_CW_INTEGER_WIDTH (5)
#define STC_CW_DECIMAL_SHIFT (0)
#define STC_CW_DECIMAL_WIDTH (27)

#define STC_CW_CONSTANT_MULTIPLIER (5)
#define STC_CW_CONSTANT_DIVISOR (16)
#define STC_CW_CONSTANT_COUNTER (300)

// for 90KHZ
//#define STC_CW_COUNTER_300_ENABLE
#ifdef STC_CW_COUNTER_300_ENABLE
#define STC_CW_IS_COUNTER_300_ENABLED (true)
#else
#define STC_CW_IS_COUNTER_300_ENABLED (false)
#endif //#ifdef STC_CW_COUNTER_300_ENABLE

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------
#define ATSC3_ALP_IS_COMPRESSED(dmx2_filter) \
	(dmx2_filter->params.alp.packet_type == DMX2_ALP_PACKET_TYPE_COMPRESSED_IP)
#define ATSC3_ALP_IS_IP_V4(dmx2_filter) \
	(dmx2_filter->params.alp.packet_type == DMX2_ALP_PACKET_TYPE_IP_V4)

#define STC_CW_GET(field_name, data) \
	GET_FIELD_U32( \
		data, \
		STC_CW_## field_name ##_WIDTH, \
		STC_CW_## field_name ##_SHIFT)

#define STC_CW_GET_INTEGER(data) \
	STC_CW_GET(INTEGER, data)
#define STC_CW_GET_DECIMAL(data) \
	STC_CW_GET(DECIMAL, data)

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------
typedef int (*dmx_alp_set_alp_filter_callback)(
		struct dmx2_filter_params dmx2_filter_params,
		demux_alp_filter_t *demux_alp_filter);

typedef int (*filter_start_callback)(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type);
typedef int (*filter_stop_callback)(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type);

struct stc_cw_info {
	u16 integer;
	u32 decimal;
};

//-------------------------------------------------------------------------------------------------
//  Functions Prototype
//-------------------------------------------------------------------------------------------------
// helpers to access external functions w/ parameter dmx2_filter
static int atsc3_dmx_get_fh_ctx(
		struct dmx2_filter *dmx2_filter,
		struct mtk_demux2_fh_ctx **ctx);
static int atsc3_dmx_get_bc_ctx(
	struct dmx2_filter *dmx2_filter,
	struct mtk_bc_ctx **bc_ctx);

static struct mtk_demux2 *atsc3_get_mtk_demux2(
	struct dmx2_filter *dmx2_filter);
static struct mtk_dvr2 *atsc3_get_mtk_dvr2(
	struct dmx2_filter *dmx2_filter);
static struct mtk_dmx2dev *atsc3_get_mtk_dmx2dev(
	struct dmx2_filter *dmx2_filter);
static demux_alp_instance_e atsc3_dmx_alp_get_instance(
	struct dmx2_filter *dmx2_filter);

static int atsc3_rm_get_filter_idx(
		struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type);
static int atsc3_rm_put_filter_idx(
		struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type,
		u16 filter_id);

static int atsc3_dmx_alp_get_filter(
		struct dmx2_filter *dmx2_filter,
		demux_alp_filter_t *demux_alp_filter);
static int atsc3_dmx_alp_clear_filter(
		struct dmx2_filter *dmx2_filter,
		demux_alp_filter_t *demux_alp_filter);

static int atsc3_dmx_set_stc_info(
		struct dmx2_filter *dmx2_filter,
		void *data);
static int atsc3_dmx_get_stc_info(
		struct dmx2_filter *dmx2_filter,
		void *data);

static int atsc3_dmx_set_filter_info(
		struct dmx2_filter *dmx2_filter,
		void *data, u32 size);

static int atsc3_dmx_set_filter_type(
		struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type);

static int atsc3_dmx_add_filter_list(
		struct dmx2_filter *dmx2_filter,
		int filter_id,
		struct filter_type filter_type);
static int atsc3_dmx_remove_filter_list(
		struct dmx2_filter *dmx2_filter,
		int filter_id,
		struct filter_type filter_type);

// helpers for internal use
static int atsc3_dmx_get_priv_packet_operations(
		struct dmx2_filter *dmx2_filter,
		struct alp_priv_packet_operations **alp_priv_packet_ops);

static int atsc3_mdrv_atsc30_sw_parser_init(
		demux_alp_instance_e demux_alp_instance,
		demux_alp_filter_t *demux_alp_filter);
static int atsc3_mdrv_atsc30_sw_parser_exit(
		demux_alp_instance_e demux_alp_instance,
		demux_alp_filter_t *demux_alp_filter);

static int atsc3_pvr_engine_init(struct mtk_atsc3_feed *mtk_atsc3_feed);
static int atsc3_pvr_engine_exit(struct mtk_atsc3_feed *mtk_atsc3_feed);
static int atsc3_pvr_engine_start(struct mtk_atsc3_feed *mtk_atsc3_feed);
static int atsc3_pvr_engine_stop(struct mtk_atsc3_feed *mtk_atsc3_feed);

static int atsc3_dmx_alp_filter_get(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type,
		demux_alp_filter_t *demux_alp_filter);
static int atsc3_dmx_alp_filter_put(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type,
		demux_alp_filter_t *demux_alp_filter);

static int atsc3_dmx_alp_metadata_init(
		struct mtk_atsc3_feed *mtk_atsc3_feed);
static int atsc3_dmx_alp_metadata_exit(
		struct mtk_atsc3_feed *mtk_atsc3_feed);

static int atsc3_dmx_feed_start(struct dmx2_feed *dmx2_feed);
static int atsc3_dmx_feed_stop(struct dmx2_feed *dmx2_feed);
static int atsc3_dmx_feed_flush(struct dmx2_feed *dmx2_feed);

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

static int atsc3_dmx_set_buf_rptr(
		struct dvb_fh *fh,
		u64 rptr);

static int atsc3_notify_event(struct mtk_demux2_notify_args *args);
static int atsc3_query_event(struct mtk_demux2_query_args *args);
static int atsc3_streaming_event(struct mtk_demux2_streaming_args *args);

static int atsc3_dmx_filter_alloc(struct dmx2_filter **dmx2_filter);
static int atsc3_dmx_filter_release(struct dmx2_filter *dmx2_filter);

static int atsc3_mdrv_atsc30_init(struct mtk_atsc3_feed *mtk_atsc3_feed);
static int atsc3_mdrv_atsc30_exit(struct mtk_atsc3_feed *mtk_atsc3_feed);

static int atsc3_dvr_start(struct dvb_dvr2 *dvb_dvr2);
static int atsc3_dvr_stop(struct dvb_dvr2 *dvb_dvr2);
static int atsc3_dvr_flush(struct dvb_dvr2 *dvb_dvr2);
static int atsc3_dvr_write(struct dvb_dvr2 *dvb_dvr2);

static int atsc3_pvr_rec_config_init(struct mtk_dvr2 *mtk_dvr2);
static u64 atsc3_dvr_get_buf_wptr(struct mtk_dvr2 *mtk_dvr2);

static int atsc3_get_stc_frequency(
		struct dmx2_filter *dmx2_filter,
		bool is_counter_enabled,
		u64 *stc_frequency);

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------
static struct dmx2_feed_operations _dmx_feed_ops = {
	.start = atsc3_dmx_feed_start,
	.stop = atsc3_dmx_feed_stop,
	.flush = atsc3_dmx_feed_flush,
};

static struct mtk_demux2_flt_ops _dmx_flt_ops = {
	.allocate_filter = atsc3_dmx_filter_alloc,
	.release_filter = atsc3_dmx_filter_release,
};

int atsc3_stc_set(
		struct dvb_fh *fh,
		struct dmx2_stc *dmx2_stc)
{
	int ret = 0;
	struct mtk_demux2_fh_ctx *mtk_demux2_fh_ctx = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct dmx2_filter *dmx2_filter= NULL;
	struct ptp_filter_cfg filter_info;
	struct stc_info stc_info = { 0 };
	u64 stc = 0;
	unsigned int base = 0;
	u32 stc_l = 0;
	u32 stc_h = 0;

	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(dmx2_stc == NULL))
		return -EINVAL;
	mtk_demux2_fh_ctx = fh_to_dmxctx(fh);
	mtk_demux2 = fh_to_demux(fh);
	dmx2_filter = dvb_dmx2_filter_find(cast_to_dmx_parent(mtk_demux2), fh);

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

	ret = call_op(mtk_demux2, stc, get_base, fh, &base);
	if (ret < 0) {
		MTK_DMX_ERR("STC get base is failed\n");
		return -EIO;
	}

	ret = atsc3_dmx_get_filter_info(dmx2_filter, (void *) &filter_info, sizeof(struct ptp_filter_cfg));
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_dmx_get_filter_info is failed\n");
		return -EIO;
	}

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

	MTK_DMX_DEBUG(
		"dmx2_stc->stc 0x%llx dmx2_stc->base %d\n",
		dmx2_stc->stc,
		dmx2_stc->base);
	if (dmx2_stc->base != STC_BASE_90KHZ)
		stc = div_u64(dmx2_stc->stc, dmx2_stc->base);

	MTK_DMX_DEBUG(
		"before: stc 0x%llx base %d\n",
		stc,
		base);
	stc *= base;
	MTK_DMX_DEBUG(
		"after: stc 0x%llx\n",
		stc);

	stc_l = STC_L(stc);
	stc_h = STC_H(stc);

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

	return ret;
}

int atsc3_stc_get(
		struct dvb_fh *fh,
		struct dmx2_stc *dmx2_stc)
{
	int ret = 0;
	struct mtk_demux2_fh_ctx *mtk_demux2_fh_ctx = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct dmx2_filter *dmx2_filter= NULL;
	struct ptp_filter_cfg filter_info;
	struct stc_info stc_info = { 0 };
	unsigned int base = 0;
	u32 stc_l = 0;
	u32 stc_h = 0;

	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(dmx2_stc == NULL))
		return -EINVAL;
	mtk_demux2_fh_ctx = fh_to_dmxctx(fh);
	mtk_demux2 = fh_to_demux(fh);
	dmx2_filter = dvb_dmx2_filter_find(cast_to_dmx_parent(mtk_demux2), fh);

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

	ret = call_op(mtk_demux2, stc, get_base, fh, &base);
	if (ret < 0) {
		MTK_DMX_ERR("STC get base is failed\n");
		return -EIO;
	}

	ret = atsc3_dmx_get_filter_info(dmx2_filter, (void *) &filter_info, sizeof(struct ptp_filter_cfg));
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_dmx_get_filter_info is failed\n");
		return -EIO;
	}

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

	mtk_dmx_stc(filter_info.pcr_filter_cfg.cfg.dst_eng_id, &stc_l, &stc_h, false);
	dmx2_stc->stc = STC(stc_h, stc_l);
	/* output: divisor for stc to get 90 kHz clock */
	dmx2_stc->base = base;

	MTK_DMX_DEBUG(
		"dmx2_stc->stc 0x%llx dmx2_stc->base %d\n",
		dmx2_stc->stc,
		dmx2_stc->base);

	return ret;
}

int atsc3_stc_get_base(
		struct dvb_fh *fh,
		unsigned int *base)
{
	int ret = 0;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct dmx2_filter *dmx2_filter= NULL;
	struct alp_priv_packet_operations *alp_priv_packet_ops = NULL;

	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;

	mtk_demux2 = fh_to_demux(fh);
	dmx2_filter = dvb_dmx2_filter_find(cast_to_dmx_parent(mtk_demux2), fh);
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	ret = atsc3_dmx_get_priv_packet_operations(
			dmx2_filter,
			&alp_priv_packet_ops);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_get_priv_packet_operations is failed\n");

		return ret;
	}

	*base = (unsigned int) div_u64(
			alp_priv_packet_ops->stc_frequency,
			FREQ_90KHZ);

	return ret;
}

static struct mtk_demux2_stc_ops _dmx_stc_ops = {
	.set_stc = atsc3_stc_set,
	.get_stc = atsc3_stc_get,
	.get_base = atsc3_stc_get_base,
};

static struct mtk_demux2_buf_ops _dmx_buf_ops = {
	.set_rptr = atsc3_dmx_set_buf_rptr,
};

static struct mtk_demux2_media_ops _dmx_media_ops = {
	.notify = atsc3_notify_event,
	.query = atsc3_query_event,
	.streaming = atsc3_streaming_event,
};

static struct dvr2_operations _dvr_ops = {
	.start = atsc3_dvr_start,
	.stop = atsc3_dvr_stop,
	.flush = atsc3_dvr_flush,
	.write = atsc3_dvr_write,
};

// DVR2_BUF_CTX_TYPE_OUT
static struct mtk_dvr2_buf_ops _dvr_buf_ops = {
	.set_rptr = NULL,
	.get_wptr = atsc3_dvr_get_buf_wptr,
};

struct alp_priv_packet_operations _alp_priv_packet_ops[] = {
	// custom_0
	{
		.stc_frequency = FREQ_90KHZ,
		.is_austin = false,

		.is_pud_packet = alp_priv_packet_is_pud_packet,
		.get_pud_packet_type = alp_priv_packet_custom_0_get_pud_packet_type,
		.get_pud_type = alp_priv_packet_get_pud_type,

		.parse_plp_id = alp_priv_packet_custom_0_parse_plp_id,
		.parse_ptp = alp_priv_packet_custom_0_parse_ptp,

		.get_stc_up_bound = alp_priv_packet_custom_0_get_stc_up_bound,
	},

	// custom_1
	{
		.stc_frequency = FREQ_90KHZ,
		.is_austin = true,

		.is_pud_packet = alp_priv_packet_is_pud_packet,
		.get_pud_packet_type = alp_priv_packet_custom_1_get_pud_packet_type,
		.get_pud_type = alp_priv_packet_get_pud_type,

		.parse_plp_id = alp_priv_packet_custom_1_parse_plp_id,
		.parse_ptp = alp_priv_packet_custom_1_parse_ptp,

		.get_stc_up_bound = alp_priv_packet_custom_1_get_stc_up_bound,
	},
};

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
static int atsc3_dmx_get_fh_ctx(
		struct dmx2_filter *dmx2_filter,
		struct mtk_demux2_fh_ctx **ctx)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(ctx == NULL))
		return -EINVAL;

	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	*ctx = fh_to_dmxctx(fh);

	return ret;
}

static int atsc3_dmx_get_bc_ctx(
		struct dmx2_filter *dmx2_filter,
		struct mtk_bc_ctx **bc_ctx)
{
	int ret = 0;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(bc_ctx == NULL))
		return -EINVAL;

	ret = atsc3_dmx_get_fh_ctx(
			dmx2_filter,
			&ctx);
	if (ret != 0)
		MTK_DMX_ERR(
			"atsc3_dmx_get_fh_ctx(...) is failed\n");
	else
		*bc_ctx = fh_dmxctx_get_bc_ctx(ctx);

	return ret;
}

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

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

	return mtk_demux2;
}

static struct mtk_dvr2 *atsc3_get_mtk_dvr2(
	struct dmx2_filter *dmx2_filter)
{
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return NULL;
	mtk_demux2 = atsc3_get_mtk_demux2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return NULL;
	mtk_dvr2 = &dumex2_to_path(mtk_demux2)->dvr_record;

	return mtk_dvr2;
}

static struct mtk_dmx2dev *atsc3_get_mtk_dmx2dev(
	struct dmx2_filter *dmx2_filter)
{
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dmx_rm *mtk_dmx_rm = NULL;
	struct mtk_dmx2dev *mtk_dmx2dev = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return NULL;
	mtk_demux2 = atsc3_get_mtk_demux2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return NULL;
	mtk_dmx_rm = mtk_demux2->rm;
	if (MTK_DMX_VERIFY(mtk_dmx_rm == NULL))
		return NULL;
	mtk_dmx2dev = container_of(mtk_dmx_rm, struct mtk_dmx2dev, rm);

	return mtk_dmx2dev;
}

static demux_alp_instance_e atsc3_dmx_alp_get_instance(
	struct dmx2_filter *dmx2_filter)
{
	struct mtk_demux2 *mtk_demux2 = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	mtk_demux2 = atsc3_get_mtk_demux2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return DEMUX_ALP_INSTANCE_NULL;
	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_demux2->path_id);

	return demux_alp_instance;
}

static int atsc3_rm_get_filter_idx(
		struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type)
{
	int ret = FILTER_IDX_INVALID;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;

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

	ret = mtk_dmx_rm_get_filter_idx(
			mtk_demux2->rm,
			filter_type);
	if (ret == FILTER_IDX_INVALID)
		MTK_DMX_ERR(
			"mtk_dmx_rm_get_filter_idx is failed\n");

	return ret;
}

static int atsc3_rm_put_filter_idx(
		struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type,
		u16 filter_id)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;

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

	mtk_dmx_rm_put_filter_idx(
			mtk_demux2->rm,
			filter_type,
			filter_id);

	return ret;
}

static int atsc3_dmx_alp_get_filter(
		struct dmx2_filter *dmx2_filter,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	int tmp_ret;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	tmp_ret = mtk_dmx_alp_get_filter(
			demux_alp_instance,
			demux_alp_filter);
	if (tmp_ret != MTK_DMX_SUCCESS) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_get_filter is failed\n");

		ret = -EIO;
	}

	return ret;
}

static int atsc3_dmx_alp_clear_filter(
		struct dmx2_filter *dmx2_filter,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	int tmp_ret;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	tmp_ret = mtk_dmx_alp_clear_filter(
			demux_alp_instance,
			demux_alp_filter);
	if (tmp_ret != MTK_DMX_SUCCESS) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_clear_filter is failed\n");

		ret = -EIO;
	}

	return ret;
}

static int atsc3_dmx_set_stc_info(
		struct dmx2_filter *dmx2_filter,
		void *data)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);

	fh_dmxctx_set_stc_info(ctx, data);

	return ret;
}

static int atsc3_dmx_get_stc_info(
		struct dmx2_filter *dmx2_filter,
		void *data)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);

	fh_dmxctx_get_stc_info(ctx, data);

	return ret;
}

static int atsc3_dmx_set_filter_info(
		struct dmx2_filter *dmx2_filter,
		void *data, u32 size)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);

	ret = fh_dmxctx_set_filter_info(ctx, data, size);
	if (ret != 0) {
		MTK_DMX_ERR(
			"fh_dmxctx_set_filter_info is failed\n");

		return -EIO;
	}

	return ret;
}

int atsc3_dmx_get_filter_info(
		struct dmx2_filter *dmx2_filter,
		void *data, u32 size)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);

	ret = fh_dmxctx_get_filter_info(ctx, data, size);
	if (ret != 0) {
		MTK_DMX_ERR(
			"fh_dmxctx_set_filter_info is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_dmx_set_filter_type(
	struct dmx2_filter *dmx2_filter,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2_fh_ctx *ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);

	fh_dmxctx_set_filter_type(ctx, filter_type);

	return ret;
}

static int atsc3_dmx_add_filter_list(
		struct dmx2_filter *dmx2_filter,
		int filter_id,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;

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

	MTK_DMX_DEBUG(
		"filter_id %d filter_sub_type %s\n",
		filter_id,
		filter_sub_type_2_str(filter_type.sub));

	if (mtk_demux2_add_filter_list(
		mtk_demux2,
		fh,
		filter_id,
		filter_type.sub) != 0) {
		MTK_DMX_ERR("mtk_demux2_add_filter_list is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_dmx_remove_filter_list(
		struct dmx2_filter *dmx2_filter,
		int filter_id,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;

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

	MTK_DMX_DEBUG(
		"filter_id %d filter_sub_type %s\n",
		filter_id,
		filter_sub_type_2_str(filter_type.sub));

	if (mtk_demux2_remove_filter_list(
		mtk_demux2,
		fh,
		filter_id,
		filter_type.sub) != 0) {
		MTK_DMX_ERR("mtk_demux2_remove_filter_list is failed\n");

		return -EIO;
	}

	return ret;
}

static struct dvb_frontend *atsc3_get_dvb_frontend(int demod_id)
{
	struct dvb_device *dvbdev = NULL;
	struct dvb_adapter *adap = mdrv_get_dvb_adapter();

	if (!adap) {
		MTK_DMX_ERR("get adapter fail\n");
		return NULL;
	}

	list_for_each_entry(dvbdev, &adap->device_list, list_head)
		if ((dvbdev->type == DVB_DEVICE_FRONTEND)
			&& (demod_id == dvbdev->id))
			return dvbdev->priv;

	return NULL;
}

static inline int atsc3_custom_of_read(
		const struct device_node *np,
		const char *propname,
		const char **out_string)
{
	int ret = 0;

	ret = of_property_read_string(
			np,
			propname,
			out_string);
	if (ret != 0)
		MTK_DMX_ERR("Fail to read %s\n", propname);

	return ret;
}

static int atsc3_custom_read(
		struct platform_device *pdev,
		const char *propname,
		const char **out_string)
{
	int ret = 0;
	struct device_node *np = NULL, *np1= NULL, *np2 = NULL;

	if (MTK_DMX_VERIFY(pdev == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(propname == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(out_string == NULL))
		return -EINVAL;

	of_node_get(pdev->dev.of_node);
	np = of_find_node_by_name(
			pdev->dev.of_node,
			ATSC3_ALP_PRIV_DTS);
	if (np) {
		np1 = of_find_node_by_name(
			pdev->dev.of_node,
			ATSC3_ALP_PRIV_IPS);
		if (np1) {
			np2 = of_find_node_by_name(
				pdev->dev.of_node,
				ATSC3_ALP_PRIV_PACKET_CUSTOMS);
			if (!np2) {
				ret = -ENOENT;
				goto FAIL;
			}
		} else {
			ret = -ENOENT;
			goto FAIL;
		}
	} else {
		ret = -ENOENT;
		goto FAIL;
	}

	ret = atsc3_custom_of_read(
			np2,
			propname,
			out_string);
	if (ret != 0)
		MTK_DMX_ERR(
			"atsc3_custom_of_read(..., %s, ...) is failed\n",
			propname);

FAIL:
	of_node_put(np2);
	of_node_put(np1);
	of_node_put(np);
	if (ret == -ENOENT)
		MTK_DMX_ERR("Fail to get atsc3 related node\n");
	return ret;
}

static int atsc3_dmx_is_austin_support(
		struct dmx2_filter *dmx2_filter)
{
	int tmp_ret = 0;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dmx_rm *mtk_dmx_rm = NULL;
	struct mtk_dmx2dev *mtk_dmx2dev = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *priv = NULL;
	struct platform_device *platform_device = NULL;
	struct dvb_frontend *dvb_frontend = NULL;
	struct cfm_property cfm_property = {
		.cmd = CFM_DECOUPLE_DMD,
	};
	struct cfm_properties cfm_properties = {
		.num = ATSC3_CFM_PROPERTYIES_NUM,
		.props = &cfm_property,
	};
	demux_alp_key_words_t stKeyWords = {
		.eKeyWordType = DEMUX_ALP_KEY_WORD_TYPE_KEY_WORD_4B_TIMESTAMP_4B,
		.u8keyWords = {
			ALP_KEY_WORD_3_DEFAULT,
			ALP_KEY_WORD_2_DEFAULT,
			ALP_KEY_WORD_1_DEFAULT,
			ALP_KEY_WORD_0_DEFAULT,
		},
		.bSidebandEnable = false,
	};
	char alp_priv_packet_custom[ATSC3_ALP_PRIV_PACKET_CUSTOMS_PROPNAME_MAX] = { 0 };
	const char *custom = NULL;
	int i = 0;
	demux_alp_instance_e demux_alp_instance;
	struct mtk_dmx_path *mtk_dmx_path = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	priv = dvb_dvr2->priv;

	mtk_demux2 = atsc3_get_mtk_demux2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return -EINVAL;

	mtk_dmx_path = dumex2_to_path(mtk_demux2);
	demux_alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(mtk_dmx_path->path_id);

	mtk_dmx_rm = mtk_demux2->rm;
	if (MTK_DMX_VERIFY(mtk_dmx_rm == NULL))
		return -EINVAL;
	mtk_dmx2dev = container_of(mtk_dmx_rm, struct mtk_dmx2dev, rm);
	platform_device = to_platform_device(mtk_dmx2dev->dev);

	dvb_frontend = atsc3_get_dvb_frontend(ATSC3_AUSTIN_EXTERNAL_DEMOD_ID);
	if (MTK_DMX_VERIFY(dvb_frontend == NULL))
		return 0;
	if (MTK_DMX_VERIFY(
		cfm_get_demod_property(
			dvb_frontend,
			&cfm_properties)))
		return 0;

	for (i = 0; i < ARRAY_SIZE(_alp_priv_packet_ops); i++) {
		if (snprintf(
			alp_priv_packet_custom,
			sizeof(alp_priv_packet_custom),
			"custom_%d",
			i) >= sizeof(alp_priv_packet_custom)) {
			MTK_DMX_ERR(
				"Fail to build string custom_%d\n",
				i);

			continue;
		}
		tmp_ret = atsc3_custom_read(
				platform_device,
				alp_priv_packet_custom,
				&custom);
		if (tmp_ret != 0) {
			MTK_DMX_DEBUG(
				"atsc3_custom_read(..., %s, ...) is failed\n",
				alp_priv_packet_custom);

			continue;
		}
		if (strncmp(
			cfm_property.u.buffer.data,
			custom,
			strlen(custom)) == 0) {
			MTK_DMX_INFO(
				"Use %s's operations\n",
				alp_priv_packet_custom);

			priv->is_austin = true;
			mtk_dmx_alp_set_key_words(demux_alp_instance, &stKeyWords);
			break;
		}
	}

	return 0;
}

static int atsc3_dmx_get_priv_packet_operations(
		struct dmx2_filter *dmx2_filter,
		struct alp_priv_packet_operations **alp_priv_packet_ops)
{
	int tmp_ret = 0;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dmx_rm *mtk_dmx_rm = NULL;
	struct mtk_dmx2dev *mtk_dmx2dev = NULL;
	struct platform_device *platform_device = NULL;
	struct dvb_frontend *dvb_frontend = NULL;
	struct cfm_property cfm_property = {
		.cmd = CFM_DECOUPLE_DMD,
	};
	struct cfm_properties cfm_properties = {
		.num = ATSC3_CFM_PROPERTYIES_NUM,
		.props = &cfm_property,
	};
	char alp_priv_packet_custom[ATSC3_ALP_PRIV_PACKET_CUSTOMS_PROPNAME_MAX] = { 0 };
	const char *custom = NULL;
	int i = 0;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_demux2 = atsc3_get_mtk_demux2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return -EINVAL;
	mtk_dmx_rm = mtk_demux2->rm;
	if (MTK_DMX_VERIFY(mtk_dmx_rm == NULL))
		return -EINVAL;
	mtk_dmx2dev = container_of(mtk_dmx_rm, struct mtk_dmx2dev, rm);
	platform_device = to_platform_device(mtk_dmx2dev->dev);

	// Use custom_0's operations as default even though frontend is not available
	*alp_priv_packet_ops =
		&_alp_priv_packet_ops[ATSC3_ALP_PRIV_PACKET_OPERATIONS_DEFAULT];

	// TODO
	dvb_frontend = atsc3_get_dvb_frontend(ATSC3_EXTERNAL_DEMOD_ID);
	if (dvb_frontend == NULL)
		return 0;
	if (MTK_DMX_VERIFY(
		cfm_get_demod_property(
			dvb_frontend,
			&cfm_properties)))
		return 0;

	for (i = 0; i < ARRAY_SIZE(_alp_priv_packet_ops); i++) {
		if (snprintf(
			alp_priv_packet_custom,
			sizeof(alp_priv_packet_custom),
			"custom_%d",
			i) >= sizeof(alp_priv_packet_custom)) {
			MTK_DMX_ERR(
				"Fail to build string custom_%d\n",
				i);

			continue;
		}
		tmp_ret = atsc3_custom_read(
				platform_device,
				alp_priv_packet_custom,
				&custom);
		if (tmp_ret != 0) {
			MTK_DMX_DEBUG(
				"atsc3_custom_read(..., %s, ...) is failed\n",
				alp_priv_packet_custom);

			continue;
		}
		if (strncmp(
			cfm_property.u.buffer.data,
			custom,
			strlen(custom)) == 0) {
			MTK_DMX_DEBUG(
				"Use %s's operations\n",
				alp_priv_packet_custom);

			*alp_priv_packet_ops =
				&_alp_priv_packet_ops[i];

			break;
		}
	}

	return 0;
}

static int atsc3_mdrv_atsc30_sw_parser_init(
		demux_alp_instance_e demux_alp_instance,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	u8 plp_num = (MAX_PLP_NUMBER_SUPPORTED + 1);
	u8 plp_ids[MAX_PLP_NUMBER_SUPPORTED] = { MAX_PLP_NUM };
	alp_stream_t alp_stream = { 0 };
	alp_stream_ID alp_stream_id = INVALID_ALP_STREAM_ID;
	int i = 0;

	if (!demux_alp_filter->stPlp.bSinglePlpMode) {
		if (!mdrv_atsc30_if_set_plp_id_filter(
			demux_alp_instance,
			&demux_alp_filter->stPlp.u8PlpId,
			1)) {
			MTK_DMX_ERR(
				"mdrv_atsc30_if_set_plp_id_filter is failed\n");

			return -EIO;
		}
	}

	plp_num = mdrv_atsc30_if_get_plp_num(demux_alp_instance);
	if (plp_num > MAX_PLP_NUMBER_SUPPORTED) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_get_plp_num is failed\n");

		return -EIO;
	}
	if (demux_alp_filter->stPlp.bSinglePlpMode
		&& (plp_num > 1)) {
		MTK_DMX_WARN(
			"plp_num(%d) > 1, but bSinglePlpMode is set\n",
			plp_num);

		return -EINVAL;
	}

	if (!demux_alp_filter->stPlp.bSinglePlpMode) {
		if (!mdrv_atsc30_if_get_plp_id(
			demux_alp_instance,
			plp_ids,
			MAX_PLP_NUMBER_SUPPORTED)) {
			MTK_DMX_ERR(
				"mdrv_atsc30_if_get_plp_id is failed\n");

			return -EIO;
		}

		for (i = 0; i < MAX_PLP_NUMBER_SUPPORTED; i++)
			if (plp_ids[i] == demux_alp_filter->stPlp.u8PlpId) {
				demux_alp_filter->u8FilterId = i;

				break;
			}
	}

	alp_stream.state = ALP_STRM_ENABLE;
	alp_stream.ignore_plp_id = demux_alp_filter->stPlp.bSinglePlpMode;
	alp_stream.plp_id = (demux_alp_filter->stPlp.bSinglePlpMode)
				 ? MAX_PLP_NUM
				 : demux_alp_filter->stPlp.u8PlpId;
	alp_stream.compress_flag = demux_alp_filter->stCid.bIsCid;
	alp_stream.context_id = demux_alp_filter->stCid.u8Cid;
	alp_stream.SID_flag = demux_alp_filter->stSid.bIsSid;
	alp_stream.SID = demux_alp_filter->stSid.u8Sid;
	// TODO
	alp_stream.tag_id = 0;
	if (demux_alp_filter->u8IpVersion == DEMUX_ALP_IP_V4) {
		alp_stream.src_port = demux_alp_filter->stSrcPort.uPort.u16Port;
		alp_stream.dst_port = demux_alp_filter->stDesPort.uPort.u16Port;
		alp_stream.src_addr = demux_alp_filter->stSrcAddr.uAddr.u32Addr;
		alp_stream.dst_addr = demux_alp_filter->stDesAddr.uAddr.u32Addr;
		alp_stream.src_port_mask = demux_alp_filter->stSrcPort.uMask.u16Mask;
		alp_stream.dst_port_mask = demux_alp_filter->stDesPort.uMask.u16Mask;
		alp_stream.src_addr_mask = demux_alp_filter->stSrcAddr.uMask.u32Mask;
		alp_stream.dst_addr_mask = demux_alp_filter->stDesAddr.uMask.u32Mask;
	} else {
		// TODO: DEMUX_ALP_IP_V6
	}

	alp_stream_id = mdrv_atsc30_if_set_stream(
				demux_alp_instance,
				&alp_stream);
	if (alp_stream_id == INVALID_ALP_STREAM_ID) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_set_stream is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_mdrv_atsc30_sw_parser_exit(
		demux_alp_instance_e demux_alp_instance,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	u8 plp_num = (MAX_PLP_NUMBER_SUPPORTED + 1);
	alp_stream_t alp_stream = { 0 };
	alp_stream_ID alp_stream_id = INVALID_ALP_STREAM_ID;

	plp_num = mdrv_atsc30_if_get_plp_num(demux_alp_instance);
	if (plp_num > MAX_PLP_NUMBER_SUPPORTED) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_get_plp_num is failed\n");

		return -EIO;
	}
	if (demux_alp_filter->stPlp.bSinglePlpMode
		&& (plp_num > 1)) {
		MTK_DMX_WARN(
			"plp_num(%d) > 1, but bSinglePlpMode is set\n",
			plp_num);

		demux_alp_filter->stPlp.bSinglePlpMode = (plp_num <= 1);
	}

	alp_stream.state = ALP_STRM_INVALID;
	alp_stream.ignore_plp_id = demux_alp_filter->stPlp.bSinglePlpMode;
	alp_stream.plp_id = (demux_alp_filter->stPlp.bSinglePlpMode)
				 ? MAX_PLP_NUM
				 : demux_alp_filter->stPlp.u8PlpId;
	alp_stream.compress_flag = demux_alp_filter->stCid.bIsCid;
	alp_stream.context_id = demux_alp_filter->stCid.u8Cid;
	alp_stream.SID_flag = demux_alp_filter->stSid.bIsSid;
	alp_stream.SID = demux_alp_filter->stSid.u8Sid;
	// TODO
	alp_stream.tag_id = 0;
	if (demux_alp_filter->u8IpVersion == DEMUX_ALP_IP_V4) {
		alp_stream.src_port = demux_alp_filter->stSrcPort.uPort.u16Port;
		alp_stream.dst_port = demux_alp_filter->stDesPort.uPort.u16Port;
		alp_stream.src_addr = demux_alp_filter->stSrcAddr.uAddr.u32Addr;
		alp_stream.dst_addr = demux_alp_filter->stDesAddr.uAddr.u32Addr;
		alp_stream.src_port_mask = demux_alp_filter->stSrcPort.uMask.u16Mask;
		alp_stream.dst_port_mask = demux_alp_filter->stDesPort.uMask.u16Mask;
		alp_stream.src_addr_mask = demux_alp_filter->stSrcAddr.uMask.u32Mask;
		alp_stream.dst_addr_mask = demux_alp_filter->stDesAddr.uMask.u32Mask;
	} else {
		// TODO: DEMUX_ALP_IP_V6
	}

	alp_stream_id = mdrv_atsc30_if_set_stream(
				demux_alp_instance,
				&alp_stream);
	if (alp_stream_id == INVALID_ALP_STREAM_ID) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_set_stream is failed\n");

		return -EIO;
	}

	if (!demux_alp_filter->stPlp.bSinglePlpMode) {
		if (!mdrv_atsc30_if_clear_plp_id_filter(
			demux_alp_instance,
			&demux_alp_filter->stPlp.u8PlpId,
			1)) {
			MTK_DMX_ERR(
				"mdrv_atsc30_if_clear_plp_id_filter is failed\n");

			return -EIO;
		}
	}

	return ret;
}

static int atsc3_mdrv_atsc30_init_pvr(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	int ret_tmp;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct mdrv_atsc30_if_context *atsc30_if_context = NULL;
	struct record_task_info default_record_task_info = {
		.policy = ATSC3_PVR_POLICY,
		.priority = ATSC3_PVR_PRIORITY,
		.wait_times = {
			ATSC3_PVR_WAIT_TIME1,
			ATSC3_PVR_WAIT_TIME2,
		},
		.finalize_callback = NULL,
		.finalize_timeout = ATSC3_PVR_FINALIZE_TIMEOUT,
	};

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	if (MTK_DMX_VERIFY(demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	// TODO
	buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_OUT];
	if (MTK_DMX_VERIFY(buffer_ctx == NULL))
		return -EINVAL;
	bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buffer_ctx);

	ret_tmp = mdrv_atsc30_if_get_context(
			demux_alp_instance,
			&atsc30_if_context);
	if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_get_context(%d) is failed\n",
			demux_alp_instance);

		return -EINVAL;
	}

	if (dvb_dvr2->state < DVR2_STATE_GO) {
		atsc30_if_context->bc_ctx = bc_ctx;
		atsc30_if_context->bTaskExit = false;
		dvb_dvr2->callback = atsc30_if_context->rec_task_callback;
		default_record_task_info.finalize_callback =
			atsc30_if_context->rec_task_finalize_callback;
		memcpy(&mtk_dvr2->record_task_info,
			&default_record_task_info,
			sizeof(default_record_task_info));
	}

	return ret;
}

static int atsc3_mdrv_atsc30_init(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct mem_info mem_info = { 0 };
	struct mem_info mem_info_austin = { 0 };
	ring_buf_init_config_t payload_ring_buf_config = { 0 };
	ring_buf_init_config_t austin_preparser_buf_config = { 0 };
	struct alp_priv_packet_operations *alp_priv_packet_ops = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	if (MTK_DMX_VERIFY(demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	// TODO
	buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_OUT];
	if (MTK_DMX_VERIFY(buffer_ctx == NULL))
		return -EINVAL;
	bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buffer_ctx);
	ret = mtk_bc_get_mem_info(bc_ctx, MEM_INFO_TYPE_MIN, &mem_info);
	if (ret != 0) {
		MTK_DMX_ERR("mtk_bc_get_mem_info is failed\n");

		return -EINVAL;
	}
	if (MTK_DMX_VERIFY(mem_info.size == 0))
		return -EINVAL;

	mtk_atsc3_dvr_private = dvb_dvr2->priv;
	ret = mtk_bc_get_mem_info(&(mtk_atsc3_dvr_private->bc_ctx_austin), MEM_INFO_TYPE_MIN, &mem_info_austin);
	if (ret != 0) {
		MTK_DMX_ERR("mtk_bc_get_mem_info_austin is failed\n");
		return -EINVAL;
	}

	do {
		payload_ring_buf_config.start_addr = (uintptr_t) mem_info.vaddr;
		payload_ring_buf_config.size = mem_info.size;
		austin_preparser_buf_config.start_addr = (uintptr_t) mem_info_austin.vaddr;
		austin_preparser_buf_config.size = mem_info_austin.size;

		ret_tmp = atsc3_dmx_get_priv_packet_operations(
				dmx2_filter,
				&alp_priv_packet_ops);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_get_priv_packet_operations is failed\n");

			ret = -EIO;
			break;
		}

		if (mtk_atsc3_dvr_private->is_austin) {
			if (MTK_DMX_VERIFY(mem_info_austin.size == 0)) {
				ret = -EINVAL;
				break;
			}

			alp_priv_packet_ops =
				&_alp_priv_packet_ops[ATSC3_ALP_PRIV_PACKET_OPERATIONS_AUSTIN];
			alp_priv_packet_ops->is_austin = true;
			ret = atsc3_get_stc_frequency(
				dmx2_filter,
				STC_CW_IS_COUNTER_300_ENABLED,
				&alp_priv_packet_ops->stc_frequency);
			if (ret != 0) {
				MTK_DMX_ERR("atsc3_get_stc_frequency is failed\n");

				ret = -EIO;
				break;
			}
		}

		ret_tmp = mdrv_atsc30_if_init(
				demux_alp_instance,
				payload_ring_buf_config,
				austin_preparser_buf_config,
				alp_priv_packet_ops);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"mdrv_atsc30_if_init(%d, ...) is failed\n",
				demux_alp_instance);

			ret = -EIO;
			break;
		}

		ret = atsc3_mdrv_atsc30_init_pvr(mtk_dvr2);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_init_pvr(...) of %d is failed\n",
				demux_alp_instance);

			break;
		}
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_mdrv_atsc30_exit_pvr(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	int ret_tmp;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mdrv_atsc30_if_context *atsc30_if_context = NULL;

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	if (MTK_DMX_VERIFY(demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	ret_tmp = mdrv_atsc30_if_get_context(
			demux_alp_instance,
			&atsc30_if_context);
	if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
		MTK_DMX_ERR(
			"mdrv_atsc30_if_get_context(%d) is failed\n",
			demux_alp_instance);

		return -EINVAL;
	}

	if (dvb_dvr2->state < DVR2_STATE_GO) {
		atsc30_if_context->bc_ctx = NULL;
		dvb_dvr2->callback = NULL;
		mtk_dvr2->record_task_info.finalize_callback = NULL;
		mtk_dvr2->record_task_info.finalize_timeout = 0;
	}

	return ret;
}

static int atsc3_mdrv_atsc30_exit(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;

	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	if (MTK_DMX_VERIFY(demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	do {
		ret = atsc3_mdrv_atsc30_exit_pvr(mtk_dvr2);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_exit_pvr(...) of %d is failed\n",
				demux_alp_instance);

			break;
		}

		ret_tmp = mdrv_atsc30_if_exit(demux_alp_instance);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"mdrv_atsc30_if_exit(%d, ...) is failed\n",
				demux_alp_instance);

			ret = -EIO;
			break;
		}
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

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

	// TODO:
	if (MTK_DMX_VERIFY(caps == NULL))
		return ret;

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

	return ret;
}

static void atsc3_ref_count_reset(
		int index,
		struct mtk_atsc3_ref_count_info *ref_count_info)
{
	if (MTK_DMX_VERIFY(ref_count_info == NULL))
		return;
	if (MTK_DMX_VERIFY(index < 0))
		return;
	if (MTK_DMX_VERIFY(
		index >= ref_count_info->total_num))
		return;

	ref_count_info->ref_count[index] = 1;
}

static void atsc3_ref_count_increase(
		int index,
		struct mtk_atsc3_ref_count_info *ref_count_info)
{
	if (MTK_DMX_VERIFY(ref_count_info == NULL))
		return;
	if (MTK_DMX_VERIFY(index < 0))
		return;
	if (MTK_DMX_VERIFY(
		index >= ref_count_info->total_num))
		return;

	ref_count_info->ref_count[index]++;
}

static void atsc3_ref_count_decrease(
		int index,
		struct mtk_atsc3_ref_count_info *ref_count_info)
{
	if (MTK_DMX_VERIFY(ref_count_info == NULL))
		return;
	if (MTK_DMX_VERIFY(index < 0))
		return;
	if (MTK_DMX_VERIFY(
		index >= ref_count_info->total_num))
		return;

	if (ref_count_info->ref_count[index] > 0)
		ref_count_info->ref_count[index]--;
}

static int atsc3_ref_count_get(
		int index,
		struct mtk_atsc3_ref_count_info *ref_count_info)
{
	if (MTK_DMX_VERIFY(ref_count_info == NULL))
		return 0;
	if (MTK_DMX_VERIFY(index < 0))
		return 0;
	if (MTK_DMX_VERIFY(
		index >= ref_count_info->total_num))
		return 0;

	return ref_count_info->ref_count[index];
}

static int atsc3_pvr_engine_init(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct mtk_dvr2_fh_ctx *ctx = NULL;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	struct mtk_dmx2dev *mtk_dmx2dev = NULL;
	struct platform_device *pdev = NULL;
	struct device_node *np = NULL, *np1= NULL, *np2 = NULL;
	u32 size;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	mtk_atsc3_dvr_private = (struct mtk_atsc3_dvr_private *)dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return -EINVAL;

	if (dvb_dvr2->state >= DVR2_STATE_ALLOCATED) {
		buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_OUT];
		if (MTK_DMX_VERIFY(buffer_ctx != NULL)) {
			MTK_DMX_DEBUG("DVR for path %d is already opened\n", mtk_dvr2->path_id);
			atsc3_ref_count_increase(
				dvb_dvr2->index,
				&mtk_atsc3_dvr_private->pvr_info);
			return ret;
		}
	}

	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx) {
		MTK_DMX_ERR("allocate ctx fail\n");

		return -EIO;
	}


	mtk_dmx2dev = container_of(mtk_dvr2->rm, struct mtk_dmx2dev, rm);
	pdev = to_platform_device(mtk_dmx2dev->dev);

	of_node_get(pdev->dev.of_node);
	np = of_find_node_by_name(
			pdev->dev.of_node,
			ATSC3_ALP_PRIV_DTS);
	if (np) {
		np1 = of_find_node_by_name(
			pdev->dev.of_node,
			ATSC3_ALP_PRIV_IPS);
		if (np1) {
			np2 = of_find_node_by_name(
				pdev->dev.of_node,
				ATSC3_ALP_PRIV_PACKET_HW_BUF);
			if (!np2) {
				ret = -ENOENT;
				goto FAIL;
			}
		} else {
			ret = -ENOENT;
			goto FAIL;
		}
	} else {
		ret = -ENOENT;
		goto FAIL;
	}

	ret = of_property_read_u32(np2, "size", &size);
	if (ret) {
		MTK_DMX_ERR("fail to get property\n");
		ret = -ENOENT;
		goto FAIL;
	}

	ret = atsc3_pvr_rec_config_init(mtk_dvr2);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_pvr_rec_config_init(...) is failed\n");

		ret = -EIO;
		goto FAIL;
	}

	ret = mtk_dvr2_fh_ctx_init(ctx, mtk_dvr2, DVR_DATA_FROM_DEVICE, size);
	if (ret != 0) {
		MTK_DMX_ERR("mtk_dvr2_fh_ctx_init is failed\n");

		ret = -EIO;
		goto FAIL;
	}

FAIL:
	if ((ret < 0) && (ctx != 0))
		kfree(ctx);
	of_node_put(np2);
	of_node_put(np1);
	of_node_put(np);
	return ret;
}

static int atsc3_pvr_engine_exit(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct mtk_dvr2_fh_ctx *ctx = NULL;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct dvb_fh *fh = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	int pvr_ref_count = 0;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	mtk_atsc3_dvr_private = (struct mtk_atsc3_dvr_private *)dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return -EINVAL;
	fh = dvb_dvr2->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dvrctx(fh);

	atsc3_ref_count_decrease(
		dvb_dvr2->index,
		&mtk_atsc3_dvr_private->pvr_info);
	pvr_ref_count =
		atsc3_ref_count_get(
			dvb_dvr2->index,
			&mtk_atsc3_dvr_private->pvr_info);
	if (pvr_ref_count > 0) {
		MTK_DMX_DEBUG("pvr_ref_count = %d\n", pvr_ref_count);

		return ret;
	}

	ret = mtk_dvr2_fh_ctx_exit(ctx);
	if (ret != 0)
		MTK_DMX_ERR("mtk_dvr2_fh_ctx_exit is failed\n");

	kfree(ctx);

	return ret;
}

static int atsc3_pvr_engine_start(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	mtk_atsc3_dvr_private = (struct mtk_atsc3_dvr_private *)dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return -EINVAL;

	if (dvb_dvr2->state >= DVR2_STATE_GO) {
		MTK_DMX_DEBUG("DVR for path %d is already started\n", mtk_dvr2->path_id);

		return ret;
	}

	ret = mtk_dvr2_start(mtk_dvr2);
	if (ret < 0) {
		MTK_DMX_ERR("mtk_dvr2_start is failed\n");

		return -EIO;
	}

	atsc3_ref_count_reset(
		dvb_dvr2->index,
		&mtk_atsc3_dvr_private->pvr_info);

	MTK_DMX_DEBUG(
		"dvb_dvr2->index %d timestamp 0x%llx\n",
		dvb_dvr2->index,
		mtk_pvr_get_timestamp(dvb_dvr2->index));

	return ret;
}

static int atsc3_pvr_engine_stop(struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	int pvr_ref_count = 0;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	struct mdrv_atsc30_if_context *atsc30_if_context = NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dvr2 = atsc3_get_mtk_dvr2(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	mtk_atsc3_dvr_private = (struct mtk_atsc3_dvr_private *)dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return -EINVAL;
	pvr_ref_count =
		atsc3_ref_count_get(
			dvb_dvr2->index,
			&mtk_atsc3_dvr_private->pvr_info);
	if (pvr_ref_count > 1) {
		MTK_DMX_DEBUG("pvr_ref_count = %d\n", pvr_ref_count);

		return ret;
	}

	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	ret = mdrv_atsc30_if_get_context(demux_alp_instance, &atsc30_if_context);
	if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
		ATSC30_DRVIF_LOG_ENHANCED_ERROR(
			"mdrv_atsc30_if_get_context(%d, ...) is failed\n",
			demux_alp_instance);
		return ret;
	}
	atsc30_if_context->bTaskExit = true;

	ret_tmp = mtk_dvr2_flush(mtk_dvr2);
	if (ret_tmp < 0) {
		// Don't return even though it is failed to flush.
		MTK_DMX_ERR("mtk_dvr2_flush is failed\n");

		ret = -EIO;
	}
	ret_tmp = mtk_dvr2_stop(mtk_dvr2);
	if (ret_tmp < 0) {
		// Don't return even though it is failed to stop.
		MTK_DMX_ERR("mtk_dvr2_stop is failed\n");

		ret = -EIO;
	}

	return ret;
}

static int atsc3_dmx_alp_filter_get(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	int tmp_ret;
	struct dmx2_filter *dmx2_filter = NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(demux_alp_filter == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	do {
		memset(demux_alp_filter, 0, sizeof(demux_alp_filter_t));
		if (cast_to_dmx2_feed(mtk_atsc3_feed)->index < 0) {
			// TODO: reuse the same IP filter
			demux_alp_filter->s8Index =
				atsc3_rm_get_filter_idx(
					dmx2_filter,
					filter_type);
			if (demux_alp_filter->s8Index < 0) {
				MTK_DMX_ERR("no free ALP filter\n");

				ret = -EIO;
				break;
			}
			cast_to_dmx2_feed(mtk_atsc3_feed)->index =
				demux_alp_filter->s8Index;
		} else {
			demux_alp_filter->s8Index =
				cast_to_dmx2_feed(mtk_atsc3_feed)->index;
		}

		ret = atsc3_dmx_alp_get_filter(
				dmx2_filter,
				demux_alp_filter);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_get_filter is failed\n");

			tmp_ret = atsc3_dmx_alp_filter_put(
					mtk_atsc3_feed,
					filter_type,
					demux_alp_filter);
			if (tmp_ret != 0)
				MTK_DMX_ERR(
					"atsc3_dmx_alp_filter_put is failed\n");

			break;
		}
	} while (0);

	return ret;
}

static int atsc3_dmx_alp_filter_put(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	int refcnt = 0;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(demux_alp_filter == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	do {
		ret = atsc3_rm_put_filter_idx(
				dmx2_filter,
				filter_type,
				demux_alp_filter->s8Index);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_put_filter_idx is failed\n");

			break;
		}

		// TODO: refcnt
		if (refcnt == 0) {
			ret = atsc3_dmx_alp_clear_filter(
					dmx2_filter,
					demux_alp_filter);
			if (ret != 0) {
				MTK_DMX_ERR(
					"atsc3_dmx_alp_clear_filter is failed\n");

				break;
			}
			cast_to_dmx2_feed(mtk_atsc3_feed)->index = FILTER_IDX_INVALID;
		}
	} while (0);

	return ret;
}

static int atsc3_dmx_alp_metadata_init(
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (dmx2_filter->params.output != DMX2_OUT_TAP) {
		MTK_DMX_DEBUG(
			"dmx2_filter->params.output %d\n",
			dmx2_filter->params.output);
		MTK_DMX_DEBUG(
			"No need to use metadata\n");

		return 0;
	}
	if (MTK_DMX_VERIFY(
		atsc3_dmx_get_bc_ctx(
			dmx2_filter,
			&bc_ctx) != 0))
		return -EINVAL;

	ret = mtk_bc_metabuf_init(
			bc_ctx,
			sizeof(struct dmx2_ip_payload_metadata),
			ATSC3_META_COUNT);
	if (ret != 0) {
		MTK_DMX_ERR(
			"mtk_bc_metabuf_init(..., %d) is failed\n",
			ATSC3_META_COUNT);

		return -ENOMEM;
	}

	return 0;
}

static int atsc3_dmx_alp_metadata_exit(
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (dmx2_filter->params.output != DMX2_OUT_TAP) {
		MTK_DMX_DEBUG(
			"dmx2_filter->params.output %d\n",
			dmx2_filter->params.output);
		MTK_DMX_DEBUG(
			"No need to use metadata\n");

		return 0;
	}
	if (MTK_DMX_VERIFY(
		atsc3_dmx_get_bc_ctx(
			dmx2_filter,
			&bc_ctx) != 0))
		return -EINVAL;

	ret = mtk_bc_metabuf_release(
			bc_ctx);
	if (ret != 0) {
		MTK_DMX_ERR(
			"mtk_bc_metabuf_release(...) is failed\n");

		return -ENOMEM;
	}

	return 0;
}

static int atsc3_dmx_alp_filter_start(
		dmx_alp_set_alp_filter_callback set_alp_filter_cb,
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	demux_alp_filter_t demux_alp_filter = { 0 };

	if (MTK_DMX_VERIFY(set_alp_filter_cb == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	do {
		ret = atsc3_dmx_alp_filter_get(
			mtk_atsc3_feed,
			filter_type,
			&demux_alp_filter);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_get is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE1;
		}
		mtk_dmx_alp_print_filter(&demux_alp_filter);

		ret = (*set_alp_filter_cb)(
				dmx2_filter->params,
				&demux_alp_filter);
		if (ret != 0) {
			MTK_DMX_ERR(
				"set_alp_filter_cb is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE1;
		}

		mtk_dmx_alp_print_filter(&demux_alp_filter);

		ret = atsc3_pvr_engine_init(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_init(...) is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE2;
		}

		ret = atsc3_mdrv_atsc30_init(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_init(...) is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE3;
		}

		ret = atsc3_mdrv_atsc30_sw_parser_init(
				demux_alp_instance,
				&demux_alp_filter);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_sw_parser_init is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE4;
		}

		ret = mdrv_atsc30_if_add_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_filter_add(%d, ...) is failed\n",
				demux_alp_instance);

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE5;
		}

		ret = atsc3_pvr_engine_start(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_start is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE6;
		}

		if (mtk_dmx_alp_set_filter(
			demux_alp_instance,
			&demux_alp_filter) != 0) {
			MTK_DMX_ERR(
				"mtk_dmx_alp_set_filter is failed\n");

			ret = -EIO;
			goto ATSC3_ALP_START_STAGE6;
		}
		mtk_dmx_log_alp_graph_print(demux_alp_instance);
	} while (0);

	if (ret != 0) {
		// Returned values are not handled in the recovery process.
ATSC3_ALP_START_STAGE6:
		ret_tmp = atsc3_pvr_engine_stop(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_pvr_engine_stop is failed\n");
		}

ATSC3_ALP_START_STAGE5:
		ret_tmp = mdrv_atsc30_if_del_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_DEBUG(
				"mdrv_atsc30_if_del_filter is failed\n");
		}

ATSC3_ALP_START_STAGE4:
		ret_tmp = atsc3_mdrv_atsc30_sw_parser_exit(
				demux_alp_instance,
				&demux_alp_filter);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_mdrv_atsc30_sw_parser_exit is failed\n");
		}

ATSC3_ALP_START_STAGE3:
		ret_tmp = atsc3_mdrv_atsc30_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_mdrv_atsc30_exit is failed\n");
		}

ATSC3_ALP_START_STAGE2:
		ret_tmp = atsc3_pvr_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_pvr_engine_exit is failed\n");
		}

ATSC3_ALP_START_STAGE1:
		ret_tmp = atsc3_dmx_alp_filter_put(
					mtk_atsc3_feed,
					filter_type,
					&demux_alp_filter);
		if (ret_tmp != 0)
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_put is failed\n");
	}

	return ret;
}

static int atsc3_dmx_alp_filter_stop(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	demux_alp_filter_t demux_alp_filter = { 0 };

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	memset(&demux_alp_filter, 0, sizeof(demux_alp_filter_t));
	if (MTK_DMX_VERIFY(cast_to_dmx2_feed(mtk_atsc3_feed)->index < 0))
		return -EINVAL;
	demux_alp_filter.s8Index = cast_to_dmx2_feed(mtk_atsc3_feed)->index;
	if (mtk_dmx_alp_get_filter(
		demux_alp_instance,
		&demux_alp_filter) != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_get_filter is failed\n");

		return -EIO;
	}

	do {
		ret_tmp = atsc3_dmx_alp_filter_put(
				mtk_atsc3_feed,
				filter_type,
				&demux_alp_filter);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_put is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_pvr_engine_stop(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_stop is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = mdrv_atsc30_if_del_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_filter_del is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_mdrv_atsc30_sw_parser_exit(
				demux_alp_instance,
				&demux_alp_filter);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_sw_parser_exit is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_mdrv_atsc30_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_exit is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_pvr_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_exit is failed\n");

			ret = -EIO;
			break;
		}
	} while (0);

	return ret;
}

static int atsc3_dmx_alp_set_alp_filter_for_signaling(
		struct dmx2_filter_params dmx2_filter_params,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	struct dmx2_alp_filter_params *dmx2_alp_filter_params = NULL;

	dmx2_alp_filter_params = &dmx2_filter_params.alp;

	demux_alp_filter->stPlp.bIsPlp = false; // depreciated
	demux_alp_filter->stPlp.bSinglePlpMode =
		(dmx2_alp_filter_params->flags
			& DMX2_PLP_FILTER_EN) == 0;
	demux_alp_filter->stPlp.u8PlpId = dmx2_alp_filter_params->plp_id;

	demux_alp_filter->stSid.bIsSid =
		(dmx2_alp_filter_params->flags & DMX2_SID_FILTER_EN) != 0;
	demux_alp_filter->stSid.u8Sid = dmx2_alp_filter_params->sid;

	return ret;
}

static int atsc3_ptp_filter_get(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	int index = -1;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	do {
		if (cast_to_dmx2_feed(mtk_atsc3_feed)->index < 0) {
			index = atsc3_rm_get_filter_idx(
					dmx2_filter,
					filter_type);
			if (index < 0) {
				MTK_DMX_ERR("no free PTP filter\n");

				ret = -EIO;
				break;
			}
			cast_to_dmx2_feed(mtk_atsc3_feed)->index = index;
		} else {
			MTK_DMX_DEBUG(
				"existed index %d\n",
				cast_to_dmx2_feed(mtk_atsc3_feed)->index);
		}
	} while (0);

	return ret;
}

static int atsc3_ptp_filter_put(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	int refcnt = 0;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	do {
		ret = atsc3_rm_put_filter_idx(
				dmx2_filter,
				filter_type,
				cast_to_dmx2_feed(mtk_atsc3_feed)->index);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_put_filter_idx is failed\n");

			break;
		}

		// TODO: refcnt
		if (refcnt == 0)
			cast_to_dmx2_feed(mtk_atsc3_feed)->index = FILTER_IDX_INVALID;
	} while (0);

	return ret;
}

static int atsc3_get_stc_cw(
		struct dmx2_filter *dmx2_filter,
		u32 *stc_cw)
{
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_atsc3_dmx_private *private = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(stc_cw == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	mtk_demux2 = fh_to_demux(fh);
	private = (struct mtk_atsc3_dmx_private *)
			cast_to_dmx_parent(mtk_demux2)->priv;
	if (MTK_DMX_VERIFY(private == NULL))
		return -EINVAL;

	*stc_cw = (private->stc_cw)
			? private->stc_cw
			: STC_SYNTH_DEFAULT_CW;

	return 0;
}

static int atsc3_get_stc_bypass_3_2(
		struct dmx2_filter *dmx2_filter,
		u32 *stc_bypass_3_2)
{
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_atsc3_dmx_private *private = NULL;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(stc_bypass_3_2 == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	mtk_demux2 = fh_to_demux(fh);
	private = (struct mtk_atsc3_dmx_private *)
			cast_to_dmx_parent(mtk_demux2)->priv;
	if (MTK_DMX_VERIFY(private == NULL))
		return -EINVAL;

	*stc_bypass_3_2 = private->stc_bypass_3_2;

	return 0;
}

static int atsc3_get_stc_cw_info(
		struct dmx2_filter *dmx2_filter,
		struct stc_cw_info *stc_cw_info)
{
	u32 stc_cw = 0;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(stc_cw_info == NULL))
		return -EINVAL;

	if (atsc3_get_stc_cw(dmx2_filter, &stc_cw) != 0) {
		MTK_DMX_ERR("atsc3_get_stc_cw is failed\n");

		return -EIO;
	}

	stc_cw_info->integer = STC_CW_GET_INTEGER(stc_cw);
	stc_cw_info->decimal = STC_CW_GET_DECIMAL(stc_cw);

	return 0;
}

static int atsc3_get_stc_frequency(
		struct dmx2_filter *dmx2_filter,
		bool is_counter_enabled,
		u64 *stc_frequency)
{
	struct mtk_dmx2dev *mtk_dmx2dev = NULL;
	u16 stc_ck_rate = 0;
	struct stc_cw_info stc_cw_info = { 0 };

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	mtk_dmx2dev = atsc3_get_mtk_dmx2dev(dmx2_filter);
	if (MTK_DMX_VERIFY(mtk_dmx2dev == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(stc_frequency == NULL))
		return -EINVAL;

	if (atsc3_get_stc_cw_info(dmx2_filter, &stc_cw_info) != 0) {
		MTK_DMX_ERR("atsc3_get_stc_cw_info is failed\n");

		return -EIO;
	}
	stc_ck_rate = mtk_dmx_rm_get_hwcap_by_id(
				&mtk_dmx2dev->rm,
				HWCAPS_TYPE_CK_RATE_STC);
	if (stc_ck_rate == 0) {
		MTK_DMX_ERR("mtk_dmx_rm_get_hwcap_by_id is failed\n");

		return -EIO;
	}
	// TODO: stc_cw_info.decimal
	*stc_frequency =
		div_u64(((u64) stc_ck_rate) *
			MHZ *
			STC_CW_CONSTANT_MULTIPLIER,
			stc_cw_info.integer *
			STC_CW_CONSTANT_DIVISOR *
			(is_counter_enabled ? STC_CW_CONSTANT_COUNTER : 1));
	MTK_DMX_DEBUG(
		"stc_ck_rate %u stc_cw_info.integer %u stc_cw_info.decimal %u\n",
		stc_ck_rate,
		stc_cw_info.integer,
		stc_cw_info.decimal);
	MTK_DMX_DEBUG(
		"stc_frequency 0x%llu\n",
		*stc_frequency);

	return 0;
}

static int atsc3_stc_engine_init(
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct alp_priv_packet_operations *alp_priv_packet_ops = NULL;
	s32 eng_id;
	struct stc_info stc_info = { 0 };
	u32 stc_cw = STC_SYNTH_DEFAULT_CW;
	u32 stc_bypass_3_2 = 0;
	bool bypass_3_2 = false;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	mtk_demux2 = fh_to_demux(fh);

	ret = atsc3_dmx_get_priv_packet_operations(
			dmx2_filter,
			&alp_priv_packet_ops);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_get_priv_packet_operations is failed\n");

		return -EIO;
	}
	ret = atsc3_get_stc_frequency(
		dmx2_filter,
		STC_CW_IS_COUNTER_300_ENABLED,
		&alp_priv_packet_ops->stc_frequency);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_get_stc_frequency is failed\n");

		return -EIO;
	}

	// ref_count is supported by STC
	eng_id = mtk_dmx_rm_stc_alloc_engine(mtk_demux2->rm, mtk_demux2->path_id, 0);
	if (eng_id < 0) {
		MTK_DMX_ERR("mtk_dmx_rm_stc_alloc_engine is failed\n");

		return -EIO;
	}
	ret = atsc3_get_stc_cw(dmx2_filter, &stc_cw);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_get_stc_cw is failed\n");

		return -EIO;
	}
	ret = atsc3_get_stc_bypass_3_2(dmx2_filter, &stc_bypass_3_2);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_get_stc_bypass_3_2 is failed\n");

		return -EIO;
	}
	bypass_3_2 = (stc_bypass_3_2 != 0);

	mtk_dmx_stc_set_mode(
		eng_id,
		STC_CW_IS_COUNTER_300_ENABLED
			? STC_MODE_TS	// divided by 300
			: STC_MODE_TLV);// not divided by 300
	mtk_dmx_stc_bypass_3_2(eng_id, bypass_3_2);
	mtk_dmx_stc_cw(eng_id, &stc_cw, bypass_3_2, true);

	stc_info.is_filter_use_stc = true;
	stc_info.eng_id = eng_id;

	ret = atsc3_dmx_set_stc_info(
			dmx2_filter,
			(void *) &stc_info);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_dmx_set_stc_info is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_stc_engine_exit(
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct dvb_fh *fh = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct stc_info stc_info = { 0 };

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	fh = dmx2_filter->fh;
	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	mtk_demux2 = fh_to_demux(fh);

	ret = atsc3_dmx_get_stc_info(
			dmx2_filter,
			(void *)&stc_info);
	if (ret != 0) {
		MTK_DMX_ERR("atsc3_dmx_get_stc_info is failed\n");

		return -EIO;
	}
	mtk_dmx_stc_set_mode(stc_info.eng_id, STC_MODE_TS);
	mtk_dmx_rm_stc_free_engine(mtk_demux2->rm, stc_info.eng_id);

	return ret;
}

// TODO: combine it w/ atsc3_dmx_alp_filter_start
static int atsc3_ptp_filter_start(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	do {
		ret = atsc3_ptp_filter_get(
				mtk_atsc3_feed,
				filter_type);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_get is failed\n");

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE1;
		}

		ret = atsc3_stc_engine_init(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_stc_engine_init(...) is failed\n");

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE2;
		}

		ret = atsc3_pvr_engine_init(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_init(...) is failed\n");

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE3;
		}

		ret = atsc3_mdrv_atsc30_init(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_init(...) is failed\n");

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE4;
		}

		ret = mdrv_atsc30_if_add_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_filter_add(%d, ...) is failed\n",
				demux_alp_instance);

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE5;
		}

		ret = atsc3_pvr_engine_start(mtk_atsc3_feed);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_start is failed\n");

			ret = -EIO;
			goto ATSC3_PTP_START_STAGE6;
		}
	} while (0);

	if (ret != 0) {
		// Returned values are not handled in the recovery process.
ATSC3_PTP_START_STAGE6:
		ret_tmp = atsc3_pvr_engine_stop(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_pvr_engine_stop is failed\n");
		}

ATSC3_PTP_START_STAGE5:
		ret_tmp = mdrv_atsc30_if_del_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_DEBUG(
				"mdrv_atsc30_if_del_filter is failed\n");
		}

ATSC3_PTP_START_STAGE4:
		ret_tmp = atsc3_mdrv_atsc30_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_mdrv_atsc30_exit is failed\n");
		}

ATSC3_PTP_START_STAGE3:
		ret_tmp = atsc3_pvr_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_pvr_engine_exit is failed\n");
		}

ATSC3_PTP_START_STAGE2:
		ret_tmp = atsc3_stc_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_stc_engine_exit is failed\n");
		}

ATSC3_PTP_START_STAGE1:
		ret_tmp = atsc3_ptp_filter_put(
				mtk_atsc3_feed,
				filter_type);
		if (ret_tmp != 0) {
			MTK_DMX_DEBUG(
				"atsc3_ptp_filter_put is failed\n");
		}
	}

	return ret;
}

// TODO: combine it w/ atsc3_dmx_alp_filter_stop
static int atsc3_ptp_filter_stop(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	int ret_tmp;
	struct dmx2_filter *dmx2_filter = NULL;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	demux_alp_instance = atsc3_dmx_alp_get_instance(dmx2_filter);
	if (MTK_DMX_VERIFY(
		demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	do {
		ret_tmp = atsc3_ptp_filter_put(
				mtk_atsc3_feed,
				filter_type);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_ptp_filter_put is failed\n");

			ret = -EIO;
			break;
		}
		ret_tmp = atsc3_pvr_engine_stop(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
					"atsc3_pvr_engine_stop is failed\n");

			ret = -EIO;
			break;
		}
		ret_tmp = mdrv_atsc30_if_del_filter(
				demux_alp_instance,
				dmx2_filter);
		if (ret_tmp != MDRV_ATSC30_RETURN_CODE_OK) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_filter_del is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_mdrv_atsc30_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_mdrv_atsc30_exit is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_pvr_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_engine_exit is failed\n");

			ret = -EIO;
			break;
		}

		ret_tmp = atsc3_stc_engine_exit(mtk_atsc3_feed);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_stc_engine_exit is failed\n");

			ret = -EIO;
			break;
		}
	} while (0);

	return ret;
}

static int atsc3_alp_filter_start_cb(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct alp_filter_cfg alp_filter_cfg;
	struct ptp_filter_cfg ptp_filter_cfg;
	struct stc_info stc_info = { 0 };
	void *filter_cfg = NULL;
	u32 size = 0;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

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

	switch (filter_type.sub) {
	case FILTER_SUB_TYPE_ALP:
		ret = atsc3_dmx_alp_filter_start(
				atsc3_dmx_alp_set_alp_filter_for_signaling,
				mtk_atsc3_feed,
				filter_type);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_start is failed\n");

			return -EIO;
		}
		filter_cfg = (void *) &alp_filter_cfg;
		size = sizeof(struct alp_filter_cfg);
		break;

	case FILTER_SUB_TYPE_PTP:
		ret = atsc3_ptp_filter_start(
				mtk_atsc3_feed,
				filter_type);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_ptp_filter_start is failed\n");

			return -EIO;
		}
		ptp_filter_cfg.pcr_filter_cfg.pcr_ctrl.reset_pcr = ATSC3_STC_RESET_RETRY;
		ptp_filter_cfg.pcr_filter_cfg.pcr_ctrl.prev_stc_base = 0;
		ptp_filter_cfg.pcr_filter_cfg.pcr_ctrl.prev_pll_ctrl = 0;

		ret = atsc3_dmx_get_stc_info(dmx2_filter, (void *)&stc_info);
		if (ret != 0) {
			MTK_DMX_ERR("atsc3_dmx_get_stc_info is failed\n");
			return -EIO;
		}
		ptp_filter_cfg.pcr_filter_cfg.cfg.dst_eng_id = stc_info.eng_id;

		filter_cfg = (void *) &ptp_filter_cfg;
		size = sizeof(struct ptp_filter_cfg);
		break;

	case FILTER_SUB_TYPE_MAX:
		// For filter w/o HW allocated.
		break;

	default:
		MTK_DMX_ERR(
			"filter_type.sub(%s) is not supported\n",
			filter_sub_type_2_str(filter_type.sub));

		return -EINVAL;
	}

	if (size != 0) {
		ret = atsc3_dmx_set_filter_info(
				dmx2_filter,
				filter_cfg, size);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_set_filter_info is failed\n");

			return -EIO;
		}
	}

	return ret;
}

static int atsc3_alp_filter_stop_cb(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct alp_filter_cfg alp_filter_cfg;
	struct ptp_filter_cfg ptp_filter_cfg;
	void *filter_cfg = NULL;
	u32 size = 0;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

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

	switch (filter_type.sub) {
	case FILTER_SUB_TYPE_ALP:
		ret = atsc3_dmx_alp_filter_stop(
				mtk_atsc3_feed,
				filter_type);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_filter_stop is failed\n");

			return -EIO;
		}
		filter_cfg = (void *) &alp_filter_cfg;
		size = sizeof(struct alp_filter_cfg);
		break;

	case FILTER_SUB_TYPE_PTP:
		ret = atsc3_ptp_filter_stop(
				mtk_atsc3_feed,
				filter_type);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_ptp_filter_stop is failed\n");

			return -EIO;
		}
		filter_cfg = (void *) &ptp_filter_cfg;
		size = sizeof(struct ptp_filter_cfg);
		break;

	case FILTER_SUB_TYPE_MAX:
		// For filter w/o HW allocated.
		break;

	default:
		MTK_DMX_ERR(
			"filter_type.sub(%s) is not supported\n",
			filter_sub_type_2_str(filter_type.sub));

		return -EINVAL;
	}

	if (size != 0) {
		ret = atsc3_dmx_set_filter_info(
				dmx2_filter,
				filter_cfg, size);
		if (ret != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_set_filter_info is failed\n");

			return -EIO;
		}
	}

	return ret;
}

static int atsc3_dmx_alp_set_ip_addr_mask(
		u8 u8IpVersion,
		ip_addr_t *ip_addr)
{
	if (MTK_DMX_VERIFY(ip_addr == NULL))
		return -EINVAL;

	if (u8IpVersion == DEMUX_ALP_IP_V4) {
		memset(&ip_addr->uMask, 0, sizeof(ip_addr->uMask));

		if (network_ip_addr_is_ignore(
			(u8 *) &ip_addr->uAddr.u32Addr,
			sizeof(ip_addr->uAddr.u32Addr)))
			ip_addr->uMask.u32Mask = ATSC3_IP_ADDR_MASK_IGNORE;
	} else {
		// TODO: IPV6

		return -EINVAL;
	}

	return 0;
}

static int atsc3_dmx_alp_set_src_ip_addr(
		u8 u8IpVersion,
		ip_addr_t *ip_addr,
		struct dmx2_ip_addr *dmx2_ip_addr)
{
	u32 *pu32Addr = NULL;

	if (MTK_DMX_VERIFY(ip_addr == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(dmx2_ip_addr == NULL))
		return -EINVAL;

	if (u8IpVersion == DEMUX_ALP_IP_V4) {
		memset(&ip_addr->uAddr, 0, sizeof(ip_addr->uAddr));

		pu32Addr = (u32 *)dmx2_ip_addr->src_ip_addr.v4;
		// TODO: It is expected that CPU uses little-endian.
		ip_addr->uAddr.u32Addr = be32_to_cpu(*pu32Addr);
	} else {
		// TODO: IPV6

		return -EINVAL;
	}

	return atsc3_dmx_alp_set_ip_addr_mask(
			u8IpVersion,
			ip_addr);
}

static int atsc3_dmx_alp_set_dst_ip_addr(
		u8 u8IpVersion,
		ip_addr_t *ip_addr,
		struct dmx2_ip_addr *dmx2_ip_addr)
{
	u32 *pu32Addr = NULL;

	if (MTK_DMX_VERIFY(ip_addr == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(dmx2_ip_addr == NULL))
		return -EINVAL;

	if (u8IpVersion == DEMUX_ALP_IP_V4) {
		memset(&ip_addr->uAddr, 0, sizeof(ip_addr->uAddr));

		pu32Addr = (u32 *)dmx2_ip_addr->dst_ip_addr.v4;
		ip_addr->uAddr.u32Addr = be32_to_cpu(*pu32Addr);
	} else {
		// TODO: IPV6

		return -EINVAL;
	}

	return atsc3_dmx_alp_set_ip_addr_mask(
			u8IpVersion,
			ip_addr);
}

static int atsc3_dmx_alp_set_udp_port_mask(
		u8 u8IpVersion,
		udp_port_t *udp_port)
{
	if (MTK_DMX_VERIFY(udp_port == NULL))
		return -EINVAL;

	if (u8IpVersion == DEMUX_ALP_IP_V4) {
		memset(&udp_port->uMask, 0, sizeof(udp_port->uMask));

		if (network_udp_port_is_ignore(
			udp_port->uPort.u16Port))
			udp_port->uMask.u16Mask = ATSC3_UDP_PORT_MASK_IGNORE;
	} else {
		// TODO: IPV6
		return -EINVAL;
	}

	return 0;
}

static int atsc3_dmx_alp_set_udp_port(
		u8 u8IpVersion,
		udp_port_t *udp_port,
		__u16 port)
{
	if (MTK_DMX_VERIFY(udp_port == NULL))
		return -EINVAL;

	udp_port->uPort.u16Port = port;

	return atsc3_dmx_alp_set_udp_port_mask(
			u8IpVersion,
			udp_port);
}

static int atsc3_dmx_alp_set_alp_filter_for_ip_data_all(
		struct dmx2_filter_params dmx2_filter_params,
		demux_alp_filter_t *demux_alp_filter)
{
	int ret = 0;
	struct dmx2_filter *prev_dmx2_filter = NULL;
	struct dmx2_alp_filter_params *dmx2_alp_filter_params = NULL;
	struct dmx2_ip_filter_params *dmx2_ip_filter_params = NULL;

	prev_dmx2_filter = dmx2_filter_params.prev_filter;
	if (MTK_DMX_VERIFY(prev_dmx2_filter == NULL))
		return -EINVAL;

	dmx2_alp_filter_params = &prev_dmx2_filter->params.alp;
	dmx2_ip_filter_params = &dmx2_filter_params.ip;

	do {
		demux_alp_filter->u8IpVersion =
			ATSC3_ALP_IS_IP_V4(prev_dmx2_filter)
				? DEMUX_ALP_IP_V4
				: DEMUX_ALP_IP_V6;

		if (atsc3_dmx_alp_set_src_ip_addr(
			demux_alp_filter->u8IpVersion,
			&demux_alp_filter->stSrcAddr,
			&dmx2_ip_filter_params->ip_addr) != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_set_src_ip_addr(...) is failed\n");

			ret = - EINVAL;
			break;
		}
		if (atsc3_dmx_alp_set_dst_ip_addr(
			demux_alp_filter->u8IpVersion,
			&demux_alp_filter->stDesAddr,
			&dmx2_ip_filter_params->ip_addr) != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_set_dst_ip_addr(...) is failed\n");

			ret = - EINVAL;
			break;
		}
		if (atsc3_dmx_alp_set_udp_port(
			demux_alp_filter->u8IpVersion,
			&demux_alp_filter->stSrcPort,
			dmx2_ip_filter_params->ip_addr.src_port) != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_set_udp_port(..., src_port) is failed\n");

			ret = - EINVAL;
			break;
		}
		if (atsc3_dmx_alp_set_udp_port(
			demux_alp_filter->u8IpVersion,
			&demux_alp_filter->stDesPort,
			dmx2_ip_filter_params->ip_addr.dst_port) != 0) {
			MTK_DMX_ERR(
				"atsc3_dmx_alp_set_udp_port(..., dst_port) is failed\n");

			ret = - EINVAL;
			break;
		}

		demux_alp_filter->stCid.bIsCid =
			ATSC3_ALP_IS_COMPRESSED(prev_dmx2_filter);
		// TODO: Obtain it from LMT
		demux_alp_filter->stCid.u8Cid = 0;

		demux_alp_filter->stPlp.bIsPlp = false; // depreciated
		demux_alp_filter->stPlp.bSinglePlpMode =
				(dmx2_alp_filter_params->flags
					& DMX2_PLP_FILTER_EN) == 0;
		demux_alp_filter->stPlp.u8PlpId = dmx2_alp_filter_params->plp_id;

		demux_alp_filter->stSid.bIsSid =
			(dmx2_alp_filter_params->flags & DMX2_SID_FILTER_EN) != 0;
		demux_alp_filter->stSid.u8Sid = dmx2_alp_filter_params->sid;
	} while (0);

	return ret;
}

static int atsc3_ip_filter_start_cb(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct alp_filter_cfg alp_filter_cfg;
	void *filter_cfg = NULL;
	u32 size;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	switch (filter_type.sub) {
	case FILTER_SUB_TYPE_ALP:
		switch (dmx2_filter->params.ip.data_type) {
		case DMX2_IP_DATA_ALL:
		case DMX2_IP_DATA_UDP_PAYLOAD:
			ret = atsc3_dmx_alp_metadata_init(mtk_atsc3_feed);
			if (ret != 0) {
				MTK_DMX_ERR(
					"atsc3_dmx_alp_metadata_init is failed\n");

				return -EIO;
			}
			ret = atsc3_dmx_alp_filter_start(
					atsc3_dmx_alp_set_alp_filter_for_ip_data_all,
					mtk_atsc3_feed,
					filter_type);
			if (ret != 0) {
				MTK_DMX_ERR(
					"atsc3_dmx_alp_filter_start is failed\n");

				ret = atsc3_dmx_alp_metadata_exit(mtk_atsc3_feed);
				if (ret != 0)
					MTK_DMX_ERR(
						"atsc3_dmx_alp_metadata_exit is failed\n");

				return -EIO;
			}
			filter_cfg = (void *) &alp_filter_cfg;
			size = sizeof(struct alp_filter_cfg);
			break;

		default:
			MTK_DMX_ERR(
				"params.ip.data_type(%s) is not supported\n",
				dmx2_ip_data_type_2_str(
					dmx2_filter->params.ip.data_type));

			return -EINVAL;
		}
		break;

	default:
		MTK_DMX_ERR(
			"filter_type.sub(%s) is not supported\n",
			filter_sub_type_2_str(filter_type.sub));

		return -EINVAL;
	}

	ret = atsc3_dmx_set_filter_info(
			dmx2_filter,
			filter_cfg, size);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_set_filter_info is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_ip_filter_stop_cb(
		struct mtk_atsc3_feed *mtk_atsc3_feed,
		struct filter_type filter_type)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct alp_filter_cfg alp_filter_cfg;
	void *filter_cfg = NULL;
	u32 size;

	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	switch (filter_type.sub) {
	case FILTER_SUB_TYPE_ALP:
		switch (dmx2_filter->params.ip.data_type) {
		case DMX2_IP_DATA_ALL:
		case DMX2_IP_DATA_UDP_PAYLOAD:
			if (atsc3_dmx_alp_filter_stop(
				mtk_atsc3_feed,
				filter_type) != 0) {
				MTK_DMX_ERR(
					"atsc3_dmx_alp_filter_stop is failed\n");

				return -EIO;
			}
			// Do it even atsc3_dmx_alp_filter_stop is failed
			if (atsc3_dmx_alp_metadata_exit(
				mtk_atsc3_feed) != 0) {
				MTK_DMX_ERR(
					"atsc3_dmx_alp_metadata_exit is failed\n");

				return -EIO;
			}
			filter_cfg = (void *) &alp_filter_cfg;
			size = sizeof(struct alp_filter_cfg);
			break;

		default:
			MTK_DMX_ERR(
				"params.ip.data_type(%s) is not supported\n",
				dmx2_ip_data_type_2_str(
					dmx2_filter->params.ip.data_type));

			return -EINVAL;
		}
		break;

	default:
		MTK_DMX_ERR(
			"filter_type.sub(%s) is not supported\n",
			filter_sub_type_2_str(filter_type.sub));

		return -EINVAL;
	}

	ret = atsc3_dmx_set_filter_info(
			dmx2_filter,
			filter_cfg, size);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_set_filter_info is failed\n");

		return -EIO;
	}

	return ret;
}

static int atsc3_alp_filter_get_filter_type(
		struct dmx2_filter *dmx2_filter,
		struct filter_type *filter_type)
{
	int ret = 0;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter_type == NULL))
		return -EINVAL;

	MTK_DMX_DEBUG(
		"data_type = %s\n",
		dmx2_alp_data_type_2_str(
			dmx2_filter->params.alp.data_type));

	switch (dmx2_filter->params.alp.data_type) {
	case DMX2_ALP_DATA_SIGNALING:
		filter_type->main = dmx2_filter->params.type;
		filter_type->sub = FILTER_SUB_TYPE_ALP;
		break;

	case DMX2_ALP_DATA_PAYLOAD:
		filter_type->main = dmx2_filter->params.type;
		// alp filter is not required for payload through.
		filter_type->sub = FILTER_SUB_TYPE_MAX;
		break;

	default:
		if ((u32)(dmx2_filter->params.alp.data_type) == DMX2_ALP_PRIV_DATA_PTP) {
			filter_type->main = dmx2_filter->params.type;
			filter_type->sub = FILTER_SUB_TYPE_PTP;
		} else {
			MTK_DMX_ERR(
				"filter->params.alp.data_type(%s) is not supported\n",
				dmx2_alp_data_type_2_str(
					dmx2_filter->params.alp.data_type));
			ret = -EINVAL;
		}
		break;
	}

	return ret;
}

static int atsc3_ip_filter_get_filter_type(
		struct dmx2_filter *dmx2_filter,
		struct filter_type *filter_type)
{
	int ret = 0;

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter_type == NULL))
		return -EINVAL;

	MTK_DMX_DEBUG(
		"data_type = %s\n",
		dmx2_ip_data_type_2_str(
			dmx2_filter->params.ip.data_type));

	switch (dmx2_filter->params.ip.data_type) {
	case DMX2_IP_DATA_ALL:
	case DMX2_IP_DATA_UDP_PAYLOAD:
	case DMX2_IP_DATA_NTP:
		filter_type->main = dmx2_filter->params.type;
		// alp filter is used to for both alp and ip parameters.
		filter_type->sub = FILTER_SUB_TYPE_ALP;
		break;

	default:
		MTK_DMX_ERR(
			"params.ip.data_type(%s) is not supported\n",
			dmx2_ip_data_type_2_str(dmx2_filter->params.ip.data_type));

		return -EINVAL;
	}

	return ret;
}

static int atsc3_get_filter_type(
		struct dmx2_filter *dmx2_filter,
		struct filter_type *filter_type)
{
	int ret = 0;

	switch (dmx2_filter->params.type) {
	case DMX2_FILTER_TYPE_ALP:
		ret = atsc3_alp_filter_get_filter_type(
				dmx2_filter,
				filter_type);
		break;

	case DMX2_FILTER_TYPE_IP:
		ret = atsc3_ip_filter_get_filter_type(
				dmx2_filter,
				filter_type);
		break;

	default:
		MTK_DMX_ERR(
			"params.type(%s) is not supported\n",
			dmx2_main_filter_type_2_str(
				dmx2_filter->params.type));

		ret = -EINVAL;
		break;
	}

	return ret;
}

static int atsc3_filter_start(
		filter_start_callback filter_start_cb,
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct filter_type filter_type = { 0 };

	if (MTK_DMX_VERIFY(filter_start_cb == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	ret = atsc3_dmx_is_austin_support(dmx2_filter);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_is_austin_support is failed\n");
	}

	ret = atsc3_get_filter_type(
			dmx2_filter,
			&filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_get_filter_type is failed\n");

		return -EINVAL;
	}
	MTK_DMX_DEBUG(
		"filter_type.sub = %s\n",
		filter_sub_type_2_str(filter_type.sub));

	// filter_type should be set before filter_start_cb,
	// because atsc3_dmx_set_filter_info is dependent on it.
	ret = atsc3_dmx_set_filter_type(
			dmx2_filter,
			filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_set_filter_type is failed\n");

		return -EINVAL;
	}

	ret = (*filter_start_cb)(
			mtk_atsc3_feed,
			filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"filter_start_cb is failed\n");

		return -EINVAL;
	}

	ret = atsc3_dmx_add_filter_list(
			dmx2_filter,
			cast_to_dmx2_feed(mtk_atsc3_feed)->index,
			filter_type);
	if (ret != 0)
		MTK_DMX_ERR(
			"atsc3_dmx_add_filter_list is failed\n");

	return ret;
}

static int atsc3_filter_stop(
		filter_stop_callback filter_stop_cb,
		struct mtk_atsc3_feed *mtk_atsc3_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct filter_type filter_type = { 0 };

	if (MTK_DMX_VERIFY(filter_stop_cb == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(mtk_atsc3_feed == NULL))
		return -EINVAL;
	dmx2_filter = cast_to_dmx2_feed(mtk_atsc3_feed)->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	ret = atsc3_get_filter_type(
			dmx2_filter,
			&filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_get_filter_type is failed\n");

		return -EINVAL;
	}

	MTK_DMX_DEBUG(
		"filter_type.sub = %s\n",
		filter_sub_type_2_str(filter_type.sub));

	// filter list should be removed first,
	// because index is clear in filter_stop_cb
	ret = atsc3_dmx_remove_filter_list(
			dmx2_filter,
			cast_to_dmx2_feed(mtk_atsc3_feed)->index,
			filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"atsc3_dmx_remove_filter_list is failed\n");

		return -EIO;
	}

	ret = (*filter_stop_cb)(
			mtk_atsc3_feed,
			filter_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"filter_stop_cb is failed\n");
	}

	return ret;
}

static int atsc3_dmx_feed_start(struct dmx2_feed *dmx2_feed)
{
	int ret = 0;
	struct mtk_atsc3_feed *mtk_atsc3_feed = NULL;
	struct dmx2_filter *dmx2_filter = NULL;
	filter_start_callback atsc3_filter_start_cb = NULL;

	if (MTK_DMX_VERIFY(dmx2_feed == NULL))
		return -EINVAL;
	mtk_atsc3_feed = cast_to_atsc3_feed(dmx2_feed);
	dmx2_filter = dmx2_feed->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

	switch (dmx2_filter->params.type) {
	case DMX2_FILTER_TYPE_ALP:
		atsc3_filter_start_cb = atsc3_alp_filter_start_cb;
		break;

	case DMX2_FILTER_TYPE_IP:
		atsc3_filter_start_cb = atsc3_ip_filter_start_cb;
		break;

	default:
		MTK_DMX_ERR(
			"params.type(%s) is not supported\n",
			dmx2_main_filter_type_2_str(
				dmx2_filter->params.type));

		ret = -EINVAL;
		break;
	}

	if (ret == 0) {
		ret = atsc3_filter_start(
				atsc3_filter_start_cb,
				mtk_atsc3_feed);
		if (ret != 0)
			MTK_DMX_ERR("atsc3_filter_start is failed\n");
	}

	return ret;
}

static int atsc3_dmx_feed_stop(struct dmx2_feed *dmx2_feed)
{
	int ret = 0;
	struct mtk_atsc3_feed *mtk_atsc3_feed = NULL;
	struct dmx2_filter *dmx2_filter = NULL;
	struct dvb_demux2 *dvb_demux2 = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dmx_path *mtk_dmx_path = NULL;
	struct dmx2dev *dmx2dev = NULL;
	filter_stop_callback atsc3_filter_stop_cb = NULL;

	if (MTK_DMX_VERIFY(dmx2_feed == NULL))
		return -EINVAL;
	mtk_atsc3_feed = cast_to_atsc3_feed(dmx2_feed);
	dmx2_filter = dmx2_feed->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	dvb_demux2 = dmx2_feed->parent;
	if (MTK_DMX_VERIFY(dvb_demux2 == NULL))
		return -EINVAL;
	mtk_demux2 = cast_to_mtk_demux(dvb_demux2);
	mtk_dmx_path = dumex2_to_path(mtk_demux2);
	dmx2dev = &mtk_dmx_path->dmx2dev;

#ifndef ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
	mutex_unlock(&dmx2dev->mutex);
#endif // ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE

	switch (dmx2_filter->params.type) {
	case DMX2_FILTER_TYPE_ALP:
		atsc3_filter_stop_cb = atsc3_alp_filter_stop_cb;
		break;

	case DMX2_FILTER_TYPE_IP:
		atsc3_filter_stop_cb = atsc3_ip_filter_stop_cb;
		break;

	default:
		MTK_DMX_ERR(
			"params.type(%s) is not supported\n",
			dmx2_main_filter_type_2_str(dmx2_filter->params.type));

		ret = -EINVAL;
		break;
	}

	if (ret == 0) {
		ret = atsc3_filter_stop(
				atsc3_filter_stop_cb,
				mtk_atsc3_feed);
		if (ret != 0)
			MTK_DMX_ERR("atsc3_filter_stop is failed\n");
	}

#ifndef ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
	if (mutex_lock_interruptible(&dmx2dev->mutex)) {
		MTK_DMX_ERR(
			"mutex_lock_interruptible() return fail\n");

		return -ERESTARTSYS;
	}
#endif // ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE

	return ret;
}

static int atsc3_dmx_feed_flush(struct dmx2_feed *dmx2_feed)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct dvb_demux2 *dvb_demux2 = NULL;
	struct mtk_demux2 *mtk_demux2 = NULL;
	struct mtk_dmx_path *path = NULL;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;

	if (MTK_DMX_VERIFY(dmx2_feed == NULL))
		return -EINVAL;

	dmx2_filter = dmx2_feed->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	dvb_demux2 = dmx2_feed->parent;
	if (MTK_DMX_VERIFY(dvb_demux2 == NULL))
		return -EINVAL;
	mtk_demux2 = cast_to_mtk_demux(dvb_demux2);
	path = dumex2_to_path(mtk_demux2);
	mtk_dvr2 = &path->dvr_record;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);

	ret = atsc3_dvr_flush(dvb_dvr2);
	if (ret != 0)
		MTK_DMX_ERR(
			"atsc3_dvr_flush is failed\n");

	return ret;
}

// refer to dvb_dmxdev_ts_callback
static int atsc3_dmx_feed_callback(
		const u8 *buffer1,
		size_t buffer1_len,
		const u8 *buffer2,
		size_t buffer2_len,
		struct dmx2_feed *dmx2_feed,
		u32 *buf_flags)
{
	int ret = 0;
	struct dmx2_filter *dmx2_filter = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;

	if (MTK_DMX_VERIFY(dmx2_feed == NULL))
		return -EINVAL;
	dmx2_filter = dmx2_feed->filter;
	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(
		atsc3_dmx_get_bc_ctx(
			dmx2_filter,
			&bc_ctx) != 0))
		return -EINVAL;

	// TODO: VB2
	ret = mtk_bc_ringbuf_write(
			bc_ctx,
			buffer1,
			buffer1_len);
	if (ret == buffer1_len)
		ret = mtk_bc_ringbuf_write(
				bc_ctx,
				buffer2,
				buffer2_len);

	return 0;
}

static int atsc3_dmx_set_buf_rptr(
		struct dvb_fh *fh,
		u64 rptr)
{
#if 0 // fix set but not used
	struct mtk_demux2_fh_ctx *ctx = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct dvb_ringbuffer *buffer = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(fh == NULL))
		return -EINVAL;
	ctx = fh_to_dmxctx(fh);
	bc_ctx = fh_dmxctx_get_bc_ctx(ctx);
	// TODO: VB2
	buffer = &bc_ctx->d_ctx.buf.ring;

	// Do nothing

	MTK_DMX_EXIT();
#endif
	return 0;
}

static int atsc3_notify_event(struct mtk_demux2_notify_args *args)
{
	struct mtk_dmx_path *path = args->path;
	link_state_t state = args->state;

	if (state == LINK_STATE_CONNECT) {
		MTK_DMX_INFO("link state connect for path %d\n", path->path_id);
	} else if (state == LINK_STATE_DISCONNECT) {
		MTK_DMX_INFO("link state disconnect for path %d\n", path->path_id);
		mtk_dmx_io_set_tsif_en(path->path_id, false);
	}

	return 0;
}

static int atsc3_query_event(struct mtk_demux2_query_args *args)
{
	struct dvbdev_properties *prop = args->prop;
	u32 i, num = prop->num;
	struct mtk_dvr2 *dvr;
	int ret = 0;
	struct dvb_dvr2 *base;
	struct mtk_atsc3_dvr_private *priv;
	bool status = 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;
			if (priv) {
				status = priv->status;
				if (status)
					prop->props[i].result = 1;
				else
					prop->props[i].result = 0;
			}
			break;
		default:
			return -EINVAL;
		}
	}
	return ret;
}


static int atsc3_streaming_event(struct mtk_demux2_streaming_args *args)
{
#define ATSC3_PARALLEL_DATA_BITS_NUM (8)
#define ATSC3_SERIAL_2_BITS_DATA_BITS_NUM (2)
#define ATSC3_SERIAL_1_BIT_DATA_BITS_NUM (1)
#define ATSC3_STREAMING_EVENT_PROP_NUM (2)
#define ATSC3_EXT_TS_PORT_NUM (0)
#define ATSC3_EXT1_TS_PORT_NUM (1)
#define ATSC3_EXT2_TS_PORT_NUM (2)
#define ATSC3_EXT3_TS_PORT_NUM (3)

	int i, ret;
	struct mtk_dmx_path *path = args->path;
	struct dvb_frontend *fe = args->fe;
	struct cfm_properties *props = args->props;
	u32 data, bits = 0;
	struct cfm_properties p;
	struct mtk_msource_settings src;
	struct mtk_dmx_rm *rm = path->demux2.rm;
	u8 pin[32];
	bool ext_ts = false, filein = false;

	memset(&pin, 0, sizeof(pin));
	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) {
				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);
				break;
			}
			if (!fe) {
				MTK_DMX_INFO("file-in start\n");
				bits = ATSC3_PARALLEL_DATA_BITS_NUM;
				filein = true;
			} else {
				MTK_DMX_INFO("demod locked: %d\n", path->path_id);

				p.num = ATSC3_STREAMING_EVENT_PROP_NUM;
				p.props = kcalloc(
					p.num,
					sizeof(struct cfm_property),
					GFP_KERNEL);
				if (!p.props) {
					MTK_DMX_INFO("pros's kcalloc is failed\n");
					return -ENOMEM;
				}

				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 = ATSC3_EXT_TS_PORT_NUM;
				} else if (!strncmp(pin, "ext1_ts_", strlen("ext1_ts_"))) {
					ext_ts = true;
					src.port_num = ATSC3_EXT1_TS_PORT_NUM;
				} else if (!strncmp(pin, "ext2_ts_", strlen("ext2_ts_"))) {
					ext_ts = true;
					src.port_num = ATSC3_EXT2_TS_PORT_NUM;
				} else if (!strncmp(pin, "ext3_ts_", strlen("ext3_ts_"))) {
					ext_ts = true;
					src.port_num = ATSC3_EXT3_TS_PORT_NUM;
				} else {
					MTK_DMX_ERR("cannot find pin: %s\n", pin);
					return -EINVAL;
				}
				MTK_DMX_INFO("bits: %d pin: %s\n", bits, pin);
			}
			src.port_type =
				(bits == ATSC3_SERIAL_2_BITS_DATA_BITS_NUM) ?
					DMX_MC_PORT_2BITS_SERIAL :
					(bits == ATSC3_SERIAL_1_BIT_DATA_BITS_NUM) ?
						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;
			src.cicam = path->cicam;
			src.cifpga = path->cifpga;
			ret = mtk_mc_set_tsp_input_source(rm, path->path_id, &src, pin);
			if (ret < 0)
				MTK_DMX_ERR("set input source fail\n");
			break;
		}

	return 0;

#undef ATSC3_PARALLEL_DATA_BITS_NUM
#undef ATSC3_SERIAL_2_BITS_DATA_BITS_NUM
#undef ATSC3_SERIAL_1_BIT_DATA_BITS_NUM
#undef ATSC3_STREAMING_EVENT_PROP_NUM
#undef ATSC3_EXT_TS_PORT_NUM
#undef ATSC3_EXT1_TS_PORT_NUM
#undef ATSC3_EXT2_TS_PORT_NUM
#undef ATSC3_EXT3_TS_PORT_NUM
}

static int atsc3_dmx_filter_alloc(struct dmx2_filter **dmx2_filter)
{
	struct mtk_atsc3_filter *mtk_atsc3_filter = NULL;
	struct mtk_atsc3_feed *mtk_atsc3_feed = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(dmx2_filter == NULL))
		return -EINVAL;

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

	mtk_atsc3_feed = kzalloc(sizeof(*mtk_atsc3_feed), GFP_KERNEL);
	if (!mtk_atsc3_feed) {
		MTK_DMX_ERR("atsc3 feed alloc fail\n");
		MTK_DMX_KVFREE(mtk_atsc3_filter);
		return -ENOMEM;
	}

	cast_to_dmx2_feed(mtk_atsc3_feed)->index = FILTER_IDX_INVALID;
	cast_to_dmx2_feed(mtk_atsc3_feed)->callback = atsc3_dmx_feed_callback;
	cast_to_dmx2_filter(mtk_atsc3_filter)->feed = cast_to_dmx2_feed(mtk_atsc3_feed);

	*dmx2_filter = cast_to_dmx2_filter(mtk_atsc3_filter);

	MTK_DMX_EXIT();

	return 0;
}

static int atsc3_dmx_filter_release(struct dmx2_filter *dmx2_filter)
{
	struct mtk_atsc3_filter *mtk_atsc3_filter = cast_to_atsc3_filter(dmx2_filter);
	struct mtk_atsc3_feed *mtk_atsc3_feed = cast_to_atsc3_feed(dmx2_filter->feed);

	MTK_DMX_ENTRY();

	MTK_DMX_KVFREE(mtk_atsc3_feed);
	MTK_DMX_KVFREE(mtk_atsc3_filter);

	MTK_DMX_EXIT();

	return 0;
}

static int atsc3_alp_record_cfg_set(
		demux_alp_instance_e alp_instance,
		enum pvr_packet_type rec_type,
		bool rec_all)
{
	int ret = 0;
	demux_alp_path_type_e alp_path_type = DEMUX_ALP_PATH_TYPE_NULL;

	if (MTK_DMX_VERIFY(alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;

	switch (rec_type) {
	case PVR_PACKET_TYPE_ALP:
		alp_path_type = DEMUX_ALP_PATH_TYPE_ALP_OUT_FIFO_FROM_FIFO_BEFORE_PVR;
		break;

	case PVR_PACKET_TYPE_ALP_WITH_TIMESTAMP:
		alp_path_type =
			DEMUX_ALP_PATH_TYPE_ALP_OUT_FIFO_FROM_TIMESTAM_KEYWORD_SIDEBAND_INSERT;
		break;

	default:
		alp_path_type = DEMUX_ALP_PATH_TYPE_NULL;
		MTK_DMX_ERR(
			"rec_type(0x%x) is not supported\n",
			rec_type);
		break;
	}

	if (alp_path_type == DEMUX_ALP_PATH_TYPE_NULL)
		return -EINVAL;

	ret = mtk_dmx_alp_set_path(alp_instance, alp_path_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_set_path(0x%x, 0x%x) is failed\n",
			alp_instance,
			alp_path_type);

		return ret;
	}

	if (rec_all)
		alp_path_type = DEMUX_ALP_PATH_TYPE_FIFO_BEFORE_PVR_FROM_TSIF_ALP;
	else
		alp_path_type = DEMUX_ALP_PATH_TYPE_FIFO_BEFORE_PVR_FROM_MTK_ALP_TOP;

	ret = mtk_dmx_alp_set_path(alp_instance, alp_path_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_set_path(0x%x, 0x%x) is failed\n",
			alp_instance,
			alp_path_type);
	}

	return ret;
}

static int atsc3_pvr_start(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	s32 eng_id = PVR_ENG_IDX_INVALID;
	enum pvr_packet_type type;
	struct dvr_rec_config *rec_cfg;
	struct mtk_atsc3_dvr_private *priv;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	demux_alp_instance =
		mtk_dmx_alp_tsio_path_2_alp_instance(
			mtk_dvr2->path_id);
	if (MTK_DMX_VERIFY(demux_alp_instance == DEMUX_ALP_INSTANCE_NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	eng_id = dvb_dvr2->index;
	if (MTK_DMX_VERIFY(eng_id < 0))
		return -EINVAL;
	rec_cfg = &mtk_dvr2->rec_cfg;

	do {
		if (dvb_dvr2->protocol == DVR2_FORMAT_ATSC3) {
			if (rec_cfg->pre_hdr_type == DVR_INSERT_TIMESTAMP)
				type = PVR_PACKET_TYPE_ALP_WITH_TIMESTAMP;
			else
				type = PVR_PACKET_TYPE_ALP;
		} else {
			MTK_DMX_ERR(
				"protocol(0x%x) is not supported\n",
				dvb_dvr2->protocol);

			ret = -EINVAL;
			break;
		}

		mtk_pvr_set_record_type(eng_id, type);
		mtk_pvr_set_record_mode(eng_id, rec_cfg->record_all);

		priv = dvb_dvr2->priv;
		if (priv->is_austin) {
			atsc3_alp_record_cfg_set(
				demux_alp_instance,
				PVR_PACKET_TYPE_ALP_WITH_TIMESTAMP,
				true);
		} else {
			atsc3_alp_record_cfg_set(
				demux_alp_instance,
				type,
				rec_cfg->record_all);
		}
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_pvr_stop(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;

	MTK_DMX_ENTRY();

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_dvr_start(struct dvb_dvr2 *dvb_dvr2)
{
	int ret = 0;
	int ret_tmp;
	struct mtk_dvr2 *mtk_dvr2 = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(dvb_dvr2 == NULL))
		return -EINVAL;
	mtk_dvr2 = cast_to_mtk_dvr(dvb_dvr2);

	do {
		ret_tmp = atsc3_pvr_start(mtk_dvr2);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_start(...) is failed\n");

			ret = -EIO;
			break;
		}
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_dvr_stop(struct dvb_dvr2 *dvb_dvr2)
{
	int ret = 0;
	int ret_tmp;
	struct mtk_dvr2 *mtk_dvr2 = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(dvb_dvr2 == NULL))
		return -EINVAL;
	mtk_dvr2 = cast_to_mtk_dvr(dvb_dvr2);

	do {
		ret_tmp = atsc3_pvr_stop(mtk_dvr2);
		if (ret_tmp != 0) {
			MTK_DMX_ERR(
				"atsc3_pvr_stop(...) is failed\n");

			ret = -EIO;
			break;
		}
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_dvr_flush(struct dvb_dvr2 *dvb_dvr2)
{
	int ret = 0;
	struct mtk_dvr2_fh_ctx *ctx = fh_to_dvrctx(dvb_dvr2->fh);
	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(dvb_dvr2 == NULL))
		return -EINVAL;

	if (MTK_DMX_VERIFY(ctx == NULL)) {
		MTK_DMX_ERR("flush fail. convert to mtk_dvr2_fh_ctx fail");
		return -EINVAL;
	}

	if (IS_INPUT_DEVICE(ctx->dir)) {
		struct dmx2_buffer_ctx *buf_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
		struct mtk_bc_ctx *bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buf_ctx);
		mtk_bc_ringbuf_flush(bc_ctx);
		return 0;
	}
	else {
		mtk_pvr_flush_data(dvb_dvr2->index);
	}

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_filein_write_from_ringbuffer(
		u16 path_id,
		u64 paddr,
		struct dvb_ringbuffer *rbuf,
		bool init_trust)
{
	int ret = 0;
	u64 paddrs[2] = { 0 };
	u64 sizes[2] = { 0 };

	MTK_DMX_ENTRY();

	if (rbuf->pwrite == rbuf->pread) {
		MTK_DMX_INFO("buffer is empty\n");
	} else if (rbuf->pwrite > rbuf->pread) {
		paddrs[0] = paddr + rbuf->pread;
		sizes[0] = rbuf->pwrite - rbuf->pread;
	} else if (rbuf->pwrite < rbuf->pread) {
		paddrs[0] = paddr + rbuf->pread;
		sizes[0] = rbuf->size - rbuf->pread;
		paddrs[1] = paddr;
		sizes[1] = rbuf->pwrite;
	}

	if (sizes[0] > 0)
		mtk_dmx_io_feed_filein(
			FILEIN_MAIN,
			paddrs[0],
			sizes[0],
			init_trust);
	if (sizes[1] > 0)
		mtk_dmx_io_feed_filein(
			FILEIN_MAIN,
			paddrs[1],
			sizes[1],
			init_trust);

	ret = sizes[0] + sizes[1];

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_filein_write(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct dvb_ringbuffer *ringbuf = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct mem_info mem_info = { 0 };
	int retry_cnt = ATSC3_FILEIN_WRITE_MAX_RETRY_COUNT;
	u16 cmdq_cnt;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	// TODO
	buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_IN];
	if (MTK_DMX_VERIFY(buffer_ctx == NULL))
		return -EINVAL;
	ringbuf = &buffer_ctx->ring;
	bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buffer_ctx);
	ret = mtk_bc_get_mem_info(bc_ctx, MEM_INFO_TYPE_MIN, &mem_info);
	if (ret != 0) {
		MTK_DMX_ERR("mtk_bc_get_mem_info is failed\n");

		return -EINVAL;
	}
	if (MTK_DMX_VERIFY(mem_info.size == 0))
		return -EINVAL;

	ret = atsc3_filein_write_from_ringbuffer(
			mtk_dvr2->path_id,
			mem_info.paddr,
			ringbuf,
			(dvb_dvr2->protocol == DVR2_FORMAT_ATSC3));
	if (ret < 0) {
		MTK_DMX_ERR(
			"_filein_write_from_ringbuffer is failed\n");

		return ret;
	}

	while (mtk_dmx_io_get_filein_state(FILEIN_MAIN, &cmdq_cnt)
		!= DEMUX_FILEIN_STATE_IDLE) {
		if (--retry_cnt < 0) {
			MTK_DMX_ERR("timeout\n");

			return -EIO;
		}

		udelay(100);
	}

	// read_offset += written size
	mtk_bc_ringbuf_set_rptr(bc_ctx, (buffer_ctx->ring.pread + (ssize_t)ret) % buffer_ctx->ring.size);

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_dvr_write(struct dvb_dvr2 *dvb_dvr2)
{
	int ret = 0;
	struct mtk_dvr2 *mtk_dvr2 = NULL;
	struct mtk_dmx_path *mtk_dmx_path = NULL;
	struct dmx2dev *dmx2dev = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(dvb_dvr2 == NULL))
		return -EINVAL;
	mtk_dvr2 = cast_to_mtk_dvr(dvb_dvr2);
	mtk_dmx_path = dvr2_to_path(mtk_dvr2);
	dmx2dev = &mtk_dmx_path->dmx2dev;

#ifdef ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
	if (mutex_lock_interruptible(&dmx2dev->mutex)) {
		MTK_DMX_ERR(
			"mutex_lock_interruptible() return fail\n");

		return -ERESTARTSYS;
	}
#endif // ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE

	while (mtk_dmx_path->state != DMX_PATH_STATE_IDLE) {
		if (mtk_dmx_path->state != DMX_PATH_STATE_BUSY) {
			MTK_DMX_ERR(
				"path->state == %d return fail\n",
				mtk_dmx_path->state);
#ifdef ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
			mutex_unlock(&dmx2dev->mutex);
#endif // ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
			return -ERESTARTSYS;
		}

		mutex_unlock(&dmx2dev->mutex);

		MTK_DMX_INFO(
			"path(%d)'s state is busy\n",
			mtk_dmx_path->path_id);
		msleep_interruptible(100);

		if (mutex_lock_interruptible(&dmx2dev->mutex)) {
			MTK_DMX_ERR(
				"mutex_lock_interruptible() return fail\n");

			return -ERESTARTSYS;
		}
	}

	ret = atsc3_filein_write(mtk_dvr2);
#ifdef ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
	mutex_unlock(&dmx2dev->mutex);
#endif // ATSC3_DVR_WRITE_NO_MUTEX_IN_DVB_CORE
	if (ret < 0) {
		MTK_DMX_ERR("_filein_write(...) return fail\n");

		return ret;
	}

	if (signal_pending(current)) {
		MTK_DMX_ERR("signal_pending() return fail\n");

		return -EINTR;
	}

	MTK_DMX_EXIT();

	return ret;
}

static u64 atsc3_dvr_get_buf_wptr(
		struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	u64 wptr = 0;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	struct dmx2_buffer_ctx *buffer_ctx = NULL;
	struct dvb_ringbuffer *ringbuf = NULL;
	struct mtk_bc_ctx *bc_ctx = NULL;
	struct mem_info mem_info = { 0 };
	u64 pwrite = 0;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return 0;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);
	mtk_atsc3_dvr_private = dvb_dvr2->priv;
	if (MTK_DMX_VERIFY(mtk_atsc3_dvr_private == NULL))
		return 0;

	if (mtk_atsc3_dvr_private->is_austin) {
		bc_ctx = &(mtk_atsc3_dvr_private->bc_ctx_austin);
		ringbuf = &bc_ctx->d_ctx.buf.ring;
	} else {
		// TODO
		buffer_ctx = dvb_dvr2->buf_ctx[DVR2_BUF_CTX_TYPE_OUT];
		if (MTK_DMX_VERIFY(buffer_ctx == NULL))
			return 0;
		ringbuf = &buffer_ctx->ring;
		bc_ctx = dynamic_cast(struct mtk_bc_ctx *, buffer_ctx);
	}

	ret = mtk_bc_get_mem_info(bc_ctx, MEM_INFO_TYPE_MIN, &mem_info);
	if (ret != 0) {
		MTK_DMX_ERR("mtk_bc_get_mem_info is failed\n");

		return 0;
	}
	if (MTK_DMX_VERIFY(mem_info.size == 0))
		return 0;

	if (dvb_dvr2->state != DVR2_STATE_GO)
		return 0;

	wptr =	mtk_pvr_get_wptr(dvb_dvr2->index);
	pwrite = wptr - mem_info.paddr;
	// TODO: ssize_t is compared to u64
	if (ringbuf->pwrite == pwrite)
		return 0;

#if (LOG_TO_FILE)
	if (pfd_ORI == NULL) {
		pfd_ORI = filp_open("/data/PVR_ORI.bin", O_RDWR | O_CREAT, 0644);
		if (IS_ERR(pfd_ORI)) {
			MTK_DMX_INFO("Open ORI.bin failed %d\n", IS_ERR(pfd_ORI));
			pfd_ORI = NULL;
		}
	}
#endif
	MTK_DMX_DEBUG("paddr = 0x%llx wptr = 0x%llx\n", mem_info.paddr, wptr);
	MTK_DMX_DEBUG("size = 0x%x\n", mem_info.size);
	// Reduce the frequency to print information
	if (mtk_atsc3_dvr_private->prev_pread == ringbuf->pread) {
		MTK_DMX_INFO(
			"wptr 0x%llx pread 0x%zx pwrite 0x%llx prev pwrite 0x%zx\n",
			wptr,
			ringbuf->pread,
			pwrite,
			ringbuf->pwrite);
	} else {
		mtk_atsc3_dvr_private->prev_pread = ringbuf->pread;
	}

#if (LOG_TO_FILE)
	if (pfd_ORI != NULL) {
		if (ringbuf->pwrite < pwrite) {
			ssize_t ret = 0;
			loff_t pos = pfd_ORI->f_pos;

			ret = kernel_write(pfd_ORI,
				(void *)((u64)(size_t)ringbuf->data + (u64)ringbuf->pwrite),
				(pwrite - ringbuf->pwrite), &pos);
			MTK_DMX_DEBUG("kernel_write ret %d", ret);

			pfd_ORI->f_pos = pos;
			vfs_fsync(pfd_ORI, 0);
		} else {
			ssize_t ret = 0;
			loff_t pos = pfd_ORI->f_pos;

			ret = kernel_write(pfd_ORI,
				(void *)((u64)(size_t)ringbuf->data + (u64)ringbuf->pwrite),
				(ringbuf->size - ringbuf->pwrite), &pos);
			MTK_DMX_DEBUG("kernel_write ret %d", ret);
			pfd_ORI->f_pos = pos;
			vfs_fsync(pfd_ORI, 0);
			pos = pfd_ORI->f_pos;
			ret = kernel_write(pfd_ORI, (void *)((u64)(size_t)ringbuf->data),
				(pwrite), &pos);
			MTK_DMX_DEBUG("kernel_write ret %d", ret);
			pfd_ORI->f_pos = pos;
			vfs_fsync(pfd_ORI, 0);
		}
	}
#endif
	ret = mtk_bc_ringbuf_set_wptr(bc_ctx, pwrite);
	if (ret != 0) {
		MTK_DMX_ERR(
			"mtk_bc_ringbuf_set_wptr is failed\n");

		return 0;
	}

	MTK_DMX_EXIT();

	return (u64)(size_t)ringbuf->data + (u64)ringbuf->pwrite;
}

static int atsc3_pvr_rec_config_init(struct mtk_dvr2 *mtk_dvr2)
{
	int ret = 0;
	struct dvr_rec_config *rec_cfg = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;

	rec_cfg = &mtk_dvr2->rec_cfg;
	rec_cfg->record_all = false;
	rec_cfg->pre_hdr_type = DVR_INSERT_TIMESTAMP;
	rec_cfg->encrypt_type = DVR_ENCRYPT_TYPE_NONE;

	MTK_DMX_EXIT();

	return ret;
}

static int atsc3_init_alp(u16 path_id)
{
	int ret;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	demux_alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(path_id);
	// Do nothing if path is not ALP
	if (demux_alp_instance == DEMUX_ALP_INSTANCE_NULL)
		return 0;

	ret = mtk_dmx_alp_init(demux_alp_instance);
	if (ret != MTK_DMX_SUCCESS) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_init(%d) fail\n",
			demux_alp_instance);

		return -EIO;
	}

	return 0;
}

static int atsc3_release_alp(u16 path_id)
{
	int ret;
	demux_alp_instance_e demux_alp_instance = DEMUX_ALP_INSTANCE_NULL;

	demux_alp_instance = mtk_dmx_alp_tsio_path_2_alp_instance(path_id);
	// Do nothing if path is not ALP
	if (demux_alp_instance == DEMUX_ALP_INSTANCE_NULL)
		return 0;

	ret = mtk_dmx_alp_exit(demux_alp_instance);
	if (ret != MTK_DMX_SUCCESS) {
		MTK_DMX_ERR(
			"mtk_dmx_alp_exit(%d) fail\n",
			demux_alp_instance);
		return -EIO;
	}

	return 0;
}

static int atsc3_allocate_ref_count_info(
		struct mtk_atsc3_ref_count_info *ref_count_info,
		struct mtk_dmx_rm *mtk_dmx_rm,
		u32 hwcap_id)
{
	if (MTK_DMX_VERIFY(mtk_dmx_rm == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(ref_count_info == NULL))
		return -EINVAL;

	ref_count_info->ref_count = NULL;
	ref_count_info->total_num =
		mtk_dmx_rm_get_hwcap_by_id(mtk_dmx_rm, hwcap_id);
	if (MTK_DMX_VERIFY(ref_count_info->total_num == 0))
		return -EINVAL;
	ref_count_info->ref_count =
		kcalloc(ref_count_info->total_num,
			sizeof(*ref_count_info->ref_count),
			GFP_KERNEL);
	if (ref_count_info->ref_count == 0) {
		MTK_DMX_ERR("allocate ref_count_info fail\n");
		ref_count_info->total_num = 0;

		return -ENOMEM;
	}

	return 0;
}

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
int mtk_dmx_atsc3_init(struct mtk_demux2 *mtk_demux2)
{
	int ret = 0;
	struct dvb_demux2 *dvb_demux2 = cast_to_dmx_parent(mtk_demux2);
	struct mtk_dmx_path *mtk_dmx_path = NULL;
	u16 alp_flt_num =
		mtk_dmx_rm_get_hwcap_by_id(
			mtk_demux2->rm,
			HWCAPS_TYPE_ALP_FILTER);
	struct mtk_atsc3_dmx_private *priv = NULL;
	struct mtk_dmx2dev *mtkdev = container_of(mtk_demux2->rm, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);
	struct device_node *np, *np2;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return -EINVAL;

	mtk_dmx_path = dumex2_to_path(mtk_demux2);
	ret = atsc3_init_alp(mtk_dmx_path->path_id);
	if (ret < 0) {
		MTK_DMX_ERR("atsc3_init_alp is failed\n");

		return -EIO;
	}

	/* base part */
	dvb_demux2->protocol = DMX2_FORMAT_ATSC3;

	memset(dvb_demux2->filter_caps, 0, sizeof(dvb_demux2->filter_caps));
	dvb_demux2->filter_caps[DMX2_FILTER_TYPE_ALP].number = alp_flt_num;
	// TODO
	dvb_demux2->filter_caps[DMX2_FILTER_TYPE_IP].number = alp_flt_num;
	dvb_demux2->filternum =
		dvb_demux2->feednum =
		atsc3_total_filter_num(dvb_demux2->filter_caps);

	dvb_demux2->priv =
		kzalloc(sizeof(struct mtk_atsc3_dmx_private), GFP_KERNEL);
	if (!dvb_demux2->priv) {
		MTK_DMX_ERR("allocate private data fail\n");

		return -ENOMEM;
	}

	dvb_demux2->feed_ops = &_dmx_feed_ops;

	/* derived part */
	mtk_demux2->capabilities =
		DMX2_CAP_IN_FRONTEND |
		DMX2_CAP_IN_MEMORY |
		DMX2_CAP_OUT_MEMORY |
		DMX2_CAP_STREAMING |
		DMX2_CAP_ALP_FILTER |
		DMX2_CAP_IP_FILTER;

	mtk_demux2->flt_ops = &_dmx_flt_ops;
	mtk_demux2->stc_ops = &_dmx_stc_ops;
	mtk_demux2->buf_ops = &_dmx_buf_ops;
	mtk_demux2->media_ops = &_dmx_media_ops;

	priv = dvb_demux2->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, "atsc3");
	if (np2) {
		ret = of_property_read_u32(np2, "stc_cw", &priv->stc_cw);
		ret = of_property_read_u32(np2, "stc_bypass_3_2", &priv->stc_bypass_3_2);
	}
	of_node_put(np2);
	of_node_put(np);

	MTK_DMX_EXIT();

	return 0;
}

int mtk_dmx_atsc3_exit(struct mtk_demux2 *mtk_demux2)
{
	int ret = 0;
	struct dvb_demux2 *dvb_demux2 = cast_to_dmx_parent(mtk_demux2);
	struct mtk_dmx_path *mtk_dmx_path = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_demux2 == NULL))
		return -EINVAL;

	mtk_dmx_path = dumex2_to_path(mtk_demux2);
	ret = atsc3_release_alp(mtk_dmx_path->path_id);
	if (ret < 0) {
		// Continue even it is failed.
		MTK_DMX_ERR("atsc3_release_alp is failed\n");
	}

	MTK_DMX_KVFREE(dvb_demux2->priv);

	MTK_DMX_EXIT();

	return 0;
}

int mtk_dvr_atsc3_init(struct mtk_dvr2 *mtk_dvr2)
{
#define DMX2_CAPS_ALPIF_FILEIN_NUMBER  "alpif_filein_numb"
	int ret = 0;
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;
	struct dmx2_device_caps dev_info = {
		.match = DMX2_CAPS_ALPIF_FILEIN_NUMBER,
	};
	struct dmx2_capability caps = {
		.count = 1,
		.device_info = &dev_info,
	};

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(mtk_dvr2->rm == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);

	/* base part */
	dvb_dvr2->protocol = DVR2_FORMAT_ATSC3;
	dvb_dvr2->priv = mtk_atsc3_dvr_private =
		kzalloc(sizeof(struct mtk_atsc3_dvr_private), GFP_KERNEL);
	if (!dvb_dvr2->priv) {
		MTK_DMX_ERR("allocate private data fail\n");

		return -ENOMEM;
	}
	if (atsc3_allocate_ref_count_info(
			&mtk_atsc3_dvr_private->pvr_info,
			mtk_dvr2->rm,
			HWCAPS_TYPE_PVR) != 0) {
		MTK_DMX_ERR("allocate pvr_info data fail\n");
		MTK_DMX_KVFREE(dvb_dvr2->priv);

		return -ENOMEM;
	}

	dvb_dvr2->ops = &_dvr_ops;

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

	ret = mtk_dvr2_query_caps(mtk_dvr2, &caps);
	if (ret == 0) {
		if (dev_info.supported)
			mtk_dvr2->capabilities |= DMX2_CAP_IN_MEMORY;
	}

	mtk_dvr2->buf_ops = &_dvr_buf_ops;

	MTK_DMX_EXIT();

	return 0;

#undef DMX2_CAPS_ALPIF_FILEIN_NUMBER
}

int mtk_dvr_atsc3_exit(struct mtk_dvr2 *mtk_dvr2)
{
	struct dvb_dvr2 *dvb_dvr2 = NULL;
	struct mtk_atsc3_dvr_private *mtk_atsc3_dvr_private = NULL;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(mtk_dvr2 == NULL))
		return -EINVAL;
	dvb_dvr2 = cast_to_dvr_parent(mtk_dvr2);

	mtk_atsc3_dvr_private = (struct mtk_atsc3_dvr_private *) dvb_dvr2->priv;
	MTK_DMX_KVFREE(mtk_atsc3_dvr_private->pvr_info.ref_count);
	MTK_DMX_KVFREE(dvb_dvr2->priv);

	MTK_DMX_EXIT();

	return 0;
}
