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-07-01
Revision:
6:4ab5aaeaa064
Parent:
5:664802e89661
Child:
7:d65476c153a4

File content as of revision 6:4ab5aaeaa064:

/*!
@ 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
uint32_t *SGTL5000::BufRX_L_safe;                                               // Private pointers assigned to users data pointers
uint32_t *SGTL5000::BufRX_R_safe;                                               // These are used to flip user pointers between safe 'active' regions of
uint32_t *SGTL5000::BufTX_L_safe;                                               // double buffered space.
uint32_t *SGTL5000::BufTX_R_safe;
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;
uint32_t SGTL5000::RX_DMAch;                                                    // User defined RX DMA channel number
uint32_t SGTL5000::TX_DMAch;                                                    // User defined TX DMA channel number
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::debug[16] = {0};

SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int i2c_freq, bool i2c_ctrl_adr0_cs)
    : mI2C(i2c_sda, i2c_scl)
{
    if(i2c_ctrl_adr0_cs) i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1;
    else i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
    mI2C.frequency(i2c_freq);
    init_i2s();
    init_codec();
    SYNC_run = false;
    TX_run = false;
    RX_run = false;
    SYNC_attached = false;
    TX_attached = false;
    RX_attached = false;
}

int32_t SGTL5000::freq(uint32_t rate)
{
    uint32_t I2S_MCLK_M;
    uint32_t I2S_MCLK_D;
    uint32_t codec_SYS_FS;
    uint32_t codec_RATE_MODE;
    switch(rate) {
        case 8:
            I2S_MCLK_M = 16;
            I2S_MCLK_D = 750;
            codec_SYS_FS = 0x02;
            codec_RATE_MODE = 0x01;
            break;
        case 11:
            I2S_MCLK_M = 1;
            I2S_MCLK_D = 34;
            codec_SYS_FS = 0x01;
            codec_RATE_MODE = 0x01;
            break;
        case 12:
            I2S_MCLK_M = 16;
            I2S_MCLK_D = 500;
            codec_SYS_FS = 0x02;
            codec_RATE_MODE = 0x01;
            break;
        case 16:
            I2S_MCLK_M = 32;
            I2S_MCLK_D = 750;
            codec_SYS_FS = 0x00;
            codec_RATE_MODE = 0x01;
            break;
        case 22:
            I2S_MCLK_M = 1;
            I2S_MCLK_D = 17;
            codec_SYS_FS = 0x01;
            codec_RATE_MODE = 0x01;
            break;
        case 24:
            I2S_MCLK_M = 32;
            I2S_MCLK_D = 500;
            codec_SYS_FS = 0x02;
            codec_RATE_MODE = 0x01;
            break;
        case 32:
            I2S_MCLK_M = 64;
            I2S_MCLK_D = 750;
            codec_SYS_FS = 0x00;
            codec_RATE_MODE = 0x0;
            break;
        case 44:
            I2S_MCLK_M = 2;
            I2S_MCLK_D = 17;
            codec_SYS_FS = 0x01;
            codec_RATE_MODE = 0x0;
            break;
        case 48:
            I2S_MCLK_M = 32;
            I2S_MCLK_D = 250;
            codec_SYS_FS = 0x02;
            codec_RATE_MODE = 0x0;
            break;
        case 96:
            I2S_MCLK_M = 32;
            I2S_MCLK_D = 125;
            codec_SYS_FS = 0x03;
            codec_RATE_MODE = 0x0;
            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:                                                               // 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;
        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
    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)
{
    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;
    if(mI2C.write(i2c_addr, (char *)&wire_addr, 2, false)) return -1;
    if(mI2C.read(i2c_addr, (char *)&wire_data, 2, false)) return -1;
    return ((__rev(wire_data) >> 16) & mask) >> shift;
}

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

int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask)
{
    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)));
    if(mI2C.write(i2c_addr, (char *)&wire_data, 4, false)) return -1;
    return 0;
}


void SGTL5000::init_i2s(void)
{
    //const int F_CPU = mcgpllfll_frequency();                                          // Get system Pll freq and Calculate. ** To Do. If not Teensy 3.1 or 3.2 **
    const uint32_t I2S_MCLK_MULT = 32;                                                  // 32 = 48Khz    2  = 44.1Khz
    const uint32_t I2S_MCLK_DIV = 250;                                                  // 250 = 48Khz    17 = 44.1Khz

    // 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

    // 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

}

void SGTL5000::init_codec(void)
{
    // Default Configure Codec
    wait(0.2);// Let codec run start-up ramp ?

    write_i2c(SGTL5000_ANA_POWER,   (   (SGTL5000_ANA_POWER_DAC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_DAC_MONO_SHIFT)) |                                           // Select stereo mode for DAC
                                        (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x1 << 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. 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_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
                                    ));
    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
                                   ));
    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
                                        ));
    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)
                                        ));
    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
                                        ));
    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
                                        ));
    modify_i2c                              (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK);                                                   // Power up Headphone amp
    modify_i2c                              (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK);                                                         // Power up DAC
    modify_i2c                              (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK);                                           // Power up capless HP block
    modify_i2c                              (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK);                                                         // Power up ADC
    modify_i2c                              (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK);                                                     // Power up Lineout Amp

    modify_i2c                              (SGTL5000_ANA_POWER, 0x0, SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK);                                               // Turn of simple regulator now the blocks are powered-up

    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))
                                        ));
    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
                                        ));
    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.
                                        ));
    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
                                        ));
    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
                                        ));
    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
                                        ));
    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
                                        ));
    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
                                        ));
    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))
                                        ));
    modify_i2c                              (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK);                                                               // Un-mute Lineout
    modify_i2c                              (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK);                                                               // Un-mute HP
    modify_i2c                              (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK);                                                              // Un-mute ADC pre-amp
    modify_i2c                              (SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK);                                                     // I2S DOUT enable
    modify_i2c                              (SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK);                                                      // I2S DIN enable
}


int32_t SGTL5000::attach_SYNC(Callback<void()> func)
{
    if(SYNC_attached) return -1;                                                                        // Assign Callback function
    SGTL5000::SYNC_user_func = func;
    SYNC_attach_type = 0;
    SYNC_attached = true;
    return 0;
}

void SGTL5000::attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    SGTL5000::SYNC_swIRQ = sw_irq;                                                                      // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::SYNC_swIRQ, user_ISR);
    NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
    SYNC_attach_type = 1;
    SYNC_attached = true;
}

void SGTL5000::detach_SYNC(void)
{
    stop_SYNC();
    SYNC_attached = false;
}

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)
{
    if(!SYNC_attached && !SYNC_attach_type) return -1;                                        // Check we have a handler if using callback
    if(SYNC_run || TX_run || RX_run ) return -1;                                                        // Check if i2s is already started
    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.
    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;
    SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe;                                                   // Assign the users pointer addresses
    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_safe = (uint32_t)&I2S_RX_Buffer[8];
    *SGTL5000::BufRX_R_safe = (uint32_t)&I2S_RX_Buffer[8 + (RX_block_size / 2)];
    *SGTL5000::BufTX_L_safe = (uint32_t)&I2S_TX_Buffer[8];
    *SGTL5000::BufTX_R_safe = (uint32_t)&I2S_TX_Buffer[8 + (TX_block_size / 2)];

    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;
    SYNC_run = true;
    init_DMA();

    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_TE_MASK;                                                                     // TX enable
    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // RX enable
    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 channels 0
    //SGTL5000::_db_sync_phase = 0;
    if(SYNC_attach_type) {
        NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR_NB);                        // Set DMA TX handler vector
        NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR_NB);                        // Set DMA RX handler vector
    } else {
        NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR);                        // Set DMA TX handler vector
        NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR);                        // Set DMA RX handler vector
    }
    NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, DMA_irq_pri);                                            // Set irq priorities the same
    NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, DMA_irq_pri);
    if(SGTL5000::TX_DMAch > SGTL5000::RX_DMAch) {
        NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);                                                       // Enable IRQ for chosen RX DMA channel if using lower priority DMA channel (lower channel number). This assumes fixed priority encoding and pre-emption, (which are default).
        NVIC_DisableIRQ((IRQn)SGTL5000::TX_DMAch);                                                      // Disable IRQ for chosen TX DMA channel. We must wait for the lowest priority channel (DMA priority) to trigger the DMA IRQ.
    } else {
        NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
        NVIC_DisableIRQ((IRQn)SGTL5000::RX_DMAch);
    }
    NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_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 TX Word Start Flag
    I2S0->RCSR |= I2S_RCSR_WSIE_MASK;                                                                   // Enable I2S TX word start IRQ
    return 0;
}

void SGTL5000::stop_SYNC(void)
{
    I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK;                                                                  // Disable TX FIFO IRQs based on watermark
    I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK;                                                                  // Disable RX FIFO IRQs based on watermark
    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(0);                                   // Disable TX channels.
    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(0);                                   // Disable RX channels.
    SYNC_run = false;
}

void SGTL5000::rx_I2S_ISR(void)
{
    I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK;                                                                  // Disable RX word start IRQs
    I2S0->RCSR |= I2S_RCSR_FR_MASK;                                                                     // Reset RX FIFO pointers
    I2S0->RCSR |= I2S_RCSR_FRDE_MASK;                                                                   // Enable DMA request based on RX FIFO watermark
}

void SGTL5000::tx_I2S_ISR(void)
{
    I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK;                                                                  // Disable TX word start IRQs
    I2S0->TCSR |= I2S_TCSR_FR_MASK;                                                                     // Reset TX FIFO pointers
    I2S0->TCSR |= I2S_TCSR_FRDE_MASK;                                                                   // Enable DMA request based on TX FIFO watermark
}

void SGTL5000::sync_I2S_ISR(void)
{
    I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK;                                                                  // Disable TX word start IRQs
    I2S0->TCSR |= I2S_TCSR_FR_MASK;                                                                     // Reset TX FIFO pointers
    I2S0->RCSR |= I2S_RCSR_FR_MASK;                                                                     // Reset RX FIFO pointers
    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
}

void SGTL5000::sync_dma_ISR_NB(void)
{
    /*!
    Refer to the DMA_init function for details of buffer layouts.

    When running both TX & RX synchronously, only 1 direction has its IRQ enabled in the NVIC, which is enabled is determined by the priority of the DMA channels.
    This assumes strict prioriy ordering of DMA channels. In sync mode TX & RX DMAs transfer the same number of bytes to the FIFO, therefore we should see only 1 FIFO word
    difference between DMA demands for TX or RX.
    The DMA transfers will be pre-empting each other, dependant on relative priority.
    Therefore before the user ISR is called we must be sure that the lowest priority channel has completed its transfer and it is this channel that has its IRQ enabled.
    We clear both flags here to avoid checking which is active as the active IRQ is chosen in the start_SYNC function (saves a couple of cycles).
    Activating only one of the IRQs avoids servicing an extra IRQ stack operation and dealing with pending IRQs.
    */
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);                                                 // Clear RX DMA IRQ flag
    DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);                                                 // Clear TX DMA IRQ flag

    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 DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]
        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]                       // Pipeline memory access
        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        LDR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        LDR     BU_TX_R, [SGTL5000::BufTX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_RX_L, #32
        ADD     BU_RX_R, #32
        ADD     BU_TX_L, #32
        ADD     BU_TX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_RX_L, #32
        SUB     BU_RX_R, #32
        SUB     BU_TX_L, #32
        SUB     BU_TX_R, #32
        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, [&db_phase]
    }

    if(!NVIC_GetActive(SGTL5000::SYNC_swIRQ)) NVIC->STIR = SGTL5000::SYNC_swIRQ;    // Trigger swIRQ or call Callback
}

