#if defined(ANDROID) || defined(ANDROID_TWO_WORLDS)
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/export.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include "imgrz_if.h"
#include "fbm_drvif.h"
#include "srm_drvif.h"
#include "vdp_drvif.h"
#include "vdp_if.h"
#include "b2r_if.h"
#include "b2r_drvif.h"
#include "vdec_drvif.h"
#include "vdo_if.h"
#include "pe_if.h"
#include "ostg_if.h"
#include "eeprom_if.h"
#include "drv_tdtv_drvif.h"
#include "gfx_drvif.h"
#include "gfx_if.h"
#include "osd_drvif.h"
#include "mt53_simple.h"
#include "mt53_accel.h"
#ifdef CONFIG_MTK_IOMMU
#include <linux/mtk_iommu.h>
#endif
#include <asm/io.h>
#include <linux/file_ops.h>

typedef unsigned long  UPTR;

#define VDP_MAIN          0
#define VDP_SUB           1
#define VDP_INVALID       2
#define VALUE_ALIGN(value, base) (((value) + ((base)-1)) & ~((base)-1))

typedef enum
{
    //initial state
    IMGRZ_INVALID,
    //free to use
    IMGRZ_FREE,
    //converting
    IMGRZ_CONVERT,
    //finish converting, to be displayed
    IMGRZ_COMPLETE
}IMGRZ_STATE_T;

typedef struct
{
    UINT32 u4ImgrzId;
    union {
    unsigned long ulSrcAddr;
    unsigned long long u8Padding;
    };
    UINT32 u4SrcWidth;
    UINT32 u4SrcHeight;
    UINT32 u4SrcPitch;
    UINT32 u4DstYAddr;
    UINT32 u4DstCAddr;
    UCHAR ucFbId;
    IMGRZ_STATE_T state;
}IMGRZ_PARAM_T;

//We assume there is only one software decode video need display on video path,
//sw decode + convert is very slow, performance of two sw decode videos would be very poor.
//if in native pip mode, another video should be hardware decode.
//this assumption would simplify the logic.
static UCHAR gFbgId = FBM_FBG_ID_UNKNOWN;
static UCHAR gVdpChannel = VDP_INVALID;
static UINT32 gWidth = 0;
static UINT32 gHeight = 0;
static UINT32 gPitch = 0;
static UINT32 gImgrzTotal = 0;
static UINT32 gImgrzFree = 0;
static UINT32 gImgrzComplete = 0;
static BOOL gForceStop = FALSE;
static IMGRZ_PARAM_T gImgrzParams[MAX_IMGRZ_NUM] = {};
static HANDLE_T _hImgrzFreeSema = 0;
static HANDLE_T _hImgrzCompleteSema = 0;
static HANDLE_T _hAtomicSema = 0;
static DEFINE_SPINLOCK(imgrzStateSpinLock);

int DumpVideoRawdata(UPTR u4SrcAddr, UINT32 u4SrcPitch, UINT32 u4SrcWidth, UINT32 u4SrcHeight)
{
    UPTR dumpBuf = GFX_GetTmpBuf();
    struct file *file;
    void *rawData = NULL;
    UINT32 size = 0;
    UINT64 offset = 0;
    const char *filePath = "/data/swvdec_rawdata.bin";

    GFX_Lock();
    GFX_MMU_Setting(TRUE, TRUE, 0);
    //don't care about the color mode here, u4SrcPitch is the length of Y
    GFX_SetSrc((UINT8 *)u4SrcAddr, CM_ARGB4444_DIRECT16, u4SrcPitch * 3 / 2);
    GFX_SetDst((UINT8 *)dumpBuf, CM_ARGB4444_DIRECT16, u4SrcPitch * 3 / 2);
    GFX_SetBltOpt((UINT32)D_GFXFLAG_NONE, 0x00000000, 0xffffffff);
    GFX_BitBlt(0, 0, 0, 0, u4SrcWidth, u4SrcHeight);
    GFX_Flush();
    GFX_Task_Wait_Sync();
    GFX_MMU_Set_Enable(FALSE);
    GFX_Unlock();

    file = FILE_Open(filePath, O_RDWR|O_CREAT|O_APPEND, 0644);

    if (!file)
    {
        printk("open %s failed\n", filePath);
        return -1;
    }
    size = u4SrcPitch * u4SrcHeight * 3 / 2;
    rawData = ioremap(dumpBuf, size);
    size = FILE_Write(file, (unsigned char *)rawData, size, &offset);
    printk("written raw data size:%ld\n", size);
    iounmap(rawData);
    FILE_Close(file);
    return 0;
}

