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

#include <linux/types.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/dma-buf.h>
#include <linux/genalloc.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/ion.h>
#include <linux/syscalls.h>
#include <linux/fdtable.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
#include <linux/pinctrl/consumer.h>
#endif

#ifndef DMX2LITE
#include "mtk_iommu_common.h"
#include "mtk_iommu_dtv_api.h"
#include "mtk-reserved-memory.h"
#include "mtk-cma.h"
#endif

#include "mtk_dmx_dev.h"
#include "mtk_dmx_common.h"
#include "mtk_dmx_rm.h"
#include "mtk_dmx_io.h"
#ifndef DMX2LITE
#include "mtk_dmx_log.h"
#include "mtk_dmx_filter.h"
#include "mtk_dmx_ctrl.h"
#include "mtk_mvd_parser.h"
#include "mtk_mad_parser.h"
#endif

#define DMX2_CAPS_PID_FILTER_NUMBER		"pid_filter_numb"
#define DMX2_CAPS_NTP_FILTER_NUMBER		"ntp_filter_numb"
#define DMX2_CAPS_PTP_FILTER_NUMBER		"ptp_filter_numb"

#define DMX2_CAPS_TSIF_NUMBER			"tsif_numb"
#define DMX2_CAPS_FILEIN_NUMBER			"filein_numb"
#define DMX2_CAPS_MMFI_NUMBER			"mmfi_numb"
#define DMX2_CAPS_TSO_TSIF_NUMBER		"tso_tsif_numb"
#define DMX2_CAPS_TLVIF_NUMBER			"tlvif_numb"
#define DMX2_CAPS_ALPIF_NUMBER			"alpif_numb"
#define DMX2_CAPS_ALPIF_FILEIN_NUMBER		"alpif_filein_numb"

#define MIUP_CID_TSO0_FILEIN_0_NAME			"tso0_filein0"
#define MIUP_CID_TSO0_FILEIN_1_NAME			"tso0_filein1"
#define MIUP_CID_TSO_SVQ_NAME				"tso_svq"
#define MIUP_CID_TSO1_FILEIN_0_NAME			"tso1_filein0"
#define MIUP_CID_TSO1_FILEIN_1_NAME			"tso1_filein1"
#define MIUP_CID_TSO1_SVQ_NAME				"tso1_svq"
#define MIUP_CID_TSP_FILEIN_R_NAME			"tsp_filein_r"
#define MIUP_CID_TSP_MMFI0_R_NAME			"tsp_mmfi0_r"
#define MIUP_CID_TSP_MMFI1_R_NAME			"tsp_mmfi1_r"
#define MIUP_CID_TSP_ORZ_W_NAME				"tsp_orz_w"
#define MIUP_CID_TSP_ORZ_R_NAME				"tsp_orz_r"
#define MIUP_CID_TSP_SEC_W_NAME				"tsp_sec_w"
#define MIUP_CID_TSP_PVR1_W_NAME			"tsp_pvr1_w"
#define MIUP_CID_TSP_PVR2_W_NAME			"tsp_pvr2_w"
#define MIUP_CID_TSP_PVR3_W_NAME			"tsp_pvr3_w"
#define MIUP_CID_TSP_PVR4_W_NAME			"tsp_pvr4_w"
#define MIUP_CID_TSP_VQ_NAME				"tsp_vq"
#define MIUP_CID_TSP_VD_PARSER_NAME		"vd_pes_parser"
#define MIUP_CID_TSP_FIQ_NAME				"tsp_fiq"

#define HWCAPS_ENTRY_SET_NAME(caps, id, iname)	\
	({ strncpy(caps->entries[id].name, iname, HWCAPS_ENTRY_NAME_LEN); })

#define MIUP_ENTRY_SET_NAME(miup, id, iname)	\
		({ strncpy(miup->entries[id].name, iname, MIUP_CID_ENTRY_NAME_LEN); })

#define DMX_DMA_ADDR(heaptype, addr) \
	((((heaptype) == HEAP_IOMMU_POOL) || ((heaptype) == HEAP_IOMMU)) ? (addr) : PA_to_BA(addr))

#define HEAP_IOMMU_POOL_OFFSET (0x100000)

#ifndef DMX2LITE
mtk_dmx_riu_bank *dmx_bank_offsets;

static int rm_hwcaps_type_2_filter_sub_type(
		enum hwcaps_type hwcaps_type,
		enum filter_sub_type *sub_type);
static int rm_get_filter_by_filter_sub_type(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		struct filter **filter);
static int rm_get_filter_by_hwcaps_type(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type,
		struct filter **filter);

static int rm_filter_deinit(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type);
static int rm_filter_init(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type);

static int rm_alloc_pid_filter(struct mtk_dmx_rm *res, struct filter_type *type);
static void rm_release_pid_filter(struct mtk_dmx_rm *res, u16 filter_id);
static int rm_alloc_pcr_filter(struct mtk_dmx_rm *res);
static void rm_release_pcr_filter(struct mtk_dmx_rm *res, u16 filter_id);

static int rm_alloc_filter(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		int *filter_id);
static int rm_release_filter(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		u16 filter_id);

static int rm_deinit(struct mtk_dmx_rm *res);

static int rm_padcfg_init(struct mtk_dmx_rm *res);
static int rm_memmap_init(struct mtk_dmx_rm *res);
static int rm_regmaps_init(struct mtk_dmx_rm *res);
static int rm_hwcaps_init(struct mtk_dmx_rm *res);
static int rm_fifos_init(struct mtk_dmx_rm *res);
static int rm_pvr_init(struct mtk_dmx_rm *res);
static int rm_filein_init(struct mtk_dmx_rm *res);
static int rm_stc_init(struct mtk_dmx_rm *res);
static int rm_filters_init(struct mtk_dmx_rm *res);

static void mtk_dmx_rm_pool_lock(
		struct mtk_dmx_rm *res,
		enum mmap_buf_type type);
static void mtk_dmx_rm_pool_unlock(
		struct mtk_dmx_rm *res,
		enum mmap_buf_type type);
static int rm_pool_buffer_create(struct mtk_dmx_rm *res, enum mmap_buf_type type);
static int rm_pool_buffer_release(struct mtk_dmx_rm *res, enum mmap_buf_type type);

struct hwcaps_type_2_filter_sub_type_mapping {
	enum hwcaps_type hwcaps_type;
	enum filter_sub_type filter_sub_type;
};

static enum hwcaps_type _hwcaps_types[] = {
	HWCAPS_TYPE_PTP_FILTER,
	HWCAPS_TYPE_ALP_FILTER,
};

static int rm_hwcaps_type_2_filter_sub_type(
		enum hwcaps_type hwcaps_type,
		enum filter_sub_type *sub_type)
{
	int ret = -EINVAL;
	int i = 0;
	struct hwcaps_type_2_filter_sub_type_mapping mappings[] = {
		{
			HWCAPS_TYPE_PTP_FILTER,
			FILTER_SUB_TYPE_PTP,
		},
		{
			HWCAPS_TYPE_ALP_FILTER,
			FILTER_SUB_TYPE_ALP,
		},
	};

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

	for (i = 0; i < ARRAY_SIZE(mappings); i++) {
		if (hwcaps_type == mappings[i].hwcaps_type) {
			*sub_type = mappings[i].filter_sub_type;

			ret = 0;
			break;
		}
	}
	if (ret != 0) {
		MTK_DMX_ERR(
			"hwcaps_type(%s) is not supported.\n",
			hwcaps_type_2_str(hwcaps_type));
		*sub_type = FILTER_SUB_TYPE_MAX;
	}

	return ret;
}

static int rm_get_filter_by_filter_sub_type(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		struct filter **filter)
{
	int ret = 0;

	if (MTK_DMX_VERIFY(res == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter_sub_type < FILTER_SUB_TYPE_MIN))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter_sub_type >= FILTER_SUB_TYPE_MAX))
		return -EINVAL;

	*filter = &res->filters[filter_sub_type];

	return ret;
}

static int rm_get_filter_by_hwcaps_type(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type,
		struct filter **filter)
{
	int ret = 0;
	enum filter_sub_type filter_sub_type = FILTER_SUB_TYPE_MAX;

	if (MTK_DMX_VERIFY(res == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter == NULL))
		return -EINVAL;
	ret = rm_hwcaps_type_2_filter_sub_type(hwcaps_type, &filter_sub_type);
	if (ret != 0) {
		MTK_DMX_ERR(
			"_hwcaps_type_2_filter_sub_type(%s, ...) is failed.\n",
			hwcaps_type_2_str(hwcaps_type));

		return -EINVAL;
	}

	ret = rm_get_filter_by_filter_sub_type(
			res,
			filter_sub_type,
			filter);
	if (ret != 0)
		MTK_DMX_ERR(
			"_get_filter_by_filter_sub_type(%s, ...) is failed.\n",
			filter_sub_type_2_str(filter_sub_type));

	return ret;
}

static int rm_filter_deinit(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type)
{
	int ret = 0;
	struct filter *filter = NULL;

	MTK_DMX_ENTRY();

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

	ret = rm_get_filter_by_hwcaps_type(res, hwcaps_type, &filter);
	if (ret != 0) {
		MTK_DMX_ERR(
			"_get_filter_by_hwcaps_type(%d, ...) is failed.\n",
			hwcaps_type);

		return -EINVAL;
	}

	do {
		if (!filter->init) {
			// Deinit even though previous init is not completed.
			MTK_DMX_WARN("filter is not initialized.\n");
		}

		filter->filter_num = 0;
		MTK_DMX_KVFREE(filter->filter_slot_map);
		filter->init = false;
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int rm_filter_init(
		struct mtk_dmx_rm *res,
		enum hwcaps_type hwcaps_type)
{
	int ret = 0;
	struct filter *filter = NULL;
	struct mtk_dmx2dev *mtkdev = NULL;
	int i = 0;

	MTK_DMX_ENTRY();

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

	ret = rm_get_filter_by_hwcaps_type(res, hwcaps_type, &filter);
	if (ret != 0) {
		MTK_DMX_ERR(
			"_get_filter_by_hwcaps_type(%d, ...) is failed.\n",
			hwcaps_type);

		return -EINVAL;
	}
	mtkdev = container_of(res, struct mtk_dmx2dev, rm);

	do {
		if (filter->init) {
			MTK_DMX_WARN("filter is already initialized.\n");

			break;
		}

		filter->filter_num = mtk_dmx_rm_get_hwcap_by_id(res, hwcaps_type);
		if (filter->filter_num == 0) {
			MTK_DMX_INFO(
				"filter_num of hwcaps_type(%d) is 0.\n",
				hwcaps_type);

			break;
		}

		spin_lock_init(&filter->lock);

		filter->filter_slot_map =
			devm_kzalloc(
				mtkdev->dev,
				filter->filter_num * sizeof(struct filter_status),
				GFP_KERNEL);
		if (filter->filter_slot_map == NULL) {
			MTK_DMX_ERR(
				"Allocate filter_slot_map of hwcaps_type(%d) is failed.\n",
				hwcaps_type);
			ret = -EIO;

			break;
		}
		for (i = 0; i < filter->filter_num; i++)
			filter->filter_slot_map[i].state = RES_STATE_FREE;

		filter->init = true;
	} while (0);

	MTK_DMX_EXIT();

	return ret;
}

static int rm_alloc_pid_filter(struct mtk_dmx_rm *res, struct filter_type *type)
{
	u16 pid_filter_num, sec_filter_num;
	int i;
	unsigned long flags;
	bool bInverse = true;

	if (type->sub == FILTER_SUB_TYPE_SEC
		|| type->pktvld_by_sec)
		bInverse = false;

	pid_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PID_FILTER);
	sec_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_SEC_FILTER);

	spin_lock_irqsave(&res->pid_filter_lock, flags);

	if (bInverse) {
		for (i = pid_filter_num - 1; i >= 0; i--) {
			if (i == SECFLT_NMASK_FLD_ID)
				continue;
			if (res->pid_filter_slot_map[i].state ==
				RES_STATE_FREE) {
				res->pid_filter_slot_map[i].state =
					RES_STATE_ALLOC;
				spin_unlock_irqrestore(&res->pid_filter_lock, flags);
				return i;
			}
		}
	} else {
		for (i = 0; i < sec_filter_num; i++) {
			if (i == SECFLT_NMASK_FLD_ID)
				continue;
			if (res->pid_filter_slot_map[i].state ==
				RES_STATE_FREE) {
				res->pid_filter_slot_map[i].state =
					RES_STATE_ALLOC;
				spin_unlock_irqrestore(&res->pid_filter_lock, flags);
				return i;
			}
		}
	}

	spin_unlock_irqrestore(&res->pid_filter_lock, flags);
	return -ENODEV;
}

