//------------------------------------------------------------------------------
//
//  Copyright (C) 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:  adc.c
//
//  Init IPU for ADC operations
//
//------------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable: 4115 4201 4204 4214)
#include <windows.h>
#include <Winbase.h>
#include <ceddk.h>
#pragma warning(pop)

#include "bsp.h"
#include "ipu.h"
#include "adc.h"


//------------------------------------------------------------------------------
// External Functions

extern void BSPDisplayIOMUXEnable(IPU_DRIVE_TYPE);
extern void BSPDisplayIOMUXDisable(IPU_DRIVE_TYPE);
extern void BSPResetLCD(IPU_DRIVE_TYPE);



//------------------------------------------------------------------------------
// External Variables


//------------------------------------------------------------------------------
// Defines
#define ADC_DMA_0_CHANNEL             IPU_DMA_CHA_DMAADC_0_LSH    //  From Pre-processing
#define ADC_DMA_1_CHANNEL             IPU_DMA_CHA_DMAADC_1_LSH    //  From Post-processing
#define ADC_DMA_2_CHANNEL             IPU_DMA_CHA_DMAADC_2_LSH    //  System Channel 1
#define ADC_DMA_3_CHANNEL             IPU_DMA_CHA_DMAADC_3_LSH    //  System Channel 2

#define FLOW_ARM                    0
#define FLOW_ROT_VF                 1
#define FLOW_ROT_PP                 2
#define FLOW_VF                     3
#define FLOW_PP                     4
#define FLOW_SNOOP                  5
#define FLOW_AUTO_REF               6
#define FLOW_AUTO_REF_SNOOP         7

#define ADC_ERROR                   DEBUGZONE(0) // use GPE_ZONE_ERROR
#define ADC_INFO                    DEBUGZONE(3) // use GPE_ZONE_TEMP

#define DELAYTIMEOUT                20000

#define TVOUT_FS453                 1

#define ADC_DEBUG_MSGS              0


//------------------------------------------------------------------------------
// Types


//------------------------------------------------------------------------------
// Global Variables
PCSP_IPU_REGS g_pIPU;
static HANDLE g_hIPUBase = NULL;
UINT32 g_IPUClk;

PANEL_INFO g_ADCPanelArray[numPanelTypes] =
{
    // Toshiba Smart Panel Definitions
    {
        (PUCHAR) "Toshiba QVGA Panel",          // Name
#if 0
        IPU_PANEL_TOSHIBA,         // type
#else
        IPU_PANEL_USER0,           // type
#endif
        IPU_PIX_FMT_RGB666,          // Pixel Format
        DISPLAY_MODE_DEVICE,         // Mode ID
        240,                         // width
        320,                         // height
        60,                          // frequency (refresh rate)
        0,                           // Vertical Sync width
        0,                           // Vertical Start Width
        0,                          // Vertical End Width
        0,                           // Horizontal Sync Width
        0,                           // Horizontal Start Width
        0,                          // Horizontal End Width
        90,                           // Read Cycle Period
        10,                         // Read Up Position
        60,                         // Read Down Position
        123,                           // Write Cycle Period
        9,                         // Write Up Position
        40,                         // Write Down Position
        2630000,                // Pixel Clock Cyle Frequency
        96,                         // Pixel Data Offset Position
        {       // ADC Display Interface signal polarities
            IPU_ADC_DISPLAY_0,                                                      // Display Number
            IPU_DI_DISP_IF_CONF_IF_MODE_SYSTEM80_TYPE2,         // Interface mode
            IPU_DI_DISP_IF_CONF_PAR_BURST_MODE_BURST_CS,    // Parallel interface burst mode
            IPU_DI_DISP_SIG_POL_DATA_POL_STRAIGHT,                 // Data Pol
            IPU_DI_DISP_SIG_POL_CS_POL_ACTIVE_LOW,                // Clock Select Pol
            IPU_DI_DISP_SIG_POL_PAR_RS_POL_STRAIGHT,             // Parallel RS Pol
            IPU_DI_DISP_SIG_POL_WR_POL_ACTIVE_LOW,                // Write Pol
            IPU_DI_DISP_SIG_POL_RD_POL_ACTIVE_LOW,                // Read Pol
            IPU_DI_DISP_SIG_POL_VSYNC_POL_ACTIVE_LOW,          // VSync Pol
            IPU_DI_DISP_SIG_POL_SD_D_POL_STRAIGHT,                // Serial Data Pol
            IPU_DI_DISP_SIG_POL_SD_CLK_POL_STRAIGHT,             // Serial Interface Clock Pol
            IPU_DI_DISP_SIG_POL_SER_RS_POL_STRAIGHT,             // Serial Interface Address bit Pol
            IPU_DI_DISP_SIG_POL_BCLK_POL_STRAIGHT,                 // Burst clock Pol
        },
        {       // SDC Display Interface signal polarities
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        }
    },

    //Epson Smart Panel definitions
    {
        (PUCHAR) "Epson Panel",                // Name
#if 0
        IPU_PANEL_EPSON,              // type
#else
        IPU_PANEL_USER0,           // type
#endif
        IPU_PIX_FMT_RGB666,           // Pixel Format
        DISPLAY_MODE_DEVICE,          // Mode ID
        240,                          // width
        320,                          // height
        60,                           // frequency (refresh rate)
//        0,                            // Vertical Sync width
        2,                            // Vertical Sync width
        0,                            // Vertical Start Width 34
        0,                            // Vertical End Width
        0,                            // Horizontal Sync Width
        0,                            // Horizontal Start Width 144
        0,                            // Horizontal End Width
        600,                          // Read Cycle Period
        100,                          // Read Up Position
        500,                          // Read Down Position
        100,                          // Write Cycle Period
        10,                           // Write Up Position
        45,                           // Write Down Position
        8000000,                      // Pixel Clock Cyle Period
        200,                          // Pixel Data Offset Position
        {       // ADC Display Interface signal polarities
            IPU_ADC_DISPLAY_0,                                   // Display Number
            IPU_DI_DISP_IF_CONF_IF_MODE_SYSTEM80_TYPE2,          // Interface mode
            IPU_DI_DISP_IF_CONF_PAR_BURST_MODE_BURST_CS,         // Parallel interface burst mode
            IPU_DI_DISP_SIG_POL_DATA_POL_STRAIGHT,               // Data Pol
            IPU_DI_DISP_SIG_POL_CS_POL_ACTIVE_LOW,               // Clock Select Pol
            IPU_DI_DISP_SIG_POL_PAR_RS_POL_STRAIGHT,             // Parallel RS Pol
            IPU_DI_DISP_SIG_POL_WR_POL_ACTIVE_LOW,               // Write Pol
            IPU_DI_DISP_SIG_POL_RD_POL_ACTIVE_LOW,               // Read Pol
            IPU_DI_DISP_SIG_POL_VSYNC_POL_ACTIVE_LOW,            // VSync Pol
            IPU_DI_DISP_SIG_POL_SD_D_POL_STRAIGHT,               // Serial Data Pol
            IPU_DI_DISP_SIG_POL_SD_CLK_POL_STRAIGHT,             // Serial Interface Clock Pol
            IPU_DI_DISP_SIG_POL_SER_RS_POL_STRAIGHT,             // Serial Interface Address bit Pol
            IPU_DI_DISP_SIG_POL_BCLK_POL_STRAIGHT,               // Burst clock Pol
        },
        {       // SDC Display Interface signal polarities
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        }
    },

    // New Panel definitions go here
    // Put other Panel info here in future expasion
};

//------------------------------------------------------------------------------
// Local Variables
static HANDLE ipu_mutex;
static BOOL   need_map = TRUE;
static HANDLE g_hADCIntrEvent = NULL;
static HANDLE g_hADCIntrThread = NULL;
static const UINT32 m_RefreshRate = 60;
static PANEL_INFO *g_pCurrentPanel;
static UINT32 g_ADCFGBuf_Saved = 0;
static int g_iXValMax = 0;
static int g_iCurrentFGXP = 0;


//------------------------------------------------------------------------------
// Local Functions
static void _init_dma(int width, int height, int bpp, int stride, BOOL vFlip, const UINT channel);
static void SetSrcBuffer(int channel, PHYSICAL_ADDRESS *pAddr, DISP_BUF_TYPE bufNum);
//static void InitializePanel(UINT32 width, UINT32 height);
//static void ADCWriteCommand(UINT32 dispNum, BOOL b_cmd_data, UINT32 cmd, UINT32* data, UINT32 numParams);
static void CreateTemplate(TEMPLATE_CMD_REG *pCmd, unsigned int width, unsigned int height);
static void ProgramTemplate(TEMPLATE_CMD_REG * pCmd, unsigned int Display, BOOL Write);

static BOOL isRectEqual(RECT *rect1, RECT *rect2);
static void ReadADCDMA(PCSP_IPU_REGS pIPU);
static void ReadTemplateMemory(PCSP_IPU_REGS pIPU);
static void WriteReadTemplate(TEMPLATE_CMD_REG * pCmd, unsigned int Display);

static void DUMP_DI_REGS(PCSP_IPU_REGS pIPU);


//------------------------------------------------------------------------------
//
// Function: InitializeADC
//
// This function initializes ADC related bits in IPU common registers, the
// IPU ADC registers and IDMAC registers for ADC operation.
//
// Parameters:
//      width
//          [in] width of the display
//      height
//          [in] height of the display
//      bpp
//          [in] bits per pixel of the display
//
// Returns:
//      TRUE if successful.
//------------------------------------------------------------------------------
UINT32 InitializeADC(PANEL_INFO *currentPanel, int bpp)
{
    UINT32 ret = FALSE, temp = 0;
    PHYSICAL_ADDRESS phyAddr;
    ADC_IPU_DI_SIGNAL_CFG m_SignalPol; // variable to contain Polling information
    UINT32 tempval;
    TEMPLATE_CMD_REG Cmd[TEMPLATE_BUF_SIZE];
    m_SignalPol = currentPanel -> ADC_SIG_POL;

    if (need_map == TRUE)
    {
        phyAddr.QuadPart = CSP_BASE_REG_PA_IPU;

        // Map peripheral physical address to virtual address
        g_pIPU = (PCSP_IPU_REGS)MmMapIoSpace(phyAddr, sizeof(CSP_IPU_REGS), FALSE);

        // Check if virtual mapping failed
        if (g_pIPU == NULL)
        {
            DEBUGMSG(ADC_ERROR,
                     (TEXT("%s(): MmMapIoSpace failed!\r\n"), __WFUNCTION__));
            return ret;
        }

        // Create event for IPU interrupt for ADC BG EOF
        g_hADCIntrEvent = CreateEvent(NULL, FALSE, FALSE, IPU_ADC_INTR_EVENT);
        if (g_hADCIntrEvent == NULL)
        {
            DEBUGMSG(ADC_ERROR,
                (TEXT("%s: CreateEvent for IPU Interrupt failed\r\n"), __WFUNCTION__));
            return ret;
        }

        // One-time creation of g_hIPUBase handle
        if (g_hIPUBase == NULL)
        {
            // open handle to the IPU_BASE driver in order to enable IC module
            g_hIPUBase = CreateFile(TEXT("IPU1:"),      // "special" file name
                GENERIC_READ|GENERIC_WRITE,             // desired access
                FILE_SHARE_READ|FILE_SHARE_WRITE,       // sharing mode
                NULL,                                   // security attributes (=NULL)
                OPEN_EXISTING,                          // creation disposition
                FILE_FLAG_RANDOM_ACCESS,                // flags and attributes
                NULL);                                  // template file (ignored)
            if (g_hIPUBase == INVALID_HANDLE_VALUE)
            {
                DEBUGMSG(ADC_ERROR,
                    (TEXT("%s: Opening IPU_BASE handle failed!\r\n"), __WFUNCTION__));
                return ret;
            }
        }

        need_map = FALSE;
    }

    //----- General configuration

    // Set little endian
    INSREG32BF(&g_pIPU->IPU_CONF,
                IPU_IPU_CONF_PXL_ENDIAN, IPU_LITTLE_ENDIAN);

    // Set no double-buffer, use buf0
    INSREG32BF(&g_pIPU->IPU_CHA_DB_MODE_SEL, IPU_DMA_CHA_DMAADC_2, IPU_SIG_BUF);
    
    // Set buffer 0 as current buffer (non double buffered)
    INSREG32BF(&g_pIPU->IPU_CHA_CUR_BUF, IPU_DMA_CHA_DMAADC_2, 0);

    switch (m_SignalPol.DISP_NUM)
    {
        case IPU_ADC_DISPLAY_0:

            //******************************
            // Data Mapping Setup
            //******************************

            //... DI_DISP0_B0_MAP
            OUTREG32(&g_pIPU->DI_DISP0_DB0_MAP,
                        // data offset
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_OFFS0, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_OFFS1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_OFFS2, 0x7)|

                        // data mapping
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M0, 3)| // mask 2 bits, since we have 6 bits per color component
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M1, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M2, 2)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M3, 2)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M4, 2)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M5, 2)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M6, 2)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB0_MAP_MD00_M7, 2));
    
            //... DI_DISP0_B1_MAP
            OUTREG32(&g_pIPU->DI_DISP0_DB1_MAP,
                        // data offset
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_OFFS0, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_OFFS1, 0x7)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_OFFS2, 0)|
    
                        // data mapping
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M0, 3)| // mask 2 bits, since we have 6 bits per color component
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M1, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M2, 1)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M3, 1)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M4, 1)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M5, 1)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M6, 1)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB1_MAP_MD01_M7, 1));

            //... DI_DISP0_B2_MAP
            OUTREG32(&g_pIPU->DI_DISP0_DB2_MAP,
                        // data offset
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_OFFS0, 0x7)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_OFFS1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_OFFS2, 0)|

                        // data mapping
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M0, 3)| // mask 2 bits, since we have 6 bits per color component
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M1, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M2, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M3, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M4, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M5, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M6, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_DB2_MAP_MD02_M7, 0));

            INSREG32BF(&g_pIPU->DI_DISP_ACC_CC,
                       IPU_DI_DISP_ACC_CC_DISP0_IF_CLK_CNT_D,
                       IPU_DI_DISP_ACC_CC_CLOCK_3_CYCLE);

            //******************************
            // Command Mapping Setup
            //******************************

            //... DI_DISP0_CB0_MAP

            // Enable byte during first clock cycle
            OUTREG32(&g_pIPU->DI_DISP0_CB0_MAP,
                        // command offset
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_OFFS0, 0x7)| // bit 7 is MSB, for first clock cycle
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_OFFS1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_OFFS2, 0)|

                        // command mapping
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M0, 0)| // 0 = enable bit during first clock cycle
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M2, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M3, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M4, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M5, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M6, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB0_MAP_MC00_M7, 0));


            //... DI_DISP0_CB1_MAP
            OUTREG32(&g_pIPU->DI_DISP0_CB1_MAP,
                        // command offset
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_OFFS0, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_OFFS1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_OFFS2, 0)|

                        // command mapping
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M0, 3)| // mask all bits...ignore second byte.
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M1, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M2, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M3, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M4, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M5, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M6, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB1_MAP_MC01_M7, 3));

            //... DI_DISP0_CB2_MAP
            OUTREG32(&g_pIPU->DI_DISP0_CB2_MAP,
                        // command offset
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_OFFS0, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_OFFS1, 0)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_OFFS2, 0)|
    
                        // command mapping
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M0, 3)| // mask all bits...ignore third byte. 
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M1, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M2, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M3, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M4, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M5, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M6, 3)|
                        CSP_BITFVAL( IPU_DI_DISP0_CB2_MAP_MC02_M7, 3));

            // 1 clk cycle per command
            INSREG32BF(&g_pIPU->DI_DISP_ACC_CC,
                        IPU_DI_DISP_ACC_CC_DISP0_IF_CLK_CNT_C,
                        IPU_DI_DISP_ACC_CC_CLOCK_1_CYCLE);

            temp = INREG32(&g_pIPU->DI_DISP_IF_CONF) & 0xFFFFFFE0;
            //... DI_DISP_IF_CONF
            OUTREG32(&g_pIPU->DI_DISP_IF_CONF,
                        // Disp0 enabled
                        CSP_BITFVAL(IPU_DI_DISP_IF_CONF_DISP0_EN, IPU_DI_DISP_IF_CONF_EN_ENABLE) |
                        // System 80 parallel mode (sampling on RW)
                        CSP_BITFVAL(IPU_DI_DISP_IF_CONF_DISP0_IF_MODE, m_SignalPol.DISP_IF_MODE) |
                        // Burst access mode with sampling by CS or R/W or ENABLE signals
                        CSP_BITFVAL(IPU_DI_DISP_IF_CONF_DISP0_PAR_BURST_MODE, m_SignalPol.DISP_PAR_BURST_MODE) |
                        // Old value
                        temp);

            temp = INREG32(&g_pIPU->DI_DISP_SIG_POL) & 0xDFFFFFC0;
            //... DI_DISP_SIG_POL
            OUTREG32(&g_pIPU->DI_DISP_SIG_POL,
                      //..0: active low data polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_DATA_POL, m_SignalPol.DATA_POL)|
                      //..chip select polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_CS_POL, m_SignalPol.CS_POL)|
                      //..0: active low address bit polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_PAR_RS_POL, m_SignalPol.PAR_RS_POL)|
                      // write signal polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_WR_POL, m_SignalPol.WR_POL) |
                      // read signal polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_RD_POL, m_SignalPol.RD_POL) |
                      //..0: active low vertical sync polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_VSYNC_POL, m_SignalPol.VSYNC_POL)|
                      //..0: active low vertical sync polarity
                      CSP_BITFVAL( IPU_DI_DISP_SIG_POL_D0_BCLK_POL, m_SignalPol.BCLK_POL)|
                      // old value
                      temp);


            // Retrieve HSP clock frequency value
            DDKClockGetFreq(DDK_CLOCK_SIGNAL_IPU, &g_IPUClk);

            // These fields are expected in units of cycles of the HSP clock
            // So, we divide our desired period by (1/g_IPUClk).
            // The result is the desired period multiplied by g_IPUClk.
            // We divide by 1000000000 to convert from nanoseconds to seconds.
            // For the UP and DOWN positions, the units are 1/2 cycles of the HSP clock,
            // which is why the value is multiplied by 2.

            
            // Clock period for write access.
            // Shift by 4 because bits 3:0 are the fractional part
            tempval = (currentPanel->WR_CYCLE_PER * (g_IPUClk / 1000L) << 4) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_1,
                       IPU_DISP0_TIME_CONF_1_DISP0_IF_CLK_PER_WR,
                       tempval);

            // Clock rising edge position for write access.
            // Shift by 2 because bits 1:0 are the fractional part
            tempval = (currentPanel->WR_UP_POS * 2 * (g_IPUClk / 1000L) << 2) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_1,
                       IPU_DISP0_TIME_CONF_1_DISP0_IF_CLK_UP_WR,
                       tempval);

            // Clock falling edge position for write access.
            // Shift by 2 because bits 1:0 are the fractional part
            tempval = (currentPanel->WR_DOWN_POS * 2 * (g_IPUClk / 1000L) << 2) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_1,
                       IPU_DISP0_TIME_CONF_1_DISP0_IF_CLK_DOWN_WR,
                       tempval);

    
            // Clock period for read access.
            tempval = (currentPanel->RD_CYCLE_PER * (g_IPUClk / 1000L) << 4) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_2,
                       IPU_DISP0_TIME_CONF_2_DISP0_IF_CLK_PER_RD,
                       tempval);

            // Clock rising edge position for read access.
            tempval = (currentPanel->RD_UP_POS * 2 * (g_IPUClk / 1000L) << 2) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_2,
                       IPU_DISP0_TIME_CONF_2_DISP0_IF_CLK_UP_RD,
                       tempval);

            // Clock falling edge position for read access.
            tempval = (currentPanel->RD_DOWN_POS * 2 * (g_IPUClk / 1000L) << 2) / 1000000L;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_2,
                       IPU_DISP0_TIME_CONF_2_DISP0_IF_CLK_DOWN_RD,
                       tempval);


            // Pix clock period
            // Different calculation for the pixel clock, since it is expressed as a
            // frequency rather than a period.
            tempval = g_IPUClk / currentPanel->PIX_CLK_FREQ;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_3,
                       IPU_DISP0_TIME_CONF_3_DISP0_PIX_CLK_PER,
                       tempval << 4);

            // Read point position
            tempval = (currentPanel->PIX_DATA_POS * 2 * (g_IPUClk / 1000L) << 2) / 1000000;
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_3,
                       IPU_DISP0_TIME_CONF_3_DISP0_READ_EN,
                       tempval);

            // 0 wait states to read from display
            INSREG32BF(&g_pIPU->DI_DISP0_TIME_CONF_3,
                       IPU_DISP0_TIME_CONF_3_DISP0_RD_WAIT_ST,
                       0x00);

            // TODO: Compute stride line to be a power of 2

            OUTREG32(&g_pIPU->ADC_DISP0_CONF,
                        // Data mapping rule
                        CSP_BITFVAL(IPU_ADC_DISP_CONF_DISP_DATA_MAP, IPU_ADC_DISP_CONF_MCU_DATA_MAP_DATA) |
                        // Data for mcu is 16 bits
                        CSP_BITFVAL(IPU_ADC_DISP_CONF_DISP_DATA_WIDTH, IPU_ADC_DISP_CONF_MCU_DATA_WIDTH_16BITS) |
                        // XY addressing
                        CSP_BITFVAL(IPU_ADC_DISP_CONF_DISP_TYPE, IPU_ADC_DISP_CONF_TYPE_XY_ADDR) |
                        // stride line length in pixels minus 1 (must be power of 2 for XY addressing mode) 
                        // = 240 => round up to 256 to meet power of 2 requirement 
                        CSP_BITFVAL(IPU_ADC_DISP_CONF_DISP_SL, 0xFF));
    
            OUTREG32(&g_pIPU->ADC_DISP0_SS,
                        // Width -1
                        CSP_BITFVAL(IPU_ADC_DISP_SS_SCREEN_WIDTH, currentPanel->WIDTH - 1) |
                        // Height -1
                        CSP_BITFVAL(IPU_ADC_DISP_SS_SCREEN_HEIGHT, currentPanel->HEIGHT - 1));
    
            OUTREG32(&g_pIPU->ADC_DISP_VSYNC,
                        // Resolution in line
                        CSP_BITFVAL(IPU_ADC_DISP_VSYNC_DISP0_VSYNC_WIDTH_L, IPU_ADC_DISP_VSYNC_WIDTH_L_LINES) |
                        // Two lines on VSYNC pulse
                        CSP_BITFVAL(IPU_ADC_DISP_VSYNC_DISP0_VSYNC_WIDTH, currentPanel->VSYNCWIDTH - 1) |
                        // Vertical Synchronization disabled
                        CSP_BITFVAL(IPU_ADC_DISP_VSYNC_DISP0_VSYNC_MODE, IPU_ADC_DISP_VSYNC_MODE_EXTERNAL_VSYNC));
            break;

        // TODO: Add initialization for case of DISP1 and DISP2
        case IPU_ADC_DISPLAY_1:
            break;
        case IPU_ADC_DISPLAY_2:
            break;
    }
    
    // No prescaler applied to HSP_CLOCK since HSP_CLOCK will not
    // be changed on the fly for this test case.
    INSREG32BF(&g_pIPU->DI_HSP_CLK_PER,
               IPU_DI_HSP_CLK_PER_HSP_CLK_PERIOD_1,
               0x10);
    INSREG32BF(&g_pIPU->DI_HSP_CLK_PER,
               IPU_DI_HSP_CLK_PER_HSP_CLK_PERIOD_2,
               0x10);

    // Set to zero
    INSREG32BF(&g_pIPU->ADC_SYSCHA1_SA, IPU_ADC_CHA_CHAN_SA, 0);

    OUTREG32(&g_pIPU->ADC_CONF, 
                // Data mapping rule
                CSP_BITFVAL(IPU_ADC_CONF_SYS1_DATA_MAP, IPU_ADC_CONF_DATA_MAP_DATA) |
                // Address increment
                CSP_BITFVAL(IPU_ADC_CONF_SYS1_ADDR_INC, IPU_ADC_CONF_ADDR_INC_1) |
                // Display number
                CSP_BITFVAL(IPU_ADC_CONF_SYS1_DISP_NUM, IPU_ADC_CONF_DISP_NUM_DISP0) |
                // Control sequence generation mode
                CSP_BITFVAL(IPU_ADC_CONF_SYS1_MODE, IPU_ADC_CONF_MODE_WRITE_TEMPLATE_UNCONDITIONAL));



    //----- IDMAC Configuration

    // Set no double-buffer, use buf0
    INSREG32BF(&g_pIPU->IPU_CHA_DB_MODE_SEL, IPU_DMA_CHA_DMAADC_2, IPU_SIG_BUF);

    // Set buffer 0 as current buffer (non double buffered)
    INSREG32BF(&g_pIPU->IPU_CHA_CUR_BUF, IPU_DMA_CHA_DMAADC_2, IPU_SIG_BUF);

    // Set high priority for DMA ADC Channel 3
    INSREG32BF(&g_pIPU->IDMAC_CHA_PRI,
                IPU_DMA_CHA_DMAADC_2, 1);

    // Set max for consecutive bursts for each channel
    INSREG32BF(&g_pIPU->IDMAC_CONF,
                IPU_IDMAC_CONF_SRCNT, 7);

    // Select source of ADC channel 2 (SYS1) as ARM
    OUTREG32(&g_pIPU->IPU_FS_DISP_FLOW,
                CSP_BITFVAL( IPU_IPU_FS_DISP_FLOW_ADC2_SRC_SEL, FLOW_ARM));
    
    // DEBUG only
    DUMP_DI_REGS(g_pIPU);

    _init_dma(currentPanel->WIDTH, currentPanel->HEIGHT, bpp, (currentPanel->WIDTH * bpp / 8), FALSE, ADC_DMA_2_CHANNEL);

    // DEBUG only
    ReadADCDMA(g_pIPU);

    g_pCurrentPanel = currentPanel;

    // Initialize template command buffer
	memset(Cmd, 0, sizeof(UINT32) * TEMPLATE_BUF_SIZE);

    // Setup ADC template parameters
    CreateTemplate(Cmd, currentPanel->WIDTH, currentPanel->HEIGHT);
    
    ProgramTemplate(Cmd, IPU_ADC_DISPLAY_0, TRUE);
    
    // DEBUG only
    ReadTemplateMemory(g_pIPU);

    ret = TRUE;
    return ret;
}


