PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

316 行
9.5KB

  1. /* SPDIF for Teensy 3.X
  2. * Copyright (c) 2015, Frank Bösing, f.boesing@gmx.de,
  3. * Thanks to KPC & Paul Stoffregen!
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice, development funding notice, and this permission
  13. * notice shall be included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. // 2015/08/23: (FB) added mute_PCM() - sets or unsets VALID in VUCP (and adjusts PARITY)
  24. #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  25. #include <Arduino.h>
  26. #include "output_spdif2.h"
  27. #include "utility/imxrt_hw.h"
  28. audio_block_t * AudioOutputSPDIF2::block_left_1st = NULL;
  29. audio_block_t * AudioOutputSPDIF2::block_right_1st = NULL;
  30. audio_block_t * AudioOutputSPDIF2::block_left_2nd = NULL;
  31. audio_block_t * AudioOutputSPDIF2::block_right_2nd = NULL;
  32. uint16_t AudioOutputSPDIF2::block_left_offset = 0;
  33. uint16_t AudioOutputSPDIF2::block_right_offset = 0;
  34. bool AudioOutputSPDIF2::update_responsibility = false;
  35. DMAChannel AudioOutputSPDIF2::dma(false);
  36. extern uint16_t spdif_bmclookup[256];
  37. DMAMEM __attribute__((aligned(32)))
  38. static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
  39. #define PREAMBLE_B (0xE8) //11101000
  40. #define PREAMBLE_M (0xE2) //11100010
  41. #define PREAMBLE_W (0xE4) //11100100
  42. #define VUCP_VALID ((0xCC) << 24)
  43. #define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
  44. uint32_t AudioOutputSPDIF2::vucp = VUCP_VALID;
  45. FLASHMEM
  46. void AudioOutputSPDIF2::begin(void)
  47. {
  48. dma.begin(true); // Allocate the DMA channel first
  49. block_left_1st = NULL;
  50. block_right_1st = NULL;
  51. // TODO: should we set & clear the I2S_TCSR_SR bit here?
  52. config_SPDIF();
  53. CORE_PIN2_CONFIG = 2; //2:TX_DATA0
  54. const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
  55. dma.TCD->SADDR = SPDIF_tx_buffer;
  56. dma.TCD->SOFF = 4;
  57. dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  58. dma.TCD->NBYTES_MLNO = nbytes_mlno;
  59. dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
  60. dma.TCD->DADDR = &I2S2_TDR0;
  61. dma.TCD->DOFF = 0;
  62. dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
  63. dma.TCD->DLASTSGA = 0;
  64. dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
  65. dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  66. dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
  67. update_responsibility = update_setup();
  68. dma.enable();
  69. I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  70. dma.attachInterrupt(isr);
  71. }
  72. /*
  73. http://www.hardwarebook.info/S/PDIF
  74. 1. To make it easier and a bit faster, the parity-bit is always the same.
  75. - With a alternating parity we had to adjust the next subframe. Instead, use a bit from the aux-info as parity.
  76. 2. The buffer is filled with an offset of 1 byte, so the last parity (which is always 0 now (see 1.) ) is written as first byte.
  77. -> A bit easier and faster to construct both subframes.
  78. */
  79. void AudioOutputSPDIF2::isr(void)
  80. {
  81. static uint16_t frame = 0;
  82. const int16_t *src;
  83. int32_t *end, *dest;
  84. audio_block_t *block;
  85. uint32_t saddr, offset;
  86. uint16_t sample, lo, hi, aux;
  87. saddr = (uint32_t)(dma.TCD->SADDR);
  88. dma.clearInterrupt();
  89. if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
  90. // DMA is transmitting the first half of the buffer
  91. // so we must fill the second half
  92. dest = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
  93. end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
  94. if (AudioOutputSPDIF2::update_responsibility) AudioStream::update_all();
  95. } else {
  96. // DMA is transmitting the second half of the buffer
  97. // so we must fill the first half
  98. dest = (int32_t *)SPDIF_tx_buffer;
  99. end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
  100. }
  101. block = AudioOutputSPDIF2::block_left_1st;
  102. if (block) {
  103. offset = AudioOutputSPDIF2::block_left_offset;
  104. src = &block->data[offset];
  105. do {
  106. sample = *src++;
  107. //Subframe Channel 1
  108. hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
  109. lo = spdif_bmclookup[(uint8_t) sample];
  110. lo ^= (~((int16_t)hi) >> 16);
  111. // 16 Bit sample:
  112. *(dest+1) = ((uint32_t)lo << 16) | hi;
  113. // 4 Bit Auxillary-audio-databits, the first used as parity
  114. aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
  115. if (++frame > 191) {
  116. // VUCP-Bits ("Valid, Subcode, Channelstatus, Parity) = 0 (0xcc) | Preamble (depends on Framno.) | Auxillary
  117. *(dest+0) = vucp | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
  118. frame = 0;
  119. } else {
  120. *(dest+0) = vucp | (PREAMBLE_M << 16 ) | aux;
  121. }
  122. dest += 4;
  123. } while (dest < end);
  124. offset += AUDIO_BLOCK_SAMPLES/2;
  125. if (offset < AUDIO_BLOCK_SAMPLES) {
  126. AudioOutputSPDIF2::block_left_offset = offset;
  127. } else {
  128. AudioOutputSPDIF2::block_left_offset = 0;
  129. AudioStream::release(block);
  130. AudioOutputSPDIF2::block_left_1st = AudioOutputSPDIF2::block_left_2nd;
  131. AudioOutputSPDIF2::block_left_2nd = NULL;
  132. }
  133. } else {
  134. do {
  135. if ( ++frame > 191 ) {
  136. *(dest+0) = vucp | 0x00e8cccc;
  137. frame = 0;
  138. } else {
  139. *(dest+0) = vucp | 0x00e2cccc;
  140. }
  141. *(dest+1) = 0xccccccccUL;
  142. dest +=4;
  143. } while (dest < end);
  144. }
  145. dest -= AUDIO_BLOCK_SAMPLES * 4/2 - 4/2;
  146. block = AudioOutputSPDIF2::block_right_1st;
  147. if (block) {
  148. offset = AudioOutputSPDIF2::block_right_offset;
  149. src = &block->data[offset];
  150. do {
  151. sample = *src++;
  152. //Subframe Channel 2
  153. hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
  154. lo = spdif_bmclookup[(uint8_t)sample];
  155. lo ^= (~((int16_t)hi) >> 16);
  156. *(dest+1) = ( ((uint32_t)lo << 16) | hi );
  157. aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
  158. *(dest+0) = vucp | (PREAMBLE_W << 16 ) | aux;
  159. dest += 4;
  160. } while (dest < end);
  161. offset += AUDIO_BLOCK_SAMPLES/2;
  162. if (offset < AUDIO_BLOCK_SAMPLES) {
  163. AudioOutputSPDIF2::block_right_offset = offset;
  164. } else {
  165. AudioOutputSPDIF2::block_right_offset = 0;
  166. AudioStream::release(block);
  167. AudioOutputSPDIF2::block_right_1st = AudioOutputSPDIF2::block_right_2nd;
  168. AudioOutputSPDIF2::block_right_2nd = NULL;
  169. }
  170. } else {
  171. do {
  172. *dest = vucp | 0x00e4ccccUL;
  173. *(dest+1) = 0xccccccccUL;
  174. dest += 4 ;
  175. } while (dest < end);
  176. }
  177. #if IMXRT_CACHE_ENABLED >= 2
  178. dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
  179. arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
  180. #endif
  181. }
  182. void AudioOutputSPDIF2::mute_PCM(const bool mute)
  183. {
  184. vucp = mute?VUCP_INVALID:VUCP_VALID;
  185. }
  186. void AudioOutputSPDIF2::update(void)
  187. {
  188. audio_block_t *block;
  189. block = receiveReadOnly(0); // input 0 = left channel
  190. if (block) {
  191. __disable_irq();
  192. if (block_left_1st == NULL) {
  193. block_left_1st = block;
  194. block_left_offset = 0;
  195. __enable_irq();
  196. } else if (block_left_2nd == NULL) {
  197. block_left_2nd = block;
  198. __enable_irq();
  199. } else {
  200. audio_block_t *tmp = block_left_1st;
  201. block_left_1st = block_left_2nd;
  202. block_left_2nd = block;
  203. block_left_offset = 0;
  204. __enable_irq();
  205. release(tmp);
  206. }
  207. }
  208. block = receiveReadOnly(1); // input 1 = right channel
  209. if (block) {
  210. __disable_irq();
  211. if (block_right_1st == NULL) {
  212. block_right_1st = block;
  213. block_right_offset = 0;
  214. __enable_irq();
  215. } else if (block_right_2nd == NULL) {
  216. block_right_2nd = block;
  217. __enable_irq();
  218. } else {
  219. audio_block_t *tmp = block_right_1st;
  220. block_right_1st = block_right_2nd;
  221. block_right_2nd = block;
  222. block_right_offset = 0;
  223. __enable_irq();
  224. release(tmp);
  225. }
  226. }
  227. }
  228. FLASHMEM
  229. void AudioOutputSPDIF2::config_SPDIF(void)
  230. {
  231. CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
  232. //PLL:
  233. int fs = AUDIO_SAMPLE_RATE_EXACT;
  234. // PLL between 27*24 = 648MHz und 54*24=1296MHz
  235. int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  236. int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
  237. double C = ((double)fs * 256 * n1 * n2) / 24000000;
  238. int c0 = C;
  239. int c2 = 10000;
  240. int c1 = C * c2 - (c0 * c2);
  241. set_audioClock(c0, c1, c2);
  242. CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
  243. | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
  244. CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
  245. | CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
  246. | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
  247. IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
  248. | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
  249. // configure transmitter
  250. I2S2_TMR = 0;
  251. I2S2_TCR1 = I2S_TCR1_RFW(0); // watermark
  252. I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  253. I2S2_TCR3 = I2S_TCR3_TCE;
  254. //4 Words per Frame 32 Bit Word-Length -> 128 Bit Frame-Length, MSB First:
  255. I2S2_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  256. I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
  257. #if 0
  258. //debug only:
  259. CORE_PIN5_CONFIG = 2; //2:MCLK 11.43MHz
  260. CORE_PIN4_CONFIG = 2; //2:TX_BCLK 5 MHz
  261. CORE_PIN3_CONFIG = 2; //2:TX_SYNC 44.1 KHz
  262. #endif
  263. }
  264. #endif