//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// Copyright (c) Samsung Electronics. Co. LTD.  All rights reserved.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:    SVEngine.c

Abstract:       Implementation of Video Driver.
                This module handle context data, and Video Engine On/Off

Functions:


Notes:


--*/

#include <bsp.h>
#include <DrvLib_mem.h>
#include "SVEngine.h"

#define TODO_POWERCONTROL      (FALSE)      //< This is Tag for Todo worklist

static SVEngineContext SVECtxt;
static SVEnginePowerContext SVEPMCtxt;
static volatile DWORD *g_pMDNIESel = NULL;        // This code should move to Display_con.c in SMDK

BOOL SVE_initialize_video_engine(void)
{
    SVEngineContext *pCtxt;

    VDE_MSG((_T("[VDE] ++SVE_initialize_video_engine()\r\n")));

    pCtxt = SVE_get_context();

    // Clear Context
    SVE_initialize_context();
    SVE_initialize_power_context();

    // map device SFR address
    if (SVE_map_device_address() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] SVE_initialize_video_engine() : SVE_map_device_address() Failed\r\n")));
        goto CleanUp;
    }

    // Intialize interrupt
    if (SVE_initialize_interrupt() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] SVE_initialize_video_engine() : SVE_intialize_interrupt() Failed\r\n")));
        goto CleanUp;
    }

    // Create Interrupt Thread
    if (SVE_initialize_thread() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] SVE_initialize_video_engine() : SVE_initialize_thread() Failed\r\n")));
        goto CleanUp;
    }

    // Open RAW Camera Driver
    if (SVE_initialize_RAW_camera_driver() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] %s() Failed\r\n"), _T(__FUNCTION__)));
        goto CleanUp;
    }

    // Open Power Control Driver
    if (SVE_initialize_power_control() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] SVE_initialize_video_engine() : SVE_initialize_power_control() Failed\r\n")));
        goto CleanUp;
    }

#ifdef USE_LOCALPATH_EMUL_WITH_MEM2MEM
    // Open local path emul functions
    if(SVE_LpeInitialize() == FALSE)
    {
        VDE_ERR((_T("[VDE:ERR] %s() : SVE_LpeInitialize() Failed\r\n"), _T(__FUNCTION__)));
        goto CleanUp;
    }
#endif

    // Initialize SFR Address of Sub Module
    LDI_initialize_register_address((void *)pCtxt->pSPIReg, \
                        (void *)pCtxt->pDispConReg, (void *)pCtxt->pGPIOReg);
    Disp_initialize_register_address((void *)pCtxt->pDispConReg, \
                        (void *)pCtxt->pGPIOReg, (void *)pCtxt->pCMUCLKReg, \
                        (void *)pCtxt->pBSPArgs, (void*)pCtxt->pCMUMISCReg);

    // ROTATOR IP was changed, so new rotator d/d will be developed
    //Rotator_initialize_register_address((void *)pCtxt->pRotatorReg);

    SVE_hw_power_control(HWPWR_DISPLAY_ON);
#if (S5PV210_EVT>0)
    SVE_hw_power_control(HWPWR_2D_ON);
#endif

    SVE_hw_clock_control(HWCLK_DISPLAY_ON);
#if (S5PV210_EVT>0)
    SVE_hw_clock_control(HWCLK_2D_ON);
#endif
    VDE_MSG((_T("[VDE] --SVE_initialize_video_engine()\r\n")));

    return TRUE;

CleanUp:

    SVE_deinitialize_video_engine();

    VDE_ERR((_T("[VDE:ERR] --SVE_initialize_video_engine() : Failed\r\n")));


    return FALSE;
}


void SVE_deinitialize_video_engine(void)
{
    VDE_MSG((_T("[VDE] ++SVE_deinitialize_video_engine()\r\n")));

    SVE_deinitialize_context();
    SVE_deinitialize_power_context();
    SVE_unmap_device_address();
    SVE_deinitialize_interrupt();
    SVE_deinitialize_thread();
    SVE_deinitialize_power_control();
    SVE_deinitialize_RAW_camera_driver();
#ifdef USE_LOCALPATH_EMUL_WITH_MEM2MEM
    SVE_LpeDeinitialize();
#endif

    VDE_MSG((_T("[VDE] --SVE_deinitialize_video_engine()\r\n")));
}

SVEngineContext* SVE_get_context(void)
{
    return &SVECtxt;
}

SVEnginePowerContext* SVE_get_power_context(void)
{
    return &SVEPMCtxt;
}

BOOL SVE_map_device_address(void)
{
    SVEngineContext *pCtxt;

    VDE_MSG((_T("[VDE] ++%s\r\n"), _T(__FUNCTION__)));

    pCtxt = SVE_get_context();

    // Translate to System Address.
    pCtxt->pDispConReg = (DISPLAY_REG *)DrvLib_MapIoSpace(BASE_REG_PA_LCDC, sizeof(DISPLAY_REG), FALSE);
    if (pCtxt->pDispConReg == NULL)
    {
        goto CleanUp;
    }

#if 0 // ROTATOR IP was changed, so new rotator d/d will be developed
    // TODO: Call the module to act as POST with mapping IO Space message
    pCtxt->pRotatorReg = (IMAGE_ROTATOR_REG *)DrvLib_MapIoSpace(BASE_REG_PA_ROTATOR, sizeof(IMAGE_ROTATOR_REG), FALSE);
    if (pCtxt->pRotatorReg == NULL)
    {
        goto CleanUp;
    }
#endif

    pCtxt->pCMUCLKReg = (CMU_CLK_REG *)DrvLib_MapIoSpace(BASE_REG_PA_CMU_CLK, sizeof(CMU_CLK_REG), FALSE);
    if (pCtxt->pCMUCLKReg == NULL)
    {
        goto CleanUp;
    }

    pCtxt->pCMUMISCReg = (CMU_MISC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_CMU_MISC, sizeof(CMU_MISC_REG), FALSE);
    if (pCtxt->pCMUMISCReg == NULL)
    {
        goto CleanUp;
    }

    pCtxt->pGPIOReg = (GPIO_REG *)DrvLib_MapIoSpace(BASE_REG_PA_GPIO, sizeof(GPIO_REG), FALSE);
    if (pCtxt->pGPIOReg == NULL)
    {
        goto CleanUp;
    }

    pCtxt->pSPIReg = (SPI_REG *)DrvLib_MapIoSpace(BASE_REG_PA_SPI0, sizeof(SPI_REG), FALSE);
    if (pCtxt->pSPIReg == NULL)
    {
        goto CleanUp;
    }

    pCtxt->pBSPArgs = (BSP_ARGS *)DrvLib_MapIoSpace(IMAGE_SHARE_ARGS_PA_START, sizeof(BSP_ARGS), FALSE);
    if (pCtxt->pBSPArgs == NULL)
    {
        goto CleanUp;
    }
    VDE_MSG((_T("[VDE] --%s\r\n"), _T(__FUNCTION__)));

    return TRUE;

CleanUp:

    VDE_ERR((_T("[VDE:ERR] --%s : Failed\r\n"), _T(__FUNCTION__)));

    return FALSE;
}

void SVE_unmap_device_address(void)
{
    SVEngineContext *pCtxt;

    VDE_MSG((_T("[VDE] ++SVE_unmap_device_address()\r\n")));

    pCtxt = SVE_get_context();

    if (pCtxt->pDispConReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pDispConReg);
        pCtxt->pDispConReg = NULL;
    }

#if 0 // ROTATOR IP was changed, so new rotator d/d will be developed
    // TODO: Call the module to act as POST with unmapping IO Space message
    if (pCtxt->pRotatorReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pRotatorReg);
        pCtxt->pRotatorReg = NULL;
    }
#endif
    if (pCtxt->pCMUCLKReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pCMUCLKReg);
        pCtxt->pCMUCLKReg = NULL;
    }
    if (pCtxt->pCMUMISCReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pCMUMISCReg);
        pCtxt->pCMUMISCReg = NULL;
    }
    if (pCtxt->pGPIOReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pGPIOReg);
        pCtxt->pGPIOReg = NULL;
    }

    if (pCtxt->pSPIReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)pCtxt->pSPIReg);
        pCtxt->pSPIReg = NULL;
    }

    VDE_MSG((_T("[VDE] --SVE_unmap_device_address()\r\n")));
}

