diff --git a/share/man/man4/snd_hdspe.4 b/share/man/man4/snd_hdspe.4
index 4b925b14aef6..6023cd3d2ccd 100644
--- a/share/man/man4/snd_hdspe.4
+++ b/share/man/man4/snd_hdspe.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 30, 2023
+.Dd January 8, 2024
.Dt SND_HDSPE 4
.Os
.Sh NAME
@@ -59,6 +59,13 @@ RME HDSPe AIO
.It
RME HDSPe RayDAT
.El
+.Pp
+By default, each
+.Xr pcm 4
+device corresponds to a physical port on the sound card.
+For ADAT ports, 8 channel, 4 channel and 2 channel formats are supported.
+Depending on sample rate and channel format selected, not all pcm channels can
+be mapped to ADAT channels and vice versa.
.Sh SYSCTL TUNABLES
These settings and informational values can be accessed at runtime with the
.Xr sysctl 8
@@ -111,3 +118,5 @@ The
.Nm
driver was written by
.An Ruslan Bukin
.
+.An Florian Walpen
+contributed clock source settings and restructured the pcm device mapping.
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index b3daed4d9599..d9d40c9877ad 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2012-2021 Ruslan Bukin
+ * Copyright (c) 2023-2024 Florian Walpen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -78,6 +79,152 @@ static struct hdspe_rate rate_map[] = {
{ 0, 0 },
};
+static uint32_t
+hdspe_channel_play_ports(struct hdspe_channel *hc)
+{
+ return (hc->ports & (HDSPE_CHAN_AIO_ALL | HDSPE_CHAN_RAY_ALL));
+}
+
+static uint32_t
+hdspe_channel_rec_ports(struct hdspe_channel *hc)
+{
+ return (hc->ports & (HDSPE_CHAN_AIO_ALL_REC | HDSPE_CHAN_RAY_ALL));
+}
+
+static unsigned int
+hdspe_adat_width(uint32_t speed)
+{
+ if (speed > 96000)
+ return (2);
+ if (speed > 48000)
+ return (4);
+ return (8);
+}
+
+static uint32_t
+hdspe_port_first(uint32_t ports)
+{
+ return (ports & (~(ports - 1))); /* Extract first bit set. */
+}
+
+static uint32_t
+hdspe_port_first_row(uint32_t ports)
+{
+ uint32_t ends;
+
+ /* Restrict ports to one set with contiguous slots. */
+ if (ports & HDSPE_CHAN_AIO_LINE)
+ ports = HDSPE_CHAN_AIO_LINE; /* Gap in the AIO slots here. */
+ else if (ports & HDSPE_CHAN_AIO_ALL)
+ ports &= HDSPE_CHAN_AIO_ALL; /* Rest of the AIO slots. */
+ else if (ports & HDSPE_CHAN_RAY_ALL)
+ ports &= HDSPE_CHAN_RAY_ALL; /* All RayDAT slots. */
+
+ /* Ends of port rows are followed by a port which is not in the set. */
+ ends = ports & (~(ports >> 1));
+ /* First row of contiguous ports ends in the first row end. */
+ return (ports & (ends ^ (ends - 1)));
+}
+
+static unsigned int
+hdspe_channel_count(uint32_t ports, uint32_t adat_width)
+{
+ unsigned int count = 0;
+
+ if (ports & HDSPE_CHAN_AIO_ALL) {
+ /* AIO ports. */
+ if (ports & HDSPE_CHAN_AIO_LINE)
+ count += 2;
+ if (ports & HDSPE_CHAN_AIO_PHONE)
+ count += 2;
+ if (ports & HDSPE_CHAN_AIO_AES)
+ count += 2;
+ if (ports & HDSPE_CHAN_AIO_SPDIF)
+ count += 2;
+ if (ports & HDSPE_CHAN_AIO_ADAT)
+ count += adat_width;
+ } else if (ports & HDSPE_CHAN_RAY_ALL) {
+ /* RayDAT ports. */
+ if (ports & HDSPE_CHAN_RAY_AES)
+ count += 2;
+ if (ports & HDSPE_CHAN_RAY_SPDIF)
+ count += 2;
+ if (ports & HDSPE_CHAN_RAY_ADAT1)
+ count += adat_width;
+ if (ports & HDSPE_CHAN_RAY_ADAT2)
+ count += adat_width;
+ if (ports & HDSPE_CHAN_RAY_ADAT3)
+ count += adat_width;
+ if (ports & HDSPE_CHAN_RAY_ADAT4)
+ count += adat_width;
+ }
+
+ return (count);
+}
+
+static unsigned int
+hdspe_channel_offset(uint32_t subset, uint32_t ports, unsigned int adat_width)
+{
+ uint32_t preceding;
+
+ /* Make sure we have a subset of ports. */
+ subset &= ports;
+ /* Include all ports preceding the first one of the subset. */
+ preceding = ports & (~subset & (subset - 1));
+
+ if (preceding & HDSPE_CHAN_AIO_ALL)
+ preceding &= HDSPE_CHAN_AIO_ALL; /* Contiguous AIO slots. */
+ else if (preceding & HDSPE_CHAN_RAY_ALL)
+ preceding &= HDSPE_CHAN_RAY_ALL; /* Contiguous RayDAT slots. */
+
+ return (hdspe_channel_count(preceding, adat_width));
+}
+
+static unsigned int
+hdspe_port_slot_offset(uint32_t port, unsigned int adat_width)
+{
+ /* Exctract the first port (lowest bit) if set of ports. */
+ switch (hdspe_port_first(port)) {
+ /* AIO ports */
+ case HDSPE_CHAN_AIO_LINE:
+ return (0);
+ case HDSPE_CHAN_AIO_PHONE:
+ return (6);
+ case HDSPE_CHAN_AIO_AES:
+ return (8);
+ case HDSPE_CHAN_AIO_SPDIF:
+ return (10);
+ case HDSPE_CHAN_AIO_ADAT:
+ return (12);
+
+ /* RayDAT ports */
+ case HDSPE_CHAN_RAY_AES:
+ return (0);
+ case HDSPE_CHAN_RAY_SPDIF:
+ return (2);
+ case HDSPE_CHAN_RAY_ADAT1:
+ return (4);
+ case HDSPE_CHAN_RAY_ADAT2:
+ return (4 + adat_width);
+ case HDSPE_CHAN_RAY_ADAT3:
+ return (4 + 2 * adat_width);
+ case HDSPE_CHAN_RAY_ADAT4:
+ return (4 + 3 * adat_width);
+ default:
+ return (0);
+ }
+}
+
+static unsigned int
+hdspe_port_slot_width(uint32_t ports, unsigned int adat_width)
+{
+ uint32_t row;
+
+ /* Count number of contiguous slots from the first physical port. */
+ row = hdspe_port_first_row(ports);
+ return (hdspe_channel_count(row, adat_width));
+}
+
static int
hdspe_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
unsigned int src, unsigned short data)
@@ -103,11 +250,34 @@ hdspe_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
static int
hdspechan_setgain(struct sc_chinfo *ch)
{
+ struct sc_info *sc;
+ uint32_t port, ports;
+ unsigned int slot, end_slot;
+ unsigned short volume;
- hdspe_hw_mixer(ch, ch->lslot, ch->lslot,
- ch->lvol * HDSPE_MAX_GAIN / 100);
- hdspe_hw_mixer(ch, ch->rslot, ch->rslot,
- ch->rvol * HDSPE_MAX_GAIN / 100);
+ sc = ch->parent->sc;
+
+ /* Iterate through all physical ports of the channel. */
+ ports = ch->ports;
+ port = hdspe_port_first(ports);
+ while (port != 0) {
+ /* Get slot range of the physical port. */
+ slot =
+ hdspe_port_slot_offset(port, hdspe_adat_width(sc->speed));
+ end_slot = slot +
+ hdspe_port_slot_width(port, hdspe_adat_width(sc->speed));
+
+ /* Treat first slot as left channel. */
+ volume = ch->lvol * HDSPE_MAX_GAIN / 100;
+ for (; slot < end_slot; slot++) {
+ hdspe_hw_mixer(ch, slot, slot, volume);
+ /* Subsequent slots all get the right channel volume. */
+ volume = ch->rvol * HDSPE_MAX_GAIN / 100;
+ }
+
+ ports &= ~port;
+ port = hdspe_port_first(ports);
+ }
return (0);
}
@@ -126,10 +296,10 @@ hdspemixer_init(struct snd_mixer *m)
mask = SOUND_MASK_PCM;
- if (scp->hc->play)
+ if (hdspe_channel_play_ports(scp->hc))
mask |= SOUND_MASK_VOLUME;
- if (scp->hc->rec)
+ if (hdspe_channel_rec_ports(scp->hc))
mask |= SOUND_MASK_RECLEV;
snd_mtxlock(sc->lock);
@@ -181,7 +351,9 @@ hdspechan_enable(struct sc_chinfo *ch, int value)
{
struct sc_pcminfo *scp;
struct sc_info *sc;
+ uint32_t row, ports;
int reg;
+ unsigned int slot, end_slot;
scp = ch->parent;
sc = scp->sc;
@@ -193,9 +365,22 @@ hdspechan_enable(struct sc_chinfo *ch, int value)
ch->run = value;
- hdspe_write_1(sc, reg + (4 * ch->lslot), value);
- if (AFMT_CHANNEL(ch->format) == 2)
- hdspe_write_1(sc, reg + (4 * ch->rslot), value);
+ /* Iterate through rows of ports with contiguous slots. */
+ ports = ch->ports;
+ row = hdspe_port_first_row(ports);
+ while (row != 0) {
+ slot =
+ hdspe_port_slot_offset(row, hdspe_adat_width(sc->speed));
+ end_slot = slot +
+ hdspe_port_slot_width(row, hdspe_adat_width(sc->speed));
+
+ for (; slot < end_slot; slot++) {
+ hdspe_write_1(sc, reg + (4 * slot), value);
+ }
+
+ ports &= ~row;
+ row = hdspe_port_first_row(ports);
+ }
}
static int
@@ -253,56 +438,152 @@ hdspe_stop_audio(struct sc_info *sc)
hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
}
-/* Multiplex / demultiplex: 2.0 <-> 2 x 1.0. */
+static void
+buffer_mux_write(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int samples, unsigned int slots, unsigned int channels)
+{
+ int slot;
+
+ for (; samples > 0; samples--) {
+ for (slot = 0; slot < slots; slot++) {
+ dma[slot * HDSPE_CHANBUF_SAMPLES + pos] =
+ pcm[pos * channels + slot];
+ }
+ pos = (pos + 1) % HDSPE_CHANBUF_SAMPLES;
+ }
+}
+
+static void
+buffer_mux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t ports,
+ unsigned int pos, unsigned int samples, unsigned int adat_width,
+ unsigned int pcm_width)
+{
+ unsigned int slot_offset, slots;
+ unsigned int channels, chan_pos;
+
+ /* Translate DMA slot offset to DMA buffer offset. */
+ slot_offset = hdspe_port_slot_offset(subset, adat_width);
+ dma += slot_offset * HDSPE_CHANBUF_SAMPLES;
+
+ /* Channel position of the port subset and total number of channels. */
+ chan_pos = hdspe_channel_offset(subset, ports, pcm_width);
+ pcm += chan_pos;
+ channels = hdspe_channel_count(ports, pcm_width);
+
+ /* Only copy as much as supported by both hardware and pcm channel. */
+ slots = hdspe_port_slot_width(subset, MIN(adat_width, pcm_width));
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (slots == 2)
+ buffer_mux_write(dma, pcm, pos, samples, 2, channels);
+ else if (slots == 4)
+ buffer_mux_write(dma, pcm, pos, samples, 4, channels);
+ else if (slots == 8)
+ buffer_mux_write(dma, pcm, pos, samples, 8, channels);
+ else
+ buffer_mux_write(dma, pcm, pos, samples, slots, channels);
+}
+
+static void
+buffer_demux_read(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int samples, unsigned int slots, unsigned int channels)
+{
+ int slot;
+
+ for (; samples > 0; samples--) {
+ for (slot = 0; slot < slots; slot++) {
+ pcm[pos * channels + slot] =
+ dma[slot * HDSPE_CHANBUF_SAMPLES + pos];
+ }
+ pos = (pos + 1) % HDSPE_CHANBUF_SAMPLES;
+ }
+}
+
+static void
+buffer_demux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t ports,
+ unsigned int pos, unsigned int samples, unsigned int adat_width,
+ unsigned int pcm_width)
+{
+ unsigned int slot_offset, slots;
+ unsigned int channels, chan_pos;
+
+ /* Translate port slot offset to DMA buffer offset. */
+ slot_offset = hdspe_port_slot_offset(subset, adat_width);
+ dma += slot_offset * HDSPE_CHANBUF_SAMPLES;
+
+ /* Channel position of the port subset and total number of channels. */
+ chan_pos = hdspe_channel_offset(subset, ports, pcm_width);
+ pcm += chan_pos;
+ channels = hdspe_channel_count(ports, pcm_width);
+
+ /* Only copy as much as supported by both hardware and pcm channel. */
+ slots = hdspe_port_slot_width(subset, MIN(adat_width, pcm_width));
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (slots == 2)
+ buffer_demux_read(dma, pcm, pos, samples, 2, channels);
+ else if (slots == 4)
+ buffer_demux_read(dma, pcm, pos, samples, 4, channels);
+ else if (slots == 8)
+ buffer_demux_read(dma, pcm, pos, samples, 8, channels);
+ else
+ buffer_demux_read(dma, pcm, pos, samples, slots, channels);
+}
+
+
+/* Copy data between DMA and PCM buffers. */
static void
buffer_copy(struct sc_chinfo *ch)
{
struct sc_pcminfo *scp;
struct sc_info *sc;
- int ssize, dsize;
- int src, dst;
- int n;
- int i;
+ uint32_t row, ports;
+ unsigned int pos;
+ unsigned int n;
+ unsigned int adat_width, pcm_width;
scp = ch->parent;
sc = scp->sc;
n = AFMT_CHANNEL(ch->format); /* n channels */
- if (ch->dir == PCMDIR_PLAY) {
- src = sndbuf_getreadyptr(ch->buffer);
- } else {
- src = sndbuf_getfreeptr(ch->buffer);
- }
+ /* Let pcm formats differ from current hardware ADAT width. */
+ adat_width = hdspe_adat_width(sc->speed);
+ if (n == hdspe_channel_count(ch->ports, 2))
+ pcm_width = 2;
+ else if (n == hdspe_channel_count(ch->ports, 4))
+ pcm_width = 4;
+ else
+ pcm_width = 8;
- src /= 4; /* Bytes per sample. */
- dst = src / n; /* Destination buffer n-times smaller. */
+ if (ch->dir == PCMDIR_PLAY)
+ pos = sndbuf_getreadyptr(ch->buffer);
+ else
+ pos = sndbuf_getfreeptr(ch->buffer);
- ssize = ch->size / 4;
- dsize = ch->size / (4 * n);
+ pos /= 4; /* Bytes per sample. */
+ pos /= n; /* Destination buffer n-times smaller. */
- /*
- * Use two fragment buffer to avoid sound clipping.
- */
+ /* Iterate through rows of ports with contiguous slots. */
+ ports = ch->ports;
+ if (pcm_width == adat_width)
+ row = hdspe_port_first_row(ports);
+ else
+ row = hdspe_port_first(ports);
- for (i = 0; i < sc->period * 2 /* fragments */; i++) {
- if (ch->dir == PCMDIR_PLAY) {
- sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot] =
- ch->data[src];
- sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot] =
- ch->data[src + 1];
+ 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);
+ else
+ buffer_demux_port(sc->rbuf, ch->data, row, ch->ports,
+ pos, sc->period * 2, adat_width, pcm_width);
- } else {
- ch->data[src] =
- sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot];
- ch->data[src+1] =
- sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot];
- }
-
- dst+=1;
- dst %= dsize;
- src += n;
- src %= ssize;
+ ports &= ~row;
+ if (pcm_width == adat_width)
+ row = hdspe_port_first_row(ports);
+ else
+ row = hdspe_port_first(ports);
}
}
@@ -312,17 +593,30 @@ clean(struct sc_chinfo *ch)
struct sc_pcminfo *scp;
struct sc_info *sc;
uint32_t *buf;
+ uint32_t row, ports;
+ unsigned int offset, slots;
scp = ch->parent;
sc = scp->sc;
buf = sc->rbuf;
- if (ch->dir == PCMDIR_PLAY) {
+ if (ch->dir == PCMDIR_PLAY)
buf = sc->pbuf;
- }
- bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->lslot, HDSPE_CHANBUF_SIZE);
- bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->rslot, HDSPE_CHANBUF_SIZE);
+ /* Iterate through rows of ports with contiguous slots. */
+ ports = ch->ports;
+ row = hdspe_port_first_row(ports);
+ while (row != 0) {
+ offset = hdspe_port_slot_offset(row,
+ hdspe_adat_width(sc->speed));
+ slots = hdspe_port_slot_width(row, hdspe_adat_width(sc->speed));
+
+ bzero(buf + offset * HDSPE_CHANBUF_SAMPLES,
+ slots * HDSPE_CHANBUF_SIZE);
+
+ ports &= ~row;
+ row = hdspe_port_first_row(ports);
+ }
return (0);
}
@@ -344,13 +638,29 @@ hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
num = scp->chnum;
ch = &scp->chan[num];
- ch->lslot = scp->hc->left;
- ch->rslot = scp->hc->right;
+
+ if (dir == PCMDIR_PLAY)
+ ch->ports = hdspe_channel_play_ports(scp->hc);
+ else
+ ch->ports = hdspe_channel_rec_ports(scp->hc);
+
ch->run = 0;
ch->lvol = 0;
ch->rvol = 0;
- ch->size = HDSPE_CHANBUF_SIZE * 2; /* max size */
+ /* Support all possible ADAT widths as channel formats. */
+ ch->cap_fmts[0] =
+ SND_FORMAT(AFMT_S32_LE, hdspe_channel_count(ch->ports, 2), 0);
+ ch->cap_fmts[1] =
+ SND_FORMAT(AFMT_S32_LE, hdspe_channel_count(ch->ports, 4), 0);
+ ch->cap_fmts[2] =
+ SND_FORMAT(AFMT_S32_LE, hdspe_channel_count(ch->ports, 8), 0);
+ ch->cap_fmts[3] = 0;
+ ch->caps = malloc(sizeof(struct pcmchan_caps), M_HDSPE, M_NOWAIT);
+ *(ch->caps) = (struct pcmchan_caps) {32000, 192000, ch->cap_fmts, 0};
+
+ /* 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->buffer = b;
@@ -430,8 +740,7 @@ hdspechan_getptr(kobj_t obj, void *data)
snd_mtxunlock(sc->lock);
pos = ret & HDSPE_BUF_POSITION_MASK;
- if (AFMT_CHANNEL(ch->format) == 2)
- pos *= 2; /* Hardbuf twice bigger. */
+ pos *= AFMT_CHANNEL(ch->format); /* Hardbuf with multiple channels. */
return (pos);
}
@@ -456,6 +765,10 @@ hdspechan_free(kobj_t obj, void *data)
free(ch->data, M_HDSPE);
ch->data = NULL;
}
+ if (ch->caps != NULL) {
+ free(ch->caps, M_HDSPE);
+ ch->caps = NULL;
+ }
snd_mtxunlock(sc->lock);
return (0);
@@ -580,9 +893,8 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
/* First look for equal latency. */
for (i = 0; latency_map[i].period != 0; i++) {
- if (latency_map[i].period == blocksize) {
+ if (latency_map[i].period == blocksize)
hl = &latency_map[i];
- }
}
/* If no match, just find nearest. */
@@ -615,20 +927,12 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
return (sndbuf_getblksz(ch->buffer));
}
-static uint32_t hdspe_rfmt[] = {
+static uint32_t hdspe_bkp_fmt[] = {
SND_FORMAT(AFMT_S32_LE, 2, 0),
0
};
-static struct pcmchan_caps hdspe_rcaps = {32000, 192000, hdspe_rfmt, 0};
-
-static uint32_t hdspe_pfmt[] = {
- SND_FORMAT(AFMT_S32_LE, 1, 0),
- SND_FORMAT(AFMT_S32_LE, 2, 0),
- 0
-};
-
-static struct pcmchan_caps hdspe_pcaps = {32000, 192000, hdspe_pfmt, 0};
+static struct pcmchan_caps hdspe_bkp_caps = {32000, 192000, hdspe_bkp_fmt, 0};
static struct pcmchan_caps *
hdspechan_getcaps(kobj_t obj, void *data)
@@ -642,8 +946,10 @@ hdspechan_getcaps(kobj_t obj, void *data)
device_printf(scp->dev, "hdspechan_getcaps()\n");
#endif
- return ((ch->dir == PCMDIR_PLAY) ?
- &hdspe_pcaps : &hdspe_rcaps);
+ if (ch->caps != NULL)
+ return (ch->caps);
+
+ return (&hdspe_bkp_caps);
}
static kobj_method_t hdspechan_methods[] = {
@@ -695,13 +1001,21 @@ hdspe_pcm_attach(device_t dev)
char status[SND_STATUSLEN];
struct sc_pcminfo *scp;
char desc[64];
- int i, err;
+ int err;
+ int play, rec;
scp = device_get_ivars(dev);
scp->ih = &hdspe_pcm_intr;
bzero(desc, sizeof(desc));
- snprintf(desc, sizeof(desc), "HDSPe AIO [%s]", scp->hc->descr);
+ if (scp->hc->ports & HDSPE_CHAN_AIO_ALL)
+ snprintf(desc, sizeof(desc), "HDSPe AIO [%s]",
+ scp->hc->descr);
+ else if (scp->hc->ports & HDSPE_CHAN_RAY_ALL)
+ snprintf(desc, sizeof(desc), "HDSPe RayDAT [%s]",
+ scp->hc->descr);
+ else
+ snprintf(desc, sizeof(desc), "HDSPe ? [%s]", scp->hc->descr);
device_set_desc_copy(dev, desc);
/*
@@ -710,19 +1024,21 @@ hdspe_pcm_attach(device_t dev)
*/
pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
- err = pcm_register(dev, scp, scp->hc->play, scp->hc->rec);
+ play = (hdspe_channel_play_ports(scp->hc)) ? 1 : 0;
+ rec = (hdspe_channel_rec_ports(scp->hc)) ? 1 : 0;
+ err = pcm_register(dev, scp, play, rec);
if (err) {
device_printf(dev, "Can't register pcm.\n");
return (ENXIO);
}
scp->chnum = 0;
- for (i = 0; i < scp->hc->play; i++) {
+ if (play) {
pcm_addchan(dev, PCMDIR_PLAY, &hdspechan_class, scp);
scp->chnum++;
}
- for (i = 0; i < scp->hc->rec; i++) {
+ if (rec) {
pcm_addchan(dev, PCMDIR_REC, &hdspechan_class, scp);
scp->chnum++;
}
diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c
index 8a7cac87fc1a..e0197d1e981a 100644
--- a/sys/dev/sound/pci/hdspe.c
+++ b/sys/dev/sound/pci/hdspe.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2012-2016 Ruslan Bukin
+ * Copyright (c) 2023-2024 Florian Walpen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,47 +70,22 @@ static struct hdspe_clock_source hdspe_clock_source_table_aio[] = {
};
static struct hdspe_channel chan_map_aio[] = {
- { 0, 1, "line", 1, 1 },
- { 6, 7, "phone", 1, 0 },
- { 8, 9, "aes", 1, 1 },
- { 10, 11, "s/pdif", 1, 1 },
- { 12, 16, "adat", 1, 1 },
-
- /* Single or double speed. */
- { 14, 18, "adat", 1, 1 },
-
- /* Single speed only. */
- { 13, 15, "adat", 1, 1 },
- { 17, 19, "adat", 1, 1 },
-
- { 0, 0, NULL, 0, 0 },
+ { HDSPE_CHAN_AIO_LINE, "line" },
+ { HDSPE_CHAN_AIO_PHONE, "phone" },
+ { HDSPE_CHAN_AIO_AES, "aes" },
+ { HDSPE_CHAN_AIO_SPDIF, "s/pdif" },
+ { HDSPE_CHAN_AIO_ADAT, "adat" },
+ { 0, NULL },
};
static struct hdspe_channel chan_map_rd[] = {
- { 0, 1, "aes", 1, 1 },
- { 2, 3, "s/pdif", 1, 1 },
- { 4, 5, "adat", 1, 1 },
- { 6, 7, "adat", 1, 1 },
- { 8, 9, "adat", 1, 1 },
- { 10, 11, "adat", 1, 1 },
-
- /* Single or double speed. */
- { 12, 13, "adat", 1, 1 },
- { 14, 15, "adat", 1, 1 },
- { 16, 17, "adat", 1, 1 },
- { 18, 19, "adat", 1, 1 },
-
- /* Single speed only. */
- { 20, 21, "adat", 1, 1 },
- { 22, 23, "adat", 1, 1 },
- { 24, 25, "adat", 1, 1 },
- { 26, 27, "adat", 1, 1 },
- { 28, 29, "adat", 1, 1 },
- { 30, 31, "adat", 1, 1 },
- { 32, 33, "adat", 1, 1 },
- { 34, 35, "adat", 1, 1 },
-
- { 0, 0, NULL, 0, 0 },
+ { HDSPE_CHAN_RAY_AES, "aes" },
+ { HDSPE_CHAN_RAY_SPDIF, "s/pdif" },
+ { HDSPE_CHAN_RAY_ADAT1, "adat1" },
+ { HDSPE_CHAN_RAY_ADAT2, "adat2" },
+ { HDSPE_CHAN_RAY_ADAT3, "adat3" },
+ { HDSPE_CHAN_RAY_ADAT4, "adat4" },
+ { 0, NULL },
};
static void
diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h
index 58362641cb01..d5d8dd46e580 100644
--- a/sys/dev/sound/pci/hdspe.h
+++ b/sys/dev/sound/pci/hdspe.h
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2012-2016 Ruslan Bukin
+ * Copyright (c) 2023-2024 Florian Walpen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -116,12 +117,34 @@
#define HDSPE_CHANBUF_SIZE (4 * HDSPE_CHANBUF_SAMPLES)
#define HDSPE_DMASEGSIZE (HDSPE_CHANBUF_SIZE * HDSPE_MAX_SLOTS)
+#define HDSPE_CHAN_AIO_LINE (1 << 0)
+#define HDSPE_CHAN_AIO_PHONE (1 << 1)
+#define HDSPE_CHAN_AIO_AES (1 << 2)
+#define HDSPE_CHAN_AIO_SPDIF (1 << 3)
+#define HDSPE_CHAN_AIO_ADAT (1 << 4)
+#define HDSPE_CHAN_AIO_ALL_REC (HDSPE_CHAN_AIO_LINE | \
+ HDSPE_CHAN_AIO_AES | \
+ HDSPE_CHAN_AIO_SPDIF | \
+ HDSPE_CHAN_AIO_ADAT)
+#define HDSPE_CHAN_AIO_ALL (HDSPE_CHAN_AIO_ALL_REC | \
+ HDSPE_CHAN_AIO_PHONE) \
+
+#define HDSPE_CHAN_RAY_AES (1 << 5)
+#define HDSPE_CHAN_RAY_SPDIF (1 << 6)
+#define HDSPE_CHAN_RAY_ADAT1 (1 << 7)
+#define HDSPE_CHAN_RAY_ADAT2 (1 << 8)
+#define HDSPE_CHAN_RAY_ADAT3 (1 << 9)
+#define HDSPE_CHAN_RAY_ADAT4 (1 << 10)
+#define HDSPE_CHAN_RAY_ALL (HDSPE_CHAN_RAY_AES | \
+ HDSPE_CHAN_RAY_SPDIF | \
+ HDSPE_CHAN_RAY_ADAT1 | \
+ HDSPE_CHAN_RAY_ADAT2 | \
+ HDSPE_CHAN_RAY_ADAT3 | \
+ HDSPE_CHAN_RAY_ADAT4)
+
struct hdspe_channel {
- uint32_t left;
- uint32_t right;
+ uint32_t ports;
char *descr;
- uint32_t play;
- uint32_t rec;
};
/* Clock sources */
@@ -150,10 +173,11 @@ struct sc_chinfo {
struct sc_pcminfo *parent;
/* Channel information */
+ struct pcmchan_caps *caps;
+ uint32_t cap_fmts[4];
uint32_t dir;
uint32_t format;
- uint32_t lslot;
- uint32_t rslot;
+ uint32_t ports;
uint32_t lvol;
uint32_t rvol;