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
This commit is contained in:
Florian Walpen 2024-02-25 21:39:36 +00:00 committed by Ruslan Bukin
parent 32fdcff870
commit dc15f02506
3 changed files with 50 additions and 4 deletions

View file

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

View file

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

View file

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