ASoC: stm32: spdifrx: fix race condition in irq handler
commit 86e1956af4c863d653136fd6e5694adf2054dbaa upstream.
When snd_pcm_stop() is called in interrupt routine,
substream context may have already been released.
Add protection on substream context.
Fixes: 03e4d5d56f
("ASoC: stm32: Add SPDIFRX support")
Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
Link: https://lore.kernel.org/r/20191204154333.7152-3-olivier.moysan@st.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
5063469c3d
commit
7835b07257
1 changed files with 19 additions and 5 deletions
|
@ -213,6 +213,7 @@
|
|||
* @slave_config: dma slave channel runtime config pointer
|
||||
* @phys_addr: SPDIFRX registers physical base address
|
||||
* @lock: synchronization enabling lock
|
||||
* @irq_lock: prevent race condition with IRQ on stream state
|
||||
* @cs: channel status buffer
|
||||
* @ub: user data buffer
|
||||
* @irq: SPDIFRX interrupt line
|
||||
|
@ -233,6 +234,7 @@ struct stm32_spdifrx_data {
|
|||
struct dma_slave_config slave_config;
|
||||
dma_addr_t phys_addr;
|
||||
spinlock_t lock; /* Sync enabling lock */
|
||||
spinlock_t irq_lock; /* Prevent race condition on stream state */
|
||||
unsigned char cs[SPDIFRX_CS_BYTES_NB];
|
||||
unsigned char ub[SPDIFRX_UB_BYTES_NB];
|
||||
int irq;
|
||||
|
@ -645,7 +647,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
|
|||
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
|
||||
{
|
||||
struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid;
|
||||
struct snd_pcm_substream *substream = spdifrx->substream;
|
||||
struct platform_device *pdev = spdifrx->pdev;
|
||||
unsigned int cr, mask, sr, imr;
|
||||
unsigned int flags;
|
||||
|
@ -713,14 +714,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
|
|||
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
|
||||
SPDIFRX_CR_SPDIFEN_MASK, cr);
|
||||
|
||||
if (substream)
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
|
||||
spin_lock(&spdifrx->irq_lock);
|
||||
if (spdifrx->substream)
|
||||
snd_pcm_stop(spdifrx->substream,
|
||||
SNDRV_PCM_STATE_DISCONNECTED);
|
||||
spin_unlock(&spdifrx->irq_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (err_xrun && substream)
|
||||
snd_pcm_stop_xrun(substream);
|
||||
spin_lock(&spdifrx->irq_lock);
|
||||
if (err_xrun && spdifrx->substream)
|
||||
snd_pcm_stop_xrun(spdifrx->substream);
|
||||
spin_unlock(&spdifrx->irq_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -729,9 +735,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&spdifrx->irq_lock, flags);
|
||||
spdifrx->substream = substream;
|
||||
spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
|
||||
|
||||
ret = clk_prepare_enable(spdifrx->kclk);
|
||||
if (ret)
|
||||
|
@ -807,8 +816,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdifrx->irq_lock, flags);
|
||||
spdifrx->substream = NULL;
|
||||
spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
|
||||
|
||||
clk_disable_unprepare(spdifrx->kclk);
|
||||
}
|
||||
|
||||
|
@ -912,6 +925,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
|
|||
spdifrx->pdev = pdev;
|
||||
init_completion(&spdifrx->cs_completion);
|
||||
spin_lock_init(&spdifrx->lock);
|
||||
spin_lock_init(&spdifrx->irq_lock);
|
||||
|
||||
platform_set_drvdata(pdev, spdifrx);
|
||||
|
||||
|
|
Loading…
Reference in a new issue