int DumpVideoConverteddata(UCHAR ucFbId)
{
    UPTR dumpBuf = GFX_GetTmpBuf();
    UPTR u4DstYAddr, u4DstCAddr;
    struct file *file;
    void *rawData = NULL;
    UINT32 size = 0;
    UINT64 offset = 0;
    const char *filePath = "/data/swvdec_converteddata.bin";

    FBM_GetFrameBufferAddr(gFbgId, ucFbId, (UINT32 *)(&u4DstYAddr), (UINT32 *)(&u4DstCAddr));

    GFX_Lock();
    GFX_MMU_Setting(FALSE, FALSE, 0);
    //don't care about the color mode here, u4SrcPitch is the length of Y
    GFX_SetSrc((UINT8 *)u4DstYAddr, CM_ARGB4444_DIRECT16, gPitch);
    GFX_SetDst((UINT8 *)dumpBuf, CM_ARGB4444_DIRECT16, gPitch);
    GFX_SetBltOpt((UINT32)D_GFXFLAG_NONE, 0x00000000, 0xffffffff);
    GFX_BitBlt(0, 0, 0, 0, gWidth, gHeight);
    GFX_Flush();
    GFX_Task_Wait_Sync();
    GFX_MMU_Set_Enable(FALSE);
    GFX_Unlock();

    file = FILE_Open(filePath, O_WRONLY|O_CREAT|O_APPEND, 0644);

    if (!file)
    {
        printk("open %s failed\n", filePath);
        return -1;
    }
    size = gPitch * gHeight;
    rawData = ioremap(dumpBuf, size);
    size = FILE_Write(file, (unsigned char *)rawData, size, &offset);
    printk("written Y data size:%ld\n", size);
    iounmap(rawData);
    FILE_Close(file);

    dumpBuf = GFX_GetTmpBuf();
    GFX_Lock();
    GFX_MMU_Setting(FALSE, FALSE, 0);
    //don't care about the color mode here, u4SrcPitch is the length of Y
    GFX_SetSrc((UINT8 *)u4DstCAddr, CM_ARGB4444_DIRECT16, gPitch);
    GFX_SetDst((UINT8 *)dumpBuf, CM_ARGB4444_DIRECT16, gPitch);
    GFX_SetBltOpt((UINT32)D_GFXFLAG_NONE, 0x00000000, 0xffffffff);
    GFX_BitBlt(0, 0, 0, 0, gWidth/2, gHeight/2);
    GFX_Flush();
    GFX_Task_Wait_Sync();
    GFX_MMU_Set_Enable(FALSE);
    GFX_Unlock();

    file = FILE_Open(filePath, O_WRONLY|O_CREAT|O_APPEND, 0644);

    if (!file)
    {
        printk("open %s failed\n", filePath);
        return -1;
    }
    size = gPitch * gHeight/2;
    rawData = ioremap(dumpBuf, size);
    size = FILE_Write(file, (unsigned char *)rawData, size, &offset);
    printk("written C data size:%ld\n", size);
    iounmap(rawData);
    FILE_Close(file);
    return 0;
}


UINT32 DisplayVideoFbmBuffer(UCHAR ucFbId)
{
    UINT32 idx = 0;
    unsigned long flags;
    //DumpVideoConverteddata(ucFbId);
    //Display
    FBM_SetFrameBufferStatus(gFbgId, ucFbId, (UCHAR)FBM_FB_STATUS_DISPLAYQ);
    // Unmute B2R
    VDP_SetMode(gVdpChannel, VDP_MODE_NORMAL);
    spin_lock_irqsave(&imgrzStateSpinLock, flags);
    for (idx = 0; idx < gImgrzTotal; idx++)
    {
        printk("%s(%d) idx:%d state:%p(%d) ucFbId:%p(%d) ulSrcAddr:%p(%x) gImgrzTotal:%p gImgrzFree:%p _hImgrzFreeSema:%p\n", __FUNCTION__, __LINE__,
               idx, &gImgrzParams[idx].state, gImgrzParams[idx].state,&gImgrzParams[idx].ucFbId, gImgrzParams[idx].ucFbId,&gImgrzParams[idx].ulSrcAddr,
               gImgrzParams[idx].ulSrcAddr,&gImgrzTotal,&gImgrzFree,&_hImgrzFreeSema);
        if ((gImgrzParams[idx].state == IMGRZ_COMPLETE)&& 
			(gImgrzParams[idx].ucFbId == ucFbId))
        {
            //Only after displayed, this imgrz could be free.
            //Because total number of fbm buffers of type FBM_VDEC_JPEG is two.
            //only B2R get the complete buffer by setting status of FBM_FB_STATUS_DISPLAYQ,
            //it could be empty again. in addition to get free imgrz,
            //we need to make sure to get empty fbm buffer.
            //after display this buffer. clear the addr
            //printk("%s(%d) addr:%x fbId:%d dispalyed freeImgrz:%d\n", __FUNCTION__, __LINE__,
                //gImgrzParams[idx].ulSrcAddr, ucFbId, gImgrzFree+1);
            gImgrzParams[idx].ulSrcAddr = 0;
            gImgrzParams[idx].state = IMGRZ_FREE;
            gImgrzFree++;
            x_sema_unlock(_hImgrzFreeSema);
            x_sema_lock(_hImgrzFreeSema, X_SEMA_OPTION_NOWAIT);
			//printk("%s(%d) _hImgrzFreeSema:%x\n", __FUNCTION__, __LINE__, _hImgrzFreeSema);
        }
    }
    //printk("%s(%d) 1 imgrzStateSpinLock:%p(%x)\n", __FUNCTION__, __LINE__, &imgrzStateSpinLock,imgrzStateSpinLock);
    spin_unlock_irqrestore(&imgrzStateSpinLock,flags);
    //printk("%s(%d) 2 imgrzStateSpinLock:%p(%x)\n", __FUNCTION__, __LINE__, &imgrzStateSpinLock,imgrzStateSpinLock);
    return 0;
}

