Library to control and transfer data from NXP SGTL5000. As used on the Teensy Audio Shield. It uses DMA to transfer I2S FIFO data.

The Library now supports dual codecs. Allowing all 4 channels of the Teensy I2S interface to RX and TX data to separate SGTL5000 devices.

The ISR routines that handles pointer swaps for double buffering has been fully coded in assembler to reduce overhead and now takes < 800nS per FIFO transfer when using all 4 channels.

Support added for all typical sample rates and system Clock speeds of 96Mhz or 120Mhz.

Pause and Resume functions added to allow quick and simple suppression of IRQs and stream halting and restart. This required software triggered IRQ, in order to ensure accurate word sync control.

sgtl5000.cpp

Committer:
aidan1971
Date:
2017-09-27
Revision:
14:9043626add45
Parent:
10:49bb33f71d32

File content as of revision 14:9043626add45:

/*!
@ author Aidan Walton, aidan.walton@gmail.com

@section LICENSE
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.

@section DESCRIPTION
    Library for NXP SGTL5000 Codec
*/

#include "sgtl5000.h"

namespace SGTL5000
{

// Static variables required within ISRs
volatile uint32_t * volatile SGTL5000::BufRX_L_safe;                            // Private pointers assigned to users data pointers
volatile uint32_t * volatile SGTL5000::BufRX_R_safe;                            // These are used to flip user pointers between safe 'active' regions of
volatile uint32_t * volatile SGTL5000::BufTX_L_safe;                            // double buffered space.
volatile uint32_t * volatile SGTL5000::BufTX_R_safe;
volatile uint32_t * volatile SGTL5000::BufRX_L_safe2;                           // Private pointers assigned to users data pointers
volatile uint32_t * volatile SGTL5000::BufRX_R_safe2;                           // These are used to flip user pointers between safe 'active' regions of
volatile uint32_t * volatile SGTL5000::BufTX_L_safe2;                           // double buffered space.
volatile uint32_t * volatile SGTL5000::BufTX_R_safe2;

uint32_t SGTL5000::BufRX_L_safeA;                                               // Precalculated double buffer addresses
uint32_t SGTL5000::BufRX_R_safeA;
uint32_t SGTL5000::BufTX_L_safeA;
uint32_t SGTL5000::BufTX_R_safeA;
uint32_t SGTL5000::BufRX_L_safeB;                                               // Precalculated double buffer addresses
uint32_t SGTL5000::BufRX_R_safeB;
uint32_t SGTL5000::BufTX_L_safeB;
uint32_t SGTL5000::BufTX_R_safeB;
uint32_t SGTL5000::BufRX_L_safeA2;                                              // Precalculated double buffer addresses 2nd codec
uint32_t SGTL5000::BufRX_R_safeA2;
uint32_t SGTL5000::BufTX_L_safeA2;
uint32_t SGTL5000::BufTX_R_safeA2;
uint32_t SGTL5000::BufRX_L_safeB2;                                              // Precalculated double buffer addresses 2nd codec
uint32_t SGTL5000::BufRX_R_safeB2;
uint32_t SGTL5000::BufTX_L_safeB2;
uint32_t SGTL5000::BufTX_R_safeB2;

uint32_t SGTL5000::I2S_RX_Buffer[16] = {0};                                     // Data buffers
uint32_t SGTL5000::I2S_TX_Buffer[16] = {0};
uint32_t SGTL5000::I2S_RX_Buffer2[16] = {0};
uint32_t SGTL5000::I2S_TX_Buffer2[16] = {0};
IRQn SGTL5000::SYNC_swIRQ;                                                      // IRQn assigned by user to the software IRQ triggered by the FIFO queues.
IRQn SGTL5000::TX_swIRQ;
IRQn SGTL5000::RX_swIRQ;
IRQn SGTL5000::CODEC_CTRL_IRQ;                                                  // Stream ctrl IRQ
uint32_t SGTL5000::RX_DMAch;                                                    // User defined RX DMA channel number
uint32_t SGTL5000::TX_DMAch;                                                    // User defined TX DMA channel number
uint32_t SGTL5000::active_RX_DMAch_bm;
uint32_t SGTL5000::active_TX_DMAch_bm;
uint32_t SGTL5000::sync_dma_irq_acks = 0;
Callback<void()> SGTL5000::TX_user_func = NULL;
Callback<void()> SGTL5000::RX_user_func = NULL;
Callback<void()> SGTL5000::SYNC_user_func = NULL;                               // User defined callback function
uint32_t SGTL5000::db_phase_sync;
uint32_t SGTL5000::db_phase_tx;
uint32_t SGTL5000::db_phase_rx;
bool SGTL5000::SYNC_run;
bool SGTL5000::TX_run;
bool SGTL5000::RX_run;

uint32_t SGTL5000::ctrl_command = 0;
bool SGTL5000::i2s_configured = false;                                          // Shared I2S state
bool SGTL5000::codec2_active = false;                                           // Which codecs are instantiated
bool SGTL5000::codec1_active = false;
uint32_t SGTL5000::SYNC_attach_type;
uint32_t SGTL5000::TX_attach_type;
uint32_t SGTL5000::RX_attach_type;

uint32_t SGTL5000::debug[16] = {0};


uint32_t SGTL5000::t1;                                                        // Used to debug timing issues
uint32_t SGTL5000::proc_time;
uint32_t SGTL5000::SYST_CVAL = 0xE000E018;
bool SGTL5000::debug_read = false;



SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq, bool i2c_ctrl_adr0_cs, IRQn _ctrl_IRQn)
    : mI2C(i2c_sda, i2c_scl), i2c_freq(_i2c_freq)
{
    if(i2c_ctrl_adr0_cs) {
        i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1;
        ctrl_codec = false;
    } else {
        i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
        SGTL5000::CODEC_CTRL_IRQ = _ctrl_IRQn;
        NVIC_SetVector(SGTL5000::CODEC_CTRL_IRQ, (uint32_t)&SGTL5000::stream_ctrl_ISR);              // Set ctrl command IRQ vector
        NVIC_SetPriority(SGTL5000::CODEC_CTRL_IRQ, 0);                                               // Set irq priority
        NVIC_EnableIRQ(SGTL5000::CODEC_CTRL_IRQ);
        ctrl_codec = true;
    }
    codec_configured = false;
    SGTL5000::SYNC_run = false;
    SGTL5000::TX_run = false;
    SGTL5000::RX_run = false;
    SYNC_attached = false;
    TX_attached = false;
    RX_attached = false;
}

int32_t SGTL5000::init(void)
{
    if(SGTL5000::codec1_active && SGTL5000::codec2_active) return -1;                               // We can only have 2 actively configured codec objects, if both are already active and configured bomb out.
    if((i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) && SGTL5000::codec1_active) return -2;
    if((i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) && SGTL5000::codec2_active) return -3;
    if(this->ctrl_codec && !SGTL5000::i2s_configured) init_i2s();
    mI2C.frequency(i2c_freq);
    int32_t error = init_codec();
    if(error) return error;
    else codec_configured = true;

    return 0;
}

int32_t SGTL5000::sample_rate(uint32_t rate)
{
    if(!codec_configured || !this->ctrl_codec) return -1;
    uint32_t I2S_MCLK_M;
    uint32_t I2S_MCLK_D;
    uint32_t codec_SYS_FS;
    uint32_t codec_RATE_MODE;
    switch(rate) {
        case 8:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 16;
                    I2S_MCLK_D = 750;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 16;
                    I2S_MCLK_D = 936;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 11:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 1;
                    I2S_MCLK_D = 34;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 1;
                    I2S_MCLK_D = 42;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 12:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 16;
                    I2S_MCLK_D = 500;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 16;
                    I2S_MCLK_D = 624;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 16:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 750;
                    codec_SYS_FS = 0x00;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 936;
                    codec_SYS_FS = 0x00;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 22:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 1;
                    I2S_MCLK_D = 17;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 1;
                    I2S_MCLK_D = 21;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 24:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 500;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
                case 120000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 624;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x01;
                    break;
            }
            break;
        case 32:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 64;
                    I2S_MCLK_D = 750;
                    codec_SYS_FS = 0x00;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 64;
                    I2S_MCLK_D = 936;
                    codec_SYS_FS = 0x00;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 44:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 2;
                    I2S_MCLK_D = 17;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 2;
                    I2S_MCLK_D = 21;
                    codec_SYS_FS = 0x01;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 48:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 250;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 312;
                    codec_SYS_FS = 0x02;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 96:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 125;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 32;
                    I2S_MCLK_D = 156;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 72:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 24;
                    I2S_MCLK_D = 125;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 24;
                    I2S_MCLK_D = 156;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 144:
            switch(SystemCoreClock) {
                case 96000000:
                    I2S_MCLK_M = 48;
                    I2S_MCLK_D = 125;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 48;
                    I2S_MCLK_D = 156;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
            }
            break;
        case 192:
            switch(SystemCoreClock) {
                case 96000000:                                                            // Not officially supported by the codec, but it seems to work.
                    I2S_MCLK_M = 64;
                    I2S_MCLK_D = 125;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
                case 120000000:
                    I2S_MCLK_M = 64;
                    I2S_MCLK_D = 156;
                    codec_SYS_FS = 0x03;
                    codec_RATE_MODE = 0x0;
                    break;
            }
        default:
            return -1;
    }
    if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK)) return -1;               // Set CODEC clocking
    if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK)) return -1;         // Set CODEC clocking
    if(SGTL5000::codec2_active) {
        if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;                     // Set 2nd CODEC clocking
        if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;               // Set 2nd CODEC clocking
    }
    while(I2S0->MCR & I2S_MCR_DUF_MASK);                                                                    // Check for active changes then set I2S Clock Fractions
    I2S0->MDR = (I2S0->MDR & ~(I2S_MDR_FRACT_MASK | I2S_MDR_DIVIDE_MASK)) | (I2S_MDR_FRACT(I2S_MCLK_M - 1) | I2S_MDR_DIVIDE(I2S_MCLK_D - 1));
    return 0;
}

int32_t SGTL5000::read_i2c(uint32_t reg_addr, uint32_t mask, int _i2c_addr)
{
    if(!_i2c_addr) _i2c_addr = i2c_addr;
    if(mask == 0x0 || mask > 0xFFFF) return -1;
    uint32_t shift;
    for (shift = 0; !(mask & (1<<shift)); ++shift) {};
    uint32_t wire_data;
    uint32_t wire_addr = __rev(reg_addr) >> 16;
    mI2C.lock();
    if(mI2C.write(_i2c_addr, (char *)&wire_addr, 2, false)) {
        mI2C.unlock();
        return -1;
    }
    if(mI2C.read(_i2c_addr, (char *)&wire_data, 2, false)) {
        mI2C.unlock();
        return -1;
    }
    return ((__rev(wire_data) >> 16) & mask) >> shift;
}

int32_t SGTL5000::write_i2c(uint32_t reg_addr, uint32_t data, int _i2c_addr)
{
    if(!_i2c_addr) _i2c_addr = i2c_addr;
    uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev(data));
    mI2C.lock();
    if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) {
        mI2C.unlock();
        return -1;
    }
    return 0;
}

int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask, int _i2c_addr)
{
    if(!_i2c_addr) _i2c_addr = i2c_addr;
    if(mask == 0x0 || mask > 0xFFFF) return -1;
    uint32_t shift;
    for (shift = 0; !(mask & (1<<shift)); ++shift) {};
    uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev((read_i2c(reg_addr) & (~mask & 0xFFFF)) | ((data << shift) & mask)));
    mI2C.lock();
    if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) {
        mI2C.unlock();
        return -1;
    }
    return 0;
}


int32_t SGTL5000::init_i2s(void)
{
    if(!this->ctrl_codec) return -1;

    // Set default rate at 48Khz
    uint32_t I2S_MCLK_MULT;
    uint32_t I2S_MCLK_DIV;
    switch(SystemCoreClock) {
        case 96000000:
            I2S_MCLK_MULT = 32;
            I2S_MCLK_DIV = 250;
            break;
        case 120000000:
            I2S_MCLK_MULT = 32;
            I2S_MCLK_DIV = 312;
            break;
    }

    // Configure System Clock Distribution for I2S
    SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK;                                                 // Enable Clock to PORTC control module.
    SIM->SCGC6 |= SIM_SCGC6_I2S_MASK;                                                   // Enable Clocking to I2S Module
    SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;                                                   // Enable DMA Clock Gate Control
    SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;                                                // Enable DMA Mux Clock Gate Control

    // Configure I2S Master Clocking
    I2S0->MCR |= I2S_MCR_MICS(3);                                                       // Configure I2S MCLK Divder Source as System PLL
    while(I2S0->MCR & I2S_MCR_DUF_MASK);                                                // Check for active changes then set I2S Clock Fractions
    I2S0->MDR = (I2S0->MDR & ~(I2S_MDR_FRACT_MASK | I2S_MDR_DIVIDE_MASK)) | (I2S_MDR_FRACT(I2S_MCLK_MULT - 1) | I2S_MDR_DIVIDE(I2S_MCLK_DIV - 1));
    I2S0->MCR |= I2S_MCR_MOE_MASK;                                                      // Enable MCLK Output

    // Configure PIN Muxing

    PORTC->PCR[6] = (PORTC->PCR[6] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6);             // Set PTC6(pin51)(Teensy pin11) <> I2S0_TX_MCLK
    PORTC->PCR[2] = (PORTC->PCR[2] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6);             // Set PTC2(pin45)(Teensy pin23) <> I2S0_TX_FS(LRCLK)
    PORTC->PCR[3] = (PORTC->PCR[3] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6);             // Set PTC3(pin46)(Teensy pin9) <> I2S0_TX_BCLK
    PORTC->PCR[1] = (PORTC->PCR[1] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6);             // Set PTC1(pin44)(Teensy pin22) <> I2S0_TXD0
    PORTC->PCR[5] = (PORTC->PCR[5] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4);             // Set PTC5(pin50)(Teensy pin13) <> I2S0_RXD0
    PORTC->PCR[0] = (PORTC->PCR[0] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6);             // Set PTC0(pin43)(Teensy pin15) <> I2S0_TXD1
    PORTC->PCR[11] = (PORTC->PCR[11] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4);           // Set PTC11(pin56)(Teensy pin30) <> I2S0_RXD1

    // Config. TX
    I2S0->TMR = 0;                                                                      // I2S TX Mask cleared.
    I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_SYNC_MASK) | I2S_TCR2_SYNC(0);                 // Set TX Mode to Async
    I2S0->TCR2 |= I2S_TCR2_BCP_MASK;                                                    // Set TX Bit Clock Polarity sample riseing edge
    I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_MSEL_MASK) | I2S_TCR2_MSEL(1);                 // Set TX MCLK Audio Master Clock to Master Clock 1.
    I2S0->TCR2 |= I2S_TCR2_BCD_MASK;                                                    // Set TX Clock Dir. internally generated
    I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_DIV_MASK) | I2S_TCR2_DIV(3);                   // Set TX Bit Clock Divide
    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_WDFL_MASK) | I2S_TCR3_WDFL(0);                 // Set Word Flag Config to be 1st word in frame.
    I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_FRSZ_MASK) | I2S_TCR4_FRSZ(1);                 // Set TX Frame Size @ 2 words per frame
    I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_SYWD_MASK) | I2S_TCR4_SYWD(15);                // Set TX frame sync width to 16bits
    I2S0->TCR4 |= I2S_TCR4_MF_MASK;                                                     // Set TX MSBit first
    I2S0->TCR4 |= I2S_TCR4_FSE_MASK;                                                    // Set TX Frame sync asserts before first bit
    I2S0->TCR4 |= I2S_TCR4_FSP_MASK;                                                    // Set TX Frame sync polarity active low.
    I2S0->TCR4 |= I2S_TCR4_FSD_MASK;                                                    // Set TX Frame sync direction from master clock
    I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_WNW_MASK) | I2S_TCR5_WNW(15);                  // Set TX to 16bits per word.
    I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_W0W_MASK) | I2S_TCR5_W0W(15);                  // Set TX to 16bits per first word.
    I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_FBT_MASK) | I2S_TCR5_FBT(15);                  // Set TX first bit transmitted index to 16th bit .

    //Config RX
    I2S0->RMR = 0;                                                                      // I2S RX Mask cleared.
    I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_SYNC_MASK) | I2S_RCR2_SYNC(1);                 // Set RX Mode to Sync with TX
    I2S0->RCR2 |= I2S_TCR2_BCP_MASK;                                                    // Set RX Bit Clock Polarity sample riseing edge
    I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_MSEL_MASK) | I2S_RCR2_MSEL(1);                 // Set RX MCLK Audio Master Clock to Master Clock 1.
    I2S0->RCR2 |= I2S_RCR2_BCD_MASK;                                                    // Set RX Clock Dir. internally generated
    I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_DIV_MASK) | I2S_RCR2_DIV(3);                   // Set RX Bit Clock Divide
    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_WDFL_MASK) | I2S_RCR3_WDFL(0);                 // Set Word Flag Config to be 1st word in frame.
    I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_FRSZ_MASK) | I2S_RCR4_FRSZ(1);                 // Set RX Frame Size @ 2 words per frame
    I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_SYWD_MASK) | I2S_RCR4_SYWD(15);                // Set RX frame sync width to 16bits
    I2S0->RCR4 |= I2S_RCR4_MF_MASK;                                                     // Set RX MSBit first
    I2S0->RCR4 |= I2S_RCR4_FSE_MASK;                                                    // Set RX Frame sync asserts before first bit
    I2S0->RCR4 |= I2S_RCR4_FSP_MASK;                                                    // Set RX Frame sync polarity active low.
    I2S0->RCR4 |= I2S_RCR4_FSD_MASK;                                                    // Set RX Frame sync direction from master clock
    I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_WNW_MASK) | I2S_RCR5_WNW(15);                  // Set RX to 16bits per word.
    I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_W0W_MASK) | I2S_RCR5_W0W(15);                  // Set RX to 16bits per first word.
    I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_FBT_MASK) | I2S_RCR5_FBT(15);                  // Set RX first bit transmitted index to 16 .

    // Configure I2S Peripheral
    I2S0->TCSR |= I2S_TCSR_BCE_MASK;                                                    // Enable I2S Tx bit clock
    I2S0->RCSR |= I2S_RCSR_BCE_MASK;                                                    // Enable I2S Rx Bit Clock

    SGTL5000::i2s_configured = true;
    return 0;
}

int32_t SGTL5000::init_codec(void)
{
    // Default Configure Codec
    if(
        write_i2c(SGTL5000_ANA_POWER,(
                      (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) |                               // Startup power sequence and reset.
                      (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_SHIFT))                     // Enable simple linear REG
                  ))
    ) return -1;
    wait_us(10000);
    if(                                                                                                                                             // Wait 10mS for powerup and reset
        write_i2c(SGTL5000_ANA_POWER,(
                      (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_SHIFT)) |                   // Disable simple digital lineregulator. Teensy shield has external reg but sequence requires simple linereg on for reset and power-up
                      (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) |                               // Disable reset and powerup circuits. This Can be cleared after powerup as VDDD is external on Teensy
                      (SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_SHIFT)) |                     // Disable charge pump as VDDA & VDDIO > 3.1V
                      (SGTL5000_ANA_POWER_PLL_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_PLL_POWERUP_SHIFT)) |                                       // PLL disabled as codec clocking is synchronous to I2S clock.
                      (SGTL5000_ANA_POWER_LINREG_D_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINREG_D_POWERUP_SHIFT)) |                             // Disable the VDDD line regulator as Teensy externally regulated at 1.85V
                      (SGTL5000_ANA_POWER_VCOAMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VCOAMP_POWERUP_SHIFT)) |                                 // Disable the PLL VCO Amp, we don't use PLL
                      (SGTL5000_ANA_POWER_VAG_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_VAG_POWERUP_SHIFT)) |                                       // Powerup VAG ref buffer. Should be enabled before powering up headphone and lineout and disabled before powering down HP and LINEOUT
                      (SGTL5000_ANA_POWER_DAC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_DAC_MONO_SHIFT)) |                                             // Select stereo mode for DAC
                      (SGTL5000_ANA_POWER_ADC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_ADC_MONO_SHIFT)) |                                             // ADC in stereo mode
                      (SGTL5000_ANA_POWER_REFTOP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_REFTOP_POWERUP_SHIFT)) |                                 // Enable bias currents
                      (SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_HEADPHONE_POWERUP_SHIFT)) |                           // Disable HP amp until we set the reference levels
                      (SGTL5000_ANA_POWER_DAC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_DAC_POWERUP_SHIFT)) |                                       // Disable DAC until we set refs
                      (SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_SHIFT)) |           // Disable capless headphones
                      (SGTL5000_ANA_POWER_ADC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_ADC_POWERUP_SHIFT)) |                                       // Disable ADC power until we set refs
                      (SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINEOUT_POWERUP_SHIFT))                                 // Disable LINEOUT power until we set refs
                  ))
    ) return -1;
    if(
        write_i2c(SGTL5000_LINREG_CTRL,(
                      (SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_MASK & (0x0 << SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_SHIFT)) |                               // Manually assign charge pump rail (VDDC) source as VDDA.
                      (SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_MASK & (0x1 << SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_SHIFT)) |                             // chargepump source manually assigned
                      (SGTL5000_LINREG_CTRL_D_PROGRAMMING_MASK & (0xC << SGTL5000_LINREG_CTRL_D_PROGRAMMING_SHIFT))                                 // VDDD Line Reg not used so leave default
                  ))
    ) return -2;
    if(
        write_i2c(SGTL5000_REF_CTRL,(
                      (SGTL5000_REF_CTRL_VAG_VAL_MASK & (0x1F << SGTL5000_REF_CTRL_VAG_VAL_SHIFT)) |                                                // Set VAG ref to 1.575V
                      (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) |                                             // Set analog bias currents to +12.5%
                      (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT))                                               // Set small pop ramp normal
                  ))
    ) return -3;
    if(
        write_i2c(SGTL5000_LINE_OUT_CTRL,(
                      (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0x3 << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) |                               // Set Lineout bias curent 0.36mA
                      (SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_MASK & (0x22 << SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_SHIFT))                                // Lineout AGND = 1.65v (line out will drive between the rails, so set at VDDA/2)
                  ))
    ) return -4;
    if(
        write_i2c(SGTL5000_SHORT_CTRL,(
                      (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) |                                             // HP R short detect 125mA
                      (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) |                                             // HP L short detect 125mA
                      (SGTL5000_SHORT_CTRL_LVLADJC_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJC_SHIFT)) |                                             // Headphone capless short detect 250mA
                      (SGTL5000_SHORT_CTRL_MODE_LR_MASK & (0x1 << SGTL5000_SHORT_CTRL_MODE_LR_SHIFT)) |                                             // en short det reset 50mS
                      (SGTL5000_SHORT_CTRL_MODE_CM_MASK & (0x2 << SGTL5000_SHORT_CTRL_MODE_CM_SHIFT))                                               // En capless short det reset vol rise
                  ))
    ) return -5;
    if(
        write_i2c(SGTL5000_ANA_CTRL,(
                      (SGTL5000_ANA_CTRL_MUTE_LO_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_LO_SHIFT)) |                                                 // Mute LINEOUT
                      (SGTL5000_ANA_CTRL_SELECT_HP_MASK & (0x0 << SGTL5000_ANA_CTRL_SELECT_HP_SHIFT)) |                                             // Select DAC as input to HP amp
                      (SGTL5000_ANA_CTRL_EN_ZCD_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_HP_SHIFT)) |                                             // Enable ZCD on HP
                      (SGTL5000_ANA_CTRL_MUTE_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_HP_SHIFT)) |                                                 // Mute the headphones
                      (SGTL5000_ANA_CTRL_SELECT_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_SELECT_ADC_SHIFT)) |                                           // Select LINEIN as input to ADC
                      (SGTL5000_ANA_CTRL_EN_ZCD_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_ADC_SHIFT)) |                                           // Enable ADC ZCD
                      (SGTL5000_ANA_CTRL_MUTE_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_ADC_SHIFT))                                                 // Mute ADC pre-amp
                  ))
    ) return -6;
    if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK)) return -7;                                                   // Power up Headphone amp
    if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK)) return -8;                                                         // Power up DAC
    if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK)) return -9;                                           // Power up capless HP block
    if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK)) return -10;                                                        // Power up ADC
    if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK)) return -11;                                                    // Power up Lineout Amp
    if(modify_i2c(SGTL5000_ANA_POWER, 0x0, SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK)) return -12;                                              // Turn of simple regulator now the blocks are powered-up

    if(
        write_i2c(SGTL5000_DIG_POWER,(
                      (SGTL5000_DIG_POWER_ADC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_ADC_POWERUP_SHIFT)) |
                      (SGTL5000_DIG_POWER_DAC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAC_POWERUP_SHIFT)) |
                      (SGTL5000_DIG_POWER_DAP_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAP_POWERUP_SHIFT)) |
                      (SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_OUT_POWERUP_SHIFT)) |                               // Keep I2S data lines disabled until config complete
                      (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT))
                  ))
    ) return -13;
    write_i2c(SGTL5000_LINE_OUT_VOL,    (   (SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_MASK & (0x11 << SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_SHIFT)) |        // Lineout Vol normalised
                                            (SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_MASK & (0x11 << SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_SHIFT))            // Verified with oscope at p2p = 2.82v driving ~30k load
                                        ));
    if(
        write_i2c(SGTL5000_PAD_STRENGTH,(
                      (SGTL5000_PAD_STRENGTH_I2S_LRCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_LRCLK_SHIFT)) |                                     // I2S LR_CLK drive dissabled (codec is slave)
                      (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) |                                       // I2S SCLK drive dissabled (codec is slave)
                      (SGTL5000_PAD_STRENGTH_I2S_DOUT_MASK & (0x1 << SGTL5000_PAD_STRENGTH_I2S_DOUT_SHIFT)) |                                       // I2S DOUT drive @ 4.02mA
                      (SGTL5000_PAD_STRENGTH_CTRL_DATA_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_DATA_SHIFT)) |                                     // I2C DATA drive @ 12.05mA.
                      (SGTL5000_PAD_STRENGTH_CTRL_CLK_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_CLK_SHIFT))                                         // I2C CLOCK drive @ 12.05mA.
                  ))
    )return -14;
    if(
        write_i2c(SGTL5000_CLK_CTRL,(
                      (SGTL5000_CLK_CTRL_RATE_MODE_MASK & (0x0 <<  SGTL5000_CLK_CTRL_RATE_MODE_SHIFT)) |                                            // Sample rate mode Fs = SYS_FS
                      (SGTL5000_CLK_CTRL_SYS_FS_MASK & (0x02 << SGTL5000_CLK_CTRL_SYS_FS_SHIFT)) |                                                  // Set SYS_FS (sampling rate depending on mode) @ 48Khz
                      (SGTL5000_CLK_CTRL_MCLK_FREQ_MASK & (0x0 << SGTL5000_CLK_CTRL_MCLK_FREQ_SHIFT))                                               // Set SYS MCLK @ 256*Fs
                  ))
    ) return -15;
    if(
        write_i2c(SGTL5000_I2S_CTRL,(
                      (SGTL5000_I2S_CTRL_SCLKFREQ_MASK & (0x1 << SGTL5000_I2S_CTRL_SCLKFREQ_SHIFT)) |                                               // I2S SCLK 32*Fs
                      (SGTL5000_I2S_CTRL_MS_MASK & (0x0 << SGTL5000_I2S_CTRL_MS_SHIFT)) |                                                           // Slave
                      (SGTL5000_I2S_CTRL_SCLK_INV_MASK & (0x0 << SGTL5000_I2S_CTRL_SCLK_INV_SHIFT)) |                                               // Data on riseing edge
                      (SGTL5000_I2S_CTRL_DLEN_MASK & (0x3 << SGTL5000_I2S_CTRL_DLEN_SHIFT)) |                                                       // Data 16bits
                      (SGTL5000_I2S_CTRL_MODE_MASK & (0x0 << SGTL5000_I2S_CTRL_MODE_SHIFT)) |                                                       // I2S mode
                      (SGTL5000_I2S_CTRL_LRALIGN_MASK & (0x0 << SGTL5000_I2S_CTRL_LRALIGN_SHIFT)) |                                                 // I2S format
                      (SGTL5000_I2S_CTRL_LRPOL_MASK & (0x0 << SGTL5000_I2S_CTRL_LRPOL_SHIFT))                                                       // Left word on LRCLK low
                  ))
    ) return -16;
    if(
        write_i2c(SGTL5000_SSS_CTRL,(
                      (SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_SHIFT)) |                                   // Define all inputs un-swapped
                      (SGTL5000_SSS_CTRL_DAP_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_LRSWAP_SHIFT)) |
                      (SGTL5000_SSS_CTRL_DAC_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAC_LRSWAP_SHIFT)) |
                      (SGTL5000_SSS_CTRL_I2S_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_LRSWAP_SHIFT)) |
                      (SGTL5000_SSS_CTRL_DAP_MIX_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_SELECT_SHIFT)) |                                   // DAP mixer source ADC
                      (SGTL5000_SSS_CTRL_DAP_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_SELECT_SHIFT)) |                                           // DAP Input source ADC
                      (SGTL5000_SSS_CTRL_DAC_SELECT_MASK & (0x1 << SGTL5000_SSS_CTRL_DAC_SELECT_SHIFT)) |                                           // DAC Input source I2SIN
                      (SGTL5000_SSS_CTRL_I2S_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_SELECT_SHIFT))                                             // I2SOUT source ADC
                  ))
    ) return -17;
    if(
        write_i2c(SGTL5000_ADCDAC_CTRL,(
                      (SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_SHIFT)) |                                   // Disable Vol ramp
                      (SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_SHIFT)) |
                      (SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_SHIFT)) |
                      (SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_SHIFT)) |
                      (SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_SHIFT)) |                             // ADC HPF normal operation
                      (SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_SHIFT))                               // ADC HPF enabled
                  ))
    ) return -18;
    if(
        write_i2c(SGTL5000_DAC_VOL,(
                      (SGTL5000_DAC_VOL_DAC_VOL_RIGHT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_RIGHT_SHIFT)) |                                      // DAC R Vol 0dB
                      (SGTL5000_DAC_VOL_DAC_VOL_LEFT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_LEFT_SHIFT))                                          // DAC L Vol 0dB
                  ))
    ) return -19;
    if(
        write_i2c(SGTL5000_ANA_HP_CTRL,(
                      (SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_SHIFT)) |                                // HP Vol set minimum
                      (SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_SHIFT))
                  ))
    ) return -20;
    if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK)) return -21;                                                               // Un-mute Lineout
    if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK)) return -22;                                                               // Un-mute HP
    if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK)) return -23;                                                              // Un-mute ADC pre-amp
    if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -24;                                                     // I2S DOUT enable
    if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -25;                                                      // I2S DIN enable

    if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) SGTL5000::codec1_active = true;
    if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) SGTL5000::codec2_active = true;

    return 0;
}


