[RFC PATCH v5 1/8] ALSA: usb: add RME Babyface Pro driver core (probe, PCM stream)
Ismaïl Bahloul <[email protected]>
| Newsgroups | org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-sound,org.kernel.vger.linux-usb |
|---|---|
| Message-ID | <[email protected]> |
Minimal first step of the series: card lifecycle (probe/disconnect), interrupt-URB PCM streaming, and the sample-rate/varispeed clock mechanism the stream depends on. No mixer controls, front-panel support, DSP EQ or suspend/resume yet - each is added by its own later patch. The sample rate is the family register (request 0x10, index 0x0030: 32k/44.1k/48k family) with SET_INTERFACE(5, alt) selecting the x1/x2/x4 speed on top of it; the 0x1B DDS quad is varispeed, a pitch ratio applied independently of the family. A running stream now locks the rate: a second client asking for a different one is refused at hw_params rather than silently retuning the device out from under a live stream, matching the vendor drivers' own behaviour. Co-developed-by: David Fredman <[email protected]> Signed-off-by: David Fredman <[email protected]> Signed-off-by: Ismaïl Bahloul <[email protected]> --- MAINTAINERS | 6 + sound/usb/Kconfig | 20 + sound/usb/Makefile | 2 +- sound/usb/babyfacepro/Makefile | 4 + sound/usb/babyfacepro/babyfacepro.c | 1200 +++++++++++++++++++++++++++ sound/usb/babyfacepro/babyfacepro.h | 165 ++++ 6 files changed, 1396 insertions(+), 1 deletion(-) create mode 100644 sound/usb/babyfacepro/Makefile create mode 100644 sound/usb/babyfacepro/babyfacepro.c create mode 100644 sound/usb/babyfacepro/babyfacepro.h diff --git a/MAINTAINERS b/MAINTAINERS index 207a6e2db..31ed00692 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23828,6 +23828,12 @@ F: include/dt-bindings/power/thead,th1520-power.h F: include/dt-bindings/reset/thead,th1520-reset.h F: include/linux/firmware/thead/thead,th1520-aon.h +RME BABYFACE PRO DRIVER (PROPRIETARY MODE) +M: Ismaïl Bahloul <[email protected]> +L: [email protected] (moderated for non-subscribers) +S: Maintained +F: sound/usb/babyfacepro/ + RNBD BLOCK DRIVERS M: Md. Haris Iqbal <[email protected]> M: Jack Wang <[email protected]> diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index b4588915e..f58eedf3d 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -204,6 +204,26 @@ config SND_USB_AUDIO_QMI To compile this driver as a module, choose M here: the module will be called snd-usb-audio-qmi. +config SND_USB_BABYFACE_PRO + tristate "RME Babyface Pro / Pro FS (proprietary mode)" + select SND_PCM + help + Say Y here to include support for the RME Babyface Pro and + Babyface Pro FS in their proprietary mode (VID 0x2a39, + PID 0x3fc0). The two models share the same USB IDs and + descriptors and are handled identically. + + The proprietary mode streams PCM over interrupt endpoints + (interface 5, ep 0x01/0x82) instead of the class-compliant + isochronous path handled by snd-usb-audio, so this driver is + standalone (snd-usb-caiaq-style interrupt streaming). This + first patch covers probe, disconnect and the PCM stream only; + later patches in the series add the mixer, front panel and + DSP EQ as standard ALSA controls. + + To compile this driver as a module, choose M here: the module + will be called snd-usb-babyface-pro. + source "sound/usb/line6/Kconfig" endif # SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index e62794a87..2f83f5881 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -35,5 +35,5 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o -obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ qcom/ +obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ qcom/ babyfacepro/ obj-$(CONFIG_SND_USB_LINE6) += line6/ diff --git a/sound/usb/babyfacepro/Makefile b/sound/usb/babyfacepro/Makefile new file mode 100644 index 000000000..5adc6d474 --- /dev/null +++ b/sound/usb/babyfacepro/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +snd-usb-babyface-pro-y := babyfacepro.o + +obj-$(CONFIG_SND_USB_BABYFACE_PRO) += snd-usb-babyface-pro.o diff --git a/sound/usb/babyfacepro/babyfacepro.c b/sound/usb/babyfacepro/babyfacepro.c new file mode 100644 index 000000000..2d71286e3 --- /dev/null +++ b/sound/usb/babyfacepro/babyfacepro.c @@ -0,0 +1,1200 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RME Babyface Pro / Pro FS - proprietary-mode USB audio driver + * + * Core driver: USB vendor requests + cold init, interrupt-URB PCM + * streaming, and the card lifecycle (probe/disconnect/module entry). + * No mixer controls, front-panel support or suspend/resume yet - see + * babyfacepro.h and the cover letter for the rest of the series. + */ +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/unaligned.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> + +#include "babyfacepro.h" + +/* -- sample-rate / alt classes -------------------- */ + +/* rate = family_base << (alt - 1): three rate families (32k / 44.1k / + * 48k), three interface speeds. Measured on hardware, all nine rates, + * through the family register alone (2026-09-16). + * + * 64 kHz is the 32 kHz family at x2, not a 64 kHz base at x1. Both clock + * 64 kHz at pitch 0, but a 64 kHz base on alt 1 needs 64000 x 56 B = + * 3584 kB/s, which is exactly alt 1's 448-byte packet ceiling: any + * positive varispeed then runs off the end of the bus, and the device + * delivers the mirrored rate instead (asked +5 %, got -5 %). At x2 the + * frame is 40 B, so the same 64 kHz costs 2560 kB/s against a 5120 kB/s + * ceiling. Same reasoning puts 128 kHz on alt 3 rather than alt 2. + */ +static const struct bf_rate bf_rates[] = { + { 32000, BF_ALT_1, 56, 8 }, + { 44100, BF_ALT_1, 56, 8 }, + { 48000, BF_ALT_1, 56, 8 }, + { 64000, BF_ALT_2, 40, 16 }, + { 88200, BF_ALT_2, 40, 16 }, + { 96000, BF_ALT_2, 40, 16 }, + { 128000, BF_ALT_3, 32, 32 }, + { 176400, BF_ALT_3, 32, 32 }, + { 192000, BF_ALT_3, 32, 32 }, +}; + +static const unsigned int bf_rate_list[ARRAY_SIZE(bf_rates)] = { + 32000, 44100, 48000, 64000, 88200, + 96000, 128000, 176400, 192000, +}; + +const struct snd_pcm_hw_constraint_list bf_rates_constraint = { + .count = ARRAY_SIZE(bf_rate_list), + .list = bf_rate_list, + .mask = 0, +}; + +const struct bf_rate *bf_rate_lookup(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bf_rates); i++) + if (bf_rates[i].rate == rate) + return &bf_rates[i]; + return NULL; +} + +unsigned int bf_rate_family(const struct bf_rate *r) +{ + switch (r->rate >> (r->alt - 1)) { + case 32000: + return 0; + case 44100: + return 1; + default: + return 2; + } +} + +/* -- vendor requests ----------------------- */ + +/* Timeout for every vendor control transfer (ms). */ +#define BF_CTL_TIMEOUT 1000 + +int bf_vendor_write(struct snd_usb_babyface *chip, u8 req, u16 val, u16 idx) +{ + return usb_control_msg_send(chip->dev, 0, req, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + val, idx, NULL, 0, BF_CTL_TIMEOUT, GFP_KERNEL); +} + +int bf_vendor_read(struct snd_usb_babyface *chip, u8 req, u16 idx, u8 *buf) +{ + return usb_control_msg_recv(chip->dev, 0, req, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + 0, idx, buf, 4, BF_CTL_TIMEOUT, GFP_KERNEL); +} + +/* Sends the BF_REG_KEEPALIVE_SETTINGS word (PROTOCOL.md: "keepalive + * 0x10 0x05CF wVal = host settings-state register" - a single shared + * word, not independent per-setting writes). Hardcoded to Internal + * clock for now; the clock-source control and the chip->clock_optical + * bit it composes in land with a later patch in this series, at which + * point this grows the same way babyface_restore_state() does further + * down the series. + */ +int bf_settings_write(struct snd_usb_babyface *chip) +{ + return bf_vendor_write(chip, BF_REQ_KEEPALIVE, BF_SETTINGS_CLOCK_INTERNAL, + BF_REG_KEEPALIVE_SETTINGS); +} + +/* Set the sample rate: the family register. The firmware derives its + * own DDS word from it; SET_INTERFACE(5, alt) (done in hw_params) is the + * x1/x2/x4 speed. This is exactly what the RME Windows driver sends on + * a rate change (USBPcap, 2026-09-14, all fourteen changes): the family, + * then the settings word, nothing else. Measured on this driver's + * target hardware for all nine rates with no quad in play (2026-09-16), + * and to -1 ppm at 44.1 kHz against an NTP-disciplined clock. + * + * An earlier version of this branch had this register and measured it as + * dead. It was not: bf_cold_init()'s 0x0021 -> 0x05ff write is this + * same register (0x05ff = 0x05cf | 0x0030) hardcoded to the 48 kHz + * family, and it followed the write. See bf_cold_init(). + */ +int bf_clock_write(struct snd_usb_babyface *chip) +{ + const struct bf_rate *r = bf_rate_lookup(chip->rate); + int ret; + + if (!r) + return -EINVAL; + ret = bf_vendor_write(chip, BF_REQ_KEEPALIVE, + bf_rate_family(r) << 4, BF_REG_RATE_FAMILY); + if (ret < 0) + return ret; + return bf_settings_write(chip); +} + +/* Varispeed: the 0x1B DDS quad. It is a pitch RATIO applied on top of + * the family rate, not a clock, so it carries no base rate and is the + * same four words at every sample rate (measured 2026-09-16: a 48 kHz + * quad over the 44.1 kHz family gives 44.1 kHz; the same quad at +5 % + * gives 46305). It is sticky: the device keeps the last ratio across + * family writes, so pitch 0 must send the x1.0 quad rather than nothing. + * + * Only bank 2 is read on the Babyface Pro (one bank at a time swapped + * between two quads: only bank 2 moved the clock). It is the ratio's + * period as a 24-bit word, 1.0 = 2^14 x 25 MHz / 48000 = 8533333.33, and + * the firmware's constant is exact: sent exact it measures -1 ppm, sent + * as RME's own Q16-truncated word (0x8234d3) it measures +14 ppm. So + * bank 2 is computed exactly. Banks 0, 1 and 3 are sent as the RME + * host software computes them - bank 0 the x1.0 period 12800000 scaled + * by the pitch, banks 1 and 3 that value times fixed Q16 constants + * (0xb9c2, 0xa3d7), truncated - so that a unit which reads a different + * bank (the FS is not measured) sees exactly what Windows gives it. + * Together this reproduces the cold-plug capture's quad at pitch 0 in + * banks 0, 1 and 3, and improves on it in bank 2. + * + * Every quad must be followed by the settings keepalive or it does not + * apply. pitch is in 0.1 % steps, -50..50. + */ +int bf_pitch_write(struct snd_usb_babyface *chip, int pitch) +{ + const u64 num0 = 12800000ULL * 1000; /* bank 0: 12800000 / (1 + p) */ + const u64 num2 = 409600000000000ULL; /* bank 2: 4.096e11 * 1000 */ + u32 den0 = 1000 + pitch; + u32 den2 = 48000 * den0; + u64 b0 = div_u64(num0 + den0 / 2, den0); + u64 b[4]; + int k, ret; + + b[0] = b0; + b[1] = (b0 * 47554) >> 16; + b[2] = div_u64(num2 + den2 / 2, den2); + b[3] = (b0 * 41943) >> 16; + + for (k = 0; k < 4; k++) { + ret = bf_vendor_write(chip, BF_REQ_DDS, (b[k] >> 8) & 0xffff, + ((b[k] & 0xff) << 8) | k); + if (ret < 0) + return ret; + } + return bf_settings_write(chip); +} + +/* The cold-start session init (cap_coldplug.pcap), verbatim from the + * user-space reference (protocol::streaming_init). Without it the + * firmware never validates a stream. + */ +int bf_cold_init(struct snd_usb_babyface *chip) +{ + const struct bf_rate *r; + int ret, i; + + for (i = 0; i <= 0x3d; i++) { + if (i == 0x1e || i == 0x1f) + continue; + ret = bf_vendor_write(chip, BF_REQ_REG_CLEAR, 0x0000, i); + if (ret < 0) + return ret; + } + /* The varispeed quad, where the coldplug capture sends its x1.0 + * quad. At pitch 0 banks 0, 1 and 3 match the capture; bank 2 is + * the exact ratio (see bf_pitch_write). + */ + ret = bf_pitch_write(chip, chip->pitch); + if (ret < 0) + return ret; + /* 0x1C status - the hardware-validated reference (protocol:: + * streaming_init) sends it as an OUT write; Windows reads it. + * Both are tolerated; match the validated path. + */ + ret = bf_vendor_write(chip, BF_REQ_STATUS_2, 0x0000, 0x0000); + if (ret < 0) + return ret; + /* The settings word and the family register in one write (0x05ff = + * 0x05cf | 0x0030). The coldplug capture hardcodes 0x0021 here: + * family 48 kHz, clock internal. Sent for the rate actually in use, + * this IS the rate write; hardcoded, it was the thing that reset every + * earlier family write to 48 kHz. + */ + r = bf_rate_lookup(chip->rate); + if (!r) + return -EINVAL; + ret = bf_vendor_write(chip, BF_REQ_KEEPALIVE, + (bf_rate_family(r) << 4) | BF_SETTINGS_CLOCK_INTERNAL, + BF_REG_KEEPALIVE_INIT); + if (ret < 0) + return ret; + /* 0x17 wIdx=0x0000 does NOT touch the preamp state (0x003F). */ + ret = bf_vendor_write(chip, BF_REQ_PREAMP, 0x000c, 0x0000); + if (ret < 0) + return ret; + ret = bf_vendor_write(chip, BF_REQ_PREAMP_COMMIT, 0x0000, 0x0000); + if (ret < 0) + return ret; + for (i = 0; i < 2; i++) { + ret = bf_vendor_write(chip, BF_REQ_KEEPALIVE, 0x0000, 0x3000); + if (ret < 0) + return ret; + } + for (i = 0; i < 3; i++) { + ret = bf_vendor_write(chip, BF_REQ_KEEPALIVE, 0x0800, 0x0800); + if (ret < 0) + return ret; + } + return 0; +} + +/* -- PCM data path ------------------------- */ + +static bool babyface_capture_copy(struct snd_usb_babyface *chip, + struct snd_pcm_substream *subs, + u8 *data, unsigned int frames) +{ + struct snd_pcm_runtime *rt = subs->runtime; + unsigned int buf_frames = rt->buffer_size; + unsigned int words = chip->frame_bytes / 4; + unsigned int chans = rt->channels; + unsigned int pos, f, i; + unsigned long new_period; + bool crossed = false; + u8 *dst; + + spin_lock(&chip->lock); + pos = chip->hw_ptr[SNDRV_PCM_STREAM_CAPTURE] % buf_frames; + for (f = 0; f < frames; f++) { + const __le32 *w = (const __le32 *)(data + f * chip->frame_bytes); + + dst = rt->dma_area + frames_to_bytes(rt, pos); + for (i = 0; i < chans; i++) { + /* Channel map: app ch0-3 = device words 0-3 (AN1-4); + * app ch4-9 = words 6-11 (ADAT/SPDIF); app ch10/11 = + * words 12/13 = a FIXED-GAIN playback tap (observed + * 2026-08-25: the playback echoes there at ~-27 dB, + * independent of the output masters - NOT the output + * bus; the ADAT/SPDIF range is words 6-11 only). The + * device words 4/5 are a fixed marker, not audio - + * skipped. At 96/192 kHz the frame has fewer words; + * missing ones read as zero. + */ + static const u8 map[12] = { 0, 1, 2, 3, 6, 7, 8, 9, + 10, 11, 12, 13 }; + u8 wi = i < 12 ? map[i] : 0xff; + s32 s = 0; + + if (wi < words) { + /* 24-bit sample in bits 31-8 of the 32-bit word; + * S32_LE with 24 msbits, so copy the word as-is. + */ + s = (s32)le32_to_cpu(w[wi]); + } + put_unaligned_le32((u32)s, dst + i * 4); + } + pos++; + if (pos >= buf_frames) + pos = 0; + } + chip->hw_ptr[SNDRV_PCM_STREAM_CAPTURE] += frames; + new_period = chip->hw_ptr[SNDRV_PCM_STREAM_CAPTURE] / rt->period_size; + if (new_period != chip->prev_period[SNDRV_PCM_STREAM_CAPTURE]) { + chip->prev_period[SNDRV_PCM_STREAM_CAPTURE] = new_period; + crossed = true; + } + spin_unlock(&chip->lock); + + return crossed; +} + +static bool babyface_playback_copy(struct snd_usb_babyface *chip, + struct snd_pcm_substream *subs, + u8 *data, unsigned int frames) +{ + struct snd_pcm_runtime *rt = subs->runtime; + unsigned int buf_frames = rt->buffer_size; + unsigned int words = chip->frame_bytes / 4; + unsigned int chans = rt->channels; + unsigned int pos, f, i; + unsigned long new_period; + bool crossed = false; + const u8 *src; + + spin_lock(&chip->lock); + /* Never copy frames the app has not prepared. With several URBs in + * flight the device can transiently be ahead of the app ring at low + * periods, so hw_ptr may reach past appl_ptr; reading beyond appl_ptr + * would trip a spurious XRUN. The device just repeats the last + * frames instead. (Both counters are unbounded, so clamp on the + * signed difference.) + */ + { + snd_pcm_sframes_t avail = + (snd_pcm_sframes_t)(rt->control->appl_ptr - + chip->hw_ptr[SNDRV_PCM_STREAM_PLAYBACK]); + + if (avail < 0) + avail = 0; + else if (avail > (snd_pcm_sframes_t)buf_frames) + avail = buf_frames; + if ((snd_pcm_uframes_t)avail < frames) + frames = (unsigned int)avail; + } + pos = chip->hw_ptr[SNDRV_PCM_STREAM_PLAYBACK] % buf_frames; + for (f = 0; f < frames; f++) { + __le32 *w = (__le32 *)(data + f * chip->frame_bytes); + + src = rt->dma_area + frames_to_bytes(rt, pos); + /* App ch n feeds the device word n (PB1-6 = words 0-11); + * words 12/13 stay zero. At 96/192 kHz the frame is + * shorter - the extra app channels are dropped. + */ + for (i = 0; i < chans && i < words; i++) { + u32 s = get_unaligned_le32(src + i * 4); + + /* S32_LE msbits=24: the app's 24 valid bits are already + * left-justified (bits 31-8) - copy the word as-is. + */ + w[i] = cpu_to_le32(s); + } + for (; i < words; i++) + w[i] = 0; + pos++; + if (pos >= buf_frames) + pos = 0; + } + chip->hw_ptr[SNDRV_PCM_STREAM_PLAYBACK] += frames; + new_period = chip->hw_ptr[SNDRV_PCM_STREAM_PLAYBACK] / rt->period_size; + if (new_period != chip->prev_period[SNDRV_PCM_STREAM_PLAYBACK]) { + chip->prev_period[SNDRV_PCM_STREAM_PLAYBACK] = new_period; + crossed = true; + } + spin_unlock(&chip->lock); + + return crossed; +} + +static void babyface_complete_in(struct urb *urb) +{ + struct snd_usb_babyface *chip = urb->context; + struct snd_pcm_substream *subs; + unsigned long flags; + unsigned int frames; + bool crossed = false; + int ret; + + if (urb->status < 0) { + if (urb->status == -ESHUTDOWN || urb->status == -ENOENT || + urb->status == -ECONNRESET) + return; /* killed */ + dev_dbg_ratelimited(&chip->dev->dev, "IN urb status %d\n", + urb->status); + if (atomic_inc_return(&chip->urb_err) >= BF_URB_ERR_STOP) + schedule_work(&chip->stream_work); + goto resubmit; + } + atomic_set(&chip->urb_err, 0); + + subs = READ_ONCE(chip->subs[SNDRV_PCM_STREAM_CAPTURE]); + if (subs) { + snd_pcm_stream_lock_irqsave(subs, flags); + if (snd_pcm_running(subs)) { + frames = urb->actual_length / chip->frame_bytes; + if (frames) + crossed = babyface_capture_copy(chip, subs, + urb->transfer_buffer, + frames); + } + snd_pcm_stream_unlock_irqrestore(subs, flags); + if (crossed) + snd_pcm_period_elapsed(subs); + } +resubmit: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + dev_err_ratelimited(&chip->dev->dev, + "IN resubmit failed: %d\n", ret); + if (atomic_inc_return(&chip->urb_err) >= BF_URB_ERR_STOP) + schedule_work(&chip->stream_work); + } +} + +static void babyface_complete_out(struct urb *urb) +{ + struct snd_usb_babyface *chip = urb->context; + struct snd_pcm_substream *subs; + unsigned long flags; + unsigned int frames; + bool crossed = false; + int ret; + + if (urb->status < 0) { + if (urb->status == -ESHUTDOWN || urb->status == -ENOENT || + urb->status == -ECONNRESET) + return; /* killed */ + dev_dbg_ratelimited(&chip->dev->dev, "OUT urb status %d\n", + urb->status); + if (atomic_inc_return(&chip->urb_err) >= BF_URB_ERR_STOP) + schedule_work(&chip->stream_work); + goto resubmit; + } + atomic_set(&chip->urb_err, 0); + + subs = READ_ONCE(chip->subs[SNDRV_PCM_STREAM_PLAYBACK]); + if (subs) { + snd_pcm_stream_lock_irqsave(subs, flags); + if (snd_pcm_running(subs)) { + frames = chip->frames_per_urb; + crossed = babyface_playback_copy(chip, subs, + urb->transfer_buffer, frames); + } + snd_pcm_stream_unlock_irqrestore(subs, flags); + if (crossed) + snd_pcm_period_elapsed(subs); + } else { + /* No consumer: silence the OUT frames. */ + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); + } +resubmit: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + dev_err_ratelimited(&chip->dev->dev, + "OUT resubmit failed: %d\n", ret); + if (atomic_inc_return(&chip->urb_err) >= BF_URB_ERR_STOP) + schedule_work(&chip->stream_work); + } +} + +void babyface_stream_kill(struct snd_usb_babyface *chip) +{ + int i; + + for (i = 0; i < chip->nurbs; i++) { + usb_kill_urb(chip->urbs_in[i]); + usb_kill_urb(chip->urbs_out[i]); + } + chip->streaming = false; +} + +/* Stream start/stop run in process context (control transfers sleep). + * The trigger only toggles stream_users and schedules this work. + */ + +/* Stop both PCM substreams (if running) so apps blocked in read/write + * wake with a clean error: XRUN for a recoverable stream error, or + * DISCONNECTED when the card is going away. + */ +void babyface_pcm_stop_both(struct snd_usb_babyface *chip, snd_pcm_state_t state) +{ + int s; + + for (s = 0; s < 2; s++) { + struct snd_pcm_substream *subs = READ_ONCE(chip->subs[s]); + + if (subs && snd_pcm_running(subs)) + snd_pcm_stop(subs, state); + } +} + +/* Re-count stream_users from the substream running states. The apps + * can recover (re-prepare + trigger) while the stream work runs, so a + * hard `= 0` would wipe a fresh increment and leave a RUNNING + * substream with no URBs (hang). Called on the error paths with the + * mutex held. + */ +static void bf_recount_users(struct snd_usb_babyface *chip) +{ + unsigned long flags; + int s, users = 0; + + for (s = 0; s < 2; s++) { + struct snd_pcm_substream *subs = READ_ONCE(chip->subs[s]); + + if (subs && snd_pcm_running(subs)) + users++; + } + spin_lock_irqsave(&chip->lock, flags); + chip->stream_users = users; + spin_unlock_irqrestore(&chip->lock, flags); +} + +/* Stream model (big picture): + * + * The PCM stream is entirely asynchronous. A trigger(START/STOP) does + * not touch the hardware; it only changes the shared stream_users + * counter (0..2, one per running substream, counted under chip->lock) + * and schedules stream_work. The work runs in process context (the + * control transfers and usb_submit_urb() calls must sleep): + * + * - 1st user (users 0 -> 1): cold-init + the session trigger pair, + * then the interrupt URBs (nurbs in each direction) are submitted + * and the session is armed. + * - last user (users 1 -> 0): the URBs are killed and the session + * disarmed. + * + * So the device has exactly one live stream regardless of how many + * substreams run, and a playback+capture pair shares it. An app that + * triggers while the work is running just increments stream_users; the + * work's re-count on the error path (bf_recount_users) reflects the + * RUNNING state so a recovered app re-arms from a clean slate. + */ +void babyface_stream_work(struct work_struct *work) +{ + struct snd_usb_babyface *chip = + container_of(work, struct snd_usb_babyface, stream_work); + unsigned int urbsize; + unsigned long flags; + int i, ret; + int users; + + mutex_lock(&chip->mutex); + urbsize = chip->frame_bytes * chip->frames_per_urb; + + if (chip->shutdown) { + mutex_unlock(&chip->mutex); + return; + } + + /* Persistent URB errors (bad link, device wedged): stop the stream + * and wake the apps with -EPIPE. stream_users is re-counted from + * the (now stopped) substreams so an app recovery (prepare+start) + * re-arms the session from a clean slate. + */ + if (atomic_read(&chip->urb_err) >= BF_URB_ERR_STOP) { + dev_err(&chip->dev->dev, + "stream error: %d consecutive bad URBs, stopping (apps re-arm)\n", + BF_URB_ERR_STOP); + babyface_pcm_stop_both(chip, SNDRV_PCM_STATE_XRUN); + if (chip->streaming) + babyface_stream_kill(chip); + bf_recount_users(chip); + atomic_set(&chip->urb_err, 0); + mutex_unlock(&chip->mutex); + return; + } + + spin_lock_irqsave(&chip->lock, flags); + users = chip->stream_users; + spin_unlock_irqrestore(&chip->lock, flags); + + if (users > 0 && !chip->streaming) { + /* The firmware only validates a stream session that is + * preceded by the full cold-init (the user-space reference + * sends streaming_init at every session start - without it + * the outputs stay silent). + */ + ret = bf_cold_init(chip); + if (ret < 0) + goto err; + + /* Stream trigger pair (cap_audio): 0x10 0x8000 + 0x1D. */ + ret = bf_vendor_write(chip, BF_REQ_KEEPALIVE, 0x0000, 0x8000); + if (ret < 0) + goto err; + ret = bf_vendor_write(chip, BF_REQ_SESSION_START, 0x0000, 0x0000); + if (ret < 0) + goto err; + + for (i = 0; i < chip->nurbs; i++) { + /* Do not replay data from the previous stream/format. */ + memset(chip->buf_out[i], 0, urbsize); + usb_fill_int_urb(chip->urbs_in[i], chip->dev, + usb_rcvintpipe(chip->dev, BF_EP_IN), + chip->buf_in[i], urbsize, + babyface_complete_in, chip, 1); + usb_fill_int_urb(chip->urbs_out[i], chip->dev, + usb_sndintpipe(chip->dev, BF_EP_OUT), + chip->buf_out[i], urbsize, + babyface_complete_out, chip, 1); + /* The buffers come from usb_alloc_coherent(), so + * they are already DMA-mapped: hand the HCD the + * mapping instead of letting it map them again. + * Without this, usb_hcd_map_urb_for_dma() calls + * dma_map_single() on a coherent allocation, which + * fails with -EAGAIN on any host where that + * allocation is a vmap (IOMMU-backed dma-iommu, + * e.g. amd_iommu in its default translated mode). + */ + chip->urbs_in[i]->transfer_dma = chip->dma_in[i]; + chip->urbs_in[i]->transfer_flags |= + URB_NO_TRANSFER_DMA_MAP; + chip->urbs_out[i]->transfer_dma = chip->dma_out[i]; + chip->urbs_out[i]->transfer_flags |= + URB_NO_TRANSFER_DMA_MAP; + } + for (i = 0; i < chip->nurbs; i++) { + ret = usb_submit_urb(chip->urbs_in[i], GFP_KERNEL); + if (ret < 0) + goto err; + ret = usb_submit_urb(chip->urbs_out[i], GFP_KERNEL); + if (ret < 0) + goto err; + } + /* Session arm (cap_audio frame 5829, after the URBs). */ + ret = bf_vendor_write(chip, BF_REQ_SESSION_ARM, 0x0000, 0xc000); + if (ret < 0) + goto err; + + chip->streaming = true; + dev_dbg(&chip->dev->dev, "stream started (%u frames/URB, %u URBs)\n", + chip->frames_per_urb, chip->nurbs); + } else if (users == 0 && chip->streaming) { + babyface_stream_kill(chip); + dev_dbg(&chip->dev->dev, "stream stopped\n"); + } + + mutex_unlock(&chip->mutex); + return; + +err: + dev_err(&chip->dev->dev, "failed to start stream: %d\n", ret); + babyface_stream_kill(chip); + /* The apps already got a successful trigger - wake them with an + * XRUN so a failed start (device wedged, cold-init error) does not + * leave them hung in read/write with no URBs in flight. + */ + babyface_pcm_stop_both(chip, SNDRV_PCM_STATE_XRUN); + bf_recount_users(chip); + mutex_unlock(&chip->mutex); +} + +/* -- PCM --------------------------- */ + +static const struct snd_pcm_hardware babyface_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 12, + .buffer_bytes_max = 1 << 20, + .period_bytes_max = 1 << 18, + .periods_min = 2, + .periods_max = 16, +}; + +/* Is the OTHER direction actually transferring? Not "is it open": a client + * that has the device open but has not started must still be able to pick the + * rate, so that an application opening playback and capture before starting + * either one can take both to 96 kHz. + */ +static bool bf_other_running(struct snd_usb_babyface *chip, + struct snd_pcm_substream *subs) +{ + struct snd_pcm_substream *other = READ_ONCE(chip->subs[!subs->stream]); + unsigned long flags; + bool running; + + if (!other) + return false; + snd_pcm_stream_lock_irqsave(other, flags); + running = snd_pcm_running(other); + snd_pcm_stream_unlock_irqrestore(other, flags); + return running; +} + +static int babyface_pcm_open(struct snd_pcm_substream *subs) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + struct snd_pcm_runtime *rt = subs->runtime; + unsigned int locked; + unsigned long flags; + int ret; + + rt->hw = babyface_pcm_hw; + ret = snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, + &bf_rates_constraint); + if (ret < 0) + return ret; + /* The stream is 24-bit audio in a 32-bit container, left-justified + * (the device word carries the sample in bits 31-8). Declare the + * valid-bit width so user-space knows to ignore the low 8 bits. + */ + ret = snd_pcm_hw_constraint_msbits(rt, 0, 32, 24); + if (ret < 0) + return ret; + /* One URB delivers frames_per_urb frames per interrupt; a period must + * span at least one URB so a completion crosses at most one period + * boundary. Constrain in frames (not bytes) so the minimum period + * does not balloon at low channel counts: 2 ch @ 48 kHz -> 256 + * frames (5.3 ms) instead of 1536 frames from a 12-ch byte clamp. + */ + ret = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + chip->frames_per_urb, 1 << 18); + if (ret < 0) + return ret; + + /* Both directions share one clock, so while audio is flowing the rate + * belongs to whoever started it. Offer that rate alone: a client + * arriving later then negotiates down to it, and the sound server + * resamples, rather than the device being retuned underneath a running + * stream. Advertising the constraint here rather than failing in + * hw_params() is what keeps a PipeWire sink alive - it picks the rate + * on offer instead of asking for one that has to be refused. + * + * This is what the vendor drivers do. RME's settings panel greys the + * sample rate out during record/playback, and their manual is explicit + * that all active ASIO clients share one rate, with WDM (or CoreAudio) + * doing any conversion - never the driver. + */ + mutex_lock(&chip->mutex); + locked = chip->streaming ? chip->rate : 0; + mutex_unlock(&chip->mutex); + if (locked) { + ret = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_RATE, + locked, locked); + if (ret < 0) + return ret; + } + + spin_lock_irqsave(&chip->lock, flags); + chip->subs[subs->stream] = subs; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int babyface_pcm_close(struct snd_pcm_substream *subs) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + unsigned long flags; + + /* Wait for the stream stop work so the URB callbacks (which + * touch subs) are done before the substream can be freed. + */ + flush_work(&chip->stream_work); + spin_lock_irqsave(&chip->lock, flags); + chip->subs[subs->stream] = NULL; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int babyface_pcm_hw_params(struct snd_pcm_substream *subs, + struct snd_pcm_hw_params *params) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + const struct bf_rate *r; + int ret = 0; + + r = bf_rate_lookup(params_rate(params)); + if (!r) + return -EINVAL; + + /* The stream URBs must be at least one alt packet wide: the device + * delivers its IN data in alt-sized packets (448/640/1024 B for + * alt 1/2/3), and a smaller URB buffer makes the host controller + * discard the transfer with -EOVERFLOW (babble) - seen at + * 176.4/192 kHz with frames_per_urb below 32. Return a clean + * error instead of a silently dead capture stream. + */ + if (chip->frames_per_urb < r->min_fpu) { + dev_err(&chip->dev->dev, + "rate %u Hz needs frames_per_urb >= %u (module has %u)\n", + r->rate, r->min_fpu, chip->frames_per_urb); + return -EINVAL; + } + + mutex_lock(&chip->mutex); + if (r->rate != chip->rate) { + /* The constraint in open() normally means nobody asks for a + * rate other than the running one. It can still happen, when + * this substream was opened before the other one started: the + * constraint is evaluated at open, so it did not apply then. + * Refuse rather than retune - the alternative is a running + * stream silently changing pitch, with no error and no xrun, + * because a negotiated rate is never revised for a live + * substream. + */ + if (bf_other_running(chip, subs)) { + dev_dbg(&chip->dev->dev, + "rate %u Hz refused: %u Hz stream running\n", + r->rate, chip->rate); + ret = -EBUSY; + goto out; + } + /* Nothing is transferring, so the clock is free. Stop any + * armed URBs, re-point the bandwidth class and let the stream + * work restart the session at the new rate. + */ + if (chip->streaming) + babyface_stream_kill(chip); + ret = usb_set_interface(chip->dev, BF_IFACE, r->alt); + if (ret < 0) { + babyface_pcm_stop_both(chip, SNDRV_PCM_STATE_XRUN); + bf_recount_users(chip); + goto out; + } + chip->rate = r->rate; + chip->alt = r->alt; + chip->frame_bytes = r->frame_bytes; + /* Publish the new geometry before restarting either stream. */ + schedule_work(&chip->stream_work); + dev_dbg(&chip->dev->dev, "rate %u Hz (alt %u)\n", + chip->rate, chip->alt); + } +out: + mutex_unlock(&chip->mutex); + return ret; +} + +static int babyface_pcm_hw_free(struct snd_pcm_substream *subs) +{ + /* The device buffer is host-side; nothing to release here. */ + return 0; +} + +static int babyface_pcm_prepare(struct snd_pcm_substream *subs) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + chip->hw_ptr[subs->stream] = 0; + chip->prev_period[subs->stream] = 0; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int babyface_pcm_trigger(struct snd_pcm_substream *subs, int cmd) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + spin_lock_irqsave(&chip->lock, flags); + chip->hw_ptr[subs->stream] = 0; + chip->prev_period[subs->stream] = 0; + /* stream_users is shared by the two substreams (separate + * locks) - serialize the ++/-- so a concurrent trigger on + * the other direction can't lose an increment (which would + * stop the stream while a substream still runs). + */ + if (chip->stream_users++ == 0) + schedule_work(&chip->stream_work); + spin_unlock_irqrestore(&chip->lock, flags); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&chip->lock, flags); + if (chip->stream_users > 0 && --chip->stream_users == 0) + schedule_work(&chip->stream_work); + spin_unlock_irqrestore(&chip->lock, flags); + return 0; + } + return -EINVAL; +} + +static snd_pcm_uframes_t babyface_pcm_pointer(struct snd_pcm_substream *subs) +{ + struct snd_usb_babyface *chip = snd_pcm_substream_chip(subs); + unsigned long flags; + snd_pcm_uframes_t pos; + + spin_lock_irqsave(&chip->lock, flags); + pos = chip->hw_ptr[subs->stream] % subs->runtime->buffer_size; + spin_unlock_irqrestore(&chip->lock, flags); + return pos; +} + +static const struct snd_pcm_ops babyface_pcm_ops = { + .open = babyface_pcm_open, + .close = babyface_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = babyface_pcm_hw_params, + .hw_free = babyface_pcm_hw_free, + .prepare = babyface_pcm_prepare, + .trigger = babyface_pcm_trigger, + .pointer = babyface_pcm_pointer, +}; + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int frames_per_urb = BF_FRAMES_PER_URB_DEFAULT; +static int nurbs = BF_NURBS_DEFAULT; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the Babyface Pro sound card."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the Babyface Pro sound card."); +module_param(frames_per_urb, int, 0644); +MODULE_PARM_DESC(frames_per_urb, "Audio frames per URB, 8..1024 (16 = low-latency floor, 256 = default)."); +module_param(nurbs, int, 0644); +MODULE_PARM_DESC(nurbs, "URBs in flight per direction, 1..16 (16 = low-latency)."); + +/* -- USB driver ------------------------- */ + +static void babyface_private_free(struct snd_card *card) +{ + struct snd_usb_babyface *chip = card->private_data; + unsigned int urbsize; + int i; + + if (!chip) + return; + + /* The URB arrays are NULL when the probe failed before allocating + * them (snd_card_free runs private_free on any probe error). + */ + if (chip->urbs_in) { + urbsize = BF_WORDS_PER_FRAME * sizeof(u32) * chip->frames_per_urb; + for (i = 0; i < chip->nurbs; i++) { + if (chip->urbs_in[i]) { + usb_kill_urb(chip->urbs_in[i]); + usb_free_urb(chip->urbs_in[i]); + } + if (chip->urbs_out[i]) { + usb_kill_urb(chip->urbs_out[i]); + usb_free_urb(chip->urbs_out[i]); + } + usb_free_coherent(chip->dev, urbsize, chip->buf_in[i], + chip->dma_in[i]); + usb_free_coherent(chip->dev, urbsize, chip->buf_out[i], + chip->dma_out[i]); + } + } + kfree(chip->urbs_in); + kfree(chip->urbs_out); + kfree(chip->buf_in); + kfree(chip->buf_out); + kfree(chip->dma_in); + kfree(chip->dma_out); + usb_put_dev(chip->dev); +} + +static int babyface_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct snd_usb_babyface *chip; + struct snd_card *card; + struct snd_pcm *pcm; + unsigned int urbsize; + int i, err; + + if (intf->cur_altsetting->desc.bInterfaceNumber != BF_IFACE) { + /* Only the proprietary audio interface is ours; the MIDI + * (standard class) and bulk interfaces stay unclaimed so + * snd-usb-audio can take the MIDI one. + */ + return -ENODEV; + } + + err = snd_card_new(&intf->dev, index[0], id[0], THIS_MODULE, + sizeof(*chip), &card); + if (err < 0) { + dev_err(&intf->dev, "snd_card_new failed: %d\n", err); + return err; + } + chip = card->private_data; + chip->card = card; + + chip->dev = usb_get_dev(dev); + /* USB autosuspend is untested: nothing in this driver holds a PM + * reference while streaming, so an autosuspend request could race + * a live stream. Disable it explicitly rather than ship an + * untested code path - full autosuspend support (correct + * autopm_get/put pairing around the stream) is a deliberate + * follow-up, not an oversight. + */ + usb_disable_autosuspend(chip->dev); + chip->iface = intf; + chip->nurbs = clamp(nurbs, 1, 16); + chip->frames_per_urb = clamp(frames_per_urb, 8, 1024) & ~7; + chip->rate = 48000; + chip->alt = BF_ALT_1; + chip->frame_bytes = 56; + mutex_init(&chip->mutex); + spin_lock_init(&chip->lock); + atomic_set(&chip->urb_err, 0); + INIT_WORK(&chip->stream_work, babyface_stream_work); + chip->card->private_free = babyface_private_free; + + /* Model-neutral on purpose. The FS and the original (2015) + * Babyface Pro share VID:PID 2a39:3fc0, report the same bcdDevice + * and the same iProduct shape, and neither says "FS" anywhere, so + * there is nothing to tell them apart at probe time - and the + * driver is reported to work unmodified on both. card->driver in + * particular is what alsa-lib configs and UCM profiles match on, + * so it has to be right before this reaches a released kernel. + */ + strscpy(chip->card->driver, "BabyfacePro", + sizeof(chip->card->driver)); + strscpy(chip->card->shortname, "Babyface Pro", + sizeof(chip->card->shortname)); + snprintf(chip->card->longname, sizeof(chip->card->longname), + "RME Babyface Pro (proprietary mode) at %s", + dev_name(&dev->dev)); + + /* If the id was not passed as a module option, derive it from the + * shortname with the whitespace removed, the way snd-usb-caiaq + * does. Letting the core derive it instead yields the last word + * of the shortname ("Pro"), which is no use as an hw: name. + */ + if (*chip->card->id == '\0') { + char cid[sizeof(chip->card->id)]; + const char *c; + size_t len; + + memset(cid, 0, sizeof(cid)); + for (c = chip->card->shortname, len = 0; + *c && len < sizeof(cid) - 1; c++) + if (*c != ' ') + cid[len++] = *c; + snd_card_set_id(chip->card, cid); + } + strscpy(chip->card->mixername, "Babyface Pro", + sizeof(chip->card->mixername)); + + /* alt 1 = the default 48-kHz bandwidth class. */ + err = usb_set_interface(dev, BF_IFACE, BF_ALT_1); + if (err < 0) { + dev_err(&intf->dev, "usb_set_interface failed: %d\n", err); + goto error; + } + + err = bf_cold_init(chip); + if (err < 0) { + dev_err(&intf->dev, "cold init failed: %d\n", err); + goto error; + } + + /* Keep the allocation size independent of the active USB mode. */ + urbsize = BF_WORDS_PER_FRAME * sizeof(u32) * chip->frames_per_urb; + chip->urbs_in = kcalloc(chip->nurbs, sizeof(*chip->urbs_in), GFP_KERNEL); + chip->urbs_out = kcalloc(chip->nurbs, sizeof(*chip->urbs_out), GFP_KERNEL); + chip->buf_in = kcalloc(chip->nurbs, sizeof(*chip->buf_in), GFP_KERNEL); + chip->buf_out = kcalloc(chip->nurbs, sizeof(*chip->buf_out), GFP_KERNEL); + chip->dma_in = kcalloc(chip->nurbs, sizeof(*chip->dma_in), GFP_KERNEL); + chip->dma_out = kcalloc(chip->nurbs, sizeof(*chip->dma_out), GFP_KERNEL); + if (!chip->urbs_in || !chip->urbs_out || !chip->buf_in || + !chip->buf_out || !chip->dma_in || !chip->dma_out) + goto error; + + for (i = 0; i < chip->nurbs; i++) { + chip->urbs_in[i] = usb_alloc_urb(0, GFP_KERNEL); + chip->urbs_out[i] = usb_alloc_urb(0, GFP_KERNEL); + chip->buf_in[i] = usb_alloc_coherent(dev, urbsize, GFP_KERNEL, + &chip->dma_in[i]); + chip->buf_out[i] = usb_alloc_coherent(dev, urbsize, GFP_KERNEL, + &chip->dma_out[i]); + if (!chip->urbs_in[i] || !chip->urbs_out[i] || + !chip->buf_in[i] || !chip->buf_out[i]) + goto error; + } + + err = snd_pcm_new(chip->card, "Babyface Pro", 0, 1, 1, &pcm); + if (err < 0) { + dev_err(&intf->dev, "snd_pcm_new failed: %d\n", err); + goto error; + } + pcm->private_data = chip; + strscpy(pcm->name, "Babyface Pro", sizeof(pcm->name)); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &babyface_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &babyface_pcm_ops); + + /* The PCM buffer is host-side (the URB callbacks copy in/out of + * it); vmalloc is the standard choice for that. + */ + err = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 1 << 20); + if (err < 0) { + dev_err(&intf->dev, "buffer allocation failed: %d\n", err); + goto error; + } + + err = snd_card_register(chip->card); + if (err < 0) { + dev_err(&intf->dev, "snd_card_register failed: %d\n", err); + goto error; + } + + usb_set_intfdata(intf, chip); + dev_info(&intf->dev, + "Babyface Pro: card %i, %u frames/URB, %u URBs/direction\n", + chip->card->number, chip->frames_per_urb, chip->nurbs); + return 0; + +error: + usb_set_intfdata(intf, NULL); + /* Balance the probe()-time usb_disable_autosuspend(): disconnect() + * is never called for a failed probe, so the disable would leak and + * leave autosuspend off on this usb_device until a physical unplug. + */ + usb_enable_autosuspend(chip->dev); + snd_card_free(chip->card); + return err; +} + +static void babyface_disconnect(struct usb_interface *intf) +{ + struct snd_usb_babyface *chip = usb_get_intfdata(intf); + + if (!chip) + return; + + /* Idempotence guard: a disconnect can race a re-probe (usbfs + * detach/re-attach) - tear the card down exactly once. + */ + usb_set_intfdata(intf, NULL); + if (chip->shutdown) + return; + + chip->shutdown = true; + cancel_work_sync(&chip->stream_work); + /* Balance the probe()-time usb_disable_autosuspend(): the usb_device + * outlives this interface claim (a usbfs detach re-probes without + * the physical device ever disconnecting), so leaving autosuspend + * disabled here would wrongly affect whatever claims the device next. + */ + usb_enable_autosuspend(chip->dev); + /* Wake apps blocked in read/write: the card is going away. */ + dev_info(&chip->dev->dev, "disconnect: stopping PCM substreams\n"); + babyface_pcm_stop_both(chip, SNDRV_PCM_STATE_DISCONNECTED); + mutex_lock(&chip->mutex); + if (chip->streaming) + babyface_stream_kill(chip); + mutex_unlock(&chip->mutex); + + snd_card_disconnect(chip->card); + /* NEVER snd_card_free() here: it blocks until the last user + * closes the card, and an open client (e.g. PipeWire) deadlocks + * the disconnect (seen live: pipewire stuck in snd_card_free, + * D state). free_when_closed frees on the last close. + */ + snd_card_free_when_closed(chip->card); +} + +static const struct usb_device_id babyface_ids[] = { + { USB_DEVICE(USB_VENDOR_RME, USB_PRODUCT_BABYFACE_PRO_FS) }, + { } +}; +MODULE_DEVICE_TABLE(usb, babyface_ids); + +static struct usb_driver babyface_driver = { + .name = "snd-usb-babyface-pro", + .probe = babyface_probe, + .disconnect = babyface_disconnect, + .id_table = babyface_ids, +}; + +static int __init babyface_init(void) +{ + return usb_register(&babyface_driver); +} + +static void __exit babyface_exit(void) +{ + usb_deregister(&babyface_driver); +} + +module_init(babyface_init); +module_exit(babyface_exit); + +MODULE_AUTHOR("Ismaïl Bahloul <[email protected]>"); +MODULE_DESCRIPTION("RME Babyface Pro / Pro FS (proprietary mode) USB audio driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/usb/babyfacepro/babyfacepro.h b/sound/usb/babyfacepro/babyfacepro.h new file mode 100644 index 000000000..de0353a59 --- /dev/null +++ b/sound/usb/babyfacepro/babyfacepro.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * RME Babyface Pro / Pro FS - proprietary-mode USB audio driver + * + * The Babyface Pro (and Pro FS) present two personalities on the USB + * bus: a class-compliant one (handled by snd-usb-audio) and a + * proprietary one (VID 0x2a39 / PID 0x3fc0) whose PCM stream runs on + * INTERRUPT endpoints (interface 5, ep 0x01 OUT / 0x82 IN). + * Isochronous transfers are rejected there with EINVAL, and + * snd-usb-audio has no interrupt-PCM path, so this driver is + * standalone (snd-usb-caiaq-style interrupt streaming) instead of an + * snd-usb-audio quirk. + * + * This file and babyfacepro.c cover the card lifecycle (probe, + * disconnect) and the PCM stream only - no mixer controls, no + * front-panel support, no suspend/resume yet. Those are added by + * later patches in this series, each with its own register map and + * device-state additions; see the cover letter for the full series + * plan. + * + * The protocol (vendor requests + 14x32-bit frame layout) was + * reverse-engineered from Windows captures and validated on hardware - + * tools/usbdump/PROTOCOL.md in the driver's development repository is + * the authoritative reference. + * + * Stream notes (hardware-validated 2026-08): + * - frames_per_urb is tunable 8..1024 (multiple of 8) but must be at + * least one alt packet wide - the device delivers IN data in + * alt-sized packets (448/640/1024 B for alt 1/2/3), smaller URBs + * get -EOVERFLOW (babble). So frames_per_urb >= 8/16/32 for + * alt 1/2/3; the driver rejects violating rates in hw_params. + * - The device only advances the stream while BOTH endpoints have a + * pending URB - IN and OUT are always submitted as a pair. + * - SET_INTERFACE(5, alt) selects single/double/quad speed and USB + * packet capacity; the BASE rate is the family register (request + * 0x10, index 0x0030: 32k / 44.1k / 48k family), from which the + * firmware derives its own DDS word. Three families times three + * speeds give all nine rates. The 0x1B DDS quad is varispeed: a + * pitch RATIO applied on top of the family rate, sticky until + * rewritten, with the 48 kHz cold-plug quad meaning x1.0. See + * bf_clock_write / bf_pitch_write. + */ + +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/unaligned.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> + +#define USB_VENDOR_RME 0x2a39 +#define USB_PRODUCT_BABYFACE_PRO_FS 0x3fc0 + +/* The proprietary audio interface (interface 5, interrupt endpoints). */ +#define BF_IFACE 5 +#define BF_EP_OUT 0x01 +#define BF_EP_IN 0x82 + +#define BF_ALT_1 1 /* x1: 32/44.1/48 kHz, 448-B packets */ +#define BF_ALT_2 2 /* x2: 64/88.2/96 kHz, 640-B packets */ +#define BF_ALT_3 3 /* x4: 128/176.4/192 kHz, 1024-B packets */ + +/* Default stream geometry - conservative, matches the RME TotalMix + * 256-sample buffer. Both are tunable via module params; the + * low-latency profile (validated) is frames_per_urb=16 nurbs=16. + */ +#define BF_FRAMES_PER_URB_DEFAULT 256 +#define BF_NURBS_DEFAULT 8 + +#define BF_WORDS_PER_FRAME 14 /* 14 x 32-bit words per frame */ + +/* Consecutive URB errors (CRC/babble/protocol or a failed resubmit) + * before the stream is stopped and the apps get a clean -EPIPE. + */ +#define BF_URB_ERR_STOP 3 + +/* Vendor requests (bmRequestType 0x40, value in wValue, no data phase). + * Only the subset the core probe/PCM path needs; the mixer, front + * panel and DSP EQ patches each add their own as those features land. + */ +#define BF_REQ_KEEPALIVE 0x10 /* settings word / stream trigger */ +#define BF_REQ_REG_CLEAR 0x16 /* cold-init register clear */ +#define BF_REQ_PREAMP 0x17 /* 48V/PAD state + readback */ +#define BF_REQ_DDS 0x1b /* clock quads */ +#define BF_REQ_STATUS_2 0x1c /* read 4 B */ +#define BF_REQ_SESSION_START 0x1d +#define BF_REQ_SESSION_ARM 0x14 +#define BF_REQ_PREAMP_COMMIT 0x21 /* commit after 0x17 */ + +#define BF_REG_RATE_FAMILY 0x0030 /* family << 4 (bReq 0x10) */ +/* 0x05ff = 0x05cf | 0x0030: the settings word and the family register + * written together (measured 2026-09-16: bits 4-5 of the value land in + * the family register). The cold-plug capture writes 0x0021 here, i.e. + * family 48 kHz + clock internal - which is why replaying it after a + * family write used to reset the rate to 48 kHz. + */ +#define BF_REG_KEEPALIVE_INIT 0x05ff +#define BF_REG_KEEPALIVE_SETTINGS 0x05cf + +/* Host settings-state word carried by the BF_REG_KEEPALIVE_SETTINGS + * keepalive (PROTOCOL.md "keepalive 0x10 0x05CF wVal = host settings- + * state register", hardware-verified 2026-08-22/23). Only the + * Internal value is used until the clock-source control lands with a + * later patch in this series. + */ +#define BF_SETTINGS_CLOCK_INTERNAL 0x0001 +#define BF_SETTINGS_CLOCK_OPTICAL 0x0004 + +struct snd_usb_babyface { + struct snd_card *card; + struct usb_device *dev; + struct usb_interface *iface; + + struct mutex mutex; /* controls + stream geometry */ + spinlock_t lock; /* hw_ptr / subs */ + + /* stream */ + struct urb **urbs_in; + struct urb **urbs_out; + void **buf_in; + void **buf_out; + dma_addr_t *dma_in; + dma_addr_t *dma_out; + unsigned int nurbs; + unsigned int frames_per_urb; + unsigned int frame_bytes; /* 56/40/32 for alt 1/2/3 */ + unsigned int rate; + unsigned int alt; + int pitch; /* varispeed in 0.1% (-500..+500) */ + int stream_users; /* PCM substreams sharing the stream */ + bool streaming; /* URBs actually in flight */ + bool shutdown; + atomic_t urb_err; /* consecutive bad URBs (stops the stream) */ + struct work_struct stream_work; + + struct snd_pcm_substream *subs[2]; + unsigned long hw_ptr[2]; + unsigned long prev_period[2]; +}; + +struct bf_rate { + unsigned int rate; + unsigned int alt; + unsigned int frame_bytes; + unsigned int min_fpu; /* frames/URB floor = one alt packet (448/640/1024 B) */ +}; + +/* -- babyfacepro.c ------------------------ */ +const struct bf_rate *bf_rate_lookup(unsigned int rate); +/* The family register value for a rate: 0 / 1 / 2 for the 32k / 44.1k / + * 48k family (rate >> (alt - 1)). + */ +unsigned int bf_rate_family(const struct bf_rate *r); +int bf_vendor_write(struct snd_usb_babyface *chip, u8 req, u16 val, u16 idx); +int bf_vendor_read(struct snd_usb_babyface *chip, u8 req, u16 idx, u8 *buf); +int bf_settings_write(struct snd_usb_babyface *chip); +int bf_clock_write(struct snd_usb_babyface *chip); +int bf_pitch_write(struct snd_usb_babyface *chip, int pitch); +int bf_cold_init(struct snd_usb_babyface *chip); +void babyface_stream_kill(struct snd_usb_babyface *chip); +void babyface_pcm_stop_both(struct snd_usb_babyface *chip, snd_pcm_state_t state); +void babyface_stream_work(struct work_struct *work); +extern const struct snd_pcm_hw_constraint_list bf_rates_constraint; -- 2.55.0