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

#include <windows.h>
#include <nkintr.h>
#include "bsp_cfg.h"
#include "MfcIntr.h"
#include "MfcLogMsg.h"
#include "intr_reg.h"
#include "MfcCommon.h"


UINT32 g_MfcIrq     = IRQ_MFC;
UINT32 g_MfcSysIntr = SYSINTR_UNDEFINED;


static HANDLE gMfcDoneEvent = NULL;
static HANDLE gMfcIntrEvent = NULL;
static HANDLE gMfcIntrThread = NULL;

static BOOL CreateInterruptNotification(void);
static int WaitPolling(MFC_FACADE_RESPONSE command);
static DWORD MFCIntrThread(void);

static BOOL CreateInterruptNotification(void)
{
    gMfcIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!gMfcIntrEvent) {
        LOG_MSG(LOG_ERROR, "CreateInterruptNotification", "gMfcIntrEvent CreateEvent Failed\n");
        return FALSE;
    }
    gMfcDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!gMfcDoneEvent){
        LOG_MSG(LOG_ERROR, "CreateInterruptNotification", "gMfcDoneEvent CreateEvent Failed\n");
        return FALSE;
    }

    return TRUE;
}


static int WaitPolling(MFC_FACADE_RESPONSE command)
{
    volatile unsigned int uRegData=0;
    volatile MFC_SFR *MfcSfr;
    unsigned int waitLoop = 5000;       // 1000msec
    int  i;

    MfcSfr = GetSfrVirAddr(); 
    for( i=0; (i<waitLoop) && (uRegData != (unsigned int)command) ; i++){
        MFCDelay(1);
        uRegData = (MfcSfr->MFC_RISC2HOST_CMD & 0xFF); 
    }

    MFCClearIntr();
      
    if(uRegData == MFC_R2H_CMD_SLICE_DONE_RET){
        LOG_MSG(LOG_TRACE, "WaitPolling", "MFC_R2H_CMD_SLICE_DONE_RET\n");
        return 1;
    }  
    
    if(uRegData != command){
        LOG_MSG(LOG_ERROR, "WaitPolling", "Polling Time Out(command:%d)\n", command);
        return 0;
    }
    return 1;

}


int WaitForDone(MFC_WAIT_DONE_MODE mode, MFC_FACADE_RESPONSE command)
{
    volatile MFC_SFR *MfcSfr;
    DWORD ret;
    DWORD intrStatus;
    unsigned int retVal = 1; 

    MfcSfr = GetSfrVirAddr(); 
    if(mode == MFC_WAIT_DONE_POLLING)
    {
        switch(command){
            case MFC_R2H_CMD_EMPTY :
            case MFC_R2H_CMD_OPEN_CH_RET :
            case MFC_R2H_CMD_CLOSE_CH_RET :
            case MFC_R2H_CMD_SEQ_DONE_RET :
            case MFC_R2H_CMD_FRAME_DONE_RET :
            case MFC_R2H_CMD_SLICE_DONE_RET :
            case MFC_R2H_CMD_SYS_INIT_RET :
            case MFC_R2H_CMD_FW_STATUS_RET :
            case MFC_R2H_CMD_INIT_BUFFER_RET :
            case MFC_R2H_CMD_SYS_SLEPP_RET :
            case MFC_R2H_CMD_SYS_WAKEUP_RET :
            case MFC_R2H_CMD_ERR_RET :
            retVal = WaitPolling(command);
            break;

        default : 
            {
            LOG_MSG(LOG_ERROR, "WaitForDone", "undefined command\n");
            retVal = 0;
            }
        }
    }
    else if(mode == MFC_WAIT_DONE_INTR)
    {
        switch(command){
            case MFC_R2H_CMD_EMPTY :
            case MFC_R2H_CMD_OPEN_CH_RET :
            case MFC_R2H_CMD_CLOSE_CH_RET :
            case MFC_R2H_CMD_SEQ_DONE_RET :
            case MFC_R2H_CMD_FRAME_DONE_RET :
            case MFC_R2H_CMD_SLICE_DONE_RET :
            case MFC_R2H_CMD_SYS_INIT_RET :
            case MFC_R2H_CMD_FW_STATUS_RET :
            case MFC_R2H_CMD_INIT_BUFFER_RET :
            case MFC_R2H_CMD_SYS_SLEPP_RET :
            case MFC_R2H_CMD_SYS_WAKEUP_RET :
            case MFC_R2H_CMD_ERR_RET :
            {
                if (gMfcDoneEvent == NULL){
                    LOG_MSG(LOG_ERROR, "WaitForDone", "gMfcDoneEvent == NULL\n");
                    retVal = 0;
                    break;
                }

                ret = WaitForSingleObject(gMfcDoneEvent, 10000);

                if (ret == WAIT_TIMEOUT){
                    LOG_MSG(LOG_ERROR, "WaitForDone", "Interrupt Time Out(%d)\n", command);
                    retVal = 0;
                    break;
                }

                intrStatus = GetEventData(gMfcDoneEvent);
                ResetEvent(gMfcDoneEvent);

                if(intrStatus == MFC_R2H_CMD_SLICE_DONE_RET){
                    LOG_MSG(LOG_TRACE, "WaitForDone", "MFC_R2H_CMD_SLICE_DONE_RET\n");
                    retVal = 1;
                    break;
                }

                if(intrStatus != command){
                    if(intrStatus == MFC_R2H_CMD_ERR_RET)
                        LOG_MSG(LOG_ERROR, "WaitForDone", "Interrupt :: MFC_R2H_CMD_ERR_RET(0x%x)\n", MfcSfr->MFC_RISC2HOST_ARG2);
                    else
                        LOG_MSG(LOG_ERROR, "WaitForDone", "Unexpected Interrupt(%d)\n", intrStatus);

                    retVal = 0;
                }

                break;
            }

            default : 
            {
                LOG_MSG(LOG_ERROR, "WaitForDone", "undefined command\n");
                retVal = 0;
            }
        }
    }

    return retVal;
}

