git: dc15f025062d - main - snd_hdspe(4): Optional unified pcm device.

From: Ruslan Bukin <br_at_FreeBSD.org>
Date: Sun, 25 Feb 2024 21:48:00 UTC
The branch main has been updated by br:

URL: https://cgit.FreeBSD.org/src/commit/?id=dc15f025062da40ac3a2529b6c979c597b9eb94c

commit dc15f025062da40ac3a2529b6c979c597b9eb94c
Author:     Florian Walpen <dev@submerge.ch>
AuthorDate: 2024-02-25 21:39:36 +0000
Commit:     Ruslan Bukin <br@FreeBSD.org>
CommitDate: 2024-02-25 21:39:36 +0000

    snd_hdspe(4): Optional unified pcm device.
    
    Add a sysctl tunable to unify all physical ports of an HDSPe sound card
    into one pcm device, with up to 14 (AIO) or 36 (RayDAT) channels. This
    makes all ports available in multi-channel audio software.
    
    Differential Revision:  https://reviews.freebsd.org/D43798
---
 share/man/man4/snd_hdspe.4    | 23 ++++++++++++++++++++++-
 sys/dev/sound/pci/hdspe-pcm.c |  9 ++++++++-
 sys/dev/sound/pci/hdspe.c     | 22 ++++++++++++++++++++--
 3 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/share/man/man4/snd_hdspe.4 b/share/man/man4/snd_hdspe.4
index 7d8cc2adc1af..a8c7be538dd1 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 January 29, 2024
+.Dd February 9, 2024
 .Dt SND_HDSPE 4
 .Os
 .Sh NAME
@@ -64,8 +64,29 @@ 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.
+The effective number of ADAT channels is 8 channels at single speed
+(32kHz-48kHz), 4 channels at double speed (64kHz-96kHz), and 2 channels at
+quad speed (128kHz-192kHz).
 Depending on sample rate and channel format selected, not all pcm channels can
 be mapped to ADAT channels and vice versa.
+.Sh LOADER TUNABLES
+These settings can be entered at the
+.Xr loader 8
+prompt or in
+.Xr loader.conf 5 .
+.Bl -tag -width indent
+.It Va hw.hdspe.unified_pcm
+If set to 1, all physical ports are combined into one unified pcm device.
+When opened in multi-channel audio software, this makes all ports available
+at the same time, and fully synchronized.
+For resulting channel numbers consult the following table:
+.El
+.Bl -column "HDSPe RayDAT" "Single Speed" "Double Speed" "Quad Speed"
+.Sy "Sound Card" Ta Sy "Single Speed" Ta Sy "Double Speed" Ta Sy "Quad Speed"
+.It "" Ta "Play | Rec" Ta "Play | Rec" Ta "Play | Rec"
+.It HDSPe AIO Ta " 16  |  14" Ta " 12  |  10" Ta " 10  |   8"
+.It HDSPe RayDAT Ta " 36  |  36" Ta " 20  |  20" Ta " 12  |  12"
+.El
 .Sh SYSCTL TUNABLES
 These settings and informational values can be accessed at runtime with the
 .Xr sysctl 8
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index cf764f19a933..594a4f704028 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -41,6 +41,8 @@
 
 #include <mixer_if.h>
 
+#define HDSPE_MATRIX_MAX	8
+
 struct hdspe_latency {
 	uint32_t n;
 	uint32_t period;
@@ -1007,6 +1009,7 @@ hdspe_pcm_attach(device_t dev)
 	char status[SND_STATUSLEN];
 	struct sc_pcminfo *scp;
 	const char *buf;
+	uint32_t pcm_flags;
 	int err;
 	int play, rec;
 
@@ -1025,7 +1028,11 @@ hdspe_pcm_attach(device_t dev)
 	 * We don't register interrupt handler with snd_setup_intr
 	 * in pcm device. Mark pcm device as MPSAFE manually.
 	 */
-	pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+	pcm_flags = pcm_getflags(dev) | SD_F_MPSAFE;
+	if (hdspe_channel_count(scp->hc->ports, 8) > HDSPE_MATRIX_MAX)
+		/* Disable vchan conversion, too many channels. */
+		pcm_flags |= SD_F_BITPERFECT;
+	pcm_setflags(dev, pcm_flags);
 
 	play = (hdspe_channel_play_ports(scp->hc)) ? 1 : 0;
 	rec = (hdspe_channel_rec_ports(scp->hc)) ? 1 : 0;
diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c
index 1d1068c9f8fe..0773362f2bc3 100644
--- a/sys/dev/sound/pci/hdspe.c
+++ b/sys/dev/sound/pci/hdspe.c
@@ -44,6 +44,14 @@
 
 #include <mixer_if.h>
 
+static bool hdspe_unified_pcm = false;
+
+static SYSCTL_NODE(_hw, OID_AUTO, hdspe, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "PCI HDSPe");
+
+SYSCTL_BOOL(_hw_hdspe, OID_AUTO, unified_pcm, CTLFLAG_RWTUN,
+    &hdspe_unified_pcm, 0, "Combine physical ports in one unified pcm device");
+
 static struct hdspe_clock_source hdspe_clock_source_table_rd[] = {
 	{ "internal", 0 << 1 | 1, HDSPE_STATUS1_CLOCK(15),       0,       0 },
 	{ "word",     0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 1 << 24, 1 << 25 },
@@ -78,6 +86,11 @@ static struct hdspe_channel chan_map_aio[] = {
 	{ 0,                        NULL },
 };
 
+static struct hdspe_channel chan_map_aio_uni[] = {
+	{ HDSPE_CHAN_AIO_ALL, "all" },
+	{ 0,                   NULL },
+};
+
 static struct hdspe_channel chan_map_rd[] = {
 	{ HDSPE_CHAN_RAY_AES,      "aes" },
 	{ HDSPE_CHAN_RAY_SPDIF, "s/pdif" },
@@ -88,6 +101,11 @@ static struct hdspe_channel chan_map_rd[] = {
 	{ 0,                        NULL },
 };
 
+static struct hdspe_channel chan_map_rd_uni[] = {
+	{ HDSPE_CHAN_RAY_ALL, "all" },
+	{ 0,                   NULL },
+};
+
 static void
 hdspe_intr(void *p)
 {
@@ -538,11 +556,11 @@ hdspe_attach(device_t dev)
 	switch (rev) {
 	case PCI_REVISION_AIO:
 		sc->type = HDSPE_AIO;
-		chan_map = chan_map_aio;
+		chan_map = hdspe_unified_pcm ? chan_map_aio_uni : chan_map_aio;
 		break;
 	case PCI_REVISION_RAYDAT:
 		sc->type = HDSPE_RAYDAT;
-		chan_map = chan_map_rd;
+		chan_map = hdspe_unified_pcm ? chan_map_rd_uni : chan_map_rd;
 		break;
 	default:
 		return (ENXIO);