void SGTL5000::sync_dma_ISR(void)
{
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);                             // Clear RX DMA IRQ flag
    DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);                             // Clear TX DMA IRQ flag

    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 DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]
        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]                               // Pipeline memory access
        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        LDR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        LDR     BU_TX_R, [SGTL5000::BufTX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_RX_L, #32
        ADD     BU_RX_R, #32
        ADD     BU_TX_L, #32
        ADD     BU_TX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_RX_L, #32
        SUB     BU_RX_R, #32
        SUB     BU_TX_L, #32
        SUB     BU_TX_R, #32
        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, [&db_phase]
    }

    SGTL5000::SYNC_user_func.call();                                             // Callback user function

}



int32_t SGTL5000::attach_TX(Callback<void()> func)
{
    if(TX_attached) return -1;                                                                          // Assign Callback function
    SGTL5000::TX_user_func = func;
    TX_attach_type = 0;
    TX_attached = true;
    return 0;
}

void SGTL5000::attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    SGTL5000::TX_swIRQ = sw_irq;                                                                        // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::TX_swIRQ, user_ISR);
    NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::TX_swIRQ);
    TX_attach_type = 1;
    TX_attached = true;
}

void SGTL5000::detach_TX(void)
{
    SGTL5000::stop_TX();
    TX_attached = false;
}

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)
{
    if(!TX_attached && !TX_attach_type) return -1;                                                      // Check we have a handler if using callback
    if(SYNC_run || TX_run) return -1;                                                                   // Check if i2s is already started on tx
    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.
    TX_block_size = block_size;
    TX_bs_bytes = block_size * 4;
    SGTL5000::BufTX_L_safe = (uint32_t*)BufTX_L_safe;                                                   // Assign the users pointer addresses
    SGTL5000::BufTX_R_safe = (uint32_t*)BufTX_R_safe;
    *SGTL5000::BufTX_L_safe = (uint32_t)&I2S_TX_Buffer[8];
    *SGTL5000::BufTX_R_safe = (uint32_t)&I2S_TX_Buffer[8 + (TX_block_size / 2)];
    packed_TX = _packed_TX;
    if(packed_TX) TX_shift = false;
    else TX_shift = _TX_shift;
    SGTL5000::TX_DMAch = _TX_DMAch;
    TX_run = true;
    init_DMA();

    I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size);                   // Set TX FIFO watermark
    I2S0->TCSR |= I2S_TCSR_TE_MASK;                                                                     // TX enable
    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1);                                   // Enable TX channel 0.
    if(TX_attach_type) NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::tx_dma_ISR_NB);    // Set DMA TX handler vector
    else NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::tx_dma_ISR);
    NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, DMA_irq_pri);                                            // Set irq priorities the same
    NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);                                                           // Enable IRQ for chosen TX DMA channel
    NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_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
    return 0;
}

