/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

#if defined(LINUX_TURNKEY_SOLUTION) && defined(CC_ENABLE_VDECOMX)

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------

#include <linux/cdev.h>
#include <linux/fs.h>          //For file_operations
#include "x_typedef.h"
#include "x_os.h"
#include "x_printf.h"
#include "x_assert.h"

#include "vdecomx_drv_if.h"
#include <linux/version.h>

#include <asm/uaccess.h>
#include "OMX_Core.h"
#include "x_kmem.h"

#include "vdec_drvif.h"

// tunneled video playback support
#include "aud_drvif.h"
#include "b2r_if.h"
#include "stc_drvif.h"

#include <linux/semaphore.h>

#if 1//1//defined(VDEC_YUV_CVT_IMG)
#include <linux/module.h>
#include "imgrz_if.h"
#include "imgrz_drvif.h"
#include "gfx_if.h"
#include "gfx_drvif.h"
#endif
LINT_EXT_HEADER_BEGIN
#define DEFINE_IS_LOG    VOMX_IsLog
#include "x_debug.h"
LINT_EXT_HEADER_END

static UCHAR _aucVomxLogOnOff[8];
#define HEIGHT_ALIGN_MINUS_ONE_DEFAULT  15

//*****************************************************************
// static functions
//*****************************************************************
#define VOMX_ERROR(x, ...)  printk("[vomx-err] "x" \n", ##__VA_ARGS__)
#define VOMX_INFO(x, ...)  printk("[vomx] "x" \n", ##__VA_ARGS__)
#define VOMX_DEBUG(x, ...)   \
            do {                           \
                if(_aucVomxLogOnOff[VDEXOMX_CLI_TYPE_DEBUG])                \
                    printk("[vomx-dbg] "x" \n", ##__VA_ARGS__);  \
            }while(0)

//use this when the log printed frequently
#define VOMX_VERBOSE(x, ...) \
            do {                           \
                if(_aucVomxLogOnOff[VDEXOMX_CLI_TYPE_VERBOSE])                \
                    printk("[vomx-verbose] "x" \n", ##__VA_ARGS__);  \
            }while(0)

//only for input related log print
#define VOMX_INPUT(x, ...) \
            do {                           \
                if(_aucVomxLogOnOff[VDEXOMX_CLI_TYPE_IN])                \
                    printk("[vomx-in] "x" \n", ##__VA_ARGS__);  \
            }while(0)

//only for output related log print
#define VOMX_OUTPUT(x, ...) \
            do {                           \
                if(_aucVomxLogOnOff[VDEXOMX_CLI_TYPE_OUT])                \
                    printk("[vomx-out] "x" \n", ##__VA_ARGS__);  \
            }while(0)

INT32 _vdecomx_log_ctrl (VDEXOMX_CLI_TYPE eCliType, UINT32 u4OnOff)
{
    switch(eCliType)
    {
        case VDEXOMX_CLI_TYPE_IN:
            VOMX_INFO("input log %s!", (u4OnOff==1) ? "on" : "off");
            break;
        case VDEXOMX_CLI_TYPE_OUT:
            VOMX_INFO("output log %s!", (u4OnOff==1) ? "on" : "off");
            break;
        case VDEXOMX_CLI_TYPE_VERBOSE:
            VOMX_INFO("verbose log %s!", (u4OnOff==1) ? "on" : "off");
            break;
        case VDEXOMX_CLI_TYPE_DEBUG:
            VOMX_INFO("debug log %s!", (u4OnOff==1) ? "on" : "off");
            break;
        case VDEXOMX_CLI_TYPE_FREEZE:
            VOMX_INFO("decoder freeze %s!", (u4OnOff==1) ? "on" : "off");
            break;
        default:
            break;
    }
    _aucVomxLogOnOff[eCliType] = (UCHAR)u4OnOff;
    return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
#define USE_UNLOCK_IOCTL
#endif

#define USR_SPACE_ACCESS_VALIDATE_ARG(arg, type)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(type)))              \
    {                                                       \
        VOMX_ERROR("vdecomx_mod argument error");     \
        return -1;                                          \
    }                                                       \

#define COPY_FROM_USER_ARG(ArgUsr, ArgKernel, type)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(type)))          \
    {                                                       \
        VOMX_ERROR("vdecomx_mod argument error");      \
        return -1;                                          \
    }

#define COPY_TO_USER_ARG(ArgUsr, ArgKernel, type)              \
    if (copy_to_user((void __user *)ArgUsr, &ArgKernel,   \
                       sizeof(type)))          \
    {                                                       \
        VOMX_ERROR("vdecomx_mod argument error");      \
        return -1;                                          \
    }

#define MAX_IMAGRZ_NUM (2)
#define INVALID_IMGRZ_ID (0xff)
#define MAX_VOMX_EVENT (512)

typedef struct _PARAMETER_VDECOMX_ALLOCATEBUFFER {           //OMX_ALLOCATEBUFFER
    OMX_ERRORTYPE               eReturnValue;
    OMX_HANDLETYPE              pHandle;
    HANDLE_T                    hPBufHeader;
    HANDLE_T                    hPBufHeaderBuf;
    OMX_U32                     nPortIndex;
    OMX_PTR                     pAppPrivate;
    OMX_U32                     nSizeBytes;
    HANDLE_T                    hPBufHeaderInternalBuf;
    OMX_BOOL                    fgUseBuf;                // use the assign address
} PARAMETER_VDECOMX_ALLOCATEBUFFER;

typedef struct _PARAMETER_VDECOMX_FREEBUFFER {           //OMX_FREEBUFFER
    OMX_ERRORTYPE               eReturnValue;
    OMX_HANDLETYPE              pHandle;
    OMX_U32                     nPortIndex;
    HANDLE_T                    hPBufHeader;
    HANDLE_T                    hPBufHeaderBuf;
} PARAMETER_VDECOMX_FREEBUFFER;

typedef struct _PARAMETER_VDECOMX_FILLBTAG {

  UINT32 nDecodeBufId;
  UINT32 nTag;
}PARAMETER_VDECOMX_FILLBTAG;


#define MAX_OUTPUT_BUFFER 32

typedef struct
{
    HANDLE_T  hMemHandle;
    UINT32 u4Address;
    UINT32 u4Size;
    UINT32 u4Tag;
}VDECOMX_KMEM_INFOR;

typedef struct
{
    UCHAR  ucImgrzId;
    UINT32 u4CVTmpAddrY;
    UINT32 u4CVTmpSizeY;
    UINT32 u4CVTmpAddrC;
    UINT32 u4CVTmpSizeC;
    UINT32 u4CVTmpAddrY2;
    UINT32 u4CVTmpAddrC2;
    UINT32 u4UserDstY;
    UINT32 u4UserDstC;
    BOOL    bEnabled;
    UINT32 u4OmxBufferHead;
    UINT32 u4SrcBufIndex;
    void *pExtra;
    UINT16 u2TGStride;
    UINT16 u2SliceHeight;
}CVT_RESOURCE_T;

// tunnel handle
typedef struct VDECOMX_TUNNEL_T {
    UINT32 u4VdecTunnelState;
    UINT32 u4AudioHwSync;
    UINT32 u4AoutPts;
    UINT8 u1AdecId;
    HANDLE_T hVideoReadySema;
} VDECOMX_TUNNEL_T;

typedef struct
{
    UINT32  u4BufAddr;
    UINT32  u4BufSize;
    ENUM_VDEC_FMT_T eFmt;
    VOID*   pVPush;
    UINT8   uVdecId;
    UINT8   u1VdpId;
    BOOL    fgPlay;
    BOOL    fgOccupied;
    BOOL    fgDecErr;
    BOOL    fgSecureInput;
    BOOL    fgTunnel; // tunneled video playback support
    BOOL    fgFBM4K;
    BOOL    fgPending;
    BOOL    fgCvtResReady;
    HANDLE_T hCvtMsgQ;
    HANDLE_T hEventMsgQ;
    HANDLE_T hCBSema;
    VDECOMX_MODE_T ePlayMode;
    VDECOMX_KMEM_INFOR rKmemInfor[MAX_OUTPUT_BUFFER];    
    CVT_RESOURCE_T rImageConvert[MAX_IMAGRZ_NUM];
    FBM_POOL_T* prFbmPool;
    VDECOMX_TUNNEL_T rTunnel;
    UINT32 u4TmpBufStart;
    UINT32 u4TmpBufferSize;
} VDECOMX_HANDLE_T;

typedef struct _VDECOMX_BUFFER_T {
    VDECOMX_HANDLE_T    *pOwner;
    HANDLE_T             hBuffer; // handle for below buffer
    void                *pBufSA;
    UINT32               u4BufSize; // althrough buffer size is included in below header, keep this for error detection
    HANDLE_T             hHeader; // handle for below structure
    UINT32               u4FilledLen; // used on empty, keep this althrough duplicated
    UINT32               u4Offset; // used on empty, keep this althrough duplicated
    OMX_BUFFERHEADERTYPE rHeader; // client will read/write this area
    OMX_TICKS            nTimeStamp;
    UINT32               u4Reserved; // for error detection
}VDECOMX_BUFFER_T;

typedef struct
{
    UINT32 u4YAddr;
    UINT32 u4CAddr;
    UINT32 u4HOffset;
    UINT32 u4VOffset;
    UINT32 u4HSize;
    UINT32 u4VSize;
    UINT32 u4LineSize;
    UINT32 u4LenYAddr;
    UINT32 u4LenCAddr;
    UCHAR  ucRaster;
    UCHAR  ucBlockType;
    UCHAR  ucUfoEnable;
    UCHAR  ucUfoType;
}IMG_DISCRIPTION_T;

typedef struct
{
    UCHAR  ucImgRzID;
    UCHAR  ucAsync;
    UCHAR  ucDoSwap;
    CVT_RESOURCE_T* prCvtRes;
}IMG_EXTRA_CVT_PARAM_T;

//---------------------------------------------------------------------------
// Public functions
//---------------------------------------------------------------------------
#define VOMX_INVALID_VALUE ((UINT32)-1)
static BOOL _fgInited = FALSE;
static int _u4VdecOmxOpenCnt = 0;
static int _u4VdecOmxLogValue = 0;
static VDECOMX_BUFFER_T _prVDOOmxInputBuf[VDECOMX_INPUT_BUF_MAX];
VDECOMX_HANDLE_T _arVdecOmx[VDECOMX_MAX_DECODER];
//    HANDLE_T _hVecOmxSema = NULL;

EXTERN UINT32 FBM_GetPanelModel(VOID);

/*****************************************************************
 * Tunneled Video Playback support
 * 1. Due to tight schedule, we don't consider multi-instance.
 * And we hardcode VDP0 & STC0.
 * But we may use u4AudioHwSync as instance id later.
 * 2. All prTunnel->u4VdecTunnelState RW MUST be protected by
 * _vdecomx_tunnel_lockSema() / _vdecomx_tunnel_unlockSema()
 *****************************************************************/

//*****************************************************************
// MACROs
//*****************************************************************


//*****************************************************************
// data structure & enumeration
//*****************************************************************

// tunnel status
typedef enum {
    VDEXOMX_TUNNEL_STATE_INITED = 1 << 0,
    VDEXOMX_TUNNEL_STATE_PLAYED = 1 << 1,
    VDEXOMX_TUNNEL_STATE_AUD_CB = 1 << 2,
    VDEXOMX_TUNNEL_STATE_FLUSHED = 1 << 3,
    // VDEXOMX_TUNNEL_STATE_VDP_ON = 1 << 3, obsolete
} VDEXOMX_TUNNEL_STATE;

//*****************************************************************
// glogbal functions
//*****************************************************************

// audio callback for tunneled mode
BOOL _vdecomx_tunnelAudioCb(UINT32 u4AudioHwSync,
#ifdef  CC_AUD_SUPPORT_NEW_TUNNEL_MODE
    AUDIO_NOTIFY_TYPE_T eAudioNotify, UINT32 u4AoutPts)
#else
    AUDIO_NOTIFY_TYPE_T eAudioNotify)
#endif    
{
    BOOL fgRet = TRUE;
    VDECOMX_TUNNEL_T *prTunnel = NULL;
    VDECOMX_HANDLE_T* prVdecOmx = NULL;
    UINT32 i;
    UCHAR ucCount = 100;

    while(ucCount--)
    {
        for (i = 0; i < VDECOMX_MAX_DECODER; i++)
        {
            if (_arVdecOmx[i].fgOccupied && _arVdecOmx[i].fgTunnel && (_arVdecOmx[i].rTunnel.u4AudioHwSync == u4AudioHwSync))
            {
                prVdecOmx = &_arVdecOmx[i];
                prTunnel = &prVdecOmx->rTunnel;
                break;
            }
        }

        if(VDECOMX_MAX_DECODER == i || !prTunnel || !prVdecOmx)
        {
            VOMX_ERROR("%s: video not ready", __FUNCTION__);
            x_thread_delay(50);
            continue;
        }
        else
        {
            if(!(prTunnel->u4VdecTunnelState & VDEXOMX_TUNNEL_STATE_AUD_CB)
                && (OSR_OK != x_sema_lock_timeout(prTunnel->hVideoReadySema, 1000)))
            {
                VOMX_ERROR("%s: lock hVideoReadySema timeout", __FUNCTION__);
                return FALSE;
            }
            break;
        }
    }

    if (!prVdecOmx)
    {
        for (i = 0; i < VDECOMX_MAX_DECODER; i++)
        {
            VOMX_ERROR("video[%d].u4AudioHwSync = %d, audio.u4AudioHwSync = %d",
                        i, _arVdecOmx[i].rTunnel.u4AudioHwSync, u4AudioHwSync);
        }
        VOMX_ERROR("%s: video not ready, prVdecOmx NULL", __FUNCTION__);
        return FALSE;
    }
#ifdef  CC_AUD_SUPPORT_NEW_TUNNEL_MODE
    prTunnel->u4AoutPts = u4AoutPts;
#endif
    VOMX_DEBUG("%s(%d,%d) state=%d", __FUNCTION__, u4AudioHwSync, eAudioNotify, prTunnel->u4VdecTunnelState);

    if (!prTunnel->u4AudioHwSync)
    {
        VOMX_ERROR("%s: wrong state. video only!!!", __FUNCTION__);
        return FALSE;
    }

    if (eAudioNotify != AUDIO_NOTIFY_DECODE_DONE)
    {
        VOMX_ERROR("%s: unexpected notify(%d)", __FUNCTION__, eAudioNotify);
        return FALSE;
    }

    if (prTunnel->u4VdecTunnelState & VDEXOMX_TUNNEL_STATE_AUD_CB)
    {
        VOMX_INFO("%s(%d): already cb, u4VdecTunnelState 0x%08x",
            __FUNCTION__, __LINE__, prTunnel->u4VdecTunnelState);
    }
    else
    {
        prTunnel->u4VdecTunnelState |= VDEXOMX_TUNNEL_STATE_AUD_CB;
        prTunnel->u4VdecTunnelState &= ~VDEXOMX_TUNNEL_STATE_FLUSHED;
        if (prTunnel->u4VdecTunnelState & VDEXOMX_TUNNEL_STATE_PLAYED)
        {
            prTunnel->u4VdecTunnelState &= ~VDEXOMX_TUNNEL_STATE_PLAYED;
        }

        VDEC_SetRenderFromPts(prVdecOmx->uVdecId, prTunnel->u4AoutPts);
        VOMX_INFO("play vdec when audio cb!");
        if (!_VPUSH_PlayVdec(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s: _VPUSH_Play fail", __FUNCTION__);
            fgRet = FALSE;
        }
    }

    // Issue: AP may not destroy sideband layer but delete decoder
    // when play->exit->play which cause _vdecomx_tunnel_VdpOn is not called.
    // So AUD_MMAoutEnable is not called and no audio out.
    // Solution: Always call VDP_TriggerAudReceive in cb.
    // Confirm with B2R owner that it's ok to call VDP_TriggerAudReceive
    // before vdp connection.
    VOMX_INFO("%s call VDP_TriggerAudReceive vdpid:%d pts:0x%x", __FUNCTION__, prVdecOmx->u1VdpId, prTunnel->u4AoutPts);
    VDP_TriggerAudReceive(prVdecOmx->u1VdpId, prTunnel->u4AoutPts);

    return fgRet;
}


// For B2R to trigger aout
void _vdecomx_tunnel_B2rTrigAudCb(UINT32 u4VdpId, UINT64 u4Data, UINT32 u4FuncPtr, UINT64 u4Tag)
{
    VDECOMX_HANDLE_T* prVdecOmx = (VDECOMX_HANDLE_T*)((UINT32)(u4Tag));
    VOMX_INFO("vdp(%d) trigger aout", u4VdpId);
    AUD_MMAoutEnable(prVdecOmx->rTunnel.u1AdecId, TRUE);
}

void vdecomx_put_event(VDECOMX_HANDLE_T* prVdecOmx, VDECOMX_NFY_T* prOmxEvent)
{
    VERIFY(OSR_OK == x_msg_q_send(prVdecOmx->hEventMsgQ, (void *)(prOmxEvent), sizeof(VDECOMX_NFY_T), 255));
}

INT32 vdecomx_get_event(VDECOMX_HANDLE_T* prVdecOmx, unsigned long arg)
{
    UINT16 u2MsgQIdx;
    SIZE_T zMsgSize = sizeof(VDECOMX_NFY_T);
    VDECOMX_NFY_T rOmxEvent;
    PAR_VDECOMX_GETEVENT rGetEventParam;
    if(OSR_OK == x_msg_q_receive_timeout_interruptible(&u2MsgQIdx, (void*)(&rOmxEvent), &zMsgSize, &(prVdecOmx->hEventMsgQ), 1, 1234))
    {
        USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_GETEVENT);
        COPY_FROM_USER_ARG(arg, rGetEventParam, PAR_VDECOMX_GETEVENT);
        USR_SPACE_ACCESS_VALIDATE_ARG(rGetEventParam.prOutOmxEvent, VDECOMX_NFY_T);
        COPY_TO_USER_ARG(rGetEventParam.prOutOmxEvent, rOmxEvent, VDECOMX_NFY_T);
        rGetEventParam.eRet = VDECOMX_MRESULT_OK;
        if(VDECOMX_NFY_TYPE_LAST_EVENT == rOmxEvent.eType)
        {
           VERIFY(OSR_OK == x_sema_unlock(prVdecOmx->hCBSema));
        }
        return 0;
    }
    else
    {
        rGetEventParam.eRet = VDECOMX_MRESULT_FAIL;
        return -1;
    }

}

static BOOL _vdecomx_decode_done(UINT32 u4Tag, UINT32 u4FbgId, UINT32 u4FbId, BOOL fgEos)
{
    VDECOMX_HANDLE_T *prVdecOmx = (VDECOMX_HANDLE_T *)u4Tag;
    VDECOMX_NFY_T tNfy;
    UCHAR ucLockFbId;
    FBM_PIC_HDR_T* prFbmPicHdr = NULL;

    if (!prVdecOmx)
    {
        VOMX_ERROR("%s WTF", __FUNCTION__);
        return TRUE;
    }

    if(prVdecOmx->ePlayMode != VDECOMX_MODE_TUNNEL)
    {
        ucLockFbId=FBM_GetDisplayFB(u4FbgId);
    }

    prFbmPicHdr = FBM_GetFrameBufferPicHdr(u4FbgId, u4FbId);
    if (prFbmPicHdr)
    {
        VOMX_OUTPUT("idx[%d, %d], size[%d x %d], pts:%lluus%s",u4FbgId,u4FbId, prFbmPicHdr->u4PicWidth, prFbmPicHdr->u4PicHeight, prFbmPicHdr->u8PTSInUs, (fgEos ? ", eos!" : ""));
    }

    if(prVdecOmx->ePlayMode != VDECOMX_MODE_TUNNEL)
    {
        ASSERT(ucLockFbId == u4FbId);
    }

    tNfy.u8Data1=(UINT64)(u4FbgId<<16)+u4FbId;
    tNfy.eType = VDECOMX_NFY_TYPE_FRAME_DECODED;
    vdecomx_put_event(prVdecOmx, &tNfy);

    while(_aucVomxLogOnOff[VDEXOMX_CLI_TYPE_FREEZE])
    {
        x_thread_delay(1000);
    }

    return TRUE;
}

static void _vdecomx_frame_rendered(UINT32 u4VdpId, UINT64 u8Arg1, UINT32 u4Arg2, UINT64 u8Arg3)
{
    VDECOMX_NFY_T tNfy;
    VDECOMX_HANDLE_T* prVdecOmx = (VDECOMX_HANDLE_T*)u4Arg2;
    x_memset(&tNfy, 0, sizeof(tNfy));
    tNfy.u8Data1=u8Arg1; //us
    tNfy.u8Data2=u8Arg3*1000; //ns
    tNfy.eType = VDECOMX_NFY_TYPE_FRAME_RENDERED;
    VOMX_VERBOSE("Frame rendered [%llu us, %llu ns]\n", tNfy.u8Data1, tNfy.u8Data2);
    vdecomx_put_event(prVdecOmx, &tNfy);
}

static BOOL _vdecomx_hdr10info_chg(UINT32 u4Tag)
{
    VDECOMX_NFY_T tNfy;
    VDECOMX_HANDLE_T* prVdecOmx = (VDECOMX_HANDLE_T*)u4Tag;
    x_memset(&tNfy, 0, sizeof(tNfy));
    tNfy.eType = VDECOMX_NFY_TYPE_HDR10INFO_CHG;
    VOMX_INFO("HDR10 info changed\n");
    vdecomx_put_event(prVdecOmx, &tNfy);
    return TRUE;
}

static HANDLE_T _vdecomx_create_kmem_handle(VDECOMX_HANDLE_T *prVdecOmx, UINT32 u4PAddr, UINT32 u4Size, UINT32 u4Tag)
{
    UINT32 u4Index;
    UINT32 u4FreeIndex = VOMX_INVALID_VALUE;
    HANDLE_T hKMemHandle = NULL_HANDLE;
    for(u4Index=0;u4Index<MAX_OUTPUT_BUFFER;u4Index++)
    {
        if(prVdecOmx->rKmemInfor[u4Index].hMemHandle == VOMX_INVALID_VALUE)
        {
            if(VOMX_INVALID_VALUE == u4FreeIndex)
                u4FreeIndex = u4Index;
        }
        else
        {
            if((u4PAddr == prVdecOmx->rKmemInfor[u4Index].u4Address) && (u4Size <= prVdecOmx->rKmemInfor[u4Index].u4Size))
            {
                ASSERT(u4Tag == prVdecOmx->rKmemInfor[u4Index].u4Tag);
                return prVdecOmx->rKmemInfor[u4Index].hMemHandle;
            }
        }
    }
    
    {
#ifdef CC_VDEC_FBM_DYNAMIC_MAPPING
        UINT32 u4DynMapAddr = 0;
        u4DynMapAddr = (UINT32)BSP_MapReservedMem((VOID *)u4PAddr, u4Size);
        ASSERT(u4DynMapAddr);
        if(u4DynMapAddr != VIRTUAL(u4PAddr))
        {
            VOMX_ERROR("Warning!! %p VS %p", u4DynMapAddr, VIRTUAL(u4PAddr));
        }
#endif
        if (VOMX_INVALID_VALUE == u4FreeIndex)
        {
            return NULL_HANDLE;
        }
        hKMemHandle = x_kmem_create_handle((void *)VIRTUAL(u4PAddr), u4Size);
        if(NULL_HANDLE == hKMemHandle)
        {
            return NULL_HANDLE;
        }
        prVdecOmx->rKmemInfor[u4FreeIndex].hMemHandle = hKMemHandle;
        prVdecOmx->rKmemInfor[u4FreeIndex].u4Address = u4PAddr;
        prVdecOmx->rKmemInfor[u4FreeIndex].u4Size = u4Size;        
        prVdecOmx->rKmemInfor[u4FreeIndex].u4Tag = u4Tag;
    }
    return hKMemHandle;
}


static void _vdecomx_free_kmem_handle_by_tag(VDECOMX_HANDLE_T *prVdecOmx, UINT32 u4Tag)
{
    UINT32 u4Index;
    for(u4Index=0;u4Index<MAX_OUTPUT_BUFFER;u4Index++)
    {
        if(MAX_OUTPUT_BUFFER != prVdecOmx->rKmemInfor[u4Index].u4Tag)
        {
            if(u4Tag == prVdecOmx->rKmemInfor[u4Index].u4Tag || VOMX_INVALID_VALUE == u4Tag)
            {
                if(VOMX_INVALID_VALUE != prVdecOmx->rKmemInfor[u4Index].hMemHandle)
                {
                    VOMX_DEBUG("destroy handle:0x%x", prVdecOmx->rKmemInfor[u4Index].hMemHandle);
                    x_kmem_destroy_handle(prVdecOmx->rKmemInfor[u4Index].hMemHandle);  
#ifdef CC_VDEC_FBM_DYNAMIC_MAPPING
                    BSP_UnMapReservedMem((VOID *)VIRTUAL(prVdecOmx->rKmemInfor[u4Index].u4Address));
#endif
                    prVdecOmx->rKmemInfor[u4Index].hMemHandle = VOMX_INVALID_VALUE;
                    prVdecOmx->rKmemInfor[u4Index].u4Address = 0;
                    prVdecOmx->rKmemInfor[u4Index].u4Size = 0;
                    prVdecOmx->rKmemInfor[u4Index].u4Tag = VOMX_INVALID_VALUE;
                }
            }
        }
    }
}

static BOOL _vdecomx_error_callback(UINT32 u4Tag, VOID* pvData)
{
    VDECOMX_HANDLE_T *prVdecOmx = (VDECOMX_HANDLE_T *)u4Tag;
    UINT32 u4ErrType = *((UINT32*)pvData);
    VDECOMX_NFY_T tNfy;

    VOMX_INFO("_vdecomx_error_callback, prVdecOmx 0x%x, errType %d", prVdecOmx, u4ErrType);

    switch (u4ErrType)
    {
        case VDEC_DEC_DECODE_FORCE_STOP:
            {
                tNfy.eType = VDECOMX_NFY_TYPE_FORCE_STOP;
            }
            break;
        case VDEC_DEC_DECODE_CODEC_NOT_SUPPORT:
        case VDEC_DEC_DECODE_HD_NOT_SUPPORT:
        case VDEC_DEC_DECODE_ERROR:
        case VDEC_DEC_DECODE_RES_NOT_SUPPORT:
            {
                tNfy.eType = VDECOMX_NFY_TYPE_DECODER_ERROR;
                prVdecOmx->fgDecErr = TRUE;
            }
            break;
        case VDEC_DEC_DECODE_RESOURCE_CONFLICT:
            {
                tNfy.eType = VDECOMX_NFY_TYPE_INSUFFICIENT_RESOURCE;
            }
            break;
        default:
            VOMX_INFO("erro ignored %d", u4ErrType);
            return TRUE;
    }
    vdecomx_put_event(prVdecOmx, &tNfy);
    return TRUE;
}

static void _vdecomx_buffer_init(void)
{
    UINT32 u4Index;

    for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index ++)
    {

        _prVDOOmxInputBuf[u4Index].pOwner     = NULL;
        _prVDOOmxInputBuf[u4Index].hBuffer    = NULL_HANDLE;
        _prVDOOmxInputBuf[u4Index].pBufSA     = NULL;
        _prVDOOmxInputBuf[u4Index].u4BufSize  = 0;
        _prVDOOmxInputBuf[u4Index].hHeader    = NULL_HANDLE;
        _prVDOOmxInputBuf[u4Index].u4Reserved = 0x5A5A5A5A;
    }
}


static void _vdecomx_buffer_uninit(void)
{
    UINT32 u4Index;

    for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index ++)
    {
        if (NULL_HANDLE != _prVDOOmxInputBuf[u4Index].hHeader)
        {
            x_kmem_destroy_handle(_prVDOOmxInputBuf[u4Index].hHeader);
            _prVDOOmxInputBuf[u4Index].hHeader=NULL_HANDLE;
        }
    }
}

static int _vdecomx_alloc_input_buffer(VDECOMX_HANDLE_T *prVdecOmx, UINT32 u4Size, VDECOMX_BUFFER_T **ppBufInfo)
{
    // allocate input buffer from FEEDER
    UINT32 u4Index;
    void *pBufSA  = NULL;

    HANDLE_T hMemHandle = NULL_HANDLE;
    VDECOMX_BUFFER_T *pAvailBuf = NULL;

    VDECOMX_HANDLE_T *prVDOOmxHandle = prVdecOmx;

    // STEP 0: get a free input buffer info node
    for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index ++)
    {
        if (NULL == _prVDOOmxInputBuf[u4Index].pOwner)
        {
            pAvailBuf = &(_prVDOOmxInputBuf[u4Index]);
            break;
        }
    }

    if (NULL == pAvailBuf)
    {
        // no more free nodes
        VOMX_ERROR("_vdecomx_alloc_input_buffer, not find node");
        goto lblErrorReturn;
    }
    else
    {
        // initialize
        pAvailBuf->pOwner    = prVDOOmxHandle;
        pAvailBuf->hBuffer   = NULL_HANDLE;
        pAvailBuf->pBufSA    = NULL;
        pAvailBuf->u4BufSize = 0;
    }

    if(!_VPUSH_AllocVFifo(prVDOOmxHandle->pVPush, (UINT32*)&pBufSA, u4Size, prVdecOmx->eFmt))
    {
        VOMX_ERROR("[VDECOMX] alloc VFIFO memory return failed, %d bytes", u4Size);
        goto lblErrorReturn;
    }

    if(pBufSA==NULL)
    {
        VOMX_ERROR("alloc u4SecureBufferAlloc memory return failed, %d bytes", u4Size);
        goto lblErrorReturn;
    }

    if(prVdecOmx->fgSecureInput)
    {
        pAvailBuf->hBuffer = (HANDLE_T)pBufSA;
    }
    else
    {
        hMemHandle = x_kmem_create_handle(pBufSA, u4Size);
        if (NULL_HANDLE == hMemHandle)
        {
            goto lblErrorReturn;
        }
        else
        {
            pAvailBuf->hBuffer = hMemHandle;
        }
    }

    pAvailBuf->pBufSA    = pBufSA;
    pAvailBuf->u4BufSize = u4Size;
    prVdecOmx->u4BufAddr = (UINT32)pBufSA;
    prVdecOmx->u4BufSize = u4Size;
    *ppBufInfo = pAvailBuf;
    return 0;

    // error handling
lblErrorReturn:

    // resource release will be done on freeing component handle, not here
    *ppBufInfo = NULL;
    return -1;
}


static int _vdecomx_free_input_buffer(VDECOMX_HANDLE_T *prVdecOmx, HANDLE_T hHeader)
{
    UINT32 u4Index;
    VDECOMX_BUFFER_T *pBufNode = NULL;

    if ((NULL == prVdecOmx) || (NULL_HANDLE == hHeader))
    {
        VOMX_ERROR("_vdecomx_free_input_buffer, handle is null\n");
        return -1;
    }

    for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index ++)
    {
        if (hHeader == _prVDOOmxInputBuf[u4Index].hBuffer)
        {
            pBufNode = &(_prVDOOmxInputBuf[u4Index]);
            break;
        }
    }

    if (NULL != pBufNode)
    {
        if ((pBufNode->pOwner == prVdecOmx) && (NULL != pBufNode->pBufSA))
        {
            VOMX_DEBUG("free input buf addr:0x%xx\n", pBufNode->pBufSA);
            _VPUSH_FreeVFifo(prVdecOmx->pVPush, (UINT32)pBufNode->pBufSA);
            if(!prVdecOmx->fgSecureInput)
            {
                x_kmem_destroy_handle(pBufNode->hBuffer);
            }
            // clear info
            pBufNode->pOwner    = NULL;
            pBufNode->hBuffer   = NULL_HANDLE;
            pBufNode->pBufSA    = NULL;
            pBufNode->u4BufSize = 0;
            pBufNode->hHeader   = NULL_HANDLE;
        }
    }

    return 0;
}


void vdecomx_cpy_img_to_user(CVT_RESOURCE_T* prCvtDone)
{
    HalFlushInvalidateDCacheMultipleLine(((VIRTUAL)(prCvtDone->u4CVTmpAddrY)), prCvtDone->u4CVTmpSizeY);
    HalFlushInvalidateDCacheMultipleLine(((VIRTUAL)(prCvtDone->u4CVTmpAddrC)), prCvtDone->u4CVTmpSizeC);
    if (copy_to_user((void __user *)prCvtDone->u4UserDstY, (void*)(VIRTUAL(prCvtDone->u4CVTmpAddrY)), prCvtDone->u4CVTmpSizeY))
    {
        VOMX_ERROR("%s vdecomx_mod argument error %d\n",__FUNCTION__,__LINE__);
        return;
    }
    
    if (copy_to_user((void __user *)(prCvtDone->u4UserDstC), (void*)(VIRTUAL(prCvtDone->u4CVTmpAddrC)), prCvtDone->u4CVTmpSizeC))
    {
        VOMX_ERROR("%s vdecomx_mod argument error %d\n",__FUNCTION__,__LINE__);
        return;
    }
    return;
}

void vdecomx_config_imgrz(VDECOMX_HANDLE_T *prVdecOmx, UINT32 u4ImgrzIdMask)
{
    INT32 i = 0;
    INT32 i4ImgrzId = 0;
    
    for (i=0; i < MAX_IMAGRZ_NUM; ++i)
    {
        x_memset(&prVdecOmx->rImageConvert[i], 0, sizeof(CVT_RESOURCE_T));
        prVdecOmx->rImageConvert[i].ucImgrzId = INVALID_IMGRZ_ID;
        prVdecOmx->rImageConvert[i].pExtra = (void*)prVdecOmx;
    }

    i = 0;
    while(u4ImgrzIdMask)
    {
        if(u4ImgrzIdMask & 0x1)
        {
            prVdecOmx->rImageConvert[i++].ucImgrzId = i4ImgrzId;
            if(i == MAX_IMAGRZ_NUM)
                break;
        }
        u4ImgrzIdMask = u4ImgrzIdMask >> 1;
        i4ImgrzId++;
        if(i4ImgrzId > 2)
            break;
    }
    
    VOMX_INFO("imgrz set to [0x%x, 0x%x]\n", prVdecOmx->rImageConvert[0].ucImgrzId, prVdecOmx->rImageConvert[1].ucImgrzId);
}

