/*
 * Project Name MFC DRIVER IN WINCE
 * Copyright  2009 Samsung Electronics Co, Ltd. All Rights Reserved.
 *
 * This file implements MFC driver.
 *
 * @name MFC DRIVER Module (MfcDriver.c)
 * @author Jiyoung Shin (idon.shin@samsung.com)
 * @date 2009/06/05
 */


#include <windows.h>
#include <oal_intr.h>
#include <devload.h>
#include <pm.h>
#include <pmplatform.h>

#include "MfcDriver.h"
#include "MfcMisc.h"
#include "MfcCommon.h"
#include "MfcMemory.h"
#include "MfcLogMsg.h"
#include "MfcInterface.h"
#include "MfcOpr.h"
#include "MfcIntr.h"
#include "MfcPower.h"

int isMFCRunning = 0;
CEDEVICE_POWER_STATE MFCPowerState;


BOOL WINAPI
DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)
{
    switch ( dwReason )
    {
    case DLL_PROCESS_ATTACH:

        DisableThreadLibraryCalls((HMODULE) hInstDll);
        break;

    case DLL_PROCESS_DETACH:
        break;

    }

    return TRUE;
}


DWORD
MFC_Init(
    DWORD dwContext
    )
{

    SSBSIP_MFC_ERROR_CODE ret;
    MFC_SFR        *MfcSfr;

    LOG_MSG(LOG_TRACE, "MFC_Init()", "\r\n");

 
    if (MFCPowerInit() == FALSE){
        LOG_MSG(LOG_ERROR, "MFC_Init", "Power Init Failed\n");
        return MFC_RET_FAIL;
    }



    if (MFCMutexCreate() == FALSE){
        LOG_MSG(LOG_ERROR, "MFC_Init", "mutex create failed\n");
        return MFC_RET_FAIL;
    }


    if (MFCMemorySetup() == FALSE){
        LOG_MSG(LOG_ERROR, "MFC_Init", "memory allocation failed\n");
        return MFC_RET_FAIL;
    }

    if ((ret = CreateIntrResource()) != MFC_RET_OK)
        return ret;


    InitInstanceNo();
    MFCPowerState = D4;
#ifdef POWER_CNTRL_APM
    MFCPowerInitToAPM();
#endif
    
    return TRUE;
}



BOOL
MFC_Deinit(
    DWORD InitHandle
    )
{
    MFCMutexDelete();
    DeleteIntrResource();

    return TRUE;
}




