git: 5355c97e129a - main - snd_hdsp: Use busdma for DMA buffers
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 02 Nov 2026 16:10:40 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=5355c97e129a004f013570be0b7a99c8610e1757
commit 5355c97e129a004f013570be0b7a99c8610e1757
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-11-02 16:09:33 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-11-02 16:09:55 +0000
snd_hdsp: Use busdma for DMA buffers
Program DMA buffer registers with the bus addresses returned by busdma.
Require one full-size contiguous DMA segment per direction and validate
its alignment and 32-bit address range.
Synchronize buffer ownership around playback, capture, clearing, and
teardown. Disable channels before clearing their DMA buffers, and unwind
partially allocated resources on failure.
This mirrors snd_hdspe's DMA handling, adapted to HDSP's single base
address per direction.
MFC after: 3 days
Sponsored by: BBOX.io
---
sys/dev/sound/pci/hdsp-pcm.c | 24 +++++++--
sys/dev/sound/pci/hdsp.c | 113 ++++++++++++++++++++++++++++++++-----------
sys/dev/sound/pci/hdsp.h | 3 ++
3 files changed, 107 insertions(+), 33 deletions(-)
diff --git a/sys/dev/sound/pci/hdsp-pcm.c b/sys/dev/sound/pci/hdsp-pcm.c
index 24969f0e22b0..ad5a040398db 100644
--- a/sys/dev/sound/pci/hdsp-pcm.c
+++ b/sys/dev/sound/pci/hdsp-pcm.c
@@ -564,15 +564,14 @@ buffer_copy(struct sc_chinfo *ch)
length -= offset;
}
} else {
- /* Buffer position scaled down to a single channel. */
+ /* Snapshot the write position before synchronizing the buffer. */
pos = sndbuf_getfreeptr(ch->buffer) / channels;
- /* Get DMA buffer write position. */
dma_pos = hdsp_read_2(sc, HDSP_STATUS_REG);
dma_pos &= HDSP_BUF_POSITION_MASK;
dma_pos %= buffer_size;
- /* Copy what is newly available. */
length = (dma_pos + buffer_size) - pos;
length %= buffer_size;
+ bus_dmamap_sync(sc->dmat, sc->rmap, BUS_DMASYNC_POSTREAD);
}
/* Position and length in samples (4 bytes). */
@@ -607,6 +606,12 @@ buffer_copy(struct sc_chinfo *ch)
row = hdsp_slot_first_row(slots);
}
+ if (ch->dir == PCMDIR_PLAY && length > 0) {
+ bus_dmamap_sync(sc->dmat, sc->pmap, BUS_DMASYNC_PREWRITE);
+ } else if (ch->dir == PCMDIR_REC) {
+ bus_dmamap_sync(sc->dmat, sc->rmap, BUS_DMASYNC_PREREAD);
+ }
+
ch->position = ((pos + length) * 4) % buffer_size;
}
@@ -626,6 +631,11 @@ clean(struct sc_chinfo *ch)
if (ch->dir == PCMDIR_PLAY)
buf = sc->pbuf;
+ if (ch->dir == PCMDIR_PLAY)
+ bus_dmamap_sync(sc->dmat, sc->pmap, BUS_DMASYNC_POSTWRITE);
+ else
+ bus_dmamap_sync(sc->dmat, sc->rmap, BUS_DMASYNC_POSTREAD);
+
/* Iterate through all of the channel's slots. */
slots = hdsp_port_slot_map(ch->ports, sc->speed);
slot = hdsp_slot_first(slots);
@@ -638,6 +648,12 @@ clean(struct sc_chinfo *ch)
slot = hdsp_slot_first(slots);
}
+ if (ch->dir == PCMDIR_PLAY)
+ bus_dmamap_sync(sc->dmat, sc->pmap, BUS_DMASYNC_PREWRITE);
+ else
+ bus_dmamap_sync(sc->dmat, sc->rmap,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
ch->position = 0;
return (0);
@@ -775,8 +791,8 @@ hdspchan_trigger(kobj_t obj, void *data, int go)
#if 0
device_printf(scp->dev, "hdspchan_trigger(): stop or abort\n");
#endif
- clean(ch);
hdspchan_enable(ch, 0);
+ clean(ch);
hdsp_stop_audio(sc);
break;
diff --git a/sys/dev/sound/pci/hdsp.c b/sys/dev/sound/pci/hdsp.c
index b52b53231e50..3c92eb3e319a 100644
--- a/sys/dev/sound/pci/hdsp.c
+++ b/sys/dev/sound/pci/hdsp.c
@@ -127,17 +127,43 @@ hdsp_intr(void *p)
mtx_unlock(&sc->lock);
}
+struct hdsp_dma_map {
+ uint32_t addr;
+ int error;
+};
+
static void
-hdsp_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+hdsp_dma_map_buffer(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
-#if 0
- device_printf(sc->dev, "hdsp_dmapsetmap()\n");
-#endif
+ struct hdsp_dma_map *map;
+
+ map = arg;
+ map->error = error;
+ if (error != 0)
+ return;
+
+ /* The hardware accepts one contiguous DMA buffer per direction. */
+ if (nseg != 1 || segs[0].ds_len != HDSP_DMASEGSIZE) {
+ map->error = EFBIG;
+ return;
+ }
+ if (segs[0].ds_addr % 4 != 0 ||
+ segs[0].ds_addr > BUS_SPACE_MAXADDR_32BIT - (HDSP_DMASEGSIZE - 1)) {
+ map->error = EINVAL;
+ return;
+ }
+ map->addr = (uint32_t)segs[0].ds_addr;
}
static int
hdsp_alloc_resources(struct sc_info *sc)
{
+ struct hdsp_dma_map map;
+ bool pmap_loaded, rmap_loaded;
+ int error;
+
+ pmap_loaded = false;
+ rmap_loaded = false;
/* Allocate resource. */
sc->csid = PCIR_BAR(0);
@@ -161,10 +187,10 @@ hdsp_alloc_resources(struct sc_info *sc)
bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
NULL, hdsp_intr, sc, &sc->ih)) {
device_printf(sc->dev, "Unable to alloc interrupt resource.\n");
- return (ENXIO);
+ goto fail;
}
- /* Allocate DMA resources. */
+ /* Allocate one contiguous DMA segment for each direction. */
if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev),
/*alignment*/4,
/*boundary*/0,
@@ -172,61 +198,87 @@ hdsp_alloc_resources(struct sc_info *sc)
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL,
/*filterarg*/NULL,
- /*maxsize*/2 * HDSP_DMASEGSIZE,
- /*nsegments*/2,
+ /*maxsize*/HDSP_DMASEGSIZE,
+ /*nsegments*/1,
/*maxsegsz*/HDSP_DMASEGSIZE,
/*flags*/0,
/*lockfunc*/NULL,
/*lockarg*/NULL,
/*dmatag*/&sc->dmat) != 0) {
device_printf(sc->dev, "Unable to create dma tag.\n");
- return (ENXIO);
+ goto fail;
}
sc->bufsize = HDSP_DMASEGSIZE;
/* pbuf (play buffer). */
- if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK,
- &sc->pmap)) {
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->pmap)) {
device_printf(sc->dev, "Can't alloc pbuf.\n");
- return (ENXIO);
+ goto fail;
}
- if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize,
- hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ map.error = 0;
+ error = bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize,
+ hdsp_dma_map_buffer, &map, BUS_DMA_NOWAIT);
+ if (error == 0)
+ pmap_loaded = true;
+ if (error != 0 || map.error != 0) {
device_printf(sc->dev, "Can't load pbuf.\n");
- return (ENXIO);
+ goto fail;
}
+ sc->paddr = map.addr;
/* rbuf (rec buffer). */
- if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK,
- &sc->rmap)) {
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rmap)) {
device_printf(sc->dev, "Can't alloc rbuf.\n");
- return (ENXIO);
+ goto fail;
}
- if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize,
- hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ map.error = 0;
+ error = bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize,
+ hdsp_dma_map_buffer, &map, BUS_DMA_NOWAIT);
+ if (error == 0)
+ rmap_loaded = true;
+ if (error != 0 || map.error != 0) {
device_printf(sc->dev, "Can't load rbuf.\n");
- return (ENXIO);
+ goto fail;
}
+ sc->raddr = map.addr;
- bzero(sc->pbuf, sc->bufsize);
- bzero(sc->rbuf, sc->bufsize);
+ /* Synchronize the buffers before the first DMA cycle. */
+ bus_dmamap_sync(sc->dmat, sc->pmap, BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(sc->dmat, sc->rmap, BUS_DMASYNC_PREREAD);
return (0);
+
+fail:
+ if (sc->ih != NULL)
+ bus_teardown_intr(sc->dev, sc->irq, sc->ih);
+ if (rmap_loaded)
+ bus_dmamap_unload(sc->dmat, sc->rmap);
+ if (sc->rbuf != NULL)
+ bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+ if (pmap_loaded)
+ bus_dmamap_unload(sc->dmat, sc->pmap);
+ if (sc->pbuf != NULL)
+ bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+ if (sc->dmat != NULL)
+ bus_dma_tag_destroy(sc->dmat);
+ if (sc->irq != NULL)
+ bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+ if (sc->cs != NULL)
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->csid, sc->cs);
+ return (ENXIO);
}
static void
hdsp_map_dmabuf(struct sc_info *sc)
{
- uint32_t paddr, raddr;
-
- paddr = vtophys(sc->pbuf);
- raddr = vtophys(sc->rbuf);
- hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_OUT, paddr);
- hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_IN, raddr);
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_OUT, sc->paddr);
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_IN, sc->raddr);
}
static const char *
@@ -878,6 +930,7 @@ hdsp_attach(device_t dev)
err = hdsp_alloc_resources(sc);
if (err) {
device_printf(dev, "Unable to allocate system resources.\n");
+ mtx_destroy(&sc->lock);
return (ENXIO);
}
@@ -965,6 +1018,8 @@ static void
hdsp_dmafree(struct sc_info *sc)
{
+ bus_dmamap_sync(sc->dmat, sc->rmap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(sc->dmat, sc->pmap, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->dmat, sc->rmap);
bus_dmamap_unload(sc->dmat, sc->pmap);
bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
diff --git a/sys/dev/sound/pci/hdsp.h b/sys/dev/sound/pci/hdsp.h
index 81a0be7dc1e6..b30845fb4bb9 100644
--- a/sys/dev/sound/pci/hdsp.h
+++ b/sys/dev/sound/pci/hdsp.h
@@ -248,6 +248,9 @@ struct sc_info {
uint32_t bufsize;
bus_dmamap_t pmap;
bus_dmamap_t rmap;
+ /* DMA buffer bus addresses. */
+ uint32_t paddr;
+ uint32_t raddr;
uint32_t period;
uint32_t speed;
uint32_t force_period;