git: 8e471fbc7dc4 - stable/14 - snd_hdspe(4): One pcm device per physical ADAT port.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 30 Jan 2024 11:08:23 UTC
The branch stable/14 has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=8e471fbc7dc4ee0a6c6cc696b28858401ebc8d63 commit 8e471fbc7dc4ee0a6c6cc696b28858401ebc8d63 Author: Florian Walpen <dev@submerge.ch> AuthorDate: 2024-01-15 10:21:57 +0000 Commit: Christos Margiolis <christos@FreeBSD.org> CommitDate: 2024-01-30 11:07:28 +0000 snd_hdspe(4): One pcm device per physical ADAT port. ADAT connections transport 8, 4 or 2 audio channels depending on the sample rate. Instead of splitting each physical ADAT port into 4 (potentially unmapped) stereo pcm devices, create just one pcm device of variable channel width for every ADAT port. Depending on the sample rate and channel width selected, the pcm channels may be only partially mapped to ADAT channels and vice versa. Added flexibility of the new channel mapping is also prerequisite to introduce more pcm device layouts in follow-up commits. Reviewed by: br Differential Revision: https://reviews.freebsd.org/D43393 (cherry picked from commit d7fde2c9eccf90b2a889e92800f3bc07376e84f6) --- share/man/man4/snd_hdspe.4 | 11 +- sys/dev/sound/pci/hdspe-pcm.c | 462 +++++++++++++++++++++++++++++++++++------- sys/dev/sound/pci/hdspe.c | 52 ++--- sys/dev/sound/pci/hdspe.h | 36 +++- 4 files changed, 443 insertions(+), 118 deletions(-) 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 <br@bsdpad.com> . +.An Florian Walpen <dev@submerge.ch> +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 <br@bsdpad.com> + * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch> * 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; + + 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; + } - 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); + 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); - } - - src /= 4; /* Bytes per sample. */ - dst = src / n; /* Destination buffer n-times smaller. */ - - ssize = ch->size / 4; - dsize = ch->size / (4 * n); + /* 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; - /* - * Use two fragment buffer to avoid sound clipping. - */ + if (ch->dir == PCMDIR_PLAY) + pos = sndbuf_getreadyptr(ch->buffer); + else + pos = sndbuf_getfreeptr(ch->buffer); - 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]; - - } else { - ch->data[src] = - sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot]; - ch->data[src+1] = - sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot]; - } + pos /= 4; /* Bytes per sample. */ + pos /= n; /* Destination buffer n-times smaller. */ - dst+=1; - dst %= dsize; - src += n; - src %= ssize; + /* 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); + + 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); + + 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 @@ end: return (sndbuf_getblksz(ch->buffer)); } -static uint32_t hdspe_rfmt[] = { - 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), +static uint32_t hdspe_bkp_fmt[] = { 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 <br@bsdpad.com> + * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch> * 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 <br@bsdpad.com> + * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch> * 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;