//called in imgrz ISR
static INT32 Imgrz_Done_Callback(UINT32 u4Which, IMGRZ_DONE_TYPE u4Type, VOID *pData)
{
    unsigned long flags;
    IMGRZ_PARAM_T *pParam = (IMGRZ_PARAM_T *)pData;

    if (gForceStop == FALSE)
    {
        spin_lock_irqsave(&imgrzStateSpinLock, flags);
        pParam->state = IMGRZ_COMPLETE;
        gImgrzComplete++;
        x_sema_unlock(_hImgrzCompleteSema);
        x_sema_lock(_hImgrzCompleteSema, X_SEMA_OPTION_NOWAIT);
        spin_unlock_irqrestore(&imgrzStateSpinLock,flags);
    }

    return 0;
}

static INT32 FbmBufferRelease(void)
{
    if (gFbgId != FBM_FBG_ID_UNKNOWN)
    {
        printk("%s(%d) - width:%d height:%d pitch:%d fbgId:%d vdpchannel:%d\n", __FUNCTION__, __LINE__,
            gWidth,gHeight,gPitch,gFbgId,gVdpChannel);
        VDP_SetMode(gVdpChannel, VDP_MODE_BG);
        FBM_FbgChgNotify(FBM_FBG_ID_UNKNOWN, 0);
        FBM_ReleaseGroup(gFbgId);
        gFbgId = FBM_FBG_ID_UNKNOWN;
    }
    return 0;
}

INT32 FbmBufferInit(UINT32 u4Width, UINT32 u4Height, UINT32 u4Pitch)
{
    UCHAR ucFbgType = FBM_FBG_TYPE_UNKNOWN, ucFbgId = FBM_FBG_ID_UNKNOWN;
    FBM_SEQ_HDR_T* prFbmSeqHdr = NULL;
    FBM_CREATE_FBG_PAR_T rPar;
    UINT8 u1VdecId = MAX_ES_NS;

    if((u4Width <= 0) || (u4Height <= 0) || (u4Pitch <= 0))
    {
        printk("%s(%d) args fault, width:%d height:%d pitch:%d\n", __FUNCTION__, __LINE__, u4Width, u4Height, u4Pitch);
        return -1;
    }

    if((u4Width == gWidth) && (u4Height == gHeight) && (u4Pitch == gPitch) && (gFbgId != FBM_FBG_ID_UNKNOWN))
    {
        printk("%s(%d) buffer (w%d-h%d-p%d) aready init\n", __FUNCTION__, __LINE__, u4Width, u4Height, u4Pitch);
        return 0;
    }

    FbmBufferRelease();

    if(gVdpChannel == VDP_MAIN)
    {
        u1VdecId = NON_HW_ES_ID_MAIN;
    }
    else
    {
        u1VdecId = NON_HW_ES_ID_SUB;
    }

    x_memset(&rPar, 0, sizeof(FBM_CREATE_FBG_PAR_T));
    rPar.u1VdecId = u1VdecId;

    ucFbgType = FBM_SelectGroupType(u4Pitch, u4Height);
    FBM_SetColorMode(FBM_CM_420);
    ucFbgId = FBM_CreateGroupExt(ucFbgType, FBM_VDEC_H265, u4Pitch, u4Height, &rPar);
    if(ucFbgId == FBM_FBG_ID_UNKNOWN)
    {
        printk("%s(%d)  Create Fbg Fail!! input resolution %dx%d\n", __FUNCTION__, __LINE__, u4Width, u4Height);
        return -1;
    }

    prFbmSeqHdr = FBM_GetFrameBufferSeqHdr(ucFbgId);
    if (!prFbmSeqHdr)
    {
        FBM_ReleaseGroup(ucFbgId);
        printk("%s(%d)  Create Fbg Fail!! input resolution %dx%d\n", __FUNCTION__, __LINE__, u4Width, u4Height);
        return -1;
    }

    prFbmSeqHdr->u2HSize = u4Width;
    prFbmSeqHdr->u2VSize = u4Height;
    prFbmSeqHdr->u2OrgHSize = prFbmSeqHdr->u2HSize;
    prFbmSeqHdr->u2OrgVSize = prFbmSeqHdr->u2VSize;
    prFbmSeqHdr->u2LineSize = u4Pitch ? (((u4Pitch + 15) >> 4) << 4) : (((u4Width + 15) >> 4) << 4);
    prFbmSeqHdr->fgProgressiveSeq = TRUE;
    prFbmSeqHdr->fgRasterOrder = TRUE;
    prFbmSeqHdr->fg422Mode = FALSE;
    prFbmSeqHdr->ucFrmRatCod = MPEG_FRAME_RATE_30;

    bApiVideoSetVideoSrc(gVdpChannel, gVdpChannel ? SV_VS_DT2 : SV_VS_DT1);
    VDP_SetInput(gVdpChannel, u1VdecId, 0);
    VDP_SetMode(gVdpChannel, VDP_MODE_BG);

    FBM_SetPlayMode(ucFbgId, FBM_FBG_MM_MODE);
    FBM_FbgChgNotify(ucFbgId, u1VdecId);

    FBM_SetFrameBufferFlag(ucFbgId, FBM_FLAG_SEQ_CHG_SPEEDUP);
    gFbgId = ucFbgId;
    gWidth = u4Width;
    gHeight = u4Height;
    gPitch = u4Pitch;
    printk("%s(%d) - width:%d height:%d pitch:%d fbgId:%d vdpchannel:%d\n", __FUNCTION__, __LINE__,
        u4Width,u4Height,u4Pitch,gFbgId,gVdpChannel);
    return 0;
}

