/*---------------------------------------------------------------------------*
  Project:  NitroSDK - SPI - libraries
  File:     mic.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: mic.c,v $
  Revision 1.11  2004/11/04 07:11:54  terui
  Revised comments.

  Revision 1.10  2004/11/02 04:31:13  terui
  Fixed comments

  Revision 1.9  2004/09/28 06:12:29  terui
  Added a 32-byte multiple check of the buffer size to the MIC_StartAutoSampling(Async) function.

  Revision 1.8  2004/09/17 09:36:03  terui
  Fixes due to changes in MIC_StartAutoSampling(Async) parameters.

  Revision 1.7  2004/07/13 09:20:07  terui
  Changes due to the addition of signed sampling types.

  Revision 1.6  2004/06/10 06:50:23  terui
  Clear of variables in shared area during initialization

  Revision 1.5  2004/06/03 04:09:29  terui
  Change to definition of shortest sampling rate

  Revision 1.4  2004/06/01 04:47:58  terui
  Support for data storage location being fixed to the shared area during mic
  stand-alone sampling

  Revision 1.3  2004/06/01 02:03:38  terui
  Fix accompanying change to MICAutoParam member name

  Revision 1.2  2004/06/01 00:57:11  terui
  Change so sampling rate is directly specified in clock units of ARM7

  Revision 1.1  2004/05/31 08:52:56  terui
  Initial upload.

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

#include    <nitro/spi.h>


/*---------------------------------------------------------------------------*
	Structure Definition
 *---------------------------------------------------------------------------*/
// Lock definition for asynchronized function exclusive processing
typedef enum MICLock
{
	MIC_LOCK_OFF = 0 ,		// Unlocked status
	MIC_LOCK_ON ,			// Locked status
	MIC_LOCK_MAX

} MICLock;

// Work Structure
typedef struct MICWork
{
    MICLock     lock;           // exclusion lock
    MICCallback callback;       // for saving asynchronous function callback
    void*       callbackArg;    // for saving callback function argument
    MICResult   commonResult;   // for saving asynchronous function process results
    MICCallback full;           // for saving sampling complete callback
    void*       fullArg;        // for saving complete callback function argument
    void*       dst_buf;        // for saving simple sampling results storage area

} MICWork;

/*---------------------------------------------------------------------------*
    Internal Variable Definitions
 *---------------------------------------------------------------------------*/
static u16      micInitialized;     // Initialization confirmation flag
static MICWork  micWork;            // work variable structure


/*---------------------------------------------------------------------------*
    Internal Function Definitions
 *---------------------------------------------------------------------------*/
static void     MicCommonCallback( PXIFifoTag tag , u32 data , BOOL err );
static BOOL     MicDoSampling( u16 type );
static BOOL     MicStartAutoSampling( void* buf , u32 size , u32 span , u8 flags );
static BOOL     MicStopAutoSampling( void );
static void     MicGetResultCallback( MICResult result , void* arg );
static void     MicWaitBusy( void );


/*---------------------------------------------------------------------------*
  Name:         MIC_Init

  Description:  Initialization of mic library.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
MIC_Init( void )
{
	// Not initialized verification
	if( micInitialized )
	{
		return;
	}
	micInitialized = 1;

	// Work variable initialization
	micWork.lock = MIC_LOCK_OFF;
	micWork.callback = NULL;

	// Wait until ARM7 MIC library started
	PXI_Init();
	while( ! PXI_IsCallbackReady( PXI_FIFO_TAG_MIC , PXI_PROC_ARM7 ) )
	{
	}

	// Clear latest sampling result storage address for shared area
	OS_GetSystemWork()->mic_last_address = 0;

	// Set up PXI callback function
	PXI_SetFifoRecvCallback( PXI_FIFO_TAG_MIC , MicCommonCallback );
}

/*---------------------------------------------------------------------------*
  Name:         MIC_DoSamplingAsync

  Description:  Carry our asynchronous sampling of mic once.

  Arguments:    type      - Specify sampling type
                buf       - Specify buffer for storing sampling data
                callback  - Specify function to be called when asynchronous
                            processing completes
                arg       - Specify argument for when callback function called

  Returns:      MICResult - Return processing result of operational start of
                            asynchronous device
 *---------------------------------------------------------------------------*/
