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.

Committer:
aidan1971
Date:
Tue Sep 26 23:03:32 2017 +0000
Revision:
10:49bb33f71d32
Parent:
9:40e0ff8c2ba2
Significant re-work.; The library now supports dual codecs. Using channel 0 and channel 1 of the I2S silicon of Teensy 3.2.; replaced c code with inline assembler reduces time of pointer swaps < 800nS.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
aidan1971 3:62c03088f256 1 /*!
aidan1971 0:8f28f25e3435 2 @ author Aidan Walton, aidan.walton@gmail.com
aidan1971 0:8f28f25e3435 3
aidan1971 0:8f28f25e3435 4 @section LICENSE
aidan1971 0:8f28f25e3435 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
aidan1971 0:8f28f25e3435 6 * of this software and associated documentation files (the "Software"), to deal
aidan1971 0:8f28f25e3435 7 * in the Software without restriction, including without limitation the rights
aidan1971 0:8f28f25e3435 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
aidan1971 0:8f28f25e3435 9 * copies of the Software, and to permit persons to whom the Software is
aidan1971 0:8f28f25e3435 10 * furnished to do so, subject to the following conditions:
aidan1971 0:8f28f25e3435 11 *
aidan1971 0:8f28f25e3435 12 * The above copyright notice, development funding notice, and this permission
aidan1971 0:8f28f25e3435 13 * notice shall be included in all copies or substantial portions of the Software.
aidan1971 0:8f28f25e3435 14 *
aidan1971 0:8f28f25e3435 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
aidan1971 0:8f28f25e3435 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
aidan1971 0:8f28f25e3435 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
aidan1971 0:8f28f25e3435 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
aidan1971 0:8f28f25e3435 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
aidan1971 0:8f28f25e3435 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
aidan1971 0:8f28f25e3435 21 * THE SOFTWARE.
aidan1971 0:8f28f25e3435 22
aidan1971 0:8f28f25e3435 23 @section DESCRIPTION
aidan1971 0:8f28f25e3435 24 Library for NXP SGTL5000 Codec
aidan1971 0:8f28f25e3435 25 */
aidan1971 0:8f28f25e3435 26
aidan1971 0:8f28f25e3435 27 #include "sgtl5000.h"
aidan1971 0:8f28f25e3435 28
aidan1971 0:8f28f25e3435 29 namespace SGTL5000
aidan1971 0:8f28f25e3435 30 {
aidan1971 0:8f28f25e3435 31
aidan1971 0:8f28f25e3435 32 // Static variables required within ISRs
aidan1971 10:49bb33f71d32 33 volatile uint32_t * volatile SGTL5000::BufRX_L_safe; // Private pointers assigned to users data pointers
aidan1971 10:49bb33f71d32 34 volatile uint32_t * volatile SGTL5000::BufRX_R_safe; // These are used to flip user pointers between safe 'active' regions of
aidan1971 10:49bb33f71d32 35 volatile uint32_t * volatile SGTL5000::BufTX_L_safe; // double buffered space.
aidan1971 10:49bb33f71d32 36 volatile uint32_t * volatile SGTL5000::BufTX_R_safe;
aidan1971 10:49bb33f71d32 37 volatile uint32_t * volatile SGTL5000::BufRX_L_safe2; // Private pointers assigned to users data pointers
aidan1971 10:49bb33f71d32 38 volatile uint32_t * volatile SGTL5000::BufRX_R_safe2; // These are used to flip user pointers between safe 'active' regions of
aidan1971 10:49bb33f71d32 39 volatile uint32_t * volatile SGTL5000::BufTX_L_safe2; // double buffered space.
aidan1971 10:49bb33f71d32 40 volatile uint32_t * volatile SGTL5000::BufTX_R_safe2;
aidan1971 10:49bb33f71d32 41
aidan1971 10:49bb33f71d32 42 uint32_t SGTL5000::BufRX_L_safeA; // Precalculated double buffer addresses
aidan1971 10:49bb33f71d32 43 uint32_t SGTL5000::BufRX_R_safeA;
aidan1971 10:49bb33f71d32 44 uint32_t SGTL5000::BufTX_L_safeA;
aidan1971 10:49bb33f71d32 45 uint32_t SGTL5000::BufTX_R_safeA;
aidan1971 10:49bb33f71d32 46 uint32_t SGTL5000::BufRX_L_safeB; // Precalculated double buffer addresses
aidan1971 10:49bb33f71d32 47 uint32_t SGTL5000::BufRX_R_safeB;
aidan1971 10:49bb33f71d32 48 uint32_t SGTL5000::BufTX_L_safeB;
aidan1971 10:49bb33f71d32 49 uint32_t SGTL5000::BufTX_R_safeB;
aidan1971 10:49bb33f71d32 50 uint32_t SGTL5000::BufRX_L_safeA2; // Precalculated double buffer addresses 2nd codec
aidan1971 10:49bb33f71d32 51 uint32_t SGTL5000::BufRX_R_safeA2;
aidan1971 10:49bb33f71d32 52 uint32_t SGTL5000::BufTX_L_safeA2;
aidan1971 10:49bb33f71d32 53 uint32_t SGTL5000::BufTX_R_safeA2;
aidan1971 10:49bb33f71d32 54 uint32_t SGTL5000::BufRX_L_safeB2; // Precalculated double buffer addresses 2nd codec
aidan1971 10:49bb33f71d32 55 uint32_t SGTL5000::BufRX_R_safeB2;
aidan1971 10:49bb33f71d32 56 uint32_t SGTL5000::BufTX_L_safeB2;
aidan1971 10:49bb33f71d32 57 uint32_t SGTL5000::BufTX_R_safeB2;
aidan1971 10:49bb33f71d32 58
aidan1971 10:49bb33f71d32 59 uint32_t SGTL5000::I2S_RX_Buffer[16] = {0}; // Data buffers
aidan1971 10:49bb33f71d32 60 uint32_t SGTL5000::I2S_TX_Buffer[16] = {0};
aidan1971 10:49bb33f71d32 61 uint32_t SGTL5000::I2S_RX_Buffer2[16] = {0};
aidan1971 10:49bb33f71d32 62 uint32_t SGTL5000::I2S_TX_Buffer2[16] = {0};
aidan1971 6:4ab5aaeaa064 63 IRQn SGTL5000::SYNC_swIRQ; // IRQn assigned by user to the software IRQ triggered by the FIFO queues.
aidan1971 0:8f28f25e3435 64 IRQn SGTL5000::TX_swIRQ;
aidan1971 0:8f28f25e3435 65 IRQn SGTL5000::RX_swIRQ;
aidan1971 10:49bb33f71d32 66 IRQn SGTL5000::CODEC_CTRL_IRQ; // Stream ctrl IRQ
aidan1971 6:4ab5aaeaa064 67 uint32_t SGTL5000::RX_DMAch; // User defined RX DMA channel number
aidan1971 6:4ab5aaeaa064 68 uint32_t SGTL5000::TX_DMAch; // User defined TX DMA channel number
aidan1971 10:49bb33f71d32 69 uint32_t SGTL5000::active_RX_DMAch_bm;
aidan1971 10:49bb33f71d32 70 uint32_t SGTL5000::active_TX_DMAch_bm;
aidan1971 10:49bb33f71d32 71 uint32_t SGTL5000::sync_dma_irq_acks = 0;
aidan1971 0:8f28f25e3435 72 Callback<void()> SGTL5000::TX_user_func = NULL;
aidan1971 0:8f28f25e3435 73 Callback<void()> SGTL5000::RX_user_func = NULL;
aidan1971 6:4ab5aaeaa064 74 Callback<void()> SGTL5000::SYNC_user_func = NULL; // User defined callback function
aidan1971 9:40e0ff8c2ba2 75 uint32_t SGTL5000::db_phase_sync;
aidan1971 9:40e0ff8c2ba2 76 uint32_t SGTL5000::db_phase_tx;
aidan1971 9:40e0ff8c2ba2 77 uint32_t SGTL5000::db_phase_rx;
aidan1971 10:49bb33f71d32 78 bool SGTL5000::SYNC_run;
aidan1971 10:49bb33f71d32 79 bool SGTL5000::TX_run;
aidan1971 10:49bb33f71d32 80 bool SGTL5000::RX_run;
aidan1971 10:49bb33f71d32 81
aidan1971 10:49bb33f71d32 82 uint32_t SGTL5000::ctrl_command = 0;
aidan1971 10:49bb33f71d32 83 bool SGTL5000::i2s_configured = false; // Shared I2S state
aidan1971 10:49bb33f71d32 84 bool SGTL5000::codec2_active = false; // Which codecs are instantiated
aidan1971 10:49bb33f71d32 85 bool SGTL5000::codec1_active = false;
aidan1971 10:49bb33f71d32 86 uint32_t SGTL5000::SYNC_attach_type;
aidan1971 10:49bb33f71d32 87 uint32_t SGTL5000::TX_attach_type;
aidan1971 10:49bb33f71d32 88 uint32_t SGTL5000::RX_attach_type;
aidan1971 0:8f28f25e3435 89
aidan1971 6:4ab5aaeaa064 90 uint32_t SGTL5000::debug[16] = {0};
aidan1971 0:8f28f25e3435 91
aidan1971 10:49bb33f71d32 92
aidan1971 10:49bb33f71d32 93 uint32_t SGTL5000::t1; // Used to debug timing issues
aidan1971 10:49bb33f71d32 94 uint32_t SGTL5000::proc_time;
aidan1971 10:49bb33f71d32 95 uint32_t SGTL5000::SYST_CVAL = 0xE000E018;
aidan1971 10:49bb33f71d32 96 bool SGTL5000::debug_read = false;
aidan1971 10:49bb33f71d32 97
aidan1971 10:49bb33f71d32 98
aidan1971 10:49bb33f71d32 99
aidan1971 10:49bb33f71d32 100 SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq, bool i2c_ctrl_adr0_cs, IRQn _ctrl_IRQn)
aidan1971 9:40e0ff8c2ba2 101 : mI2C(i2c_sda, i2c_scl), i2c_freq(_i2c_freq)
aidan1971 0:8f28f25e3435 102 {
aidan1971 10:49bb33f71d32 103 if(i2c_ctrl_adr0_cs) {
aidan1971 10:49bb33f71d32 104 i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1;
aidan1971 10:49bb33f71d32 105 ctrl_codec = false;
aidan1971 10:49bb33f71d32 106 } else {
aidan1971 10:49bb33f71d32 107 i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
aidan1971 10:49bb33f71d32 108 SGTL5000::CODEC_CTRL_IRQ = _ctrl_IRQn;
aidan1971 10:49bb33f71d32 109 NVIC_SetVector(SGTL5000::CODEC_CTRL_IRQ, (uint32_t)&SGTL5000::stream_ctrl_ISR); // Set ctrl command IRQ vector
aidan1971 10:49bb33f71d32 110 NVIC_SetPriority(SGTL5000::CODEC_CTRL_IRQ, 0); // Set irq priority
aidan1971 10:49bb33f71d32 111 NVIC_EnableIRQ(SGTL5000::CODEC_CTRL_IRQ);
aidan1971 10:49bb33f71d32 112 ctrl_codec = true;
aidan1971 10:49bb33f71d32 113 }
aidan1971 8:9fdf8501d14b 114 codec_configured = false;
aidan1971 10:49bb33f71d32 115 SGTL5000::SYNC_run = false;
aidan1971 10:49bb33f71d32 116 SGTL5000::TX_run = false;
aidan1971 10:49bb33f71d32 117 SGTL5000::RX_run = false;
aidan1971 0:8f28f25e3435 118 SYNC_attached = false;
aidan1971 0:8f28f25e3435 119 TX_attached = false;
aidan1971 0:8f28f25e3435 120 RX_attached = false;
aidan1971 0:8f28f25e3435 121 }
aidan1971 0:8f28f25e3435 122
aidan1971 8:9fdf8501d14b 123 int32_t SGTL5000::init(void)
aidan1971 8:9fdf8501d14b 124 {
aidan1971 10:49bb33f71d32 125 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.
aidan1971 10:49bb33f71d32 126 if((i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) && SGTL5000::codec1_active) return -2;
aidan1971 10:49bb33f71d32 127 if((i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) && SGTL5000::codec2_active) return -3;
aidan1971 10:49bb33f71d32 128 if(this->ctrl_codec && !SGTL5000::i2s_configured) init_i2s();
aidan1971 9:40e0ff8c2ba2 129 mI2C.frequency(i2c_freq);
aidan1971 9:40e0ff8c2ba2 130 int32_t error = init_codec();
aidan1971 9:40e0ff8c2ba2 131 if(error) return error;
aidan1971 8:9fdf8501d14b 132 else codec_configured = true;
aidan1971 9:40e0ff8c2ba2 133
aidan1971 8:9fdf8501d14b 134 return 0;
aidan1971 8:9fdf8501d14b 135 }
aidan1971 8:9fdf8501d14b 136
aidan1971 10:49bb33f71d32 137 int32_t SGTL5000::sample_rate(uint32_t rate)
aidan1971 0:8f28f25e3435 138 {
aidan1971 10:49bb33f71d32 139 if(!codec_configured || !this->ctrl_codec) return -1;
aidan1971 0:8f28f25e3435 140 uint32_t I2S_MCLK_M;
aidan1971 0:8f28f25e3435 141 uint32_t I2S_MCLK_D;
aidan1971 0:8f28f25e3435 142 uint32_t codec_SYS_FS;
aidan1971 0:8f28f25e3435 143 uint32_t codec_RATE_MODE;
aidan1971 0:8f28f25e3435 144 switch(rate) {
aidan1971 0:8f28f25e3435 145 case 8:
aidan1971 10:49bb33f71d32 146 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 147 case 96000000:
aidan1971 10:49bb33f71d32 148 I2S_MCLK_M = 16;
aidan1971 10:49bb33f71d32 149 I2S_MCLK_D = 750;
aidan1971 10:49bb33f71d32 150 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 151 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 152 break;
aidan1971 10:49bb33f71d32 153 case 120000000:
aidan1971 10:49bb33f71d32 154 I2S_MCLK_M = 16;
aidan1971 10:49bb33f71d32 155 I2S_MCLK_D = 936;
aidan1971 10:49bb33f71d32 156 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 157 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 158 break;
aidan1971 10:49bb33f71d32 159 }
aidan1971 0:8f28f25e3435 160 break;
aidan1971 0:8f28f25e3435 161 case 11:
aidan1971 10:49bb33f71d32 162 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 163 case 96000000:
aidan1971 10:49bb33f71d32 164 I2S_MCLK_M = 1;
aidan1971 10:49bb33f71d32 165 I2S_MCLK_D = 34;
aidan1971 10:49bb33f71d32 166 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 167 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 168 break;
aidan1971 10:49bb33f71d32 169 case 120000000:
aidan1971 10:49bb33f71d32 170 I2S_MCLK_M = 1;
aidan1971 10:49bb33f71d32 171 I2S_MCLK_D = 42;
aidan1971 10:49bb33f71d32 172 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 173 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 174 break;
aidan1971 10:49bb33f71d32 175 }
aidan1971 0:8f28f25e3435 176 break;
aidan1971 0:8f28f25e3435 177 case 12:
aidan1971 10:49bb33f71d32 178 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 179 case 96000000:
aidan1971 10:49bb33f71d32 180 I2S_MCLK_M = 16;
aidan1971 10:49bb33f71d32 181 I2S_MCLK_D = 500;
aidan1971 10:49bb33f71d32 182 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 183 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 184 break;
aidan1971 10:49bb33f71d32 185 case 120000000:
aidan1971 10:49bb33f71d32 186 I2S_MCLK_M = 16;
aidan1971 10:49bb33f71d32 187 I2S_MCLK_D = 624;
aidan1971 10:49bb33f71d32 188 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 189 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 190 break;
aidan1971 10:49bb33f71d32 191 }
aidan1971 0:8f28f25e3435 192 break;
aidan1971 0:8f28f25e3435 193 case 16:
aidan1971 10:49bb33f71d32 194 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 195 case 96000000:
aidan1971 10:49bb33f71d32 196 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 197 I2S_MCLK_D = 750;
aidan1971 10:49bb33f71d32 198 codec_SYS_FS = 0x00;
aidan1971 10:49bb33f71d32 199 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 200 break;
aidan1971 10:49bb33f71d32 201 case 120000000:
aidan1971 10:49bb33f71d32 202 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 203 I2S_MCLK_D = 936;
aidan1971 10:49bb33f71d32 204 codec_SYS_FS = 0x00;
aidan1971 10:49bb33f71d32 205 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 206 break;
aidan1971 10:49bb33f71d32 207 }
aidan1971 0:8f28f25e3435 208 break;
aidan1971 0:8f28f25e3435 209 case 22:
aidan1971 10:49bb33f71d32 210 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 211 case 96000000:
aidan1971 10:49bb33f71d32 212 I2S_MCLK_M = 1;
aidan1971 10:49bb33f71d32 213 I2S_MCLK_D = 17;
aidan1971 10:49bb33f71d32 214 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 215 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 216 break;
aidan1971 10:49bb33f71d32 217 case 120000000:
aidan1971 10:49bb33f71d32 218 I2S_MCLK_M = 1;
aidan1971 10:49bb33f71d32 219 I2S_MCLK_D = 21;
aidan1971 10:49bb33f71d32 220 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 221 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 222 break;
aidan1971 10:49bb33f71d32 223 }
aidan1971 0:8f28f25e3435 224 break;
aidan1971 0:8f28f25e3435 225 case 24:
aidan1971 10:49bb33f71d32 226 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 227 case 96000000:
aidan1971 10:49bb33f71d32 228 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 229 I2S_MCLK_D = 500;
aidan1971 10:49bb33f71d32 230 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 231 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 232 break;
aidan1971 10:49bb33f71d32 233 case 120000000:
aidan1971 10:49bb33f71d32 234 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 235 I2S_MCLK_D = 624;
aidan1971 10:49bb33f71d32 236 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 237 codec_RATE_MODE = 0x01;
aidan1971 10:49bb33f71d32 238 break;
aidan1971 10:49bb33f71d32 239 }
aidan1971 0:8f28f25e3435 240 break;
aidan1971 0:8f28f25e3435 241 case 32:
aidan1971 10:49bb33f71d32 242 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 243 case 96000000:
aidan1971 10:49bb33f71d32 244 I2S_MCLK_M = 64;
aidan1971 10:49bb33f71d32 245 I2S_MCLK_D = 750;
aidan1971 10:49bb33f71d32 246 codec_SYS_FS = 0x00;
aidan1971 10:49bb33f71d32 247 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 248 break;
aidan1971 10:49bb33f71d32 249 case 120000000:
aidan1971 10:49bb33f71d32 250 I2S_MCLK_M = 64;
aidan1971 10:49bb33f71d32 251 I2S_MCLK_D = 936;
aidan1971 10:49bb33f71d32 252 codec_SYS_FS = 0x00;
aidan1971 10:49bb33f71d32 253 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 254 break;
aidan1971 10:49bb33f71d32 255 }
aidan1971 0:8f28f25e3435 256 break;
aidan1971 0:8f28f25e3435 257 case 44:
aidan1971 10:49bb33f71d32 258 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 259 case 96000000:
aidan1971 10:49bb33f71d32 260 I2S_MCLK_M = 2;
aidan1971 10:49bb33f71d32 261 I2S_MCLK_D = 17;
aidan1971 10:49bb33f71d32 262 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 263 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 264 break;
aidan1971 10:49bb33f71d32 265 case 120000000:
aidan1971 10:49bb33f71d32 266 I2S_MCLK_M = 2;
aidan1971 10:49bb33f71d32 267 I2S_MCLK_D = 21;
aidan1971 10:49bb33f71d32 268 codec_SYS_FS = 0x01;
aidan1971 10:49bb33f71d32 269 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 270 break;
aidan1971 10:49bb33f71d32 271 }
aidan1971 0:8f28f25e3435 272 break;
aidan1971 0:8f28f25e3435 273 case 48:
aidan1971 10:49bb33f71d32 274 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 275 case 96000000:
aidan1971 10:49bb33f71d32 276 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 277 I2S_MCLK_D = 250;
aidan1971 10:49bb33f71d32 278 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 279 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 280 break;
aidan1971 10:49bb33f71d32 281 case 120000000:
aidan1971 10:49bb33f71d32 282 I2S_MCLK_M = 32;
aidan1971 10:49bb33f71d32 283 I2S_MCLK_D = 312;
aidan1971 10:49bb33f71d32 284 codec_SYS_FS = 0x02;
aidan1971 10:49bb33f71d32 285 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 286 break;
aidan1971 10:49bb33f71d32 287 }
aidan1971 0:8f28f25e3435 288 break;
aidan1971 0:8f28f25e3435 289 case 96:
aidan1971 9:40e0ff8c2ba2 290 switch(SystemCoreClock) {
aidan1971 9:40e0ff8c2ba2 291 case 96000000:
aidan1971 9:40e0ff8c2ba2 292 I2S_MCLK_M = 32;
aidan1971 9:40e0ff8c2ba2 293 I2S_MCLK_D = 125;
aidan1971 9:40e0ff8c2ba2 294 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 295 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 296 break;
aidan1971 9:40e0ff8c2ba2 297 case 120000000:
aidan1971 9:40e0ff8c2ba2 298 I2S_MCLK_M = 32;
aidan1971 9:40e0ff8c2ba2 299 I2S_MCLK_D = 156;
aidan1971 9:40e0ff8c2ba2 300 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 301 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 302 break;
aidan1971 9:40e0ff8c2ba2 303 }
aidan1971 10:49bb33f71d32 304 break;
aidan1971 10:49bb33f71d32 305 case 72:
aidan1971 10:49bb33f71d32 306 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 307 case 96000000:
aidan1971 10:49bb33f71d32 308 I2S_MCLK_M = 24;
aidan1971 10:49bb33f71d32 309 I2S_MCLK_D = 125;
aidan1971 10:49bb33f71d32 310 codec_SYS_FS = 0x03;
aidan1971 10:49bb33f71d32 311 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 312 break;
aidan1971 10:49bb33f71d32 313 case 120000000:
aidan1971 10:49bb33f71d32 314 I2S_MCLK_M = 24;
aidan1971 10:49bb33f71d32 315 I2S_MCLK_D = 156;
aidan1971 10:49bb33f71d32 316 codec_SYS_FS = 0x03;
aidan1971 10:49bb33f71d32 317 codec_RATE_MODE = 0x0;
aidan1971 10:49bb33f71d32 318 break;
aidan1971 10:49bb33f71d32 319 }
aidan1971 10:49bb33f71d32 320 break;
aidan1971 4:91354c908416 321 case 144:
aidan1971 6:4ab5aaeaa064 322 switch(SystemCoreClock) {
aidan1971 6:4ab5aaeaa064 323 case 96000000:
aidan1971 6:4ab5aaeaa064 324 I2S_MCLK_M = 48;
aidan1971 6:4ab5aaeaa064 325 I2S_MCLK_D = 125;
aidan1971 6:4ab5aaeaa064 326 codec_SYS_FS = 0x03;
aidan1971 6:4ab5aaeaa064 327 codec_RATE_MODE = 0x0;
aidan1971 6:4ab5aaeaa064 328 break;
aidan1971 6:4ab5aaeaa064 329 case 120000000:
aidan1971 6:4ab5aaeaa064 330 I2S_MCLK_M = 48;
aidan1971 6:4ab5aaeaa064 331 I2S_MCLK_D = 156;
aidan1971 6:4ab5aaeaa064 332 codec_SYS_FS = 0x03;
aidan1971 6:4ab5aaeaa064 333 codec_RATE_MODE = 0x0;
aidan1971 6:4ab5aaeaa064 334 break;
aidan1971 6:4ab5aaeaa064 335 }
aidan1971 4:91354c908416 336 break;
aidan1971 9:40e0ff8c2ba2 337 case 192:
aidan1971 9:40e0ff8c2ba2 338 switch(SystemCoreClock) {
aidan1971 9:40e0ff8c2ba2 339 case 96000000: // Not officially supported by the codec, but it seems to work.
aidan1971 9:40e0ff8c2ba2 340 I2S_MCLK_M = 64;
aidan1971 9:40e0ff8c2ba2 341 I2S_MCLK_D = 125;
aidan1971 9:40e0ff8c2ba2 342 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 343 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 344 break;
aidan1971 9:40e0ff8c2ba2 345 case 120000000:
aidan1971 9:40e0ff8c2ba2 346 I2S_MCLK_M = 64;
aidan1971 9:40e0ff8c2ba2 347 I2S_MCLK_D = 156;
aidan1971 9:40e0ff8c2ba2 348 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 349 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 350 break;
aidan1971 9:40e0ff8c2ba2 351 }
aidan1971 0:8f28f25e3435 352 default:
aidan1971 0:8f28f25e3435 353 return -1;
aidan1971 0:8f28f25e3435 354 }
aidan1971 0:8f28f25e3435 355 if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK)) return -1; // Set CODEC clocking
aidan1971 0:8f28f25e3435 356 if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK)) return -1; // Set CODEC clocking
aidan1971 10:49bb33f71d32 357 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 358 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
aidan1971 10:49bb33f71d32 359 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
aidan1971 10:49bb33f71d32 360 }
aidan1971 0:8f28f25e3435 361 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 362 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));
aidan1971 0:8f28f25e3435 363 return 0;
aidan1971 0:8f28f25e3435 364 }
aidan1971 0:8f28f25e3435 365
aidan1971 10:49bb33f71d32 366 int32_t SGTL5000::read_i2c(uint32_t reg_addr, uint32_t mask, int _i2c_addr)
aidan1971 0:8f28f25e3435 367 {
aidan1971 10:49bb33f71d32 368 if(!_i2c_addr) _i2c_addr = i2c_addr;
aidan1971 0:8f28f25e3435 369 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 370 uint32_t shift;
aidan1971 0:8f28f25e3435 371 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 372 uint32_t wire_data;
aidan1971 0:8f28f25e3435 373 uint32_t wire_addr = __rev(reg_addr) >> 16;
aidan1971 7:d65476c153a4 374 mI2C.lock();
aidan1971 10:49bb33f71d32 375 if(mI2C.write(_i2c_addr, (char *)&wire_addr, 2, false)) {
aidan1971 7:d65476c153a4 376 mI2C.unlock();
aidan1971 7:d65476c153a4 377 return -1;
aidan1971 7:d65476c153a4 378 }
aidan1971 10:49bb33f71d32 379 if(mI2C.read(_i2c_addr, (char *)&wire_data, 2, false)) {
aidan1971 7:d65476c153a4 380 mI2C.unlock();
aidan1971 7:d65476c153a4 381 return -1;
aidan1971 7:d65476c153a4 382 }
aidan1971 0:8f28f25e3435 383 return ((__rev(wire_data) >> 16) & mask) >> shift;
aidan1971 0:8f28f25e3435 384 }
aidan1971 0:8f28f25e3435 385
aidan1971 10:49bb33f71d32 386 int32_t SGTL5000::write_i2c(uint32_t reg_addr, uint32_t data, int _i2c_addr)
aidan1971 0:8f28f25e3435 387 {
aidan1971 10:49bb33f71d32 388 if(!_i2c_addr) _i2c_addr = i2c_addr;
aidan1971 0:8f28f25e3435 389 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev(data));
aidan1971 7:d65476c153a4 390 mI2C.lock();
aidan1971 10:49bb33f71d32 391 if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) {
aidan1971 7:d65476c153a4 392 mI2C.unlock();
aidan1971 7:d65476c153a4 393 return -1;
aidan1971 7:d65476c153a4 394 }
aidan1971 0:8f28f25e3435 395 return 0;
aidan1971 0:8f28f25e3435 396 }
aidan1971 0:8f28f25e3435 397
aidan1971 10:49bb33f71d32 398 int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask, int _i2c_addr)
aidan1971 0:8f28f25e3435 399 {
aidan1971 10:49bb33f71d32 400 if(!_i2c_addr) _i2c_addr = i2c_addr;
aidan1971 0:8f28f25e3435 401 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 402 uint32_t shift;
aidan1971 0:8f28f25e3435 403 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 404 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev((read_i2c(reg_addr) & (~mask & 0xFFFF)) | ((data << shift) & mask)));
aidan1971 7:d65476c153a4 405 mI2C.lock();
aidan1971 10:49bb33f71d32 406 if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) {
aidan1971 7:d65476c153a4 407 mI2C.unlock();
aidan1971 7:d65476c153a4 408 return -1;
aidan1971 7:d65476c153a4 409 }
aidan1971 0:8f28f25e3435 410 return 0;
aidan1971 0:8f28f25e3435 411 }
aidan1971 0:8f28f25e3435 412
aidan1971 0:8f28f25e3435 413
aidan1971 10:49bb33f71d32 414 int32_t SGTL5000::init_i2s(void)
aidan1971 0:8f28f25e3435 415 {
aidan1971 10:49bb33f71d32 416 if(!this->ctrl_codec) return -1;
aidan1971 10:49bb33f71d32 417
aidan1971 10:49bb33f71d32 418 // Set default rate at 48Khz
aidan1971 10:49bb33f71d32 419 uint32_t I2S_MCLK_MULT;
aidan1971 10:49bb33f71d32 420 uint32_t I2S_MCLK_DIV;
aidan1971 10:49bb33f71d32 421 switch(SystemCoreClock) {
aidan1971 10:49bb33f71d32 422 case 96000000:
aidan1971 10:49bb33f71d32 423 I2S_MCLK_MULT = 32;
aidan1971 10:49bb33f71d32 424 I2S_MCLK_DIV = 250;
aidan1971 10:49bb33f71d32 425 break;
aidan1971 10:49bb33f71d32 426 case 120000000:
aidan1971 10:49bb33f71d32 427 I2S_MCLK_MULT = 32;
aidan1971 10:49bb33f71d32 428 I2S_MCLK_DIV = 312;
aidan1971 10:49bb33f71d32 429 break;
aidan1971 10:49bb33f71d32 430 }
aidan1971 0:8f28f25e3435 431
aidan1971 0:8f28f25e3435 432 // Configure System Clock Distribution for I2S
aidan1971 0:8f28f25e3435 433 SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; // Enable Clock to PORTC control module.
aidan1971 0:8f28f25e3435 434 SIM->SCGC6 |= SIM_SCGC6_I2S_MASK; // Enable Clocking to I2S Module
aidan1971 0:8f28f25e3435 435 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable DMA Clock Gate Control
aidan1971 0:8f28f25e3435 436 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable DMA Mux Clock Gate Control
aidan1971 0:8f28f25e3435 437
aidan1971 0:8f28f25e3435 438 // Configure I2S Master Clocking
aidan1971 0:8f28f25e3435 439 I2S0->MCR |= I2S_MCR_MICS(3); // Configure I2S MCLK Divder Source as System PLL
aidan1971 0:8f28f25e3435 440 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 441 I2S0->MDR = (I2S0->MDR & ~(I2S_MDR_FRACT_MASK | I2S_MDR_DIVIDE_MASK)) | (I2S_MDR_FRACT(I2S_MCLK_MULT - 1) | I2S_MDR_DIVIDE(I2S_MCLK_DIV - 1));
aidan1971 0:8f28f25e3435 442 I2S0->MCR |= I2S_MCR_MOE_MASK; // Enable MCLK Output
aidan1971 0:8f28f25e3435 443
aidan1971 0:8f28f25e3435 444 // Configure PIN Muxing
aidan1971 0:8f28f25e3435 445
aidan1971 0:8f28f25e3435 446 PORTC->PCR[6] = (PORTC->PCR[6] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC6(pin51)(Teensy pin11) <> I2S0_TX_MCLK
aidan1971 0:8f28f25e3435 447 PORTC->PCR[2] = (PORTC->PCR[2] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC2(pin45)(Teensy pin23) <> I2S0_TX_FS(LRCLK)
aidan1971 0:8f28f25e3435 448 PORTC->PCR[3] = (PORTC->PCR[3] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC3(pin46)(Teensy pin9) <> I2S0_TX_BCLK
aidan1971 0:8f28f25e3435 449 PORTC->PCR[1] = (PORTC->PCR[1] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC1(pin44)(Teensy pin22) <> I2S0_TXD0
aidan1971 0:8f28f25e3435 450 PORTC->PCR[5] = (PORTC->PCR[5] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC5(pin50)(Teensy pin13) <> I2S0_RXD0
aidan1971 10:49bb33f71d32 451 PORTC->PCR[0] = (PORTC->PCR[0] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC0(pin43)(Teensy pin15) <> I2S0_TXD1
aidan1971 10:49bb33f71d32 452 PORTC->PCR[11] = (PORTC->PCR[11] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC11(pin56)(Teensy pin30) <> I2S0_RXD1
aidan1971 0:8f28f25e3435 453
aidan1971 0:8f28f25e3435 454 // Config. TX
aidan1971 0:8f28f25e3435 455 I2S0->TMR = 0; // I2S TX Mask cleared.
aidan1971 0:8f28f25e3435 456 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_SYNC_MASK) | I2S_TCR2_SYNC(0); // Set TX Mode to Async
aidan1971 0:8f28f25e3435 457 I2S0->TCR2 |= I2S_TCR2_BCP_MASK; // Set TX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 458 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_MSEL_MASK) | I2S_TCR2_MSEL(1); // Set TX MCLK Audio Master Clock to Master Clock 1.
aidan1971 0:8f28f25e3435 459 I2S0->TCR2 |= I2S_TCR2_BCD_MASK; // Set TX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 460 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_DIV_MASK) | I2S_TCR2_DIV(3); // Set TX Bit Clock Divide
aidan1971 0:8f28f25e3435 461 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_WDFL_MASK) | I2S_TCR3_WDFL(0); // Set Word Flag Config to be 1st word in frame.
aidan1971 0:8f28f25e3435 462 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_FRSZ_MASK) | I2S_TCR4_FRSZ(1); // Set TX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 463 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_SYWD_MASK) | I2S_TCR4_SYWD(15); // Set TX frame sync width to 16bits
aidan1971 0:8f28f25e3435 464 I2S0->TCR4 |= I2S_TCR4_MF_MASK; // Set TX MSBit first
aidan1971 0:8f28f25e3435 465 I2S0->TCR4 |= I2S_TCR4_FSE_MASK; // Set TX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 466 I2S0->TCR4 |= I2S_TCR4_FSP_MASK; // Set TX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 467 I2S0->TCR4 |= I2S_TCR4_FSD_MASK; // Set TX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 468 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_WNW_MASK) | I2S_TCR5_WNW(15); // Set TX to 16bits per word.
aidan1971 0:8f28f25e3435 469 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_W0W_MASK) | I2S_TCR5_W0W(15); // Set TX to 16bits per first word.
aidan1971 0:8f28f25e3435 470 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_FBT_MASK) | I2S_TCR5_FBT(15); // Set TX first bit transmitted index to 16th bit .
aidan1971 0:8f28f25e3435 471
aidan1971 0:8f28f25e3435 472 //Config RX
aidan1971 0:8f28f25e3435 473 I2S0->RMR = 0; // I2S RX Mask cleared.
aidan1971 0:8f28f25e3435 474 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_SYNC_MASK) | I2S_RCR2_SYNC(1); // Set RX Mode to Sync with TX
aidan1971 0:8f28f25e3435 475 I2S0->RCR2 |= I2S_TCR2_BCP_MASK; // Set RX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 476 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_MSEL_MASK) | I2S_RCR2_MSEL(1); // Set RX MCLK Audio Master Clock to Master Clock 1.
aidan1971 0:8f28f25e3435 477 I2S0->RCR2 |= I2S_RCR2_BCD_MASK; // Set RX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 478 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_DIV_MASK) | I2S_RCR2_DIV(3); // Set RX Bit Clock Divide
aidan1971 0:8f28f25e3435 479 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_WDFL_MASK) | I2S_RCR3_WDFL(0); // Set Word Flag Config to be 1st word in frame.
aidan1971 0:8f28f25e3435 480 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_FRSZ_MASK) | I2S_RCR4_FRSZ(1); // Set RX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 481 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_SYWD_MASK) | I2S_RCR4_SYWD(15); // Set RX frame sync width to 16bits
aidan1971 0:8f28f25e3435 482 I2S0->RCR4 |= I2S_RCR4_MF_MASK; // Set RX MSBit first
aidan1971 0:8f28f25e3435 483 I2S0->RCR4 |= I2S_RCR4_FSE_MASK; // Set RX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 484 I2S0->RCR4 |= I2S_RCR4_FSP_MASK; // Set RX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 485 I2S0->RCR4 |= I2S_RCR4_FSD_MASK; // Set RX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 486 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_WNW_MASK) | I2S_RCR5_WNW(15); // Set RX to 16bits per word.
aidan1971 0:8f28f25e3435 487 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_W0W_MASK) | I2S_RCR5_W0W(15); // Set RX to 16bits per first word.
aidan1971 0:8f28f25e3435 488 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_FBT_MASK) | I2S_RCR5_FBT(15); // Set RX first bit transmitted index to 16 .
aidan1971 0:8f28f25e3435 489
aidan1971 0:8f28f25e3435 490 // Configure I2S Peripheral
aidan1971 0:8f28f25e3435 491 I2S0->TCSR |= I2S_TCSR_BCE_MASK; // Enable I2S Tx bit clock
aidan1971 0:8f28f25e3435 492 I2S0->RCSR |= I2S_RCSR_BCE_MASK; // Enable I2S Rx Bit Clock
aidan1971 0:8f28f25e3435 493
aidan1971 10:49bb33f71d32 494 SGTL5000::i2s_configured = true;
aidan1971 10:49bb33f71d32 495 return 0;
aidan1971 0:8f28f25e3435 496 }
aidan1971 0:8f28f25e3435 497
aidan1971 8:9fdf8501d14b 498 int32_t SGTL5000::init_codec(void)
aidan1971 0:8f28f25e3435 499 {
aidan1971 0:8f28f25e3435 500 // Default Configure Codec
aidan1971 8:9fdf8501d14b 501 if(
aidan1971 8:9fdf8501d14b 502 write_i2c(SGTL5000_ANA_POWER,(
aidan1971 10:49bb33f71d32 503 (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) | // Startup power sequence and reset.
aidan1971 10:49bb33f71d32 504 (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_SHIFT)) // Enable simple linear REG
aidan1971 10:49bb33f71d32 505 ))
aidan1971 10:49bb33f71d32 506 ) return -1;
aidan1971 10:49bb33f71d32 507 wait_us(10000);
aidan1971 10:49bb33f71d32 508 if( // Wait 10mS for powerup and reset
aidan1971 10:49bb33f71d32 509 write_i2c(SGTL5000_ANA_POWER,(
aidan1971 10:49bb33f71d32 510 (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
aidan1971 10:49bb33f71d32 511 (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
aidan1971 8:9fdf8501d14b 512 (SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_SHIFT)) | // Disable charge pump as VDDA & VDDIO > 3.1V
aidan1971 8:9fdf8501d14b 513 (SGTL5000_ANA_POWER_PLL_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_PLL_POWERUP_SHIFT)) | // PLL disabled as codec clocking is synchronous to I2S clock.
aidan1971 8:9fdf8501d14b 514 (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
aidan1971 8:9fdf8501d14b 515 (SGTL5000_ANA_POWER_VCOAMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VCOAMP_POWERUP_SHIFT)) | // Disable the PLL VCO Amp, we don't use PLL
aidan1971 8:9fdf8501d14b 516 (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
aidan1971 10:49bb33f71d32 517 (SGTL5000_ANA_POWER_DAC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_DAC_MONO_SHIFT)) | // Select stereo mode for DAC
aidan1971 8:9fdf8501d14b 518 (SGTL5000_ANA_POWER_ADC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_ADC_MONO_SHIFT)) | // ADC in stereo mode
aidan1971 8:9fdf8501d14b 519 (SGTL5000_ANA_POWER_REFTOP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_REFTOP_POWERUP_SHIFT)) | // Enable bias currents
aidan1971 8:9fdf8501d14b 520 (SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_HEADPHONE_POWERUP_SHIFT)) | // Disable HP amp until we set the reference levels
aidan1971 8:9fdf8501d14b 521 (SGTL5000_ANA_POWER_DAC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_DAC_POWERUP_SHIFT)) | // Disable DAC until we set refs
aidan1971 8:9fdf8501d14b 522 (SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_SHIFT)) | // Disable capless headphones
aidan1971 8:9fdf8501d14b 523 (SGTL5000_ANA_POWER_ADC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_ADC_POWERUP_SHIFT)) | // Disable ADC power until we set refs
aidan1971 8:9fdf8501d14b 524 (SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINEOUT_POWERUP_SHIFT)) // Disable LINEOUT power until we set refs
aidan1971 8:9fdf8501d14b 525 ))
aidan1971 8:9fdf8501d14b 526 ) return -1;
aidan1971 8:9fdf8501d14b 527 if(
aidan1971 8:9fdf8501d14b 528 write_i2c(SGTL5000_LINREG_CTRL,(
aidan1971 8:9fdf8501d14b 529 (SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_MASK & (0x0 << SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_SHIFT)) | // Manually assign charge pump rail (VDDC) source as VDDA.
aidan1971 8:9fdf8501d14b 530 (SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_MASK & (0x1 << SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_SHIFT)) | // chargepump source manually assigned
aidan1971 8:9fdf8501d14b 531 (SGTL5000_LINREG_CTRL_D_PROGRAMMING_MASK & (0xC << SGTL5000_LINREG_CTRL_D_PROGRAMMING_SHIFT)) // VDDD Line Reg not used so leave default
aidan1971 8:9fdf8501d14b 532 ))
aidan1971 9:40e0ff8c2ba2 533 ) return -2;
aidan1971 8:9fdf8501d14b 534 if(
aidan1971 8:9fdf8501d14b 535 write_i2c(SGTL5000_REF_CTRL,(
aidan1971 8:9fdf8501d14b 536 (SGTL5000_REF_CTRL_VAG_VAL_MASK & (0x1F << SGTL5000_REF_CTRL_VAG_VAL_SHIFT)) | // Set VAG ref to 1.575V
aidan1971 8:9fdf8501d14b 537 (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) | // Set analog bias currents to +12.5%
aidan1971 8:9fdf8501d14b 538 (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT)) // Set small pop ramp normal
aidan1971 8:9fdf8501d14b 539 ))
aidan1971 9:40e0ff8c2ba2 540 ) return -3;
aidan1971 8:9fdf8501d14b 541 if(
aidan1971 8:9fdf8501d14b 542 write_i2c(SGTL5000_LINE_OUT_CTRL,(
aidan1971 8:9fdf8501d14b 543 (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0x3 << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) | // Set Lineout bias curent 0.36mA
aidan1971 8:9fdf8501d14b 544 (SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_MASK & (0x22 << SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_SHIFT)) // Lineout AGND = 1.65v (line out will drive between the rails, so set at VDDA/2)
aidan1971 8:9fdf8501d14b 545 ))
aidan1971 9:40e0ff8c2ba2 546 ) return -4;
aidan1971 8:9fdf8501d14b 547 if(
aidan1971 8:9fdf8501d14b 548 write_i2c(SGTL5000_SHORT_CTRL,(
aidan1971 8:9fdf8501d14b 549 (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) | // HP R short detect 125mA
aidan1971 8:9fdf8501d14b 550 (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) | // HP L short detect 125mA
aidan1971 8:9fdf8501d14b 551 (SGTL5000_SHORT_CTRL_LVLADJC_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJC_SHIFT)) | // Headphone capless short detect 250mA
aidan1971 8:9fdf8501d14b 552 (SGTL5000_SHORT_CTRL_MODE_LR_MASK & (0x1 << SGTL5000_SHORT_CTRL_MODE_LR_SHIFT)) | // en short det reset 50mS
aidan1971 8:9fdf8501d14b 553 (SGTL5000_SHORT_CTRL_MODE_CM_MASK & (0x2 << SGTL5000_SHORT_CTRL_MODE_CM_SHIFT)) // En capless short det reset vol rise
aidan1971 8:9fdf8501d14b 554 ))
aidan1971 9:40e0ff8c2ba2 555 ) return -5;
aidan1971 8:9fdf8501d14b 556 if(
aidan1971 8:9fdf8501d14b 557 write_i2c(SGTL5000_ANA_CTRL,(
aidan1971 8:9fdf8501d14b 558 (SGTL5000_ANA_CTRL_MUTE_LO_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_LO_SHIFT)) | // Mute LINEOUT
aidan1971 8:9fdf8501d14b 559 (SGTL5000_ANA_CTRL_SELECT_HP_MASK & (0x0 << SGTL5000_ANA_CTRL_SELECT_HP_SHIFT)) | // Select DAC as input to HP amp
aidan1971 8:9fdf8501d14b 560 (SGTL5000_ANA_CTRL_EN_ZCD_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_HP_SHIFT)) | // Enable ZCD on HP
aidan1971 8:9fdf8501d14b 561 (SGTL5000_ANA_CTRL_MUTE_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_HP_SHIFT)) | // Mute the headphones
aidan1971 8:9fdf8501d14b 562 (SGTL5000_ANA_CTRL_SELECT_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_SELECT_ADC_SHIFT)) | // Select LINEIN as input to ADC
aidan1971 8:9fdf8501d14b 563 (SGTL5000_ANA_CTRL_EN_ZCD_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_ADC_SHIFT)) | // Enable ADC ZCD
aidan1971 8:9fdf8501d14b 564 (SGTL5000_ANA_CTRL_MUTE_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_ADC_SHIFT)) // Mute ADC pre-amp
aidan1971 8:9fdf8501d14b 565 ))
aidan1971 9:40e0ff8c2ba2 566 ) return -6;
aidan1971 9:40e0ff8c2ba2 567 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK)) return -7; // Power up Headphone amp
aidan1971 9:40e0ff8c2ba2 568 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK)) return -8; // Power up DAC
aidan1971 9:40e0ff8c2ba2 569 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK)) return -9; // Power up capless HP block
aidan1971 10:49bb33f71d32 570 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK)) return -10; // Power up ADC
aidan1971 10:49bb33f71d32 571 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK)) return -11; // Power up Lineout Amp
aidan1971 10:49bb33f71d32 572 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
aidan1971 0:8f28f25e3435 573
aidan1971 8:9fdf8501d14b 574 if(
aidan1971 8:9fdf8501d14b 575 write_i2c(SGTL5000_DIG_POWER,(
aidan1971 8:9fdf8501d14b 576 (SGTL5000_DIG_POWER_ADC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_ADC_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 577 (SGTL5000_DIG_POWER_DAC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAC_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 578 (SGTL5000_DIG_POWER_DAP_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAP_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 579 (SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_OUT_POWERUP_SHIFT)) | // Keep I2S data lines disabled until config complete
aidan1971 8:9fdf8501d14b 580 (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT))
aidan1971 8:9fdf8501d14b 581 ))
aidan1971 9:40e0ff8c2ba2 582 ) return -13;
aidan1971 10:49bb33f71d32 583 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
aidan1971 10:49bb33f71d32 584 (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
aidan1971 0:8f28f25e3435 585 ));
aidan1971 8:9fdf8501d14b 586 if(
aidan1971 8:9fdf8501d14b 587 write_i2c(SGTL5000_PAD_STRENGTH,(
aidan1971 8:9fdf8501d14b 588 (SGTL5000_PAD_STRENGTH_I2S_LRCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_LRCLK_SHIFT)) | // I2S LR_CLK drive dissabled (codec is slave)
aidan1971 8:9fdf8501d14b 589 (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) | // I2S SCLK drive dissabled (codec is slave)
aidan1971 8:9fdf8501d14b 590 (SGTL5000_PAD_STRENGTH_I2S_DOUT_MASK & (0x1 << SGTL5000_PAD_STRENGTH_I2S_DOUT_SHIFT)) | // I2S DOUT drive @ 4.02mA
aidan1971 8:9fdf8501d14b 591 (SGTL5000_PAD_STRENGTH_CTRL_DATA_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_DATA_SHIFT)) | // I2C DATA drive @ 12.05mA.
aidan1971 8:9fdf8501d14b 592 (SGTL5000_PAD_STRENGTH_CTRL_CLK_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_CLK_SHIFT)) // I2C CLOCK drive @ 12.05mA.
aidan1971 8:9fdf8501d14b 593 ))
aidan1971 9:40e0ff8c2ba2 594 )return -14;
aidan1971 8:9fdf8501d14b 595 if(
aidan1971 8:9fdf8501d14b 596 write_i2c(SGTL5000_CLK_CTRL,(
aidan1971 8:9fdf8501d14b 597 (SGTL5000_CLK_CTRL_RATE_MODE_MASK & (0x0 << SGTL5000_CLK_CTRL_RATE_MODE_SHIFT)) | // Sample rate mode Fs = SYS_FS
aidan1971 8:9fdf8501d14b 598 (SGTL5000_CLK_CTRL_SYS_FS_MASK & (0x02 << SGTL5000_CLK_CTRL_SYS_FS_SHIFT)) | // Set SYS_FS (sampling rate depending on mode) @ 48Khz
aidan1971 8:9fdf8501d14b 599 (SGTL5000_CLK_CTRL_MCLK_FREQ_MASK & (0x0 << SGTL5000_CLK_CTRL_MCLK_FREQ_SHIFT)) // Set SYS MCLK @ 256*Fs
aidan1971 8:9fdf8501d14b 600 ))
aidan1971 9:40e0ff8c2ba2 601 ) return -15;
aidan1971 8:9fdf8501d14b 602 if(
aidan1971 8:9fdf8501d14b 603 write_i2c(SGTL5000_I2S_CTRL,(
aidan1971 8:9fdf8501d14b 604 (SGTL5000_I2S_CTRL_SCLKFREQ_MASK & (0x1 << SGTL5000_I2S_CTRL_SCLKFREQ_SHIFT)) | // I2S SCLK 32*Fs
aidan1971 8:9fdf8501d14b 605 (SGTL5000_I2S_CTRL_MS_MASK & (0x0 << SGTL5000_I2S_CTRL_MS_SHIFT)) | // Slave
aidan1971 8:9fdf8501d14b 606 (SGTL5000_I2S_CTRL_SCLK_INV_MASK & (0x0 << SGTL5000_I2S_CTRL_SCLK_INV_SHIFT)) | // Data on riseing edge
aidan1971 8:9fdf8501d14b 607 (SGTL5000_I2S_CTRL_DLEN_MASK & (0x3 << SGTL5000_I2S_CTRL_DLEN_SHIFT)) | // Data 16bits
aidan1971 8:9fdf8501d14b 608 (SGTL5000_I2S_CTRL_MODE_MASK & (0x0 << SGTL5000_I2S_CTRL_MODE_SHIFT)) | // I2S mode
aidan1971 8:9fdf8501d14b 609 (SGTL5000_I2S_CTRL_LRALIGN_MASK & (0x0 << SGTL5000_I2S_CTRL_LRALIGN_SHIFT)) | // I2S format
aidan1971 8:9fdf8501d14b 610 (SGTL5000_I2S_CTRL_LRPOL_MASK & (0x0 << SGTL5000_I2S_CTRL_LRPOL_SHIFT)) // Left word on LRCLK low
aidan1971 8:9fdf8501d14b 611 ))
aidan1971 9:40e0ff8c2ba2 612 ) return -16;
aidan1971 8:9fdf8501d14b 613 if(
aidan1971 8:9fdf8501d14b 614 write_i2c(SGTL5000_SSS_CTRL,(
aidan1971 8:9fdf8501d14b 615 (SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_SHIFT)) | // Define all inputs un-swapped
aidan1971 8:9fdf8501d14b 616 (SGTL5000_SSS_CTRL_DAP_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 617 (SGTL5000_SSS_CTRL_DAC_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAC_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 618 (SGTL5000_SSS_CTRL_I2S_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 619 (SGTL5000_SSS_CTRL_DAP_MIX_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_SELECT_SHIFT)) | // DAP mixer source ADC
aidan1971 8:9fdf8501d14b 620 (SGTL5000_SSS_CTRL_DAP_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_SELECT_SHIFT)) | // DAP Input source ADC
aidan1971 8:9fdf8501d14b 621 (SGTL5000_SSS_CTRL_DAC_SELECT_MASK & (0x1 << SGTL5000_SSS_CTRL_DAC_SELECT_SHIFT)) | // DAC Input source I2SIN
aidan1971 8:9fdf8501d14b 622 (SGTL5000_SSS_CTRL_I2S_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_SELECT_SHIFT)) // I2SOUT source ADC
aidan1971 8:9fdf8501d14b 623 ))
aidan1971 9:40e0ff8c2ba2 624 ) return -17;
aidan1971 8:9fdf8501d14b 625 if(
aidan1971 8:9fdf8501d14b 626 write_i2c(SGTL5000_ADCDAC_CTRL,(
aidan1971 8:9fdf8501d14b 627 (SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_SHIFT)) | // Disable Vol ramp
aidan1971 8:9fdf8501d14b 628 (SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_SHIFT)) |
aidan1971 8:9fdf8501d14b 629 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_SHIFT)) |
aidan1971 8:9fdf8501d14b 630 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_SHIFT)) |
aidan1971 8:9fdf8501d14b 631 (SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_SHIFT)) | // ADC HPF normal operation
aidan1971 8:9fdf8501d14b 632 (SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_SHIFT)) // ADC HPF enabled
aidan1971 8:9fdf8501d14b 633 ))
aidan1971 9:40e0ff8c2ba2 634 ) return -18;
aidan1971 8:9fdf8501d14b 635 if(
aidan1971 8:9fdf8501d14b 636 write_i2c(SGTL5000_DAC_VOL,(
aidan1971 8:9fdf8501d14b 637 (SGTL5000_DAC_VOL_DAC_VOL_RIGHT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_RIGHT_SHIFT)) | // DAC R Vol 0dB
aidan1971 8:9fdf8501d14b 638 (SGTL5000_DAC_VOL_DAC_VOL_LEFT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_LEFT_SHIFT)) // DAC L Vol 0dB
aidan1971 8:9fdf8501d14b 639 ))
aidan1971 9:40e0ff8c2ba2 640 ) return -19;
aidan1971 8:9fdf8501d14b 641 if(
aidan1971 8:9fdf8501d14b 642 write_i2c(SGTL5000_ANA_HP_CTRL,(
aidan1971 8:9fdf8501d14b 643 (SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_SHIFT)) | // HP Vol set minimum
aidan1971 8:9fdf8501d14b 644 (SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_SHIFT))
aidan1971 8:9fdf8501d14b 645 ))
aidan1971 9:40e0ff8c2ba2 646 ) return -20;
aidan1971 9:40e0ff8c2ba2 647 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK)) return -21; // Un-mute Lineout
aidan1971 9:40e0ff8c2ba2 648 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK)) return -22; // Un-mute HP
aidan1971 9:40e0ff8c2ba2 649 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK)) return -23; // Un-mute ADC pre-amp
aidan1971 9:40e0ff8c2ba2 650 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -24; // I2S DOUT enable
aidan1971 9:40e0ff8c2ba2 651 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -25; // I2S DIN enable
aidan1971 10:49bb33f71d32 652
aidan1971 10:49bb33f71d32 653 if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) SGTL5000::codec1_active = true;
aidan1971 10:49bb33f71d32 654 if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) SGTL5000::codec2_active = true;
aidan1971 10:49bb33f71d32 655
aidan1971 8:9fdf8501d14b 656 return 0;
aidan1971 0:8f28f25e3435 657 }
aidan1971 0:8f28f25e3435 658
aidan1971 0:8f28f25e3435 659
aidan1971 0:8f28f25e3435 660 int32_t SGTL5000::attach_SYNC(Callback<void()> func)
aidan1971 0:8f28f25e3435 661 {
aidan1971 10:49bb33f71d32 662 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC.
aidan1971 0:8f28f25e3435 663 if(SYNC_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 664 SGTL5000::SYNC_user_func = func;
aidan1971 10:49bb33f71d32 665 SGTL5000::SYNC_attach_type = 0;
aidan1971 0:8f28f25e3435 666 SYNC_attached = true;
aidan1971 0:8f28f25e3435 667 return 0;
aidan1971 0:8f28f25e3435 668 }
aidan1971 0:8f28f25e3435 669
aidan1971 10:49bb33f71d32 670
aidan1971 10:49bb33f71d32 671 int32_t SGTL5000::attach_SYNC_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 672 {
aidan1971 10:49bb33f71d32 673 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 0:8f28f25e3435 674 SGTL5000::SYNC_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 10:49bb33f71d32 675 NVIC_SetVector(SGTL5000::SYNC_swIRQ, (uint32_t)user_ISR);
aidan1971 0:8f28f25e3435 676 NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 677 NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
aidan1971 10:49bb33f71d32 678 SGTL5000::SYNC_attach_type = 1;
aidan1971 0:8f28f25e3435 679 SYNC_attached = true;
aidan1971 8:9fdf8501d14b 680 return 0;
aidan1971 0:8f28f25e3435 681 }
aidan1971 0:8f28f25e3435 682
aidan1971 8:9fdf8501d14b 683 int32_t SGTL5000::detach_SYNC(void)
aidan1971 0:8f28f25e3435 684 {
aidan1971 10:49bb33f71d32 685 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 8:9fdf8501d14b 686 if(SYNC_attached) {
aidan1971 10:49bb33f71d32 687 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
aidan1971 10:49bb33f71d32 688 else SGTL5000::stop_SYNC(); // When using callback we must stop SYNC channel before we detach.
aidan1971 8:9fdf8501d14b 689 SYNC_attached = false;
aidan1971 8:9fdf8501d14b 690 }
aidan1971 8:9fdf8501d14b 691 return 0;
aidan1971 0:8f28f25e3435 692 }
aidan1971 0:8f28f25e3435 693
aidan1971 10:49bb33f71d32 694 void SGTL5000::stream_ctrl_ISR(void)
aidan1971 0:8f28f25e3435 695 {
aidan1971 10:49bb33f71d32 696 switch(SGTL5000::ctrl_command) {
aidan1971 10:49bb33f71d32 697 case STOP_SYNC:
aidan1971 10:49bb33f71d32 698 I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to stop stream
aidan1971 10:49bb33f71d32 699 I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to stop stream
aidan1971 10:49bb33f71d32 700 while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 701 while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 702 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO DMA request based on watermark
aidan1971 10:49bb33f71d32 703 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO DMA request based on watermark
aidan1971 10:49bb33f71d32 704 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 705 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3); // Disable TX channels 0 & 1.
aidan1971 10:49bb33f71d32 706 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3); // Disable RX channels 0 & 1.
aidan1971 10:49bb33f71d32 707 } else {
aidan1971 10:49bb33f71d32 708 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel 0.
aidan1971 10:49bb33f71d32 709 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channel 0
aidan1971 10:49bb33f71d32 710 }
aidan1971 10:49bb33f71d32 711 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 10:49bb33f71d32 712 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 10:49bb33f71d32 713 while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Ensure TX & RX have stopped.
aidan1971 10:49bb33f71d32 714 SGTL5000::SYNC_run = false;
aidan1971 10:49bb33f71d32 715 break;
aidan1971 10:49bb33f71d32 716 case PAUSE_SYNC:
aidan1971 10:49bb33f71d32 717 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 10:49bb33f71d32 718 while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 719 I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to halt stream
aidan1971 10:49bb33f71d32 720 I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream
aidan1971 10:49bb33f71d32 721 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
aidan1971 10:49bb33f71d32 722 break;
aidan1971 10:49bb33f71d32 723 case RESUME_SYNC:
aidan1971 10:49bb33f71d32 724 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 10:49bb33f71d32 725 while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 726 I2S0->RMR = 0x0; // Unmask RX channels to resume stream
aidan1971 10:49bb33f71d32 727 I2S0->TMR = 0x0; // Unmask TX channels to resume stream
aidan1971 10:49bb33f71d32 728 break;
aidan1971 10:49bb33f71d32 729 case STOP_TX:
aidan1971 10:49bb33f71d32 730 I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream
aidan1971 10:49bb33f71d32 731 while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 732 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO DMA request based on watermark
aidan1971 10:49bb33f71d32 733 if(SGTL5000::codec2_active) I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3); // Disable TX channel 0 & 1.
aidan1971 10:49bb33f71d32 734 else (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel 0.
aidan1971 10:49bb33f71d32 735 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 10:49bb33f71d32 736 while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Ensure TX has stopped.
aidan1971 10:49bb33f71d32 737 SGTL5000::TX_run = false;
aidan1971 10:49bb33f71d32 738 break;
aidan1971 10:49bb33f71d32 739 case PAUSE_TX:
aidan1971 10:49bb33f71d32 740 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 10:49bb33f71d32 741 while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 742 I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream
aidan1971 10:49bb33f71d32 743 while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 744 break;
aidan1971 10:49bb33f71d32 745 case RESUME_TX:
aidan1971 10:49bb33f71d32 746 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 10:49bb33f71d32 747 while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 748 I2S0->TMR = 0x0; // Clear TX mask to start stream
aidan1971 10:49bb33f71d32 749 break;
aidan1971 10:49bb33f71d32 750 case STOP_RX:
aidan1971 10:49bb33f71d32 751 I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to halt stream
aidan1971 10:49bb33f71d32 752 while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 753 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO DMA request based on watermark
aidan1971 10:49bb33f71d32 754 if(SGTL5000::codec2_active) I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3); // Disable RX channel 0 & 1.
aidan1971 10:49bb33f71d32 755 else (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channel 0.
aidan1971 10:49bb33f71d32 756 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 10:49bb33f71d32 757 while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Ensure RX has stopped.
aidan1971 10:49bb33f71d32 758 SGTL5000::RX_run = false;
aidan1971 10:49bb33f71d32 759 break;
aidan1971 10:49bb33f71d32 760 case PAUSE_RX:
aidan1971 10:49bb33f71d32 761 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 10:49bb33f71d32 762 while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 763 I2S0->RMR = 0xFFFFFFFF; // Mask TX channels to halt stream
aidan1971 10:49bb33f71d32 764 while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 10:49bb33f71d32 765 break;
aidan1971 10:49bb33f71d32 766 case RESUME_RX:
aidan1971 10:49bb33f71d32 767 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 10:49bb33f71d32 768 while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start
aidan1971 10:49bb33f71d32 769 I2S0->RMR = 0x0; // Unmask TX channels to resume stream
aidan1971 10:49bb33f71d32 770 break;
aidan1971 10:49bb33f71d32 771 }
aidan1971 10:49bb33f71d32 772 SGTL5000::ctrl_command = 0;
aidan1971 10:49bb33f71d32 773 }
aidan1971 10:49bb33f71d32 774
aidan1971 10:49bb33f71d32 775
aidan1971 10:49bb33f71d32 776 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,
aidan1971 10:49bb33f71d32 777 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)
aidan1971 10:49bb33f71d32 778 {
aidan1971 10:49bb33f71d32 779 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 780 if(SGTL5000::SYNC_run || SGTL5000::TX_run || SGTL5000::RX_run ) return -1; // Check if i2s is already started
aidan1971 10:49bb33f71d32 781 if(!SYNC_attached && !SGTL5000::SYNC_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 782 if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 10:49bb33f71d32 783 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 10:49bb33f71d32 784 // Enable I2S interfaces for active codecs
aidan1971 10:49bb33f71d32 785 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
aidan1971 10:49bb33f71d32 786 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
aidan1971 10:49bb33f71d32 787 }
aidan1971 10:49bb33f71d32 788 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
aidan1971 10:49bb33f71d32 789 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
aidan1971 9:40e0ff8c2ba2 790 }
aidan1971 10:49bb33f71d32 791 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 792 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
aidan1971 10:49bb33f71d32 793 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
aidan1971 10:49bb33f71d32 794 }
aidan1971 10:49bb33f71d32 795 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
aidan1971 10:49bb33f71d32 796 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
aidan1971 10:49bb33f71d32 797 }
aidan1971 9:40e0ff8c2ba2 798 }
aidan1971 4:91354c908416 799 packed_RX = _packed_RX;
aidan1971 4:91354c908416 800 packed_TX = _packed_TX;
aidan1971 9:40e0ff8c2ba2 801 TX_block_size = _block_size;
aidan1971 6:4ab5aaeaa064 802 RX_block_size = TX_block_size;
aidan1971 9:40e0ff8c2ba2 803 TX_bs_bytes = _block_size * 4;
aidan1971 0:8f28f25e3435 804 RX_bs_bytes = TX_bs_bytes;
aidan1971 4:91354c908416 805 if(packed_RX) RX_shift = false;
aidan1971 4:91354c908416 806 else RX_shift = _RX_shift;
aidan1971 4:91354c908416 807 if(packed_TX) TX_shift = false;
aidan1971 4:91354c908416 808 else TX_shift = _TX_shift;
aidan1971 0:8f28f25e3435 809 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 0:8f28f25e3435 810 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 10:49bb33f71d32 811 SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch;
aidan1971 10:49bb33f71d32 812 SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch;
aidan1971 10:49bb33f71d32 813
aidan1971 10:49bb33f71d32 814 SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe; // Assign the users pointer addresses codec 1
aidan1971 10:49bb33f71d32 815 SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe;
aidan1971 10:49bb33f71d32 816 SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe;
aidan1971 10:49bb33f71d32 817 SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
aidan1971 10:49bb33f71d32 818 SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 819 SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 820 SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
aidan1971 10:49bb33f71d32 821 SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 822 SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 10:49bb33f71d32 823 SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 824 SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 10:49bb33f71d32 825 SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 826
aidan1971 10:49bb33f71d32 827 *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 828 *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_R_safeB;
aidan1971 10:49bb33f71d32 829 *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB;
aidan1971 10:49bb33f71d32 830 *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB;
aidan1971 0:8f28f25e3435 831
aidan1971 10:49bb33f71d32 832 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 833 SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2; // Assign the users pointer addresses codec 2
aidan1971 10:49bb33f71d32 834 SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2;
aidan1971 10:49bb33f71d32 835 SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2;
aidan1971 10:49bb33f71d32 836 SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2;
aidan1971 10:49bb33f71d32 837 SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 838 SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 839 SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0];
aidan1971 10:49bb33f71d32 840 SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 841 SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
aidan1971 10:49bb33f71d32 842 SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 843 SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
aidan1971 10:49bb33f71d32 844 SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 845 *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 846 *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_R_safeB2;
aidan1971 10:49bb33f71d32 847 *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2;
aidan1971 10:49bb33f71d32 848 *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2;
aidan1971 10:49bb33f71d32 849 }
aidan1971 10:49bb33f71d32 850
aidan1971 10:49bb33f71d32 851 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 10:49bb33f71d32 852 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 10:49bb33f71d32 853 I2S0->TMR = 0xFFFFFFFF; // Mask TX & RX traffic to prevent buffer under/overflow before we aquire sync.
aidan1971 10:49bb33f71d32 854 I2S0->RMR = 0xFFFFFFFF;
aidan1971 9:40e0ff8c2ba2 855 I2S0->TCSR |= I2S_TCSR_SR_MASK; // Reset TX & RX logic
aidan1971 9:40e0ff8c2ba2 856 I2S0->RCSR |= I2S_RCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 857 I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 858 I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 859 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark
aidan1971 9:40e0ff8c2ba2 860 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark
aidan1971 10:49bb33f71d32 861 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 10:49bb33f71d32 862 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 10:49bb33f71d32 863
aidan1971 10:49bb33f71d32 864 while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Make sure TX & RX are disabled before enabling corresponding channels
aidan1971 9:40e0ff8c2ba2 865
aidan1971 10:49bb33f71d32 866 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::SYNC_TX_dma_ISR); // Set TX_DMAch transfer complete vector
aidan1971 10:49bb33f71d32 867 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::SYNC_RX_dma_ISR); // Set RX_DMAch transfer complete vector
aidan1971 10:49bb33f71d32 868 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priorities the same
aidan1971 10:49bb33f71d32 869 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri);
aidan1971 10:49bb33f71d32 870 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
aidan1971 10:49bb33f71d32 871 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);
aidan1971 10:49bb33f71d32 872 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 873 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3); // Enable TX channels 0 & 1.
aidan1971 10:49bb33f71d32 874 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3); // Enable RX channels 0 & 1
aidan1971 6:4ab5aaeaa064 875 } else {
aidan1971 10:49bb33f71d32 876 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 10:49bb33f71d32 877 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0
aidan1971 2:a9d8242f76ea 878 }
aidan1971 9:40e0ff8c2ba2 879
aidan1971 9:40e0ff8c2ba2 880 NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_WS_ISR); // Set vector for SYNC word start ISR
aidan1971 2:a9d8242f76ea 881 NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of SYNC word start ISR
aidan1971 2:a9d8242f76ea 882 NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable SYNC word start ISR using RX direction
aidan1971 9:40e0ff8c2ba2 883 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 9:40e0ff8c2ba2 884 I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ
aidan1971 10:49bb33f71d32 885
aidan1971 10:49bb33f71d32 886 SGTL5000::db_phase_sync = 0;
aidan1971 10:49bb33f71d32 887 SGTL5000::SYNC_run = true;
aidan1971 10:49bb33f71d32 888 init_DMA();
aidan1971 10:49bb33f71d32 889 I2S0->TCSR |= I2S_TCSR_TE_MASK; // Start TX
aidan1971 10:49bb33f71d32 890 I2S0->RCSR |= I2S_RCSR_RE_MASK; // Start RX
aidan1971 10:49bb33f71d32 891 return 0;
aidan1971 10:49bb33f71d32 892 }
aidan1971 10:49bb33f71d32 893
aidan1971 10:49bb33f71d32 894 int32_t SGTL5000::pause_SYNC(void)
aidan1971 10:49bb33f71d32 895 {
aidan1971 10:49bb33f71d32 896 if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 897 SGTL5000::ctrl_command = PAUSE_SYNC;
aidan1971 10:49bb33f71d32 898 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 899 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 10:49bb33f71d32 900 return 0;
aidan1971 10:49bb33f71d32 901 }
aidan1971 10:49bb33f71d32 902
aidan1971 10:49bb33f71d32 903 int32_t SGTL5000::resume_SYNC(void)
aidan1971 10:49bb33f71d32 904 {
aidan1971 10:49bb33f71d32 905 if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 906 SGTL5000::ctrl_command = RESUME_SYNC;
aidan1971 10:49bb33f71d32 907 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 908 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 0:8f28f25e3435 909 return 0;
aidan1971 0:8f28f25e3435 910 }
aidan1971 0:8f28f25e3435 911
aidan1971 8:9fdf8501d14b 912 int32_t SGTL5000::stop_SYNC(void)
aidan1971 0:8f28f25e3435 913 {
aidan1971 10:49bb33f71d32 914 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 915 modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK); // codec I2S data lines tristate
aidan1971 10:49bb33f71d32 916 modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK);
aidan1971 10:49bb33f71d32 917 if(SGTL5000::codec2_active) { // codec2 I2S data lines tristate
aidan1971 10:49bb33f71d32 918 modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
aidan1971 10:49bb33f71d32 919 modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
aidan1971 10:49bb33f71d32 920 }
aidan1971 10:49bb33f71d32 921 SGTL5000::ctrl_command = STOP_SYNC;
aidan1971 10:49bb33f71d32 922 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 923 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 8:9fdf8501d14b 924 return 0;
aidan1971 0:8f28f25e3435 925 }
aidan1971 0:8f28f25e3435 926
aidan1971 9:40e0ff8c2ba2 927 void SGTL5000::sync_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 928 {
aidan1971 9:40e0ff8c2ba2 929 I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs
aidan1971 0:8f28f25e3435 930 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 0:8f28f25e3435 931 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 10:49bb33f71d32 932
aidan1971 10:49bb33f71d32 933 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
aidan1971 10:49bb33f71d32 934 I2S0->TDR[0] = 0;
aidan1971 10:49bb33f71d32 935 }
aidan1971 10:49bb33f71d32 936 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 937 for(uint32_t i = 0; i <= ((I2S0->RCR1 & I2S_RCR1_RFW_MASK) >> I2S_RCR1_RFW_SHIFT); ++i) {
aidan1971 10:49bb33f71d32 938 I2S0->TDR[1] = 0;
aidan1971 10:49bb33f71d32 939 }
aidan1971 10:49bb33f71d32 940 }
aidan1971 10:49bb33f71d32 941 I2S0->TMR = 0x0; // Clear TX & RX Masks to start data flowing
aidan1971 10:49bb33f71d32 942 I2S0->RMR = 0x0;
aidan1971 0:8f28f25e3435 943 }
aidan1971 0:8f28f25e3435 944
aidan1971 10:49bb33f71d32 945 void SGTL5000::SYNC_TX_dma_ISR(void)
aidan1971 0:8f28f25e3435 946 {
aidan1971 10:49bb33f71d32 947 SGTL5000::t_stamp_stop();
aidan1971 10:49bb33f71d32 948 register uint32_t ACKS;
aidan1971 10:49bb33f71d32 949 register uint32_t TX_IRQ_BM;
aidan1971 10:49bb33f71d32 950 register uint32_t DMA_INT;
aidan1971 10:49bb33f71d32 951 __asm {
aidan1971 10:49bb33f71d32 952 LDR TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm]
aidan1971 10:49bb33f71d32 953 LDR DMA_INT, [&DMA0->INT]
aidan1971 10:49bb33f71d32 954 LDR ACKS, [&sync_dma_irq_acks]
aidan1971 10:49bb33f71d32 955
aidan1971 10:49bb33f71d32 956 ANDS TX_IRQ_BM, TX_IRQ_BM, DMA_INT
aidan1971 10:49bb33f71d32 957 ITT NE
aidan1971 10:49bb33f71d32 958 ORRNE ACKS, #1
aidan1971 10:49bb33f71d32 959 STRNE TX_IRQ_BM, [&DMA0->INT]
aidan1971 10:49bb33f71d32 960
aidan1971 10:49bb33f71d32 961 TEQ ACKS, #3
aidan1971 10:49bb33f71d32 962 IT NE
aidan1971 10:49bb33f71d32 963 BNE finish_isr
aidan1971 10:49bb33f71d32 964 MOV ACKS, #0
aidan1971 10:49bb33f71d32 965 BL SGTL5000::SYNC_pointer_swap
aidan1971 10:49bb33f71d32 966
aidan1971 10:49bb33f71d32 967 finish_isr:
aidan1971 10:49bb33f71d32 968 STR ACKS, [&sync_dma_irq_acks]
aidan1971 10:49bb33f71d32 969 }
aidan1971 10:49bb33f71d32 970 }
aidan1971 0:8f28f25e3435 971
aidan1971 10:49bb33f71d32 972 void SGTL5000::SYNC_RX_dma_ISR(void)
aidan1971 10:49bb33f71d32 973 {
aidan1971 10:49bb33f71d32 974 SGTL5000::t_stamp_start();
aidan1971 10:49bb33f71d32 975 register uint32_t ACKS;
aidan1971 10:49bb33f71d32 976 register uint32_t RX_IRQ_BM;
aidan1971 10:49bb33f71d32 977 register uint32_t DMA_INT;
aidan1971 10:49bb33f71d32 978 __asm {
aidan1971 10:49bb33f71d32 979 LDR RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm]
aidan1971 10:49bb33f71d32 980 LDR DMA_INT, [&DMA0->INT]
aidan1971 10:49bb33f71d32 981 LDR ACKS, [&sync_dma_irq_acks]
aidan1971 10:49bb33f71d32 982
aidan1971 10:49bb33f71d32 983 ANDS RX_IRQ_BM, RX_IRQ_BM, DMA_INT
aidan1971 10:49bb33f71d32 984 ITT NE
aidan1971 10:49bb33f71d32 985 ORRNE ACKS, #2
aidan1971 10:49bb33f71d32 986 STRNE RX_IRQ_BM, [&DMA0->INT]
aidan1971 0:8f28f25e3435 987
aidan1971 10:49bb33f71d32 988 TEQ ACKS, #3
aidan1971 10:49bb33f71d32 989 IT NE
aidan1971 10:49bb33f71d32 990 BNE finish_isr
aidan1971 10:49bb33f71d32 991 MOV ACKS, #0
aidan1971 10:49bb33f71d32 992 BL SGTL5000::SYNC_pointer_swap
aidan1971 0:8f28f25e3435 993
aidan1971 10:49bb33f71d32 994 finish_isr:
aidan1971 10:49bb33f71d32 995 STR ACKS, [&sync_dma_irq_acks]
aidan1971 10:49bb33f71d32 996 }
aidan1971 10:49bb33f71d32 997 }
aidan1971 10:49bb33f71d32 998
aidan1971 10:49bb33f71d32 999
aidan1971 10:49bb33f71d32 1000 void SGTL5000::SYNC_pointer_swap(void)
aidan1971 10:49bb33f71d32 1001 {
aidan1971 10:49bb33f71d32 1002 register uint32_t CODEC2;
aidan1971 10:49bb33f71d32 1003 register uint32_t IRQNR;
aidan1971 10:49bb33f71d32 1004 register uint32_t DSP_BUSY;
aidan1971 10:49bb33f71d32 1005 register uint32_t DB_PHASE;
aidan1971 10:49bb33f71d32 1006 register uint32_t TYPE;
aidan1971 6:4ab5aaeaa064 1007 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 1008 register uint32_t BU_RX_R;
aidan1971 6:4ab5aaeaa064 1009 register uint32_t BU_TX_L;
aidan1971 6:4ab5aaeaa064 1010 register uint32_t BU_TX_R;
aidan1971 10:49bb33f71d32 1011 register uint32_t BU_RX_L2;
aidan1971 10:49bb33f71d32 1012 register uint32_t BU_RX_R2;
aidan1971 10:49bb33f71d32 1013 register uint32_t BU_TX_L2;
aidan1971 10:49bb33f71d32 1014 register uint32_t BU_TX_R2;
aidan1971 6:4ab5aaeaa064 1015 __asm {
aidan1971 9:40e0ff8c2ba2 1016 LDR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 10:49bb33f71d32 1017 LDR TYPE, [&SGTL5000::SYNC_attach_type]
aidan1971 10:49bb33f71d32 1018 LDRB CODEC2, [&SGTL5000::codec2_active]
aidan1971 10:49bb33f71d32 1019 TEQ CODEC2, #0x1
aidan1971 10:49bb33f71d32 1020 IT EQ
aidan1971 10:49bb33f71d32 1021 BEQ dual_codecs
aidan1971 6:4ab5aaeaa064 1022
aidan1971 6:4ab5aaeaa064 1023 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 1024 IT EQ
aidan1971 6:4ab5aaeaa064 1025 BEQ buf_base
aidan1971 6:4ab5aaeaa064 1026
aidan1971 10:49bb33f71d32 1027 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB]
aidan1971 10:49bb33f71d32 1028 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB]
aidan1971 10:49bb33f71d32 1029 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB]
aidan1971 10:49bb33f71d32 1030 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB]
aidan1971 6:4ab5aaeaa064 1031 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1032 B store
aidan1971 6:4ab5aaeaa064 1033
aidan1971 6:4ab5aaeaa064 1034 buf_base:
aidan1971 10:49bb33f71d32 1035 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA]
aidan1971 10:49bb33f71d32 1036 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA]
aidan1971 10:49bb33f71d32 1037 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA]
aidan1971 10:49bb33f71d32 1038 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA]
aidan1971 6:4ab5aaeaa064 1039 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1040
aidan1971 6:4ab5aaeaa064 1041 store:
aidan1971 6:4ab5aaeaa064 1042 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 1043 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 1044 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 1045 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 1046 STR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 10:49bb33f71d32 1047 B finish_update
aidan1971 10:49bb33f71d32 1048
aidan1971 10:49bb33f71d32 1049
aidan1971 10:49bb33f71d32 1050 dual_codecs:
aidan1971 10:49bb33f71d32 1051 TEQ DB_PHASE, #0x0
aidan1971 10:49bb33f71d32 1052 IT EQ
aidan1971 10:49bb33f71d32 1053 BEQ buf_base2
aidan1971 10:49bb33f71d32 1054
aidan1971 10:49bb33f71d32 1055 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB]
aidan1971 10:49bb33f71d32 1056 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB]
aidan1971 10:49bb33f71d32 1057 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB]
aidan1971 10:49bb33f71d32 1058 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB]
aidan1971 10:49bb33f71d32 1059 LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeB2]
aidan1971 10:49bb33f71d32 1060 LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeB2]
aidan1971 10:49bb33f71d32 1061 LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeB2]
aidan1971 10:49bb33f71d32 1062 LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeB2]
aidan1971 10:49bb33f71d32 1063 SUB DB_PHASE, #0x1
aidan1971 10:49bb33f71d32 1064 B store2
aidan1971 10:49bb33f71d32 1065
aidan1971 10:49bb33f71d32 1066 buf_base2:
aidan1971 10:49bb33f71d32 1067 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA]
aidan1971 10:49bb33f71d32 1068 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA]
aidan1971 10:49bb33f71d32 1069 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA]
aidan1971 10:49bb33f71d32 1070 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA]
aidan1971 10:49bb33f71d32 1071 LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeA2]
aidan1971 10:49bb33f71d32 1072 LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeA2]
aidan1971 10:49bb33f71d32 1073 LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeA2]
aidan1971 10:49bb33f71d32 1074 LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeA2]
aidan1971 10:49bb33f71d32 1075 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1076
aidan1971 10:49bb33f71d32 1077 store2:
aidan1971 10:49bb33f71d32 1078 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 10:49bb33f71d32 1079 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 10:49bb33f71d32 1080 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 10:49bb33f71d32 1081 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 10:49bb33f71d32 1082 STR BU_RX_L2, [SGTL5000::BufRX_L_safe2] // Pipeline memory access
aidan1971 10:49bb33f71d32 1083 STR BU_RX_R2, [SGTL5000::BufRX_R_safe2]
aidan1971 10:49bb33f71d32 1084 STR BU_TX_L2, [SGTL5000::BufTX_L_safe2]
aidan1971 10:49bb33f71d32 1085 STR BU_TX_R2, [SGTL5000::BufTX_R_safe2]
aidan1971 10:49bb33f71d32 1086 STR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 6:4ab5aaeaa064 1087
aidan1971 10:49bb33f71d32 1088 finish_update:
aidan1971 10:49bb33f71d32 1089 TEQ TYPE, #1
aidan1971 10:49bb33f71d32 1090 IT EQ
aidan1971 10:49bb33f71d32 1091 BEQ call_sync_nb
aidan1971 10:49bb33f71d32 1092 }
aidan1971 10:49bb33f71d32 1093 SGTL5000::SYNC_user_func.call();
aidan1971 10:49bb33f71d32 1094 __asm {
aidan1971 10:49bb33f71d32 1095 B finish_isr
aidan1971 10:49bb33f71d32 1096
aidan1971 10:49bb33f71d32 1097 call_sync_nb:
aidan1971 10:49bb33f71d32 1098 LDRB IRQNR, [&SGTL5000::SYNC_swIRQ]
aidan1971 10:49bb33f71d32 1099 BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
aidan1971 10:49bb33f71d32 1100 TEQ DSP_BUSY, #1
aidan1971 10:49bb33f71d32 1101 IT EQ
aidan1971 10:49bb33f71d32 1102 BEQ finish_isr
aidan1971 10:49bb33f71d32 1103 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!
aidan1971 10:49bb33f71d32 1104
aidan1971 10:49bb33f71d32 1105 finish_isr:
aidan1971 10:49bb33f71d32 1106
aidan1971 10:49bb33f71d32 1107 }
aidan1971 0:8f28f25e3435 1108 }
aidan1971 0:8f28f25e3435 1109
aidan1971 0:8f28f25e3435 1110
aidan1971 0:8f28f25e3435 1111 int32_t SGTL5000::attach_TX(Callback<void()> func)
aidan1971 0:8f28f25e3435 1112 {
aidan1971 10:49bb33f71d32 1113 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC.
aidan1971 10:49bb33f71d32 1114 if(TX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 1115 SGTL5000::TX_user_func = func;
aidan1971 10:49bb33f71d32 1116 SGTL5000::TX_attach_type = 0;
aidan1971 0:8f28f25e3435 1117 TX_attached = true;
aidan1971 0:8f28f25e3435 1118 return 0;
aidan1971 0:8f28f25e3435 1119 }
aidan1971 0:8f28f25e3435 1120
aidan1971 10:49bb33f71d32 1121
aidan1971 10:49bb33f71d32 1122 int32_t SGTL5000::attach_TX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 1123 {
aidan1971 10:49bb33f71d32 1124 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1125 SGTL5000::TX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 10:49bb33f71d32 1126 NVIC_SetVector(SGTL5000::TX_swIRQ, (uint32_t)user_ISR);
aidan1971 0:8f28f25e3435 1127 NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 1128 NVIC_EnableIRQ(SGTL5000::TX_swIRQ);
aidan1971 10:49bb33f71d32 1129 SGTL5000::TX_attach_type = 1;
aidan1971 0:8f28f25e3435 1130 TX_attached = true;
aidan1971 8:9fdf8501d14b 1131 return 0;
aidan1971 0:8f28f25e3435 1132 }
aidan1971 0:8f28f25e3435 1133
aidan1971 10:49bb33f71d32 1134
aidan1971 8:9fdf8501d14b 1135 int32_t SGTL5000::detach_TX(void)
aidan1971 0:8f28f25e3435 1136 {
aidan1971 10:49bb33f71d32 1137 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 8:9fdf8501d14b 1138 if(TX_attached) {
aidan1971 10:49bb33f71d32 1139 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
aidan1971 10:49bb33f71d32 1140 else SGTL5000::stop_TX(); // When using callback we must stop TX channel before we detach.
aidan1971 8:9fdf8501d14b 1141 TX_attached = false;
aidan1971 8:9fdf8501d14b 1142 }
aidan1971 8:9fdf8501d14b 1143 return 0;
aidan1971 0:8f28f25e3435 1144 }
aidan1971 0:8f28f25e3435 1145
aidan1971 9:40e0ff8c2ba2 1146 int32_t SGTL5000::start_TX(uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe,
aidan1971 10:49bb33f71d32 1147 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)
aidan1971 0:8f28f25e3435 1148 {
aidan1971 10:49bb33f71d32 1149 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1150 if(SGTL5000::SYNC_run || SGTL5000::TX_run ) return -1; // Check if i2s TX is already started
aidan1971 10:49bb33f71d32 1151 if(!TX_attached && !SGTL5000::TX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 1152 if(_TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 10:49bb33f71d32 1153 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 10:49bb33f71d32 1154 // Enable I2S TX interfaces for active codecs
aidan1971 10:49bb33f71d32 1155 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
aidan1971 10:49bb33f71d32 1156 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
aidan1971 10:49bb33f71d32 1157 }
aidan1971 10:49bb33f71d32 1158 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1159 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
aidan1971 10:49bb33f71d32 1160 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
aidan1971 10:49bb33f71d32 1161 }
aidan1971 10:49bb33f71d32 1162 }
aidan1971 9:40e0ff8c2ba2 1163
aidan1971 10:49bb33f71d32 1164 packed_TX = _packed_TX;
aidan1971 9:40e0ff8c2ba2 1165 TX_block_size = _block_size;
aidan1971 9:40e0ff8c2ba2 1166 TX_bs_bytes = _block_size * 4;
aidan1971 4:91354c908416 1167 if(packed_TX) TX_shift = false;
aidan1971 4:91354c908416 1168 else TX_shift = _TX_shift;
aidan1971 0:8f28f25e3435 1169 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 10:49bb33f71d32 1170 SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch;
aidan1971 10:49bb33f71d32 1171
aidan1971 10:49bb33f71d32 1172 SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe; // Assign the users pointer addresses codec 1
aidan1971 10:49bb33f71d32 1173 SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
aidan1971 10:49bb33f71d32 1174 SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 1175 SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 1176 SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 10:49bb33f71d32 1177 SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 1178
aidan1971 10:49bb33f71d32 1179 *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 1180 *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB;
aidan1971 0:8f28f25e3435 1181
aidan1971 10:49bb33f71d32 1182 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1183 SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2; // Assign the users pointer addresses codec 2
aidan1971 10:49bb33f71d32 1184 SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2;
aidan1971 10:49bb33f71d32 1185 SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 1186 SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 1187 SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
aidan1971 10:49bb33f71d32 1188 SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)];
aidan1971 10:49bb33f71d32 1189 *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 1190 *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2;
aidan1971 10:49bb33f71d32 1191 }
aidan1971 10:49bb33f71d32 1192
aidan1971 10:49bb33f71d32 1193 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 10:49bb33f71d32 1194 I2S0->TMR = 0xFFFFFFFF; // Mask TX traffic to prevent buffer underflow before we aquire sync.
aidan1971 10:49bb33f71d32 1195 I2S0->TCSR |= I2S_TCSR_SR_MASK; // Reset TX logic
aidan1971 9:40e0ff8c2ba2 1196 I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
aidan1971 6:4ab5aaeaa064 1197 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark
aidan1971 10:49bb33f71d32 1198 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 10:49bb33f71d32 1199
aidan1971 9:40e0ff8c2ba2 1200 while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Make sure TX is disabled before enabling corresponding channel
aidan1971 10:49bb33f71d32 1201
aidan1971 10:49bb33f71d32 1202 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::TX_dma_ISR); // Set TX_DMAch transfer complete vector
aidan1971 10:49bb33f71d32 1203 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priority
aidan1971 10:49bb33f71d32 1204 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
aidan1971 10:49bb33f71d32 1205 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1206 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3); // Enable TX channels 0 & 1.
aidan1971 10:49bb33f71d32 1207 } else {
aidan1971 10:49bb33f71d32 1208 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 10:49bb33f71d32 1209 }
aidan1971 10:49bb33f71d32 1210
aidan1971 10:49bb33f71d32 1211 NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_WS_ISR); // Set vector for TX word start ISR
aidan1971 0:8f28f25e3435 1212 NVIC_SetPriority(I2S0_Tx_IRQn, 0); // Set priority of TX word start ISR
aidan1971 0:8f28f25e3435 1213 NVIC_EnableIRQ(I2S0_Tx_IRQn); // Enable TX word start ISR
aidan1971 0:8f28f25e3435 1214 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 0:8f28f25e3435 1215 I2S0->TCSR |= I2S_TCSR_WSIE_MASK; // Enable I2S TX word start IRQ
aidan1971 10:49bb33f71d32 1216
aidan1971 10:49bb33f71d32 1217 SGTL5000::db_phase_tx = 0;
aidan1971 10:49bb33f71d32 1218 SGTL5000::TX_run = true;
aidan1971 10:49bb33f71d32 1219 init_DMA();
aidan1971 10:49bb33f71d32 1220 I2S0->TCSR |= I2S_TCSR_TE_MASK; // TX Enable
aidan1971 0:8f28f25e3435 1221 return 0;
aidan1971 0:8f28f25e3435 1222 }
aidan1971 0:8f28f25e3435 1223
aidan1971 10:49bb33f71d32 1224 int32_t SGTL5000::pause_TX(void)
aidan1971 10:49bb33f71d32 1225 {
aidan1971 10:49bb33f71d32 1226 if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1227 SGTL5000::ctrl_command = PAUSE_TX;
aidan1971 10:49bb33f71d32 1228 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1229 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 10:49bb33f71d32 1230 return 0;
aidan1971 10:49bb33f71d32 1231 }
aidan1971 10:49bb33f71d32 1232
aidan1971 10:49bb33f71d32 1233 int32_t SGTL5000::resume_TX(void)
aidan1971 10:49bb33f71d32 1234 {
aidan1971 10:49bb33f71d32 1235 if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1236 SGTL5000::ctrl_command = RESUME_TX;
aidan1971 10:49bb33f71d32 1237 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1238 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 10:49bb33f71d32 1239 return 0;
aidan1971 10:49bb33f71d32 1240 }
aidan1971 10:49bb33f71d32 1241
aidan1971 10:49bb33f71d32 1242
aidan1971 8:9fdf8501d14b 1243 int32_t SGTL5000::stop_TX(void)
aidan1971 0:8f28f25e3435 1244 {
aidan1971 10:49bb33f71d32 1245 if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1246 modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK); // codec I2S data lines tristate
aidan1971 10:49bb33f71d32 1247 if(SGTL5000::codec2_active) modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1));
aidan1971 10:49bb33f71d32 1248 SGTL5000::ctrl_command = STOP_TX;
aidan1971 10:49bb33f71d32 1249 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1250 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 8:9fdf8501d14b 1251 return 0;
aidan1971 0:8f28f25e3435 1252 }
aidan1971 0:8f28f25e3435 1253
aidan1971 10:49bb33f71d32 1254 void SGTL5000::tx_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 1255 {
aidan1971 10:49bb33f71d32 1256 I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK; // Disable TX word start IRQs
aidan1971 10:49bb33f71d32 1257 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 10:49bb33f71d32 1258 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.
aidan1971 10:49bb33f71d32 1259 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.
aidan1971 10:49bb33f71d32 1260 } // This can be critical as at 192Ks/s we only have 5nS after starting TX fifo and an underrun, unless we stuff.
aidan1971 10:49bb33f71d32 1261 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1262 for(uint32_t i = 1; i <= (8 - ((I2S0->TCR1 & I2S_TCR1_TFW_MASK) >> I2S_TCR1_TFW_SHIFT)); ++i) {
aidan1971 10:49bb33f71d32 1263 I2S0->TDR[1] = 0;
aidan1971 10:49bb33f71d32 1264 }
aidan1971 10:49bb33f71d32 1265 }
aidan1971 10:49bb33f71d32 1266 I2S0->TMR = 0x0; // Clear TX Mask to start data flowing
aidan1971 10:49bb33f71d32 1267 }
aidan1971 0:8f28f25e3435 1268
aidan1971 10:49bb33f71d32 1269 void SGTL5000::TX_dma_ISR(void)
aidan1971 10:49bb33f71d32 1270 {
aidan1971 10:49bb33f71d32 1271 register uint32_t TX_IRQ_BM;
aidan1971 10:49bb33f71d32 1272 register uint32_t DMA_INT;
aidan1971 10:49bb33f71d32 1273 register uint32_t CODEC2;
aidan1971 10:49bb33f71d32 1274 register uint32_t IRQNR;
aidan1971 10:49bb33f71d32 1275 register uint32_t DSP_BUSY;
aidan1971 10:49bb33f71d32 1276 register uint32_t DB_PHASE;
aidan1971 10:49bb33f71d32 1277 register uint32_t TYPE;
aidan1971 6:4ab5aaeaa064 1278 register uint32_t BU_TX_L;
aidan1971 6:4ab5aaeaa064 1279 register uint32_t BU_TX_R;
aidan1971 10:49bb33f71d32 1280 register uint32_t BU_TX_L2;
aidan1971 10:49bb33f71d32 1281 register uint32_t BU_TX_R2;
aidan1971 6:4ab5aaeaa064 1282 __asm {
aidan1971 10:49bb33f71d32 1283 LDR TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm]
aidan1971 10:49bb33f71d32 1284 LDR DMA_INT, [&DMA0->INT]
aidan1971 6:4ab5aaeaa064 1285
aidan1971 10:49bb33f71d32 1286 AND TX_IRQ_BM, TX_IRQ_BM, DMA_INT // Clear DMA IRQ flag
aidan1971 10:49bb33f71d32 1287 STR TX_IRQ_BM, [&DMA0->INT]
aidan1971 9:40e0ff8c2ba2 1288
aidan1971 10:49bb33f71d32 1289 LDR DB_PHASE, [&SGTL5000::db_phase_tx]
aidan1971 10:49bb33f71d32 1290 LDR TYPE, [&SGTL5000::TX_attach_type]
aidan1971 10:49bb33f71d32 1291 LDRB CODEC2, [&SGTL5000::codec2_active]
aidan1971 10:49bb33f71d32 1292 TEQ CODEC2, #0x1
aidan1971 10:49bb33f71d32 1293 IT EQ
aidan1971 10:49bb33f71d32 1294 BEQ dual_codecs
aidan1971 9:40e0ff8c2ba2 1295
aidan1971 9:40e0ff8c2ba2 1296 TEQ DB_PHASE, #0x0
aidan1971 9:40e0ff8c2ba2 1297 IT EQ
aidan1971 9:40e0ff8c2ba2 1298 BEQ buf_base
aidan1971 9:40e0ff8c2ba2 1299
aidan1971 10:49bb33f71d32 1300 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB]
aidan1971 10:49bb33f71d32 1301 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB]
aidan1971 9:40e0ff8c2ba2 1302 SUB DB_PHASE, #0x1
aidan1971 9:40e0ff8c2ba2 1303 B store
aidan1971 9:40e0ff8c2ba2 1304
aidan1971 9:40e0ff8c2ba2 1305 buf_base:
aidan1971 10:49bb33f71d32 1306 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA]
aidan1971 10:49bb33f71d32 1307 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA]
aidan1971 9:40e0ff8c2ba2 1308 ADD DB_PHASE, #0x1
aidan1971 9:40e0ff8c2ba2 1309
aidan1971 10:49bb33f71d32 1310 store:
aidan1971 10:49bb33f71d32 1311 STR BU_TX_L, [SGTL5000::BufTX_L_safe] // Pipeline memory access
aidan1971 9:40e0ff8c2ba2 1312 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 1313 STR DB_PHASE, [&SGTL5000::db_phase_tx]
aidan1971 10:49bb33f71d32 1314 B finish_update
aidan1971 10:49bb33f71d32 1315
aidan1971 10:49bb33f71d32 1316 dual_codecs:
aidan1971 10:49bb33f71d32 1317 TEQ DB_PHASE, #0x0
aidan1971 10:49bb33f71d32 1318 IT EQ
aidan1971 10:49bb33f71d32 1319 BEQ buf_base2
aidan1971 10:49bb33f71d32 1320
aidan1971 10:49bb33f71d32 1321 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB]
aidan1971 10:49bb33f71d32 1322 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB]
aidan1971 10:49bb33f71d32 1323 LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeB2]
aidan1971 10:49bb33f71d32 1324 LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeB2]
aidan1971 10:49bb33f71d32 1325 SUB DB_PHASE, #0x1
aidan1971 10:49bb33f71d32 1326 B store2
aidan1971 10:49bb33f71d32 1327
aidan1971 10:49bb33f71d32 1328 buf_base2:
aidan1971 10:49bb33f71d32 1329 LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA]
aidan1971 10:49bb33f71d32 1330 LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA]
aidan1971 10:49bb33f71d32 1331 LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeA2]
aidan1971 10:49bb33f71d32 1332 LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeA2]
aidan1971 10:49bb33f71d32 1333 ADD DB_PHASE, #0x1
aidan1971 10:49bb33f71d32 1334
aidan1971 10:49bb33f71d32 1335 store2:
aidan1971 10:49bb33f71d32 1336 STR BU_TX_L, [SGTL5000::BufTX_L_safe] // Pipeline memory access
aidan1971 10:49bb33f71d32 1337 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 10:49bb33f71d32 1338 STR BU_TX_L2, [SGTL5000::BufTX_L_safe2]
aidan1971 10:49bb33f71d32 1339 STR BU_TX_R2, [SGTL5000::BufTX_R_safe2]
aidan1971 10:49bb33f71d32 1340 STR DB_PHASE, [&SGTL5000::db_phase_tx]
aidan1971 10:49bb33f71d32 1341
aidan1971 10:49bb33f71d32 1342 finish_update:
aidan1971 10:49bb33f71d32 1343 TEQ TYPE, #1
aidan1971 10:49bb33f71d32 1344 IT EQ
aidan1971 10:49bb33f71d32 1345 BEQ call_tx_nb
aidan1971 6:4ab5aaeaa064 1346 }
aidan1971 10:49bb33f71d32 1347 SGTL5000::TX_user_func.call();
aidan1971 10:49bb33f71d32 1348 __asm {
aidan1971 10:49bb33f71d32 1349 B finish_isr
aidan1971 10:49bb33f71d32 1350
aidan1971 10:49bb33f71d32 1351 call_tx_nb:
aidan1971 10:49bb33f71d32 1352 LDRB IRQNR, [&SGTL5000::TX_swIRQ]
aidan1971 10:49bb33f71d32 1353 BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
aidan1971 10:49bb33f71d32 1354 TEQ DSP_BUSY, #1
aidan1971 10:49bb33f71d32 1355 IT EQ
aidan1971 10:49bb33f71d32 1356 BEQ finish_isr
aidan1971 10:49bb33f71d32 1357 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!
aidan1971 10:49bb33f71d32 1358
aidan1971 10:49bb33f71d32 1359 finish_isr:
aidan1971 10:49bb33f71d32 1360
aidan1971 10:49bb33f71d32 1361 }
aidan1971 0:8f28f25e3435 1362 }
aidan1971 0:8f28f25e3435 1363
aidan1971 0:8f28f25e3435 1364 int32_t SGTL5000::attach_RX(Callback<void()> func)
aidan1971 0:8f28f25e3435 1365 {
aidan1971 10:49bb33f71d32 1366 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC.
aidan1971 10:49bb33f71d32 1367 if(RX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 1368 SGTL5000::RX_user_func = func;
aidan1971 10:49bb33f71d32 1369 SGTL5000::RX_attach_type = 0;
aidan1971 0:8f28f25e3435 1370 RX_attached = true;
aidan1971 0:8f28f25e3435 1371 return 0;
aidan1971 0:8f28f25e3435 1372 }
aidan1971 0:8f28f25e3435 1373
aidan1971 10:49bb33f71d32 1374 int32_t SGTL5000::attach_RX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 1375 {
aidan1971 10:49bb33f71d32 1376 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1377 SGTL5000::RX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 10:49bb33f71d32 1378 NVIC_SetVector(SGTL5000::RX_swIRQ, (uint32_t)user_ISR);
aidan1971 0:8f28f25e3435 1379 NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 1380 NVIC_EnableIRQ(SGTL5000::RX_swIRQ);
aidan1971 10:49bb33f71d32 1381 SGTL5000::RX_attach_type = 1;
aidan1971 0:8f28f25e3435 1382 RX_attached = true;
aidan1971 8:9fdf8501d14b 1383 return 0;
aidan1971 0:8f28f25e3435 1384 }
aidan1971 0:8f28f25e3435 1385
aidan1971 10:49bb33f71d32 1386
aidan1971 8:9fdf8501d14b 1387 int32_t SGTL5000::detach_RX(void)
aidan1971 0:8f28f25e3435 1388 {
aidan1971 10:49bb33f71d32 1389 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 8:9fdf8501d14b 1390 if(RX_attached) {
aidan1971 10:49bb33f71d32 1391 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
aidan1971 10:49bb33f71d32 1392 else SGTL5000::stop_RX(); // When using callback we must stop RX channel before we detach.
aidan1971 8:9fdf8501d14b 1393 RX_attached = false;
aidan1971 8:9fdf8501d14b 1394 }
aidan1971 8:9fdf8501d14b 1395 return 0;
aidan1971 0:8f28f25e3435 1396 }
aidan1971 0:8f28f25e3435 1397
aidan1971 10:49bb33f71d32 1398 int32_t SGTL5000::start_RX(uint32_t _BufRX_L_safe, uint32_t _BufRX_R_safe,
aidan1971 10:49bb33f71d32 1399 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)
aidan1971 0:8f28f25e3435 1400 {
aidan1971 10:49bb33f71d32 1401 if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1402 if(SGTL5000::SYNC_run || SGTL5000::RX_run ) return -1; // Check if i2s RX is already started
aidan1971 10:49bb33f71d32 1403 if(!RX_attached && !SGTL5000::RX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 1404 if(_RX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 10:49bb33f71d32 1405 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 10:49bb33f71d32 1406 // Enable I2S RX interfaces for active codecs
aidan1971 10:49bb33f71d32 1407 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) {
aidan1971 10:49bb33f71d32 1408 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1;
aidan1971 9:40e0ff8c2ba2 1409 }
aidan1971 10:49bb33f71d32 1410 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1411 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) {
aidan1971 10:49bb33f71d32 1412 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1;
aidan1971 10:49bb33f71d32 1413 }
aidan1971 10:49bb33f71d32 1414 }
aidan1971 10:49bb33f71d32 1415 packed_RX = _packed_RX;
aidan1971 9:40e0ff8c2ba2 1416 RX_block_size = _block_size;
aidan1971 9:40e0ff8c2ba2 1417 RX_bs_bytes = _block_size * 4;
aidan1971 4:91354c908416 1418 if(packed_RX) RX_shift = false;
aidan1971 4:91354c908416 1419 else RX_shift = _RX_shift;
aidan1971 0:8f28f25e3435 1420 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 10:49bb33f71d32 1421 SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch;
aidan1971 10:49bb33f71d32 1422
aidan1971 10:49bb33f71d32 1423 SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe; // Assign the users pointer addresses codec 1
aidan1971 10:49bb33f71d32 1424 SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe;
aidan1971 10:49bb33f71d32 1425 SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 1426 SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 1427 SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 10:49bb33f71d32 1428 SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 1429
aidan1971 10:49bb33f71d32 1430 *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 1431 *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_L_safeB;
aidan1971 0:8f28f25e3435 1432
aidan1971 10:49bb33f71d32 1433 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1434 SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2; // Assign the users pointer addresses codec 2
aidan1971 10:49bb33f71d32 1435 SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2;
aidan1971 10:49bb33f71d32 1436 SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0]; // Precalculate double buffer addresses
aidan1971 10:49bb33f71d32 1437 SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 1438 SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
aidan1971 10:49bb33f71d32 1439 SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)];
aidan1971 10:49bb33f71d32 1440 *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2; // Assign starting addresses to double buffer pointers
aidan1971 10:49bb33f71d32 1441 *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_L_safeB2;
aidan1971 10:49bb33f71d32 1442 }
aidan1971 10:49bb33f71d32 1443
aidan1971 10:49bb33f71d32 1444 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // TX Disable
aidan1971 10:49bb33f71d32 1445 I2S0->RMR = 0xFFFFFFFF; // Mask RX traffic to prevent buffer underflow before we aquire sync.
aidan1971 10:49bb33f71d32 1446 I2S0->RCSR |= I2S_RCSR_SR_MASK; // Reset RX logic
aidan1971 9:40e0ff8c2ba2 1447 I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
aidan1971 6:4ab5aaeaa064 1448 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark
aidan1971 10:49bb33f71d32 1449 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 10:49bb33f71d32 1450
aidan1971 9:40e0ff8c2ba2 1451 while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Make sure RX is disabled before enabling corresponding channel
aidan1971 10:49bb33f71d32 1452
aidan1971 10:49bb33f71d32 1453 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::RX_dma_ISR); // Set RX_DMAch transfer complete vector
aidan1971 10:49bb33f71d32 1454 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri); // Set irq priority
aidan1971 10:49bb33f71d32 1455 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Enable RX DMA complete IRQs
aidan1971 10:49bb33f71d32 1456 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1457 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3); // Enable RX channels 0 & 1.
aidan1971 10:49bb33f71d32 1458 } else {
aidan1971 10:49bb33f71d32 1459 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0.
aidan1971 10:49bb33f71d32 1460 }
aidan1971 10:49bb33f71d32 1461
aidan1971 10:49bb33f71d32 1462 NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_WS_ISR); // Set vector for RX word start ISR
aidan1971 0:8f28f25e3435 1463 NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of RX word start ISR
aidan1971 0:8f28f25e3435 1464 NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable RX word start ISR
aidan1971 0:8f28f25e3435 1465 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 0:8f28f25e3435 1466 I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ
aidan1971 10:49bb33f71d32 1467
aidan1971 10:49bb33f71d32 1468 SGTL5000::db_phase_rx = 0;
aidan1971 10:49bb33f71d32 1469 SGTL5000::RX_run = true;
aidan1971 10:49bb33f71d32 1470 init_DMA();
aidan1971 10:49bb33f71d32 1471 I2S0->RCSR |= I2S_RCSR_RE_MASK; // RX Enable
aidan1971 10:49bb33f71d32 1472 return 0;
aidan1971 10:49bb33f71d32 1473 }
aidan1971 10:49bb33f71d32 1474
aidan1971 10:49bb33f71d32 1475 int32_t SGTL5000::pause_RX(void)
aidan1971 10:49bb33f71d32 1476 {
aidan1971 10:49bb33f71d32 1477 if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1478 SGTL5000::ctrl_command = PAUSE_RX;
aidan1971 10:49bb33f71d32 1479 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1480 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 10:49bb33f71d32 1481 return 0;
aidan1971 10:49bb33f71d32 1482 }
aidan1971 10:49bb33f71d32 1483
aidan1971 10:49bb33f71d32 1484 int32_t SGTL5000::resume_RX(void)
aidan1971 10:49bb33f71d32 1485 {
aidan1971 10:49bb33f71d32 1486 if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1487 SGTL5000::ctrl_command = RESUME_RX;
aidan1971 10:49bb33f71d32 1488 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1489 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 0:8f28f25e3435 1490 return 0;
aidan1971 0:8f28f25e3435 1491 }
aidan1971 0:8f28f25e3435 1492
aidan1971 8:9fdf8501d14b 1493 int32_t SGTL5000::stop_RX(void)
aidan1971 0:8f28f25e3435 1494 {
aidan1971 10:49bb33f71d32 1495 if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow.
aidan1971 10:49bb33f71d32 1496 if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1; // codec I2S data lines tristate
aidan1971 10:49bb33f71d32 1497 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;
aidan1971 10:49bb33f71d32 1498 SGTL5000::ctrl_command = STOP_RX;
aidan1971 10:49bb33f71d32 1499 NVIC->STIR = CODEC_CTRL_IRQ;
aidan1971 10:49bb33f71d32 1500 while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ));
aidan1971 8:9fdf8501d14b 1501 return 0;
aidan1971 0:8f28f25e3435 1502 }
aidan1971 0:8f28f25e3435 1503
aidan1971 10:49bb33f71d32 1504
aidan1971 10:49bb33f71d32 1505 void SGTL5000::rx_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 1506 {
aidan1971 10:49bb33f71d32 1507 I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs
aidan1971 10:49bb33f71d32 1508 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 10:49bb33f71d32 1509 I2S0->RMR = 0x0; // Clear RX Mask to start data flowing
aidan1971 10:49bb33f71d32 1510 }
aidan1971 0:8f28f25e3435 1511
aidan1971 10:49bb33f71d32 1512 void SGTL5000::RX_dma_ISR(void)
aidan1971 10:49bb33f71d32 1513 {
aidan1971 10:49bb33f71d32 1514 register uint32_t RX_IRQ_BM;
aidan1971 10:49bb33f71d32 1515 register uint32_t DMA_INT;
aidan1971 10:49bb33f71d32 1516 register uint32_t CODEC2;
aidan1971 10:49bb33f71d32 1517 register uint32_t IRQNR;
aidan1971 10:49bb33f71d32 1518 register uint32_t DSP_BUSY;
aidan1971 10:49bb33f71d32 1519 register uint32_t DB_PHASE;
aidan1971 10:49bb33f71d32 1520 register uint32_t TYPE;
aidan1971 6:4ab5aaeaa064 1521 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 1522 register uint32_t BU_RX_R;
aidan1971 10:49bb33f71d32 1523 register uint32_t BU_RX_L2;
aidan1971 10:49bb33f71d32 1524 register uint32_t BU_RX_R2;
aidan1971 6:4ab5aaeaa064 1525 __asm {
aidan1971 10:49bb33f71d32 1526 LDR RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm]
aidan1971 10:49bb33f71d32 1527 LDR DMA_INT, [&DMA0->INT]
aidan1971 10:49bb33f71d32 1528
aidan1971 10:49bb33f71d32 1529 AND RX_IRQ_BM, RX_IRQ_BM, DMA_INT // Clear DMA IRQ flag
aidan1971 10:49bb33f71d32 1530 STR RX_IRQ_BM, [&DMA0->INT]
aidan1971 10:49bb33f71d32 1531
aidan1971 10:49bb33f71d32 1532 LDR DB_PHASE, [&SGTL5000::db_phase_rx]
aidan1971 10:49bb33f71d32 1533 LDR TYPE, [&SGTL5000::RX_attach_type]
aidan1971 10:49bb33f71d32 1534 LDRB CODEC2, [&SGTL5000::codec2_active]
aidan1971 10:49bb33f71d32 1535 TEQ CODEC2, #0x1
aidan1971 10:49bb33f71d32 1536 IT EQ
aidan1971 10:49bb33f71d32 1537 BEQ dual_codecs
aidan1971 6:4ab5aaeaa064 1538
aidan1971 6:4ab5aaeaa064 1539 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 1540 IT EQ
aidan1971 6:4ab5aaeaa064 1541 BEQ buf_base
aidan1971 6:4ab5aaeaa064 1542
aidan1971 10:49bb33f71d32 1543 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB]
aidan1971 10:49bb33f71d32 1544 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB]
aidan1971 6:4ab5aaeaa064 1545 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1546 B store
aidan1971 6:4ab5aaeaa064 1547
aidan1971 6:4ab5aaeaa064 1548 buf_base:
aidan1971 10:49bb33f71d32 1549 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA]
aidan1971 10:49bb33f71d32 1550 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA]
aidan1971 6:4ab5aaeaa064 1551 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1552
aidan1971 10:49bb33f71d32 1553 store:
aidan1971 10:49bb33f71d32 1554 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 1555 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 9:40e0ff8c2ba2 1556 STR DB_PHASE, [&SGTL5000::db_phase_rx]
aidan1971 10:49bb33f71d32 1557 B finish_update
aidan1971 6:4ab5aaeaa064 1558
aidan1971 10:49bb33f71d32 1559 dual_codecs:
aidan1971 6:4ab5aaeaa064 1560 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 1561 IT EQ
aidan1971 10:49bb33f71d32 1562 BEQ buf_base2
aidan1971 6:4ab5aaeaa064 1563
aidan1971 10:49bb33f71d32 1564 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB]
aidan1971 10:49bb33f71d32 1565 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB]
aidan1971 10:49bb33f71d32 1566 LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeB2]
aidan1971 10:49bb33f71d32 1567 LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeB2]
aidan1971 6:4ab5aaeaa064 1568 SUB DB_PHASE, #0x1
aidan1971 10:49bb33f71d32 1569 B store2
aidan1971 6:4ab5aaeaa064 1570
aidan1971 10:49bb33f71d32 1571 buf_base2:
aidan1971 10:49bb33f71d32 1572 LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA]
aidan1971 10:49bb33f71d32 1573 LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA]
aidan1971 10:49bb33f71d32 1574 LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeA2]
aidan1971 10:49bb33f71d32 1575 LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeA2]
aidan1971 6:4ab5aaeaa064 1576 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1577
aidan1971 10:49bb33f71d32 1578 store2:
aidan1971 10:49bb33f71d32 1579 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 1580 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 10:49bb33f71d32 1581 STR BU_RX_L2, [SGTL5000::BufRX_L_safe2]
aidan1971 10:49bb33f71d32 1582 STR BU_RX_R2, [SGTL5000::BufRX_R_safe2]
aidan1971 9:40e0ff8c2ba2 1583 STR DB_PHASE, [&SGTL5000::db_phase_rx]
aidan1971 10:49bb33f71d32 1584
aidan1971 10:49bb33f71d32 1585 finish_update:
aidan1971 10:49bb33f71d32 1586 TEQ TYPE, #1
aidan1971 10:49bb33f71d32 1587 IT EQ
aidan1971 10:49bb33f71d32 1588 BEQ call_rx_nb
aidan1971 6:4ab5aaeaa064 1589 }
aidan1971 10:49bb33f71d32 1590 SGTL5000::RX_user_func.call();
aidan1971 10:49bb33f71d32 1591 __asm {
aidan1971 10:49bb33f71d32 1592 B finish_isr
aidan1971 10:49bb33f71d32 1593
aidan1971 10:49bb33f71d32 1594 call_rx_nb:
aidan1971 10:49bb33f71d32 1595 LDRB IRQNR, [&SGTL5000::RX_swIRQ]
aidan1971 10:49bb33f71d32 1596 BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0}
aidan1971 10:49bb33f71d32 1597 TEQ DSP_BUSY, #1
aidan1971 10:49bb33f71d32 1598 IT EQ
aidan1971 10:49bb33f71d32 1599 BEQ finish_isr
aidan1971 10:49bb33f71d32 1600 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!
aidan1971 10:49bb33f71d32 1601
aidan1971 10:49bb33f71d32 1602 finish_isr:
aidan1971 10:49bb33f71d32 1603
aidan1971 10:49bb33f71d32 1604 }
aidan1971 0:8f28f25e3435 1605 }
aidan1971 0:8f28f25e3435 1606
aidan1971 0:8f28f25e3435 1607 void SGTL5000::init_DMA(void)
aidan1971 0:8f28f25e3435 1608 {
aidan1971 3:62c03088f256 1609 /*!
aidan1971 0:8f28f25e3435 1610 The DMA transfer uses 2 channels. 1 for RX data 1 for TX data. Each DMA splits the Left and Right channels across the respective buffer.
aidan1971 0:8f28f25e3435 1611 The chain is configured as follows:
aidan1971 0:8f28f25e3435 1612 Each minor loop moves 4 bytes of data (2 * 16bit words, 1 left 1 right channel). Bus reads/writes are 16bit and DOFF is used to jump forward BLOCK_SIZE/2 32bit words on each bus read or write respectively.
aidan1971 4:91354c908416 1613 At the end of a minor loop MLOFF jumps back (BLOCK_SIZE - 1) 32bit words or packed (BLOCK_SIZE - 0.5) words. At Major loop termination the source or dest address is reloaded with a new TCD which points to the source or dest respectively at the
aidan1971 0:8f28f25e3435 1614 uppper half of the double buffer.
aidan1971 0:8f28f25e3435 1615 eg.
aidan1971 4:91354c908416 1616 Unpacked buffer layout:
aidan1971 4:91354c908416 1617 Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 4:91354c908416 1618 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 4:91354c908416 1619 |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x| |L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x||L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x| |L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x||L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x|
aidan1971 6:4ab5aaeaa064 1620
aidan1971 4:91354c908416 1621 Packed buffer layout:
aidan1971 4:91354c908416 1622 Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 4:91354c908416 1623 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 4:91354c908416 1624 |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x| |L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x||L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x| |L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x||L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x|
aidan1971 6:4ab5aaeaa064 1625
aidan1971 4:91354c908416 1626 The users pointers are always updated to point to L0 & R0 of the current safe double buffer area.
aidan1971 0:8f28f25e3435 1627
aidan1971 10:49bb33f71d32 1628 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
aidan1971 10:49bb33f71d32 1629
aidan1971 0:8f28f25e3435 1630 */
aidan1971 0:8f28f25e3435 1631
aidan1971 10:49bb33f71d32 1632 static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000500))); // Allocate memory for scatter gather TCD definitions
aidan1971 10:49bb33f71d32 1633 static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000520)));
aidan1971 10:49bb33f71d32 1634 static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000540)));
aidan1971 10:49bb33f71d32 1635 static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000560)));
aidan1971 10:49bb33f71d32 1636 static uint32_t SG_rx_TCD_A2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000580))); // Allocate memory for scatter gather TCD2 definitions
aidan1971 10:49bb33f71d32 1637 static uint32_t SG_rx_TCD_B2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005A0)));
aidan1971 10:49bb33f71d32 1638 static uint32_t SG_tx_TCD_A2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005C0)));
aidan1971 10:49bb33f71d32 1639 static uint32_t SG_tx_TCD_B2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005E0)));
aidan1971 0:8f28f25e3435 1640
aidan1971 0:8f28f25e3435 1641 // Clock Control config to DMA Controller and DMAMux and common DMA Setup
aidan1971 0:8f28f25e3435 1642 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable Clocking to DMAMUX Module
aidan1971 0:8f28f25e3435 1643 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable Clocking to DMA Module
aidan1971 0:8f28f25e3435 1644 DMA0->CR |= DMA_CR_EMLM_MASK; // Enable minor loop mapping
aidan1971 0:8f28f25e3435 1645
aidan1971 10:49bb33f71d32 1646 if(SGTL5000::SYNC_run || SGTL5000::RX_run) {
aidan1971 0:8f28f25e3435 1647 // DMAMUX Config
aidan1971 0:8f28f25e3435 1648 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK; // MUX channels disabled while we configure
aidan1971 0:8f28f25e3435 1649 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK; // Trigger Modes Normal
aidan1971 10:49bb33f71d32 1650 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
aidan1971 10:49bb33f71d32 1651 DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::RX_DMAch); // Disable requests for chosen DMA channel first while its configured
aidan1971 10:49bb33f71d32 1652 //if(SGTL5000::codec2_active) DMA0->CERQ = DMA_CERQ_CERQ(RX_DMAch2);
aidan1971 0:8f28f25e3435 1653
aidan1971 10:49bb33f71d32 1654 //Configure RX DMA Transfer Control Descriptor for channel 0
aidan1971 0:8f28f25e3435 1655 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop
aidan1971 0:8f28f25e3435 1656 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop
aidan1971 4:91354c908416 1657 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
aidan1971 4:91354c908416 1658 else DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 4)); // After each minor loop step back to lower half of buffer and increment
aidan1971 0:8f28f25e3435 1659 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_NBYTES_MASK) | DMA_NBYTES_MLOFFYES_NBYTES(4); // 4 Bytes (2 * 16bits words one left one right channel) transferred in each minor cycle.
aidan1971 0:8f28f25e3435 1660 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DONE_MASK; // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
aidan1971 0:8f28f25e3435 1661 DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0); // Disable Bandwidth control
aidan1971 0:8f28f25e3435 1662 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 1663 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 0:8f28f25e3435 1664 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle
aidan1971 0:8f28f25e3435 1665 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
aidan1971 0:8f28f25e3435 1666 DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0; // Signed Source offset set to zero (always read from RDR[0]).
aidan1971 4:91354c908416 1667 DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 1668 DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1669 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
aidan1971 0:8f28f25e3435 1670 DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1671 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
aidan1971 10:49bb33f71d32 1672 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
aidan1971 0:8f28f25e3435 1673 DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;
aidan1971 10:49bb33f71d32 1674
aidan1971 10:49bb33f71d32 1675 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1676 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
aidan1971 10:49bb33f71d32 1677 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; // Enable major loop linking
aidan1971 10:49bb33f71d32 1678 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A2[0]; // Set scatter gather TCD definition location to transfer channel 2
aidan1971 10:49bb33f71d32 1679
aidan1971 10:49bb33f71d32 1680 } else {
aidan1971 10:49bb33f71d32 1681 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1682 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 10:49bb33f71d32 1683 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 10:49bb33f71d32 1684 }
aidan1971 10:49bb33f71d32 1685 if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
aidan1971 10:49bb33f71d32 1686 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
aidan1971 10:49bb33f71d32 1687 DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0]; // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
aidan1971 0:8f28f25e3435 1688 memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD A to memory
aidan1971 10:49bb33f71d32 1689 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 10:49bb33f71d32 1690 if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0] + 2;
aidan1971 10:49bb33f71d32 1691 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0];
aidan1971 10:49bb33f71d32 1692 DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1];
aidan1971 10:49bb33f71d32 1693 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1694 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1695 memcpy(&SG_rx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD B to memory
aidan1971 0:8f28f25e3435 1696 // Set TCD elements in the DMA controller back to initial TCD A
aidan1971 10:49bb33f71d32 1697 if(SGTL5000::codec2_active) {
aidan1971 10:49bb33f71d32 1698 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
aidan1971 10:49bb33f71d32 1699 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; // Enable major loop linking
aidan1971 10:49bb33f71d32 1700 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B2[0]; // Set scatter gather TCD definition location to transfer channel 2
aidan1971 10:49bb33f71d32 1701
aidan1971 10:49bb33f71d32 1702 } else {
aidan1971 10:49bb33f71d32 1703 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1704 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 10:49bb33f71d32 1705 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0]; // Set scatter gather TCD definition location
aidan1971 10:49bb33f71d32 1706 } // Set scatter gather TCD definition location
aidan1971 10:49bb33f71d32 1707 if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2;
aidan1971 10:49bb33f71d32 1708 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 10:49bb33f71d32 1709 DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0];
aidan1971 10:49bb33f71d32 1710 memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);
aidan1971 10:49bb33f71d32 1711 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1712 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1713 if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8] + 2;
aidan1971 10:49bb33f71d32 1714 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8];
aidan1971 10:49bb33f71d32 1715 DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1];
aidan1971 10:49bb33f71d32 1716 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0];
aidan1971 10:49bb33f71d32 1717 memcpy(&SG_rx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);
aidan1971 10:49bb33f71d32 1718
aidan1971 10:49bb33f71d32 1719 memcpy((void*)&DMA0->TCD[SGTL5000::RX_DMAch], &SG_rx_TCD_A[0], 32); // Initialise RX TCD to first SG definition
aidan1971 0:8f28f25e3435 1720 }
aidan1971 0:8f28f25e3435 1721
aidan1971 10:49bb33f71d32 1722 if(SGTL5000::SYNC_run || SGTL5000::TX_run) {
aidan1971 0:8f28f25e3435 1723 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK;
aidan1971 0:8f28f25e3435 1724 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK;
aidan1971 10:49bb33f71d32 1725 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.
aidan1971 10:49bb33f71d32 1726 DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch); // Disable requests for first DMA channel while its configured.
aidan1971 0:8f28f25e3435 1727
aidan1971 10:49bb33f71d32 1728 //Configure TX DMA Transfer Control Descriptor channel 1
aidan1971 10:49bb33f71d32 1729 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Enable source offset on minor loop
aidan1971 10:49bb33f71d32 1730 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Disable dest offset on minor loop
aidan1971 4:91354c908416 1731 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));
aidan1971 4:91354c908416 1732 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
aidan1971 0:8f28f25e3435 1733 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.
aidan1971 0:8f28f25e3435 1734 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DONE_MASK; // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
aidan1971 0:8f28f25e3435 1735 DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0); // Disable Bandwidth control
aidan1971 0:8f28f25e3435 1736 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 1737 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 10:49bb33f71d32 1738 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK;
aidan1971 10:49bb33f71d32 1739 DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_MAJORLINKCH_MASK) | DMA_CSR_MAJORLINKCH(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 1740 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
aidan1971 10:49bb33f71d32 1741 DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0; // Signed Dest offset set to zero (always write TDR[0]).
aidan1971 0:8f28f25e3435 1742 DMA0->TCD[SGTL5000::TX_DMAch].SOFF = (TX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 1743 DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1744 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
aidan1971 0:8f28f25e3435 1745 DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1746 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.
aidan1971 10:49bb33f71d32 1747 if(SGTL5000::codec2_active) { // Disable IRQ at Half Completion of Major cycle
aidan1971 10:49bb33f71d32 1748 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1749 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1750 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A2[0]; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1751 } else {
aidan1971 10:49bb33f71d32 1752 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1753 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1754 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];
aidan1971 10:49bb33f71d32 1755 }
aidan1971 10:49bb33f71d32 1756 if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
aidan1971 10:49bb33f71d32 1757 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
aidan1971 10:49bb33f71d32 1758 DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];
aidan1971 0:8f28f25e3435 1759 memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD A to memory
aidan1971 0:8f28f25e3435 1760 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 10:49bb33f71d32 1761 if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0] + 2;
aidan1971 10:49bb33f71d32 1762 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0];
aidan1971 10:49bb33f71d32 1763 DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1];
aidan1971 10:49bb33f71d32 1764 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1765 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1766 memcpy(&SG_tx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD B to memory
aidan1971 10:49bb33f71d32 1767 if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2;
aidan1971 10:49bb33f71d32 1768 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 10:49bb33f71d32 1769 DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];
aidan1971 10:49bb33f71d32 1770 if(SGTL5000::codec2_active) { // Disable IRQ at Half Completion of Major cycle
aidan1971 10:49bb33f71d32 1771 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1772 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1773 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B2[0]; // Enable IRQ at Completion of Major cycle for first channel
aidan1971 10:49bb33f71d32 1774 } else {
aidan1971 10:49bb33f71d32 1775 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1776 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1777 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0];
aidan1971 10:49bb33f71d32 1778 }
aidan1971 10:49bb33f71d32 1779 memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);
aidan1971 10:49bb33f71d32 1780 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0];
aidan1971 10:49bb33f71d32 1781 if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8] + 2;
aidan1971 10:49bb33f71d32 1782 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8];
aidan1971 10:49bb33f71d32 1783 DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1];
aidan1971 10:49bb33f71d32 1784 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;
aidan1971 10:49bb33f71d32 1785 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;
aidan1971 10:49bb33f71d32 1786 memcpy(&SG_tx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);
aidan1971 10:49bb33f71d32 1787
aidan1971 10:49bb33f71d32 1788 memcpy((void*)&DMA0->TCD[SGTL5000::TX_DMAch], &SG_tx_TCD_A[0], 32); // Initialise TX TCD to first SG definition
aidan1971 0:8f28f25e3435 1789 }
aidan1971 0:8f28f25e3435 1790
aidan1971 10:49bb33f71d32 1791 if(SGTL5000::SYNC_run || SGTL5000::RX_run) {
aidan1971 10:49bb33f71d32 1792 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch); // Enable requests for RX DMA channel 0
aidan1971 0:8f28f25e3435 1793 DMAMUX->CHCFG[SGTL5000::RX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 1794 }
aidan1971 10:49bb33f71d32 1795 if(SGTL5000::SYNC_run || SGTL5000::TX_run) {
aidan1971 0:8f28f25e3435 1796 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::TX_DMAch); // Enable requests for TX DMA channel
aidan1971 10:49bb33f71d32 1797 DMAMUX->CHCFG[SGTL5000::TX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 1798 }
aidan1971 0:8f28f25e3435 1799 }
aidan1971 0:8f28f25e3435 1800
aidan1971 10:49bb33f71d32 1801 int32_t SGTL5000::read_debug(uint32_t index, bool finished)
aidan1971 0:8f28f25e3435 1802 {
aidan1971 10:49bb33f71d32 1803 if(finished) SGTL5000::debug_read = true;
aidan1971 0:8f28f25e3435 1804 return SGTL5000::debug[index];
aidan1971 0:8f28f25e3435 1805 };
aidan1971 10:49bb33f71d32 1806
aidan1971 10:49bb33f71d32 1807 // Time stamp functions
aidan1971 10:49bb33f71d32 1808
aidan1971 10:49bb33f71d32 1809 void SGTL5000::t_stamp_start(void)
aidan1971 10:49bb33f71d32 1810 {
aidan1971 10:49bb33f71d32 1811 SGTL5000::t1 = *(uint32_t*)SGTL5000::SYST_CVAL;
aidan1971 10:49bb33f71d32 1812 }
aidan1971 10:49bb33f71d32 1813
aidan1971 10:49bb33f71d32 1814 void SGTL5000::t_stamp_stop(void)
aidan1971 10:49bb33f71d32 1815 {
aidan1971 10:49bb33f71d32 1816 SGTL5000::proc_time = SGTL5000::t1 - *(uint32_t*)SGTL5000::SYST_CVAL;
aidan1971 10:49bb33f71d32 1817 }
aidan1971 10:49bb33f71d32 1818
aidan1971 0:8f28f25e3435 1819 }