int32_t SGTL5000::attach_SYNC(Callback<void()> func)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow for SYNC.
    if(SYNC_attached) return -1;                                                                        // Assign Callback function
    SGTL5000::SYNC_user_func = func;
    SGTL5000::SYNC_attach_type = 0;
    SYNC_attached = true;
    return 0;
}


int32_t SGTL5000::attach_SYNC_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    SGTL5000::SYNC_swIRQ = sw_irq;                                                                      // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::SYNC_swIRQ, (uint32_t)user_ISR);
    NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
    SGTL5000::SYNC_attach_type = 1;
    SYNC_attached = true;
    return 0;
}

int32_t SGTL5000::detach_SYNC(void)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(SYNC_attached) {
        if(SGTL5000::SYNC_attach_type) NVIC_DisableIRQ(SGTL5000::SYNC_swIRQ);                           // When using Interrupt driven NB calls, we disable the IRQ. The SYNC channel continues running, a different user function can be assigned with another call to the attach_NB function
        else SGTL5000::stop_SYNC();                                                                     // When using callback we must stop SYNC channel before we detach.
        SYNC_attached = false;
    }
    return 0;
}

void SGTL5000::stream_ctrl_ISR(void)
{
    switch(SGTL5000::ctrl_command) {
        case STOP_SYNC:
            I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX channels to stop stream
            I2S0->RMR = 0xFFFFFFFF;                                                                             // Mask RX channels to stop stream
            while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK;                                                                  // Disable TX FIFO DMA request based on watermark
            I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK;                                                                  // Disable RX FIFO DMA request based on watermark
            if(SGTL5000::codec2_active) {
                I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3);                              // Disable TX channels 0 & 1.
                I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3);                              // Disable RX channels 0 & 1.
            } else {
                I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1);                              // Disable TX channel 0.
                I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1);                              // Disable RX channel 0
            }
            I2S0->TCSR &= ~I2S_TCSR_TE_MASK;                                                                    // TX Disable
            I2S0->RCSR &= ~I2S_RCSR_RE_MASK;                                                                    // RX Disable
            while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK));                          // Ensure TX & RX have stopped.
            SGTL5000::SYNC_run = false;
            break;
        case PAUSE_SYNC:
            I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
            while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->RMR = 0xFFFFFFFF;                                                                             // Mask RX channels to halt stream
            I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX channels to halt stream
            while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK) || !(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK));    // Ensure any active DMA transfers are complete
            break;
        case RESUME_SYNC:
            I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
            while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->RMR = 0x0;                                                                                    // Unmask RX channels to resume stream
            I2S0->TMR = 0x0;                                                                                    // Unmask TX channels to resume stream
            break;
        case STOP_TX:
            I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX channels to halt stream
            while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK;                                                                  // Disable TX FIFO DMA request based on watermark
            if(SGTL5000::codec2_active) I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3);      // Disable TX channel 0 & 1.
            else (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1);                                          // Disable TX channel 0.
            I2S0->TCSR &= ~I2S_TCSR_TE_MASK;                                                                    // TX Disable
            while(I2S0->TCSR & I2S_TCSR_TE_MASK);                                                               // Ensure TX has stopped.
            SGTL5000::TX_run = false;
            break;
        case PAUSE_TX:
            I2S0->TCSR |= I2S_TCSR_WSF_MASK;                                                                    // Clear TX Word Start Flag
            while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX channels to halt stream
            while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            break;
        case RESUME_TX:
            I2S0->TCSR |= I2S_TCSR_WSF_MASK;                                                                    // Clear TX Word Start Flag
            while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->TMR = 0x0;                                                                                    // Clear TX mask to start stream
            break;
        case STOP_RX:
            I2S0->RMR = 0xFFFFFFFF;                                                                             // Mask RX channels to halt stream
            while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK;                                                                  // Disable RX FIFO DMA request based on watermark
            if(SGTL5000::codec2_active) I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3);      // Disable RX channel 0 & 1.
            else (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1);                                          // Disable RX channel 0.
            I2S0->RCSR &= ~I2S_RCSR_RE_MASK;                                                                    // RX Disable
            while(I2S0->RCSR & I2S_RCSR_RE_MASK);                                                               // Ensure RX has stopped.
            SGTL5000::RX_run = false;
            break;
        case PAUSE_RX:
            I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
            while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->RMR = 0xFFFFFFFF;                                                                             // Mask TX channels to halt stream
            while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
            break;
        case RESUME_RX:
            I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
            while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK));                                                           // wait for Frame Start
            I2S0->RMR = 0x0;                                                                                    // Unmask TX channels to resume stream
            break;
    }
    SGTL5000::ctrl_command = 0;
}


int32_t SGTL5000::start_SYNC(uint32_t _BufRX_L_safe, uint32_t _BufRX_R_safe, uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe,
                             uint32_t _block_size, bool _packed_RX, bool _packed_TX, bool _RX_shift, bool _TX_shift, uint32_t _RX_DMAch, uint32_t _TX_DMAch, uint32_t _DMA_irq_pri, uint32_t _BufRX_L_safe2, uint32_t _BufRX_R_safe2, uint32_t _BufTX_L_safe2, uint32_t _BufTX_R_safe2)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(SGTL5000::SYNC_run || SGTL5000::TX_run || SGTL5000::RX_run ) return -1;                                                        // Check if i2s is already started
    if(!SYNC_attached && !SGTL5000::SYNC_attach_type) return -1;                                        // Check we have a handler if using callback
    if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1;                                                     // Sanity check DMAMUX channels
    if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1;                         // Only accept block size 2^n within range.
    // Enable I2S interfaces for active codecs
    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
    }
    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
    }
    if(SGTL5000::codec2_active) {
        if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
            if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
        }
        if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
            if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
        }
    }
    packed_RX = _packed_RX;
    packed_TX = _packed_TX;
    TX_block_size = _block_size;
    RX_block_size = TX_block_size;
    TX_bs_bytes = _block_size * 4;
    RX_bs_bytes = TX_bs_bytes;
    if(packed_RX) RX_shift = false;
    else RX_shift = _RX_shift;
    if(packed_TX) TX_shift = false;
    else TX_shift = _TX_shift;
    SGTL5000::RX_DMAch = _RX_DMAch;
    SGTL5000::TX_DMAch = _TX_DMAch;
    SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch;
    SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch;

    SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe;                                                   // Assign the users pointer addresses codec 1
    SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe;
    SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe;
    SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
    SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];                                    // Precalculate double buffer addresses
    SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)];
    SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
    SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)];
    SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
    SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)];
    SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
    SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)];

    *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB;                                                  // Assign starting addresses to double buffer pointers
    *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_R_safeB;
    *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB;
    *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB;

    if(SGTL5000::codec2_active) {
        SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2;                                            // Assign the users pointer addresses codec 2
        SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2;
        SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2;
        SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2;
        SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0];                              // Precalculate double buffer addresses
        SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)];
        SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0];
        SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)];
        SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
        SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)];
        SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
        SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)];
        *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2;                                            // Assign starting addresses to double buffer pointers
        *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_R_safeB2;
        *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2;
        *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2;
    }

    I2S0->TCSR &= ~I2S_TCSR_TE_MASK;                                                                    // TX Disable
    I2S0->RCSR &= ~I2S_RCSR_RE_MASK;                                                                    // RX Disable
    I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX & RX traffic to prevent buffer under/overflow before we aquire sync.
    I2S0->RMR = 0xFFFFFFFF;
    I2S0->TCSR |= I2S_TCSR_SR_MASK;                                                                     // Reset TX & RX logic
    I2S0->RCSR |= I2S_RCSR_SR_MASK;
    I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
    I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
    I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size);                   // Set TX FIFO watermark
    I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1);                   // Set RX FIFO watermark
    I2S0->TCSR |= I2S_TCSR_FRDE_MASK;                                                                   // Enable DMA request based on TX FIFO watermark
    I2S0->RCSR |= I2S_RCSR_FRDE_MASK;                                                                   // Enable DMA request based on RX FIFO watermark

    while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK));                          // Make sure TX & RX are disabled before enabling corresponding channels

    NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::SYNC_TX_dma_ISR);                     // Set TX_DMAch transfer complete vector
    NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::SYNC_RX_dma_ISR);                     // Set RX_DMAch transfer complete vector
    NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri);                                           // Set irq priorities the same
    NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri);
    NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
    NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);
    if(SGTL5000::codec2_active) {
        I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3);                               // Enable TX channels 0 & 1.
        I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3);                               // Enable RX channels 0 & 1
    } else {
        I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1);                               // Enable TX channel 0.
        I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1);                               // Enable RX channel 0
    }

    NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_WS_ISR);                                 // Set vector for SYNC word start ISR
    NVIC_SetPriority(I2S0_Rx_IRQn, 0);                                                                  // Set priority of SYNC word start ISR
    NVIC_EnableIRQ(I2S0_Rx_IRQn);                                                                       // Enable SYNC word start ISR using RX direction
    I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
    I2S0->RCSR |= I2S_RCSR_WSIE_MASK;                                                                   // Enable I2S RX word start IRQ

    SGTL5000::db_phase_sync = 0;
    SGTL5000::SYNC_run = true;
    init_DMA();
    I2S0->TCSR |= I2S_TCSR_TE_MASK;                                                                     // Start TX
    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // Start RX
    return 0;
}

int32_t SGTL5000::pause_SYNC(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1;                        // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = PAUSE_SYNC;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

int32_t SGTL5000::resume_SYNC(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1;                        // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = RESUME_SYNC;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

int32_t SGTL5000::stop_SYNC(void)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK);                       // codec I2S data lines tristate
    modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK);
    if(SGTL5000::codec2_active) {                                                                       // codec2 I2S data lines tristate
        modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
        modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
    }
    SGTL5000::ctrl_command = STOP_SYNC;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

void SGTL5000::sync_I2S_WS_ISR(void)
{
    I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK;                                                                  // Disable RX word start IRQs
    I2S0->TCSR |= I2S_TCSR_FR_MASK;                                                                     // Reset TX FIFO pointers
    I2S0->RCSR |= I2S_RCSR_FR_MASK;                                                                     // Reset RX FIFO pointers

    for(uint32_t i = 0; i <= ((I2S0->RCR1 & I2S_RCR1_RFW_MASK) >> I2S_RCR1_RFW_SHIFT); ++i) {           // Stuff the TX FIFO(s) with samples, to bring RX & TX FIFO demand requests to within 1 I2S frame length of each other. Hopefully giving max processing time for DSP work
        I2S0->TDR[0] = 0;
    }
    if(SGTL5000::codec2_active) {
        for(uint32_t i = 0; i <= ((I2S0->RCR1 & I2S_RCR1_RFW_MASK) >> I2S_RCR1_RFW_SHIFT); ++i) {
            I2S0->TDR[1] = 0;
        }
    }
    I2S0->TMR = 0x0;                                                                                    // Clear TX & RX Masks to start data flowing
    I2S0->RMR = 0x0;
}

void SGTL5000::SYNC_TX_dma_ISR(void)
{
    SGTL5000::t_stamp_stop();
    register uint32_t ACKS;
    register uint32_t TX_IRQ_BM;
    register uint32_t DMA_INT;
    __asm {
        LDR     TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm]
        LDR     DMA_INT, [&DMA0->INT]
        LDR     ACKS, [&sync_dma_irq_acks]

        ANDS    TX_IRQ_BM, TX_IRQ_BM, DMA_INT
        ITT     NE
        ORRNE   ACKS, #1
        STRNE   TX_IRQ_BM, [&DMA0->INT]

        TEQ     ACKS, #3
        IT      NE
        BNE     finish_isr
        MOV     ACKS, #0
        BL      SGTL5000::SYNC_pointer_swap

        finish_isr:
        STR     ACKS, [&sync_dma_irq_acks]
    }
}

void SGTL5000::SYNC_RX_dma_ISR(void)
{
    SGTL5000::t_stamp_start();
    register uint32_t ACKS;
    register uint32_t RX_IRQ_BM;
    register uint32_t DMA_INT;
    __asm {
        LDR     RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm]
        LDR     DMA_INT, [&DMA0->INT]
        LDR     ACKS, [&sync_dma_irq_acks]

        ANDS    RX_IRQ_BM, RX_IRQ_BM, DMA_INT
        ITT     NE
        ORRNE   ACKS, #2
        STRNE   RX_IRQ_BM, [&DMA0->INT]

        TEQ     ACKS, #3
        IT      NE
        BNE     finish_isr
        MOV     ACKS, #0
        BL      SGTL5000::SYNC_pointer_swap

        finish_isr:
        STR     ACKS, [&sync_dma_irq_acks]
    }
}


void SGTL5000::SYNC_pointer_swap(void)
{
    register uint32_t CODEC2;
    register uint32_t IRQNR;
    register uint32_t DSP_BUSY;
    register uint32_t DB_PHASE;
    register uint32_t TYPE;
    register uint32_t BU_RX_L;
    register uint32_t BU_RX_R;
    register uint32_t BU_TX_L;
    register uint32_t BU_TX_R;
    register uint32_t BU_RX_L2;
    register uint32_t BU_RX_R2;
    register uint32_t BU_TX_L2;
    register uint32_t BU_TX_R2;
    __asm {
        LDR     DB_PHASE, [&SGTL5000::db_phase_sync]
        LDR     TYPE, [&SGTL5000::SYNC_attach_type]
        LDRB    CODEC2, [&SGTL5000::codec2_active]
        TEQ     CODEC2, #0x1
        IT      EQ
        BEQ     dual_codecs

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeB]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeB]
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeB]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeB]
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeA]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeA]
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeA]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeA]
        ADD     DB_PHASE, #0x1

        store:
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]                               // Pipeline memory access
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     DB_PHASE, [&SGTL5000::db_phase_sync]
        B       finish_update


        dual_codecs:
        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base2

        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeB]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeB]
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeB]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeB]
        LDR     BU_RX_L2, [&SGTL5000::BufRX_L_safeB2]
        LDR     BU_RX_R2, [&SGTL5000::BufRX_R_safeB2]
        LDR     BU_TX_L2, [&SGTL5000::BufTX_L_safeB2]
        LDR     BU_TX_R2, [&SGTL5000::BufTX_R_safeB2]
        SUB     DB_PHASE, #0x1
        B       store2

        buf_base2:
        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeA]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeA]
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeA]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeA]
        LDR     BU_RX_L2, [&SGTL5000::BufRX_L_safeA2]
        LDR     BU_RX_R2, [&SGTL5000::BufRX_R_safeA2]
        LDR     BU_TX_L2, [&SGTL5000::BufTX_L_safeA2]
        LDR     BU_TX_R2, [&SGTL5000::BufTX_R_safeA2]
        ADD     DB_PHASE, #0x1

        store2:
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]                               // Pipeline memory access
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     BU_RX_L2, [SGTL5000::BufRX_L_safe2]                             // Pipeline memory access
        STR     BU_RX_R2, [SGTL5000::BufRX_R_safe2]
        STR     BU_TX_L2, [SGTL5000::BufTX_L_safe2]
        STR     BU_TX_R2, [SGTL5000::BufTX_R_safe2]
        STR     DB_PHASE, [&SGTL5000::db_phase_sync]

        finish_update:
        TEQ     TYPE, #1
        IT      EQ
        BEQ     call_sync_nb
    }
    SGTL5000::SYNC_user_func.call();
    __asm {
        B       finish_isr

        call_sync_nb:
        LDRB    IRQNR, [&SGTL5000::SYNC_swIRQ]
        BL      NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
        TEQ     DSP_BUSY, #1
        IT      EQ
        BEQ     finish_isr
        STR     IRQNR, [&NVIC->STIR]                                            // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers!

        finish_isr:

    }
}