static void rm_release_pid_filter(struct mtk_dmx_rm *res, u16 filter_id)
{
	unsigned long flags;
	u16 filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PID_FILTER);

	if (WARN_ON(filter_id >= filter_num))
		return;
	spin_lock_irqsave(&res->pid_filter_lock, flags);

	res->pid_filter_slot_map[filter_id].state = RES_STATE_FREE;

	spin_unlock_irqrestore(&res->pid_filter_lock, flags);
}

static int rm_alloc_pcr_filter(struct mtk_dmx_rm *res)
{
	int i;
	unsigned long flags;
	u16 pcr_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PCR_FILTER);

	spin_lock_irqsave(&res->pcr_filter_lock, flags);

	for (i = 0; i < pcr_filter_num; i++) {
		if (res->pcr_filter_slot_map[i].state ==
			RES_STATE_FREE) {
			res->pcr_filter_slot_map[i].state =
				RES_STATE_ALLOC;
			spin_unlock_irqrestore(&res->pcr_filter_lock, flags);
			return i;
		}
	}
	spin_unlock_irqrestore(&res->pcr_filter_lock, flags);
	return -ENODEV;
}

static void rm_release_pcr_filter(struct mtk_dmx_rm *res, u16 filter_id)
{
	unsigned long flags;
	u16 filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PCR_FILTER);

	if (WARN_ON(filter_id >= filter_num))
		return;
	spin_lock_irqsave(&res->pcr_filter_lock, flags);

	res->pcr_filter_slot_map[filter_id].state = RES_STATE_FREE;

	spin_unlock_irqrestore(&res->pcr_filter_lock, flags);
}

static int rm_alloc_ip_filter(struct mtk_dmx_rm *res)
{
	u16 ip_filter_num;
	int i;
	unsigned long flags;

	ip_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_IP_FILTER);

	spin_lock_irqsave(&res->ip_filter_lock, flags);

	for (i = 0; i < ip_filter_num; i++) {
		if (res->ip_filter_slot_map[i].state == RES_STATE_FREE) {
			res->ip_filter_slot_map[i].state = RES_STATE_ALLOC;
			spin_unlock_irqrestore(&res->ip_filter_lock, flags);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->ip_filter_lock, flags);
	return -ENODEV;
}

static void rm_release_ip_filter(struct mtk_dmx_rm *res, u16 filter_id)
{
	unsigned long flags;

	spin_lock_irqsave(&res->ip_filter_lock, flags);

	res->ip_filter_slot_map[filter_id].state = RES_STATE_FREE;

	spin_unlock_irqrestore(&res->ip_filter_lock, flags);
}

static int rm_alloc_filter(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		int *filter_id)
{
	int ret = 0;
	struct filter *filter = NULL;
	int i;
	unsigned long flags;

	MTK_DMX_ENTRY();

	if (MTK_DMX_VERIFY(res == NULL))
		return -EINVAL;
	if (MTK_DMX_VERIFY(filter_id == NULL))
		return -EINVAL;

	ret = rm_get_filter_by_filter_sub_type(
			res,
			filter_sub_type,
			&filter);
	if (ret != 0) {
		MTK_DMX_ERR(
			"_get_filter_by_filter_sub_type(..., %d, ...) is failed.\n",
			filter_sub_type);

		return -EINVAL;
	}

	spin_lock_irqsave(&filter->lock, flags);

	do {
		if (MTK_DMX_VERIFY(!filter->init)) {
			ret = -EIO;

			break;
		}

		for (i = 0; i < filter->filter_num; i++) {
			if (filter->filter_slot_map[i].state == RES_STATE_FREE) {
				filter->filter_slot_map[i].state = RES_STATE_ALLOC;
				*filter_id = i;

				break;
			}
		}
	} while (0);

	spin_unlock_irqrestore(&filter->lock, flags);

	return ret;
}

static int rm_release_filter(
		struct mtk_dmx_rm *res,
		enum filter_sub_type filter_sub_type,
		u16 filter_id)
{
	int ret = 0;
	struct filter *filter = NULL;
	unsigned long flags;

	MTK_DMX_ENTRY();

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

	ret = rm_get_filter_by_filter_sub_type(
			res,
			filter_sub_type,
			&filter);
	if (ret != 0) {
		MTK_DMX_ERR(
			"_get_filter_by_filter_sub_type(..., %d, ...) is failed.\n",
			filter_sub_type);

		return -EINVAL;
	}

	spin_lock_irqsave(&filter->lock, flags);

	do {
		// Return 0 even though it is not initialized.
		if (MTK_DMX_VERIFY(!filter->init))
			break;

		if (filter_id >= filter->filter_num) {
			MTK_DMX_WARN(
				"filter_id(%d) >= filter_num(%d)\n",
				filter_id,
				filter->filter_num);

			ret = -EINVAL;
			break;
		}

		filter->filter_slot_map[filter_id].state = RES_STATE_FREE;

	} while (0);

	spin_unlock_irqrestore(&filter->lock, flags);

	MTK_DMX_EXIT();

	return ret;
}

static int rm_deinit(struct mtk_dmx_rm *res)
{
	int ret = 0;
	int tmp_ret = 0;
	int i = 0;

	for (i = 0; i < ARRAY_SIZE(_hwcaps_types); i++) {
		tmp_ret = rm_filter_deinit(res, _hwcaps_types[i]);
		if (tmp_ret != 0) {
			MTK_DMX_ERR(
				"rm_filter_deinit(..., %d) is failed.\n",
				_hwcaps_types[i]);

			ret = -EIO;
			break;
		}
	}

	// TODO: Release other resources.

	return ret;
}

const struct dmx_memmap_tag dmx_mmap[MMAP_BUF_TYPE_MAX] = { // TODO: DTS?
	{FW_MMAP_ID, "MI_TSP_FW_BUF", -1, "tsp_fw"},
	{VQ_MMAP_ID, "MI_TSP_VQ_BUF", -1, "tsp_vq"},
	{SEC_MMAP_ID, "MI_TSP_SECT_BUF", SECT_BUF_ID, "tsp_sect"},
	{-1, "MI_PVR_DOWNLOAD_BUF", PVR_BUF_ID, "tsp_pvr"},
	{-1, "MI_PVR_UPLOAD_BUF", MMFI_BUF_ID, "tsp_filein"},
	{-1, "MI_TSP_FQ_BUF", FQ_BUF_ID, "tsp_fiq"},
	{-1, NULL, AESDMA_BUF_ID, "shm_buf_aesdma"},
	{-1, NULL, MAD_BUF_ID, "shm_buf_mad"},
	{TSO_FILEIN_MMAP_ID, "MI_TSO_FILEIN_BUF", -1, "tso_filein"},
	{-1, NULL, ALP_PREPARSER_BUF_ID, "tsp_alppreparser"},
	{-1, NULL, TLV_FR_ID, "tsp_tlvfr"},
};

static void rm_memmap_get(enum mmap_buf_type idx, struct platform_device *pdev,
	struct mem_info *pInfo, struct mtk_dmx_rm *res)
{
#define ADDR_CELLS (2)
#define SIZE_CELLS (2)
	enum mmap_heap_type type = HEAP_NONE;
	struct device_node *mmap_np = NULL, *buftag_np = NULL, *np = NULL, *node = NULL;
	__be32 *p32 = NULL;
	__be64 *p64 = NULL;
	uint32_t len = 0;
	int cma_id = 0, id = 0;
	u64 ba = 0, pa = 0;
	u32 size = 0;
	unsigned long cma_base = 0, cma_size = 0;

	mmap_np = of_find_node_by_name(NULL, "mmap_info");
	buftag_np = of_find_node_by_name(NULL, "buf_tag");
	if (mmap_np == NULL || buftag_np == NULL)
		goto FAIL;
	if (dmx_mmap[(unsigned int)idx].mmap)
		np = of_find_node_by_name(mmap_np, dmx_mmap[(unsigned int)idx].mmap);

	if (np)
		p32 = (__be32 *)of_get_property(np, "cma_id", &len);
	if (p32) { // CMA or NON_MMA
		cma_id = be32_to_cpup(p32);

		p32 = (__be32 *)of_get_property(np, "reg", &len);
		if (p32 == NULL ||
			((len / sizeof(__be32)) < (ADDR_CELLS + SIZE_CELLS)))
			goto FAIL;
		ba = of_read_number(p32, ADDR_CELLS);
		size = of_read_number(p32 + ADDR_CELLS, SIZE_CELLS);
		pa = BA_to_PA(ba);

		if (cma_id != 0 && (res->cma_dev)) {
			type = HEAP_CMA;

			p32 = (__be32 *) of_get_property(res->cma_dev->of_node, "cmas", &len);
			if (p32 == NULL)
				goto FAIL;
			id = be32_to_cpup(p32);
			node = of_parse_phandle(res->cma_dev->of_node, "memory-region", id);
			p32 = (__be32 *) of_get_property(node, "reg", &len);
			if (p32 == NULL ||
				((len / sizeof(__be32)) < (ADDR_CELLS + SIZE_CELLS)))
				goto FAIL;
			cma_base = of_read_number(p32, ADDR_CELLS);
			cma_size = of_read_number(p32 + ADDR_CELLS, SIZE_CELLS);
		} else
			type = HEAP_NON_MMA;
	} else { // IOMMU
		np = of_find_node_by_name(buftag_np, dmx_mmap[(unsigned int)idx].buftag);
		if (np == NULL)
			goto FAIL;
		p64 = (__be64 *) of_get_property(np, "max_size", &len);
		if (p64 == NULL)
			goto FAIL;
		size = be64_to_cpup(p64);

		type = HEAP_IOMMU;

		if (idx == MMAP_BUF_TYPE_SEC) {
			MTK_DMX_INFO("Use iommu with pool for section buffer.\n");
			type = HEAP_IOMMU_POOL;
		}
	}

FAIL:
	if (mmap_np)
		of_node_put(mmap_np);
	if (buftag_np)
		of_node_put(buftag_np);
	if (np)
		of_node_put(np);

	pInfo->paddr = pa;
	pInfo->size = size;
	pInfo->heaptype = type;
	pInfo->id = (type == HEAP_IOMMU || type == HEAP_IOMMU_POOL)
		? dmx_mmap[(unsigned int)idx].buftag_id : cma_id;
	pInfo->cma_base = cma_base;
	pInfo->cma_size = cma_size;
	MTK_DMX_INFO("pa:0x%llx, size:0x%x, type:%s, id:%d, cma:0x%lx, size:0x%lx\n",
		pInfo->paddr, pInfo->size,
		(pInfo->heaptype == HEAP_IOMMU_POOL) ? "IOMMU_POOL" :
		(pInfo->heaptype == HEAP_IOMMU) ? "IOMMU" :
		(pInfo->heaptype == HEAP_CMA) ? "CMA" : "NON_MMA",
		pInfo->id, pInfo->cma_base, pInfo->cma_size);
}

static int rm_dma_alloc(struct device *dev, int size,
	dma_addr_t *iova, unsigned long attrs, void __iomem **va)
{
#define RM_DMA_ALLOC_RETRY (3)
	u8 retry = RM_DMA_ALLOC_RETRY;

ALLOC:
	*va = dma_alloc_attrs(dev, size, iova, GFP_KERNEL, attrs);
	if ((dma_mapping_error(dev, *iova) != 0) || (*iova == 0)) {
		MTK_DMX_ERR("dma_alloc_attrs fail\n");
		retry--;
		if (retry != 0)
			goto ALLOC;
		else
			return -EFAULT;
	}

	return 0;
}