static int _vdecomx_imgrz_done(unsigned int u4Which, IMGRZ_DONE_TYPE u4Type, void *tag)
{

    CVT_RESOURCE_T* prCvtRes = (CVT_RESOURCE_T*)tag;
    if (prCvtRes)
    {
        VDECOMX_NFY_T tNfy;
        tNfy.u8Data1 = (UINT64)prCvtRes->u4OmxBufferHead;
        tNfy.u8Data2 = (UINT64)prCvtRes->u4SrcBufIndex;
        tNfy.eType = VDECOMX_NFY_TYPE_ASYNC_CVT;
        vdecomx_put_event((VDECOMX_HANDLE_T*)prCvtRes->pExtra, &tNfy);
    }

    return 0;
}

BOOL vdecomx_do_image_convert(IMG_DISCRIPTION_T* pSrcImg, IMG_DISCRIPTION_T* pDstImg, IMG_EXTRA_CVT_PARAM_T* pCvtParam)
{
    UCHAR ucRzId = pCvtParam->ucImgRzID;     //Fixme Find a way to manager rzID
    RZ_VDO_SCL_PARAM_SET_T rImgrzScaleParam;    

    x_memset(&rImgrzScaleParam,0,sizeof(RZ_VDO_SCL_PARAM_SET_T));

    if (pSrcImg->ucUfoEnable)
    {
        rImgrzScaleParam.u4UfoEn = TRUE;
        rImgrzScaleParam.eUfoType = pSrcImg->ucUfoType;
        rImgrzScaleParam.u4UfoYBufSZSA = pSrcImg->u4LenYAddr;
        rImgrzScaleParam.u4UfoCBufSZSA = pSrcImg->u4LenCAddr;
    }

    rImgrzScaleParam.u4SrcSwap = pSrcImg->ucBlockType ? 0 : 6;//0 for 16x32, 6 for 64x32
    rImgrzScaleParam.u4OutSwap = 0;
    rImgrzScaleParam.u4InMode = E_RZ_VDO_OUTMD_420;
    rImgrzScaleParam.u4IsRsIn = pSrcImg->ucRaster;
    rImgrzScaleParam.u4YSrcBase = pSrcImg->u4YAddr;
    rImgrzScaleParam.u4YSrcBufLen = pSrcImg->u4LineSize;
    rImgrzScaleParam.u4YSrcHOffset = pSrcImg->u4HOffset;
    rImgrzScaleParam.u4YSrcVOffset = pSrcImg->u4VOffset;
    rImgrzScaleParam.u4YSrcW = pSrcImg->u4HSize;
    rImgrzScaleParam.u4YSrcH = pSrcImg->u4VSize;
    rImgrzScaleParam.u4CSrcBase = pSrcImg->u4CAddr;
    rImgrzScaleParam.u4CSrcHOffset = pSrcImg->u4HOffset;
    rImgrzScaleParam.u4CSrcVOffset = pSrcImg->u4VOffset;
    rImgrzScaleParam.u4CSrcW = rImgrzScaleParam.u4YSrcW / 2;
    rImgrzScaleParam.u4CSrcH = rImgrzScaleParam.u4YSrcH / 2;
    rImgrzScaleParam.u4IsRsOut = pDstImg->ucRaster;
    rImgrzScaleParam.u4OutMode = E_RZ_VDO_OUTMD_420;
    rImgrzScaleParam.u4IsVdo2Osd = 0;
    rImgrzScaleParam.u4VdoCbCrSwap = 0; //0 for NV12, 1 for NV21
    rImgrzScaleParam.u4YTgBase = pDstImg->u4YAddr;
    rImgrzScaleParam.u4YTgCM = E_RZ_VDO_OUTMD_420;
    rImgrzScaleParam.u4YTgBufLen = pDstImg->u4LineSize;
    rImgrzScaleParam.u4YTgHOffset = 0;
    rImgrzScaleParam.u4YTgVOffset = 0;
    rImgrzScaleParam.u4YTgW = pDstImg->u4HSize;
    rImgrzScaleParam.u4YTgH = pDstImg->u4VSize;
    rImgrzScaleParam.u4CTgBase = pDstImg->u4CAddr;
    rImgrzScaleParam.u4CTgW = rImgrzScaleParam.u4YTgW / 2;
    rImgrzScaleParam.u4CTgH = rImgrzScaleParam.u4YTgH / 2;

    IMGRZ_Lock_Ex((UINT32)ucRzId);
    IMGRZ_ReInit_Ex((UINT32)ucRzId);
    IMGRZ_SetScaleOpt_Ex((UINT32)ucRzId, E_RZ_VDO_OUTMD_420);
    if (pCvtParam->ucAsync)
    {
        IMGRZ_Async_Ex(ucRzId, _vdecomx_imgrz_done, (void *)pCvtParam->prCvtRes); 
    }
    IMGRZ_Scale_Ex((UINT32)ucRzId, (void *)(&rImgrzScaleParam));
    IMGRZ_Flush_Ex((UINT32)ucRzId);
    IMGRZ_Wait_Ex((UINT32)ucRzId);
    IMGRZ_Unlock_Ex((UINT32)ucRzId);
    return 0;
}

INT32 vdec_generate_srcImage(VDECOMX_HANDLE_T* prVdecOmx, VDECOMX_SET_COLOR_CVT_T* eDrvColorCvtParam, IMG_DISCRIPTION_T* outImage)
{
    UCHAR ucFbgId;
    UCHAR ucFbId;
    FBM_SEQ_HDR_T* prFbmSeqHdr = NULL;
    FBM_PIC_HDR_T* prFbmPicHdr = NULL;
    UINT32 u4BufIndex=(UINT32)eDrvColorCvtParam->u4SrcBufIndex;

    ucFbgId = (u4BufIndex>>16)&0xff;
    ucFbId = u4BufIndex&0xff;
    FBM_GetFrameBufferAddr(ucFbgId, ucFbId, &(outImage->u4YAddr), &(outImage->u4CAddr));
    prFbmPicHdr = FBM_GetFrameBufferPicHdr(ucFbgId, ucFbId);
    prFbmSeqHdr = FBM_GetFrameBufferSeqHdr(ucFbgId);
    if(NULL == prFbmPicHdr || NULL == prFbmSeqHdr)
    {
        VOMX_ERROR("%s(%d) convert to nv12 failed, fbg or fb not exist\n",__FUNCTION__,__LINE__);
        return -1;
    }
    
    outImage->u4LineSize = prFbmSeqHdr->u2LineSize;
    outImage->u4HSize = prFbmSeqHdr->u2HSize;
    outImage->u4VSize = prFbmSeqHdr->u2VSize;
    outImage->ucBlockType = prFbmSeqHdr->ucBlockType;
    outImage->ucRaster = eDrvColorCvtParam->fgRasterIn;
    outImage->u4HOffset= 0;
    outImage->u4VOffset= 0;

    if(prFbmPicHdr->u4PicWidth && prFbmPicHdr->u4PicHeight && prFbmPicHdr->u4PicWidthPitch)
    {
        //all codec output is 64 align now
        outImage->u4LineSize = (prFbmPicHdr->u4PicWidth + 63) & (~63);//prFbmPicHdr->u4PicWidthPitch;
        outImage->u4HSize  = prFbmPicHdr->u4PicWidth;
        outImage->u4VSize = prFbmPicHdr->u4PicHeight;
    }


    VOMX_VERBOSE("L:%d H:%d V%d\n",outImage->u4LineSize, outImage->u4HSize, outImage->u4VSize);
    //error handle for sometime seq/pic header not set
    if((0==outImage->u4HSize) || (0==outImage->u4VSize) || (0==outImage->u4LineSize))
    {
        UINT32 u4FbWidth;
        UINT32 u4FbHeight;
        FBM_GetFrameBufferSize(ucFbgId, &u4FbWidth, &u4FbHeight);
        outImage->u4LineSize = u4FbWidth;
        outImage->u4HSize = u4FbWidth;
        outImage->u4VSize = u4FbHeight;
    }
    outImage->u4VSize = (outImage->u4VSize + HEIGHT_ALIGN_MINUS_ONE_DEFAULT)&(~HEIGHT_ALIGN_MINUS_ONE_DEFAULT);

  //Preprocess for UFO and 10 pixel case
    if((prFbmSeqHdr->fgUfoEn) || (10 == prFbmSeqHdr->u1Depth))
    {
         UINT32 u4UfoAddrLenY;
         UINT32 u4UfoAddrLenC;
         FBM_GetFrameBufferAddrExt(ucFbgId, ucFbId, &u4UfoAddrLenY,&u4UfoAddrLenC);
         outImage->ucUfoEnable = TRUE;
         outImage->ucUfoType = (10 == prFbmSeqHdr->u1Depth) ? RZ_UFO_TYPE_10BIT_COMPACT : RZ_UFO_TYPE_8BIT;
         outImage->u4LenYAddr = u4UfoAddrLenY;
         outImage->u4LenCAddr = u4UfoAddrLenC;
    }
    
    if(!prVdecOmx->fgCvtResReady)
    {
        UCHAR ucIdx;
        UINT32 u4TmpBufStart = 0;
        UINT32 u4TmpBufEnd = 0;
        UINT32 u4BufferSize = 0;
        UINT32 u4BufPtr = 0;
        UCHAR ucReadyCnt = 0;
        UINT32 u4DynMapAddr = 0;
        CHAR szBuf[64];
        CHAR pBuf[64];
        CHAR *pName;
        
        if(prVdecOmx->fgFBM4K)
        {
            prVdecOmx->prFbmPool = FBM_Alloc(FBM_TYPE_MPEG3);
            if (prVdecOmx->prFbmPool)
            {
                u4TmpBufStart = prVdecOmx->prFbmPool->u4Addr;
                u4BufferSize = prVdecOmx->prFbmPool->u4Size;
            }
        }
        else
        {
            _GetSeamlessBuffer(&u4TmpBufStart, &u4BufferSize);
        }

        if((0 == u4BufferSize) || (0 == u4TmpBufStart))
        {
            VOMX_ERROR("Tmp Buf Addr Error, addr:0x%x, size:0x%x\n", u4TmpBufStart, u4BufferSize);
            return -1;
        }
        prVdecOmx->u4TmpBufStart = u4TmpBufStart;
        prVdecOmx->u4TmpBufferSize = u4BufferSize;
        u4TmpBufEnd = u4TmpBufStart + u4BufferSize;
        u4BufPtr = u4TmpBufStart;
        for (ucIdx=0; ucIdx < MAX_IMAGRZ_NUM; ++ucIdx)
        {
            prVdecOmx->rImageConvert[ucIdx].bEnabled = FALSE;
            if(INVALID_IMGRZ_ID != prVdecOmx->rImageConvert[ucIdx].ucImgrzId)
            {
                prVdecOmx->rImageConvert[ucIdx].u4CVTmpAddrY = u4BufPtr;
                prVdecOmx->rImageConvert[ucIdx].u4CVTmpSizeY = eDrvColorCvtParam->u2TGStride * eDrvColorCvtParam->u2SliceHeight;
                prVdecOmx->rImageConvert[ucIdx].u4CVTmpAddrC = u4BufPtr + prVdecOmx->rImageConvert[ucIdx].u4CVTmpSizeY;
                prVdecOmx->rImageConvert[ucIdx].u4CVTmpSizeC = prVdecOmx->rImageConvert[ucIdx].u4CVTmpSizeY / 2;
                prVdecOmx->rImageConvert[ucIdx].u2TGStride = eDrvColorCvtParam->u2TGStride;
                prVdecOmx->rImageConvert[ucIdx].u2SliceHeight = eDrvColorCvtParam->u2SliceHeight;
                u4BufPtr = prVdecOmx->rImageConvert[ucIdx].u4CVTmpAddrC + prVdecOmx->rImageConvert[ucIdx].u4CVTmpSizeC;

                if(u4BufPtr > u4TmpBufEnd)
                {
                    break;
                }
                
                ucReadyCnt++;
                prVdecOmx->rImageConvert[ucIdx].bEnabled = TRUE;
            }
        }

        if(0 == ucReadyCnt)
        {
            VOMX_ERROR("Tmp Buffer size[0x%x] is Not Enough for CVT\n", u4BufferSize);
            if(!prVdecOmx->fgFBM4K)
            {
                _FreeSeamlessBuffer(u4TmpBufStart);
            }
            else
            {
                if(prVdecOmx->prFbmPool)
                {
                    FBM_Free(prVdecOmx->prFbmPool);
                    prVdecOmx->prFbmPool = NULL;
                }
            }
            return -1;
        }

#ifdef CC_VDEC_FBM_DYNAMIC_MAPPING
        u4DynMapAddr = (UINT32)BSP_MapReservedMem((VOID *)u4TmpBufStart, u4BufferSize);
        if((0 == u4DynMapAddr) || (u4DynMapAddr != (UINT32)VIRTUAL(u4TmpBufStart)))
        {
            VOMX_ERROR("map address failed 0x%x\n", u4DynMapAddr);
            if(!prVdecOmx->fgFBM4K)
            {
                _FreeSeamlessBuffer(u4TmpBufStart);
            }
            else
            {
                if(prVdecOmx->prFbmPool)
                {
                    FBM_Free(prVdecOmx->prFbmPool);
                    prVdecOmx->prFbmPool = NULL;
                }
            }
            if(u4DynMapAddr)
            {
                BSP_UnMapReservedMem((VOID *)u4DynMapAddr);
            }
            return -1;
        }
#endif
        x_snprintf(pBuf, sizeof(pBuf), "%p", prVdecOmx);
        pName = strlen(pBuf) > 8?(pBuf + strlen(pBuf) - 8):pBuf;
        // szBuf is limitted within 16 bytes.
        x_snprintf(szBuf, sizeof(szBuf), "OMXCVT%s", pName);
        VERIFY(x_msg_q_create(&prVdecOmx->hCvtMsgQ, szBuf, sizeof(CVT_RESOURCE_T*), MAX_IMAGRZ_NUM) == OSR_OK);
        for (ucIdx=0; ucIdx < MAX_IMAGRZ_NUM; ++ucIdx)
        {
            if(INVALID_IMGRZ_ID != prVdecOmx->rImageConvert[ucIdx].ucImgrzId)
            {
                if(eDrvColorCvtParam->fgAsync)
                {
                    CVT_RESOURCE_T* prCvtRes = &prVdecOmx->rImageConvert[ucIdx];
                    x_msg_q_send(prVdecOmx->hCvtMsgQ, (void *)(&prCvtRes), sizeof(CVT_RESOURCE_T*), 255);
                }
            }
        }
        VOMX_DEBUG("Tmp Buffer Allocate Done\n");
        prVdecOmx->fgCvtResReady = TRUE;
    }
    return 0;
}

static BOOL vdecomx_colorcvtNV12(VDECOMX_HANDLE_T *prVdecOmx, VDECOMX_SET_COLOR_CVT_T* eDrvColorCvtParam)
{
    UINT16 u2MsgQIdx;
    SIZE_T zMsgSize = sizeof(CVT_RESOURCE_T*);
    CVT_RESOURCE_T* prCvtRes = NULL;
    IMG_DISCRIPTION_T srcImage;
    IMG_DISCRIPTION_T distImage;
    IMG_EXTRA_CVT_PARAM_T rCvtExtraParam;
    x_memset(&srcImage,0,sizeof(IMG_DISCRIPTION_T));
    x_memset(&distImage,0,sizeof(IMG_DISCRIPTION_T));
    x_memset(&rCvtExtraParam,0,sizeof(IMG_EXTRA_CVT_PARAM_T));
    if(0 > vdec_generate_srcImage(prVdecOmx, eDrvColorCvtParam, &srcImage))
    {
        return FALSE;
    }
    
    if(eDrvColorCvtParam->fgAsync)
    {
        UINT16 u2Count = 10;
        while(OSR_OK !=  x_msg_q_receive_timeout_interruptible(&u2MsgQIdx, (void*)(&prCvtRes), &zMsgSize, &(prVdecOmx->hCvtMsgQ), 1, 500))
        {
            u2Count--;
            LOG(0,"wait imgrz timeout %d\n", u2Count);
            if(0 == u2Count)
            {
                return FALSE;
            }
        }
    }
    else
    {
        //sync mode always use 0
        prCvtRes = &prVdecOmx->rImageConvert[0];
    }

    if ((prCvtRes->u2TGStride != eDrvColorCvtParam->u2TGStride) ||
        (prCvtRes->u2SliceHeight != eDrvColorCvtParam->u2SliceHeight))
    {
        prCvtRes->u2TGStride = eDrvColorCvtParam->u2TGStride;
        prCvtRes->u2SliceHeight = eDrvColorCvtParam->u2SliceHeight;
        prCvtRes->u4CVTmpSizeY = prCvtRes->u2TGStride * prCvtRes->u2SliceHeight;
        prCvtRes->u4CVTmpSizeC = prCvtRes->u4CVTmpSizeY / 2;
        if (prCvtRes->u4CVTmpAddrY != prVdecOmx->u4TmpBufStart)
        {
            prCvtRes->u4CVTmpAddrY = prVdecOmx->u4TmpBufStart + prCvtRes->u4CVTmpSizeY + prCvtRes->u4CVTmpSizeC;
        }
        prCvtRes->u4CVTmpAddrC = prCvtRes->u4CVTmpAddrY + prCvtRes->u4CVTmpSizeY;
    }
    rCvtExtraParam.ucImgRzID = prCvtRes->ucImgrzId;
    
    distImage.u4YAddr = prCvtRes->u4CVTmpAddrY;
    distImage.u4CAddr = prCvtRes->u4CVTmpAddrC;
    distImage.u4LineSize = eDrvColorCvtParam->u2TGStride;
    distImage.u4HSize = srcImage.u4HSize;  // eDrvColorCvtParam->u2HTGSize;
    distImage.u4VSize = srcImage.u4VSize;  // eDrvColorCvtParam->u2VTGSize;
    distImage.ucRaster = 1;
    distImage.ucBlockType = srcImage.ucBlockType;
    rCvtExtraParam.ucAsync = eDrvColorCvtParam->fgAsync;
    rCvtExtraParam.prCvtRes = prCvtRes;
    rCvtExtraParam.ucDoSwap = eDrvColorCvtParam->fgNV21;
    prCvtRes->u4OmxBufferHead = eDrvColorCvtParam->u4OmxBufferHead;
    prCvtRes->u4SrcBufIndex = eDrvColorCvtParam->u4SrcBufIndex;
    prCvtRes->u4UserDstY = eDrvColorCvtParam->u4DstAddr;
    prCvtRes->u4UserDstC = eDrvColorCvtParam->u4DstAddr + prCvtRes->u4CVTmpSizeY;
    vdecomx_do_image_convert(&srcImage, &distImage, &rCvtExtraParam);
    if(!eDrvColorCvtParam->fgAsync)
    {
        vdecomx_cpy_img_to_user(prCvtRes);
    }
    return TRUE;
}


int vdecomx_open(struct inode *inode, struct file *pfile)
{
    UINT32 i;
    VDECOMX_HANDLE_T *prVdecOmx;
    if(!_fgInited)
    {
        VOMX_INFO("%s(L:%d) init", __FUNCTION__, __LINE__);
        _VPUSH_DecodeInit();

        for (i = 0; i < VDECOMX_MAX_DECODER; i++)
        {
            VERIFY(OSR_OK == x_sema_create(&_arVdecOmx[i].rTunnel.hVideoReadySema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK));
        }
        _fgInited = TRUE;
    }

    for (i = 0; i < VDECOMX_MAX_DECODER; i++)
    {
        if (!_arVdecOmx[i].fgOccupied)
        {
            _arVdecOmx[i].fgOccupied = TRUE;
            break;
        }
    }
    if(i >= VDECOMX_MAX_DECODER)
    {
        VOMX_ERROR("%s(L:%d) inst full", __FUNCTION__, __LINE__);
        return -1;
    }

    pfile->private_data = &_arVdecOmx[i];
    prVdecOmx=&_arVdecOmx[i];

    if(prVdecOmx)
    {
       for(i=0;i<MAX_OUTPUT_BUFFER;i++)
       {
           prVdecOmx->rKmemInfor[i].u4Size=0;
           prVdecOmx->rKmemInfor[i].u4Address=0;
           prVdecOmx->rKmemInfor[i].hMemHandle = VOMX_INVALID_VALUE;
           prVdecOmx->rKmemInfor[i].u4Tag = VOMX_INVALID_VALUE;
       }
       prVdecOmx->hCvtMsgQ = NULL_HANDLE;
       prVdecOmx->hEventMsgQ = NULL_HANDLE;
       prVdecOmx->hCBSema = NULL_HANDLE;
       prVdecOmx->fgCvtResReady = FALSE;
       prVdecOmx->fgFBM4K = (3 == FBM_GetPanelModel()) ? TRUE : FALSE;
       prVdecOmx->fgTunnel = FALSE;
    }

    if (0 == _u4VdecOmxOpenCnt)
    {
        _vdecomx_buffer_init();
    }

    _u4VdecOmxOpenCnt ++;

    return 0;
}

static ENUM_VDEC_FMT_T vdecomx_fmt_map(VDECOMX_VIDEO_FORMAT_T eFmtIn)
{
    ENUM_VDEC_FMT_T eFmtOut;
    switch(eFmtIn)
    {
    case VDECOMX_VIDEO_FORMAT_DIVX311:
    case VDECOMX_VIDEO_FORMAT_DIVX4:
    case VDECOMX_VIDEO_FORMAT_DIVX5:
    case VDECOMX_VIDEO_FORMAT_XVID:
    case VDECOMX_VIDEO_FORMAT_MPEG4:
    case VDECOMX_VIDEO_FORMAT_H263:
    case VDECOMX_VIDEO_FORMAT_S263:
            eFmtOut = VDEC_FMT_MP4;
            break;
    case VDECOMX_VIDEO_FORMAT_MPEG1:
    case VDECOMX_VIDEO_FORMAT_MPEG2:
            eFmtOut = VDEC_FMT_MPV;
            break;
    case VDECOMX_VIDEO_FORMAT_H264:
    case VDECOMX_VIDEO_FORMAT_H264HP:
            eFmtOut = VDEC_FMT_H264;
            break;
    case VDECOMX_VIDEO_FORMAT_WMV:
            eFmtOut = VDEC_FMT_WMV;
            break;
    case VDECOMX_VIDEO_FORMAT_RV:
            eFmtOut = VDEC_FMT_RV;
            break;
    case VDECOMX_VIDEO_FORMAT_VP6:
            eFmtOut = VDEC_FMT_VP6;
            break;
    case VDECOMX_VIDEO_FORMAT_VP8:
            eFmtOut = VDEC_FMT_VP8;
            break;
    case VDECOMX_VIDEO_FORMAT_VP9:
            eFmtOut = VDEC_FMT_VP9;
            break;
    case VDECOMX_VIDEO_FORMAT_AVS:
            eFmtOut = VDEC_FMT_AVS;
            break;
    case VDECOMX_VIDEO_FORMAT_MJPEG:
            eFmtOut = VDEC_FMT_MJPEG;
            break;
    case VDECOMX_VIDEO_FORMAT_H265:
            eFmtOut = VDEC_FMT_H265;
            break;
    default:
            eFmtOut = VDEC_FMT_MAX;
            break;
    }
    return eFmtOut;
}

int vdecomx_create(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
    PAR_VDECOMX_CREATE rCreate;
    ENUM_VDEC_FMT_T eFmt = VDEC_FMT_MAX;
    VDEC_SET_INTO_T rVdecSetInfo;
    BOOL fgLowLatency = FALSE;
    UINT32 u4DecIdx;

    USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_CREATE);
    COPY_FROM_USER_ARG(arg, rCreate, PAR_VDECOMX_CREATE);

    prVdecOmx->pVPush = NULL;
    prVdecOmx->u4BufAddr = 0;
    prVdecOmx->fgDecErr = FALSE;
    prVdecOmx->fgTunnel = FALSE;
    prVdecOmx->fgCvtResReady = FALSE;
    prVdecOmx->fgSecureInput = rCreate.fgSecurePlay;
    prVdecOmx->u1VdpId = 0xFF;

    eFmt = vdecomx_fmt_map(rCreate.eVideoFormat);
    VOMX_VERBOSE("vdecomx_create FMT=%d",eFmt);

    if(eFmt == VDEC_FMT_MAX)
    {
        VOMX_ERROR("%s(L:%d) not support format(%d)", __FUNCTION__, __LINE__,
            rCreate.eVideoFormat);
        return -1;
    }

    if(rCreate.uVdecId>=VDECOMX_MAX_DECODER)
    {
         VOMX_ERROR("%s(L:%d) vdecId %d error", __FUNCTION__, __LINE__,rCreate.uVdecId);
         return -1;
    }

    for (u4DecIdx = 0; u4DecIdx < VDECOMX_MAX_DECODER; u4DecIdx++)
    {
        if ((prVdecOmx != &_arVdecOmx[u4DecIdx]) && _arVdecOmx[u4DecIdx].fgOccupied
            && (rCreate.fgSecurePlay || _arVdecOmx[u4DecIdx].fgSecureInput))
        {
            VOMX_ERROR("we only support one secure instance!");
            return -1;
        }
    }

    prVdecOmx->eFmt = eFmt;
    prVdecOmx->uVdecId = rCreate.uVdecId;
    // esid always equal to vpush id
    prVdecOmx->pVPush = _VPUSH_AllocVideoDecoder(eFmt, prVdecOmx->uVdecId);
    if(!prVdecOmx->pVPush)
    {
        VOMX_ERROR("vdec-%d not available", prVdecOmx->uVdecId);
        return -1;
    }

    x_memset(&rVdecSetInfo, 0, sizeof(VDEC_SET_INTO_T));
    rVdecSetInfo.u4InfoMask = VDEC_PUSH_SET_INFO_PLAY_MODE;
