Skip to contentSkip to conversation

ASoC: arizona: Disable AIF TX/RX before configuring it

PATCH Superseded Richard Fitzgerald 3 participants 8 messages Jul 16, 2014 – Jul 22, 2014

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ASoC: arizona: Disable AIF TX/RX before configuring it

If we don't disable the AIF TX/RX then we may fall into a situation where the new AIF settings are ignored by the device. For example, this problem manifests when switching between different sample rates.

---
 sound/soc/codecs/arizona.c |   20 ++++++++++++++++++--
 1 files changed, 18 insertions(+), 2 deletions(-)
sound/soc/codecs/arizona.c +18 -2
index fb30c82..270e7ad 100644
@@ -1249,6 +1249,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 	int tdm_width = arizona->tdm_width[dai->id - 1];
 	int tdm_slots = arizona->tdm_slots[dai->id - 1];
 	int bclk, lrclk, wl, frame, bclk_target;
+	unsigned int aif_tx_state, aif_rx_state;
 
 	if (params_rate(params) % 8000)
 		rates = &arizona_44k1_bclk_rates[0];
@@ -1299,9 +1300,18 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 	wl = snd_pcm_format_width(params_format(params));
 	frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
 
+	/* Save AIF TX/RX state */
+	aif_tx_state = snd_soc_read(codec, base + ARIZONA_AIF_TX_ENABLES);
+	aif_rx_state = snd_soc_read(codec, base + ARIZONA_AIF_RX_ENABLES);
+	/* Disable AIF TX/RX before configuring it */
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_ENABLES,
+			    0xff, 0x0);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_ENABLES,
+			    0xff, 0x0);
+
 	ret = arizona_hw_params_rate(substream, params, dai);
 	if (ret != 0)
-		return ret;
+		goto restore_aif;
 
 	regmap_update_bits_async(arizona->regmap,
 				 base + ARIZONA_AIF_BCLK_CTRL,
@@ -1320,7 +1330,13 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 			   ARIZONA_AIF1RX_WL_MASK |
 			   ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
 
-	return 0;
+restore_aif:
+	/* Restore AIF TX/RX state */
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_ENABLES,
+			    0xff, aif_tx_state);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_ENABLES,
+			    0xff, aif_rx_state);
+	return ret;
 }
 
 static const char *arizona_dai_clk_str(int clk_id)
Signature · 1 line
1.7.2.5
maintainer
#1 lore raw
Quoted 4 lines · Richard Fitzgerald

If we don't disable the AIF TX/RX then we may fall into a situation where the new AIF settings are ignored by the device. For example, this problem manifests when switching between different sample rates.

So, what this does is momentarily disable the AIF when reconfiguring. That will glitch any running audio, making me wonder if the driver shouldn't be returning an error or at least complaining if it has to reconfigure instead. What's the use case where this might get triggered?

#2 lore raw
Quoted 7 lines · Mark Brown
Quoted 4 lines · Richard Fitzgerald

If we don't disable the AIF TX/RX then we may fall into a situation where the new AIF settings are ignored by the device. For example, this problem manifests when switching between different sample rates.

Quoted 5 lines

So, what this does is momentarily disable the AIF when reconfiguring. That will glitch any running audio, making me wonder if the driver shouldn't be returning an error or at least complaining if it has to reconfigure instead. What's the use case where this might get triggered?

The case being fixed is like this:

aplay 48kHz.wav; aplay 96kHz.wav

The second open happens before pmdown_time so the AIF is still enabled. Writes to the AIF config registers only take effect if the AIF is disabled. Without this patch, the 96kHz.wav will play at 48kHz

#3 lore raw

At Mon, 21 Jul 2014 10:01:22 +0100,

Quoted 15 lines · Richard Fitzgerald
Quoted 7 lines · Mark Brown
Quoted 4 lines · Richard Fitzgerald

