#include <ai.h>

//
// local data
//

static BOOL __AI_init_flag;

static AIDCallback __AID_Callback;
static AISCallback __AIS_Callback;

// ----------------------------------------------------------------------------

// AUDIO FIFO-DMA

AIDCallback AIRegisterDMACallback(AIDCallback callback)
{
    BOOL old = OSDisableInterrupts();
    AIDCallback old_callback = __AID_Callback;
    __AID_Callback = callback;
    OSRestoreInterrupts(old);
    return old_callback;
}

void AIInitDMA(u32 start_addr, u32 length)
{
    BOOL old = OSDisableInterrupts();

    //
    // set dma address
    //

    *(u16 *)(0xCC005030) =
        (*(u16 *)(0xCC005030) & 0xFC00) |       // keep 6 bits unchaged
        (start_addr >> 16);                     // upper part

    *(u16 *)(0xCC005032) =
        (*(u16 *)(0xCC005032) & 0xFFE0) |       // 32-byte aligned
        (start_addr & 0xFFFF);                  // lower part

    // check alignment
    if(length & 0x1F)
    {
        OSHalt("AIStartDMA: length must be multiple of 32 bytes");
    }

    // TODO : Nintendo missed checking for maximum length

    //
    // set length counter
    //

    // somewhat like this :
    *(u16 *)(0xCC005036) = length / 32;

    OSRestoreInterrupts(old);
}

BOOL AIGetDMAEnableFlag(void)
{
    return *(u16 *)(0xCC005036) >> 15;
}

void AIStartDMA(void)
{
    *(u16 *)(0xCC005036) |= 0x8000;
}

void AIStopDMA(void)
{
    *(u16 *)(0xCC005036) &= ~0x8000;
}

u32 AIGetDMABytesLeft(void)
{
    return *(u16 *)(0xCC00503A) * 32;
}

u32 AIGetDMAStartAddr(void)
{
    return  ((*(u16 *)(0xCC005030) << 16) |     // upper part
             (*(u16 *)(0xCC005032)      ) )     // lower part
             & 0x03FFFFE0;                      // apply address mask
}

u32 AIGetDMALength(void)
{
    return (*(u16 *)(0xCC005036) & ~0x8000) * 32;
}

BOOL AICheckInit(void)
{
    return __AI_init_flag;
}                                              

// ----------------------------------------------------------------------------

// AUDIO STREAMING 

// hardware registers
#define AICR    ( *(volatile u32 *)(0xCC006C00 + 0x00) )
#define AIVR    ( *(volatile u32 *)(0xCC006C00 + 0x04) )
#define AISCNT  ( *(volatile u32 *)(0xCC006C00 + 0x08) )
#define AIIT    ( *(volatile u32 *)(0xCC006C00 + 0x0C) )

// AICR bit shifts
#define AICR_DFR_SHFT       6                   // HW2 only !!
#define AICR_SCRESET_SHFT   5
#define AICR_AIINTVLD_SHFT  4
#define AICR_AIINT_SHFT     3
#define AICR_AIINTMSK_SHFT  2
#define AICR_AFR_SHFT       1
#define AICR_PSTAT_SHFT     0

// AICR mask layout
#define AICR_DFR        (1 << AICR_DFR_SHFT)    // HW2 only !!
#define AICR_SCRESET    (1 << AICR_SCRESET_SHFT)
#define AICR_AIINTVLD   (1 << AICR_AIINTVLD_SHFT)
#define AICR_AIINT      (1 << AICR_AIINT_SHFT)
#define AICR_AIINTMSK   (1 << AICR_AIINTMSK_SHFT)
#define AICR_AFR        (1 << AICR_AFR_SHFT)
#define AICR_PSTAT      (1 << AICR_PSTAT_SHFT)

// volume register macros
#define AIVR_SETR(val)  (AIVR = (AIVR & 0x00FF) | (val << 8))
#define AIVR_SETL(val)  (AIVR = (AIVR & 0xFF00) | (val))
#define AIVR_GETR       (AIVR >> 8)
#define AIVR_GETL       (AIVR & 0xFF)

AISCallback AIRegisterStreamCallback(AISCallback callback)
{
    BOOL old = OSDisableInterrupts();
    AISCallback old_callback = __AIS_Callback;
    __AIS_Callback = callback;
    OSRestoreInterrupts(old);
    return old_callback;
}

u32 AIGetStreamSampleCount(void)
{
    return AISCNT;
}

void AIResetStreamSampleCount(void)
{
    AICR = (AICR & ~AICR_SCRESET) | AICR_SCRESET;
}

void AISetStreamTrigger(u32 trigger)
{
    AIIT = trigger;
}

u32 AIGetStreamTrigger(void)
{
    return AIIT;
}

void AISetStreamPlayState(u32 state)
{
}

u32 AIGetStreamPlayState(void)
{
    return (AICR & AICR_PSTAT);
}

void AISetDSPSampleRate(u32 rate)
{
}

u32 AIGetDSPSampleRate(void)
{
    return (AICR >> AICR_DFR_SHFT) & 1;
}

void AISetStreamSampleRate(u32 rate)
{
}

u32 AIGetStreamSampleRate(void)
{
    return (AICR >> AICR_AFR_SHFT) & 1;
}

void        AISetStreamVolLeft          (u8 vol);
void        AISetStreamVolRight         (u8 vol);

u8          AIGetStreamVolLeft          (void);
u8          AIGetStreamVolRight         (void);

