git: ee36e7faceaf - main - snd_hdspe(4): Only buffer_copy() audio data once.

From: Ruslan Bukin <br_at_FreeBSD.org>
Date: Sun, 31 Mar 2024 19:21:37 UTC
The branch main has been updated by br:

URL: https://cgit.FreeBSD.org/src/commit/?id=ee36e7faceafeef05c5e81654a1d8ec11d314894

commit ee36e7faceafeef05c5e81654a1d8ec11d314894
Author:     Florian Walpen <dev@submerge.ch>
AuthorDate: 2024-03-31 19:14:16 +0000
Commit:     Ruslan Bukin <br@FreeBSD.org>
CommitDate: 2024-03-31 19:14:16 +0000

    snd_hdspe(4): Only buffer_copy() audio data once.
    
    Instead of blindly copying two periods of audio data to and from DMA
    buffers, keep track of the writing position and derive the actual
    part of audio data that needs to be copied.
    This approximately halves the number of samples copied in total.
    
    Differential Revision:  https://reviews.freebsd.org/D44084
---
 sys/dev/sound/pci/hdspe-pcm.c | 46 ++++++++++++++++++++++++++++++++++---------
 sys/dev/sound/pci/hdspe.h     |  1 +
 2 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index 594a4f704028..0e78be113a66 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -540,7 +540,8 @@ buffer_copy(struct sc_chinfo *ch)
 	struct sc_pcminfo *scp;
 	struct sc_info *sc;
 	uint32_t row, ports;
-	unsigned int pos;
+	uint32_t dma_pos;
+	unsigned int pos, length, offset;
 	unsigned int n;
 	unsigned int adat_width, pcm_width;
 
@@ -558,13 +559,35 @@ buffer_copy(struct sc_chinfo *ch)
 	else
 		pcm_width = 8;
 
-	if (ch->dir == PCMDIR_PLAY)
-		pos = sndbuf_getreadyptr(ch->buffer);
-	else
-		pos = sndbuf_getfreeptr(ch->buffer);
+	/* Derive buffer position and length to be copied. */
+	if (ch->dir == PCMDIR_PLAY) {
+		/* Position per channel is n times smaller than PCM. */
+		pos = sndbuf_getreadyptr(ch->buffer) / n;
+		length = sndbuf_getready(ch->buffer) / n;
+		/* Copy no more than 2 periods in advance. */
+		if (length > (sc->period * 4 * 2))
+			length = (sc->period * 4 * 2);
+		/* Skip what was already copied last time. */
+		offset = (ch->position + HDSPE_CHANBUF_SIZE) - pos;
+		offset %= HDSPE_CHANBUF_SIZE;
+		if (offset <= length) {
+			pos = (pos + offset) % HDSPE_CHANBUF_SIZE;
+			length -= offset;
+		}
+	} else {
+		/* Position per channel is n times smaller than PCM. */
+		pos = sndbuf_getfreeptr(ch->buffer) / n;
+		/* Get DMA buffer write position. */
+		dma_pos = hdspe_read_2(sc, HDSPE_STATUS_REG);
+		dma_pos &= HDSPE_BUF_POSITION_MASK;
+		/* Copy what is newly available. */
+		length = (dma_pos + HDSPE_CHANBUF_SIZE) - pos;
+		length %= HDSPE_CHANBUF_SIZE;
+	}
 
-	pos /= 4; /* Bytes per sample. */
-	pos /= n; /* Destination buffer n-times smaller. */
+	/* Position and length in samples (4 bytes). */
+	pos /= 4;
+	length /= 4;
 
 	/* Iterate through rows of ports with contiguous slots. */
 	ports = ch->ports;
@@ -576,10 +599,10 @@ buffer_copy(struct sc_chinfo *ch)
 	while (row != 0) {
 		if (ch->dir == PCMDIR_PLAY)
 			buffer_mux_port(sc->pbuf, ch->data, row, ch->ports, pos,
-			    sc->period * 2, adat_width, pcm_width);
+			    length, adat_width, pcm_width);
 		else
 			buffer_demux_port(sc->rbuf, ch->data, row, ch->ports,
-			    pos, sc->period * 2, adat_width, pcm_width);
+			    pos, length, adat_width, pcm_width);
 
 		ports &= ~row;
 		if (pcm_width == adat_width)
@@ -587,6 +610,8 @@ buffer_copy(struct sc_chinfo *ch)
 		else
 			row = hdspe_port_first(ports);
 	}
+
+	ch->position = ((pos + length) * 4) % HDSPE_CHANBUF_SIZE;
 }
 
 static int
@@ -620,6 +645,8 @@ clean(struct sc_chinfo *ch)
 		row = hdspe_port_first_row(ports);
 	}
 
+	ch->position = 0;
+
 	return (0);
 }
 
@@ -664,6 +691,7 @@ hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
 	/* Allocate maximum buffer size. */
 	ch->size = HDSPE_CHANBUF_SIZE * hdspe_channel_count(ch->ports, 8);
 	ch->data = malloc(ch->size, M_HDSPE, M_NOWAIT);
+	ch->position = 0;
 
 	ch->buffer = b;
 	ch->channel = c;
diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h
index 26694f60ce58..de20462170e7 100644
--- a/sys/dev/sound/pci/hdspe.h
+++ b/sys/dev/sound/pci/hdspe.h
@@ -184,6 +184,7 @@ struct sc_chinfo {
 	/* Buffer */
 	uint32_t	*data;
 	uint32_t	size;
+	uint32_t	position;
 
 	/* Flags */
 	uint32_t	run;