diff --git a/share/man/man4/snd_hdspe.4 b/share/man/man4/snd_hdspe.4 --- 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 @@ .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 @@ .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 --- 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 @@ { 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 @@ 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 @@ 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 @@ { 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ */ 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.h b/sys/dev/sound/pci/hdspe.h --- 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_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; diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c --- 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_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