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

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/syscalls.h>

#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include "mtk_iommu_dtv_api.h"
#include <linux/ion.h>

#include "mhal_demux_common.h"
#include "mdrv_mvd_parser.h"
#include "mhal_mvd_parser.h"
#include "mhal_mvd_parser_reg.h"
#include <linux/sysfs.h>

#include <linux/iommu.h>
#include <linux/dma-buf.h>

#include <media/dmxdev.h>
#include <media/dvb_demux.h>
#include <linux/dvb/dmx_mtk.h>
//-------------------------------------------------------------------------------------------------
//	Local Defines
//-------------------------------------------------------------------------------------------------
#define MAX_VPAS_NUM      2
#define MAX_PTS_NUM      48
#define MAX_VPAS_FD_SIZE  128

#define VPAS_IsInit(_ID)	(p_gDrvVPASCtx->_stVPASDrvCtrl[_ID]._stVPASDrvStat.bIsInit == true)

#define ENABLE_VPAS_MUTEX				 0
#define inc_vpas_fd_ptr(ptr)	   (ptr = ((ptr + 1) >= MAX_VPAS_FD_SIZE) ? 0 : (ptr + 1))
#define inc_vpas_ptstbl_ptr(ptr)	   (ptr = ((ptr + 1) >= MAX_PTS_NUM) ? 0 : (ptr + 1))

#define ENABLE_COPY_PATCH			0
/* 0: write_pointer_mode; 1: es_counter_mode */
#define ES_COUNTER_MODE				1

#define DRV_NAME "mtk-dtv-parser"
//-------------------------------------------------------------------------------------------------
//	Local Variables
//-------------------------------------------------------------------------------------------------

typedef void (*HAL_RegSetBase) (u64 u32Base);

typedef struct {
	VPAS_DrvStatus _stVPASDrvStat;
} VPAS_DrvCtrlSt;

typedef struct {
	u32 u32Offset;
	u32 u32Length;
	u64 u64PTS;
} VPAS_FD_INFO;

typedef struct {
	VPAS_FD_INFO vpas_fd_info[MAX_VPAS_FD_SIZE];
	u32 vpas_fd_rd;
	u32 vpas_fd_wr;
} VPAS_FD_RING;

typedef struct {
	u32 u32rptr;
	u32 u32wptr;
#if ES_COUNTER_MODE
	u32 u32ByteCnt[MAX_PTS_NUM];
#else
	u32 u32EsWptr[MAX_PTS_NUM];
#endif
	u64 u64pts[MAX_PTS_NUM];
} VPAS_PTS_TABLE;

typedef enum {
	E_VPAS_BUF_TASK_NONE,
	E_VPAS_BUF_TASK_START,
	E_VPAS_BUF_TASK_STOP
} VPAS_BUF_TASK_STAT;

typedef struct {
	u32 u32EntrySentCnt;
	u32 u32QuerryCnt;
	u32 u32InterruptCnt;
} VPAS_Statistic;

typedef struct {
	spinlock_t interrupt_lock;
	VPAS_DrvCtrlSt _stVPASDrvCtrl[MAX_VPAS_NUM];
	u64 u32ParserBufAddr[MAX_VPAS_NUM];
	u32 u32ParserBufSize[MAX_VPAS_NUM];
#if ENABLE_COPY_PATCH
	u64 u32PasCopyBufAddr[MAX_VPAS_NUM];
	u32 u32PasCopyBufSize[MAX_VPAS_NUM];
	u32 u32PasCopyBufWriteoffset[MAX_VPAS_NUM];
#endif
	VPAS_FD_RING stVpasFdRing[MAX_VPAS_NUM];
	u64 u32PasLastCopyAddr[MAX_VPAS_NUM];
	u64 u64PasLastCopyPts[MAX_VPAS_NUM];
	u32                  u32PrevWptr[MAX_VPAS_NUM];
	s32 s32PasOutputBufFd[MAX_VPAS_NUM];
	bool bVPASIsrAttached;
	bool bVPASDisableFlag;
	VPAS_EventCb cb_fun;
	u32 u32Handle[MAX_VPAS_NUM];
	video_parser_source eSrcMode[MAX_VPAS_NUM];
	bool bVPASIsSecure[MAX_VPAS_NUM];
	u32 buf_tag[MAX_VPAS_NUM];
	u32 package_cnt[MAX_VPAS_NUM];
	bool bVPASIsBypass[MAX_VPAS_NUM];
	bool bVPASIsTLV[MAX_VPAS_NUM];
	int pipeline_id[MAX_VPAS_NUM];
	VPAS_PTS_TABLE stPtsTable[MAX_VPAS_NUM];
#if ES_COUNTER_MODE
	u32 u32EsBufferIndex[MAX_VPAS_NUM];
	u64 u64PreviousPts[MAX_VPAS_NUM];
#endif
	struct dma_buf *db[MAX_VPAS_NUM];
	struct dma_buf_attachment *attach[MAX_VPAS_NUM];
	struct sg_table *sgt[MAX_VPAS_NUM];
	struct dvb_vb2_ctx *ctx[MAX_VPAS_NUM];
	struct platform_device *pdev[MAX_VPAS_NUM];
	VPAS_BUF_TASK_STAT eBufTaskStat[MAX_VPAS_NUM];
	int current_buf_idx[MAX_VPAS_NUM];
	VPAS_Statistic stStatistic[MAX_VPAS_NUM];
} VPAS_CTX_DRV;

typedef enum {
	E_VPAS_DBG_NONE,
	E_VPAS_DBG_BYPASS,
	E_VPAS_DBG_LOCK,
	E_VPAS_DBG_UNLOCK,
	E_VPAS_DBG_MAX
} VPAS_DBG_IDX;

typedef enum {
	E_VPAS_BUF_STAT_NONE,
	E_VPAS_BUF_STAT_INIT,
	E_VPAS_BUF_STAT_ALLOC,
	E_VPAS_BUF_STAT_DONE,
	E_VPAS_BUF_STAT_LOCKED,
	E_VPAS_BUF_STAT_DEV_CTX,
	E_VPAS_BUF_STAT_NOTSTREAMING,
	E_VPAS_BUF_STAT_WAIT_QBUF,
	E_VPAS_BUF_STAT_MAX
} VPAS_DBG_BUF_STAT;

static VPAS_CTX_DRV *p_gDrvVPASCtx;

#ifndef ALLOC_CTX
	static VPAS_CTX_DRV g_stVPASDrvCtx;
#endif

static struct task_struct *pTask[MAX_VPAS_NUM] = {0};
static video_parser_isr_cb pVideoParserISRCB;

