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

#include <linux/module.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/dma-buf.h>
#include <linux/ion.h>
#include <linux/syscalls.h>

#include "mtk_iommu_common.h"
#include "mtk_iommu_ion.h"
#include "mtk_iommu_dtv_api.h"
#include "mtk-reserved-memory.h"
#include "mtk-cma.h"

#include "mdrv_demux_common.h"
#include "mdrv_demux_debug.h"
#include "mdrv_demux_resource.h"

#define IOMMU_HEAP_NAME	"ion_mtkdtv_iommu_heap"
#define CMA_HEAP_NAME	"mtk-demux"

static ssize_t kobj_attr_get_pid_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_sec_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_stc_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_pcr_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_ip_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_ntp_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_vfifo_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_afifo_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_pvr_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_vq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_svq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_livein_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_merge_stream_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_filein_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_filein_cmdQ_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_mmfi_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_mmfi_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_mmfi_cmdQ_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_fiq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_tso_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_tso_tsif_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_tlv_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_alp_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);
static ssize_t kobj_attr_get_cilink_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf);

static ssize_t kboj_attr_get_mmaps(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

static struct kobj_attribute kobj_attr_pid_filter_num =
	__ATTR(pid_filter_num, 0444, kobj_attr_get_pid_filter_num, NULL);
static struct kobj_attribute kobj_attr_sec_filter_num =
	__ATTR(sec_filter_num, 0444, kobj_attr_get_sec_filter_num, NULL);
static struct kobj_attribute kobj_attr_stc_num =
	__ATTR(stc_num, 0444, kobj_attr_get_stc_num, NULL);
static struct kobj_attribute kobj_attr_pcr_filter_num =
	__ATTR(pcr_filter_num, 0444, kobj_attr_get_pcr_filter_num, NULL);
static struct kobj_attribute kobj_attr_ip_filter_num =
	__ATTR(ip_filter_num, 0444, kobj_attr_get_ip_filter_num, NULL);
static struct kobj_attribute kobj_attr_ntp_filter_num =
	__ATTR(ntp_filter_num, 0444, kobj_attr_get_ntp_filter_num, NULL);
static struct kobj_attribute kobj_attr_vfifo_num =
	__ATTR(vfifo_num, 0444, kobj_attr_get_vfifo_num, NULL);
static struct kobj_attribute kobj_attr_afifo_num =
	__ATTR(afifo_num, 0444, kobj_attr_get_afifo_num, NULL);
static struct kobj_attribute kobj_attr_pvr_num =
	__ATTR(pvr_num, 0444, kobj_attr_get_pvr_num, NULL);
static struct kobj_attribute kobj_attr_vq_num =
	__ATTR(vq_num, 0444, kobj_attr_get_vq_num, NULL);
static struct kobj_attribute kobj_attr_svq_num =
	__ATTR(svq_num, 0444, kobj_attr_get_svq_num, NULL);
static struct kobj_attribute kobj_attr_livein_num =
	__ATTR(livein_num, 0444, kobj_attr_get_livein_num, NULL);
static struct kobj_attribute kobj_attr_merge_stream_num =
	__ATTR(merge_stream_num, 0444, kobj_attr_get_merge_stream_num, NULL);
static struct kobj_attribute kobj_attr_filein_num =
	__ATTR(filein_num, 0444, kobj_attr_get_filein_num, NULL);
static struct kobj_attribute kobj_attr_filein_cmdQ_num =
	__ATTR(filein_cmdQ_num, 0444, kobj_attr_get_filein_cmdQ_num, NULL);
static struct kobj_attribute kobj_attr_mmfi_num =
	__ATTR(mmfi_num, 0444, kobj_attr_get_mmfi_num, NULL);
static struct kobj_attribute kobj_attr_mmfi_filter_num =
	__ATTR(mmfi_filter_num, 0444, kobj_attr_get_mmfi_filter_num, NULL);
static struct kobj_attribute kobj_attr_mmfi_cmdQ_num =
	__ATTR(mmfi_cmdQ_num, 0444, kobj_attr_get_mmfi_cmdQ_num, NULL);
static struct kobj_attribute kobj_attr_fiq_num =
	__ATTR(fiq_num, 0444, kobj_attr_get_fiq_num, NULL);
static struct kobj_attribute kobj_attr_tso_num =
	__ATTR(tso_num, 0444, kobj_attr_get_tso_num, NULL);
static struct kobj_attribute kobj_attr_tso_tsif_num =
	__ATTR(tso_tsif_num, 0444, kobj_attr_get_tso_tsif_num, NULL);
static struct kobj_attribute kobj_attr_tlv_num =
	__ATTR(tlv_num, 0444, kobj_attr_get_tlv_num, NULL);
static struct kobj_attribute kobj_attr_alp_num =
	__ATTR(alp_num, 0444, kobj_attr_get_alp_num, NULL);
static struct kobj_attribute kobj_attr_cilink_num =
	__ATTR(cilink_num, 0444, kobj_attr_get_cilink_num, NULL);

static struct kobj_attribute kobj_attr_mmap = __ATTR(all, 0444, kboj_attr_get_mmaps, NULL);

static struct attribute *hwcaps_attrs[] = {
	&kobj_attr_pid_filter_num.attr,
	&kobj_attr_sec_filter_num.attr,
	&kobj_attr_stc_num.attr,
	&kobj_attr_pcr_filter_num.attr,
	&kobj_attr_ip_filter_num.attr,
	&kobj_attr_ntp_filter_num.attr,
	&kobj_attr_vfifo_num.attr,
	&kobj_attr_afifo_num.attr,
	&kobj_attr_pvr_num.attr,
	&kobj_attr_vq_num.attr,
	&kobj_attr_svq_num.attr,
	&kobj_attr_livein_num.attr,
	&kobj_attr_merge_stream_num.attr,
	&kobj_attr_filein_num.attr,
	&kobj_attr_filein_cmdQ_num.attr,
	&kobj_attr_mmfi_num.attr,
	&kobj_attr_mmfi_filter_num.attr,
	&kobj_attr_mmfi_cmdQ_num.attr,
	&kobj_attr_fiq_num.attr,
	&kobj_attr_tso_num.attr,
	&kobj_attr_tso_tsif_num.attr,
	&kobj_attr_tlv_num.attr,
	&kobj_attr_alp_num.attr,
	&kobj_attr_cilink_num.attr,
	NULL
};

static struct attribute *mmap_attrs[] = {
	&kobj_attr_mmap.attr,
	NULL
};

static struct attribute_group hwcaps_attrs_group = {
	.attrs = hwcaps_attrs,
};

static struct attribute_group mmap_attrs_group = {
	.attrs = mmap_attrs,
};

static inline int mdrv_demux_resource_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) {
		DEMUX_DEBUG_ERR("[%s][%d] Failed to get %s\n",
			__func__, __LINE__, propname);
		return 1;
	}
	return 0;
}

