//
//                  SAMSUNG ELECTRONICS
//                  Copyright(C) 2008
//                  ALL RIGHTS RESERVED
//
// This program is proprietary to Samsung Electronics,and is protected under International
// Copyright Act as an unpublished work.Its use and disclosure is limited by the terms and
// conditions of a license agreement.  It may not  be copied  or  otherwise   reproduced or
// disclosed  to persons  outside  the  licensee's   organization  except  in  accordance with
// the  terms and conditions  of such  an agreement. All copies and  reproductions shall be
// the property of  Samsung  Electronics  and  must bear this  notice in its entirety.
//

//
// File name : tsp.cpp
//
// Descriptions : this file contains HW Dependent code for Touch.
//

#include "tsp.h"

#ifdef TSP_QT602240	
	#include "qt602240.h"
#endif



// The touch device driver uses a PWM timer to control the sample rate
DWORD g_dwTSPTimerClockFreq;
DWORD g_dwTSPTimerClockPeriodNs;

// Default TSADC register values
DWORD g_dwResetValue_TSADCCON0;
DWORD g_dwResetValue_TSCON0;
DWORD g_dwResetValue_TSDLY0;	

DWORD g_dwResetValue_TSADCCON1;
DWORD g_dwResetValue_TSCON1;
DWORD g_dwResetValue_TSDLY1;		


INT g_TSPCurSampleRateSetting	= TPDC_SAMPLE_RATE_HIGH;
BOOL g_bTSPInitialized			= FALSE;
BOOL g_bTSPTouchDown				= FALSE;

HANDLE g_hPwrControl 			= INVALID_HANDLE_VALUE;

volatile PMU_MISC_REG			*v_pPMUMISCregs;
volatile GPIO_REG				*v_pGPIOregs;
volatile TSADC_REG 				*v_pTSADC0regs;
volatile TSADC_REG				*v_pTSADC1regs;
volatile VIC_REG				*v_pVIC0regs;
volatile VIC_REG				*v_pVIC2regs;
volatile VIC_REG				*v_pVIC3regs;
volatile PWM_REG				*v_pPWMregs;
volatile CMU_CLK_REG			*v_pCMUCLKregs;

//    Name : TSP_VirtualAlloc(VOID)
//
//    Descriptions : Allocation memory of the TOUCH, ADC, Interrupt registers.
//
//    Return Value : True/Fail
//
BOOL
TSP_VirtualAlloc(VOID)
{
    BOOL ReturnValue = TRUE;
    PHYSICAL_ADDRESS ioPhysicalBase = {0, 0};

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

     v_pPMUMISCregs = (PMU_MISC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_PMU_MISC, sizeof(PMU_MISC_REG), FALSE);
    if (v_pPMUMISCregs == NULL)
    {
        TSPERR(TRUE, (_T("v_pPMUMISCregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

     v_pGPIOregs = (GPIO_REG *)DrvLib_MapIoSpace(BASE_REG_PA_GPIO, sizeof(GPIO_REG), FALSE);
    if (v_pGPIOregs == NULL)
    {
        TSPERR(TRUE, (_T("v_pGPIOregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

	// TSADCCON0 is always used to select an appropriate channel.
    v_pTSADC0regs = (TSADC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_TSADC0, sizeof(TSADC_REG), FALSE);
    if (v_pTSADC0regs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pTSADC0regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pTSADC1regs = (TSADC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_TSADC1, sizeof(TSADC_REG), FALSE);
    if (v_pTSADC1regs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pTSADC1regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }


    v_pVIC0regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC0, sizeof(VIC_REG), FALSE);
    if (v_pVIC0regs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pVIC0regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pVIC2regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC2, sizeof(VIC_REG), FALSE);
    if (v_pVIC2regs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pVIC2regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pVIC3regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC3, sizeof(VIC_REG), FALSE);
    if (v_pVIC3regs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pVIC3regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }
    
    v_pPWMregs = (PWM_REG *)DrvLib_MapIoSpace(BASE_REG_PA_PWMTIMER, sizeof(PWM_REG), FALSE);
    if (v_pPWMregs == NULL)
    {
        TSPERR(TRUE, (_T("TSP_VirtualAlloc() : v_pPWMregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

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

    
CleanUp:

    if (!ReturnValue)
    {
        TSP_VirtualFree();
	    g_bTSPInitialized = FALSE;
    }
    else
    {
        TSP_CreateADCMutex();
	    g_bTSPInitialized = TRUE;
    }

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return ReturnValue;
}


//    Name : TSP_VirtualFree(VOID)
//
//    Descriptions : Deallocation memory of the TOUCH, ADC, Interrupt registers.
//
//    Return Value : True/Fail
//
VOID
TSP_VirtualFree(VOID)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if (v_pPMUMISCregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pPMUMISCregs);
        v_pPMUMISCregs = NULL;
    }
    if (v_pGPIOregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pGPIOregs);
        v_pGPIOregs = NULL;
    }

    if (v_pTSADC0regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pTSADC0regs);
        v_pTSADC0regs = NULL;
    }

    if (v_pTSADC1regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pTSADC1regs);
        v_pTSADC1regs = NULL;
    }

    if (v_pVIC0regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC0regs);
        v_pVIC0regs = NULL;
    }

    if (v_pVIC2regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC2regs);
        v_pVIC2regs = NULL;
    }

    if (v_pVIC3regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC3regs);
        v_pVIC3regs = NULL;
    }

    if (v_pPWMregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pPWMregs);
        v_pPWMregs = NULL;
    }

    if (v_pCMUCLKregs)
    {
    	DrvLib_UnmapIoSpace((PVOID)v_pCMUCLKregs);
    	v_pCMUCLKregs = NULL;
    };

    if (g_hPwrControl != INVALID_HANDLE_VALUE)
    {
    	CloseHandle(g_hPwrControl);
    	g_hPwrControl = INVALID_HANDLE_VALUE;
    }
    
    g_bTSPInitialized = FALSE;

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_EnableInt(INT type, BOOL En)
//
// Descriptions : Touch or Timer interrupt Enable/Disable.
//
//             INT     type     : TOUCH_DEV/TIMER_DEV
//            BOOL    En        : True/False (Enable/Disable)
//            Return Value    : None
//
VOID
TSP_EnableInt(INT type, BOOL En)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if ( g_bTSPInitialized )
    {
        if ( type & TOUCH_DEV )
        {
        	// Touch interrupt (PENDN) enable/disable is controlled by OAL. (VICENABLE)
        }
        if ( type & TIMER_DEV )
        {
#ifdef TSP_USE_TIMER3        
            if ( En )
            {
                v_pPWMregs->TINT_CSTAT |= (BIT_TINT_CSTAT_TIMER3_INT_EN);   // Enable Timer 3 Interrupt.
            }
            else
            {
                v_pPWMregs->TINT_CSTAT &= ~(BIT_TINT_CSTAT_TIMER3_INT_EN);   // Disable Timer 3 Interrupt.
            }
#else //TSP_USE_TIMER1       
            if ( En )
            {
                v_pPWMregs->TINT_CSTAT |= (BIT_TINT_CSTAT_TIMER1_INT_EN);   // Enable Timer 1 Interrupt.
            }
            else
            {
                v_pPWMregs->TINT_CSTAT &= ~(BIT_TINT_CSTAT_TIMER1_INT_EN);   // Disable Timer 1 Interrupt.
            }
#endif
            
        }
    }

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_SetPollingRate(DWORD RATE)
//
// Descriptions : Setting timer period for ink sampling.
//
// DWORD dwSampleRate		: Sampling rate
// Return Value				: None
//
VOID
TSP_SetPollingRate(DWORD dwSampleRate)
{
	DWORD dwTSPTimerPrescaler;
	DWORD dwTSPTimerInputClockFreq;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
    if (g_bTSPInitialized)
    {
#ifdef TSP_USE_TIMER3    
	    dwTSPTimerPrescaler = (v_pPWMregs->TCFG0 & 0xFF00) >> 8;
		dwTSPTimerInputClockFreq = GET_PCLKPSYS(v_pCMUCLKregs->PLL_CON.MPLL_CON, v_pCMUCLKregs->CLK_DIV.CLK_DIV0);
		g_dwTSPTimerClockFreq = dwTSPTimerInputClockFreq / (dwTSPTimerPrescaler + 1) / (0 + 1);
    	g_dwTSPTimerClockPeriodNs = (DWORD)((float)(1000000000 / (float)g_dwTSPTimerClockFreq) + 0.5);

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

		TSP_TCNTB = g_dwTSPTimerClockFreq / dwSampleRate;			// Polling rate

		DBGMSG(TCH_INFO, (TEXT("Touch sample rate        : %d\r\n"), dwSampleRate));
		DBGMSG(TCH_INFO, (TEXT("Timer prescaler          : %d\r\n"), dwTSPTimerPrescaler));
		DBGMSG(TCH_INFO, (TEXT("Timer input clock freq   : %d\r\n"), dwTSPTimerInputClockFreq));
		DBGMSG(TCH_INFO, (TEXT("Timer clock freq         : %d\r\n"), g_dwTSPTimerClockFreq));
		DBGMSG(TCH_INFO, (TEXT("Timer clock period (ns)  : %d\r\n"), g_dwTSPTimerClockPeriodNs));
		DBGMSG(TCH_INFO, (TEXT("Sample rate              : %d\r\n"), dwSampleRate));
		DBGMSG(TCH_INFO, (TEXT("TCNTB3                   : %d\r\n"), TSP_TCNTB));
#else //TSP_USE_TIMER1
	    dwTSPTimerPrescaler = (v_pPWMregs->TCFG0 & 0xFF);
		dwTSPTimerInputClockFreq = GET_PCLKPSYS(v_pCMUCLKregs->PLL_CON.MPLL_CON, v_pCMUCLKregs->CLK_DIV.CLK_DIV0);
		g_dwTSPTimerClockFreq = dwTSPTimerInputClockFreq / (dwTSPTimerPrescaler + 1) / (0 + 1);
    	g_dwTSPTimerClockPeriodNs = (DWORD)((float)(1000000000 / (float)g_dwTSPTimerClockFreq) + 0.5);

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

		TSP_TCNTB = g_dwTSPTimerClockFreq / dwSampleRate;			// Polling rate

		DBGMSG(TCH_INFO, (TEXT("Touch sample rate        : %d\r\n"), dwSampleRate));
		DBGMSG(TCH_INFO, (TEXT("Timer prescaler          : %d\r\n"), dwTSPTimerPrescaler));
		DBGMSG(TCH_INFO, (TEXT("Timer input clock freq   : %d\r\n"), dwTSPTimerInputClockFreq));
		DBGMSG(TCH_INFO, (TEXT("Timer clock freq         : %d\r\n"), g_dwTSPTimerClockFreq));
		DBGMSG(TCH_INFO, (TEXT("Timer clock period (ns)  : %d\r\n"), g_dwTSPTimerClockPeriodNs));
		DBGMSG(TCH_INFO, (TEXT("Sample rate              : %d\r\n"), dwSampleRate));
		DBGMSG(TCH_INFO, (TEXT("TCNTB1                   : %d\r\n"), TSP_TCNTB));
#endif		
	}

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_CheckInt(VOID)
//
// Descriptions : Identify which interrupt occur (PENDN/TIMER).
//
// Return Value    : True/False (PENDN/TIMER)
//
INT
TSP_CheckInt(VOID)
{
	INT iReturn = 0;
    DWORD x;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

#ifdef TSP_CH0    
    x = (v_pVIC2regs->VICRAWINTR);

    if((x & BIT_PENDN))
    {
        TSPMSG(TCH_DBG, (TEXT("PENDN0\r\n")));
        iReturn = TOUCH_DEV;
        goto VALID_INTERRUPT;
    }
#endif

#ifdef TSP_CH1
    x = (v_pVIC3regs->VICRAWINTR);

    if((x & BIT_PENDN1))
    {
        TSPMSG(TCH_DBG, (TEXT("PENDN1\r\n")));
        iReturn = TOUCH_DEV;
        goto VALID_INTERRUPT;
    }
#endif

#ifdef TSP_QT602240
	// We don't need to identify the type of an interrupt in this case.
	iReturn = TOUCH_DEV;
    goto VALID_INTERRUPT;
#endif


    x = (v_pVIC0regs->VICRAWINTR);

#if TSP_USE_TIMER3    
    if(x & BIT_TIMER3)
    {
        TSPMSG(TCH_DBG, (TEXT("TIMER3\r\n")));
        iReturn = TIMER_DEV;
        goto VALID_INTERRUPT;
    }
#else // TSP_USE_TIMER1
	if(x & BIT_TIMER1)
    {
        TSPMSG(TCH_DBG, (TEXT("TIMER1\r\n")));
        iReturn = TIMER_DEV;
        goto VALID_INTERRUPT;
    }
#endif
	

	TSPMSG(TCH_DBG, (TEXT("All interrupts were cleared.\r\n")));

VALID_INTERRUPT:
	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
	return iReturn;

}


// Name : TSP_DetectPnDn(VOID)
//
// Descriptions : Notify HW to detect PENDN interrupt.
//
// Return Value    : NONE
//
VOID
TSP_DetectPnDn(VOID)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    TSP_ADCLock();
    if ( g_bTSPInitialized )
    {
#ifdef TSP_CH0    
        v_pTSADC0regs->TSCON &= ~(1 << 8);
#endif

#ifdef TSP_CH1
		v_pTSADC1regs->TSCON &= ~(1 << 8);
#endif

    }
    TSP_ADCUnlock();

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_GetSysIntrVal(DWORD * TouchIntr, DWORD * TouchIntrChanged, UINT32 * IrqTable)
//
// Descriptions : Credit Touch/Timer Interrupt.
//
//      DWORD*    TouchIntr            : Touch Interrupt ID
//      DWORD*    TouchIntrChanged    : Polling(Timer) Interrupt ID
//      UINT32*    IrqTable            : Multiple Interrupt Source Table
//      Return Value    : True/False
//
BOOL
TSP_GetSysIntrVal(DWORD *TouchIntr, DWORD *TouchIntrChanged, UINT32 *IrqTable)
{
	UINT32 irqBuf[3];

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
   	
/* TODO : dual channel */

#ifdef TSP_CH0
    irqBuf[0] = IrqTable[0];
    irqBuf[1] = IrqTable[1];
    irqBuf[2] = IRQ_PENDN;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irqBuf, sizeof(irqBuf), TouchIntr, sizeof(UINT32), NULL))
    {
        TSPERR(TRUE, (TEXT("PENDN SYSINTR Request was failed.\r\n")));
        goto FAILED_TO_GET_SYSINTR;
    }
#endif

#ifdef TSP_CH1
    irqBuf[0] = IrqTable[0];
    irqBuf[1] = IrqTable[1];
    irqBuf[2] = IRQ_PENDN1;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irqBuf, sizeof(irqBuf), TouchIntr, sizeof(UINT32), NULL))
    {
        TSPERR(TRUE, (TEXT("PENDN SYSINTR Request was failed.\r\n")));
        goto FAILED_TO_GET_SYSINTR;
    }
#endif

#ifdef TSP_QT602240
    irqBuf[0] = IrqTable[0];
    irqBuf[1] = IrqTable[1];
    irqBuf[2] = TOUCH_INT_NUM;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irqBuf, sizeof(irqBuf), TouchIntr, sizeof(UINT32), NULL))
    {
        TSPERR(TRUE, (TEXT("PENDN SYSINTR Request was failed.\r\n")));
        goto FAILED_TO_GET_SYSINTR;
    }
