summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/dac.cpp117
-rw-r--r--src/drivers/include/dac.hpp117
2 files changed, 149 insertions, 85 deletions
diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp
index 60679678..40663219 100644
--- a/src/drivers/dac.cpp
+++ b/src/drivers/dac.cpp
@@ -5,9 +5,7 @@
#include "assert.h"
#include "driver/i2c.h"
-#include "driver/i2s_common.h"
-#include "driver/i2s_std.h"
-#include "driver/i2s_types.h"
+#include "driver/i2s_types_legacy.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
@@ -19,6 +17,7 @@
#include "gpio_expander.hpp"
#include "hal/i2s_types.h"
#include "i2c.hpp"
+#include "soc/clk_tree_defs.h"
#include "sys/_stdint.h"
namespace drivers {
@@ -38,6 +37,7 @@ 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);
+ channel_config.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL));
//
@@ -84,20 +84,22 @@ auto AudioDac::create(GpioExpander* expander)
// The DAC should be booted but in power down mode, but it might not be if we
// didn't shut down cleanly. Reset it to ensure it is in a consistent state.
- dac->WriteRegister(Register::POWER_MODE, 1 << 4);
- dac->WriteRegister(Register::RESET, 0b10001);
+ dac->WriteRegister(pcm512x::POWER, 1 << 4);
+ dac->WriteRegister(pcm512x::RESET, 0b10001);
// Use BCK for the internal PLL.
// dac->WriteRegister(Register::PLL_CLOCK_SOURCE, 1 << 4);
+ // dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
- // dac->WriteRegister(Register::PLL_ENABLE, 0);
- dac->WriteRegister(Register::INTERPOLATION, 1 << 4);
+ //dac->WriteRegister(Register::PLL_ENABLE, 0);
+ //dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b0110000);
+ //dac->WriteRegister(Register::CLOCK_ERRORS, 0b01000001);
+ //dac->WriteRegister(Register::I2S_FORMAT, 0b110000);
+ // dac->WriteRegister(Register::INTERPOLATION, 1 << 4);
dac->Reconfigure(BPS_16, SAMPLE_RATE_44_1);
- dac->WriteRegister(Register::POWER_MODE, 0);
// Now configure the DAC for standard auto-clock SCK mode.
- // dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
// Enable auto clocking, and do your best to carry on despite errors.
// dac->WriteRegister(Register::CLOCK_ERRORS, 0b1111101);
@@ -115,9 +117,11 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
: gpio_(gpio),
i2s_handle_(i2s_handle),
i2s_active_(false),
+ active_page_(),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)),
- slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
- I2S_SLOT_MODE_STEREO)) {
+ slot_config_(I2S_STD_PHILIPS_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);
gpio_->Write();
}
@@ -130,29 +134,14 @@ AudioDac::~AudioDac() {
}
void AudioDac::WriteVolume(uint8_t volume) {
- WriteRegister(Register::DIGITAL_VOLUME_L, volume);
- WriteRegister(Register::DIGITAL_VOLUME_R, volume);
+ // Left channel.
+ WriteRegister(pcm512x::DIGITAL_VOLUME_2, volume);
+ // Right channel.
+ WriteRegister(pcm512x::DIGITAL_VOLUME_3, volume);
}
std::pair<bool, AudioDac::PowerState> AudioDac::ReadPowerState() {
- uint8_t result = 0;
-
- I2CTransaction transaction;
- transaction.start()
- .write_addr(kPcm5122Address, I2C_MASTER_WRITE)
- .write_ack(DSP_BOOT_POWER_STATE)
- .start()
- .write_addr(kPcm5122Address, I2C_MASTER_READ)
- .read(&result, I2C_MASTER_NACK)
- .stop();
-
- esp_err_t err = transaction.Execute();
- if (err == ESP_ERR_TIMEOUT) {
- return std::pair(false, POWERDOWN);
- } else {
- }
- ESP_ERROR_CHECK(err);
-
+ uint8_t result = ReadRegister(pcm512x::POWER_STATE);
bool is_booted = result >> 7;
PowerState detail = (PowerState)(result & 0b1111);
return std::pair(is_booted, detail);
@@ -176,17 +165,17 @@ bool AudioDac::WaitForPowerState(
}
auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
- WriteRegister(Register::RESYNC_REQUEST, 1);
if (i2s_active_) {
+ WriteRegister(pcm512x::POWER, 1 << 4);
i2s_channel_disable(i2s_handle_);
}
// I2S reconfiguration.
- slot_config_.slot_bit_width = (i2s_slot_bit_width_t)bps;
+ slot_config_.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT;
ESP_ERROR_CHECK(i2s_channel_reconfig_std_slot(i2s_handle_, &slot_config_));
- clock_config_.sample_rate_hz = rate;
+ clock_config_.sample_rate_hz = 44100;
// If we have an MCLK/SCK, then it must be a multiple of both the sample rate
// and the bit clock. At 24 BPS, we therefore have to change the MCLK multiple
// to avoid issues at some sample rates. (e.g. 48KHz)
@@ -194,21 +183,19 @@ 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_));
- // DAC reconfiguration.
- if (rate == SAMPLE_RATE_44_1) {
- WriteRegister(Register::DE_EMPHASIS, 1 << 4);
- } else {
- WriteRegister(Register::DE_EMPHASIS, 0);
- }
-
// TODO: base on BPS
- WriteRegister(Register::I2S_FORMAT, 0b00);
+ // WriteRegister(Register::I2S_FORMAT, 0b110000);
// 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
// shut itself down.
- WriteRegister(Register::RESYNC_REQUEST, 0);
ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_));
+ WriteRegister(pcm512x::POWER, 0);
+ WriteRegister(pcm512x::SYNCHRONIZE, 1);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ WriteRegister(pcm512x::SYNCHRONIZE, 0);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ LogStatus();
i2s_active_ = true;
}
@@ -223,7 +210,7 @@ auto AudioDac::WriteData(const cpp::span<const std::byte>& data) -> void {
auto AudioDac::Stop() -> void {
LogStatus();
- WriteRegister(Register::POWER_MODE, 1 << 4);
+ WriteRegister(pcm512x::POWER, 1 << 4);
i2s_channel_disable(i2s_handle_);
}
@@ -237,35 +224,47 @@ auto AudioDac::Stop() -> void {
auto AudioDac::LogStatus() -> void {
uint8_t res;
- res = ReadRegister(Register::SAMPLE_RATE_DETECTION);
- ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res >> 4) && 0b111);
+ res = ReadRegister(pcm512x::RATE_DET_1);
+ ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res & 0b01110000) >> 4);
ESP_LOGI(kTag, "detected SCK ratio (want 6): %u", res && 0b1111);
- res = ReadRegister(Register::BCK_DETECTION);
+ res = ReadRegister(pcm512x::RATE_DET_3);
ESP_LOGI(kTag, "detected BCK (want... 16? 32?): %u", res);
- res = ReadRegister(Register::CLOCK_ERROR_STATE);
+ res = ReadRegister(pcm512x::RATE_DET_4);
ESP_LOGI(kTag, "clock errors (want zeroes): ");
ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b1111111));
- res = ReadRegister(Register::CLOCK_STATUS);
+ res = ReadRegister(pcm512x::CLOCK_STATUS);
ESP_LOGI(kTag, "clock status (want zeroes): ");
ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b10111));
- res = ReadRegister(Register::AUTO_MUTE_STATE);
- ESP_LOGI(kTag, "automute status (want 3): %u", res & 0b11);
-
- res = ReadRegister(Register::SOFT_MUTE_STATE);
- ESP_LOGI(kTag, "soft mute pin status (want 3): %u", res & 0b11);
-
- res = ReadRegister(Register::SAMPLE_RATE_STATE);
- ESP_LOGI(kTag, "detected sample speed mode (want 0): %u", res & 0b11);
+ res = ReadRegister(pcm512x::DIGITAL_MUTE_DET);
+ ESP_LOGI(kTag, "automute status (want 0): %u", res & 0b10001);
auto power = ReadPowerState();
ESP_LOGI(kTag, "current power state (want 5): %u", power.second);
}
-void AudioDac::WriteRegister(Register reg, uint8_t val) {
+void AudioDac::WriteRegister(pcm512x::Register r, uint8_t val) {
+ SelectPage(r.page);
+ WriteRegisterRaw(r.reg, val);
+}
+
+uint8_t AudioDac::ReadRegister(pcm512x::Register r) {
+ SelectPage(r.page);
+ return ReadRegisterRaw(r.reg);
+}
+
+void AudioDac::SelectPage(uint8_t page) {
+ if (active_page_ && active_page_ == page) {
+ return;
+ }
+ WriteRegisterRaw(0, page);
+ active_page_ = page;
+}
+
+void AudioDac::WriteRegisterRaw(uint8_t reg, uint8_t val) {
I2CTransaction transaction;
transaction.start()
.write_addr(kPcm5122Address, I2C_MASTER_WRITE)
@@ -275,7 +274,7 @@ void AudioDac::WriteRegister(Register reg, uint8_t val) {
transaction.Execute();
}
-uint8_t AudioDac::ReadRegister(Register reg) {
+uint8_t AudioDac::ReadRegisterRaw(uint8_t reg) {
uint8_t result = 0;
I2CTransaction transaction;
transaction.start()
diff --git a/src/drivers/include/dac.hpp b/src/drivers/include/dac.hpp
index 6849d92c..6836cf59 100644
--- a/src/drivers/include/dac.hpp
+++ b/src/drivers/include/dac.hpp
@@ -20,6 +20,91 @@
namespace drivers {
+namespace pcm512x {
+class Register {
+ public:
+ uint8_t page;
+ uint8_t reg;
+
+ constexpr Register(uint8_t page, uint8_t reg) : page(page), reg(reg) {}
+};
+
+constexpr Register RESET(0, 1);
+constexpr Register POWER(0, 2);
+constexpr Register MUTE(0, 3);
+constexpr Register PLL_EN(0, 4);
+constexpr Register SPI_MISO_FUNCTION(0, 6);
+constexpr Register DSP(0, 7);
+constexpr Register GPIO_EN(0, 8);
+constexpr Register BCLK_LRCLK_CFG(0, 9);
+constexpr Register DSP_GPIO_INPUT(0, 10);
+constexpr Register MASTER_MODE(0, 12);
+constexpr Register PLL_REF(0, 13);
+constexpr Register DAC_REF(0, 14);
+constexpr Register GPIO_DACIN(0, 16);
+constexpr Register GPIO_PLLIN(0, 18);
+constexpr Register SYNCHRONIZE(0, 19);
+constexpr Register PLL_COEFF_0(0, 20);
+constexpr Register PLL_COEFF_1(0, 21);
+constexpr Register PLL_COEFF_2(0, 22);
+constexpr Register PLL_COEFF_3(0, 23);
+constexpr Register PLL_COEFF_4(0, 24);
+constexpr Register DSP_CLKDIV(0, 27);
+constexpr Register DAC_CLKDIV(0, 28);
+constexpr Register NCP_CLKDIV(0, 29);
+constexpr Register OSR_CLKDIV(0, 30);
+constexpr Register MASTER_CLKDIV_1(0, 32);
+constexpr Register MASTER_CLKDIV_2(0, 33);
+constexpr Register FS_SPEED_MODE(0, 34);
+constexpr Register IDAC_1(0, 35);
+constexpr Register IDAC_2(0, 36);
+constexpr Register ERROR_DETECT(0, 37);
+constexpr Register I2S_1(0, 40);
+constexpr Register I2S_2(0, 41);
+constexpr Register DAC_ROUTING(0, 42);
+constexpr Register DSP_PROGRAM(0, 43);
+constexpr Register CLKDET(0, 44);
+constexpr Register AUTO_MUTE(0, 59);
+constexpr Register DIGITAL_VOLUME_1(0, 60);
+constexpr Register DIGITAL_VOLUME_2(0, 61);
+constexpr Register DIGITAL_VOLUME_3(0, 62);
+constexpr Register DIGITAL_MUTE_1(0, 63);
+constexpr Register DIGITAL_MUTE_2(0, 64);
+constexpr Register DIGITAL_MUTE_3(0, 65);
+constexpr Register GPIO_OUTPUT_1(0, 80);
+constexpr Register GPIO_OUTPUT_2(0, 81);
+constexpr Register GPIO_OUTPUT_3(0, 82);
+constexpr Register GPIO_OUTPUT_4(0, 83);
+constexpr Register GPIO_OUTPUT_5(0, 84);
+constexpr Register GPIO_OUTPUT_6(0, 85);
+constexpr Register GPIO_CONTROL_1(0, 86);
+constexpr Register GPIO_CONTROL_2(0, 87);
+constexpr Register OVERFLOW(0, 90);
+constexpr Register RATE_DET_1(0, 91);
+constexpr Register RATE_DET_2(0, 92);
+constexpr Register RATE_DET_3(0, 93);
+constexpr Register RATE_DET_4(0, 94);
+constexpr Register CLOCK_STATUS(0, 95);
+constexpr Register ANALOG_MUTE_DET(0, 108);
+constexpr Register POWER_STATE(0, 118);
+constexpr Register GPIN(0, 119);
+constexpr Register DIGITAL_MUTE_DET(0, 120);
+
+constexpr Register OUTPUT_AMPLITUDE(1, 1);
+constexpr Register ANALOG_GAIN_CTRL(1, 2);
+constexpr Register UNDERVOLTAGE_PROT(1, 5);
+constexpr Register ANALOG_MUTE_CTRL(1, 6);
+constexpr Register ANALOG_GAIN_BOOST(1, 7);
+constexpr Register VCOM_CTRL_1(1, 8);
+constexpr Register VCOM_CTRL_2(1, 9);
+
+constexpr Register CRAM_CTRL(44, 1);
+
+constexpr Register FLEX_A(253, 63);
+constexpr Register FLEX_B(253, 64);
+
+} // namespace pcm512x
+
/**
* Interface for a PCM5122PWR DAC, configured over I2C.
*/
@@ -84,6 +169,7 @@ class AudioDac {
GpioExpander* gpio_;
i2s_chan_handle_t i2s_handle_;
bool i2s_active_;
+ std::optional<uint8_t> active_page_;
i2s_std_clk_config_t clock_config_;
i2s_std_slot_config_t slot_config_;
@@ -94,33 +180,12 @@ class AudioDac {
*/
bool WaitForPowerState(std::function<bool(bool, PowerState)> predicate);
- enum Register {
- PAGE_SELECT = 0,
- RESET = 1,
- POWER_MODE = 2,
- PLL_ENABLE = 4,
- DE_EMPHASIS = 7,
- PLL_CLOCK_SOURCE = 13,
- DAC_CLOCK_SOURCE = 14,
- RESYNC_REQUEST = 19,
- CLOCK_ERRORS = 37,
- INTERPOLATION = 34,
- I2S_FORMAT = 40,
- DIGITAL_VOLUME_L = 61,
- DIGITAL_VOLUME_R = 62,
-
- SAMPLE_RATE_DETECTION = 91,
- BCK_DETECTION = 93,
- CLOCK_ERROR_STATE = 94,
- CLOCK_STATUS = 95,
- AUTO_MUTE_STATE = 108,
- SOFT_MUTE_STATE = 114,
- SAMPLE_RATE_STATE = 115,
- DSP_BOOT_POWER_STATE = 118,
- };
+ void WriteRegister(pcm512x::Register r, uint8_t val);
+ uint8_t ReadRegister(pcm512x::Register r);
- void WriteRegister(Register reg, uint8_t val);
- uint8_t ReadRegister(Register reg);
+ void SelectPage(uint8_t page);
+ void WriteRegisterRaw(uint8_t reg, uint8_t val);
+ uint8_t ReadRegisterRaw(uint8_t reg);
};
} // namespace drivers