static int mdrv_demux_hwcaps_init(struct platform_device *pdev, struct mdrv_demux_hwcaps *phwcaps)
{
	int ret = 0;
	struct device_node *hwcaps_node;

	DEMUX_KOBJ_INIT_ADD(&phwcaps->kobj, &hwcaps_attrs_group,
					&phwcaps->res->kobj, "hwcaps");

	of_node_get(pdev->dev.of_node);
	hwcaps_node = of_find_node_by_name(pdev->dev.of_node, "hwcaps");
	if (hwcaps_node == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] Failed to get hwcaps node\n", __func__, __LINE__);
		return -ENOENT;
	}

	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"pid_filter_num", &phwcaps->pid_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"sec_filter_num", &phwcaps->sec_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"pcr_filter_num", &phwcaps->pcr_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"mmfi_filter_num", &phwcaps->mmfi_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"ip_filter_num", &phwcaps->ip_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"ntp_filter_num", &phwcaps->ntp_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"alp_filter_num", &phwcaps->alp_filter_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"stc_num", &phwcaps->stc_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"vfifo_num", &phwcaps->vfifo_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"afifo_num", &phwcaps->afifo_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"pvr_num", &phwcaps->pvr_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"vq_num", &phwcaps->vq_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"svq_num", &phwcaps->svq_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"livein_num", &phwcaps->livein_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"filein_num", &phwcaps->filein_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"filein_cmdQ_num", &phwcaps->filein_cmdQ_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"merge_stream_num", &phwcaps->merge_stream_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"mmfi_num", &phwcaps->mmfi_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"mmfi_cmdQ_num", &phwcaps->mmfi_cmdQ_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"fiq_num", &phwcaps->fiq_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"tso_num", &phwcaps->tso_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"tso_tsif_num", &phwcaps->tso_tsif_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"tlv_num", &phwcaps->tlv_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"alp_num", &phwcaps->alp_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"cilink_num", &phwcaps->cilink_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), hwcaps_node,
		"parser_ck_rate", &phwcaps->parser_ck_rate);

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

static int mdrv_demux_query_heap_id(bool iommu)
{
	int i;
	size_t nr_heaps;
	struct ion_heap_data *data;
	int heap_id = -ENODEV;
	char* heap_name;

	nr_heaps = ion_query_heaps_kernel(NULL, 0);
	if (nr_heaps == 0)
		return -ENODEV;

	data = kcalloc(nr_heaps, sizeof(struct ion_heap_data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	if (ion_query_heaps_kernel(data, nr_heaps) == 0) {
		kfree(data);
		DEMUX_DEBUG_ERR("[%s][%d] query heaps failed\n"
			, __func__, __LINE__);
		return -ENODEV;
	}

	if (iommu)
		heap_name = IOMMU_HEAP_NAME;
	else
		heap_name = CMA_HEAP_NAME;

	for (i = 0; i < nr_heaps; i++) {
		if (strstr(data[i].name, heap_name)) {
			heap_id = data[i].heap_id;
			break;
		}
	}
	kfree(data);

	return heap_id;
}

static int mdrv_demux_dma_alloc(int size, int heap, int flags
	, struct dma_buf **db, int *fd, void __iomem **va)
{
	*db = ion_alloc(size, heap, flags);
	if (IS_ERR(*db)) {
		DEMUX_DEBUG_ERR("[%s][%d] ion_alloc size 0x%x bytes failed\n"
			, __func__, __LINE__, size);
		return -ENOMEM;
	}

	*fd = dma_buf_fd(*db, O_CLOEXEC);
	if (*fd < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get dma fd failed\n"
			, __func__, __LINE__);
		dma_buf_put(*db);
		return -EFAULT;
	}
	dma_buf_get(*fd);

	*va = dma_buf_vmap(*db);
	if (IS_ERR_OR_NULL(*va)) {
		DEMUX_DEBUG_ERR("[%s][%d] dma_buf_vmap failed\n"
			, __func__, __LINE__);
		dma_buf_put(*db);
		ksys_close(*fd);
		return -EFAULT;
	}

	return 0;
}

static void mdrv_demux_dma_free(struct dma_buf *db, void __iomem *va, int fd)
{
	dma_buf_vunmap(db, va);
	dma_buf_put(db);
}

static int mdrv_demux_get_memory_iommu(struct platform_device *pdev, int size
	, int id, struct mem_info *info, u32 buf_size)
{
	int ret;
	int heap_id;
	int alloc_size;
	int iommu_size;

	struct dma_buf *db;
	int fd;
	void __iomem *va = NULL;

	dma_addr_t iova;
	int get_size;

	heap_id = mdrv_demux_query_heap_id(true);
	if (heap_id < 0)
		return -EFAULT;

	alloc_size = (buf_size > 0 && buf_size < size) ? buf_size : size;
	iommu_size = ALIGN(alloc_size, DMX_IOVA_ALIGN);
	ret = mdrv_demux_dma_alloc(iommu_size, 1 << heap_id, id << BUFTAG_SHIFT
		, &db, &fd, &va);
	if (ret)
		return ret;

	ret = mtkd_iommu_getiova(fd, &iova, &get_size);
	if (ret || get_size != iommu_size) {
		DEMUX_DEBUG_ERR("[%s][%d] get iova failed\n"
			, __func__, __LINE__);
		ksys_close(fd);
		mdrv_demux_dma_free(db, va, fd);
		return -EFAULT;
	}
	ksys_close(fd);

	info->id = id;
	info->paddr = iova;
	info->size = alloc_size;
	info->vaddr = va;
	info->dmabuf_fd = -1;
	info->heaptype = HEAP_TYPE_IOMMU;
	info->db = db;

	DEMUX_DEBUG_INFO("[id: %d] paddr: %llx, va: %llx, size: %x\n"
		, id , iova, (u64)va, alloc_size);

	return 0;
}

static void mdrv_demux_put_memory_iommu(struct platform_device *pdev
	, struct mem_info *info)
{
	mdrv_demux_dma_free(info->db, info->vaddr, info->dmabuf_fd);

	DEMUX_DEBUG_INFO("[id: %d] paddr: %llx, vaddr: %llx, size: %x\n"
		, info->id, info->paddr, (u64)info->vaddr, info->size);
}

static int mdrv_demux_get_cma_info(struct platform_device *pdev
	, unsigned long *base, unsigned long *size)
{
#define BUF_INFO_ADDR_CELLS 2
#define BUF_INFO_SIZE_CELLS 2
	uint32_t len = 0;
	__be32 *p32 = NULL;
	int id;
	struct device_node *node;
	int naddr = BUF_INFO_ADDR_CELLS;
	int nsize = BUF_INFO_SIZE_CELLS;

	p32 = (__be32 *) of_get_property(pdev->dev.of_node, "cmas", &len);
	if (p32 == NULL)
		return -EFAULT;

	id = be32_to_cpup(p32);
	node = of_parse_phandle(pdev->dev.of_node, "memory-region", id);

	p32 = (__be32 *) of_get_property(node, "reg", &len);
	if (p32 == NULL || ((len / sizeof(__be32)) < (naddr + nsize))) {
		DEMUX_DEBUG_ERR("[%s][%d] cannot get correct reg in node\n"
			, __func__, __LINE__);
		return -EINVAL;
	}
	*base = of_read_number(p32, naddr);
	*size = of_read_number(p32+naddr, nsize);
	return 0;
#undef BUF_INFO_ADDR_CELLS
#undef BUF_INFO_SIZE_CELLS
}

static int mdrv_demux_get_memory_cma(struct platform_device *pdev, u64 ba
	, u64 pa, int size, struct mem_info *info, u32 buf_size)
{
	int ret;
	int heap_id;
	unsigned long cma_base, cma_size;
	int alloc_size;

	struct dma_buf *db;
	int fd;
	void __iomem *va = NULL;

	heap_id = mdrv_demux_query_heap_id(false);
	if (heap_id < 0)
		return -EFAULT;

	alloc_size = (buf_size > 0 && buf_size < size) ? buf_size : size;

	ret = mdrv_demux_get_cma_info(pdev, &cma_base, &cma_size);
	if (ret)
		return ret;
	if (ba < cma_base || ba >= (cma_base + cma_size)) {
		DEMUX_DEBUG_ERR("ba(%X) is not valid. base: %X, size: %X\n"
			, ba, cma_base, cma_size);
		return -EINVAL;
	}
	ret = mdrv_demux_dma_alloc(alloc_size, 1 << heap_id, ba-cma_base
		, &db, &fd, &va);
	if (ret)
		return ret;
	ksys_close(fd);

	info->paddr = pa;
	info->size = alloc_size;
	info->vaddr = va;
	info->dmabuf_fd = -1;
	info->heaptype = HEAP_TYPE_CMA;
	info->db = db;

	DEMUX_DEBUG_INFO("paddr: %llx, vaddr: %llx, size: %x\n"
		, pa, (u64)va, alloc_size);

	return 0;
}

static void mdrv_demux_put_memory_cma(struct platform_device *pdev
	, struct mem_info *info)
{
	mdrv_demux_dma_free(info->db, info->vaddr, info->dmabuf_fd);

	DEMUX_DEBUG_INFO("[id: %d] paddr: %llx, vaddr: %llx, size: %x\n"
		, info->id, info->paddr, (u64)info->vaddr, info->size);
}

static int mdrv_demux_get_memory_mmap(struct platform_device *pdev, u64 ba
	, u64 pa, int size, struct mem_info *info)
{
	info[0].paddr = pa;
	info[0].size = size;
	info[0].dmabuf_fd = -1;
	info[0].heaptype = HEAP_TYPE_NON_MMA;

	if (!devm_request_mem_region(&pdev->dev, ba, size, DRV_NAME)) {
		DEMUX_DEBUG_ERR("[%s][%d] could not reserve memory\n"
			, __func__, __LINE__);
		return -ENODEV;
	}

	info[0].vaddr = devm_ioremap_wc(&pdev->dev, ba, size);
	if (!info[0].vaddr) {
		DEMUX_DEBUG_ERR("[%s][%d] ioremap fail\n", __func__, __LINE__);
		return -ENODEV;
	}

	DEMUX_DEBUG_INFO("paddr: %llx, vaddr: %llx, size: %x\n"
		, pa, (u64)info[0].vaddr, size);

	return 0;
}

static void mdrv_demux_put_memory_mmap(struct platform_device *pdev
	, struct mem_info *info)
{
	devm_iounmap(&pdev->dev, info[0].vaddr);
	devm_release_mem_region(&pdev->dev, PA_to_BA(info[0].paddr)
		, info[0].size);
}

static int mdrv_demux_query_buf_info_iommu(struct device_node *node, int *size
	, int *id)
{
	uint32_t len = 0;
	__be32 *p32 = NULL;
	__be64 *p64 = NULL;

	p64 = (__be64 *) of_get_property(node, "max_size", &len);
	if (p64 == NULL)
		return -EFAULT;
	*size = be64_to_cpup(p64);

	p32 = (__be32 *) of_get_property(node, "id", &len);
	if (p32 == NULL)
		return -EFAULT;
	*id = be32_to_cpup(p32);

	return 0;
}

static int mdrv_demux_query_buf_info_mmap(struct device_node *node
	, int *cma_id, u64 *ba, u64 *pa, int *size)
{
#define BUF_INFO_ADDR_CELLS 2
#define BUF_INFO_SIZE_CELLS 2
	uint32_t len = 0;
	__be32 *p = NULL;

	int naddr = BUF_INFO_ADDR_CELLS;
	int nsize = BUF_INFO_SIZE_CELLS;

	p = (__be32 *) of_get_property(node, "cma_id", &len);
	if (p == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] cannot get cma_id in node\n"
			, __func__, __LINE__);
		return -EINVAL;
	}
	*cma_id = be32_to_cpup(p);

	p = (__be32 *) of_get_property(node, "reg", &len);
	if (p == NULL || ((len / sizeof(__be32)) < (naddr + nsize))) {
		DEMUX_DEBUG_ERR("[%s][%d] cannot get correct reg in node\n"
			, __func__, __LINE__);
		return -EINVAL;
	}
	*ba = of_read_number(p, naddr);
	*size = of_read_number(p + naddr, nsize);
	*pa = BA_to_PA(*ba);

	return 0;