static inline void rm_dma_free(struct device *dev, struct mem_info *pInfo)
{

	dma_free_attrs(dev, pInfo->size, pInfo->vaddr == 0 ? (void __iomem *)(-1) : pInfo->vaddr,
		DMX_DMA_ADDR(pInfo->heaptype, pInfo->paddr),
		(pInfo->heaptype == HEAP_IOMMU || pInfo->heaptype == HEAP_IOMMU_POOL)
		? pInfo->id << BUFTAG_SHIFT : 0);
}

static inline void rm_dma_flush(struct device *dev, struct mem_info *pInfo)
{
	dma_sync_single_for_device(dev,
		DMX_DMA_ADDR(pInfo->heaptype, pInfo->paddr), pInfo->size,
		DMA_TO_DEVICE);
}

static int rm_mma_get_memory(struct device *dev, bool iommu,
				struct mem_info *pMemmapInfo,
				struct mem_info *pInfo, u32 buf_size, bool dbg)
{
	unsigned int req_size;
	int ret;
	unsigned long attrs;
	void __iomem *va = NULL;
	dma_addr_t iova;

	if (dbg)
		MTK_DMX_INFO("query %s id: %d size: 0x%x\n",
			(iommu) ? "buftag" : "cma", pMemmapInfo->id, buf_size);

	req_size = (buf_size > 0) ? buf_size : pMemmapInfo->size;
	req_size = ALIGN(req_size, DMX_4K_ALIGN);

	iova = (iommu) ? 0 : (PA_to_BA(pInfo->paddr) - pMemmapInfo->cma_base);
	attrs = (iommu) ? (DMA_ATTR_WRITE_COMBINE | (pMemmapInfo->id << BUFTAG_SHIFT)) :
		DMA_ATTR_WRITE_COMBINE;

	if (pInfo->no_mapping)
		attrs |= DMA_ATTR_NO_KERNEL_MAPPING;

	ret = rm_dma_alloc(dev, req_size, &iova, attrs, &va);
	if (ret) {
		MTK_DMX_ERR("dma alloc fail\n");
		return ret;
	}

	pInfo->id = pMemmapInfo->id;
	pInfo->paddr = (iommu) ? iova : BA_to_PA(iova);
	pInfo->cma_base = pMemmapInfo->cma_base;
	pInfo->size = req_size;
	pInfo->vaddr = va;
	pInfo->dmabuf_fd = -1;
	pInfo->heaptype = (iommu) ? HEAP_IOMMU : HEAP_CMA;
	pInfo->pool = pMemmapInfo->pool;

	if (dbg)
		MTK_DMX_INFO("%s id: %d paddr: %llx, vaddr: %p, size: %x\n",
			(iommu) ? "buftag" : "cma", pInfo->id,
			pInfo->paddr, pInfo->vaddr, pInfo->size);

	return 0;
}

static inline void rm_mma_put_memory(struct device *dev, struct mem_info *pInfo, bool dbg)
{
	rm_dma_free(dev, pInfo);

	if (dbg)
		MTK_DMX_INFO("%s id: %d paddr: %llx, vaddr: %p, size: %x\n",
			(pInfo->heaptype == HEAP_IOMMU || pInfo->heaptype == HEAP_IOMMU_POOL)
			? "buftag" : "cma",
			pInfo->id, pInfo->paddr, pInfo->vaddr, pInfo->size);
}

static inline void rm_mma_flush_memory(struct device *dev, struct mem_info *pInfo, bool dbg)
{
	rm_dma_flush(dev, pInfo);

	if (dbg)
		MTK_DMX_INFO("%s id: %d paddr: %llx, vaddr: %p, size: %x\n",
			(pInfo->heaptype == HEAP_IOMMU || pInfo->heaptype == HEAP_IOMMU_POOL)
			? "buftag" : "cma",
			pInfo->id, pInfo->paddr, pInfo->vaddr, pInfo->size);
}

static int rm_mmap_get_memory(struct device *pdev, enum mmap_buf_type buf_type,
				struct mem_info *pInfo)
{
	struct of_mmap_info_data of_mmap_info = { };
	int ret, id;
	char *buf_tag = NULL;

	id = dmx_mmap[(unsigned int)buf_type].mmap_id;
	buf_tag = dmx_mmap[(unsigned int)buf_type].buftag;
	if (id < 0)
		return -ENOMEM;

	ret = of_mtk_get_reserved_memory_info_by_idx(pdev->of_node, id, &of_mmap_info);
	if (ret < 0) {
		MTK_DMX_ERR("get reserved mem info fail ret: %d\n", ret);
		return -EFAULT;
	}

	pInfo[0].id = id;
	pInfo[0].paddr = BA_to_PA(of_mmap_info.start_bus_address);
	pInfo[0].size = (u32)of_mmap_info.buffer_size;
	pInfo[0].dmabuf_fd = -1;
	pInfo[0].heaptype = HEAP_NON_MMA;

	MTK_DMX_INFO("[%s] pa = %llx, size = %u\n", buf_tag, pInfo[0].paddr, pInfo[0].size);

	if (!devm_request_mem_region(pdev, PA_to_BA(pInfo[0].paddr),
			pInfo[0].size, DRV2_NAME)) {
		MTK_DMX_ERR("could not reserve memory\n");
		return -ENODEV;
	}

	pInfo[0].vaddr =
		devm_ioremap_wc(pdev, PA_to_BA(pInfo[0].paddr), pInfo[0].size);
	if (!pInfo[0].vaddr) {
		MTK_DMX_ERR("ioremap fail\n");
		return -ENODEV;
	}

	return 0;
}

static inline void rm_mmap_put_memory(struct device *pdev, struct mem_info *pInfo)
{
	devm_iounmap(pdev, pInfo[0].vaddr);
	devm_release_mem_region(pdev, PA_to_BA(pInfo[0].paddr), pInfo[0].size);
}

static int rm_pool_get_memory(struct device *pdev, struct mtk_dmx_rm *res,
	enum mmap_buf_type buf_type, struct mem_info *pInfo, u32 buf_size, bool dbg)
{
	dma_addr_t dma = { 0 };
	struct gen_pool *pool = NULL;
	void *vaddr = NULL;
	int id;
	char *name = NULL;
	enum mmap_heap_type heaptype = res->memmap->list[(unsigned int)buf_type]->heaptype;

	mtk_dmx_rm_pool_lock(res, buf_type);
	name = dmx_mmap[(unsigned int)buf_type].mmap;
	id = res->memmap->list[(unsigned int)buf_type]->id;

	if (heaptype == HEAP_IOMMU_POOL) {
		if (rm_pool_buffer_create(res, buf_type)) {
			MTK_DMX_ERR("create pool buffer fail\n");
			mtk_dmx_rm_pool_unlock(res, buf_type);
			return -ENOMEM;
		}

		if (res->memmap->list[(unsigned int)buf_type]->paddr == 0
			|| res->memmap->list[(unsigned int)buf_type]->vaddr == 0) {
			MTK_DMX_ERR("Memory is not allocated successfully.\n");
			mtk_dmx_rm_pool_unlock(res, buf_type);
			return -ENOMEM;
		}
	}

	pool = res->memmap->list[buf_type]->pool;
	if (!pool) {
		MTK_DMX_ERR("gen pool fail\n");
		mtk_dmx_rm_pool_unlock(res, buf_type);
		return -1;
	}

	vaddr = gen_pool_dma_alloc(pool, buf_size, &dma);
	if (!vaddr &&
		((heaptype == HEAP_NON_MMA) ||
			(heaptype == HEAP_IOMMU_POOL))) {
		MTK_DMX_ERR("allocate memory by %s pool fail\n", name);
		mtk_dmx_rm_pool_unlock(res, buf_type);
		return -1;
	}

	pInfo->id = id;
	if (heaptype == HEAP_NON_MMA) {
		pInfo->paddr = dma;
		pInfo->vaddr = vaddr;
	} else if (heaptype == HEAP_CMA) {
		pInfo->offset = (u64)((size_t)vaddr);
		pInfo->paddr = res->memmap->list[(unsigned int)buf_type]->paddr + pInfo->offset;
	} else if (heaptype == HEAP_IOMMU_POOL) {
		pInfo->offset = (u64)((size_t)vaddr) - HEAP_IOMMU_POOL_OFFSET;
		pInfo->paddr = res->memmap->list[(unsigned int)buf_type]->paddr + pInfo->offset;
		pInfo->vaddr = res->memmap->list[(unsigned int)buf_type]->vaddr + pInfo->offset;
	}
	pInfo->size = buf_size;
	pInfo->dmabuf_fd = -1;
	pInfo->heaptype =
		(heaptype == HEAP_IOMMU_POOL) ? HEAP_IOMMU_POOL :
		(heaptype == HEAP_NON_MMA) ? HEAP_NON_MMA :
		HEAP_CMA;
	pInfo->pool_name = name;
	pInfo->buf_type = buf_type;

	if (dbg)
		MTK_DMX_INFO("[id: %d] paddr: %llx, vaddr: %p, size: %x, offset: %llx\n",
			pInfo->id, pInfo->paddr, pInfo->vaddr, pInfo->size, pInfo->offset);

	if (heaptype == HEAP_IOMMU_POOL)
		atomic_add(1, &res->memmap->list[buf_type]->pool_refcnt);
	mtk_dmx_rm_pool_unlock(res, buf_type);
	return 0;
}

static inline void rm_pool_put_memory(
		struct mtk_dmx_rm *res,
		struct device *pdev,
		struct mem_info *pInfo,
		bool dbg)
{
	struct gen_pool *pool = NULL;
	enum mmap_buf_type buf_type = MMAP_BUF_TYPE_MAX;

	if ((pInfo->buf_type < MMAP_BUF_TYPE_MAX) && (pInfo->buf_type >= MMAP_BUF_TYPE_FW)) {
		buf_type = (enum mmap_buf_type)pInfo->buf_type;
		mtk_dmx_rm_pool_lock(res, buf_type);
		pool = res->memmap->list[buf_type]->pool;
		if (!pool) {
			MTK_DMX_ERR("gen pool buf_type %d fail\n", buf_type);
			mtk_dmx_rm_pool_unlock(res, buf_type);
			return;
		}

		if (pInfo->heaptype == HEAP_NON_MMA)
			gen_pool_free(pool, static_cast(unsigned long, pInfo->vaddr), pInfo->size);
		else if (pInfo->heaptype == HEAP_CMA)
			gen_pool_free(pool, static_cast(unsigned long, pInfo->offset), pInfo->size);
		else if (pInfo->heaptype == HEAP_IOMMU_POOL) {
			gen_pool_free(
				pool,
				static_cast(unsigned long, pInfo->offset + HEAP_IOMMU_POOL_OFFSET),
				pInfo->size);
			atomic_sub(1, &res->memmap->list[buf_type]->pool_refcnt);

			rm_pool_buffer_release(res, buf_type);
		}
		mtk_dmx_rm_pool_unlock(res, buf_type);

		if (dbg)
			MTK_DMX_INFO("[id: %d] paddr: %llx, vaddr: %p, size: %x, offset: %llx\n",
				pInfo->id,
				pInfo->paddr,
				pInfo->vaddr,
				pInfo->size,
				(u64)pInfo->offset);

	}

}

static int rm_parse_banks(
	struct platform_device *pdev, unsigned int idx, u32 bank_num, REG16 __iomem ***pppBank)
{
	unsigned int i;
	struct resource *res;
	REG16 __iomem **ppBank;
	struct device *dev = &pdev->dev;
	struct device_node *node = dev->of_node;

	if (pdev == NULL) {
		MTK_DMX_ERR("pdev is NULL\n");
		return -EINVAL;
	}
	if (pppBank == NULL) {
		MTK_DMX_ERR("pppBank is NULL\n");
		return -EINVAL;
	}

	ppBank =
		devm_kzalloc(&pdev->dev, bank_num * sizeof(REG16 *), GFP_KERNEL);
	if (!ppBank) {
		MTK_DMX_ERR("allocate bank array fail\n");
		return -ENOMEM;
	}
	*pppBank = ppBank;

