/*---------------------------------------------------------------------------*
  Project:  NitroSDK - demos - tp-2
  File:     calibrate.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: calibrate.c,v $
  Revision 1.7  2004/11/05 12:43:16  takano_makoto
  Added a notice relating to calibrate.c.

  Revision 1.6  2004/11/02 08:50:47  takano_makoto
  swap MAIN/SUB display.

  Revision 1.5  2004/09/29 06:10:21  takano_makoto
  Changed to use default value for chattering parameter. Deleted TP_RequestSetupStability.

  Revision 1.4  2004/06/02 06:23:13  takano_makoto
  Change parameter format in TP_RequestAutoSamplingStart().

  Revision 1.3  2004/05/28 09:36:31  takano_makoto
  Change max time of AutoSampling frequency by frame.

  Revision 1.2  2004/05/21 09:59:34  takano_makoto
  Modified comment.

  Revision 1.1  2004/05/21 09:57:21  takano_makoto
  Get calibration value from NVRAM.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#include    <nitro.h>
#include    <nitro/spi/ARM9/tp.h>
#include    "data.h"
#include    "calibrate.h"

//============================================================================
// calibrate.c is for operating touch panel samples on hardware
// such as the TEG board and the TS board that can not acquire owner information.
// Normally, it is not required when operating with hardware such as
// the DS system or IS-NITRO-EMULATOR in which the start menu has been updated.
//===========================================================================



#define     SAMPLING_FREQUENCE      4       // Touch panel sampling frequence per frame
#define     SAMPLING_BUFSIZE        (SAMPLING_FREQUENCE + 1)    // AutoSampling buffer size
#define     SAMPLING_START_VCOUNT   0       // base vcount value in auto sampling.


/*---------------------------------------------------------------------------*
    Static variables definition
 *---------------------------------------------------------------------------*/
static GXOamAttr    gOam[128];
static TPData  gTpBuf[ SAMPLING_BUFSIZE ];

/*---------------------------------------------------------------------------*
    Prototype definition
 *---------------------------------------------------------------------------*/
static void VBlankIntr(void);



/*---------------------------------------------------------------------------*
  Name:         GetSamplePointInAuto

  Description:  Get touched point by Auto Sampling.
                This function use TP_GetLatestRawPoint()

  Arguments:    None.

  Returns:      data - gotten TouchPanel data.
                BOOL - if touched and got point this function returns TRUE.
                       else  FALSE.
 *---------------------------------------------------------------------------*/
