diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/card.c | 1 | ||||
-rw-r--r-- | sound/usb/card.h | 3 | ||||
-rw-r--r-- | sound/usb/clock.c | 7 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 90 | ||||
-rw-r--r-- | sound/usb/implicit.c | 10 | ||||
-rw-r--r-- | sound/usb/pcm.c | 17 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 3 | ||||
-rw-r--r-- | sound/usb/quirks.c | 6 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 7 |
9 files changed, 122 insertions, 22 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c index 376962291c4d..0fff96a5d3ab 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -635,6 +635,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, INIT_LIST_HEAD(&chip->pcm_list); INIT_LIST_HEAD(&chip->ep_list); INIT_LIST_HEAD(&chip->iface_ref_list); + INIT_LIST_HEAD(&chip->clock_ref_list); INIT_LIST_HEAD(&chip->midi_list); INIT_LIST_HEAD(&chip->mixer_list); diff --git a/sound/usb/card.h b/sound/usb/card.h index 87f042d06ce0..ca75f2206170 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -44,6 +44,7 @@ struct audioformat { struct snd_usb_substream; struct snd_usb_iface_ref; +struct snd_usb_clock_ref; struct snd_usb_endpoint; struct snd_usb_power_domain; @@ -62,6 +63,7 @@ struct snd_urb_ctx { struct snd_usb_endpoint { struct snd_usb_audio *chip; struct snd_usb_iface_ref *iface_ref; + struct snd_usb_clock_ref *clock_ref; int opened; /* open refcount; protect with chip->mutex */ atomic_t running; /* running status */ @@ -138,7 +140,6 @@ struct snd_usb_endpoint { unsigned int cur_period_frames; unsigned int cur_period_bytes; unsigned int cur_buffer_periods; - unsigned char cur_clock; spinlock_t lock; struct list_head list; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 4dfe76416794..3c435d379306 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -572,6 +572,13 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, /* continue processing */ } + /* FIXME - TEAC devices require the immediate interface setup */ + if (rate != prev_rate && USB_ID_VENDOR(chip->usb_id) == 0x0644) { + usb_set_interface(chip->dev, fmt->iface, fmt->altsetting); + if (chip->quirk_flags & QUIRK_FLAG_IFACE_DELAY) + msleep(50); + } + validation: /* validate clock after rate change */ if (!uac_clock_source_is_valid(chip, fmt, clock)) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 743b8287cfcd..f9c921683948 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -35,6 +35,14 @@ struct snd_usb_iface_ref { struct list_head list; }; +/* clock refcounting */ +struct snd_usb_clock_ref { + unsigned char clock; + atomic_t locked; + int rate; + struct list_head list; +}; + /* * snd_usb_endpoint is a model that abstracts everything related to an * USB endpoint and its streaming. @@ -591,6 +599,25 @@ iface_ref_find(struct snd_usb_audio *chip, int iface) return ip; } +/* Similarly, a refcount object for clock */ +static struct snd_usb_clock_ref * +clock_ref_find(struct snd_usb_audio *chip, int clock) +{ + struct snd_usb_clock_ref *ref; + + list_for_each_entry(ref, &chip->clock_ref_list, list) + if (ref->clock == clock) + return ref; + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) + return NULL; + ref->clock = clock; + atomic_set(&ref->locked, 0); + list_add_tail(&ref->list, &chip->clock_ref_list); + return ref; +} + /* * Get the existing endpoint object corresponding EP * Returns NULL if not present. @@ -768,6 +795,14 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, goto unlock; } + if (fp->protocol != UAC_VERSION_1) { + ep->clock_ref = clock_ref_find(chip, fp->clock); + if (!ep->clock_ref) { + ep = NULL; + goto unlock; + } + } + ep->cur_audiofmt = fp; ep->cur_channels = fp->channels; ep->cur_rate = params_rate(params); @@ -777,7 +812,6 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ep->cur_period_frames = params_period_size(params); ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes; ep->cur_buffer_periods = params_periods(params); - ep->cur_clock = fp->clock; if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) endpoint_set_syncinterval(chip, ep); @@ -894,8 +928,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, ep->altsetting = 0; ep->cur_audiofmt = NULL; ep->cur_rate = 0; - ep->cur_clock = 0; ep->iface_ref = NULL; + ep->clock_ref = NULL; usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num); } mutex_unlock(&chip->mutex); @@ -907,6 +941,8 @@ void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep) ep->need_setup = true; if (ep->iface_ref) ep->iface_ref->need_setup = true; + if (ep->clock_ref) + ep->clock_ref->rate = 0; } /* @@ -1314,6 +1350,33 @@ static int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, return 0; } +static int init_sample_rate(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) +{ + struct snd_usb_clock_ref *clock = ep->clock_ref; + int err; + + if (clock) { + if (atomic_read(&clock->locked)) + return 0; + if (clock->rate == ep->cur_rate) + return 0; + if (clock->rate && clock->rate != ep->cur_rate) { + usb_audio_dbg(chip, "Mismatched sample rate %d vs %d for EP 0x%x\n", + clock->rate, ep->cur_rate, ep->ep_num); + return -EINVAL; + } + } + + err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate); + if (err < 0) + return err; + + if (clock) + clock->rate = ep->cur_rate; + return 0; +} + /* * snd_usb_endpoint_configure: Configure the endpoint * @@ -1343,8 +1406,7 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip, * to update at each EP configuration */ if (ep->cur_audiofmt->protocol == UAC_VERSION_1) { - err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, - ep->cur_rate); + err = init_sample_rate(chip, ep); if (err < 0) goto unlock; } @@ -1374,7 +1436,7 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip, if (err < 0) goto unlock; - err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate); + err = init_sample_rate(chip, ep); if (err < 0) goto unlock; @@ -1407,15 +1469,15 @@ unlock: /* get the current rate set to the given clock by any endpoint */ int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock) { - struct snd_usb_endpoint *ep; + struct snd_usb_clock_ref *ref; int rate = 0; if (!clock) return 0; mutex_lock(&chip->mutex); - list_for_each_entry(ep, &chip->ep_list, list) { - if (ep->cur_clock == clock && ep->cur_rate) { - rate = ep->cur_rate; + list_for_each_entry(ref, &chip->clock_ref_list, list) { + if (ref->clock == clock) { + rate = ref->rate; break; } } @@ -1456,6 +1518,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (atomic_inc_return(&ep->running) != 1) return 0; + if (ep->clock_ref) + atomic_inc(&ep->clock_ref->locked); + ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; @@ -1565,6 +1630,9 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending) if (ep->sync_source) WRITE_ONCE(ep->sync_source->sync_sink, NULL); stop_urbs(ep, false, keep_pending); + if (ep->clock_ref) + if (!atomic_dec_return(&ep->clock_ref->locked)) + ep->clock_ref->rate = 0; } } @@ -1591,12 +1659,16 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip) { struct snd_usb_endpoint *ep, *en; struct snd_usb_iface_ref *ip, *in; + struct snd_usb_clock_ref *cp, *cn; list_for_each_entry_safe(ep, en, &chip->ep_list, list) kfree(ep); list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list) kfree(ip); + + list_for_each_entry_safe(cp, cn, &chip->clock_ref_list, list) + kfree(cp); } /* diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 2d444ec74202..e1bf1b5da423 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -45,11 +45,6 @@ struct snd_usb_implicit_fb_match { /* Implicit feedback quirk table for playback */ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { - /* Generic matching */ - IMPLICIT_FB_GENERIC_DEV(0x0499, 0x1509), /* Steinberg UR22 */ - IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2030), /* M-Audio Fast Track C400 */ - IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2031), /* M-Audio Fast Track C600 */ - /* Fixed EP */ /* FIXME: check the availability of generic matching */ IMPLICIT_FB_FIXED_DEV(0x0763, 0x2080, 0x81, 2), /* M-Audio FastTrack Ultra */ @@ -350,7 +345,8 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, } /* Try the generic implicit fb if available */ - if (chip->generic_implicit_fb) + if (chip->generic_implicit_fb || + (chip->quirk_flags & QUIRK_FLAG_GENERIC_IMPLICIT_FB)) return add_generic_implicit_fb(chip, fmt, alts); /* No quirk */ @@ -387,6 +383,8 @@ int snd_usb_parse_implicit_fb_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { + if (chip->quirk_flags & QUIRK_FLAG_SKIP_IMPLICIT_FB) + return 0; if (fmt->endpoint & USB_DIR_IN) return audioformat_capture_quirk(chip, fmt, alts); else diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 6d699065e81a..b470404a5376 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -439,16 +439,21 @@ static int configure_endpoints(struct snd_usb_audio *chip, /* stop any running stream beforehand */ if (stop_endpoints(subs, false)) sync_pending_stops(subs); + if (subs->sync_endpoint) { + err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); + if (err < 0) + return err; + } err = snd_usb_endpoint_configure(chip, subs->data_endpoint); if (err < 0) return err; snd_usb_set_format_quirk(subs, subs->cur_audiofmt); - } - - if (subs->sync_endpoint) { - err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); - if (err < 0) - return err; + } else { + if (subs->sync_endpoint) { + err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); + if (err < 0) + return err; + } } return 0; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 40a5e3eb4ef2..78eb41b621d6 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2672,6 +2672,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .altset_idx = 1, .attributes = 0, .endpoint = 0x82, + .ep_idx = 1, .ep_attr = USB_ENDPOINT_XFER_ISOC, .datainterval = 1, .maxpacksize = 0x0126, @@ -2875,6 +2876,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .altset_idx = 1, .attributes = 0x4, .endpoint = 0x81, + .ep_idx = 1, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, .maxpacksize = 0x130, @@ -3391,6 +3393,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .altset_idx = 1, .attributes = 0, .endpoint = 0x03, + .ep_idx = 1, .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index fbbe59054c3f..e8468f9b007d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1793,6 +1793,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x046d, 0x09a4, /* Logitech QuickCam E 3500 */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */ @@ -1826,6 +1828,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x074d, 0x3553, /* Outlaw RR2150 (Micronas UAC3553B) */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0763, 0x2030, /* M-Audio Fast Track C400 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b8359a0aa008..ffbb4b0d09a0 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -45,6 +45,7 @@ struct snd_usb_audio { struct list_head pcm_list; /* list of pcm streams */ struct list_head ep_list; /* list of audio-related endpoints */ struct list_head iface_ref_list; /* list of interface refcounts */ + struct list_head clock_ref_list; /* list of clock refcounts */ int pcm_devs; struct list_head midi_list; /* list of midi interfaces */ @@ -164,6 +165,10 @@ extern bool snd_usb_skip_validation; * Support generic DSD raw U32_BE format * QUIRK_FLAG_SET_IFACE_FIRST: * Set up the interface at first like UAC1 + * QUIRK_FLAG_GENERIC_IMPLICIT_FB + * Apply the generic implicit feedback sync mode (same as implicit_fb=1 option) + * QUIRK_FLAG_SKIP_IMPLICIT_FB + * Don't apply implicit feedback sync mode */ #define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0) @@ -183,5 +188,7 @@ extern bool snd_usb_skip_validation; #define QUIRK_FLAG_IGNORE_CTL_ERROR (1U << 14) #define QUIRK_FLAG_DSD_RAW (1U << 15) #define QUIRK_FLAG_SET_IFACE_FIRST (1U << 16) +#define QUIRK_FLAG_GENERIC_IMPLICIT_FB (1U << 17) +#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18) #endif /* __USBAUDIO_H */ |