//	    rVdecSetInfo.ucVdecId=prVdecOmx->uVdecId;

    if(rCreate.eMode != VDECOMX_MODE_ASYNC && rCreate.eMode != VDECOMX_MODE_TUNNEL)
    {
        VOMX_ERROR("%s(L:%d)  no such mode[%d]!", __FUNCTION__, __LINE__, rCreate.eMode);
        goto CREATE_FAIL;
    }
    prVdecOmx->ePlayMode = rCreate.eMode;

    switch(rCreate.eMode)
    {
        case VDECOMX_MODE_ASYNC:
            rVdecSetInfo.eVpushPlayMode = VDEC_PUSH_MODE_ASYNC;
            break;
        case VDECOMX_MODE_TUNNEL:
            rVdecSetInfo.eVpushPlayMode = VDEC_PUSH_MODE_TUNNEL;
            break;
        default:
            rVdecSetInfo.eVpushPlayMode = VDEC_PUSH_MODE_UNKNOWN;
            break;
    }

    switch (prVdecOmx->eFmt)
    {
    case VDEC_FMT_H264:
        {
            if ((rCreate.eMode == VDECOMX_MODE_TUNNEL) && (FALSE == prVdecOmx->fgSecureInput))
            {
                fgLowLatency = TRUE;
            }
        }
        break;
    case VDEC_FMT_VP9:
        {
            fgLowLatency = TRUE;
        }
        break;

	case VDEC_FMT_VP8:
    	{
       	 	fgLowLatency = TRUE;
    	}
        break;
    default:
        break;
    }
    
    //low latency mode, deocde do not keep last frame.
    if(fgLowLatency)
    {
        rVdecSetInfo.u4InfoMask |= VDEC_PUSH_SET_INFO_LOW_LATENCY;
    }
        
    if(!_VPUSH_SetInfo(prVdecOmx->pVPush, &rVdecSetInfo))
    {
        VOMX_ERROR("%s(L:%d) _VPUSH_SetInfo fail, Play mode: %d", __FUNCTION__, __LINE__, rVdecSetInfo.eVpushPlayMode);
        goto CREATE_FAIL;
    }

    if(prVdecOmx->fgSecureInput)
    {
        x_memset(&rVdecSetInfo, 0, sizeof(VDEC_SET_INTO_T));
        rVdecSetInfo.u4InfoMask |= VDEC_PUSH_SET_INFO_SECURE_INPUT;
        VOMX_INFO("secure Play!");
        if(!_VPUSH_SetInfo(prVdecOmx->pVPush, &rVdecSetInfo))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_SetInfo VDEC_PUSH_SET_INFO_FBG_FROM_INST fail", __FUNCTION__, __LINE__);
            goto CREATE_FAIL;
        }
    }
    
    if(NULL_HANDLE == prVdecOmx->hEventMsgQ)
    {
        CHAR szBuf[64];
        CHAR pBuf[64];
        CHAR *pName;
        x_snprintf(pBuf, sizeof(pBuf), "%p", prVdecOmx);
        pName = strlen(pBuf) > 8?(pBuf + strlen(pBuf) - 8):pBuf;
        // szBuf is limitted within 16 bytes.
        x_snprintf(szBuf, sizeof(szBuf), "OMXEVNQ%s", pName);
        VERIFY(OSR_OK == x_msg_q_create(&prVdecOmx->hEventMsgQ, szBuf, sizeof(VDECOMX_NFY_T), MAX_VOMX_EVENT));
        VERIFY(OSR_OK == x_sema_create(&prVdecOmx->hCBSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK));
    }

    {
        VDEC_PUSH_CB_T rPushCb;
        x_memset(&rPushCb, 0, sizeof(VDEC_PUSH_CB_T));
        //rPushCb.u4Mask |= VDEC_PUSH_CB_STOP_DONE;
        //rPushCb.u4StopTag = (UINT32)prVdecOmx;
        //rPushCb.pfnVdecStopDone = i4VDOOmxCompVPushStopDone;
        //rPushCb.u4Mask |= VDEC_PUSH_CB_FLUSH_DONE;
        //rPushCb.u4FlushTag = (UINT32)prVdecOmx;
        //rPushCb.pfnVdecFlushDone = i4VDOOmxCompVPushFlushDone;
        //rPushCb.u4Mask |= VDEC_PUSH_CB_PUT_DATA_DONE;
        //rPushCb.u4PutTag = (UINT32)prVdecOmx;
        //rPushCb.pfnVdecPutDataDone = i4VDOOmxCompVPushPutDone;

        rPushCb.u4Mask |= VDEC_PUSH_CB_DECODE_DONE;
        rPushCb.u4DecodeTag = (UINT32)prVdecOmx;
        rPushCb.pfnVdecDecodeDone = _vdecomx_decode_done;
    
        rPushCb.u4Mask |= VDEC_PUSH_CB_VDEC_ERR;
        rPushCb.u4VdecErrTag = (UINT32)prVdecOmx;
        rPushCb.pfnVdecErrHandler = _vdecomx_error_callback;

        rPushCb.u4Mask |= VDEC_PUSH_CB_HDR10INFO;
        rPushCb.u4VdecHdr10infoTag = (UINT32)prVdecOmx;
        rPushCb.pfnVdecHdr10infoCb = _vdecomx_hdr10info_chg;

        if(!_VPUSH_RegCb(prVdecOmx->pVPush, &rPushCb))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_RegCb fail", __FUNCTION__, __LINE__);
            ASSERT(0);
            goto CREATE_FAIL;
        }
    }
    
    vdecomx_config_imgrz(prVdecOmx, 0x1);//default use imgrz 0
    VOMX_INFO("vdecomx_create done mode=%d fmt=%d vdecId=%d", rCreate.eMode, eFmt, prVdecOmx->uVdecId);
    rCreate.eRet = VDECOMX_MRESULT_OK;
    COPY_TO_USER_ARG(arg, rCreate, PAR_VDECOMX_CREATE);
    return 0;

CREATE_FAIL:
    prVdecOmx->eFmt = VDEC_FMT_MAX;
    if(prVdecOmx->pVPush)
    {
        _VPUSH_ReleaseVideoDecoder(prVdecOmx->pVPush);
        prVdecOmx->pVPush = NULL;
    }
    return -1;
}

int vdecomx_init(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
    PAR_VDECOMX_INIT rInit;
    VDEC_BYTES_INTO_T rBytesInfo;
    VDECOMX_RINGBUF_T rBitstream;
    FBM_SEQ_HDR_T rVdecSeqInfo;
    UINT32 u4Index;
    VDECOMX_BUFFER_T *pBufNode = NULL;
    B2R_REG_CB_OBJ_T t_obj;

    USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_INIT);
    COPY_FROM_USER_ARG(arg, rInit, PAR_VDECOMX_INIT);
    USR_SPACE_ACCESS_VALIDATE_ARG(rInit.prBitstream, VDECOMX_RINGBUF_T);
    COPY_FROM_USER_ARG(rInit.prBitstream, rBitstream, VDECOMX_RINGBUF_T);
    
    x_memset(&rBytesInfo, 0, sizeof(VDEC_BYTES_INTO_T));
    x_memset(&rVdecSeqInfo, 0, sizeof(VDEC_SEQUENCE_DATA_T));

    if (0xFF != prVdecOmx->u1VdpId)
    {
        x_memset(&t_obj, 0, sizeof(t_obj));
        t_obj.pfCb = (VOID *)prVdecOmx;
        t_obj.rCbInfo.u.t_comm.pv_tag = (VOID *)prVdecOmx;
        B2R_RegCbFunc(prVdecOmx->u1VdpId, B2R_CB_FUNC_PTS_RENDER, (UINT32)_vdecomx_frame_rendered, (UINT32)&t_obj);
        if(prVdecOmx->fgTunnel)
        {
            B2R_RegCbFunc(prVdecOmx->u1VdpId, B2R_CB_FUNC_TRIG_AUD_TO_PLAY, (UINT32)_vdecomx_tunnel_B2rTrigAudCb, (UINT32)&t_obj);
        }
    }

    if (prVdecOmx->fgTunnel && prVdecOmx->rTunnel.u4AudioHwSync)
    {
        // (u4AudioHwSync == 0) is video only, no need to pause for init av sync
        if(!_VPUSH_Play(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_Play fail", __FUNCTION__, __LINE__);
            return -1;
        }
        prVdecOmx->rTunnel.u4VdecTunnelState |= VDEXOMX_TUNNEL_STATE_PLAYED;
        if (!(prVdecOmx->rTunnel.u4VdecTunnelState & VDEXOMX_TUNNEL_STATE_AUD_CB))
        {
            _VPUSH_PauseVdec(prVdecOmx->pVPush);
        }
        x_sema_unlock(prVdecOmx->rTunnel.hVideoReadySema);
    }
    else
    {
        // video only case needs sideband driver start stc...
        if (prVdecOmx->fgTunnel)
        {
            STC_StartStc(0);
        }

        if(!_VPUSH_Play(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_Play fail", __FUNCTION__, __LINE__);
            return -1;
        }
    }
    
    prVdecOmx->fgPlay = TRUE;
    prVdecOmx->fgCvtResReady = FALSE;

    // copy ES data from the rBitstream to prVdecOmx memory address
    if( prVdecOmx->ePlayMode == VDECOMX_MODE_ASYNC || prVdecOmx->ePlayMode == VDECOMX_MODE_TUNNEL)
    {
        for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index++)
        {
            if (rBitstream.u4BufHandle == _prVDOOmxInputBuf[u4Index].hBuffer)
            {
                pBufNode = &(_prVDOOmxInputBuf[u4Index]);
                break;
            }
        }

        if(pBufNode == NULL)
        {
            VOMX_ERROR("%s(%d), Not find input buffer header", __FUNCTION__, __LINE__);
            return -1;
        }
    }

    rBytesInfo.u4BytesAddr = (UINT32)(pBufNode->pBufSA) + rBitstream.u4Offset;
    rBytesInfo.u4BytesSize = rBitstream.u4Size;
    rBytesInfo.u8BytesPTS = (UINT64)rBitstream.u8Timestamp;
    rBytesInfo.u4SecureInputBufStartAddr = (UINT32)(pBufNode->pBufSA);
    rBytesInfo.u4SecureInputBufEndAddr = rBytesInfo.u4SecureInputBufStartAddr + pBufNode->u4BufSize;
    rBytesInfo.fgMoveBufDone=TRUE;

    VOMX_INPUT("[1st]addr: 0x%p, size: 0x%x, pts: %lluus", rBytesInfo.u4BytesAddr, rBytesInfo.u4BytesSize, rBytesInfo.u8BytesPTS);
    //low latency mode
    if((prVdecOmx->ePlayMode == VDECOMX_MODE_TUNNEL) && (prVdecOmx->eFmt == VDEC_FMT_H264) && (FALSE == prVdecOmx->fgSecureInput))
    {
        UINT64 u4Delim = 0xF00901000000;
        x_memcpy((VOID*)(VIRTUAL(rBytesInfo.u4BytesAddr + rBytesInfo.u4BytesSize)), (VOID*)(&u4Delim), 6);
        rBytesInfo.u4BytesSize += 6;
    }

    // call vpush to parse
    if(!_VPUSH_PrsSeqHdr(prVdecOmx->pVPush, &rBytesInfo, &rVdecSeqInfo))
    {
        VOMX_ERROR("%s(L:%d) _VPUSH_PrsSeqHdr fail", __FUNCTION__, __LINE__);
        return -1;
    }

    rInit.eRet = VDECOMX_MRESULT_OK;
    COPY_TO_USER_ARG(arg, rInit, PAR_VDECOMX_INIT);
    if(prVdecOmx->fgPending)
    {
        if(!_VPUSH_PauseVdec(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_PauseVdec fail", __FUNCTION__, __LINE__);
            return -1;
        }
    }
    return 0;
}

static int vdecomx_deinit(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
    if(prVdecOmx->fgPlay)
    {
        VOMX_INFO("vdecomx_deinit\n");
        if(!_VPUSH_StopSync(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_StopSync fail", __FUNCTION__, __LINE__);
            return -1;
        }
        prVdecOmx->fgPlay = FALSE;
    }
    
    if(prVdecOmx->fgCvtResReady)
    {
#ifdef CC_VDEC_FBM_DYNAMIC_MAPPING
        BSP_UnMapReservedMem((VOID *)VIRTUAL(prVdecOmx->rImageConvert[0].u4CVTmpAddrY));
#endif 
        if(!prVdecOmx->fgFBM4K)
        {
            _FreeSeamlessBuffer(prVdecOmx->rImageConvert[0].u4CVTmpAddrY);
        }
        else
        {
            if(prVdecOmx->prFbmPool)
            {
                FBM_Free(prVdecOmx->prFbmPool);
                prVdecOmx->prFbmPool = NULL;
            }
        }
        
        prVdecOmx->fgCvtResReady = FALSE;
        VERIFY(x_msg_q_delete(prVdecOmx->hCvtMsgQ)== OSR_OK);
        prVdecOmx->hCvtMsgQ = NULL_HANDLE;
    }
    VOMX_INFO("vdecomx_deinit done\n");
    return 0;
}

int vdecomx_decode(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
    PAR_VDECOMX_DECODE rDecode;
    VDEC_BYTES_INTO_T rBytesInfo;
    UINT32 u4Index = VDECOMX_INPUT_BUF_MAX;
    VDECOMX_BUFFER_T *pBufNode = NULL;
    VDECOMX_RINGBUF_T rBitstream;          // IN
    INT32 iRet = VDEC_DEC_ERROR_NONE;

    x_memset(&rBitstream, 0, sizeof(rBitstream));
    
    USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_DECODE);
    COPY_FROM_USER_ARG(arg, rDecode, PAR_VDECOMX_DECODE);
    
    if(rDecode.prBitstream)
    {
        USR_SPACE_ACCESS_VALIDATE_ARG(rDecode.prBitstream, VDECOMX_RINGBUF_T);
        COPY_FROM_USER_ARG(rDecode.prBitstream, rBitstream, VDECOMX_RINGBUF_T);
    }
    
    if (prVdecOmx->ePlayMode == VDECOMX_MODE_ASYNC || prVdecOmx->ePlayMode == VDECOMX_MODE_TUNNEL)
    {
        x_memset(&rBytesInfo, 0, sizeof(VDEC_BYTES_INTO_T));

        for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index++)
        {
            if (rBitstream.u4BufHandle == _prVDOOmxInputBuf[u4Index].hBuffer)
            {
                pBufNode = &(_prVDOOmxInputBuf[u4Index]);
                break;
            }
        }

        if(pBufNode == NULL)
        {
            VOMX_ERROR("%s(%d), Not find input buffer header", __FUNCTION__, __LINE__);
            return -1;
        }

        if(rBitstream.u4Size)
        {
            // prepare rBytesInfo
            rBytesInfo.u4BytesAddr = (UINT32)pBufNode->pBufSA + rBitstream.u4Offset;
            rBytesInfo.u4BytesSize = rBitstream.u4Size;
            rBytesInfo.u8BytesPTS  = (UINT64)rBitstream.u8Timestamp;
            //low latency mode
            if((prVdecOmx->ePlayMode == VDECOMX_MODE_TUNNEL) && (prVdecOmx->eFmt == VDEC_FMT_H264) && (FALSE == prVdecOmx->fgSecureInput))
            {
                UINT64 u4Delim = 0xF00901000000;
                x_memcpy((VOID*)(VIRTUAL(rBytesInfo.u4BytesAddr + rBytesInfo.u4BytesSize)), (VOID*)(&u4Delim), 6);
                rBytesInfo.u4BytesSize += 6;
            }
        }
        else
        {
            rBytesInfo.u4BytesAddr = (UINT32)pBufNode->pBufSA;
            rBytesInfo.u4BytesSize = 0;
            rBytesInfo.u8BytesPTS = 0;
            // EOS
            rBytesInfo.fgEos = TRUE;
        }

        rBytesInfo.u4SecureInputBufStartAddr = (UINT32)(pBufNode->pBufSA);
        rBytesInfo.u4SecureInputBufEndAddr = rBytesInfo.u4SecureInputBufStartAddr + pBufNode->u4BufSize;
    }

    // call vpush to decode
    rBytesInfo.fgMoveBufDone=TRUE;
    iRet = _VPUSH_PutData(prVdecOmx->pVPush, &rBytesInfo);
    if (iRet < VDEC_DEC_ERROR_NONE)
    {
        VOMX_ERROR("%s(L:%d) _VPUSH_PutData fail", __FUNCTION__, __LINE__);
        rDecode.eRet = VDECOMX_MRESULT_FATAL;
        goto lbFinish;
    }
    else if (iRet > VDEC_DEC_ERROR_NONE) 
    {
        VOMX_VERBOSE("%s(L:%d) vfifo full", __FUNCTION__, __LINE__);
        rDecode.eRet = VDECOMX_MRESULT_FAIL;
        goto lbFinish;
    }

    if (prVdecOmx->fgDecErr && !rBytesInfo.fgEos)
    {
         VOMX_ERROR("vdecomx_decode return error");
         rDecode.eRet = VDECOMX_MRESULT_FATAL;
         goto lbFinish;
    }
    VOMX_INPUT("addr:0x%p, size:0x%x, pts:%lluus%s", rBytesInfo.u4BytesAddr, rBytesInfo.u4BytesSize, rBytesInfo.u8BytesPTS, (rBytesInfo.fgEos ? ", eos!" : ""));

    rDecode.eRet = VDECOMX_MRESULT_OK;

    lbFinish:
    COPY_TO_USER_ARG(arg, rDecode, PAR_VDECOMX_DECODE);
    return 0;
}

int vdecomx_getparam(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
    BOOL fgRet;
    PAR_VDECOMX_GETPARAM rGetparam;
    USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_GETPARAM);
    COPY_FROM_USER_ARG(arg, rGetparam, PAR_VDECOMX_GETPARAM);

    switch (rGetparam.eType)
    {
        case VDECOMX_GET_TYPE_DISP_FRAME_BUFFER:
            {
                VDECOMX_FRAME_T rFrame;          // IN
                VDEC_DISP_FRAME_T rDispFrame;
                UINT32 u4FbIndex=(UINT32)rGetparam.pvInParam;
                UINT8 u1FbgId,u1FbId;

                u1FbgId=(u4FbIndex>>16)&0xff;
                u1FbId=u4FbIndex&0xff;

                x_memset(&rDispFrame, 0, sizeof(VDEC_DISP_FRAME_T));
                x_memset(&rFrame, 0, sizeof(VDECOMX_FRAME_T));
                fgRet = _VPUSH_GetDisplayInfor(prVdecOmx->pVPush, &rDispFrame,u1FbgId,u1FbId);

                if(!fgRet)
                {
                    // return VDECOMX_MRESULT_FAIL if no disp buffer
                    // rFrame.rFrameBuf.rBaseAddr is 0;
                    rGetparam.eRet = VDECOMX_MRESULT_FATAL;
                    return -1;
                }
                else
                {
                    rFrame.rFrameFormat.u4Offset = 0;
                    rFrame.rFrameFormat.u4Width = (((rDispFrame.u4Width+15)/16) * 16);
                    rFrame.rFrameFormat.u4Height = (((rDispFrame.u4Height+15)/16) * 16);
                    if((rDispFrame.fgCropping) &&
                       (rDispFrame.u4CropWidth > 0) &&
                       (rDispFrame.u4CropHeight > 0))
                    {
                        rFrame.rFrameFormat.u2CropX = (UINT16)rDispFrame.u4CropX;
                        rFrame.rFrameFormat.u2CropY = (UINT16)rDispFrame.u4CropY;
                        rFrame.rFrameFormat.u2CropWidth = (UINT16)rDispFrame.u4CropWidth;
                        rFrame.rFrameFormat.u2CropHeight = (UINT16)rDispFrame.u4CropHeight;
                    }
                    else
                    {
                        ASSERT(rDispFrame.u4Width > 0);
                        ASSERT(rDispFrame.u4Height > 0);
                        rFrame.rFrameFormat.u2CropX = 0;
                        rFrame.rFrameFormat.u2CropY = 0;
                        rFrame.rFrameFormat.u2CropWidth = (UINT16)rDispFrame.u4Width;
                        rFrame.rFrameFormat.u2CropHeight = (UINT16)rDispFrame.u4Height;
                    }
                    rFrame.rFrameBuf.fgCropping= rDispFrame.fgCropping;
                    rFrame.rFrameBuf.u4CropX= rFrame.rFrameFormat.u2CropX;
                    rFrame.rFrameBuf.u4CropY= rFrame.rFrameFormat.u2CropY;
                    rFrame.rFrameBuf.u4CropWidth = rFrame.rFrameFormat.u2CropWidth;
                    rFrame.rFrameBuf.u4CropHeight = rFrame.rFrameFormat.u2CropHeight;
                    rFrame.rFrameBuf.u4AddressY= rDispFrame.u4BufAddr;
                    rFrame.rFrameBuf.u4AddressC= rDispFrame.u4BufAddrC;
                    rFrame.rFrameBuf.u4Size = rDispFrame.u4BufSize;
                    rFrame.rFrameBuf.u4BufWidth = rDispFrame.u4BufWidth;
                    rFrame.rFrameBuf.u4BufHeight = rDispFrame.u4BufHeight;
                    rFrame.rFrameBuf.u4DispWidth = rDispFrame.u4Width;
                    rFrame.rFrameBuf.u4DispHeight = rDispFrame.u4Height;
                    rFrame.rFrameBuf.u4DispPitch = rDispFrame.u4Pitch;
                    rFrame.rFrameBuf.u4Timestamp = (UINT32)rDispFrame.u8Timestamp;
                    rFrame.rFrameBuf.u8Timestamp = rDispFrame.u8Timestamp;
                    rFrame.rFrameBuf.u4AspectRatioW = rDispFrame.u4ParWidth;
                    rFrame.rFrameBuf.u4AspectRatioH = rDispFrame.u4ParHeight;
                    rFrame.rFrameBuf.u4FrameBufferType = 0;
                    rFrame.rFrameBuf.u4PictureStructure = 0;
                    rFrame.rFrameBuf.u4FrameBufferStatus = 0;
                    rFrame.rFrameBuf.u4Eos = rDispFrame.fgEos;

                    if (rDispFrame.u4FrameBufferStatus & VDEC_FBSTSTUS_NOT_DISPLAY)
                    {
                        rFrame.rFrameBuf.u4FrameBufferStatus |= VDECOMX_FBSTATUS_NOT_DISPLAY;
                    }
                    if (rDispFrame.u4FrameBufferStatus & VDEC_FBSTSTUS_REPEAT_LAST)
                    {
                        rFrame.rFrameBuf.u4FrameBufferStatus |= VDECOMX_FBSTATUS_REPEAT_LAST;
                    }
                    if (rDispFrame.u4FrameBufferStatus & VDEC_FBSTSTUS_NOT_USED)
                    {
                        rFrame.rFrameBuf.u4FrameBufferStatus |= VDECOMX_FBSTATUS_NOT_USED;
                    }

                    rGetparam.eRet = VDECOMX_MRESULT_OK;
                }
                // copy to user
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, VDECOMX_FRAME_T);
                COPY_TO_USER_ARG(rGetparam.pvOutParam, rFrame, VDECOMX_FRAME_T);
            }
            break;

        case VDECOMX_GET_TYPE_GET_SEQUENCE_INFO:
            {
                VDECOMX_SEQINFO_T rSeqinfo;
                VDEC_ES_INFO_T *prVdecEsInfo;
                VDEC_GET_INTO_T rVdecGetInfo;
                x_memset(&rVdecGetInfo, 0, sizeof(VDEC_GET_INTO_T));
                if(!_VPUSH_GetInfo(prVdecOmx->pVPush, &rVdecGetInfo))
                {
                    VOMX_ERROR("_VPUSH_GetInfo fail");
                    rGetparam.eRet = VDECOMX_MRESULT_FAIL;
                }
                else
                {
                    prVdecEsInfo = _VDEC_GetEsInfo(rVdecGetInfo.ucVdecId);
                    rGetparam.eRet = VDECOMX_MRESULT_OK;
                    if (!prVdecEsInfo)
                    {
                        VOMX_ERROR("%s(%d): FBM_GetFrameBufferSeqHdr fail", __FUNCTION__, __LINE__);
                        rGetparam.eRet = VDECOMX_MRESULT_FAIL;
                    }
                    else
                    {
                        FBM_SEQ_HDR_T *prFbmSeqHdr;
                        x_memset(&rSeqinfo, 0, sizeof(VDECOMX_SEQINFO_T));
                        prFbmSeqHdr = FBM_GetFrameBufferSeqHdr(prVdecEsInfo->ucFbgId);
                        if(NULL != prFbmSeqHdr)
                        {
                            rSeqinfo.u4PicWidth = prFbmSeqHdr->u2HSize;
                            rSeqinfo.u4PicHeight = prFbmSeqHdr->u2VSize;
                            rSeqinfo.u4Width = prFbmSeqHdr->u2HSize;
                            rSeqinfo.u4Height = prFbmSeqHdr->u2VSize;
                            rSeqinfo.bCropping = prFbmSeqHdr->fgCropping;
                            rSeqinfo.u4CropX = prFbmSeqHdr->u4CropX;
                            rSeqinfo.u4CropY = prFbmSeqHdr->u4CropY;
                            rSeqinfo.u4CropWidth = prFbmSeqHdr->u4CropWidth;
                            rSeqinfo.u4CropHeight = prFbmSeqHdr->u4CropHeight;
                            USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, VDECOMX_SEQINFO_T);
                            COPY_TO_USER_ARG(rGetparam.pvOutParam, rSeqinfo, VDECOMX_SEQINFO_T);
                            VOMX_VERBOSE( "%s(%d) ret (%dx%d)", __FUNCTION__, __LINE__, rSeqinfo.u4PicWidth, rSeqinfo.u4PicHeight);
                        }
                        else
                        {
                            rGetparam.eRet = VDECOMX_MRESULT_FAIL;
                        }
                    }
                }
            }
            break;

        case VDECOMX_GET_TYPE_QUERY_FEEDER_INFO:
            {
                VDEC_FEEDER_INTO_T rVdecFeederInfo;
                VDECOMX_FEEDER_INFO_T rFeederInfo;
                x_memset(&rFeederInfo, 0, sizeof(VDECOMX_FEEDER_INFO_T));
                if(!prVdecOmx->pVPush)
                {
                    VOMX_ERROR("%s(L:%d) vpush not inited", __FUNCTION__, __LINE__);
                    return -1;
                }

                if(!_VPUSH_GetFeederInfo(prVdecOmx->pVPush, &rVdecFeederInfo))
                {
                    VOMX_ERROR("%s(L:%d) vpush get feeder info failed", __FUNCTION__, __LINE__);
                    return -1;
                }
                rFeederInfo.u4TotalSize = rVdecFeederInfo.u4TotalSize;
                rFeederInfo.u4BufferCount = rVdecFeederInfo.u4BufferCount;
                rFeederInfo.u4BufferSize = rVdecFeederInfo.u4BufferSize;
                // copy to user
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, VDECOMX_FEEDER_INFO_T);
                COPY_TO_USER_ARG(rGetparam.pvOutParam, rFeederInfo, VDECOMX_FEEDER_INFO_T);
            }
             break;

        case VDECOMX_GET_TYPE_ALLOCATE_BUFFER:
            {
                PARAMETER_VDECOMX_ALLOCATEBUFFER OMX_AllocBuf_Param;
                VDECOMX_BUFFER_T *pBuffer = NULL; // internal buffer handle
        
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvInParam, PARAMETER_VDECOMX_ALLOCATEBUFFER);
                COPY_FROM_USER_ARG(rGetparam.pvInParam, OMX_AllocBuf_Param, PARAMETER_VDECOMX_ALLOCATEBUFFER);

                _vdecomx_alloc_input_buffer(prVdecOmx, OMX_AllocBuf_Param.nSizeBytes, &pBuffer);
                if (NULL != pBuffer)
                {
                    OMX_AllocBuf_Param.hPBufHeaderBuf = pBuffer->hBuffer;
                    VOMX_VERBOSE("input buf allocated: pBuffer = 0x%X, pBuffer->pOwner = 0x%X.", pBuffer, pBuffer->pOwner);
                }
        
                // copy to user
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, PARAMETER_VDECOMX_ALLOCATEBUFFER);
                COPY_TO_USER_ARG(rGetparam.pvOutParam, OMX_AllocBuf_Param, PARAMETER_VDECOMX_ALLOCATEBUFFER);
            }
            break;

        case VDECOMX_GET_TYPE_GET_KMEM_HANDLER:
            {
                VDECOMX_FRAMEBUF_T rFrameBuf;
                UINT32 u4FbIndex=(UINT32)rGetparam.pvInParam;
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, VDECOMX_FRAMEBUF_T);
                COPY_FROM_USER_ARG(rGetparam.pvOutParam, rFrameBuf, VDECOMX_FRAMEBUF_T);
                rFrameBuf.u4MemHeaderY = NULL_HANDLE;
                rFrameBuf.u4MemHeaderC = NULL_HANDLE;
                rFrameBuf.u4MemHeaderY = _vdecomx_create_kmem_handle(prVdecOmx, rFrameBuf.u4AddressY, rFrameBuf.u4Size, u4FbIndex);
                if(NULL_HANDLE == rFrameBuf.u4MemHeaderY)
                {
                    VOMX_ERROR("create kmem handle failed");
                    return -1;
                }
                VOMX_DEBUG("create handle for Y PA:0x%x VA:0x%x, sz:0x%x, handle:0x%x",rFrameBuf.u4AddressY, VIRTUAL(rFrameBuf.u4AddressY), rFrameBuf.u4Size, rFrameBuf.u4MemHeaderY);
                rFrameBuf.u4MemHeaderC = _vdecomx_create_kmem_handle(prVdecOmx, rFrameBuf.u4AddressC, rFrameBuf.u4Size/2, u4FbIndex);
                if(NULL_HANDLE == rFrameBuf.u4MemHeaderC)
                {
                    _vdecomx_free_kmem_handle_by_tag(prVdecOmx, u4FbIndex);
                    return -1;
                }
                VOMX_DEBUG("create handle for C PA:0x%x VA:0x%x, sz:0x%x, handle:0x%x", rFrameBuf.u4AddressC, VIRTUAL(rFrameBuf.u4AddressC), rFrameBuf.u4Size/2, rFrameBuf.u4MemHeaderC);
                COPY_TO_USER_ARG(rGetparam.pvOutParam, rFrameBuf, VDECOMX_FRAMEBUF_T);
            }
            break;
        case VDECOMX_GET_TYPE_GET_VDEC_CNT:
            {
               UINT32 u4DecCnt;
               u4DecCnt=_VPUSH_GetDecoderCnt();
               USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvOutParam, UINT32);
               COPY_TO_USER_ARG(rGetparam.pvOutParam, u4DecCnt, UINT32);
            }
            break;

        case VDECOMX_GET_TYPE_QUERY_HW_ABILITY:
            {
#ifdef CC_VDEC_RM_SUPPORT
                ENUM_VDEC_FMT_T eFmt = VDEC_FMT_MAX;
                UINT32 u4FrmRateCod;
                VDECOMX_SET_TYPE_PICTURE_INFO_T rQueryInput;
                USR_SPACE_ACCESS_VALIDATE_ARG(rGetparam.pvInParam, VDECOMX_SET_TYPE_PICTURE_INFO_T);
                COPY_FROM_USER_ARG(rGetparam.pvInParam, rQueryInput, VDECOMX_SET_TYPE_PICTURE_INFO_T);
                u4FrmRateCod = rQueryInput.u4FrameRate * 100;
                eFmt = vdecomx_fmt_map(rQueryInput.eVideoFormat);
                if(!VDEC_VLDMngrQueryCap(eFmt, rQueryInput.u4Width, rQueryInput.u4Height, u4FrmRateCod))
                    return -1;
#endif
            }
            break;

        case VDECOMX_GET_TYPE_GET_COLOR_CONVERT_BUFFER:
            {
                UINT32 BufferHead = (UINT32)rGetparam.pvInParam;
                UCHAR ucIdx;
                for (ucIdx=0; ucIdx < MAX_IMAGRZ_NUM; ++ucIdx)
                {
                    if((BufferHead == prVdecOmx->rImageConvert[ucIdx].u4OmxBufferHead) && prVdecOmx->rImageConvert[ucIdx].bEnabled)
                    {
                        CVT_RESOURCE_T* prCvtDone = &prVdecOmx->rImageConvert[ucIdx];
                        prCvtDone->u4OmxBufferHead = 0;
                        prCvtDone->u4SrcBufIndex = 0;
                        vdecomx_cpy_img_to_user(prCvtDone);
                        x_msg_q_send(prVdecOmx->hCvtMsgQ, (void *)(&prCvtDone), sizeof(CVT_RESOURCE_T*), 255);
                        break;
                    }
                }
                if(MAX_IMAGRZ_NUM == ucIdx)
                {
                    VOMX_ERROR("cvt result not found!!");
                    return -1;
                }
            }
            break;

        case VDECOMX_GET_TYPE_HDR10_INFO:
        {
            VDEC_HDR10_INFO_T rInfo;
            if (sizeof(VDECOMX_HDR10_INFO_T) != sizeof(VDEC_HDR10_INFO_T))
            {
                VOMX_ERROR("VDECOMX_HDR10_INFO_T=%d, VDEC_HDR10_INFO_T=%d",
                    sizeof(VDECOMX_HDR10_INFO_T), sizeof(VDEC_HDR10_INFO_T));
                return -1;
            }

            if (!_VPUSH_GetHDR10Info(prVdecOmx->pVPush, &rInfo))
            {
                VOMX_ERROR("_VPUSH_GetHDR10Info fail");
                return -1;
            }

            COPY_TO_USER_ARG(rGetparam.pvOutParam, rInfo, VDEC_HDR10_INFO_T);
            break;
        }

        case VDECOMX_GET_TYPE_HDR10_METADATA_INFO:
        {
            VDEC_HDR10_METADATA_INFO_T rInfo;
            if (sizeof(VDECOMX_HDR10_METADATA_INFO_T) != sizeof(VDEC_HDR10_METADATA_INFO_T))
            {
                VOMX_ERROR("VDECOMX_HDR10_METADATA_INFO_T=%d, VDEC_HDR10_METADATA_INFO_T=%d",
                    sizeof(VDECOMX_HDR10_METADATA_INFO_T), sizeof(VDEC_HDR10_METADATA_INFO_T));
                return -1;
            }

            if (!_VPUSH_GetHDR10MetadataInfo(prVdecOmx->pVPush, &rInfo))
            {
                VOMX_ERROR("_VPUSH_GetHDR10MetadataInfo fail");
                return -1;
            }

            COPY_TO_USER_ARG(rGetparam.pvOutParam, rInfo, VDEC_HDR10_METADATA_INFO_T);
            break;
        }

        default:
        rGetparam.eRet = VDECOMX_MRESULT_FAIL;
        break;
    }

    COPY_TO_USER_ARG(arg, rGetparam, PAR_VDECOMX_GETPARAM);

    return 0;
}

int vdecomx_setparam(VDECOMX_HANDLE_T *prVdecOmx, unsigned long arg)
{
//    BOOL fgRet;;
//    UINT32 u4Addr;
    PAR_VDECOMX_SETPARAM rSetparam;

    USR_SPACE_ACCESS_VALIDATE_ARG(arg, PAR_VDECOMX_SETPARAM);
    COPY_FROM_USER_ARG(arg, rSetparam, PAR_VDECOMX_SETPARAM);

    rSetparam.eRet = VDECOMX_MRESULT_OK;
    switch (rSetparam.eType)
    {
        case VDECOMX_SET_TYPE_SET_DECODE_MODE:
            {
                VDEC_SET_INTO_T rVdecSetInfo;
                VDECOMX_DECODE_MODE_T eDrvDecodeMode;
                x_memset(&rVdecSetInfo, 0, sizeof(VDEC_SET_INTO_T));
                rVdecSetInfo.u4InfoMask = VDEC_PUSH_SET_INFO_DECODE_MODE;
                if(!rSetparam.pvInParam)
                {
                    VOMX_ERROR("%s(L:%d) pvInParam null", __FUNCTION__, __LINE__);
                    return -1;
                }
                eDrvDecodeMode = ((VDECOMX_SET_DECODE_MODE_T*)rSetparam.pvInParam)->eDecodeMode;
                switch(eDrvDecodeMode)
                {
                    case VDECOMX_DECODE_MODE_I_ONLY:
                        rVdecSetInfo.eDecodeMode = VDEC_DECODE_MODE_I_ONLY;
                        break;
                    case VDECOMX_DECODE_MODE_B_SKIP:
                        rVdecSetInfo.eDecodeMode = VDEC_DECODE_MODE_B_SKIP;
                        break;
                    case VDECOMX_DECODE_MODE_NO_REORDER:
                        rVdecSetInfo.eDecodeMode = VDEC_DECODE_MODE_NO_REORDER;
                        break;
                    case VDECOMX_DECODE_MODE_NORMAL:
                        rVdecSetInfo.eDecodeMode = VDEC_DECODE_MODE_NORMAL;
                        break;
                    case VDECOMX_DECODE_MODE_DROPFRAME:
                    case VDECOMX_DECODE_MODE_UNKNOWN:
                        rVdecSetInfo.eDecodeMode = VDEC_DECODE_MODE_UNKNOWN;
                        break;
                }
                VOMX_DEBUG("setparam DecMode=%d",rVdecSetInfo.eDecodeMode);
                if(!_VPUSH_SetInfo(prVdecOmx->pVPush, &rVdecSetInfo))
                {
                    VOMX_ERROR("%s(L:%d) _VPUSH_SetInfo fail", __FUNCTION__, __LINE__);
                    return -1;
                }

            }
            break;
        case VDECOMX_SET_TYPE_SET_BUFTAG:
            {
                PARAMETER_VDECOMX_FILLBTAG rBufferTag;
                USR_SPACE_ACCESS_VALIDATE_ARG(rSetparam.pvInParam, PARAMETER_VDECOMX_FILLBTAG);
                COPY_FROM_USER_ARG(rSetparam.pvInParam,rBufferTag,PARAMETER_VDECOMX_FILLBTAG);
				if (FBM_GetFrameBufferStatus((rBufferTag.nDecodeBufId>>16)&0xff,rBufferTag.nDecodeBufId&0xff) == FBM_FB_STATUS_LOCK)
				{
                    VOMX_VERBOSE("tag fb[%d, %d] with[0x%p]", (rBufferTag.nDecodeBufId>>16)&0xff, rBufferTag.nDecodeBufId&0xff, rBufferTag.nTag);
				    FBM_SetDispTag((rBufferTag.nDecodeBufId>>16)&0xff, rBufferTag.nDecodeBufId&0xff, rBufferTag.nTag);
				}
                else
                {
                    VOMX_ERROR("fb[%d, %d] status not illegal!!", (rBufferTag.nDecodeBufId>>16)&0xff, rBufferTag.nDecodeBufId&0xff);
                }
                
                break;
            }
        case VDECOMX_SET_TYPE_SET_DROP_TAGBUFFER:
            {
                UINT32 u4BufTag=(UINT32)rSetparam.pvInParam;
                if(FBM_ReleaseBufferByDispTag(u4BufTag))
                    VOMX_OUTPUT("dropped frame[%p]",u4BufTag);
                break;
            }
        case VDECOMX_SET_TYPE_SET_DROP_UNTAGBUFFER:
            {
                UINT32 u4BufIndex=(UINT32)rSetparam.pvInParam;
                UCHAR u1BufIndex;
                _vdecomx_free_kmem_handle_by_tag(prVdecOmx, u4BufIndex);//for mjpeg unmmap memory
                if (FBM_GetFrameBufferStatus((u4BufIndex>>16)&0xff,u4BufIndex&0xff) == FBM_FB_STATUS_LOCK)
                {
                    VOMX_VERBOSE("release frame[%d, %d]",(u4BufIndex>>16)&0xff, u4BufIndex&0xff);
                    u1BufIndex = u4BufIndex&0xff;
                    if (u1BufIndex > 73)
                    {
                        VOMX_ERROR("FB index overflow: FBID(0x%x)", u1BufIndex);
                        break;
                    }
                    FBM_ReleaseBufferByIndex((u4BufIndex>>16)&0xff,u1BufIndex);
                }
                break;
            }
        case VDECOMX_SET_TYPE_SET_PICTURE_INFO:
            {
                VDECOMX_SET_TYPE_PICTURE_INFO_T* pPicInfo = (VDECOMX_SET_TYPE_PICTURE_INFO_T*)(rSetparam.pvInParam);
                _VPUSH_SetPicSz(prVdecOmx->pVPush, pPicInfo->u4Width, pPicInfo->u4Height, pPicInfo->u4FrameRate);
            }
            break;
        case VDECOMX_SET_TYPE_SET_COLOR_CONVERT:
            {
                VDECOMX_SET_COLOR_CVT_T* pDrvColorCvtParam;
                if(!rSetparam.pvInParam)
                {
                    VOMX_ERROR("%s(L:%d) pvInParam null", __FUNCTION__, __LINE__);
                    return -1;
                }
                pDrvColorCvtParam = (VDECOMX_SET_COLOR_CVT_T*)rSetparam.pvInParam;
                if(!vdecomx_colorcvtNV12(prVdecOmx, pDrvColorCvtParam))
                {
                    return -1;
                }
            }
            break;

        case VDECOMX_SET_TYPE_FREE_BUFFER:
            {
                int i4Ret = -1;
                PARAMETER_VDECOMX_FREEBUFFER OMX_FreeBuf_Param;

                USR_SPACE_ACCESS_VALIDATE_ARG(rSetparam.pvInParam, PARAMETER_VDECOMX_FREEBUFFER);
                COPY_FROM_USER_ARG(rSetparam.pvInParam, OMX_FreeBuf_Param, PARAMETER_VDECOMX_FREEBUFFER);

                i4Ret = _vdecomx_free_input_buffer(prVdecOmx, OMX_FreeBuf_Param.hPBufHeaderBuf);
                if (-1 == i4Ret)
                {
                    rSetparam.eRet = VDECOMX_MRESULT_FAIL;
                }
            }
            break;
        case VDECOMX_SET_TYPE_FLUSH_DEOCDER:
            {
                VDECOMX_NFY_T tNfy;
                VOMX_INFO("flush");
                if(!_VPUSH_FlushSync(prVdecOmx->pVPush))
                {
                    VOMX_ERROR("%s(L:%d) _VPUSH_FlushSync fail", __FUNCTION__, __LINE__);                    
                    return -1;
                }
                VOMX_INFO("flush done");
                tNfy.u8Data1 = (UINT64)(FBM_FBG_ID_UNKNOWN<<16) + FBM_FB_ID_UNKNOWN;
                tNfy.eType = VDECOMX_NFY_TYPE_FRAME_DECODED;
                vdecomx_put_event(prVdecOmx, &tNfy);
                
                if (prVdecOmx->fgTunnel)
                {
                    if(prVdecOmx->rTunnel.u4AudioHwSync)
                    {
                        VOMX_INFO("pause vdec");
                        _VPUSH_PauseVdec(prVdecOmx->pVPush);
                        prVdecOmx->rTunnel.u4VdecTunnelState |= VDEXOMX_TUNNEL_STATE_FLUSHED;
                        prVdecOmx->rTunnel.u4VdecTunnelState &= ~VDEXOMX_TUNNEL_STATE_AUD_CB;
                        if(prVdecOmx->fgPlay)
                        {
                            x_sema_unlock(prVdecOmx->rTunnel.hVideoReadySema);
                        }
                    }
					VOMX_INFO("flush trigger Pb restart");
                    B2R_PreparePbRestart(prVdecOmx->u1VdpId);
                    AUD_MMAoutEnable(prVdecOmx->rTunnel.u1AdecId, TRUE);//trigger aout to clear audio fifo to unlock write, dtv0069284
                }
            }
            break;
        case VDECOMX_SET_TYPE_SET_SEAMLESS_INFO:
            {
                VDECOMX_SET_TYPE_SEAMLESS_INFO_T* pSeamlessInfo = (VDECOMX_SET_TYPE_SEAMLESS_INFO_T*)(rSetparam.pvInParam);

                if (TRUE == pSeamlessInfo->fgseamless)
                {
                    if(!_VPUSH_SetSeamlessMode(prVdecOmx->pVPush, pSeamlessInfo->u4MaxWidth, pSeamlessInfo->u4MaxHeight, pSeamlessInfo->u4ResizerId))
                    {
                        VOMX_ERROR("%s(L:%d) _VPUSH_SetSeamlessMode fail", __FUNCTION__, __LINE__);
                        return -1;
                    }
                    VOMX_INFO("adaptive playback enabled, max size: %dx%d", pSeamlessInfo->u4MaxWidth, pSeamlessInfo->u4MaxHeight);
                }
            }
            break;
        case VDECOMX_SET_TYPE_SET_VDP_ID:
           {
               VDECOMX_SET_TYPE_VDP_INFO_T* pVdpInfo = (VDECOMX_SET_TYPE_VDP_INFO_T*)(rSetparam.pvInParam);
               VDEC_SET_INTO_T rVdecSetInfo;
               x_memset(&rVdecSetInfo, 0, sizeof(VDEC_SET_INTO_T));
               rVdecSetInfo.ucVdpId = pVdpInfo->u4Id;
               rVdecSetInfo.u4InfoMask = VDEC_PUSH_SET_INFO_VDP_ID;
               if(!_VPUSH_SetInfo(prVdecOmx->pVPush, &rVdecSetInfo))
               {
                   VOMX_ERROR("%s(L:%d) _VPUSH_SetInfo fail", __FUNCTION__, __LINE__);
                   return -1;
               }
               prVdecOmx->u1VdpId = rVdecSetInfo.ucVdpId;
               VOMX_INFO("use vdp%d\n", prVdecOmx->u1VdpId);
           }
           break;

               case VDECOMX_SET_TYPE_SET_TUNNEL:
            {
                VDEC_SET_INTO_T rVdecSetInfo;
                VDECOMX_SET_TYPE_TUNNEL_T rTunnelInfo;
                USR_SPACE_ACCESS_VALIDATE_ARG(rSetparam.pvInParam, VDECOMX_SET_TYPE_TUNNEL_T);
                COPY_FROM_USER_ARG(rSetparam.pvInParam, rTunnelInfo, VDECOMX_SET_TYPE_TUNNEL_T);
                VOMX_INFO("%stunnel mode!", (rTunnelInfo.u4AudioHwSync ? " " : "video only "));
                prVdecOmx->fgTunnel = TRUE;
                prVdecOmx->ePlayMode = VDECOMX_MODE_TUNNEL;
                x_memset(&rVdecSetInfo, 0, sizeof(VDEC_SET_INTO_T));
                rVdecSetInfo.u4InfoMask = VDEC_PUSH_SET_INFO_PLAY_MODE | VDEC_PUSH_SET_INFO_SIDE_BAND;
                rVdecSetInfo.eVpushPlayMode = VDEC_PUSH_MODE_TUNNEL;
                if(!_VPUSH_SetInfo(prVdecOmx->pVPush, &rVdecSetInfo))
                {
                    VOMX_ERROR("%s(L:%d) _VPUSH_SetInfo fail, Play mode: %d", __FUNCTION__, __LINE__, rVdecSetInfo.eVpushPlayMode);
                    return -1;
                }
                prVdecOmx->rTunnel.u4AudioHwSync = rTunnelInfo.u4AudioHwSync;
                prVdecOmx->rTunnel.u4VdecTunnelState = VDEXOMX_TUNNEL_STATE_INITED;
                prVdecOmx->rTunnel.u1AdecId = 0; // hardcode to main for now
                prVdecOmx->rTunnel.u4AoutPts = 0; // in android current design start pts is always 0
                if(prVdecOmx->rTunnel.u4AudioHwSync)
                {
                    // VERIFY(OSR_WOULD_BLOCK == x_sema_lock(prVdecOmx->rTunnel.hVideoReadySema, X_SEMA_OPTION_NOWAIT));
                }
            }
            break;

       case VDECOMX_SET_TYPE_SET_IMGRZ_ID:
           {
               UINT32 u4ImgrzIdMask = *(UINT32*)rSetparam.pvInParam;
               VOMX_INFO("config imgrz to 0x%x(mask)", u4ImgrzIdMask);
               vdecomx_config_imgrz(prVdecOmx, u4ImgrzIdMask);
           }
           break;

        case VDECOMX_SET_TYPE_VDEC_PAUSE:
           {            
               if(prVdecOmx->fgPlay)
               {
                   if(!_VPUSH_PauseVdec(prVdecOmx->pVPush))
                   {
                       VOMX_ERROR("%s(L:%d) _VPUSH_PauseVdec fail", __FUNCTION__, __LINE__);
                       return -1;
                   }
               }
               else
               {               
                    VOMX_ERROR("%s(L:%d) pending requested", __FUNCTION__, __LINE__);
                    prVdecOmx->fgPending = TRUE;
               }
           }
           break;
        case VDECOMX_SET_TYPE_VDEC_RESUME:
           {
               if(!_VPUSH_PlayVdec(prVdecOmx->pVPush))
               {
                   VOMX_ERROR("%s(L:%d) _VPUSH_PlayVdec", __FUNCTION__, __LINE__);
                   return -1;
               }
               prVdecOmx->fgPlay = TRUE;               
               prVdecOmx->fgPending = FALSE;
           }
           break;
		case VDECOMX_SET_TYPE_SET_GAMEMODEL:
		   {
              if(!_VPUSH_SetGameMode(prVdecOmx->pVPush))
              {
				  VOMX_ERROR("%s(L:%d) _VPUSH_SetGameMode", __FUNCTION__, __LINE__);
				  return -1;
			  }
		   }
		   break;

        case VDECOMX_SET_TYPE_SEND_LAST_EVENT:
            {
                VDECOMX_NFY_T rOmxEvent;
                INT32 i4Ret;
                VERIFY(OSR_WOULD_BLOCK == x_sema_lock(prVdecOmx->hCBSema, X_SEMA_OPTION_NOWAIT));
                rOmxEvent.eType = VDECOMX_NFY_TYPE_LAST_EVENT;
                vdecomx_put_event(prVdecOmx, &rOmxEvent);
                i4Ret = x_sema_lock_timeout(prVdecOmx->hCBSema, 500);
                if(i4Ret == OSR_TIMEOUT)
                {
                    VOMX_ERROR("wait cb thread end fail");
                    return -1;
                }
            }
            break;

        case VDECOMX_SET_TYPE_HDR10_INFO:
        {
            VDEC_HDR10_INFO_T rInfo;
            if (sizeof(VDECOMX_HDR10_INFO_T) != sizeof(VDEC_HDR10_INFO_T)) {
                VOMX_ERROR("VDECOMX_HDR10_INFO_T=%d, VDEC_HDR10_INFO_T=%d",
                    sizeof(VDECOMX_HDR10_INFO_T), sizeof(VDEC_HDR10_INFO_T));
                return -1;
            }
            USR_SPACE_ACCESS_VALIDATE_ARG(rSetparam.pvInParam, VDEC_HDR10_INFO_T);
            COPY_FROM_USER_ARG(rSetparam.pvInParam, rInfo, VDEC_HDR10_INFO_T);
            if (!_VPUSH_SetHDR10Info(prVdecOmx->pVPush, &rInfo))
            {
                VOMX_ERROR("_VPUSH_SetHDR10Info fail");
                return -1;
            }
            break;
        }

        case VDECOMX_SET_TYPE_HDR10_METADATA_INFO:
        {
            VDEC_HDR10_METADATA_INFO_T rInfo;
            if (sizeof(VDECOMX_HDR10_METADATA_INFO_T) != sizeof(VDEC_HDR10_METADATA_INFO_T)) {
                VOMX_ERROR("VDECOMX_HDR10_METADATA_INFO_T=%d, VDEC_HDR10_METADATA_INFO_T=%d",
                    sizeof(VDECOMX_HDR10_METADATA_INFO_T), sizeof(VDEC_HDR10_METADATA_INFO_T));
                return -1;
            }
            USR_SPACE_ACCESS_VALIDATE_ARG(rSetparam.pvInParam, VDEC_HDR10_METADATA_INFO_T);
            COPY_FROM_USER_ARG(rSetparam.pvInParam, rInfo, VDEC_HDR10_METADATA_INFO_T);
            if (!_VPUSH_SetHDR10MetadataInfo(prVdecOmx->pVPush, &rInfo))
            {
                VOMX_ERROR("_VPUSH_SetHDR10MetadataInfo fail");
                return -1;
            }
            break;
        }

        default:
            rSetparam.eRet = VDECOMX_MRESULT_FAIL;
            break;
    }

    COPY_TO_USER_ARG(arg, rSetparam, PAR_VDECOMX_SETPARAM);

    return 0;
}