#endif

    irqBuf[0] = IrqTable[0];
    irqBuf[1] = IrqTable[1];
#ifdef TSP_USE_TIMER3    
    irqBuf[2] = IRQ_TIMER3;
#else //TSP_USE_TIMER1
	irqBuf[2] = IRQ_TIMER1;
#endif

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irqBuf, sizeof(irqBuf), TouchIntrChanged, sizeof(UINT32), NULL))
    {
        TSPERR(TRUE, (TEXT("TIMER(Touch Changed) SYSINTR Request was failed.\r\n")));
        goto FAILED_TO_GET_SYSINTR;
    }



    TSPMSG(TCH_DBG, (TEXT("TSP_GetSysIntrVal: SysIntr [ 0x%X | 0x%X ]\r\n"), *TouchIntr, *TouchIntrChanged));

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return (TRUE);


FAILED_TO_GET_SYSINTR:
	*TouchIntr = SYSINTR_UNDEFINED;
	return (FALSE);

    
}


// Name : TSP_ClearInt(INT type)
//
// Descriptions : Clear Timer/Touch Interrupt.
//
//      INT        type    : TOUCH_DEV/TIMER_DEV
// Return Value    : NONE
//
VOID
TSP_ClearInt(INT type)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if ( g_bTSPInitialized )
    {
        if ( type & TOUCH_DEV )
        {

#ifdef TSP_QT602240
			v_pGPIOregs->GPIOINTPND.GPJ0_INT_PEND = (1 << 5);			// GPJ0_INT5
#else

            TSP_ADCLock();

	#ifdef TSP_CH0
            v_pTSADC0regs->CLRINTPEN = 1;
            v_pTSADC0regs->CLRINTADC = 1;
	#endif

	#ifdef TSP_CH1
            v_pTSADC1regs->CLRINTPEN = 1;
            v_pTSADC1regs->CLRINTADC = 1;
	#endif

            TSP_ADCUnlock();
#endif	// TSP_QT602240

			TSPMSG(TCH_DBG, (TEXT("Clear TOUCH_INT\r\n")));
        }

        if(type & TIMER_DEV)
        {
#ifdef TSP_USE_TIMER3        
            v_pPWMregs->TINT_CSTAT |= (BIT_TINT_CSTAT_TIMER3_INT_STAT); // Clear by writing '1'.
#else //TSP_USE_TIMER1
            v_pPWMregs->TINT_CSTAT |= (BIT_TINT_CSTAT_TIMER1_INT_STAT); // Clear by writing '1'.
#endif      
			TSPMSG(TCH_DBG, (TEXT("Clear TIMER_INT\r\n")));
        }
    }

    TSPMSG(TCH_FUNC, (TEXT("-TSP_ClearInt()\r\n")));
}


void TSP_ClearPenHistory(DWORD dwUpDown)
{
    TSP_ADCLock();

	if(dwUpDown == PEN_UP)
	{
		#ifdef TSP_CH0
			v_pTSADC0regs->TSPENSTAT &= ~(1 << 1);
		#endif

		#ifdef TSP_CH1
			v_pTSADC1regs->TSPENSTAT &= ~(1 << 1);
		#endif
	}
	
	if(dwUpDown == PEN_DOWN)
	{
		#ifdef TSP_CH0
			v_pTSADC0regs->TSPENSTAT &= ~(1 << 0);
		#endif

		#ifdef TSP_CH1
			v_pTSADC1regs->TSPENSTAT &= ~(1 << 0);
		#endif
	}

    TSP_ADCUnlock();
	
}



BOOL TSP_GetPenHistory(DWORD dwUpDown)
{
	BOOL bReturn = FALSE;

    TSP_ADCLock();
	
	if(dwUpDown == PEN_UP)
	{
		#ifdef TSP_CH0
			bReturn = (v_pTSADC0regs->TSPENSTAT & (1 << 1)) ? TRUE : FALSE;
		#endif

		#ifdef TSP_CH1
			bReturn = (v_pTSADC1regs->TSPENSTAT & (1 << 1)) ? TRUE : FALSE;
		#endif
	}
	
	if(dwUpDown == PEN_DOWN)
	{
		#ifdef TSP_CH0
			bReturn = (v_pTSADC0regs->TSPENSTAT & (1 << 0)) ? TRUE : FALSE;
		#endif

		#ifdef TSP_CH1
			bReturn = (v_pTSADC1regs->TSPENSTAT & (1 << 0)) ? TRUE : FALSE;
		#endif
	}

    TSP_ADCUnlock();

	return bReturn;
}


// Name : TSP_GetDefaultRegs()
//
// Descriptions : Backup current ADC register value.
//
// Return Value    : NONE
//
// Power management
VOID
TSP_GetDefaultRegs()
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
#ifdef TSP_QT602240
	// Nothing to do
#else

    if(g_bTSPInitialized)
    {
        TSP_ADCLock();

	#ifdef TSP_CH0		
        g_dwResetValue_TSADCCON0 = v_pTSADC0regs->TSADCCON;
        g_dwResetValue_TSCON0 = v_pTSADC0regs->TSCON;
        g_dwResetValue_TSDLY0 = v_pTSADC0regs->TSDLY;
	#endif	// TSP_CH0

	#ifdef TSP_CH1
        g_dwResetValue_TSADCCON1 = v_pTSADC1regs->TSADCCON;
        g_dwResetValue_TSCON1 = v_pTSADC1regs->TSCON;
        g_dwResetValue_TSDLY1 = v_pTSADC1regs->TSDLY;
	#endif	// TSP_CH1

        TSP_ADCUnlock();
    }