//------------------------------------------------------------------------------
//
// Function: EnableADC
//
// This function enables ADC for normal opertion
//
// Parameters:
//      none
//
// Returns:
//      none.
//------------------------------------------------------------------------------
void EnableADC(void)
{
    UINT32 iOldVal, iNewVal, iMask, iBitval;
    DWORD dwBytesTransferred;
    IPU_DRIVER driver = IPU_DRIVER_ADC;
             
    BSPDisplayIOMUXEnable(eIPU_ADC);

    // Enable DI and ADC

    // Call to IPU Base to turn on ADC_EN in IPU_CONF reg.
    // This will also turn on IPU clocks if no other IPU
    // modules have already turned them on.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_ENABLE_ADC,           // I/O control code
             NULL,                           // in buffer
             0,                              // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to enable ADC!\r\n"), __WFUNCTION__));
    }

    // Call to IPU Base to turn on DI_EN in IPU_CONF reg.
    // This will also turn on IPU clocks if no other IPU
    // modules have already turned them on.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_ENABLE_DI,            // I/O control code
             &driver,                        // in buffer
             sizeof(IPU_DRIVER),             // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to enable DI!\r\n"), __WFUNCTION__));
    }


    // Enable DMA ADC Channel 2

    // Compute bitmask and shifted bit value for IPU Conf register
    iMask = CSP_BITFMASK(IPU_DMA_CHA_DMAADC_2);
    iBitval = CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_ENABLE);

    // Use interlocked function to Enable DMA ADC Channel 3.
    do
    {
        iOldVal = INREG32(&g_pIPU->IDMAC_CHA_EN);
        iNewVal = (iOldVal & (~iMask)) | iBitval;
    } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IDMAC_CHA_EN,
                iOldVal, iNewVal) != iOldVal);
}