	for (i = 0; i < bank_num; i++) {
		res = platform_get_resource(pdev, IORESOURCE_MEM, idx);
		if (!res) {
			MTK_DMX_ERR("failed to get #%u address\n", i);
			return -ENOENT;
		}

		if ((idx == MAD0_REG_OFFSET) || (idx == MAD1_REG_OFFSET)) {
			if (!node)
				return -ENODEV;

			ppBank[i] = of_iomap(node, idx);
		} else
			ppBank[i] = devm_ioremap_resource(&pdev->dev, res);

		if (IS_ERR((const void *)ppBank[i])) {
			MTK_DMX_ERR("failed to ioremap #%u address\n", i);
			return PTR_ERR((const void *)ppBank[i]);
		}
		idx++;
	}

	return 0;
}
#endif

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)) {
		u32 temp;

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

#ifndef DMX2LITE
static int rm_pool_create(struct device *dev, enum mmap_buf_type type,
	struct mem_info *info, bool dbg)
{
	#define MIN_ALLOC_ORDER 4

	struct mem_info data = { 0 };

	info->is_pool_devm = true;
	switch (info->heaptype) {
	case HEAP_NON_MMA:
		if (rm_mmap_get_memory(dev, type, &data) < 0) {
			MTK_DMX_ERR("allocate memory fail\n");
			return -ENOMEM;
		}
		break;
	case HEAP_CMA:
		data.vaddr = 0;
		data.paddr = info->paddr;
		data.size = info->size;
		info->is_pool_devm = true;
		break;
	case HEAP_IOMMU_POOL:
		data.vaddr = (void *) HEAP_IOMMU_POOL_OFFSET;
		data.paddr = HEAP_IOMMU_POOL_OFFSET;
		data.size = info->size;
		info->is_pool_devm = false;
		break;
	default:
		return 0;
	}

	if (info->is_pool_devm) {
		info->pool = devm_gen_pool_create(
			dev,
			MIN_ALLOC_ORDER,
			-1,
			dmx_mmap[(unsigned int)type].mmap);
	} else {
		info->pool = gen_pool_create(
			MIN_ALLOC_ORDER,
			-1);
	}
	if (info->pool == NULL) {
		MTK_DMX_ERR("gen pool invalid\n");
		return -ENOMEM;
	} else {
		unsigned long vaddr = 0;
		phys_addr_t paddr = 0;

		vaddr = static_cast(unsigned long, data.vaddr);
		paddr = data.paddr;
		if (gen_pool_add_owner(
			info->pool,
			vaddr,
			paddr,
			data.size,
			-1,
			NULL) < 0) {
			MTK_DMX_ERR("gen pool fail\n");
			return -ENOMEM;
		}
	}

	if (dbg) {
		MTK_DMX_INFO("size: %x, pa: %llx, va: %lx, type: %d\n",
			data.size,
			data.paddr,
			(unsigned long)data.vaddr,
			type);
	}

	return 0;
}

static inline size_t rm_pool_chunk_size(const struct gen_pool_chunk *chunk)
{
	return chunk->end_addr - chunk->start_addr + 1;
}

static void rm_pool_dump(struct gen_pool *pool)
{
#define RM_POOL_DUMP_WAIT_TIME (1000)
	struct list_head *_chunk;
	struct gen_pool_chunk *chunk;
	int order = pool->min_alloc_order;
	int bit, end_bit;
	int nbits;
	int nbytes;

	list_for_each(_chunk, &pool->chunks) {
		chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);

		end_bit = rm_pool_chunk_size(chunk) >> order;
		bit = find_next_bit(chunk->bits, end_bit, 0);
		// BUG_ON(bit < end_bit) in gen_pool_destroy
		if (bit < end_bit) {
			MTK_DMX_INFO("end_bit 0x%x bit 0x%x\n", end_bit, bit);
			nbits = rm_pool_chunk_size(chunk) >> order;
			nbytes = sizeof(struct gen_pool_chunk) +
					BITS_TO_LONGS(nbits) * sizeof(long);
			MTK_DMX_INFO("nbits %d nbytes %d\n", nbits, nbytes);
			msleep_interruptible(RM_POOL_DUMP_WAIT_TIME);
		}
	}
#undef RM_POOL_DUMP_WAIT_TIME
}

static int rm_pool_release(struct device *dev, enum mmap_buf_type type,
	struct mem_info *info, bool dbg)
{
	if (dev == NULL) {
		MTK_DMX_ERR("dev is NULL\n");
		return -EINVAL;
	}
	if (info == NULL) {
		MTK_DMX_ERR("info is NULL\n");
		return -EINVAL;
	}
	if (info->pool == NULL) {
		MTK_DMX_ERR("pool is NULL\n");
		return -EINVAL;
	}
	if (info->is_pool_devm)
		return 0;

	rm_pool_dump(info->pool);
	gen_pool_destroy(info->pool);
	info->pool = NULL;

	if (dbg)
		MTK_DMX_INFO("type: %d\n", type);

	return 0;
}

static int rm_pool_buffer_create(struct mtk_dmx_rm *res, enum mmap_buf_type type)
{
	struct mem_info info = {0};
	struct mtk_dmx2dev *mtkdev;
	struct device *dev;
	bool dbg;
	int ret = 0;

	if (res == NULL) {
		MTK_DMX_ERR("dev is NULL\n");
		return -EINVAL;
	}

	mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	dev = mtkdev->dev;
	dbg = mtkdev->rm.mma_dbg_en;

	if (res->memmap->list[type]->heaptype == HEAP_IOMMU_POOL) {
		do {
			if (res->memmap->list[type]->paddr != 0) {
				MTK_DMX_DEBUG("pool buffer for type %d is already created\n", type);
				break;
			}

			memset(&info, 0, sizeof(struct mem_info));
			if (rm_mma_get_memory(
				dev, true, res->memmap->list[type], &info, 0, true) != 0) {
				ret = -ENOMEM;
				break;
			}

			res->memmap->list[type]->paddr = info.paddr;
			res->memmap->list[type]->vaddr = info.vaddr;
			MTK_DMX_INFO("pool buffer for type %d created pa = 0x%llx size = 0x%x\n"
				, type, info.paddr, info.size);

			if (info.paddr == 0 || info.vaddr == 0) {
				MTK_DMX_ERR("buffer allocate failed\n");
				ret = -ENOMEM;
				break;
			}
			if (rm_pool_create(dev, type, res->memmap->list[type], dbg) < 0) {
				MTK_DMX_ERR("rm_pool_create fail\n");
				ret = -ENOMEM;
				break;
			}
			info.pool = res->memmap->list[type]->pool;
			if (info.pool == NULL) {
				MTK_DMX_ERR("pool is null pointer\n");
				ret = -ENOMEM;
				break;
			}
			// Set section buffer range for debug
			mtk_dmx_set_sec_dmaw_protect(info.paddr, info.size, DMAW1, true);
			mtk_dmx_set_sec_dmaw_protect(info.paddr, info.size, DMAW2, true);
			mtk_dmx_set_sec_dmaw_protect(info.paddr, info.size, DMAW3, true);
			mtk_dmx_set_sec_dmaw_protect(info.paddr, info.size, DMAW4, true);
			mtk_dmx_set_sec_dmaw_protect(info.paddr, info.size, DMAW5, true);
		} while (0);
		if (ret != 0) {
			if (info.paddr != 0 && info.vaddr != 0)
				rm_mma_put_memory(dev, &info, true);

			res->memmap->list[type]->paddr = 0;
			res->memmap->list[type]->vaddr = 0;
		}
	} else {
		MTK_DMX_ERR("Invalid heaptype\n");
	}
	return ret;
}

static int rm_pool_buffer_release(struct mtk_dmx_rm *res, enum mmap_buf_type type)
{
	struct mem_info *info;
	struct mtk_dmx2dev *mtkdev;
	struct device *dev;
	bool dbg;

	if (res == NULL) {
		MTK_DMX_ERR("dev is NULL\n");
		return -EINVAL;
	}

	info = res->memmap->list[type];
	mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	dev = mtkdev->dev;
	dbg = mtkdev->rm.mma_dbg_en;

	if (info->heaptype == HEAP_IOMMU_POOL) {
		if (atomic_read(&res->memmap->list[type]->pool_refcnt) != 0) {
			return 0;
		}

		do {
			if (info->paddr == 0) {
				MTK_DMX_DEBUG("pool buffer for type %d is already released\n",
					type);
				break;
			}

			if (rm_pool_release(dev, type, info, dbg) < 0) {
				// Continue even through error is returned
				MTK_DMX_INFO("rm_pool_release fail\n");
			}
			rm_mma_put_memory(dev, info, true);
			info->paddr = 0;
			info->vaddr = 0;
		} while (0);
	} else {
		MTK_DMX_ERR("Invalid heaptype\n");
	}

	MTK_DMX_INFO("pool buffer for type %d released\n", type);

	return 0;
}

static int rm_memmap_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);
	s32 i = 0;
	u64 dma_mask;
	bool bDmxTeeInited;
	bool dbg = mtkdev->rm.mma_dbg_en;

	res->memmap = devm_kzalloc(mtkdev->dev, sizeof(struct dmx_memmap), GFP_KERNEL);
	if (!res->memmap) {
		MTK_DMX_ERR("allocate memmap struct fail\n");
		return -ENOMEM;
	}

	mtk_dmx_ctrl_init(&bDmxTeeInited);

	for (i = 0 ; i < MMAP_BUF_TYPE_MAX ; i++) {
		res->memmap->list[i] = devm_kzalloc(mtkdev->dev, sizeof(struct mem_info),
							GFP_KERNEL);
		if (!res->memmap->list[i]) {
			MTK_DMX_ERR(
				"allocate index %d mem_info of memmap fail\n",
				i);
			return -ENOMEM;
		}
		rm_memmap_get(i, pdev, res->memmap->list[i], res);
		if (res->memmap->list[i]->heaptype == HEAP_IOMMU)
			continue;
		switch (i) {
		case MMAP_BUF_TYPE_VQ:
			continue;
		case MMAP_BUF_TYPE_FW:
			if (bDmxTeeInited)
				continue;
			break;
		default:
			break;
		}

		if (res->memmap->list[i]->heaptype == HEAP_IOMMU_POOL) {
			mutex_init(&res->memmap->list[i]->pool_lock);
			atomic_set(&res->memmap->list[i]->pool_refcnt, 0);
		}

		res->memmap->list[i]->buf_type = i;

		if (res->memmap->list[i]->heaptype != HEAP_IOMMU_POOL) {
			if (rm_pool_create(mtkdev->dev, i, res->memmap->list[i], dbg) < 0)
				MTK_DMX_ERR("fail to create mem pool\n");
		}
	}

#ifndef VPAS_PASS_THROUGH_MODE
	dma_mask = DMA_BIT_MASK(VPAS_BIT_MASK);
	if (dma_set_mask(&pdev->dev, dma_mask) ||
		dma_set_coherent_mask(&pdev->dev, dma_mask)) {
		MTK_DMX_ERR("[vpas] cannot accept dma mask 0x%llx\n", dma_mask);
		return -EOPNOTSUPP;
	}
#endif

	return 0;
}

static int rm_memmap_exit(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);

	iommu_fwspec_free(&pdev->dev);

	return 0;
}

