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

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/nospec.h>
#include <linux/spinlock.h>
#include <linux/sched/signal.h>
#include <linux/kthread.h>
#include <linux/string.h>

#include <media/dvb_ringbuffer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <media/dvb_ringbuffer.h>

#include "mdrv_adapter.h"
#include "mtk_dscmb.h"
//#include "mdrv_demux_dvb.h"

#include "dscmb_private.h"
#include <media/demux.h>

#include "mtk_dmx_rm.h"
#include "mtk_dmx_common.h"
#include "mtk_dmx_filter.h"




#ifndef U32_MAX
#define U32_MAX (0xFFFFFFFFUL)
#endif

#define DEVICE_READER_NUM   6  //3 DSC HW engine, 2 numbers for a engine
#define DEVICE_WRITER_NUM   6  //may modify in the further
#define DEVICE_USER_NUM    12

static int _ca_num = 0;

//Dmx open live+ record filter in 1 ts path
//The flag is indicated for share frontend.
static bool bCheckPurpose = true;

/*--------------------------------------------------*/
/*-----Local Type Definitions---------*/
/*--------------------------------------------------*/

/* Information on a CA slot */
struct dvb_ca_slot {
	/* current state of the CAM */
	int slot_state;

	/* mutex used for serializing access to one CI slot */
	struct mutex slot_lock;

	/* Number of CAMCHANGES that have occurred since last processing */
	atomic_t camchange_count;

	/* Type of last CAMCHANGE */
	int camchange_type;

	/* base address of CAM config */
	u32 config_base;

	/* value to write into Config Control register */
	u8 config_option;

	/* if 1, the CAM supports DA IRQs */
	u8 da_irq_supported:1;

	/* size of the buffer to use when talking to the CAM */
	int link_buf_size;

	/* buffer for incoming packets */
	struct dvb_ringbuffer rx_buffer;

	/* timer used during various states of the slot */
	unsigned long timeout;
};
/*-------------------------------------------------*/
/*-----Macro Definitions----*/
/*--------------------------------------------------*/
#define MsOS_DelayTask(x)  msleep_interruptible((unsigned int)x)


/*--------------------------------------------------*/
/*-----LOCAL VARIABLEs----*/
/*--------------------------------------------------*/

/*---------------------------------------------------*/
/*---Static Variables & Function Prototypes Declarationss----*/
/*---------------------------------------------------*/
#define DVB_CA_SLOTSTATE_NONE           0
#define DVB_CA_SLOTSTATE_UNINITIALISED  1
#define DVB_CA_SLOTSTATE_RUNNING        2
#define DVB_CA_SLOTSTATE_INVALID        3
#define DVB_CA_SLOTSTATE_WAITREADY      4
#define DVB_CA_SLOTSTATE_VALIDATE       5
#define DVB_CA_SLOTSTATE_WAITFR         6
#define DVB_CA_SLOTSTATE_LINKINIT       7

#define DVB_CA_ENG_SUPPORT  4
struct dsc_ca_crtl_info ca_crtl_info[DVB_CA_ENG_SUPPORT] ={};


/* Private CA-interface information */
struct dvb_ca_private {
	struct kref refcount;

	struct dvb_ca_en50221 *pub;

	/* the DVB device */
	struct dvb_device *dvbdev;

	/* Flags describing the interface (DVB_CA_FLAG_*) */
	u32 flags;

	/* number of slots supported by this CA interface */
	unsigned int slot_count;

	/* Flag indicating if the CA device is open */
	unsigned int open;

	unsigned int engine_id;
	unsigned int keyslot;
	//dmx path id
	unsigned int ca_source;

	unsigned long long keytoken;

	unsigned int index;

	/* mutex serializing ioctls */
	struct mutex ioctl_mutex;

	dmx_purpose_t purpose;
};

static struct mtk_dscmb_cb dsc_internal_ops = {0};

static struct mtk_dmx2dev *g_dmx_dev;

/*----------------------------------------------------------------*/
/*-    Function Definitions----*/
/*----------------------------------------------------------------*/

static int _dvb_usercopy(struct file *file,
		     unsigned int cmd, unsigned long arg,
		     int (*func)(struct file *file,
		     unsigned int cmd, void *arg))
{
	char    sbuf[128];
	void    *mbuf = NULL;
	void    *parg = NULL;
	int     err  = -EINVAL;

	/*  Copy arguments into temp kernel buffer  */
	switch (_IOC_DIR(cmd)) {
	case _IOC_NONE:
		/*
		 * For this command, the pointer is actually an integer
		 * argument.
		 */
		parg = (void *) arg;
		break;
	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
	case _IOC_WRITE:
	case (_IOC_WRITE | _IOC_READ):
		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
			parg = sbuf;
		} else {
			/* too big to allocate from stack */
			mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
			if (mbuf == NULL)
				return -ENOMEM;

			parg = mbuf;
		}

		err = -EFAULT;
		if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
			goto out;
		break;
	}

	/* call driver */
	err = func(file, cmd, parg);
	if (err == -ENOIOCTLCMD)
		err = -ENOTTY;

	if (err < 0)
		goto out;

	/*  Copy results into user buffer  */
	switch (_IOC_DIR(cmd)) {
	case _IOC_READ:
	case (_IOC_WRITE | _IOC_READ):
		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
			err = -EFAULT;
		break;
	}

out:
	kfree(mbuf);
	return err;
}

static inline int rm_of_read(int width, const struct device_node *np,
			const char *propname, void *value)
{
	int ret = -1;

	if (width == sizeof(u16))
		ret = of_property_read_u16(np, propname, (u16 *)value);
	else if (width == sizeof(u32))
		ret = of_property_read_u32(np, propname, (u32 *)value);
	if (ret) {
		DSCMB_DEBUG("Failed to get %s\n", propname);
		return -1;
	}
	return 0;
}

static bool _is_tlv_path(u16 path_id)
{
	if (path_id >= TLV_TSIF_0 && path_id <= TLV_TSIF_2)
		return true;

	return false;
}

static void _get_delay_pid_info(dmx_purpose_t purpose,
			struct delay_pid_info **delay_info, u8 engid)
{
	if (purpose == PURPOSE_PLAYBACK)
		*delay_info = &ca_crtl_info[engid].playback_pid_info;
	else if (purpose == PURPOSE_RECORD)
		*delay_info = &ca_crtl_info[engid].rec_pid_info;
	else
		*delay_info = &ca_crtl_info[engid].pid_info;
}

static bool _dscmb_compare_ip_addr(struct dmx2_ip_addr *ip_addr1, struct dmx2_ip_addr *ip_addr2)
{
	const u8 len = IPV6_Len;
	u8 *src_ip1, *src_ip2;
	u8 *dst_ip1, *dst_ip2;

	if ((!ip_addr1) || (!ip_addr2)) {
		DSCMB_ERR("ip_addr1/ip_addr2 is NULL.\n");
		return false;
	}
	src_ip1 = &(ip_addr1->src_ip_addr.v6[0]);
	dst_ip1 = &(ip_addr1->dst_ip_addr.v6[0]);
	src_ip2 = &(ip_addr2->src_ip_addr.v6[0]);
	dst_ip2 = &(ip_addr2->dst_ip_addr.v6[0]);

	if ((memcmp(src_ip1, src_ip2, len)) ||
		(memcmp(dst_ip1, dst_ip2, len)) ||
		(ip_addr1->src_port != ip_addr2->src_port) ||
		(ip_addr1->dst_port != ip_addr2->dst_port)) {
		DSCMB_ERR("compare ip failed.\n");
		return false;
	}

	return true;
}

static int _dscmb_copy_tlv_pid_info(struct dvb_ca_private *ca,
	u16 pid, struct dmx2_ip_addr *ip_addr)
{
	__u8 i;
	struct delay_pid_info *delay_info;

	if ((!ca) || (!ip_addr)) {
		DSCMB_ERR("ca/ip_addr is NULL.\n");
		return -EFAULT;
	}

	for (i = 0; i < CA_CRTL_SLOT_MAX; i++) {
		_get_delay_pid_info(ca->purpose, &delay_info, ca->engine_id);
		if (delay_info->pid[i] == 0) {
			delay_info->pid[i] = pid;
			delay_info->ip_addr[i].src_port = ip_addr->src_port;
			delay_info->ip_addr[i].dst_port = ip_addr->dst_port;
			ca_crtl_info[ca->engine_id].keytoken = ca->keytoken;

			memcpy(delay_info->ip_addr[i].src_ip_addr.v6,
				ip_addr->src_ip_addr.v6,
				IPV6_Len);

			memcpy(delay_info->ip_addr[i].dst_ip_addr.v6,
				ip_addr->dst_ip_addr.v6,
				IPV6_Len);

			return i;
		}
	}

	DSCMB_ERR("Pid array is full\n");
	return -EPERM;
}

static bool release_ca_resource(struct dvb_ca_private *ca)
{
	__u8 engid = ca->engine_id;
	bool ret = false;

	if (engid >= DVB_CA_ENG_SUPPORT) {
		DSCMB_INFO("ca eng id is invalid %u\n", engid);
		return ret;
	}

	if (ca->keytoken == 0) {
		DSCMB_INFO("ca idx : %d: keytoken value is already 0\n", ca->index);
		return ret;
	}

	if (ca_crtl_info[engid].process_num)
		ca_crtl_info[engid].process_num--;

	if (ca_crtl_info[engid].process_num > 0) {
		DSCMB_INFO("ca idx : %d: process num: %d,"
			"there are still other processes using the same eng id %d\n",
			ca->index, ca_crtl_info[engid].process_num, engid);
		goto exit;
	}

	if (ca->keyslot != DRV_DSCMB_FLT_NULL)
		MDrv_DSCMB_FltFree(engid, ca->keyslot);
	MDrv_DSCMB_EngFree(engid);
	memset(&ca_crtl_info[engid], 0, sizeof(struct dsc_ca_crtl_info));
	ret = true;

exit:
	ca->keyslot = DRV_DSCMB_FLT_NULL;
	ca->engine_id = DRV_DSCMB_ENG_NULL;
	ca->keytoken = 0;
	ca->ca_source = DEMUX_PATH_NULL;
	ca->flags = CA_DES_UNUSED;
	return ret;
}

static void dvb_ca_private_release(struct kref *ref)
{
	struct dvb_ca_private *ca;

	ca = container_of(ref, struct dvb_ca_private, refcount);
	release_ca_resource(ca);
	ca->open = 0;
	DSCMB_INFO("release ca resource %d\n", ca->index);
}

static void dvb_ca_private_get(struct dvb_ca_private *ca)
{
	if (ca->open == 0) {
		kref_init(&ca->refcount);
		ca->open = 1;
		DSCMB_INFO("allocate ca resource %d\n", ca->index);
	} else {
		kref_get(&ca->refcount);
	}
}

static void dvb_ca_private_put(struct dvb_ca_private *ca)
{
	if (ca->open == 0)
		return;

	kref_put(&ca->refcount, dvb_ca_private_release);
}

int mtk_dscmb_register_cb(struct mtk_dscmb_cb *mtk_dsc_cb)
{
	if (mtk_dsc_cb == NULL) {
		DSCMB_ERR("mtk_dsc_cb is NULL\n");
		return -EFAULT;
	}
	dsc_internal_ops.get_filter_id = mtk_dsc_cb->get_filter_id;
	dsc_internal_ops.set_ca_ctrl = mtk_dsc_cb->set_ca_ctrl;
	dsc_internal_ops.set_secure_mode = mtk_dsc_cb->set_secure_mode;
	dsc_internal_ops.set_crypto_type = mtk_dsc_cb->set_crypto_type;
	dsc_internal_ops.get_tlv_path = mtk_dsc_cb->get_tlv_path;
	dsc_internal_ops.get_dst_type = mtk_dsc_cb->get_dst_type;
	return 0;
}

static int _eng_check(struct dvb_ca_private *ca)
{
	if (ca->engine_id >= DVB_CA_ENG_SUPPORT) {
		DSCMB_ERR("Wrong CA engine %u\n", ca->engine_id);
		return -EIO;
	} else if (ca->keyslot >= KEYSKOT_NUM) {
		DSCMB_ERR("Wrong CA keyslot %u\n", ca->keyslot);
		return -EIO;
	} else {
		return 0;
	}
}

static int mdrv_dscmb_clear_vpas_pipelineid(struct dvb_ca_private *ca,
		struct dmx_query_info *query_info, __u32 dmx_flt_id)
{
	struct dmx_secure_info secure_info;
	enum filter_dst_type dst_type;
	int ret = 0;

	dst_type = dsc_op(
				   dsc_internal_ops,
				   get_dst_type,
				   dmx_flt_id);
	/* Record doesn't need to set secure mode */
	if ((query_info->purpose != PURPOSE_RECORD) &&
			(dst_type == FILTER_DST_TYPE_VIDEO)) {
		DSCMB_DEBUG("stop set_secure_mode ");
		secure_info.enable = FALSE;
		secure_info.pipeline_id = 0;
		secure_info.filter_id = dmx_flt_id;
		secure_info.key_token = ca->keytoken;
		ret = dsc_op(
				  dsc_internal_ops,
				  set_secure_mode,
				  secure_info,
				  query_info->purpose);

		if (ret <= 0)
			DSCMB_ERR(" dmx stop set_secure_mode fail ret = 0x%x\n", ret);
	}

	return ret;
}

static int mdrv_dscmb_backup_set_tlv_avsrc(
		struct dvb_ca_private *ca,
		struct dmx_tlv_path tlv_path,
		bool set_tlv_avsrc)
{
	if (ca == NULL)
		return -EINVAL;
	if (_eng_check(ca) < 0)
		return -EINVAL;

	if ((tlv_path.type == FILTER_DST_TYPE_VIDEO) ||
		(tlv_path.type == FILTER_DST_TYPE_AUDIO)) {
		ca_crtl_info[ca->engine_id].set_tlv_avsrc = set_tlv_avsrc;
		DSCMB_DEBUG(
			"ca_crtl_info[%d].set_tlv_avsrc %d\n",
			ca->engine_id,
			ca_crtl_info[ca->engine_id].set_tlv_avsrc);
	}

	return 0;
}

static int mdrv_dscmb_ext_ioctl(struct file *file, unsigned int cmd,
			    void *parg)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int ret = 0;

	DSCMB_DEBUG("cmd = 0x%x:\n", cmd);

	switch (cmd) {
	case CA_GET_SLOT_INFO:
		{
			struct ca_slot_info *info = parg;

			info->type = CA_DESCR;
			info->flags = ca->flags;
			info->num = _ca_num;
			DSCMB_DEBUG("CA_GET_SLOT_INFO: _ca_num %d\n", _ca_num);
		}
		break;

	case CA_SET_SOURCE:
		{
			unsigned int *source = (unsigned int *)parg;

			ca->ca_source = *source;
			ca->flags = CA_DES_USED;
			DSCMB_INFO("CA_SET_SOURCE:*keytoken=0x%llx, source=%d\n",
				ca->keytoken, ca->ca_source);
			ret = MDrv_DSCMB2_Init();
			if (ret == FALSE)
				DSCMB_ERR("DSCMB_Init fail\n");
		}
		break;

	case CA_SET_KEYTOKEN:
		{
			unsigned long long *keytoken = (unsigned long long *)parg;
			struct dmx_ca_info ca_info = { 0 };
			__u32 encrypt_type = 0;
			struct dmx_crypto_info crypto_info;
			__u8 engid;

			ca->keytoken = *keytoken;
			ca_info.path_id = ca->ca_source;
			ca->flags = CA_DES_USED;
			bCheckPurpose = true;

			if (*keytoken & MarkReordKeytoken)
				ca->purpose = PURPOSE_RECORD;
			else if (*keytoken & MarkLiveKeytoken)
				ca->purpose = PURPOSE_PLAYBACK;
			else {
				bCheckPurpose = false;
				ca->purpose = PURPOSE_PLAYBACK_RECORD;
			}

			ca->keytoken &= ~(MarkLiveKeytoken | MarkReordKeytoken);
			//default algo is AES
			ca->engine_id = MDrv_DSCMB_EngAlloc(
				E_DSCMB_MAIN_ALGO_AES, 0, ca->keytoken);
			engid = ca->engine_id;
			if (ca->engine_id >= DVB_CA_ENG_SUPPORT)
				return -EINVAL;

			if (ca_crtl_info[ca->engine_id].process_num == 0) {
				ca_crtl_info[engid].path_id = ca_info.path_id = ca->ca_source;
				ca_crtl_info[engid].eng_id = ca_info.eng_id = engid;
				ca_info.pass_through = true;
				ca_crtl_info[engid].used = true;
				ca_crtl_info[engid].keytoken = ca->keytoken;
				ca_crtl_info[engid].purpose = ca->purpose;
				ca_crtl_info[ca->engine_id].set_tlv_avsrc = ca_info.set_tlv_avsrc;
				DSCMB_DEBUG(
					"ca_crtl_info[%d].set_tlv_avsrc %d\n",
					ca->engine_id,
					ca_crtl_info[ca->engine_id].set_tlv_avsrc);

				if (_is_tlv_path(ca_info.path_id))
					MDrv_DSCMB2_SetTLVPath(ca->engine_id, true);
				else
					MDrv_DSCMB2_SetTLVPath(ca->engine_id, false);

				ret = dsc_op(dsc_internal_ops, set_ca_ctrl, ca_info);
				if (ret < 0)
					DSCMB_ERR(" dmx set_ca_ctrl fail ret = %d\n", ret);

				ca->keyslot = MDrv_DSCMB_FltAlloc(
					ca->engine_id, ca->keytoken,
					E_DSCMB_FLT_2_KEYS, &encrypt_type);
			} else
				ca->keyslot = CA_KEYSLOT;
			ca_crtl_info[ca->engine_id].process_num++;
			ca_crtl_info[ca->engine_id].dscmb_flt_id = ca->keyslot;

			crypto_info.path_id = ca->ca_source;
			crypto_info.type = encrypt_type;
			ret = dsc_op(dsc_internal_ops, set_crypto_type, crypto_info);
			if (ret < 0)
				DSCMB_ERR("dmx_set_crypto_type fail ret = %d\n", ret);

			DSCMB_INFO("[engId:%d]SetKeyToken done: "
					"[keytoken, src, slot, process_num, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d",
					ca->engine_id, ca->keytoken, ca->ca_source, ca->keyslot,
					ca_crtl_info[ca->engine_id].process_num,
					ca->purpose == PURPOSE_PLAYBACK,
					ca->purpose == PURPOSE_RECORD,
					ca->purpose == PURPOSE_PLAYBACK_RECORD);
		}
		break;

	case CA_ADD_PID:
		{
			unsigned short *pid = (unsigned short *)parg;
			struct dmx_query_info query_info;
			struct dmx_secure_info secure_info;
			__u32    dmx_flt_id;
			__u32    pipe_id;
			__u8   i = 0, engid;
			struct delay_pid_info *delay_info;

			engid = ca->engine_id;
			memset(&query_info, 0, sizeof(struct dmx_query_info));
			delay_info = NULL;

			if (_eng_check(ca) < 0)
				return -EINVAL;

			pipe_id = 0;

			query_info.path_id = ca->ca_source;
			query_info.pid = *pid;
			query_info.purpose = ca->purpose;

			_get_delay_pid_info(ca->purpose, &delay_info, ca->engine_id);

			for (i = 0; i < CA_CRTL_SLOT_MAX; i++) {
				if (delay_info->pid[i] == 0) {
					delay_info->pid[i] = *pid;
					ca_crtl_info[engid].keytoken = ca->keytoken;
					ca_crtl_info[engid].dscmb_flt_id = ca->keyslot;
					break;
				}
			}

			if (i == CA_CRTL_SLOT_MAX) {
				DSCMB_ERR(" delay pid is bigger than CA_CRTL_SLOT_MAX\n");
				return -EINVAL;
			}

			dmx_flt_id = dsc_op(dsc_internal_ops, get_filter_id, &query_info);
			DSCMB_DEBUG("CA_ADD_PID :dmx_flt_id = %d *pid =%d\n", dmx_flt_id, *pid);

			delay_info->delay_pid_num++;

			if (dmx_flt_id >= PIDFLT_NUM) {
				DSCMB_ERR("AddPid fail: dmx flt id = %d! Wait pid start\n",
					dmx_flt_id);
				return 0;
			}

			ret = MDrv_DSCMB_FltConnectFltId(ca->keytoken, ca->engine_id, ca->keyslot,
			dmx_flt_id, &pipe_id);

			DSCMB_DEBUG("CA_ADD_PID: pipe_id = %d\n", pipe_id);

			/* Record doesn't need to set secure mode */
			if (query_info.purpose != PURPOSE_RECORD) {
				DSCMB_DEBUG("CA_ADD_PID: start set_secure_mode ");
				if (pipe_id != 0) {
					secure_info.enable = TRUE;
					secure_info.pipeline_id = pipe_id;
					secure_info.filter_id = dmx_flt_id;
					secure_info.key_token = ca->keytoken;
					ret = dsc_op(
						dsc_internal_ops,
						set_secure_mode,
						secure_info,
						query_info.purpose);
				}

				if (ret < 0)
					DSCMB_ERR(" dmx set_secure_mode fail ret = 0x%x\n", ret);
			}

			DSCMB_INFO("[engId:%d]AddPid done: "
					"[keytoken, src, slot, fltId, pid, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d, %d",
					ca->engine_id, ca->keytoken, ca->ca_source, ca->keyslot,
					dmx_flt_id, query_info.pid,
					ca->purpose == PURPOSE_PLAYBACK,
					ca->purpose == PURPOSE_RECORD,
					ca->purpose == PURPOSE_PLAYBACK_RECORD);
		}
		break;

	case CA_REMOVE_PID:
		{
			unsigned short *pid = (unsigned short *)parg;
			struct dmx_query_info query_info;
			__u32    dmx_flt_id;
			__u8 i = 0, engid = 0;
			struct dscmb_config_in config_in;
			struct dscmb_config_out config_out;
			struct delay_pid_info *delay_info;

			delay_info = NULL;
			engid = ca->engine_id;
			memset(&query_info, 0, sizeof(struct dmx_query_info));
			memset(&config_in, 0, sizeof(struct dscmb_config_in));
			memset(&config_out, 0, sizeof(struct dscmb_config_out));

			if (_eng_check(ca) < 0)
				return -EINVAL;

			query_info.path_id = ca->ca_source;
			query_info.pid = *pid;
			query_info.purpose = ca->purpose;

			_get_delay_pid_info(ca->purpose, &delay_info, engid);

			if (delay_info->delay_pid_num > 0) {
				for (i = 0; i < CA_CRTL_SLOT_MAX; i++) {
					if (delay_info->pid[i] == *pid) {
						delay_info->pid[i] = 0;
						delay_info->delay_pid_num--;
						memset(&delay_info->ip_addr[i],
							0, sizeof(struct dmx2_ip_addr));
						break;
					}
				}
			}

			dmx_flt_id = dsc_op(dsc_internal_ops, get_filter_id, &query_info);
			if (dmx_flt_id >= PIDFLT_NUM) {
				DSCMB_ERR("Error dmx flt id = %d!\n", dmx_flt_id);
				return -EINVAL;
			}

			config_in.u32EngId = ca->engine_id;
			config_in.u32DmxFltId = dmx_flt_id;
			config_in.u32DscmbId = ca->keyslot;
			ret = MDrv_DSCMB2_GetConfig(config_in, &config_out);

			if (ret == true) {
				DSCMB_INFO("dsc config: keyValid:0x%x u32FltHit:0x%x\n",
				config_out.u32keyValid, config_out.u32FltHit);
				DSCMB_INFO("dsc config: SCMB:0x%x , value:0x%x\n",
				config_out.u32SCMB, config_out.u32DBGVal1);
			}

			ret = MDrv_DSCMB_FltDisconnectFltId(
			ca->engine_id, ca->keyslot, dmx_flt_id);
			if (ret <= 0)
				DSCMB_ERR("DisconnectFltId fail ret = 0x%x\n", ret);

			ret = mdrv_dscmb_clear_vpas_pipelineid(ca, &query_info, dmx_flt_id);

			DSCMB_INFO("[engId:%d]RemovePid done: "
					"[keytoken, src, slot, fltId, pid, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d, %d",
					ca->engine_id, ca->keytoken, ca->ca_source, ca->keyslot,
					dmx_flt_id, query_info.pid,
					ca->purpose == PURPOSE_PLAYBACK,
					ca->purpose == PURPOSE_RECORD,
					ca->purpose == PURPOSE_PLAYBACK_RECORD);

		}
		break;

	case CA_ADD_TLVPID:
		{
			struct dscmb_tlvpid *tlv_pid;
			struct dmx_query_info query_info;
			struct dmx_secure_info secure_info;
			struct delay_pid_info *delay_info;

			__u32  dmx_flt_id;
			__u32  pipe_id = 0;
			int    i = 0;
			__u8   engid = 0;

			struct dmx2_ip_addr ip_addr;
			struct dmx_tlv_path tlv_path = { 0 };
			struct dmx_ca_info ca_info = { 0 };
			int ret = 0;

			if (_eng_check(ca) < 0)
				return -EINVAL;
			engid =  ca->engine_id;
			delay_info = NULL;
			tlv_pid = (struct dscmb_tlvpid *)parg;
			ip_addr.src_port = tlv_pid->ip_addr.src_port;
			ip_addr.dst_port = tlv_pid->ip_addr.dst_port;

			for (i = 0; i < IPV6_Len; i++) {
				ip_addr.src_ip_addr.v6[i] = tlv_pid->ip_addr.src_ip_addr.v6[i];
				ip_addr.dst_ip_addr.v6[i] = tlv_pid->ip_addr.dst_ip_addr.v6[i];
			}
			memset(&query_info, 0, sizeof(struct dmx_query_info));
			query_info.path_id = ca->ca_source;
			query_info.pid = tlv_pid->pid;
			query_info.ip_addr = &ip_addr;
			query_info.purpose = ca->purpose;

			_get_delay_pid_info(ca->purpose, &delay_info, engid);

			ret = _dscmb_copy_tlv_pid_info(ca, tlv_pid->pid, &ip_addr);

			if (ret < 0) {
				DSCMB_ERR("_dscmb_copy_tlv_pid_info fail, err: %d\n", ret);
				return -EINVAL;
			}

			delay_info->delay_pid_num++;
			dmx_flt_id = dsc_op(dsc_internal_ops, get_filter_id, &query_info);

			if (dmx_flt_id >= PIDFLT_NUM) {
				DSCMB_ERR("dmx flt id = %d! Wait pid start\n", dmx_flt_id);
				return 0;
			}

			//set TLV AVFIFO source from DSCMB
			tlv_path.filter_id = dmx_flt_id;
			tlv_path.path_id = ca->ca_source;
			ret = dsc_op(dsc_internal_ops, get_tlv_path, &tlv_path);
			if (ret < 0) {
				DSCMB_WARN("dmx get_tlv_path fail ret = 0x%x\n", ret);
				ca_info.set_tlv_avsrc = false;
			} else {
				ca_info.set_tlv_avsrc = true;
				mdrv_dscmb_backup_set_tlv_avsrc(
						ca,
						tlv_path,
						ca_info.set_tlv_avsrc);
			}

			ca_info.path_id = ca->ca_source;
			ca_info.eng_id = ca->engine_id;
			ca_info.pass_through = true;
			ca_info.fifo_id[tlv_path.type] = tlv_path.idx;
			ca_info.fifo_id_used[tlv_path.type] = true;
			ca_crtl_info[ca_info.eng_id].fifo_id[tlv_path.type] = tlv_path.idx;
			ca_crtl_info[ca_info.eng_id].fifo_id_used[tlv_path.type] = true;

			ret = dsc_op(dsc_internal_ops, set_ca_ctrl, ca_info);
			if (ret < 0) {
				DSCMB_ERR(" dmx set_ca_ctrl fail ret = %d\n", ret);
				return -EINVAL;
			}

			ret = MDrv_DSCMB_FltConnectFltId(ca->keytoken, ca->engine_id, ca->keyslot,
			dmx_flt_id, &pipe_id);

			DSCMB_DEBUG("CA_ADD_TLVPID: pipe_id = %d\n", pipe_id);

			/* Record doesn't need to set secure mode */
			if (query_info.purpose != PURPOSE_RECORD) {
				DSCMB_DEBUG("start set_secure_mode ");
				if (pipe_id != 0) {
					secure_info.enable = TRUE;
					secure_info.pipeline_id = pipe_id;
					secure_info.filter_id = dmx_flt_id;
					secure_info.key_token = ca->keytoken;
					ret = dsc_op(
						dsc_internal_ops,
						set_secure_mode,
						secure_info,
						query_info.purpose);
				}

				if (ret <= 0)
					DSCMB_ERR("dmx set_secure_mode fail ret = 0x%x\n", ret);
			}
			DSCMB_INFO("[engId:%d]AddTlvPid done: "
					"[keytoken, src, slot, fltId, pid, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d, %d",
					ca->engine_id, ca->keytoken, ca->ca_source, ca->keyslot,
					dmx_flt_id, query_info.pid,
					ca->purpose == PURPOSE_PLAYBACK,
					ca->purpose == PURPOSE_RECORD,
					ca->purpose == PURPOSE_PLAYBACK_RECORD);

		}

		break;

	case CA_REMOVE_TLVPID:
		{
			struct dscmb_tlvpid *tlv_pid = (struct dscmb_tlvpid *)parg;
			struct dmx_query_info query_info;
			__u32    dmx_flt_id;
			__u8     i, engid = 0;
			struct dmx2_ip_addr ip_addr;
			struct dscmb_config_in config_in = {0};
			struct dscmb_config_out config_out = {0};
			struct delay_pid_info *delay_info;

			if (_eng_check(ca) < 0)
				return -EINVAL;

			engid = ca->engine_id;
			ip_addr.src_port = tlv_pid->ip_addr.src_port;
			ip_addr.dst_port = tlv_pid->ip_addr.dst_port;
			delay_info = NULL;

			for (i = 0; i < IPV6_Len; i++) {
				ip_addr.src_ip_addr.v6[i] = tlv_pid->ip_addr.src_ip_addr.v6[i];
				ip_addr.dst_ip_addr.v6[i] = tlv_pid->ip_addr.dst_ip_addr.v6[i];
			}
			memset(&query_info, 0, sizeof(struct dmx_query_info));
			query_info.path_id = ca->ca_source;
			query_info.pid = tlv_pid->pid;
			query_info.ip_addr = &ip_addr;
			query_info.purpose = ca->purpose;

			dmx_flt_id = dsc_op(dsc_internal_ops, get_filter_id, &query_info);

			_get_delay_pid_info(ca->purpose, &delay_info, engid);

			if (delay_info->delay_pid_num > 0) {
				for (i = 0; i < CA_CRTL_SLOT_MAX; i++) {
					if (delay_info->pid[i] == tlv_pid->pid) {
						delay_info->pid[i] = 0;
						delay_info->delay_pid_num--;
						memset(&delay_info->ip_addr[i]
							, 0, sizeof(struct dmx2_ip_addr));
						break;
					}
				}
			}

			if (dmx_flt_id >= PIDFLT_NUM) {
				DSCMB_ERR("Error dmx flt id = %d!\n", dmx_flt_id);
				return -EINVAL;
			}

			config_in.u32EngId = ca->engine_id;
			config_in.u32DmxFltId = dmx_flt_id;
			config_in.u32DscmbId = ca->keyslot;
			ret = MDrv_DSCMB2_GetConfig(config_in, &config_out);

			if (ret == true) {
				DSCMB_INFO("dsc config: keyValid:0x%x FltHit:0x%x\n",
				config_out.u32keyValid, config_out.u32FltHit);
				DSCMB_INFO("dsc config: SCMB:0x%x , value:0x%x\n",
				config_out.u32SCMB, config_out.u32DBGVal1);
			}

			ret = MDrv_DSCMB_FltDisconnectFltId(
			ca->engine_id, ca->keyslot, dmx_flt_id);
			if (ret <= 0)
				DSCMB_ERR("DisconnectFltId fail ret = 0x%x\n", ret);

			ret = mdrv_dscmb_clear_vpas_pipelineid(ca, &query_info, dmx_flt_id);
			DSCMB_INFO("[engId:%d]RemoveTlvPid done: "
					"[keytoken, src, slot, fltId, pid, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d, %d",
					ca->engine_id, ca->keytoken, ca->ca_source, ca->keyslot,
					dmx_flt_id, query_info.pid,
					ca->purpose == PURPOSE_PLAYBACK,
					ca->purpose == PURPOSE_RECORD,
					ca->purpose == PURPOSE_PLAYBACK_RECORD);

		}
		break;

	case CA_DSC_SET_ENGINE:
		{
			struct dscmb_engine *dsc_engine =
					(struct dscmb_engine *) parg;
			unsigned int engid = dsc_engine->engine_id;

			DSCMB_INFO("CA_DSC_SET_ENGINE: engid=%u  main_algo = %u\n"
			, dsc_engine->engine_id, dsc_engine->main_algo);
			engid = MDrv_DSCMB2_EngAlloc((DSCMB_MainAlgo_Type)dsc_engine->main_algo, engid);
			if (engid != dsc_engine->engine_id) {
				DSCMB_INFO(" engine set fail, ret engid = %u\n", engid);
				ret = -EFAULT;
			}
		}
		break;

	case CA_DSC_GET_KEYSLOT:
		{
			struct dscmb_keyslot *dsc_keyslot =
					(struct dscmb_keyslot *) parg;

			if (_eng_check(ca) < 0)
				return -EINVAL;

			dsc_keyslot->engine_id= ca->engine_id;
			dsc_keyslot->keyslot = ca->keyslot;

			DSCMB_INFO("CA_DSC_GET_KEYSLOT  engid=%u  type = %u keyslot = %d\n"
			, dsc_keyslot->engine_id, dsc_keyslot->flt_type, dsc_keyslot->keyslot);
		}
		break;

	case CA_DSC_FREE_KEYSLOT:
		{
			struct dscmb_keyslot *dsc_keyslot =
					(struct dscmb_keyslot *) parg;
			DSCMB_INFO("CA_DSC_FREE_KEYSLOT: engid=%u  keyslot = %u\n"
			, dsc_keyslot->engine_id, dsc_keyslot->keyslot);
			ret = MDrv_DSCMB_FltFree(dsc_keyslot->engine_id,
			dsc_keyslot->keyslot);
		}
		break;

	case CA_DSC_CONNECT_FLT:
		{
			struct dscmb_connect *dsc_connect =
					(struct dscmb_connect *) parg;

			DSCMB_INFO("CA_DSC_CONNECT_FLT: engid=%u  keyslot=%u flt id = %u\n",
				 dsc_connect->engine_id, dsc_connect->keyslot,
				 dsc_connect->dmx_flt_id);
			ret = MDrv_DSCMB2_FltConnectFltId(dsc_connect->engine_id,
			dsc_connect->keyslot, dsc_connect->dmx_flt_id);
		}
		break;

	case CA_DSC_DISCONNECT_FLT:
		{
			struct dscmb_connect *dsc_connect =
					(struct dscmb_connect *) parg;
			DSCMB_INFO("CA_DSC_DISCONNECT_FLT: engid=%u ,flt id = %u\n",
				dsc_connect->engine_id, dsc_connect->dmx_flt_id);
			ret = MDrv_DSCMB_FltDisconnectFltId(dsc_connect->engine_id,
			dsc_connect->keyslot, dsc_connect->dmx_flt_id);
		}
		break;

	case CA_DSC_SETKEY:
		{
			struct dscmb_key_iv *dscmb_setkey =
				(struct dscmb_key_iv *) parg;

			char *key = kzalloc(sizeof(char)*DSCMB_KEY_LENGTH, GFP_KERNEL);

			ret = copy_from_user(key, (void __user *)
				(size_t)(dscmb_setkey->key), DSCMB_KEY_LENGTH*sizeof(char));
			if (ret < 0) {
				kfree(key);
				DSCMB_ERR("copy (%p)fail\n", key);
				return -EFAULT;
			}

			ret = MDrv_DSCMB2_FltKeySet(dscmb_setkey->engine_id,
			dscmb_setkey->keyslot, (DSCMB_Key_Type)dscmb_setkey->key_type, key);
			kfree(key);
		}
		break;

	case CA_DSC_SETTYPE:
		{
			struct dscmb_set_type *dsc_type =
					(struct dscmb_set_type *) parg;

			DSCMB_INFO("CA_DSC_SETTYPE: engid=%u keyslot = %u type = %u\n"
			, dsc_type->engine_id, dsc_type->keyslot, dsc_type->dsc_type);
			ret = MDrv_DSCMB2_FltTypeSet(dsc_type->engine_id,
			dsc_type->keyslot, (DSCMB_Type)dsc_type->dsc_type);
		}
		break;

	case CA_DSC_RESET_KEY:
		{
			struct dscmb_reset_key *dsc_reset_key =
					(struct dscmb_reset_key *) parg;

			DSCMB_INFO("CA_DSC_RESET_KEY: RESET_KEY:\n");
			ret = MDrv_DSCMB2_FltKeyReset(dsc_reset_key->engine_id,
			dsc_reset_key->keyslot, (DSCMB_Key_Type)dsc_reset_key->key_type);
		}
		break;

	case CA_DSC_SETIV:
		{
			struct dscmb_key_iv *dscmb_setiv =
				(struct dscmb_key_iv *) parg;

			char *iv = kzalloc(sizeof(char)*DSCMB_IV_LENGTH, GFP_KERNEL);

			ret = copy_from_user(iv, (void __user *)
				(size_t)(dscmb_setiv->iv), DSCMB_IV_LENGTH*sizeof(char));
			if (ret < 0) {
				kfree(iv);
				DSCMB_ERR("copy (%p)fail\n", iv);
				return -EFAULT;
			}

			ret = MDrv_DSCMB2_FltIVSet(dscmb_setiv->engine_id,
			dscmb_setiv->keyslot, (DSCMB_Key_Type)dscmb_setiv->key_type, iv);
			kfree(iv);
		}
		break;

	case CA_DSC_SETHDCPIV:
		{
			struct dscmb_key_iv *dscmb_setiv =
				(struct dscmb_key_iv *) parg;

			char *iv = kzalloc(sizeof(char)*DSCMB_IV_LENGTH, GFP_KERNEL);

			ret = copy_from_user(iv, (void __user *)
				(size_t)(dscmb_setiv->iv), DSCMB_IV_LENGTH*sizeof(char));
			if (ret < 0) {
				kfree(iv);
				DSCMB_ERR("copy (%p)fail\n", iv);
				return -EFAULT;
			}

			ret = MDrv_DSCMB2_HDCP2_SetRIV(dscmb_setiv->engine_id, iv);
			kfree(iv);
		}
		break;

	case CA_DSC_SETMULT2:
		{

			struct dscmb_mult2 *dscmb_multi2 =
			dscmb_multi2 = (struct dscmb_mult2 *) parg;

			char *key = kzalloc(sizeof(char)*DSCMB_MULTI2_SYSKEY_LENGTH, GFP_KERNEL);

			DSCMB_INFO("CA_DSC_SETMULT2 dscmb_multi2.round = %d\n", dscmb_multi2->round);

			ret = copy_from_user(key, (void __user *)
			(size_t)(dscmb_multi2->key), DSCMB_MULTI2_SYSKEY_LENGTH*sizeof(char));
			if (ret < 0) {
				kfree(key);
				DSCMB_ERR("copy (%p)fail\n", key);
				return -EFAULT;
			}

			ret = MDrv_DSCMB2_Multi2_SetSystemKey(dscmb_multi2->engine_id, key);
			ret = MDrv_DSCMB2_Multi2_SetRound(dscmb_multi2->engine_id,
				dscmb_multi2->round);
			kfree(key);
		}
		break;

	case CA_DSC_SETPATH:
		{
			struct dscmb_ca_crtl *dsc_crtl = (struct dscmb_ca_crtl *) parg;
			struct dmx_ca_info ca_info = { 0 };

			ca_info.eng_id = dsc_crtl->engine_id;
			ca_info.path_id = dsc_crtl->tsif;
			ca_info.pass_through = true;

			DSCMB_INFO("CA_DSC_SETPATH: CA_SET_PATH\n");
			ret = mtk_dmx_set_ca_ctrl(ca_info);
			if (ret < 0)
				DSCMB_DEBUG("dmx set_ca_ctrl fail ret = %d\n", ret);
		}
		break;

	case CA_DSC_FREE_ENGINE:
		{
			struct dscmb_engine *dsc_engine =
					(struct dscmb_engine *) parg;

			DSCMB_INFO("CA_DSC_FREE_ENGINE: engid=%u\n", dsc_engine->engine_id);

			if (dsc_engine->engine_id < DVB_CA_ENG_SUPPORT) {
				MDrv_DSCMB_EngFree(dsc_engine->engine_id);
				memset(&ca_crtl_info[dsc_engine->engine_id]
					, 0, sizeof(struct dsc_ca_crtl_info));
			}
		}
		break;

	case CA_DISCONNECT_KEYTOKEN:
		{
			struct dmx_ca_info ca_info = { 0 };
			__u8 engid, pathid;
			unsigned long long keytoken;
			int keyslot;
			dmx_purpose_t purpose;

			if (_eng_check(ca) < 0)
				return -EINVAL;

			engid = ca->engine_id;
			pathid = ca->ca_source;
			keytoken = ca->keytoken;
			keyslot = ca->keyslot;
			purpose = ca->purpose;

			if (engid  >= DVB_CA_ENG_SUPPORT)
				return 0;

			ca_info.path_id = pathid;
			ca_info.eng_id = engid;
			ca_info.pass_through = false;
			ca_info.set_tlv_avsrc = ca_crtl_info[engid].set_tlv_avsrc;
			memcpy(ca_info.fifo_id,
				ca_crtl_info[engid].fifo_id,
				sizeof(ca_info.fifo_id));
			memcpy(ca_info.fifo_id_used,
				ca_crtl_info[engid].fifo_id_used,
				sizeof(ca_info.fifo_id_used));

			if (release_ca_resource(ca)) {
				DSCMB_INFO("DisconnectKeyToken reset ca_ctrl. ca_source:%d\n",
					pathid);

				dsc_op(dsc_internal_ops, set_ca_ctrl, ca_info);
			}

			DSCMB_INFO("[engId:%d]DisconnectKeyToken done: "
					"[keytoken, src, slot, process_num, live, rec, rec_play]:"
					"0x%llx, %d, %d, %d, %d, %d, %d",
					engid, keytoken, pathid, keyslot,
					ca_crtl_info[engid].process_num,
					purpose == PURPOSE_PLAYBACK,
					purpose == PURPOSE_RECORD,
					purpose == PURPOSE_PLAYBACK_RECORD);
		}
		break;

	default:
		DSCMB_ERR("cmd =%d not support.\n", cmd);
		ret = -EINVAL;
		break;
	}


	return ret;
}

static long dvb_ca_dscmb_io_ioctl(struct file *file,
				    unsigned int cmd, unsigned long arg)
{
	long ret;

	FH_READ_LOCK(&g_dmx_dev->tm.fh_lock);
	ret = _dvb_usercopy(file, cmd, arg, mdrv_dscmb_ext_ioctl);
	FH_READ_UNLOCK(&g_dmx_dev->tm.fh_lock);

	return ret;
}

static int dvb_ca_dsc_io_open(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int err;

	if (!try_module_get(ca->pub->owner)) {
		DSCMB_ERR(" try_module_get\n");
		return -EIO;
	}
	err = dvb_generic_open(inode, file);

	if (err < 0) {
		DSCMB_ERR("open err\n");
		module_put(ca->pub->owner);
		return err;
	}

	dvb_ca_private_get(ca);

	return 0;
}

static int dvb_ca_dsc_io_release(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int err;

	DSCMB_INFO("\n");

	err = dvb_generic_release(inode, file);

	module_put(ca->pub->owner);

	dvb_ca_private_put(ca);

	return err;
}

static const struct file_operations dvb_ca_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = dvb_ca_dscmb_io_ioctl,
	.open = dvb_ca_dsc_io_open,
	.release = dvb_ca_dsc_io_release,
	.llseek = noop_llseek,
