[alsa-devel] [PATCH 1/1] ASoC Blackfin: fix bug - Audio Latency on AD1981 with MMAP enabled

Bryan Wu cooloney at kernel.org
Sat Sep 27 10:58:09 CEST 2008


From: Cliff Cai <cliff.cai at analog.com>

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 at analog.com>
Signed-off-by: Bryan Wu <cooloney at kernel.org>
---
 sound/soc/blackfin/bf5xx-ac97-pcm.c |   46 +++++++++++++++++++++++++++++-----
 sound/soc/blackfin/bf5xx-ac97.c     |    1 -
 sound/soc/blackfin/bf5xx-sport.h    |    2 +
 3 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 51f4907..d61febe 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -24,6 +24,10 @@
  * along with this program; if not, see the file COPYING, or write
  * to the Free Software Foundation, Inc.,
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Revision history
+ *      6th June 2008 Initial version
+ *      25th Sep 2008 fix latency when enable mmap
  */
 
 #include <linux/module.h>
@@ -56,6 +60,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
 		sport->tx_pos += runtime->period_size;
 		if (sport->tx_pos >= runtime->buffer_size)
 			sport->tx_pos %= runtime->buffer_size;
+		sport->tx_delay_pos = sport->tx_pos;
 	} else {
 		bf5xx_ac97_to_pcm(
 			(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
@@ -72,7 +77,15 @@ static void bf5xx_dma_irq(void *data)
 	struct snd_pcm_substream *pcm = data;
 #if defined(CONFIG_SND_MMAP_SUPPORT)
 	struct snd_pcm_runtime *runtime = pcm->runtime;
+	struct sport_device *sport = runtime->private_data;
 	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
 	snd_pcm_period_elapsed(pcm);
 }
@@ -114,6 +127,10 @@ static int bf5xx_pcm_hw_params(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);
 	return 0;
 }
@@ -127,16 +144,11 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
 	 * SPORT working in TMD mode(include AC97).
 	 */
 #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) {
-		memset(sport->tx_dma_buf, 0, size);
 		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
 		sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
 			runtime->period_size * sizeof(struct ac97_frame));
 	} else {
-		memset(sport->rx_dma_buf, 0, size);
 		sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
 		sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
 			runtime->period_size * sizeof(struct ac97_frame));
@@ -164,8 +176,12 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	pr_debug("%s enter\n", __func__);
 	switch (cmd) {
 	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);
+		}
 		else
 			sport_rx_start(sport);
 		break;
@@ -198,7 +214,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
 
 #if defined(CONFIG_SND_MMAP_SUPPORT)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		curr = sport->tx_pos;
+		curr = sport->tx_delay_pos;
 	else
 		curr = sport->rx_pos;
 #else
@@ -237,6 +253,21 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
 	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
 static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
 	struct vm_area_struct *vma)
@@ -272,6 +303,7 @@ static	int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 
 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
 	.open		= bf5xx_pcm_open,
+	.close		= bf5xx_pcm_close,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.hw_params	= bf5xx_pcm_hw_params,
 	.hw_free	= bf5xx_pcm_hw_free,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index c782e31..5e5aafb 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -129,7 +129,6 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 	struct ac97_frame *nextwrite;
 
 	sport_incfrag(sport, &nextfrag, 1);
-	sport_incfrag(sport, &nextfrag, 1);
 
 	nextwrite = (struct ac97_frame *)(sport->tx_buf + \
 			nextfrag * sport->tx_fragsize);
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index 4c16345..fcadcc0 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -123,6 +123,8 @@ struct sport_device {
 	int rx_pos;
 	unsigned int tx_buffer_size;
 	unsigned int rx_buffer_size;
+	int tx_delay_pos;
+	int once;
 #endif
 	void *private_data;
 };
-- 
1.5.6



More information about the Alsa-devel mailing list