ASoC: fsl_esai: Add spin lock to protect reset, stop and start
xrun may happen at the end of stream, the
trigger->fsl_esai_trigger_stop maybe called in the middle of
fsl_esai_hw_reset, this may cause esai in wrong state
after stop, and there may be endless xrun interrupt.
This issue may also happen with trigger->fsl_esai_trigger_start.
So Add spin lock to lock those functions.
Fixes: 7ccafa2b38
("ASoC: fsl_esai: recover the channel swap after xrun")
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Acked-by: Nicolin Chen <nicoleotsuka@gmail.com>
Link: https://lore.kernel.org/r/52e92c4221a83e39a84a6cd92fc3d5479b44894c.1572252321.git.shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
b39eb1e250
Коммит
35dac62747
|
@ -33,6 +33,7 @@
|
||||||
* @fsysclk: system clock source to derive HCK, SCK and FS
|
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||||
* @task: tasklet to handle the reset operation
|
* @task: tasklet to handle the reset operation
|
||||||
|
* @lock: spin lock between hw_reset() and trigger()
|
||||||
* @fifo_depth: depth of tx/rx FIFO
|
* @fifo_depth: depth of tx/rx FIFO
|
||||||
* @slot_width: width of each DAI slot
|
* @slot_width: width of each DAI slot
|
||||||
* @slots: number of slots
|
* @slots: number of slots
|
||||||
|
@ -56,6 +57,7 @@ struct fsl_esai {
|
||||||
struct clk *fsysclk;
|
struct clk *fsysclk;
|
||||||
struct clk *spbaclk;
|
struct clk *spbaclk;
|
||||||
struct tasklet_struct task;
|
struct tasklet_struct task;
|
||||||
|
spinlock_t lock; /* Protect hw_reset and trigger */
|
||||||
u32 fifo_depth;
|
u32 fifo_depth;
|
||||||
u32 slot_width;
|
u32 slot_width;
|
||||||
u32 slots;
|
u32 slots;
|
||||||
|
@ -676,8 +678,10 @@ static void fsl_esai_hw_reset(unsigned long arg)
|
||||||
{
|
{
|
||||||
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
|
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
|
||||||
bool tx = true, rx = false, enabled[2];
|
bool tx = true, rx = false, enabled[2];
|
||||||
|
unsigned long lock_flags;
|
||||||
u32 tfcr, rfcr;
|
u32 tfcr, rfcr;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||||
/* Save the registers */
|
/* Save the registers */
|
||||||
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
|
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
|
||||||
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
|
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
|
||||||
|
@ -715,6 +719,8 @@ static void fsl_esai_hw_reset(unsigned long arg)
|
||||||
fsl_esai_trigger_start(esai_priv, tx);
|
fsl_esai_trigger_start(esai_priv, tx);
|
||||||
if (enabled[rx])
|
if (enabled[rx])
|
||||||
fsl_esai_trigger_start(esai_priv, rx);
|
fsl_esai_trigger_start(esai_priv, rx);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
|
@ -722,6 +728,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
{
|
{
|
||||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||||
|
unsigned long lock_flags;
|
||||||
|
|
||||||
esai_priv->channels[tx] = substream->runtime->channels;
|
esai_priv->channels[tx] = substream->runtime->channels;
|
||||||
|
|
||||||
|
@ -729,12 +736,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int 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:
|
||||||
|
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||||
fsl_esai_trigger_start(esai_priv, tx);
|
fsl_esai_trigger_start(esai_priv, tx);
|
||||||
|
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
|
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||||
fsl_esai_trigger_stop(esai_priv, tx);
|
fsl_esai_trigger_stop(esai_priv, tx);
|
||||||
|
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1002,6 +1013,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, esai_priv);
|
dev_set_drvdata(&pdev->dev, esai_priv);
|
||||||
|
|
||||||
|
spin_lock_init(&esai_priv->lock);
|
||||||
ret = fsl_esai_hw_init(esai_priv);
|
ret = fsl_esai_hw_init(esai_priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче