mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									a95e163a4b
								
							
						
					
					
						commit
						fd28941cff
					
				
					 9 changed files with 60 additions and 12 deletions
				
			
		| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue