[PATCH AUTOSEL 6.19-6.18] ASoC: cs35l56: Only patch ASP registers if the DAI is part of a DAIlink
From: Sasha Levin
Date: Tue Mar 10 2026 - 05:07:04 EST
From: Richard Fitzgerald <rf@xxxxxxxxxxxxxxxxxxxxx>
[ Upstream commit 9351cf3fd92dc1349bb75f2f7f7324607dcf596f ]
Move the ASP register patches to a separate struct and apply this from the
ASP DAI probe() function so that the registers are only patched if the DAI
is part of a DAI link.
Some systems use the ASP as a special-purpose interconnect and on these
systems the ASP registers are configured by a third party (the firmware,
the BIOS, or another device using the amp's secondary host control
interface).
If the machine driver does not hook up the ASP DAI then the ASP registers
must be omitted from the patch to prevent overwriting the third party
configuration.
If the machine driver includes the ASP DAI in a DAI link, this implies that
the machine driver and higher components (such as alsa-ucm) are taking
ownership of the ASP. In this case the ASP registers are patched to known
defaults and the machine driver should configure the ASP.
Signed-off-by: Richard Fitzgerald <rf@xxxxxxxxxxxxxxxxxxxxx>
Link: https://patch.msgid.link/20260226110137.1664562-1-rf@xxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
LLM Generated explanations, may be completely bogus:
I notice the HDA path still calls `cs35l56_set_patch()` but NOT
`cs35l56_set_asp_patch()`. This makes sense - the HDA path uses HDA for
audio, not ASP. The ASP on HDA systems would be used as a special-
purpose interconnect and should NOT have its registers overwritten.
## Analysis
### What the commit does
This commit splits the ASP (Audio Serial Port) register initialization
out of the general `cs35l56_set_patch()` function into a separate
`cs35l56_set_asp_patch()` function. The ASP patch is now only applied
from the DAI `.probe()` callback, meaning it only runs when the ASP DAI
is actually included in a machine driver's DAI link.
### The bug being fixed
On systems where the CS35L56 amplifier's ASP is used as a special-
purpose interconnect (configured by firmware, BIOS, or a secondary host
control interface), the kernel driver was unconditionally overwriting
the ASP registers with hard-coded defaults during initialization. This
would break the ASP configuration set by those third parties, causing
**audio malfunction on those systems**.
This is a real-world functional bug - audio would break or behave
incorrectly on affected systems. The commit message explicitly describes
the problem: "the ASP registers must be omitted from the patch to
prevent overwriting the third party configuration."
### Stable kernel criteria assessment
1. **Fixes a real bug**: Yes - ASP register overwrite causes audio
malfunction on systems where ASP is configured by firmware/BIOS.
2. **Obviously correct**: Yes - the logic is clean and clear: only patch
ASP registers when the ASP DAI is actively used by the machine
driver.
3. **Small and contained**: The change is moderate in size (~40 lines
across 3 files) but very contained - it only splits an existing array
and adds a new calling path.
4. **No new features**: The commit does not add new functionality. It
makes existing functionality conditional to prevent a bug.
5. **Risk**: Low - the change is well-scoped and only affects one codec
driver. The worst case if something goes wrong is audio issues on
cs35l56 devices.
### Concerns
1. **Code exists in stable trees**: The driver was introduced in v6.4,
so it exists in stable trees 6.6.y and later.
2. **Dependencies**: This commit appears to be self-contained - it only
restructures existing code and adds a standard DAI `.probe` callback.
3. **New exported symbol**: `cs35l56_set_asp_patch` is a new export, but
it's only called from the same driver (cs35l56.c), and exporting from
shared modules is the established pattern for this driver.
4. **Backport complexity**: The patch should apply relatively cleanly to
recent stable trees (6.6.y, 6.12.y) though minor conflicts may occur
if the register list has changed across versions.
### Verdict
This fixes a real audio regression/bug on systems where the ASP is
managed by firmware/BIOS rather than by the Linux machine driver. The
fix is well-contained, obviously correct, low risk, and self-contained.
The driver exists in stable trees from v6.4 onward. It meets stable
kernel criteria as a bug fix for real-world hardware.
### Verification
- `git log --diff-filter=A` confirmed cs35l56-shared.c was introduced in
commit e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic
CS35L56")
- `git tag --contains` confirmed the driver was first included in v6.4
- `grep` confirmed `cs35l56_set_patch` is called from both the ASoC path
(cs35l56.c:1726) and HDA path (cs35l56_hda.c:1186)
- `grep` confirmed `cs35l56_set_asp_patch` is only called from the new
DAI probe in cs35l56.c:353
- Read of cs35l56_hda.c confirmed the HDA path does NOT call
`cs35l56_set_asp_patch`, consistent with HDA systems not using ASP via
the Linux driver
- Read of the current cs35l56-shared.c confirmed the patch was properly
split - ASP registers in `cs35l56_asp_patch[]`, non-ASP registers in
`cs35l56_patch[]`
- The commit is very recent (Feb 2026) and not yet in a release tag
(unverified whether it's had extensive testing in mainline, but it has
been reviewed by Mark Brown, the ASoC maintainer)
**YES**
include/sound/cs35l56.h | 1 +
sound/soc/codecs/cs35l56-shared.c | 16 +++++++++++++++-
sound/soc/codecs/cs35l56.c | 8 ++++++++
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 5928af539c468..d0ae1ae2ae2a0 100644
--- a/include/sound/cs35l56.h
+++ b/include/sound/cs35l56.h
@@ -374,6 +374,7 @@ extern const char * const cs35l56_cal_set_status_text[3];
extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
+int cs35l56_set_asp_patch(struct cs35l56_base *cs35l56_base);
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base);
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command);
int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base);
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 60100c8f8c952..0ec6a96e80858 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -23,7 +23,7 @@
#include "cs35l56.h"
-static const struct reg_sequence cs35l56_patch[] = {
+static const struct reg_sequence cs35l56_asp_patch[] = {
/*
* Firmware can change these to non-defaults to satisfy SDCA.
* Ensure that they are at known defaults.
@@ -40,6 +40,20 @@ static const struct reg_sequence cs35l56_patch[] = {
{ CS35L56_ASP1TX2_INPUT, 0x00000000 },
{ CS35L56_ASP1TX3_INPUT, 0x00000000 },
{ CS35L56_ASP1TX4_INPUT, 0x00000000 },
+};
+
+int cs35l56_set_asp_patch(struct cs35l56_base *cs35l56_base)
+{
+ return regmap_register_patch(cs35l56_base->regmap, cs35l56_asp_patch,
+ ARRAY_SIZE(cs35l56_asp_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_set_asp_patch, "SND_SOC_CS35L56_SHARED");
+
+static const struct reg_sequence cs35l56_patch[] = {
+ /*
+ * Firmware can change these to non-defaults to satisfy SDCA.
+ * Ensure that they are at known defaults.
+ */
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 55b4d0d55712a..1c1924c6f4070 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -346,6 +346,13 @@ static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
return wm_adsp_event(w, kcontrol, event);
}
+static int cs35l56_asp_dai_probe(struct snd_soc_dai *codec_dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
+
+ return cs35l56_set_asp_patch(&cs35l56->base);
+}
+
static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
@@ -550,6 +557,7 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
}
static const struct snd_soc_dai_ops cs35l56_ops = {
+ .probe = cs35l56_asp_dai_probe,
.set_fmt = cs35l56_asp_dai_set_fmt,
.set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
.hw_params = cs35l56_asp_dai_hw_params,
--
2.51.0