From: Hanh Kieu <hhkieu@gmail.com>
To: perex@perex.cz, tiwai@suse.com
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] ALSA: usb-audio: add Pioneer DJ DDJ-SZ support
Date: Thu, 3 Sep 2026 16:16:41 -0700 [thread overview]
Message-ID: <20260903231641.18536-1-hhkieu@gmail.com> (raw)
The Pioneer DJ DDJ-SZ exposes its audio interface as USB vendor-specific
class (0xFF) rather than USB Audio Class, so it needs a quirks-table
entry like its sibling Pioneer devices (DJM-750, DJM-850, DJM-900NXS2,
DJM-450, DJM-V10) already have.
The device presents 10 channels of S24_3LE audio in both directions,
fixed at 44.1kHz, on interface 0 altsetting 1: playback on endpoint
0x01, capture on endpoint 0x82. The unit contains its own analog mixer,
and each playback channel pair feeds one of its physical channel strips:
0/1, 2/3, 4/5 and 6/7 feed strips 1-4 respectively, and 8/9 feed the
booth output. The master output is produced in analog by that mixer and
is not carried over USB at all. On the capture side, channels 8/9 are
the mic input; capture channels 0-7 are not yet mapped to specific
physical inputs.
Implicit feedback needs no quirk flag here: is_pioneer_implicit_fb() in
implicit.c already covers vendor 0x08e4 with a vendor-spec class
interface and two endpoints, and the driver duly reports endpoint 0x82
as the playback sync endpoint.
Activation reuses the existing pioneer_djm_set_format_quirk() used by
the DJM-750/850/900NXS2/450/V10 (SET_INTERFACE to altsetting 1, then a
UAC-shaped SET_CUR sample-rate control transfer) with this device's own
captured wIndex (0x0082). Unlike those devices, the DDJ-SZ additionally
needs a vendor "arm" sequence before its capture path produces real
audio -- without it, capture opens and runs with no USB/ALSA errors
but delivers silence (a hard zero on every channel) rather than any
error, so this is easy to miss. The arm sequence is six vendor control
transfers (bmRequestType=0x40, bRequest=3, varying wValue/wIndex, each
followed by a bmRequestType=0xc0, bRequest=0 status read), replicated
byte-for-byte from a USB capture of the official Windows driver.
All of the above -- endpoint numbers, format, channel mapping, and the
arm sequence bytes -- were determined by capturing and decoding real USB
traffic from the Windows driver (USBPcap + Wireshark) during
enumeration, playback, and mic recording, then verifying the format
hypothesis against actual de-interleaved payload data rather than
packet-size arithmetic alone. Both playback and capture have been
verified working with real audio, not just clean enumeration.
Signed-off-by: Hanh Kieu <hhkieu@gmail.com>
---
sound/usb/quirks-table.h | 47 +++++++++++++++++++++++++++++++
sound/usb/quirks.c | 60 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index a1a33f11d..2da8cbe3e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3731,6 +3731,53 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /*
+ * Pioneer DJ DDJ-SZ
+ * 10 channels playback & 10 channels capture @ 44.1kHz S24LE
+ */
+ USB_DEVICE_VENDOR_SPEC(0x08e4, 0x0191),
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ {
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ {
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x82,
+ .ep_idx = 1,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ QUIRK_COMPOSITE_END
+ }
+ }
+},
{
/*
* Pioneer DJ DJM-750MK2
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 4936d66f9..f09599c0d 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1767,6 +1767,62 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs,
subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
}
+/*
+ * The DDJ-SZ needs a vendor "arm" sequence before its capture path
+ * produces real audio; without it capture runs with no USB or ALSA error
+ * but delivers a hard zero on every channel. The sequence is replicated
+ * byte-for-byte from a USB capture of the Windows driver: each write is
+ * followed by a status read whose content is a fixed value regardless of
+ * what was written, but the read is replicated too, since it is unclear
+ * whether the device requires it to process the preceding write.
+ *
+ * This runs from snd_usb_set_format_quirk(), i.e. on every format setup
+ * rather than once per device. Re-arming is harmless in practice and
+ * keeps the device armed if it is reset behind our back.
+ */
+static void ddj_sz_arm_quirk(struct usb_device *dev)
+{
+ static const struct {
+ u16 value;
+ u16 index;
+ u8 read_len;
+ } cmds[] = {
+ { 0x0100, 0x8002, 6 },
+ { 0x0200, 0x8002, 6 },
+ { 0x0303, 0x8002, 6 },
+ { 0x0403, 0x8002, 6 },
+ { 0x050a, 0x8002, 6 },
+ { 0x0000, 0x8003, 2 },
+ };
+ u8 buf[6];
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(cmds); i++) {
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 3,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ cmds[i].value, cmds[i].index, NULL, 0);
+ if (err < 0)
+ goto err_out;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ 0x0000, cmds[i].index, buf,
+ cmds[i].read_len);
+ if (err < 0)
+ goto err_out;
+ }
+
+ return;
+
+err_out:
+ dev_warn(&dev->dev,
+ "DDJ-SZ: arm sequence step %u failed (%d), capture may be silent\n",
+ i, err);
+}
+
static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs,
u16 windex)
{
@@ -1948,6 +2004,10 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x08e4, 0x0163): /* Pioneer DJM-850 */
pioneer_djm_set_format_quirk(subs, 0x0086);
break;
+ case USB_ID(0x08e4, 0x0191): /* Pioneer DDJ-SZ */
+ ddj_sz_arm_quirk(subs->dev);
+ pioneer_djm_set_format_quirk(subs, 0x0082);
+ break;
case USB_ID(0x0dba, 0x5000):
mbox3_set_format_quirk(subs, fmt); /* Digidesign Mbox 3 */
break;
base-commit: 4c4e4be4edd5dbf5f7f6a3ef4fc0c243f2d01b3c
--
2.47.3
next reply other threads:[~2026-09-03 23:17 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 23:16 Hanh Kieu [this message]
2026-09-04 3:59 ` Geraldo Nascimento
2026-09-04 18:20 ` Hanh Kieu
2026-09-05 3:30 ` Geraldo Nascimento
[not found] ` <CAEWo5SpV6eyHo-tn+hKX4s98uCjGJg5Dz9fMkoWNDNqgofJ5og@mail.gmail.com>
2026-09-05 6:19 ` Geraldo Nascimento
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260903231641.18536-1-hhkieu@gmail.com \
--to=hhkieu@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®