snd_hdspe(4): Per device sysctl for sample rate.

Some hardware setups require a specific sample rate due to devices being
connected to digital ports (AES, S/PDIF, ADAT). Add a per device sysctl
"sample_rate" to let the user override sample rate requests from the pcm
infrastructure, when needed.

Differential Revision:	https://reviews.freebsd.org/D43659
This commit is contained in:
Florian Walpen 2024-01-30 15:07:57 +00:00 committed by Ruslan Bukin
parent 0c0ec5c8cc
commit 6c892b79de
4 changed files with 51 additions and 1 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 19, 2024
.Dd January 29, 2024
.Dt SND_HDSPE 4
.Os
.Sh NAME
@ -76,6 +76,10 @@ To adjust the following sysctl identifiers for a specific sound card, insert
the respective device number in place of
.Ql 0 .
.Bl -tag -width indent
.It Va dev.hdspe.0.sample_rate
Set a fixed sample rate from 32000, 44100, 48000, up to 192000.
This is usually required for digital connections (AES, S/PDIF, ADAT).
The default value of 0 adjusts the sample rate according to pcm device settings.
.It Va dev.hdspe.0.period
The number of samples processed per interrupt, from 32, 64, 128, up to 4096.
Setting a lower value here results in less latency, but increases system load

View file

@ -814,6 +814,9 @@ hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed)
if (hdspe_running(sc) == 1)
goto end;
if (sc->force_speed > 0)
speed = sc->force_speed;
/* First look for equal frequency. */
for (i = 0; rate_map[i].speed != 0; i++) {
if (rate_map[i].speed == speed)

View file

@ -228,6 +228,41 @@ hdspe_map_dmabuf(struct sc_info *sc)
}
}
static int
hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
{
struct sc_info *sc = oidp->oid_arg1;
int error;
unsigned int speed, multiplier;
speed = sc->force_speed;
/* Process sysctl (unsigned) integer request. */
error = sysctl_handle_int(oidp, &speed, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
/* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */
sc->force_speed = 0;
if (speed > 0) {
multiplier = 1;
if (speed > (96000 + 128000) / 2)
multiplier = 4;
else if (speed > (48000 + 64000) / 2)
multiplier = 2;
if (speed < ((32000 + 44100) / 2) * multiplier)
sc->force_speed = 32000 * multiplier;
else if (speed < ((44100 + 48000) / 2) * multiplier)
sc->force_speed = 44100 * multiplier;
else
sc->force_speed = 48000 * multiplier;
}
return (0);
}
static int
hdspe_sysctl_period(SYSCTL_HANDLER_ARGS)
{
@ -455,6 +490,7 @@ hdspe_init(struct sc_info *sc)
/* Set rate. */
sc->speed = HDSPE_SPEED_DEFAULT;
sc->force_speed = 0;
sc->ctrl_register &= ~HDSPE_FREQ_MASK;
sc->ctrl_register |= HDSPE_FREQ_MASK_DEFAULT;
hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
@ -562,6 +598,12 @@ hdspe_attach(device_t dev)
sc, 0, hdspe_sysctl_period, "A",
"Force period of samples per interrupt (32, 64, ... 4096)");
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
sc, 0, hdspe_sysctl_sample_rate, "A",
"Force sample rate (32000, 44100, 48000, ... 192000)");
return (bus_generic_attach(dev));
}

View file

@ -228,6 +228,7 @@ struct sc_info {
uint32_t period;
uint32_t speed;
uint32_t force_period;
uint32_t force_speed;
};
#define hdspe_read_1(sc, regno) \