//------------------------------------------------------------------------------
//
// Function: DisableADC
//
// This function disables ADC
//
// Parameters:
//      none
//
// Returns:
//      none.
//------------------------------------------------------------------------------
void DisableADC(void)
{
    UINT32 uTempReg1, uTempReg2, uCount = 0;
    UINT32 oldVal, newVal, iMask, iBitval;
    DWORD dwBytesTransferred;
    IPU_DRIVER driver = IPU_DRIVER_ADC;

    // Can we do this, or perhaps not because a system
    // level call is required (WaitForSingleObject)?
    // ADCDisplayWaitForNotBusy();

    // initalize ... for the first time through
    uTempReg1 = INREG32(&g_pIPU->IDMAC_CHA_BUSY);
    uTempReg2 = INREG32(&g_pIPU->IPU_CHA_BUF0_RDY);

    // We can't disable tasks until the active channel
    // has completed its current frames.  Make sure
    // that buffers aren't set as ready (indicating that
    // they are yet to start) and that channels are
    // not busy (indicating that channels are still running).
    while ((uTempReg1 & (1 << ADC_DMA_2_CHANNEL)) || (uTempReg2 & (1 << ADC_DMA_2_CHANNEL)))
    {
        if (uCount <= DELAYTIMEOUT)
        {
            uCount++;

            //.. need to check after the sleep delay
            uTempReg1 = INREG32(&g_pIPU->IDMAC_CHA_BUSY);
            uTempReg2 = INREG32(&g_pIPU->IPU_CHA_BUF0_RDY);
        }
        else
        {
            //.. there is something wrong ....break out
            return;
        }
    }

    // Compute bitmask and shifted bit value for idmac register
    iMask = CSP_BITFMASK(IPU_DMA_CHA_DMAADC_2);
    iBitval = CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_DISABLE);

    // Use interlocked function to Disable DMA ADC Channel 0.
    do
    {
        oldVal = INREG32(&g_pIPU->IDMAC_CHA_EN);
        newVal = (oldVal & (~iMask)) | iBitval;
    } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IDMAC_CHA_EN,
                oldVal, newVal) != oldVal);

    // Call to IPU Base to turn off ADC_EN in IPU_CONF reg.
    // This will also shut off IPU clocks if no other IPU
    // modules are enabled.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_DISABLE_ADC,          // I/O control code
             NULL,                           // in buffer
             0,                              // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to disable ADC!\r\n"), __WFUNCTION__));
    }

    // Call to IPU Base to turn off DI_EN in IPU_CONF reg.
    // This will also shut off IPU clocks if no other IPU
    // modules are enabled.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_DISABLE_DI,           // I/O control code
             &driver,                        // in buffer
             sizeof(IPU_DRIVER),             // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to disable DI!\r\n"), __WFUNCTION__));
    }

    BSPDisplayIOMUXDisable(eIPU_ADC);
}


void ADCWriteCommand(UINT32 dispNum, BOOL b_cmd_data, UINT32 cmd, UINT32* data, UINT32 numParams)
{
    UINT32 i;

    // Write low-level access configuration register
    OUTREG32(&g_pIPU->DI_DISP_LLA_CONF,
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_BE_MODE, 0) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_MAP_DC, 1) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_LOCK, 0) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_DISP_NUM, dispNum) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_RS, b_cmd_data ? 0 : 1));

    // Write low-level access data (send command first)
    OUTREG32(&g_pIPU->DI_DISP_LLA_DATA, cmd);

    // Write low-level access configuration register
    OUTREG32(&g_pIPU->DI_DISP_LLA_CONF,
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_BE_MODE, 0) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_MAP_DC, 1) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_LOCK, 0) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_DISP_NUM, dispNum) |
        CSP_BITFVAL(IPU_DI_DISP_LLA_CONF_DRCT_RS, 1));

    for (i = 0; i < numParams; i++)
    {
        // Write low-level access data (send command first)
        OUTREG32(&g_pIPU->DI_DISP_LLA_DATA, data[i]);
    }
}


