Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added multi-pin TDM support. #1

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 74 additions & 35 deletions input_tdm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@
#include "utility/imxrt_hw.h"

DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
audio_block_t * AudioInputTDM::block_incoming[16] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*TDM_IN_N_AUDIO_BLOCKS];
audio_block_t * AudioInputTDM::block_incoming[TDM_IN_N_AUDIO_BLOCKS];
bool AudioInputTDM::update_responsibility = false;
DMAChannel AudioInputTDM::dma(false);


void AudioInputTDM::begin(void)
{
for (int i = 0; i < TDM_IN_N_AUDIO_BLOCKS; i++)
block_incoming[i] = nullptr;
dma.begin(true); // Allocate the DMA channel first

// TODO: should we set & clear the I2S_RCSR_SR bit here?
Expand All @@ -68,7 +66,22 @@ void AudioInputTDM::begin(void)
dma.attachInterrupt(isr);
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // TEENSY P8, GPIO_B1_00_ALT3 as rx 0
// When using combine mode, you *must* use the data pins in order.
switch (AUDIO_N_SAI1_RX_DATAPINS) {
case 4:
CORE_PIN32_CONFIG = 3; //RX_DATA3
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // TEENSY P32, GPIO_B0_12_ALT3 as rx 1
case 3:
CORE_PIN9_CONFIG = 3; //RX_DATA2
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // TEENSY P9, GPIO_B0_11_ALT3 as rx 2
case 2:
CORE_PIN6_CONFIG = 3; //RX_DATA1
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // TEENSY P6, GPIO_B0_10_ALT3 as rx 1
break;
default:
break;
}
dma.TCD->SADDR = &I2S1_RDR0;
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
Expand All @@ -89,33 +102,65 @@ void AudioInputTDM::begin(void)
#endif
}

// TODO: needs optimization...
static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
{
uint32_t i, in1, in2;

for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
in1 = *src;
in2 = *(src+8);
src += 16;
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
}
// Since we're grabbing data out of the fifo at 32-bits, but the words
// are 16-bits and we're sharing fifos in a round-robbin manner, with
//
// 1 FIFO (pin) active, the word/channel order for the first frame is
// C00 C02 C04 C06 C08 C10 C12 C14
// C01 C03 C05 C07 C09 C11 C13 C15
//
// 2 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C02 C18 C04 C20 C06 C22 C08 C24 C10 C26 C12 C28 C14 C30
// C01 C17 C03 C19 C05 C21 C07 C23 C09 C25 C11 C27 C13 C29 C15 C31
//
// 3 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C32 C02 C18 C34 C04 C20 C36 C06 C22 C38 C08 C24 C40 C10 C26 C42 C12 C28 C44 C14 C30 C46
// C01 C17 C33 C03 C19 C35 C05 C21 C37 C07 C23 C39 C09 C25 C41 C11 C27 C43 C13 C29 C45 C15 C31 C47
//
// 4 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C32 C48|C02 C18 C34 C50|C04 C20 C36 C52|C06 C22 C38 C54|C08 C24 C40 C56|C10 C26 C42 C58|C12 C28 C44 C60|C14 C30 C46 C62
// C01 C17 C33 C49|C03 C19 C35 C51|C05 C21 C37 C53|C07 C23 C39 C55|C09 C25 C41 C57|C11 C27 C43 C59|C13 C29 C45 C61|C15 C31 C47 C63
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <--- word index
// where each column is a single 32-bit word popped from the fifo
static void deinterleave(audio_block_t *block_incoming[TDM_IN_N_AUDIO_BLOCKS], const uint32_t *src) {
int l = 0;
for (int i=0; i < TDM_IN_N_AUDIO_BLOCKS/2; i++) {
// ix = 0, 2, 4, 6, ... 1 pin
// ix = 0, 16, 2, 18, 3, 20, ... 2 pins
// ix = 0, 16, 32, 2, 18, 33 ... 3 pins
// ix = 0, 16, 32, 48, 2, 18 ... 4 pins

// i = 0, 1, 2, 3, 4, 5,
// ix = 0, 16, 32, 2, 18, 33 ... 3 pins
// = (0*16 1*16 2*16)+i
int ix = (l*16) + i/AUDIO_N_SAI1_RX_DATAPINS*2;
l = (l + 1) % AUDIO_N_SAI1_RX_DATAPINS;
uint32_t *dest1 = (uint32_t *)(block_incoming[ix]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[ix+1]->data);
const uint32_t *psrc = src;
for (int j=0; j < AUDIO_BLOCK_SAMPLES/2; j++) {
uint32_t in1 = *psrc;
uint32_t in2 = *(psrc + TDM_IN_N_AUDIO_BLOCKS * AUDIO_N_SAI1_RX_DATAPINS / 2);
psrc += TDM_IN_N_AUDIO_BLOCKS;
*dest1++ = ((in1 >> 16) & 0x0000FFFF) | ((in2 << 0) & 0xFFFF0000);
*dest2++ = ((in1 >> 0) & 0x0000FFFF) | ((in2 << 16) & 0xFFFF0000);
}
src++;
}
}

void AudioInputTDM::isr(void)
{
uint32_t daddr;
const uint32_t *src;
unsigned int i;

daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();

if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*8];
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES * TDM_IN_N_AUDIO_BLOCKS / 2];
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
Expand All @@ -125,25 +170,19 @@ void AudioInputTDM::isr(void)
#if IMXRT_CACHE_ENABLED >=1
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
#endif
for (i=0; i < 16; i += 2) {
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
memcpy_tdm_rx(dest1, dest2, src);
src++;
}
deinterleave(block_incoming, src);
}
if (update_responsibility) update_all();
}


void AudioInputTDM::update(void)
{
unsigned int i, j;
audio_block_t *new_block[16];
audio_block_t *out_block[16];

// allocate 16 new blocks. If any fails, allocate none
for (i=0; i < 16; i++) {
unsigned int i, j;
audio_block_t *new_block[TDM_IN_N_AUDIO_BLOCKS];
audio_block_t *out_block[TDM_IN_N_AUDIO_BLOCKS];
// allocate TDM_IN_N_AUDIO_BLOCKS new blocks. If any fails, allocate none
for (i=0; i < TDM_IN_N_AUDIO_BLOCKS; i++) {
new_block[i] = allocate();
if (new_block[i] == nullptr) {
for (j=0; j < i; j++) {
Expand All @@ -158,8 +197,8 @@ void AudioInputTDM::update(void)
memcpy(block_incoming, new_block, sizeof(block_incoming));
__enable_irq();
if (out_block[0] != nullptr) {
// if we got 1 block, all 16 are filled
for (i=0; i < 16; i++) {
// if we got 1 block, all TDM_IN_N_AUDIO_BLOCKS are filled
for (i=0; i < TDM_IN_N_AUDIO_BLOCKS; i++) {
transmit(out_block[i], i);
release(out_block[i]);
}
Expand Down
5 changes: 4 additions & 1 deletion input_tdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
#include <Arduino.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/Arduino.h
#include <AudioStream.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h
#include <DMAChannel.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/DMAChannel.h
#include "AudioRate.h"

#define TDM_IN_N_AUDIO_BLOCKS (TDM_CHANNELS_PER_PIN*AUDIO_N_SAI1_RX_DATAPINS)

class AudioInputTDM : public AudioStream
{
Expand All @@ -42,7 +45,7 @@ class AudioInputTDM : public AudioStream
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_incoming[16];
static audio_block_t *block_incoming[TDM_IN_N_AUDIO_BLOCKS];
};

#endif
4 changes: 2 additions & 2 deletions input_tdm2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
in1 = *src;
in2 = *(src+8);
src += 16;
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
*dest1++ = ((in1 >> 16) & 0x0000FFFF) | ((in2 << 0) & 0xFFFF0000);
*dest2++ = ((in1 >> 0) & 0x0000FFFF) | ((in2 << 16) & 0xFFFF0000);
}
}

Expand Down
17 changes: 13 additions & 4 deletions output_tdm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>

#if !defined(KINETISL)
Expand Down Expand Up @@ -319,20 +318,30 @@ void AudioOutputTDM::config_tdm(void)
I2S1_TCR1 = I2S_TCR1_RFW(4);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_TCR3 = I2S_TCR3_TCE;

// enable all TX data pins. Channels must be used in order, so
// enabled chanels can (0b0001, 0b0011, 0b0111, or 0b1111) only. This
// limitation is because of the fifo combine mode limitation.
I2S1_TCR3 = ((1 << AUDIO_N_SAI1_TX_DATAPINS)-1) << 16;

I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSD;
I2S1_TCR4 |= (AUDIO_N_SAI1_TX_DATAPINS > 1) ? I2S_TCR4_FCOMB_ENABLED_ON_WRITES : 0;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(4);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(0);
I2S1_RCR3 = I2S_RCR3_RCE;
// enable all RX data pins. Channels must be used in order, so
// enabled chanels can (0b0001, 0b0011, 0b0111, or 0b1111) only. This
// limitation is because of the fifo combine mode limitation.
I2S1_RCR3 = ( (1 << AUDIO_N_SAI1_RX_DATAPINS) - 1 ) << 16;

I2S1_RCR4 = I2S_RCR4_FRSZ(7) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S1_RCR4 |= (AUDIO_N_SAI1_RX_DATAPINS > 1) ? I2S_RCR4_FCOMB_ENABLED_ON_READS : 0;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
Expand Down
1 change: 1 addition & 0 deletions output_tdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <Arduino.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/Arduino.h
#include <AudioStream.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h
#include <DMAChannel.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/DMAChannel.h
#include "AudioRate.h"

class AudioOutputTDM : public AudioStream
{
Expand Down