diff mbox series

[1/2] ALSA: usb-audio: Add quirk for RME Digiface USB

Message ID 20240902-rme-digiface-v1-1-6e88472a2744@asahilina.net (mailing list archive)
State Superseded
Headers show
Series ALSA: usb-audio: Add basic support for RME Digiface USB | expand

Commit Message

Asahi Lina Sept. 1, 2024, 9:31 p.m. UTC
From: Cyan Nyan <cyan.vtb@gmail.com>

Add trivial support for audio streaming on the RME Digiface USB. Binds
only to the first interface to allow userspace to directly drive the
complex I/O and matrix mixer controls.

Signed-off-by: Cyan Nyan <cyan.vtb@gmail.com>
[Lina: Added 2x/4x sample rate support & boot/format quirks]
Co-developed-by: Asahi Lina <lina@asahilina.net>
Signed-off-by: Asahi Lina <lina@asahilina.net>
---
 sound/usb/quirks-table.h | 190 ++++++++++++++++++++++++++++++++++++++++++++++-
 sound/usb/quirks.c       |  58 +++++++++++++++
 2 files changed, 247 insertions(+), 1 deletion(-)

Comments

Takashi Iwai Sept. 2, 2024, 6:10 a.m. UTC | #1
On Sun, 01 Sep 2024 23:31:50 +0200,
Asahi Lina wrote:
> 
> From: Cyan Nyan <cyan.vtb@gmail.com>
> 
> Add trivial support for audio streaming on the RME Digiface USB. Binds
> only to the first interface to allow userspace to directly drive the
> complex I/O and matrix mixer controls.
> 
> Signed-off-by: Cyan Nyan <cyan.vtb@gmail.com>
> [Lina: Added 2x/4x sample rate support & boot/format quirks]
> Co-developed-by: Asahi Lina <lina@asahilina.net>
> Signed-off-by: Asahi Lina <lina@asahilina.net>
> ---
>  sound/usb/quirks-table.h | 190 ++++++++++++++++++++++++++++++++++++++++++++++-
>  sound/usb/quirks.c       |  58 +++++++++++++++
>  2 files changed, 247 insertions(+), 1 deletion(-)
> 
> diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
> index 73abc38a5400..47fd18791396 100644
> --- a/sound/usb/quirks-table.h
> +++ b/sound/usb/quirks-table.h
> @@ -4546,6 +4546,194 @@ YAMAHA_DEVICE(0x7010, "UB99"),
>  		}
>  	}
>  },
> -
> +{
> +	/* Only claim interface 0 */
> +	.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
> +		       USB_DEVICE_ID_MATCH_PRODUCT |
> +		       USB_DEVICE_ID_MATCH_INT_CLASS |
> +		       USB_DEVICE_ID_MATCH_INT_NUMBER,
> +	.idVendor = 0x2a39,
> +	.idProduct = 0x3f8c,
> +	.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
> +	.bInterfaceNumber = 0,
> +	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
> +		.ifnum = QUIRK_ANY_INTERFACE,
> +		.type = QUIRK_COMPOSITE,
(snip)

Recently the quirk tables are rewritten with macros.
See the commit d79e13f8e8abb5cd3a2a0f9fc9bc3fc750c5b06f
    ALSA: usb-audio: Replace complex quirk lines with macros
in for-next branch of sound.git tree.

Could you try to apply to your new entries, too?


thanks,

Takashi
Asahi Lina Sept. 2, 2024, 8:12 a.m. UTC | #2
On 9/2/24 3:10 PM, Takashi Iwai wrote:
> On Sun, 01 Sep 2024 23:31:50 +0200,
> Asahi Lina wrote:
>>
>> From: Cyan Nyan <cyan.vtb@gmail.com>
>>
>> Add trivial support for audio streaming on the RME Digiface USB. Binds
>> only to the first interface to allow userspace to directly drive the
>> complex I/O and matrix mixer controls.
>>
>> Signed-off-by: Cyan Nyan <cyan.vtb@gmail.com>
>> [Lina: Added 2x/4x sample rate support & boot/format quirks]
>> Co-developed-by: Asahi Lina <lina@asahilina.net>
>> Signed-off-by: Asahi Lina <lina@asahilina.net>
>> ---
>>  sound/usb/quirks-table.h | 190 ++++++++++++++++++++++++++++++++++++++++++++++-
>>  sound/usb/quirks.c       |  58 +++++++++++++++
>>  2 files changed, 247 insertions(+), 1 deletion(-)
>>
>> diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
>> index 73abc38a5400..47fd18791396 100644
>> --- a/sound/usb/quirks-table.h
>> +++ b/sound/usb/quirks-table.h
>> @@ -4546,6 +4546,194 @@ YAMAHA_DEVICE(0x7010, "UB99"),
>>  		}
>>  	}
>>  },
>> -
>> +{
>> +	/* Only claim interface 0 */
>> +	.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
>> +		       USB_DEVICE_ID_MATCH_PRODUCT |
>> +		       USB_DEVICE_ID_MATCH_INT_CLASS |
>> +		       USB_DEVICE_ID_MATCH_INT_NUMBER,
>> +	.idVendor = 0x2a39,
>> +	.idProduct = 0x3f8c,
>> +	.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
>> +	.bInterfaceNumber = 0,
>> +	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
>> +		.ifnum = QUIRK_ANY_INTERFACE,
>> +		.type = QUIRK_COMPOSITE,
> (snip)
> 
> Recently the quirk tables are rewritten with macros.
> See the commit d79e13f8e8abb5cd3a2a0f9fc9bc3fc750c5b06f
>     ALSA: usb-audio: Replace complex quirk lines with macros
> in for-next branch of sound.git tree.
> 
> Could you try to apply to your new entries, too?

Sure! Note that I open-coded the USB match because I need to add
USB_DEVICE_ID_MATCH_INT_NUMBER (to stop the driver from binding to and
claiming all interfaces, QUIRK_DATA_IGNORE() is not enough for that).
Should I leave it like that (since it's the only case so far) or do you
want me to introduce another macro for that case?

I'll rewrite the quirk info stuff with the macros for v2. I'll also
remove USB_ENDPOINT_USAGE_IMPLICIT_FB since I'm pretty sure that does
nothing here (at first I thought I could get it to pick up the implicit
FB with that but I ended up having to specify the sync/implicit_fb stuff
explicitly).

~~ Lina
Takashi Iwai Sept. 2, 2024, 8:16 a.m. UTC | #3
On Mon, 02 Sep 2024 10:12:18 +0200,
Asahi Lina wrote:
> 
> 
> 
> On 9/2/24 3:10 PM, Takashi Iwai wrote:
> > On Sun, 01 Sep 2024 23:31:50 +0200,
> > Asahi Lina wrote:
> >>
> >> From: Cyan Nyan <cyan.vtb@gmail.com>
> >>
> >> Add trivial support for audio streaming on the RME Digiface USB. Binds
> >> only to the first interface to allow userspace to directly drive the
> >> complex I/O and matrix mixer controls.
> >>
> >> Signed-off-by: Cyan Nyan <cyan.vtb@gmail.com>
> >> [Lina: Added 2x/4x sample rate support & boot/format quirks]
> >> Co-developed-by: Asahi Lina <lina@asahilina.net>
> >> Signed-off-by: Asahi Lina <lina@asahilina.net>
> >> ---
> >>  sound/usb/quirks-table.h | 190 ++++++++++++++++++++++++++++++++++++++++++++++-
> >>  sound/usb/quirks.c       |  58 +++++++++++++++
> >>  2 files changed, 247 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
> >> index 73abc38a5400..47fd18791396 100644
> >> --- a/sound/usb/quirks-table.h
> >> +++ b/sound/usb/quirks-table.h
> >> @@ -4546,6 +4546,194 @@ YAMAHA_DEVICE(0x7010, "UB99"),
> >>  		}
> >>  	}
> >>  },
> >> -
> >> +{
> >> +	/* Only claim interface 0 */
> >> +	.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
> >> +		       USB_DEVICE_ID_MATCH_PRODUCT |
> >> +		       USB_DEVICE_ID_MATCH_INT_CLASS |
> >> +		       USB_DEVICE_ID_MATCH_INT_NUMBER,
> >> +	.idVendor = 0x2a39,
> >> +	.idProduct = 0x3f8c,
> >> +	.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
> >> +	.bInterfaceNumber = 0,
> >> +	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
> >> +		.ifnum = QUIRK_ANY_INTERFACE,
> >> +		.type = QUIRK_COMPOSITE,
> > (snip)
> > 
> > Recently the quirk tables are rewritten with macros.
> > See the commit d79e13f8e8abb5cd3a2a0f9fc9bc3fc750c5b06f
> >     ALSA: usb-audio: Replace complex quirk lines with macros
> > in for-next branch of sound.git tree.
> > 
> > Could you try to apply to your new entries, too?
> 
> Sure! Note that I open-coded the USB match because I need to add
> USB_DEVICE_ID_MATCH_INT_NUMBER (to stop the driver from binding to and
> claiming all interfaces, QUIRK_DATA_IGNORE() is not enough for that).
> Should I leave it like that (since it's the only case so far) or do you
> want me to introduce another macro for that case?

The open-code with match_flags is fine, you can keep that.
If we have other similar cases, we can introduce a macro for cleanup
later, too.


> I'll rewrite the quirk info stuff with the macros for v2. I'll also
> remove USB_ENDPOINT_USAGE_IMPLICIT_FB since I'm pretty sure that does
> nothing here (at first I thought I could get it to pick up the implicit
> FB with that but I ended up having to specify the sync/implicit_fb stuff
> explicitly).

Sounds good.  Thanks!


Takashi
diff mbox series

Patch

diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 73abc38a5400..47fd18791396 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -4546,6 +4546,194 @@  YAMAHA_DEVICE(0x7010, "UB99"),
 		}
 	}
 },
