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: Cliff Cai <cliff.cai@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
5564b14b88
commit
6b58a82121
@ -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)
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
#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)
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
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 {
|
|||||||
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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user