static VOID ConvertFormatByImgrz(IMGRZ_PARAM_T* pParam)
{
    RZ_JPG_SCL_PARAM_SET_T rJpgParam;
    UINT32 u4SrcAddr = (UINT32)(pParam->ulSrcAddr);

    //from YV12 to yc420
    x_memset((void *)&rJpgParam, 0, sizeof(RZ_OSD_SCL_PARAM_SET_T));

    rJpgParam.u4IsRsIn    = 1; //block/raster in
    rJpgParam.u4YSrcBase1   = u4SrcAddr;
    rJpgParam.u4YSrcBase2   = u4SrcAddr;

    rJpgParam.u4YSrcBufLen  = pParam->u4SrcPitch;
    rJpgParam.u4CSrcBufLen  = pParam->u4SrcPitch >> 1;

    rJpgParam.u4YSrcHOffset = 0;
    rJpgParam.u4YSrcVOffset = 0;
    rJpgParam.u4YSrcW     = pParam->u4SrcWidth;
    rJpgParam.u4YSrcH     = pParam->u4SrcHeight;

    rJpgParam.u4CbSrcBase1  = (u4SrcAddr + pParam->u4SrcPitch*pParam->u4SrcHeight + pParam->u4SrcPitch*pParam->u4SrcHeight/4);
    rJpgParam.u4CbSrcBase2  = rJpgParam.u4CbSrcBase1;
    rJpgParam.u4CbSrcHOffset      = 0;
    rJpgParam.u4CbSrcVOffset      = 0;
    rJpgParam.u4CbSrcW    = pParam->u4SrcWidth >> 1;
    rJpgParam.u4CbSrcH    = pParam->u4SrcHeight >> 1;

    rJpgParam.u4CrSrcBase1  = (u4SrcAddr + pParam->u4SrcPitch*pParam->u4SrcHeight);
    rJpgParam.u4CrSrcBase2  = rJpgParam.u4CrSrcBase1;
    rJpgParam.u4CrSrcHOffset = 0;
    rJpgParam.u4CrSrcVOffset = 0;
    rJpgParam.u4CrSrcW = pParam->u4SrcWidth >> 1;
    rJpgParam.u4CrSrcH = pParam->u4SrcHeight >> 1;

    rJpgParam.u4IsRsOut = 1;    //block/raster out
    rJpgParam.u4OutSwap = 0;
    rJpgParam.u4ColorComp = 7; //y,cb,cr exists

    rJpgParam.u4YTgBase   = pParam->u4DstYAddr;
    rJpgParam.u4CTgBase   = pParam->u4DstCAddr;

    rJpgParam.u4IsJpgSameaddr = 0;
    rJpgParam.u4YTgHOffset = 0;
    rJpgParam.u4YTgVOffset = 0;
    rJpgParam.u4YTgBufLen = pParam->u4SrcPitch;
    rJpgParam.u4CTgBufLen = pParam->u4SrcPitch;

    rJpgParam.u4IsVdo2Osd = 0;     // output in OSD format ?
    rJpgParam.u4YTgCM = 0;
    rJpgParam.u4YTgW = pParam->u4SrcWidth;
    rJpgParam.u4YTgH = pParam->u4SrcHeight;
    rJpgParam.u4OutMode = E_RZ_VDO_OUTMD_420;
    rJpgParam.u4CTgW = pParam->u4SrcWidth >> 1;
    rJpgParam.u4CTgH = pParam->u4SrcHeight >> 1;

    rJpgParam.u4IsFstBl = 1;
    rJpgParam.u4IsLstBl = 1;
    rJpgParam.u4JpgVfacY = 1;
    rJpgParam.u4JpgVfacCb = 0;
    rJpgParam.u4JpgVfacCr = 0;
    rJpgParam.fgBlassign = TRUE; //indicates to directly assign input/output address

    //DumpVideoRawdata(u4SrcAddr,pParam->u4SrcPitch,pParam->u4SrcWidth,pParam->u4SrcHeight);

    IMGRZ_Lock_Ex(pParam->u4ImgrzId);
    IMGRZ_ReInit_Ex(pParam->u4ImgrzId);
#ifdef GFX_IOMMU_IOVA_SUPPORT
    rJpgParam.u4RzIOMMU_MMUTableAddr = MtkIommuGetPhyPgd();
#else
    rJpgParam.u4RzIOMMU_MMUTableAddr = HalGetMMUTableAddress();
#endif
    IMGRZ_Cfg_MMU_IO_Ex(pParam->u4ImgrzId, (BOOL) 1, (BOOL) 0);
    IMGRZ_SetScaleOpt_Ex(pParam->u4ImgrzId, E_RZ_INOUT_TYPE_JPGMD);
    IMGRZ_Async_Ex(pParam->u4ImgrzId, Imgrz_Done_Callback, (void *)pParam);
    IMGRZ_Scale_Ex(pParam->u4ImgrzId, &rJpgParam);
    IMGRZ_Flush_Ex(pParam->u4ImgrzId);
    IMGRZ_Wait_Ex(pParam->u4ImgrzId);
    IMGRZ_Unlock_Ex(pParam->u4ImgrzId);
}

INT32 SetImgrzIdMask(unsigned long ulArg)
{
    int idx = 0;
    unsigned long flags;
    IMGRZ_ID_MASK_T rInfo;

    if (copy_from_user(&rInfo, (void *)ulArg, sizeof(IMGRZ_ID_MASK_T)))
    {
        printk("%s(%d) - copy_from_user() fail\n", __FUNCTION__, __LINE__);
        return -EFAULT;
    }
    printk("%s: channel:%d, imgrz mask:%d\n", __FUNCTION__, rInfo.u4VdpChannel, rInfo.u4ImgrzIdMask);
    if(gImgrzFree != gImgrzTotal)
    {
        while(idx < 50)
        {
             if(gImgrzFree != gImgrzTotal)
             {
                 x_sema_lock_timeout(_hImgrzFreeSema, 2);
             }else
             {
                 break;
             }
             idx++;
        }
        idx = 0;
        if(gImgrzFree != gImgrzTotal)
        {
            printk("%s(%d) wait timeout display convert or complete status buffer freeImgrz:%d\n", __FUNCTION__, __LINE__,gImgrzFree);
        }
    }

    //when video paly end. acodec will call setImgrzMask 0 on MtkOMXNodeInstancePlugin::free()
    //also call vdp_set_out_channel to close vdp and disable b2r,so we need release fbm buffer.
    //when another video start play.setImgrzMask 3. but video layer is not new .so we need fbm buffer init here
    gVdpChannel = (UCHAR)rInfo.u4VdpChannel;
    if(rInfo.u4ImgrzIdMask == 0)
    {
        FbmBufferRelease();
    }else
    {
        if((gFbgId == FBM_FBG_ID_UNKNOWN) && (gWidth != 0))
        {
            FbmBufferInit(gWidth,gHeight,gPitch);
        }
    }

    spin_lock_irqsave(&imgrzStateSpinLock, flags);
    gImgrzTotal = 0;
    gImgrzFree = 0;
    gImgrzComplete = 0;

    for (; idx < MAX_IMGRZ_NUM; idx++)
    {
        UINT32 u4Id = rInfo.u4ImgrzIdMask & (1 << idx);
        if (u4Id != 0)
        {
            gImgrzParams[idx].u4ImgrzId = u4Id;
            gImgrzParams[idx].state = IMGRZ_FREE;
            gImgrzTotal++;
        }
        else
        {
            gImgrzParams[idx].u4ImgrzId = 0;
            gImgrzParams[idx].state = IMGRZ_INVALID;
        }
    }
    gImgrzFree = gImgrzTotal;
    if (gImgrzTotal == 0)
    {
        gForceStop = TRUE;
    }
    else
    {
        gForceStop = FALSE;
    }
    spin_unlock_irqrestore(&imgrzStateSpinLock,flags);
    return 0;
}

UINT32 GetNextImgrzParamIdx(void)
{
    int idx = 0;
    unsigned long flags;

    spin_lock_irqsave(&imgrzStateSpinLock, flags);
    for (; idx < gImgrzTotal; idx++)
    {
        if (gImgrzParams[idx].state == IMGRZ_FREE)
        {
            gImgrzFree--;
            gImgrzParams[idx].state = IMGRZ_CONVERT;
            break;
        }
    }
    spin_unlock_irqrestore(&imgrzStateSpinLock,flags);

    return idx;
}