static int rm_regmaps_init(struct mtk_dmx_rm *res)
{
	s32 ret = 0;
	u32 idx;
	struct device_node *bank_node;
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);

	res->regmap = devm_kzalloc(mtkdev->dev, sizeof(struct dmx_regmap), GFP_KERNEL);
	if (!res->regmap) {
		MTK_DMX_ERR("allocate regmap struct fail\n");
		return -ENOMEM;
	}

	memset(&res->regmap->banks[0], 0x0, sizeof(mtk_dmx_riu_bank)*RIU_BANK_TYPE_MAX);

	of_node_get(pdev->dev.of_node);
	bank_node = of_find_node_by_name(pdev->dev.of_node, "banks");
	if (bank_node == NULL) {
		MTK_DMX_ERR("Failed to get banks node\n");
		return -ENOENT;
	}

	ret |= rm_of_read(sizeof(u32), bank_node,
		"tsp_bank_num", &res->regmap->banks[RIU_BANK_TYPE_TS].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"tso_bank_num", &res->regmap->banks[RIU_BANK_TYPE_TS_OUT].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"video_parser_bank_num", &res->regmap->banks[RIU_BANK_TYPE_MVD_PARSER].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"ts_sample_bank_num", &res->regmap->banks[RIU_BANK_TYPE_TS_SAMPLE].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"cilink_bank_num", &res->regmap->banks[RIU_BANK_TYPE_CILINK].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"tlv_bank_num", &res->regmap->banks[RIU_BANK_TYPE_TLV].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"alp_bank_num", &res->regmap->banks[RIU_BANK_TYPE_ALP].bank_num);
	ret |= rm_of_read(sizeof(u32), bank_node,
		"mad_parser_bank_num", &res->regmap->banks[RIU_BANK_TYPE_MAD_PARSER].bank_num);
	of_node_put(bank_node);
	if (ret != 0) {
		MTK_DMX_ERR("Get RIU bank number fail\n");
		return -ENOENT;
	}

	// tsp banks
	idx = 0;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_TS].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_TS].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get tsp addresses\n");
		return ret;
	}

	// tso banks
	idx += res->regmap->banks[RIU_BANK_TYPE_TS].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_TS_OUT].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_TS_OUT].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get tso addresses\n");
		return ret;
	}

	// video parser_sample
	idx += res->regmap->banks[RIU_BANK_TYPE_TS_OUT].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_MVD_PARSER].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_MVD_PARSER].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get video parser_sample addresses\n");
		return ret;
	}

	// tlv banks
	idx += res->regmap->banks[RIU_BANK_TYPE_MVD_PARSER].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_TLV].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_TLV].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get tlv addresses\n");
		return ret;
	}

	// ts_sample
	idx += res->regmap->banks[RIU_BANK_TYPE_TLV].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_TS_SAMPLE].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_TS_SAMPLE].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get ts_sample bank addr\n");
		return ret;
	}

	// cilink_soc_if
	if (res->regmap->banks[RIU_BANK_TYPE_CILINK].bank_num != 0) {
		idx += res->regmap->banks[RIU_BANK_TYPE_TS_SAMPLE].bank_num;
		ret = rm_parse_banks(pdev, idx,
				res->regmap->banks[RIU_BANK_TYPE_CILINK].bank_num,
				&res->regmap->banks[RIU_BANK_TYPE_CILINK].ptr);
		if (ret != 0) {
			MTK_DMX_ERR("failed to get ts_sample bank addr\n");
			return ret;
		}
	}

	// alp banks
	idx += res->regmap->banks[RIU_BANK_TYPE_CILINK].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_ALP].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_ALP].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get alp addresses\n");
		return ret;
	}

	// mad banks
	idx += res->regmap->banks[RIU_BANK_TYPE_ALP].bank_num;
	ret = rm_parse_banks(pdev, idx,
			res->regmap->banks[RIU_BANK_TYPE_MAD_PARSER].bank_num,
			&res->regmap->banks[RIU_BANK_TYPE_MAD_PARSER].ptr);
	if (ret != 0) {
		MTK_DMX_ERR("failed to get mad addresses\n");
		return ret;
	}

	return 0;
}
#endif

static void _create_hwcaps_name_map(struct dmx_hwcaps *caps)
{
	if (MTK_DMX_VERIFY(caps == NULL))
		return;

	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_VFIFO, DMX2_CAPS_VIDEO_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_AFIFO, DMX2_CAPS_AUDIO_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_SEC_FILTER, DMX2_CAPS_SECTION_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_PCR_FILTER, DMX2_CAPS_PCR_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_PID_FILTER, DMX2_CAPS_PID_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_NTP_FILTER, DMX2_CAPS_NTP_FILTER_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_PTP_FILTER, DMX2_CAPS_PTP_FILTER_NUMBER);

	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_PVR, DMX2_CAPS_PVR_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_STC, DMX2_CAPS_STC_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_CILINK, DMX2_CAPS_CILINK_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_TSO, DMX2_CAPS_TSO_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_TSO_TSIF, DMX2_CAPS_TSO_TSIF_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_LIVEIN, DMX2_CAPS_TSIF_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_FILEIN, DMX2_CAPS_FILEIN_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_MMFI, DMX2_CAPS_MMFI_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_TLV, DMX2_CAPS_TLVIF_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_ALP, DMX2_CAPS_ALPIF_NUMBER);
	HWCAPS_ENTRY_SET_NAME(caps, HWCAPS_TYPE_ALP_FILEIN, DMX2_CAPS_ALPIF_FILEIN_NUMBER);
}

static void _create_miup_name_map(struct dmx_miup *miu)
{
	if (MTK_DMX_VERIFY(miu == NULL))
		return;

	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO0_FILEIN_0, MIUP_CID_TSO0_FILEIN_0_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO0_FILEIN_1, MIUP_CID_TSO0_FILEIN_1_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO_SVQ, MIUP_CID_TSO_SVQ_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO1_FILEIN_0, MIUP_CID_TSO1_FILEIN_0_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO1_FILEIN_1, MIUP_CID_TSO1_FILEIN_1_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSO1_SVQ, MIUP_CID_TSO1_SVQ_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_FILEIN_R, MIUP_CID_TSP_FILEIN_R_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_MMFI0_R, MIUP_CID_TSP_MMFI0_R_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_MMFI1_R, MIUP_CID_TSP_MMFI1_R_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_ORZ_W, MIUP_CID_TSP_ORZ_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_ORZ_R, MIUP_CID_TSP_ORZ_R_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_SEC_W, MIUP_CID_TSP_SEC_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_PVR1_W, MIUP_CID_TSP_PVR1_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_PVR2_W, MIUP_CID_TSP_PVR2_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_PVR3_W, MIUP_CID_TSP_PVR3_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_PVR4_W, MIUP_CID_TSP_PVR4_W_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_VQ, MIUP_CID_TSP_VQ_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_VD_PES_PARSER, MIUP_CID_TSP_VD_PARSER_NAME);
	MIUP_ENTRY_SET_NAME(miu, MIUP_CLIENT_CID_TSP_FIQ, MIUP_CID_TSP_FIQ_NAME);
}

static int rm_hwcaps_init(struct mtk_dmx_rm *res)
{
	int ret = 0;
	struct device_node *np;
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	struct platform_device *pdev = to_platform_device(mtkdev->dev);

	res->hwcap = devm_kzalloc(mtkdev->dev, sizeof(struct dmx_hwcaps), GFP_KERNEL);
	if (!res->hwcap) {
		MTK_DMX_ERR("allocate hwcaps struct fail\n");
		return -ENOMEM;
	}

	res->miup = devm_kzalloc(mtkdev->dev, sizeof(struct dmx_miup), GFP_KERNEL);
	if (!res->miup) {
		MTK_DMX_ERR("allocate miup struct fail\n");
		return -ENOMEM;
	}

	_create_hwcaps_name_map(res->hwcap);
	_create_miup_name_map(res->miup);

	of_node_get(pdev->dev.of_node);
	np = of_find_node_by_name(pdev->dev.of_node, "hwcaps");
	if (np == NULL) {
		MTK_DMX_ERR("Failed to get hwcaps node\n");
		return -ENOENT;
	}

	ret |= rm_of_read(sizeof(u16), np,
		"pid_filter_num", &res->hwcap->entries[HWCAPS_TYPE_PID_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"sec_filter_num", &res->hwcap->entries[HWCAPS_TYPE_SEC_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"scramb_filter_num", &res->hwcap->entries[HWCAPS_TYPE_SCRAMB_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"pcr_filter_num", &res->hwcap->entries[HWCAPS_TYPE_PCR_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"mmfi_filter_num", &res->hwcap->entries[HWCAPS_TYPE_MMFI_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"ip_filter_num", &res->hwcap->entries[HWCAPS_TYPE_IP_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"ntp_filter_num", &res->hwcap->entries[HWCAPS_TYPE_NTP_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"ptp_filter_num", &res->hwcap->entries[HWCAPS_TYPE_PTP_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"alp_filter_num", &res->hwcap->entries[HWCAPS_TYPE_ALP_FILTER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"stc_num", &res->hwcap->entries[HWCAPS_TYPE_STC].num);
	ret |= rm_of_read(sizeof(u16), np,
		"vfifo_num", &res->hwcap->entries[HWCAPS_TYPE_VFIFO].num);
	ret |= rm_of_read(sizeof(u16), np,
		"afifo_num", &res->hwcap->entries[HWCAPS_TYPE_AFIFO].num);
	ret |= rm_of_read(sizeof(u16), np,
		"pvr_num", &res->hwcap->entries[HWCAPS_TYPE_PVR].num);
	ret |= rm_of_read(sizeof(u16), np,
		"vq_num", &res->hwcap->entries[HWCAPS_TYPE_VQ].num);
	ret |= rm_of_read(sizeof(u16), np,
		"svq_num", &res->hwcap->entries[HWCAPS_TYPE_SVQ].num);
	ret |= rm_of_read(sizeof(u16), np,
		"livein_num", &res->hwcap->entries[HWCAPS_TYPE_LIVEIN].num);
	ret |= rm_of_read(sizeof(u16), np,
		"filein_num", &res->hwcap->entries[HWCAPS_TYPE_FILEIN].num);
	ret |= rm_of_read(sizeof(u16), np,
		"filein_cmdQ_num", &res->hwcap->entries[HWCAPS_TYPE_FILEIN_CMDQ].num);
	ret |= rm_of_read(sizeof(u16), np,
		"merge_stream_num", &res->hwcap->entries[HWCAPS_TYPE_MERGE_STREAM].num);
	ret |= rm_of_read(sizeof(u16), np,
		"mmfi_num", &res->hwcap->entries[HWCAPS_TYPE_MMFI].num);
	ret |= rm_of_read(sizeof(u16), np,
		"mmfi_cmdQ_num", &res->hwcap->entries[HWCAPS_TYPE_MMFI_CMDQ].num);
	ret |= rm_of_read(sizeof(u16), np,
		"fiq_num", &res->hwcap->entries[HWCAPS_TYPE_FIQ].num);
	ret |= rm_of_read(sizeof(u16), np,
		"tso_num", &res->hwcap->entries[HWCAPS_TYPE_TSO].num);
	ret |= rm_of_read(sizeof(u16), np,
		"tso_tsif_num", &res->hwcap->entries[HWCAPS_TYPE_TSO_TSIF].num);
	ret |= rm_of_read(sizeof(u16), np,
		"tlv_num", &res->hwcap->entries[HWCAPS_TYPE_TLV].num);
	ret |= rm_of_read(sizeof(u16), np,
		"alp_num", &res->hwcap->entries[HWCAPS_TYPE_ALP].num);
	ret |= rm_of_read(sizeof(u16), np,
		"alp_filein_num", &res->hwcap->entries[HWCAPS_TYPE_ALP_FILEIN].num);
	ret |= rm_of_read(sizeof(u16), np,
		"cilink_num", &res->hwcap->entries[HWCAPS_TYPE_CILINK].num);
	ret |= rm_of_read(sizeof(u16), np,
		"ca_num", &res->hwcap->entries[HWCPAS_TYPE_CA].num);
	ret |= rm_of_read(sizeof(u16), np,
		"tlv_filein_mux_num", &res->hwcap->entries[HWCPAS_TYPE_TLV_FILEIN_MUX].num);
	ret |= rm_of_read(sizeof(u16), np,
		"tlv_fr_patch_enable", &res->hwcap->entries[HWCPAS_TYPE_TLV_FR_PATCH_ENABLE].num);

	of_node_put(np);

	// clk rate
	np = of_find_node_by_name(pdev->dev.of_node, "ck_rate");
	if (np == NULL) {
		MTK_DMX_ERR("Failed to get hwcaps node\n");
		return -ENOENT;
	}

	ret |= rm_of_read(sizeof(u16), np,
		"tsp", &res->hwcap->entries[HWCAPS_TYPE_CK_RATE_TSP].num);
	ret |= rm_of_read(sizeof(u16), np,
		"parser", &res->hwcap->entries[HWCAPS_TYPE_CK_RATE_PARSER].num);
	ret |= rm_of_read(sizeof(u16), np,
		"stc", &res->hwcap->entries[HWCAPS_TYPE_CK_RATE_STC].num);

	of_node_put(np);

	// miup cid
	np = of_find_node_by_name(pdev->dev.of_node, "miup_cid");
	if (np == NULL) {
		MTK_DMX_ERR("Failed to get miup_cid node\n");
		return -ENOENT;
	}

	ret |= rm_of_read(sizeof(u16), np,
		"tso0_filein0", &res->miup->entries[MIUP_CLIENT_CID_TSO0_FILEIN_0].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tso0_filein1", &res->miup->entries[MIUP_CLIENT_CID_TSO0_FILEIN_1].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tso_svq", &res->miup->entries[MIUP_CLIENT_CID_TSO_SVQ].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tso1_filein0", &res->miup->entries[MIUP_CLIENT_CID_TSO1_FILEIN_0].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tso1_filein1", &res->miup->entries[MIUP_CLIENT_CID_TSO1_FILEIN_1].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tso1_svq", &res->miup->entries[MIUP_CLIENT_CID_TSO1_SVQ].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_filein_r", &res->miup->entries[MIUP_CLIENT_CID_TSP_FILEIN_R].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_mmfi0_r", &res->miup->entries[MIUP_CLIENT_CID_TSP_MMFI0_R].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_mmfi1_r", &res->miup->entries[MIUP_CLIENT_CID_TSP_MMFI1_R].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_orz_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_ORZ_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_orz_r", &res->miup->entries[MIUP_CLIENT_CID_TSP_ORZ_R].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_sec_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_SEC_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_pvr1_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_PVR1_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_pvr2_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_PVR2_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_pvr3_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_PVR3_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_pvr4_w", &res->miup->entries[MIUP_CLIENT_CID_TSP_PVR4_W].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_vq", &res->miup->entries[MIUP_CLIENT_CID_TSP_VQ].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"vd_pes_parser", &res->miup->entries[MIUP_CLIENT_CID_TSP_VD_PES_PARSER].cid);
	ret |= rm_of_read(sizeof(u16), np,
		"tsp_fiq", &res->miup->entries[MIUP_CLIENT_CID_TSP_FIQ].cid);

	of_node_put(np);

	return (ret == 0) ? 0 : -ENOENT;
}

#ifndef DMX2LITE
static int rm_fifos_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 vfifo_num, afifo_num;
	u32 map_size;

	vfifo_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_VFIFO);
	afifo_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_AFIFO);

	spin_lock_init(&res->fifo_lock);

	map_size = vfifo_num * sizeof(struct fifo_status);
	res->vfifo_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->vfifo_slot_map) {
		MTK_DMX_ERR("allocate vfifo_slot_map fail\n");
		return -ENOMEM;
	}

	map_size = afifo_num * sizeof(struct fifo_status);
	res->afifo_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->afifo_slot_map) {
		MTK_DMX_ERR("allocate afifo_slot_map fail\n");
		return -ENOMEM;
	}

	return 0;
}

static int rm_pvr_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 pvr_eng_num;
	u32 map_size;

	pvr_eng_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PVR);

	spin_lock_init(&res->pvr_lock);

	map_size = pvr_eng_num * sizeof(struct pvr_status);
	res->pvr_eng_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->pvr_eng_slot_map) {
		MTK_DMX_ERR("allocate pvr_eng_slot_map fail\n");
		return -ENOMEM;
	}

	return 0;
}

static int rm_filein_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 filein_eng_num;
	u16 tlv_filein_mux_num;
	u32 map_size;

	filein_eng_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_FILEIN) +
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_MMFI_FILTER);
	tlv_filein_mux_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCPAS_TYPE_TLV_FILEIN_MUX);

	spin_lock_init(&res->filein_lock);

	map_size = filein_eng_num * sizeof(struct filein_status);
	res->filein_eng_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->filein_eng_slot_map) {
		MTK_DMX_ERR("allocate filein_eng_slot_map fail\n");
		return -ENOMEM;
	}

	res->tlv_filein_slot_map = devm_kzalloc(mtkdev->dev,
		tlv_filein_mux_num * sizeof(struct tlv_filein_mux_status), GFP_KERNEL);
	if (!res->tlv_filein_slot_map) {
		MTK_DMX_ERR("allocate filein_eng_slot_map fail\n");
		return -ENOMEM;
	}

	return 0;
}

