summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.c1
-rw-r--r--sound/usb/card.h3
-rw-r--r--sound/usb/clock.c7
-rw-r--r--sound/usb/endpoint.c90
-rw-r--r--sound/usb/implicit.c10
-rw-r--r--sound/usb/pcm.c17
-rw-r--r--sound/usb/quirks-table.h3
-rw-r--r--sound/usb/quirks.c6
-rw-r--r--sound/usb/usbaudio.h7
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 */