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
This commit is contained in:
Florian Walpen 2024-01-15 10:21:57 +00:00 committed by Ruslan Bukin
parent 787cb30d20
commit d7fde2c9ec
4 changed files with 441 additions and 116 deletions

View file

@ -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.

View file

@ -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;
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++;
}

View file

@ -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

View file

@ -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;