static int rm_stc_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 stc_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_STC);
	u32 map_size;
	int i;

	spin_lock_init(&res->stc_lock);

	map_size = stc_num * sizeof(struct stc_status);
	res->stc_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->stc_slot_map) {
		MTK_DMX_ERR("allocate stc slot map fail\n");
		return -ENOMEM;
	}
	for (i=0; i<stc_num; i++) {
		res->stc_slot_map[i].ref_cnt = 0;
		res->stc_slot_map[i].path_id = PATH_ID_INVALID;
		res->stc_slot_map[i].filter_id = FILTER_IDX_INVALID;
	}

	return 0;
}

static int rm_tso_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 tso_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_TSO);
	u32 map_size, i;

	spin_lock_init(&res->tso_lock);

	map_size = tso_num * sizeof(struct tso_status);
	res->tso_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->tso_slot_map) {
		MTK_DMX_ERR("allocate tso slot map fail\n");
		return -ENOMEM;
	}

	for (i = 0; i < tso_num ; i++) {
		res->tso_slot_map[i].id = DEMUX_PATH_NULL;
		res->tso_slot_map[i].state = RES_STATE_FREE;
	}

	return 0;
}

static int rm_filters_init(struct mtk_dmx_rm *res)
{
	s32 ret = 0;
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	u16 pid_filter_num, pcr_filter_num, ip_filter_num;
	u32 map_size;
	int i = 0;

	pid_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PID_FILTER);
	pcr_filter_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PCR_FILTER);
	ip_filter_num  = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_IP_FILTER);

	spin_lock_init(&res->pid_filter_lock);
	spin_lock_init(&res->sec_filter_lock);
	spin_lock_init(&res->pcr_filter_lock);
	spin_lock_init(&res->ip_filter_lock);

	map_size = pid_filter_num * sizeof(struct filter_status);
	res->pid_filter_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->pid_filter_slot_map) {
		MTK_DMX_ERR("allocate pid filter slot map fail\n");
		return -ENOMEM;
	}

	map_size = pcr_filter_num * sizeof(struct filter_status);
	res->pcr_filter_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->pcr_filter_slot_map) {
		MTK_DMX_ERR("allocate pcr filter slot map fail\n");
		return -ENOMEM;
	}

	map_size = ip_filter_num * sizeof(struct filter_status);
	res->ip_filter_slot_map = devm_kzalloc(mtkdev->dev, map_size, GFP_KERNEL);
	if (!res->ip_filter_slot_map) {
		MTK_DMX_ERR("allocate ip filter slot map fail\n");
		return -ENOMEM;
	}

	// TODO: Refactor others later
	for (i = 0; i < ARRAY_SIZE(_hwcaps_types); i++) {
		ret = rm_filter_init(res, _hwcaps_types[i]);
		if (ret != 0) {
			MTK_DMX_ERR(
				"rm_filter_init(..., %d) is failed.\n",
				_hwcaps_types[i]);

			ret = rm_deinit(res);

			return -ENOMEM;
		}
	}

	return 0;
}

static int rm_padcfg_init(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);

	res->padcfg = devm_kzalloc(mtkdev->dev, sizeof(struct dmx_padcfg), GFP_KERNEL);
	if (!res->padcfg) {
		MTK_DMX_ERR("allocate pad config fail\n");
		return -ENOMEM;
	}

	res->padcfg->pin_ctrl = devm_pinctrl_get(mtkdev->dev);
	if (!res->padcfg->pin_ctrl) {
		MTK_DMX_ERR("devm_pinctrl_get fail\n");
		return -EINVAL;
	}

	return 0;
}
#endif

int mtk_dmx_rm_init(struct mtk_dmx_rm *res)
{
	s32 ret = 0;
#ifndef DMX2LITE
	//process regmaps
	ret = rm_regmaps_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init regmaps struct fail\n");
		return ret;
	}
	dmx_bank_offsets = res->regmap->banks;

	//Init pin control
	ret = rm_padcfg_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("rm_padcfg_init fail\n");
		return ret;
	}

	//process memmap
	ret = rm_memmap_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init memmap struct fail\n");
		return ret;
	}
#endif
	//process hwcap
	ret = rm_hwcaps_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init hwcaps struct fail\n");
		return ret;
	}
#ifndef DMX2LITE
	ret = rm_fifos_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init fifos struct fail\n");
		return ret;
	}

	ret = rm_pvr_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init pvr struct fail\n");
		return ret;
	}

	ret = rm_filein_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init filein struct fail\n");
		return ret;
	}

	ret = rm_stc_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init stc struct fail\n");
		return ret;
	}

	ret = rm_filters_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init filters' struct fail\n");
		return ret;
	}

	ret = rm_tso_init(res);
	if (ret < 0) {
		MTK_DMX_ERR("init tso' struct fail\n");
		return ret;
	}

	mutex_init(&res->fiq_mutex);
#endif
	return 0;
}

#ifndef DMX2LITE
int mtk_dmx_rm_exit(struct mtk_dmx_rm *res)
{
	int ret = 0;

	dmx_bank_offsets = NULL;
	rm_memmap_exit(res);

	ret = rm_deinit(res);
	if (ret != 0)
		MTK_DMX_ERR(
			"rm_deinit(...) is failed.\n");

	return ret;
}

u16 mtk_dmx_rm_get_hwcap_by_name(struct mtk_dmx_rm *res, const char *name)
{
	struct hwcaps_entry *entries = &(res->hwcap->entries[0]);
	int i;
	u16 num = 0;

	if (!name) {
		MTK_DMX_ERR("name should not be a NULL string\n");
		return 0;
	}

	for (i = 0; i < HWCPAS_TYPE_MAX; i++) {
		if (!strncmp(entries[i].name, name, HWCAPS_ENTRY_NAME_LEN)) {
			num = entries[i].num;
			break;
		}
	}

	return num;
}
#endif

u16 mtk_dmx_rm_get_hwcap_by_id(struct mtk_dmx_rm *res, u32 id)
{
	u16 max_scmb_detect_flt_num = res->hwcap->entries[HWCAPS_TYPE_SCRAMB_FILTER].num;
	u16 num;

	if (id >= HWCPAS_TYPE_MAX)
		return 0;

	num = res->hwcap->entries[id].num;

	if (id == HWCAPS_TYPE_PID_FILTER || id == HWCAPS_TYPE_SEC_FILTER)
		return min(num, max_scmb_detect_flt_num);
	else
		return num;
}

#ifndef DMX2LITE
int mtk_dmx_rm_get_buf(
	struct mtk_dmx_rm *res,
	enum mmap_buf_type type,
	u32 buf_size,
	struct mem_info *mem_info)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	bool dbg = mtkdev->rm.mma_dbg_en;

	int ret = 0;
	enum mmap_heap_type heaptype = res->memmap->list[(unsigned int)type]->heaptype;
	u32 req_size = 0;

	if (!mem_info) {
		MTK_DMX_ERR("mem_info is null pointer\n");
		return -ENOMEM;
	}

	switch (heaptype) {
	case HEAP_IOMMU:
		ret = rm_mma_get_memory(mtkdev->dev, true, res->memmap->list[(unsigned int)type],
			&mem_info[0], buf_size, dbg);

		if (mem_info[0].vaddr == 0) {
			MTK_DMX_ERR("va map failed\n");
			rm_mma_put_memory(mtkdev->dev, &mem_info[0], dbg);
			return -ENOMEM;
		}
		return ret;
	case HEAP_CMA:
	case HEAP_IOMMU_POOL:
		req_size = ALIGN(buf_size, DMX_4K_ALIGN);
		break;
	case HEAP_NON_MMA:
		req_size = buf_size;
		break;
	default:
		MTK_DMX_ERR("type: %x is not support\n", type);
		return -EINVAL;
	}

	ret = rm_pool_get_memory(mtkdev->dev, res, type, &mem_info[0], req_size, dbg);
	if (ret == 0 && heaptype == HEAP_CMA)
		ret = rm_mma_get_memory(mtkdev->rm.cma_dev, false, res->memmap->list[(unsigned int)type],
			&mem_info[0], req_size, dbg);
	if (ret == 0 && heaptype == HEAP_IOMMU_POOL) {
		mem_info[0].cma_base = res->memmap->list[(unsigned int)type]->cma_base;
		mem_info[0].size = req_size;
		mem_info[0].dmabuf_fd = -1;
		mem_info[0].heaptype = HEAP_IOMMU_POOL;
		MTK_DMX_INFO("Get iommu pool, size: %x, pa: %llx, va: %lx, type = %d, ref = %d\n",
			mem_info[0].size,
			mem_info[0].paddr,
			(unsigned long)mem_info[0].vaddr,
			type,
			atomic_read(&res->memmap->list[(unsigned int)type]->pool_refcnt));
	}
	return ret;
}


