ALSA: usb-audio: work around streaming quirk for MacroSilicon MS2109
authorHector Martin <marcan@marcan.st>
Mon, 10 Aug 2020 08:24:00 +0000 (17:24 +0900)
committerTakashi Iwai <tiwai@suse.de>
Mon, 10 Aug 2020 10:57:12 +0000 (12:57 +0200)
Further investigation of the L-R swap problem on the MS2109 reveals that
the problem isn't that the channels are swapped, but rather that they
are swapped and also out of phase by one sample. In other words, the
issue is actually that the very first frame that comes from the hardware
is a half-frame containing only the right channel, and after that
everything becomes offset.

So introduce a new quirk field to drop the very first 2 bytes that come
in after the format is configured and a capture stream starts. This puts
the channels in phase and in the correct order.

Cc: stable@vger.kernel.org
Signed-off-by: Hector Martin <marcan@marcan.st>
Link: https://lore.kernel.org/r/20200810082400.225858-1-marcan@marcan.st
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/card.h
sound/usb/pcm.c
sound/usb/quirks.c
sound/usb/stream.c

index de43267b9c8af77dca423d8a1bc6079323806ac9..5351d7183b1bdc74d5253fe5728d3a66ce6647a1 100644 (file)
@@ -137,6 +137,7 @@ struct snd_usb_substream {
        unsigned int tx_length_quirk:1; /* add length specifier to transfers */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
        unsigned int pkt_offset_adj;    /* Bytes to drop from beginning of packets (for non-compliant devices) */
+       unsigned int stream_offset_adj; /* Bytes to drop from beginning of stream (for non-compliant devices) */
 
        unsigned int running: 1;        /* running status */
 
index 415bfec49a01a01567786bdafb4d26e0f962b0dc..5600751803cfe1bd322b47d3b2c632468f8ba001 100644 (file)
@@ -1420,6 +1420,12 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
                        // continue;
                }
                bytes = urb->iso_frame_desc[i].actual_length;
+               if (subs->stream_offset_adj > 0) {
+                       unsigned int adj = min(subs->stream_offset_adj, bytes);
+                       cp += adj;
+                       bytes -= adj;
+                       subs->stream_offset_adj -= adj;
+               }
                frames = bytes / stride;
                if (!subs->txfr_quirk)
                        bytes = frames * stride;
index c551141f337ea366d7d3de5d8fa793274f37e309..abf99b814a0f819a7d1d3ecd6cf268e19c9082d9 100644 (file)
@@ -1495,6 +1495,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
        case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
                pioneer_djm_set_format_quirk(subs);
                break;
+       case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
+               subs->stream_offset_adj = 2;
+               break;
        }
 }
 
index 4d1e6579e54dcb1cdbc9aa245b5b6bb157f1f69d..ca76ba5b5c0b2a848266208ee1135a8eb6210d27 100644 (file)
@@ -94,6 +94,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
        subs->tx_length_quirk = as->chip->tx_length_quirk;
        subs->speed = snd_usb_get_speed(subs->dev);
        subs->pkt_offset_adj = 0;
+       subs->stream_offset_adj = 0;
 
        snd_usb_set_pcm_ops(as->pcm, stream);