//------------------------------------------------------------------------------
//
//  Copyright (C) 2004, Motorola Inc. All Rights Reserved
//
//------------------------------------------------------------------------------
//
//  Copyright (C) 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//------------------------------------------------------------------------------
//
//  File:  bsptouch.c
//
//  Provides the implementation for BSP-specific touch support.
//
//------------------------------------------------------------------------------

#pragma warning(push)
#pragma warning(disable: 4115 4201)
#include <windows.h>
#include <tchddsi.h>
#pragma warning(pop)

#include "bsp.h"
#include "pmic_ioctl.h"
#include "pmic_lla.h"
#include "pmic_adc.h"

#include "regs.h"
#include "regs_adc.h"

//-----------------------------------------------------------------------------
// External Functions

//-----------------------------------------------------------------------------
// External Variables
extern const int MIN_CAL_COUNT = 20;

//-----------------------------------------------------------------------------
// Defines

// Maximum allowed variance in the X coordinate samples.
#define DELTA_X_COORD_VARIANCE          24

// Maximum allowed variance in the X coordinate samples.
#define DELTA_Y_COORD_VARIANCE          24


#define TOUCHCLKSRC_FREQ            BSP_CLK_CKIL_FREQ       // clock source is CKIL
#define TOUCHCLKSRC_VAL             EPIT_CR_CLKSRC_CKIL
#define TOUCHPRESCALAR_VAL          0x000               // divide by 1
#define TOUCH_EPIT_TICKS_PER_MSEC   ((TOUCHCLKSRC_FREQ/(TOUCHPRESCALAR_VAL+1))/1000)
#define TOUCH_EPIT_TICKS(dwMSec)    (((dwMSec) * TOUCHCLKSRC_FREQ) / ((TOUCHPRESCALAR_VAL + 1) * 1000))

#define ABS(x)  ((x) >= 0 ? (x) : (-(x)))

//-----------------------------------------------------------------------------
// Types


//-----------------------------------------------------------------------------
// Global Variables
extern UINT32 gTouchTimerIrq = IRQ_EPIT2;


//-----------------------------------------------------------------------------
// Local Variables
static PCSP_EPIT_REG g_pEPIT;

static HANDLE hPMI;
static TCHAR *gEventNamePri = TEXT("EVENT_TS_PRI");
static TCHAR *gEventNameAlt = TEXT("EVENT_TS_ALT");
// Named event for interrupt registration
static TCHAR *gTouchEventName;


//-----------------------------------------------------------------------------
// Local Functions

//-----------------------------------------------------------------------------
//
// Function: PmicTouchDeinit
//
// This function uninitializes the PMIC touch controller.
//
// Parameters:
//      None.
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
static void PmicTouchDeinit(void)
{

    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    // Deregister for PMIC pen down interrupts.
    if (PmicInterruptDeregister(PMIC_MC13783_INT_TSI) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (_T("PmicTouchDeinit: PmicInterruptDeregister ")
                        _T("failed\r\n")));
    }

    // Disable PMIC touch interrupt
    // Set to inactive mode
    if (PmicADCTouchStandby(FALSE) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (_T("PmicTouchDeinit: PmicTouchStandby failed\r\n")));
    }

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
}


//------------------------------------------------------------------------------
//
// Function: PmicTouchInit
//
// This function initializes the PMIC touch controller.
//
// Parameters:
//      None.
//
// Returns:
//      Status.
//
//------------------------------------------------------------------------------
static PMIC_STATUS PmicTouchInit(void)
{
    PMIC_STATUS rc = PMIC_ERROR;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    // Register for PMIC pen down interrupts.  Event name must match one given
    // to event created in MDD for IST signaling.
    if (PmicInterruptRegister(PMIC_MC13783_INT_TSI, gTouchEventName)
        != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (_T("PmicTouchInit: PmicInterruptRegister failed\r\n")));
        goto cleanUp;
    }

    // Put PMIC touch ADC into standby (required for pen down interrupt)
    if (PmicADCTouchStandby(TRUE) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (_T("PmicTouchInit: PmicTouchStandby failed\r\n")));
        goto cleanUp;
    }

    // Make sure pen down interrupt are unmasked
    if (PmicInterruptHandlingComplete(PMIC_MC13783_INT_TSI) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (_T("PmicTouchInit: PmicInterruptHandlingComplete ")
                        _T("failed\r\n")));
        goto cleanUp;
    }

    rc = PMIC_SUCCESS;