//------------------------------------------------------------------------------
//
// Function: _create_template
//
// This function creates a template
//
// Parameters:
//      none
//
// Returns:
//      none.
//------------------------------------------------------------------------------
void CreateTemplate(TEMPLATE_CMD_REG *pCmd,
                      unsigned int width,
                      unsigned int height)
{
    unsigned int i = 0;

    pCmd[i].reg.Data = CMD_CASET;               // X coordinate command
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 0;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = 0;                       // X Start (MSB = 0)
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;    // X Start (LSB = X ADDR)
    pCmd[i].reg.Data = 0x01;                    // Send address bits 7:0
    pCmd[i].reg.Opcode = WR_XADDR;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = (width - 1) >> 8;        // X End (MSB = width >> 8)
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = width - 1;               // X End (LSB = width)
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;

    pCmd[i].reg.Data = CMD_RASET;               // Y coordinate command
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 0;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = 0x09;                    // Y Start (MSB = Y ADDR >> 8)
    pCmd[i].reg.Opcode = WR_YADDR;              // Send address bits 22:16
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;    // Y Start (LSB = Y ADDR)
    pCmd[i].reg.Data = 0x01;                    // Send address bits 22:8
    pCmd[i].reg.Opcode = WR_YADDR;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = (height - 1) >> 8;       // Y End (MSB = height >> 8)
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = height - 1;              // Y End (LSB = height)
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;

    pCmd[i].reg.Data = CMD_RAMWR;               // RAM Write
    pCmd[i].reg.Opcode = WR_CMND;
    pCmd[i].reg.RS = 0;
    pCmd[i++].reg.FlowControl = SINGLE_STEP;
    pCmd[i].reg.Data = 0x0;                     // Write data
    pCmd[i].reg.Opcode = WR_DATA;
    pCmd[i].reg.RS = 1;
    pCmd[i++].reg.FlowControl = STOP;
}

//Program the template memory
void ProgramTemplate(TEMPLATE_CMD_REG * pCmd, unsigned int Display, BOOL Write)
{
    unsigned int row_nu;
    int i;
    UINT32 oldVal, newVal;
    
    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    // MEM_NU = 0x0001 (CPM)
    // ROW_NU = 2*N ( N is channel number)
    // WORD_NU = 0
    if (Write)
    {
        row_nu = Display * 2 * ATM_ADDR_RANGE;
    }
    else
    {
        row_nu = (Display * 2 + 1) * ATM_ADDR_RANGE;
    }

    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_TM) |
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, row_nu)|
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0);
    
    // Software-controlled access to IMA registers
    // IMA registers may only be accessed if IMA_ADDR is
    // set to 0.

    for (;;)
    {
        oldVal = INREG32(&g_pIPU->IPU_IMA_ADDR);
        if (oldVal == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_IMA_ADDR,
                oldVal, newVal) == oldVal)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(1);
    }

    for (i = 0; i < TEMPLATE_BUF_SIZE; i++)
    {
        OUTREG32(&g_pIPU->IPU_IMA_DATA, pCmd[i].data);
    }

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&g_pIPU->IPU_IMA_ADDR, 0);
}

//------------------------------------------------------------------------------
//
// Function: ADCSetSrcBuffer
//
// This function sets the source buffer for the main ADC display window.
//
// Parameters:
//      pAddr
//          [in] Address of buffer to set as source for main ADC
//              display window.
//
// Returns:
//      none.
//------------------------------------------------------------------------------
void ADCSetSrcBuffer(PHYSICAL_ADDRESS *pAddr)
{
    SetSrcBuffer(ADC_DMA_2_CHANNEL, pAddr, eBUF_0);

    // Set DMA ADC Channel 2 as ready
//    SETREG32(&g_pIPU->IPU_CHA_BUF0_RDY,
//                CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_DMA_CHA_READY));
}

//------------------------------------------------------------------------------
//
// Function: ADCConfigDMA
//
// Configure the ADC DMA channel to update some or all of the contents of
// the display.  Also, update the ADC Sys Channel 1 Start Address register
// according with the requested rectangle for update.
//
// Parameters:
//      None.
//
// Returns:
//      None.
//------------------------------------------------------------------------------
void ADCConfigDMA(PRECT rectangle, int bpp, int stride)
{
    int width, height;
    static RECT lastUpdateRect = {0, 0, 0, 0};
    static int lastBpp = 0;
    static int lastStride = 0;

    // Compare current and old ADC config data to determine if
    // we need to re-configure ADC DMA channel
    if (isRectEqual(rectangle, &lastUpdateRect) && (lastBpp == bpp) && (lastStride == stride))
    {
        return;
    }
    else
    {
        lastUpdateRect.left = rectangle->left;
        lastUpdateRect.top = rectangle->top;
        lastUpdateRect.right = rectangle->right;
        lastUpdateRect.bottom = rectangle->bottom;
        lastBpp = bpp;
        lastStride = stride;
    }

    width = rectangle->right - rectangle->left + 1;
    height = rectangle->bottom - rectangle->top + 1;

    _init_dma(width, height, bpp, stride, FALSE, ADC_DMA_2_CHANNEL);
}

void ADCSetOffset(ADC_SOURCE src, LONG xPos, LONG yPos)
{
    UINT32 startAddrVal;
    UINT32 startTime;
    
    // Update Start Address register
    // Bits 22:8 are Y position, Bits 7:0 are X position
    startAddrVal = (yPos << 8) | xPos;

    startTime = 1;

    switch (src)
    {
        case ADC_SOURCE_SYS1:
            INSREG32BF(&g_pIPU->ADC_SYSCHA1_SA, IPU_ADC_CHA_START_TIME, startTime);
            INSREG32BF(&g_pIPU->ADC_SYSCHA1_SA, IPU_ADC_CHA_CHAN_SA, startAddrVal);
            break;
        case ADC_SOURCE_SYS2:
            OUTREG32(&g_pIPU->ADC_SYSCHA2_SA, startAddrVal);
            break;
        case ADC_SOURCE_PP:
            OUTREG32(&g_pIPU->ADC_PPCHAN_SA, startAddrVal);
            break;
    }
            

}


void ADCWaitForVSync()
{
    UINT32 oldCtrl, newCtrl, ctrlMask, ctrlBitval;

    // We have received our interrupt, so we can clear the
    // status bit (write-1-to-clear).
    OUTREG32(&g_pIPU->IPU_INT_STAT_3, 0x20000);

    // Background plane is busy, so we must enable and
    // wait for an EOF interrupt

    // Compute bitmask and shifted bit value for ctrl register
    ctrlMask = CSP_BITFMASK(IPU_IPU_INT_CTRL_3_ADC_DISP0_VSYNC);
    ctrlBitval = CSP_BITFVAL(IPU_IPU_INT_CTRL_3_ADC_DISP0_VSYNC, IPU_ENABLE);

    // Use interlocked function to update control registers
    do
    {
        oldCtrl = INREG32(&g_pIPU->IPU_INT_CTRL_3);
        newCtrl = (oldCtrl & (~ctrlMask)) | ctrlBitval;
    } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_INT_CTRL_3,
                oldCtrl, newCtrl) != oldCtrl);

    if (WaitForSingleObject(g_hADCIntrEvent, 1000) == WAIT_TIMEOUT)
    {
        DEBUGMSG(1,
                 (TEXT("%s(): Waiting for ADC VSYNC interrupt time out!\r\n"), __WFUNCTION__));
    }
}


void ADCStartDMA()
{
//    ReadADCDMA(g_pIPU);

//    DUMP_DI_REGS(g_pIPU);

    // Set DMA ADC Channel 2 as ready
    SETREG32(&g_pIPU->IPU_CHA_BUF0_RDY,
                CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_DMA_CHA_READY));
}

void ADCEnableModules()
{
    DWORD dwBytesTransferred;
    IPU_DRIVER driver = IPU_DRIVER_ADC;

    // modules have already turned them on.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_ENABLE_ADC,           // I/O control code
             NULL,                           // in buffer
             0,                              // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to enable ADC!\r\n"), __WFUNCTION__));
    }

    // Call to IPU Base to turn on DI_EN in IPU_CONF reg.
    // This will also turn on IPU clocks if no other IPU
    // modules have already turned them on.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_ENABLE_DI,            // I/O control code
             &driver,                        // in buffer
             sizeof(IPU_DRIVER),             // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to enable DI!\r\n"), __WFUNCTION__));
    }
}


void ADCDisableModules()
{
    DWORD dwBytesTransferred;
    IPU_DRIVER driver = IPU_DRIVER_ADC;
    
    // Call to IPU Base to turn off ADC_EN in IPU_CONF reg.
    // This will also shut off IPU clocks if no other IPU
    // modules are enabled.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_DISABLE_ADC,          // I/O control code
             NULL,                           // in buffer
             0,                              // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to disable ADC!\r\n"), __WFUNCTION__));
    }
    
    // Call to IPU Base to turn off DI_EN in IPU_CONF reg.
    // This will also shut off IPU clocks if no other IPU
    // modules are enabled.
    if (!DeviceIoControl(g_hIPUBase,         // file handle to the driver
             IPU_IOCTL_DISABLE_DI,           // I/O control code
             &driver,                        // in buffer
             sizeof(IPU_DRIVER),             // in buffer size
             NULL,                           // out buffer
             0,                              // out buffer size
             &dwBytesTransferred,            // number of bytes returned
             NULL))                          // ignored (=NULL)
    {
        DEBUGMSG (ADC_ERROR,
            (TEXT("%s: Failed to disable DI!\r\n"), __WFUNCTION__));
    }
}

void ADCEnableDMA()
{
    UINT32 iOldVal, iNewVal, iMask, iBitval;

    // Enable DMA ADC Channel 2
    
    // Compute bitmask and shifted bit value for IPU Conf register
    iMask = CSP_BITFMASK(IPU_DMA_CHA_DMAADC_2);
    iBitval = CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_ENABLE);
    
    // Use interlocked function to Enable DMA ADC Channel 3.
    do
    {
        iOldVal = INREG32(&g_pIPU->IDMAC_CHA_EN);
        iNewVal = (iOldVal & (~iMask)) | iBitval;
    } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IDMAC_CHA_EN,
                iOldVal, iNewVal) != iOldVal);
}

void ADCWriteConf(ADC_SOURCE src)
{
    switch (src)
    {
        case ADC_SOURCE_SYS1:
            OUTREG32(&g_pIPU->ADC_CONF, 
                        // Data mapping rule
                        CSP_BITFVAL(IPU_ADC_CONF_SYS1_DATA_MAP, IPU_ADC_CONF_DATA_MAP_DATA) |
                        // Address increment
                        CSP_BITFVAL(IPU_ADC_CONF_SYS1_ADDR_INC, IPU_ADC_CONF_ADDR_INC_1) |
                        // Display number
                        CSP_BITFVAL(IPU_ADC_CONF_SYS1_DISP_NUM, IPU_ADC_CONF_DISP_NUM_DISP0) |
                        // Control sequence generation mode
                        CSP_BITFVAL(IPU_ADC_CONF_SYS1_MODE, IPU_ADC_CONF_MODE_WRITE_TEMPLATE_NON_SEQUENTIAL));
            break;
        case ADC_SOURCE_PP:
            OUTREG32(&g_pIPU->ADC_CONF, 
                        // Data mapping rule
                        CSP_BITFVAL(IPU_ADC_CONF_PP_CHAN_EN, IPU_ADC_CONF_CHAN_EN_ENABLE) |
                        // Data mapping rule
                        CSP_BITFVAL(IPU_ADC_CONF_PP_DATA_MAP, IPU_ADC_CONF_DATA_MAP_DATA) |
                        // Address increment
                        CSP_BITFVAL(IPU_ADC_CONF_PP_ADDR_INC, IPU_ADC_CONF_ADDR_INC_1) |
                        // Display number
                        CSP_BITFVAL(IPU_ADC_CONF_PP_DISP_NUM, IPU_ADC_CONF_DISP_NUM_DISP0));
            break;
    }
}

void ADCClearConf()
{
    OUTREG32(&g_pIPU->ADC_CONF, 0);
}

void ADCDisableDMA()
{
    UINT32 iOldVal, iNewVal, iMask, iBitval;
    UINT32 uTempReg, uTempReg1, uTempReg2, uCount = 0;

    // initalize ... for the first time through
    uTempReg = INREG32(&g_pIPU->IDMAC_CHA_BUSY);
    uTempReg1 = INREG32(&g_pIPU->IPU_CHA_BUF0_RDY);
    uTempReg2 = INREG32(&g_pIPU->IPU_CHA_BUF1_RDY);

    // We can't disable tasks until the active channels
    // have completed their current frames.  Make sure
    // that buffers aren't set as ready (indicating that
    // they are yet to start) and that channels are
    // not busy (indicating that channels are still running).
    while ((uTempReg1 & (1 << IPU_DMA_CHA_DMAADC_2_LSH)) || (uTempReg2 & (1 << IPU_DMA_CHA_DMAADC_2_LSH))
        || (uTempReg & (1 << IPU_DMA_CHA_DMAADC_2_LSH)))
    {
        if (uCount <= DELAYTIMEOUT)
        {
            //..give up the remainder of time slice
            Sleep(1);
            uCount++;

            //.. need to check after the sleep delay
            uTempReg = INREG32(&g_pIPU->IDMAC_CHA_BUSY);
            uTempReg1 = INREG32(&g_pIPU->IPU_CHA_BUF0_RDY);
            uTempReg2 = INREG32(&g_pIPU->IPU_CHA_BUF1_RDY);
        }
        else
        {
            //.. there is something wrong ....break out
            break;
        }
    }

    // Compute bitmask and shifted bit value for idmac register
    iMask = CSP_BITFMASK(IPU_DMA_CHA_DMAADC_2);
    iBitval = CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_DISABLE);
    
    // Use interlocked function to Disable DMA ADC Channel 2.
    do
    {
        iOldVal = INREG32(&g_pIPU->IDMAC_CHA_EN);
        iNewVal = (iOldVal & (~iMask)) | iBitval;
    } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IDMAC_CHA_EN,
                iOldVal, iNewVal) != iOldVal);
}



