diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c --- a/sys/dev/sound/pci/hdspe-pcm.c +++ b/sys/dev/sound/pci/hdspe-pcm.c @@ -540,7 +540,8 @@ 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 @@ 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 @@ 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 @@ else row = hdspe_port_first(ports); } + + ch->position = ((pos + length) * 4) % HDSPE_CHANBUF_SIZE; } static int @@ -620,6 +645,8 @@ row = hdspe_port_first_row(ports); } + ch->position = 0; + return (0); } @@ -664,6 +691,7 @@ /* 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 --- a/sys/dev/sound/pci/hdspe.h +++ b/sys/dev/sound/pci/hdspe.h @@ -184,6 +184,7 @@ /* Buffer */ uint32_t *data; uint32_t size; + uint32_t position; /* Flags */ uint32_t run;