//-------------------------------------------------------------------------------------------------
//  mutex
//-------------------------------------------------------------------------------------------------
#if ENABLE_VPAS_MUTEX
	static s32 s32VPASMutexID[2] = {-1, -1};
	static u8 _u8VPAS_Mutex[2][32] = { "VPAS1_Mutex", "VPAS2_Mutex"};

#define _DRV_VPAS_MutexCreate(_eDevID)                                  \
	do {                                                           \
		if (s32VPASMutexID[_eDevID] < 0) {                         \
			s32VPASMutexID[_eDevID] = OSAL_VPAS_MutexCreate(_u8VPAS_Mutex[_eDevID]); \
		}                                                       \
	} while (0)

#define _DRV_VPAS_MutexDelete(_eDevID)                                  \
	do {                                                           \
		if (s32VPASMutexID[_eDevID] >= 0) {                 \
			OSAL_VPAS_MutexDelete(s32VPASMutexID[_eDevID]);                \
			s32VPASMutexID[_eDevID] = -1;                                 \
		}                                                       \
	} while (0)

#define  _DRV_VPAS_Entry(_eDevID)                                                       \
	do {                                                                           \
		if (s32VPASMutexID[_eDevID] >= 0) {                             \
			if (!OSAL_VPAS_MutexObtain(s32VPASMutexID[_eDevID], MSOS_WAIT_FOREVER)) { \
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_WARN, "Mutex taking timeout\n");   \
			}                                                                   \
		}                                                                       \
	} while (0)


#define _DRV_VPAS_Release(_eDevID)                                      \
	do {                                                           \
		if (s32VPASMutexID[_eDevID] >= 0) {                    \
			OSAL_VPAS_MutexRelease(s32VPASMutexID[_eDevID]);               \
		}                                                       \
	} while (0)

#else

#define _DRV_VPAS_MutexCreate(_eDevID)
#define _DRV_VPAS_MutexDelete(_eDevID)
#define _DRV_VPAS_Entry(_eDevID)
#define _DRV_VPAS_Release(_eDevID)

#endif
void _MDrv_VPAS_WritePTS(u8 u8ParserID);

/* ========================== Attribute Functions ========================== */
int dbg_cmd;
int dbg_buf_stat[MAX_VPAS_NUM];
int dbg_buf_wptr[MAX_VPAS_NUM];
int dbg_err_code[MAX_VPAS_NUM];

#define DBG_SHOW_SZ 64
#define DBG_STORE_SZ 10

ssize_t help_show(struct device *dev,
	struct device_attribute *attr,
	char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "Debug Help:\n");
}

ssize_t log_level_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "log_level = %d\n", log_level);
}

ssize_t log_level_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long val;
	int err;

	err = kstrtoul(buf, DBG_STORE_SZ, &val);
	if (err)
		return err;
	if (val > 255)
		return -EINVAL;

	log_level = val;
	return count;
}

ssize_t dbg_cmd_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "dbg_cmd = %d\n", dbg_cmd);
}

ssize_t dbg_cmd_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long val;
	int err;

	err = kstrtoul(buf, DBG_STORE_SZ, &val);
	if (err)
		return err;
	if (val > E_VPAS_DBG_MAX)
		return -EINVAL;

	dbg_cmd = val;
	return count;
}

ssize_t dbg_buf_stat_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "%x %x\n", dbg_buf_stat[0], dbg_buf_stat[1]);
}

ssize_t dbg_buf_wptr_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "%x %x\n", dbg_buf_wptr[0], dbg_buf_wptr[1]);
}

ssize_t dbg_err_code_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, DBG_SHOW_SZ, "%x %x\n", dbg_err_code[0], dbg_err_code[1]);
}

DEVICE_ATTR_RO(help);
DEVICE_ATTR_RW(log_level);
DEVICE_ATTR_RW(dbg_cmd);
DEVICE_ATTR_RO(dbg_buf_stat);
DEVICE_ATTR_RO(dbg_buf_wptr);
DEVICE_ATTR_RO(dbg_err_code);

struct attribute *mtk_dtv_parser_attrs[] = {
	&dev_attr_help.attr,
	&dev_attr_log_level.attr,
	&dev_attr_dbg_cmd.attr,
	&dev_attr_dbg_buf_stat.attr,
	&dev_attr_dbg_buf_wptr.attr,
	&dev_attr_dbg_err_code.attr,
	NULL,
};

const struct attribute_group mtk_dtv_parser_attr_group = {
	.name = "mtk_dbg",
	.attrs = mtk_dtv_parser_attrs
};

int mtk_dtv_parser_create_sysfs_attr(struct platform_device *pdev)
{
	int ret = 0;

	/* Create device attribute files */
	//dev_info (pdev->dev, "Create device attribute files\n");
	ret = sysfs_create_group(&pdev->dev.kobj, &mtk_dtv_parser_attr_group);
	if (ret) {
		dev_err(&pdev->dev, "[%d]Fail to create sysfs files: %d\n", __LINE__, ret);
	}
	return ret;
}

int mtk_dtv_parser_remove_sysfs_attr(struct platform_device *pdev)
{
	/* Remove device attribute files */
	//dev_info (pdev->dev, "Remove device attribute files\n");
	sysfs_remove_group(&pdev->dev.kobj, &mtk_dtv_parser_attr_group);

	return 0;
}


static void _MDrv_VPAS_DbgSetBufStat(int parser_id, VPAS_DBG_BUF_STAT stat)
{
	dbg_buf_stat[parser_id] = stat;
}

static void _MDrv_VPAS_DbgSetBufWptr(int parser_id, unsigned int wptr)
{
	dbg_buf_wptr[parser_id] = wptr;
}

static void _MDrv_VPAS_DbgSetErrCode(int parser_id, VPAS_ErrCode ret)
{
	dbg_err_code[parser_id] = ret;
}