void MFCClearIntr()
{
    volatile MFC_SFR *MfcSfr;


    MfcSfr = GetSfrVirAddr(); 
    MfcSfr->MFC_RISC_HOST_INT = 0; 
    MfcSfr->MFC_RISC2HOST_CMD = 0;

}

static DWORD MFCIntrThread(void)
{
    MFC_FACADE_RESPONSE intrStatus;
    volatile MFC_SFR *MfcSfr;


    MfcSfr = GetSfrVirAddr(); 
    while (1)
    {

        WaitForSingleObject(gMfcIntrEvent, INFINITE);


        intrStatus = (MFC_FACADE_RESPONSE)(MfcSfr->MFC_RISC2HOST_CMD & 0xFF);
        MFCClearIntr();
        
        if ((intrStatus >= MFC_R2H_CMD_EMPTY) && (intrStatus <= MFC_R2H_CMD_ERR_RET) ){
                SetEventData(gMfcDoneEvent, intrStatus);  
                SetEvent(gMfcDoneEvent); 
                LOG_MSG(LOG_DEBUG, "MFCIntrThread", "Interrupt !! : %d\n", intrStatus);
        }else
                LOG_MSG(LOG_ERROR, "MFCIntrThread", "Undefined interrupt : %d\n", intrStatus);

        // Notify to Kernel that MFC Interrupt processing is completed.
        InterruptDone(g_MfcSysIntr);
    }

}

SSBSIP_MFC_ERROR_CODE CreateIntrResource()
{
    BOOL r;

    LOG_MSG(LOG_DEBUG, "InitializeIST++", "..........................\n");
    if (!CreateInterruptNotification()) 
        return MFC_RET_FAIL;
    

    r = KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR,
                        &g_MfcIrq,     sizeof(UINT32),
                        &g_MfcSysIntr, sizeof(UINT32),
                        NULL);
    if (r != TRUE) {
        LOG_MSG(LOG_ERROR, "InitializeIST", "IOCTL_HAL_REQUEST_SYSINTR Failed\n");
        return MFC_RET_FAIL;
    }


    r = InterruptInitialize(g_MfcSysIntr, gMfcIntrEvent, NULL, 0);
    if (r != TRUE) {
        LOG_MSG(LOG_ERROR, "InitializeIST", "InterruptInitialize Failed\n");
        return MFC_RET_FAIL;
    }

    gMfcIntrThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL,
                                  0,
                                  (LPTHREAD_START_ROUTINE)MFCIntrThread,
                                  0,
                                  0,
                                  NULL);
    if (!gMfcIntrThread) {
        LOG_MSG(LOG_ERROR, "InitializeIST", "CreateThread Failed\n");
        return MFC_RET_FAIL;
    }

    // Bump up the priority since the interrupt must be serviced immediately.
    CeSetThreadPriority(gMfcIntrThread, 100);

    LOG_MSG(LOG_DEBUG, "InitializeIST--", "..........................\n");

    return MFC_RET_OK;
}


void DeleteIntrResource(void)
{
    CloseHandle(gMfcDoneEvent);
    gMfcDoneEvent = NULL;

    CloseHandle(gMfcIntrEvent);
    gMfcIntrEvent = NULL;

    CloseHandle(gMfcIntrThread);
}

