summaryrefslogtreecommitdiff
path: root/src/drivers/dac.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/dac.cpp')
-rw-r--r--src/drivers/dac.cpp123
1 files changed, 117 insertions, 6 deletions
diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp
index dc9b51c8..9fdd7b94 100644
--- a/src/drivers/dac.cpp
+++ b/src/drivers/dac.cpp
@@ -33,6 +33,12 @@ auto AudioDac::create(GpioExpander* expander)
i2s_chan_handle_t i2s_handle;
i2s_chan_config_t channel_config =
I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER);
+ // Use the maximum possible DMA buffer size, since a smaller number of large
+ // copies is faster than a large number of small copies.
+ channel_config.dma_frame_num = 1024;
+ // Triple buffering should be enough to keep samples flowing smoothly.
+ // TODO(jacqueline): verify this with 192kHz 32bps.
+ channel_config.dma_desc_num = 8;
// channel_config.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL));
@@ -47,8 +53,7 @@ auto AudioDac::create(GpioExpander* expander)
i2s_std_config_t i2s_config = {
.clk_cfg = dac->clock_config_,
.slot_cfg = dac->slot_config_,
- .gpio_cfg = {.mclk = GPIO_NUM_0,
- //.mclk = I2S_GPIO_UNUSED,
+ .gpio_cfg = {.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_26,
.ws = GPIO_NUM_27,
.dout = GPIO_NUM_5,
@@ -57,7 +62,7 @@ auto AudioDac::create(GpioExpander* expander)
{
.mclk_inv = false,
.bclk_inv = false,
- .ws_inv = false,
+ .ws_inv = true,
}},
};
@@ -115,7 +120,7 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
i2s_active_(false),
active_page_(),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)),
- slot_config_(I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
+ slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_STEREO)) {
clock_config_.clk_src = I2S_CLK_SRC_PLL_160M;
gpio_->set_pin(GpioExpander::AMP_EN, true);
@@ -192,8 +197,114 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
bps == BPS_24 ? I2S_MCLK_MULTIPLE_384 : I2S_MCLK_MULTIPLE_256;
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_));
- WriteRegister(pcm512x::I2S_1, bps_bits);
- // WriteRegister(pcm512x::I2S_2, 0);
+ // DAC reconfiguration.
+ //See here : https://e2e.ti.com/support/data_converters/audio_converters/f/64/t/428281
+ // for a config example
+
+ // Check that the bit clock (PLL input) is between 1MHz and 50MHz
+ uint32_t bckFreq = rate * bps * 2;
+ if (bckFreq < 1000000 || bckFreq > 50000000) {
+ ESP_LOGE(kTag, "bck freq out of range");
+ return;
+ }
+
+ // 24 bits is not supported for 44.1kHz and 48kHz.
+ if ((rate == SAMPLE_RATE_44_1 || rate == SAMPLE_RATE_48) && bps == BPS_24) {
+ // TODO(jacqueline): implement
+ ESP_LOGE(kTag, "sample rate and bps mismatch");
+ return;
+ }
+
+ //Initialize system clock from the I2S BCK input
+ WriteRegister(pcm512x::ERROR_DETECT, 0x1A); // Disable clock autoset and ignore SCK detection
+ WriteRegister(pcm512x::PLL_REF, 0x10); // Set PLL clock source to BCK
+ WriteRegister(pcm512x::DAC_REF, 0x10); // Set DAC clock source to PLL output
+
+ //PLL configuration
+ int p, j, d, r;
+
+ //Clock dividers
+ int nmac, ndac, ncp, dosr, idac;
+
+ if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 || rate == SAMPLE_RATE_44_1)
+ {
+ //44.1kHz and derivatives.
+ //P = 1, R = 2, D = 0 for all supported combinations.
+ //Set J to have PLL clk = 90.3168 MHz
+ p = 1;
+ r = 2;
+ j = 90316800 / bckFreq / r;
+ d = 0;
+
+ //Derive clocks from the 90.3168MHz PLL
+ nmac = 2;
+ ndac = 16;
+ ncp = 4;
+ dosr = 8;
+ idac = 1024; // DSP clock / sample rate
+ }
+ else
+ {
+ //8kHz and multiples.
+ //PLL config for a 98.304 MHz PLL clk
+ if (bps == BPS_24 && bckFreq > 1536000) {
+ p = 3;
+ } else if (bckFreq > 12288000) {
+ p = 2;
+ } else {
+ p = 1;
+ }
+
+ r = 2;
+ j = 98304000 / (bckFreq / p) / r;
+ d = 0;
+
+ //Derive clocks from the 98.304MHz PLL
+ switch (rate) {
+ case SAMPLE_RATE_16: nmac = 6; break;
+ case SAMPLE_RATE_32: nmac = 3; break;
+ default: nmac = 2; break;
+ }
+
+ ndac = 16;
+ ncp = 4;
+ dosr = 384000 / rate;
+ idac = 98304000 / nmac / rate; // DSP clock / sample rate
+ }
+
+
+ // Configure PLL
+ WriteRegister(pcm512x::PLL_COEFF_0, p - 1);
+ WriteRegister(pcm512x::PLL_COEFF_1, j);
+ WriteRegister(pcm512x::PLL_COEFF_2, (d >> 8) & 0x3F);
+ WriteRegister(pcm512x::PLL_COEFF_3, d & 0xFF);
+ WriteRegister(pcm512x::PLL_COEFF_4, r - 1);
+
+ // Clock dividers
+ WriteRegister(pcm512x::DSP_CLKDIV, nmac - 1);
+ WriteRegister(pcm512x::DAC_CLKDIV, ndac - 1);
+ WriteRegister(pcm512x::NCP_CLKDIV, ncp - 1);
+ WriteRegister(pcm512x::OSR_CLKDIV, dosr - 1);
+
+ // IDAC (nb of DSP clock cycles per sample)
+ WriteRegister(pcm512x::IDAC_1, (idac >> 8) & 0xFF);
+ WriteRegister(pcm512x::IDAC_2, idac & 0xFF);
+
+ // FS speed mode
+ int speedMode;
+ if (rate <= SAMPLE_RATE_48) {
+ speedMode = 0;
+ } else if (rate <= SAMPLE_RATE_96) {
+ speedMode = 1;
+ } else if (rate <= SAMPLE_RATE_192) {
+ speedMode = 2;
+ } else {
+ speedMode = 3;
+ }
+ WriteRegister(pcm512x::FS_SPEED_MODE, speedMode);
+
+ WriteRegister(pcm512x::I2S_1, (0b11 << 4) | bps_bits);
+ WriteRegister(pcm512x::I2S_2, 0);
// Configuration is all done, so we can now bring the DAC and I2S stream back
// up. I2S first, since otherwise the DAC will see that there's no clocks and