static VPAS_Result _MDrv_VPAS_SetLockBuf(u8 parser_id, int pipeline_id, u32 u32AidType)
{
	int res = 0;
	VPAS_Result eRet;

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_gDrvVPASCtx is NULL\n");
		return E_VPAS_FAIL;
	}
	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return E_VPAS_RET_INVALID_PARAM;
	}
	if (pipeline_id != 0) {
		p_gDrvVPASCtx->bVPASIsSecure[parser_id] = true;
		res = mtkd_iommu_buffer_authorize(p_gDrvVPASCtx->buf_tag[parser_id],
			p_gDrvVPASCtx->u32ParserBufAddr[parser_id],
			p_gDrvVPASCtx->u32ParserBufSize[parser_id],
			pipeline_id);
		if (res == 0) {
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"pas[%d] pipeline[%d] lock success!!\n",
				parser_id,
				pipeline_id);
			_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_LOCKED);
			HAL_VPAS_SetAid(parser_id, &u32AidType);
			eRet = E_VPAS_OK;
		} else {
			_MDrv_VPAS_DbgSetErrCode(parser_id, E_VPAS_ERR_LOCK_FAIL);
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"pas[%d] pipeline[%d] lock fail!!\n", parser_id, pipeline_id);
			eRet = E_VPAS_FAIL;
		}
	} else {
		u64 u32ParserBufAddr = p_gDrvVPASCtx->u32ParserBufAddr[parser_id];

		p_gDrvVPASCtx->bVPASIsSecure[parser_id] = false;
		res = mtkd_iommu_buffer_unauthorize(u32ParserBufAddr);
		if (res == 0) {
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"pas[%d] unlock success !!\n",
				parser_id);
			_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_DONE);
			HAL_VPAS_SetAid(parser_id, &u32AidType);
			eRet = E_VPAS_OK;
		} else {
			_MDrv_VPAS_DbgSetErrCode(parser_id, E_VPAS_ERR_UNLOCK_FAIL);
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"pas[%d] unlock fail!!\n", parser_id);
			eRet = E_VPAS_FAIL;
		}
	}
	return eRet;
}
static int _MDrv_VPAS_IsSecureStatChg(u8 parser_id)
{
	if (p_gDrvVPASCtx != NULL && parser_id < MAX_VPAS_NUM) {
		if ((p_gDrvVPASCtx->bVPASIsSecure[parser_id] == true &&
					p_gDrvVPASCtx->pipeline_id[parser_id] == 0) ||
					(p_gDrvVPASCtx->bVPASIsSecure[parser_id] == false &&
					p_gDrvVPASCtx->pipeline_id[parser_id] != 0) ||
					(dbg_cmd == E_VPAS_DBG_LOCK) ||
					(dbg_cmd == E_VPAS_DBG_UNLOCK))
			return true;
	}
	return false;
}

static bool _MDrv_VPAS_Resume(u8 parser_id)
{
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "parser_id=%u\n", parser_id);

	HAL_VPAS_SetStart(parser_id);
	if (p_gDrvVPASCtx->bVPASIsBypass[parser_id] == true)
		HAL_VPAS_SetBypass(parser_id, true);
	if (p_gDrvVPASCtx->bVPASIsTLV[parser_id] == true)
		HAL_VPAS_SetTLV(parser_id, true);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}

#define SLEEP_TIME 5//ms
#define TIMEOUT 10
#define STATISTIC_CNT 100
static int _MDrv_VPAS_BufHandler(void *data)
{
	u8 parser_id = *((u8 *)data);
	int loopcnt = 0;
	u32 u32Wptr = 0;

	if (p_gDrvVPASCtx != NULL)
		p_gDrvVPASCtx->eBufTaskStat[parser_id] = E_VPAS_BUF_TASK_START;
#if (AUTO_ALLOC_ES_BUF == 0)
	while (1) {
		if (kthread_should_stop()) {
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"[%s] id %d kthread_should_stop\n", __func__, parser_id);
			break;
		}

		if (p_gDrvVPASCtx == NULL) {
			msleep(SLEEP_TIME);
			continue;
		}

		if (p_gDrvVPASCtx->_stVPASDrvCtrl[parser_id]._stVPASDrvStat.bIsInit == true
			&& p_gDrvVPASCtx->u32ParserBufAddr[parser_id] == 0) {
			struct dvb_vb2_ctx *ctx = p_gDrvVPASCtx->ctx[parser_id];
			struct dma_buf *db = NULL;

			_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_INIT);

			if (ctx == NULL) {
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "dmx ctx null !!\n");
				_MDrv_VPAS_DbgSetBufStat(parser_id,
					E_VPAS_BUF_STAT_DEV_CTX);
				msleep(SLEEP_TIME);
				continue;
			}
			if (!dvb_vb2_is_streaming(ctx)) {
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d] stream off\n", parser_id);
				_MDrv_VPAS_DbgSetBufStat(parser_id,
					E_VPAS_BUF_STAT_NOTSTREAMING);
				msleep(SLEEP_TIME);
				continue;
			}

			if (list_empty(&ctx->dvb_q)) {
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%s]Buffer overflow!!\n"
					, ctx->name);
				_MDrv_VPAS_DbgSetBufStat(parser_id,
					E_VPAS_BUF_STAT_WAIT_QBUF);
				continue;
			}

			ctx->buf = list_entry(ctx->dvb_q.next, struct dvb_buffer, list);

			if (ctx->buf) {
				struct dma_buf_attachment *buf_att;
				struct sg_table *sgt;
				struct platform_device *pdev =
					p_gDrvVPASCtx->pdev[parser_id];
				u64 iova;
				u32 size;

				ctx->offset[0] = 0;
				ctx->remain[0] = vb2_plane_size(&ctx->buf->vb, 0);

				if (ctx->buf_siz[0] <= ctx->remain[0]) {
					ctx->remain[0] = ctx->buf_siz[0];
					vb2_set_plane_payload(&ctx->buf->vb,
						0, ctx->buf_siz[0]);
				}
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]offset = 0x%x\n",
					parser_id, ctx->offset[0]);
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]buf_siz = 0x%x\n",
					parser_id, ctx->buf_siz[0]);
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]remain = %d\n",
					parser_id, ctx->remain[0]);

				_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_ALLOC);
				db = ctx->buf->vb.planes[0].dbuf;
				// get iova

				buf_att = dma_buf_attach(db, &pdev->dev);
				sgt = dma_buf_map_attachment(buf_att, DMA_BIDIRECTIONAL);

				if (!sgt) {
					PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
						"dma_buf_map_attachment fail\n");
					_MDrv_VPAS_DbgSetErrCode(parser_id,
						E_VPAS_ERR_ALLOC_ION);
					break;
				}

				iova = sg_dma_address(sgt->sgl);
				size = sg_dma_len(sgt->sgl);
				p_gDrvVPASCtx->u32ParserBufAddr[parser_id] = iova;
				p_gDrvVPASCtx->u32ParserBufSize[parser_id] =
					ctx->buf_siz[0];
				p_gDrvVPASCtx->s32PasOutputBufFd[parser_id] =
				ctx->buf->vb.planes[0].m.fd;//dma_buf_fd(db, O_CLOEXEC);
				p_gDrvVPASCtx->attach[parser_id] = buf_att;
				p_gDrvVPASCtx->sgt[parser_id] = sgt;
				p_gDrvVPASCtx->current_buf_idx[parser_id] =
					ctx->buf->vb.index;

				dma_buf_unmap_attachment(buf_att, sgt, DMA_BIDIRECTIONAL);
				dma_buf_detach(db, buf_att);
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
				"iova= 0x%llx size= 0x%llx BufFd = %d  fd = %d idx = %d\n",
					iova, size,
					p_gDrvVPASCtx->s32PasOutputBufFd[parser_id],
					ctx->buf->vb.planes[0].m.fd,
					p_gDrvVPASCtx->current_buf_idx[parser_id]);

				HAL_VPAS_SetBuf(parser_id,
					p_gDrvVPASCtx->u32ParserBufAddr[parser_id],
					p_gDrvVPASCtx->u32ParserBufSize[parser_id]);

				_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_DONE);

				if (_MDrv_VPAS_IsSecureStatChg(parser_id)) {
					PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
						"secure status changed\n");
					_MDrv_VPAS_SetLockBuf(parser_id,
						p_gDrvVPASCtx->pipeline_id[parser_id],
						E_AID_TYPE_S);
				}

				_MDrv_VPAS_Resume(parser_id);
			} else {
				_MDrv_VPAS_DbgSetErrCode(parser_id, E_VPAS_ERR_GET_DQBUF);
			}
		} else if (p_gDrvVPASCtx->u32ParserBufAddr[parser_id] != 0 &&
			p_gDrvVPASCtx->_stVPASDrvCtrl[parser_id]._stVPASDrvStat.bIsInit == true) {
			_MDrv_VPAS_WritePTS(parser_id);
					loopcnt++;
			if (loopcnt % STATISTIC_CNT == 0) {
				HAL_VPAS_GetWritePointer(parser_id, &u32Wptr);
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
					"EntrySent = %d Querry %d Isr %d pts %lld wptr 0x%x\n",
					p_gDrvVPASCtx->stStatistic[parser_id].u32InterruptCnt,
					p_gDrvVPASCtx->stStatistic[parser_id].u32QuerryCnt,
					p_gDrvVPASCtx->stStatistic[parser_id].u32InterruptCnt,
					HAL_VPAS_ReadPTS(parser_id),
					u32Wptr
					);
			}
		}
		msleep(SLEEP_TIME);
	}
