[PATCH] ASoC: tas2781: Do not report cross-instance writes as errors on SPI

From: Andreas Axelsson

Date: Thu Jun 11 2026 - 04:21:55 EST


On SPI-based TAS2781 HDA systems (e.g. HP laptops with two amplifiers
on separate SPI chip selects), each tasdevice_priv instance services a
single device. The SPI bus glue intentionally returns -EXDEV for
register access addressed to a channel the instance does not own, and
logs it at debug level as "Not error" (see
tasdevice_spi_change_chn_book()).

The shared firmware library does not handle this convention: firmware
blocks explicitly addressed to another device (dev_idx != 0) and
calibration data for other channels are still processed, and the
resulting -EXDEV returns are reported via dev_err and counted as block
errors, triggering pointless retries. This floods the log on every
boot, resume and profile switch:

  tas2781-hda spi1-TXNW2781:00-tas2781-hda.0: tasdevice_process_block: bulk_write error = -18
  tas2781-hda spi1-TXNW2781:00-tas2781-hda.0: process_block: single write error
  tas2781-hda spi1-TXNW2781:00-tas2781-hda.0: chn 1 r0_reg bulk_wr err = -18

Treat -EXDEV as "block not for this instance": skip it silently in
tasdevice_process_block(), keeping the sub-block parsing intact so
the data offset stays correct, and skip foreign channels entirely in
tasdev_load_calibrated_data().

Tested on an HP laptop (subsystem 103C:8DE8) with two TAS2781 amps on
SPI: the error spam is gone, firmware and calibration still load, and
audio is unaffected.

The root cause analysis and fix were developed by Claude (Anthropic AI
assistant); tested by me on the hardware above.

Fixes: 9fa6a693ad8d ("ALSA: hda/tas2781: Remove tas2781_spi_fwlib.c and leverage SND_SOC_TAS2781_FMWLIB")
Signed-off-by: Andreas Axelsson <andreas.axelsson@xxxxxxxxxxxxxxxxx>
---
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -896,6 +896,14 @@
         }
     }

+    /*
+     * On SPI each instance services a single device, but explicitly
+     * addressed blocks (idx != 0) may target any device. Writes for
+     * a channel this instance does not own return -EXDEV from the
+     * bus glue ("Not error", see tasdevice_spi_change_chn_book);
+     * they are expected and must not be reported as failures. The
+     * block is still parsed so the sub-block offset stays correct.
+     */
     for (; chn < chnend; chn++) {
         if (tas_priv->tasdevice[chn].is_loading == false)
             continue;
@@ -921,7 +929,7 @@
                         data[subblk_offset + 1],
                         data[subblk_offset + 2]),
                     data[subblk_offset + 3]);
-                if (rc < 0) {
+                if (rc < 0 && rc != -EXDEV) {
                     is_err = true;
                     dev_err(tas_priv->dev,
                     "process_block: single write error\n");
@@ -953,7 +961,7 @@
                 data[subblk_offset + 1],
                 data[subblk_offset + 2]),
                 &(data[subblk_offset + 4]), len);
-            if (rc < 0) {
+            if (rc < 0 && rc != -EXDEV) {
                 is_err = true;
                 dev_err(tas_priv->dev,
                     "%s: bulk_write error = %d\n",
@@ -991,7 +999,7 @@
                 data[subblk_offset + 4]),
                 data[subblk_offset + 1],
                 data[subblk_offset + 5]);
-            if (rc < 0) {
+            if (rc < 0 && rc != -EXDEV) {
                 is_err = true;
                 dev_err(tas_priv->dev,
                     "%s: update_bits error = %d\n",
@@ -2552,6 +2560,10 @@
     if (!data || !cali_data->total_sz)
         return;

+    /* On SPI each instance owns a single device; skip the others. */
+    if (priv->isspi && i != priv->index)
+        return;
+
     if (data[k] != i) {
         dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
             __func__, i);