diff mbox series

[3/3] usb: gadget: u_audio: add real feedback implementation

Message ID 1604794711-8661-4-git-send-email-ruslan.bilovol@gmail.com (mailing list archive)
State New, archived
Headers show
Series UAC2 Gadget: feedback endpoint support | expand

Commit Message

Ruslan Bilovol Nov. 8, 2020, 12:18 a.m. UTC
This adds interface between userspace and feedback
endpoint to report real feedback frequency to the Host.

Current implementation adds new userspace interface
ALSA mixer control "PCM Feedback Frequency Hz" (similar
to aloop driver's "PCM Rate Shift 100000" mixer control)

We allow +/-20% deviation of nominal sampling frequency,
that usually is more than enough in real-world usecases

Usage of this new control is easy to implement in
existing userspace tools like alsaloop from alsa-utils.

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/function/f_uac2.c  |  4 ++
 drivers/usb/gadget/function/u_audio.c | 93 +++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/function/u_audio.h |  7 +++
 3 files changed, 104 insertions(+)

Comments

Pavel Hofman Nov. 9, 2020, 8:24 a.m. UTC | #1
Hi Bilovol,

Dne 08. 11. 20 v 1:18 Ruslan Bilovol napsal(a):
> This adds interface between userspace and feedback
> endpoint to report real feedback frequency to the Host.
> 
> Current implementation adds new userspace interface
> ALSA mixer control "PCM Feedback Frequency Hz" (similar
> to aloop driver's "PCM Rate Shift 100000" mixer control)
> 
> We allow +/-20% deviation of nominal sampling frequency,
> that usually is more than enough in real-world usecases
> 
> Usage of this new control is easy to implement in
> existing userspace tools like alsaloop from alsa-utils.
> 
> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
> ---
>  drivers/usb/gadget/function/f_uac2.c  |  4 ++
>  drivers/usb/gadget/function/u_audio.c | 93 +++++++++++++++++++++++++++++++++++
>  drivers/usb/gadget/function/u_audio.h |  7 +++
>  3 files changed, 104 insertions(+)

Thanks a lot for the great implementation. IIUC the control element sets
integer frequency in Hz. Often the clocks deviate by small fractions of
Hz. Please have you considered the value to be e.g. in 100th of Hz for
finer control of the samplerate? Similar to the PCM Rate Shift which has
a step 100000th of the samplerate.

Thanks a lot.

Best regards,

Pavel.
Pavel Hofman Nov. 9, 2020, 8:25 a.m. UTC | #2
Hi Ruslan, please excuse my stupid error, I really apologize...

Dne 09. 11. 20 v 9:24 Pavel Hofman napsal(a):
> Hi Bilovol,
> 
> Dne 08. 11. 20 v 1:18 Ruslan Bilovol napsal(a):
>> This adds interface between userspace and feedback
>> endpoint to report real feedback frequency to the Host.
>>
>> Current implementation adds new userspace interface
>> ALSA mixer control "PCM Feedback Frequency Hz" (similar
>> to aloop driver's "PCM Rate Shift 100000" mixer control)
>>
>> We allow +/-20% deviation of nominal sampling frequency,
>> that usually is more than enough in real-world usecases
>>
>> Usage of this new control is easy to implement in
>> existing userspace tools like alsaloop from alsa-utils.
>>
>> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
>> ---
>>  drivers/usb/gadget/function/f_uac2.c  |  4 ++
>>  drivers/usb/gadget/function/u_audio.c | 93 +++++++++++++++++++++++++++++++++++
>>  drivers/usb/gadget/function/u_audio.h |  7 +++
>>  3 files changed, 104 insertions(+)
> 
> Thanks a lot for the great implementation. IIUC the control element sets
> integer frequency in Hz. Often the clocks deviate by small fractions of
> Hz. Please have you considered the value to be e.g. in 100th of Hz for
> finer control of the samplerate? Similar to the PCM Rate Shift which has
> a step 100000th of the samplerate.
> 
> Thanks a lot.
> 
> Best regards,
> 
> Pavel.
>
Pavel Hofman Nov. 12, 2020, 11:26 a.m. UTC | #3
Dne 09. 11. 20 v 9:24 Pavel Hofman napsal(a):
> Hi Ruslan,
> 
> Dne 08. 11. 20 v 1:18 Ruslan Bilovol napsal(a):
>> This adds interface between userspace and feedback
>> endpoint to report real feedback frequency to the Host.
>>
>> Current implementation adds new userspace interface
>> ALSA mixer control "PCM Feedback Frequency Hz" (similar
>> to aloop driver's "PCM Rate Shift 100000" mixer control)
>>
>> We allow +/-20% deviation of nominal sampling frequency,
>> that usually is more than enough in real-world usecases
>>
>> Usage of this new control is easy to implement in
>> existing userspace tools like alsaloop from alsa-utils.
>>
>> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
>> ---
>>   drivers/usb/gadget/function/f_uac2.c  |  4 ++
>>   drivers/usb/gadget/function/u_audio.c | 93 +++++++++++++++++++++++++++++++++++
>>   drivers/usb/gadget/function/u_audio.h |  7 +++
>>   3 files changed, 104 insertions(+)
> 
> Thanks a lot for the great implementation. IIUC the control element sets
> integer frequency in Hz. Often the clocks deviate by small fractions of
> Hz. Please have you considered the value to be e.g. in 100th of Hz for
> finer control of the samplerate? Similar to the PCM Rate Shift which has
> a step 100000th of the samplerate.
> 

My stupid, I did not realize that one Hz in the samplerate value is 
basically the same order of magnitude as the 100,000ths in the "PCM Rate 
Shift 100000" mixer control. Sorry for disturbing.

Thanks,

Pavel.
diff mbox series

Patch

diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 3187ad3..ebdb2a1 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -487,6 +487,10 @@  static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
 
 	max_packet_size = num_channels(chmask) * ssize *
 		DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1)));
+
+	if (!is_playback && (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC))
+		max_packet_size = max_packet_size * FBACK_FREQ_MAX / 100;
+
 	ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_packet_size,
 				le16_to_cpu(ep_desc->wMaxPacketSize)));
 }
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 33c9288..ae9e1f3 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@ 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/control.h>
 
 #include "u_audio.h"
 
