Przeglądaj źródła

Merge pull request #317 from FrankBoesing/spdif

Spdif-input
dds
Paul Stoffregen 4 lat temu
rodzic
commit
4a3a990df7
Brak konta powiązanego z adresem e-mail autora
6 zmienionych plików z 424 dodań i 79 usunięć
  1. +1
    -0
      Audio.h
  2. +253
    -0
      input_spdif3.cpp
  3. +56
    -0
      input_spdif3.h
  4. +2
    -0
      keywords.txt
  5. +107
    -77
      output_spdif3.cpp
  6. +5
    -2
      output_spdif3.h

+ 1
- 0
Audio.h Wyświetl plik

#include "input_tdm.h" #include "input_tdm.h"
#include "input_tdm2.h" #include "input_tdm2.h"
#include "input_pdm.h" #include "input_pdm.h"
#include "input_spdif3.h"
#include "mixer.h" #include "mixer.h"
#include "output_dac.h" #include "output_dac.h"
#include "output_dacs.h" #include "output_dacs.h"

+ 253
- 0
input_spdif3.cpp Wyświetl plik

/* Audio Library for Teensy 3.X
* Copyright (c) 2019, 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.
*/
/*
by Frank Bösing
*/

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "input_spdif3.h"
#include "output_spdif3.h"
#include "utility/imxrt_hw.h"

DMAMEM __attribute__((aligned(32)))
static uint32_t spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 4];
audio_block_t * AudioInputSPDIF3::block_left = NULL;
audio_block_t * AudioInputSPDIF3::block_right = NULL;
uint16_t AudioInputSPDIF3::block_offset = 0;
bool AudioInputSPDIF3::update_responsibility = false;
DMAChannel AudioInputSPDIF3::dma(false);

FLASHMEM
void AudioInputSPDIF3::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

AudioOutputSPDIF3::config_spdif3();

const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
dma.TCD->SADDR = &SPDIF_SRL;
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_SMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8);
dma.TCD->SLAST = -8;
dma.TCD->DADDR = spdif_rx_buffer;
dma.TCD->DOFF = 4;
dma.TCD->DLASTSGA = -sizeof(spdif_rx_buffer);
dma.TCD->CITER_ELINKNO = sizeof(spdif_rx_buffer) / nbytes_mlno;
dma.TCD->BITER_ELINKNO = sizeof(spdif_rx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

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

SPDIF_SRCD = 0;
SPDIF_SCR |= SPDIF_SCR_DMA_RX_EN;
CORE_PIN15_CONFIG = 3;
IOMUXC_SPDIF_IN_SELECT_INPUT = 0; // GPIO_AD_B1_03_ALT3
//pinMode(13, OUTPUT);
}

void AudioInputSPDIF3::isr(void)
{
uint32_t daddr, offset;
const int32_t *src, *end;
int16_t *dest_left, *dest_right;
audio_block_t *left, *right;

dma.clearInterrupt();
//digitalWriteFast(13, !digitalReadFast(13));
if (AudioInputSPDIF3::update_responsibility) AudioStream::update_all();

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

if (daddr < (uint32_t)spdif_rx_buffer + sizeof(spdif_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 2];
end = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 4];
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int32_t *)&spdif_rx_buffer[0];
end = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES*2];
}

left = AudioInputSPDIF3::block_left;
right = AudioInputSPDIF3::block_right;

if (left != NULL && right != NULL) {
offset = AudioInputSPDIF3::block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES*2) {
dest_left = &(left->data[offset]);
dest_right = &(right->data[offset]);
AudioInputSPDIF3::block_offset = offset + AUDIO_BLOCK_SAMPLES*2;

do {
#if IMXRT_CACHE_ENABLED >=1
SCB_CACHE_DCIMVAC = (uintptr_t)src;
asm("dsb":::"memory");
#endif

*dest_left++ = (*src++) >> 8;
*dest_right++ = (*src++) >> 8;

*dest_left++ = (*src++) >> 8;
*dest_right++ = (*src++) >> 8;

*dest_left++ = (*src++) >> 8;
*dest_right++ = (*src++) >> 8;

*dest_left++ = (*src++) >> 8;
*dest_right++ = (*src++) >> 8;

} while (src < end);
}
}
else if (left != NULL) {
offset = AudioInputSPDIF3::block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES*2) {
dest_left = &(left->data[offset]);
AudioInputSPDIF3::block_offset = offset + AUDIO_BLOCK_SAMPLES*2;

do {
#if IMXRT_CACHE_ENABLED >=1
SCB_CACHE_DCIMVAC = (uintptr_t)src;
asm("dsb":::"memory");
#endif

*dest_left++ = (*src++) >> 8;
src++;

*dest_left++ = (*src++) >> 8;
src++;

*dest_left++ = (*src++) >> 8;
src++;

*dest_left++ = (*src++) >> 8;
src++;

} while (src < end);
}
}
else if (right != NULL) {
offset = AudioInputSPDIF3::block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES*2) {
dest_right = &(right->data[offset]);
AudioInputSPDIF3::block_offset = offset + AUDIO_BLOCK_SAMPLES*2;

do {
#if IMXRT_CACHE_ENABLED >=1
SCB_CACHE_DCIMVAC = (uintptr_t)src;
asm("dsb":::"memory");
#endif

src++;
*dest_right++ = (*src++) >> 8;

src++;
*dest_right++ = (*src++) >> 8;

src++;
*dest_right++ = (*src++) >> 8;

src++;
*dest_right++ = (*src++) >> 8;

} while (src < end);
}
}

}


void AudioInputSPDIF3::update(void)
{
audio_block_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL;

// allocate 2 new blocks, but if one fails, allocate neither
new_left = allocate();
if (new_left != NULL) {
new_right = allocate();
if (new_right == NULL) {
release(new_left);
new_left = NULL;
}
}
__disable_irq();
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
// the DMA filled 2 blocks, so grab them and get the
// 2 new blocks to the DMA, as quickly as possible
out_left = block_left;
block_left = new_left;
out_right = block_right;
block_right = new_right;
block_offset = 0;
__enable_irq();
// then transmit the DMA's former blocks
transmit(out_left, 0);
release(out_left);
transmit(out_right, 1);
release(out_right);
//Serial.print(".");
} else if (new_left != NULL) {
// the DMA didn't fill blocks, but we allocated blocks
if (block_left == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_left = new_left;
block_right = new_right;
block_offset = 0;
__enable_irq();
} else {
// the DMA already has blocks, doesn't need these
__enable_irq();
release(new_left);
release(new_right);
}
} else {
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}

bool AudioInputSPDIF3::pllLocked(void)
{
return (SPDIF_SRPC & SPDIF_SRPC_LOCK) == SPDIF_SRPC_LOCK ? true:false;
}

unsigned int AudioInputSPDIF3::sampleRate(void) {
if (!pllLocked()) return 0;
return (float)((uint64_t)F_BUS_ACTUAL * SPDIF_SRFM) / (0x8000000ULL * AudioOutputSPDIF3::dpll_Gain()) + 0.5F;
}

#endif

+ 56
- 0
input_spdif3.h Wyświetl plik

/* Audio Library for Teensy 3.X
* Copyright (c) 2019, 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.
*/
// Frank Bösing

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef _input_spdif3_h_
#define _input_spdif3_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioInputSPDIF3 : public AudioStream
{
public:
AudioInputSPDIF3(void) : AudioStream(0, NULL) { begin(); }
virtual void update(void);
void begin(void);
static bool pllLocked(void);
static unsigned int sampleRate(void);
protected:
//AudioInputSPDIF3(int dummy): AudioStream(0, NULL) {} // to be used only inside AudioInputSPDIF3slave !!
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left;
static audio_block_t *block_right;
static uint16_t block_offset;
};

#endif
#endif

+ 2
- 0
keywords.txt Wyświetl plik

AudioInputTDM2 KEYWORD2 AudioInputTDM2 KEYWORD2
AudioInputPDM KEYWORD2 AudioInputPDM KEYWORD2
AudioInputUSB KEYWORD2 AudioInputUSB KEYWORD2
AudioInputSPDIF3 KEYWORD2
AudioOutputI2S KEYWORD2 AudioOutputI2S KEYWORD2
AudioOutputI2S2 KEYWORD2 AudioOutputI2S2 KEYWORD2
AudioOutputI2SQuad KEYWORD2 AudioOutputI2SQuad KEYWORD2
muteOutput KEYWORD2 muteOutput KEYWORD2
unmuteOutput KEYWORD2 unmuteOutput KEYWORD2
muteInput KEYWORD2 muteInput KEYWORD2
pllLocked KEYWORD2
unmuteInput KEYWORD2 unmuteInput KEYWORD2
enableDither KEYWORD2 enableDither KEYWORD2
disableDither KEYWORD2 disableDither KEYWORD2

+ 107
- 77
output_spdif3.cpp Wyświetl plik

DMAMEM __attribute__((aligned(32))) DMAMEM __attribute__((aligned(32)))
audio_block_t AudioOutputSPDIF3::block_silent; audio_block_t AudioOutputSPDIF3::block_silent;


