ALSA: usb-audio: Add new quirk FIXED_RATE for JBL Quantum810 Wireless

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>
This commit is contained in:
Jaroslav Kysela 2022-12-15 16:30:37 +01:00 committed by Takashi Iwai
parent a95e163a4b
commit fd28941cff
9 changed files with 60 additions and 12 deletions

View File

@ -131,6 +131,7 @@ struct snd_usb_endpoint {
bool lowlatency_playback; /* low-latency playback mode */ bool lowlatency_playback; /* low-latency playback mode */
bool need_setup; /* (re-)need for hw_params? */ bool need_setup; /* (re-)need for hw_params? */
bool need_prepare; /* (re-)need for prepare? */ bool need_prepare; /* (re-)need for prepare? */
bool fixed_rate; /* skip rate setup */
/* for hw constraints */ /* for hw constraints */
const struct audioformat *cur_audiofmt; const struct audioformat *cur_audiofmt;

View File

@ -769,7 +769,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip, snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp, const struct audioformat *fp,
const struct snd_pcm_hw_params *params, const struct snd_pcm_hw_params *params,
bool is_sync_ep) bool is_sync_ep,
bool fixed_rate)
{ {
struct snd_usb_endpoint *ep; struct snd_usb_endpoint *ep;
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint; 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->implicit_fb_sync = fp->implicit_fb;
ep->need_setup = true; ep->need_setup = true;
ep->need_prepare = 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", 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, 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) if (clock && !clock->need_setup)
return 0; return 0;
err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate); if (!ep->fixed_rate) {
if (err < 0) { err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate);
if (clock) if (err < 0) {
clock->rate = 0; /* reset rate */ if (clock)
return err; clock->rate = 0; /* reset rate */
return err;
}
} }
if (clock) if (clock)

View File

@ -14,7 +14,8 @@ struct snd_usb_endpoint *
snd_usb_endpoint_open(struct snd_usb_audio *chip, snd_usb_endpoint_open(struct snd_usb_audio *chip,
const struct audioformat *fp, const struct audioformat *fp,
const struct snd_pcm_hw_params *params, 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, void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep); struct snd_usb_endpoint *ep);
int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,

View File

@ -15,6 +15,7 @@
#include "usbaudio.h" #include "usbaudio.h"
#include "card.h" #include "card.h"
#include "helper.h" #include "helper.h"
#include "pcm.h"
#include "implicit.h" #include "implicit.h"
enum { enum {
@ -455,7 +456,8 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target, const struct audioformat *target,
const struct snd_pcm_hw_params *params, const struct snd_pcm_hw_params *params,
int stream) int stream,
bool *fixed_rate)
{ {
struct snd_usb_substream *subs; struct snd_usb_substream *subs;
const struct audioformat *fp, *sync_fmt = NULL; 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; return sync_fmt;
} }

View File

@ -9,6 +9,6 @@ const struct audioformat *
snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
const struct audioformat *target, const struct audioformat *target,
const struct snd_pcm_hw_params *params, const struct snd_pcm_hw_params *params,
int stream); int stream, bool *fixed_rate);
#endif /* __USBAUDIO_IMPLICIT_H */ #endif /* __USBAUDIO_IMPLICIT_H */

View File

@ -157,6 +157,31 @@ find_substream_format(struct snd_usb_substream *subs,
true, 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) static int init_pitch_v1(struct snd_usb_audio *chip, int ep)
{ {
struct usb_device *dev = chip->dev; 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; struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fmt; const struct audioformat *fmt;
const struct audioformat *sync_fmt; const struct audioformat *sync_fmt;
bool fixed_rate, sync_fixed_rate;
int ret; int ret;
ret = snd_media_start_pipeline(subs); ret = snd_media_start_pipeline(subs);
if (ret) if (ret)
return ret; return ret;
fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
fmt = find_substream_format(subs, hw_params); fmt = find_substream_format(subs, hw_params);
if (!fmt) { if (!fmt) {
usb_audio_dbg(chip, usb_audio_dbg(chip,
@ -469,7 +496,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->implicit_fb) { if (fmt->implicit_fb) {
sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt, sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
hw_params, hw_params,
!substream->stream); !substream->stream,
&sync_fixed_rate);
if (!sync_fmt) { if (!sync_fmt) {
usb_audio_dbg(chip, usb_audio_dbg(chip,
"cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n", "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 { } else {
sync_fmt = fmt; sync_fmt = fmt;
sync_fixed_rate = fixed_rate;
} }
ret = snd_usb_lock_shutdown(chip); 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); 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) { if (!subs->data_endpoint) {
ret = -EINVAL; ret = -EINVAL;
goto unlock; goto unlock;
@ -508,7 +537,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->sync_ep) { if (fmt->sync_ep) {
subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt, subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
hw_params, hw_params,
fmt == sync_fmt); fmt == sync_fmt,
sync_fixed_rate);
if (!subs->sync_endpoint) { if (!subs->sync_endpoint) {
ret = -EINVAL; ret = -EINVAL;
goto unlock; goto unlock;

View 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_suspend(struct snd_usb_stream *as);
int snd_usb_pcm_resume(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, int snd_usb_init_pitch(struct snd_usb_audio *chip,
const struct audioformat *fmt); const struct audioformat *fmt);
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs); void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);

View File

@ -2152,6 +2152,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB), QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */ DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
QUIRK_FLAG_IFACE_SKIP_CLOSE), QUIRK_FLAG_IFACE_SKIP_CLOSE),
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
QUIRK_FLAG_FIXED_RATE),
/* Vendor matches */ /* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */ VENDOR_FLG(0x045e, /* MS Lifecam */

View File

@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation;
* QUIRK_FLAG_FORCE_IFACE_RESET * QUIRK_FLAG_FORCE_IFACE_RESET
* Force an interface reset whenever stopping & restarting a stream * Force an interface reset whenever stopping & restarting a stream
* (e.g. after xrun) * (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) #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_SKIP_IMPLICIT_FB (1U << 18)
#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19) #define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19)
#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20) #define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
#define QUIRK_FLAG_FIXED_RATE (1U << 21)
#endif /* __USBAUDIO_H */ #endif /* __USBAUDIO_H */