@@ -596,12 +597,87 @@  void u_audio_stop_playback(struct g_audio *audio_dev)
 }
 EXPORT_SYMBOL_GPL(u_audio_stop_playback);
 
+static int u_audio_rate_shift_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+	struct snd_uac_chip *uac = prm->uac;
+	struct g_audio *audio_dev = uac->audio_dev;
+	struct uac_params *params = &audio_dev->params;
+	unsigned int ffback_min, ffback_max;
+
+	ffback_min = params->c_srate * FBACK_FREQ_MIN / 100;
+	ffback_max = params->c_srate * FBACK_FREQ_MIN / 100;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = ffback_min;
+	uinfo->value.integer.max = ffback_max;
+	uinfo->value.integer.step = 1;
+	return 0;
+}
+
+static int u_audio_rate_shift_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&prm->lock, flags);
+	ucontrol->value.integer.value[0] = prm->ffback;
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	return 0;
+}
+
+static int u_audio_rate_shift_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+	struct snd_uac_chip *uac = prm->uac;
+	struct g_audio *audio_dev = uac->audio_dev;
+	struct uac_params *params = &audio_dev->params;
+	unsigned int val;
+	unsigned int ffback_min, ffback_max;
+	unsigned long flags;
+	int change = 0;
+
+	ffback_min = params->c_srate * FBACK_FREQ_MIN / 100;
+	ffback_max = params->c_srate * FBACK_FREQ_MAX / 100;
+
+	val = ucontrol->value.integer.value[0];
+	if (val < ffback_min)
+		val = ffback_min;
+	if (val > ffback_max)
+		val = ffback_max;
+
+	spin_lock_irqsave(&prm->lock, flags);
+	if (prm->ffback != val) {
+		prm->ffback = val;
+		change = 1;
+	}
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	return change;
+}
+
+static const struct snd_kcontrol_new u_audio_controls[]  = {
+{
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Feedback Frequency Hz",
+	.info =         u_audio_rate_shift_info,
+	.get =          u_audio_rate_shift_get,
+	.put =          u_audio_rate_shift_put,
+},
+};
+
 int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
 					const char *card_name)
 {
 	struct snd_uac_chip *uac;
 	struct snd_card *card;
 	struct snd_pcm *pcm;
+	struct snd_kcontrol *kctl;
 	struct uac_params *params;
 	int p_chmask, c_chmask;
 	int err;
@@ -687,6 +763,23 @@  int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
 
+	if (c_chmask && g_audio->in_ep_fback) {
+		strlcpy(card->mixername, card_name, sizeof(card->driver));
+
+		kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
+		if (!kctl) {
+			err = -ENOMEM;
+			goto snd_fail;
+		}
+
+		kctl->id.device = pcm->device;
+		kctl->id.subdevice = 0;
+
+		err = snd_ctl_add(card, kctl);
+		if (err < 0)
+			goto snd_fail;
+	}
+
 	strlcpy(card->driver, card_name, sizeof(card->driver));
 	strlcpy(card->shortname, card_name, sizeof(card->shortname));
 	sprintf(card->longname, "%s %i", card_name, card->dev->id);
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 53e6baf..fd70808 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -11,6 +11,13 @@ 
 
 #include <linux/usb/composite.h>
 
+/*
+ * Min/max percentage of nominal sampling frequency deviation
+ * reported through feedback endpoint to the host
+ */
+#define FBACK_FREQ_MIN	80
+#define FBACK_FREQ_MAX	120
+
 struct uac_params {
 	/* playback */
 	int p_chmask;	/* channel mask */