cleanUp:
    if (rc != PMIC_SUCCESS) PmicTouchDeinit();

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));

    return rc;
}

//-----------------------------------------------------------------------------
//
// Function: BSPTouchSetSampleRate
//
// This function configures the rate for acquiring touch samples by
// configuring the periodic interval of the EPIT2 timer.
//
// Parameters:
//      dwIntervalMsec
//          [in] Specifies the interval in msec to be used for generating
//          a periodic timer interrupt.  This interval sets the sample
//          rate of the touch driver.
//
// Returns:
//      None
//
//-----------------------------------------------------------------------------
void BSPTouchSetSampleRate(DWORD dwIntervalMsec)
{
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    // Enable EPIT2 clocks
    DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
        DDK_CLOCK_GATE_MODE_ENABLED_ALL);

    // Configure load register with the number of EPIT ticks required
    // to achieve the specified touch timer tick interval
    OUTREG32(&g_pEPIT->LR, TOUCH_EPIT_TICKS(dwIntervalMsec));

    // Configure the compare register to generate interrupt when
    // timer reloads from EPITLR
    // OUTREG32(&g_pEPIT->CMPR, dwIntervalMsec*TOUCH_EPIT_TICKS_PER_MSEC);

    // Disable EPIT2 clocks
    DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
        DDK_CLOCK_GATE_MODE_DISABLED);

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchTimerInit
//
// This function initializes the EPIT2 timer for use as the touch
// periodic timer.
//
// Parameters:
//      dwIntervalMsec
//          [in] Specifies the interval in msec to be used for generating
//          a periodic timer interrupt.  This interval sets the sample
//          rate of the touch driver.
//
// Returns:
//      TRUE if successful, otherwise FALSE.
//
//-----------------------------------------------------------------------------
static BOOL BSPTouchTimerInit(DWORD dwIntervalMsec)
{
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s(%d)\r\n"), __WFUNCTION__, dwIntervalMsec));

    // Enable EPIT2 clocks
    DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
        DDK_CLOCK_GATE_MODE_ENABLED_ALL);

    // Disable EPIT and clear all configuration bits
    OUTREG32(&g_pEPIT->CR, 0);

    // Assert software reset for the timer
    OUTREG32(&g_pEPIT->CR, CSP_BITFMASK(EPIT_CR_SWR));

    // Wait for the software reset to complete
    while (INREG32(&g_pEPIT->CR) & CSP_BITFMASK(EPIT_CR_SWR));

    // Clear timer compare interrupt flag (write-1-clear)
    OUTREG32(&g_pEPIT->SR, CSP_BITFMASK(EPIT_SR_OCIF));

    // Enable timer for "set-and-forget" mode where timer is
    // loaded with EPITLR when count down to zero is reached
    OUTREG32(&g_pEPIT->CR,
        CSP_BITFVAL(EPIT_CR_EN, EPIT_CR_EN_DISABLE) |
        CSP_BITFVAL(EPIT_CR_ENMOD, EPIT_CR_ENMOD_LOAD) |
        CSP_BITFVAL(EPIT_CR_OCIEN, EPIT_CR_OCIEN_ENABLE) |
        CSP_BITFVAL(EPIT_CR_RLD, EPIT_CR_RLD_RELOAD) |
        CSP_BITFVAL(EPIT_CR_PRESCALAR, TOUCHPRESCALAR_VAL) |
        CSP_BITFVAL(EPIT_CR_SWR, EPIT_CR_SWR_NORESET) |
        CSP_BITFVAL(EPIT_CR_IOVW, EPIT_CR_IOVW_OVR) |
        CSP_BITFVAL(EPIT_CR_DBGEN, EPIT_CR_DBGEN_ACTIVE) |
        CSP_BITFVAL(EPIT_CR_WAITEN, EPIT_CR_WAITEN_ENABLE) |
        CSP_BITFVAL(EPIT_CR_DOZEN, EPIT_CR_DOZEN_ENABLE) |
        CSP_BITFVAL(EPIT_CR_STOPEN, EPIT_CR_STOPEN_ENABLE) |
        CSP_BITFVAL(EPIT_CR_OM, EPIT_CR_OM_DICONNECT) |
        CSP_BITFVAL(EPIT_CR_CLKSRC, TOUCHCLKSRC_VAL));

    // Configure the sample rate
    BSPTouchSetSampleRate(dwIntervalMsec);

    // Disable EPIT2 clocks
    DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
        DDK_CLOCK_GATE_MODE_DISABLED);

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));

    return TRUE;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchTimerEnable
//
// This function activates the touch timer for the specified
// periodic timer interrupt.
//
// Parameters:
//      None.
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPTouchTimerEnable(void)
{
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    // Enable EPIT2 clocks
    DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
        DDK_CLOCK_GATE_MODE_ENABLED_ALL);

    // Clear timer compare interrupt flag (write-1-clear)
    OUTREG32(&g_pEPIT->SR, CSP_BITFMASK(EPIT_SR_OCIF));

    // Enable the timer
    INSREG32(&g_pEPIT->CR, CSP_BITFMASK(EPIT_CR_EN),
        CSP_BITFVAL(EPIT_CR_EN, EPIT_CR_EN_ENABLE));

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));

    return;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchTimerDisable
//
// DESCRIPTION:
//      This function disables the touch timer and clears any pending
//      timer interrupt.
//
// Parameters:
//      None.
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPTouchTimerDisable(void)
{
    DDK_CLOCK_GATE_MODE cgMode;
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    // If the touch timer is enabled
    if (DDKClockGetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2, &cgMode) &&
        (cgMode != DDK_CLOCK_GATE_MODE_DISABLED))
    {
        // Disable the timer
        CLRREG32(&g_pEPIT->CR, CSP_BITFMASK(EPIT_CR_EN));

        // Clear timer compare interrupt flag (write-1-clear)
        OUTREG32(&g_pEPIT->SR, CSP_BITFMASK(EPIT_SR_OCIF));

        DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));

        // Disable EPIT2 clocks
        DDKClockSetGatingMode(DDK_CLOCK_GATE_INDEX_EPIT2,
            DDK_CLOCK_GATE_MODE_DISABLED);
    }

    return;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchTimerIrqQuery
//
// DESCRIPTION:
//      This function queries the touch timer interrupt status.
//
// Parameters:
//      None.
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
BOOL BSPTouchTimerIrqQuery(void)
{
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

     // Return timer compare interrupt flag
    return INREG32(&g_pEPIT->SR);

#if 0 // Remove-W4: Warning C4702 workaround
    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
#endif
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchDealloc
//
// This function frees the resources allocated by the touch driver.
//
// Parameters:
//      None.
//
// RETURNS:
//      None.
//
//-----------------------------------------------------------------------------
static void BSPTouchDealloc(void)
{
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   // If touch timer was allocated
   if (g_pEPIT != NULL) {
      // Free virtual space allocated for peripheral registers
      MmUnmapIoSpace(g_pEPIT, sizeof(CSP_EPIT_REG));
      g_pEPIT = NULL;
   }
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
   return;
}


///-----------------------------------------------------------------------------
//
// Function: BSPTouchAlloc
//
// This function allocates the resources needed by the touch driver.
//
// Parameters:
//      None.
//
// Returns:
//      TRUE if successful, FALSE otherwise.
//
//-----------------------------------------------------------------------------
static BOOL BSPTouchAlloc(void)
{
   BOOL rc = FALSE;
   
   PHYSICAL_ADDRESS phyAddr;
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   phyAddr.QuadPart = CSP_BASE_REG_PA_EPIT2;
   
   // Map peripheral physical address to virtual address
   g_pEPIT = (PCSP_EPIT_REG) MmMapIoSpace(phyAddr, sizeof(CSP_EPIT_REG),
                                          FALSE);
   
   // Check if virtual mapping failed
   if (g_pEPIT == NULL) {
      DEBUGMSG(ZONE_ERROR,
               (_T("BSPTouchTimerAlloc: MmMapIoSpace failed!\r\n")));
      goto cleanUp;
   }
   
   rc = TRUE;
   
cleanUp:
   if (!rc) {
      BSPTouchDealloc();
   }
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
   
   return rc;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchDeinit
//
// Deinitializes the BSP-specific interfaces and data structures for
// touch support
//
// Parameters:
//      None.
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPTouchDeinit(void)
{
   HANDLE hEvent;
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   // Disable timer
   if (g_pEPIT != NULL) {
      BSPTouchTimerDisable();
   }
   
   // Deinitialize PMIC touch interface
   PmicTouchDeinit();
   
   // If interrupt has been registered with OAL
   if (gIntrTouch != SYSINTR_UNDEFINED) {
      if (gIntrTouch != SYSINTR_NOP) {
         InterruptDone( gIntrTouch );
         InterruptDisable( gIntrTouch );
         KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, &gIntrTouch,
                         sizeof(gIntrTouch), NULL, 0, NULL);
      }
      gIntrTouch = (DWORD)SYSINTR_UNDEFINED;
      
      // If the touch panel was hijacked from GWES during CETK testing,
      // restore the GWES bindings to the OAL
      if (gTouchEventName == gEventNameAlt) {
         gTouchEventName = gEventNamePri;
         
         // Reenable panel
         PmicTouchInit();
         
         // Obtain SYSINTR values from the OAL for the touch interrupts.
         // InterruptInitialize is called within MDD
         KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &gTouchTimerIrq,
             sizeof(gTouchTimerIrq), &gIntrTouch, sizeof(gIntrTouch), NULL);
         
         hEvent = CreateEvent(NULL, FALSE, FALSE, gTouchEventName);
         
         InterruptInitialize(gIntrTouch, hEvent, NULL, 0);
         
         CloseHandle(hEvent);
         
         hEvent = NULL;
         gIntrTouch = (DWORD)SYSINTR_UNDEFINED;
      }
   }
   
   // Deallocate touch data structures
   BSPTouchDealloc();
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchAttach
//
// The function performs BSP-specific attachment allocations and assignments.
//
// Parameters:
//      None.
//
// Returns:
//      Handle to an event that will be used by the touch MDD for interrupt
//      registration.
//
//-----------------------------------------------------------------------------
extern HANDLE BSPTouchAttach(void)
{
   gTouchEventName = gEventNamePri;
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   // Determine if touch is already to attached (i.e. to GWES) and
   // we are trying to hijack the interrupt signaling (i.e. for TUX)
   if (KernelIoControl(IOCTL_HAL_IRQ2SYSINTR, &gTouchTimerIrq,
         sizeof(gTouchTimerIrq), &gIntrTouch, sizeof(gIntrTouch), NULL)) {
      // If panel interrupt registered to another process
      if (gIntrTouch != SYSINTR_UNDEFINED) {
         InterruptDone( gIntrTouch );
         InterruptDisable( gIntrTouch );
         KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, &gIntrTouch,
                         sizeof(gIntrTouch), NULL, 0, NULL);
         gIntrTouch = (DWORD)SYSINTR_UNDEFINED;
         gTouchEventName = gEventNameAlt;
      }
   }
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
   
   return CreateEvent(NULL, FALSE, FALSE, gTouchEventName);
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchInit
//
// Initializes BSP-specific interfaces and data structures for touch support
//
// Parameters:
//      dwIntervalMsec
//          [in] Specifies the interval in msec to be used for generating
//          a periodic timer interrupt.  This interval sets the sample
//          rate of the touch driver.
//
// Returns:
//      TRUE  indicates success.
//      FALSE indicates failure.
//
//-----------------------------------------------------------------------------
BOOL BSPTouchInit(DWORD dwIntervalMsec)
{
   BOOL rc = FALSE;
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   // Allocate the touch driver data structures.
   if (!BSPTouchAlloc()) {
      ERRORMSG(TRUE, (_T("BSPTouchInit: BSPTouchAlloc failed\r\n")));
      goto cleanUp;
   }
   
   // Obtain SYSINTR values from the OAL for the touch interrupts.
   // InterruptInitialize is called within the MDD
   if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &gTouchTimerIrq,
       sizeof(gTouchTimerIrq), &gIntrTouch, sizeof(gIntrTouch), NULL)) {
      ERRORMSG(TRUE, (_T("BSPTouchInit: Failed to request the touch sysintr.\r\n")));
      gIntrTouch = (DWORD)SYSINTR_UNDEFINED;
      goto cleanUp;
   }
   
   // Initialize the touch controller on the PMIC.
   if (PmicTouchInit() != PMIC_SUCCESS) {
      ERRORMSG(TRUE, (_T("BSPTouchInit: PmicTouchInit failed\r\n")));
      goto cleanUp;
   }
   
   // Initialize the touch timer
   if (!BSPTouchTimerInit(dwIntervalMsec)) {
      ERRORMSG(TRUE, (_T("BSPTouchInit: BSPTouchTimerInit failed\r\n")));
      goto cleanUp;
   }
   
   rc = TRUE;
   
cleanUp:
   if (!rc) {
      BSPTouchDeinit();
   }
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
   
   return rc;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchGetSample
//
// This function is used to get touch screen samples from the ADS7843
// touch screen controller.
//
// Parameters:
//      x
//          [out] Points to x sample coordinate.
//
//      y
//          [out] Points to y sample coordinate.
//
// Returns:
//      Current touch panel status as TOUCH_PANEL_SAMPLE_FLAGS type.
//
//-----------------------------------------------------------------------------
TOUCH_PANEL_SAMPLE_FLAGS BSPTouchGetSample(INT *x, INT *y)
{
   UINT16 adc_x[3], adc_y[3];
   INT32  d0, d1, d2;
   UINT   i;
   
   static UINT16 hys_x[2];
   static UINT16 hys_y[2];
   static UINT32 hysIndex;
   static BOOL bPenDown = FALSE;
   
   TOUCH_PANEL_SAMPLE_FLAGS StateFlags = TouchSampleDownFlag;
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));
   
   // Request touch samples from PMIC
   if (PmicADCTouchRead(adc_x, adc_y) != PMIC_SUCCESS) {
      // Tell upper layers to ignore this attempt and
      // clear the TouchSampleDownFlag
      StateFlags = TouchSampleIgnore;
      goto cleanUp;
   }
   
   // Check for pen up condition
   for (i=0; i<3; i++) {
      // Check both X-Y coordinates for out-of-bounds values to indicate
      // a pen up condition.
      //
      // Note that testing on the MX31ADS in low power mode has shown that
      // we must also check for the equals to zero case in order to properly
      // detect a pen up condition. Previously, we only had to test for the
      // ADC_SAMPLE_MAX_VALUE case but this by itself does not work when
      // using the new MX31 low power modes.
      if ((adc_x[i] >= ADC_SAMPLE_MAX_VALUE) || (adc_x[i] == 0) ||
          (adc_y[i] >= ADC_SAMPLE_MAX_VALUE) || (adc_y[i] == 0)) {
         // Tell upper layers to ignore this attempt and
         // clear the TouchSampleDownFlag
         StateFlags = TouchSampleIgnore;
         goto cleanUp;
      }
   }
   
   // Calculate absolute differences between x-coordinate samples
   d0 = ABS(adc_x[0] - adc_x[1]);
   d1 = ABS(adc_x[1] - adc_x[2]);
   d2 = ABS(adc_x[0] - adc_x[2]);
   
   // Reject the samples if the spread is too large
   if ((d0 > DELTA_X_COORD_VARIANCE) || (d1 > DELTA_X_COORD_VARIANCE) ||
       (d2 > DELTA_X_COORD_VARIANCE)) {
      StateFlags |= TouchSampleIgnore;
      goto cleanUp;
   }
   
   // Pick the two samples closest together
   if (d0 < d1) {
      if (d0 < d2) {
         // Sample 0 & 1 closest together
         *x = adc_x[0] + adc_x[1];
      }
      else {
         // Sample 0 & 2 closest together
         *x = adc_x[0] + adc_x[2];
      }
   }
   else {
      if (d1 < d2) {
         // Sample 1 & 2 closest together
         *x = adc_x[1] + adc_x[2];
      }
      else {
         // Sample 0 & 2 closest together
         *x = adc_x[0] + adc_x[2];
      }
   }
   
   // Calculate absolute differences between y-coordinate samples
   d0 = ABS(adc_y[0] - adc_y[1]);
   d1 = ABS(adc_y[1] - adc_y[2]);
   d2 = ABS(adc_y[0] - adc_y[2]);
   
   if ((d0 > DELTA_Y_COORD_VARIANCE) || (d1 > DELTA_Y_COORD_VARIANCE) ||
       (d2 > DELTA_Y_COORD_VARIANCE)) {
      StateFlags |= TouchSampleIgnore;
      goto cleanUp;
   }
   
   // Pick the two samples closest together
   if (d0 < d1) {
      if (d0 < d2) {
         // Sample 0 & 1 closest together
         *y = adc_y[0] + adc_y[1];
      }
      else {
         // Sample 0 & 2 closest together
         *y = adc_y[0] + adc_y[2];
      }
   }
   else {
      if (d1 < d2) {
         // Sample 1 & 2 closest together
         *y = adc_y[1] + adc_y[2];
      }
      else {
         // Sample 0 & 2 closest together
         *y = adc_y[0] + adc_y[2];
      }
   }
   
   if (!bPenDown) {
      bPenDown = TRUE;
      
      // Prime the hysteresis buffers with average of two
      // best samples from ADC
      *x = *x >> 1;
      *y = *y >> 1;
      hys_x[0] = hys_x[1] = (UINT16)(*x);
      hys_y[0] = hys_y[1] = (UINT16)(*y);
   }
   else {
      // Implement noise rejection since transition to pen up
      // condition often gives us a spike in samples
      if (ABS(*x - (hys_x[0] + hys_x[1])) > (DELTA_X_COORD_VARIANCE*16)) {
         StateFlags |= TouchSampleIgnore;
         goto cleanUp;
      }
      
      if (ABS(*y - (hys_y[0] + hys_y[1])) > (DELTA_Y_COORD_VARIANCE*16)) {
         StateFlags |= TouchSampleIgnore;
         goto cleanUp;
      }
      
      // Average two best samples from ADC with samples
      // from hysteresis buffer
      *x = (hys_x[0] + hys_x[1] + *x) >> 2;
      *y = (hys_y[0] + hys_y[1] + *y) >> 2;
      
      // Replace an entry in hysteresis buffer
      hys_x[hysIndex & 0x1] = (UINT16)(*x);
      hys_y[hysIndex & 0x1] = (UINT16)(*y);
      
      hysIndex++;
   }
   
   // Tell upper layers that this attempt was valid
   StateFlags |= TouchSampleValidFlag;
   