#undef BUF_INFO_ADDR_CELLS
#undef BUF_INFO_SIZE_CELLS
}

int mdrv_demux_get_memory(struct platform_device *pdev, enum memmap_buf_type type, u16 eng_num,
			  struct mem_info **pmem_info, u32 buf_size)
{
	struct mem_info *ptr = NULL;
	char *mmap_name, *buf_tag_name;
	int ret = 0;

	struct device_node *mmap_np = NULL, *tsp_mmap_np = NULL;
	struct device_node *buf_tag_np = NULL, *tsp_buf_tag_np = NULL;
	int cma_id, id, size;
	u64 ba, pa;

	// @NOTE: currently only support eng_num = 1
	ptr =
		(struct mem_info *)devm_kzalloc(&pdev->dev, sizeof(struct mem_info) * eng_num,
						GFP_KERNEL);
	if (!ptr) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate mem_info struct fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	switch (type) {
	case MEMMAP_FW_BUF:
		mmap_name = "MI_TSP_FW_BUF";
		buf_tag_name = "tsp_fw";
		break;
	case MEMMAP_VQ_BUF:
		mmap_name = "MI_TSP_VQ_BUF";
		buf_tag_name = "tsp_vq";
		break;
	case MEMMAP_SEC_BUF:
		mmap_name = "MI_TSP_SECT_BUF";
		buf_tag_name = "tsp_sect";
		break;
	case MEMMAP_TSO_SVQ_BUF:
		mmap_name = "MI_TSP_SVQ_BUF";
		buf_tag_name = "tsp_svq";
		break;
	case MEMMAP_FQ_BUF:
		mmap_name = "MI_TSP_FQ_BUF";
		buf_tag_name = "tsp_fiq";
		break;
	case MEMMAP_PVR_BUF:
		mmap_name = "MI_PVR_DOWNLOAD_BUF";
		buf_tag_name = "tsp_pvr";
		break;
	case MEMMAP_FILEIN_BUF:
		mmap_name = "MI_PVR_UPLOAD_BUF";
		buf_tag_name = "tsp_filein";
		break;
	default:
		return -EINVAL;
	}