int32_t SGTL5000::attach_TX(Callback<void()> func)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow for SYNC.
    if(TX_attached) return -1;                                                                          // Assign Callback function
    SGTL5000::TX_user_func = func;
    SGTL5000::TX_attach_type = 0;
    TX_attached = true;
    return 0;
}


int32_t SGTL5000::attach_TX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    SGTL5000::TX_swIRQ = sw_irq;                                                                        // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::TX_swIRQ, (uint32_t)user_ISR);
    NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::TX_swIRQ);
    SGTL5000::TX_attach_type = 1;
    TX_attached = true;
    return 0;
}


int32_t SGTL5000::detach_TX(void)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(TX_attached) {
        if(SGTL5000::TX_attach_type) NVIC_DisableIRQ(SGTL5000::TX_swIRQ);                               // When using Interrupt driven NB calls, we disable the IRQ. The TX channel continues running, a different user function can be assigned with another call to the attach_NB function
        else SGTL5000::stop_TX();                                                                       // When using callback we must stop TX channel before we detach.
        TX_attached = false;
    }
    return 0;
}

int32_t SGTL5000::start_TX(uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe,
                           uint32_t _block_size, bool _packed_TX, bool _TX_shift, uint32_t _TX_DMAch, uint32_t _DMA_irq_pri, uint32_t _BufTX_L_safe2, uint32_t _BufTX_R_safe2)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(SGTL5000::SYNC_run || SGTL5000::TX_run ) return -1;                                                                  // Check if i2s TX is already started
    if(!TX_attached && !SGTL5000::TX_attach_type) return -1;                                            // Check we have a handler if using callback
    if(_TX_DMAch > 15) return -1;                                                                       // Sanity check DMAMUX channels
    if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1;                         // Only accept block size 2^n within range.
    // Enable I2S TX interfaces for active codecs
    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
    }
    if(SGTL5000::codec2_active) {
        if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
            if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
        }
    }

    packed_TX = _packed_TX;
    TX_block_size = _block_size;
    TX_bs_bytes = _block_size * 4;
    if(packed_TX) TX_shift = false;
    else TX_shift = _TX_shift;
    SGTL5000::TX_DMAch = _TX_DMAch;
    SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch;

    SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe;                                                  // Assign the users pointer addresses codec 1
    SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
    SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];                                    // Precalculate double buffer addresses
    SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)];
    SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
    SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)];

    *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB;                                                  // Assign starting addresses to double buffer pointers
    *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB;

    if(SGTL5000::codec2_active) {
        SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2;                                            // Assign the users pointer addresses codec 2
        SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2;
        SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0];                              // Precalculate double buffer addresses
        SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)];
        SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
        SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)];
        *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2;                                            // Assign starting addresses to double buffer pointers
        *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2;
    }

    I2S0->TCSR &= ~I2S_TCSR_TE_MASK;                                                                    // TX Disable
    I2S0->TMR = 0xFFFFFFFF;                                                                             // Mask TX traffic to prevent buffer underflow before we aquire sync.
    I2S0->TCSR |= I2S_TCSR_SR_MASK;                                                                     // Reset TX logic
    I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
    I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size);                   // Set TX FIFO watermark
    I2S0->TCSR |= I2S_TCSR_FRDE_MASK;                                                                   // Enable DMA request based on TX FIFO watermark

    while(I2S0->TCSR & I2S_TCSR_TE_MASK);                                                               // Make sure TX is disabled before enabling corresponding channel

    NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::TX_dma_ISR);                          // Set TX_DMAch transfer complete vector
    NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri);                                           // Set irq priority
    NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
    if(SGTL5000::codec2_active) {
        I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3);                               // Enable TX channels 0 & 1.
    } else {
        I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1);                               // Enable TX channel 0.
    }

    NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_WS_ISR);                                   // Set vector for TX word start ISR
    NVIC_SetPriority(I2S0_Tx_IRQn, 0);                                                                  // Set priority of TX word start ISR
    NVIC_EnableIRQ(I2S0_Tx_IRQn);                                                                       // Enable TX word start ISR
    I2S0->TCSR |= I2S_TCSR_WSF_MASK;                                                                    // Clear TX Word Start Flag
    I2S0->TCSR |= I2S_TCSR_WSIE_MASK;                                                                   // Enable I2S TX word start IRQ

    SGTL5000::db_phase_tx = 0;
    SGTL5000::TX_run = true;
    init_DMA();
    I2S0->TCSR |= I2S_TCSR_TE_MASK;                                                                     // TX Enable
    return 0;
}

int32_t SGTL5000::pause_TX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1;                                    // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = PAUSE_TX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

int32_t SGTL5000::resume_TX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1;                                    // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = RESUME_TX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}


int32_t SGTL5000::stop_TX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1;                                    // Must use codec1 to control data flow.
    modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK);                        // codec I2S data lines tristate
    if(SGTL5000::codec2_active) modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
    SGTL5000::ctrl_command = STOP_TX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

void SGTL5000::tx_I2S_WS_ISR(void)
{
    I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK;                                                                  // Disable TX word start IRQs
    I2S0->TCSR |= I2S_TCSR_FR_MASK;                                                                     // Reset TX FIFO pointers
    for(uint32_t i = 1; i <= (8 - ((I2S0->TCR1 & I2S_TCR1_TFW_MASK) >> I2S_TCR1_TFW_SHIFT)); ++i) {     // Stuff the TX FIFO(s) with samples. This is to avoid underrun before we DMA some data into the buffers.
        I2S0->TDR[0] = 0;                                                                               // It allows the time between FIFO wm and an empty buffer at the current sample rate before a DMA request is made.
    }                                                                                                   // This can be critical as at 192Ks/s we only have 5nS after starting TX fifo and an underrun, unless we stuff.
    if(SGTL5000::codec2_active) {
        for(uint32_t i = 1; i <= (8 - ((I2S0->TCR1 & I2S_TCR1_TFW_MASK) >> I2S_TCR1_TFW_SHIFT)); ++i) {
            I2S0->TDR[1] = 0;
        }
    }
    I2S0->TMR = 0x0;                                                                                    // Clear TX Mask to start data flowing
}

void SGTL5000::TX_dma_ISR(void)
{
    register uint32_t TX_IRQ_BM;
    register uint32_t DMA_INT;
    register uint32_t CODEC2;
    register uint32_t IRQNR;
    register uint32_t DSP_BUSY;
    register uint32_t DB_PHASE;
    register uint32_t TYPE;
    register uint32_t BU_TX_L;
    register uint32_t BU_TX_R;
    register uint32_t BU_TX_L2;
    register uint32_t BU_TX_R2;
    __asm {
        LDR     TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm]
        LDR     DMA_INT, [&DMA0->INT]

        AND     TX_IRQ_BM, TX_IRQ_BM, DMA_INT                                   // Clear DMA IRQ flag
        STR     TX_IRQ_BM, [&DMA0->INT]

        LDR     DB_PHASE, [&SGTL5000::db_phase_tx]
        LDR     TYPE, [&SGTL5000::TX_attach_type]
        LDRB    CODEC2, [&SGTL5000::codec2_active]
        TEQ     CODEC2, #0x1
        IT      EQ
        BEQ     dual_codecs

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeB]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeB]
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeA]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeA]
        ADD     DB_PHASE, #0x1

        store:
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]                               // Pipeline memory access
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     DB_PHASE, [&SGTL5000::db_phase_tx]
        B       finish_update

        dual_codecs:
        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base2

        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeB]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeB]
        LDR     BU_TX_L2, [&SGTL5000::BufTX_L_safeB2]
        LDR     BU_TX_R2, [&SGTL5000::BufTX_R_safeB2]
        SUB     DB_PHASE, #0x1
        B       store2

        buf_base2:
        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeA]
        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeA]
        LDR     BU_TX_L2, [&SGTL5000::BufTX_L_safeA2]
        LDR     BU_TX_R2, [&SGTL5000::BufTX_R_safeA2]
        ADD     DB_PHASE, #0x1

        store2:
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]                               // Pipeline memory access
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     BU_TX_L2, [SGTL5000::BufTX_L_safe2]
        STR     BU_TX_R2, [SGTL5000::BufTX_R_safe2]
        STR     DB_PHASE, [&SGTL5000::db_phase_tx]

        finish_update:
        TEQ     TYPE, #1
        IT      EQ
        BEQ     call_tx_nb
    }
    SGTL5000::TX_user_func.call();
    __asm {
        B       finish_isr

        call_tx_nb:
        LDRB    IRQNR, [&SGTL5000::TX_swIRQ]
        BL      NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
        TEQ     DSP_BUSY, #1
        IT      EQ
        BEQ     finish_isr
        STR     IRQNR, [&NVIC->STIR]                                                                    // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers!

        finish_isr:

    }
}