If we don't disable the AIF TX/RX then we may fall into a situation where the new AIF settings are ignored by the device. For example, this problem manifests when switching between different sample rates.

Quoted 5 lines

So, what this does is momentarily disable the AIF when reconfiguring. That will glitch any running audio, making me wonder if the driver shouldn't be returning an error or at least complaining if it has to reconfigure instead. What's the use case where this might get triggered?

Quoted 7 lines

The case being fixed is like this:

aplay 48kHz.wav; aplay 96kHz.wav

The second open happens before pmdown_time so the AIF is still enabled. Writes to the AIF config registers only take effect if the AIF is disabled. Without this patch, the 96kHz.wav will play at 48kHz

If the glitch really matters, the driver can minimize by checking the change of sample rate and doing temporary turn on/off only when required.

Takashi

#4 lore raw
Quoted 25 lines · Takashi Iwai

At Mon, 21 Jul 2014 10:01:22 +0100,

Quoted 15 lines · Richard Fitzgerald
Quoted 7 lines · Mark Brown
Quoted 4 lines · Richard Fitzgerald

If we don't disable the AIF TX/RX then we may fall into a situation where the new AIF settings are ignored by the device. For example, this problem manifests when switching between different sample rates.

Quoted 5 lines

So, what this does is momentarily disable the AIF when reconfiguring. That will glitch any running audio, making me wonder if the driver shouldn't be returning an error or at least complaining if it has to reconfigure instead. What's the use case where this might get triggered?

Quoted 7 lines

The case being fixed is like this:

aplay 48kHz.wav; aplay 96kHz.wav

The second open happens before pmdown_time so the AIF is still enabled. Writes to the AIF config registers only take effect if the AIF is disabled. Without this patch, the 96kHz.wav will play at 48kHz

Quoted 6 lines

If the glitch really matters, the driver can minimize by checking the change of sample rate and doing temporary turn on/off only when required.

Takashi

That's a fair point.

maintainer
#5 lore raw
Quoted 11 lines · Richard Fitzgerald
Quoted 9 lines · Takashi Iwai
Quoted 7 lines · Richard Fitzgerald

The case being fixed is like this:

aplay 48kHz.wav; aplay 96kHz.wav

The second open happens before pmdown_time so the AIF is still enabled. Writes to the AIF config registers only take effect if the AIF is disabled. Without this patch, the 96kHz.wav will play at 48kHz

Quoted 5 lines
Quoted 3 lines

If the glitch really matters, the driver can minimize by checking the change of sample rate and doing temporary turn on/off only when required.

That's a fair point.

The case I was particularly worrying about here was simultaneous playback and capture - we won't be changing rates here since these devices currently require symmetry which is enforced via constraints but we are likely to at least be starting multiple streams close to each other frequently.

#6 lore raw

PATCH v2 1/1 ASoC: arizona: Disable AIF TX/RX before configuring it

In reply to Richard Fitzgerald

Changes to the AIF configuration registers only take effect when the AIF is disabled. If the configuration is being changed from the previous setup, temporarily disable the AIF.

---
 sound/soc/codecs/arizona.c |   87 +++++++++++++++++++++++++++++++++++---------
 1 files changed, 69 insertions(+), 18 deletions(-)
sound/soc/codecs/arizona.c +69 -18
index fb30c82..bd41ee4 100644
@@ -1234,6 +1234,27 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+				    int base, int bclk, int lrclk, int frame)
+{
+	int val;
+
+	val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL);
+	if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
+		return true;
+
+	val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE);
+	if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+		return true;
+
+	val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+	if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
+			     ARIZONA_AIF1TX_SLOT_LEN_MASK)))
+		return true;
+
+	return false;
+}
+
 static int arizona_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params,
 			     struct snd_soc_dai *dai)
@@ -1249,6 +1270,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 	int tdm_width = arizona->tdm_width[dai->id - 1];
 	int tdm_slots = arizona->tdm_slots[dai->id - 1];
 	int bclk, lrclk, wl, frame, bclk_target;