static BOOL GetSamplePointInAuto(TPData *data)
{
    TPData temp;

    TP_GetLatestRawPointInAuto( &temp );

    if (! temp.touch ) {
        return FALSE;
    }

    *data = temp;
    return TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         WaitPanelReleaseInAuto

  Description:  Wait to released TouchPanel, using Auto Sampling.
                This function is using TP_GetLatestRawPointInAuto().

  Arguments:    None.

  Returns:      BOOL  - if TouchPanel is released , this returns TRUE.
                else FALSE.

 *---------------------------------------------------------------------------*/
static BOOL WaitPanelReleaseInAuto( void )
{
    TPData temp;
    static u16 count = 0;

    enum {
        INTERVAL_CNT = 10
    };

    TP_GetLatestRawPointInAuto( &temp );

    if ( temp.touch ) {
        count = 0;
        return FALSE;
    }

    count++;
    if ( count >= INTERVAL_CNT ) {
        count = 0;
        return TRUE;
    } else {
        return FALSE;
    }
}


/*---------------------------------------------------------------------------*
  Name:         SetPoint8x8

  Description:  Display a 8x8 OBJ on indicated point.

  Arguments:    x - position X.
                y - position Y.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static inline void SetPoint8x8(u16 pos_x, u16 pos_y)
{
    G2_SetOBJAttr( &gOam[0],                 // OAM number
                   pos_x - 4,                // X position
                   pos_y - 4,                // Y position
                   0,                        // Priority
                   GX_OAM_MODE_BITMAPOBJ,    // Bitmap mode
                   FALSE,                    // mosaic off
                   GX_OAM_EFFECT_NONE,       // affine off
                   GX_OAM_SHAPE_8x8,         // 8x8 size
                   GX_OAM_COLOR_16,          // 16 color
                   0,                        // character
                   15,                       // alpha
                   0);
}


/*---------------------------------------------------------------------------*
  Name:         DisplayInit

  Description:  Graphics Initialization

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void Calibrate_DisplayInit()
{
    GX_Init();
    FX_Init();

    GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);

    GX_DispOff();
    GXS_DispOff();

    OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
    (void)OS_EnableIrqMask( OS_IE_V_BLANK );
    (void)GX_VBlankIntr(TRUE);         // to generate VBlank interrupt request
    (void)OS_EnableIrq();

    GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
    MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);

    MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);       // clear OAM
    MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);         // clear the standard palette

    MI_CpuFillFast((void*)HW_DB_OAM, 192, HW_DB_OAM_SIZE);  // clear OAM
    MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);   // clear the standard palette
    MI_DmaFill32(3, (void *)HW_LCDC_VRAM_C, 0x7FFF7FFF, 256 * 192 * sizeof(u16));


    GX_SetBankForOBJ(GX_VRAM_OBJ_256_AB);                   // Set VRAM-A,B for OBJ

    GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS,                // 2D / 3D Mode
                       GX_BGMODE_0,                         // BGMODE 0
                       GX_BG0_AS_2D);                       // Set BG0 as 2D

    GX_SetVisiblePlane(GX_PLANEMASK_OBJ);                   // make OBJ visible
    GX_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_1D_128K);       // 2D mapping OBJ

    /* Load character bitmap data */
    GX_LoadOBJ( (const void *)IMAGE_DATA, 0, IMAGE_DATA_SIZE ); // Transfer OBJ bitmap data to VRAM

    SVC_WaitVBlankIntr();                                   // Waiting the end of VBlank interrupt

    GX_DispOn();

}


/*---------------------------------------------------------------------------*
  Name:         ExecGetCalibrate

  Description:  Get calibration parameter from 2 coordinates.

  Arguments:    None.

  Returns:      param - a pointer for store calibration parameter.
 *---------------------------------------------------------------------------*/