int32_t SGTL5000::attach_RX(Callback<void()> func)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow for SYNC.
    if(RX_attached) return -1;                                                                          // Assign Callback function
    SGTL5000::RX_user_func = func;
    SGTL5000::RX_attach_type = 0;
    RX_attached = true;
    return 0;
}

int32_t SGTL5000::attach_RX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    SGTL5000::RX_swIRQ = sw_irq;                                                                        // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::RX_swIRQ, (uint32_t)user_ISR);
    NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::RX_swIRQ);
    SGTL5000::RX_attach_type = 1;
    RX_attached = true;
    return 0;
}


int32_t SGTL5000::detach_RX(void)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(RX_attached) {
        if(SGTL5000::RX_attach_type) NVIC_DisableIRQ(SGTL5000::RX_swIRQ);                               // When using Interrupt driven NB calls, we disable the IRQ. The RX channel continues running, a different user function can be assigned with another call to the attach_NB function
        else SGTL5000::stop_RX();                                                                       // When using callback we must stop RX channel before we detach.
        RX_attached = false;
    }
    return 0;
}

int32_t SGTL5000::start_RX(uint32_t _BufRX_L_safe, uint32_t _BufRX_R_safe,
                           uint32_t _block_size, bool _packed_RX, bool _RX_shift, uint32_t _RX_DMAch, uint32_t _DMA_irq_pri, uint32_t _BufRX_L_safe2, uint32_t _BufRX_R_safe2)
{
    if(!codec_configured || !this->ctrl_codec) return -1;                                               // Must use codec1 to control data flow.
    if(SGTL5000::SYNC_run || SGTL5000::RX_run ) return -1;                                                                  // Check if i2s RX is already started
    if(!RX_attached && !SGTL5000::RX_attach_type) return -1;                                            // Check we have a handler if using callback
    if(_RX_DMAch > 15) return -1;                                                                       // Sanity check DMAMUX channels
    if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1;                         // Only accept block size 2^n within range.
    // Enable I2S RX interfaces for active codecs
    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
    }
    if(SGTL5000::codec2_active) {
        if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
            if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
        }
    }
    packed_RX = _packed_RX;
    RX_block_size = _block_size;
    RX_bs_bytes = _block_size * 4;
    if(packed_RX) RX_shift = false;
    else RX_shift = _RX_shift;
    SGTL5000::RX_DMAch = _RX_DMAch;
    SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch;

    SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe;                                                  // Assign the users pointer addresses codec 1
    SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe;
    SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];                                    // Precalculate double buffer addresses
    SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)];
    SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
    SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)];

    *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB;                                                  // Assign starting addresses to double buffer pointers
    *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_L_safeB;

    if(SGTL5000::codec2_active) {
        SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2;                                            // Assign the users pointer addresses codec 2
        SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2;
        SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0];                              // Precalculate double buffer addresses
        SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)];
        SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
        SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)];
        *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2;                                            // Assign starting addresses to double buffer pointers
        *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_L_safeB2;
    }

    I2S0->RCSR &= ~I2S_RCSR_RE_MASK;                                                                    // TX Disable
    I2S0->RMR = 0xFFFFFFFF;                                                                             // Mask RX traffic to prevent buffer underflow before we aquire sync.
    I2S0->RCSR |= I2S_RCSR_SR_MASK;                                                                     // Reset RX logic
    I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
    I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1);                   // Set RX FIFO watermark
    I2S0->RCSR |= I2S_RCSR_FRDE_MASK;                                                                   // Enable DMA request based on RX FIFO watermark

    while(I2S0->RCSR & I2S_RCSR_RE_MASK);                                                               // Make sure RX is disabled before enabling corresponding channel

    NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::RX_dma_ISR);                          // Set RX_DMAch transfer complete vector
    NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri);                                           // Set irq priority
    NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);                                                           // Enable RX DMA complete IRQs
    if(SGTL5000::codec2_active) {
        I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3);                               // Enable RX channels 0 & 1.
    } else {
        I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1);                               // Enable RX channel 0.
    }

    NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_WS_ISR);                                   // Set vector for RX word start ISR
    NVIC_SetPriority(I2S0_Rx_IRQn, 0);                                                                  // Set priority of RX word start ISR
    NVIC_EnableIRQ(I2S0_Rx_IRQn);                                                                       // Enable RX word start ISR
    I2S0->RCSR |= I2S_RCSR_WSF_MASK;                                                                    // Clear RX Word Start Flag
    I2S0->RCSR |= I2S_RCSR_WSIE_MASK;                                                                   // Enable I2S RX word start IRQ

    SGTL5000::db_phase_rx = 0;
    SGTL5000::RX_run = true;
    init_DMA();
    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // RX Enable
    return 0;
}

int32_t SGTL5000::pause_RX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1;                          // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = PAUSE_RX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

int32_t SGTL5000::resume_RX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1;                          // Must use codec1 to control data flow.
    SGTL5000::ctrl_command = RESUME_RX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}

int32_t SGTL5000::stop_RX(void)
{
    if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1;                                  // Must use codec1 to control data flow.
    if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;                 // codec I2S data lines tristate
    if(SGTL5000::codec2_active) if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
    SGTL5000::ctrl_command = STOP_RX;
    NVIC->STIR = CODEC_CTRL_IRQ;
    while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
    return 0;
}


void SGTL5000::rx_I2S_WS_ISR(void)
{
    I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK;                                                                  // Disable RX word start IRQs
    I2S0->RCSR |= I2S_RCSR_FR_MASK;                                                                     // Reset RX FIFO pointers
    I2S0->RMR = 0x0;                                                                                    // Clear RX Mask to start data flowing
}

void SGTL5000::RX_dma_ISR(void)
{
    register uint32_t RX_IRQ_BM;
    register uint32_t DMA_INT;
    register uint32_t CODEC2;
    register uint32_t IRQNR;
    register uint32_t DSP_BUSY;
    register uint32_t DB_PHASE;
    register uint32_t TYPE;
    register uint32_t BU_RX_L;
    register uint32_t BU_RX_R;
    register uint32_t BU_RX_L2;
    register uint32_t BU_RX_R2;
    __asm {
        LDR     RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm]
        LDR     DMA_INT, [&DMA0->INT]

        AND     RX_IRQ_BM, RX_IRQ_BM, DMA_INT                                   // Clear DMA IRQ flag
        STR     RX_IRQ_BM, [&DMA0->INT]

        LDR     DB_PHASE, [&SGTL5000::db_phase_rx]
        LDR     TYPE, [&SGTL5000::RX_attach_type]
        LDRB    CODEC2, [&SGTL5000::codec2_active]
        TEQ     CODEC2, #0x1
        IT      EQ
        BEQ     dual_codecs

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeB]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeB]
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeA]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeA]
        ADD     DB_PHASE, #0x1

        store:
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]                               // Pipeline memory access
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     DB_PHASE, [&SGTL5000::db_phase_rx]
        B       finish_update

        dual_codecs:
        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base2

        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeB]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeB]
        LDR     BU_RX_L2, [&SGTL5000::BufRX_L_safeB2]
        LDR     BU_RX_R2, [&SGTL5000::BufRX_R_safeB2]
        SUB     DB_PHASE, #0x1
        B       store2

        buf_base2:
        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeA]
        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeA]
        LDR     BU_RX_L2, [&SGTL5000::BufRX_L_safeA2]
        LDR     BU_RX_R2, [&SGTL5000::BufRX_R_safeA2]
        ADD     DB_PHASE, #0x1

        store2:
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]                               // Pipeline memory access
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     BU_RX_L2, [SGTL5000::BufRX_L_safe2]
        STR     BU_RX_R2, [SGTL5000::BufRX_R_safe2]
        STR     DB_PHASE, [&SGTL5000::db_phase_rx]

        finish_update:
        TEQ     TYPE, #1
        IT      EQ
        BEQ     call_rx_nb
    }
    SGTL5000::RX_user_func.call();
    __asm {
        B       finish_isr

        call_rx_nb:
        LDRB    IRQNR, [&SGTL5000::RX_swIRQ]
        BL      NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
        TEQ     DSP_BUSY, #1
        IT      EQ
        BEQ     finish_isr
        STR     IRQNR, [&NVIC->STIR]                                            // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers!

        finish_isr:

    }
}

