/* ADAT for Teensy 4.X
Copyright (c) 2017, Ernstjan Freriks,
Thanks to Frank Bösing & KPC & Paul Stoffregen!
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice, development funding notice, and this permission
notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Chris O. 3/25/2023
// Fix compile error on Teensy LC
// T4.x -PLL frequency correction @88200 for ADAT @44100, MCLK-22,579,200MHz was 22,579,125MHz
// T4.x -Disabled receiver, ADAT IN not implemented yet.
// T4.x -Set Transmit FIFO Watermark 1, was 0.
//
// -- GUItool: AudioOutputADAT --> adat1 --
// T3.x - T4.x Implemented Mute/Unmute on all 8ch. ADAT
// adat1.mute_PCM(bool mute); // 1 mute - 0 unmute
//
// Implemented Teensy 3.x - 4.x simple way to change the samplerate of ADAT at runtime.
// NOTE: 44100(default) or 48000 ADAT Only 8ch, No ADAT SMUX II-IV yet.
// !!! But be aware that not the entire lib will work. Not all parts of it support that!
// adat1.set_Freq(44100);
// adat1.set_Freq(48000);
// This needs edit in output_adat.h public: class
// void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!
#include <Arduino.h>
#include "output_adat.h"
#include "utility/imxrt_hw.h"
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
audio_block_t * AudioOutputADAT::block_ch1_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch2_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch3_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch4_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch5_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch6_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch7_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch8_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch1_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch2_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch3_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch4_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch5_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch6_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch7_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch8_2nd = NULL;
uint16_t AudioOutputADAT::ch1_offset = 0;
uint16_t AudioOutputADAT::ch2_offset = 0;
uint16_t AudioOutputADAT::ch3_offset = 0;
uint16_t AudioOutputADAT::ch4_offset = 0;
uint16_t AudioOutputADAT::ch5_offset = 0;
uint16_t AudioOutputADAT::ch6_offset = 0;
uint16_t AudioOutputADAT::ch7_offset = 0;
uint16_t AudioOutputADAT::ch8_offset = 0;
bool AudioOutputADAT::update_responsibility = false;
DMAMEM __attribute__((aligned(32))) static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128
DMAChannel AudioOutputADAT::dma(false);
static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES / 4] = {0};
// These are the lookup tables. There are four of them so that the remainder of the 32 bit result can easily be XORred with the next 8 bits.
static const uint32_t LookupTable_firstword[256] = { 0x0, 0x1ffffff, 0x3ffffff, 0x2000000, 0x7ffffff, 0x6000000, 0x4000000, 0x5ffffff, 0xfffffff, 0xe000000, 0xc000000, 0xdffffff, 0x8000000, 0x9ffffff, 0xbffffff, 0xa000000, 0x1fffffff, 0x1e000000, 0x1c000000, 0x1dffffff, 0x18000000, 0x19ffffff, 0x1bffffff, 0x1a000000, 0x10000000, 0x11ffffff, 0x13ffffff, 0x12000000, 0x17ffffff, 0x16000000, 0x14000000, 0x15ffffff, 0x3fffffff, 0x3e000000, 0x3c000000, 0x3dffffff, 0x38000000, 0x39ffffff, 0x3bffffff, 0x3a000000, 0x30000000, 0x31ffffff, 0x33ffffff, 0x32000000, 0x37ffffff, 0x36000000, 0x34000000, 0x35ffffff, 0x20000000, 0x21ffffff, 0x23ffffff, 0x22000000, 0x27ffffff, 0x26000000, 0x24000000, 0x25ffffff, 0x2fffffff, 0x2e000000, 0x2c000000, 0x2dffffff, 0x28000000, 0x29ffffff, 0x2bffffff, 0x2a000000, 0x7fffffff, 0x7e000000, 0x7c000000, 0x7dffffff, 0x78000000, 0x79ffffff, 0x7bffffff, 0x7a000000, 0x70000000, 0x71ffffff, 0x73ffffff, 0x72000000, 0x77ffffff, 0x76000000, 0x74000000, 0x75ffffff, 0x60000000, 0x61ffffff, 0x63ffffff, 0x62000000, 0x67ffffff, 0x66000000, 0x64000000, 0x65ffffff, 0x6fffffff, 0x6e000000, 0x6c000000, 0x6dffffff, 0x68000000, 0x69ffffff, 0x6bffffff, 0x6a000000, 0x40000000, 0x41ffffff, 0x43ffffff, 0x42000000, 0x47ffffff, 0x46000000, 0x44000000, 0x45ffffff, 0x4fffffff, 0x4e000000, 0x4c000000, 0x4dffffff, 0x48000000, 0x49ffffff, 0x4bffffff, 0x4a000000, 0x5fffffff, 0x5e000000, 0x5c000000, 0x5dffffff, 0x58000000, 0x59ffffff, 0x5bffffff, 0x5a000000, 0x50000000, 0x51ffffff, 0x53ffffff, 0x52000000, 0x57ffffff, 0x56000000, 0x54000000, 0x55ffffff, 0xffffffff, 0xfe000000, 0xfc000000, 0xfdffffff, 0xf8000000, 0xf9ffffff, 0xfbffffff, 0xfa000000, 0xf0000000, 0xf1ffffff, 0xf3ffffff, 0xf2000000, 0xf7ffffff, 0xf6000000, 0xf4000000, 0xf5ffffff, 0xe0000000, 0xe1ffffff, 0xe3ffffff, 0xe2000000, 0xe7ffffff, 0xe6000000, 0xe4000000, 0xe5ffffff, 0xefffffff, 0xee000000, 0xec000000, 0xedffffff, 0xe8000000, 0xe9ffffff, 0xebffffff, 0xea000000, 0xc0000000, 0xc1ffffff, 0xc3ffffff, 0xc2000000, 0xc7ffffff, 0xc6000000, 0xc4000000, 0xc5ffffff, 0xcfffffff, 0xce000000, 0xcc000000, 0xcdffffff, 0xc8000000, 0xc9ffffff, 0xcbffffff, 0xca000000, 0xdfffffff, 0xde000000, 0xdc000000, 0xddffffff, 0xd8000000, 0xd9ffffff, 0xdbffffff, 0xda000000, 0xd0000000, 0xd1ffffff, 0xd3ffffff, 0xd2000000, 0xd7ffffff, 0xd6000000, 0xd4000000, 0xd5ffffff, 0x80000000, 0x81ffffff, 0x83ffffff, 0x82000000, 0x87ffffff, 0x86000000, 0x84000000, 0x85ffffff, 0x8fffffff, 0x8e000000, 0x8c000000, 0x8dffffff, 0x88000000, 0x89ffffff, 0x8bffffff, 0x8a000000, 0x9fffffff, 0x9e000000, 0x9c000000, 0x9dffffff, 0x98000000, 0x99ffffff, 0x9bffffff, 0x9a000000, 0x90000000, 0x91ffffff, 0x93ffffff, 0x92000000, 0x97ffffff, 0x96000000, 0x94000000, 0x95ffffff, 0xbfffffff, 0xbe000000, 0xbc000000, 0xbdffffff, 0xb8000000, 0xb9ffffff, 0xbbffffff, 0xba000000, 0xb0000000, 0xb1ffffff, 0xb3ffffff, 0xb2000000, 0xb7ffffff, 0xb6000000, 0xb4000000, 0xb5ffffff, 0xa0000000, 0xa1ffffff, 0xa3ffffff, 0xa2000000, 0xa7ffffff, 0xa6000000, 0xa4000000, 0xa5ffffff, 0xafffffff, 0xae000000, 0xac000000, 0xadffffff, 0xa8000000, 0xa9ffffff, 0xabffffff, 0xaa000000};
static const uint32_t LookupTable_secondword[256] = { 0x0, 0x1ffff, 0x3ffff, 0x20000, 0x7ffff, 0x60000, 0x40000, 0x5ffff, 0xfffff, 0xe0000, 0xc0000, 0xdffff, 0x80000, 0x9ffff, 0xbffff, 0xa0000, 0x1fffff, 0x1e0000, 0x1c0000, 0x1dffff, 0x180000, 0x19ffff, 0x1bffff, 0x1a0000, 0x100000, 0x11ffff, 0x13ffff, 0x120000, 0x17ffff, 0x160000, 0x140000, 0x15ffff, 0x3fffff, 0x3e0000, 0x3c0000, 0x3dffff, 0x380000, 0x39ffff, 0x3bffff, 0x3a0000, 0x300000, 0x31ffff, 0x33ffff, 0x320000, 0x37ffff, 0x360000, 0x340000, 0x35ffff, 0x200000, 0x21ffff, 0x23ffff, 0x220000, 0x27ffff, 0x260000, 0x240000, 0x25ffff, 0x2fffff, 0x2e0000, 0x2c0000, 0x2dffff, 0x280000, 0x29ffff, 0x2bffff, 0x2a0000, 0x7fffff, 0x7e0000, 0x7c0000, 0x7dffff, 0x780000, 0x79ffff, 0x7bffff, 0x7a0000, 0x700000, 0x71ffff, 0x73ffff, 0x720000, 0x77ffff, 0x760000, 0x740000, 0x75ffff, 0x600000, 0x61ffff, 0x63ffff, 0x620000, 0x67ffff, 0x660000, 0x640000, 0x65ffff, 0x6fffff, 0x6e0000, 0x6c0000, 0x6dffff, 0x680000, 0x69ffff, 0x6bffff, 0x6a0000, 0x400000, 0x41ffff, 0x43ffff, 0x420000, 0x47ffff, 0x460000, 0x440000, 0x45ffff, 0x4fffff, 0x4e0000, 0x4c0000, 0x4dffff, 0x480000, 0x49ffff, 0x4bffff, 0x4a0000, 0x5fffff, 0x5e0000, 0x5c0000, 0x5dffff, 0x580000, 0x59ffff, 0x5bffff, 0x5a0000, 0x500000, 0x51ffff, 0x53ffff, 0x520000, 0x57ffff, 0x560000, 0x540000, 0x55ffff, 0xffffff, 0xfe0000, 0xfc0000, 0xfdffff, 0xf80000, 0xf9ffff, 0xfbffff, 0xfa0000, 0xf00000, 0xf1ffff, 0xf3ffff, 0xf20000, 0xf7ffff, 0xf60000, 0xf40000, 0xf5ffff, 0xe00000, 0xe1ffff, 0xe3ffff, 0xe20000, 0xe7ffff, 0xe60000, 0xe40000, 0xe5ffff, 0xefffff, 0xee0000, 0xec0000, 0xedffff, 0xe80000, 0xe9ffff, 0xebffff, 0xea0000, 0xc00000, 0xc1ffff, 0xc3ffff, 0xc20000, 0xc7ffff, 0xc60000, 0xc40000, 0xc5ffff, 0xcfffff, 0xce0000, 0xcc0000, 0xcdffff, 0xc80000, 0xc9ffff, 0xcbffff, 0xca0000, 0xdfffff, 0xde0000, 0xdc0000, 0xddffff, 0xd80000, 0xd9ffff, 0xdbffff, 0xda0000, 0xd00000, 0xd1ffff, 0xd3ffff, 0xd20000, 0xd7ffff, 0xd60000, 0xd40000, 0xd5ffff, 0x800000, 0x81ffff, 0x83ffff, 0x820000, 0x87ffff, 0x860000, 0x840000, 0x85ffff, 0x8fffff, 0x8e0000, 0x8c0000, 0x8dffff, 0x880000, 0x89ffff, 0x8bffff, 0x8a0000, 0x9fffff, 0x9e0000, 0x9c0000, 0x9dffff, 0x980000, 0x99ffff, 0x9bffff, 0x9a0000, 0x900000, 0x91ffff, 0x93ffff, 0x920000, 0x97ffff, 0x960000, 0x940000, 0x95ffff, 0xbfffff, 0xbe0000, 0xbc0000, 0xbdffff, 0xb80000, 0xb9ffff, 0xbbffff, 0xba0000, 0xb00000, 0xb1ffff, 0xb3ffff, 0xb20000, 0xb7ffff, 0xb60000, 0xb40000, 0xb5ffff, 0xa00000, 0xa1ffff, 0xa3ffff, 0xa20000, 0xa7ffff, 0xa60000, 0xa40000, 0xa5ffff, 0xafffff, 0xae0000, 0xac0000, 0xadffff, 0xa80000, 0xa9ffff, 0xabffff, 0xaa0000};
static const uint32_t LookupTable_thirdword[256] = { 0x0, 0x1ff, 0x3ff, 0x200, 0x7ff, 0x600, 0x400, 0x5ff, 0xfff, 0xe00, 0xc00, 0xdff, 0x800, 0x9ff, 0xbff, 0xa00, 0x1fff, 0x1e00, 0x1c00, 0x1dff, 0x1800, 0x19ff, 0x1bff, 0x1a00, 0x1000, 0x11ff, 0x13ff, 0x1200, 0x17ff, 0x1600, 0x1400, 0x15ff, 0x3fff, 0x3e00, 0x3c00, 0x3dff, 0x3800, 0x39ff, 0x3bff, 0x3a00, 0x3000, 0x31ff, 0x33ff, 0x3200, 0x37ff, 0x3600, 0x3400, 0x35ff, 0x2000, 0x21ff, 0x23ff, 0x2200, 0x27ff, 0x2600, 0x2400, 0x25ff, 0x2fff, 0x2e00, 0x2c00, 0x2dff, 0x2800, 0x29ff, 0x2bff, 0x2a00, 0x7fff, 0x7e00, 0x7c00, 0x7dff, 0x7800, 0x79ff, 0x7bff, 0x7a00, 0x7000, 0x71ff, 0x73ff, 0x7200, 0x77ff, 0x7600, 0x7400, 0x75ff, 0x6000, 0x61ff, 0x63ff, 0x6200, 0x67ff, 0x6600, 0x6400, 0x65ff, 0x6fff, 0x6e00, 0x6c00, 0x6dff, 0x6800, 0x69ff, 0x6bff, 0x6a00, 0x4000, 0x41ff, 0x43ff, 0x4200, 0x47ff, 0x4600, 0x4400, 0x45ff, 0x4fff, 0x4e00, 0x4c00, 0x4dff, 0x4800, 0x49ff, 0x4bff, 0x4a00, 0x5fff, 0x5e00, 0x5c00, 0x5dff, 0x5800, 0x59ff, 0x5bff, 0x5a00, 0x5000, 0x51ff, 0x53ff, 0x5200, 0x57ff, 0x5600, 0x5400, 0x55ff, 0xffff, 0xfe00, 0xfc00, 0xfdff, 0xf800, 0xf9ff, 0xfbff, 0xfa00, 0xf000, 0xf1ff, 0xf3ff, 0xf200, 0xf7ff, 0xf600, 0xf400, 0xf5ff, 0xe000, 0xe1ff, 0xe3ff, 0xe200, 0xe7ff, 0xe600, 0xe400, 0xe5ff, 0xefff, 0xee00, 0xec00, 0xedff, 0xe800, 0xe9ff, 0xebff, 0xea00, 0xc000, 0xc1ff, 0xc3ff, 0xc200, 0xc7ff, 0xc600, 0xc400, 0xc5ff, 0xcfff, 0xce00, 0xcc00, 0xcdff, 0xc800, 0xc9ff, 0xcbff, 0xca00, 0xdfff, 0xde00, 0xdc00, 0xddff, 0xd800, 0xd9ff, 0xdbff, 0xda00, 0xd000, 0xd1ff, 0xd3ff, 0xd200, 0xd7ff, 0xd600, 0xd400, 0xd5ff, 0x8000, 0x81ff, 0x83ff, 0x8200, 0x87ff, 0x8600, 0x8400, 0x85ff, 0x8fff, 0x8e00, 0x8c00, 0x8dff, 0x8800, 0x89ff, 0x8bff, 0x8a00, 0x9fff, 0x9e00, 0x9c00, 0x9dff, 0x9800, 0x99ff, 0x9bff, 0x9a00, 0x9000, 0x91ff, 0x93ff, 0x9200, 0x97ff, 0x9600, 0x9400, 0x95ff, 0xbfff, 0xbe00, 0xbc00, 0xbdff, 0xb800, 0xb9ff, 0xbbff, 0xba00, 0xb000, 0xb1ff, 0xb3ff, 0xb200, 0xb7ff, 0xb600, 0xb400, 0xb5ff, 0xa000, 0xa1ff, 0xa3ff, 0xa200, 0xa7ff, 0xa600, 0xa400, 0xa5ff, 0xafff, 0xae00, 0xac00, 0xadff, 0xa800, 0xa9ff, 0xabff, 0xaa00};
static const uint32_t LookupTable_fourthword[256] = { 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x1f, 0x1e, 0x1c, 0x1d, 0x18, 0x19, 0x1b, 0x1a, 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, 0x3f, 0x3e, 0x3c, 0x3d, 0x38, 0x39, 0x3b, 0x3a, 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, 0x2f, 0x2e, 0x2c, 0x2d, 0x28, 0x29, 0x2b, 0x2a, 0x7f, 0x7e, 0x7c, 0x7d, 0x78, 0x79, 0x7b, 0x7a, 0x70, 0x71, 0x73, 0x72, 0x77, 0x76, 0x74, 0x75, 0x60, 0x61, 0x63, 0x62, 0x67, 0x66, 0x64, 0x65, 0x6f, 0x6e, 0x6c, 0x6d, 0x68, 0x69, 0x6b, 0x6a, 0x40, 0x41, 0x43, 0x42, 0x47, 0x46, 0x44, 0x45, 0x4f, 0x4e, 0x4c, 0x4d, 0x48, 0x49, 0x4b, 0x4a, 0x5f, 0x5e, 0x5c, 0x5d, 0x58, 0x59, 0x5b, 0x5a, 0x50, 0x51, 0x53, 0x52, 0x57, 0x56, 0x54, 0x55, 0xff, 0xfe, 0xfc, 0xfd, 0xf8, 0xf9, 0xfb, 0xfa, 0xf0, 0xf1, 0xf3, 0xf2, 0xf7, 0xf6, 0xf4, 0xf5, 0xe0, 0xe1, 0xe3, 0xe2, 0xe7, 0xe6, 0xe4, 0xe5, 0xef, 0xee, 0xec, 0xed, 0xe8, 0xe9, 0xeb, 0xea, 0xc0, 0xc1, 0xc3, 0xc2, 0xc7, 0xc6, 0xc4, 0xc5, 0xcf, 0xce, 0xcc, 0xcd, 0xc8, 0xc9, 0xcb, 0xca, 0xdf, 0xde, 0xdc, 0xdd, 0xd8, 0xd9, 0xdb, 0xda, 0xd0, 0xd1, 0xd3, 0xd2, 0xd7, 0xd6, 0xd4, 0xd5, 0x80, 0x81, 0x83, 0x82, 0x87, 0x86, 0x84, 0x85, 0x8f, 0x8e, 0x8c, 0x8d, 0x88, 0x89, 0x8b, 0x8a, 0x9f, 0x9e, 0x9c, 0x9d, 0x98, 0x99, 0x9b, 0x9a, 0x90, 0x91, 0x93, 0x92, 0x97, 0x96, 0x94, 0x95, 0xbf, 0xbe, 0xbc, 0xbd, 0xb8, 0xb9, 0xbb, 0xba, 0xb0, 0xb1, 0xb3, 0xb2, 0xb7, 0xb6, 0xb4, 0xb5, 0xa0, 0xa1, 0xa3, 0xa2, 0xa7, 0xa6, 0xa4, 0xa5, 0xaf, 0xae, 0xac, 0xad, 0xa8, 0xa9, 0xab, 0xaa};
void AudioOutputADAT::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_ch1_1st = NULL;
block_ch2_1st = NULL;
block_ch3_1st = NULL;
block_ch4_1st = NULL;
block_ch5_1st = NULL;
block_ch6_1st = NULL;
block_ch7_1st = NULL;
block_ch8_1st = NULL;
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_ADAT();
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop
dma.TCD->SADDR = ADAT_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); //transfersize 2 = 32 bit, 5 = 32 byte
dma.TCD->NBYTES_MLNO = nbytes_mlno;
dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
dma.TCD->DADDR = &I2S0_TDR0;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
#endif
const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop
dma.TCD->SADDR = ADAT_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = nbytes_mlno;
dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
dma.TCD->DADDR = &I2S1_TDR0; // TEENSY 4.x I2S1
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); // TEENSY 4.x I2S1
update_responsibility = update_setup();
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE; // todo: yes we need this but why ?
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
#endif
}
/*
https://ackspace.nl/wiki/ADAT_project
https://github.com/xcore/sc_adat/blob/master/module_adat_tx/src/adat_tx_port.xc
for information about the clock:
http://cache.freescale.com/files/32bit/doc/app_note/AN4800.pdf
We need a bitrate twice as high as the SPDIF example.
Because BITCLK can not be the same as MCLK, but only half of MCLK, we need a 2*MCLK (so for 44100 samplerate we need 88200 MCLK)
*/
void AudioOutputADAT::isr(void)
{
const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8;
const int16_t *zeros = (const int16_t *)zerodata;
uint32_t *end, *dest;
uint32_t saddr;
uint32_t sample1, sample2, sample3, sample4, sample5, sample6, sample7, sample8;
static uint32_t previousnrzi_highlow = 0; //this is used for the NZRI encoding to remember the last state.
// if the result of the lookup table LSB is other than this lastbit, the result of the lookup table must be inverted.
//static bool previousframeinverted=false;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)ADAT_tx_buffer + sizeof(ADAT_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8];
if (AudioOutputADAT::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (uint32_t *)ADAT_tx_buffer;
end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
}
#if IMXRT_CACHE_ENABLED >= 2
uint32_t *toFlush = dest;
uint32_t flushLen = sizeof ADAT_tx_buffer / 2;
#endif
src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros;
src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros;
//Non-NZRI encoded 'empty' ADAT Frame
/*
(dest+0) = 0b10000100001000010000100001000010; // bit 0-31
(dest+1) = 0b00010000100001000010000100001000; // bit 32-63
(dest+2) = 0b01000010000100001000010000100001; // bit 64-95
(dest+3) = 0b00001000010000100001000010000100; // bit 96-127
(dest+4) = 0b00100001000010000100001000010000; // bit 128-159
(dest+5) = 0b10000100001000010000100001000010; // bit 160-191
(dest+6) = 0b00010000100001000010000100001000; // bit 192-223
(dest+7) = 0b01000010000100001000000000010000; // bit 224-255
dest+=8;
*/
/*
//NZRI encoded 'empty' ADAT Frame
(dest+0) = 0b11111000001111100000111110000011; // bit 0-31
(dest+1) = 0b11100000111110000011111000001111; // bit 32-63
(dest+2) = 0b10000011111000001111100000111110; // bit 64-95
(dest+3) = 0b00001111100000111110000011111000; // bit 96-127
(dest+4) = 0b00111110000011111000001111100000; // bit 128-159
(dest+5) = 0b11111000001111100000111110000011; // bit 160-191
(dest+6) = 0b11100000111110000011111000001111; // bit 192-223
(dest+7) = 0b10000011111000001111111111100000; // bit 224-255
dest+=8;
*/
do
{
sample1 = (*src1++);
sample2 = (*src2++);
sample3 = (*src3++);
sample4 = (*src4++);
sample5 = (*src5++);
sample6 = (*src6++);
sample7 = (*src7++);
sample8 = (*src8++);
uint32_t value;
uint32_t nzrivalue;
value = 0b10000100001000010000100001000010 /* bit 0-31 */
// b00000000000000001000000000000000 // start of 16 bit sample
| ((sample1 << 15) & 0b01111000000000000000000000000000)
| ((sample1 << 14) & 0b00000011110000000000000000000000)
| ((sample1 << 13) & 0b00000000000111100000000000000000)
| ((sample1 << 12) & 0b00000000000000001111000000000000)
| ((sample2 >> 15) & 0b00000000000000000000000000000001)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 0) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b00010000100001000010000100001000 /* bit 32-63 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample2 << 17) & 0b11100000000000000000000000000000)
| ((sample2 << 16) & 0b00001111000000000000000000000000)
| ((sample2 << 15) & 0b00000000011110000000000000000000)
| ((sample2 << 14) & 0b00000000000000111100000000000000)
| ((sample3 >> 13) & 0b00000000000000000000000000000111)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 1) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b01000010000100001000010000100001 /* bit 64-95 */
// b00000000000000001000000000000000 // start of 16 bit sample
| ((sample3 << 19) & 0b10000000000000000000000000000000)
| ((sample3 << 18) & 0b00111100000000000000000000000000)
| ((sample3 << 17) & 0b00000001111000000000000000000000)
| ((sample3 << 16) & 0b00000000000011110000000000000000)
| ((sample4 >> 11) & 0b00000000000000000000000000011110)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 2) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b00001000010000100001000010000100 /* bit 96-127 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample4 << 20) & 0b11110000000000000000000000000000)
| ((sample4 << 19) & 0b00000111100000000000000000000000)
| ((sample4 << 18) & 0b00000000001111000000000000000000)
| ((sample5 >> 9 ) & 0b00000000000000000000000001111000)
| ((sample5 >> 10) & 0b00000000000000000000000000000011)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 3) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b00100001000010000100001000010000 /* bit 128-159 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample5 << 22) & 0b11000000000000000000000000000000)
| ((sample5 << 21) & 0b00011110000000000000000000000000)
| ((sample5 << 20) & 0b00000000111100000000000000000000)
| ((sample6 >> 7 ) & 0b00000000000000000000000111100000)
| ((sample6 >> 8 ) & 0b00000000000000000000000000001111)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 4) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b10000100001000010000100001000010 /* bit 160-191 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample6 << 23) & 0b01111000000000000000000000000000)
| ((sample6 << 22) & 0b00000011110000000000000000000000)
| ((sample7 >> 5 ) & 0b00000000000000000000011110000000)
| ((sample7 >> 6 ) & 0b00000000000000000000000000111100)
| ((sample7 >> 7 ) & 0b00000000000000000000000000000001)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 5) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b00010000100001000010000100001000 /* bit 192-223 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample7 << 25) & 0b11100000000000000000000000000000)
| ((sample7 << 24) & 0b00001111000000000000000000000000)
| ((sample8 >> 3 ) & 0b00000000000000000001111000000000)
| ((sample8 >> 4 ) & 0b00000000000000000000000011110000)
| ((sample8 >> 5 ) & 0b00000000000000000000000000000111)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 6) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
value = 0b01000010000100001000000000010000 /* bit 224-255 */
// b00000000000000001000100010001000 // start of 16 bit sample
| ((sample8 << 27) & 0b10000000000000000000000000000000)
| ((sample8 << 26) & 0b00111100000000000000000000000000)
;
nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
*(dest + 7) = nzrivalue;
previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;
dest += 8;
} while (dest < end);
#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(toFlush, flushLen);
#endif // IMXRT_CACHE_ENABLED >= 2
/*
block = AudioOutputADAT::block_ch1_1st;
if (block) {
offset = AudioOutputADAT::ch1_offset;
src = &block->data[offset];
do {
sample = (uint32_t)*src++;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputADAT::ch1_offset = offset;
} else {
AudioOutputADAT::ch1_offset = 0;
AudioStream::release(block);
AudioOutputADAT::block_ch1_1st = AudioOutputADAT::block_ch1_2nd;
AudioOutputADAT::block_ch1_2nd = NULL;
}
} else {
sample = 0;
do {
// *(dest+0) = 0b11111000001111100000111110000011; // bit 0-31
// *(dest+1) = 0b11100000111110000011111000001111; // bit 32-63
// *(dest+2) = 0b10000011111000001111100000111110; // bit 64-95
// *(dest+3) = 0b00001111100000111110000011111000; // bit 96-127
// *(dest+4) = 0b00111110000011111000001111100000; // bit 128-159
// *(dest+5) = 0b11111000001111100000111110000011; // bit 160-191
// *(dest+6) = 0b11100000111110000011111000001111; // bit 192-223
// *(dest+7) = 0b10000011111000001111111111100000; // bit 224-255
dest+=8;
} while (dest < end);
}
dest -= AUDIO_BLOCK_SAMPLES * 8/2 - 8/2;
block = AudioOutputADAT::block_ch2_1st;
if (block) {
offset = AudioOutputADAT::ch2_offset;
src = &block->data[offset];
do {
sample = *src++;
// *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
// *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
// *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
// *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
dest+=8;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputADAT::ch2_offset = offset;
} else {
AudioOutputADAT::ch2_offset = 0;
AudioStream::release(block);
AudioOutputADAT::block_ch2_1st = AudioOutputADAT::block_ch2_2nd;
AudioOutputADAT::block_ch2_2nd = NULL;
}
} else {
// *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
// *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
// *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
// *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
dest+=8;
}*/
if (block_ch1_1st) {
if (ch1_offset == 0) {
ch1_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch1_offset = 0;
release(block_ch1_1st);
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = NULL;
}
}
if (block_ch2_1st) {
if (ch2_offset == 0) {
ch2_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch2_offset = 0;
release(block_ch2_1st);
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = NULL;
}
}
if (block_ch3_1st) {
if (ch3_offset == 0) {
ch3_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch3_offset = 0;
release(block_ch3_1st);
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = NULL;
}
}
if (block_ch4_1st) {
if (ch4_offset == 0) {
ch4_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch4_offset = 0;
release(block_ch4_1st);
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = NULL;
}
}
if (block_ch5_1st) {
if (ch5_offset == 0) {
ch5_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch5_offset = 0;
release(block_ch5_1st);
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = NULL;
}
}
if (block_ch6_1st) {
if (ch6_offset == 0) {
ch6_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch6_offset = 0;
release(block_ch6_1st);
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = NULL;
}
}
if (block_ch7_1st) {
if (ch7_offset == 0) {
ch7_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch7_offset = 0;
release(block_ch7_1st);
block_ch7_1st = block_ch7_2nd;
block_ch7_2nd = NULL;
}
}
if (block_ch8_1st) {
if (ch8_offset == 0) {
ch8_offset = AUDIO_BLOCK_SAMPLES / 2;
} else {
ch8_offset = 0;
release(block_ch8_1st);
block_ch8_1st = block_ch8_2nd;
block_ch8_2nd = NULL;
}
}
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
// if you dont have this you just get noise! - AJ, https://forum.pjrc.com/threads/60914-ADAT-white-noise-on-Teensy-4-0
arm_dcache_flush_delete((void *)((uint32_t)dest - sizeof(ADAT_tx_buffer) / 2), sizeof(ADAT_tx_buffer) / 2 );
#endif
}
void AudioOutputADAT::mute_PCM(const bool mute)
{
//vucp = mute?VUCP_INVALID:VUCP_VALID;
//#TODO
// The TMR causes the Transmit Data pin to be tri-stated for the length of each selected
// word and the transmit FIFO is not read for masked words.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
// configure transmitter T 4.x
// SAI Transmit Mask Register (TMR)
I2S1_TMR = mute; // 1 mute - 0 unmute
#elif defined(KINETISK)
// configure transmitter T 3.x
I2S0_TMR = mute; // 1 mute - 0 unmute
#endif
}
void AudioOutputADAT::update(void)
{
audio_block_t *block, *tmp;
block = receiveReadOnly(0); // input 0 = channel 1
if (block) {
__disable_irq();
if (block_ch1_1st == NULL) {
block_ch1_1st = block;
ch1_offset = 0;
__enable_irq();
} else if (block_ch1_2nd == NULL) {
block_ch1_2nd = block;
__enable_irq();
} else {
tmp = block_ch1_1st;
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = block;
ch1_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = channel 2
if (block) {
__disable_irq();
if (block_ch2_1st == NULL) {
block_ch2_1st = block;
ch2_offset = 0;
__enable_irq();
} else if (block_ch2_2nd == NULL) {
block_ch2_2nd = block;
__enable_irq();
} else {
tmp = block_ch2_1st;
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = block;
ch2_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(2); // channel 3
if (block) {
__disable_irq();
if (block_ch3_1st == NULL) {
block_ch3_1st = block;
ch3_offset = 0;
__enable_irq();
} else if (block_ch3_2nd == NULL) {
block_ch3_2nd = block;
__enable_irq();
} else {
tmp = block_ch3_1st;
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = block;
ch3_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(3); // channel 4
if (block) {
__disable_irq();
if (block_ch4_1st == NULL) {
block_ch4_1st = block;
ch4_offset = 0;
__enable_irq();
} else if (block_ch4_2nd == NULL) {
block_ch4_2nd = block;
__enable_irq();
} else {
tmp = block_ch4_1st;
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = block;
ch4_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(4); // channel 5
if (block) {
__disable_irq();
if (block_ch5_1st == NULL) {
block_ch5_1st = block;
ch5_offset = 0;
__enable_irq();
} else if (block_ch5_2nd == NULL) {
block_ch5_2nd = block;
__enable_irq();
} else {
tmp = block_ch5_1st;
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = block;
ch5_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(5); // channel 6
if (block) {
__disable_irq();
if (block_ch6_1st == NULL) {
block_ch6_1st = block;
ch6_offset = 0;
__enable_irq();
} else if (block_ch6_2nd == NULL) {
block_ch6_2nd = block;
__enable_irq();
} else {
tmp = block_ch6_1st;
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = block;
ch6_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(6); // channel 7
if (block) {
__disable_irq();
if (block_ch7_1st == NULL) {
block_ch7_1st = block;
ch7_offset = 0;
__enable_irq();
} else if (block_ch7_2nd == NULL) {
block_ch7_2nd = block;
__enable_irq();
} else {
tmp = block_ch7_1st;
block_ch7_1st = block_ch7_2nd;
block_ch7_2nd = block;
ch7_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(7); // channel 8
if (block) {
__disable_irq();
if (block_ch8_1st == NULL) {
block_ch8_1st = block;
ch8_offset = 0;
__enable_irq();
} else if (block_ch8_2nd == NULL) {
block_ch8_2nd = block;
__enable_irq();
} else {
tmp = block_ch8_1st;
block_ch8_1st = block_ch8_2nd;
block_ch8_2nd = block;
ch8_offset = 0;
__enable_irq();
release(tmp);
}
}
}
#ifndef MCLK_SRC
#if F_CPU >= 20000000
#define MCLK_SRC 3 // the PLL
#else
#define MCLK_SRC 0 // system clock
#endif
#endif
#if defined(KINETISK)
// Teensy 3.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
// TODO: should we set & clear the I2S_TCSR_SR bit here?
setI2SFreq(_FREQ * 2); // TODO; test this on T3.x
// https://forum.pjrc.com/threads/42092-Audio-Sample-Rate-how-to-change-from-44100-Hz-(44117-65-Hz)-to-48000
}
void AudioOutputADAT::config_ADAT(void)
{
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// enable MCLK output
I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
//while (I2S0_MCR & I2S_MCR_DUF) ;
//I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));
AudioOutputADAT::setI2SFreq(88200); // (default)
// configure transmitter
I2S0_TMR = 0; // SAI Transmit Mask Register (TMR)
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S0_TCR3 = I2S_TCR3_TCE;
//8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
I2S0_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
I2S0_RCSR = 0;
#if 0
// configure pin mux for 3 clock signals (debug only)
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
// CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#endif
}
/*
https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate
*/
void AudioOutputADAT::setI2SFreq(int freq) {
typedef struct {
uint8_t mult;
uint16_t div;
} tmclk;
const int numfreqs = 14;
const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int) 44117.64706 , 48000, 88200, (int) (44117.64706 * 2.0), 96000, 176400, (int) (44117.64706 * 4.0), 192000};
#if (F_PLL==16000000)
const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#elif (F_PLL==256000000)
// TODO: fix these...
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif
for (int f = 0; f < numfreqs; f++) {
if ( freq == samplefreqs[f] ) {
while (I2S0_MCR & I2S_MCR_DUF) ;
I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
return;
}
}
}
#endif
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
// Teensy 4.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
// TODO: should we set & clear the I2S_TCSR_SR bit here?
setI2SFreq(_FREQ * 2); // for ADAT we need (MCLK @24,576,000Mhz 48000kHz) or (MCLK @22,579,200Mhz 44100kHz)
}
void AudioOutputADAT::config_ADAT(void)
{
// Set this to double the audio samplerate for adat to work! - AJ
int fs = AUDIO_SAMPLE_RATE_EXACT * 2; // (default)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing /AAA from output_i2s
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
AudioOutputADAT::setI2SFreq(fs); // set PLL4, CCM_ANALOG
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
//Select SAI1_MCLK1
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
// int rsync = 1; // 1b - Synchronous with receiver
int tsync = 0; // transmitter -- 0b - Asynchronous mode, 1b - Synchronous with receiver.
// configure transmitter
I2S1_TMR = 0; // SAI Transmit Mask Register (TMR)
I2S1_TCR1 = I2S_TCR1_RFW(1); //was 0, supposed to be: I2S_TCR1_TFW(n), Transmit FIFO Watermark
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_TCR3 = I2S_TCR3_TCE; // Enable
// i want.. 8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver, ADAT IN not implemented yet.
I2S1_RCSR = 0; // disabled
/*
I2S1_RMR = 0;
//I2S1_RCSR = (1<<25); //Reset
//I2S1_RCR1 = I2S_RCR1_RFW(0);
// should this be I2S_TCR4_FRSZ(7) as above? both 3 and 7 work in both places - AJ
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S1_RCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
*/
#if 0
//debug only:
CORE_PIN23_CONFIG = 3; //1:MCLK 22,579,200Mhz--@44100kHz, 24,576,000Mhz--@48000kHz
CORE_PIN21_CONFIG = 3; //1:RX_BCLK 5.6 MHz
CORE_PIN20_CONFIG = 3; //1:RX_SYNC 44.1 KHz
// CORE_PIN6_CONFIG = 3; //1:TX_DATA0
// CORE_PIN7_CONFIG = 3; //1:RX_DATA0
#endif
}
#endif
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
void AudioOutputADAT::setI2SFreq(int freq) { // PLL4, CCM_ANALOG
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
double C = ((double)freq * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
// PLL frequency correction @88200
if (freq == 88200) { // see debug
c1 = c1 + 1;
}
// set PLL4 CCM_ANALOG_PLL_AUDIO
set_audioClock(c0, c1, c2, true); // see \libraries\Audio\utility\imxrt_hw.ccp
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
#if 0
//debug only:
Serial.printf("SetI2SFreq(%d)\n", freq);
//Serial.print("Freq "),Serial.println(freq);
Serial.print("c0 DIV "), Serial.println(c0);
Serial.print("c1 NUM "), Serial.println(c1);
Serial.print("c2 DENOM "), Serial.println(c2);
Serial.print("n1 PRED/ "), Serial.println(n1);
Serial.print("n2 PODF/ "), Serial.println(n2);
Serial.println();
// -- Freq 88200 for 44100kHz ADAT, MCLK1 22,579,125Mhz -- we need 22,579,200MHz
// c0 DIV 30 -- OSC_24000000Mhz *(30 + (1055/10000)) = 722,532,000MHz
// c1 NUM 1055 <--- issue
// c2 DENOM 10000
// n1 PRED/ 4 -- 722,532,000 / 4 = 180,633,000MHz
// n2 PODF/ 8 -- 180,633,000 / 8 = 22,579,125
// MCLK--22,579,125MHz
// -- Freq 88200 for 44100kHz ADAT, MCLK1 22,579,200Mhz
// c0 DIV 30 -- OSC_24000000Mhz *(30 + (1056/10000)) = 722,534,400MHz
// c1 NUM 1056 <--- PLL frequency correction @88200
// c2 DENOM 10000
// n1 PRED/ 4 -- 722,534,400 / 4 = 180,633,600MHz
// n2 PODF/ 8 -- 180,633,600 / 8 = 22,579,200
// MCLK--22,579,200MHz -- good @88200
// -- Freq 96000 for 48000kHz ADAT, MCLK1 24,576,000Mhz
// c0 DIV 28 -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000
// c1 NUM 6720
// c2 DENOM 10000
// n1 PRED/ 4 -- 688,128,000 / 4 = 172,032,000
// n2 PODF/ 7 -- 172,032,000 / 7 = 24,576,000
// MCLK-- 24,576,000MHz good@48000
// -- Freq 48000 for 48000kHz, MCLK1 12,288,000Mhz
// c0 DIV 28 -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000MHz
// c1 NUM 6720
// c2 DENOM 10000
// n1 PRED/ 4 -- 688,128,000 / 4 = 172,032,000MHz
// n2 PODF/ 14 -- 172,032,000 / 14 = 12,288,000
// MCLK--12,288,000MHz -- good @48000
// -- Freq 44100 -- OSC_24000000Mhz *(28 + (2240/10000)) = 677,376,000
// c0 DIV 28
// c1 NUM 2240
// c2 DENOM 10000
// n1 PRED/ 4 -- 677,376,000 / 4 = 169,344,000
// n2 PODF/ 15 -- 169,344,000 / 15 = 11,289,600MHz
// MCLK--11,289,600MHz -- good @44100
#endif
}
#endif
#elif defined(KINETISL)
// Fix compile error on Teensy LC
void AudioOutputADAT::mute_PCM(const bool mute)
{
// Teensy LC return
}
// simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
// Teensy LC return
}
void AudioOutputADAT::begin(void)
{
// Teensy LC return
}
void AudioOutputADAT::update(void)
{
audio_block_t *block;
block = receiveReadOnly(0); // input 0 = ch1 channel
if (block) release(block);
block = receiveReadOnly(1); // input 1 = ch2 channel
if (block) release(block);
block = receiveReadOnly(2); // input 2 = ch3 channel
if (block) release(block);
block = receiveReadOnly(3); // input 3 = ch4 channel
if (block) release(block);
block = receiveReadOnly(4); // input 4 = ch5 channel
if (block) release(block);
block = receiveReadOnly(5); // input 5 = ch6 channel
if (block) release(block);
block = receiveReadOnly(6); // input 6 = ch7 channel
if (block) release(block);
block = receiveReadOnly(7); // input 7 = ch8 channel
if (block) release(block);
}
#endif