cleanUp:
   
   // If we need to look for next pen down event
   if (!(StateFlags & TouchSampleDownFlag)) {
      // Place touch controller back in standby mode
      if (PmicADCTouchStandby(TRUE) != PMIC_SUCCESS) {
         ERRORMSG(1, (_T("PmicTouchStandby failed\r\n")));
      }
      
      // Make sure pen down interrupt is unmasked
      if (PmicInterruptHandlingComplete(PMIC_MC13783_INT_TSI) != PMIC_SUCCESS) {
         ERRORMSG(1, (_T("PmicInterruptHandlingComplete failed\r\n")));
      }
      
      bPenDown = FALSE;
   }
   
   DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s(0x%02X, [%d, %d])\r\n"), __WFUNCTION__, StateFlags, *x, *y));
   
   return StateFlags;
}


//-----------------------------------------------------------------------------
//
// Function: BSPTouchPowerHandler
//
// DESCRIPTION:
//      This function controls the touchpanel driver state when a power state
//      transition occurs.
//
// Parameters:
//      boff - TRUE  : power down
//           - FALSE : power up
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPTouchPowerHandler(BOOL boff)
{
    DEBUGMSG(ZONE_FUNCTION, (TEXT("+%s()\r\n"), __WFUNCTION__));

    UNREFERENCED_PARAMETER(boff);

    // We currently do nothing in the touchpanel driver during system power
    // state transitions since the touchpanel must always remain active. This
    // allows the system to possibly resume from the suspend state by just
    // tapping the touchpanel.

    DEBUGMSG(ZONE_FUNCTION, (TEXT("-%s()\r\n"), __WFUNCTION__));
}

//-----------------------------------------------------------------------------
//
// Function: BSPCheckIRQ
//
// DESCRIPTION:
//      This function will check the IRQ to see if it is a pen down state.
//
// Parameters:
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPCheckIRQ(void)
{
    // If timer IRQ is pending, we assume a pen down state
    if (BSPTouchTimerIrqQuery())
    {
        // Disable the timer (also clears pending timer interrupt)
        BSPTouchTimerDisable();

        // Signal touch IRQ processing complete
        InterruptDone(gIntrTouch);
    }
}

//-----------------------------------------------------------------------------
//
// Function: BSPTouchADCDelay
//
// DESCRIPTION:
//      This function is a dummy for MX31.
//
// Parameters:
//
// Returns:
//      None.
//
//-----------------------------------------------------------------------------
void BSPTouchADCDelay(void)
{
    return;
}

//-----------------------------------------------------------------------------
//
// Function: BSPTouchInterruptInitialize
//
// DESCRIPTION:
//      This function will do an interrupt intialize.
//
// Parameters:WORD idInt, HANDLE hEvent, 
//                     LPVOID pvData, DWORD cbData
//
// Returns:
//      BOOL.
//
//-----------------------------------------------------------------------------

BOOL BSPTouchInterruptInitialize(  DWORD idInt, HANDLE hEvent, 
                                                                   LPVOID pvData, DWORD cbData )
{
    UNREFERENCED_PARAMETER(pvData);
    UNREFERENCED_PARAMETER(cbData);

    if(!InterruptInitialize(idInt, hEvent, NULL, 0))
        return FALSE;
    else
        return TRUE;
}
