git: 6c892b79de59 - main - snd_hdspe(4): Per device sysctl for sample rate.

From: Ruslan Bukin <br_at_FreeBSD.org>
Date: Tue, 30 Jan 2024 15:14:59 UTC
The branch main has been updated by br:

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

commit 6c892b79de590443a180269507f61f3d8a9ec879
Author:     Florian Walpen <dev@submerge.ch>
AuthorDate: 2024-01-30 15:07:57 +0000
Commit:     Ruslan Bukin <br@FreeBSD.org>
CommitDate: 2024-01-30 15:07:57 +0000

    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
---
 share/man/man4/snd_hdspe.4    |  6 +++++-
 sys/dev/sound/pci/hdspe-pcm.c |  3 +++
 sys/dev/sound/pci/hdspe.c     | 42 ++++++++++++++++++++++++++++++++++++++++++
 sys/dev/sound/pci/hdspe.h     |  1 +
 4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/share/man/man4/snd_hdspe.4 b/share/man/man4/snd_hdspe.4
index d4ddc70f4070..7d8cc2adc1af 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 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
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index f6c7c84e026e..cf764f19a933 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -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)
diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c
index b1ca67bdc313..1d1068c9f8fe 100644
--- a/sys/dev/sound/pci/hdspe.c
+++ b/sys/dev/sound/pci/hdspe.c
@@ -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));
 }
 
diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h
index 81116e0cd437..26694f60ce58 100644
--- a/sys/dev/sound/pci/hdspe.h
+++ b/sys/dev/sound/pci/hdspe.h
@@ -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)						\