void ExecGetCalibrate( TPCalibrateParam *param )
{

    enum {
        SEQ_CALIBRATE_1, SEQ_INTERVAL_1,
        SEQ_CALIBRATE_2, SEQ_INTERVAL_2,
        SEQ_CHECK_PARAM, SEQ_INTERVAL_3,
        SEQ_INTERVAL_4, SEQ_GET_POINT
    };


    u32 seq = SEQ_CALIBRATE_1;  // Calibration Sequence state
    TPData sample1;
    TPData sample2;
    TPData sample3;
    TPData dispPos;
    TPCalibrateParam calibrate;

    Calibrate_DisplayInit();

    // unnecessary called.
    // be able to use default parameter (retry,range).
#if 0
    // Send parameter of revision noise.
    if ( TP_RequestSetStability( 3, 15 ) != 0 ) {
        OS_Panic("SetStability request err!\n");
    }
#endif

    if ( TP_RequestAutoSamplingStart( SAMPLING_START_VCOUNT, SAMPLING_FREQUENCE, gTpBuf, SAMPLING_BUFSIZE ) )
    {
        OS_Panic("auto sampling start reqeuest err!\n");
    }
    OS_Printf("Start auto sampling\n");

    while ( TRUE )
    {
        switch (seq) {
        case SEQ_CALIBRATE_1:
        // Get a first point.
            SetPoint8x8(32, 32);
            if ( GetSamplePointInAuto(&sample1) ) {
                OS_Printf("OK! ( %d, %d )\n", sample1.x, sample1.y);
                seq = SEQ_INTERVAL_1;
            }
            break;

        case SEQ_INTERVAL_1:
        // Wait release TouchPanel.
            if ( WaitPanelReleaseInAuto() ) {
                seq = SEQ_CALIBRATE_2;
            }
            break;

        case SEQ_CALIBRATE_2:
        // Get a second point.
            SetPoint8x8( GX_LCD_SIZE_X - 32, GX_LCD_SIZE_Y - 32 );
            if ( GetSamplePointInAuto(&sample2) ) {
                OS_Printf("OK! ( %d, %d )\n", sample2.x, sample2.y);

                // Calc and Set a Calibration Parameter from two point.
                if ( TP_CalcCalibrateParam( &calibrate,
                                        sample1.x, sample1.y,
                                        32, 32,
                                        sample2.x, sample2.y,
                                        GX_LCD_SIZE_X - 32, GX_LCD_SIZE_Y - 32 ) != 0 )
                {
                    // retry when the margin of error is too large
                    seq = SEQ_INTERVAL_4;
                    break;

                }
                TP_SetCalibrateParam( &calibrate );

                OS_Printf("Calibrate param: \n");
                OS_Printf("\tx0 = %d, xDotSize = %d\n", calibrate.x0, calibrate.xDotSize);
                OS_Printf("\ty0 = %d, yDotSize = %d\n", calibrate.y0, calibrate.yDotSize);
                OS_Printf("Check calibrate param\n");

                seq = SEQ_INTERVAL_2;
            }
            break;

        case SEQ_INTERVAL_2:
        // Wait release TouchPanel
            if ( WaitPanelReleaseInAuto() ) {
                seq = SEQ_CHECK_PARAM;
            }
            break;

        case SEQ_CHECK_PARAM:
        // Verify Calibration Parameter.
            SetPoint8x8( GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2 );
            if ( GetSamplePointInAuto(&sample3) ) {
                s32 xRange, yRange;

                OS_Printf("OK! ( %d, %d )\n", sample3.x, sample3.y);

                TP_GetUnCalibratedPoint( &dispPos.x, &dispPos.y, GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2 );

                xRange = sample3.x - dispPos.x;
                yRange = sample3.y - dispPos.y;

                // if margin is too big, detect points again.
                if ( xRange > 100 || xRange < -100 || yRange > 100 || yRange < -100) {
                    OS_Printf("( %d, %d ) NG Resampling\n", dispPos.x, dispPos.y);
                    seq = SEQ_INTERVAL_4;
                    break;
                }

                OS_Printf("Raw ( %d, %d )\n", dispPos.x, dispPos.y);
                OS_Printf("\txRange = %d, yRange = %d\n", xRange, yRange);

                seq = SEQ_INTERVAL_3;
                break;;
            }
            break;
        case SEQ_INTERVAL_3:
        // Wait Release Touch Panel and goto Draw Picture.
            if ( WaitPanelReleaseInAuto() ) {
                if ( TP_RequestAutoSamplingStop() ) {
                    OS_Panic("auto sampling stop reqeuest err!\n");
                }
                OS_Printf("Stop auto sampling\n");
                seq = SEQ_CALIBRATE_1;

                param->x0 = calibrate.x0;
                param->y0 = calibrate.y0;
                param->xDotSize = calibrate.xDotSize;
                param->yDotSize = calibrate.yDotSize;
                return;
            }
            break;
        case SEQ_INTERVAL_4:
        // Wait Release Touch Panel and goto Detect points again.
            if ( WaitPanelReleaseInAuto() ) {
                seq = SEQ_CALIBRATE_1;
            }
            break;

        }

        /* flush cache of OAM buffers to main memory */
        DC_FlushRange( gOam, sizeof(gOam) );

        GX_LoadOAM( gOam, 0, sizeof(gOam) );
        MI_DmaFill32( 3, gOam, 192, sizeof(gOam) );   // Clear OAM buffer

        // Wait V-Blank interrupt
        SVC_WaitVBlankIntr();
    }
}



/*---------------------------------------------------------------------------*
  Name:         VBlankIntr

  Description:  V-Blank function

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void VBlankIntr(void)
{

    // Set IRQ check flag
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}