DWORD
MFC_Open(
    DWORD InitHandle,
    DWORD dwAccess,
    DWORD dwShareMode
    )
{
    MFC_INST_CTX  *MfcCtx;

    LOG_MSG(LOG_TRACE, "MFC_Open()", "\r\n");

    MFCMutexLock();

    MfcCtx = (MFC_INST_CTX *) malloc(sizeof(MFC_INST_CTX));
    if (MfcCtx == NULL) {
        LOG_MSG(LOG_ERROR, "MFC_Open", "context memory allocation failed\n");
        MFCMutexRelease();
        return MFC_RET_OPEN_FAIL;
    }
    memset(MfcCtx, 0, sizeof(MFC_INST_CTX));

    if (dwAccess & DEVACCESS_BUSNAMESPACE ) {
        dwAccess &=~(GENERIC_READ |GENERIC_WRITE|GENERIC_EXECUTE|GENERIC_ALL);
    }

#ifdef POWER_CNTRL
    if (dwAccess &  DEVACCESS_BUSNAMESPACE )
    {
        // OK, We do not need initialize pOpenContext and start any thread. return the handle now.
        LOG_MSG(LOG_WARNING, "MFC_Open", "This is Called by Power Manager\n");
        MfcCtx->dwAccess = dwAccess;
        MFCMutexRelease();
        return (DWORD)MfcCtx;
    }
#endif



    MfcCtx->InstNo = GetInstanceNo();
    if (MfcCtx->InstNo < 0) {
        free(MfcCtx);
        MFCMutexRelease();
        LOG_MSG(LOG_ERROR, "MFC_Open", "too many instance\n");
        return MFC_RET_OPEN_FAIL;
    }
#ifdef POWER_CNTRL
    else if(MfcCtx->InstNo == 0){
        LOG_MSG(LOG_WARNING, "MFC_Open", "It is called by Power Manager\n");
        MFCPowerState = D4;
        MFCMutexRelease();
        return (DWORD)MfcCtx;
    }
#endif

    if(SetMfcState(MfcCtx, MFCINST_STATE_OPENED) == 0){
        LOG_MSG(LOG_ERROR, "MFC_Open", "state is invalid\n");
        free(MfcCtx);
        MFCMutexRelease();
        return MFC_RET_OPEN_FAIL;
    }


    if(MFCPowerState == D4){
        if (MFCPowerOnOff(POWER_ON) == FALSE){
            free(MfcCtx);
            MFCMutexRelease();
            LOG_MSG(LOG_ERROR, "MFC_Open", "power on failed\n");
            return MFC_RET_OPEN_FAIL;
        }

        MFCClockOnOff(CLK_ON);
        MFCPowerState = D0; // power_on
#ifdef POWER_CNTRL_APM
        MFCPowerSetToAPM(D0);
#endif

#ifdef BSP_USEDVFS
        MFCDvfsSetLevelFix();
#endif

        MFCOpen();
    }
    MfcCtx->extraDPB = MFC_MAX_EXTRA_DPB;
    MfcCtx->FrameType = -1;
    MfcCtx->displayDelay = 16;

    LOG_MSG(LOG_WARNING, "MFC_Open()", "Instance Number : %d\r\n", MfcCtx->InstNo);
    MFCMutexRelease();

#ifdef CLK_CNTL
    LOG_MSG(LOG_WARNING, "MFC_Open()", "CLK_CNTL is enabled\r\n");
#endif

    return (DWORD) MfcCtx;
}



BOOL
MFC_Close(
    DWORD OpenHandle
    )
{
    MFC_INST_CTX  *MfcCtx;


    MfcCtx = (MFC_INST_CTX *) OpenHandle;
    if (MfcCtx == NULL){
        LOG_MSG(LOG_ERROR, "MFC_Close", "invalid parameter\n");
        return MFC_RET_CLOSE_FAIL;
    }
    LOG_MSG(LOG_WARNING, "MFC_Close()", "MfcCtx->InstNo : %d\r\n", MfcCtx->InstNo);
    
    MFCMutexLock();
#ifdef CLK_CNTL
    MFCClockOnOff(CLK_ON);
#endif

#ifdef POWER_CNTRL
    if (MfcCtx->dwAccess &  DEVACCESS_BUSNAMESPACE )
    {
        LOG_MSG(LOG_WARNING, "MFC_Close", "This is Called by Power Manager\n");
        free(MfcCtx);
        MFCMutexRelease();
        return TRUE;;
    }
#endif

    MFCClose(MfcCtx);
    ReturnInstanceNo(MfcCtx->InstNo);
    free(MfcCtx);

    if(!IsMFCRunning()){
        MFCClockOnOff(CLK_OFF); 
#if(S5PV210_EVT!=0)
        if (MFCPowerOnOff(POWER_OFF) == FALSE)
            return MFC_RET_CLOSE_FAIL;
#endif       
        MFCPowerState = D4;

#if(S5PV210_EVT!=0)
        MFCPowerSetToAPM(D4);
#endif

#ifdef BSP_USEDVFS
        MFCDvfsClearLevelFix();
#endif
    }

    MFCMutexRelease();
    return TRUE;
}


BOOL
MFC_IOControl(
    DWORD OpenHandle,
    DWORD dwIoControlCode,
    PBYTE pInBuf,
    DWORD nInBufSize,
    PBYTE pOutBuf,
    DWORD nOutBufSize,
    PDWORD pBytesReturned
    )
{
    MFC_INST_CTX  *MfcCtx = NULL;
    MFC_ARGS    *InParm = NULL;
    DWORD           startTime = 0;


    MFCMutexLock();
    InParm = (MFC_ARGS *) pInBuf;

    LOG_MSG(LOG_DEBUG, "MFC_IOControl()++", "\r\n");
    MfcCtx   = (MFC_INST_CTX *) OpenHandle;

    if(MfcCtx->InstNo > 0){
        if (OpenHandle == 0){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "OpenHandle == 0\n");
            InParm->ret_code  = MFC_RET_FAIL;
            MFCMutexRelease();
            return FALSE;
        }

        if (pInBuf == NULL){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "pInBuf == NULL\n");
            InParm->ret_code  = MFC_RET_FAIL;
            MFCMutexRelease();
            return FALSE;
        }
        if (nInBufSize <= 0){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "nInBufSize <= 0\n");
            InParm->ret_code  = MFC_RET_FAIL;
            MFCMutexRelease();
            return FALSE;
        }
    }

    MFCMutexRelease();

    switch ( dwIoControlCode ) {
#ifdef POWER_CNTRL
    case IOCTL_POWER_CAPABILITIES:
        // tell the power manager about ourselves
        if (nOutBufSize < sizeof(POWER_CAPABILITIES))
        {
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "POWER_CAPABILITIES has invalid parameter\n");
            return FALSE;
        }
        else
        {
            PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES) pOutBuf;
            memset(ppc, 0, sizeof(POWER_CAPABILITIES));
            // support D0, D4
            ppc->DeviceDx = DX_MASK(D0) | DX_MASK(D4);
            LOG_MSG(LOG_WARNING, "MFC_IOControl", "POWER_CAPABILITIES : 0x%x\n", ppc->DeviceDx);
            *pBytesReturned = sizeof(POWER_CAPABILITIES);
            return TRUE;
        }
        break;

    case IOCTL_POWER_GET:
        if (nOutBufSize < sizeof(POWER_CAPABILITIES))
        {
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "POWER_CAPABILITIES has invalid parameter\n");
            return FALSE;
        }
        else
        {
            *(PCEDEVICE_POWER_STATE)pOutBuf = MFCPowerState;
            LOG_MSG(LOG_WARNING, "MFC_IOControl", "IOCTL_POWER_GET: D%u \r\n", MFCPowerState);
            *pBytesReturned = sizeof(POWER_CAPABILITIES);
            return TRUE;
        }
        break;

    case IOCTL_POWER_SET:
        {
            CEDEVICE_POWER_STATE NewDx;

            MFCMutexLock();
            LOG_MSG(LOG_WARNING, "MFC_IOControl", "IOCTL_POWER_SET..current MFCPowerState: D%d\r\n", MFCPowerState == D0?0:4);

            NewDx = *(PCEDEVICE_POWER_STATE)pOutBuf;
            LOG_MSG(LOG_WARNING, "MFC_IOControl", "NewDx: D%d \r\n", NewDx == D0?0:4);
            if ( VALID_DX(NewDx) )
            {

                switch ( NewDx )
                {
                case D0:
                    if ((MFCPowerState != D0) && (IsMFCRunning() == TRUE))
                    {
                        MFCPowerState = D0;
                        if(SetMFCWakeUp(MfcCtx) != MFC_RET_OK)
                        {
                            MFCPowerState = D4;
                        }
                    }
                    break;
                default:
                    if (MFCPowerState != D4)
                    {
                        if(SetMFCSleep(MfcCtx) == MFC_RET_OK)
                        {
                            MFCPowerState = D4;
                        }
                    }
                    break;
                }

                // return our state
                *(PCEDEVICE_POWER_STATE)pOutBuf = NewDx;

                LOG_MSG(LOG_WARNING, "MFC_IOControl", "IOCTL_POWER_SET: D%u \r\n", NewDx == D0?0:4);

                *pBytesReturned = sizeof(CEDEVICE_POWER_STATE);
                MFCMutexRelease();
                return TRUE;
            }
            else
            {
                LOG_MSG(LOG_ERROR, "MFC_IOControl", "POWER_CAPABILITIES has invalid parameter\n");
                MFCMutexRelease();
                return FALSE;
            }

        }
        MFCMutexRelease();
        break;