MICResult
MIC_DoSamplingAsync( MICSamplingType type , void* buf , MICCallback callback , void* arg )
{
    OSIntrMode  enabled;
    u16         wtype;

    SDK_NULL_ASSERT( buf );
    SDK_NULL_ASSERT( callback );

    // parameter verification
    if( type >= MIC_SAMPLING_TYPE_MAX )
    {
        return MIC_RESULT_ILLEGAL_PARAMETER;
    }
    switch( type )
    {
    case MIC_SAMPLING_TYPE_8BIT:
        wtype = SPI_MIC_SAMPLING_TYPE_8BIT;
        break;
    case MIC_SAMPLING_TYPE_12BIT:
        wtype = SPI_MIC_SAMPLING_TYPE_12BIT;
        break;
    case MIC_SAMPLING_TYPE_SIGNED_8BIT:
        wtype = SPI_MIC_SAMPLING_TYPE_S8BIT;
        break;
    case MIC_SAMPLING_TYPE_SIGNED_12BIT:
        wtype = SPI_MIC_SAMPLING_TYPE_S12BIT;
        break;
    default:
        return MIC_RESULT_ILLEGAL_PARAMETER;
    }

    // lock verification
    enabled = OS_DisableInterrupts();
    if( micWork.lock != MIC_LOCK_OFF )
    {
        (void)OS_RestoreInterrupts( enabled );
        return MIC_RESULT_BUSY;
    }
    micWork.lock = MIC_LOCK_ON;
    (void)OS_RestoreInterrupts( enabled );

    // Send execute sampling command
    micWork.callback = callback;
    micWork.callbackArg = arg;
    micWork.dst_buf = buf;
    if( MicDoSampling( wtype ) )
    {
        return MIC_RESULT_SUCCESS;
    }
    return MIC_RESULT_SEND_ERROR;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_DoSampling

  Description:  Sample mic once.

  Arguments:    type      - Specify sampling type
                buf       - Specify buffer for storing sampling data

  Returns:      MICResult - Return processing result of device operation
 *---------------------------------------------------------------------------*/
MICResult
MIC_DoSampling( MICSamplingType type , void* buf )
{
    micWork.commonResult = MIC_DoSamplingAsync( type , buf , MicGetResultCallback , NULL );
    if( micWork.commonResult == MIC_RESULT_SUCCESS )
    {
        MicWaitBusy();
    }
    return micWork.commonResult;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_StartAutoSamplingAsync

  Description:  Asynchronously starts microphone auto-sampling.

  Arguments:    param        - Specifies autosampling settings with pointer to structure.
                callback     - Specifies function to call when asynchronous process completes.
                arg          - Specifies argument at time of callback function call.

  Returns:      MICResult    - Returns process results of asynchronous device operation start.
 *---------------------------------------------------------------------------*/
MICResult
MIC_StartAutoSamplingAsync( const MICAutoParam* param , MICCallback callback , void* arg )
{
    OSIntrMode  enabled;
    u8          flags;

    SDK_NULL_ASSERT( callback );
    SDK_NULL_ASSERT( param->buffer );

    // parameter verification
    {
        // buffer 32 byte align
        if( (u32)( param->buffer ) & 0x01f )
        {
#ifdef  SDK_DEBUG
            OS_TWarning( "Parameter param->buffer must be 32-byte aligned.\n" );
#endif
            return MIC_RESULT_ILLEGAL_PARAMETER;
        }
        // Buffer size alignment
        if( param->size & 0x01f )
        {
#ifdef  SDK_DEBUG
            OS_TWarning( "Parameter param->size must be a multiple of 32-byte.\n" );
#endif
            return MIC_RESULT_ILLEGAL_PARAMETER;
        }
        // buffer size
        if( param->size <= 0 )
        {
            return MIC_RESULT_ILLEGAL_PARAMETER;
        }
        // sampling rate
        if( param->rate < MIC_SAMPLING_RATE_LIMIT )
        {
            return MIC_RESULT_ILLEGAL_PARAMETER;
        }
        // AD conversion bit width
        switch( param->type )
        {
        case MIC_SAMPLING_TYPE_8BIT:
            flags = SPI_MIC_SAMPLING_TYPE_8BIT;
            break;
        case MIC_SAMPLING_TYPE_12BIT:
            flags = SPI_MIC_SAMPLING_TYPE_12BIT;
            break;
        case MIC_SAMPLING_TYPE_SIGNED_8BIT:
            flags = SPI_MIC_SAMPLING_TYPE_S8BIT;
            break;
        case MIC_SAMPLING_TYPE_SIGNED_12BIT:
            flags = SPI_MIC_SAMPLING_TYPE_S12BIT;
            break;
        default:
            return MIC_RESULT_ILLEGAL_PARAMETER;
        }
        // loop determination
        if( param->loop_enable )
        {
            flags = (u8)( flags | SPI_MIC_SAMPLING_TYPE_LOOP_ON );
        }
        else
        {
            flags = (u8)( flags | SPI_MIC_SAMPLING_TYPE_LOOP_OFF );
        }
        // Correction flag for expansion is currently fixed in OFF.
        flags = (u8)( flags | SPI_MIC_SAMPLING_TYPE_CORRECT_OFF );
    }

    // lock verification
    enabled = OS_DisableInterrupts();
    if( micWork.lock != MIC_LOCK_OFF )
    {
        (void)OS_RestoreInterrupts( enabled );
        return MIC_RESULT_BUSY;
    }
    micWork.lock = MIC_LOCK_ON;
    (void)OS_RestoreInterrupts( enabled );

    // Send auto-sampling start command
    micWork.callback = callback;
    micWork.callbackArg = arg;
    micWork.full = param->full_callback;
    micWork.fullArg = param->full_arg;
    if( MicStartAutoSampling( param->buffer , param->size , param->rate , flags ) )
    {
        return MIC_RESULT_SUCCESS;
    }
    return MIC_RESULT_SEND_ERROR;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_StartAutoSampling

  Description:  Starts microphone autosampling.

  Arguments:    param     - Specifies autosampling settings with pointer to structure.

  Returns:      MICResult - Returns process results of device operation.
 *---------------------------------------------------------------------------*/
MICResult
MIC_StartAutoSampling( const MICAutoParam* param )
{
    micWork.commonResult = MIC_StartAutoSamplingAsync( param, MicGetResultCallback , NULL );
    if( micWork.commonResult == MIC_RESULT_SUCCESS )
    {
        MicWaitBusy();
    }
    return micWork.commonResult;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_StopAutoSamplingAsync

  Description:  Asynchronous Stop of Mic Auto-Sampling

  Arguments:    callback  - Specify function to be called when asynchronous
                            processing completes
                arg       - Specify argument for when callback function called

  Returns:      MICResult - Return processing result of operational start of
                            asynchronous device
 *---------------------------------------------------------------------------*/
MICResult
MIC_StopAutoSamplingAsync( MICCallback callback , void* arg )
{
    OSIntrMode  enabled;

    SDK_NULL_ASSERT( callback );

    // lock verification
    enabled = OS_DisableInterrupts();
    if( micWork.lock != MIC_LOCK_OFF )
    {
        (void)OS_RestoreInterrupts( enabled );
        return MIC_RESULT_BUSY;
    }
    micWork.lock = MIC_LOCK_ON;
    (void)OS_RestoreInterrupts( enabled );

    // send auto-sampling stop command
    micWork.callback = callback;
    micWork.callbackArg = arg;
    if( MicStopAutoSampling() )
    {
        return MIC_RESULT_SUCCESS;
    }
    return MIC_RESULT_SEND_ERROR;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_StopAutoSampling

  Description:  Stop Auto-Sampling of Mic
                If loop was not specified during start of auto-sampling, the
                sampling stops automatically at point when buffer is filled.

  Arguments:    None.

  Returns:      MICResult - Return processing result of device operation
 *---------------------------------------------------------------------------*/
MICResult
MIC_StopAutoSampling( void )
{
    micWork.commonResult = MIC_StopAutoSamplingAsync( MicGetResultCallback , NULL );
    if( micWork.commonResult == MIC_RESULT_SUCCESS )
    {
        MicWaitBusy();
    }
    return micWork.commonResult;
}

/*---------------------------------------------------------------------------*
  Name:         MIC_GetLastSamplingAddress

  Description:  Obtain latest sampling result storage address for mic.

  Arguments:    None.

  Returns:      void* - Return sampling result storage address
                        Returns NULL if not yet sampling.

 *---------------------------------------------------------------------------*/
void*
MIC_GetLastSamplingAddress( void )
{
    return (void*)( OS_GetSystemWork()->mic_last_address );
}

/*---------------------------------------------------------------------------*
  Name:         MicCommonCallback

  Description:  Common callback function for asynchronous MIC function

  Arguments:    tag -  PXI tag which show message type.
                data - message from ARM7.
                err -  PXI transfer error flag.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
MicCommonCallback( PXIFifoTag tag , u32 data , BOOL err )
{
    #pragma unused( tag )

    u16         command;
    u16         pxiresult;
    MICResult   result;
    MICCallback cb;

    // Verify PXI communication error
    if( err )
    {
        if( micWork.lock != MIC_LOCK_OFF )
        {
            micWork.lock = MIC_LOCK_OFF;
        }
        if( micWork.callback )
        {
            cb = micWork.callback;
            micWork.callback = NULL;
            cb( MIC_RESULT_FATAL_ERROR , micWork.callbackArg );
        }
    }

    // Analyze data received
    command = (u16)(
        ( data & SPI_PXI_RESULT_COMMAND_MASK ) >> SPI_PXI_RESULT_COMMAND_SHIFT
    );
    pxiresult = (u16)(
        ( data & SPI_PXI_RESULT_DATA_MASK ) >> SPI_PXI_RESULT_DATA_SHIFT
    );

    // Verify processing result
    switch( pxiresult )
    {
    case SPI_PXI_RESULT_SUCCESS:
        result = MIC_RESULT_SUCCESS;
        break;
    case SPI_PXI_RESULT_INVALID_COMMAND:
        result = MIC_RESULT_INVALID_COMMAND;
        break;
    case SPI_PXI_RESULT_INVALID_PARAMETER:
        result = MIC_RESULT_ILLEGAL_PARAMETER;
        break;
    case SPI_PXI_RESULT_ILLEGAL_STATUS:
        result = MIC_RESULT_ILLEGAL_STATUS;
        break;
    case SPI_PXI_RESULT_EXCLUSIVE:
        result = MIC_RESULT_BUSY;
        break;
    default:
        result = MIC_RESULT_FATAL_ERROR;
    }

    // Verify processing target command
    if( command == SPI_PXI_COMMAND_MIC_BUFFER_FULL )
    {
        // Callback buffer full notification
        if( micWork.full )
        {
            micWork.full( result , micWork.fullArg );
        }
    }
    else
    {
        if( command == SPI_PXI_COMMAND_MIC_SAMPLING )
        {
            // Store sampling result in buffer
            if( micWork.dst_buf )
            {
                *(u16*)( micWork.dst_buf ) = OS_GetSystemWork()->mic_sampling_data;
            }
        }
        // Unlock exclusive lock
        if( micWork.lock != MIC_LOCK_OFF )
        {
            micWork.lock = MIC_LOCK_OFF;
        }
        // Callback processing result
        if( micWork.callback )
        {
            cb = micWork.callback;
            micWork.callback = NULL;
            cb( result , micWork.callbackArg );
        }
    }
}

/*---------------------------------------------------------------------------*
  Name:         MicDoSampling

  Description:  Send command for sampling mic once to ARM7.

  Arguments:    type - Sampling type
                ( 0: 8-bit, 1: 12-bit, 2: signed 8-bit, 3: signed 12 bit )

  Returns:      BOOL - If instruction was sent properly through PXI, TRUE.
                       If fails, FALSE.
 *---------------------------------------------------------------------------*/
static BOOL
MicDoSampling( u16 type )
{
	// PXI pocket [ 0 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        SPI_PXI_START_BIT |
        SPI_PXI_END_BIT |
        ( 0 << SPI_PXI_INDEX_SHIFT ) |
        ( SPI_PXI_COMMAND_MIC_SAMPLING << 8 ) |
        (u32)type ,
        0 )
    )
    {
        return FALSE;
    }

    return TRUE;
}

/*---------------------------------------------------------------------------*
  Name:         MicStartAutoSampling

  Description:  Send start command for mic auto-sampling to ARM7.

  Arguments:    buf   - Buffer address for storing data sampled
                size  - Buffer size. Specified in byte units.
                span  - Sampling interval (Specify with ARM7 CPU clock)
                        Due to timer characteristics, an accurate setup can
                        only be done for 16 bits x 1or64or256or1024.
                        Fractional bits are rounded off.
                flags - Specify AD conversion bit width, loop determination,
                        and correction determination.

  Returns:      BOOL - If instruction was sent properly through PXI, TRUE.
                       If fails, FALSE.
 *---------------------------------------------------------------------------*/
static BOOL
MicStartAutoSampling( void* buf , u32 size , u32 span , u8 flags )
{
    // PXI Pocket [ 0 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        SPI_PXI_START_BIT |
        ( 0 << SPI_PXI_INDEX_SHIFT ) |
        ( SPI_PXI_COMMAND_MIC_AUTO_ON << 8 ) |
        (u32)flags ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 1 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        ( 1 << SPI_PXI_INDEX_SHIFT ) |
        ( (u32)buf >> 16) ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 2 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        ( 2 << SPI_PXI_INDEX_SHIFT ) |
        ( (u32)buf & 0x0000ffff ) ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 3 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        ( 3 << SPI_PXI_INDEX_SHIFT ) |
        ( size >> 16 ) ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 4 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        ( 4 << SPI_PXI_INDEX_SHIFT ) |
        ( size & 0x0000ffff ) ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 5 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        ( 5 << SPI_PXI_INDEX_SHIFT ) |
        ( span >> 16 ) ,
        0 )
    )
    {
        return FALSE;
    }

    // PXI Pocket[ 6 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        SPI_PXI_END_BIT |
        ( 6 << SPI_PXI_INDEX_SHIFT ) |
        ( span & 0x0000ffff ) ,
        0 )
    )
    {
        return FALSE;
    }
    return TRUE;
}

