// DSP mailbox registers
#define DSP_OUTMBOXH        *(volatile u16 *)(0xCC005000)
#define DSP_OUTMBOXL        *(volatile u16 *)(0xCC005002)
#define DSP_INMBOXH         *(volatile u16 *)(0xCC005004)
#define DSP_INMBOXL         *(volatile u16 *)(0xCC005006)                               

// known ARAM controller registers
#define AR_SIZE             (*(volatile u16 *)(0xCC005012))     // 16 bit regs
#define AR_MODE             (*(volatile u16 *)(0xCC005016))
#define AR_REFRESH          (*(volatile u16 *)(0xCC00501A))
#define AR_DMA_MMADDR_H     (*(volatile u16 *)(0xCC005020))
#define AR_DMA_MMADDR_L     (*(volatile u16 *)(0xCC005022))
#define AR_DMA_ARADDR_H     (*(volatile u16 *)(0xCC005024))
#define AR_DMA_ARADDR_L     (*(volatile u16 *)(0xCC005026))
#define AR_DMA_CNT_H        (*(volatile u16 *)(0xCC005028))
#define AR_DMA_CNT_L        (*(volatile u16 *)(0xCC00502A))

#define AR_DMA_MMADDR       (*(volatile u16 *)(0xCC005020))     // 32 bit regs
#define AR_DMA_ARADDR       (*(volatile u16 *)(0xCC005024))
#define AR_DMA_CNT          (*(volatile u16 *)(0xCC005028))

// DSP interface main control register
#define AIDCR               (*(volatile u16 *)(0xCC00500A))

// AIDCR bits (bits 10 and 11 are unknown)
#define AIDCR_DSPDMA        (1 << 9)        // dsp task dma in progress, if set
#define AIDCR_DSPINTMSK     (1 << 8)        // * interrupt mask   (RW)
#define AIDCR_DSPINT        (1 << 7)        // * interrupt active (RWC)
#define AIDCR_ARINTMSK      (1 << 6)
#define AIDCR_ARINT         (1 << 5)
#define AIDCR_AIINTMSK      (1 << 4)
#define AIDCR_AIINT         (1 << 3)
#define AIDCR_HALT          (1 << 2)        // halt DSP
#define AIDCR_PIINT         (1 << 1)        // assert DSP PI interrupt
#define AIDCR_RES           (1 << 0)        // reset DSP

static  u16 DSPInitCode[0x40] = {
    0x029F, 0x0010, 0x029F, 0x0033, 0x029F, 0x0034, 0x029F, 0x0035,
    0x029F, 0x0036, 0x029F, 0x0037, 0x029F, 0x0038, 0x029F, 0x0039,
    0x1206, 0x1203, 0x1204, 0x1205, 0x0080, 0x8000, 0x0088, 0xFFFF,
    0x0084, 0x1000, 0x0064, 0x001D, 0x0218, 0x0000, 0x8100, 0x1C1E,
    0x0044, 0x1B1E, 0x0084, 0x0800, 0x0064, 0x0027, 0x191E, 0x0000,
    0x00DE, 0xFFFC, 0x02A0, 0x8000, 0x029C, 0x0028, 0x16FC, 0x0054,
    0x16FD, 0x4348, 0x0021, 0x02FF, 0x02FF, 0x02FF, 0x02FF, 0x02FF
};

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

void __OSInitAudioSystem()
{
    // copy DSP init ucode to 32B aligned memory area
    // save used region below of arenaHi
    memcpy(OSGetArenaHi() - 128, 0x81000000, sizeof(DSPInitCode));
    memcpy(0x81000000, DSPInitCode, sizeof(DSPInitCode));
    DCFlushRange(0x81000000, sizeof(DSPInitCode));

    AR_SIZE = 0x0043;
    AIDCR = 0x0800 | AIDCR_DSPINT | AIDCR_ARINT | AIDCR_AIINT | AIDCR_HALT;

    // reset DSP and wait
    AIDCR |= AIDCR_RES;
    while(AIDCR & AIDCR_RES);

    // write 0 and wait mail delivery
    DSP_OUTMBOXH = 0;
    while(((DSP_INMBOXH << 16) | DSP_INMBOXL) & 0x80000000);

    // send DSP initialization ucode to ARAM at offset 0
    AR_DMA_MMADDR = OSCachedToPhysical(0x81000000);
    AR_DMA_ARADDR = 0;
    AR_DMA_CNT = 32;

    // wait DMA complete
    u16 old = AIDCR;    // keep AIDCR_DSPDMA
    while(AIDCR & AIDCR_DSPDMA);
    AIDCR = old;        // clear DMA status

    // wait a little
    OSTick old = OSGetTick();
    while((OSGetTick() - old) < 2194);

    // send DSP initialization ucode to ARAM at offset 0 (again)
    AR_DMA_MMADDR = OSCachedToPhysical(0x81000000);
    AR_DMA_ARADDR = 0;
    AR_DMA_CNT = 32;

    // wait DMA complete
    u16 old = AIDCR;    // keep AIDCR_DSPDMA
    while(AIDCR & AIDCR_DSPDMA);
    AIDCR = old;        // clear DMA status

    AIDCR &= ~0x0800
    while(AIDCR & 0x0400);
    AIDCR &= ~AIDCR_HALT;
    while((DSP_INMBOXH & 0x8000) == 0);
    u16 dummy = DSP_INMBOXL;    // read nowhere
    AIDCR |= AIDCR_HALT;

    AIDCR = 0x0800 | AIDCR_DSPINT | AIDCR_ARINT | AIDCR_AIINT | AIDCR_HALT;

    // reset DSP and wait
    AIDCR |= AIDCR_RES;
    while(AIDCR & AIDCR_RES);    

    // restore saved region
    memcpy(0x81000000, OSGetArenaHi() - 128, sizeof(DSPInitCode));
}