#endif
	_MDrv_VPAS_DbgSetBufStat(parser_id, E_VPAS_BUF_STAT_NONE);
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "[%s] id %d end\n", __func__, parser_id);
	if (p_gDrvVPASCtx != NULL) {
		HAL_VPAS_GetWritePointer(parser_id, &u32Wptr);
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
			"EntrySent = %d Querry %d Isr %d pts %lld wptr 0x%x\n",
			p_gDrvVPASCtx->stStatistic[parser_id].u32InterruptCnt,
			p_gDrvVPASCtx->stStatistic[parser_id].u32QuerryCnt,
			p_gDrvVPASCtx->stStatistic[parser_id].u32InterruptCnt,
			HAL_VPAS_ReadPTS(parser_id),
			u32Wptr
			);
		p_gDrvVPASCtx->eBufTaskStat[parser_id] = E_VPAS_BUF_TASK_NONE;
	}
	return 1;
}

static int _MDrv_VPAS_BufHandler0(void *data)
{
	int parser_id = 0;

	return _MDrv_VPAS_BufHandler(&parser_id);
}

static int _MDrv_VPAS_BufHandler1(void *data)
{
	int parser_id = 1;

	return _MDrv_VPAS_BufHandler(&parser_id);
}

void _MDrv_VPAS_CreateBufHandleThread(u8 u8ParserID)
{
	if (u8ParserID < MAX_VPAS_NUM) {
		if (u8ParserID == 0)
			pTask[u8ParserID] = kthread_create(_MDrv_VPAS_BufHandler0, &u8ParserID,
				"mtkvpas_buf_handler0");
		else
			pTask[u8ParserID] = kthread_create(_MDrv_VPAS_BufHandler1, &u8ParserID,
				"mtkvpas_buf_handler1");

		if (IS_ERR(pTask[u8ParserID])) {
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "kthread_create fail\n");
			_MDrv_VPAS_DbgSetErrCode(0, E_VPAS_ERR_BUF_TASK);
		} else
			wake_up_process(pTask[u8ParserID]);
	}
}

static void _MDrv_VPAS_PackFd(u8 u8ParserID, u32 u32Para, u64 u64Pts)
{
	int vpas_fd_wr;
#if (ES_COUNTER_MODE == 0)
	u64 phyPasWriteAddr;
#endif

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_gDrvVPASCtx is NULL\n");
		return;
	}

	vpas_fd_wr = p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr;
#if ES_COUNTER_MODE
	if (u32Para + p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] <
		p_gDrvVPASCtx->u32ParserBufSize[u8ParserID]) {
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS =
			u64Pts;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset =
			p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID];
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
			u32Para;

		inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);

		p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] += u32Para;
	} else if (u32Para + p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] >
		p_gDrvVPASCtx->u32ParserBufSize[u8ParserID]) {
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS = u64Pts;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset =
			p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID];
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
			p_gDrvVPASCtx->u32ParserBufSize[u8ParserID] -
			p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID];

		inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);
		vpas_fd_wr = p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr;

		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS = u64Pts;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset = 0;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
			u32Para +
			p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] -
			p_gDrvVPASCtx->u32ParserBufSize[u8ParserID];

		inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);

		p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] =
			u32Para +
			p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] -
			p_gDrvVPASCtx->u32ParserBufSize[u8ParserID];
	} else {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "get 2 pts at same location or wrap around\n");
	}
#else
	/* parser write pointer address in unit 8 bytes */
	phyPasWriteAddr = (u64)u32Para * 8;

	if (p_gDrvVPASCtx->package_cnt[u8ParserID] != 0) {
		if (phyPasWriteAddr > p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID]) {
			u32 szCopyLength =
				(phyPasWriteAddr - p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID]);
			int vpas_fd_wr = p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr;

			if ((vpas_fd_wr + 1)%MAX_VPAS_FD_SIZE ==
				p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_rd)
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "mentry/pts table overflow\n");

			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS =
				p_gDrvVPASCtx->u64PasLastCopyPts[u8ParserID];
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset =
				(p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID] -
				p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID]);
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
				szCopyLength;

			inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);
		} else if (phyPasWriteAddr < p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID]) {
			u32 szCopyLength2 = 0;
			int vpas_fd_wr = p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr;
			u64 PhyPasBufEnd = p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID] +
				p_gDrvVPASCtx->u32ParserBufSize[u8ParserID];

			u32 szCopyLength =
				(PhyPasBufEnd - p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID]);

			if ((vpas_fd_wr + 1) % MAX_VPAS_FD_SIZE ==
				p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_rd)
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "entry/pts table overflow\n");

			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS =
				p_gDrvVPASCtx->u64PasLastCopyPts[u8ParserID];
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset =
				(p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID] -
				p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID]);
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
				szCopyLength;

			inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);

			szCopyLength2 =
				(phyPasWriteAddr - p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID]);
			vpas_fd_wr = p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr;

			if ((vpas_fd_wr + 1) % MAX_VPAS_FD_SIZE ==
				p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_rd)
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "entry/pts table overflow\n");

			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u64PTS =
				p_gDrvVPASCtx->u64PasLastCopyPts[u8ParserID];
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Offset =
				0;
			p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_info[vpas_fd_wr].u32Length =
				szCopyLength2;

			inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr);
		} else {
			PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
				"get 2 pts at same location or wrap around\n");
		}
		p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID] = phyPasWriteAddr;
	} else {
		p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID] =
			p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID];
		p_gDrvVPASCtx->package_cnt[u8ParserID]++;
	}
	p_gDrvVPASCtx->u64PasLastCopyPts[u8ParserID] = u64Pts;
