Browse Source

Merge pull request #289 from FrankBoesing/master

SPDIF3, initial cache-support
dds
Paul Stoffregen 5 years ago
parent
commit
f7e5e30cd3
No account linked to committer's email address
12 changed files with 438 additions and 110 deletions
  1. +1
    -0
      Audio.h
  2. +63
    -0
      data_spdif.c
  3. +1
    -0
      keywords.txt
  4. +5
    -2
      output_i2s.cpp
  5. +6
    -4
      output_i2s2.cpp
  6. +12
    -7
      output_mqs.cpp
  7. +15
    -41
      output_spdif.cpp
  8. +16
    -55
      output_spdif2.cpp
  9. +258
    -0
      output_spdif3.cpp
  10. +53
    -0
      output_spdif3.h
  11. +1
    -1
      utility/imxrt_hw.cpp
  12. +7
    -0
      utility/imxrt_hw.h

+ 1
- 0
Audio.h View File

@@ -105,6 +105,7 @@
#include "output_pwm.h"
#include "output_spdif.h"
#include "output_spdif2.h"
#include "output_spdif3.h"
#include "output_pt8211.h"
#include "output_pt8211_2.h"
#include "output_tdm.h"

+ 63
- 0
data_spdif.c View File

@@ -0,0 +1,63 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* 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.
*/
#include <stdint.h>

const __attribute__((aligned(32)))
uint16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};

+ 1
- 0
keywords.txt View File

@@ -13,6 +13,7 @@ AudioOutputI2SQuad KEYWORD2
AudioOutputI2Sslave KEYWORD2
AudioOutputSPDIF KEYWORD2
AudioOutputSPDIF2 KEYWORD2
AudioOutputSPDIF3 KEYWORD2
AudioOutputPT8211 KEYWORD2
AudioOutputPT8211_2 KEYWORD2
AudioOutputTDM KEYWORD2

+ 5
- 2
output_i2s.cpp View File

@@ -35,8 +35,8 @@ audio_block_t * AudioOutputI2S::block_right_2nd = NULL;
uint16_t AudioOutputI2S::block_left_offset = 0;
uint16_t AudioOutputI2S::block_right_offset = 0;
bool AudioOutputI2S::update_responsibility = false;
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
DMAChannel AudioOutputI2S::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"
@@ -147,9 +147,12 @@ void AudioOutputI2S::isr(void)
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
return;
}

#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );
#endif
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S::block_left_offset = offsetL;
} else {

+ 6
- 4
output_i2s2.cpp View File

@@ -35,9 +35,8 @@ audio_block_t * AudioOutputI2S2::block_right_2nd = NULL;
uint16_t AudioOutputI2S2::block_left_offset = 0;
uint16_t AudioOutputI2S2::block_right_offset = 0;
bool AudioOutputI2S2::update_responsibility = false;
static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES];
DMAChannel AudioOutputI2S2::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES];

#include "utility/imxrt_hw.h"

@@ -105,10 +104,13 @@ void AudioOutputI2S2::isr(void)
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
return;
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
}

#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2 );
#endif
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S2::block_left_offset = offsetL;
} else {

+ 12
- 7
output_mqs.cpp View File

@@ -38,8 +38,10 @@ audio_block_t * AudioOutputMQS::block_right_2nd = NULL;
uint16_t AudioOutputMQS::block_left_offset = 0;
uint16_t AudioOutputMQS::block_right_offset = 0;
bool AudioOutputMQS::update_responsibility = false;
static uint32_t I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES];
DMAChannel AudioOutputMQS::dma(false);
DMAMEM __attribute__((aligned(32)))
static uint32_t I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES];


void AudioOutputMQS::begin(void)
{
@@ -99,18 +101,21 @@ void AudioOutputMQS::isr(void)
if (blockL && blockR) {
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockL) {
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetL += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockR) {
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
return;
memset(dest,0, sizeof(I2S3_tx_buffer) / 2);
}

#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(I2S3_tx_buffer) / 2 );
#endif
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputMQS::block_left_offset = offsetL;
} else {

+ 15
- 41
output_spdif.cpp View File

@@ -34,9 +34,11 @@ audio_block_t * AudioOutputSPDIF::block_right_2nd = NULL;
uint16_t AudioOutputSPDIF::block_left_offset = 0;
uint16_t AudioOutputSPDIF::block_right_offset = 0;
bool AudioOutputSPDIF::update_responsibility = false;

/* DMAMEM */ static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
DMAChannel AudioOutputSPDIF::dma(false);
extern uint16_t spdif_bmclookup[256];
DMAMEM __attribute__((aligned(32)))
static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB


#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)

@@ -49,43 +51,9 @@ DMAChannel AudioOutputSPDIF::dma(false);

uint32_t AudioOutputSPDIF::vucp = VUCP_VALID;

static const
uint16_t bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};
#endif

PROGMEM
void AudioOutputSPDIF::begin(void)
{

@@ -189,8 +157,8 @@ void AudioOutputSPDIF::isr(void)
sample = *src++;

//Subframe Channel 1
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t) sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t) sample];
lo ^= (~((int16_t)hi) >> 16);
// 16 Bit sample:
*(dest+1) = ((uint32_t)lo << 16) | hi;
@@ -241,8 +209,8 @@ void AudioOutputSPDIF::isr(void)
sample = *src++;

//Subframe Channel 2
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t)sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t)sample];
lo ^= (~((int16_t)hi) >> 16);

*(dest+1) = ( ((uint32_t)lo << 16) | hi );
@@ -270,6 +238,11 @@ void AudioOutputSPDIF::isr(void)
} while (dest < end);
}

#if IMXRT_CACHE_ENABLED >= 2
dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
#endif
}

void AudioOutputSPDIF::mute_PCM(const bool mute)
@@ -370,6 +343,7 @@ void AudioOutputSPDIF::update(void)
#endif
#endif

PROGMEM
void AudioOutputSPDIF::config_SPDIF(void)
{
#if defined(KINETISK)

+ 16
- 55
output_spdif2.cpp View File

@@ -23,6 +23,8 @@

// 2015/08/23: (FB) added mute_PCM() - sets or unsets VALID in VUCP (and adjusts PARITY)

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_spdif2.h"
#include "utility/imxrt_hw.h"
@@ -34,11 +36,11 @@ audio_block_t * AudioOutputSPDIF2::block_right_2nd = NULL;
uint16_t AudioOutputSPDIF2::block_left_offset = 0;
uint16_t AudioOutputSPDIF2::block_right_offset = 0;
bool AudioOutputSPDIF2::update_responsibility = false;

/* DMAMEM */ static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
DMAChannel AudioOutputSPDIF2::dma(false);
extern uint16_t spdif_bmclookup[256];

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
DMAMEM __attribute__((aligned(32)))
static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB

#define PREAMBLE_B (0xE8) //11101000
#define PREAMBLE_M (0xE2) //11100010
@@ -49,42 +51,7 @@ DMAChannel AudioOutputSPDIF2::dma(false);

uint32_t AudioOutputSPDIF2::vucp = VUCP_VALID;

static const
uint16_t bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};

PROGMEM
void AudioOutputSPDIF2::begin(void)
{

@@ -154,7 +121,6 @@ void AudioOutputSPDIF2::isr(void)
end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
}


block = AudioOutputSPDIF2::block_left_1st;
if (block) {
offset = AudioOutputSPDIF2::block_left_offset;
@@ -164,8 +130,8 @@ void AudioOutputSPDIF2::isr(void)
sample = *src++;

//Subframe Channel 1
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t) sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t) sample];
lo ^= (~((int16_t)hi) >> 16);
// 16 Bit sample:
*(dest+1) = ((uint32_t)lo << 16) | hi;
@@ -216,8 +182,8 @@ void AudioOutputSPDIF2::isr(void)
sample = *src++;

//Subframe Channel 2
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t)sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t)sample];
lo ^= (~((int16_t)hi) >> 16);

*(dest+1) = ( ((uint32_t)lo << 16) | hi );
@@ -245,6 +211,11 @@ void AudioOutputSPDIF2::isr(void)
} while (dest < end);
}

#if IMXRT_CACHE_ENABLED >= 2
dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
#endif

}

void AudioOutputSPDIF2::mute_PCM(const bool mute)
@@ -298,6 +269,7 @@ void AudioOutputSPDIF2::update(void)

}

PROGMEM
void AudioOutputSPDIF2::config_SPDIF(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
@@ -339,16 +311,5 @@ void AudioOutputSPDIF2::config_SPDIF(void)
}


#else

void AudioOutputSPDIF2::update(void)
{

audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) release(block);
block = receiveReadOnly(1); // input 1 = right channel
if (block) release(block);
}

#endif

+ 258
- 0
output_spdif3.cpp View File

@@ -0,0 +1,258 @@
/* Hardware-SPDIF for Teensy 4
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
*
* 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.
*/
/*
http://www.hardwarebook.info/S/PDIF
https://www.mikrocontroller.net/articles/S/PDIF
https://en.wikipedia.org/wiki/S/PDIF
*/

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)

#include <Arduino.h>
#include "output_spdif3.h"
#include "utility/imxrt_hw.h"
#include "memcpy_audio.h"
#include <math.h>

audio_block_t * AudioOutputSPDIF3::block_left_1st = nullptr;
audio_block_t * AudioOutputSPDIF3::block_right_1st = nullptr;
audio_block_t * AudioOutputSPDIF3::block_left_2nd = nullptr;
audio_block_t * AudioOutputSPDIF3::block_right_2nd = nullptr;
bool AudioOutputSPDIF3::update_responsibility = false;
DMAChannel AudioOutputSPDIF3::dma(false);

DMAMEM __attribute__((aligned(32)))
static int32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
DMAMEM __attribute__((aligned(32)))
audio_block_t AudioOutputSPDIF3::block_silent;

PROGMEM
void AudioOutputSPDIF3::begin(void)
{

dma.begin(true); // Allocate the DMA channel first

block_left_1st = nullptr;
block_right_1st = nullptr;
memset(&block_silent, 0, sizeof(block_silent));
uint32_t fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
// n1, n2 choosen for compatibility with I2S (same PLL frequency) :
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);

double pllclock = (c0 + (double)c1 / c2) * 24000000; //677376000 Hz

//use new pred/podf values
n1 = 7; //0: divide by 1 (do not use with high input frequencies), 1:/2, 2: /3, 7:/8
n2 = 0; //0: divide by 1, 7: divide by 8

uint32_t clock = pllclock / (1 + n1) / (1 + n2);
uint32_t clkdiv = clock / (fs * 64); // 1 .. 128
uint32_t mod = clock % (fs * 64);
if (mod > ((fs * 64) / 2)) clkdiv += 1; //nearest divider

CCM_CCGR5 &= ~CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate off

CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_SPDIF0_CLK_SEL_MASK | CCM_CDCDR_SPDIF0_CLK_PRED_MASK | CCM_CDCDR_SPDIF0_CLK_PODF_MASK))
| CCM_CDCDR_SPDIF0_CLK_SEL(0) // 0 PLL4, 1 PLL3 PFD2, 2 PLL5, 3 pll3_sw_clk
| CCM_CDCDR_SPDIF0_CLK_PRED(n1)
| CCM_CDCDR_SPDIF0_CLK_PODF(n2);

CCM_CCGR5 |= CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate on

if ((SPDIF_SCR & 0x400) != 0) { //If default value:
SPDIF_SCR = SPDIF_SCR_SOFT_RESET; //Reset SPDIF
while (SPDIF_SCR & SPDIF_SCR_SOFT_RESET) {;} //Wait for Reset (takes 8 cycles)
SPDIF_SCR = 0;
}

SPDIF_SCR |=
SPDIF_SCR_RXFIFOFULL_SEL(0) |// Full interrupt if at least 1 sample in Rx left and right FIFOs
SPDIF_SCR_RXAUTOSYNC |
SPDIF_SCR_TXAUTOSYNC |
SPDIF_SCR_TXFIFOEMPTY_SEL(3) | // Empty interrupt if at most 4 sample in Tx left and right FIFOs
SPDIF_SCR_TXFIFO_CTRL(1) | // 0:Send zeros 1: normal operation
SPDIF_SCR_DMA_TX_EN |
//SPDIF_SCR_VALCTRL | // Outgoing Validity always clear
SPDIF_SCR_TXSEL(5) | //0:off and output 0, 1:Feed-though SPDIFIN, 5:Tx Normal operation
SPDIF_SCR_USRC_SEL(3); // No embedded U channel
SPDIF_SRCD = 0;
SPDIF_STCSCH = 0;
SPDIF_STCSCL = 0;
SPDIF_SRCD = 0;

