OSDN Git Service

ALSA: usb-audio: Add new quirk FIXED_RATE for JBL Quantum810 Wireless
authorJaroslav Kysela <perex@perex.cz>
Thu, 15 Dec 2022 15:30:37 +0000 (16:30 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 22 Dec 2022 08:13:54 +0000 (09:13 +0100)
It seems that the firmware is broken and does not accept
the UAC_EP_CS_ATTR_SAMPLE_RATE URB. There is only one rate (48000Hz)
available in the descriptors for the output endpoint.

Create a new quirk QUIRK_FLAG_FIXED_RATE to skip the rate setup
when only one rate is available (fixed).

BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=216798
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20221215153037.1163786-1-perex@perex.cz
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/implicit.c
sound/usb/implicit.h
sound/usb/pcm.c
sound/usb/pcm.h
sound/usb/quirks.c
sound/usb/usbaudio.h

index 4006155..6ec95b2 100644 (file)
@@ -131,6 +131,7 @@ struct snd_usb_endpoint {
        bool lowlatency_playback;       /* low-latency playback mode */
        bool need_setup;                /* (re-)need for hw_params? */
        bool need_prepare;              /* (re-)need for prepare? */
+       bool fixed_rate;                /* skip rate setup */
 
        /* for hw constraints */
        const struct audioformat *cur_audiofmt;
index 4aaf078..419302e 100644 (file)
@@ -769,7 +769,8 @@ struct snd_usb_endpoint *
 snd_usb_endpoint_open(struct snd_usb_audio *chip,
                      const struct audioformat *fp,
                      const struct snd_pcm_hw_params *params,
-                     bool is_sync_ep)
+                     bool is_sync_ep,
+                     bool fixed_rate)
 {
        struct snd_usb_endpoint *ep;
        int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
@@ -825,6 +826,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
                ep->implicit_fb_sync = fp->implicit_fb;
                ep->need_setup = true;
                ep->need_prepare = true;
+               ep->fixed_rate = fixed_rate;
 
                usb_audio_dbg(chip, "  channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n",
                              ep->cur_channels, ep->cur_rate,
@@ -1413,11 +1415,13 @@ static int init_sample_rate(struct snd_usb_audio *chip,
        if (clock && !clock->need_setup)
                return 0;
 
-       err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
-       if (err < 0) {
-               if (clock)
-                       clock->rate = 0; /* reset rate */
-               return err;
+       if (!ep->fixed_rate) {
+               err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
+               if (err < 0) {
+                       if (clock)
+                               clock->rate = 0; /* reset rate */
+                       return err;
+               }
        }
 
        if (clock)
index e67ea28..924f435 100644 (file)
@@ -14,7 +14,8 @@ struct snd_usb_endpoint *
 snd_usb_endpoint_open(struct snd_usb_audio *chip,
                      const struct audioformat *fp,
                      const struct snd_pcm_hw_params *params,
-                     bool is_sync_ep);
+                     bool is_sync_ep,
+                     bool fixed_rate);
 void snd_usb_endpoint_close(struct snd_usb_audio *chip,
                            struct snd_usb_endpoint *ep);
 int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
index f3e8484..41ac718 100644 (file)
@@ -15,6 +15,7 @@
 #include "usbaudio.h"
 #include "card.h"
 #include "helper.h"
+#include "pcm.h"
 #include "implicit.h"
 
 enum {
@@ -455,7 +456,8 @@ const struct audioformat *
 snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
                                     const struct audioformat *target,
                                     const struct snd_pcm_hw_params *params,
-                                    int stream)
+                                    int stream,
+                                    bool *fixed_rate)
 {
        struct snd_usb_substream *subs;
        const struct audioformat *fp, *sync_fmt = NULL;
@@ -483,6 +485,8 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
                }
        }
 
+       if (fixed_rate)
+               *fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
        return sync_fmt;
 }
 
index ccb415a..7f1577b 100644 (file)
@@ -9,6 +9,6 @@ const struct audioformat *
 snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
                                     const struct audioformat *target,
                                     const struct snd_pcm_hw_params *params,
-                                    int stream);
+                                    int stream, bool *fixed_rate);
 
 #endif /* __USBAUDIO_IMPLICIT_H */
