From 3392bf394b32492223a8b50d653cbc829e18d60c Mon Sep 17 00:00:00 2001 From: turmary Date: Wed, 7 Nov 2018 18:33:55 -1000 Subject: [PATCH] Fix: try to stop aif clock in thread environment --- ac101.c | 12 +++++++----- ac108.c | 42 ++++++++++++++++++++------------------- seeed-voicecard.c | 50 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/ac101.c b/ac101.c index 14004a1..1a7bb78 100644 --- a/ac101.c +++ b/ac101.c @@ -644,7 +644,7 @@ static int ac101_sysclk_started(void) { static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) { struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); - int ret; + int ret = 0; /* spin_lock move to machine trigger */ @@ -687,7 +687,7 @@ static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) { AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU, SND_SOC_DAPM_POST_PMD); - return 0; + return ret; } /** @@ -1240,15 +1240,17 @@ int ac101_audio_startup(struct snd_pcm_substream *substream, #if _MASTER_MULTI_CODEC == _MASTER_AC101 static int ac101_set_clock(int y_start_n_stop) { + int r; + if (y_start_n_stop) { /* enable global clock */ - ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1); + r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_PRE_PMU, 1); } else { /* disable global clock */ static_ac10x->aif1_clken = 1; - ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD, 0); + r = ac101_aif1clk(static_ac10x->codec, SND_SOC_DAPM_POST_PMD, 0); } - return 0; + return r; } #endif diff --git a/ac108.c b/ac108.c index e569e5b..e9782de 100644 --- a/ac108.c +++ b/ac108.c @@ -453,11 +453,13 @@ static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) { } static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) { + int r = 0; u8 i; + for (i = 0; i < ac10x->codec_cnt; i++) { - ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]); + r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]); } - return 0; + return r; } static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -986,46 +988,46 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { * due to miss channels order in cpu_dai, we meed defer the clock starting. */ static int ac108_set_clock(int y_start_n_stop) { - u8 r; + u8 reg; + int ret = 0; dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop); /* spin_lock move to machine trigger */ - if (y_start_n_stop) { - if (ac10x->sysclk_en == 0) { - + if (y_start_n_stop && ac10x->sysclk_en == 0) { /* enable lrck clock */ - ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); - if (r & (0x01 << BCLK_IOEN)) { - ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); + ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); + if (reg & (0x01 << BCLK_IOEN)) { + ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); } /*0x10: PLL Common voltage enable, PLL enable */ - ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, + ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x); /* enable global clock */ - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x); + ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x); ac10x->sysclk_en = 1UL; - } - } else if (ac10x->sysclk_en != 0) { + } else if (!y_start_n_stop && ac10x->sysclk_en != 0) { /* disable global clock */ - ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x); + ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x); /*0x10: PLL Common voltage disable, PLL disable */ - ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, + ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x); /* disable lrck clock if it's enabled */ - ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); - if (r & (0x01 << LRCK_IOEN)) { - ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); + ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); + if (reg & (0x01 << LRCK_IOEN)) { + ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); + } + if (!ret) { + ac10x->sysclk_en = 0UL; } - ac10x->sysclk_en = 0UL; } - return 0; + return ret; } static int ac108_prepare(struct snd_pcm_substream *substream, diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 73b856f..174e9f0 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -48,6 +48,9 @@ struct seeed_card_data { unsigned channels_capture_override; struct snd_soc_dai_link *dai_link; spinlock_t lock; + struct work_struct work_codec_clk; + #define TRY_STOP_MAX 3 + int try_stop; }; struct seeed_card_info { @@ -161,6 +164,27 @@ int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) { } EXPORT_SYMBOL(seeed_voice_card_register_set_clock); +/* + * work_cb_codec_clk: clear audio codec inner clock. + */ +static void work_cb_codec_clk(struct work_struct *work) +{ + struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk); + int r = 0; + + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) { + r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); + } + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) { + r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); + } + + if (r && priv->try_stop++ < TRY_STOP_MAX) { + if (0 != schedule_work(&priv->work_codec_clk)) {} + } + return; +} + static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -173,15 +197,16 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd __FUNCTION__, snd_pcm_stream_str(substream), cmd, dai->playback_active, dai->capture_active); - /* I know it will degrades performance, but I have no choice */ - spin_lock_irqsave(&priv->lock, flags); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (cancel_work_sync(&priv->work_codec_clk) != 0) {} + /* I know it will degrades performance, but I have no choice */ + spin_lock_irqsave(&priv->lock, flags); if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1); if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1); + spin_unlock_irqrestore(&priv->lock, flags); break; case SNDRV_PCM_TRIGGER_STOP: @@ -191,15 +216,21 @@ static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { break; } - if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); - if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); + + /* interrupt environment */ + if (in_irq() || in_nmi() || in_serving_softirq()) { + priv->try_stop = 0; + if (0 != schedule_work(&priv->work_codec_clk)) { + } + } else { + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); + } break; default: ret = -EINVAL; } - spin_unlock_irqrestore(&priv->lock, flags); - return ret; } @@ -558,6 +589,8 @@ static int seeed_voice_card_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); + INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk); + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); if (ret >= 0) return ret; @@ -571,7 +604,10 @@ err: static int seeed_voice_card_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct seeed_card_data *priv = snd_soc_card_get_drvdata(card); + if (cancel_work_sync(&priv->work_codec_clk) != 0) { + } return asoc_simple_card_clean_reference(card); }