PROGMEM
#define SPDIF_DPLL_GAIN24 0
#define SPDIF_DPLL_GAIN16 1
#define SPDIF_DPLL_GAIN12 2
#define SPDIF_DPLL_GAIN8 3
#define SPDIF_DPLL_GAIN6 4
#define SPDIF_DPLL_GAIN4 5
#define SPDIF_DPLL_GAIN3 6
#define SPDIF_DPLL_GAIN1 7

#define SPDIF_DPLL_GAIN SPDIF_DPLL_GAIN8 //Actual Gain
static const uint8_t spdif_gain[8] = {24, 16, 12, 8, 6, 4, 3, 1};

FLASHMEM
void AudioOutputSPDIF3::begin(void) void AudioOutputSPDIF3::begin(void)
{ {


block_left_1st = nullptr; block_left_1st = nullptr;
block_right_1st = nullptr; block_right_1st = nullptr;
memset(&block_silent, 0, sizeof(block_silent)); 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);


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


dma.TCD->SADDR = SPDIF_tx_buffer; dma.TCD->SADDR = SPDIF_tx_buffer;
dma.TCD->SOFF = 4; 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->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_NBYTES_MLOFFYES_MLOFF(-8);
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer); dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
dma.TCD->DADDR = &SPDIF_STL; dma.TCD->DADDR = &SPDIF_STL;
dma.TCD->DOFF = 4; dma.TCD->DOFF = 4;
dma.TCD->DLASTSGA = -8; dma.TCD->DLASTSGA = -8;
//dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3); //dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3);
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->BITER_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.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;


dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX);


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


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


src_left = (const int16_t *)(block_left->data); src_left = (const int16_t *)(block_left->data);
src_right = (const int16_t *)(block_right->data); src_right = (const int16_t *)(block_right->data);
do { do {
#if IMXRT_CACHE_ENABLED >= 2
SCB_CACHE_DCCIMVAC = (uintptr_t) dest;
asm volatile("dsb");
#endif
*dest++ = (*src_left++) << 8; *dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8; *dest++ = (*src_right++) << 8;


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


*dest++ = (*src_left++) << 8; *dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8; *dest++ = (*src_right++) << 8;
#if IMXRT_CACHE_ENABLED >= 2
SCB_CACHE_DCCIMVAC = (uint32_t) dest - 32;
#endif

} while (dest < end); } while (dest < end);

if (block_left != &block_silent) { if (block_left != &block_silent) {
release(block_left);
release(block_left);
block_left_1st = block_left_2nd; block_left_1st = block_left_2nd;
block_left_2nd = nullptr; block_left_2nd = nullptr;
} }
if (block_right != &block_silent) { if (block_right != &block_silent) {
release(block_right);
release(block_right);
block_right_1st = block_right_2nd; block_right_1st = block_right_2nd;
block_right_2nd = nullptr; block_right_2nd = nullptr;
} }


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


void AudioOutputSPDIF3::update(void) void AudioOutputSPDIF3::update(void)
} }
__enable_irq(); __enable_irq();
if (block_left) { if (block_left) {
release(block_left);
release(block_left);
} }
if (block_right) {
release(block_right);
if (block_right) {
release(block_right);
} }


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

uint32_t AudioOutputSPDIF3::dpll_Gain(void)
{
return spdif_gain[SPDIF_DPLL_GAIN];
}

PROGMEM
void AudioOutputSPDIF3::config_spdif3(void)
{
delay(1); //WHY IS THIS NEEDED?

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);

//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

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 & (SPDIF_SCR_DMA_RX_EN | SPDIF_SCR_DMA_TX_EN))) {
//Serial.print("Reset SPDIF3");
SPDIF_SCR = SPDIF_SCR_SOFT_RESET; //Reset SPDIF
while (SPDIF_SCR & SPDIF_SCR_SOFT_RESET) {;} //Wait for Reset (takes 8 cycles)
} else return;

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(2) | // Empty interrupt if at most 8 samples in Tx left and right FIFOs
SPDIF_SCR_TXFIFO_CTRL(1) | // 0:Send zeros 1: normal operation
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);

SPDIF_SRPC =
SPDIF_SRPC_CLKSRC_SEL(1) | //if (DPLL Locked) SPDIF_RxClk else tx_clk (SPDIF0_CLK_ROOT)
SPDIF_SRPC_GAINSEL(SPDIF_DPLL_GAIN);

uint32_t pllclock = (c0 + (float)c1 / c2) * 24000000ULL; //677376000 Hz
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

#if 0
Serial.printf("PLL: %d\n", pllclock);
Serial.printf("clock: %d\n", clock);
Serial.printf("clkdiv: %d\n", clkdiv);
#endif

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

#endif #endif

+ 5
- 2
output_spdif3.h Wyświetl plik

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

Ładowanie…
Anuluj
Zapisz