index 9557bd4..99a66d0 100644 (file)
@@ -157,6 +157,31 @@ find_substream_format(struct snd_usb_substream *subs,
                           true, subs);
 }
 
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
+{
+       const struct audioformat *fp;
+       struct snd_usb_audio *chip = subs->stream->chip;
+       int rate = -1;
+
+       if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE))
+               return false;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+                       return false;
+               if (fp->nr_rates < 1)
+                       continue;
+               if (fp->nr_rates > 1)
+                       return false;
+               if (rate < 0) {
+                       rate = fp->rate_table[0];
+                       continue;
+               }
+               if (rate != fp->rate_table[0])
+                       return false;
+       }
+       return true;
+}
+
 static int init_pitch_v1(struct snd_usb_audio *chip, int ep)
 {
        struct usb_device *dev = chip->dev;
@@ -450,12 +475,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        struct snd_usb_audio *chip = subs->stream->chip;
        const struct audioformat *fmt;
        const struct audioformat *sync_fmt;
+       bool fixed_rate, sync_fixed_rate;
        int ret;
 
        ret = snd_media_start_pipeline(subs);
        if (ret)
                return ret;
 
+       fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
        fmt = find_substream_format(subs, hw_params);
        if (!fmt) {
                usb_audio_dbg(chip,
@@ -469,7 +496,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        if (fmt->implicit_fb) {
                sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
                                                                hw_params,
-                                                               !substream->stream);
+                                                               !substream->stream,
+                                                               &sync_fixed_rate);
                if (!sync_fmt) {
                        usb_audio_dbg(chip,
                                      "cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n",
@@ -482,6 +510,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
                }
        } else {
                sync_fmt = fmt;
+               sync_fixed_rate = fixed_rate;
        }
 
        ret = snd_usb_lock_shutdown(chip);
@@ -499,7 +528,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
                close_endpoints(chip, subs);
        }
 
-       subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false);
+       subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate);
        if (!subs->data_endpoint) {
                ret = -EINVAL;
                goto unlock;
@@ -508,7 +537,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        if (fmt->sync_ep) {
                subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
                                                            hw_params,
-                                                           fmt == sync_fmt);
+                                                           fmt == sync_fmt,
+                                                           sync_fixed_rate);
                if (!subs->sync_endpoint) {
                        ret = -EINVAL;
                        goto unlock;
index 493a4e3..388fe2b 100644 (file)
@@ -6,6 +6,8 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
 int snd_usb_pcm_suspend(struct snd_usb_stream *as);
 int snd_usb_pcm_resume(struct snd_usb_stream *as);
 
+bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *as);
+
 int snd_usb_init_pitch(struct snd_usb_audio *chip,
                       const struct audioformat *fmt);
 void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
index 58b37bf..3d13fdf 100644 (file)
@@ -2152,6 +2152,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
                   QUIRK_FLAG_GENERIC_IMPLICIT_FB),
        DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
                   QUIRK_FLAG_IFACE_SKIP_CLOSE),
+       DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
+                  QUIRK_FLAG_FIXED_RATE),
 
        /* Vendor matches */
        VENDOR_FLG(0x045e, /* MS Lifecam */
index 2aba508..f5a8dca 100644 (file)
@@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation;
  * QUIRK_FLAG_FORCE_IFACE_RESET
  *  Force an interface reset whenever stopping & restarting a stream
  *  (e.g. after xrun)
+ * QUIRK_FLAG_FIXED_RATE
+ *  Do not set PCM rate (frequency) when only one rate is available
+ *  for the given endpoint.
  */
 
 #define QUIRK_FLAG_GET_SAMPLE_RATE     (1U << 0)
@@ -198,5 +201,6 @@ extern bool snd_usb_skip_validation;
 #define QUIRK_FLAG_SKIP_IMPLICIT_FB    (1U << 18)
 #define QUIRK_FLAG_IFACE_SKIP_CLOSE    (1U << 19)
 #define QUIRK_FLAG_FORCE_IFACE_RESET   (1U << 20)
+#define QUIRK_FLAG_FIXED_RATE          (1U << 21)
 
 #endif /* __USBAUDIO_H */