void SGTL5000::stop_TX(void)
{

    I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK;                                                                  // Disable TX FIFO IRQs based on watermark
    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(0);                                   // Disable TX channels.
    TX_run = false;
}

void SGTL5000::tx_dma_ISR_NB(void)
{
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);

    register uint32_t BU_TX_L;
    register uint32_t BU_TX_R;
    register uint32_t DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]                                           // Pipeline memory access
        LDR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        LDR     BU_TX_R, [SGTL5000::BufTX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_TX_L, #32
        ADD     BU_TX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_TX_L, #32
        SUB     BU_TX_R, #32
        ADD     DB_PHASE, #0x1

        store:                                                                  // Pipeline memory access
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     DB_PHASE, [&db_phase]
    }
    if(!NVIC_GetActive(SGTL5000::TX_swIRQ)) NVIC->STIR = SGTL5000::TX_swIRQ;// Trigger swIRQ if not still processing
}

void SGTL5000::tx_dma_ISR(void)
{
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);

    register uint32_t BU_TX_L;
    register uint32_t BU_TX_R;
    register uint32_t DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]                                           // Pipeline memory access
        LDR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        LDR     BU_TX_R, [SGTL5000::BufTX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_TX_L, #32
        ADD     BU_TX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_TX_L, #32
        SUB     BU_TX_R, #32
        ADD     DB_PHASE, #0x1

        store:                                                                  // Pipeline memory access
        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
        STR     DB_PHASE, [&db_phase]
    }
    SGTL5000::TX_user_func.call();                                              // Callback user function
}