	mmap_np = of_find_node_by_name(NULL, "mmap_info");
	if (mmap_np == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] cannot find mtk_mmap node\n"
			, __func__, __LINE__);
		ret = -ENODEV;
		goto DEMUX_GET_MEMORY_ERR;
	}

	buf_tag_np = of_find_node_by_name(NULL, "buf_tag");
	if (buf_tag_np == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] cannot find buf_tag node\n"
			, __func__, __LINE__);
		ret = -ENODEV;
		goto DEMUX_GET_MEMORY_ERR;
	}

	tsp_mmap_np = of_find_node_by_name(mmap_np, mmap_name);
	if (tsp_mmap_np == NULL) {
		tsp_buf_tag_np = of_find_node_by_name(buf_tag_np, buf_tag_name);
		if (tsp_buf_tag_np == NULL) {
			DEMUX_DEBUG_ERR("[%s][%d] buf_tag node %s not found\n"
				, __func__, __LINE__, buf_tag_name);
			ret = -ENODEV;
			goto DEMUX_GET_MEMORY_ERR;
		} else {
			if (mdrv_demux_query_buf_info_iommu(tsp_buf_tag_np
				, &size, &id)) {
				ret = -EFAULT;
				goto DEMUX_GET_MEMORY_ERR;
			}
			DEMUX_DEBUG_INFO("mmap %d uses iommu\n", type);
			ret = mdrv_demux_get_memory_iommu(pdev, size, id
				, &ptr[0], buf_size);
		}
	} else {
		if (mdrv_demux_query_buf_info_mmap(tsp_mmap_np, &cma_id, &ba
			, &pa, &size)) {
			ret = -EFAULT;
			goto DEMUX_GET_MEMORY_ERR;
		}
		if (cma_id == 0) {
			DEMUX_DEBUG_INFO("mmap %d uses non mma\n", type);
			ret = mdrv_demux_get_memory_mmap(pdev, ba, pa, size
				, &ptr[0]);
		} else {
			DEMUX_DEBUG_INFO("mmap %d uses cma\n", type);
			ret = mdrv_demux_get_memory_cma(pdev, ba, pa, size
				, &ptr[0], buf_size);
		}
	}

	if (!ret)
		*pmem_info = ptr;

DEMUX_GET_MEMORY_ERR:
	if (mmap_np)
		of_node_put(mmap_np);
	if (tsp_mmap_np)
		of_node_put(tsp_mmap_np);
	if (buf_tag_np)
		of_node_put(buf_tag_np);
	if (tsp_buf_tag_np)
		of_node_put(tsp_buf_tag_np);
	if (ret)
		devm_kfree(&pdev->dev, ptr);

	return ret;
}

void mdrv_demux_put_memory(struct platform_device *pdev, struct mem_info *pmem_info, u16 eng_num)
{
	if (pmem_info == NULL) 
		return;

	switch (pmem_info[0].heaptype) {
	case HEAP_TYPE_IOMMU:
		mdrv_demux_put_memory_iommu(pdev, &pmem_info[0]);
		break;
	case HEAP_TYPE_CMA:
		mdrv_demux_put_memory_cma(pdev, &pmem_info[0]);
		break;
	case HEAP_TYPE_NON_MMA:
		mdrv_demux_put_memory_mmap(pdev, &pmem_info[0]);
		break;
	default:
		return;
	}

	devm_kfree(&pdev->dev, pmem_info);
}

static int mdrv_demux_memmap_init(struct platform_device *pdev, struct mdrv_demux_memmap *pmemmap)
{
	DEMUX_KOBJ_INIT_ADD(&pmemmap->kobj, &mmap_attrs_group,
					&pmemmap->res->kobj, "mmap");

	if (mdrv_demux_get_memory(pdev, MEMMAP_SEC_BUF, 1, &pmemmap->section_buf, 0) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get SEC buf fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	if (mdrv_demux_get_memory(pdev, MEMMAP_FILEIN_BUF, 1, &pmemmap->filein_buf, 0) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get Filein buf fail\n", __func__, __LINE__);
	}

	if (mdrv_demux_get_memory(pdev, MEMMAP_FQ_BUF, 1, &pmemmap->fq_buf, 0) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get FQ buf fail\n", __func__, __LINE__);
		return -ENOMEM;
	}
#if 0				// currently bypass SVQ
	if (mdrv_demux_get_memory(pdev, MEMMAP_TSO_SVQ_BUF, 1, &pmemmap->svq_buf, 0) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get SVQ buf fail\n", __func__, __LINE__);
		return -ENOMEM;
	}
#endif

	return 0;
}

static int mdrv_demux_memmap_exit(struct platform_device *pdev, struct mdrv_demux_memmap *pmemmap)
{
	mdrv_demux_put_memory(pdev, pmemmap->section_buf, 1);
	mdrv_demux_put_memory(pdev, pmemmap->filein_buf, 1);
	mdrv_demux_put_memory(pdev, pmemmap->fq_buf, 1);

#if 0				// currently bypass SVQ
	mdrv_demux_put_memory(pdev, pmemmap->svq_buf, 1);
#endif

	iommu_fwspec_free(&pdev->dev);

	return 0;
}

static int _mdrv_demux_regmaps_parse_banks(
	struct platform_device *pdev, unsigned int idx, u32 bank_num, REG16 __iomem ***pppBank)
{
	unsigned int i;
	struct resource *res;
	REG16 __iomem **ppBank;

	if (pdev == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] pdev is NULL\n", __func__, __LINE__);
		return -EINVAL;
	}
	if (pppBank == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] pppBank is NULL\n", __func__, __LINE__);
		return -EINVAL;
	}

	ppBank =
		devm_kzalloc(&pdev->dev, bank_num * sizeof(REG16 *),
			GFP_KERNEL);
	if (!ppBank) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate bank array fail\n", __func__, __LINE__);
		return -ENOMEM;
	}
	*pppBank = ppBank;

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

		ppBank[i] = devm_ioremap_resource(&pdev->dev, res);
		if (IS_ERR((const void *)ppBank[i])) {
			DEMUX_DEBUG_ERR("[%s][%d] failed to ioremap #%u address\n",
				__func__, __LINE__, i);
			return PTR_ERR((const void *)ppBank[i]);
		}
		idx++;
	}

	return 0;
}

static int mdrv_demux_regmaps_init(struct platform_device *pdev,
				   struct mdrv_demux_regmaps *pregmaps)
{
	int ret = 0;
	unsigned int i, idx;
	struct resource *res;
	struct device_node *bank_node;

	memset(&pregmaps->riu_banks, 0, sizeof(mtk_demux_riu_banks));

	of_node_get(pdev->dev.of_node);
	bank_node = of_find_node_by_name(pdev->dev.of_node, "banks");
	if (bank_node == NULL) {
		DEMUX_DEBUG_ERR("[%s][%d] Failed to get banks node\n", __func__, __LINE__);
		return -ENOENT;
	}

	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"tsp_bank_num", &pregmaps->riu_banks.tsp_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"tso_bank_num", &pregmaps->riu_banks.tso_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"video_parser_bank_num", &pregmaps->riu_banks.video_parser_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"ts_sample_bank_num", &pregmaps->riu_banks.ts_sample_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"cilink_bank_num", &pregmaps->riu_banks.cilink_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"tlv_bank_num", &pregmaps->riu_banks.tlv_bank_num);
	ret |= mdrv_demux_resource_of_read(sizeof(u32), bank_node,
		"alp_bank_num", &pregmaps->riu_banks.alp_bank_num);
	of_node_put(bank_node);
	if (ret != 0)
		return -ENOENT;

	// tsp banks
	idx = 0;
	ret = _mdrv_demux_regmaps_parse_banks(pdev, idx,
			pregmaps->riu_banks.tsp_bank_num,
			&pregmaps->riu_banks.bank_tsp);
	if (ret != 0) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get tsp addresses\n",
			__func__, __LINE__);
		return ret;
	}

	// tso banks
	idx += pregmaps->riu_banks.tsp_bank_num;
	ret = _mdrv_demux_regmaps_parse_banks(pdev, idx,
			pregmaps->riu_banks.tso_bank_num,
			&pregmaps->riu_banks.bank_tso);
	if (ret != 0) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get tso addresses\n",
			__func__, __LINE__);
		return ret;
	}

	// parser_sample
	idx += pregmaps->riu_banks.tso_bank_num;
	ret = _mdrv_demux_regmaps_parse_banks(pdev, idx,
			pregmaps->riu_banks.video_parser_bank_num,
			&pregmaps->riu_banks.bank_parser);
	if (ret != 0) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get parser_sample addresses\n",
			__func__, __LINE__);
		return ret;
	}

	// tlv banks
	idx += pregmaps->riu_banks.video_parser_bank_num;
	ret = _mdrv_demux_regmaps_parse_banks(pdev, idx,
			pregmaps->riu_banks.tlv_bank_num,
			&pregmaps->riu_banks.bank_tlv);
	if (ret != 0) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get tlv addresses\n",
			__func__, __LINE__);
		return ret;
	}

	// ts_sample
	idx += pregmaps->riu_banks.tlv_bank_num;
	res = platform_get_resource(pdev, IORESOURCE_MEM, idx);
	if (!res) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get ts_sample address\n",
			__func__, __LINE__);
		return -ENOENT;
	}
	pregmaps->riu_banks.bank_ts_sample = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR((const void *)pregmaps->riu_banks.bank_ts_sample)) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to ioremap ts_sample address\n",
			__func__, __LINE__);
		return PTR_ERR((const void *)pregmaps->riu_banks.bank_ts_sample);
	}

	// cilink_soc_if
	if (pregmaps->riu_banks.cilink_bank_num != 0) {
		idx += pregmaps->riu_banks.ts_sample_bank_num;
		res = platform_get_resource(pdev, IORESOURCE_MEM, idx);
		if (!res) {
			DEMUX_DEBUG_ERR("[%s][%d] failed to get cilink_soc_if address\n",
				__func__, __LINE__);
			return -ENOENT;
		}
		pregmaps->riu_banks.bank_cilink = devm_ioremap_resource(&pdev->dev, res);
		if (IS_ERR((const void *)pregmaps->riu_banks.bank_cilink)) {
			DEMUX_DEBUG_ERR("[%s][%d] failed to ioremap cilink_soc_if address\n",
				__func__, __LINE__);
			return PTR_ERR((const void *)pregmaps->riu_banks.bank_cilink);
		}
	}

	// alp banks
	idx += pregmaps->riu_banks.cilink_bank_num;
	ret = _mdrv_demux_regmaps_parse_banks(pdev, idx,
			pregmaps->riu_banks.alp_bank_num,
			&pregmaps->riu_banks.bank_alp);
	if (ret != 0) {
		DEMUX_DEBUG_ERR("[%s][%d] failed to get alp addresses\n",
			__func__, __LINE__);
		return ret;
	}

	return 0;
}

int mdrv_demux_res_init(struct platform_device *pdev, struct mdrv_demux_res *pres)
{
	int ret = 0;

	pres->hwcaps = devm_kzalloc(&pdev->dev, sizeof(struct mdrv_demux_hwcaps), GFP_KERNEL);
	if (!pres->hwcaps) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate hwcaps struct fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	DEMUX_KOBJ_INIT_ADD(&pres->kobj, NULL,
					mdrv_demux_debug_get_root_kobj(), "resources");

	pres->hwcaps->res = pres;
	ret = mdrv_demux_hwcaps_init(pdev, pres->hwcaps);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] init hwcaps struct fail\n", __func__, __LINE__);
		return ret;
	}

	pres->memmap = devm_kzalloc(&pdev->dev, sizeof(struct mdrv_demux_memmap), GFP_KERNEL);
	if (!pres->memmap) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate memmap struct fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	pres->memmap->res = pres;
	ret = mdrv_demux_memmap_init(pdev, pres->memmap);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] init memmap struct fail\n", __func__, __LINE__);
		return ret;
	}

	pres->regmaps = devm_kzalloc(&pdev->dev, sizeof(struct mdrv_demux_regmaps), GFP_KERNEL);
	if (!pres->regmaps) {
		DEMUX_DEBUG_ERR("[%s][%d] allocate regmaps struct fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	pres->regmaps->res = pres;
	ret = mdrv_demux_regmaps_init(pdev, pres->regmaps);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] init regmaps struct fail\n", __func__, __LINE__);
		return ret;
	}

	return 0;
}