+	bool reconfig;
+	unsigned int aif_tx_state, aif_rx_state;
 
 	if (params_rate(params) % 8000)
 		rates = &arizona_44k1_bclk_rates[0];
@@ -1299,28 +1322,56 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 	wl = snd_pcm_format_width(params_format(params));
 	frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
 
+	reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+
+	if (reconfig) {
+		/* Save AIF TX/RX state */
+		aif_tx_state = snd_soc_read(codec,
+					    base + ARIZONA_AIF_TX_ENABLES);
+		aif_rx_state = snd_soc_read(codec,
+					    base + ARIZONA_AIF_RX_ENABLES);
+		/* Disable AIF TX/RX before reconfiguring it */
+		regmap_update_bits_async(arizona->regmap,
+				    base + ARIZONA_AIF_TX_ENABLES, 0xff, 0x0);
+		regmap_update_bits(arizona->regmap,
+				    base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+	}
+
 	ret = arizona_hw_params_rate(substream, params, dai);
 	if (ret != 0)
-		return ret;
+		goto restore_aif;
 
-	regmap_update_bits_async(arizona->regmap,
-				 base + ARIZONA_AIF_BCLK_CTRL,
-				 ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
-	regmap_update_bits_async(arizona->regmap,
-				 base + ARIZONA_AIF_TX_BCLK_RATE,
-				 ARIZONA_AIF1TX_BCPF_MASK, lrclk);
-	regmap_update_bits_async(arizona->regmap,
-				 base + ARIZONA_AIF_RX_BCLK_RATE,
-				 ARIZONA_AIF1RX_BCPF_MASK, lrclk);
-	regmap_update_bits_async(arizona->regmap,
-				 base + ARIZONA_AIF_FRAME_CTRL_1,
-				 ARIZONA_AIF1TX_WL_MASK |
-				 ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
-	regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FRAME_CTRL_2,
-			   ARIZONA_AIF1RX_WL_MASK |
-			   ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+	if (reconfig) {
+		regmap_update_bits_async(arizona->regmap,
+					 base + ARIZONA_AIF_BCLK_CTRL,
+					 ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
+		regmap_update_bits_async(arizona->regmap,
+					 base + ARIZONA_AIF_TX_BCLK_RATE,
+					 ARIZONA_AIF1TX_BCPF_MASK, lrclk);
+		regmap_update_bits_async(arizona->regmap,
+					 base + ARIZONA_AIF_RX_BCLK_RATE,
+					 ARIZONA_AIF1RX_BCPF_MASK, lrclk);
+		regmap_update_bits_async(arizona->regmap,
+					 base + ARIZONA_AIF_FRAME_CTRL_1,
+					 ARIZONA_AIF1TX_WL_MASK |
+					 ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
+		regmap_update_bits(arizona->regmap,
+				   base + ARIZONA_AIF_FRAME_CTRL_2,
+				   ARIZONA_AIF1RX_WL_MASK |
+				   ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+	}
 
-	return 0;
+restore_aif:
+	if (reconfig) {
+		/* Restore AIF TX/RX state */
+		regmap_update_bits_async(arizona->regmap,
+					 base + ARIZONA_AIF_TX_ENABLES,
+					 0xff, aif_tx_state);
+		regmap_update_bits(arizona->regmap,
+				   base + ARIZONA_AIF_RX_ENABLES,
+				   0xff, aif_rx_state);
+	}
+	return ret;
 }
 
 static const char *arizona_dai_clk_str(int clk_id)
Signature · 1 line
1.7.2.5
maintainer Applied
#7 lore raw
Quoted 4 lines · Richard Fitzgerald

Changes to the AIF configuration registers only take effect when the AIF is disabled. If the configuration is being changed from the previous setup, temporarily disable the AIF.

Applied, thanks.