Commit 6b58a821 authored by Cliff Cai's avatar Cliff Cai Committed by Takashi Iwai

ALSA: ASoC Blackfin: fix bug - Audio Latency on AD1981 with MMAP enabled

With MMAP enabled (DMA mode) on the AD1981, there is +/- 250ms of delay between
writing data to alsa and audio starts coming out of the AD1981.

Copy more data to local buffer before starting DMA
Signed-off-by: default avatarCliff Cai <cliff.cai@analog.com>
Signed-off-by: default avatarBryan Wu <cooloney@kernel.org>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 5564b14b
...@@ -56,6 +56,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, ...@@ -56,6 +56,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
sport->tx_pos += runtime->period_size; sport->tx_pos += runtime->period_size;
if (sport->tx_pos >= runtime->buffer_size) if (sport->tx_pos >= runtime->buffer_size)
sport->tx_pos %= runtime->buffer_size; sport->tx_pos %= runtime->buffer_size;
sport->tx_delay_pos = sport->tx_pos;
} else { } else {
bf5xx_ac97_to_pcm( bf5xx_ac97_to_pcm(
(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
...@@ -72,7 +73,15 @@ static void bf5xx_dma_irq(void *data) ...@@ -72,7 +73,15 @@ static void bf5xx_dma_irq(void *data)
struct snd_pcm_substream *pcm = data; struct snd_pcm_substream *pcm = data;
#if defined(CONFIG_SND_MMAP_SUPPORT) #if defined(CONFIG_SND_MMAP_SUPPORT)
struct snd_pcm_runtime *runtime = pcm->runtime; struct snd_pcm_runtime *runtime = pcm->runtime;
struct sport_device *sport = runtime->private_data;
bf5xx_mmap_copy(pcm, runtime->period_size); bf5xx_mmap_copy(pcm, runtime->period_size);
if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (sport->once == 0) {
snd_pcm_period_elapsed(pcm);
bf5xx_mmap_copy(pcm, runtime->period_size);
sport->once = 1;
}
}
#endif #endif
snd_pcm_period_elapsed(pcm); snd_pcm_period_elapsed(pcm);
} }
...@@ -114,6 +123,10 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -114,6 +123,10 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
memset(runtime->dma_area, 0, runtime->buffer_size);
snd_pcm_lib_free_pages(substream); snd_pcm_lib_free_pages(substream);
return 0; return 0;
} }
...@@ -127,16 +140,11 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -127,16 +140,11 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
* SPORT working in TMD mode(include AC97). * SPORT working in TMD mode(include AC97).
*/ */
#if defined(CONFIG_SND_MMAP_SUPPORT) #if defined(CONFIG_SND_MMAP_SUPPORT)
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
* sizeof(struct ac97_frame) / 4;
/*clean up intermediate buffer*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
memset(sport->tx_dma_buf, 0, size);
sport_set_tx_callback(sport, bf5xx_dma_irq, substream); sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
runtime->period_size * sizeof(struct ac97_frame)); runtime->period_size * sizeof(struct ac97_frame));
} else { } else {
memset(sport->rx_dma_buf, 0, size);
sport_set_rx_callback(sport, bf5xx_dma_irq, substream); sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods, sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
runtime->period_size * sizeof(struct ac97_frame)); runtime->period_size * sizeof(struct ac97_frame));
...@@ -164,8 +172,12 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -164,8 +172,12 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
bf5xx_mmap_copy(substream, runtime->period_size);
snd_pcm_period_elapsed(substream);
sport->tx_delay_pos = 0;
sport_tx_start(sport); sport_tx_start(sport);
}
else else
sport_rx_start(sport); sport_rx_start(sport);
break; break;
...@@ -198,7 +210,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) ...@@ -198,7 +210,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
#if defined(CONFIG_SND_MMAP_SUPPORT) #if defined(CONFIG_SND_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
curr = sport->tx_pos; curr = sport->tx_delay_pos;
else else
curr = sport->rx_pos; curr = sport->rx_pos;
#else #else
...@@ -237,6 +249,21 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) ...@@ -237,6 +249,21 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
return ret; return ret;
} }
static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
sport->once = 0;
memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
} else
memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
return 0;
}
#ifdef CONFIG_SND_MMAP_SUPPORT #ifdef CONFIG_SND_MMAP_SUPPORT
static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma) struct vm_area_struct *vma)
...@@ -272,6 +299,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, ...@@ -272,6 +299,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
struct snd_pcm_ops bf5xx_pcm_ac97_ops = { struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
.open = bf5xx_pcm_open, .open = bf5xx_pcm_open,
.close = bf5xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params, .hw_params = bf5xx_pcm_hw_params,
.hw_free = bf5xx_pcm_hw_free, .hw_free = bf5xx_pcm_hw_free,
......
...@@ -128,7 +128,6 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) ...@@ -128,7 +128,6 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
int nextfrag = sport_tx_curr_frag(sport); int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite; struct ac97_frame *nextwrite;
sport_incfrag(sport, &nextfrag, 1);
sport_incfrag(sport, &nextfrag, 1); sport_incfrag(sport, &nextfrag, 1);
nextwrite = (struct ac97_frame *)(sport->tx_buf + \ nextwrite = (struct ac97_frame *)(sport->tx_buf + \
......
...@@ -123,6 +123,8 @@ struct sport_device { ...@@ -123,6 +123,8 @@ struct sport_device {
int rx_pos; int rx_pos;
unsigned int tx_buffer_size; unsigned int tx_buffer_size;
unsigned int rx_buffer_size; unsigned int rx_buffer_size;
int tx_delay_pos;
int once;
#endif #endif
void *private_data; void *private_data;
}; };
......
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