/*---------------------------------------------------------------------------*
  Name:         MicStopAutoSampling

  Description:  Send stop command for mic auto-sampling to ARM7.

  Arguments:    None.

  Returns:      BOOL - If instruction was sent properly through PXI, TRUE.
                       If fails, FALSE.
 *---------------------------------------------------------------------------*/
static BOOL
MicStopAutoSampling( void )
{
    // PXI Pocket[ 0 ]
    if( 0 > PXI_SendWordByFifo(
        PXI_FIFO_TAG_MIC ,
        SPI_PXI_START_BIT |
        SPI_PXI_END_BIT |
        ( 0 << SPI_PXI_INDEX_SHIFT ) |
        ( SPI_PXI_COMMAND_MIC_AUTO_OFF << 8 ) ,
        0 )
    )
    {
        return FALSE;
    }
    return TRUE;
}

/*---------------------------------------------------------------------------*
  Name:         MicGetResultCallback

  Description:  Called when asynchronous processing completes. Updates processing
                result for internal variables.

  Arguments:    result - processing result of asynchronous function
                arg    - not used

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
MicGetResultCallback( MICResult result , void* arg )
{
    #pragma unused( arg )

    micWork.commonResult = result;
}

#include    <nitro/code32.h>
/*---------------------------------------------------------------------------*
  Name:         MicWaitBusy

  Description:  Wait while asynchronous processing for MIC is locked

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static asm void
MicWaitBusy( void )
{
    ldr     r12,    =micWork.lock
loop:
    ldr     r0,     [ r12,  #0 ]
    cmp     r0,     #MIC_LOCK_ON
    beq     loop
    bx      lr
}
#include    <nitro/codereset.h>


/*---------------------------------------------------------------------------*
  End of file
 *---------------------------------------------------------------------------*/