//called in hwc compose thread
INT32 ConvertVideoColorFormat(unsigned long ulArg)
{
    UCHAR ucFbId;
    UPTR u4DstYAddr, u4DstCAddr;
    FBM_PIC_HDR_T *prFbmPicHdr = NULL;
    UINT32 idx = 0;
    IMGRZ_PARAM_T *pParam = NULL;
    SWDEC_BUFFER_T srcBuffer;
    //MTIOMMU_PIN_RANGE_T tPinRange = {0};
    //UINT32 luma_stride;
    //UINT32 chroma_stride;

    if (copy_from_user(&srcBuffer, (void *)ulArg, sizeof(SWDEC_BUFFER_T)))
    {
        printk("%s(%d) - copy_from_user() fail\n", __FUNCTION__, __LINE__);
        return -EFAULT;
    }

    //wait for free imgrz
    if((gImgrzFree == 0) && (gImgrzTotal != 0))
    {
        if((OSR_TIMEOUT == x_sema_lock_timeout(_hImgrzFreeSema, 40)))
        {
            if(gImgrzFree == 0)
            {
                printk("%s: wait 40ms for free imgrz timeout gImgrzFree:%d\n", __FUNCTION__,gImgrzFree);
                return -1;
            }
        }
        //printk("%s: wait for free imgrz gImgrzFree:%d\n", __FUNCTION__,gImgrzFree);
    }
    //we need flush cpu page table after get free imgrz. becasue the buffer maybe unmap when wait.
    //if mxx_iomap_user_pages return fail(buffer is unmap).we need to drop this frame.
    //but the imgrz state can not change(free->convert.imgrzfree--)
    //if the addr is converting.we need to wait when unmap this addr(WaitImgrzConvertDone)
    #if 0
    tPinRange.start = (UINT32)(srcBuffer.ulAddr);
    luma_stride = VALUE_ALIGN(srcBuffer.u4Width, 128);
    chroma_stride = VALUE_ALIGN(luma_stride/2, 128);
    tPinRange.size = (VALUE_ALIGN(srcBuffer.u4Height, 2))*(luma_stride + chroma_stride);
    if (mxx_iomap_user_pages(&tPinRange) <= 0)
    {
        printk("%s(%d) mxx_iomap_user_pages fail!", __FUNCTION__, __LINE__);
        return -1;
    }
    #endif

    if((srcBuffer.u4Width != gWidth) || (srcBuffer.u4Height != gHeight) || (srcBuffer.u4Pitch != gPitch))
    {
        while(idx < 50)
        {
             if(gImgrzFree != gImgrzTotal)
             {
                 x_sema_lock_timeout(_hImgrzFreeSema, 2);
             }else
             {
                 break;
             }
             idx++;
        }
        idx = 0;
        if(gImgrzFree != gImgrzTotal)
        {
            printk("%s(%d) wait timeout display the converting frame freeImgrz:%d\n", __FUNCTION__, __LINE__, gImgrzFree);
        }
        printk("%s(%d) buffer (w%d-h%d-p%d)-->(w%d-h%d-p%d),need fbm buffer init\n", __FUNCTION__, __LINE__,
            gWidth,gHeight,gPitch,srcBuffer.u4Width,srcBuffer.u4Height,srcBuffer.u4Pitch);
        FbmBufferRelease();
        FbmBufferInit(srcBuffer.u4Width,srcBuffer.u4Height,srcBuffer.u4Pitch);
    }

    ucFbId = FBM_GetEmptyFrameBuffer(gFbgId, (UINT32)500);
    //shouldn't fail!!!
    if (ucFbId == (UCHAR)FBM_FB_ID_UNKNOWN)
    {
        printk("%s(%d) Get empty frame buffer failed.\n", __FUNCTION__, __LINE__);
        return -1;
    }

    FBM_GetFrameBufferAddr(gFbgId, ucFbId, (UINT32 *)(&u4DstYAddr), (UINT32 *)(&u4DstCAddr));
    prFbmPicHdr = FBM_GetFrameBufferPicHdr(gFbgId, ucFbId);
    if ((prFbmPicHdr == NULL) || (u4DstYAddr == 0) || (u4DstCAddr == 0))
    {
        printk("%s(%d) Alloc frame buffer failed.\n", __FUNCTION__, __LINE__);
        return -1;
    }

    idx = GetNextImgrzParamIdx();
    pParam = &gImgrzParams[idx];
    pParam->ulSrcAddr = srcBuffer.ulAddr;
    pParam->u4SrcWidth = srcBuffer.u4Width;
    pParam->u4SrcHeight = srcBuffer.u4Height;
    pParam->u4SrcPitch = srcBuffer.u4Pitch;
    pParam->u4DstYAddr = u4DstYAddr;
    pParam->u4DstCAddr = u4DstCAddr;
    pParam->ucFbId = ucFbId;

    //printk("%s(%d) addr:%x w:%d h:%d idx:%d freeImgrz:%d\n", __FUNCTION__, __LINE__,
    //    srcBuffer.ulAddr,srcBuffer.u4Width,srcBuffer.u4Height,idx,gImgrzFree);

    ConvertFormatByImgrz(pParam);
    return 0;
}

//called in hwc present thread
INT32 GetConvertedBuffers(unsigned long ulArg)
{
    CONVERT_RESULT_T result;
    UINT32 idx = 0;
    unsigned long flags;
    x_memset(&(result.completeBuffers), 0, sizeof(CONVERT_RESULT_T));

    if(gImgrzFree == gImgrzTotal)
    {
        printk("%s(%d) no imgrz converting. no need wait\n", __FUNCTION__, __LINE__);
        return -1;
    }

    //wait for converted buffer
    if (gImgrzComplete == 0)
    {
        if((OSR_TIMEOUT == x_sema_lock_timeout(_hImgrzCompleteSema, 40)))
        {
            if(gImgrzComplete == 0)
            {
                printk("%s: wait 40ms for converted buffer timeout gImgrzComplete:%d\n", __FUNCTION__, gImgrzComplete);
                return -1;
            }
        }
        //printk("%s: wait for converted buffer gImgrzComplete:%d\n", __FUNCTION__, gImgrzComplete);
    }
    spin_lock_irqsave(&imgrzStateSpinLock, flags);
    for (idx = 0; idx < gImgrzTotal; idx++)
    {
        //we also need to get the uncomplete buffer addr. it used for hwc to judge whether the 
        //current frame addr to present is valid and not return in convert function. if it return fail in compose thread.
        //we can not wait this frame complete.
        COMPLETE_BUFFER_T *completeBuffer = &(result.completeBuffers[idx]);
        completeBuffer->ulSrcAddr = gImgrzParams[idx].ulSrcAddr;
        completeBuffer->ucFbId = gImgrzParams[idx].ucFbId;
        if (gImgrzParams[idx].state == IMGRZ_COMPLETE)
        {
            completeBuffer->bComplete = TRUE;
            gImgrzComplete--;
        }else{
            completeBuffer->bComplete = FALSE;
        }
    }
	//printk("%s(%d) 1 imgrzStateSpinLock:%p(%x)\n", __FUNCTION__, __LINE__, &imgrzStateSpinLock,imgrzStateSpinLock);
    spin_unlock_irqrestore(&imgrzStateSpinLock,flags);
	//printk("%s(%d) 2 imgrzStateSpinLock:%p(%x)\n", __FUNCTION__, __LINE__, &imgrzStateSpinLock,imgrzStateSpinLock);
    if (copy_to_user((void *)ulArg, &result, sizeof(CONVERT_RESULT_T)))
    {
        printk("%s(%d) - copy_to_user() fail\n", __FUNCTION__, __LINE__);
        return -EFAULT;
    }
    return 0;
}

