mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Richard Fitzgerald <rf@opensource.cirrus.com>
To: broonie@kernel.org
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	patches@opensource.cirrus.com
Subject: [PATCH 1/2] ASoC: cs35l56: Only write ASP1TXn registers when widget is powered-up
Date: Fri, 13 Nov 2026 11:29:34 +0000	[thread overview]
Message-ID: <20261113112935.2926431-2-rf@opensource.cirrus.com> (raw)
In-Reply-To: <20261113112935.2926431-1-rf@opensource.cirrus.com>

Cache the value written to the "ASP1 TXn Source" controls and write them
to registers only when the widget is powered-up. Remove them from the
regmap defaults table so regmap won't write them during a regcache_sync().

The ASP registers have multiple ownership, depending on the hardware
design. They can be owned by the firmware, or by a MCU on a secondary
control interface, or by the driver. If the driver doesn't own control
of the ASP it must not overwrite the registers.

ALSA control read/write now goes to cached values in struct
cs35l56_private. The values are written out if the widget powers up,
which can only happen if the machine driver has connected the ASP DAI in
a DAIlink. If the control value is changed while the widget is powered
the change will be deferred until the next widget power-up. Changing
the hardware mux on-the-fly while active is not supported.

Fixes: 9351cf3fd92dc ("ASoC: cs35l56: Only patch ASP registers if the DAI is part of a DAIlink")
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
---
 sound/soc/codecs/cs35l56-shared.c |  12 ----
 sound/soc/codecs/cs35l56.c        | 104 ++++++++++++++++++++++++------
 sound/soc/codecs/cs35l56.h        |   1 +
 3 files changed, 86 insertions(+), 31 deletions(-)

diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index b37f65cd4d33..c44bf8bc4e0c 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -39,10 +39,6 @@ static const struct reg_sequence cs35l56_asp_patch[] = {
 	{ CS35L56_ASP1_FRAME_CONTROL5,		0x00020100 },
 	{ CS35L56_ASP1_DATA_CONTROL1,		0x00000018 },
 	{ CS35L56_ASP1_DATA_CONTROL5,		0x00000018 },
-	{ CS35L56_ASP1TX1_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX2_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX3_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX4_INPUT,		0x00000000 },
 };
 
 int cs35l56_set_asp_patch(struct cs35l56_base *cs35l56_base)
@@ -117,10 +113,6 @@ static const struct reg_default cs35l56_reg_defaults[] = {
 	{ CS35L56_ASP1_FRAME_CONTROL5,		0x00020100 },
 	{ CS35L56_ASP1_DATA_CONTROL1,		0x00000018 },
 	{ CS35L56_ASP1_DATA_CONTROL5,		0x00000018 },
-	{ CS35L56_ASP1TX1_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX2_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX3_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX4_INPUT,		0x00000000 },
 	{ CS35L56_SWIRE_DP3_CH1_INPUT,		0x00000018 },
 	{ CS35L56_SWIRE_DP3_CH2_INPUT,		0x00000019 },
 	{ CS35L56_SWIRE_DP3_CH3_INPUT,		0x00000029 },
@@ -145,10 +137,6 @@ static const struct reg_default cs35l63_reg_defaults[] = {
 	{ CS35L56_ASP1_FRAME_CONTROL5,		0x00020100 },
 	{ CS35L56_ASP1_DATA_CONTROL1,		0x00000018 },
 	{ CS35L56_ASP1_DATA_CONTROL5,		0x00000018 },
-	{ CS35L56_ASP1TX1_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX2_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX3_INPUT,		0x00000000 },
-	{ CS35L56_ASP1TX4_INPUT,		0x00000000 },
 	{ 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 a4d25c287941..852491f8340b 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -178,41 +178,91 @@ static const struct snd_kcontrol_new cs35l63_controls[] = {
 			 SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
 };
 
+static int cs35l56_asp_tx_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+	snd_soc_dapm_mutex_lock(dapm);
+	ucontrol->value.enumerated.item[0] =
+		snd_soc_enum_val_to_item(e, cs35l56->asp_tx_input[e->shift_l]);
+	snd_soc_dapm_mutex_unlock(dapm);
+
+	return 0;
+}
+
+static int cs35l56_asp_tx_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int item = ucontrol->value.enumerated.item[0];
+	unsigned int val;
+	bool change = false;
+
+	if (item >= e->items)
+		return -EINVAL;
+
+	val = snd_soc_enum_item_to_val(e, item);
+
+	snd_soc_dapm_mutex_lock(dapm);
+	if (cs35l56->asp_tx_input[e->shift_l] != val)
+		change = true;
+
+	cs35l56->asp_tx_input[e->shift_l] = val;
+	snd_soc_dapm_mutex_unlock(dapm);
+
+	if (change)
+		snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL);
+
+	return change;
+}
+
+/* The shift field of each virtual enum holds the ASP TX channel index. */
 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
-				  CS35L56_ASP1TX1_INPUT,
+				  SND_SOC_NOPM,
 				  0, CS35L56_ASP_TXn_SRC_MASK,
 				  cs35l56_tx_input_texts,
 				  cs35l56_tx_input_values);
 
 static const struct snd_kcontrol_new asp1_tx1_mux =
-	SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+	SOC_DAPM_ENUM_EXT("ASP1TX1 SRC", cs35l56_asp1tx1_enum,
+			  cs35l56_asp_tx_get, cs35l56_asp_tx_put);
 
 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
-				  CS35L56_ASP1TX2_INPUT,
-				  0, CS35L56_ASP_TXn_SRC_MASK,
+				  SND_SOC_NOPM,
+				  1, CS35L56_ASP_TXn_SRC_MASK,
 				  cs35l56_tx_input_texts,
 				  cs35l56_tx_input_values);
 
 static const struct snd_kcontrol_new asp1_tx2_mux =
-	SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+	SOC_DAPM_ENUM_EXT("ASP1TX2 SRC", cs35l56_asp1tx2_enum,
+			  cs35l56_asp_tx_get, cs35l56_asp_tx_put);
 
 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
-				  CS35L56_ASP1TX3_INPUT,
-				  0, CS35L56_ASP_TXn_SRC_MASK,
+				  SND_SOC_NOPM,
+				  2, CS35L56_ASP_TXn_SRC_MASK,
 				  cs35l56_tx_input_texts,
 				  cs35l56_tx_input_values);
 
 static const struct snd_kcontrol_new asp1_tx3_mux =
-	SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+	SOC_DAPM_ENUM_EXT("ASP1TX3 SRC", cs35l56_asp1tx3_enum,
+			  cs35l56_asp_tx_get, cs35l56_asp_tx_put);
 
 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
-				  CS35L56_ASP1TX4_INPUT,
-				  0, CS35L56_ASP_TXn_SRC_MASK,
+				  SND_SOC_NOPM,
+				  3, CS35L56_ASP_TXn_SRC_MASK,
 				  cs35l56_tx_input_texts,
 				  cs35l56_tx_input_values);
 
 static const struct snd_kcontrol_new asp1_tx4_mux =
-	SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+	SOC_DAPM_ENUM_EXT("ASP1TX4 SRC", cs35l56_asp1tx4_enum,
+			  cs35l56_asp_tx_get, cs35l56_asp_tx_put);
 
 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
 				CS35L56_SWIRE_DP3_CH1_INPUT,
@@ -282,6 +332,16 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
 	}
 }
 
+static int cs35l56_asp_tx_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+	return regmap_write(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT + (4 * w->shift),
+			    cs35l56->asp_tx_input[w->shift]);
+}
+
 static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
 	SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
 	SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
@@ -299,14 +359,18 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
 			    CS35L56_ASP_RX1_EN_SHIFT, 0),
 	SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1,
 			    CS35L56_ASP_RX2_EN_SHIFT, 0),
-	SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
-			     CS35L56_ASP_TX1_EN_SHIFT, 0),
-	SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
-			     CS35L56_ASP_TX2_EN_SHIFT, 0),
-	SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
-			     CS35L56_ASP_TX3_EN_SHIFT, 0),
-	SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
-			     CS35L56_ASP_TX4_EN_SHIFT, 0),
+	SND_SOC_DAPM_AIF_OUT_E("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+			       CS35L56_ASP_TX1_EN_SHIFT, 0,
+			       cs35l56_asp_tx_event, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_AIF_OUT_E("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+			       CS35L56_ASP_TX2_EN_SHIFT, 0,
+			       cs35l56_asp_tx_event, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_AIF_OUT_E("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
+			       CS35L56_ASP_TX3_EN_SHIFT, 0,
+			       cs35l56_asp_tx_event, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_AIF_OUT_E("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
+			       CS35L56_ASP_TX4_EN_SHIFT, 0,
+			       cs35l56_asp_tx_event, SND_SOC_DAPM_PRE_PMU),
 
 	SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux),
 	SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux),
@@ -1438,6 +1502,8 @@ static int _cs35l56_component_probe(struct snd_soc_component *component)
 
 	BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
 
+	memset(&cs35l56->asp_tx_input, 0, sizeof(cs35l56->asp_tx_input));
+
 	cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
 	if (!cs35l56->dsp.part)
 		return -ENOMEM;
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index b46f410f0e09..020bee21c806 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -49,6 +49,7 @@ struct cs35l56_private {
 	int speaker_id;
 	u32 rx_mask;
 	u32 tx_mask;
+	u8 asp_tx_input[4];
 	u8 asp_slot_width;
 	u8 asp_slot_count;
 	bool tdm_mode;
-- 
2.47.3


  reply	other threads:[~2026-11-13 11:39 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 11:29 [PATCH 0/2] ASoC: cs35l56: Series to stop writing ASP registers the driver doesn't own Richard Fitzgerald
2026-11-13 11:29 ` Richard Fitzgerald [this message]
2026-11-13 11:29 ` [PATCH 2/2] ASoC: cs35l56: Fix unwanted writes to ASP config registers Richard Fitzgerald

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261113112935.2926431-2-rf@opensource.cirrus.com \
    --to=rf@opensource.cirrus.com \
    --cc=broonie@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=patches@opensource.cirrus.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®