// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cma.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/genalloc.h>
#include <linux/dma-mapping.h>
#include <linux/dma-map-ops.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched/clock.h>

#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/page-isolation.h>
#include <asm/pgtable.h>
#ifdef CONFIG_ARM64
#include <asm/mte.h>
#endif
#include <linux/swap.h>
#include <linux/swapops.h>

#if IS_ENABLED(CONFIG_AMLOGIC_CPU_INFO)
#include <linux/amlogic/media/registers/cpu_version.h>
#else
unsigned char get_meson_cpu_version(int level)
{
	return 0;
}
#endif

#if IS_BUILTIN(CONFIG_AMLOGIC_MEDIA_MODULE)
#include <linux/amlogic/aml_cma.h>
#endif

#include "codec_mm_priv.h"
#include "codec_mm_scatter_priv.h"
#include "codec_mm_keeper_priv.h"
#include "codec_mm_track_priv.h"
#include <linux/highmem.h>
#include <linux/page-flags.h>
#include <linux/vmalloc.h>

#if IS_ENABLED(CONFIG_AMLOGIC_TEE)
#include <linux/amlogic/tee.h>
#else
#define TEE_MEM_TYPE_STREAM_INPUT	0x4
u32 tee_protect_tvp_mem(u32 start, u32 size,
			u32 *handle)
{
	return 0xFFFFFFFF;
}

int tee_check_in_mem(u32 pa, u32 size)
{
	pr_info("no tee config\n");
	return (-1);
}

void tee_unprotect_tvp_mem(u32 handle)
{
}

int tee_check_out_mem(u32 pa, u32 size)
{
	pr_info("no tee config\n");
	return (-1);
}

u32 tee_protect_mem_by_type(u32 type,
		u32 start, u32 size,
		u32 *handle)
{
	pr_info("no tee config\n");
	return (-1);
}

void tee_unprotect_mem(u32 handle)
{
}

u32 tee_register_mem(u32 type, phys_addr_t pa, size_t size)
{
	pr_info("no tee config\n");
	return (-1);
}
#endif

#if IS_MODULE(CONFIG_AMLOGIC_MEDIA_MODULE) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
/* aml_media is ko can't use cma_mmu_op() func */
void (*aml_mte_sync_tags)(pte_t old_pte, pte_t pte);

struct mm_struct *aml_init_mm;

struct device *codec_dev;

#ifdef CONFIG_ARM64
static void aml_set_pte_at(struct mm_struct *mm, unsigned long addr,
			      pte_t *ptep, pte_t pte)
{
	/*
	 * if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte))
	 *	__sync_icache_dcache(pte);
	 */

	/*
	 * If the PTE would provide user space access to the tags associated
	 * with it then ensure that the MTE tags are synchronised.  Although
	 * pte_access_permitted() returns false for exec only mappings, they
	 * don't expose tags (instruction fetches don't check tags).
	 */
	if (system_supports_mte() && pte_access_permitted(pte, false) &&
	    !pte_special(pte)) {
		pte_t old_pte = READ_ONCE(*ptep);
		/*
		 * We only need to synchronise if the new PTE has tags enabled
		 * or if swapping in (in which case another mapping may have
		 * set tags in the past even if this PTE isn't tagged).
		 * (!pte_none() && !pte_present()) is an open coded version of
		 * is_swap_pte()
		 */
		if (pte_tagged(pte) || (!pte_none(old_pte) && !pte_present(old_pte)))
			aml_mte_sync_tags(old_pte, pte);
	}

	__check_racy_pte_update(mm, ptep, pte);

	set_pte(ptep, pte);
}

static bool is_cma_page(struct page *page)
{
	int migrate_type = 0;

	if (!page)
		return false;
	migrate_type = get_pageblock_migratetype(page);
	if (is_migrate_cma(migrate_type) ||
	    is_migrate_isolate(migrate_type)) {
		return true;
	}
	return false;
}

int cma_mmu_op(struct page *page, int count, bool set)
{
	pgd_t *pgd;
	p4d_t *p4d;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long addr, end;
	struct mm_struct *mm;
	struct cma *cma;

	if (!page || PageHighMem(page))
		return -EINVAL;

	/* TODO: owner must make sure this cma pool have called
	 * setup_cma_full_pagemap before call this function
	 */
	if (!is_cma_page(page)) {
		pr_debug("%s, page:%lx is not cma or no clear-map, cma:%px\n",
			 __func__, page_to_pfn(page), cma);
		return -EINVAL;
	}

	if (!aml_init_mm || !aml_mte_sync_tags) {
		pr_debug("%s, no cma mmu operation.\n", __func__);
		return -EINVAL;
	}

	addr = (unsigned long)page_address(page);
	end  = addr + count * PAGE_SIZE;
	mm = aml_init_mm;
	for (; addr < end; addr += PAGE_SIZE) {
		pgd = pgd_offset(mm, addr);
		if (pgd_none(*pgd) || pgd_bad(*pgd))
			break;

		p4d = p4d_offset(pgd, addr);
		if (p4d_none(*p4d) || p4d_bad(*p4d))
			break;

		pud = pud_offset(p4d, addr);
		if (pud_none(*pud) || pud_bad(*pud))
			break;

		pmd = pmd_offset(pud, addr);
		if (pmd_none(*pmd))
			break;

		pte = pte_offset_map(pmd, addr);
		if (set)
			aml_set_pte_at(mm, addr, pte, mk_pte(page, PAGE_KERNEL));
		else
			pte_clear(mm, addr, pte);
		pte_unmap(pte);
	#ifdef CONFIG_ARM
		pr_debug("%s, add:%lx, pgd:%p %x, pmd:%p %x, pte:%p %x\n",
			 __func__, addr, pgd, (int)pgd_val(*pgd),
			 pmd, (int)pmd_val(*pmd), pte, (int)pte_val(*pte));
	#elif defined(CONFIG_ARM64)
		pr_debug("%s, add:%lx, pgd:%p %llx, pmd:%p %llx, pte:%p %llx\n",
			 __func__, addr, pgd, pgd_val(*pgd),
			 pmd, pmd_val(*pmd), pte, pte_val(*pte));
	#endif
		page++;
	}
	return 0;
}
#else
int cma_mmu_op(struct page *page, int count, bool set)
{
	return 0;
}
#endif
#endif
#include <linux/amlogic/cpu_version.h>

#define TVP_POOL_NAME "TVP_POOL"
#define CMA_RES_POOL_NAME "CMA_RES"

#define CONFIG_PATH "media.codec_mm"
#define CONFIG_PREFIX "media"

#define MM_ALIGN_DOWN_2N(addr, alg2n)  ((addr) & (~((1 << (alg2n)) - 1)))

#define RES_IS_MAPED
#define DEFAULT_TVP_SIZE_FOR_4K (236 * SZ_1M)
#define DEFAULT_TVP_SIZE_FOR_NO4K (160 * SZ_1M)
#define DEFAULT_TVP_SEGMENT_MIN_SIZE (16 * SZ_1M)

#define ALLOC_MAX_RETRY 1

#define CODEC_MM_FOR_DMA_ONLY(flags) \
	(((flags) & CODEC_MM_FLAGS_FROM_MASK) == CODEC_MM_FLAGS_DMA)

#define CODEC_MM_FOR_CPU_ONLY(flags) \
	(((flags) & CODEC_MM_FLAGS_FROM_MASK) == CODEC_MM_FLAGS_CPU)

#define CODEC_MM_FOR_DMACPU(flags) \
	(((flags) & CODEC_MM_FLAGS_FROM_MASK) == CODEC_MM_FLAGS_DMA_CPU)

#define RESERVE_MM_ALIGNED_2N	17

#define TVP_MM_ALIGNED_2N	16

#define RES_MEM_FLAGS_HAVE_MAPED 0x4

#define MAP_RANGE (SZ_1M)
#ifdef CONFIG_PRINTK_CALLER
#define PREFIX_MAX		48
#else
#define PREFIX_MAX		32
#endif

/* the maximum size of a formatted record (i.e. with prefix added per line) */
#define CONSOLE_LOG_MAX		1024

/* the maximum size allowed to be reserved for a record */
#define LOG_LINE_MAX		(CONSOLE_LOG_MAX - PREFIX_MAX)

static void dump_mem_infos(void);

static int dump_free_mem_infos(void *buf, int size);
static int __init secure_vdec_res_setup(struct reserved_mem *rmem);

static inline u32 codec_mm_align_up2n(u32 addr, u32 alg2n)
{
	return ((addr + (1 << alg2n) - 1) & (~((1 << alg2n) - 1)));
}

static inline u64 codec_mm_get_current_ms(void)
{
	return div64_u64(local_clock(), 1000000);
}

/* dma_alloc_from_contiguous and dma_release_from_contiguous
 * not EXPORT_SYMBOL in kernel. To be consistent with 5.4,
 * copy them in here when media build as ko.
 */
#if IS_MODULE(CONFIG_AMLOGIC_MEDIA_MODULE)
/**
 * dma_alloc_from_contiguous() - allocate pages from contiguous area
 * @dev:   Pointer to device for which the allocation is performed.
 * @count: Requested number of pages.
 * @align: Requested alignment of pages (in PAGE_SIZE order).
 * @no_warn: Avoid printing message about failed allocation.
 *
 * This function allocates memory buffer for specified device. It uses
 * device specific contiguous memory area if available or the default
 * global one. Requires architecture specific dev_get_cma_area() helper
 * function.
 */
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
				       unsigned int align, bool no_warn)
{
	if (align > CONFIG_CMA_ALIGNMENT)
		align = CONFIG_CMA_ALIGNMENT;

#if CONFIG_AMLOGIC_KERNEL_VERSION >= 14515
	if (no_warn)
		return cma_alloc(dev_get_cma_area(dev), count, align, GFP_KERNEL | __GFP_NOWARN);
	else
		return cma_alloc(dev_get_cma_area(dev), count, align, GFP_KERNEL);
#else
	return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
#endif
}

/**
 * dma_release_from_contiguous() - release allocated pages
 * @dev:   Pointer to device for which the pages were allocated.
 * @pages: Allocated pages.
 * @count: Number of allocated pages.
 *
 * This function releases memory allocated by dma_alloc_from_contiguous().
 * It returns false when provided pages do not belong to contiguous area and
 * true otherwise.
 */
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
				 int count)
{
	return cma_release(dev_get_cma_area(dev), pages, count);
}

int is_vmalloc_or_module_addr(const void *x)
{
	/*
	 * ARM, x86-64 and sparc64 put modules in a special place,
	 * and fall back on vmalloc() if that fails. Others
	 * just put it in the vmalloc space.
	 */
#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
	unsigned long addr = (unsigned long)kasan_reset_tag(x);

	if (addr >= MODULES_VADDR && addr < MODULES_END)
		return 1;
#endif
	return is_vmalloc_addr(x);
}

struct cma {
	unsigned long   base_pfn;
	unsigned long   count;
	unsigned long   *bitmap;
	unsigned int order_per_bit; /* Order of pages represented by one bit */
	spinlock_t	lock;
};

phys_addr_t cma_get_base(const struct cma *cma)
{
	return PFN_PHYS(cma->base_pfn);
}

unsigned long cma_get_size(const struct cma *cma)
{
	return cma->count << PAGE_SHIFT;
}
#endif

/*
 *debug_mode:
 *
 *disable reserved:1
 *disable cma:2
 *disable sys memory:4
 *disable half memory:8,
 *	only used half memory,for debug.
 *	return nomem,if alloced > total/2;
 *dump memory info on failed:0x10,
 *trace memory alloc/free info:0x20,
 */
static u32 debug_mode;

static u32 debug_sc_mode;
u32 codec_mm_get_sc_debug_mode(void)
{
	return debug_sc_mode;
}
EXPORT_SYMBOL(codec_mm_get_sc_debug_mode);

static u32 debug_keep_mode;
u32 codec_mm_get_keep_debug_mode(void)
{
	return debug_keep_mode;
}
EXPORT_SYMBOL(codec_mm_get_keep_debug_mode);

static u32 default_tvp_size;
static u32 default_tvp_4k_size;
static u32 default_cma_res_size;
static u32 default_tvp_pool_segment_size[4];

static u32 default_tvp_pool_size_0;
static u32 tvp_pool_segment_maxsize_0;
static u32 default_tvp_pool_size_1;
static u32 default_tvp_pool_size_2;
static u32 tvp_dynamic_increase_disable;
static u32 tvp_dynamic_alloc_max_size;
static u32 tvp_dynamic_alloc_force_small_segment;
static u32 tvp_dynamic_alloc_force_small_segment_size;
static u32 tvp_pool_early_release_switch;
static bool dbuf_trace;

#define TVP_POOL_SEGMENT_MAX_USED 4
#define TVP_MAX_SLOT 8
/*
 *tvp_mode == 0 means protect secure memory in secmem ta
 *tvp_mode == 1 means use protect secure memory in codec_mm.
 */
static u32 tvp_mode;

struct extpool_mgt_s {
	struct gen_pool *gen_pool[TVP_MAX_SLOT];
	struct codec_mm_s *mm[TVP_MAX_SLOT];
	int slot_num;
	int alloced_size;
	int total_size;
	/* mutex lock */
	struct mutex pool_lock;
	struct task_struct *tvp_kthread;
};

struct codec_mm_mgt_s {
	struct cma *cma;
	struct device *dev;
	struct list_head mem_list;
	struct gen_pool *res_pool;
	struct extpool_mgt_s tvp_pool;
	struct extpool_mgt_s cma_res_pool;
	struct reserved_mem rmem;
	int total_codec_mem_size;
	int total_alloced_size;
	int total_cma_size;
	int total_reserved_size;
	int max_used_mem_size;

	int alloced_res_size;
	int alloced_cma_size;
	int alloced_sys_size;
	int alloced_for_sc_size;
	int alloced_for_sc_cnt;
	int alloced_from_coherent;
	int phys_vmaped_page_cnt;

	int alloc_from_sys_pages_max;
	int enable_kmalloc_on_nomem;
	int res_mem_flags;
	int global_memid;
	/*1:for 1080p,2:for 4k*/
	int tvp_enable;
	/*1:for 1080p,2:for 4k */
	int fastplay_enable;
	int codec_mm_for_linear;
	int codec_mm_scatter_watermark;
	int codec_mm_scatter_free_size;
	/* spin lock for protect scatter_watermark */
	spinlock_t scatter_watermark_lock;
	/* spin lock */
	spinlock_t lock;
	atomic_t tvp_user_count;
	/* for tvp operator used */
	struct mutex tvp_protect_lock;
	void *trk_h;
};

#define PHY_OFF() offsetof(struct codec_mm_s, phy_addr)
#define HANDLE_OFF() offsetof(struct codec_mm_s, mem_handle)
#define VADDR_OFF() offsetof(struct codec_mm_s, vbuffer)
#define VAL_OFF_VAL(mem, off) (*(unsigned long *)((unsigned long)(mem) + (off)))

static int codec_mm_extpool_pool_release(struct extpool_mgt_s *tvp_pool);
static int codec_mm_tvp_pool_alloc_by_slot(struct extpool_mgt_s *tvp_pool, int memflags, int flags);
static int codec_mm_tvp_pool_unprotect_and_release(struct extpool_mgt_s *tvp_pool);

static struct codec_mm_mgt_s *get_mem_mgt(void)
{
	static struct codec_mm_mgt_s mgt;
	static int inited;
	int ret = 0;
	int handle = 0;

	if (!inited) {
		memset(&mgt, 0, sizeof(struct codec_mm_mgt_s));
		/*If tee_protect_tvp_mem is not implement
		 *will return 0xFFFFFFF we used to init use
		 *which mode
		 */
		ret = tee_protect_tvp_mem(0, 0, &handle);
		if (ret == 0xFFFFFFFF) {
			tvp_mode = 0;
			tvp_dynamic_increase_disable = 1;
		} else {
			tvp_mode = 1;
		}
		mutex_init(&mgt.tvp_protect_lock);
		inited++;
	}
	return &mgt;
};

static void *codec_mm_extpool_alloc(struct extpool_mgt_s *tvp_pool,
				    void **from_pool, int size)
{
	int i = 0;
	void *handle = NULL;

	for (i = 0; i < tvp_pool->slot_num; i++) {
		if (!tvp_pool->gen_pool[i])
			return NULL;
		handle = (void *)gen_pool_alloc(tvp_pool->gen_pool[i], size);
		if (handle) {
			*from_pool = tvp_pool->gen_pool[i];
			return handle;
		}
	}
	return NULL;
}

static void *codec_mm_extpool_free(struct gen_pool *from_pool, void *handle,
				   int size)
{
	gen_pool_free(from_pool, (unsigned long)handle, size);
	return 0;
}

static int codec_mm_valid_mm_locked(struct codec_mm_s *mmhandle)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem;
	int have_found = 0;

	if (!list_empty(&mgt->mem_list)) {
		list_for_each_entry(mem, &mgt->mem_list, list) {
			if (mem == mmhandle) {
				have_found =  1;
				break;
			}
		}
	}
	return have_found;
}

static int codec_mm_alloc_tvp_pre_check_in(struct codec_mm_mgt_s *mgt,
		int need_size, int flags)
{
	if (!mgt)
		return 0;

	struct extpool_mgt_s *tvp_pool = &mgt->tvp_pool;
	int i = 0;

	mutex_lock(&tvp_pool->pool_lock);
	for (i = 0; i < tvp_pool->slot_num; i++) {
		if (gen_pool_avail(tvp_pool->gen_pool[i]) >= need_size) {
			mutex_unlock(&tvp_pool->pool_lock);
			return 1;
		}
	}
	mutex_unlock(&tvp_pool->pool_lock);
	return 0;
}

/*
 *have_space:
 *1:	can alloced from reserved
 *2:	can alloced from cma
 *4:  can alloced from sys.
 *8:  can alloced from tvp.
 *
 *flags:
 *	is tvp = (flags & 1)
 */
static int codec_mm_alloc_pre_check_in(struct codec_mm_mgt_s *mgt,
				       int need_size, int flags)
{
	int have_space = 0;
	int aligned_size = PAGE_ALIGN(need_size);

	if (aligned_size <= mgt->total_reserved_size - mgt->alloced_res_size)
		have_space |= 1;
	if (aligned_size <= mgt->cma_res_pool.total_size -
		mgt->cma_res_pool.alloced_size)
		have_space |= 1;

	if (aligned_size <= mgt->total_cma_size - mgt->alloced_cma_size)
		have_space |= 2;

	if (aligned_size / PAGE_SIZE <= mgt->alloc_from_sys_pages_max)
		have_space |= 4;
	if (tvp_dynamic_increase_disable) {
		if (aligned_size <= mgt->tvp_pool.total_size -
			mgt->tvp_pool.alloced_size)
			have_space |= 8;
	} else {
		if (flags & CODEC_MM_FLAGS_TVP) {
			if (codec_mm_alloc_tvp_pre_check_in(mgt, aligned_size, flags)) {
				have_space |= 8;
			} else {
				do {
					if (codec_mm_tvp_pool_alloc_by_slot(&mgt->tvp_pool,
						0, mgt->tvp_enable) == 0) {
						pr_err("no more memory %d", mgt->tvp_enable);
						break;
					}
					if (codec_mm_alloc_tvp_pre_check_in(mgt, aligned_size,
						flags)) {
						have_space |= 8;
						break;
					}
				} while (mgt->tvp_pool.slot_num <= TVP_POOL_SEGMENT_MAX_USED);
			}
		}
	}

	if (debug_mode & 0xf) {
		have_space = have_space & (~(debug_mode & 1));
		have_space = have_space & (~(debug_mode & 2));
		have_space = have_space & (~(debug_mode & 4));
		if (debug_mode & 8) {
			if (mgt->total_alloced_size >
				mgt->total_codec_mem_size / 2) {
				pr_info("codec mm memory is limited by %d, (bit8 enable)\n",
					debug_mode);
				have_space = 0;
			}
		}
	}

	return have_space;
}

static ulong codec_mm_search_phy_addr(char *vaddr)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	ulong phy_addr = 0;
	unsigned long flags;

	spin_lock_irqsave(&mgt->lock, flags);

	list_for_each_entry(mem, &mgt->mem_list, list) {
		/*
		 * If work on the fast play mode that is allocate a lot of
		 * memory from CMA, It will be a reserve memory and add to the
		 * codec_mm list, but this area can't be used for search vaddr
		 * thus the node of codec_mm list must be ignored.
		 */
		if (mem->flags & CODEC_MM_FLAGS_FOR_LOCAL_MGR)
			continue;

		if (vaddr - mem->vbuffer >= 0 &&
		    vaddr - mem->vbuffer < mem->buffer_size) {
			if (mem->phy_addr)
				phy_addr = mem->phy_addr +
					(vaddr - mem->vbuffer);
			break;
		}
	}

	spin_unlock_irqrestore(&mgt->lock, flags);

	return phy_addr;
}

static void *codec_mm_search_vaddr(unsigned long phy_addr)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	void *vaddr = NULL;
	unsigned long flags;

	if (!PageHighMem(phys_to_page(phy_addr)))
		return phys_to_virt(phy_addr);

	spin_lock_irqsave(&mgt->lock, flags);

	list_for_each_entry(mem, &mgt->mem_list, list) {
		/*
		 * If work on the fast play mode that is allocate a lot of
		 * memory from CMA, It will be a reserve memory and add to the
		 * codec_mm list, but this area can't be used for search vaddr
		 * thus the node of codec_mm list must be ignored.
		 */
		if (mem->flags & CODEC_MM_FLAGS_FOR_LOCAL_MGR)
			continue;

		if (phy_addr >= mem->phy_addr &&
		    phy_addr < mem->phy_addr + mem->buffer_size) {
			if (mem->vbuffer)
				vaddr = mem->vbuffer +
					(phy_addr - mem->phy_addr);
			break;
		}
	}

	spin_unlock_irqrestore(&mgt->lock, flags);

	return vaddr;
}

u8 *codec_mm_vmap(ulong addr, u32 size)
{
	u8 *vaddr = NULL;
	struct page **pages = NULL;
	u32 i, npages, offset = 0;
	ulong phys, page_start;
	pgprot_t pgprot = PAGE_KERNEL;

	if (!PageHighMem(phys_to_page(addr)))
		return phys_to_virt(addr);

	offset = offset_in_page(addr);
	page_start = addr - offset;
	npages = DIV_ROUND_UP(size + offset, PAGE_SIZE);

	pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
	if (!pages)
		return NULL;

	for (i = 0; i < npages; i++) {
		phys = page_start + i * PAGE_SIZE;
		pages[i] = pfn_to_page(phys >> PAGE_SHIFT);
	}

	vaddr = vmap(pages, npages, VM_MAP, pgprot);
	if (!vaddr) {
		pr_err("the phy(%lx) vmaped fail, size: %d\n",
		       page_start, npages << PAGE_SHIFT);
		kfree(pages);
		return NULL;
	}

	kfree(pages);

	if (debug_mode & 0x20) {
		pr_info("[HIGH-MEM-MAP] %s, pa(%lx) to va(%lx), size: %d\n",
			__func__, page_start, (ulong)vaddr,
			npages << PAGE_SHIFT);
	}

	return vaddr + offset;
}
EXPORT_SYMBOL(codec_mm_vmap);

void codec_mm_memset(ulong phys, u32 val, u32 size)
{
	void *ptr = NULL;
	struct page *page = phys_to_page(phys);
	int i, len;

	/*any data lurking in the kernel direct-mapped region is invalidated.*/
	if (!PageHighMem(page)) {
		ptr = page_address(page);
		codec_mm_dma_flush(ptr, size, DMA_FROM_DEVICE);
		return;
	}

	/* memset highmem area */
	for (i = 0; i < size; i += MAP_RANGE) {
		len = ((size - i) > MAP_RANGE) ? MAP_RANGE : size - i;
		ptr = codec_mm_vmap(phys + i, len);
		if (!ptr) {
			pr_err("%s,vmap the page failed.\n", __func__);
			return;
		}
		memset(ptr, val, len);
		codec_mm_dma_flush(ptr, len, DMA_TO_DEVICE);
		codec_mm_unmap_phyaddr(ptr);
	}
}
EXPORT_SYMBOL(codec_mm_memset);

void codec_mm_unmap_phyaddr(u8 *vaddr)
{
	void *addr = (void *)(PAGE_MASK & (ulong)vaddr);

	if (is_vmalloc_or_module_addr(vaddr))
		vunmap(addr);
}
EXPORT_SYMBOL(codec_mm_unmap_phyaddr);

static void *codec_mm_map_phyaddr(struct codec_mm_s *mem)
{
	void *vaddr = NULL;
	unsigned int phys = mem->phy_addr;
	unsigned int size = mem->buffer_size;

	if (!PageHighMem(phys_to_page(phys)))
		return phys_to_virt(phys);

	vaddr = codec_mm_vmap(phys, size);
	/*vaddr = ioremap_nocache(phy_addr, size);*/

	if (vaddr)
		mem->flags |= CODEC_MM_FLAGS_FOR_PHYS_VMAPED;

	return vaddr;
}

int codec_mm_add_release_callback(struct codec_mm_s *mem, struct codec_mm_cb_s *cb)
{
	unsigned long flags;

	if (!mem || !cb)
		return -EINVAL;

	spin_lock_irqsave(&mem->lock, flags);

	if (cb->func) {
		list_add_tail(&cb->node, &mem->release_cb_list);
		mem->release_cb_cnt += 1;
	} else {
		INIT_LIST_HEAD(&cb->node);
	}
	spin_unlock_irqrestore(&mem->lock, flags);

	return 0;
}
EXPORT_SYMBOL(codec_mm_add_release_callback);

static int codec_mm_alloc_in(struct codec_mm_mgt_s *mgt, struct codec_mm_s *mem)
{
	int try_alloced_from_sys = 0;
	int try_alloced_from_reserved = 0;
	int align_2n = mem->align2n < PAGE_SHIFT ? PAGE_SHIFT : mem->align2n;
	int try_cma_first = mem->flags & CODEC_MM_FLAGS_CMA_FIRST;
	int max_retry = ALLOC_MAX_RETRY;
	int have_space;
	int alloc_trace_mask = 0;

	int can_from_res = ((mgt->res_pool ||
		(mgt->cma_res_pool.total_size > 0)) &&	/*have res */
		!(mem->flags & CODEC_MM_FLAGS_CMA)) ||	/*must not CMA */
		((mem->flags & CODEC_MM_FLAGS_RESERVED) ||/*need RESERVED */
		CODEC_MM_FOR_DMA_ONLY(mem->flags) ||	/*NO CPU */
		((mem->flags & CODEC_MM_FLAGS_CPU) &&
			(mgt->res_mem_flags & RES_MEM_FLAGS_HAVE_MAPED)));
	 /*CPU*/
	int can_from_cma = ((mgt->total_cma_size > 0) &&/*have cma */
		!(mem->flags & CODEC_MM_FLAGS_RESERVED)) ||
		(mem->flags & CODEC_MM_FLAGS_CMA);	/*can from CMA */
	/*not always reserved. */

	int can_from_sys = (mem->flags & CODEC_MM_FLAGS_DMA_CPU) &&
		(mem->page_count <= mgt->alloc_from_sys_pages_max);

	int can_from_tvp = (mem->flags & CODEC_MM_FLAGS_TVP);

	if (can_from_tvp) {
		can_from_sys = 0;
		can_from_res = 0;
		can_from_cma = 0;
	}

	have_space = codec_mm_alloc_pre_check_in(mgt, mem->buffer_size, mem->flags);
	if (!have_space)
		return -10001;

	can_from_res = can_from_res && (have_space & 1);
	can_from_cma = can_from_cma && (have_space & 2);
	can_from_sys = can_from_sys && (have_space & 4);
	can_from_tvp = can_from_tvp && (have_space & 8);
	if (!can_from_res && !can_from_cma &&
	    !can_from_sys && !can_from_tvp) {
		if (debug_mode & 0x10)
			pr_info("error, codec mm have space:%x\n",
				have_space);
		return -10002;
	}

	do {
		if ((mem->flags & CODEC_MM_FLAGS_DMA_CPU) &&
		    mem->page_count <= mgt->alloc_from_sys_pages_max &&
		    align_2n <= PAGE_SHIFT) {
			alloc_trace_mask |= 1 << 0;
			mem->mem_handle = (void *)__get_free_pages(GFP_KERNEL,
				get_order(mem->buffer_size));
			mem->from_flags =
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_PAGES;
			if (mem->mem_handle) {
				mem->vbuffer = mem->mem_handle;
				mem->phy_addr = virt_to_phys(mem->mem_handle);
				break;
			}
			try_alloced_from_sys = 1;
		}
		/*cma first. */
		if (try_cma_first && can_from_cma) {
			/*
			 *normal cma.
			 */
			alloc_trace_mask |= 1 << 1;
			mem->mem_handle =
				dma_alloc_from_contiguous(mgt->dev,
							  mem->page_count,
							  align_2n -
							  PAGE_SHIFT, false);
			mem->from_flags = AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA;
			if (mem->mem_handle) {
				mem->vbuffer = mem->mem_handle;
				mem->phy_addr =
				 page_to_phys((struct page *)mem->mem_handle);
#ifdef CONFIG_ARM64
				if (mem->flags & CODEC_MM_FLAGS_CMA_CLEAR) {
					/*dma_clear_buffer((struct page *)*/
					/*mem->vbuffer, mem->buffer_size);*/
				}
#endif
				break;
			}
		}
		/*reserved alloc..*/
		if ((can_from_res && mgt->res_pool) &&
			align_2n <= RESERVE_MM_ALIGNED_2N) {
			int aligned_buffer_size = ALIGN(mem->buffer_size,
				(1 << RESERVE_MM_ALIGNED_2N));
			alloc_trace_mask |= 1 << 2;
			mem->mem_handle = (void *)gen_pool_alloc(mgt->res_pool,
							aligned_buffer_size);
			mem->from_flags =
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_REVERSED;
			if (mem->mem_handle) {
				/*default is no maped */
				mem->vbuffer = NULL;
				mem->phy_addr = (unsigned long)mem->mem_handle;
				mem->buffer_size = aligned_buffer_size;
				break;
			}
			try_alloced_from_reserved = 1;
		}
		/*can_from_res is reserved.. */
		if (can_from_res) {
			if (mgt->cma_res_pool.total_size > 0 &&
			    (mgt->cma_res_pool.alloced_size +
			    mem->buffer_size) < mgt->cma_res_pool.total_size) {
				/*
				 *from cma res first.
				 */
				int aligned_buffer_size =
					ALIGN(mem->buffer_size,
					      (1 << RESERVE_MM_ALIGNED_2N));
				alloc_trace_mask |= 1 << 3;
				mem->mem_handle =
					(void *)
					codec_mm_extpool_alloc(&mgt
							       ->cma_res_pool,
								&mem->from_ext,
							  aligned_buffer_size);
				mem->from_flags =
					AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES;
				if (mem->mem_handle) {
					/*no vaddr for TVP MEMORY */
					mem->phy_addr =
						(unsigned long)mem->mem_handle;
					mem->buffer_size = aligned_buffer_size;
					mem->vbuffer = (mem->flags &
						CODEC_MM_FLAGS_CPU) ?
						codec_mm_map_phyaddr(mem) :
						NULL;
					break;
				}
			}
		}
		if (can_from_cma) {
			/*
			 *normal cma.
			 */
			alloc_trace_mask |= 1 << 4;
			mem->mem_handle =
				dma_alloc_from_contiguous(mgt->dev,
							  mem->page_count,
							  align_2n - PAGE_SHIFT,
							  false);
			mem->from_flags = AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA;
			if (mem->mem_handle) {
				mem->phy_addr =
					page_to_phys((struct page *)
						mem->mem_handle);

				if (mem->flags & CODEC_MM_FLAGS_CPU)
					mem->vbuffer =
						codec_mm_map_phyaddr(mem);
#ifdef CONFIG_ARM64
				if (mem->flags & CODEC_MM_FLAGS_CMA_CLEAR) {
					/*dma_clear_buffer((struct page *)*/
					/*mem->vbuffer, mem->buffer_size);*/
				}
#endif
				break;
			}
		}
		if (can_from_tvp &&
			align_2n <= TVP_MM_ALIGNED_2N) {
			/* 64k,align */
			int aligned_buffer_size = ALIGN(mem->buffer_size,
					(1 << TVP_MM_ALIGNED_2N));
			alloc_trace_mask |= 1 << 5;
			if (tvp_dynamic_increase_disable) {
				mem->mem_handle = (void *)codec_mm_extpool_alloc(&mgt->tvp_pool,
					&mem->from_ext,
					aligned_buffer_size);
				mem->from_flags =
					AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP;
				if (mem->mem_handle) {
					/*no vaddr for TVP MEMORY */
					mem->vbuffer = NULL;
					mem->phy_addr = (unsigned long)mem->mem_handle;
					mem->buffer_size = aligned_buffer_size;
					break;
				}
			} else {
				do {
					mem->mem_handle =
						(void *)codec_mm_extpool_alloc(&mgt->tvp_pool,
							&mem->from_ext,
							aligned_buffer_size);
					if (mem->mem_handle) {
						/*no vaddr for TVP MEMORY */
						mem->from_flags =
							AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP;
						mem->vbuffer = NULL;
						mem->phy_addr = (unsigned long)mem->mem_handle;
						mem->buffer_size = aligned_buffer_size;
						break;
					}
					if (codec_mm_tvp_pool_alloc_by_slot(&mgt->tvp_pool,
						0, mgt->tvp_enable) == 0) {
						pr_err("no more memory can be alloc %d",
							mgt->tvp_enable);
						break;
					}
				} while (mgt->tvp_pool.slot_num <= TVP_POOL_SEGMENT_MAX_USED);
			}
		}

		if ((mem->flags & CODEC_MM_FLAGS_DMA_CPU) &&
		    mgt->enable_kmalloc_on_nomem &&
		    !try_alloced_from_sys) {
			alloc_trace_mask |= 1 << 6;
			mem->mem_handle = (void *)__get_free_pages(GFP_KERNEL,
				get_order(mem->buffer_size));
			mem->from_flags =
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_PAGES;
			if (mem->mem_handle) {
				mem->vbuffer = mem->mem_handle;
				mem->phy_addr =
					virt_to_phys((void *)mem->mem_handle);
				break;
			}
		}
	} while (--max_retry > 0);

	if (mem->mem_handle)
		return 0;
	if (debug_mode & 0x10) {
		pr_info("codec mm have space:%x\n",
			have_space);
		pr_info("canfrom: %d,%d,%d,%d\n",
			can_from_tvp,
			can_from_sys,
			can_from_res,
			can_from_cma);
		pr_info("alloc flags:%d,align=%d,%d,pages:%d,s:%d\n",
			mem->flags,
			mem->align2n,
			align_2n,
			mem->page_count,
			mem->buffer_size);
		pr_info("try alloc mask:%x\n",
			alloc_trace_mask);
	}
	return -10003;
}

static void codec_mm_free_in(struct codec_mm_mgt_s *mgt,
			     struct codec_mm_s *mem)
{
	unsigned long flags;

	if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA) {
		if (mem->flags & CODEC_MM_FLAGS_FOR_PHYS_VMAPED)
			codec_mm_unmap_phyaddr(mem->vbuffer);

		dma_release_from_contiguous(mgt->dev, mem->mem_handle,
					    mem->page_count);
	} else if (mem->from_flags ==
		AMPORTS_MEM_FLAGS_FROM_GET_FROM_REVERSED) {
		gen_pool_free(mgt->res_pool,
			      (unsigned long)mem->mem_handle,
			      mem->buffer_size);
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP) {
		codec_mm_extpool_free((struct gen_pool *)mem->from_ext,
				      mem->mem_handle,
				      mem->buffer_size);
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_PAGES) {
		free_pages((unsigned long)mem->mem_handle,
			   get_order(mem->buffer_size));
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES) {
		if (mem->flags & CODEC_MM_FLAGS_FOR_PHYS_VMAPED)
			codec_mm_unmap_phyaddr(mem->vbuffer);
		codec_mm_extpool_free((struct gen_pool *)mem->from_ext,
				      mem->mem_handle,
				      mem->buffer_size);
	}
	spin_lock_irqsave(&mgt->lock, flags);
	if (!(mem->flags & CODEC_MM_FLAGS_FOR_LOCAL_MGR))
		mgt->total_alloced_size -= mem->buffer_size;
	if (mem->flags & CODEC_MM_FLAGS_FOR_SCATTER) {
		mgt->alloced_for_sc_size -= mem->buffer_size;
		mgt->alloced_for_sc_cnt--;
	}

	if (mem->flags & CODEC_MM_FLAGS_FOR_PHYS_VMAPED)
		mgt->phys_vmaped_page_cnt -= mem->page_count;

	if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA) {
		mgt->alloced_cma_size -= mem->buffer_size;
	} else if (mem->from_flags ==
		AMPORTS_MEM_FLAGS_FROM_GET_FROM_REVERSED) {
		mgt->alloced_res_size -= mem->buffer_size;
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP) {
		mgt->tvp_pool.alloced_size -= mem->buffer_size;
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_PAGES) {
		mgt->alloced_sys_size -= mem->buffer_size;
	} else if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES) {
		mgt->cma_res_pool.alloced_size -= mem->buffer_size;
	}

	spin_unlock_irqrestore(&mgt->lock, flags);
	if (mem->from_flags == AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP &&
	    tvp_mode >= 1) {
		mutex_lock(&mgt->tvp_protect_lock);
		if (atomic_read(&mgt->tvp_user_count) == 0) {
			if (codec_mm_tvp_pool_unprotect_and_release(&mgt->tvp_pool) == 0) {
				mgt->tvp_enable = 0;
				pr_info("disable tvp\n");
			}
		}
		mutex_unlock(&mgt->tvp_protect_lock);
	}

	/*return;*/
}

static void dump_tvp_pool_info(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct extpool_mgt_s *tvp_pool = &mgt->tvp_pool;
	int i, ret, size = 0;
	char *buf, *pbuf;

	buf = kzalloc(120, GFP_KERNEL);
	if (!buf)
		return;
	pbuf = buf;

	for (i = 0; i < TVP_POOL_SEGMENT_MAX_USED; i++) {
		struct gen_pool *gpool = tvp_pool->gen_pool[i];

		if (gpool) {
			ret = snprintf(pbuf, 120 - size,
					"TVP_POOL[%d]%lx/%lx\n", i,
					(ulong)(gen_pool_size(gpool) - gen_pool_avail(gpool)),
					(ulong)gen_pool_size(gpool));
			pbuf += ret;
			size += ret;
		}
	}
	pr_info("%s", buf);
	kfree(buf);
}

struct codec_mm_s *codec_mm_alloc(const char *owner, int size,
				  int align2n, int memflags)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = kmalloc(sizeof(*mem), GFP_KERNEL);
	int count;
	int ret;
	unsigned long flags;

	if (!mem) {
		pr_err("not enough mem for struct codec_mm_s\n");
		return NULL;
	}

	if (mgt->tvp_enable & 3) {
		/*if tvp & video decoder used tvp memory.*/
		   /*Audio don't protect for default now.*/
		if (memflags & CODEC_MM_FLAGS_TVP) {
			/*clear other flags, when tvp mode.*/
			memflags = memflags & (~CODEC_MM_FLAGS_FROM_MASK);
			memflags |= CODEC_MM_FLAGS_TVP;
		}
	} else { /*tvp not enabled*/
		if (memflags & CODEC_MM_FLAGS_TVP) {
			if (tvp_dynamic_increase_disable)
				pr_err("TVP not enabled, when alloc from tvp %s need %d\n",
					owner, size);
		}
	}
	if ((memflags & CODEC_MM_FLAGS_FROM_MASK) == 0)
		memflags |= CODEC_MM_FLAGS_DMA;

	memset(mem, 0, sizeof(struct codec_mm_s));
	mem->buffer_size = PAGE_ALIGN(size);
	count = mem->buffer_size / PAGE_SIZE;
	mem->page_count = count;
	mem->align2n = align2n;
	mem->flags = memflags;
	INIT_LIST_HEAD(&mem->release_cb_list);
	mem->release_cb_cnt = 0;
	ret = codec_mm_alloc_in(mgt, mem);
	if (ret < 0) {
		if (mgt->alloced_for_sc_cnt > 0 && /*have used for scatter.*/
			!(memflags & CODEC_MM_FLAGS_FOR_SCATTER)) {
			/*if not scatter, free scatter caches. */
			pr_err(" No mem ret=%d, clear scatter cache!!\n", ret);
			dump_free_mem_infos(NULL, 0);
			if (memflags & CODEC_MM_FLAGS_TVP)
				codec_mm_scatter_free_all_ignorecache(2);
			else
				codec_mm_scatter_free_all_ignorecache(1);
		}
		if (atomic_read(&mgt->tvp_user_count) != 0 &&
			!(memflags & CODEC_MM_FLAGS_TVP) &&
			strncmp((void *)mem->owner, TVP_POOL_NAME, strlen(TVP_POOL_NAME)) &&
			tvp_pool_early_release_switch) {
			codec_mm_tvp_pool_unprotect_and_release(&mgt->tvp_pool);
			dump_tvp_pool_info();
		}
		ret = codec_mm_alloc_in(mgt, mem);
	}
	if (ret < 0) {
		if (mgt->total_codec_mem_size - mgt->total_alloced_size <= size)
			pr_err("not enough mem for %s size %d, ret=%d\n",
				owner, size, ret);
		else
			pr_err("not enough mem for %s size %d, ret=%d due to mem fragmentation\n",
				owner, size, ret);
		pr_err("mem flags %d %d, %d\n",
		       memflags, mem->flags, align2n);
		kfree(mem);
		dump_mem_infos();
		if (mgt->tvp_enable)
			dump_tvp_pool_info();
		return NULL;
	}

	atomic_set(&mem->use_cnt, 1);
	mem->owner[0] = owner;
	spin_lock_init(&mem->lock);
	spin_lock_irqsave(&mgt->lock, flags);
	mem->mem_id = mgt->global_memid++;
	list_add_tail(&mem->list, &mgt->mem_list);
	switch (mem->from_flags) {
	case AMPORTS_MEM_FLAGS_FROM_GET_FROM_PAGES:
		mgt->alloced_sys_size += mem->buffer_size;
		break;
	case AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA:
		mgt->alloced_cma_size += mem->buffer_size;
		break;
	case AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP:
		mgt->tvp_pool.alloced_size += mem->buffer_size;
		break;
	case AMPORTS_MEM_FLAGS_FROM_GET_FROM_REVERSED:
		mgt->alloced_res_size += mem->buffer_size;
		break;
	case AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES:
		mgt->cma_res_pool.alloced_size += mem->buffer_size;
		break;
	default:
		pr_err("error alloc flags %d\n", mem->from_flags);
	}
	if (!(mem->flags & CODEC_MM_FLAGS_FOR_LOCAL_MGR)) {
		mgt->total_alloced_size += mem->buffer_size;
		if (mgt->total_alloced_size > mgt->max_used_mem_size)
			mgt->max_used_mem_size = mgt->total_alloced_size;
	}
	if ((mem->flags & CODEC_MM_FLAGS_FOR_SCATTER)) {
		mgt->alloced_for_sc_size += mem->buffer_size;
		mgt->alloced_for_sc_cnt++;
	}

	if (mem->flags & CODEC_MM_FLAGS_FOR_PHYS_VMAPED)
		mgt->phys_vmaped_page_cnt += mem->page_count;

	spin_unlock_irqrestore(&mgt->lock, flags);
	mem->alloced_jiffies = get_jiffies_64();
	if (debug_mode & 0x20)
		pr_err("mem_id [%d] %s alloc size %d at 0x%lx from %d,2n:%d,flags:%d\n",
		       mem->mem_id, owner, size, mem->phy_addr,
		       mem->from_flags,
		       align2n,
		       memflags);
	return mem;
}
EXPORT_SYMBOL(codec_mm_alloc);

void codec_mm_release(struct codec_mm_s *mem, const char *owner)
{
	int index;
	unsigned long flags;
	int i, j;
	const char *max_owner;
	struct codec_mm_cb_s **release_cb;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	if (!mem)
		return;

	release_cb = (struct codec_mm_cb_s **)
		vzalloc(sizeof(struct codec_mm_cb_s *) * (mem->release_cb_cnt + 1));

	spin_lock_irqsave(&mgt->lock, flags);
	if (!codec_mm_valid_mm_locked(mem)) {
		pr_err("codec mm not valid!\n");
		spin_unlock_irqrestore(&mgt->lock, flags);
		if (release_cb)
			vfree(release_cb);
		return;
	}
	index = atomic_dec_return(&mem->use_cnt);
	max_owner = mem->owner[index];
	for (i = 0; i < index; i++) {
		if (mem->owner[i] && strcmp(owner, mem->owner[i]) == 0)
			mem->owner[i] = max_owner;
	}
	if (debug_mode & 0x20)
		pr_err("mem_id [%d] %s free mem size %d at %lx from %d,index =%d\n",
		       mem->mem_id, owner, mem->buffer_size, mem->phy_addr,
		       mem->from_flags, index);
	mem->owner[index] = NULL;
	if (index == 0) {
		struct codec_mm_cb_s *cur, *tmp;
		list_del(&mem->list);
		i = 0;
		list_for_each_entry_safe(cur,
			tmp, &mem->release_cb_list, node) {
			list_del_init(&cur->node);
			if (release_cb)
				release_cb[i] = cur;
			i++;
			if (i > mem->release_cb_cnt) {
				pr_err("%s, error, release cnt %d less than loop %d\n",
					__func__, mem->release_cb_cnt, i);
				break;
			}
		}
		spin_unlock_irqrestore(&mgt->lock, flags);

		if (release_cb) {
			for (j = 0; j < i; j++) {
				cur = release_cb[j];
				if (cur)
					cur->func(mem, cur);
				mem->release_cb_cnt--;
			}
		}
		if (mem->release_cb_cnt != 0) {
			pr_err("%s, error, i %d, release %d\n",
					__func__, i, mem->release_cb_cnt);
		}
		codec_mm_free_in(mgt, mem);
		kfree(mem);
		if (release_cb)
			vfree(release_cb);
		return;
	}
	spin_unlock_irqrestore(&mgt->lock, flags);

	if (release_cb)
		vfree(release_cb);
}
EXPORT_SYMBOL(codec_mm_release);

void *codec_mm_dma_alloc_coherent(ulong *handle,
				  ulong *phy_out,
				  int size,
				  const char *owner)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	void *vaddr = NULL;
	int space, s_res, s_cma, s_sys;
	dma_addr_t dma_handle = 0;
	int buf_size = PAGE_ALIGN(size);
	ulong flags;

	vaddr = dma_alloc_coherent(mgt->dev, buf_size, &dma_handle, GFP_KERNEL);
	if (!vaddr)
		goto err;

	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	if (!mem)
		goto err;

	mem->owner[0]	= owner;
	mem->vbuffer	= vaddr;
	mem->phy_addr	= dma_handle;
	mem->buffer_size = buf_size;
	mem->from_flags	= AMPORTS_MEM_FLAGS_FROM_GET_FROM_COHERENT;

	space = codec_mm_alloc_pre_check_in(mgt, mem->buffer_size, 0);
	if (!space)
		goto err;

	s_res = (space & 1);
	s_cma = (space & 2);
	s_sys = (space & 4);
	if (!s_res && !s_cma && !s_sys) {
		if (debug_mode & 0x20)
			pr_err("error, codec mm have space: %x\n", space);
		goto err;
	}
	mem->flags = space;

	spin_lock_irqsave(&mgt->lock, flags);

	mem->mem_id = mgt->global_memid++;
	if (s_cma) {
		mgt->alloced_cma_size	+= buf_size;
	} else if (s_res) {
		if (mgt->cma_res_pool.total_size > 0)
			mgt->cma_res_pool.total_size += buf_size;
		else
			mgt->alloced_res_size += buf_size;
	} else {
		mgt->alloced_sys_size	+= buf_size;
	}
	mgt->alloced_from_coherent	+= buf_size;
	mgt->total_alloced_size		+= buf_size;
	if (mgt->total_alloced_size > mgt->max_used_mem_size)
		mgt->max_used_mem_size = mgt->total_alloced_size;
	*handle				= (ulong)mem;
	*phy_out			= mem->phy_addr;
	list_add_tail(&mem->list, &mgt->mem_list);

	spin_unlock_irqrestore(&mgt->lock, flags);

	if (debug_mode & 0x20) {
		pr_info("mem_id [%d] [%s] alloc coherent mem (phy %lx, vddr %px) size (%d).\n",
			mem->mem_id, owner, mem->phy_addr, vaddr, buf_size);
	}
	return vaddr;
err:
	kfree(mem);
	if (vaddr)
		dma_free_coherent(mgt->dev, buf_size, vaddr, dma_handle);

	return NULL;
}
EXPORT_SYMBOL(codec_mm_dma_alloc_coherent);

void codec_mm_dma_free_coherent(ulong handle)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = (struct codec_mm_s *)handle;
	ulong flags;

	if (!handle)
		return;

	dma_free_coherent(mgt->dev, mem->buffer_size,
		mem->vbuffer, mem->phy_addr);

	spin_lock_irqsave(&mgt->lock, flags);

	if (mem->flags & 2)
		mgt->alloced_cma_size	-= mem->buffer_size;
	else if (mem->flags & 1)
		if (mgt->cma_res_pool.total_size > 0)
			mgt->cma_res_pool.total_size += mem->buffer_size;
		else
			mgt->alloced_res_size += mem->buffer_size;
	else
		mgt->alloced_sys_size	-= mem->buffer_size;
	mgt->alloced_from_coherent	-= mem->buffer_size;
	mgt->total_alloced_size		-= mem->buffer_size;
	list_del(&mem->list);

	spin_unlock_irqrestore(&mgt->lock, flags);

	if (debug_mode & 0x20) {
		pr_info("mem_id [%d] [%s] free coherent mem (phy %lx, vddr %px) size (%d)\n",
			mem->mem_id, mem->owner[0], mem->phy_addr, mem->vbuffer,
			mem->buffer_size);
	}

	kfree(mem);
}
EXPORT_SYMBOL(codec_mm_dma_free_coherent);

void codec_mm_release_with_check(struct codec_mm_s *mem, const char *owner)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&mgt->lock, flags);
	ret = codec_mm_valid_mm_locked(mem);
	spin_unlock_irqrestore(&mgt->lock, flags);
	if (ret) {
		/*for check,*/
		return codec_mm_release(mem, owner);
	}
}
EXPORT_SYMBOL(codec_mm_release_with_check);

void codec_mm_dma_flush(void *vaddr,
			int size,
			enum dma_data_direction dir)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	dma_addr_t dma_addr;
	ulong phy_addr;

	if (is_vmalloc_or_module_addr(vaddr)) {
		phy_addr = codec_mm_search_phy_addr(vaddr);
		if (!phy_addr)
			phy_addr = page_to_phys(vmalloc_to_page(vaddr))
				+ offset_in_page(vaddr);
		if (phy_addr && PageHighMem(phys_to_page(phy_addr)))
			dma_sync_single_for_device(mgt->dev,
						   phy_addr, size, dir);
		return;
	}

	/* only apply to the lowmem. */
	dma_addr = dma_map_single(mgt->dev, vaddr, size, dir);
	if (dma_mapping_error(mgt->dev, dma_addr)) {
		pr_err("dma map %d bytes error\n", size);
		return;
	}

	dma_sync_single_for_device(mgt->dev, dma_addr, size, dir);
	dma_unmap_single(mgt->dev, dma_addr, size, dir);
}
EXPORT_SYMBOL(codec_mm_dma_flush);

int codec_mm_has_owner(struct codec_mm_s *mem, const char *owner)
{
	int index;
	int i;
	unsigned long flags;
	int is_owner = 0;

	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	if (mem) {
		spin_lock_irqsave(&mgt->lock, flags);
		if (!codec_mm_valid_mm_locked(mem)) {
			spin_unlock_irqrestore(&mgt->lock, flags);
			pr_err("codec mm %p not valid!\n", mem);
			return 0;
		}

		index = atomic_read(&mem->use_cnt);

		for (i = 0; i < index; i++) {
			if (mem->owner[i] &&
			    strcmp(owner, mem->owner[i]) == 0) {
				is_owner = 1;
				break;
			}
		}
		spin_unlock_irqrestore(&mgt->lock, flags);
	}

	return is_owner;
}

int codec_mm_request_shared_mem(struct codec_mm_s *mem, const char *owner)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned long flags;
	int ret = -1;

	spin_lock_irqsave(&mgt->lock, flags);
	if (!codec_mm_valid_mm_locked(mem)) {
		ret = -2;
		goto out;
	}
	if (atomic_read(&mem->use_cnt) > 7) {
		ret = -3;
		goto out;
	}
	ret = 0;
	mem->owner[atomic_inc_return(&mem->use_cnt) - 1] = owner;

out:
	spin_unlock_irqrestore(&mgt->lock, flags);

	return ret;
}
EXPORT_SYMBOL(codec_mm_request_shared_mem);

static struct codec_mm_s *codec_mm_get_by_val_off(unsigned long val, int off)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem, *want_mem;
	unsigned long flags;

	want_mem = NULL;
	spin_lock_irqsave(&mgt->lock, flags);
	if (!list_empty(&mgt->mem_list)) {
		list_for_each_entry(mem, &mgt->mem_list, list) {
			if (mem && VAL_OFF_VAL(mem, off) == val)
				want_mem = mem;
		}
	}
	spin_unlock_irqrestore(&mgt->lock, flags);
	return want_mem;
}

unsigned long codec_mm_alloc_for_dma(const char *owner, int page_cnt,
				     int align2n, int memflags)
{
	struct codec_mm_s *mem;

	mem = codec_mm_alloc(owner, page_cnt << PAGE_SHIFT, align2n, memflags);
	if (!mem)
		return 0;
	return mem->phy_addr;
}
EXPORT_SYMBOL(codec_mm_alloc_for_dma);

unsigned long codec_mm_alloc_for_dma_ex(const char *owner,
					int page_cnt,
					int align2n,
					int memflags,
					int ins_id,
					int buffer_id)
{
	struct codec_mm_s *mem;

	mem = codec_mm_alloc(owner, page_cnt << PAGE_SHIFT, align2n, memflags);
	if (!mem)
		return 0;
	mem->ins_id = ins_id;
	mem->ins_buffer_id = buffer_id;
	if (debug_mode & 0x20) {
		pr_err("%s, for ins %d, buffer id:%d\n",
		       mem->owner[0] ? mem->owner[0] : "no",
		       mem->ins_id,
		       buffer_id);
	}
	return mem->phy_addr;
}
EXPORT_SYMBOL(codec_mm_alloc_for_dma_ex);

int codec_mm_free_for_dma(const char *owner, unsigned long phy_addr)
{
	struct codec_mm_s *mem;

	mem = codec_mm_get_by_val_off(phy_addr, PHY_OFF());

	if (mem)
		codec_mm_release(mem, owner);
	else
		return -1;
	return 0;
}
EXPORT_SYMBOL(codec_mm_free_for_dma);

static int codec_mm_init_tvp_pool(struct extpool_mgt_s *tvp_pool,
				  struct codec_mm_s *mm)
{
	struct gen_pool *pool;
	int ret;

	pool = gen_pool_create(TVP_MM_ALIGNED_2N, -1);
	if (!pool)
		return -ENOMEM;
	ret = gen_pool_add(pool, mm->phy_addr, mm->buffer_size, -1);
	if (ret < 0) {
		gen_pool_destroy(pool);
		return -1;
	}
	tvp_pool->gen_pool[tvp_pool->slot_num] = pool;
	mm->tvp_handle = -1;
	tvp_pool->mm[tvp_pool->slot_num] = mm;
	return 0;
}

static int codec_mm_tvp_pool_protect(struct extpool_mgt_s *tvp_pool)
{
	int ret = 0;
	int i = 0;

	for (i = 0; i < tvp_pool->slot_num; i++) {
		if (tvp_pool->mm[i]->tvp_handle == -1) {
			ret = tee_protect_tvp_mem
				((uint32_t)tvp_pool->mm[i]->phy_addr,
				(uint32_t)tvp_pool->mm[i]->buffer_size,
				&tvp_pool->mm[i]->tvp_handle);
			pr_info("protect tvp %d %d ret %d %x %x\n",
				i, tvp_pool->mm[i]->tvp_handle, ret,
				(unsigned int)tvp_pool->mm[i]->phy_addr,
				(unsigned int)tvp_pool->mm[i]->buffer_size);
			if (ret)
				break;
		} else {
			pr_info("protect tvp %d %d ret %d\n",
				i, tvp_pool->mm[i]->tvp_handle, ret);
		}
	}
	return ret;
}

static int codec_mm_extpool_pool_release_inner(int slot_num_start,
					       struct extpool_mgt_s *tvp_pool)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	int i;

	for (i = slot_num_start; i < tvp_pool->slot_num; i++) {
		struct gen_pool *gpool = tvp_pool->gen_pool[i];
		int slot_mem_size = 0;

		if (gpool) {
			if (tvp_pool->mm[i] && tvp_pool->mm[i]->tvp_handle > 0) {
				pr_info("unprotect tvp %d handle is %d\n", i,
					tvp_pool->mm[i]->tvp_handle);
				tee_unprotect_tvp_mem(tvp_pool->mm[i]->tvp_handle);
				tvp_pool->mm[i]->tvp_handle = -1;
			}
			slot_mem_size = gen_pool_size(gpool);
			gen_pool_destroy(tvp_pool->gen_pool[i]);
			if (tvp_pool->mm[i]) {
				struct page *mm = tvp_pool->mm[i]->mem_handle;

				if (tvp_pool->mm[i]->from_flags ==
					AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES)
					mm = phys_to_page((unsigned long)mm);
				cma_mmu_op(mm,
					   tvp_pool->mm[i]->page_count,
					   1);
				codec_mm_release(tvp_pool->mm[i],
						 TVP_POOL_NAME);
			}
		}
		mgt->tvp_pool.total_size -= slot_mem_size;
		tvp_pool->gen_pool[i] = NULL;
		tvp_pool->mm[i] = NULL;
	}
	tvp_pool->slot_num = slot_num_start;
	return slot_num_start;
}

static int codec_mm_tvp_pool_alloc_by_type(struct extpool_mgt_s *tvp_pool,
	int size, int memflags, int type)
{
	struct codec_mm_s *mem;
	int ret;

	if (type == CODEC_MM_FLAGS_CMA) {
		mem = codec_mm_alloc(TVP_POOL_NAME,
					size,
					RESERVE_MM_ALIGNED_2N,
					CODEC_MM_FLAGS_FOR_LOCAL_MGR |
					CODEC_MM_FLAGS_CMA);
		if (mem) {
			struct page *mm = mem->mem_handle;

			if (mem->from_flags ==
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES)
				mm = phys_to_page((unsigned long)mm);
			cma_mmu_op(mm, mem->page_count, 0);
			ret = codec_mm_init_tvp_pool(tvp_pool, mem);
			if (ret < 0) {
				cma_mmu_op(mm, mem->page_count, 1);
				codec_mm_release(mem, TVP_POOL_NAME);
			} else {
				tvp_pool->total_size += size;
				tvp_pool->slot_num++;
				if (tvp_mode >= 1) {
					if (codec_mm_tvp_pool_protect(tvp_pool)) {
						codec_mm_extpool_pool_release_inner
							(tvp_pool->slot_num - 1, tvp_pool);
						return 1;
					}
				}
				return 0;
			}
		}
	} else if (type == CODEC_MM_FLAGS_RESERVED) {
		mem = codec_mm_alloc(TVP_POOL_NAME,
				size,
				RESERVE_MM_ALIGNED_2N,
				CODEC_MM_FLAGS_FOR_LOCAL_MGR |
				CODEC_MM_FLAGS_RESERVED);

		if (mem) {
			ret = codec_mm_init_tvp_pool(tvp_pool, mem);
			if (ret < 0) {
				codec_mm_release(mem, TVP_POOL_NAME);
			} else {
				tvp_pool->total_size += size;
				tvp_pool->slot_num++;
				if (tvp_mode >= 1) {
					if (codec_mm_tvp_pool_protect(tvp_pool)) {
						codec_mm_extpool_pool_release_inner
							(tvp_pool->slot_num - 1, tvp_pool);
						return 1;
					}
				}
				return 0;
			}
		}
	}
	return 1;
}

static int codec_mm_try_alloc_more_tvp(void *param)
{
	int alloc_size = 0;
	struct extpool_mgt_s *tvp_pool = (struct extpool_mgt_s *)param;

	if (!tvp_pool)
		return 0;

	if (!kthread_should_stop()) {
		mutex_lock(&tvp_pool->pool_lock);
		if (tvp_pool->slot_num == 1) {
			mutex_unlock(&tvp_pool->pool_lock);
			alloc_size = codec_mm_tvp_pool_alloc_by_slot(tvp_pool, 0, 2);
			if (alloc_size <= 0)
				pr_err("Async alloc more tvp failed\n");
		} else {
			mutex_unlock(&tvp_pool->pool_lock);
		}
	}

	tvp_pool->tvp_kthread = NULL;
	return 0;
}

static int codec_mm_tvp_pool_alloc_by_slot(struct extpool_mgt_s *tvp_pool,
	int memflags, int flags)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	int size = 0;
	int try_alloced_size = 0;
	int max_reserved_free_size = 0;
	int max_cma_reserved_free_size = 0;
	int max_cma_free_size = 0;
	int use_cma_pool_first = 0;
	int retry_cnt = 0;
	int alloc_more = 0;
	u64 start_ms = codec_mm_get_current_ms();
	u64 end_ms = 0;
	u64 spend_ms = 0;

	mutex_lock(&tvp_pool->pool_lock);
	max_cma_reserved_free_size = mgt->cma_res_pool.total_size - mgt->cma_res_pool.alloced_size;
	max_reserved_free_size = mgt->total_reserved_size - mgt->alloced_res_size;
	if (max_cma_reserved_free_size > max_reserved_free_size)
		max_reserved_free_size = max_cma_reserved_free_size;
	max_cma_free_size = mgt->total_cma_size - mgt->alloced_cma_size;
	use_cma_pool_first = max_cma_free_size > max_reserved_free_size ? 1 : 0;

	if (tvp_pool->slot_num >= TVP_POOL_SEGMENT_MAX_USED) {
		try_alloced_size = 0;
		pr_info("slot_num  exceed the limit %d\n", tvp_pool->slot_num);
		goto alloced_finished;
	}

	size = default_tvp_pool_segment_size[tvp_pool->slot_num];
	if (size <= 0) {
		try_alloced_size = 0;
		goto alloced_finished;
	}

	if (!tvp_dynamic_increase_disable && tvp_pool->slot_num == 0 &&
		tvp_pool_segment_maxsize_0 >= DEFAULT_TVP_SEGMENT_MIN_SIZE) {
		if (size - tvp_pool_segment_maxsize_0 >= DEFAULT_TVP_SEGMENT_MIN_SIZE) {
			default_tvp_pool_segment_size[0] = tvp_pool_segment_maxsize_0;
			default_tvp_pool_segment_size[1] = size - tvp_pool_segment_maxsize_0;
			size = tvp_pool_segment_maxsize_0;
			alloc_more = 1;
		}
	}

	if (use_cma_pool_first) {
		try_alloced_size = size > max_cma_free_size ? max_cma_free_size : size;
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
		retry_cnt = try_alloced_size / (4 * SZ_1M);
		if (try_alloced_size > 0) {
			int retry = 0;

			do {
				if (codec_mm_tvp_pool_alloc_by_type(tvp_pool,
					try_alloced_size, memflags, CODEC_MM_FLAGS_CMA)) {
					try_alloced_size = try_alloced_size - 4 * SZ_1M;
					if (try_alloced_size < 16 * SZ_1M)
						break;
					try_alloced_size = codec_mm_align_up2n(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
				} else {
					goto alloced_finished;
				}
			} while (retry++ < retry_cnt);
		}
	} else {
		try_alloced_size = size > max_reserved_free_size ? max_reserved_free_size : size;
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
		retry_cnt = try_alloced_size / (4 * SZ_1M);
		if (try_alloced_size > 0) {
			int retry = 0;

			do {
				if (codec_mm_tvp_pool_alloc_by_type(tvp_pool,
					try_alloced_size, memflags, CODEC_MM_FLAGS_RESERVED)) {
					try_alloced_size = try_alloced_size - 4 * SZ_1M;
					if (try_alloced_size < 16 * SZ_1M)
						break;
					try_alloced_size = codec_mm_align_up2n(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
				} else {
					goto alloced_finished;
				}
			} while (retry++ < retry_cnt);
		}
	}

	if (use_cma_pool_first) {
		try_alloced_size = size > max_reserved_free_size ? max_reserved_free_size : size;
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
		retry_cnt = try_alloced_size / (4 * SZ_1M);
		if (try_alloced_size > 0) {
			int retry = 0;

			do {
				if (codec_mm_tvp_pool_alloc_by_type(tvp_pool,
					try_alloced_size, memflags, CODEC_MM_FLAGS_RESERVED)) {
					try_alloced_size = try_alloced_size - 4 * SZ_1M;
					if (try_alloced_size < 16 * SZ_1M)
						break;
					try_alloced_size = codec_mm_align_up2n(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
				} else {
					goto alloced_finished;
				}
			} while (retry++ < retry_cnt);
		}
	} else {
		try_alloced_size = size > max_cma_free_size ? max_cma_free_size : size;
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
		retry_cnt = try_alloced_size / (4 * SZ_1M);
		if (try_alloced_size > 0) {
			int retry = 0;

			do {
				if (codec_mm_tvp_pool_alloc_by_type(tvp_pool,
					try_alloced_size, memflags, CODEC_MM_FLAGS_CMA)) {
					try_alloced_size = try_alloced_size - 4 * SZ_1M;
					if (try_alloced_size < 16 * SZ_1M)
						break;
					try_alloced_size = codec_mm_align_up2n(try_alloced_size,
							RESERVE_MM_ALIGNED_2N);
				} else {
					goto alloced_finished;
				}
			} while (retry++ < retry_cnt);
		}
	}
	try_alloced_size = 0;
alloced_finished:
	if (try_alloced_size && !tvp_dynamic_increase_disable && !flags) {
		pr_info("Force enable tvp, please enable it by resource manager or secmem");
		mgt->tvp_enable = 2;
	}

	if (alloc_more && try_alloced_size > 0 && !tvp_pool->tvp_kthread) {
		tvp_pool->tvp_kthread = kthread_run(codec_mm_try_alloc_more_tvp,
			(void *)tvp_pool, "tvp_alloc_more");
		if (!tvp_pool->tvp_kthread)
			pr_info("Create thread enable second segment error");
	}

	mutex_unlock(&tvp_pool->pool_lock);

	end_ms = codec_mm_get_current_ms();
	spend_ms = end_ms - start_ms;

	pr_info("Enable tvp %d size %d MB spend %d ms\n", tvp_pool->slot_num,
		try_alloced_size / 1024 / 1024, (u32)spend_ms);

	return try_alloced_size;
}

int codec_mm_extpool_pool_alloc(struct extpool_mgt_s *tvp_pool,
	int size, int memflags, int for_tvp)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem;
	int alloced_size = tvp_pool->total_size;
	int try_alloced_size = 0;
	int ret;
	int retry_cnt = size / (4 * SZ_1M);
	int slot_num = tvp_pool->slot_num;
	int max_cma_reserved_free_size = 0;
	u64 start_ms = codec_mm_get_current_ms();
	u64 end_ms = 0;
	u64 spend_ms = 0;

	/*alloced from reserved*/
	mutex_lock(&tvp_pool->pool_lock);
	if (alloced_size >= size)
		goto alloced_finished1;
	max_cma_reserved_free_size = mgt->cma_res_pool.total_size - mgt->cma_res_pool.alloced_size;
	try_alloced_size = mgt->total_reserved_size - mgt->alloced_res_size;
	if (max_cma_reserved_free_size > try_alloced_size)
		try_alloced_size = max_cma_reserved_free_size;
	if (try_alloced_size > 0 && for_tvp) {
		int retry = 0;
		try_alloced_size = min_t(int,
			size - alloced_size, try_alloced_size);
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
			RESERVE_MM_ALIGNED_2N);
		do {
			mem = codec_mm_alloc(TVP_POOL_NAME,
						try_alloced_size,
						RESERVE_MM_ALIGNED_2N,
						CODEC_MM_FLAGS_FOR_LOCAL_MGR |
						CODEC_MM_FLAGS_RESERVED);

			if (mem) {
				ret = codec_mm_init_tvp_pool(tvp_pool, mem);
				if (ret < 0) {
					codec_mm_release(mem, TVP_POOL_NAME);
				} else {
					alloced_size += try_alloced_size;
					tvp_pool->slot_num++;
				}
				try_alloced_size = size - alloced_size;
			} else {
				try_alloced_size = try_alloced_size - 4 * SZ_1M;
				if (try_alloced_size < 16 * SZ_1M)
					break;
			}
			if (tvp_pool->slot_num < TVP_POOL_SEGMENT_MAX_USED &&
			    alloced_size < size) {
				try_alloced_size = codec_mm_align_up2n
					(try_alloced_size, RESERVE_MM_ALIGNED_2N);
			} else {
				break;
			}
		} while (retry++ < retry_cnt);
	}
	if (alloced_size >= size || tvp_pool->slot_num >= TVP_POOL_SEGMENT_MAX_USED) {
		/*alloc finished. */
		goto alloced_finished;
	}

/*alloced from cma:*/
	try_alloced_size = mgt->total_cma_size - mgt->alloced_cma_size;
	if (try_alloced_size > 0) {
		int retry = 0;
		int memflags = CODEC_MM_FLAGS_FOR_LOCAL_MGR | CODEC_MM_FLAGS_CMA;

		if (for_tvp)
			memflags |= CODEC_MM_FLAGS_RESERVED;

		try_alloced_size = min_t(int,
			size - alloced_size, try_alloced_size);
		try_alloced_size = MM_ALIGN_DOWN_2N(try_alloced_size,
			RESERVE_MM_ALIGNED_2N);
		do {
			mem = codec_mm_alloc(for_tvp ? TVP_POOL_NAME :
						CMA_RES_POOL_NAME,
					try_alloced_size, RESERVE_MM_ALIGNED_2N,
					memflags);
			if (mem) {
				struct page *mm = mem->mem_handle;

				if (mem->from_flags ==
					AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES)
					mm = phys_to_page((unsigned long)mm);
				if (for_tvp) {
					cma_mmu_op(mm,
						mem->page_count,
						0);
				}
				ret = codec_mm_init_tvp_pool(tvp_pool, mem);
				if (ret < 0) {
					if (for_tvp) {
						cma_mmu_op(mm,
							mem->page_count,
							1);
					}
					codec_mm_release(mem, TVP_POOL_NAME);
				} else {
					alloced_size += try_alloced_size;
					tvp_pool->slot_num++;
				}
				try_alloced_size = size - alloced_size;
			} else {
				try_alloced_size = try_alloced_size - 4 * SZ_1M;
				if (try_alloced_size < 16 * SZ_1M)
					break;
			}
			if (tvp_pool->slot_num < TVP_POOL_SEGMENT_MAX_USED &&
			    alloced_size < size) {
				try_alloced_size = codec_mm_align_up2n(try_alloced_size,
					RESERVE_MM_ALIGNED_2N);
			} else {
				break;
			}
		} while (retry++ < retry_cnt);
	}

alloced_finished:
	if (alloced_size > 0)
		tvp_pool->total_size = alloced_size;
	if (for_tvp) {
		if (alloced_size >= size) {
			if (tvp_mode >= 1)
				codec_mm_tvp_pool_protect(tvp_pool);
		} else {
			codec_mm_extpool_pool_release_inner(slot_num, tvp_pool);
			alloced_size = 0;
		}
	}
alloced_finished1:
	mutex_unlock(&tvp_pool->pool_lock);

	end_ms = codec_mm_get_current_ms();
	spend_ms = end_ms - start_ms;

	pr_info("Enable segment for tvp %d %d size %d MB spend %d ms\n", for_tvp,
		tvp_pool->slot_num, try_alloced_size / 1024 / 1024, (u32)spend_ms);

	return alloced_size;
}
EXPORT_SYMBOL(codec_mm_extpool_pool_alloc);

/*
 *call this before free all
 *alloced TVP memory;
 *it not free,some memory may free ignore.
 *return :
 */
static int codec_mm_extpool_pool_release(struct extpool_mgt_s *tvp_pool)
{
	int i;
	int ignored = 0;

	mutex_lock(&tvp_pool->pool_lock);
	for (i = 0; i < tvp_pool->slot_num; i++) {
		struct gen_pool *gpool = tvp_pool->gen_pool[i];
		int slot_mem_size = 0;

		if (gpool) {
			if (gen_pool_avail(gpool) != gen_pool_size(gpool)) {
				pr_err("ext pool is not free.\n");
				ignored++;
				continue;	/*ignore this free now, */
			}
			slot_mem_size = gen_pool_size(gpool);
			gen_pool_destroy(tvp_pool->gen_pool[i]);
			if (tvp_pool->mm[i]) {
				struct page *mm = tvp_pool->mm[i]->mem_handle;

				if (tvp_pool->mm[i]->from_flags ==
				    AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES)
					mm = phys_to_page((unsigned long)mm);
				cma_mmu_op(mm,
					   tvp_pool->mm[i]->page_count,
					   1);
				codec_mm_release(tvp_pool->mm[i],
						 TVP_POOL_NAME);
			}
		}
		tvp_pool->total_size -= slot_mem_size;
		tvp_pool->gen_pool[i] = NULL;
		tvp_pool->mm[i] = NULL;
	}
	if (ignored > 0) {
		int before_free_slot = tvp_pool->slot_num + 1;

		for (i = 0; i < tvp_pool->slot_num; i++) {
			if (tvp_pool->gen_pool[i] && before_free_slot < i) {
				tvp_pool->gen_pool[before_free_slot] =
					tvp_pool->gen_pool[i];
				tvp_pool->mm[before_free_slot] =
					tvp_pool->mm[i];
				swap(default_tvp_pool_segment_size[i],
						default_tvp_pool_segment_size[before_free_slot]);
				tvp_pool->gen_pool[i] = NULL;
				tvp_pool->mm[i] = NULL;
				before_free_slot++;
			}
			if (!tvp_pool->gen_pool[i] && before_free_slot > i) {
				before_free_slot = i;
				/**/
			}
		}
	}
	tvp_pool->slot_num = ignored;
	mutex_unlock(&tvp_pool->pool_lock);
	return ignored;
}

static int codec_mm_tvp_pool_unprotect_and_release(struct extpool_mgt_s *tvp_pool)
{
	int i;
	int ignored = 0;

	mutex_lock(&tvp_pool->pool_lock);
	for (i = 0; i < tvp_pool->slot_num; i++) {
		struct gen_pool *gpool = tvp_pool->gen_pool[i];
		int slot_mem_size = 0;

		if (gpool) {
			if (gen_pool_avail(gpool) != gen_pool_size(gpool)) {
				if (debug_mode & 0x20)
					pr_err("Warn: TVP pool will release later.\n");
				ignored++;
				continue;	/*ignore this free now, */
			}
			slot_mem_size = gen_pool_size(gpool);
			gen_pool_destroy(tvp_pool->gen_pool[i]);
			if (tvp_pool->mm[i]) {
				struct page *mm = tvp_pool->mm[i]->mem_handle;

				pr_info("unprotect tvp %d handle is %d\n",
					i, tvp_pool->mm[i]->tvp_handle);
				if (tvp_pool->mm[i]->tvp_handle > 0) {
					tee_unprotect_tvp_mem(tvp_pool->mm[i]->tvp_handle);
					tvp_pool->mm[i]->tvp_handle = -1;
				}
				if (tvp_pool->mm[i]->from_flags ==
					AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES)
					mm = phys_to_page((unsigned long)mm);
				cma_mmu_op(mm, tvp_pool->mm[i]->page_count, 1);
				codec_mm_release(tvp_pool->mm[i], TVP_POOL_NAME);
			}
		}
		tvp_pool->total_size -= slot_mem_size;
		tvp_pool->gen_pool[i] = NULL;
		tvp_pool->mm[i] = NULL;
	}
	if (ignored > 0) {
		int before_free_slot = tvp_pool->slot_num + 1;

		for (i = 0; i < tvp_pool->slot_num; i++) {
			if (tvp_pool->gen_pool[i] && before_free_slot < i) {
				tvp_pool->gen_pool[before_free_slot] =
					tvp_pool->gen_pool[i];
				tvp_pool->mm[before_free_slot] =
					tvp_pool->mm[i];
				tvp_pool->mm[before_free_slot]->tvp_handle =
					tvp_pool->mm[i]->tvp_handle;
				swap(default_tvp_pool_segment_size[i],
						default_tvp_pool_segment_size[before_free_slot]);
				tvp_pool->gen_pool[i] = NULL;
				tvp_pool->mm[i] = NULL;
				before_free_slot++;
			}
			if (!tvp_pool->gen_pool[i] && before_free_slot > i) {
				before_free_slot = i;
				/**/
			}
		}
	}
	tvp_pool->slot_num = ignored;
	mutex_unlock(&tvp_pool->pool_lock);
	return ignored;
}

/*
 *victor_size
 *=sizeof(res)/sizeof(ulong)
 *res[0]=addr1_start;
 *res[1]=addr1_end;
 *res[2]=addr2_start;
 *res[3]=addr2_end;
 *..
 *res[2*n]=addrx_start;
 *res[2*n+1]=addrx_end;
 *
 *return n;
 */
static int codec_mm_tvp_get_mem_resource(ulong *res, int victor_size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct extpool_mgt_s *tvp_pool = &mgt->tvp_pool;
	int i;
	mutex_lock(&tvp_pool->pool_lock);
	for (i = 0; i < tvp_pool->slot_num && i < victor_size / 2; i++) {
		if (tvp_pool->mm[i]) {
			res[2 * i] = tvp_pool->mm[i]->phy_addr;
			res[2 * i + 1] = tvp_pool->mm[i]->phy_addr +
				tvp_pool->mm[i]->buffer_size - 1;
		}
	}
	mutex_unlock(&tvp_pool->pool_lock);
	return i;
}

static int codec_mm_is_in_tvp_region(ulong phy_addr)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct extpool_mgt_s *tvp_pool = &mgt->tvp_pool;
	int i;
	int in = 0, in2 = 0;

	mutex_lock(&tvp_pool->pool_lock);
	for (i = 0; i < tvp_pool->slot_num; i++) {
		if (tvp_pool->mm[i]) {
			in = tvp_pool->mm[i]->phy_addr <= phy_addr;
			in2 = (tvp_pool->mm[i]->phy_addr +
				tvp_pool->mm[i]->buffer_size - 1) >= phy_addr;
			in = in && in2;
			if (in)
				break;
			in = 0;
		}
	}
	mutex_unlock(&tvp_pool->pool_lock);
	return in;
}

void *codec_mm_phys_to_virt(unsigned long phy_addr)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	if (codec_mm_is_in_tvp_region(phy_addr))
		return NULL;	/* no virt for tvp memory; */

	if (phy_addr >= mgt->rmem.base &&
	    phy_addr < mgt->rmem.base + mgt->rmem.size) {
		if (mgt->res_mem_flags & RES_MEM_FLAGS_HAVE_MAPED)
			return codec_mm_search_vaddr(phy_addr);
		return NULL;	/* no virt for reserved memory; */
	}

	return codec_mm_search_vaddr(phy_addr);
}
EXPORT_SYMBOL(codec_mm_phys_to_virt);

unsigned long codec_mm_virt_to_phys(void *vaddr)
{
	return page_to_phys((struct page *)vaddr);
}
EXPORT_SYMBOL(codec_mm_virt_to_phys);

unsigned long dma_get_cma_size_int_byte(struct device *dev)
{
	unsigned long size = 0;
	struct cma *cma = NULL;

	if (!dev) {
		pr_err("CMA: NULL DEV\n");
		return 0;
	}

	cma = dev_get_cma_area(dev);
	if (!cma) {
		pr_err("CMA:  NO CMA region\n");
		return 0;
	}
	size = cma_get_size(cma);
	return size;
}
EXPORT_SYMBOL(dma_get_cma_size_int_byte);

static int codec_mm_get_cma_size_int_byte(struct device *dev)
{
	static int static_size = -1;
	struct cma *cma = NULL;

	if (static_size >= 0)
		return static_size;
	if (!dev) {
		pr_err("CMA: NULL DEV\n");
		return 0;
	}

	cma = dev_get_cma_area(dev);
	if (!cma) {
		pr_err("CMA:  NO CMA region\n");
		return 0;
	}
	if (cma == dev_get_cma_area(NULL))
		static_size = 0;/*ignore default cma pool*/
	else
		static_size = cma_get_size(cma);

	return static_size;
}

static int codec_mm_cal_dump_buf_size(void)
{
	int size = PAGE_SIZE;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	unsigned long flags;

	spin_lock_irqsave(&mgt->lock, flags);
	if (!list_empty(&mgt->mem_list)) {
		list_for_each_entry(mem, &mgt->mem_list, list) {
			size += 128 + PREFIX_MAX;
		}
	}
	spin_unlock_irqrestore(&mgt->lock, flags);

	return codec_mm_align_up2n(size, PAGE_SHIFT);
}

int get_string_offset(int *buf_len, int *pos, int len)
{
	int log_line_step = len + PREFIX_MAX;
	int remain = LOG_LINE_MAX -
		 ((*pos + log_line_step) % LOG_LINE_MAX);

	*pos = (remain > 256) ?
		(*pos + log_line_step) :
		((((*pos + log_line_step) / LOG_LINE_MAX) + 1) * LOG_LINE_MAX);
	len = (remain > 256) ?
		(len) : (*pos - *buf_len);
	*buf_len += len;

	return len;
}

int get_string_segment(int size)
{
	return (size % LOG_LINE_MAX == 0) ?
		(size / LOG_LINE_MAX) :
		(size / LOG_LINE_MAX + 1);
}

static void dump_mem_infos(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	unsigned long flags = 0;
	char *alloc_buf = NULL;
	int buf_size = 0;
	char *pbuf = NULL;
	int tsize = 0;
	int s = 0;
	int buf_len = 0;
	int segment_count = 0;
	int i = 0;

	buf_size = codec_mm_cal_dump_buf_size();
	alloc_buf = vzalloc(buf_size);
	pbuf = alloc_buf;

	s = snprintf(pbuf, buf_size - tsize,
			"codec mem info:\n");
	pbuf += get_string_offset(&buf_len, &tsize, s);

	s = snprintf(pbuf, buf_size - tsize,
			"\ttotal codec mem size:%d MB\n",
			mgt->total_codec_mem_size / SZ_1M);
	pbuf += get_string_offset(&buf_len, &tsize, s);

	s = snprintf(pbuf, buf_size - tsize,
			"\talloced size= %d MB\n",
			mgt->total_alloced_size / SZ_1M);
	pbuf += get_string_offset(&buf_len, &tsize, s);

	s = snprintf(pbuf, buf_size - tsize,
			"\tmax alloced: %d MB\n",
			mgt->max_used_mem_size / SZ_1M);
	pbuf += get_string_offset(&buf_len, &tsize, s);

	s = snprintf(pbuf, buf_size - tsize,
		"\tCMA:%d,RES:%d,TVP:%d,SYS:%d,COHER:%d,VMAPED:%d MB\n",
		mgt->alloced_cma_size / SZ_1M,
		mgt->alloced_res_size / SZ_1M,
		mgt->tvp_pool.alloced_size / SZ_1M,
		mgt->alloced_sys_size / SZ_1M,
		mgt->alloced_from_coherent / SZ_1M,
		(mgt->phys_vmaped_page_cnt << PAGE_SHIFT) / SZ_1M);
	pbuf += get_string_offset(&buf_len, &tsize, s);

	if (mgt->res_pool) {
		s = snprintf(pbuf, buf_size - tsize,
				"\t[%d]RES size:%d MB,alloced:%d MB free:%d MB\n",
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_REVERSED,
				(int)(gen_pool_size(mgt->res_pool) / SZ_1M),
				(int)(mgt->alloced_res_size / SZ_1M),
				(int)(gen_pool_avail(mgt->res_pool) / SZ_1M));
		pbuf += get_string_offset(&buf_len, &tsize, s);
	}

	s = snprintf(pbuf, buf_size - tsize,
			"\t[%d]CMA size:%d MB:alloced: %d MB,free:%d MB\n",
			AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA,
			(int)(mgt->total_cma_size / SZ_1M),
			(int)(mgt->alloced_cma_size / SZ_1M),
			(int)((mgt->total_cma_size -
			mgt->alloced_cma_size) / SZ_1M));
	pbuf += get_string_offset(&buf_len, &tsize, s);

	if (mgt->tvp_pool.slot_num > 0) {
		s = snprintf(pbuf, buf_size - tsize,
				"\t[%d]TVP size:%d MB,alloced:%d MB free:%d MB\n",
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_TVP,
				(int)(mgt->tvp_pool.total_size / SZ_1M),
				(int)(mgt->tvp_pool.alloced_size / SZ_1M),
				(int)((mgt->tvp_pool.total_size -
					mgt->tvp_pool.alloced_size) / SZ_1M));
		pbuf += get_string_offset(&buf_len, &tsize, s);
	}

	if (mgt->cma_res_pool.slot_num > 0) {
		s = snprintf(pbuf, buf_size - tsize,
				"\t[%d]CMA_RES size:%d MB,alloced:%d MB free:%d MB\n",
				AMPORTS_MEM_FLAGS_FROM_GET_FROM_CMA_RES,
				(int)(mgt->cma_res_pool.total_size / SZ_1M),
				(int)(mgt->cma_res_pool.alloced_size / SZ_1M),
				(int)((mgt->cma_res_pool.total_size -
				mgt->cma_res_pool.alloced_size) / SZ_1M));
		pbuf += get_string_offset(&buf_len, &tsize, s);
	}

	spin_lock_irqsave(&mgt->lock, flags);
	if (!list_empty(&mgt->mem_list)) {
		list_for_each_entry(mem, &mgt->mem_list, list) {
			s = snprintf(pbuf, buf_size - tsize,
					"\t[%d].%d:%s.%d,addr=0x%lx,size=%d,from=%d,cnt=%d,",
				mem->mem_id,
				mem->ins_id,
				mem->owner[0] ? mem->owner[0] : "no",
				mem->ins_buffer_id,
				mem->phy_addr,
				mem->buffer_size,
				mem->from_flags,
				atomic_read(&mem->use_cnt)
				);
			s += snprintf(pbuf + s, buf_size - tsize,
				"flags=%d,used:%u ms\n",
				mem->flags, jiffies_to_msecs(get_jiffies_64() -
				mem->alloced_jiffies));
			pbuf += get_string_offset(&buf_len, &tsize, s);

			if (tsize > buf_size - 256) {
				s = snprintf(pbuf, buf_size - tsize,
					"\n\t\t**NOT END**\n");
				tsize += s;
				break;/*no memory for dump now.*/
			}
		}
	}
	spin_unlock_irqrestore(&mgt->lock, flags);

	segment_count = get_string_segment(tsize);
	for (i = 0; i < segment_count; i++)
		pr_info("%s", alloc_buf + i * LOG_LINE_MAX);

	vfree(alloc_buf);
}

int codec_mm_alloc_cma_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->total_alloced_size / SZ_1M;
}
EXPORT_SYMBOL(codec_mm_alloc_cma_size);

static int dump_free_mem_infos(void *buf, int size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem;
	unsigned long flags;
	int i = 0, j, k;
	unsigned long minphy;
	u32 cma_base_phy, cma_end_phy;
	struct dump_buf_s {
		unsigned long phy_addr;
		int buffer_size;
		int align2n;
	} *usedb, *freeb;
	int total_free_size = 0;

	usedb = kzalloc(sizeof(*usedb) * 256, GFP_KERNEL);
	if (!usedb)
		return 0;
	freeb = usedb + 128;

	spin_lock_irqsave(&mgt->lock, flags);
	list_for_each_entry(mem, &mgt->mem_list, list) {
		usedb[i].phy_addr = mem->phy_addr;
		usedb[i].buffer_size = mem->buffer_size;
		usedb[i].align2n = mem->align2n;
		if (++i >= 128) {
			pr_info("too many memlist in codec_mm, break;\n");
			break;
		}
	};
	cma_base_phy = cma_get_base(dev_get_cma_area(mgt->dev));
	cma_end_phy = cma_base_phy + dma_get_cma_size_int_byte(mgt->dev);
	spin_unlock_irqrestore(&mgt->lock, flags);
	pr_info("free cma idea[%x-%x] from codec_mm items %d\n", cma_base_phy,
		cma_end_phy, i);
	for (j = 0; j < i; j++) { /* free */
		freeb[j].phy_addr = usedb[j].phy_addr +
			codec_mm_align_up2n(usedb[j].buffer_size, usedb[j].align2n);
		minphy = cma_end_phy;
		for (k = 0; k < i; k++) { /* used */
			if (usedb[k].phy_addr >= freeb[j].phy_addr &&
			    usedb[k].phy_addr < minphy)
				minphy = usedb[k].phy_addr;
		}
		freeb[j].buffer_size = minphy - freeb[j].phy_addr;
		total_free_size += freeb[j].buffer_size;
		if (freeb[j].buffer_size > 0)
			pr_info("\t[%d] free buf phyaddr=%p, used [%p,%x], size=%x\n",
				j, (void *)freeb[j].phy_addr,
				(void *)usedb[j].phy_addr,
				usedb[j].buffer_size, freeb[j].buffer_size);
	}
	pr_info("total_free_size %x\n", total_free_size);
	kfree(usedb);
	return 0;
}

static void codec_mm_tvp_segment_init(void)
{
	u32 size = 0;
	u32 segment_size = 0;
	u32 rest_size = 0;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	/*2M for audio not protect.*/
	if (tvp_dynamic_increase_disable) {
		if (default_tvp_4k_size <= 0 ||
			(tvp_dynamic_alloc_max_size > 0 &&
				default_tvp_4k_size >= tvp_dynamic_alloc_max_size)) {
			default_tvp_4k_size = mgt->total_codec_mem_size - SZ_1M * 2;
			if (default_tvp_4k_size > DEFAULT_TVP_SIZE_FOR_4K)
				default_tvp_4k_size = DEFAULT_TVP_SIZE_FOR_4K;
		}
		/*97MB -> 160MB, may not enough for h265*/
		default_tvp_size = (mgt->total_codec_mem_size - (SZ_1M * 2)) >
				DEFAULT_TVP_SIZE_FOR_NO4K ?
					DEFAULT_TVP_SIZE_FOR_NO4K :
					default_tvp_4k_size;
		return;
	}
	default_tvp_4k_size = mgt->total_codec_mem_size - SZ_1M * 2;
	default_tvp_size = default_tvp_4k_size;

	if (default_tvp_pool_size_0 > default_tvp_size) {
		default_tvp_pool_size_0 = default_tvp_size;
		default_tvp_pool_size_1 = 0;
		default_tvp_pool_size_2 = 0;
	}

	size = default_tvp_pool_size_0 +
		default_tvp_pool_size_1 + default_tvp_pool_size_2;
	if (size > default_tvp_size) {
		default_tvp_pool_size_0 = 0;
		default_tvp_pool_size_1 = 0;
		default_tvp_pool_size_2 = 0;
	}

	if (default_tvp_pool_size_0 >= DEFAULT_TVP_SEGMENT_MIN_SIZE) {
		default_tvp_pool_segment_size[0] =
			default_tvp_pool_size_0;
	} else {
		segment_size = default_tvp_size / 3;
		default_tvp_pool_segment_size[0] = segment_size;
	}
	size = default_tvp_pool_segment_size[0];
	if (default_tvp_pool_size_1 >= DEFAULT_TVP_SEGMENT_MIN_SIZE) {
		default_tvp_pool_segment_size[1] =
			default_tvp_pool_size_1;
	} else {
		segment_size = default_tvp_size / 4;
		rest_size = default_tvp_size - size;
		default_tvp_pool_segment_size[1] =
			(rest_size > segment_size) ? segment_size : rest_size;
	}
	size += default_tvp_pool_segment_size[1];
	if (default_tvp_pool_size_2 >= DEFAULT_TVP_SEGMENT_MIN_SIZE) {
		default_tvp_pool_segment_size[2] =
			default_tvp_pool_size_2;
	} else {
		segment_size = default_tvp_size / 4;
		rest_size = default_tvp_size - size;
		default_tvp_pool_segment_size[2] =
			(rest_size > segment_size) ? segment_size : rest_size;
	}
	size += default_tvp_pool_segment_size[2];
	rest_size = default_tvp_size - size;
	if (rest_size >= DEFAULT_TVP_SEGMENT_MIN_SIZE)
		default_tvp_pool_segment_size[3] = rest_size;
	else
		default_tvp_pool_segment_size[3] = 0;
	size += default_tvp_pool_segment_size[3];
	default_tvp_4k_size = size;
	default_tvp_size = size;
	tvp_dynamic_alloc_max_size = size;
}

static int codec_mm_calc_init_pool_count(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct extpool_mgt_s *tvp_pool = &mgt->tvp_pool;
	u32 size = default_tvp_pool_segment_size[tvp_pool->slot_num];
	u32 need_pool_count = 0;
	u32 last_segment_size = 0;
	u32 segment_size[TVP_POOL_SEGMENT_MAX_USED] = { 0 };
	u32 i = 0;

	if (tvp_dynamic_alloc_force_small_segment > 0) {
		if (tvp_dynamic_alloc_force_small_segment_size == 0)
			tvp_dynamic_alloc_force_small_segment_size =
				50 * 1024 * 1024;
		need_pool_count =
			size / tvp_dynamic_alloc_force_small_segment_size;
		last_segment_size =
			size % tvp_dynamic_alloc_force_small_segment_size;
		if (last_segment_size < DEFAULT_TVP_SEGMENT_MIN_SIZE)
			last_segment_size = 0;
		else
			need_pool_count++;
		if (need_pool_count <= TVP_POOL_SEGMENT_MAX_USED) {
			for (i = 0; i < need_pool_count; i++) {
				if (i == need_pool_count - 1 &&
					last_segment_size > 0)
					segment_size[i] =
						last_segment_size;
				else
					segment_size[i] =
				tvp_dynamic_alloc_force_small_segment_size;
			}
			if (need_pool_count > TVP_POOL_SEGMENT_MAX_USED - 1)
				need_pool_count = TVP_POOL_SEGMENT_MAX_USED - 1;
			last_segment_size =
				mgt->total_codec_mem_size - 2 * SZ_1M;
			for (i = 0; i < TVP_POOL_SEGMENT_MAX_USED - 1; i++) {
				if (i < need_pool_count) {
					last_segment_size -= segment_size[i];
					default_tvp_pool_segment_size[i] =
						segment_size[i];
				} else {
					last_segment_size -=
					default_tvp_pool_segment_size[i];
				}
			}
		default_tvp_pool_segment_size[TVP_POOL_SEGMENT_MAX_USED - 1] =
				last_segment_size;
		} else {
			need_pool_count = 1;
		}
	} else {
		need_pool_count = 1;
	}
	return need_pool_count;
}

int codec_mm_enable_tvp(int size, int flags)
{
	int ret;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	u32 count = 0;
	u32 i = 0;

	mutex_lock(&mgt->tvp_protect_lock);
	if (mgt->tvp_pool.slot_num <= 0)
		codec_mm_tvp_segment_init();
	if (size == 0) {
		if (flags == 1)
			size = default_tvp_size;
		else
			size = default_tvp_4k_size;
	}
	if (tvp_dynamic_increase_disable) {
		ret = codec_mm_extpool_pool_alloc(&mgt->tvp_pool,
			size, 0, 1);
		if (ret) {
			ret = 0;
			mgt->tvp_enable = flags;
			if (tvp_mode > 0)
				atomic_add_return(1, &mgt->tvp_user_count);
			pr_info("enable tvp for %d\n", flags);
		} else {
			pr_info("tvp enable failed size %d\n", size);
			mutex_unlock(&mgt->tvp_protect_lock);
			return -1;
		}
	} else {
		if (mgt->tvp_pool.slot_num <= 0) {
			count = codec_mm_calc_init_pool_count();
			if (count > 1) {
				for (i = 0; i < count; i++) {
					ret =
					codec_mm_tvp_pool_alloc_by_slot
					(&mgt->tvp_pool, 0, flags);
					if (ret) {
						ret = 0;
						continue;
					}
					if (i == 0) {
						pr_info("tvp enable failed\n");
						mutex_unlock
						(&mgt->tvp_protect_lock);
						return -1;
					}
					break;
				}
				if (mgt->tvp_enable != 2)
					mgt->tvp_enable = flags;
				if (tvp_mode > 0)
					atomic_add_return(1,
						&mgt->tvp_user_count);
				pr_info("enable tvp %d %d %d %d\n",
					flags, count, i, atomic_read
					(&mgt->tvp_user_count));
			} else {
				ret = codec_mm_tvp_pool_alloc_by_slot
					(&mgt->tvp_pool, 0, flags);
				if (ret) {
					ret = 0;
					if (mgt->tvp_enable != 2)
						mgt->tvp_enable = flags;
					if (tvp_mode > 0)
						atomic_add_return(1,
							&mgt->tvp_user_count);
					pr_info("enable tvp for %d\n", flags);
				} else {
					pr_info("tvp enable failed size %d\n",
						size);
					mutex_unlock(&mgt->tvp_protect_lock);
					return -1;
				}
			}
		} else {
			ret = 0;
			if (mgt->tvp_enable != 2)
				mgt->tvp_enable = flags;
			if (tvp_mode > 0)
				atomic_add_return(1, &mgt->tvp_user_count);
			pr_info("enable tvp for %d\n", flags);
		}
	}
	if (tvp_mode > 0)
		pr_info("tvp_user_count is %d\n",
			atomic_read(&mgt->tvp_user_count));
	mutex_unlock(&mgt->tvp_protect_lock);
	return ret;
}
EXPORT_SYMBOL(codec_mm_enable_tvp);

int codec_mm_disable_tvp(void)
{
	int ret = 0;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	mutex_lock(&mgt->tvp_protect_lock);
	if (tvp_mode == 0) {
		ret = codec_mm_extpool_pool_release(&mgt->tvp_pool);
		mgt->tvp_enable = 0;
		pr_info("disable tvp\n");
		mutex_unlock(&mgt->tvp_protect_lock);
		return ret;
	}

	if (atomic_dec_and_test(&mgt->tvp_user_count)) {
		if (codec_mm_tvp_pool_unprotect_and_release(&mgt->tvp_pool) == 0) {
			mgt->tvp_enable = 0;
			pr_info("disable tvp\n");
			mutex_unlock(&mgt->tvp_protect_lock);
			return ret;
		}
	}
	if (atomic_read(&mgt->tvp_user_count) < 0)
		atomic_set(&mgt->tvp_user_count, 0);
	pr_info("tvp_user_count is %d\n",
		atomic_read(&mgt->tvp_user_count));
	mutex_unlock(&mgt->tvp_protect_lock);
	return ret;
}
EXPORT_SYMBOL(codec_mm_disable_tvp);

int codec_mm_video_tvp_enabled(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->tvp_enable;
}
EXPORT_SYMBOL(codec_mm_video_tvp_enabled);

int codec_mm_get_total_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	int total_size = mgt->total_codec_mem_size;

	if ((debug_mode & 0xf) == 0) {	/*no debug memory mode. */
		return total_size;
	}
	/*
	 *disable reserved:1
	 *disable cma:2
	 *disable sys memory:4
	 *disable half memory:8,
	 */
	if (debug_mode & 0x8)
		total_size -= mgt->total_codec_mem_size / 2;
	if (debug_mode & 0x1) {
		total_size -= mgt->total_reserved_size;
		total_size -= mgt->cma_res_pool.total_size;
	}
	if (debug_mode & 0x2)
		total_size -= mgt->total_cma_size;
	if (total_size < 0)
		total_size = 0;
	return total_size;
}
EXPORT_SYMBOL(codec_mm_get_total_size);

/*count remain size for no tvp*/
int codec_mm_get_free_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	if (debug_mode & 0x40)
		dump_mem_infos();
	return codec_mm_get_total_size() -
		mgt->tvp_pool.total_size  + mgt->tvp_pool.alloced_size -
		mgt->total_alloced_size;
}
EXPORT_SYMBOL(codec_mm_get_free_size);

int codec_mm_get_tvp_free_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	if (debug_mode & 0x20)
		dump_mem_infos();
	return mgt->tvp_pool.total_size -
		mgt->tvp_pool.alloced_size;
}

int codec_mm_get_reserved_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->total_reserved_size;
}
EXPORT_SYMBOL(codec_mm_get_reserved_size);

struct device *v4l_get_dev_from_codec_mm(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->dev;
}
EXPORT_SYMBOL(v4l_get_dev_from_codec_mm);

struct codec_mm_s *v4l_reqbufs_from_codec_mm(const char *owner,
						 unsigned int addr,
						 unsigned int size,
						 unsigned int index)
{
	unsigned long flags;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	struct codec_mm_s *mem = NULL;
	int buf_size = PAGE_ALIGN(size);

	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	if (IS_ERR_OR_NULL(mem))
		goto out;

	mem->owner[0]	= owner;
	mem->mem_handle  = NULL;
	mem->buffer_size = buf_size;
	mem->page_count  = buf_size / PAGE_SIZE;
	mem->phy_addr	= addr;
	mem->ins_buffer_id = index;
	mem->alloced_jiffies = get_jiffies_64();
	mem->from_flags =
		AMPORTS_MEM_FLAGS_FROM_GET_FROM_COHERENT;

	spin_lock_irqsave(&mgt->lock, flags);

	mem->mem_id = mgt->global_memid++;
	mgt->alloced_from_coherent += buf_size;
	mgt->total_alloced_size += buf_size;
	mgt->alloced_cma_size += buf_size;
	list_add_tail(&mem->list, &mgt->mem_list);

	spin_unlock_irqrestore(&mgt->lock, flags);

	if (debug_mode & 0x20)
		pr_info("mem_id [%d] %s alloc coherent size %d at %lx from %d.\n",
			mem->mem_id, owner, buf_size, mem->phy_addr, mem->from_flags);
out:
	return mem;
}
EXPORT_SYMBOL(v4l_reqbufs_from_codec_mm);