#ifdef CONFIG_COMPAT
	.compat_ioctl = dvb_ca_dscmb_io_ioctl,
#endif
};

static const struct dvb_device dvbdev_ca = {
	.priv = NULL,
	.users = DEVICE_USER_NUM,
	.readers = DEVICE_READER_NUM,
	.writers = DEVICE_WRITER_NUM,
#if defined(CONFIG_MEDIA_CONTROLLER_DVB) || defined(CONFIG_MTK_MEDIA_CONTROLLER_DVB)
	.name = "dvb-ca-dscmb",
#endif
	.fops = &dvb_ca_fops,
};

int mtk_dscmb_dev_init(struct platform_device *pdev, struct mtk_dmx2dev *mtkdev)
{
	struct mtk_dvb_dscmb *dvb = NULL;
	int ret = 0;
	struct device_node *hwcaps_node;
	int i;
	struct dvb_ca_private *ca = NULL;
	ca = NULL;
	DSCMB_INFO("enable\n");

	g_dmx_dev = mtkdev;

	/* initialise the system data */
	dvb = devm_kzalloc(&pdev->dev, sizeof(struct mtk_dvb_dscmb),
			GFP_KERNEL);
	of_node_get(pdev->dev.of_node);
	hwcaps_node = of_find_node_by_name(pdev->dev.of_node, "hwcaps");
	if (hwcaps_node == NULL) {
		DSCMB_ERR("Failed to get hwcaps node\n");
		return -ENOENT;
	}
	if (!dvb) {
		DSCMB_ERR("devm_kzalloc error.\n");
		return -ENOMEM;
	}


	dvb->pdev = pdev;

	dvb->adapter = mdrv_get_dvb_adapter();
	if (!dvb->adapter) {
		DSCMB_ERR("mdrv_get_dvb_adapter error.\n");
		ret = -ENODEV;
		kfree(ca);
		return -ENODEV;
	}


	dvb->ca.data = dvb;
	dvb->ca.owner = THIS_MODULE;
	memset(ca_crtl_info, 0, sizeof(struct dsc_ca_crtl_info)*DVB_CA_ENG_SUPPORT);

	ret |= rm_of_read(sizeof(u32), hwcaps_node,
		"ca_num", &_ca_num);
	DSCMB_INFO("dsc ca_num = %d.\n", _ca_num);

	of_node_put(hwcaps_node);

	for (i = 0; i < _ca_num; i++) {
		/* initialise the system data */
		ca = kzalloc(sizeof(*ca), GFP_KERNEL);
		if (!ca)
			return -ENOMEM;

		ca->pub = &dvb->ca;
		ca->flags = CA_DES_UNUSED;
		ca->slot_count = _ca_num;
		ca->engine_id = DRV_DSCMB_ENG_NULL;
		ca->keyslot = DRV_DSCMB_FLT_NULL;
		ca->open = 0;
		ca->index = i;
		ca->keytoken = 0;

		/* register the DVB device */
		ret = dvb_register_device(dvb->adapter, &ca->dvbdev,
				 &dvbdev_ca, ca, DVB_DEVICE_CA, 0);
			if (ret) {
				DSCMB_ERR("dvb_register_device error.\n");
				ret = -ENOENT;
				kfree(ca);
				return ret;
			}
	}

	platform_set_drvdata(pdev, dvb);

	MDrv_DSCMB2_Init();
	return 0;
}

static void dvb_dsc_ca_release(struct dvb_ca_en50221 *pubca)
{
	struct dvb_ca_private *ca = pubca->private;

	DSCMB_DEBUG("\n");
	dvb_remove_device(ca->dvbdev);
	dvb_ca_private_put(ca);
	pubca->private = NULL;
}

int mtk_dscmb_dev_exit(struct platform_device *pdev)
{
	struct mtk_dvb_dscmb *dvb = platform_get_drvdata(pdev);
	dvb_dsc_ca_release(&dvb->ca);

	return 0;
}

static const struct of_device_id mtk_dscmb_dt_match[] = {
	{ .compatible = DRVIVER_NAME },
	{},
};

void mtk_dscmb_connect_pid(struct dscmb_connect_info connect_info)
{
	__u8  i, j;
	__u32 pipe_id = 0;
	int ret;
	struct dmx_secure_info secure_info = { 0 };
	struct dmx_ca_info ca_info = { 0 };
	struct delay_pid_info *delay_info;
	enum filter_dst_type filter_dst_type = connect_info.filter_dst_type;

	DSCMB_INFO("path=%d, fltid=%d, pid = 0x%x, purpose = %d\n",
		connect_info.path_id,
		connect_info.pid_flt_id,
		connect_info.pid,
		connect_info.purpose);

	for (i = 0; i < DVB_CA_ENG_SUPPORT; i++) {
		if (bCheckPurpose == false)
			delay_info = &ca_crtl_info[i].pid_info;
		else if (connect_info.purpose == PURPOSE_RECORD)
			delay_info = &ca_crtl_info[i].rec_pid_info;
		else if (connect_info.purpose == PURPOSE_PLAYBACK)
			delay_info = &ca_crtl_info[i].playback_pid_info;
		else {
			DSCMB_ERR("Wrong purpose = %d\n", connect_info.purpose);
			return;
		}

		if (delay_info->delay_pid_num <= 0 ||
			ca_crtl_info[i].path_id != connect_info.path_id)
			continue;

		for (j = 0; j < CA_CRTL_SLOT_MAX; j++) {
			if (delay_info->pid[j] != connect_info.pid)
				continue;

			//find match pid
			DSCMB_INFO("DSC[%u] find match pid delay_pid_num:%d\n", i,
				delay_info->delay_pid_num);
			// Check TLV IP address is match
			if (_is_tlv_path(connect_info.path_id) &&
				(!_dscmb_compare_ip_addr(
						&connect_info.ip_addr,
						&delay_info->ip_addr[j])))
				continue;

			ca_crtl_info[i].pid_info.purpose = connect_info.purpose;

			if (connect_info.set_tlv_avsrc) {
				ca_info.set_tlv_avsrc = true;
				ca_info.pass_through = true;
				ca_info.eng_id = i;
				ca_info.path_id = connect_info.path_id;
				ca_info.fifo_id[filter_dst_type] = connect_info.fifo_id;
				ca_info.fifo_id_used[filter_dst_type] = true;
				ca_crtl_info[ca_info.eng_id].fifo_id[filter_dst_type] =
					connect_info.fifo_id;
				ca_crtl_info[ca_info.eng_id].fifo_id_used[filter_dst_type] = true;
				ca_crtl_info[ca_info.eng_id].set_tlv_avsrc = ca_info.set_tlv_avsrc;

				ret = dsc_op(dsc_internal_ops, set_ca_ctrl, ca_info);
				if (ret < 0)
					DSCMB_ERR(" dmx set_ca_ctrl fail ret = %d\n", ret);
			}

			ret = MDrv_DSCMB_FltConnectFltId(
				ca_crtl_info[i].keytoken,
				ca_crtl_info[i].eng_id,
				ca_crtl_info[i].dscmb_flt_id,
				connect_info.pid_flt_id,
				&pipe_id);
			if (ret <= 0)
				DSCMB_ERR("MDrv_DSCMB_FltConnectFltId failed :0x%x\n", ret);

			if (pipe_id != 0  &&
				connect_info.purpose == PURPOSE_PLAYBACK) {
				secure_info.enable = TRUE;
				secure_info.pipeline_id = pipe_id;
				secure_info.filter_id = connect_info.pid_flt_id;
				ret = mtk_dmx_set_secure_mode(
					secure_info,
					connect_info.purpose);
				if (ret <= 0)
					DSCMB_ERR("set_secure fail :0x%x\n", ret);
			}

			DSCMB_INFO("[%d] Connected\n", __LINE__);
			return;
		}
	}
	DSCMB_INFO("[%d] Nothing Connected\n", __LINE__);
}

