From nobody Mon Jan 15 10:29:52 2024
X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1])
by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4TD7gN1pNvz56Zg2;
Mon, 15 Jan 2024 10:29:52 +0000 (UTC)
(envelope-from git@FreeBSD.org)
Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256
client-signature RSA-PSS (4096 bits) client-digest SHA256)
(Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK))
by mx1.freebsd.org (Postfix) with ESMTPS id 4TD7gN1QfQz45Hr;
Mon, 15 Jan 2024 10:29:52 +0000 (UTC)
(envelope-from git@FreeBSD.org)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim;
t=1705314592;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=GwFS5Fw111U+ZOTor+ujjAoysbgfaK/9J7JrBFrGX+o=;
b=X171l7Co3Rg6ecwgpAAVbOImNo8X2WcJJApvI3ia6GRhBpINkO7bRgYfqJQ+rSBnpcwR6d
VNMu+u6GuObKqISRQvVDoo547U2OmIqULTvMTasEiQHDjh2HoT6kIQzteBCznrTryHwCOl
ivC7/1uykgaoIBCxR5emEocZw8+lt2cTcNwTmV4QiuN8TABFNNn8nS95Zh2DypK1anDmpE
glsBl2f+YKU9WXTAx2GpFioSN4elipJa6Jr+VTg4ZhHomuDoSWBN2TO916vLvkQCzXzIEJ
BLb64ucGm7XOBEkf9PD81/GJzY/tNQOdTsbOh+sk5GCb0sHHcY71ESSPFd2giQ==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org;
s=dkim; t=1705314592;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=GwFS5Fw111U+ZOTor+ujjAoysbgfaK/9J7JrBFrGX+o=;
b=A13qn8M8BkrA4NcTHQUu8v0WX5yTRPuAasNifu/L8l6zfFWex2xMrGwqEMjO/KbXVqasyx
Iyzumo/Hi8hXjtwYPIaQNSQB8xut4HJKBJLaWXPQ+KTaCQHA4AiFwIx1yxv6sTJau5QydF
+F54aJ1PSEEB/Wblzvk29K62yqN2KvW84j3vg2lmMuJLdEDDtj/Xwsrtm0zZign8rxCxVO
SnbAvSTYGhzp0Qm8xp5sIAjKa51fbibYCtjKB7D4jVTElhZg+K56nWKV1UzV2S8y3Cpj99
E3TvouRyv3L6RDr1LbTctDj/D3fMFVMdcBJj81Yh1PyG93Th8f4KJeJdfB1Q2A==
ARC-Authentication-Results: i=1;
mx1.freebsd.org;
none
ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1705314592; a=rsa-sha256; cv=none;
b=FG4g/iaFKr1VU1ee3L8rk1acNRtGei0ERPA3qrLoeyiGciibis9b89FKhoYPiUnCS64sfn
KgLkH2o+9EefmTn85AgAoWReK14e+RqSYya5GBCX1xAXYA6Q4SPaUN9Dc59VlCBGhoGgvv
72hl371KsERlljdpoChhUX2CeGz8uKQoevTMYiTIP73U9zXEizmQrNfoZdsl7Esn2jx2cX
2TNtI9Qjphh8c8wgl/d0r3NqGwACh11y7REmykXoPVcLk+BdRXPMPgjjtKCQ4o0SEkrBv2
A2LOnbP5wzmg6H6+ShrnAtODh2mEAP+SMsYhrq14XTE/Y2nPSMd3uk/GPmbKOw==
Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256)
(Client did not present a certificate)
by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4TD7gN0TfKzsgf;
Mon, 15 Jan 2024 10:29:52 +0000 (UTC)
(envelope-from git@FreeBSD.org)
Received: from gitrepo.freebsd.org ([127.0.1.44])
by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 40FATqPn004063;
Mon, 15 Jan 2024 10:29:52 GMT
(envelope-from git@gitrepo.freebsd.org)
Received: (from git@localhost)
by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 40FATq1E004060;
Mon, 15 Jan 2024 10:29:52 GMT
(envelope-from git)
Date: Mon, 15 Jan 2024 10:29:52 GMT
Message-Id: <202401151029.40FATq1E004060@gitrepo.freebsd.org>
To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org,
dev-commits-src-main@FreeBSD.org
From: Ruslan Bukin
Subject: git: d7fde2c9eccf - main - snd_hdspe(4): One pcm device
per physical ADAT port.
List-Id: Commit messages for all branches of the src repository
List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all
List-Help:
List-Post:
List-Subscribe:
List-Unsubscribe:
Sender: owner-dev-commits-src-all@freebsd.org
X-BeenThere: dev-commits-src-all@freebsd.org
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
X-Git-Committer: br
X-Git-Repository: src
X-Git-Refname: refs/heads/main
X-Git-Reftype: branch
X-Git-Commit: d7fde2c9eccf90b2a889e92800f3bc07376e84f6
Auto-Submitted: auto-generated
The branch main has been updated by br:
URL: https://cgit.FreeBSD.org/src/commit/?id=d7fde2c9eccf90b2a889e92800f3bc07376e84f6
commit d7fde2c9eccf90b2a889e92800f3bc07376e84f6
Author: Florian Walpen
AuthorDate: 2024-01-15 10:21:57 +0000
Commit: Ruslan Bukin
CommitDate: 2024-01-15 10:26:41 +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
---
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
.
+.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;
+
+ 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
+ * 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;