void SVE_initialize_context(void)
{
    SVEngineContext *pCtxt;

    VDE_MSG((_T("[VDE] ++SVE_initialize_context()\r\n")));

    pCtxt = SVE_get_context();

    // Clear Context
    memset(pCtxt, 0x0, sizeof(SVEngineContext));

    pCtxt->dwSignature = SVE_DRIVER_SIGNITURE;
    pCtxt->dwLastOpenContext = SVE_DRIVER_SIGNITURE;

    pCtxt->dwSysIntrDisp = SYSINTR_UNDEFINED;
    pCtxt->dwSysIntrRotator = SYSINTR_UNDEFINED;
    pCtxt->hPowerControl = INVALID_HANDLE_VALUE;

    // Critical Section for IOCTL
    InitializeCriticalSection(&pCtxt->csProc);

    // Command Event
    pCtxt->hDispCmdDone = CreateEvent(NULL, TRUE, FALSE, NULL);    // Manual Reset, You should call ResetEvent() or use PulseEvent() Only
//    pCtxt->hPostCmdDone = CreateEvent(NULL, TRUE, FALSE, NULL);    // Manual Reset, You should call ResetEvent() or use PulseEvent() Only

    // Operation Finish Event
    pCtxt->hRotatorFinish = CreateEvent(NULL, TRUE, FALSE, NULL);    // Manual Reset, You should call ResetEvent() or use PulseEvent() Only

    VDE_MSG((_T("[VDE] --SVE_initialize_context()\r\n")));
}


void SVE_deinitialize_context(void)
{
    SVEngineContext *pCtxt;

    VDE_MSG((_T("[VDE] ++SVE_deinitialize_context()\r\n")));

    pCtxt = SVE_get_context();

    // Critical Section for IOCTL
    DeleteCriticalSection(&pCtxt->csProc);

    if (pCtxt->hDispCmdDone != NULL)
    {
        CloseHandle(pCtxt->hDispCmdDone);
    }

    if (pCtxt->hRotatorFinish != NULL)
    {
        CloseHandle(pCtxt->hRotatorFinish);
    }

    VDE_MSG((_T("[VDE] --SVE_deinitialize_context()\r\n")));
}


DWORD SVE_get_api_function_code(DWORD dwCode)
{
    //#define CTL_CODE( DeviceType, Function, Method, Access ) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )
    return ((dwCode>>2)&0xFFF);
}

DWORD SVE_get_driver_signature(void)
{
    return (SVE_get_context()->dwSignature);
}

DWORD SVE_add_open_context(void)
{
    SVEngineContext *pCtxt = SVE_get_context();

    pCtxt->dwOpenCount++;
    pCtxt->dwLastOpenContext++;

    VDE_MSG((_T("[VDE] SVE_add_open_context() : OpenCount = %d, OpenContext = 0x%08x\r\n"), pCtxt->dwOpenCount, pCtxt->dwLastOpenContext));

    return (pCtxt->dwLastOpenContext);
}

BOOL SVE_remove_open_context(DWORD dwOpenContext)
{
    SVEngineContext *pCtxt = SVE_get_context();

    if (pCtxt->dwOpenCount > 0)
    {
        // Release H/W Resource
        if (pCtxt->dwOccupantFIMD == dwOpenContext) pCtxt->dwOccupantFIMD = 0;
        if (pCtxt->dwOccupantFIMDWindow[0] == dwOpenContext) pCtxt->dwOccupantFIMDWindow[0] = 0;
        if (pCtxt->dwOccupantFIMDWindow[1] == dwOpenContext) pCtxt->dwOccupantFIMDWindow[1] = 0;
        if (pCtxt->dwOccupantFIMDWindow[2] == dwOpenContext) pCtxt->dwOccupantFIMDWindow[2] = 0;
        if (pCtxt->dwOccupantFIMDWindow[3] == dwOpenContext) pCtxt->dwOccupantFIMDWindow[3] = 0;
        if (pCtxt->dwOccupantFIMDWindow[4] == dwOpenContext) pCtxt->dwOccupantFIMDWindow[4] = 0;
        if (pCtxt->dwOccupantPost == dwOpenContext) pCtxt->dwOccupantPost = 0;
        if (pCtxt->dwOccupantRotator == dwOpenContext) pCtxt->dwOccupantRotator = 0;

        pCtxt->dwOpenCount--;

        VDE_MSG((_T("[VDE] SVE_remove_open_context(0x%08x) : OpenCount = %d\r\n"), pCtxt->dwLastOpenContext, pCtxt->dwOpenCount));

        return TRUE;
    }
    else
    {
        VDE_ERR((_T("[VDE:ERR] SVE_remove_open_context() : Current Open Count is 0 !!!\r\n")));

        return FALSE;
    }
}

DWORD SVE_get_current_open_count(void)
{
    return (SVE_get_context()->dwOpenCount);
}

