13static constexpr uint8_t TAS2780_PAGE_SELECT = 0x00;
14static constexpr uint8_t TAS2780_PAGE_0 = 0x00;
15static constexpr uint8_t TAS2780_PAGE_1 = 0x01;
16static constexpr uint8_t TAS2780_PAGE_FD = 0xFD;
19static constexpr uint8_t TAS2780_SW_RESET = 0x01;
20static constexpr uint8_t TAS2780_SW_RESET_CMD = 0x01;
21static constexpr uint8_t TAS2780_MODE_CTRL = 0x02;
22static constexpr uint8_t TAS2780_MODE_CTRL_MODE_MASK = 0x07;
23static constexpr uint8_t TAS2780_MODE_CTRL_MODE_ACTIVE = 0x00;
24static constexpr uint8_t TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED = 0x01;
25static constexpr uint8_t TAS2780_MODE_CTRL_MODE_SFTW_SHTDWN = 0x02;
27static constexpr uint8_t TAS2780_CHNL_0 = 0x03;
28static constexpr uint8_t TAS2780_CHNL_0_CDS_MODE_SHIFT = 6;
29static constexpr uint8_t TAS2780_CHNL_0_CDS_MODE_MASK = (0x03 << TAS2780_CHNL_0_CDS_MODE_SHIFT);
30static constexpr uint8_t TAS2780_CHNL_0_AMP_LEVEL_SHIFT = 1;
31static constexpr uint8_t TAS2780_CHNL_0_AMP_LEVEL_MASK = (0x1F) << TAS2780_CHNL_0_AMP_LEVEL_SHIFT;
32static constexpr uint8_t TAS2780_AMP_LEVEL_MAX = 0x14;
34static constexpr uint8_t TAS2780_DC_BLK0 = 0x04;
35static constexpr uint8_t TAS2780_DC_BLK0_VBAT1S_MODE_MASK = (1 << 7);
36static constexpr uint8_t TAS2780_DC_BLK1 = 0x05;
37static constexpr uint8_t TAS2780_DC_BLK1_RESET_VAL = 0x41;
39static constexpr uint8_t TAS2780_TDM_CFG2 = 0x0A;
40static constexpr uint8_t TAS2780_TDM_CFG2_RX_SCFG_SHIFT = 4;
41static constexpr uint8_t TAS2780_TDM_CFG2_RX_SCFG_MASK = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT);
42static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_SHIFT = 2;
43static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_MASK = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT);
44static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_32BIT = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT);
45static constexpr uint8_t TAS2780_TDM_CFG2_RX_SLEN_MASK = (3 << 0);
46static constexpr uint8_t TAS2780_TDM_CFG2_RX_SLEN_32BIT = 2;
48static constexpr uint8_t TAS2780_TDM_CFG5 = 0x0E;
49static constexpr uint8_t TAS2780_TDM_CFG5_TX_VSNS_EN_SLOT4 = 0x44;
50static constexpr uint8_t TAS2780_TDM_CFG6 = 0x0F;
51static constexpr uint8_t TAS2780_TDM_CFG6_TX_ISNS_EN_SLOT0 = 0x40;
53static constexpr uint8_t TAS2780_DVC = 0x1A;
54static constexpr uint8_t TAS2780_DVC_MAX_ATTEN = 200;
57static constexpr uint8_t TAS2780_INT_MASK_ALL = 0xFF;
58static constexpr uint8_t TAS2780_INT_MASK0 = 0x3B;
59static constexpr uint8_t TAS2780_INT_MASK1 = 0x3C;
60static constexpr uint8_t TAS2780_INT_MASK1_0 = 0x3D;
61static constexpr uint8_t TAS2780_INT_MASK2 = 0x40;
62static constexpr uint8_t TAS2780_INT_MASK3 = 0x41;
63static constexpr uint8_t TAS2780_INT_LTCH0 = 0x49;
64static constexpr uint8_t TAS2780_INT_LTCH2 = 0x4F;
66static constexpr uint8_t TAS2780_INT_CLK_CFG = 0x5C;
67static constexpr uint8_t TAS2780_INT_CLK_CFG_CLR_LATCH = (1 << 2);
68static constexpr uint8_t TAS2780_INT_CLK_CFG_MODE_MASK = 0x03;
69static constexpr uint8_t TAS2780_INT_CLK_CFG_MODE_LIVE = 0x00;
70static constexpr uint8_t TAS2780_PVDD_UVLO = 0x71;
71static constexpr uint8_t TAS2780_PVDD_UVLO_2V76 = 0x03;
74static constexpr uint8_t TAS2780_INIT_0 = 0x17;
75static constexpr uint8_t TAS2780_INIT_0_VAL = 0xC8;
76static constexpr uint8_t TAS2780_LSR = 0x19;
77static constexpr uint8_t TAS2780_LSR_PWM_MODE = 0x00;
78static constexpr uint8_t TAS2780_INIT_1 = 0x21;
79static constexpr uint8_t TAS2780_INIT_1_VAL = 0x00;
80static constexpr uint8_t TAS2780_INIT_2 = 0x35;
81static constexpr uint8_t TAS2780_INIT_2_VAL = 0x74;
84static constexpr uint8_t TAS2780_PAGE_FD_ACCESS = 0x0D;
85static constexpr uint8_t TAS2780_PAGE_FD_ACCESS_UNLOCK = 0x0D;
86static constexpr uint8_t TAS2780_PAGE_FD_ACCESS_LOCK = 0x00;
87static constexpr uint8_t TAS2780_INIT_3 = 0x3E;
88static constexpr uint8_t TAS2780_INIT_3_VAL = 0x4A;
92static constexpr uint8_t POWER_MODE_CDS_MODES = 0x72;
93static constexpr uint8_t POWER_MODE_VBAT1S_MODES = 0x04;
97static constexpr uint32_t TAS2780_INT_LTCH_ERROR_MASKS = 0x0FA058C7;
98static constexpr uint32_t TAS2780_INT_LTCH_INFO_MASKS = 0x00002138;
101static const LogString *fault_name(uint8_t reg, uint8_t bit) {
102 const uint8_t key = (reg << 3) | bit;
104 return LOG_STR(
"Over temperature error");
106 return LOG_STR(
"Over current error");
108 return LOG_STR(
"TDM Clock Error");
110 return LOG_STR(
"Limiter active");
112 return LOG_STR(
"PVDD below limiter inflection point");
114 return LOG_STR(
"Limiter max attenuation");
116 return LOG_STR(
"BOP infinite hold");
118 return LOG_STR(
"BOP Mute");
120 return LOG_STR(
"Gain limiter active");
122 return LOG_STR(
"Load Diagnostic mode fault status");
124 return LOG_STR(
"Load diagnostic complete");
126 return LOG_STR(
"OTP CRC error flag");
128 return LOG_STR(
"VBAT1S Under Voltage");
130 return LOG_STR(
"Internal PLL Clock Error");
132 return LOG_STR(
"PVDD UVLO");
134 return LOG_STR(
"Internal VBAT1S LDO Over Load");
136 return LOG_STR(
"Internal VBAT1S LDO Over Voltage");
138 return LOG_STR(
"Internal VBAT1S LDO Under Voltage");
143 if (!this->
init_()) {
153 if (!this->
write_byte(TAS2780_PAGE_SELECT, page)) {
164 ESP_LOGE(TAG,
"Failed to read register 0x%02X",
reg);
167 return this->
write_byte(reg, (current & ~mask) | (value & mask));
173 ESP_LOGE(TAG,
"I2C write failed during init");
178 this->
reg(TAS2780_SW_RESET) = TAS2780_SW_RESET_CMD;
182 if (!this->
read_byte(TAS2780_DC_BLK1, &chd1)) {
183 ESP_LOGE(TAG,
"I2C read failed during init");
186 if (chd1 != TAS2780_DC_BLK1_RESET_VAL) {
187 ESP_LOGE(TAG,
"Init failed (DC_BLK1=0x%02X, expected 0x%02X)", chd1, TAS2780_DC_BLK1_RESET_VAL);
194 this->
reg(TAS2780_TDM_CFG5) = TAS2780_TDM_CFG5_TX_VSNS_EN_SLOT4;
195 this->
reg(TAS2780_TDM_CFG6) = TAS2780_TDM_CFG6_TX_ISNS_EN_SLOT0;
200 this->
reg(TAS2780_LSR) = TAS2780_LSR_PWM_MODE;
201 this->
reg(TAS2780_INIT_0) = TAS2780_INIT_0_VAL;
202 this->
reg(TAS2780_INIT_1) = TAS2780_INIT_1_VAL;
203 this->
reg(TAS2780_INIT_2) = TAS2780_INIT_2_VAL;
208 this->
reg(TAS2780_PAGE_FD_ACCESS) = TAS2780_PAGE_FD_ACCESS_UNLOCK;
209 this->
reg(TAS2780_INIT_3) = TAS2780_INIT_3_VAL;
210 this->
reg(TAS2780_PAGE_FD_ACCESS) = TAS2780_PAGE_FD_ACCESS_LOCK;
220 this->
reg(TAS2780_PVDD_UVLO) = TAS2780_PVDD_UVLO_2V76;
223 this->
reg(TAS2780_INT_MASK0) = TAS2780_INT_MASK_ALL;
224 this->
reg(TAS2780_INT_MASK1) = TAS2780_INT_MASK_ALL;
225 this->
reg(TAS2780_INT_MASK1_0) = TAS2780_INT_MASK_ALL;
226 this->
reg(TAS2780_INT_MASK2) = TAS2780_INT_MASK_ALL;
227 this->
reg(TAS2780_INT_MASK3) = TAS2780_INT_MASK_ALL;
229 if (!this->
update_bits_(TAS2780_INT_CLK_CFG, TAS2780_INT_CLK_CFG_MODE_MASK, TAS2780_INT_CLK_CFG_MODE_LIVE))
242 ESP_LOGD(TAG,
"Activating (PWR_MODE:%u)", this->
power_mode_);
252 ESP_LOGD(TAG,
"Deactivating");
265 if (!this->
init_()) {
266 ESP_LOGE(TAG,
"Re-initialization failed");
277 ESP_LOGE(TAG,
"Invalid power mode %u, must be 0-3",
power_mode);
284 uint8_t cds_mode = (POWER_MODE_CDS_MODES >> (
power_mode * 2)) & 0x03;
285 uint8_t vbat1s_mode = (POWER_MODE_VBAT1S_MODES >>
power_mode) & 0x01;
286 return this->
update_bits_(TAS2780_CHNL_0, TAS2780_CHNL_0_CDS_MODE_MASK, cds_mode << TAS2780_CHNL_0_CDS_MODE_SHIFT) &&
287 this->
update_bits_(TAS2780_DC_BLK0, TAS2780_DC_BLK0_VBAT1S_MODE_MASK, vbat1s_mode ? 0xFF : 0);
291 this->
update_bits_(TAS2780_INT_CLK_CFG, TAS2780_INT_CLK_CFG_CLR_LATCH, TAS2780_INT_CLK_CFG_CLR_LATCH);
298 !this->
read_byte(TAS2780_INT_LTCH2, &latched[3])) {
302 if (latched[1] & (1 << 4))
303 latched[1] = (latched[1] & ~(1 << 4)) | (1 << 3);
305 uint8_t errors = latched[
reg] & (TAS2780_INT_LTCH_ERROR_MASKS >> (
reg * 8));
306 uint8_t infos = latched[
reg] & (TAS2780_INT_LTCH_INFO_MASKS >> (
reg * 8));
307 for (uint8_t bit = 0; bit < 8; bit++) {
308 if (errors & (1 << bit)) {
309 ESP_LOGE(TAG,
"%s", LOG_STR_ARG(fault_name(
reg, bit)));
310 }
else if (infos & (1 << bit)) {
311 ESP_LOGD(TAG,
"%s", LOG_STR_ARG(fault_name(
reg, bit)));
315 return (latched[0] | latched[1] | latched[2] | latched[3]) != 0;
325 ESP_LOGCONFIG(TAG,
"Audio Amplifier:");
326 LOG_I2C_DEVICE(
this);
327 LOG_UPDATE_INTERVAL(
this);
331 " Volume Range: %.2f - %.2f\n"
336 : LOG_STR_LITERAL(
"Mono Downmix"));
340 return this->
update_bits_(TAS2780_MODE_CTRL, TAS2780_MODE_CTRL_MODE_MASK,
mode);
344 return this->
is_muted_ ? TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED : TAS2780_MODE_CTRL_MODE_ACTIVE;
352 ESP_LOGE(TAG,
"Failed to read MODE_CTRL");
356 uint8_t current_mode = mode_ctrl & TAS2780_MODE_CTRL_MODE_MASK;
358 if ((current_mode == TAS2780_MODE_CTRL_MODE_ACTIVE || current_mode == TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED) &&
359 !this->
write_byte(TAS2780_MODE_CTRL, (mode_ctrl & ~TAS2780_MODE_CTRL_MODE_MASK) | this->
active_mode_())) {
360 ESP_LOGE(TAG,
"Failed to write MODE_CTRL");
368 float previous = this->
volume_;
381 float volume = clamp(std::lerp(range_min, range_max, this->
volume_), 0.0f, 1.0f);
383 ESP_LOGD(TAG,
"Setting attenuation to: %u", dvc);
391 if (this->
amp_level_ > TAS2780_AMP_LEVEL_MAX) {
392 ESP_LOGW(TAG,
"Amp level %u out of range, using %u", this->
amp_level_, TAS2780_AMP_LEVEL_MAX);
395 ESP_LOGD(TAG,
"Update amp to level idx: %u", this->
amp_level_);
396 if (!this->
update_bits_(TAS2780_CHNL_0, TAS2780_CHNL_0_AMP_LEVEL_MASK,
397 this->
amp_level_ << TAS2780_CHNL_0_AMP_LEVEL_SHIFT)) {
401 TAS2780_TDM_CFG2, TAS2780_TDM_CFG2_RX_SCFG_MASK | TAS2780_TDM_CFG2_RX_WLEN_MASK | TAS2780_TDM_CFG2_RX_SLEN_MASK,
402 (this->
selected_channel_ << TAS2780_TDM_CFG2_RX_SCFG_SHIFT) | TAS2780_TDM_CFG2_RX_WLEN_32BIT |
403 TAS2780_TDM_CFG2_RX_SLEN_32BIT);