[PATCH] ALSA: hda/tas2781: clear cali_data.total_sz when calibration read fails

Philipp Oster via B4 Relay posted 1 patch 4 days, 16 hours ago
sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++
1 file changed, 3 insertions(+)
[PATCH] ALSA: hda/tas2781: clear cali_data.total_sz when calibration read fails
Posted by Philipp Oster via B4 Relay 4 days, 16 hours ago
From: Philipp Oster <philippdev5396@outlook.de>

tas2563_save_calibration() assigns cali_data.total_sz before it reads the
per-device calibration data from EFI, but its error paths return without
clearing it again. cali_data.cali_reg_array is left all zero, because the
function returns before the register addresses are assigned.

On the first playback tasdev_load_calibrated_data() does

	if (!data || !cali_data->total_sz)
		return;

which passes, since total_sz is still non-zero. It then issues five
4-byte bulk writes to p->r0_reg, p->r0_low_reg, p->invr0_reg, p->pow_reg
and p->tlimit_reg, all of which are 0. Register 0 decodes to book 0 /
page 0 / register 0x00, so the auto-incrementing block write zeroes
registers 0x00 to 0x03. Register 0x03 is PB_CFG1, which holds AMP_LEVEL,
so the amplifier gain is set to its minimum and the speaker stays silent.

This is reproducible on a Lenovo Yoga 7 14ARB7 (two TAS2563 on I2C,
ACPI INT8866) whose factory calibration was never written to UEFI, so the
EFI read fails with EFI_NOT_FOUND. The two woofers driven by the
amplifiers are silent while the tweeters driven directly by the ALC287
play. Reading the amplifier registers over i2c shows PWR_CTL = 0x00
(active) and the TDM slots correctly programmed by the RCA profile, but
PB_CFG1 = 0x00. With this change PB_CFG1 keeps its power-on default of
0x20 and both woofers play.

tas2781_save_calibration() in tas2781_hda.c already clears total_sz on
failure; do the same for the TAS2563 variant.

Signed-off-by: Philipp Oster <philippdev5396@outlook.de>
---
Single patch - see the commit message for the full analysis and the
register-level evidence from the affected machine.
---
 sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 67240ce18..b1c89cc35 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -355,6 +355,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
 			if (ret < 0 || ret >= sizeof(var8) - 1) {
 				dev_err(p->dev, "%s: Read %s failed\n",
 					__func__, var8);
+				cd->total_sz = 0;
 				return -EINVAL;
 			}
 			/*
@@ -372,6 +373,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
 				dev_warn(p->dev,
 					"Dev %d: Caldat[%d] read failed %ld\n",
 					i, j, status);
+				cd->total_sz = 0;
 				return -EINVAL;
 			}
 			bedata = cpu_to_be32(*(uint32_t *)&data[offset]);
@@ -383,6 +385,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
 	if (cd->total_sz != offset) {
 		dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) mismatch\n",
 			__func__, cd->total_sz, offset);
+		cd->total_sz = 0;
 		return -EINVAL;
 	}
 

---
base-commit: 199c9959d3a9b53f346c221757fc7ac507fbac50
change-id: 20260720-tas2781-calfix-3a1d661ef44b

Best regards,
-- 
Philipp Oster <philippdev5396@outlook.de>
Re: [PATCH] ALSA: hda/tas2781: clear cali_data.total_sz when calibration read fails
Posted by Takashi Iwai 4 days, 7 hours ago
On Mon, 20 Jul 2026 11:41:51 +0200,
Philipp Oster via B4 Relay wrote:
> 
> From: Philipp Oster <philippdev5396@outlook.de>
> 
> tas2563_save_calibration() assigns cali_data.total_sz before it reads the
> per-device calibration data from EFI, but its error paths return without
> clearing it again. cali_data.cali_reg_array is left all zero, because the
> function returns before the register addresses are assigned.
> 
> On the first playback tasdev_load_calibrated_data() does
> 
> 	if (!data || !cali_data->total_sz)
> 		return;
> 
> which passes, since total_sz is still non-zero. It then issues five
> 4-byte bulk writes to p->r0_reg, p->r0_low_reg, p->invr0_reg, p->pow_reg
> and p->tlimit_reg, all of which are 0. Register 0 decodes to book 0 /
> page 0 / register 0x00, so the auto-incrementing block write zeroes
> registers 0x00 to 0x03. Register 0x03 is PB_CFG1, which holds AMP_LEVEL,
> so the amplifier gain is set to its minimum and the speaker stays silent.
> 
> This is reproducible on a Lenovo Yoga 7 14ARB7 (two TAS2563 on I2C,
> ACPI INT8866) whose factory calibration was never written to UEFI, so the
> EFI read fails with EFI_NOT_FOUND. The two woofers driven by the
> amplifiers are silent while the tweeters driven directly by the ALC287
> play. Reading the amplifier registers over i2c shows PWR_CTL = 0x00
> (active) and the TDM slots correctly programmed by the RCA profile, but
> PB_CFG1 = 0x00. With this change PB_CFG1 keeps its power-on default of
> 0x20 and both woofers play.
> 
> tas2781_save_calibration() in tas2781_hda.c already clears total_sz on
> failure; do the same for the TAS2563 variant.
> 
> Signed-off-by: Philipp Oster <philippdev5396@outlook.de>

Applied now.  Thanks.


Takashi