//called in gralloc_unregister_buffer
INT32 WaitImgrzConvertDone(unsigned long ulArg)
{
    UINT32 idx;
    for (idx = 0; idx < gImgrzTotal; idx++)
    {
        if (gImgrzParams[idx].ulSrcAddr == ulArg)
        {
            if(gImgrzParams[idx].state == IMGRZ_CONVERT)
            {
                while(gImgrzParams[idx].state == IMGRZ_CONVERT)
                {
                    printk("%s(%d) Delay 2ms waiting imgrz convert done ,addr:%x(%dx%d) will unmap\n", __FUNCTION__, __LINE__,
                        ulArg,gImgrzParams[idx].u4SrcWidth,gImgrzParams[idx].u4SrcHeight);
                    x_thread_delay(2);
                }
            }else{
                    printk("%s(%d) buffer addr:%x(%dx%d) will unmap, now state:%d\n", __FUNCTION__, __LINE__,
                        ulArg,gImgrzParams[idx].u4SrcWidth,gImgrzParams[idx].u4SrcHeight,gImgrzParams[idx].state);
            }
            break;
        }
    }
    //printk("%s(%d) buffer:%x will unmap, can not access later\n", __FUNCTION__, __LINE__, ulArg);
    return 0;
}

INT32 SwVdecOnVdp_Ioctl(UINT32 i4Cmd, unsigned long ulArg)
{
    FBM_BUFFER_INFO_T rInfo;
    INT32 ret = -EFAULT;
    switch (i4Cmd)
    {
    case SIMPLE_VDP_CONVERT_INIT:
        if (copy_from_user(&rInfo, (void *)ulArg, sizeof(FBM_BUFFER_INFO_T)))
        {
            printk("%s(%d) - copy_from_user() fail\n", __FUNCTION__, __LINE__);
            return -EFAULT;
        }
        return FbmBufferInit(rInfo.u4Width,rInfo.u4Height,rInfo.u4Pitch);
    case SIMPLE_VDP_CONVERT_DEINIT:
        return FbmBufferRelease();
    case SIMPLE_VDP_SET_IMGRZ_ID_MASK:
        x_sema_lock(_hAtomicSema, X_SEMA_OPTION_WAIT);
        ret = SetImgrzIdMask(ulArg);
        x_sema_unlock(_hAtomicSema);
        return ret;
    case SIMPLE_VDP_CONVERT_FORMAT:
        //atomic.ensure imgrz status being convert.thus,setImgrzIdMask will wait finsih this frame
        x_sema_lock(_hAtomicSema, X_SEMA_OPTION_WAIT);
        if (gForceStop == TRUE)
        {
            printk("Convert video color format has been forced to stop\n");
        }
        else
        {
            ret = ConvertVideoColorFormat(ulArg);
        }
        x_sema_unlock(_hAtomicSema);
        return ret;
    case SIMPLE_VDP_GET_COMPLETE:
        return GetConvertedBuffers(ulArg);
    case SIMPLE_VDP_DISPLAY:
        return DisplayVideoFbmBuffer((UCHAR)ulArg);
    case SIMPLE_VDP_BUFFER_UNMAP:
        //atomic.ensure unmap addr status is convert when ConvertVideoColorFormat called.
        x_sema_lock(_hAtomicSema, X_SEMA_OPTION_WAIT);
        ret = WaitImgrzConvertDone(ulArg);
        x_sema_unlock(_hAtomicSema);
        return ret;
    default:
        return -EFAULT;
    }
}

INT32 PanelCmd_Ioctl(UINT32 i4Cmd, unsigned long ulArg)
{
#ifdef CC_QUIET_HOT_BOOT_SUPPORT
    //HWC should not turn on panel due to QHB
    DTVCFG_T rDtvCfg;
#endif

    if (i4Cmd == SIMPLE_PANEL_POWER)
    {
#ifdef CC_QUIET_HOT_BOOT_SUPPORT
        EEPDTV_GetCfg(&rDtvCfg);
        if(rDtvCfg.u1Flags4 & DTVCFG_FLAG4_QUIET_HOT_BOOT)
        {
            printk("%s(%d) HWC should not turn on panel due to QHB\n", __FUNCTION__, __LINE__);
        }
        else
#endif
        {
#if 1 //defined(CC_S_PLATFORM) && (defined(CC_OX_CUST) || defined(CC_WK_CUST) || defined(CC_HK_CUST))
            vApiPanelPowerSequenceEx((BOOL)ulArg);
            printk("%s(%d) vApiPanelPowerSequenceEx(%d)\n", __FUNCTION__, __LINE__, ulArg);
#else
            vApiPanelPowerSequence((BOOL)ulArg);
            printk("%s(%d) vApiPanelPowerSequence(%d)\n", __FUNCTION__, __LINE__, ulArg);
#endif
        }
    }

    return 0;
}