-
+{
+	/* Only claim interface 0 */
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+		       USB_DEVICE_ID_MATCH_PRODUCT |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_NUMBER,
+	.idVendor = 0x2a39,
+	.idProduct = 0x3f8c,
+	.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+	.bInterfaceNumber = 0,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			/*
+			 * Three modes depending on sample rate band,
+			 * with different channel counts for in/out
+			 */
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 34, // outputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x02,
+					.ep_idx = 1,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC,
+					.rates = SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_44100 |
+						SNDRV_PCM_RATE_48000,
+					.rate_min = 32000,
+					.rate_max = 48000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						32000, 44100, 48000,
+					},
+					.sync_ep = 0x81,
+					.sync_iface = 0,
+					.sync_altsetting = 1,
+					.sync_ep_idx = 0,
+					.implicit_fb = 1,
+				},
+			},
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 18, // outputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x02,
+					.ep_idx = 1,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC,
+					.rates = SNDRV_PCM_RATE_64000 |
+						SNDRV_PCM_RATE_88200 |
+						SNDRV_PCM_RATE_96000,
+					.rate_min = 64000,
+					.rate_max = 96000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						64000, 88200, 96000,
+					},
+					.sync_ep = 0x81,
+					.sync_iface = 0,
+					.sync_altsetting = 1,
+					.sync_ep_idx = 0,
+					.implicit_fb = 1,
+				},
+			},
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 10, // outputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x02,
+					.ep_idx = 1,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC,
+					.rates = SNDRV_PCM_RATE_KNOT |
+						SNDRV_PCM_RATE_176400 |
+						SNDRV_PCM_RATE_192000,
+					.rate_min = 128000,
+					.rate_max = 192000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						128000, 176400, 192000,
+					},
+					.sync_ep = 0x81,
+					.sync_iface = 0,
+					.sync_altsetting = 1,
+					.sync_ep_idx = 0,
+					.implicit_fb = 1,
+				},
+			},
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 32, // inputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x81,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC|
+						USB_ENDPOINT_USAGE_IMPLICIT_FB,
+					.rates = SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_44100 |
+						SNDRV_PCM_RATE_48000,
+					.rate_min = 32000,
+					.rate_max = 48000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						32000, 44100, 48000,
+					}
+				}
+			},
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 16, // inputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x81,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC|
+						USB_ENDPOINT_USAGE_IMPLICIT_FB,
+					.rates = SNDRV_PCM_RATE_64000 |
+						SNDRV_PCM_RATE_88200 |
+						SNDRV_PCM_RATE_96000,
+					.rate_min = 64000,
+					.rate_max = 96000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						64000, 88200, 96000,
+					}
+				}
+			},
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = &(const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S32_LE,
+					.channels = 8, // inputs
+					.fmt_bits = 24,
+					.iface = 0,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.endpoint = 0x81,
+					.ep_attr = USB_ENDPOINT_XFER_ISOC|
+						USB_ENDPOINT_SYNC_ASYNC|
+						USB_ENDPOINT_USAGE_IMPLICIT_FB,
+					.rates = SNDRV_PCM_RATE_KNOT |
+						SNDRV_PCM_RATE_176400 |
+						SNDRV_PCM_RATE_192000,
+					.rate_min = 128000,
+					.rate_max = 192000,
+					.nr_rates = 3,
+					.rate_table = (unsigned int[]) {
+						128000, 176400, 192000,
+					}
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
 #undef USB_DEVICE_VENDOR_SPEC
 #undef USB_AUDIO_DEVICE
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index ea063a14cdd8..3e44595add89 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1389,6 +1389,27 @@  static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev)
 	return 0;
 }
 
+static int snd_usb_rme_digiface_boot_quirk(struct usb_device *dev)
+{
+	/* Disable mixer, internal clock, all outputs ADAT, 48kHz, TMS off */
+	snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+			16, 0x40, 0x2410, 0x7fff, NULL, 0);
+	snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+			18, 0x40, 0x0104, 0xffff, NULL, 0);
+
+	/* Disable loopback for all inputs */
+	for (int ch = 0; ch < 32; ch++)
+		snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+				22, 0x40, 0x400, ch, NULL, 0);
+
+	/* Unity gain for all outputs */
+	for (int ch = 0; ch < 34; ch++)
+		snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+				21, 0x40, 0x9000, 0x100 + ch, NULL, 0);
+
+	return 0;
+}
+
 /*
  * Setup quirks
  */
@@ -1616,6 +1637,8 @@  int snd_usb_apply_boot_quirk(struct usb_device *dev,
 		    get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
 			return snd_usb_motu_microbookii_boot_quirk(dev);
 		break;
+	case USB_ID(0x2a39, 0x3f8c): /* RME Digiface USB */
+		return snd_usb_rme_digiface_boot_quirk(dev);
 	}
 
 	return 0;
@@ -1771,6 +1794,38 @@  static void mbox3_set_format_quirk(struct snd_usb_substream *subs,
 		dev_warn(&subs->dev->dev, "MBOX3: Couldn't set the sample rate");
 }
 
+static const int rme_digiface_rate_table[] = {
+	32000, 44100, 48000, 0,
+	64000, 88200, 96000, 0,
+	128000, 176400, 192000, 0,
+};
+
+static int rme_digiface_set_format_quirk(struct snd_usb_substream *subs)
+{
+	unsigned int cur_rate = subs->data_endpoint->cur_rate;
+	u16 val;
+	int speed_mode;
+	int id;
+
+	for (id = 0; id < ARRAY_SIZE(rme_digiface_rate_table); id++) {
+		if (rme_digiface_rate_table[id] == cur_rate)
+			break;
+	}
+
+	if (id >= ARRAY_SIZE(rme_digiface_rate_table))
+		return -EINVAL;
+
+	/* 2, 3, 4 for 1x, 2x, 4x */
+	speed_mode = (id >> 2) + 2;
+	val = (id << 3) | (speed_mode << 12);
+
+	/* Set the sample rate */
+	snd_usb_ctl_msg(subs->stream->chip->dev,
+		usb_sndctrlpipe(subs->stream->chip->dev, 0),
+		16, 0x40, val, 0x7078, NULL, 0);
+	return 0;
+}
+
 void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
 			      const struct audioformat *fmt)
 {
@@ -1795,6 +1850,9 @@  void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
 	case USB_ID(0x0dba, 0x5000):
 		mbox3_set_format_quirk(subs, fmt); /* Digidesign Mbox 3 */
 		break;
+	case USB_ID(0x2a39, 0x3f8c): /* RME Digiface USB */
+		rme_digiface_set_format_quirk(subs);
+		break;
 	}
 }