[alsa-devel] [PATCH] 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.
Signed-off-by: Dimitris Papastamos dp@opensource.wolfsonmicro.com Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/arizona.c | 20 ++++++++++++++++++-- 1 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index fb30c82..270e7ad 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -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)
On Wed, Jul 16, 2014 at 01:10:39PM +0100, Richard Fitzgerald wrote:
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?
On Thu, Jul 17, 2014 at 08:42:15PM +0100, Mark Brown wrote:
On Wed, Jul 16, 2014 at 01:10:39PM +0100, Richard Fitzgerald wrote:
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?
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
At Mon, 21 Jul 2014 10:01:22 +0100, Richard Fitzgerald wrote:
On Thu, Jul 17, 2014 at 08:42:15PM +0100, Mark Brown wrote:
On Wed, Jul 16, 2014 at 01:10:39PM +0100, Richard Fitzgerald wrote:
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?
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
On Mon, Jul 21, 2014 at 11:04:56AM +0200, Takashi Iwai wrote:
At Mon, 21 Jul 2014 10:01:22 +0100, Richard Fitzgerald wrote:
On Thu, Jul 17, 2014 at 08:42:15PM +0100, Mark Brown wrote:
On Wed, Jul 16, 2014 at 01:10:39PM +0100, Richard Fitzgerald wrote:
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?
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
That's a fair point.
On Mon, Jul 21, 2014 at 11:33:27AM +0100, Richard Fitzgerald wrote:
On Mon, Jul 21, 2014 at 11:04:56AM +0200, Takashi Iwai wrote:
Richard Fitzgerald wrote:
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.
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.
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.
Signed-off-by: Dimitris Papastamos dp@opensource.wolfsonmicro.com Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/arizona.c | 87 +++++++++++++++++++++++++++++++++++--------- 1 files changed, 69 insertions(+), 18 deletions(-)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index fb30c82..bd41ee4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -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)
On Tue, Jul 22, 2014 at 11:42:06AM +0100, Richard Fitzgerald wrote:
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.
participants (3)
-
Mark Brown
-
Richard Fitzgerald
-
Takashi Iwai