#endif
}

void _MDrv_VPAS_WritePTS(u8 u8ParserID)
{
	int ptsWptr = 0;
	int ptsRptr = 0;
	unsigned long flags;

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_gDrvVPASCtx is NULL\n");
		return;
	}
	if (!VPAS_IsInit(u8ParserID)) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "VPAS driver is not initialized!\n");
		return;
	}
	spin_lock_irqsave(&p_gDrvVPASCtx->interrupt_lock, flags);
	ptsWptr = p_gDrvVPASCtx->stPtsTable[u8ParserID].u32wptr;
	ptsRptr = p_gDrvVPASCtx->stPtsTable[u8ParserID].u32rptr;
	spin_unlock_irqrestore(&p_gDrvVPASCtx->interrupt_lock, flags);
	while (ptsWptr != ptsRptr) {
#if ES_COUNTER_MODE
		_MDrv_VPAS_PackFd(
			u8ParserID,
			p_gDrvVPASCtx->stPtsTable[u8ParserID].u32ByteCnt[ptsRptr],
			p_gDrvVPASCtx->stPtsTable[u8ParserID].u64pts[ptsRptr]);
		inc_vpas_ptstbl_ptr(ptsRptr);
#else
		u64 u64Pts = 0;
		u32 u32Wptr = 0;

		u64Pts = p_gDrvVPASCtx->stPtsTable[u8ParserID].u64pts[ptsRptr];
		u32Wptr = p_gDrvVPASCtx->stPtsTable[u8ParserID].u32EsWptr[ptsRptr];
		_MDrv_VPAS_DbgSetBufWptr(u8ParserID, u32Wptr);
		inc_vpas_ptstbl_ptr(ptsRptr);

		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "wptr= 0x%x PrevWptr= 0x%x Pts = %lld\n",
			u32Wptr,
			p_gDrvVPASCtx->u32PrevWptr[u8ParserID], u64Pts);

		if (p_gDrvVPASCtx->u32PrevWptr[u8ParserID] != u32Wptr) {
			p_gDrvVPASCtx->u32PrevWptr[u8ParserID] = u32Wptr;
			_MDrv_VPAS_PackFd(u8ParserID, u32Wptr, u64Pts);
		}
#endif
	}
	//spin_lock_irqsave(&p_gDrvVPASCtx->interrupt_lock, flags);
	p_gDrvVPASCtx->stPtsTable[u8ParserID].u32rptr = ptsRptr;
	//spin_unlock_irqrestore(&p_gDrvVPASCtx->interrupt_lock, flags);
}

VPAS_Result _MDrv_VPAS_Init_Context(u8 u8ParserID)
{
#ifdef ALLOC_CTX
	int i = 0;

	if (p_gDrvVPASCtx == NULL) {
		p_gDrvVPASCtx = kzalloc(sizeof(*p_gDrvVPASCtx), GFP_KERNEL);
		if (p_gDrvVPASCtx == NULL) {
			_MDrv_VPAS_DbgSetErrCode(0, E_VPAS_ERR_INIT_FAIL);
			return E_VPAS_RET_INIT_FAIL;
		}
		memset((u8 *)p_gDrvVPASCtx, 0, sizeof(VPAS_CTX_DRV));
		spin_lock_init(&(p_gDrvVPASCtx->interrupt_lock));

#if (AUTO_ALLOC_ES_BUF == 0)
		_MDrv_VPAS_CreateBufHandleThread(u8ParserID);
#endif
	} else {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "ctx existed, pTask[%d] = %d\n",
			u8ParserID, pTask[u8ParserID]);
		if (pTask[u8ParserID] == 0)
			_MDrv_VPAS_CreateBufHandleThread(u8ParserID);
		return E_VPAS_RET_INIT_ALREADY_EXIST;
	}
#else
	p_gDrvVPASCtx =  &g_stVPASDrvCtx;
#endif
	return E_VPAS_OK;
}

static void _MDrv_VPAS_InitVar(u8 u8ParserID)
{
	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_WARN, "p_gDrvMVOPCtx is NULL pointer\n");
		return;
	}
		p_gDrvVPASCtx->_stVPASDrvCtrl[u8ParserID]._stVPASDrvStat.bIsInit = false;
#if ENABLE_COPY_PATCH
		p_gDrvVPASCtx->u32PasCopyBufAddr[u8ParserID] = 0;
		p_gDrvVPASCtx->u32PasCopyBufSize[u8ParserID] = 0;
		p_gDrvVPASCtx->u32PasCopyBufWriteoffset[u8ParserID] = 0;
#endif
		p_gDrvVPASCtx->u32PasLastCopyAddr[u8ParserID] = 0;
		p_gDrvVPASCtx->u64PasLastCopyPts[u8ParserID] = 0;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_rd = 0;
		p_gDrvVPASCtx->stVpasFdRing[u8ParserID].vpas_fd_wr = 0;
		p_gDrvVPASCtx->s32PasOutputBufFd[u8ParserID] = 0;
		p_gDrvVPASCtx->u32PrevWptr[u8ParserID] = 0xFFFFFFFF;
		p_gDrvVPASCtx->package_cnt[u8ParserID] = 0;
		p_gDrvVPASCtx->bVPASIsBypass[u8ParserID] = false;
		p_gDrvVPASCtx->bVPASIsSecure[u8ParserID] = false;
		p_gDrvVPASCtx->bVPASIsTLV[u8ParserID] = false;
		p_gDrvVPASCtx->stPtsTable[u8ParserID].u32rptr = 0;
		p_gDrvVPASCtx->stPtsTable[u8ParserID].u32wptr = 0;
		p_gDrvVPASCtx->pipeline_id[u8ParserID] = 0;
#if ES_COUNTER_MODE
		p_gDrvVPASCtx->u32EsBufferIndex[u8ParserID] = 0;
		p_gDrvVPASCtx->u64PreviousPts[u8ParserID] = 0;
#endif
		p_gDrvVPASCtx->current_buf_idx[u8ParserID] = -1;
		p_gDrvVPASCtx->stStatistic[u8ParserID].u32EntrySentCnt = 0;
		p_gDrvVPASCtx->stStatistic[u8ParserID].u32QuerryCnt = 0;
		p_gDrvVPASCtx->stStatistic[u8ParserID].u32InterruptCnt = 0;
}

//========================================
int video_parser_isr(int irq, void *dev_id)
{
	u8 u8Ret = 0;
	int parser_id = 0;
	unsigned long flags;
	if (pVideoParserISRCB)
		return pVideoParserISRCB(irq, dev_id);

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_WARN, "p_gDrvVPASCtx is NULL\n");
		return 0;
	}

	p_gDrvVPASCtx->stStatistic[parser_id].u32InterruptCnt++;
	spin_lock_irqsave(&p_gDrvVPASCtx->interrupt_lock, flags);
	u8Ret = HAL_VPAS_Sel_Interrupt();
	spin_unlock_irqrestore(&p_gDrvVPASCtx->interrupt_lock, flags);

	for (parser_id = 0; parser_id < MAX_VPAS_NUM; parser_id++) {
		if (u8Ret & (1 << parser_id)) {
			int ptsWptr = p_gDrvVPASCtx->stPtsTable[parser_id].u32wptr;

			if (VPAS_IsInit(parser_id)) {
				VPAS_PTS_TABLE stPtsTable;

				spin_lock_irqsave(&p_gDrvVPASCtx->interrupt_lock, flags);
#if ES_COUNTER_MODE
				HAL_VPAS_GetByteCount(
					parser_id,
					&p_gDrvVPASCtx->stPtsTable[parser_id].u32ByteCnt[ptsWptr]);
				if (p_gDrvVPASCtx->u64PreviousPts[parser_id] != 0)
					inc_vpas_ptstbl_ptr(
						p_gDrvVPASCtx->stPtsTable[parser_id].u32wptr);
				ptsWptr = p_gDrvVPASCtx->stPtsTable[parser_id].u32wptr;
				p_gDrvVPASCtx->stPtsTable[parser_id].u64pts[ptsWptr] = HAL_VPAS_ReadPTS(parser_id);
				p_gDrvVPASCtx->u64PreviousPts[parser_id] = p_gDrvVPASCtx->stPtsTable[parser_id].u64pts[ptsWptr];
#else
				p_gDrvVPASCtx->stPtsTable[parser_id].u64pts[ptsWptr] =
					HAL_VPAS_ReadPTS(parser_id);
				HAL_VPAS_GetWritePointer(
					parser_id,
					&p_gDrvVPASCtx->stPtsTable[parser_id].u32EsWptr[ptsWptr]);
				inc_vpas_ptstbl_ptr(p_gDrvVPASCtx->stPtsTable[parser_id].u32wptr);
#endif
				spin_unlock_irqrestore(&p_gDrvVPASCtx->interrupt_lock, flags);
				if (p_gDrvVPASCtx->stPtsTable[parser_id].u32wptr ==
					p_gDrvVPASCtx->stPtsTable[parser_id].u32rptr)
					PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
						"pts table[%d] over flow\n",
						parser_id);

				stPtsTable = p_gDrvVPASCtx->stPtsTable[parser_id];

#if ES_COUNTER_MODE
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
					"ptstbl %d wptr %d pts %lld byteCnt 0x%x\n",
					parser_id,
					stPtsTable.u32wptr,
					stPtsTable.u64pts[ptsWptr],
					p_gDrvVPASCtx->stPtsTable[parser_id].u32ByteCnt[ptsWptr]);
#else
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
					"ptstbl %d wptr %d pts %lld esw 0x%llx\n",
					parser_id,
					stPtsTable.u32wptr,
					stPtsTable.u64pts[ptsWptr],
					p_gDrvVPASCtx->stPtsTable[parser_id].u32EsWptr[ptsWptr]);
#endif
			}
			spin_lock_irqsave(&p_gDrvVPASCtx->interrupt_lock, flags);
			HAL_VPAS_CLR_Interrupt(parser_id);
			spin_unlock_irqrestore(&p_gDrvVPASCtx->interrupt_lock, flags);
		}
	}
	return 1;
}

VPAS_Result MDrv_VPAS_V2_Init(u8 u8ParserID, parser_init_config *p_parser_init,
	struct platform_device *pdev)
{
	VPAS_Result eRet = E_VPAS_RET_INIT_FAIL;
	int pBufTag = 0;


	if (u8ParserID >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", u8ParserID);
		return E_VPAS_RET_ILLEGAL_ACCESS;
	}
	_MDrv_VPAS_DbgSetErrCode(u8ParserID, E_VPAS_ERR_NONE);

	_DRV_VPAS_MutexCreate(u8ParserID);

	pBufTag = p_parser_init->es_buf_tag;

	eRet = _MDrv_VPAS_Init_Context(u8ParserID);

	if ((eRet != E_VPAS_OK && eRet != E_VPAS_RET_INIT_ALREADY_EXIST) ||
		p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]Context Init FAILED! ret %d\n",
			u8ParserID, eRet);
		return E_VPAS_FAIL;
	}

	_MDrv_VPAS_InitVar(u8ParserID);

	if (!HAL_VPAS_RegSetBase()) {
		_MDrv_VPAS_DbgSetErrCode(u8ParserID, E_VPAS_ERR_REGBASE);
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]reg base Init FAILED!\n",
			u8ParserID);
		return E_VPAS_FAIL;
	}

	HAL_VPAS_InitVar(u8ParserID);

	_DRV_VPAS_Entry(u8ParserID);

	HAL_VPAS_SetReset(u8ParserID, 0, 0);
	if (dbg_cmd == E_VPAS_DBG_BYPASS)
		p_gDrvVPASCtx->bVPASIsBypass[u8ParserID] = true;
	if (p_gDrvVPASCtx->bVPASIsBypass[u8ParserID] == true)
		HAL_VPAS_SetBypass(u8ParserID, true);
	if (p_gDrvVPASCtx->bVPASIsTLV[u8ParserID] == true)
		HAL_VPAS_SetTLV(u8ParserID, true);

	p_gDrvVPASCtx->buf_tag[u8ParserID] = pBufTag;
	p_gDrvVPASCtx->ctx[u8ParserID] = p_parser_init->ctx;
	p_gDrvVPASCtx->pdev[u8ParserID] = pdev;
	p_gDrvVPASCtx->_stVPASDrvCtrl[u8ParserID]._stVPASDrvStat.bIsInit = true;

	_DRV_VPAS_Release(u8ParserID);

	return eRet;
}

