Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. #include <Arduino.h>
  25. #include "output_spdif2.h"
  26. #include "utility/imxrt_hw.h"
  27. audio_block_t * AudioOutputSPDIF2::block_left_1st = NULL;
  28. audio_block_t * AudioOutputSPDIF2::block_right_1st = NULL;
  29. audio_block_t * AudioOutputSPDIF2::block_left_2nd = NULL;
  30. audio_block_t * AudioOutputSPDIF2::block_right_2nd = NULL;
  31. uint16_t AudioOutputSPDIF2::block_left_offset = 0;
  32. uint16_t AudioOutputSPDIF2::block_right_offset = 0;
  33. bool AudioOutputSPDIF2::update_responsibility = false;
  34. /* DMAMEM */ static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
  35. DMAChannel AudioOutputSPDIF2::dma(false);
  36. #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  37. #define PREAMBLE_B (0xE8) //11101000
  38. #define PREAMBLE_M (0xE2) //11100010
  39. #define PREAMBLE_W (0xE4) //11100100
  40. #define VUCP_VALID ((0xCC) << 24)
  41. #define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
  42. uint32_t AudioOutputSPDIF2::vucp = VUCP_VALID;
  43. static const
  44. uint16_t bmclookup[256] = { //biphase mark encoded values (least significant bit first)
  45. 0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
  46. 0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
  47. 0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
  48. 0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
  49. 0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
  50. 0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
  51. 0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
  52. 0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
  53. 0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
  54. 0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
  55. 0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
  56. 0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
  57. 0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
  58. 0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
  59. 0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
  60. 0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
  61. 0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
  62. 0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
  63. 0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
  64. 0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
  65. 0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
  66. 0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
  67. 0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
  68. 0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
  69. 0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
  70. 0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
  71. 0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
  72. 0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
  73. 0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
  74. 0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
  75. 0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
  76. 0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
  77. };
  78. void AudioOutputSPDIF2::begin(void)
  79. {
  80. dma.begin(true); // Allocate the DMA channel first
  81. block_left_1st = NULL;
  82. block_right_1st = NULL;
  83. // TODO: should we set & clear the I2S_TCSR_SR bit here?
  84. config_SPDIF();
  85. CORE_PIN2_CONFIG = 2; //2:TX_DATA0
  86. const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
  87. dma.TCD->SADDR = SPDIF_tx_buffer;
  88. dma.TCD->SOFF = 4;
  89. dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  90. dma.TCD->NBYTES_MLNO = nbytes_mlno;
  91. dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
  92. dma.TCD->DADDR = &I2S2_TDR0;
  93. dma.TCD->DOFF = 0;
  94. dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
  95. dma.TCD->DLASTSGA = 0;
  96. dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
  97. dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  98. dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
  99. update_responsibility = update_setup();
  100. dma.enable();
  101. I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  102. dma.attachInterrupt(isr);
  103. }
  104. /*
  105. http://www.hardwarebook.info/S/PDIF
  106. 1. To make it easier and a bit faster, the parity-bit is always the same.
  107. - With a alternating parity we had to adjust the next subframe. Instead, use a bit from the aux-info as parity.
  108. 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.
  109. -> A bit easier and faster to construct both subframes.
  110. */
  111. void AudioOutputSPDIF2::isr(void)
  112. {
  113. static uint16_t frame = 0;
  114. const int16_t *src;
  115. int32_t *end, *dest;
  116. audio_block_t *block;
  117. uint32_t saddr, offset;
  118. uint16_t sample, lo, hi, aux;
  119. saddr = (uint32_t)(dma.TCD->SADDR);
  120. dma.clearInterrupt();
  121. if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
  122. // DMA is transmitting the first half of the buffer
  123. // so we must fill the second half
  124. dest = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
  125. end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
  126. if (AudioOutputSPDIF2::update_responsibility) AudioStream::update_all();
  127. } else {
  128. // DMA is transmitting the second half of the buffer
  129. // so we must fill the first half
  130. dest = (int32_t *)SPDIF_tx_buffer;
  131. end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
  132. }
  133. block = AudioOutputSPDIF2::block_left_1st;
  134. if (block) {
  135. offset = AudioOutputSPDIF2::block_left_offset;
  136. src = &block->data[offset];
  137. do {
  138. sample = *src++;
  139. //Subframe Channel 1
  140. hi = bmclookup[(uint8_t)(sample >> 8)];
  141. lo = bmclookup[(uint8_t) sample];
  142. lo ^= (~((int16_t)hi) >> 16);
  143. // 16 Bit sample:
  144. *(dest+1) = ((uint32_t)lo << 16) | hi;
  145. // 4 Bit Auxillary-audio-databits, the first used as parity
  146. aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
  147. if (++frame > 191) {
  148. // VUCP-Bits ("Valid, Subcode, Channelstatus, Parity) = 0 (0xcc) | Preamble (depends on Framno.) | Auxillary
  149. *(dest+0) = vucp | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
  150. frame = 0;
  151. } else {
  152. *(dest+0) = vucp | (PREAMBLE_M << 16 ) | aux;
  153. }
  154. dest += 4;
  155. } while (dest < end);
  156. offset += AUDIO_BLOCK_SAMPLES/2;
  157. if (offset < AUDIO_BLOCK_SAMPLES) {
  158. AudioOutputSPDIF2::block_left_offset = offset;
  159. } else {
  160. AudioOutputSPDIF2::block_left_offset = 0;
  161. AudioStream::release(block);
  162. AudioOutputSPDIF2::block_left_1st = AudioOutputSPDIF2::block_left_2nd;
  163. AudioOutputSPDIF2::block_left_2nd = NULL;
  164. }
  165. } else {
  166. do {
  167. if ( ++frame > 191 ) {
  168. *(dest+0) = vucp | 0x00e8cccc;
  169. frame = 0;
  170. } else {
  171. *(dest+0) = vucp | 0x00e2cccc;
  172. }
  173. *(dest+1) = 0xccccccccUL;
  174. dest +=4;
  175. } while (dest < end);
  176. }
  177. dest -= AUDIO_BLOCK_SAMPLES * 4/2 - 4/2;
  178. block = AudioOutputSPDIF2::block_right_1st;
  179. if (block) {
  180. offset = AudioOutputSPDIF2::block_right_offset;
  181. src = &block->data[offset];
  182. do {
  183. sample = *src++;
  184. //Subframe Channel 2
  185. hi = bmclookup[(uint8_t)(sample >> 8)];
  186. lo = bmclookup[(uint8_t)sample];
  187. lo ^= (~((int16_t)hi) >> 16);
  188. *(dest+1) = ( ((uint32_t)lo << 16) | hi );
  189. aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
  190. *(dest+0) = vucp | (PREAMBLE_W << 16 ) | aux;
  191. dest += 4;
  192. } while (dest < end);
  193. offset += AUDIO_BLOCK_SAMPLES/2;
  194. if (offset < AUDIO_BLOCK_SAMPLES) {
  195. AudioOutputSPDIF2::block_right_offset = offset;
  196. } else {
  197. AudioOutputSPDIF2::block_right_offset = 0;
  198. AudioStream::release(block);
  199. AudioOutputSPDIF2::block_right_1st = AudioOutputSPDIF2::block_right_2nd;
  200. AudioOutputSPDIF2::block_right_2nd = NULL;
  201. }
  202. } else {
  203. do {
  204. *dest = vucp | 0x00e4ccccUL;
  205. *(dest+1) = 0xccccccccUL;
  206. dest += 4 ;
  207. } while (dest < end);
  208. }
  209. }
  210. void AudioOutputSPDIF2::mute_PCM(const bool mute)
  211. {
  212. vucp = mute?VUCP_INVALID:VUCP_VALID;
  213. }
  214. void AudioOutputSPDIF2::update(void)
  215. {
  216. audio_block_t *block;
  217. block = receiveReadOnly(0); // input 0 = left channel
  218. if (block) {
  219. __disable_irq();
  220. if (block_left_1st == NULL) {
  221. block_left_1st = block;
  222. block_left_offset = 0;
  223. __enable_irq();
  224. } else if (block_left_2nd == NULL) {
  225. block_left_2nd = block;
  226. __enable_irq();
  227. } else {
  228. audio_block_t *tmp = block_left_1st;
  229. block_left_1st = block_left_2nd;
  230. block_left_2nd = block;
  231. block_left_offset = 0;
  232. __enable_irq();
  233. release(tmp);
  234. }
  235. }
  236. block = receiveReadOnly(1); // input 1 = right channel
  237. if (block) {
  238. __disable_irq();
  239. if (block_right_1st == NULL) {
  240. block_right_1st = block;
  241. block_right_offset = 0;
  242. __enable_irq();
  243. } else if (block_right_2nd == NULL) {
  244. block_right_2nd = block;
  245. __enable_irq();
  246. } else {
  247. audio_block_t *tmp = block_right_1st;
  248. block_right_1st = block_right_2nd;
  249. block_right_2nd = block;
  250. block_right_offset = 0;
  251. __enable_irq();
  252. release(tmp);
  253. }
  254. }
  255. }
  256. void AudioOutputSPDIF2::config_SPDIF(void)
  257. {
  258. CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
  259. //PLL:
  260. int fs = AUDIO_SAMPLE_RATE_EXACT;
  261. // PLL between 27*24 = 648MHz und 54*24=1296MHz
  262. int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  263. int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
  264. double C = ((double)fs * 256 * n1 * n2) / 24000000;
  265. int c0 = C;
  266. int c2 = 10000;
  267. int c1 = C * c2 - (c0 * c2);
  268. set_audioClock(c0, c1, c2);
  269. CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
  270. | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
  271. CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
  272. | CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
  273. | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
  274. IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
  275. | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
  276. // configure transmitter
  277. I2S2_TMR = 0;
  278. I2S2_TCR1 = I2S_TCR1_RFW(0); // watermark
  279. I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  280. I2S2_TCR3 = I2S_TCR3_TCE;
  281. //4 Words per Frame 32 Bit Word-Length -> 128 Bit Frame-Length, MSB First:
  282. I2S2_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  283. I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
  284. #if 0
  285. //debug only:
  286. CORE_PIN5_CONFIG = 2; //2:MCLK 11.43MHz
  287. CORE_PIN4_CONFIG = 2; //2:TX_BCLK 5 MHz
  288. CORE_PIN3_CONFIG = 2; //2:TX_SYNC 44.1 KHz
  289. #endif
  290. }
  291. #else
  292. void AudioOutputSPDIF2::update(void)
  293. {
  294. audio_block_t *block;
  295. block = receiveReadOnly(0); // input 0 = left channel
  296. if (block) release(block);
  297. block = receiveReadOnly(1); // input 1 = right channel
  298. if (block) release(block);
  299. }
  300. #endif