//------------------------------------------------------------------------------
//
// Function: _init_dma
//
// Init the IPU DMA module
//
// Parameters:
//      None.
//
// Returns:
//      None.
//------------------------------------------------------------------------------
static void _init_dma(int width, int height, int bpp, int stride, BOOL vFlip, const UINT channel)
{
    UINT32 bpp_code, npb_code, sat_code, bam_code, ofs[4], wid[4];
    UINT32 oldVal, newVal;

    //=================================
    // Configure First 132 bit word
    //=================================

    // Software-controlled access to IMA registers
    // IMA registers may only be accessed if IMA_ADDR is
    // set to 0.

    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_CPM) |
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, (2 * channel))|
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0);

    for (;;)
    {
        oldVal = INREG32(&g_pIPU->IPU_IMA_ADDR);
//        RETAILMSG(1, (TEXT("%s(): IMA_ADDR = %x.\r\n"), __WFUNCTION__, oldVal));
        if (oldVal == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_IMA_ADDR,
                oldVal, newVal) == oldVal)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(1);
    }

    //...0th 32 bit word
    // XV [9:0], YV [19:10], XB [31:20]
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_XV, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_YV, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_XB, 0));

    //...1st 32 bit word
    // YB [11:0], SCE [12], RESERVED [13], NSB [14], LNPB [20:15], SX [30:21],
    // SY~ [31]
    // - Set NSB
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_YB, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SCE, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_NSB, 1)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_LNPB, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SX, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_LOW_SY, 0));

    //...2nd 32 bit word
    // ~SY [8:0], NS [18:9], SM [28:10] SDX~ [31:29]
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_HIGH_SY, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_NS, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SM, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_LOW_SDX, 0));

    //...3rd 32 bit word
    // ~SDX [1:0], SDY [6:2], SDRX [7], SDRY [8], SCRQ [9], RESERVED [11:10]
    // - FW [23:12], FH~ [31:24]
    // - Set FW & FH
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_HIGH_SDX, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SDY, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SDRX, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SDRY, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SCRQ, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_FW, (width - 1))|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_LOW_FH, (height - 1)));

    //...4th 32 bit word
    // ~FH [3:0]
    // NOTE: this takes care of the upper four bits in the FH field
    OUTREG32(&g_pIPU->IPU_IMA_DATA, ((height - 1) >> 8));


    //=================================
    // Configure Second 132 bit word
    //=================================
    //..skip over EBA0 and EBA1
    //.. Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    OUTREG32(&g_pIPU->IPU_IMA_ADDR,
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_CPM) |
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, (2 * channel + 1))|
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 2));

    //...2nd 32 bit word

    // Default access type to 32-bit
    sat_code = 2;

    switch (bpp)
    {
        case 32:
            bpp_code = 0;
            npb_code = 8-1;
            /*
            ofs[0] = 8;
            ofs[1] = 16;
            ofs[2] = 24;
            ofs[3] = 0;
            */
            ofs[0] = 0;
            ofs[1] = 8;
            ofs[2] = 16;
            ofs[3] = 24;

            wid[0] = 8-1;
            wid[1] = 8-1;
            wid[2] = 8-1;
//            wid[3] = 8-1;
            wid[3] = 0;
            break;

        case 24:
            bpp_code = 1;
            npb_code = 10-1;
            sat_code = 0;
            ofs[0] = 0;
            ofs[1] = 8;
            ofs[2] = 16;
            ofs[3] = 0;
            wid[0] = 8-1;
            wid[1] = 8-1;
            wid[2] = 8-1;
            wid[3] = 0;
            break;

        case 16:
            bpp_code = 2;
            npb_code = 16-1;
            ofs[0] = 0;
            ofs[1] = 5;
            ofs[2] = 11;
            ofs[3] = 0;
            wid[0] = 5-1;
            wid[1] = 6-1;
            wid[2] = 5-1;
            wid[3] = 0;
            break;

        case 8:
            bpp_code = 3;
            npb_code = 32-1;
            ofs[0] = 0;
            ofs[1] = 0;
            ofs[2] = 0;
            ofs[3] = 0;
            wid[0] = 8-1;
            wid[1] = 0;
            wid[2] = 0;
            wid[3] = 0;
            break;

        case 4:
            bpp_code = 4;
            npb_code = 32-1;
            ofs[0] = 0;
            ofs[1] = 0;
            ofs[2] = 0;
            ofs[3] = 0;
            wid[0] = 4-1;
            wid[1] = 0;
            wid[2] = 0;
            wid[3] = 0;
            break;

        case 1:
            bpp_code = 5;
            npb_code = 64-1;
            ofs[0] = 0;
            ofs[1] = 0;
            ofs[2] = 0;
            ofs[3] = 0;
            wid[0] = 1-1;
            wid[1] = 0;
            wid[2] = 0;
            wid[3] = 0;
            break;

        default:
            bpp_code = 2;
            npb_code = 16-1;
            ofs[0] = 0;
            ofs[1] = 5;
            ofs[2] = 11;
            ofs[3] = 0;
            wid[0] = 5-1;
            wid[1] = 6-1;
            wid[2] = 5-1;
            wid[3] = 0;
        // TODO:  Should report error here.  bpp_code is reserved.
        break;
    }

    bam_code = vFlip ? 1 : 0;

    // - BPP [2:0], SL [16:3], PFS [19:17], BAM [24:20], NPB [30:25],
    // - RESERVED [31]
    // - Set BPP to 24bpp (1)
    // - Set SL (Scaling Factor) to bytes_pp * width
    // - Set PFS (Packing) to RGB (%100)
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_BPP, bpp_code)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SL, (stride - 1))|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_PFS, 0x4)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_BAM, bam_code)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_NPB, npb_code));

    //...3rd 32 bit word
    // SAT [1:0], SCC [2], OFS0 [7:3], 0FS1 [12:8], OFS2 [17:13], OFS3 [22:18]
    // - WID0 [25:23], WID1 [28:26], WID2 [31:29]
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SAT, sat_code)|
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_SCC, 0)|

                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_OFS0, ofs[0]) |
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_OFS1, ofs[1]) |
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_OFS2, ofs[2]) |
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_OFS3, ofs[3]) |

                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_WID0, wid[0]) |
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_WID1, wid[1]) |
                CSP_BITFVAL( IPU_IPU_IMA_DATA_PARAM_WID2, wid[2]));

    //...4th 32 bit word
    // WID3 [2:0], DEC_SEL [3],
    // Set WID3 (7 - 8 bit size), Color component 3 width (Alpha)
    OUTREG32(&g_pIPU->IPU_IMA_DATA,
                CSP_BITFVAL(IPU_IPU_IMA_DATA_PARAM_WID3, wid[3]));

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&g_pIPU->IPU_IMA_ADDR, 0);
}


//------------------------------------------------------------------------------
//
// Function: SetSrcBuffer
//
// Set the buffer for Init the IPU DMA module
//
// Parameters:
//      None.
//
// Returns:
//      None.
//------------------------------------------------------------------------------
static void SetSrcBuffer(int channel, PHYSICAL_ADDRESS *pAddr, DISP_BUF_TYPE bufNum)
{
    UINT32 wordNo = (bufNum == eBUF_0) ? 0 : 1;
    UINT32 oldVal, newVal;

    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL(IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_CPM) |
            CSP_BITFVAL(IPU_IPU_IMA_ADDR_ROW_NU, (2 * channel + 1))|
            CSP_BITFVAL(IPU_IPU_IMA_ADDR_WORD_NU, wordNo);

    for (;;)
    {
        oldVal = INREG32(&g_pIPU->IPU_IMA_ADDR);
        if (oldVal == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_IMA_ADDR,
                oldVal, newVal) == oldVal)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(1);
    }

    //..Set buffer to physical frame buffer address
    OUTREG32(&g_pIPU->IPU_IMA_DATA, (unsigned int) pAddr->QuadPart);

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&g_pIPU->IPU_IMA_ADDR, 0);
}


//------------------------------------------------------------------------------
//
// Function: ADCDisplayIsBusy
//
// This function queries whether or not the specified display
// plane is currently busy displaying data.
//
// Parameters:
//      dispPlane
//          [in] Specifies whether to query for the background
//          (DisplayPlane_0) or foreground plane (DisplayPlane_1).
//
// Returns:
//      TRUE if specified plane is busy; FALSE if not busy.
//------------------------------------------------------------------------------
BOOL ADCDisplayIsBusy()
{
    BOOL retval = FALSE;

    // Poll Background Channel DMA channel busy bit.  If bit is set,
    // background window is busy.
    if (EXTREG32BF(&g_pIPU->IDMAC_CHA_BUSY, IPU_DMA_CHA_DMAADC_2))
    {
        retval = TRUE;
    }

    return retval;
}