void v4l_freebufs_back_to_codec_mm(const char *owner, struct codec_mm_s *mem)
{
	unsigned long flags;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	if (IS_ERR_OR_NULL(mem))
		return;

	if (!mem->owner[0] || strcmp(owner, mem->owner[0]) ||
		!mem->buffer_size)
		goto out;

	spin_lock_irqsave(&mgt->lock, flags);

	mgt->alloced_from_coherent -= mem->buffer_size;
	mgt->total_alloced_size -= mem->buffer_size;
	mgt->alloced_cma_size -= mem->buffer_size;
	list_del(&mem->list);

	spin_unlock_irqrestore(&mgt->lock, flags);

	if (debug_mode & 0x20)
		pr_info("mem_id [%d] %s free mem size %d at %lx from %d\n", mem->mem_id,
			mem->owner[0], mem->buffer_size, mem->phy_addr, mem->from_flags);
out:
	kfree(mem);
}
EXPORT_SYMBOL(v4l_freebufs_back_to_codec_mm);

/*
 *with_wait:
 *1: if no mem, do wait and free some cache.
 *0: do not wait.
 */
int codec_mm_enough_for_size(int size, int with_wait, int mem_flags)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	int have_mem = codec_mm_alloc_pre_check_in(mgt, size, mem_flags);

	if (!have_mem && with_wait && mgt->alloced_for_sc_cnt > 0) {
		pr_err(" No mem, clear scatter cache!!\n");
		//codec_mm_scatter_free_all_ignorecache(1);
		have_mem = codec_mm_alloc_pre_check_in(mgt, size, 0);
		if (have_mem)
			return 1;
		if (debug_mode & 0x20)
			dump_mem_infos();
		msleep(50);
		return 0;
	}
	return 1;
}
EXPORT_SYMBOL(codec_mm_enough_for_size);

int codec_mm_mgt_init(struct device *dev)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	int ret;

	INIT_LIST_HEAD(&mgt->mem_list);
	mgt->dev = dev;
	mgt->alloc_from_sys_pages_max = 4;
	if (mgt->rmem.size > 0) {
		unsigned long aligned_addr;
		int aligned_size;
		/*mem aligned, */
		mgt->res_pool = gen_pool_create(RESERVE_MM_ALIGNED_2N, -1);
		if (!mgt->res_pool)
			return -ENOMEM;
		aligned_addr = ((unsigned long)mgt->rmem.base +
			((1 << RESERVE_MM_ALIGNED_2N) - 1)) &
			(~((1 << RESERVE_MM_ALIGNED_2N) - 1));
		aligned_size = mgt->rmem.size -
			(int)(aligned_addr - (unsigned long)mgt->rmem.base);
		ret = gen_pool_add(mgt->res_pool,
				 aligned_addr, aligned_size, -1);
		if (ret < 0) {
			gen_pool_destroy(mgt->res_pool);
			return -1;
		}
		pr_debug("add reserve memory %p(aligned %p) size=%x(aligned %x)\n",
			 (void *)mgt->rmem.base, (void *)aligned_addr,
			 (int)mgt->rmem.size, (int)aligned_size);
		mgt->total_reserved_size = aligned_size;
		mgt->total_codec_mem_size = aligned_size;
#ifdef RES_IS_MAPED
		mgt->res_mem_flags |= RES_MEM_FLAGS_HAVE_MAPED;
#endif
	}

	mgt->total_cma_size = codec_mm_get_cma_size_int_byte(mgt->dev);
	mgt->total_codec_mem_size += mgt->total_cma_size;
	if (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MAJOR) <
		MESON_CPU_MAJOR_ID_G12A)
		tvp_dynamic_increase_disable = 1;
	default_tvp_4k_size = 0;
	codec_mm_tvp_segment_init();
	default_cma_res_size = mgt->total_cma_size;
	mgt->global_memid = 0;
	mutex_init(&mgt->tvp_pool.pool_lock);
	mutex_init(&mgt->cma_res_pool.pool_lock);
	spin_lock_init(&mgt->lock);
	return 0;
}
EXPORT_SYMBOL(codec_mm_mgt_init);

void codec_mm_get_default_tvp_size(int *tvp_fhd, int *tvp_uhd)
{
	if (tvp_dynamic_increase_disable) {
		if (tvp_fhd)
			*tvp_fhd = default_tvp_size;
		if (tvp_uhd)
			*tvp_uhd = default_tvp_4k_size;
	} else {
		if (tvp_fhd) {
			*tvp_fhd = default_tvp_size <
				DEFAULT_TVP_SIZE_FOR_NO4K ?
				default_tvp_size : DEFAULT_TVP_SIZE_FOR_NO4K;
		}
		if (tvp_uhd) {
			*tvp_uhd = default_tvp_4k_size <
				DEFAULT_TVP_SIZE_FOR_4K ?
				default_tvp_4k_size : DEFAULT_TVP_SIZE_FOR_4K;
		}
	}
}
EXPORT_SYMBOL(codec_mm_get_default_tvp_size);

static int __init amstream_test_init(void)
{
	return 0;
}

static ssize_t codec_mm_dump_show(struct class *class,
				  struct class_attribute *attr, char *buf)
{
	size_t ret = 0;

	dump_mem_infos();
	return ret;
}

static ssize_t codec_mm_scatter_dump_show(struct class *class,
					  struct class_attribute *attr,
					  char *buf)
{
	size_t ret;

	ret = codec_mm_scatter_info_dump(buf, PAGE_SIZE);
	return ret;
}

static ssize_t codec_mm_keeper_dump_show(struct class *class,
					 struct class_attribute *attr,
					 char *buf)
{
	size_t ret;

	ret = codec_mm_keeper_dump_info(buf, PAGE_SIZE);
	return ret;
}

static ssize_t tvp_enable_show(struct class *class,
		struct class_attribute *attr,
		char *buf)
{
	ssize_t size = 0;

	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	mutex_lock(&mgt->tvp_protect_lock);
	size += sprintf(buf, "tvp_flag=%d\n",
			(tvp_mode << 4) + mgt->tvp_enable);
	size += sprintf(buf + size, "tvp ref count=%d\n",
			atomic_read(&mgt->tvp_user_count));
	size += sprintf(buf + size, "tvp enable help:\n");
	size += sprintf(buf + size, "echo n > tvp_enable\n");
	size += sprintf(buf + size, "0: disable tvp(tvp size to 0)\n");
	size += sprintf(buf + size,
		"1: enable tvp for 1080p playing(use default size)\n");
	size += sprintf(buf + size,
		"2: enable tvp for 4k playing(use default 4k size)\n");
	mutex_unlock(&mgt->tvp_protect_lock);
	return size;
}

static ssize_t tvp_enable_store(struct class *class,
		struct class_attribute *attr,
		const char *buf, size_t size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned int val;
	ssize_t ret;

	val = -1;
	/*ret = sscanf(buf, "%d", &val);*/
	ret = kstrtoint(buf, 0, &val);
	if (ret != 0)
		return -EINVAL;
	/*
	 * always free all scatter cache and unused keeper for
	 * tvp changes when tvp mode is 0.
	 */
	if (tvp_mode < 1) {
		mutex_lock(&mgt->tvp_protect_lock);
		codec_mm_keeper_free_all_keep(2);
		codec_mm_scatter_free_all_ignorecache(3);
		mutex_unlock(&mgt->tvp_protect_lock);
	}
	switch (val) {
	case 0:
		codec_mm_disable_tvp();
		break;
	case 1:
		ret = codec_mm_enable_tvp(default_tvp_size, val);
		if (ret) {
			pr_info("tvp enable failed tvp mode %d %d %d\n",
				tvp_mode, val, default_tvp_size);
			return -1;
		}
		break;
	case 2:
		ret = codec_mm_enable_tvp(default_tvp_4k_size, val);
		if (ret) {
			pr_info("tvp enable failed tvp mode %d %d %d\n",
				tvp_mode, val, default_tvp_4k_size);
			return -1;
		}
		break;
	default:
		pr_err("unknown cmd! %d\n", val);
		break;
	}
	return size;
}

static ssize_t fastplay_enable_show(struct class *class,
		struct class_attribute *attr,
		char *buf)
{
	ssize_t size = 0;

	size += sprintf(buf, "fastplay enable help:\n");
	size += sprintf(buf + size, "echo n > tvp_enable\n");
	size += sprintf(buf + size, "0: disable fastplay\n");
	size += sprintf(buf + size,
		"1: enable fastplay for 1080p playing(use default size)\n");
	return size;
}

static ssize_t fastplay_enable_store(struct class *class,
					 struct class_attribute *attr,
					 const char *buf, size_t size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned int val;
	ssize_t ret;

	val = -1;
	/*ret = sscanf(buf, "%d", &val);*/
	ret = kstrtoint(buf, 0, &val);
	if (ret != 0)
		return -EINVAL;
	switch (val) {
	case 0:
		ret = codec_mm_extpool_pool_release(&mgt->cma_res_pool);
		mgt->fastplay_enable = 0;
		pr_err("disable fastplay ret 0x%zx\n", ret);
		break;
	case 1:
		codec_mm_extpool_pool_alloc(&mgt->cma_res_pool,
						default_cma_res_size, 0, 0);
		mgt->fastplay_enable = 1;
		pr_err("enable fastplay\n");
		break;
	default:
		pr_err("unknown cmd! %d\n", val);
	}
	return size;
}

static ssize_t config_show(struct class *class,
	struct class_attribute *attr, char *buf)
{
	ssize_t ret;

	ret = configs_list_path_nodes(CONFIG_PATH, buf, PAGE_SIZE,
					  LIST_MODE_NODE_CMDVAL_ALL);
	return ret;
}

static ssize_t config_store(struct class *class,
			struct class_attribute *attr,
			const char *buf, size_t size)
{
	int ret;

	ret = configs_set_prefix_path_valonpath(CONFIG_PREFIX, buf);
	if (ret < 0)
		pr_err("set config failed %s\n", buf);
	return size;
}

static ssize_t tvp_region_show(struct class *class,
				   struct class_attribute *attr, char *buf)
{
	size_t ret;
	ulong res_victor[16];
	int i;
	int off = 0;

	ret = codec_mm_tvp_get_mem_resource(res_victor, 8);
	for (i = 0; i < ret; i++) {
		off += sprintf(buf + off,
			"segment%d:0x%lx - 0x%lx (size:0x%x)\n",
			i,
			res_victor[2 * i],
			res_victor[2 * i + 1],
			(int)(res_victor[2 * i + 1] - res_victor[2 * i] + 1));
	}
	return off;
}

static ssize_t debug_show(struct class *class,
		struct class_attribute *attr,
		char *buf)
{
	ssize_t size = 0;

	size += sprintf(buf, "mm_scatter help:\n");
	size += sprintf(buf + size, "echo n > mm_scatter_debug\n");
	size += sprintf(buf + size, "n==0: clear all debugs)\n");
	size += sprintf(buf + size,
	"n=1: dump all alloced scatters\n");
	size += sprintf(buf + size,
	"n=2: dump all slots\n");

	size += sprintf(buf + size,
	"n=3: dump all free slots\n");

	size += sprintf(buf + size,
	"n=4: dump all sid hash table\n");

	size += sprintf(buf + size,
	"n=5: free all free slot now!\n");

	size += sprintf(buf + size,
	"n=6: clear all time infos!\n");

	size += sprintf(buf + size,
	"n=10: force free all keeper\n");

	size += sprintf(buf + size,
	"n=20: dump memory,# 20 #addr(hex) #len\n");

	size += sprintf(buf + size,
	"n==100: cmd mode p1 p ##mode:0,dump, 1,alloc 2,more,3,free some,4,free all\n");

	size += sprintf(buf + size,
	"n=200: walk dmabuf infos.\n");

	return size;
}

static int codec_mm_mem_dump(unsigned long addr, int isphy, int len)
{
	void *vaddr;
	int is_map = 0;

	pr_info("start dump addr: %p %d\n", (void *)addr, len);
	if (!isphy) {
		vaddr = (void *)addr;
	} else {
		vaddr = ioremap(addr, len);
		if (!vaddr) {
			pr_info("map addr: %p len: %d, failed\n",
				(void *)addr, len);
			vaddr = codec_mm_phys_to_virt(addr);
		} else {
			is_map = 1;
		}
	}
	if (vaddr) {
		unsigned int *p, *vint;
		int i;

		vint = (unsigned int *)vaddr;
		for (i = 0; i <= len - 32; i += sizeof(int) * 8) {
			p = (int *)&vint[i];
			pr_info("%p: %08x %08x %08x %08x %08x %08x %08x %08x\n",
				p,
				p[0], p[1], p[2], p[3],
				p[4], p[5], p[6], p[7]);
		}
	}
	if (vaddr && is_map) {
		/*maped free...*/
		iounmap(vaddr);
	}
	return 0;
}

static ssize_t debug_store(struct class *class,
		struct class_attribute *attr,
		const char *buf, size_t size)
{
	unsigned int val;
	ssize_t ret;

	val = -1;
	/*ret = sscanf(buf, "%d", &val);*/
	ret = kstrtoint(buf, 0, &val);
	if (ret != 0)
		return -EINVAL;
	switch (val) {
	case 0:
		pr_info("clear debug!\n");
		break;
	case 1:
		codec_mm_dump_all_scatters();
		break;
	case 2:
		codec_mm_dump_all_slots();
		break;
	case 3:
		codec_mm_dump_free_slots();
		break;
	case 4:
		codec_mm_dump_all_hash_table();
		break;
	case 5:
		codec_mm_free_all_free_slots();
		break;
	case 6:
		codec_mm_clear_alloc_infos();
		break;
	case 10:
		codec_mm_keeper_free_all_keep(1);
		break;
	case 11:
		dump_mem_infos();
		break;
	case 12:
		dump_free_mem_infos(NULL, 0);
		break;
	case 20: {
		int cmd = 0, len = 0;
		unsigned int addr;

		addr = 0;
		ret = sscanf(buf, "%d %x %d", &cmd, &addr, &len);
		if (addr > 0 && len > 0)
			codec_mm_mem_dump(addr, 1, len);
		}
		break;
	case 100:{
			int cmd, mode = 0, p1 = 0, p2 = 0;

			ret = sscanf(buf, "%d %d %d %d", &cmd, &mode, &p1, &p2);
			codec_mm_scatter_test(mode, p1, p2);
		}
		break;
	case 200:
		codec_mm_walk_dbuf();
		break;
	default:
		pr_err("unknown cmd! %d\n", val);
	}
	return size;

}

static ssize_t debug_sc_mode_show(struct class *class,
				  struct class_attribute *attr, char *buf)
{
	ssize_t size = 0;

	size = sprintf(buf, "%u\n", debug_sc_mode);

	return size;
}

static ssize_t debug_sc_mode_store(struct class *class,
				   struct class_attribute *attr,
				   const char *buf, size_t size)
{
	unsigned int val;
	ssize_t ret;

	val = -1;
	ret = kstrtoint(buf, 0, &val);
	if (ret != 0)
		return -EINVAL;

	debug_sc_mode = val;

	return size;
}

static ssize_t debug_keep_mode_show(struct class *class,
					struct class_attribute *attr, char *buf)
{
	ssize_t size = 0;

	size = sprintf(buf, "%u\n", debug_keep_mode);

	return size;
}

static ssize_t debug_keep_mode_store(struct class *class,
					 struct class_attribute *attr,
					 const char *buf, size_t size)
{
	unsigned int val;
	ssize_t ret;

	val = -1;
	ret = kstrtoint(buf, 0, &val);
	if (ret != 0)
		return -EINVAL;

	debug_keep_mode = val;

	return size;
}

bool codec_mm_scatter_available_check(int alloc)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	/* if codec_mm_scatter_watermark has been configed, use scatter
	 * free size to check, else use reserved size to check
	 */
	if (mgt->codec_mm_scatter_watermark) {
		if (mgt->codec_mm_scatter_free_size > alloc)
			return true;
	} else {
		if (codec_mm_get_free_size() >
			codec_mm_scatter_get_reserved_size())
			return true;
	}
	if (debug_mode & 0x80)
		pr_info("[%s]%s not enough. (watermark:%d/%d)\n", __func__,
				mgt->codec_mm_for_linear ? "scatter watermark" : "reserved",
				mgt->codec_mm_scatter_free_size,
				mgt->codec_mm_scatter_watermark);
	return false;
}

void codec_mm_scatter_level_decrease(int alloc_size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned long flags;

	if (mgt->codec_mm_for_linear) {
		spin_lock_irqsave(&mgt->scatter_watermark_lock, flags);
		mgt->codec_mm_scatter_free_size -= alloc_size;
		spin_unlock_irqrestore(&mgt->scatter_watermark_lock, flags);
	}
}

void codec_mm_scatter_level_increase(int free_size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	unsigned long flags;

	if (mgt->codec_mm_for_linear) {
		spin_lock_irqsave(&mgt->scatter_watermark_lock, flags);
		mgt->codec_mm_scatter_free_size += free_size;
		spin_unlock_irqrestore(&mgt->scatter_watermark_lock, flags);
	}
}

