Improve DAC power+mute management to reduce clicks and pops

custom
jacqueline 10 months ago
parent bcaa133a4c
commit 00b1ba58f0
  1. 2
      src/drivers/gpios.cpp
  2. 45
      src/drivers/i2s_dac.cpp
  3. 2
      src/drivers/include/drivers/i2s_dac.hpp
  4. 8
      src/tangara/audio/i2s_audio_output.cpp
  5. 1
      src/tangara/audio/i2s_audio_output.hpp

@ -43,7 +43,7 @@ static const uint8_t kPortADefault = 0b00111110;
// 6 - NC // 6 - NC
// 7 - NC // 7 - NC
// Default inputs high, amp off. // Default inputs high, amp off.
static const uint8_t kPortBDefault = 0b00001101; static const uint8_t kPortBDefault = 0b00011111;
/* /*
* Convenience mehod for packing the port a and b bytes into a single 16 bit * Convenience mehod for packing the port a and b bytes into a single 16 bit

@ -137,54 +137,43 @@ I2SDac::I2SDac(IGpios& gpio, PcmBuffer& buf, i2s_chan_handle_t i2s_handle)
I2S_SLOT_MODE_STEREO)) { I2S_SLOT_MODE_STEREO)) {
clock_config_.clk_src = I2S_CLK_SRC_APLL; clock_config_.clk_src = I2S_CLK_SRC_APLL;
// The amplifier's power rails ramp unevenly, with the negative rail coming // Power up the DAC.
// up ~5ms after the positive rail. Ensure that headphone output is muted wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b01);
// during this to avoid a loud pop during power up.
gpio_.WriteSync(IGpios::Pin::kAmplifierMute, true);
vTaskDelay(pdMS_TO_TICKS(1));
gpio_.WriteSync(IGpios::Pin::kAmplifierEnable, true);
// Reset all registers back to their default values. // Reset all registers back to their default values.
wm8523::WriteRegister(wm8523::Register::kReset, 1); wm8523::WriteRegister(wm8523::Register::kReset, 1);
// Wait for DAC reset + analog rails ramp. // Wait for DAC to reset.
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b0); // Enable zero-cross detection and ramping for volume changes.
// Use zero-cross detection for volume changes. wm8523::WriteRegister(wm8523::Register::kDacCtrl, 0b10011);
wm8523::WriteRegister(wm8523::Register::kDacCtrl, 0b10000);
// Ready to play!
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10);
} }
I2SDac::~I2SDac() { I2SDac::~I2SDac() {
Stop(); if (i2s_active_) {
i2s_del_channel(i2s_handle_); SetPaused(true);
gpio_.WriteSync(IGpios::Pin::kAmplifierMute, true);
vTaskDelay(pdMS_TO_TICKS(1));
gpio_.WriteSync(IGpios::Pin::kAmplifierEnable, false);
} }
auto I2SDac::Start() -> void { // Power down the DAC.
std::lock_guard<std::mutex> lock(configure_mutex_); wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b01);
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11); wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b00);
}
auto I2SDac::Stop() -> void { i2s_del_channel(i2s_handle_);
std::lock_guard<std::mutex> lock(configure_mutex_);
set_channel(false);
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b0);
} }
auto I2SDac::SetPaused(bool paused) -> void { auto I2SDac::SetPaused(bool paused) -> void {
if (paused) { if (paused) {
gpio_.WriteSync(IGpios::Pin::kAmplifierUnmuteLegacy, false); wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10);
gpio_.WriteSync(IGpios::Pin::kAmplifierMute, true); gpio_.WriteSync(IGpios::Pin::kAmplifierMute, true);
set_channel(false); set_channel(false);
} else { } else {
set_channel(true); set_channel(true);
gpio_.WriteSync(IGpios::Pin::kAmplifierUnmuteLegacy, true);
gpio_.WriteSync(IGpios::Pin::kAmplifierMute, false); gpio_.WriteSync(IGpios::Pin::kAmplifierMute, false);
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11);
} }
} }
@ -250,6 +239,8 @@ auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
if (i2s_active_) { if (i2s_active_) {
i2s_channel_enable(i2s_handle_); i2s_channel_enable(i2s_handle_);
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11); wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11);
} else {
wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10);
} }
} }

@ -45,8 +45,6 @@ class I2SDac {
I2SDac(IGpios& gpio, PcmBuffer&, i2s_chan_handle_t i2s_handle); I2SDac(IGpios& gpio, PcmBuffer&, i2s_chan_handle_t i2s_handle);
~I2SDac(); ~I2SDac();
auto Start() -> void;
auto Stop() -> void;
auto SetPaused(bool) -> void; auto SetPaused(bool) -> void;
enum Channels { enum Channels {

@ -41,8 +41,6 @@ static constexpr uint16_t kMaxVolumeBeforeClipping = 0x185;
static constexpr uint16_t kLineLevelVolume = 0x13d; static constexpr uint16_t kLineLevelVolume = 0x13d;
static constexpr uint16_t kDefaultVolume = 0x100; static constexpr uint16_t kDefaultVolume = 0x100;
static constexpr size_t kDrainBufferSize = 8 * 1024;
I2SAudioOutput::I2SAudioOutput(drivers::IGpios& expander, I2SAudioOutput::I2SAudioOutput(drivers::IGpios& expander,
drivers::PcmBuffer& buffer) drivers::PcmBuffer& buffer)
: IAudioOutput(), : IAudioOutput(),
@ -55,10 +53,6 @@ I2SAudioOutput::I2SAudioOutput(drivers::IGpios& expander,
current_volume_(kDefaultVolume), current_volume_(kDefaultVolume),
max_volume_(0) {} max_volume_(0) {}
I2SAudioOutput::~I2SAudioOutput() {
dac_->Stop();
}
auto I2SAudioOutput::changeMode(Modes mode) -> void { auto I2SAudioOutput::changeMode(Modes mode) -> void {
if (mode == current_mode_) { if (mode == current_mode_) {
return; return;
@ -70,7 +64,6 @@ auto I2SAudioOutput::changeMode(Modes mode) -> void {
// Turning off this output. Ensure we clean up the I2SDac instance to // Turning off this output. Ensure we clean up the I2SDac instance to
// reclaim its valuable DMA buffers. // reclaim its valuable DMA buffers.
if (dac_) { if (dac_) {
dac_->Stop();
dac_.reset(); dac_.reset();
} }
return; return;
@ -87,7 +80,6 @@ auto I2SAudioOutput::changeMode(Modes mode) -> void {
} }
// Set up the new instance properly. // Set up the new instance properly.
SetVolume(GetVolume()); SetVolume(GetVolume());
dac_->Start();
} }
current_mode_ = mode; current_mode_ = mode;

@ -22,7 +22,6 @@ namespace audio {
class I2SAudioOutput : public IAudioOutput { class I2SAudioOutput : public IAudioOutput {
public: public:
I2SAudioOutput(drivers::IGpios&, drivers::PcmBuffer&); I2SAudioOutput(drivers::IGpios&, drivers::PcmBuffer&);
~I2SAudioOutput();
auto SetMaxVolume(uint16_t) -> void; auto SetMaxVolume(uint16_t) -> void;
auto SetVolumeDb(uint16_t) -> void; auto SetVolumeDb(uint16_t) -> void;

Loading…
Cancel
Save