#endif


    case IOCTL_MFC_ENC_INIT:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        if(!SetMfcState(MfcCtx, MFCINST_STATE_ENC_INITIALIZE)){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }

        InParm->ret_code = MFCEncodeInit(MfcCtx, &(InParm->args));
        LOG_MSG(LOG_TRACE, "IOCTL_MFC_ENC_INIT", "InParm->ret_code : %d\r\n", InParm->ret_code);
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif
        MFCMutexRelease();
        break;

    case IOCTL_MFC_ENC_EXE:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        if(!SetMfcState(MfcCtx, MFCINST_STATE_ENC_EXE)){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }

        InParm->ret_code = MFCEncodeExe(MfcCtx, &(InParm->args));
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif
        LOG_MSG(LOG_TRACE, "IOCTL_MFC_ENC_EXE", "InParm->ret_code : %d\r\n", InParm->ret_code);
        MFCMutexRelease();
        break;


    case IOCTL_MFC_DEC_BUFFER_INIT:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        LOG_MSG(LOG_DEBUG, "MFCINST_STATE_DEC_BUFFER_INIT", "\r\n");
        if(!SetMfcState(MfcCtx, MFCINST_STATE_DEC_BUFFER_INIT)){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }

        InParm->ret_code = MFCDecodeBufferInit(MfcCtx, &(InParm->args));
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif        
        MFCMutexRelease();
        break;

    case IOCTL_MFC_DEC_INIT:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        LOG_MSG(LOG_DEBUG, "IOCTL_MFC_DEC_INIT", "\r\n");
        if(!SetMfcState(MfcCtx, MFCINST_STATE_DEC_INITIALIZE)){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }

        InParm->ret_code = MFCDecodeInit(MfcCtx, &(InParm->args));
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif        
        MFCMutexRelease();
        break;
    case IOCTL_MFC_DEC_EXE:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        LOG_MSG(LOG_DEBUG, "IOCTL_MFC_DEC_EXE", "\r\n");

        if(!SetMfcState(MfcCtx, MFCINST_STATE_DEC_EXE)){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }

        InParm->ret_code = MFCDecodeExe(MfcCtx, &(InParm->args));
        
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif        
        MFCMutexRelease();
        break;

    case IOCTL_MFC_GET_CONFIG:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        if(MfcCtx->MfcState <= MFCINST_STATE_DEC_INITIALIZE){
            LOG_MSG(LOG_ERROR, "MFC_IOControl", "MFCINST_ERR_STATE_INVALID\n");
            InParm->ret_code = MFC_RET_FAIL;
            MFCMutexRelease();
            break;
        }
        
        InParm->ret_code = MFCGetConfig(MfcCtx, &(InParm->args));
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif        
        MFCMutexRelease();
        break;

    case IOCTL_MFC_SET_CONFIG:
        MFCMutexLock();
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_ON);
#endif
        InParm->ret_code = MFCSetConfig(MfcCtx, &(InParm->args));
#ifdef CLK_CNTL
        MFCClockOnOff(CLK_OFF);
#endif
        MFCMutexRelease();
        break;
        
    default:
        LOG_MSG(LOG_ERROR, "MFC_IOControl", "Requested ioctl command is not defined. (ioctl cmd=0x%08x)\n", dwIoControlCode);
        InParm->ret_code  = MFC_RET_FAIL;
    }


    LOG_MSG(LOG_DEBUG, "MFC_IOControl", "---------------IOCTL return--------------------------\n");
    if(InParm->ret_code == MFC_RET_OK)
        return TRUE;
    else
        return FALSE;	
}


BOOL MFC_PowerUp(DWORD InitHandle)
{
    RETAILMSG(1, (L"MFC Power Up"));
    return TRUE;
}

BOOL MFC_PowerDown(DWORD InitHandle)
{
    RETAILMSG(1, (L"MFC Power Down"));
    return TRUE;
}


