//
// 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.
//
//  
//------------------------------------------------------------------------------
//
//  File: Backlight_pdd.c
//
//  Backlight PDD driver source code, S3C6410
//
#include <windows.h>
#include <ceddk.h>
#include <bsp.h>
#include "backlight_pdd.h"

// Driver libraries
#include <drvmsg.h>
#include <drvlib_mem.h>


volatile static GPIO_REG		*v_pGPIOregs = NULL;
volatile static PWM_REG			*v_pPWMregs = NULL;
volatile static CMU_CLK_REG		*v_pCMUCLKregs = NULL;

DWORD g_dwD0Brightness = 100;



//-----------------------------------------------------------------------------
//  Initialize hardware etc
//  Returned DWORD will be passed to BacklightDeInit and should be used to store context if necessary
//  pDeviceState should be set to the start state of the backlight (usually D0)
//
extern "C"
void BL_InitPWM()
{
	DWORD dwBKLTimerPrescaler;
	DWORD dwBKLTimerInputClockFreq;
	DWORD dwBKLTimerClockFreq;
	DWORD dwBKLTimerClockPeriodNs;

	
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
#ifdef BKL_USE_TIMER3
	dwBKLTimerPrescaler = (v_pPWMregs->TCFG0 & 0xFF00) >> 8;
	dwBKLTimerInputClockFreq = GET_PCLKPSYS(v_pCMUCLKregs->PLL_CON.MPLL_CON, v_pCMUCLKregs->CLK_DIV.CLK_DIV0);
	dwBKLTimerClockFreq = dwBKLTimerInputClockFreq / (dwBKLTimerPrescaler + 1) / (0 + 1);
	dwBKLTimerClockPeriodNs = (DWORD)((float)(1000000 / (float)dwBKLTimerClockFreq) + 0.5);

	v_pPWMregs->TCFG1 &= ~(0xF << 12);									// PWM timer 3 divider = 1/1
	v_pPWMregs->TCFG1 |= (0 << 12);

	// PWM could make some noisy sound.
	// Higher frequency of pulse makes higher pitch of sound.
	BKL_TCNTB = BKL_TCNTB_VALUE;
	
	BKLMSG(BKL_INFO, (TEXT("Timer prescaler          : %d\r\n"), dwBKLTimerPrescaler));
	BKLMSG(BKL_INFO, (TEXT("Timer input clock freq   : %d\r\n"), dwBKLTimerInputClockFreq));
	BKLMSG(BKL_INFO, (TEXT("Timer clock freq         : %d\r\n"), dwBKLTimerClockFreq));
	BKLMSG(BKL_INFO, (TEXT("Timer clock period in ns : %d\r\n"), dwBKLTimerClockPeriodNs));
	BKLMSG(BKL_INFO, (TEXT("PWM frequency in hz      : %d\r\n"), dwBKLTimerClockFreq / BKL_TCNTB));
	BKLMSG(BKL_INFO, (TEXT("TCNTB                    : %d\r\n"), BKL_TCNTB));
	
#else
	#error Not supported PWM timer for Backlight driver.
#endif

    
	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


//
// turn on/off the backlight
//
extern "C"
void BL_Set(BOOL bOn)
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if(bOn)
    {
		BL_SetBrightness(100);
    }
    else
    {
		BL_SetBrightness(0);
    }

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


void BL_SetBrightness(DWORD dwValue)
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	DWORD dwBrightness;

	// confirm GPIO configuration (TOUT)
	Set_PinFunction(v_pGPIOregs, PWM_MIE);
	Set_PinPullUD(v_pGPIOregs, PWM_MIE, sgip_PULL_DISABLE);


	if(dwValue == 100)
	{
		dwBrightness = BKL_TCNTB - 1;
	}
	else if(dwValue == 0)
	{
		dwBrightness = 0;
	}
	else
	{
		dwBrightness = ((BKL_TCMPB_MAX - BKL_TCMPB_MIN) * ((float)dwValue / 100)) + BKL_TCMPB_MIN;
	}

	if(dwBrightness >= BKL_TCNTB) dwBrightness = BKL_TCNTB - 1;
		
	BKL_TCMPB = dwBrightness;
	
    BKLMSG(BKL_INFO, (TEXT("Brightness=%d TCMPB=%d\r\n"), dwValue, BKL_TCMPB));
	
	BKL_TCON &= ~(0xF << BKL_TCON_OFFSETBIT);		// clear TCON
	BKL_TCON |= BKL_TCON_AutoReload | BKL_TCON_Update | BKL_TCON_Start;
	BKL_TCON &= ~BKL_TCON_Update;

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
}