int32_t SGTL5000::attach_RX(Callback<void()> func)
{
    if(RX_attached) return -1;                                                                          // Assign Callback function
    SGTL5000::RX_user_func = func;
    RX_attach_type = 0;
    RX_attached = true;
    return 0;
}

void SGTL5000::attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
{
    SGTL5000::RX_swIRQ = sw_irq;                                                                        // Assign ISR address and enable users IRQ
    NVIC_SetVector(SGTL5000::RX_swIRQ, user_ISR);
    NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri);
    NVIC_EnableIRQ(SGTL5000::RX_swIRQ);
    RX_attach_type = 1;
    RX_attached = true;
}

void SGTL5000::detach_RX(void)
{
    SGTL5000::stop_RX();
    RX_attached = false;
}

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)
{
    if(!RX_attached && !RX_attach_type) return -1;                                                      // Check we have a handler if using callback
    if(SYNC_run || RX_run) return -1;                                                                   // Check if i2s is already started on rx
    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.
    RX_block_size = block_size;
    RX_bs_bytes = block_size * 4;
    SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe;                                                   // Assign the users pointer addresses
    SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
    *SGTL5000::BufRX_L_safe = (uint32_t)&I2S_RX_Buffer[8];
    *SGTL5000::BufRX_R_safe = (uint32_t)&I2S_RX_Buffer[8 + (RX_block_size / 2)];
    packed_RX = _packed_RX;
    if(packed_RX) RX_shift = false;
    else RX_shift = _RX_shift;
    SGTL5000::RX_DMAch = _RX_DMAch;
    RX_run = true;
    init_DMA();

    I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1);                   // Set RX FIFO watermark
    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // RX enable
    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1);                                   // Enable RX channel 0.
    if(RX_attach_type) NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::rx_dma_ISR_NB);    // Set DMA RX handler vector
    else NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::rx_dma_ISR);
    NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, DMA_irq_pri);                                            // Set irq priorities the same
    NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);                                                           // Enable IRQ for chosen RX DMA channel
    NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_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
    return 0;
}