INT32 HdrCmd_Ioctl(UINT32 i4Cmd, unsigned long ulArg)
{
    if (i4Cmd == SIMPLE_HDR_GET_CAPABILITIES)
    {
        HDR_CAPABILITIES_T rHdrInfo;
        UINT32 *upTypes = NULL;
        UINT32 u4NumTypes = 0;

        if ((void *)ulArg == NULL)
        {
            printk("%s(%d) - ulArg is NULL\n", __FUNCTION__, __LINE__);
            return -EFAULT;
        }
        if (copy_from_user(&rHdrInfo, (void *)ulArg, sizeof(HDR_CAPABILITIES_T)))
        {
            printk("%s(%d) - copy_from_user() fail\n", __FUNCTION__, __LINE__);
            return -EFAULT;
        }
        u4NumTypes = rHdrInfo.u4NumTypes;
        if (u4NumTypes != 0)
        {
            if((upTypes = kmalloc((rHdrInfo.u4NumTypes) * sizeof(UINT32), GFP_KERNEL)) == NULL)
            {
                printk("%s(%d) - kmalloc fail\n", __FUNCTION__, __LINE__);
                return -ENOMEM;
            }
        }
        vDrvGetHDRCapabilities(&rHdrInfo.u4NumTypes, upTypes, &rHdrInfo.u4MaxLuminance,
                       &rHdrInfo.u4MaxAverageLuminance, &rHdrInfo.u4MinLuminance);
        printk("%s(%d) - u4NumTypes=%d,u4Types=%p,u4MaxLuminance=%d,u4MaxAverageLuminance=%d,u4MinLuminance=%d\n",
                __FUNCTION__, __LINE__, rHdrInfo.u4NumTypes, upTypes, rHdrInfo.u4MaxLuminance,
                rHdrInfo.u4MaxAverageLuminance, rHdrInfo.u4MinLuminance);
        if (upTypes != NULL)
        {
            if (copy_to_user((void __user *)(UPTR)rHdrInfo.u8Types, upTypes, u4NumTypes * sizeof(UINT32)))
            {
                printk("%s(%d) - copy_to_user() fail\n", __FUNCTION__, __LINE__);
                kfree(upTypes);
                return -EFAULT;
            }
            kfree(upTypes);
        }
        if (copy_to_user((void __user *)ulArg, &rHdrInfo, sizeof(HDR_CAPABILITIES_T)))
        {
            printk("%s(%d) - copy_to_user() fail\n", __FUNCTION__, __LINE__);
            return -EFAULT;
        }
    }

    return 0;
}

INT32 mt53simple_init(void)
{
    if (!_hAtomicSema)
    {
        VERIFY(x_sema_create(&_hAtomicSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_UNLOCK) == OSR_OK);
    }
    if (!_hImgrzFreeSema)
    {
        VERIFY(x_sema_create(&_hImgrzFreeSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);
    }
    if (!_hImgrzCompleteSema)
    {
        VERIFY(x_sema_create(&_hImgrzCompleteSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);
    }
	printk("%s(%d) gImgrzParams[0] addr:%p gImgrzParams[1] addr:%p\n", __FUNCTION__, __LINE__,&gImgrzParams[0], &gImgrzParams[1]);
	printk("%s(%d) gImgrzTotal:%p gImgrzFree:%p _hImgrzFreeSema:%p(%x)\n", __FUNCTION__, __LINE__,&gImgrzTotal, &gImgrzFree, &_hImgrzFreeSema,_hImgrzFreeSema);
    return 0;
}

INT32 mt53simple_ioctl(unsigned long ulArg)
{
    SIMPLE_CMD_T rCmd;

    if (copy_from_user(&rCmd, (void *)ulArg, sizeof(SIMPLE_CMD_T)))
    {
        printk("%s(%d) - copy_from_user() fail\n", __FUNCTION__, __LINE__);
        return -EFAULT;
    }

    if(rCmd.u4Cmd >= SIMPLE_VDP_CMD_START && rCmd.u4Cmd <= SIMPLE_VDP_CMD_END)
    {
        return SwVdecOnVdp_Ioctl(rCmd.u4Cmd, rCmd.ulArg);
    }

    if(rCmd.u4Cmd >= SIMPLE_PANEL_CMD_START && rCmd.u4Cmd <= SIMPLE_PANEL_CMD_END)
    {
        return PanelCmd_Ioctl(rCmd.u4Cmd, rCmd.ulArg);
    }

    if (rCmd.u4Cmd >= SIMPLE_HDR_CMD_START && rCmd.u4Cmd <= SIMPLE_HDR_CMD_END)
    {
        return HdrCmd_Ioctl(rCmd.u4Cmd, rCmd.ulArg);
    }

    if(rCmd.u4Cmd == SIMPLE_TEST_IO)
    {
        printk("mt53simple_ioctl test \n");
    }

    return 0;
}

#endif