int mtk_dmx_rm_put_buf(struct mtk_dmx_rm *res, struct mem_info *mem_info)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	bool dbg = mtkdev->rm.mma_dbg_en;
	int ret = 0;
	enum mmap_buf_type type = mem_info[0].buf_type;

	switch (mem_info[0].heaptype) {
	case HEAP_IOMMU:
		rm_mma_put_memory(mtkdev->dev, &mem_info[0], dbg);
		break;
	case HEAP_IOMMU_POOL:
		rm_pool_put_memory(res, mtkdev->dev, &mem_info[0], dbg);
		MTK_DMX_INFO("Put iommu pool, size: %x, pa: %llx, type: %d, ref: %d\n",
			mem_info[0].size, mem_info[0].paddr,
			type,
			atomic_read(&res->memmap->list[(unsigned int)type]->pool_refcnt));
		break;
	case HEAP_CMA:
		rm_mma_put_memory(mtkdev->rm.cma_dev, &mem_info[0], dbg);
		rm_pool_put_memory(res, mtkdev->dev, &mem_info[0], dbg);
		break;
	case HEAP_NON_MMA:
		rm_pool_put_memory(res, mtkdev->dev, &mem_info[0], dbg);
		break;
	default:
		return -EINVAL;
	}
	return ret;
}

void mtk_dmx_rm_flush_buf(struct mtk_dmx_rm *res, struct mem_info *mem_info)
{
	struct mtk_dmx2dev *mtkdev = container_of(res, struct mtk_dmx2dev, rm);
	bool dbg = mtkdev->rm.mma_dbg_en;

	switch (mem_info[0].heaptype) {
	case HEAP_CMA:
		rm_mma_flush_memory(mtkdev->rm.cma_dev, &mem_info[0], dbg);
		break;
	default:
		return;
	}
	return;
}

static int rm_pvr_find_engine_by_path(struct mtk_dmx_rm *res, u16 path_id)
{
	u16 i;
	u16 pvr_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PVR);

	for (i = 0; i < pvr_num; i++) {
		if ((res->pvr_eng_slot_map[i].state != RES_STATE_FREE) &&
			res->pvr_eng_slot_map[i].path_id == path_id) {
			return i;
		}
	}

	return -1;
}

int mtk_dmx_rm_pvr_find_engine_by_path(struct mtk_dmx_rm *res, u16 path_id)
{
	unsigned long flags;
	int engine_id;

	spin_lock_irqsave(&res->pvr_lock, flags);
	engine_id = rm_pvr_find_engine_by_path(res, path_id);
	spin_unlock_irqrestore(&res->pvr_lock, flags);

	return engine_id;
}

int mtk_dmx_rm_pvr_alloc_engine(struct mtk_dmx_rm *res, u16 path_id)
{
	u16 i;
	unsigned long flags;
	u16 pvr_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_PVR);

	spin_lock_irqsave(&res->pvr_lock, flags);

	for (i = 0; i < pvr_num; i++) {
		if ((res->pvr_eng_slot_map[i].state != RES_STATE_FREE) &&
			res->pvr_eng_slot_map[i].path_id == path_id) {
			spin_unlock_irqrestore(&res->pvr_lock, flags);
			return i;
		}
	}

	for (i = 0; i < pvr_num; i++) {
		if (res->pvr_eng_slot_map[i].state == RES_STATE_FREE) {
			res->pvr_eng_slot_map[i].state = RES_STATE_ALLOC;
			res->pvr_eng_slot_map[i].path_id = path_id;
			spin_unlock_irqrestore(&res->pvr_lock, flags);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->pvr_lock, flags);
	return -1;
}

int mtk_dmx_rm_pvr_free_engine(struct mtk_dmx_rm *res, int eng_id)
{
	unsigned long flags;

	if (res->pvr_eng_slot_map[eng_id].state == RES_STATE_FREE)
		return 0;

	spin_lock_irqsave(&res->pvr_lock, flags);
	res->pvr_eng_slot_map[eng_id].state = RES_STATE_FREE;
	spin_unlock_irqrestore(&res->pvr_lock, flags);

	return 0;
}

int mtk_dmx_rm_filein_find_engine_by_path(struct mtk_dmx_rm *res, u16 path_id)
{
	int ret = -ENODEV;
	u16 i;
	unsigned long flags;
	u16 filein_eng_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_FILEIN) +
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_MMFI_FILTER);
	u16 cnt_ts, cnt_tlv, total;
	u16 max_ts = filein_eng_num;
	u16 max_tlv = mtk_dmx_rm_get_hwcap_by_id(res, HWCPAS_TYPE_TLV_FILEIN_MUX);

	spin_lock_irqsave(&res->filein_lock, flags);

	cnt_ts = cnt_tlv = total = 0;
	for (i = 0; i < filein_eng_num; i++) {
		if (res->filein_eng_slot_map[i].state != RES_STATE_FREE) {
			if (IS_TSIF_TS(res->filein_eng_slot_map[i].path_id))
				cnt_ts++;
			else if (IS_TSIF_TLV(res->filein_eng_slot_map[i].path_id))
				cnt_tlv++;
			total++;
		}
	}

	if ((IS_TSIF_TS(path_id) && (cnt_ts > max_ts) && (total < filein_eng_num)) ||
		(IS_TSIF_TLV(path_id) && (cnt_tlv > max_tlv) && (total < filein_eng_num)))
		goto out;

	for (i = 0; i < filein_eng_num; i++) {
		if ((res->filein_eng_slot_map[i].state != RES_STATE_FREE) &&
			res->filein_eng_slot_map[i].path_id == path_id) {
			ret = i;
			goto out;
		}
	}
out:
	spin_unlock_irqrestore(&res->filein_lock, flags);
	return ret;
}

int mtk_dmx_rm_filein_alloc_engine(struct mtk_dmx_rm *res, u16 path_id)
{
	u16 i;
	unsigned long flags;
	u16 filein_eng_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_FILEIN) +
		mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_MMFI_FILTER);
	int eng;

	eng = mtk_dmx_rm_filein_find_engine_by_path(res, path_id);
	if (eng >= 0) {
		return eng;
	}

	spin_lock_irqsave(&res->filein_lock, flags);

	for (i = 0; i < filein_eng_num; i++) {
		if (res->filein_eng_slot_map[i].state == RES_STATE_FREE) {
			res->filein_eng_slot_map[i].state = RES_STATE_ALLOC;
			res->filein_eng_slot_map[i].path_id = path_id;
			spin_unlock_irqrestore(&res->filein_lock, flags);
			if (res->filein_alloc_dbg_en)
				MTK_DMX_INFO("allocate eng:%d for path:%d\n", i, path_id);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->filein_lock, flags);
	if (res->filein_alloc_dbg_en)
		MTK_DMX_INFO("busy, allocate fail\n");
	return -1;
}

int mtk_dmx_rm_filein_free_engine(struct mtk_dmx_rm *res, u16 eng_id)
{
	unsigned long flags;

	if (res->filein_eng_slot_map[eng_id].state == RES_STATE_FREE)
		return 0;
	if (res->filein_alloc_dbg_en)
		MTK_DMX_INFO("free eng:%d\n", eng_id);

	mtk_dmx_io_filein_x_mux_sel(res->filein_eng_slot_map[eng_id].path_id,
		eng_id, false);

	spin_lock_irqsave(&res->filein_lock, flags);
	res->filein_eng_slot_map[eng_id].state = RES_STATE_FREE;
	spin_unlock_irqrestore(&res->filein_lock, flags);

	return 0;
}

int mtk_dmx_rm_tlv_filein_alloc(struct mtk_dmx_rm *res, u16 path_id)
{
	unsigned long flags;
	int eng;
	u16 tlv_filein_mux_num;

	eng = mtk_dmx_rm_tlv_filein_find(res, path_id);
	if (eng >= 0)
		return eng;

	tlv_filein_mux_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCPAS_TYPE_TLV_FILEIN_MUX);

	spin_lock_irqsave(&res->filein_lock, flags);
	for (eng = 0; eng < tlv_filein_mux_num; eng++) {
		if (res->tlv_filein_slot_map[eng].state == RES_STATE_FREE) {
			res->tlv_filein_slot_map[eng].state = RES_STATE_ALLOC;
			res->tlv_filein_slot_map[eng].path_id = path_id;
			spin_unlock_irqrestore(&res->filein_lock, flags);
			if (res->filein_alloc_dbg_en)
				MTK_DMX_INFO("allocate eng:%d for path:%d\n", eng, path_id);
			return eng;
		}
	}
	spin_unlock_irqrestore(&res->filein_lock, flags);
	if (res->filein_alloc_dbg_en)
		MTK_DMX_INFO("busy, allocate fail\n");
	return -1;
}

int mtk_dmx_rm_tlv_filein_find(struct mtk_dmx_rm *res, u16 path_id)
{
	unsigned long flags;
	u16 tlv_filein_mux_num;
	int eng;

	tlv_filein_mux_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCPAS_TYPE_TLV_FILEIN_MUX);

	spin_lock_irqsave(&res->filein_lock, flags);
	for (eng = 0; eng < tlv_filein_mux_num; eng++) {
		if ((res->tlv_filein_slot_map[eng].state == RES_STATE_ALLOC) &&
			(res->tlv_filein_slot_map[eng].path_id == path_id)) {
			spin_unlock_irqrestore(&res->filein_lock, flags);
			return eng;
		}
	}

	spin_unlock_irqrestore(&res->filein_lock, flags);
	return -1;
}

int mtk_dmx_rm_tlv_filein_free(struct mtk_dmx_rm *res, u16 eng_id)
{
	unsigned long flags;
	u16 tlv_filein_mux_num;

	if (res->filein_alloc_dbg_en)
		MTK_DMX_INFO("free eng:%d\n", eng_id);
	tlv_filein_mux_num =
		mtk_dmx_rm_get_hwcap_by_id(res, HWCPAS_TYPE_TLV_FILEIN_MUX);

	spin_lock_irqsave(&res->filein_lock, flags);

	if (eng_id < tlv_filein_mux_num) {
		if (res->tlv_filein_slot_map[eng_id].state == RES_STATE_ALLOC) {
			// disable tlv path mux select
			mtk_dmx_io_set_tlv_input_type(res->tlv_filein_slot_map[eng_id].path_id,
				true, 0, eng_id);

			res->tlv_filein_slot_map[eng_id].path_id = 0;
			res->tlv_filein_slot_map[eng_id].state = RES_STATE_FREE;
		}
	}

	spin_unlock_irqrestore(&res->filein_lock, flags);

	return 0;
}

int mtk_dmx_rm_get_vfifo_idx(struct mtk_dmx_rm *res)
{
	u16 i;
	unsigned long flags;
	u16 vfifo_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_VFIFO);

	spin_lock_irqsave(&res->fifo_lock, flags);

	for (i = 0; i < vfifo_num; i++) {
		if (res->vfifo_slot_map[i].state == RES_STATE_FREE) {
			res->vfifo_slot_map[i].state = RES_STATE_ALLOC;
			spin_unlock_irqrestore(&res->fifo_lock, flags);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->fifo_lock, flags);
	return -1;
}

void mtk_dmx_rm_put_vfifo_idx(struct mtk_dmx_rm *res, int idx)
{
	unsigned long flags;

	spin_lock_irqsave(&res->fifo_lock, flags);
	res->vfifo_slot_map[idx].state = RES_STATE_FREE;
	spin_unlock_irqrestore(&res->fifo_lock, flags);
}

int mtk_dmx_rm_get_afifo_idx(struct mtk_dmx_rm *res)
{
	u16 i;
	unsigned long flags;
	u16 afifo_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_AFIFO);

	spin_lock_irqsave(&res->fifo_lock, flags);

	for (i = 0; i < afifo_num; i++) {
		if (res->afifo_slot_map[i].state == RES_STATE_FREE) {
			res->afifo_slot_map[i].state = RES_STATE_ALLOC;
			spin_unlock_irqrestore(&res->fifo_lock, flags);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->fifo_lock, flags);
	return -1;
}

