ESPHome  2024.12.0
aic3204.cpp
Go to the documentation of this file.
1 #include "aic3204.h"
2 
3 #include "esphome/core/defines.h"
4 #include "esphome/core/helpers.h"
5 #include "esphome/core/log.h"
6 
7 namespace esphome {
8 namespace aic3204 {
9 
10 static const char *const TAG = "aic3204";
11 
12 #define ERROR_CHECK(err, msg) \
13  if (!(err)) { \
14  ESP_LOGE(TAG, msg); \
15  this->mark_failed(); \
16  return; \
17  }
18 
20  ESP_LOGCONFIG(TAG, "Setting up AIC3204...");
21 
22  // Set register page to 0
23  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
24  // Initiate SW reset (PLL is powered off as part of reset)
25  ERROR_CHECK(this->write_byte(AIC3204_SW_RST, 0x01), "Software reset failed");
26  // *** Program clock settings ***
27  // Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
28  // MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
29  // (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
30  // We do need MDAC*DOSR/32 >= the resource compute level for the processing block
31  // So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
32  // See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
33  // for determining these settings.
34 
35  // Power up NDAC and set to 2
36  ERROR_CHECK(this->write_byte(AIC3204_NDAC, 0x82), "Set NDAC failed");
37  // Power up MDAC and set to 2
38  ERROR_CHECK(this->write_byte(AIC3204_MDAC, 0x82), "Set MDAC failed");
39  // Program DOSR = 128
40  ERROR_CHECK(this->write_byte(AIC3204_DOSR, 0x80), "Set DOSR failed");
41  // Set Audio Interface Config: I2S, 32 bits, DOUT always driving
42  ERROR_CHECK(this->write_byte(AIC3204_CODEC_IF, 0x30), "Set CODEC_IF failed");
43  // For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
44  ERROR_CHECK(this->write_byte(AIC3204_SCLK_MFP3, 0x02), "Set SCLK/MFP3 failed");
45  ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_4, 0x01), "Set AUDIO_IF_4 failed");
46  ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_5, 0x01), "Set AUDIO_IF_5 failed");
47  // Program the DAC processing block to be used - PRB_P1
48  ERROR_CHECK(this->write_byte(AIC3204_DAC_SIG_PROC, 0x01), "Set DAC_SIG_PROC failed");
49 
50  // *** Select Page 1 ***
51  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x01), "Set page 1 failed");
52  // Enable the internal AVDD_LDO:
53  ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x09), "Set LDO_CTRL failed");
54  // *** Program Analog Blocks ***
55  // Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
56  ERROR_CHECK(this->write_byte(AIC3204_PWR_CFG, 0x08), "Set PWR_CFG failed");
57  // Enable Master Analog Power Control
58  ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x01), "Set LDO_CTRL failed");
59  // Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
60  // We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
61  // Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
62  // We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
63  // (All pages refer to the TLV320AIC3204 Application Reference Guide)
64  ERROR_CHECK(this->write_byte(AIC3204_CM_CTRL, 0x40), "Set CM_CTRL failed");
65  // *** Set PowerTune Modes ***
66  // Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
67  ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG1, 0x00), "Set PLAY_CFG1 failed");
68  ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG2, 0x00), "Set PLAY_CFG2 failed");
69  // Set the REF charging time to 40ms
70  ERROR_CHECK(this->write_byte(AIC3204_REF_STARTUP, 0x01), "Set REF_STARTUP failed");
71  // HP soft stepping settings for optimal pop performance at power up
72  // Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
73  // capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
74  ERROR_CHECK(this->write_byte(AIC3204_HP_START, 0x25), "Set HP_START failed");
75  // Route Left DAC to HPL
76  ERROR_CHECK(this->write_byte(AIC3204_HPL_ROUTE, 0x08), "Set HPL_ROUTE failed");
77  // Route Right DAC to HPR
78  ERROR_CHECK(this->write_byte(AIC3204_HPR_ROUTE, 0x08), "Set HPR_ROUTE failed");
79  // Route Left DAC to LOL
80  ERROR_CHECK(this->write_byte(AIC3204_LOL_ROUTE, 0x08), "Set LOL_ROUTE failed");
81  // Route Right DAC to LOR
82  ERROR_CHECK(this->write_byte(AIC3204_LOR_ROUTE, 0x08), "Set LOR_ROUTE failed");
83 
84  // Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
85  ERROR_CHECK(this->write_byte(AIC3204_HPL_GAIN, 0x3e), "Set HPL_GAIN failed");
86  // Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
87  ERROR_CHECK(this->write_byte(AIC3204_HPR_GAIN, 0x3e), "Set HPR_GAIN failed");
88  // Unmute LOL and set gain to 0dB
89  ERROR_CHECK(this->write_byte(AIC3204_LOL_DRV_GAIN, 0x00), "Set LOL_DRV_GAIN failed");
90  // Unmute LOR and set gain to 0dB
91  ERROR_CHECK(this->write_byte(AIC3204_LOR_DRV_GAIN, 0x00), "Set LOR_DRV_GAIN failed");
92 
93  // Power up HPL and HPR, LOL and LOR drivers
94  ERROR_CHECK(this->write_byte(AIC3204_OP_PWR_CTRL, 0x3C), "Set OP_PWR_CTRL failed");
95 
96  // Wait for 2.5 sec for soft stepping to take effect before attempting power-up
97  this->set_timeout(2500, [this]() {
98  // *** Power Up DAC ***
99  // Select Page 0
100  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set PAGE_CTRL failed");
101  // Power up the Left and Right DAC Channels. Route Left data to Left DAC and Right data to Right DAC.
102  // DAC Vol control soft step 1 step per DAC word clock.
103  ERROR_CHECK(this->write_byte(AIC3204_DAC_CH_SET1, 0xd4), "Set DAC_CH_SET1 failed");
104  // Set left and right DAC digital volume control
105  ERROR_CHECK(this->write_volume_(), "Set volume failed");
106  // Unmute left and right channels
107  ERROR_CHECK(this->write_mute_(), "Set mute failed");
108  });
109 }
110 
112  ESP_LOGCONFIG(TAG, "AIC3204:");
113  LOG_I2C_DEVICE(this);
114 
115  if (this->is_failed()) {
116  ESP_LOGE(TAG, "Communication with AIC3204 failed");
117  }
118 }
119 
121  this->is_muted_ = false;
122  return this->write_mute_();
123 }
124 
126  this->is_muted_ = true;
127  return this->write_mute_();
128 }
129 
130 bool AIC3204::set_auto_mute_mode(uint8_t auto_mute_mode) {
131  this->auto_mute_mode_ = auto_mute_mode & 0x07;
132  ESP_LOGVV(TAG, "Setting auto_mute_mode to 0x%.2x", this->auto_mute_mode_);
133  return this->write_mute_();
134 }
135 
137  this->volume_ = clamp<float>(volume, 0.0, 1.0);
138  return this->write_volume_();
139 }
140 
141 bool AIC3204::is_muted() { return this->is_muted_; }
142 
143 float AIC3204::volume() { return this->volume_; }
144 
146  uint8_t mute_mode_byte = this->auto_mute_mode_ << 4; // auto-mute control is bits 4-6
147  mute_mode_byte |= this->is_muted_ ? 0x0c : 0x00; // mute bits are 2-3
148  if (!this->write_byte(AIC3204_PAGE_CTRL, 0x00) || !this->write_byte(AIC3204_DAC_CH_SET2, mute_mode_byte)) {
149  ESP_LOGE(TAG, "Writing mute modes failed");
150  return false;
151  }
152  return true;
153 }
154 
156  const int8_t dvc_min_byte = -127;
157  const int8_t dvc_max_byte = 48;
158 
159  int8_t volume_byte = dvc_min_byte + (this->volume_ * (dvc_max_byte - dvc_min_byte));
160  volume_byte = clamp<int8_t>(volume_byte, dvc_min_byte, dvc_max_byte);
161 
162  ESP_LOGVV(TAG, "Setting volume to 0x%.2x", volume_byte & 0xFF);
163 
164  if ((!this->write_byte(AIC3204_PAGE_CTRL, 0x00)) || (!this->write_byte(AIC3204_DACL_VOL_D, volume_byte)) ||
165  (!this->write_byte(AIC3204_DACR_VOL_D, volume_byte))) {
166  ESP_LOGE(TAG, "Writing volume failed");
167  return false;
168  }
169  return true;
170 }
171 
172 } // namespace aic3204
173 } // namespace esphome
float volume() override
Definition: aic3204.cpp:143
bool is_failed() const
Definition: component.cpp:143
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
void dump_config() override
Definition: aic3204.cpp:111
bool set_mute_on() override
Definition: aic3204.cpp:125
bool set_auto_mute_mode(uint8_t auto_mute_mode)
Definition: aic3204.cpp:130
bool is_muted() override
Definition: aic3204.cpp:141
bool set_volume(float volume) override
Definition: aic3204.cpp:136
bool set_mute_off() override
Definition: aic3204.cpp:120
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void setup() override
Definition: aic3204.cpp:19