VPAS_Result MDrv_VPAS_V2_Exit(u8 u8ParserID, struct platform_device *pdev)
{
	VPAS_Result ret = E_VPAS_OK;
	int id = 0;
	int timeout = 0;

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "id: %d\n", u8ParserID);

	HAL_VPAS_SetStop(u8ParserID);
	HAL_VPAS_Exit(u8ParserID);

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_gDrvVPASCtx is NULL pointer\n");
		return E_VPAS_FAIL;
	}
	if (!VPAS_IsInit(u8ParserID)) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "VPAS driver is not initialized!\n");
		return E_VPAS_FAIL;
	}
	_DRV_VPAS_Entry(u8ParserID);

	p_gDrvVPASCtx->eBufTaskStat[u8ParserID] = E_VPAS_BUF_TASK_STOP;
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
		"[%s] kthread_stop id %d end >>>\n", __func__, u8ParserID);
	kthread_stop(pTask[u8ParserID]);
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
		"[%s] kthread_stop id %d end <<<\n", __func__, u8ParserID);

	pTask[u8ParserID] = 0;
	p_gDrvVPASCtx->_stVPASDrvCtrl[u8ParserID]._stVPASDrvStat.bIsInit = false;

	p_gDrvVPASCtx->u32ParserBufAddr[u8ParserID] = 0;

	for (id = 0; id < MAX_VPAS_NUM; id++) {
		if (p_gDrvVPASCtx->_stVPASDrvCtrl[u8ParserID]._stVPASDrvStat.bIsInit == true)
			break;
	}

	if (id == MAX_VPAS_NUM) {
		kfree(p_gDrvVPASCtx);
		p_gDrvVPASCtx = NULL;
	}

	_DRV_VPAS_Release(u8ParserID);

	return ret;
}

int _MDrv_VPAS_GetPasId(parser_id_enum parser_id)
{
	switch (parser_id) {
	case PARSER_ID_0:
		return 0;
	case PARSER_ID_1:
		return 1;
	default:
		return MAX_VPAS_NUM;
	}
}

int video_parser_init(parser_init_config *p_parser_init, struct platform_device *pdev)
{
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "\n");

	if (p_parser_init == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_parser_init is NULL\n");
		return 0;
	}
	int parser_id = _MDrv_VPAS_GetPasId(p_parser_init->parser_id);

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "parser_id=%u, es_buf_tag=%d, RegBaseAddr=%d\n",
		p_parser_init->parser_id,
		p_parser_init->es_buf_tag,
		p_parser_init->RegBaseAddr);

	MDrv_VPAS_V2_Init(parser_id, p_parser_init, pdev);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}
#if (AUTO_ALLOC_ES_BUF == 1)
int video_parser_resume(parser_id_enum e_parser_id)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	return _MDrv_VPAS_Resume(parser_id);
}
#endif
int video_parser_suspend(parser_id_enum e_parser_id)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "e_parser_id=%u\n", e_parser_id);

	HAL_VPAS_SetStop(parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}

int video_parser_stop(parser_id_enum e_parser_id)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "e_parser_id=%u\n", e_parser_id);

	HAL_VPAS_SetStop(parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}

int video_parser_get_entry(parser_id_enum e_parser_id, parser_es_meta *p_meta)
{
	int ret = 0;
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (p_meta == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "pVpasEntry == NULL\n");
		return -EINVAL;
	}
	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return -EINVAL;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "e_parser_id=%u\n", e_parser_id);

	p_meta->version = VIDEO_PARSER_ES_MATA_VERSION;
	p_meta->struct_size = sizeof(parser_es_meta);
	p_meta->pts = 0;
	p_meta->es_section_offset = 0;
	p_meta->es_section_length = 0;

	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]p_gDrvVPASCtx is NULL\n",
			parser_id);
		return -ENOMEM;
	}
	if (!VPAS_IsInit(parser_id)) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "[%d]VPAS driver is not initialized!\n",
			parser_id);
		return -EINVAL;
	}

	p_meta->es_buf_info.es_buf_addr = p_gDrvVPASCtx->u32ParserBufAddr[parser_id];
	p_meta->es_buf_info.es_buf_size = p_gDrvVPASCtx->u32ParserBufSize[parser_id];
	p_meta->es_buf_info.es_buf_fd = p_gDrvVPASCtx->s32PasOutputBufFd[parser_id];

	if (p_gDrvVPASCtx->bVPASIsSecure[parser_id] == true)
		p_meta->dataid = 1;
	else
		p_meta->dataid = 0;

	if (p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_rd !=
		p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_wr) {
		int vpas_fd_rd = p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_rd;
		VPAS_FD_RING vpas_fd_ring = p_gDrvVPASCtx->stVpasFdRing[parser_id];

		p_meta->es_buf_info.es_buf_fd =
			p_gDrvVPASCtx->s32PasOutputBufFd[parser_id];
		p_meta->es_section_offset =
			vpas_fd_ring.vpas_fd_info[vpas_fd_rd].u32Offset;
		p_meta->es_section_length =
			vpas_fd_ring.vpas_fd_info[vpas_fd_rd].u32Length;

		p_meta->pts = vpas_fd_ring.vpas_fd_info[vpas_fd_rd].u64PTS;

		inc_vpas_fd_ptr(p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_rd);
	} else {
		/*
		 * cannot get byte count under es counter mode
		 * using write pointer mode instead
		 */
		u64 u64Pts = 0;
		u32 u32Wptr = 0;
		u64 u64Wptr = 0;

		u64Pts = HAL_VPAS_ReadPTS(parser_id);
		HAL_VPAS_GetWritePointer(parser_id, &u32Wptr);
		_MDrv_VPAS_DbgSetBufWptr(parser_id, u32Wptr);
		u64Wptr = ((u64)u32Wptr) * 8;
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
			"vpas r:%d w:%d id=%d BufFd=%d payload=0x%x pts=%lld\n",
			p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_rd,
			p_gDrvVPASCtx->stVpasFdRing[parser_id].vpas_fd_wr,
			parser_id,
			p_gDrvVPASCtx->s32PasOutputBufFd[parser_id],
			u32Wptr,
			u64Pts);
		if (p_gDrvVPASCtx->bVPASIsBypass[parser_id] == true &&
			p_gDrvVPASCtx->u32PasLastCopyAddr[parser_id] != u64Wptr) {
			u64 *pLastCopyAddr = &p_gDrvVPASCtx->u32PasLastCopyAddr[parser_id];

			if (*pLastCopyAddr == 0 &&
				u64Wptr == p_gDrvVPASCtx->u32ParserBufAddr[parser_id]) {
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "bypass mode, no input\n");
				ret = 0;
			} else {
				p_meta->pts = -1;
				if (*pLastCopyAddr == 0) {
					p_meta->es_section_offset = 0;
					if (u64Wptr > *pLastCopyAddr) {
						p_meta->es_section_length =
							u64Wptr -
							*pLastCopyAddr;
					}
					else
						PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
							"ERR u64Wptr = 0x%llx\n",
							(unsigned long long)u64Wptr);
					if (u64Wptr != (p_gDrvVPASCtx->u32ParserBufAddr[parser_id]
						+ p_gDrvVPASCtx->u32ParserBufSize[parser_id]))
						*pLastCopyAddr = u64Wptr;
					else
						*pLastCopyAddr =
							p_gDrvVPASCtx->u32ParserBufAddr[parser_id];
				} else {
					p_meta->es_section_offset =
						*pLastCopyAddr -
						p_gDrvVPASCtx->u32ParserBufAddr[parser_id];
					if (u64Wptr > *pLastCopyAddr) {
						p_meta->es_section_length =
							u64Wptr -
							*pLastCopyAddr;
						*pLastCopyAddr = u64Wptr;
					} else {
						p_meta->es_section_length =
							p_gDrvVPASCtx->u32ParserBufAddr[parser_id] +
							p_gDrvVPASCtx->u32ParserBufSize[parser_id] -
							*pLastCopyAddr;
						*pLastCopyAddr =
							p_gDrvVPASCtx->u32ParserBufAddr[parser_id];
					}
				}
				p_meta->es_buf_info.es_buf_fd =
					p_gDrvVPASCtx->s32PasOutputBufFd[parser_id];
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
					"bypass pts 0x%llx ES: 0x%x, 0x%x dataid %d\n",
					p_meta->pts,
					p_meta->es_section_offset,
					p_meta->es_section_length,
					p_meta->dataid);
				ret = 1;
			}
		} else {
			ret = 0;
		}
	}

	if (p_meta->es_section_length > 0) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO,
			"pts = 0x%llx offset = 0x%x len = 0x%x buf_fd = %d dataid %d\n",
			p_meta->pts,
			p_meta->es_section_offset,
			p_meta->es_section_length,
			p_meta->es_buf_info.es_buf_fd,
			p_meta->dataid);
		p_gDrvVPASCtx->stStatistic[parser_id].u32EntrySentCnt++;
	}

	p_gDrvVPASCtx->stStatistic[parser_id].u32QuerryCnt++;
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return ret;
}

int video_parser_update_es_rptr(parser_id_enum e_parser_id, parser_es_meta *p_meta)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	if (p_meta == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_meta is NULL\n");
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG,
		"e_parser_id=%u, es_section_offset=0x%x\n",
		e_parser_id,
		p_meta->es_section_offset);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_WARN,
		"es_section_offset = 0x%x u32ParserBufAddr[parser_id]=0x%llx\n",
		p_meta->es_section_offset,
		p_gDrvVPASCtx->u32ParserBufAddr[parser_id]);
	HAL_VPAS_SetEsOffset(parser_id, p_meta->es_section_offset +
		p_gDrvVPASCtx->u32ParserBufAddr[parser_id]);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}
int video_parser_set_control(parser_id_enum e_parser_id, video_parser_control_command cmd,
	vedio_parser_ctrl *p_ctrl)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (p_ctrl == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_ctrl is NULL\n");
		return 0;
	}
	if (p_gDrvVPASCtx == NULL) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "p_gDrvVPASCtx is NULL\n");
		return 0;
	}
	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG,
		"e_parser_id=%u, cmd=0x%x, p_ctrl->val=%u\n",
		e_parser_id,
		cmd,
		p_ctrl->value);

	switch (cmd) {
	case VPAS_CMD_SET_INPUT_MODE:
		break;
	case VPAS_CMD_SET_SECURE:
		{
			VPAS_Result ret;
			int pipeline_id = 0;
			u32 u32AidType = E_AID_TYPE_S;

			// TODO: get aid type from seal api
			pipeline_id = p_ctrl->value;
			p_gDrvVPASCtx->pipeline_id[parser_id] = pipeline_id;

			if (p_gDrvVPASCtx->u32ParserBufAddr[parser_id] != 0)
				ret = _MDrv_VPAS_SetLockBuf(parser_id, pipeline_id, u32AidType);
			else
				PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR,
					"id: %d try lock, but buffer addr = 0\n", parser_id);
		}
		break;
	case VPAS_CMD_SET_TLV_MODE:
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "parser[%d] TLV mode: 0x%x\n",
				parser_id, p_ctrl->value);
		p_gDrvVPASCtx->bVPASIsTLV[parser_id] = (bool)p_ctrl->value;
		HAL_VPAS_SetTLV(parser_id, p_ctrl->value);
		break;
	case VPAS_CMD_SET_BYPASS_MODE:
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_INFO, "parser[%d] Bypass mode: 0x%x\n",
				parser_id, p_ctrl->value);
		p_gDrvVPASCtx->bVPASIsBypass[parser_id] = (bool)p_ctrl->value;
		HAL_VPAS_SetBypass(parser_id, p_ctrl->value);
		break;
	default:
		break;
	}

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}
int video_parser_get_control(parser_id_enum e_parser_id,
	video_parser_control_command cmd, vedio_parser_ctrl *p_ctrl)
{// TODO:
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "e_parser_id=%u, cmd=0x%x\n",
		e_parser_id,
		cmd);

	switch (cmd) {
	case VPAS_CMD_GET_ES_BUF_INFO:
		break;
	default:
		break;
	}

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}

int video_parser_deinit(parser_id_enum e_parser_id, struct platform_device *pdev)
{
	int parser_id = _MDrv_VPAS_GetPasId(e_parser_id);

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "\n");

	if (parser_id >= MAX_VPAS_NUM) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "id: %d\n", parser_id);
		return 0;
	}
	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "e_parser_id=%u\n", e_parser_id);

	if (MDrv_VPAS_V2_Exit(parser_id, pdev) != E_VPAS_OK) {
		PAS_DEBUG_LOG(PAS_LOG_LEVEL_ERROR, "MDrv_VPAS_V2_Exit != E_VPAS_OK\n");
		return 0;
	}

	PAS_DEBUG_LOG(PAS_LOG_LEVEL_DEBUG, "ok\n");
	return 1;
}

int video_parser_install_isrcb(video_parser_isr_cb pfn)
{
	pVideoParserISRCB = pfn;
	return 1;
}
EXPORT_SYMBOL(video_parser_install_isrcb);