int vdecomx_release(struct inode *inode, struct file *pfile)
{
//    UINT32 i;
    VDECOMX_HANDLE_T *prVdecOmx;
    VDECOMX_BUFFER_T *pBufNode = NULL;
    UINT32 u4Index;
    #if 0
    //?????
    if(!_fgInited)
    {
        LOG(5, "%s(L:%d) init\n", __FUNCTION__, __LINE__);
        _VPUSH_DecodeInit();
        _fgInited = TRUE;
        return -1;
    }
    #endif

    VOMX_DEBUG("release vdecomx begin");
    
    if(!pfile)
    {
        VOMX_ERROR("%s(L:%d) pfile null", __FUNCTION__, __LINE__);
        return -1;
    }
    prVdecOmx = (VDECOMX_HANDLE_T*)pfile->private_data;
    if(!prVdecOmx)
    {
        VOMX_ERROR("%s(L:%d) prVdecOmx null", __FUNCTION__, __LINE__);
        return -1;
    }

    for (u4Index = 0; u4Index < VDECOMX_INPUT_BUF_MAX; u4Index ++)
    {
        pBufNode = &(_prVDOOmxInputBuf[u4Index]);
        if ((NULL != pBufNode) && (NULL_HANDLE != pBufNode->hBuffer))
        {
            _vdecomx_free_input_buffer(prVdecOmx, pBufNode->hBuffer);
        }
    }
    
    if(prVdecOmx->fgPlay)
    {
        if(!_VPUSH_StopSync(prVdecOmx->pVPush))
        {
            VOMX_ERROR("%s(L:%d) _VPUSH_StopSync fail", __FUNCTION__, __LINE__);
            return -1;
        }
        prVdecOmx->fgPlay = FALSE;
    }

    if (0xFF != prVdecOmx->u1VdpId)
        B2R_UnRegCbFunc(prVdecOmx->u1VdpId, B2R_CB_FUNC_PTS_RENDER);

    if(prVdecOmx->fgTunnel)
    {
        if(prVdecOmx->rTunnel.u4AudioHwSync)
        {
            x_sema_lock(prVdecOmx->rTunnel.hVideoReadySema, X_SEMA_OPTION_NOWAIT);
        }
        B2R_UnRegCbFunc(prVdecOmx->u1VdpId, B2R_CB_FUNC_TRIG_AUD_TO_PLAY);
    }

    if(prVdecOmx->fgCvtResReady)
    {
#ifdef CC_VDEC_FBM_DYNAMIC_MAPPING
        BSP_UnMapReservedMem((VOID *)VIRTUAL(prVdecOmx->rImageConvert[0].u4CVTmpAddrY));
#endif 
        if(!prVdecOmx->fgFBM4K)
            _FreeSeamlessBuffer(prVdecOmx->rImageConvert[0].u4CVTmpAddrY);
        else
        {
            if(prVdecOmx->prFbmPool)
            {
                FBM_Free(prVdecOmx->prFbmPool);
                prVdecOmx->prFbmPool = NULL;
            }
        }
        prVdecOmx->fgCvtResReady = FALSE;
        VERIFY(x_msg_q_delete(prVdecOmx->hCvtMsgQ)== OSR_OK);
        prVdecOmx->hCvtMsgQ = NULL_HANDLE;
        prVdecOmx->fgTunnel = FALSE;
    }

    _u4VdecOmxOpenCnt --;

    if (0 == _u4VdecOmxOpenCnt)
    {
        // do something?
        _vdecomx_buffer_uninit();
    }

    prVdecOmx->eFmt = VDEC_FMT_MAX;
    if(prVdecOmx->pVPush)
    {
        _VPUSH_ReleaseVideoDecoder(prVdecOmx->pVPush);
        prVdecOmx->pVPush = NULL;
    }
    _vdecomx_free_kmem_handle_by_tag(prVdecOmx, VOMX_INVALID_VALUE);
    prVdecOmx->fgOccupied = FALSE;
    if(NULL_HANDLE != prVdecOmx->hEventMsgQ)
    {
        VERIFY(x_msg_q_delete(prVdecOmx->hEventMsgQ)== OSR_OK);
        VERIFY(x_sema_delete(prVdecOmx->hCBSema) == OSR_OK);
        prVdecOmx->hEventMsgQ = NULL_HANDLE;
        prVdecOmx->hCBSema = NULL_HANDLE;
    }
    
    pfile->private_data = NULL;
    VOMX_DEBUG("release vdecomx end");
    return 0;
}



//-----------------------------------------------------------------------------
/** vdec_ioctl.
 */
//-----------------------------------------------------------------------------
int vdecomx_ioctl(struct inode *inode,
                struct file *pfile,
                unsigned int cmd,
                 unsigned long arg)
{
    INT32  i4Ret = 0;
    VDECOMX_HANDLE_T *prVdecOmx = (VDECOMX_HANDLE_T *)pfile->private_data;
    if(!prVdecOmx)
    {
        return -1;
    }

    switch (cmd)
    {
        case VDEXOMX_CREATE:
            i4Ret = vdecomx_create(prVdecOmx, arg);
            break;
        case VDEXOMX_INIT:
            i4Ret = vdecomx_init(prVdecOmx, arg);
            break;
        case VDEXOMX_DECODE:
            i4Ret = vdecomx_decode(prVdecOmx, arg);
            break;
        case VDEXOMX_GETPARAM:
            i4Ret = vdecomx_getparam(prVdecOmx, arg);
            break;
        case VDEXOMX_SETPARAM:
            i4Ret = vdecomx_setparam(prVdecOmx, arg);
            break;
        case VDEXOMX_DEINIT:
            i4Ret = vdecomx_deinit(prVdecOmx, arg);
            break;
        case VDEXOMX_GETEVENT:
            i4Ret = vdecomx_get_event(prVdecOmx, arg);
            break;
        case VDEXOMX_GETLOGMASK:
            USR_SPACE_ACCESS_VALIDATE_ARG(arg, UINT32);
            COPY_TO_USER_ARG(arg, _u4VdecOmxLogValue, UINT32);
            break;
        default:
            break;
    }

    return i4Ret;
}

#ifdef USE_UNLOCK_IOCTL
long vdecomx_unlocked_ioctl(struct file *pfile, unsigned int cmd, unsigned long param)
{
    return vdecomx_ioctl(0, pfile, cmd, param);
}
#endif

struct file_operations vdecomx_fops = {
    .owner              = THIS_MODULE,
    .open               = vdecomx_open,
#ifdef USE_UNLOCK_IOCTL
    .unlocked_ioctl     = vdecomx_unlocked_ioctl,
#else
    .ioctl              = vdecomx_ioctl,
#endif
    .release            = vdecomx_release,
};

EXPORT_SYMBOL(_vdecomx_tunnelAudioCb);
#endif


