Fix: try to stop aif clock in thread environment
This commit is contained in:
parent
0965139ead
commit
3392bf394b
3 changed files with 72 additions and 32 deletions
12
ac101.c
12
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) {
|
static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
|
||||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
/* spin_lock move to machine trigger */
|
/* 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);
|
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
|
#if _MASTER_MULTI_CODEC == _MASTER_AC101
|
||||||
static int ac101_set_clock(int y_start_n_stop) {
|
static int ac101_set_clock(int y_start_n_stop) {
|
||||||
|
int r;
|
||||||
|
|
||||||
if (y_start_n_stop) {
|
if (y_start_n_stop) {
|
||||||
/* enable global clock */
|
/* 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 {
|
} else {
|
||||||
/* disable global clock */
|
/* disable global clock */
|
||||||
static_ac10x->aif1_clken = 1;
|
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
|
#endif
|
||||||
|
|
||||||
|
|
42
ac108.c
42
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) {
|
static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) {
|
||||||
|
int r = 0;
|
||||||
u8 i;
|
u8 i;
|
||||||
|
|
||||||
for (i = 0; i < ac10x->codec_cnt; 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) {
|
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.
|
* due to miss channels order in cpu_dai, we meed defer the clock starting.
|
||||||
*/
|
*/
|
||||||
static int ac108_set_clock(int y_start_n_stop) {
|
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);
|
dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
|
||||||
|
|
||||||
/* spin_lock move to machine trigger */
|
/* spin_lock move to machine trigger */
|
||||||
|
|
||||||
if (y_start_n_stop) {
|
if (y_start_n_stop && ac10x->sysclk_en == 0) {
|
||||||
if (ac10x->sysclk_en == 0) {
|
|
||||||
|
|
||||||
/* enable lrck clock */
|
/* enable lrck clock */
|
||||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
if (r & (0x01 << BCLK_IOEN)) {
|
if (reg & (0x01 << BCLK_IOEN)) {
|
||||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x03 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*0x10: PLL Common voltage enable, PLL enable */
|
/*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);
|
0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
|
||||||
/* enable global clock */
|
/* 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;
|
ac10x->sysclk_en = 1UL;
|
||||||
}
|
} else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
|
||||||
} else if (ac10x->sysclk_en != 0) {
|
|
||||||
/* disable global clock */
|
/* 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 */
|
/*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);
|
0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
|
||||||
|
|
||||||
/* disable lrck clock if it's enabled */
|
/* disable lrck clock if it's enabled */
|
||||||
ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
|
ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]);
|
||||||
if (r & (0x01 << LRCK_IOEN)) {
|
if (reg & (0x01 << LRCK_IOEN)) {
|
||||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
|
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,
|
static int ac108_prepare(struct snd_pcm_substream *substream,
|
||||||
|
|
|
@ -48,6 +48,9 @@ struct seeed_card_data {
|
||||||
unsigned channels_capture_override;
|
unsigned channels_capture_override;
|
||||||
struct snd_soc_dai_link *dai_link;
|
struct snd_soc_dai_link *dai_link;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
struct work_struct work_codec_clk;
|
||||||
|
#define TRY_STOP_MAX 3
|
||||||
|
int try_stop;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct seeed_card_info {
|
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);
|
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)
|
static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
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,
|
__FUNCTION__, snd_pcm_stream_str(substream), cmd,
|
||||||
dai->playback_active, dai->capture_active);
|
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) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
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_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1);
|
||||||
if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1);
|
if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1);
|
||||||
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
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) {
|
if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
break;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,6 +589,8 @@ static int seeed_voice_card_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
spin_lock_init(&priv->lock);
|
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);
|
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -571,7 +604,10 @@ err:
|
||||||
static int seeed_voice_card_remove(struct platform_device *pdev)
|
static int seeed_voice_card_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct snd_soc_card *card = platform_get_drvdata(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);
|
return asoc_simple_card_clean_reference(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue