Commit 6deab6fe authored by Ezequiel Garcia's avatar Ezequiel Garcia Committed by Mauro Carvalho Chehab

[media] tw686x: audio: Prevent hw param changes while busy

Audio hw params are shared across all DMA channels,
so if the user changes any of these while any DMA channel is
enabled, it will impact the enabled channels, potentially causing
serious instability issues.

This commit avoids such situation, by preventing any hw param
change (on any DMA channel) if any other DMA audio channel is capturing.
Signed-off-by: default avatarEzequiel Garcia <ezequiel@vanguardiasur.com.ar>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 447d7c32
...@@ -94,10 +94,8 @@ static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss) ...@@ -94,10 +94,8 @@ static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss)
/* /*
* Audio parameters are global and shared among all * Audio parameters are global and shared among all
* capture channels. The driver makes no effort to prevent * capture channels. The driver prevents changes to
* any modifications. User is free change the audio rate, * the parameters if any audio channel is capturing.
* or period size, thus changing parameters for all capture
* sub-devices.
*/ */
static const struct snd_pcm_hardware tw686x_capture_hw = { static const struct snd_pcm_hardware tw686x_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | .info = (SNDRV_PCM_INFO_MMAP |
...@@ -154,6 +152,14 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) ...@@ -154,6 +152,14 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
int i; int i;
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
/*
* Given the audio parameters are global (i.e. shared across
* DMA channels), we need to check new params are allowed.
*/
if (((dev->audio_rate != rt->rate) ||
(dev->period_size != period_size)) && dev->audio_enabled)
goto err_audio_busy;
tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
...@@ -210,6 +216,10 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) ...@@ -210,6 +216,10 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
spin_unlock_irqrestore(&ac->lock, flags); spin_unlock_irqrestore(&ac->lock, flags);
return 0; return 0;
err_audio_busy:
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
} }
static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
...@@ -223,6 +233,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) ...@@ -223,6 +233,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (ac->curr_bufs[0] && ac->curr_bufs[1]) { if (ac->curr_bufs[0] && ac->curr_bufs[1]) {
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
dev->audio_enabled = 1;
tw686x_enable_channel(dev, tw686x_enable_channel(dev,
AUDIO_CHANNEL_OFFSET + ac->ch); AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
...@@ -235,6 +246,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) ...@@ -235,6 +246,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
dev->audio_enabled = 0;
tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
......
...@@ -141,6 +141,7 @@ struct tw686x_dev { ...@@ -141,6 +141,7 @@ struct tw686x_dev {
/* Per-device audio parameters */ /* Per-device audio parameters */
int audio_rate; int audio_rate;
int period_size; int period_size;
int audio_enabled;
struct timer_list dma_delay_timer; struct timer_list dma_delay_timer;
u32 pending_dma_en; /* must be protected by lock */ u32 pending_dma_en; /* must be protected by lock */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment