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.

Revision:
10:49bb33f71d32
Parent:
9:40e0ff8c2ba2
--- a/sgtl5000.cpp	Sat Jul 15 13:15:08 2017 +0000
+++ b/sgtl5000.cpp	Tue Sep 26 23:03:32 2017 +0000
@@ -30,33 +30,91 @@
 {
 
 // 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;
+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};
 
-SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq, bool i2c_ctrl_adr0_cs)
+
+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;
-    else i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
+    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;
-    SYNC_run = false;
-    TX_run = false;
-    RX_run = false;
+    SGTL5000::SYNC_run = false;
+    SGTL5000::TX_run = false;
+    SGTL5000::RX_run = false;
     SYNC_attached = false;
     TX_attached = false;
     RX_attached = false;
@@ -64,7 +122,10 @@
 
 int32_t SGTL5000::init(void)
 {
-    init_i2s();
+    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;
@@ -73,67 +134,157 @@
     return 0;
 }
 
-int32_t SGTL5000::freq(uint32_t rate)
+int32_t SGTL5000::sample_rate(uint32_t rate)
 {
-    if(!codec_configured) return -1;
+    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:
-            I2S_MCLK_M = 16;
-            I2S_MCLK_D = 750;
-            codec_SYS_FS = 0x02;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 1;
-            I2S_MCLK_D = 34;
-            codec_SYS_FS = 0x01;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 16;
-            I2S_MCLK_D = 500;
-            codec_SYS_FS = 0x02;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 32;
-            I2S_MCLK_D = 750;
-            codec_SYS_FS = 0x00;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 1;
-            I2S_MCLK_D = 17;
-            codec_SYS_FS = 0x01;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 32;
-            I2S_MCLK_D = 500;
-            codec_SYS_FS = 0x02;
-            codec_RATE_MODE = 0x01;
+            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:
-            I2S_MCLK_M = 64;
-            I2S_MCLK_D = 750;
-            codec_SYS_FS = 0x00;
-            codec_RATE_MODE = 0x0;
+            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:
-            I2S_MCLK_M = 2;
-            I2S_MCLK_D = 17;
-            codec_SYS_FS = 0x01;
-            codec_RATE_MODE = 0x0;
+            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:
-            I2S_MCLK_M = 32;
-            I2S_MCLK_D = 250;
-            codec_SYS_FS = 0x02;
-            codec_RATE_MODE = 0x0;
+            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) {
@@ -150,6 +301,23 @@
                     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:
@@ -186,50 +354,56 @@
     }
     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)
+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)) {
+    if(mI2C.write(_i2c_addr, (char *)&wire_addr, 2, false)) {
         mI2C.unlock();
         return -1;
     }
-    if(mI2C.read(i2c_addr, (char *)&wire_data, 2, false)) {
+    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)
+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)) {
+    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)
+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)) {
+    if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) {
         mI2C.unlock();
         return -1;
     }
@@ -237,11 +411,23 @@
 }
 
 
-void SGTL5000::init_i2s(void)
+int32_t 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
+    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.
@@ -262,6 +448,8 @@
     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.
@@ -303,6 +491,8 @@
     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)
@@ -310,14 +500,21 @@
     // Default Configure Codec
     if(
         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_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
@@ -370,9 +567,9 @@
     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(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,(
@@ -383,8 +580,8 @@
                       (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
+    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,(
@@ -452,52 +649,152 @@
     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;
-    SYNC_attach_type = 0;
+    SGTL5000::SYNC_attach_type = 0;
     SYNC_attached = true;
     return 0;
 }
 
-int32_t SGTL5000::attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
+
+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, user_ISR);
+    NVIC_SetVector(SGTL5000::SYNC_swIRQ, (uint32_t)user_ISR);
     NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
     NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
-    SYNC_attach_type = 1;
+    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) {
-        stop_SYNC();
+        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;
 }
 
-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)
+void SGTL5000::stream_ctrl_ISR(void)
 {
-    if(!codec_configured) return -1;
-    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
+    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.
-    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK) == 0) {
-        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;
+    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(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK) == 0) {
-        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) 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;
@@ -505,201 +802,240 @@
     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)];
-    SGTL5000::db_phase_sync = 0;
     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();
+    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_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));                          // Make sure TX & RX are disabled before enabling corresponding channel
-    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
-    I2S0->TCSR |= I2S_TCSR_TE_MASK;                                                                     // Now switch TX enable
-    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // Now switch RX enable
+    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
 
-    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
+    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 {
-        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);
+        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) return -1;
-    if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;         // codec I2S disable
-    if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
-    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
-    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_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.
-    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 channels 0
-    SYNC_run = false;
+    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::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->RCSR |= I2S_RCSR_FRDE_MASK;                                                                   // Enable DMA request based on RX FIFO watermark
-}
-
-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
-    I2S0->TDR[0] = 0;                                                                                   // Stuff the TX FIFO with 2 samples 1 Left 1 Right. Timing here is critical as we have just 3 bit clocks to get something into the FIFO
-    I2S0->TDR[0] = 0;                                                                                   // before we suffer an under-run. At 192Ks/s that's approx 5nS! So stuff a couple of samples to give some headroom.
-    I2S0->TCSR |= I2S_TCSR_FRDE_MASK;                                                                   // Enable DMA request based on TX FIFO watermark
-}
-
 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
-    I2S0->TDR[0] = 0;                                                                                   // Stuff the TX FIFO with 2 samples 1 Left 1 Right. Timing here is critical as we have just 3 bit clocks to get something into the FIFO
-    I2S0->TDR[0] = 0;                                                                                   // before we suffer an under-run. At 192Ks/s that's approx 5nS! So stuff a couple of samples to give some headroom.
-    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
+
+    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_dma_ISR_NB(void)
+void SGTL5000::SYNC_TX_dma_ISR(void)
 {
-    /*!
-    Refer to the DMA_init function for details of buffer layouts.
+    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]
+    }
+}
 
-    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.
-    The DMA transfers will be pre-empting each other, dependant on relative priority.
-    Therefore before the users function 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.
-    */
+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]
 
-    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
+        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 DB_PHASE;
+    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     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]
+        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
 
-        ADD     BU_RX_L, #32
-        ADD     BU_RX_R, #32
-        ADD     BU_TX_L, #32
-        ADD     BU_TX_R, #32
+        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:
-        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, [&SGTL5000::db_phase_sync]
-    }
-
-    if(!NVIC_GetActive(SGTL5000::SYNC_swIRQ)) NVIC->STIR = SGTL5000::SYNC_swIRQ;// Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers!
-}
-
-void SGTL5000::sync_dma_ISR(void)
-{
-
-    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, [&SGTL5000::db_phase_sync]
-        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
+        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:
@@ -708,331 +1044,566 @@
         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
 
-    SGTL5000::SYNC_user_func.call();                                             // Callback user function
+        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(TX_attached) return -1;                                                  // Assign Callback function
+    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;
-    TX_attach_type = 0;
+    SGTL5000::TX_attach_type = 0;
     TX_attached = true;
     return 0;
 }
 
-int32_t SGTL5000::attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
+
+int32_t SGTL5000::attach_TX_NB(void* 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);
+    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);
-    TX_attach_type = 1;
+    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) {
-        SGTL5000::stop_TX();
+        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 _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) return -1;
-    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(!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.
+    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;
+        }
+    }
 
-    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK) == 0) {
-        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
-    }
+    packed_TX = _packed_TX;
     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)];
-    SGTL5000::db_phase_tx = 0;
-    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();
+    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;
 
-    I2S0->TCSR |= I2S_TCSR_SR_MASK;
+    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_TE_MASK;                                                                    // TX Disable
+    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
-    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1);                                   // Enable TX channel 0.
-    I2S0->TCSR |= I2S_TCSR_TE_MASK;                                                                     // TX enable
-    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_WS_ISR);                                      // Set vector for TX word start ISR
+
+    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) return -1;
-    if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
-    I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK;                                                                  // Disable TX FIFO IRQs based on watermark
-    while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
-    I2S0->TCSR &= ~I2S_TCSR_TE_MASK;                                                                    // TX Disable
-    while(I2S0->TCSR & I2S_TCSR_TE_MASK);                                                               // Make sure TX is disabled before disabling corresponding channel
-    I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1);                                  // Disable TX channel.
-    TX_run = false;
+    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_dma_ISR_NB(void)
+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
+}
 
-    DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);
-
+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 DB_PHASE;
+    register uint32_t BU_TX_L2;
+    register uint32_t BU_TX_R2;
     __asm {
-        LDR     DB_PHASE, [&SGTL5000::db_phase_tx]                              // 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
+        LDR     TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm]
+        LDR     DMA_INT, [&DMA0->INT]
 
-        store:                                                                  // Pipeline memory access
-        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
-        STR     BU_TX_R, [SGTL5000::BufTX_R_safe]
-        STR     DB_PHASE, [&SGTL5000::db_phase_tx]
-    }
-    if(!NVIC_GetActive(SGTL5000::TX_swIRQ)) NVIC->STIR = SGTL5000::TX_swIRQ;    // Trigger swIRQ if not still processing
-}
+        AND     TX_IRQ_BM, TX_IRQ_BM, DMA_INT                                   // Clear DMA IRQ flag
+        STR     TX_IRQ_BM, [&DMA0->INT]
 
-void SGTL5000::tx_dma_ISR(void)
-{
-
-    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, [&SGTL5000::db_phase_tx]                              // Pipeline memory access
-        LDR     BU_TX_L, [SGTL5000::BufTX_L_safe]
-        LDR     BU_TX_R, [SGTL5000::BufTX_R_safe]
+        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
 
-        ADD     BU_TX_L, #32
-        ADD     BU_TX_R, #32
+        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeB]
+        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeB]
         SUB     DB_PHASE, #0x1
         B       store
 
         buf_base:
-        SUB     BU_TX_L, #32
-        SUB     BU_TX_R, #32
+        LDR     BU_TX_L, [&SGTL5000::BufTX_L_safeA]
+        LDR     BU_TX_R, [&SGTL5000::BufTX_R_safeA]
         ADD     DB_PHASE, #0x1
 
-        store:                                                                  // Pipeline memory access
-        STR     BU_TX_L, [SGTL5000::BufTX_L_safe]
+        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();                                              // Callback user function
+    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(RX_attached) return -1;                                                  // Assign Callback function
+    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;
-    RX_attach_type = 0;
+    SGTL5000::RX_attach_type = 0;
     RX_attached = true;
     return 0;
 }
 
-int32_t SGTL5000::attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
+int32_t SGTL5000::attach_RX_NB(void* 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);
+    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);
-    RX_attach_type = 1;
+    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) {
-        SGTL5000::stop_RX();
+        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)
+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) return -1;
-    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(!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.
-    if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK) == 0) {
-        if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;
+    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;
-    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)];
-    SGTL5000::db_phase_rx = 0;
-    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();
+    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;
 
-    I2S0->RCSR |= I2S_RCSR_SR_MASK;
+    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_RE_MASK;                                                                    // RX Disable
+    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
-    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1);                                   // Enable RX channel 0.
-    I2S0->RCSR |= I2S_RCSR_RE_MASK;                                                                     // RX enable
-    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_WS_ISR);                                      // Set vector for RX word start ISR
+
+    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) return -1;
-    if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;                                                     // codec I2S disable
-    I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK;                                                                  // Disable RX FIFO IRQs based on watermark
-    while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK));                                    // Ensure any active DMA transfers are complete
-    I2S0->RCSR &= ~I2S_RCSR_RE_MASK;                                                                    // RX Disable
-    while(I2S0->RCSR & I2S_RCSR_RE_MASK);                                                               // Make sure RX is disabled before disabling corresponding channel
-    I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1);                                  // Disable RX channel.
-    RX_run = false;
+    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_dma_ISR_NB(void)
+
+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
+}
 
-    DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);
-
+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 DB_PHASE;
+    register uint32_t BU_RX_L2;
+    register uint32_t BU_RX_R2;
     __asm {
-        LDR     DB_PHASE, [&SGTL5000::db_phase_rx]                              // Pipeline memory access
-        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]
-        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]
+        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
 
-        ADD     BU_RX_L, #32
-        ADD     BU_RX_R, #32
+        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeB]
+        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeB]
         SUB     DB_PHASE, #0x1
         B       store
 
         buf_base:
-        SUB     BU_RX_L, #32
-        SUB     BU_RX_R, #32
+        LDR     BU_RX_L, [&SGTL5000::BufRX_L_safeA]
+        LDR     BU_RX_R, [&SGTL5000::BufRX_R_safeA]
         ADD     DB_PHASE, #0x1
 
-        store:                                                                  // Pipeline memory access
-        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]
+        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]
-    }
-
-    if(!NVIC_GetActive(SGTL5000::RX_swIRQ)) NVIC->STIR = SGTL5000::RX_swIRQ;    // Trigger swIRQ if not still active
-
-}
-
-void SGTL5000::rx_dma_ISR(void)
-{
+        B       finish_update
 
-    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, [&SGTL5000::db_phase_rx]                              // Pipeline memory access
-        LDR     BU_RX_L, [SGTL5000::BufRX_L_safe]
-        LDR     BU_RX_R, [SGTL5000::BufRX_R_safe]
-
+        dual_codecs:
         TEQ     DB_PHASE, #0x0
         IT      EQ
-        BEQ     buf_base
+        BEQ     buf_base2
 
-        ADD     BU_RX_L, #32
-        ADD     BU_RX_R, #32
+        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       store
+        B       store2
 
-        buf_base:
-        SUB     BU_RX_L, #32
-        SUB     BU_RX_R, #32
+        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
 
-        store:                                                                  // Pipeline memory access
-        STR     BU_RX_L, [SGTL5000::BufRX_L_safe]
+        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();                                              // Callback user function
+    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)
 {
     /*!
@@ -1054,26 +1625,33 @@
 
     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)));                                                                          // 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)));
+    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(SYNC_run || RX_run) {
+    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 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
+        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
+        //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
@@ -1084,41 +1662,72 @@
         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].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_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
+        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
-        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(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(SYNC_run || TX_run) {
+    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 request to DMA.
-        DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch);
+        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
-        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
+        //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.
@@ -1126,46 +1735,85 @@
         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].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
-        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].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.
-        DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
+        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_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(!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(SYNC_run || RX_run) {
-        DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch);                                                                                                                                    // Enable requests for RX DMA channel
+    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(SYNC_run || TX_run) {
+    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.
+        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)
+int32_t SGTL5000::read_debug(uint32_t index, bool finished)
 {
-    //SGTL5000::debug[0] = packed_RX;
-    //SGTL5000::debug[1] = packed_TX;
-    //SGTL5000::debug[2] = I2S_RX_Buffer[0];
+    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;
+}
+
 }
\ No newline at end of file