summaryrefslogtreecommitdiff
path: root/src/drivers/dac.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2022-11-07 12:01:29 +1100
committerjacqueline <me@jacqueline.id.au>2022-11-07 12:01:29 +1100
commit28d73ad8660e27f9c7b20b6e978d3d0c412dec00 (patch)
treec50b739ae4712f5ddb9fb6e44e39e01e4c20356d /src/drivers/dac.cpp
parentb13a9793e17e7e0e52ea08fa5fb69ca9b90ad56d (diff)
downloadtangara-fw-28d73ad8660e27f9c7b20b6e978d3d0c412dec00.tar.gz
Split driver-y things into a separate component
Diffstat (limited to 'src/drivers/dac.cpp')
-rw-r--r--src/drivers/dac.cpp106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp
new file mode 100644
index 00000000..d74e9523
--- /dev/null
+++ b/src/drivers/dac.cpp
@@ -0,0 +1,106 @@
+#include "dac.hpp"
+
+#include "gpio-expander.hpp"
+#include "i2c.hpp"
+
+#include <cstdint>
+
+#include "assert.h"
+#include "driver/i2c.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "hal/i2c_types.h"
+
+namespace gay_ipod {
+
+static const char* kTag = "AUDIODAC";
+static const uint8_t kPcm5122Address = 0x4C;
+static const uint8_t kPcm5122Timeout = 100 / portTICK_RATE_MS;
+
+auto AudioDac::create(GpioExpander* expander)
+ -> cpp::result<std::unique_ptr<AudioDac>, Error> {
+ std::unique_ptr<AudioDac> dac = std::make_unique<AudioDac>(expander);
+
+ bool is_booted = dac->WaitForPowerState(
+ [](bool booted, PowerState state) { return booted; });
+ if (!is_booted) {
+ ESP_LOGE(kTag, "Timed out waiting for boot");
+ return cpp::fail(Error::FAILED_TO_BOOT);
+ }
+
+ dac->WriteRegister(Register::DE_EMPHASIS, 1 << 4);
+ dac->WriteVolume(100);
+
+ bool is_configured =
+ dac->WaitForPowerState([](bool booted, PowerState state) {
+ return state == WAIT_FOR_CP || state == RAMP_UP || state == RUN ||
+ state == STANDBY;
+ });
+ if (!is_configured) {
+ return cpp::fail(Error::FAILED_TO_CONFIGURE);
+ }
+
+ return dac;
+}
+
+AudioDac::AudioDac(GpioExpander* gpio) {
+ this->gpio_ = gpio;
+};
+
+AudioDac::~AudioDac(){
+ // TODO: reset stuff like de-emphasis? Reboot the whole dac? Need to think
+ // about this.
+};
+
+void AudioDac::WriteVolume(uint8_t volume) {
+ WriteRegister(Register::DIGITAL_VOLUME_L, volume);
+ WriteRegister(Register::DIGITAL_VOLUME_R, 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_ERROR_CHECK(transaction.Execute());
+
+ bool is_booted = result >> 7;
+ PowerState detail = (PowerState)(result & 0b1111);
+ return std::pair(is_booted, detail);
+}
+
+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(kTag, "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