void SGTL5000::init_DMA(void)
{
    /*!
    The DMA transfer uses 2 channels. 1 for RX data 1 for TX data. Each DMA splits the Left and Right channels across the respective buffer.
    The chain is configured as follows:
    Each minor loop moves 4 bytes of data (2 * 16bit words, 1 left 1 right channel). Bus reads/writes are 16bit and DOFF is used to jump forward BLOCK_SIZE/2 32bit words on each bus read or write respectively.
    At the end of a minor loop MLOFF jumps back (BLOCK_SIZE - 1) 32bit words or packed (BLOCK_SIZE - 0.5) words. At Major loop termination the source or dest address is reloaded with a new TCD which points to the source or dest respectively at the
    uppper half of the double buffer.
    eg.
    Unpacked buffer layout:
    Block Size = 2                                                            Block Size = 4                                                                Block Size = 8
    Double Buffer A                     Double Buffer B                       Double Buffer A                       Double Buffer B                         Double Buffer A                          Double Buffer B
    |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x|    |L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x||L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x|    |L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x||L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x|

    Packed buffer layout:
    Block Size = 2                                                            Block Size = 4                                                                Block Size = 8
    Double Buffer A                     Double Buffer B                       Double Buffer A                       Double Buffer B                         Double Buffer A                          Double Buffer B
    |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x|    |L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x||L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x|    |L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x||L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x|

    The users pointers are always updated to point to L0 & R0 of the current safe double buffer area.

    If unpacked and TX or RX shift option is specified the 16 LSBits of each word are shifted to the 16MSB bits of each word

    */

    static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x14000500)));                                                                          // Allocate memory for scatter gather TCD definitions
    static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x14000520)));
    static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x14000540)));
    static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x14000560)));
    static uint32_t SG_rx_TCD_A2[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x14000580)));                                                                          // Allocate memory for scatter gather TCD2 definitions
    static uint32_t SG_rx_TCD_B2[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x140005A0)));
    static uint32_t SG_tx_TCD_A2[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x140005C0)));
    static uint32_t SG_tx_TCD_B2[8] __attribute ((aligned (0x20)));//  __attribute__((at(0x140005E0)));

    // Clock Control config to DMA Controller and DMAMux and common DMA Setup
    SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;                                                                                                    // Enable Clocking to DMAMUX Module
    SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;                                                                                                       // Enable Clocking to DMA Module
    DMA0->CR |= DMA_CR_EMLM_MASK;                                                                                                           // Enable minor loop mapping

    if(SGTL5000::SYNC_run || SGTL5000::RX_run) {
        // DMAMUX Config
        DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK;                                                                      // MUX channels disabled while we configure
        DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK;                                                                      // Trigger Modes Normal
        DMAMUX->CHCFG[SGTL5000::RX_DMAch] = (DMAMUX->CHCFG[SGTL5000::RX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(14);     // Route I2S FIFO RX request to DMA. i2s0 RX =14 TX = 15
        DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::RX_DMAch);                                                                                     // Disable requests for chosen DMA channel first while its configured
        //if(SGTL5000::codec2_active) DMA0->CERQ = DMA_CERQ_CERQ(RX_DMAch2);

        //Configure RX DMA Transfer Control Descriptor for channel 0
        DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK;                                                                                                   // Disable source offset on minor loop
        DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK;                                                                                                    // Enable dest offset on minor loop
        if(packed_RX) DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 2));  // After each minor loop step back to lower half of buffer and increment
        else DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
        DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_NBYTES_MASK) | DMA_NBYTES_MLOFFYES_NBYTES(4);                 // 4 Bytes (2 * 16bits words one left one right channel) transferred in each minor cycle.
        DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DONE_MASK;                                                                                                                            // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
        DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0);                                                                       // Disable Bandwidth control
        DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_ESG_MASK;                                                                                                                              // Enable scatter gather
        DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK;                                                                                                                            // Disable auto reset of request enable on major
        DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK;                                                                                                                         // Disable IRQ at Half Completion of Major cycle
        DMA0->TCD[SGTL5000::RX_DMAch].ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);                                                                   // Set data transfer size @ 16bits per memory access across the memory bus
        DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0;                                                                                                                                             // Signed Source offset set to zero (always read from RDR[0]).
        DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2);                                                                                                                        // After each write step into upper half of the buffer
        DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
        DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(RX_block_size / 2); // Major loop current iter count starting value
        DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
        DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(RX_block_size / 2); // Major loop iter count to load again at after major completes
        DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_MAJORLINKCH_MASK) | DMA_CSR_MAJORLINKCH(SGTL5000::RX_DMAch);                                                       // Major link to second channel
        DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;

        if(SGTL5000::codec2_active) {
            DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;                                                                                                                    // Disable IRQ at Completion of Major cycle irq generated by major complete on second channel
            DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;                                                                                                                   // Enable major loop linking
            DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A2[0];     // Set scatter gather TCD definition location to transfer channel 2

        } else {
            DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                     // Enable IRQ at Completion of Major cycle for first channel
            DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                   // Disable major loop linking
            DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0];     // Set scatter gather TCD definition location
        }
        if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
        DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0];                                                                                                                      // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
        memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);                                                                                                                 // Copy TCD A to memory
        DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
        if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0];
        DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1];
        DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                     // Enable IRQ at Completion of Major cycle for first channel
        DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
        memcpy(&SG_rx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);                                                                                                                 // Copy TCD B to memory
        // Set TCD elements in the DMA controller back to initial TCD A
        if(SGTL5000::codec2_active) {
            DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;                                                                                                                    // Disable IRQ at Completion of Major cycle irq generated by major complete on second channel
            DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;                                                                                                                   // Enable major loop linking
            DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B2[0];     // Set scatter gather TCD definition location to transfer channel 2

        } else {
            DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                     // Enable IRQ at Completion of Major cycle for first channel
            DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                   // Disable major loop linking
            DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0];     // Set scatter gather TCD definition location
        }                                                                                                                // Set scatter gather TCD definition location
        if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
        DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0];
        memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);
        DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                     // Enable IRQ at Completion of Major cycle for first channel
        DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
        if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
        DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1];
        DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0];
        memcpy(&SG_rx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);

        memcpy((void*)&DMA0->TCD[SGTL5000::RX_DMAch], &SG_rx_TCD_A[0], 32);            // Initialise RX TCD to first SG definition
    }

    if(SGTL5000::SYNC_run || SGTL5000::TX_run) {
        DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK;
        DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK;
        DMAMUX->CHCFG[SGTL5000::TX_DMAch] = (DMAMUX->CHCFG[SGTL5000::TX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(15);  // Route I2S FIFO TX request to DMA.
        DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch);                                                           // Disable requests for first DMA channel while its configured.

        //Configure TX DMA Transfer Control Descriptor channel 1
        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK;                                                                                                    // Enable source offset on minor loop
        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK;                                                                                                   // Disable dest offset on minor loop
        if(packed_TX) DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(TX_bs_bytes - 2));
        else DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(TX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_NBYTES_MASK) | DMA_NBYTES_MLOFFYES_NBYTES(4);                 // 4 Bytes (2 * 16bits words one left one right channel) transferred in each minor cycle.
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DONE_MASK;                                                                                                                            // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
        DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0);                                                                       // Disable Bandwidth control
        DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_ESG_MASK;                                                                                                                              // Enable scatter gather
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK;                                                                                                                            // Disable auto reset of request enable on major
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK;
        DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_MAJORLINKCH_MASK) | DMA_CSR_MAJORLINKCH(SGTL5000::TX_DMAch);
        DMA0->TCD[SGTL5000::TX_DMAch].ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);                                                                   // Set data transfer size @ 16bits per memory access across the memory bus
        DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0;                                                                                                                                             // Signed Dest offset set to zero (always write TDR[0]).
        DMA0->TCD[SGTL5000::TX_DMAch].SOFF = (TX_bs_bytes / 2);                                                                                                                             // After each write step into upper half of the buffer
        DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
        DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(TX_block_size / 2); // Major loop current iter count starting value
        DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
        DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(TX_block_size / 2); // Major loop iter count to load again at after major completes                                                                                         // Reset dest addr to start address.
        if(SGTL5000::codec2_active) {                                                                                                                        // Disable IRQ at Half Completion of Major cycle
            DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A2[0];                                                                                                                      // Enable IRQ at Completion of Major cycle for first channel
        } else {
            DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];
        }
        if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
        DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];
        memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);                                                                                                                 // Copy TCD A to memory
        DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
        if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0];
        DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1];
        DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
        memcpy(&SG_tx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);                                                                                                                 // Copy TCD B to memory
        if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
        DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];
        if(SGTL5000::codec2_active) {                                                                                                                        // Disable IRQ at Half Completion of Major cycle
            DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B2[0];                                                                                                                     // Enable IRQ at Completion of Major cycle for first channel
        } else {
            DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
            DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0];
        }
        memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);
        DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0];
        if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
        DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1];
        DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
        memcpy(&SG_tx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);

        memcpy((void*)&DMA0->TCD[SGTL5000::TX_DMAch], &SG_tx_TCD_A[0], 32);            // Initialise TX TCD to first SG definition
    }

    if(SGTL5000::SYNC_run || SGTL5000::RX_run) {
        DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch);                                                                                                                                    // Enable requests for RX DMA channel 0
        DMAMUX->CHCFG[SGTL5000::RX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK;                                                                                                                       // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
    }
    if(SGTL5000::SYNC_run || SGTL5000::TX_run) {
        DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::TX_DMAch);                                                                                                                                    // Enable requests for TX DMA channel
        DMAMUX->CHCFG[SGTL5000::TX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK;                                                                                                                      // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
    }
}

int32_t SGTL5000::read_debug(uint32_t index, bool finished)
{
    if(finished) SGTL5000::debug_read = true;
    return SGTL5000::debug[index];
};

// Time stamp functions

void SGTL5000::t_stamp_start(void)
{
    SGTL5000::t1 = *(uint32_t*)SGTL5000::SYST_CVAL;
}

void SGTL5000::t_stamp_stop(void)
{
    SGTL5000::proc_time = SGTL5000::t1 - *(uint32_t*)SGTL5000::SYST_CVAL;
}

}