void mtk_dmx_rm_put_afifo_idx(struct mtk_dmx_rm *res, int idx)
{
	unsigned long flags;

	spin_lock_irqsave(&res->fifo_lock, flags);
	res->afifo_slot_map[idx].state = RES_STATE_FREE;
	spin_unlock_irqrestore(&res->fifo_lock, flags);
}

int mtk_dmx_rm_stc_alloc_engine(struct mtk_dmx_rm *res, u16 path_id, s32 filter_id)
{
	u16 i;
	unsigned long flags;
	u16 stc_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_STC);

	spin_lock_irqsave(&res->stc_lock, flags);

	if (path_id != PATH_ID_INVALID && filter_id != FILTER_IDX_INVALID) {
		for (i = 0; i < stc_num; i++) {
			if (res->stc_slot_map[i].ref_cnt != 0 &&
				res->stc_slot_map[i].path_id == path_id &&
				res->stc_slot_map[i].filter_id == filter_id) {
				res->stc_slot_map[i].ref_cnt++;
				spin_unlock_irqrestore(&res->stc_lock, flags);
				return i;
			}
		}
	}

	for (i = 0; i < stc_num; i++) {
		if (res->stc_slot_map[i].ref_cnt == 0) {
			res->stc_slot_map[i].ref_cnt++;
			res->stc_slot_map[i].path_id = path_id;
			res->stc_slot_map[i].filter_id = filter_id;
			spin_unlock_irqrestore(&res->stc_lock, flags);
			return i;
		}
	}

	spin_unlock_irqrestore(&res->stc_lock, flags);
	return -1;
}

// @TODO: when dmx close and remove parameter indicated, need to call it
int mtk_dmx_rm_stc_free_engine(struct mtk_dmx_rm *res, u16 eng_id)
{
	unsigned long flags;

	spin_lock_irqsave(&res->stc_lock, flags);

	if (res->stc_slot_map[eng_id].ref_cnt != 0) {
		res->stc_slot_map[eng_id].ref_cnt--;
		if (res->stc_slot_map[eng_id].ref_cnt != 0) {
			MTK_DMX_WARN("stc still used, eng_id = %d, ref_cnt = %d, path_id = %d, filter_id = %d\n",
				eng_id, res->stc_slot_map[eng_id].ref_cnt,
				res->stc_slot_map[eng_id].path_id,
				res->stc_slot_map[eng_id].filter_id);
		}
	}

	spin_unlock_irqrestore(&res->stc_lock, flags);

	return 0;
}

int mtk_dmx_rm_alloc_tso_idx(struct mtk_dmx_rm *res, u16 path_or_eng)
{
	u16 i;
	unsigned long flags;
	u16 tso_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_TSO);

	spin_lock_irqsave(&res->tso_lock, flags);

	for (i = 0; i < tso_num; i++) {
		if (res->tso_slot_map[i].state == RES_STATE_ALLOC &&
			res->tso_slot_map[i].id == path_or_eng) {
			spin_unlock_irqrestore(&res->tso_lock, flags);
			return i;
		}
	}

	for (i = 0; i < tso_num; i++) {
		if (res->tso_slot_map[i].state == RES_STATE_FREE) {
			res->tso_slot_map[i].state = RES_STATE_ALLOC;
			res->tso_slot_map[i].id = path_or_eng;
			spin_unlock_irqrestore(&res->tso_lock, flags);
			return i;
		}
	}
	spin_unlock_irqrestore(&res->tso_lock, flags);

	return -1;
}

int mtk_dmx_rm_free_tso_idx(struct mtk_dmx_rm *res, u16 path_or_eng)
{
	u16 i;
	unsigned long flags;
	u16 tso_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_TSO);

	spin_lock_irqsave(&res->tso_lock, flags);

	for (i = 0; i < tso_num; i++) {
		if (res->tso_slot_map[i].state == RES_STATE_ALLOC &&
			res->tso_slot_map[i].id == path_or_eng) {
			res->tso_slot_map[i].state = RES_STATE_FREE;
			spin_unlock_irqrestore(&res->tso_lock, flags);
			return i;
		}
	}
	spin_unlock_irqrestore(&res->tso_lock, flags);

	return -1;
}

int mtk_dmx_rm_get_filter_idx(struct mtk_dmx_rm *res, struct filter_type type)
{
	int ret = 0;
	int filter_idx = FILTER_IDX_INVALID;

	switch (type.main) {
	case DMX2_FILTER_TYPE_TS: {
		switch (type.sub) {
		case FILTER_SUB_TYPE_PID:
		case FILTER_SUB_TYPE_SEC:
			filter_idx = rm_alloc_pid_filter(res, &type);
			break;
		case FILTER_SUB_TYPE_PCR:
			filter_idx = rm_alloc_pcr_filter(res);
			break;
		default:
			break;
		}
		break;
	}
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		filter_idx = rm_alloc_pid_filter(res, &type);
		break;
	case DMX2_FILTER_TYPE_ALP:
		ret = rm_alloc_filter(res, type.sub, &filter_idx);
		if (ret != 0) {
			MTK_DMX_ERR(
				"rm_alloc_filter(..., %d, ...) is failed.\n",
				type.sub);

			return FILTER_IDX_INVALID;
		}
		break;
	case DMX2_FILTER_TYPE_IP:
		switch (type.sub) {
		case FILTER_SUB_TYPE_ALP:
			ret = rm_alloc_filter(res, type.sub, &filter_idx);
			if (ret != 0) {
				MTK_DMX_ERR(
					"rm_alloc_filter(..., %d, ...) is failed.\n",
					type.sub);

				return FILTER_IDX_INVALID;
			}
			break;
		case FILTER_SUB_TYPE_IP:
			filter_idx = rm_alloc_ip_filter(res);
			break;
		default:
			MTK_DMX_ERR(
				"type.sub(%d) is not supported.\n",
				type.sub);

			return FILTER_IDX_INVALID;
		}
		break;
	default:
		break;
	}

	return filter_idx;
}

void mtk_dmx_rm_put_filter_idx(struct mtk_dmx_rm *res, struct filter_type type, u16 filter_id)
{
	int ret = 0;

	switch (type.main) {
	case DMX2_FILTER_TYPE_TS: {
		switch (type.sub) {
		case FILTER_SUB_TYPE_PID:
		case FILTER_SUB_TYPE_SEC:
			rm_release_pid_filter(res, filter_id);
			break;
		case FILTER_SUB_TYPE_PCR:
			rm_release_pcr_filter(res, filter_id);
			break;
		default:
			break;
		}
		break;
	}
	case DMX2_FILTER_TYPE_TLV:
	case DMX2_FILTER_TYPE_MMTP:
		rm_release_pid_filter(res, filter_id);
		break;
	case DMX2_FILTER_TYPE_ALP:
		ret = rm_release_filter(res, type.sub, filter_id);
		if (ret != 0) {
			MTK_DMX_ERR(
				"rm_release_filter(..., %d, %d) is failed.\n",
				type.sub,
				filter_id);

			return;
		}
		break;
	case DMX2_FILTER_TYPE_IP:
		switch (type.sub) {
		case FILTER_SUB_TYPE_ALP:
			ret = rm_release_filter(res, type.sub, filter_id);
			if (ret != 0) {
				MTK_DMX_ERR(
					"rm_release_filter(..., %d, %d) is failed.\n",
					type.sub,
					filter_id);

				return;
			}
			break;
		case FILTER_SUB_TYPE_IP:
			rm_release_ip_filter(res, filter_id);
			break;
		default:
			MTK_DMX_ERR(
				"type.sub(%d) is not supported.\n",
				type.sub);
			return;
		}
		break;
	default:
		break;
	}
}

int mtk_dmx_rm_get_allocated_filter_num(struct mtk_dmx_rm *res)
{
	struct mtk_dmx2dev *mtkdev;
	struct mtk_demux2 *dmx2;
	u16 ts_num, tlv_num, alp_num;
	int path_id;
	int count = 0;

	if (res == NULL)
		return 0;
	mtkdev = container_of(res, struct mtk_dmx2dev, rm);

	ts_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_LIVEIN);
	tlv_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_TLV);
	alp_num = mtk_dmx_rm_get_hwcap_by_id(res, HWCAPS_TYPE_ALP);

	for (path_id = 0; path_id < (ts_num + tlv_num + alp_num); path_id++) {
		dmx2 = &mtkdev->paths[path_id].demux2;
		count += mtk_demux2_get_filter_num(dmx2);
	}

	return count;
}

int mtk_dmx_rm_set_padcfg(struct mtk_dmx_rm *res, u8 *pin)
{
	struct pinctrl_state *state;

	state = pinctrl_lookup_state(res->padcfg->pin_ctrl, pin);
	if (IS_ERR(state)) {
		MTK_DMX_ERR("pinctrl_lookup_state couldn't find state of type %s\n", pin);
		return PTR_ERR(state);
	}

	if (pinctrl_select_state(res->padcfg->pin_ctrl, state)) {
		MTK_DMX_ERR("pinctrl_select_state failed\n");
		return -EINVAL;
	}

	return 0;
}

bool mtk_dmx_rm_check_miup_cid_match(struct mtk_dmx_rm *res, uint32_t hit_cid)
{
	int i;

	for (i = 0; i < MIUP_CLIENT_CID_MAX; i++)
		if (res->miup->entries[i].cid  == hit_cid) {
			MTK_DMX_ERR("Detect IP index:%d, cid = %d, name: %s, miu hit !!!!\n",
				i, hit_cid, res->miup->entries[i].name);
			return true;
		}

	return false;
}

void mtk_dmx_rm_pool_lock(
		struct mtk_dmx_rm *res,
		enum mmap_buf_type type)
{
	enum mmap_heap_type heaptype;

	if (res == NULL || type >= MMAP_BUF_TYPE_MAX || type < 0)
		return;

	heaptype = res->memmap->list[(unsigned int)type]->heaptype;
	if (heaptype == HEAP_IOMMU_POOL) {
		MTK_DMX_DEBUG("enter pool_lock %d\n", type);
		mutex_lock(&res->memmap->list[(unsigned int)type]->pool_lock);
	}
}

void mtk_dmx_rm_pool_unlock(
		struct mtk_dmx_rm *res,
		enum mmap_buf_type type)
{
	enum mmap_heap_type heaptype;

	if (res == NULL)
		return;

	heaptype = res->memmap->list[(unsigned int)type]->heaptype;
	if (heaptype == HEAP_IOMMU_POOL) {
		mutex_unlock(&res->memmap->list[(unsigned int)type]->pool_lock);
		MTK_DMX_DEBUG("exit pool_lock %d\n", type);
	}

}

void mtk_dmx_rm_dump_mem_info(struct mem_info *mem_info, int mem_info_type)
{
	if (mem_info == NULL)
		return;

	MTK_DMX_INFO("mem_info[%d]\n", mem_info_type);

	MTK_DMX_INFO("vaddr 0x%llx\n", (u64)mem_info->vaddr);
	MTK_DMX_INFO("paddr 0x%llx\n", mem_info->paddr);
	MTK_DMX_INFO("size 0x%x\n", mem_info->size);
	MTK_DMX_INFO("heaptype %d\n", mem_info->heaptype);
	MTK_DMX_INFO("id %d\n", mem_info->id);
	MTK_DMX_INFO("dmabuf_fd %d\n", mem_info->dmabuf_fd);
	MTK_DMX_INFO("db 0x%llx\n", (u64)mem_info->db);
	MTK_DMX_INFO("pool 0x%llx\n", (u64)mem_info->pool);
	MTK_DMX_INFO("is_pool_devm %s\n", mem_info->is_pool_devm ? "true" : "false");
	MTK_DMX_INFO("cma_base 0x%lx\n", mem_info->cma_base);
	MTK_DMX_INFO("cma_base 0x%lx\n", mem_info->cma_size);
	MTK_DMX_INFO("pool_name %s\n", mem_info->pool_name ? mem_info->pool_name : "");
	MTK_DMX_INFO("no_mapping %s\n", mem_info->no_mapping ? "true" : "false");
	MTK_DMX_INFO("buf_type %d\n", mem_info->buf_type);
}
#endif
