[PATCH AUTOSEL 6.19-5.10] ASoC: wm8962: Add WM8962_ADC_MONOMIX to "3D Coefficients" mask

Sasha Levin
2026-02-14T01:02:51Z

From: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>

[ Upstream commit 66c26346ae30c883eef70acf9cf9054dfdb4fb2f ]

This bit is handled by a separate control.

Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20260105-wm8962-l5-fixes-v1-1-f4f4eeacf089@puri.sm
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have a complete understanding. Let me compile the analysis.

---

## Detailed Analysis

### 1. COMMIT MESSAGE ANALYSIS

The commit message is concise: "This bit is handled by a separate
control." It's authored by Sebastian Krzyszkowiak from Purism (who works
on the Librem 5 phone, which uses the WM8962 codec), and it's reviewed
by Charles Keepax from Cirrus Logic (the codec manufacturer's software
team). The subject line says "Add WM8962_ADC_MONOMIX to '3D
Coefficients' mask." The patch series name in the link
(`wm8962-l5-fixes`) confirms this is a fix, not a feature.

### 2. CODE CHANGE ANALYSIS — The Bug

**Register layout of `WM8962_THREED1` (R268, 0x10C):**
- Bit 6: `ADC_MONOMIX` (0x0040) — controls ADC mono downmix
- Bit 5: `THREED_SIGN_L` (0x0020)
- Bit 4: `THREED_SIGN_R` (0x0010)
- Bit 2: `THREED_LHPF_MODE` (0x0004)
- Bit 1: `THREED_LHPF_ENA` (0x0002)
- Bit 0: `THREED_ENA` (0x0001)

**Two separate ALSA controls share this register:**

1. **"ADC Monomix Switch"** (line 1707) — a `SOC_SINGLE` control for bit
6, added in commit `89383a2707e54` (August 2020, merged in
v5.10-rc3).

2. **"3D Coefficients"** (line 1763) — a `SND_SOC_BYTES_MASK` bulk
control covering 4 registers starting at `WM8962_THREED1`, added in
commit `69e5a39f39c37` (February 2012).

**How `SND_SOC_BYTES_MASK` works:** The mask parameter tells
`snd_soc_bytes_put()` which bits in the first register should be
**preserved** (read-modify-write) when userspace writes coefficient
data. Looking at lines 585-628 of `soc-ops.c`:

1. Read the current register value
2. `val &= params->mask` — extract the preserved bits
3. `data[0] &= ~params->mask` — clear the preserved bits in the incoming
data
4. `data[0] |= val` — restore the preserved bits

**The bug:** Before this fix, the mask was only `WM8962_THREED_ENA`
(0x0001, bit 0). This meant that when userspace wrote "3D Coefficients",
only the `THREED_ENA` bit was preserved. **Bit 6 (`ADC_MONOMIX`) was NOT
preserved** — it would be overwritten with whatever value happened to be
in the userspace data blob at that bit position.

**Consequence:** Writing the "3D Coefficients" control from userspace
would silently and unpredictably clobber the "ADC Monomix Switch"
setting. If a user (or ALSA UCM configuration) had enabled ADC mono
downmix (important for single-speaker devices like the Librem 5 phone),
writing 3D coefficients could randomly disable it, causing audio routing
issues. Conversely, if monomix was off, writing 3D coefficients could
accidentally enable it, causing unexpected mono downmix.

### 3. FIX ANALYSIS

The fix adds `WM8962_ADC_MONOMIX` (0x0040) to the mask:

```c
// Before: mask = 0x0001 (only THREED_ENA preserved)
SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4,
WM8962_THREED_ENA),

// After: mask = 0x0041 (THREED_ENA and ADC_MONOMIX preserved)
SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4,
WM8962_THREED_ENA | WM8962_ADC_MONOMIX),
```

This ensures that when "3D Coefficients" is written, both the 3D enable
bit AND the ADC monomix bit are preserved from the current register
value, preventing one control from clobbering the other.

### 4. SCOPE AND RISK ASSESSMENT

- **Size:** Single character change — adding `| WM8962_ADC_MONOMIX` to a
mask value. This is the smallest possible fix.
- **Files touched:** 1 file (`sound/soc/codecs/wm8962.c`)
- **Risk:** Essentially zero. The only effect is that one additional bit
is read-modify-write preserved instead of being overwritten. This is
strictly a correctness improvement.
- **Could this break something?** No. Before this fix, writing 3D
coefficients could clobber the monomix bit. After this fix, it cannot.
There's no scenario where the old (buggy) behavior was desirable.

### 5. USER IMPACT

- **Who is affected:** Anyone using the WM8962 codec (common in
embedded/mobile devices) who uses both the ADC Monomix and 3D
enhancement features. The Librem 5 phone is one real-world device
affected.
- **Severity:** Audio routing bug — the monomix setting is unexpectedly
changed when 3D coefficients are written, which can cause incorrect
audio output (mono/stereo confusion).
- **How long has the bug existed:** Since v5.10-rc3 (August 2020), when
the ADC Monomix switch was added sharing the same register. That's
about 5.5 years.

### 6. STABILITY INDICATORS

- **Reviewed-by:** Charles Keepax from Cirrus Logic (the codec
manufacturer) — authoritative review
- **Author:** Sebastian Krzyszkowiak from Purism — someone who actively
works with this hardware
- **Subsystem maintainer ack:** Mark Brown (ASoC maintainer) applied it

### 7. STABLE TREE APPLICABILITY

- The bug was introduced by `89383a2707e54` which is in stable trees
5.10, 5.15, 6.1, and newer.
- The fix has zero dependencies on other commits.
- The code context around the change is stable and unchanged across all
these versions.
- The fix applies cleanly to all affected stable trees.

### 8. CLASSIFICATION

This is a **hardware register clobbering bug fix** — an audio codec
quirk/control fix. It's analogous to audio codec quirks (like
`SND_PCI_QUIRK` entries) in that it fixes incorrect hardware control
behavior. It falls squarely into the "fixes a real bug" category: two
ALSA controls sharing a hardware register were interfering with each
other due to an incomplete bit mask.

### Decision

This is a textbook stable backport candidate:
- **Obviously correct:** The mask was missing a bit that's controlled by
another control sharing the same register
- **Fixes a real bug:** Audio routing corruption when 3D coefficients
are written
- **Small and contained:** One-line change adding a bit to a mask
- **No new features:** Purely corrective
- **No risk of regression:** Only makes the read-modify-write preserve
one additional bit
- **Reviewed by codec manufacturer engineer**
- **Affects real users on real hardware** (Librem 5 and any other
WM8962-based device)
- **Bug exists in all stable trees from 5.10 onward**

**YES**

sound/soc/codecs/wm8962.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e9e317ce68982..1040740fc80f8 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1760,7 +1760,7 @@ SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18),

SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
-SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA | WM8962_ADC_MONOMIX),

SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
--
2.51.0