#endif	// TSP_QT602240


	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

}


// Name : TSP_SetDefaultRegs()
//
// Descriptions : Restore the previous ADC register value.
//
// Return Value    : NONE
//
VOID
TSP_SetDefaultRegs(BOOL bPowerHandler)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
#ifdef TSP_QT602240
	// Nothing to do
#else

    if (g_bTSPInitialized)
    {
        if (!bPowerHandler)
        {
            TSP_ADCLock();
        }

	#ifdef TSP_CH0		
        v_pTSADC0regs->TSDLY = g_dwResetValue_TSDLY0;
        v_pTSADC0regs->TSADCCON = g_dwResetValue_TSADCCON0;
        v_pTSADC0regs->TSCON = g_dwResetValue_TSCON0;
	#endif

	#ifdef TSP_CH1
		v_pTSADC0regs->TSADCCON |= (1 << 17);					// select TSADC1
		
        v_pTSADC1regs->TSDLY = g_dwResetValue_TSDLY1;
        v_pTSADC1regs->TSADCCON = g_dwResetValue_TSADCCON1;
        v_pTSADC1regs->TSCON = g_dwResetValue_TSCON1;
	#endif		
		
        if (!bPowerHandler)
        {
            TSP_ADCUnlock();
        }
    }
#endif	// TSP_QT602240

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

}


// Name : TSP_GetTSPState()
//
// Descriptions : Check stylus state (UP or Down).
//
// Return Value    : 0-down, Not 0-up
//
INT
TSP_GetTSPState()
{
    INT ReturnValue = 0;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
    TSP_ADCLock();
	
#ifdef TSP_CH0	
    ReturnValue = ( (v_pTSADC0regs->TSDATX & (1 << 15)) | (v_pTSADC0regs->TSDATY & (1 << 15)) ) ? PEN_UP : PEN_DOWN;
#endif

#ifdef TSP_CH1
	ReturnValue = ( (v_pTSADC1regs->TSDATX & (1 << 15)) | (v_pTSADC1regs->TSDATY & (1 << 15)) ) ? PEN_UP : PEN_DOWN;
#endif

    TSP_ADCUnlock();

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return ReturnValue;
}



// Name : TSP_SampleStart(VOID)
//
// Descriptions : Trigger Timer for sampling under down state.
//
// Return Value    : NONE
//
VOID
TSP_SampleStart(VOID)
{
    DWORD tmp = 0;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


#ifdef TSP_USE_TIMER3	
    // Timer Control Register Setting
    // TCON[16:19] =
    // |Stop / Start Timer3|No Op / Update TCNTB3, TCMPB3|Inverter off/TOUT3 inverter-on|one-shot/auto-reload|
    tmp = v_pPWMregs->TCON & (~(0xf << TCONBIT_TIMER3));        // Clear TCON Register Bits on Timer3
    v_pPWMregs->TCON = tmp | (TCONT3_Update);                    // Update TCNTB3, stop
    v_pPWMregs->TCON = tmp | (TCONT3_Start|TCONT3_AutoReload);    // Interval mode(auto-reload), Start
#else // TSP_USE_TIMER1
    tmp = v_pPWMregs->TCON & (~(0xf << TCONBIT_TIMER1));        // Clear TCON Register Bits on Timer1
    v_pPWMregs->TCON = tmp | (TCONT1_Update);                    // Update TCNTB1, stop
    v_pPWMregs->TCON = tmp | (TCONT1_Start|TCONT1_AutoReload);    // Interval mode(auto-reload), Start
#endif

    TSP_EnableInt(TIMER_DEV, TRUE); // Emable Timer Interrupt.


	// configure conversion delay on the continuous pen down state..
	TSP_ADCLock();
#ifdef TSP_CH0	
	v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT_2;
#endif

#ifdef TSP_CH1
	v_pTSADC1regs->TSDLY = CONVERSION_DELAY_COUNT_2;
#endif
	TSP_ADCUnlock();	


	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_SampleStop(VOID)
//
// Descriptions : Stop the timer sampling.
//
// Return Value : NONE
//
VOID
TSP_SampleStop(VOID)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

#ifdef TSP_USE_TIMER3	
    v_pPWMregs->TCON &= ~(TCONT3_Start);                        // Timer3, stop
#else //TSP_USE_TIMER1
    v_pPWMregs->TCON &= ~(TCONT1_Start);                        // Timer1, stop
#endif

    TSP_EnableInt(TIMER_DEV, FALSE);                            // Disable Timer Interrupt
    TSP_ClearInt(TIMER_DEV);                                    // Clear Timer Interrupt

	// restore conversion delay on the wait for pen down state.
	TSP_ADCLock();
#ifdef TSP_CH0	
	v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;
#endif

#ifdef TSP_CH1
	v_pTSADC1regs->TSDLY = CONVERSION_DELAY_COUNT;
#endif
	TSP_ADCUnlock();


	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
}



// Name : TSP_PowerOn(BOOL bPowerHandler)
//
// Descriptions : Initialize HW (ADC, Timer).
//
// Return Value : NONE
//
VOID
TSP_PowerOn(BOOL bPowerHandler)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	// GPIO configuration
#ifdef TSP_QT602240	
	if(bPowerHandler == TRUE)
	{
		QT602240_Resume();
	}
	else
	{
		// Enable QT602240
	
		// Turn on QT602240
	    Set_PinFunction(v_pGPIOregs, TOUCH_EN);
		Set_PinConPD(v_pGPIOregs, TOUCH_EN, sgip_Prev_Val);			// keep previous value on power down
	    
	    Set_PinData(v_pGPIOregs, TOUCH_EN, 1);
		Sleep(100);													// spec : 40ms at minimum

		// Reset QT602240
		Set_PinFunction(v_pGPIOregs, TOUCH_RST);
		Set_PinConPD(v_pGPIOregs, TOUCH_RST, sgip_Prev_Val);		// keep previous value on power down
		
		Set_PinData(v_pGPIOregs, TOUCH_RST, 0);
		Sleep(10);													// spec : 90ns at minimum
		Set_PinData(v_pGPIOregs, TOUCH_RST, 1);				
		Sleep(100);													// spec : 40ms at minimum

		// Interrupt pin configuration
		Set_PinFunction(v_pGPIOregs, TOUCH_INT);
		Set_PinPullUD(v_pGPIOregs, TOUCH_INT, sgip_PULL_UP);
		Set_PinConPD(v_pGPIOregs, TOUCH_INT, sgip_Prev_Val);
		Set_PinPullPD(v_pGPIOregs, TOUCH_INT, sgip_PULL_UP);
		
		v_pGPIOregs->GPIOINTCON.GPJ0_INT_CON &= ~(0x7 << 20);		// 000 : Low level	

		if(QT602240_Init() == FALSE)
		{	
			TSPERR(TRUE, (TEXT("Failed to initialize the QT602240\r\n")));
		}
	}