SPDIF_STC = SPDIF_STC_TXCLK_SOURCE(1) //tx_clk input (from SPDIF0_CLK_ROOT)
| SPDIF_STC_SYSCLK_DF(0)
| SPDIF_STC_TXCLK_DF(clkdiv - 1);

const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop

dma.TCD->SADDR = SPDIF_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(nbytes_mlno) | DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8);
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
dma.TCD->DADDR = &SPDIF_STL;
dma.TCD->DOFF = 4;
dma.TCD->DLASTSGA = -8;
//dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3);
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX);

update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);

CORE_PIN14_CONFIG = 3; //3:SPDIF_OUT
SPDIF_STC |= SPDIF_STC_TX_ALL_CLK_EN;
// pinMode(13, OUTPUT);
}

void AudioOutputSPDIF3::isr(void)
{

const int16_t *src_left, *src_right;
const int32_t *end;
int32_t *dest;
audio_block_t *block_left, *block_right;
uint32_t saddr;

saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*4;
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = SPDIF_tx_buffer;
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
}
block_left = block_left_1st;
if (!block_left) block_left = &block_silent;
block_right = block_right_1st;
if (!block_right) block_right = &block_silent;

src_left = (const int16_t *)(block_left->data);
src_right = (const int16_t *)(block_right->data);
do {
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;

*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;

*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
#if IMXRT_CACHE_ENABLED >= 2
SCB_CACHE_DCCIMVAC = (uint32_t) dest - 32;
#endif
} while (dest < end);
if (block_left != &block_silent) {
release(block_left);
block_left_1st = block_left_2nd;
block_left_2nd = nullptr;
}
if (block_right != &block_silent) {
release(block_right);
block_right_1st = block_right_2nd;
block_right_2nd = nullptr;
}

if (update_responsibility) update_all();
//digitalWriteFast(13,!digitalReadFast(13));
}

void AudioOutputSPDIF3::update(void)
{
audio_block_t *block_left, *block_right;

block_left = receiveReadOnly(0); // input 0
block_right = receiveReadOnly(1); // input 1
__disable_irq();
if (block_left) {
if (block_left_1st == nullptr) {
block_left_1st = block_left;
block_left = nullptr;
} else if (block_left_2nd == nullptr) {
block_left_2nd = block_left;
block_left = nullptr;
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block_left;
block_left = tmp;
}
}
if (block_right) {
if (block_right_1st == nullptr) {
block_right_1st = block_right;
block_right = nullptr;
} else if (block_right_2nd == nullptr) {
block_right_2nd = block_right;
block_right = nullptr;
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block_right;
block_right = tmp;
}
}
__enable_irq();
if (block_left) {
release(block_left);
}
if (block_right) {
release(block_right);
}

}

void AudioOutputSPDIF3::mute_PCM(const bool mute)
{
if (mute)
SPDIF_SCR |= SPDIF_SCR_VALCTRL;
else
SPDIF_SCR &= ~SPDIF_SCR_VALCTRL;
}
#endif

+ 53
- 0
output_spdif3.h View File

@@ -0,0 +1,53 @@
/* Hardware-SPDIF for Teensy 4
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
*
* 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.
*/

#ifndef output_SPDIF3_h_
#define output_SPDIF3_h_

#include <Arduino.h>
#include <AudioStream.h>
#include <DMAChannel.h>

class AudioOutputSPDIF3 : public AudioStream
{
public:
AudioOutputSPDIF3(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
//friend class AudioInputSPDIF;
static void mute_PCM(const bool mute);
protected:
//AudioOutputSPDIF3(int dummy): AudioStream(2, inputQueueArray) {}
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static audio_block_t block_silent;
audio_block_t *inputQueueArray[2];
};


#endif

+ 1
- 1
utility/imxrt_hw.cpp View File

@@ -40,7 +40,7 @@ void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv) // sets PLL4
CCM_ANALOG_PLL_AUDIO = 0;
//CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_BYPASS;
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 0: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);

CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;

+ 7
- 0
utility/imxrt_hw.h View File

@@ -32,10 +32,17 @@
#ifndef imxr_hw_h_
#define imxr_hw_h_

#define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB

#include <Arduino.h>
#include <imxrt.h>

void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv); // sets PLL4

#endif

#else
//No IMXRT
#define IMXRT_CACHE_ENABLED 0
#endif

Loading…
Cancel
Save