//------------------------------------------------------------------------------
//
// Function: ADCDisplayWaitForNotBusy
//
// This function waits for the specified display
// plane to complete processing the current frame.  If
// the plane is not busy, this function returns immediately.
//
// Parameters:
//      dispPlane
//          [in] Specifies whether to query for the background
//          (DisplayPlane_0) or foreground plane (DisplayPlane_1).
//
// Returns:
//      None.
//------------------------------------------------------------------------------
void ADCDisplayWaitForNotBusy()
{
    UINT32 oldCtrl, newCtrl, ctrlMask, ctrlBitval;
    UINT32 uTempReg, uTempReg1;
    
    // initalize ... for the first time through
    uTempReg = INREG32(&g_pIPU->IDMAC_CHA_BUSY);
    uTempReg1 = EXTREG32BF(&g_pIPU->IPU_TASKS_STAT, IPU_IPU_TASKS_STAT_ADCSYS1_TSTAT);

    // We can't disable tasks until the active channels
    // have completed their current frames.  Make sure
    // that buffers aren't set as ready (indicating that
    // they are yet to start) and that channels are
    // not busy (indicating that channels are still running).
    if ((uTempReg1 == IPU_IPU_TASKS_STAT_ACTIVE) || (uTempReg & 0x40000))
    {

/*
    // First Check if the plane is busy
    // If it is not, we return.
    if (ADCDisplayIsBusy())
    {
*/

        // Background plane is busy, so we must enable and
        // wait for an EOF interrupt

        // Compute bitmask and shifted bit value for ctrl register
        ctrlMask = CSP_BITFMASK(IPU_DMA_CHA_DMAADC_2);
        ctrlBitval = CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, IPU_ENABLE);

        // Use interlocked function to update control registers
        do
        {
            oldCtrl = INREG32(&g_pIPU->IPU_INT_CTRL_1);
            newCtrl = (oldCtrl & (~ctrlMask)) | ctrlBitval;
        } while ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_INT_CTRL_1,
                    oldCtrl, newCtrl) != oldCtrl);

        if (WaitForSingleObject(g_hADCIntrEvent, 1000) == WAIT_TIMEOUT)
        {
            DEBUGMSG(ADC_ERROR,
                     (TEXT("%s(): Waiting for ADC BG EOF interrupt time out!\r\n"), __WFUNCTION__));
        }

        // We have received our interrupt, so we can clear the
        // status bit (write-1-to-clear).
        OUTREG32(&g_pIPU->IPU_INT_STAT_1,
            CSP_BITFVAL(IPU_DMA_CHA_DMAADC_2, 1));
    }
    
//    DUMP_DI_REGS(g_pIPU);

}

// Helper function to compare two rectangles
BOOL isRectEqual(RECT *rect1, RECT *rect2)
{
    if ((rect1->top == rect2->top) &&
        (rect1->left == rect2->left) &&
        (rect1->bottom == rect2->bottom) &&
        (rect1->right == rect2->right))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

static void ReadADCDMA(PCSP_IPU_REGS pIPU)
{
    int i;
    UINT32 data;

    // Software-controlled access to IMA registers
    // IMA registers may only be accessed if IMA_ADDR is
    // set to 0.

    for (;;)
    {
        if (INREG32(&pIPU->IPU_IMA_ADDR) == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&pIPU->IPU_IMA_ADDR, 0, 1) == 0)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(0);
    }

    OUTREG32(&pIPU->IPU_IMA_ADDR,
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_CPM) |
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, 36)| // 2n = 36, where n = 18 (channel 18 for SYS1)
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0));

    for (i = 0; i < 132; i += 32)
    {
        data = INREG32(&pIPU->IPU_IMA_DATA);
        DEBUGMSG(ADC_INFO,
                 (TEXT("%s(): Word0, bits %d - %d: %x\r\n"), __WFUNCTION__, i, i+32, data));
    }

    OUTREG32(&pIPU->IPU_IMA_ADDR,
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_CPM) |
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, 37)|
            CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0));

    for (i = 0; i < 132; i += 32)
    {
        data = INREG32(&pIPU->IPU_IMA_DATA);
        DEBUGMSG(ADC_INFO,
                 (TEXT("%s(): Word1, bits %d - %d: %x\r\n"), __WFUNCTION__, i, i+32, data));
    }

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&pIPU->IPU_IMA_ADDR, 0);
}


static void ReadTemplateMemory(PCSP_IPU_REGS pIPU)
{
    int i;
    UINT32 data;
    UINT32 oldVal, newVal;

    // Software-controlled access to IMA registers
    // IMA registers may only be accessed if IMA_ADDR is
    // set to 0.

    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_TM) |
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, 0)|
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0);
    
    for (;;)
    {
        oldVal = INREG32(&g_pIPU->IPU_IMA_ADDR);
        if (oldVal == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_IMA_ADDR,
                oldVal, newVal) == oldVal)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(0);
    }

    for (i = 0; i < 32; i++)
    {
        data = INREG32(&pIPU->IPU_IMA_DATA);
        DEBUGMSG(ADC_INFO,
                 (TEXT("%s(): DWord%d: %x\r\n"), __WFUNCTION__, i, data));
    }

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&pIPU->IPU_IMA_ADDR, 0);
}


static void DUMP_DI_REGS(PCSP_IPU_REGS pIPU)
{
#ifndef DEBUG
    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(pIPU);
#else
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_CONF: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_CONF)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_CHA_BUF0_RDY: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_CHA_BUF0_RDY)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IDMAC_CHA_EN: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IDMAC_CHA_EN)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IDMAC_CHA_BUSY: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IDMAC_CHA_BUSY)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_TASKS_STAT: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_TASKS_STAT)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_INT_CTRL_1: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_INT_CTRL_1)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_INT_STAT_1: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_INT_STAT_1)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: IPU_FS_DISP_FLOW: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->IPU_FS_DISP_FLOW)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: ADC_CONF: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->ADC_CONF)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: ADC_DISP0_CONF: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->ADC_DISP0_CONF)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: ADC_DISP0_SS: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->ADC_DISP0_SS)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: ADC_SYSCHA1_SA: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->ADC_SYSCHA1_SA)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: ADC_DISP_VSYNC: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->ADC_DISP_VSYNC)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP_IF_CONF: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP_IF_CONF)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP_SIG_POL: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP_SIG_POL)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_TIME_CONF_1: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_TIME_CONF_1)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_TIME_CONF_2: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_TIME_CONF_2)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_TIME_CONF_3: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_TIME_CONF_3)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_DB0_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_DB0_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_DB1_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_DB1_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_DB2_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_DB2_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_CB0_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_CB0_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_CB1_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_CB1_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP0_CB2_MAP: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP0_CB2_MAP)));
    DEBUGMSG (ADC_INFO, (TEXT("%s: DI_DISP_LLA_CONF: %x\r\n"), __WFUNCTION__, INREG32(&pIPU->DI_DISP_LLA_CONF)));
#endif
}

static void WriteReadTemplate(TEMPLATE_CMD_REG * pCmd, unsigned int Display)
{
    unsigned int row_nu;
    UINT32 oldVal, newVal, data;
    
    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    // MEM_NU = 0x0001 (CPM)
    // ROW_NU = 2*N ( N is channel number)
    // WORD_NU = 0

    row_nu = Display * 2 * ATM_ADDR_RANGE;


    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_TM) |
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, row_nu)|
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0);
    
    // Software-controlled access to IMA registers
    // IMA registers may only be accessed if IMA_ADDR is
    // set to 0.

    for (;;)
    {
        oldVal = INREG32(&g_pIPU->IPU_IMA_ADDR);
        if (oldVal == 0)
        {
            // Try to set IPU_IMA registers.
            if ((UINT32) InterlockedTestExchange((LPLONG)&g_pIPU->IPU_IMA_ADDR,
                oldVal, newVal) == oldVal)
            {
                // Successfully set IMA_ADDR.
                break;
            }
        }
        // IPU_IMA controlled by another process.
        // Surrender CPU and then try again.
        Sleep(0);
    }

    OUTREG32(&g_pIPU->IPU_IMA_DATA, pCmd[0].data);
    
    DEBUGMSG(ADC_INFO, (TEXT("%s(): Just wrote %d to IMA_ADDR %d (dword 0)\r\n"), __WFUNCTION__, pCmd[0].data, newVal));

    // Now Read it back
    row_nu = Display * 2 * ATM_ADDR_RANGE;

    // Set IPU_IMA_ADDR (IPU Internal Memory Access Address)
    newVal = CSP_BITFVAL( IPU_IPU_IMA_ADDR_MEM_NU, IPU_IMA_ADDR_MEM_NU_TM) |
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_ROW_NU, row_nu)|
                CSP_BITFVAL( IPU_IPU_IMA_ADDR_WORD_NU, 0);
    
    OUTREG32(&g_pIPU->IPU_IMA_ADDR, newVal);

    data = INREG32(&g_pIPU->IPU_IMA_DATA);
    
    DEBUGMSG(ADC_INFO, (TEXT("%s(): Just read %d from IMA_ADDR %d (dword 0)\r\n"), __WFUNCTION__, data, newVal));

    // Cede control of IPU_IMA registers by writing 0 to IPU_IMA_ADDR
    OUTREG32(&g_pIPU->IPU_IMA_ADDR, 0);
}