void codec_mm_set_min_linear_size(int min_mem_val)
{
	int val = min_mem_val;
	unsigned long flags;
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	if (val <= 0)
		return;

	if (val > codec_mm_get_total_size()) {
		pr_err("[%s]set value %d is too large, set as %d\n",
				__func__, val, codec_mm_get_total_size());
		val = codec_mm_get_total_size();
	}

	if (!mgt->codec_mm_for_linear) { // first time set
		spin_lock_init(&mgt->scatter_watermark_lock);
		mgt->codec_mm_for_linear = val;
		mgt->codec_mm_scatter_watermark =
			codec_mm_get_total_size() - mgt->codec_mm_for_linear;
		mgt->codec_mm_scatter_free_size =
			mgt->codec_mm_scatter_watermark;
	} else { // has been set, just need to make adjustment
		spin_lock_irqsave(&mgt->scatter_watermark_lock, flags);
		mgt->codec_mm_scatter_watermark += (mgt->codec_mm_for_linear - val);
		mgt->codec_mm_scatter_free_size += (mgt->codec_mm_for_linear - val);
		mgt->codec_mm_for_linear = val;
		spin_unlock_irqrestore(&mgt->scatter_watermark_lock, flags);
		if (mgt->codec_mm_scatter_free_size < 0)
			pr_warn_once("[%s]adjust watermark, scatter need free %d cma\n",
				__func__, mgt->codec_mm_scatter_free_size);
	}

	if (debug_mode & 0x80)
		pr_info("[%s]linear:%d, scatter:%d/%d\n",
					__func__, mgt->codec_mm_for_linear,
					mgt->codec_mm_scatter_free_size,
					mgt->codec_mm_scatter_watermark);
}

int codec_mm_get_min_linear_size(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->codec_mm_for_linear;
}

int codec_mm_get_scatter_watermark(void)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	return mgt->codec_mm_scatter_watermark;
}

static ssize_t dbuf_trace_show(struct class *class,
			       struct class_attribute *attr, char *buf)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();
	char *pbuf = buf;

	pbuf += sprintf(pbuf, "Dmabuf tracking [%s]\n",
		mgt->trk_h ? "enable" : "disable");

	return pbuf - buf;
}

static ssize_t dbuf_trace_store(struct class *class,
			       struct class_attribute *attr,
			       const char *buf, size_t size)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	bool val = false;
	ssize_t ret;

	ret = kstrtobool(buf, &val);
	if (ret != 0)
		return -EINVAL;

	if (val) {
		codec_mm_track_open(&mgt->trk_h);
	} else {
		codec_mm_track_close(mgt->trk_h);
		mgt->trk_h = NULL;
	}

	return size;
}

static CLASS_ATTR_RO(codec_mm_dump);
static CLASS_ATTR_RO(codec_mm_scatter_dump);
static CLASS_ATTR_RO(codec_mm_keeper_dump);
static CLASS_ATTR_RO(tvp_region);
static CLASS_ATTR_RW(tvp_enable);
static CLASS_ATTR_RW(fastplay_enable);
static CLASS_ATTR_RW(config);
static CLASS_ATTR_RW(debug);
//static CLASS_ATTR_RW(debug_mode);
static CLASS_ATTR_RW(debug_sc_mode);
static CLASS_ATTR_RW(debug_keep_mode);
static CLASS_ATTR_RW(dbuf_trace);

static struct attribute *codec_mm_class_attrs[] = {
	&class_attr_codec_mm_dump.attr,
	&class_attr_codec_mm_scatter_dump.attr,
	&class_attr_codec_mm_keeper_dump.attr,
	&class_attr_tvp_region.attr,
	&class_attr_tvp_enable.attr,
	&class_attr_fastplay_enable.attr,
	&class_attr_config.attr,
	&class_attr_debug.attr,
	//&class_attr_debug_mode.attr,
	&class_attr_debug_sc_mode.attr,
	&class_attr_debug_keep_mode.attr,
	&class_attr_dbuf_trace.attr,
	NULL
};

ATTRIBUTE_GROUPS(codec_mm_class);

static struct class codec_mm_class = {
		.name = "codec_mm",
		.class_groups = codec_mm_class_groups,
	};

static struct mconfig codec_mm_configs[] = {
	MC_PI32("default_tvp_size", &default_tvp_size),
	MC_PI32("default_tvp_4k_size", &default_tvp_4k_size),
	MC_PI32("default_cma_res_size", &default_cma_res_size),
	MC_PI32("default_tvp_pool_size_0", &default_tvp_pool_size_0),
	MC_PI32("default_tvp_pool_size_1", &default_tvp_pool_size_1),
	MC_PI32("default_tvp_pool_size_2", &default_tvp_pool_size_2),
	MC_PI32("tvp_pool_segment_maxsize_0", &tvp_pool_segment_maxsize_0),
	MC_PBOOL("dmabuf_trace_enable", &dbuf_trace)
};

static struct mconfig_node codec_mm_trigger_node;

int codec_mm_trigger_fun(const char *trigger, int id, const char *buf, int size)
{
	int ret = size;

	switch (trigger[0]) {
	case 't':
		tvp_enable_store(NULL, NULL, buf, size);
		break;
	case 'f':
		fastplay_enable_store(NULL, NULL, buf, size);
		break;
	case 'd':
		debug_store(NULL, NULL, buf, size);
		break;
	default:
		ret = -1;
	}
	return size;
}

int codec_mm_trigger_help_fun(const char *trigger, int id, char *sbuf, int size)
{
	int ret = -1;
	void *buf, *getbuf = NULL;

	if (size < PAGE_SIZE) {
		getbuf = (void *)__get_free_page(GFP_KERNEL);

		if (!getbuf)
			return -ENOMEM;
		buf = getbuf;
	} else {
		buf = sbuf;
	}
	switch (trigger[0]) {
	case 't':
		ret = tvp_enable_show(NULL, NULL, buf);
		break;
	case 'f':
		ret = fastplay_enable_show(NULL, NULL, buf);
		break;
	case 'd':
		ret = debug_show(NULL, NULL, buf);
		break;
	default:
		pr_err("unknown trigger:[%s]\n", trigger);
		ret = -1;
	}
	if (ret > 0 && getbuf) {
		ret = min_t(int, ret, size);

		strncpy(sbuf, buf, ret);
	}
	if (getbuf)
		free_page((unsigned long)getbuf);
	return ret;
}

static struct mconfig codec_mm_trigger[] = {
	MC_FUN("tvp_enable", codec_mm_trigger_help_fun, codec_mm_trigger_fun),
	MC_FUN("fastplay", codec_mm_trigger_help_fun, codec_mm_trigger_fun),
	MC_FUN("debug", codec_mm_trigger_help_fun, codec_mm_trigger_fun),
};

#if IS_MODULE(CONFIG_AMLOGIC_MEDIA_MODULE) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
#ifdef CONFIG_ARM64
static int tvp_clear_cma_pagemap(unsigned long pfn, unsigned long count)
{
	pgd_t *pgd;
	p4d_t *p4d;
	pud_t *pud;
	pmd_t *pmd;
	unsigned long addr, end;
	struct mm_struct *mm;

	addr = (unsigned long)pfn_to_kaddr(pfn);
	end  = addr + count * PAGE_SIZE;
	mm = aml_init_mm;
	for (; addr < end; addr += PMD_SIZE) {
		pgd = pgd_offset(mm, addr);
		if (pgd_none(*pgd) || pgd_bad(*pgd))
			break;

		p4d = p4d_offset(pgd, addr);
		if (p4d_none(*p4d) || p4d_bad(*p4d))
			break;

		pud = pud_offset(p4d, addr);
		if (pud_none(*pud) || pud_bad(*pud))
			break;

		pmd = pmd_offset(pud, addr);
		if (pmd_none(*pmd))
			break;

		pr_debug("%s, addr:%lx, pgd:%p %llx, pmd:%p %llx\n",
			 __func__, addr, pgd,
			 pgd_val(*pgd), pmd, pmd_val(*pmd));
		pmd_clear(pmd);
	}

	return 0;
}
#endif

static int tvp_setup_cma_full_pagemap(unsigned long pfn, unsigned long count)
{
#ifdef CONFIG_ARM
	/*
	 * arm already create level 3 mmu mapping for lowmem cma.
	 * And if high mem cma, there is no mapping. So nothing to
	 * do for arch arm.
	 */
	return 0;
#elif defined(CONFIG_ARM64)
	struct vm_area_struct vma = {};
	unsigned long addr, size;
	int ret;

	tvp_clear_cma_pagemap(pfn, count);
	addr = (unsigned long)pfn_to_kaddr(pfn);
	size = count * PAGE_SIZE;
	vma.vm_mm    = aml_init_mm;
	vma.vm_start = addr;
	vma.vm_end   = addr + size;
	vma.vm_page_prot = PAGE_KERNEL;
	ret = remap_pfn_range(&vma, addr, pfn,
			      size, vma.vm_page_prot);
	if (ret < 0)
		pr_info("%s, remap pte failed:%d, cma:%lx\n",
			__func__, ret, pfn);
	return 0;
#else
	#error "NOT supported ARCH"
#endif
}

#if defined(CONFIG_ARM64)
unsigned long (*aml_syms_lookup)(const char *name);

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp_lookup_name = {
	.symbol_name	= "kallsyms_lookup_name",
};
#endif

void __nocfi get_mte_sync_tags_hook(void)
{
	static DEFINE_MUTEX(lock);
	struct cma *cma = NULL;
	struct page *page = NULL;
#if defined(CONFIG_ARM64)
	int ret;

	ret = register_kprobe(&kp_lookup_name);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d\n", ret);
		return;
	}
	pr_info("kprobe lookup offset at %px\n", kp_lookup_name.addr);

	aml_syms_lookup = (unsigned long (*)(const char *name))kp_lookup_name.addr;

	aml_init_mm = (struct mm_struct *)aml_syms_lookup("init_mm");
	aml_mte_sync_tags = (void (*)(pte_t old_pte, pte_t pte))aml_syms_lookup("mte_sync_tags");
	pr_info("aml_init_mm: %px, aml_mte_sync_tags: %px\n", aml_init_mm, aml_mte_sync_tags);
#endif

	cma = dev_get_cma_area(codec_dev);
	if (!cma) {
		pr_err("CMA:  NO CMA region\n");
		return;
	}
	pr_info("%s, %s, %lx, %lx\n", __func__, cma_get_name(cma), cma->base_pfn, cma->count);
	/* spin_lock_irqsave(&cma->lock, flags); */
	page = cma_alloc(cma, cma->count, 0, 0);
	if (!page) {
		pr_err("cma alloc failed.\n");
		return;
	}
	mutex_lock(&lock);
	if (aml_init_mm)
		tvp_setup_cma_full_pagemap(cma->base_pfn, cma->count);
	/* spin_unlock_irqrestore(&cma->lock, flags); */
	mutex_unlock(&lock);
	cma_release(cma, page, cma->count);
}
#endif

static int codec_mm_probe(struct platform_device *pdev)
{
	int r;
	struct reserved_mem *mem = NULL;
	int secure_region_index = 0;
	struct device_node *search_target = NULL;

	while (1) {
		search_target = of_parse_phandle(pdev->dev.of_node,
						"memory-region",
						secure_region_index);
		if (!search_target)
			break;

		if (!strcmp(search_target->name, "linux,secure_vdec_reserved")) {
			mem = of_reserved_mem_lookup(search_target);
			if (mem) {
				r = secure_vdec_res_setup(mem);
				if (r)
					pr_err("secure_vdec_res_setup res %x\n", r);
				r = of_reserved_mem_device_init_by_idx(&pdev->dev,
					pdev->dev.of_node, secure_region_index);
				if (r)
					pr_err("secure_vdec_res_setup device init failed\n");
			}
			break;
		}
		secure_region_index++;
	}

	pdev->dev.platform_data = get_mem_mgt();
	r = of_reserved_mem_device_init(&pdev->dev);
	if (r == 0)
		pr_debug("%s mem init done\n", __func__);

	codec_mm_mgt_init(&pdev->dev);
	r = class_register(&codec_mm_class);
	if (r) {
		pr_err("vdec class create fail.\n");
		return r;
	}
	r = of_reserved_mem_device_init(&pdev->dev);
	if (r == 0)
		pr_debug("codec_mm reserved memory probed done\n");

	pr_info("%s ok\n", __func__);

#if IS_MODULE(CONFIG_AMLOGIC_MEDIA_MODULE) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
	codec_dev = &pdev->dev;
#endif
	codec_mm_scatter_mgt_init(&pdev->dev);
	codec_mm_keeper_mgr_init();
	amstream_test_init();
	codec_mm_scatter_mgt_test();
	REG_PATH_CONFIGS(CONFIG_PATH, codec_mm_configs);
	INIT_REG_NODE_CONFIGS(CONFIG_PATH, &codec_mm_trigger_node,
				  "trigger", codec_mm_trigger,
				  CONFIG_FOR_RW | CONFIG_FOR_T);
#if IS_MODULE(CONFIG_AMLOGIC_MEDIA_MODULE) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
	get_mte_sync_tags_hook();
#endif
	return 0;
}

static const struct of_device_id amlogic_mem_dt_match[] = {
	{
			.compatible = "amlogic, codec, mm",
		},
	{},
};

static struct platform_driver codec_mm_driver = {
	.probe = codec_mm_probe,
	.remove = NULL,
	.driver = {
			.owner = THIS_MODULE,
			.name = "codec_mm",
			.of_match_table = amlogic_mem_dt_match,
		}
};

int __init codec_mm_module_init(void)
{
	pr_err("now in %s\n", __func__);

	if (platform_driver_register(&codec_mm_driver)) {
		pr_err("failed to register amports mem module\n");
		return -ENODEV;
	}

	return 0;
}

//MODULE_DESCRIPTION("AMLOGIC amports mem  driver");
//MODULE_LICENSE("GPL");

static int codec_mm_reserved_init(struct reserved_mem *rmem, struct device *dev)
{
	struct codec_mm_mgt_s *mgt = get_mem_mgt();

	mgt->rmem = *rmem;
	pr_debug("%s %p->%p\n", __func__,
		 (void *)mgt->rmem.base,
		 (void *)mgt->rmem.base + mgt->rmem.size);
	return 0;
}

static const struct reserved_mem_ops codec_mm_rmem_vdec_ops = {
	.device_init = codec_mm_reserved_init,
};

static int __init codec_mm_res_setup(struct reserved_mem *rmem)
{
	rmem->ops = &codec_mm_rmem_vdec_ops;
	pr_debug("vdec: reserved mem setup\n");

	return 0;
}

RESERVEDMEM_OF_DECLARE(codec_mm_reserved, "amlogic, codec-mm-reserved",
			   codec_mm_res_setup);

static int secure_vdec_reserved_init(struct reserved_mem *rmem,
	struct device *dev)
{
	int ret = -1;

	if (!rmem || rmem->size <= 0) {
		pr_err("Invalid reserved secure vdec memory");
		return 0;
	}

	ret = tee_register_mem(TEE_MEM_TYPE_STREAM_INPUT,
				rmem->base,
				rmem->size);
	if (ret) {
		pr_err("protect vdec failed addr %llx %llx ret is %x\n",
			(u64)rmem->base, (u64)rmem->size, ret);
	}

	return ret;
}

static const struct reserved_mem_ops secure_vdec_rmem_ops = {
	.device_init = secure_vdec_reserved_init,
};

static int __init secure_vdec_res_setup(struct reserved_mem *rmem)
{
	rmem->ops = &secure_vdec_rmem_ops;

	return 0;
}

RESERVEDMEM_OF_DECLARE(secure_vdec_reserved, "amlogic, secure-vdec-reserved",
			secure_vdec_res_setup);

module_param(debug_mode, uint, 0664);
MODULE_PARM_DESC(debug_mode, "\n debug module\n");
module_param(debug_sc_mode, uint, 0664);
MODULE_PARM_DESC(debug_sc_mode, "\n debug scatter module\n");
module_param(debug_keep_mode, uint, 0664);
MODULE_PARM_DESC(debug_keep_mode, "\n debug keep module\n");
module_param(tvp_mode, uint, 0664);
MODULE_PARM_DESC(tvp_mode, "\n tvp module\n");
module_param(tvp_dynamic_increase_disable, uint, 0664);
MODULE_PARM_DESC(tvp_dynamic_increase_disable, "\n disable tvp_dynamic_increase\n");
module_param(tvp_dynamic_alloc_force_small_segment, uint, 0664);
MODULE_PARM_DESC(tvp_dynamic_alloc_force_small_segment,
	"\n enable tvp_dynamic_alloc_force_small_segment\n");
module_param(tvp_dynamic_alloc_force_small_segment_size, uint, 0664);
MODULE_PARM_DESC(tvp_dynamic_alloc_force_small_segment_size,
	"\n setting tvp_dynamic_alloc_force_small_segment_size\n");
module_param(tvp_pool_early_release_switch, uint, 0664);
MODULE_PARM_DESC(tvp_pool_early_release_switch,
	"\n forbiden tvp pool release when tvp not disable\n");
