summaryrefslogtreecommitdiff
path: root/main/dac.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'main/dac.cpp')
-rw-r--r--main/dac.cpp223
1 files changed, 41 insertions, 182 deletions
diff --git a/main/dac.cpp b/main/dac.cpp
index cf1ad4b9..513793db 100644
--- a/main/dac.cpp
+++ b/main/dac.cpp
@@ -1,5 +1,6 @@
#include "dac.h"
+#include "esp_err.h"
#include "i2c.h"
#include "esp_log.h"
#include "assert.h"
@@ -18,175 +19,29 @@ AudioDac::AudioDac(GpioExpander *gpio) {
AudioDac::~AudioDac() {};
-void AudioDac::Start(SampleRate sample_rate, BitDepth bit_depth) {
- // First check that the arguments look okay.
-
- // Check that the bit clock (PLL input) is between 1MHz and 50MHz
- uint32_t bckFreq = sample_rate * bit_depth * 2;
- if (bckFreq < 1000000 || bckFreq > 50000000) {
- return;
- }
-
- // 24 bits is not supported for 44.1kHz and 48kHz.
- if ((sample_rate == SAMPLE_RATE_44_1K || sample_rate == SAMPLE_RATE_48K)
- && bit_depth == BIT_DEPTH_24) {
- return;
- }
-
- // Next, wait for the IC to boot up before we try configuring it.
- bool is_booted = false;
- for (int i=0; i<10; i++) {
- uint8_t result = ReadPowerState();
- is_booted = result >> 7;
- if (is_booted) {
- break;
- } else {
- ESP_LOGI(TAG, "Waiting for boot...");
- vTaskDelay(pdMS_TO_TICKS(1));
- }
- }
-
+esp_err_t AudioDac::Start() {
+ bool is_booted = WaitForPowerState([](bool booted, PowerState state){ return booted; });
if (!is_booted) {
- // TODO: properly handle
- ESP_LOGE(TAG, "Timed out waiting for boot!");
- return;
- }
-
- // Now do all the math required to correctly set up the internal clocks.
- int p, j, d, r;
- int nmac, ndac, ncp, dosr, idac;
- if (sample_rate == SAMPLE_RATE_11_025K ||
- sample_rate == SAMPLE_RATE_22_05K ||
- sample_rate == SAMPLE_RATE_44_1K) {
- //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 (bit_depth == BIT_DEPTH_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 (sample_rate) {
- case SAMPLE_RATE_16K: nmac = 6; break;
- case SAMPLE_RATE_32K: nmac = 3; break;
- default: nmac = 2; break;
- };
-
- ndac = 16;
- ncp = 4;
- dosr = 384000 / sample_rate;
- idac = 98304000 / nmac / sample_rate; // DSP clock / sample rate
- }
-
- // FS speed mode
- int speedMode;
- if (sample_rate <= SAMPLE_RATE_48K) {
- speedMode = 0;
- } else if (sample_rate <= SAMPLE_RATE_96K) {
- speedMode = 1;
- } else if (sample_rate <= SAMPLE_RATE_192K) {
- speedMode = 2;
- } else {
- speedMode = 3;
+ ESP_LOGE(TAG, "Timed out waiting for boot");
+ return ESP_ERR_TIMEOUT;
}
- // Set correct I2S config
- uint8_t i2s_format = 0;
- switch (bit_depth) {
- case BIT_DEPTH_16: i2s_format = 0x00; break;
- case BIT_DEPTH_24: i2s_format = 0x02; break;
- case BIT_DEPTH_32: i2s_format = 0x03; break;
- };
+ WriteRegister(Register::DE_EMPHASIS, 1 << 4);
+ WriteVolume(100);
- // We've calculated all of our data. Now assemble the big list of registers
- // that we need to configure.
- I2CTransaction transaction;
- transaction
- .start()
- .write_addr(kPCM5122Address, I2C_MASTER_WRITE)
- // All our registers are on the first page.
- .write_ack(Register::PAGE_SELECT, 0)
- // Disable clock autoset and ignore SCK detection
- .write_ack(Register::IGNORE_ERRORS, 0x1A)
- // Set PLL clock source to BCK
- .write_ack(Register::PLL_CLOCK_SOURCE, 0x10)
- // Set DAC clock source to PLL output
- .write_ack(Register::DAC_CLOCK_SOURCE, 0x10)
-
- // Configure PLL
- .write_ack(Register::PLL_P, p - 1)
- .write_ack(Register::PLL_J, j)
- .write_ack(Register::PLL_D_MSB, (d >> 8) & 0x3F)
- .write_ack(Register::PLL_D_LSB, d & 0xFF)
- .write_ack(Register::PLL_R, r - 1)
-
- // Clock dividers
- .write_ack(Register::DSP_CLOCK_DIV, nmac - 1)
- .write_ack(Register::DAC_CLOCK_DIV, ndac - 1)
- .write_ack(Register::NCP_CLOCK_DIV, ncp - 1)
- .write_ack(Register::OSR_CLOCK_DIV, dosr - 1)
-
- // IDAC (nb of DSP clock cycles per sample)
- .write_ack(Register::IDAC_MSB, (idac >> 8) & 0xFF)
- .write_ack(Register::IDAC_LSB, idac & 0xFF)
-
- .write_ack(Register::FS_SPEED_MODE, speedMode)
- .write_ack(Register::I2S_FORMAT, i2s_format)
+ WaitForPowerState([](bool booted, PowerState state){
+ return state == WAIT_FOR_CP || state == RAMP_UP || state == RUN || state == STANDBY;
+ });
- .stop();
-
- ESP_LOGI(TAG, "Configuring DAC");
- // TODO: Handle this gracefully.
- ESP_ERROR_CHECK(transaction.Execute());
-
- // The DAC takes a moment to reconfigure itself. Give it some time before we
- // start asking for its state.
- vTaskDelay(pdMS_TO_TICKS(5));
-
- // TODO: investigate why it's stuck waiting for CP voltage.
- /*
- bool is_configured = false;
- for (int i=0; i<10; i++) {
- uint8_t result = ReadPowerState();
- is_configured = (result & 0b1111) == 0b1001;
- if (is_configured) {
- break;
- } else {
- ESP_LOGI(TAG, "Waiting for configure...");
- vTaskDelay(pdMS_TO_TICKS(1));
- }
- }
+ return ESP_OK;
+}
- if (!is_configured) {
- // TODO: properly handle
- ESP_LOGE(TAG, "Timed out waiting for configure!");
- return;
- }
- */
+void AudioDac::WriteVolume(uint8_t volume) {
+ WriteRegister(Register::DIGITAL_VOLUME_L, volume);
+ WriteRegister(Register::DIGITAL_VOLUME_R, volume);
}
-uint8_t AudioDac::ReadPowerState() {
+std::pair<bool, AudioDac::PowerState> AudioDac::ReadPowerState() {
uint8_t result = 0;
I2CTransaction transaction;
@@ -201,31 +56,35 @@ uint8_t AudioDac::ReadPowerState() {
ESP_ERROR_CHECK(transaction.Execute());
- return result;
+ bool is_booted = result >> 7;
+ PowerState detail = (PowerState) (result & 0b1111);
+ return std::pair(is_booted, detail);
}
-void AudioDac::WriteVolume(uint8_t volume) {
- I2CTransaction transaction;
- transaction
- .start()
- .write_addr(kPCM5122Address, I2C_MASTER_WRITE)
- .write_ack(Register::DIGITAL_VOLUME_L, volume)
- .write_ack(Register::DIGITAL_VOLUME_R, volume)
- .stop();
-
- ESP_ERROR_CHECK(transaction.Execute());
-}
-
-void AudioDac::WritePowerMode(PowerMode mode) {
- switch (mode) {
- case ON:
- case STANDBY:
- // TODO: enable power switch.
- break;
- case OFF:
- // TODO: disable power switch.
+bool AudioDac::WaitForPowerState(std::function<bool(bool,AudioDac::PowerState)> predicate) {
+ bool has_matched = false;
+ for (int i=0; i<10; i++) {
+ std::pair<bool, PowerState> result = ReadPowerState();
+ has_matched = predicate(result.first, result.second);
+ if (has_matched) {
break;
+ } else {
+ ESP_LOGI(TAG, "Waiting for power state (was %d %x)", result.first, (uint8_t) result.second);
+ vTaskDelay(pdMS_TO_TICKS(1));
+ }
}
+ return has_matched;
+}
+
+void AudioDac::WriteRegister(Register reg, uint8_t val) {
+ I2CTransaction transaction;
+ transaction
+ .start()
+ .write_addr(kPCM5122Address, I2C_MASTER_WRITE)
+ .write_ack(reg, val)
+ .stop();
+ // TODO: Retry once?
+ ESP_ERROR_CHECK(transaction.Execute());
}
} // namespace gay_ipod