void mtk_dscmb_disconnect_pid(struct dscmb_connect_info connect_info)
{
	__u8  i, j;
	int ret;
	struct delay_pid_info *delay_info;

	DSCMB_INFO("pathid=%d, fltid=%d, pid = 0x%x, purposse = %d\n",
		connect_info.path_id,
		connect_info.pid_flt_id,
		connect_info.pid,
		connect_info.purpose);

	for (i = 0; i < DVB_CA_ENG_SUPPORT; i++) {
		if (bCheckPurpose == false)
			delay_info = &ca_crtl_info[i].pid_info;
		else if (connect_info.purpose == PURPOSE_RECORD)
			delay_info = &ca_crtl_info[i].rec_pid_info;
		else if (connect_info.purpose == PURPOSE_PLAYBACK)
			delay_info = &ca_crtl_info[i].playback_pid_info;
		else {
			DSCMB_ERR("Wrong purpose = %d\n", connect_info.purpose);
			return;
		}

		if (delay_info->delay_pid_num <= 0 ||
			ca_crtl_info[i].path_id != connect_info.path_id)
			continue;

		for (j = 0; j < CA_CRTL_SLOT_MAX; j++) {
			if (delay_info->pid[j] != connect_info.pid)
				continue;

			//find match pid
			DSCMB_INFO("DSC[%u] find match disconnect pid\n", i);
			ret = MDrv_DSCMB_FltDisconnectFltId(ca_crtl_info[i].eng_id,
				ca_crtl_info[i].dscmb_flt_id, connect_info.pid_flt_id);

			if (ret <= 0)
				DSCMB_ERR("MDrv_DSCMB_FltDisconnectFltId failed :0x%x\n", ret);

			DSCMB_INFO("[%d] DisConnected\n", __LINE__);
			return;
		}
	}
	DSCMB_INFO("[%d] Nothing DisConnected\n", __LINE__);
}

u64 mtk_dscmb_get_keytoken(u16 path_id, dmx_purpose_t purpose)
{
	int i;

	for (i = 0; i < DVB_CA_ENG_SUPPORT; i++) {
		if (ca_crtl_info[i].path_id != path_id)
			continue;

		if (bCheckPurpose && ca_crtl_info[i].purpose != purpose)
			continue;

		return ca_crtl_info[i].keytoken;
	}

	return 0;
}


MODULE_DEVICE_TABLE(of, mtk_dscmb_dt_match);



MODULE_DESCRIPTION("Mediatek DSCMB driver");
MODULE_AUTHOR("Mediatek Author");
MODULE_LICENSE("GPL");