#else

	DWORD dwIPIndex;
	DWORD dwBytes;

	if(g_hPwrControl == INVALID_HANDLE_VALUE)
	{
		// Open a POWERCONTROL device
	    g_hPwrControl = CreateFile( L"PWC0:", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
	    if (INVALID_HANDLE_VALUE == g_hPwrControl)
	    {
	        TSPERR(TRUE, (TEXT("Failed to open PWC0!\r\n")));
	        goto CLEAN_UP;
	    }
	}

#if (S5PV210_EVT == 0)
	// Turn on the power
	dwIPIndex = PWR_IP_TS_ADC;
	if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_POWER_ON, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
	{
		TSPERR(TRUE, (TEXT("Failed to turn on TSADC!\r\n")));
	}
#endif

	// Enable the clock
	dwIPIndex = CLK_IP3_TSADC;
	if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_CLOCK_ON, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
	{
		TSPERR(TRUE, (TEXT("Failed to enable TSADC clock!\r\n")));
	}

    
    TSP_ClearInt(TOUCH_DEV);                                    // Clear TSADC Interrupt

    if (!bPowerHandler)
    {
        TSP_ADCLock();
    }

#ifdef TSP_CH0
	// A/D conversion delay (D)
	// T1(Delay)        : D * (1 / FILCLK)
	// T2(X-conversion) : (D + 5) * (1 / PCLK)
	// T3(Y-conversion) : (D + 5) * (1 / PCLK)
	// A/D conversion time = T1 + T2 + T3
	v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;

    // Touch screen 0
    // 12bit A/D conversion
    // A/D converter prescaler is enabled
    // Normal operation mode (not standby mode)
    v_pTSADC0regs->TSADCCON = (0x1<<16)|(0x1<<14)|(PRSCVL << 6)|(0<<2);
    

	// Mode configuration : Wait for interrupt mode (INT_PEN0)
    // Detection   : Pen down
    // YM          : VSSA_ADC  (enabled)
    // YP          : Hi-z      (disabled)
    // XM          : Hi-z      (disabled)
    // XP          : Hi-z      (disabled)
    // XP pull-up  : enabled
    // AUTO_PST    : Normal A/D conversion
    // XY_PST      : Waiting for interrupt mode
    v_pTSADC0regs->TSCON = (0<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
	
    TSPMSG(TCH_DBG, (TEXT("TSDLY0[%08x] TSADCCON0[%08x] TSCON0[%08x] VIC2INTENABLE[%08x]\r\n"), \
        v_pTSADC0regs->TSDLY, v_pTSADC0regs->TSADCCON, \
        v_pTSADC0regs->TSCON, v_pVIC2regs->VICINTENABLE));	
#endif	// TSP_CH0

#ifdef TSP_CH1

#ifdef TOUCH_PRESSURE_DETECTION

	// TSADC0 is also used for normal ADC conversion.

	// select TSADC0
	v_pTSADC0regs->TSADCCON &= ~(1 << 17);
	
	// Initialize TSADCCON0
    v_pTSADC0regs->TSADCCON = (0x1<<16)|(0x1<<14)|(PRSCVL << 6)|(0<<2);
    v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;

#endif

	// A/D conversion delay (D)
	// T1(Delay)        : D * (1 / FILCLK)
	// T2(X-conversion) : (D + 5) * (1 / PCLK)
	// T3(Y-conversion) : (D + 5) * (1 / PCLK)
	// A/D conversion time = T1 + T2 + T3
	v_pTSADC1regs->TSDLY = CONVERSION_DELAY_COUNT;

	// select TSADC1
	v_pTSADC0regs->TSADCCON |= (1 << 17);
	
    // Touch screen 0
    // 12bit A/D conversion
    // A/D converter prescaler is enabled
    // Normal operation mode (not standby mode)
    v_pTSADC1regs->TSADCCON = (0x1<<16)|(0x1<<14)|(PRSCVL << 6)|(0<<2);
    

	// Mode configuration : Wait for interrupt mode (INT_PEN1)
    // Detection   : Pen down
    // YM          : VSSA_ADC  (enabled)
    // YP          : Hi-z      (disabled)
    // XM          : Hi-z      (disabled)
    // XP          : Hi-z      (disabled)
    // XP pull-up  : enabled
    // AUTO_PST    : Normal A/D conversion
    // XY_PST      : Waiting for interrupt mode
    v_pTSADC1regs->TSCON = (0<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
	
    TSPMSG(TCH_DBG, (TEXT("TSDLY1[%08x] TSADCCON1[%08x] TSCON1[%08x] VIC3INTENABLE[%08x]\r\n"), \
        v_pTSADC1regs->TSDLY, v_pTSADC1regs->TSADCCON, \
        v_pTSADC1regs->TSCON, v_pVIC3regs->VICINTENABLE));
#endif	// TSP_CH1

   
    if ( !bPowerHandler )
    {
        TSP_ADCUnlock();
    }

    // We don't need to set the prescaler of PWM timer for touch, because OS timer already set it: PCLK/(1+1)/(div val)

	if(g_TSPCurSampleRateSetting == TPDC_SAMPLE_RATE_HIGH)
	{
		TSP_SetPollingRate(TSP_SAMPLE_RATE_HIGH);
	}
	else
	{
		TSP_SetPollingRate(TSP_SAMPLE_RATE_LOW);
	}


CLEAN_UP:

#endif	// TSP_QT602240

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


// Name : TSP_PowerOff(BOOL bPowerHandler)
//
// Descriptions : Deinitialize HW (ADC, Timer).
//
// Return Value : NONE
//
VOID
TSP_PowerOff(BOOL bPowerHandler)
{
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

#ifdef TSP_QT602240

	QT602240_Suspend();
	
#else

#if (S5PV210_EVT == 0)
	DWORD dwIPIndex;
	DWORD dwBytes;
#endif

#ifdef TSP_USE_TIMER3
    v_pPWMregs->TCON &= ~(TCONT3_Start);        // Timer3, stop
#else //TSP_USE_TIMER1
    v_pPWMregs->TCON &= ~(TCONT1_Start);        // Timer1, stop
#endif

    TSP_EnableInt(TOUCH_DEV|TIMER_DEV, FALSE);

    TSP_ClearInt(TOUCH_DEV);

    // Reset value to TSCON0 and Stand by mode
    TSP_SetDefaultRegs(bPowerHandler);

#ifdef TSP_CH0
    TSPMSG(TCH_DBG, (TEXT("TSDLY0[%08x] TSADCCON0[%08x] TSCON0[%08x] VIC2INTENABLE[%08x]\r\n"), \
        v_pTSADC0regs->TSDLY, v_pTSADC0regs->TSADCCON, \
        v_pTSADC0regs->TSCON, v_pVIC2regs->VICINTENABLE));	
#endif	// TSP_CH0

#ifdef TSP_CH1
    TSPMSG(TCH_DBG, (TEXT("TSDLY1[%08x] TSADCCON1[%08x] TSCON1[%08x] VIC3INTENABLE[%08x]\r\n"), \
        v_pTSADC1regs->TSDLY, v_pTSADC1regs->TSADCCON, \
        v_pTSADC1regs->TSCON, v_pVIC3regs->VICINTENABLE));
#endif	// TSP_CH1


#if (S5PV210_EVT == 0)
	// power is automatically turned off on sleep mode
	dwIPIndex = PWR_IP_TS_ADC;
	if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_POWER_OFF, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
	{
		TSPERR(TRUE, (TEXT("Failed to tyrn off PWR_IP_TS_ADC clock!\r\n")));
	}
#endif

#if 0 // clock is automatically disabled on sleep mode
	dwIPIndex = CLK_IP3_TSADC;
	if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_CLOCK_OFF, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
	{
		TSPERR(TRUE, (TEXT("Failed to disable TSADC clock!\r\n")));
	}
#endif


#endif	// TSP_QT602240

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}



int TSP_GetPressure(DWORD dwWaitTargetStatus)
{

#ifdef CALC_FLOATING
	FLOAT YP_F = 0.0;
	FLOAT XP_F = 0.0;
	FLOAT YM_F = 0.0;

	FLOAT R_Contact_F = 0.0;
#else
	DWORD YP = 0;
	DWORD XP = 0;
	DWORD YM = 0;

	int R_Contact = 0;
#endif  // CALC_FLOATING

	DWORD dwTSDLY = v_pTSADC0regs->TSDLY;			// back-up original TSDLY0 value

	TSP_ADCLock();

	// Normal X/Y conversion mode uses TSADC0 only.
	
	// configure conversion delay on the continuous pen down state..
	v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT_2;

#ifdef TSP_CH0

	// read YP
	v_pTSADC0regs->TSCON = (0<<8)|(0<<7)|(1<<6)|(1<<5)|(0<<4)|(1<<3)|(0<<2)|(0); // YP
	v_pTSADC0regs->ADCMUX = 3;		// YP0
	v_pTSADC0regs->TSADCCON |= (1 << 0);
	
	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

#ifdef CALC_FLOATING
	YP_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	YP = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING

	// read XP
	v_pTSADC0regs->TSCON = (0<<8)|(0<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2)|(0); // XP
	v_pTSADC0regs->ADCMUX = 5;		// XP0
	v_pTSADC0regs->TSADCCON |= (1 << 0);

	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion
	
#ifdef CALC_FLOATING
	XP_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	XP = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING

	// read YM

	v_pTSADC0regs->TSCON = (0<<8)|(0<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2)|(0); // YM
	v_pTSADC0regs->ADCMUX = 2;	// YM0
	v_pTSADC0regs->TSADCCON |= (1 << 0);

	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

#ifdef CALC_FLOATING
	YM_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	YM = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING


#ifdef CALC_FLOATING
//	R_Contact_F = (FLOAT)( (R_XPLATE_VALUE * ((YP_F * 100) / 4096)) * ( ((YM_F / XP_F) * 100) - 100) );  // weight : 10000
//	R_Contact_F = R_Contact_F / 10000;
	R_Contact_F = R_XPLATE_VALUE * (YP_F / 4096) * ((YM_F / XP_F) - 1);

//	TSPMSG(TCH_DBG, (TEXT("XP = %04d  YP = %04d  YM = %04d\r\n"), (DWORD)XP_F, (DWORD)YP_F, (DWORD)YM_F));
	
#else
	if ( XP == 0 )
	{
		XP = 1;
	}

	TSPMSG(TCH_DBG, (TEXT("XP = %04d  YP = %04d  YM = %04d\r\n"), XP, YP, YM));
	
	R_Contact = ((YM-XP)<<(WEIGHT_VALUE_FOR_POWER_OF_2 - 12)) * YP / XP;  // 12 powers of 2 means 4096
	R_Contact = R_XPLATE_VALUE * (R_Contact >> RESOLUTION_RANGE);  // caution for 32bit overflow
	R_Contact = R_Contact >> (WEIGHT_VALUE_FOR_POWER_OF_2-RESOLUTION_RANGE);
	
#endif  // CALC_FLOATING

	if (dwWaitTargetStatus = PEN_DOWN)
	{
		v_pTSADC0regs->TSCON = (0<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
		v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;
	}
	else
	{
		v_pTSADC0regs->TSCON = (1<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
		v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT_2;
	}

#endif // TSP_CH0





#ifdef TSP_CH1

	// read YP
	v_pTSADC0regs->TSADCCON |= (1 << 17);											// select TSADC1
	v_pTSADC1regs->TSCON = (0<<8)|(0<<7)|(1<<6)|(1<<5)|(0<<4)|(1<<3)|(0<<2)|(0);
	v_pTSADC0regs->TSADCCON &= ~(1 << 17);											// select TSADC0
	v_pTSADC0regs->ADCMUX = 7;														// YP1
	v_pTSADC0regs->TSADCCON |= (1 << 0);
	

	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

#ifdef CALC_FLOATING
	YP_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	YP = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING

	// read XP
	v_pTSADC0regs->TSADCCON |= (1 << 17);											// select TSADC1
	v_pTSADC1regs->TSCON = (0<<8)|(0<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2)|(0);
	v_pTSADC0regs->TSADCCON &= ~(1 << 17);											// select TSADC0
	v_pTSADC0regs->ADCMUX = 9;														// XP1
	v_pTSADC0regs->TSADCCON |= (1 << 0);

	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion
	
#ifdef CALC_FLOATING
	XP_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	XP = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING

	// read YM
	v_pTSADC0regs->TSADCCON |= (1 << 17);											// select TSADC1
	v_pTSADC1regs->TSCON = (0<<8)|(0<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(0<<2)|(0);	// YM
	v_pTSADC0regs->TSADCCON &= ~(1 << 17);											// select TSADC0
	v_pTSADC0regs->ADCMUX = 6;														// YM1(AIN6)
	v_pTSADC0regs->TSADCCON |= (1 << 0);

	while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
	while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

#ifdef CALC_FLOATING
	YM_F = (FLOAT)(0xFFF & v_pTSADC0regs->TSDATX);
#else
	YM = (0xFFF & v_pTSADC0regs->TSDATX);
#endif  // CALC_FLOATING


#ifdef CALC_FLOATING
//	R_Contact_F = (FLOAT)( (R_XPLATE_VALUE * ((YP_F * 100) / 4096)) * ( ((YM_F / XP_F) * 100) - 100) );  // weight : 10000
//	R_Contact_F = R_Contact_F / 10000;
	R_Contact_F = R_XPLATE_VALUE * (YP_F / 4096) * ((YM_F / XP_F) - 1);
#else
	if ( XP == 0 )
	{
		XP = 1;
	}

	TSPMSG(TCH_DBG, (TEXT("XP = %04d  YP = %04d  YM = %04d\r\n"), XP, YP, YM));
	
	R_Contact = ((YM-XP)<<(WEIGHT_VALUE_FOR_POWER_OF_2 - 12)) * YP / XP;  // 12 powers of 2 means 4096
	R_Contact = R_XPLATE_VALUE * (R_Contact >> RESOLUTION_RANGE);  // caution for 32bit overflow
	R_Contact = R_Contact >> (WEIGHT_VALUE_FOR_POWER_OF_2-RESOLUTION_RANGE);

	
#endif  // CALC_FLOATING

	if (dwWaitTargetStatus = PEN_DOWN)
	{
		v_pTSADC1regs->TSCON = (0<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
		v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;
	}
	else
	{
		v_pTSADC1regs->TSCON = (1<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
		v_pTSADC0regs->TSDLY = CONVERSION_DELAY_COUNT;
	}

	// restore conversion delay on the wait for pen down state. (TSDLY0 only)
	v_pTSADC0regs->TSDLY = dwTSDLY;

#endif // TSP_CH1

	TSP_ADCUnlock();

#ifdef CALC_FLOATING
	return (int)R_Contact_F;
#else
	return (int)R_Contact;
#endif

}

	

// Name : TSP_GetXY(int *px, int *py)
//
// Descriptions : Get the point coordinates and filtering.
//
//      int *px : X coordinates
//      int *py : Y coordinates
// Return Value    : True/False
//
#ifndef TSP_QT602240
BOOL
TSP_GetXY(int *px, int *py)
{
	BOOL bRet = FALSE;
    INT i, j, k;
    int x[TSP_SAMPLE_NUM], y[TSP_SAMPLE_NUM];

    int dx, dy;
    int temp;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    TSP_ADCLock();


#ifdef TSP_CH0
    v_pTSADC0regs->TSADCCON &= ~(1 << 17);			// Select TSADC0
    
    for (i = 0; i < TSP_SAMPLE_NUM; i++)
    {
		// Mode configuration : Auto X/Y position conversion (INT_PEN0)
	    // Detection   : Pen up
	    // YM          : Hi-z      (disabled)
	    // YP          : Hi-z      (disabled)
	    // XM          : Hi-z      (disabled)
	    // XP          : Hi-z      (disabled)
	    // XP pull-up  : disabled
	    // AUTO_PST    : Auto X/Y position conversion
	    // XY_PST      : No operation mode
	    v_pTSADC0regs->TSCON = (1<<8)|(0<<7)|(1<<6)|(0<<5)|(1<<4)|(1<<3)|(1<<2)|(0);			// 0x5C : Auto X/Y position conversion mode
	    v_pTSADC0regs->TSADCCON |= (1 << 0);													// Start auto conversion

		// Actually, Sleep(0) relinquishes the rest of thread time slice.
		// Thus, it is possible to wait for more than several milli-seconds if other threads in the queue have same or higher priority.
        while (v_pTSADC0regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
        while (!(v_pTSADC0regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

		x[i] = (0xFFF & v_pTSADC0regs->TSDATX);
		y[i] = (0xFFF & v_pTSADC0regs->TSDATY);
    }

    v_pTSADC0regs->TSCON = (1<<8)|(1<<7)|(1<<6)|(0<<5)|(1<< 4)|(0<<3)|(0<<2)|(3);				// 0x1D3 : Wait for Interrupt mode (Pen up)
#endif


#ifdef TSP_CH1
    v_pTSADC0regs->TSADCCON |= (1 << 17);			// Select TSADC1
    
    for (i = 0; i < TSP_SAMPLE_NUM; i++)
    {
		// Mode configuration : Auto X/Y position conversion (INT_PEN1)
	    // Detection   : Pen up
	    // YM          : Hi-z      (disabled)
	    // YP          : Hi-z      (disabled)
	    // XM          : Hi-z      (disabled)
	    // XP          : Hi-z      (disabled)
	    // XP pull-up  : disabled
	    // AUTO_PST    : Auto X/Y position conversion
	    // XY_PST      : No operation mode 
        v_pTSADC1regs->TSCON = (1<<8)|(0<<7)|(1<<6)|(0<<5)|(1<<4)|(1<<3)|(1<<2)|(0);			// 0x5C : Auto X/Y position conversion mode
        v_pTSADC1regs->TSADCCON |= (1 << 0);													// Start auto conversion

		// Actually, Sleep(0) relinquishes the rest of thread time slice.
		// Thus, it is possible to wait for more than several milli-seconds if other threads in the queue have same or higher priority.
        while (v_pTSADC1regs->TSADCCON & 0x1) {/*for(l=0;l<3000;l++);*/Sleep(0);}				// check if Enable_start is low
        while (!(v_pTSADC1regs->TSADCCON & (1 << 15))) {/*for(l=0;l<3000;l++);*/Sleep(0);}		// Check ECFLG for end of A/D conversion

		x[i] = (0xFFF & v_pTSADC1regs->TSDATX);
		y[i] = (0xFFF & v_pTSADC1regs->TSDATY);
    }

    v_pTSADC1regs->TSCON = (1<<8)|(1<<7)|(1<<6)|(0<<5)|(1<< 4)|(0<<3)|(0<<2)|(3);				// 0x1D3 : Wait for Interrupt mode (Pen up)
#endif

    TSP_ADCUnlock();



	// Sort samples (ascending)
    for (j = 0; j < TSP_SAMPLE_NUM -1; ++j)
    {
        for (k = j+1; k < TSP_SAMPLE_NUM; ++k)
        {
            if ( x[j] > x[k] )
            {
                temp = x[j];
                x[j]=x[k];
                x[k]=temp;
            }

            if ( y[j] > y[k] )
            {
                temp = y[j];
                y[j]=y[k];
                y[k]=temp;
            }
        }
    }


#if 0
    for (j = 0; j < TSP_SAMPLE_NUM; ++j)
    {
		DBGMSG(1, (TEXT("%04d %04d\r\n"), x[j], y[j]));
    }
#endif    


// lowermost sample & uppermost sample are discarded
#ifndef    DETAIL_SAMPLING
    dx = x[2] - x[1];
    dy = y[2] - y[1];
#else
    dx = x[5] - x[2];
    dy = y[5] - y[2];
#endif


	if(((dx > SAMPLE_DISCARD_THRESHOLD) || (dy > SAMPLE_DISCARD_THRESHOLD)))
	{
		TSPMSG(TCH_DBG, (TEXT("An invalid sample %04d %04d\n"), dx, dy));
		bRet = FALSE;
	}
	else
	{
#ifndef DETAIL_SAMPLING
		*px = (x[1] + x[2]) >> 1;
		*py = (y[1] + y[2]) >> 1;
#else
		*px = (x[2] + x[3] + x[4] + x[5]) >> 2;
		*py = (y[2] + y[3] + y[4] + y[5]) >> 2;
#endif		
		bRet = TRUE;
	}


	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

	return bRet;	
}
#else

BOOL TSP_GetXY(int *px, int *py)
{
	return FALSE;
}

#endif // ifndef TSP_QT602240

// Name : TSP_Filtering(int *px, int *py)
//
// Descriptions : Return the average coordinates.
//
// Return Value : True/False
//
BOOL
TSP_Filtering(int *px, int *py)
{
    static int count = 0;
    static INT x[3], y[3];
    INT dx, dy;

	DWORD dwPenDownCheckStartTCNTO;
	DWORD dwPenDownCheckEndTCNTO;
	
	DWORD dwPenDownCheckTimeoutCounter;
	
	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	// Clear pre-collected samples
    if(*px <0 && *py <0)
    {
        count = 0;
        goto FILTER_ERROR;
    }
    else
    {
		// If pre-collected samples are not enough for filtering, get more samples.
		if(count < 3)
		{
			x[count] = *px;
			y[count] = *py;
			count++;

			while(count < 3)
			{
				if(TSP_GetXY(&(x[count]), &(y[count])) == FALSE)
				{
					// If a sample is not valid, cancel this filtering and discard the sample.
					goto FILTER_ERROR;
				}
				else
				{
					count++;
				}
			}

			// three samples are ready
			// take one sample more for filtering
			if(TSP_GetXY(px, py) == FALSE)
			{
				TSPMSG(TCH_DBG, (TEXT("One more smaple was required for filtering but it was not acquired.\r\n")));
				goto FILTER_ERROR;
			}
		}

		// averaging within pre-collected samples

		*px = (x[0] + x[1] + x[2] + *px) >> 2;
		*py = (y[0] + y[1] + y[2] + *py) >> 2;

		dx = (x[2] > *px) ? x[2] - *px : *px - x[2];
		dy = (y[2] > *py) ? y[2] - *py : *py - y[2];	

		// It is possible that a X/Y position is sampled as if it is a valid sample at the moment the pen is up.
		// However, that sample would be invalid because the resistance at the moment the pen is up is not stable.
		// We need to check the status of pen whether it is up(stable) or down(unstable).
		//
		// If it causes a performance issue, PEN_UP_CHECK_THRESHOLD_DIFFERENCE and PEN_UP_CHECK_TIME_IN_NS should be changed.
		if(dx > PEN_UP_CHECK_THRESHOLD_DIFFERENCE || dy > PEN_UP_CHECK_THRESHOLD_DIFFERENCE)
		{
			// Timer value when the pen status check is started
			dwPenDownCheckStartTCNTO = TSP_TCNTO;

			// How long will we check the status of pen	
			dwPenDownCheckTimeoutCounter = PEN_UP_CHECK_TIME_IN_NS / g_dwTSPTimerClockPeriodNs;

			// Pen status check has to be finished within in a sample time(1 / sample rate)
			if((dwPenDownCheckStartTCNTO > dwPenDownCheckTimeoutCounter) &&
				(dwPenDownCheckStartTCNTO - dwPenDownCheckTimeoutCounter < TSP_TCMPB))
			{
				//TSPERR(TRUE, (TEXT("Pen up check can't be started\r\n")));//lqm masked.11.10.01
				goto FILTER_ERROR;
			}

			// Timer value when the pen status check will be ended
			dwPenDownCheckEndTCNTO = dwPenDownCheckStartTCNTO - dwPenDownCheckTimeoutCounter;

			while(1)
			{
				if(TSP_GetTSPState() == PEN_UP)
				{
					// Pen Up
					TSPMSG(TCH_DBG, (TEXT("Pen up, This sample will be ignored. Check time = %d ns\r\n"), (dwPenDownCheckStartTCNTO - v_pPWMregs->TCNTO3) * g_dwTSPTimerClockPeriodNs));
					goto FILTER_ERROR;
				}
				else
				{
					if(dwPenDownCheckEndTCNTO >= TSP_TCNTO)
					{
						// Timeout, pen is down
						break;
					}
					Sleep(0);
				}
			}
		}


		// A latest sample is saved.
		// A oldest sample is discarded.
		x[0] = x[1];
		x[1] = x[2];
		x[2] = *px;

		y[0] = y[1];
		y[1] = y[2];
		y[2] = *py;


		TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
		return TRUE;
	}

FILTER_ERROR:

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
	return FALSE;
}


// Name : TSP_CreateADCMutex(void)
//
// Descriptions : Create Mutex for ADC resource.
//
// Return Value : GetLastError()
//
#define TSP_MUTEX_NAME    (TEXT("ADC_MUTEX"))
#define TSP_MUTEX_TIMEOUT (5000) // 5000 ms

static HANDLE ghTSP_Mutex = NULL;

DWORD
TSP_CreateADCMutex(void)
{
    DWORD dwStatus = ERROR_SUCCESS;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    ghTSP_Mutex = CreateMutex(NULL, FALSE, TSP_MUTEX_NAME);
    if (ghTSP_Mutex == NULL)
    {
        TSPERR(TRUE, (TEXT("TCH#CREATEMUTEX\r\n")));
        dwStatus = GetLastError();
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
    }
    else // if (ghTSP_Mutex != NULL)
    {
        if ( ERROR_ALREADY_EXISTS == GetLastError() )
        {
            TSPMSG(TCH_DBG, (TEXT("CreateMutex() opened existing mutex.\r\n")));
            dwStatus = ERROR_ALREADY_EXISTS;
        }
        else
        {
            TSPMSG(TCH_DBG, (TEXT("CreateMutex() created new mutex.\r\n")));
            dwStatus = ERROR_SUCCESS;
        }
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
    }

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
	
    return dwStatus;
}


// Name : TSP_ADCLock(void)
//
// Descriptions : Obtain the Mutex.
//
//            Return Value    : GetLastError()
//
DWORD
TSP_ADCLock(void)
{
    DWORD dwStatus = ERROR_SUCCESS;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    DEBUGCHK(ghTSP_Mutex != NULL);
    
    do    // while(dwStatus !=ERROR_SUCCESS);
    {
        dwStatus = WaitForSingleObject(ghTSP_Mutex, TSP_MUTEX_TIMEOUT);
        if(dwStatus == WAIT_OBJECT_0)
        {
            dwStatus = ERROR_SUCCESS;
        }
        else
        {
            dwStatus = GetLastError();
            DEBUGCHK(dwStatus != ERROR_SUCCESS);
            TSPMSG(TCH_DBG, (TEXT("WaitForSingleObject() failed for %d : %d\r\n"), TSP_MUTEX_TIMEOUT, GetLastError()));
        }

        TSPMSG(TCH_DBG, (TEXT("TSP_Receive Mutex\r\n")));
    } while ( dwStatus != ERROR_SUCCESS );

    DEBUGCHK(dwStatus == ERROR_SUCCESS);


	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return dwStatus;
}


// Name : TSP_ADCUnlock(void)
//
// Descriptions : Release the Mutex.
//
// Return Value : GetLastError()
//
DWORD
TSP_ADCUnlock(void)
{
    DWORD dwStatus = ERROR_SUCCESS;
    BOOL fOk;

	TSPMSG(TCH_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    DEBUGCHK(ghTSP_Mutex != NULL);

    fOk = ReleaseMutex(ghTSP_Mutex);
    if( !fOk )
    {
        dwStatus = GetLastError();
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
        TSPMSG(TCH_DBG, (TEXT("ReleaseMutex() failed %d\r\n"), GetLastError()));
    }

    DEBUGCHK(dwStatus == ERROR_SUCCESS);

	TSPMSG(TCH_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return dwStatus;
}