void SGTL5000::stop_RX(void)
{

    I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK;                                                                  // Disable RX FIFO IRQs based on watermark
    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(0);                                   // Disable RX channels.
    RX_run = false;
}

void SGTL5000::rx_dma_ISR_NB(void)
{
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);

    register uint32_t BU_RX_L;
    register uint32_t BU_RX_R;
    register uint32_t DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]                                           // Pipeline memory access
        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]
        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_RX_L, #32
        ADD     BU_RX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_RX_L, #32
        SUB     BU_RX_R, #32
        ADD     DB_PHASE, #0x1

        store:                                                                  // Pipeline memory access
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     DB_PHASE, [&db_phase]
    }

    if(!NVIC_GetActive(SGTL5000::RX_swIRQ)) NVIC->STIR = SGTL5000::RX_swIRQ;// Trigger swIRQ if not still processing

}

void SGTL5000::rx_dma_ISR(void)
{
    static uint32_t db_phase = 0;

    DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);

    register uint32_t BU_RX_L;
    register uint32_t BU_RX_R;
    register uint32_t DB_PHASE;
    __asm {
        LDR     DB_PHASE, [&db_phase]                                 // Pipeline memory access
        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]
        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]

        TEQ     DB_PHASE, #0x0
        IT      EQ
        BEQ     buf_base

        ADD     BU_RX_L, #32
        ADD     BU_RX_R, #32
        SUB     DB_PHASE, #0x1
        B       store

        buf_base:
        SUB     BU_RX_L, #32
        SUB     BU_RX_R, #32
        ADD     DB_PHASE, #0x1

        store:                                                                  // Pipeline memory access
        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]
        STR     BU_RX_R, [SGTL5000::BufRX_R_safe]
        STR     DB_PHASE, [&db_phase]
    }
    SGTL5000::RX_user_func.call();                                              // Callback user function
}



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.

    */

    static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20)));                                                                          // Allocate memory for scatter gather TCD definitions
    static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));
    static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));
    static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));

    // 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(SYNC_run || 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 request to DMA. Expect Left channel first. i2s0 RX =14 TX = 15
        DMA0->CERQ =DMA_CERQ_CERQ(SGTL5000::RX_DMAch);                                                                                     // Disable requests for chosen DMA channels

        //Configure RX DMA Transfer Control Descriptor
        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].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                      // Disable major loop linking
        DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                         // Enable IRQ at 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
        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&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
        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].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0];     // Set scatter gather TCD definition location
        DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;
        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_A[0];                                                                                                                // Set scatter gather TCD definition location
        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[8] + 2;                                                                             // Swap RX double buffer
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[8];
        memcpy(&SG_rx_TCD_B[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
        DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0];
    }


    if(SYNC_run || 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 request to DMA.
        DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch);

        //Configure TX DMA Transfer Control Descriptor
        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK;                                                                                                    // Disable source offset on minor loop
        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK;                                                                                                   // Enable 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;                                                                                                                         // Disable IRQ at Half Completion of Major cycle
        DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                      // Disable major loop linking
        DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                         // Enable IRQ at Completion of Major cycle
        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
        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0];
        DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];                                                                                                                      // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
        DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0;                                                                                                                                             // Signed Source 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.
        DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
        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_A[0];                                                                                                                // Set scatter gather TCD definition location
        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[8] + 2;                                                                             // Swap TX double buffer
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[8];
        memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);                                                                                                                 // Copy TCD B to memory
        // Set TCD elements in the DMA controller back to initial TCD A
        DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0] + 2;
        else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0];
    }

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

uint32_t SGTL5000::read_debug(uint32_t index)
{
    //SGTL5000::debug[0] = packed_RX;
    //SGTL5000::debug[1] = packed_TX;
    //SGTL5000::debug[2] = I2S_RX_Buffer[0];
    return SGTL5000::debug[index];
};
}