extern "C"
void BacklightDeInit(DWORD dwContext)
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if(v_pGPIOregs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pGPIOregs);
        v_pGPIOregs = NULL;
    }
    if(v_pPWMregs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pPWMregs);
        v_pPWMregs = NULL;
    }
    if(v_pCMUCLKregs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pCMUCLKregs);
        v_pCMUCLKregs = NULL;
    }

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


extern "C"
DWORD BacklightInit(LPCTSTR pContext, LPCVOID lpvBusContext, CEDEVICE_POWER_STATE *pDeviceState)
{

    BOOL bRet = TRUE;
    PHYSICAL_ADDRESS    ioPhysicalBase = {0,0};

	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	v_pGPIOregs = (GPIO_REG *)DrvLib_MapIoSpace(BASE_REG_PA_GPIO, sizeof(GPIO_REG), FALSE);
	if (v_pGPIOregs == NULL)
	{
		BKLERR(TRUE, (_T("v_pGPIOregs Allocation Fail\r\n")));
		bRet = FALSE;
		goto CleanUp;
	}
	
	v_pPWMregs = (PWM_REG *)DrvLib_MapIoSpace(BASE_REG_PA_PWMTIMER, sizeof(PWM_REG), FALSE);
	if (v_pPWMregs == NULL)
	{
		BKLERR(TRUE, (_T("BKL_VirtualAlloc() : v_pPWMregs Allocation Fail\r\n")));
		bRet = FALSE;
		goto CleanUp;
	}

    v_pCMUCLKregs = (CMU_CLK_REG *)DrvLib_MapIoSpace(BASE_REG_PA_CMU_CLK, sizeof(CMU_CLK_REG), FALSE);
    if (v_pCMUCLKregs == NULL)
    {
        BKLERR(TRUE, (_T("BKL_VirtualAlloc() : v_pCMUCLKregs Allocation Fail\r\n")));
        bRet = FALSE;
        goto CleanUp;
    }



	BL_InitPWM();
	BL_Set(TRUE);
	*pDeviceState = D0;

CleanUp:

	if (!bRet)
	{
		BacklightDeInit((DWORD)pContext);
	}

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return TRUE;
}




extern "C"
BOOL BackLightSetState(DWORD dwContext, CEDEVICE_POWER_STATE state)
{
    // sets the backlight state (turns the backlight on and off)
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    BOOL bRet=TRUE;
    
    switch (state)
    {
        case D0:
            BL_InitPWM();
            BL_SetBrightness(g_dwD0Brightness);
            break;
        case D1:
			BL_SetBrightness(g_dwD0Brightness);
            break;
        case D2:
			BL_SetBrightness(BKL_BRIGHTNESS_D2);
			break;
        case D3:
        case D4:
            BL_SetBrightness(BKL_BRIGHTNESS_D4);
            break;
        default:
			BKLMSG(TCH_INFO, (TEXT("Unsupported power state\r\n")));
            bRet=FALSE;
    }

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
    return bRet;
}

extern "C"
UCHAR BacklightGetSupportedStates()
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
	
    return DX_MASK(D0)|DX_MASK(D1)|DX_MASK(D2)|DX_MASK(D4);     //support D0,D1, D4 (ON, LOW,OFF)


}

extern "C"
DWORD BacklightIOControl(DWORD dwOpenContext, DWORD dwIoControlCode, LPBYTE lpInBuf,
                   DWORD nInBufSize, LPBYTE lpOutBuf, DWORD nOutBufSize,
                   LPDWORD lpBytesReturned)
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    // For IOCTls that MDD doesn't know. ie non-pm IOCTLs
    return ERROR_NOT_SUPPORTED;
}

extern "C"
void BacklightRegChanged(DWORD dwBrightness)
{
	BKLMSG(BKL_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
    // Called when the MDD gets a backlight registry changed event
    // eg: read brightness settings from registry and update backlight accordingly
    g_dwD0Brightness = dwBrightness;
    BL_SetBrightness(dwBrightness);
    
	BKLMSG(BKL_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
}



