diff --git a/share/man/man4/snd_hdspe.4 b/share/man/man4/snd_hdspe.4 index ac81356fb783..35dea518da64 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 September 28, 2024 +.Dd November 2, 2024 .Dt SND_HDSPE 4 .Os .Sh NAME @@ -132,6 +132,32 @@ for accurately synchronized signals (required for recording digital audio). .El .Pp +The following tunables are applicable to HDSPe AIO devices only: +.Bl -tag -width indent +.It Va dev.hdspe.0.input_level +Select the sensitivity of the analog line input. +Available reference levels for the input signal are +.Ql LowGain , +.Ql +4dBu +and +.Ql -10dBV . +.It Va dev.hdspe.0.output_level +Select the gain level of the analog line output. +Available reference levels for the output signal are +.Ql HighGain , +.Ql +4dBu +and +.Ql -10dBV . +.It Va dev.hdspe.0.phones_level +Adjust the gain level of the phones output, separately from the analog line +output. +Available reference levels for the output signal are +.Ql HighGain , +.Ql +4dBu +and +.Ql -10dBV . +.El +.Pp Where appropriate these sysctl values are modeled after official RME software on other platforms, and adopt their terminology. Consult the RME user manuals for additional information. diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c index 2561fcdebb1c..f983f0be2275 100644 --- a/sys/dev/sound/pci/hdspe.c +++ b/sys/dev/sound/pci/hdspe.c @@ -246,6 +246,198 @@ hdspe_map_dmabuf(struct sc_info *sc) } } +static const char * +hdspe_settings_input_level(uint32_t settings) +{ + switch (settings & HDSPE_INPUT_LEVEL_MASK) { + case HDSPE_INPUT_LEVEL_LOWGAIN: + return ("LowGain"); + case HDSPE_INPUT_LEVEL_PLUS4DBU: + return ("+4dBu"); + case HDSPE_INPUT_LEVEL_MINUS10DBV: + return ("-10dBV"); + default: + return (NULL); + } +} + +static int +hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS) +{ + struct sc_info *sc; + const char *label; + char buf[16] = "invalid"; + int error; + uint32_t settings; + + sc = oidp->oid_arg1; + + /* Only available on HDSPE AIO. */ + if (sc->type != HDSPE_AIO) + return (ENXIO); + + /* Extract current input level from settings register. */ + settings = sc->settings_register & HDSPE_INPUT_LEVEL_MASK; + label = hdspe_settings_input_level(settings); + if (label != NULL) + strlcpy(buf, label, sizeof(buf)); + + /* Process sysctl string request. */ + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* Find input level matching the sysctl string. */ + label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_LOWGAIN); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_INPUT_LEVEL_LOWGAIN; + label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_PLUS4DBU); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_INPUT_LEVEL_PLUS4DBU; + label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_MINUS10DBV); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_INPUT_LEVEL_MINUS10DBV; + + /* Set input level in settings register. */ + settings &= HDSPE_INPUT_LEVEL_MASK; + if (settings != (sc->settings_register & HDSPE_INPUT_LEVEL_MASK)) { + snd_mtxlock(sc->lock); + sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK; + sc->settings_register |= settings; + hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); + snd_mtxunlock(sc->lock); + } + return (0); +} + +static const char * +hdspe_settings_output_level(uint32_t settings) +{ + switch (settings & HDSPE_OUTPUT_LEVEL_MASK) { + case HDSPE_OUTPUT_LEVEL_HIGHGAIN: + return ("HighGain"); + case HDSPE_OUTPUT_LEVEL_PLUS4DBU: + return ("+4dBu"); + case HDSPE_OUTPUT_LEVEL_MINUS10DBV: + return ("-10dBV"); + default: + return (NULL); + } +} + +static int +hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS) +{ + struct sc_info *sc; + const char *label; + char buf[16] = "invalid"; + int error; + uint32_t settings; + + sc = oidp->oid_arg1; + + /* Only available on HDSPE AIO. */ + if (sc->type != HDSPE_AIO) + return (ENXIO); + + /* Extract current output level from settings register. */ + settings = sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK; + label = hdspe_settings_output_level(settings); + if (label != NULL) + strlcpy(buf, label, sizeof(buf)); + + /* Process sysctl string request. */ + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* Find output level matching the sysctl string. */ + label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_HIGHGAIN); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_OUTPUT_LEVEL_HIGHGAIN; + label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_PLUS4DBU); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_OUTPUT_LEVEL_PLUS4DBU; + label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_MINUS10DBV); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_OUTPUT_LEVEL_MINUS10DBV; + + /* Set output level in settings register. */ + settings &= HDSPE_OUTPUT_LEVEL_MASK; + if (settings != (sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK)) { + snd_mtxlock(sc->lock); + sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK; + sc->settings_register |= settings; + hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); + snd_mtxunlock(sc->lock); + } + return (0); +} + +static const char * +hdspe_settings_phones_level(uint32_t settings) +{ + switch (settings & HDSPE_PHONES_LEVEL_MASK) { + case HDSPE_PHONES_LEVEL_HIGHGAIN: + return ("HighGain"); + case HDSPE_PHONES_LEVEL_PLUS4DBU: + return ("+4dBu"); + case HDSPE_PHONES_LEVEL_MINUS10DBV: + return ("-10dBV"); + default: + return (NULL); + } +} + +static int +hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS) +{ + struct sc_info *sc; + const char *label; + char buf[16] = "invalid"; + int error; + uint32_t settings; + + sc = oidp->oid_arg1; + + /* Only available on HDSPE AIO. */ + if (sc->type != HDSPE_AIO) + return (ENXIO); + + /* Extract current phones level from settings register. */ + settings = sc->settings_register & HDSPE_PHONES_LEVEL_MASK; + label = hdspe_settings_phones_level(settings); + if (label != NULL) + strlcpy(buf, label, sizeof(buf)); + + /* Process sysctl string request. */ + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* Find phones level matching the sysctl string. */ + label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_HIGHGAIN); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_PHONES_LEVEL_HIGHGAIN; + label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_PLUS4DBU); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_PHONES_LEVEL_PLUS4DBU; + label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_MINUS10DBV); + if (strncasecmp(buf, label, sizeof(buf)) == 0) + settings = HDSPE_PHONES_LEVEL_MINUS10DBV; + + /* Set phones level in settings register. */ + settings &= HDSPE_PHONES_LEVEL_MASK; + if (settings != (sc->settings_register & HDSPE_PHONES_LEVEL_MASK)) { + snd_mtxlock(sc->lock); + sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK; + sc->settings_register |= settings; + hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); + snd_mtxunlock(sc->lock); + } + return (0); +} + static int hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) { @@ -529,6 +721,15 @@ hdspe_init(struct sc_info *sc) /* Other settings. */ sc->settings_register = 0; + + /* Default gain levels. */ + sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK; + sc->settings_register |= HDSPE_INPUT_LEVEL_LOWGAIN; + sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK; + sc->settings_register |= HDSPE_OUTPUT_LEVEL_MINUS10DBV; + sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK; + sc->settings_register |= HDSPE_PHONES_LEVEL_MINUS10DBV; + hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); return (0); @@ -623,6 +824,27 @@ hdspe_attach(device_t dev) sc, 0, hdspe_sysctl_sample_rate, "A", "Force sample rate (32000, 44100, 48000, ... 192000)"); + if (sc->type == HDSPE_AIO) { + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + sc, 0, hdspe_sysctl_phones_level, "A", + "Phones output level ('HighGain', '+4dBU', '-10dBV')"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + sc, 0, hdspe_sysctl_output_level, "A", + "Analog output level ('HighGain', '+4dBU', '-10dBV')"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + sc, 0, hdspe_sysctl_input_level, "A", + "Analog input level ('LowGain', '+4dBU', '-10dBV')"); + } + + return (bus_generic_attach(dev)); } diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h index 2f0bdddd930f..bced78758068 100644 --- a/sys/dev/sound/pci/hdspe.h +++ b/sys/dev/sound/pci/hdspe.h @@ -74,35 +74,43 @@ #define HDSPE_LAT_BYTES_MIN (32 * 4) #define hdspe_encode_latency(x) (((x)<<1) & HDSPE_LAT_MASK) -/* Gain */ -#define HDSP_ADGain0 (1 << 25) -#define HDSP_ADGain1 (1 << 26) -#define HDSP_DAGain0 (1 << 27) -#define HDSP_DAGain1 (1 << 28) -#define HDSP_PhoneGain0 (1 << 29) -#define HDSP_PhoneGain1 (1 << 30) - -#define HDSP_ADGainMask (HDSP_ADGain0 | HDSP_ADGain1) -#define HDSP_ADGainMinus10dBV (HDSP_ADGainMask) -#define HDSP_ADGainPlus4dBu (HDSP_ADGain0) -#define HDSP_ADGainLowGain 0 - -#define HDSP_DAGainMask (HDSP_DAGain0 | HDSP_DAGain1) -#define HDSP_DAGainHighGain (HDSP_DAGainMask) -#define HDSP_DAGainPlus4dBu (HDSP_DAGain0) -#define HDSP_DAGainMinus10dBV 0 - -#define HDSP_PhoneGainMask (HDSP_PhoneGain0|HDSP_PhoneGain1) -#define HDSP_PhoneGain0dB HDSP_PhoneGainMask -#define HDSP_PhoneGainMinus6dB (HDSP_PhoneGain0) -#define HDSP_PhoneGainMinus12dB 0 - -/* Settings */ +/* Register addresses */ #define HDSPE_SETTINGS_REG 0 #define HDSPE_CONTROL_REG 64 #define HDSPE_STATUS_REG 0 #define HDSPE_STATUS1_REG 64 #define HDSPE_STATUS2_REG 192 + +/* Settings register flags */ +#define HDSPE_SETTINGS_INPUT_GAIN0 (1 << 20) +#define HDSPE_SETTINGS_INPUT_GAIN1 (1 << 21) +#define HDSPE_SETTINGS_OUTPUT_GAIN0 (1 << 22) +#define HDSPE_SETTINGS_OUTPUT_GAIN1 (1 << 23) +#define HDSPE_SETTINGS_PHONES_GAIN0 (1 << 24) +#define HDSPE_SETTINGS_PHONES_GAIN1 (1 << 25) + +/* Analog input gain level */ +#define HDSPE_INPUT_LEVEL_MASK (HDSPE_SETTINGS_INPUT_GAIN0 | \ + HDSPE_SETTINGS_INPUT_GAIN1) +#define HDSPE_INPUT_LEVEL_LOWGAIN 0 +#define HDSPE_INPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_INPUT_GAIN0) +#define HDSPE_INPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_INPUT_GAIN1) + +/* Analog output gain level */ +#define HDSPE_OUTPUT_LEVEL_MASK (HDSPE_SETTINGS_OUTPUT_GAIN0 | \ + HDSPE_SETTINGS_OUTPUT_GAIN1) +#define HDSPE_OUTPUT_LEVEL_HIGHGAIN 0 +#define HDSPE_OUTPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_OUTPUT_GAIN0) +#define HDSPE_OUTPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_OUTPUT_GAIN1) + +/* Phones output gain level */ +#define HDSPE_PHONES_LEVEL_MASK (HDSPE_SETTINGS_PHONES_GAIN0 | \ + HDSPE_SETTINGS_PHONES_GAIN1) +#define HDSPE_PHONES_LEVEL_HIGHGAIN 0 +#define HDSPE_PHONES_LEVEL_PLUS4DBU (HDSPE_SETTINGS_PHONES_GAIN0) +#define HDSPE_PHONES_LEVEL_MINUS10DBV (HDSPE_SETTINGS_PHONES_GAIN1) + +/* Control register flags */ #define HDSPE_ENABLE (1 << 0) /* Interrupts */