aboutsummaryrefslogtreecommitdiff
path: root/sound/usb/endpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/endpoint.c')
-rw-r--r--sound/usb/endpoint.c70
1 files changed, 47 insertions, 23 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 8c8f9a851f89..48a3843a08f1 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -39,7 +39,9 @@ struct snd_usb_iface_ref {
struct snd_usb_clock_ref {
unsigned char clock;
atomic_t locked;
+ int opened;
int rate;
+ bool need_setup;
struct list_head list;
};
@@ -93,12 +95,13 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
*/
static void release_urb_ctx(struct snd_urb_ctx *u)
{
- if (u->buffer_size)
+ if (u->urb && u->buffer_size)
usb_free_coherent(u->ep->chip->dev, u->buffer_size,
u->urb->transfer_buffer,
u->urb->transfer_dma);
usb_free_urb(u->urb);
u->urb = NULL;
+ u->buffer_size = 0;
}
static const char *usb_error_string(int err)
@@ -802,6 +805,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep = NULL;
goto unlock;
}
+ ep->clock_ref->opened++;
}
ep->cur_audiofmt = fp;
@@ -925,8 +929,10 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
endpoint_set_interface(chip, ep, false);
if (!--ep->opened) {
- if (ep->clock_ref && !atomic_read(&ep->clock_ref->locked))
- ep->clock_ref->rate = 0;
+ if (ep->clock_ref) {
+ if (!--ep->clock_ref->opened)
+ ep->clock_ref->rate = 0;
+ }
ep->iface = 0;
ep->altsetting = 0;
ep->cur_audiofmt = NULL;
@@ -1262,6 +1268,7 @@ static int sync_ep_set_params(struct snd_usb_endpoint *ep)
if (!ep->syncbuf)
return -ENOMEM;
+ ep->nurbs = SYNC_URBS;
for (i = 0; i < SYNC_URBS; i++) {
struct snd_urb_ctx *u = &ep->urb[i];
u->index = i;
@@ -1281,8 +1288,6 @@ static int sync_ep_set_params(struct snd_usb_endpoint *ep)
u->urb->complete = snd_complete_urb;
}
- ep->nurbs = SYNC_URBS;
-
return 0;
out_of_memory:
@@ -1290,6 +1295,29 @@ out_of_memory:
return -ENOMEM;
}
+/* update the rate of the referred clock; return the actual rate */
+static int update_clock_ref_rate(struct snd_usb_audio *chip,
+ struct snd_usb_endpoint *ep)
+{
+ struct snd_usb_clock_ref *clock = ep->clock_ref;
+ int rate = ep->cur_rate;
+
+ if (!clock || clock->rate == rate)
+ return rate;
+ if (clock->rate) {
+ if (atomic_read(&clock->locked))
+ return clock->rate;
+ if (clock->rate != rate) {
+ usb_audio_err(chip, "Mismatched sample rate %d vs %d for EP 0x%x\n",
+ clock->rate, rate, ep->ep_num);
+ return clock->rate;
+ }
+ }
+ clock->rate = rate;
+ clock->need_setup = true;
+ return rate;
+}
+
/*
* snd_usb_endpoint_set_params: configure an snd_usb_endpoint
*
@@ -1351,33 +1379,30 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes;
ep->curframesize = ep->curpacksize / ep->cur_frame_bytes;
- return 0;
+ return update_clock_ref_rate(chip, ep);
}
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;
+ int rate, 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;
- }
- }
+ rate = update_clock_ref_rate(chip, ep);
+ if (rate < 0)
+ return rate;
+ if (clock && !clock->need_setup)
+ return 0;
- err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate);
- if (err < 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 (clock)
- clock->rate = ep->cur_rate;
+ clock->need_setup = false;
return 0;
}
@@ -1628,8 +1653,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending)
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;
+ atomic_dec(&ep->clock_ref->locked);
}
}