int mdrv_demux_res_exit(struct platform_device *pdev, struct mdrv_demux_res *pres)
{
	if (mdrv_demux_memmap_exit(pdev, pres->memmap) < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] exit memmap struct fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	mdrv_demux_debug_remove_attr_group(&pres->hwcaps->kobj, &hwcaps_attrs_group);
	mdrv_demux_debug_remove_attr_group(&pres->memmap->kobj, &mmap_attrs_group);
	mdrv_demux_debug_remove_attr_group(&pres->kobj, NULL);

	return 0;
}

static ssize_t kobj_attr_get_pid_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->pid_filter_num);

	return len;
}

static ssize_t kobj_attr_get_sec_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->sec_filter_num);

	return len;
}

static ssize_t kobj_attr_get_stc_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->stc_num);

	return len;
}

static ssize_t kobj_attr_get_pcr_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->pcr_filter_num);

	return len;
}

static ssize_t kobj_attr_get_ip_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->ip_filter_num);

	return len;
}

static ssize_t kobj_attr_get_ntp_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->ntp_filter_num);

	return len;
}

static ssize_t kobj_attr_get_vfifo_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->vfifo_num);

	return len;
}

static ssize_t kobj_attr_get_afifo_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->afifo_num);

	return len;
}

static ssize_t kobj_attr_get_pvr_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->pvr_num);

	return len;
}

static ssize_t kobj_attr_get_vq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->vq_num);

	return len;
}

static ssize_t kobj_attr_get_svq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->svq_num);

	return len;
}

static ssize_t kobj_attr_get_livein_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->livein_num);

	return len;
}

static ssize_t kobj_attr_get_merge_stream_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->merge_stream_num);

	return len;
}

static ssize_t kobj_attr_get_filein_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->filein_num);

	return len;
}

static ssize_t kobj_attr_get_filein_cmdQ_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->filein_cmdQ_num);

	return len;
}

static ssize_t kobj_attr_get_mmfi_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->mmfi_num);

	return len;
}

static ssize_t kobj_attr_get_mmfi_filter_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->mmfi_filter_num);

	return len;
}

static ssize_t kobj_attr_get_mmfi_cmdQ_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->mmfi_cmdQ_num);

	return len;
}

static ssize_t kobj_attr_get_fiq_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->fiq_num);

	return len;
}

static ssize_t kobj_attr_get_tso_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->tso_num);

	return len;
}

static ssize_t kobj_attr_get_tso_tsif_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->tso_tsif_num);

	return len;
}

static ssize_t kobj_attr_get_tlv_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->tlv_num);

	return len;
}

static ssize_t kobj_attr_get_alp_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->alp_num);

	return len;
}

static ssize_t kobj_attr_get_cilink_num(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_hwcaps *hwcaps = container_of(kobj, struct mdrv_demux_hwcaps, kobj);
	int len;

	len = scnprintf(buf, PAGE_SIZE, "%u\n", hwcaps->cilink_num);

	return len;
}

static ssize_t kboj_attr_get_mmaps(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	struct mdrv_demux_memmap *mmaps = container_of(kobj, struct mdrv_demux_memmap, kobj);
	char *output = kzalloc(PAGE_SIZE, GFP_KERNEL);
	char cur[64] = { 0 };
	int len;

	if (!output) {
		DEMUX_DEBUG_ERR("[%s][%d] kmalloc buffer fail\n", __func__, __LINE__);
		return -ENOMEM;
	}

	if (mmaps->fw_buf) {
		snprintf(cur, sizeof(cur), "fw_buf = 0x%llx 0x%x\n", mmaps->fw_buf->paddr,
			 mmaps->fw_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->vq_buf) {
		snprintf(cur, sizeof(cur), "vq_buf = 0x%llx 0x%x\n", mmaps->vq_buf->paddr,
			 mmaps->vq_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->section_buf) {
		snprintf(cur, sizeof(cur), "section_buf = 0x%llx 0x%x\n", mmaps->section_buf->paddr,
			 mmaps->section_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->pvr_buf) {
		snprintf(cur, sizeof(cur), "pvr_buf = 0x%llx 0x%x\n", mmaps->pvr_buf->paddr,
			 mmaps->pvr_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->filein_buf) {
		snprintf(cur, sizeof(cur), "filein_buf = 0x%llx 0x%x\n", mmaps->filein_buf->paddr,
			 mmaps->filein_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->fq_buf) {
		snprintf(cur, sizeof(cur), "fq_buf = 0x%llx 0x%x\n", mmaps->fq_buf->paddr,
			 mmaps->fq_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	if (mmaps->svq_buf) {
		snprintf(cur, sizeof(cur), "svq_buf = 0x%llx 0x%x\n", mmaps->svq_buf->paddr,
			 mmaps->svq_buf->size);
		strncat(output, cur, strlen(cur) + 1);
	}

	len = scnprintf(buf, PAGE_SIZE, "%s", output);

	kfree(output);

	return len;
}
