diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/caiaq/device.c | 2 | ||||
-rw-r--r-- | sound/usb/card.c | 4 | ||||
-rw-r--r-- | sound/usb/clock.c | 3 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 32 | ||||
-rw-r--r-- | sound/usb/endpoint.h | 3 | ||||
-rw-r--r-- | sound/usb/mixer_quirks.c | 159 | ||||
-rw-r--r-- | sound/usb/pcm.c | 67 |
7 files changed, 183 insertions, 87 deletions
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 64aed432ae2..7da0d0aa72c 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -485,7 +485,7 @@ static int __devinit snd_probe(struct usb_interface *intf, const struct usb_device_id *id) { int ret; - struct snd_card *card; + struct snd_card *card = NULL; struct usb_device *device = interface_to_usbdev(intf); ret = create_card(device, intf, &card); diff --git a/sound/usb/card.c b/sound/usb/card.c index d5b5c3388e2..4a469f0cb6d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -553,7 +553,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, struct snd_usb_audio *chip) { struct snd_card *card; - struct list_head *p; + struct list_head *p, *n; if (chip == (void *)-1L) return; @@ -570,7 +570,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, snd_usb_stream_disconnect(p); } /* release the endpoint resources */ - list_for_each(p, &chip->ep_list) { + list_for_each_safe(p, n, &chip->ep_list) { snd_usb_endpoint_free(p); } /* release the midi resources */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 379baad3d5a..5e634a2eb28 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -111,7 +111,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) return 0; /* If a clock source can't tell us whether it's valid, we assume it is */ - if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) + if (!uac2_control_is_readable(cs_desc->bmControls, + UAC2_CS_CONTROL_CLOCK_VALID - 1)) return 1; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 0f647d22cb4..060dccb9ec7 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -141,7 +141,7 @@ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) * * For implicit feedback, next_packet_size() is unused. */ -static int next_packet_size(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -177,15 +177,6 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, ep->retire_data_urb(ep->data_subs, urb); } -static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *ctx) -{ - int i; - - for (i = 0; i < ctx->packets; ++i) - ctx->packet_size[i] = next_packet_size(ep); -} - /* * Prepare a PLAYBACK urb for submission to the bus. */ @@ -206,7 +197,13 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, /* no data provider, so send silence */ unsigned int offs = 0; for (i = 0; i < ctx->packets; ++i) { - int counts = ctx->packet_size[i]; + int counts; + + if (ctx->packet_size[i]) + counts = ctx->packet_size[i]; + else + counts = snd_usb_endpoint_next_packet_size(ep); + urb->iso_frame_desc[i].offset = offs * ep->stride; urb->iso_frame_desc[i].length = counts * ep->stride; offs += counts; @@ -370,7 +367,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; } - prepare_outbound_urb_sizes(ep, ctx); prepare_outbound_urb(ep, ctx); } else { retire_inbound_urb(ep, ctx); @@ -799,7 +795,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, /** * snd_usb_endpoint_start: start an snd_usb_endpoint * - * @ep: the endpoint to start + * @ep: the endpoint to start + * @can_sleep: flag indicating whether the operation is executed in + * non-atomic context * * A call to this function will increment the use count of the endpoint. * In case it is not already running, the URBs for this endpoint will be @@ -809,7 +807,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) { int err; unsigned int i; @@ -822,8 +820,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) return 0; /* just to be sure */ - deactivate_urbs(ep, 0, 1); - wait_clear_urbs(ep); + deactivate_urbs(ep, 0, can_sleep); + if (can_sleep) + wait_clear_urbs(ep); ep->active_mask = 0; ep->unlink_mask = 0; @@ -854,7 +853,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) goto __error; if (usb_pipeout(ep->pipe)) { - prepare_outbound_urb_sizes(ep, urb->context); prepare_outbound_urb(ep, urb->context); } else { prepare_inbound_urb(ep, urb->context); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index ee2723fb174..cbbbdf226d6 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -13,7 +13,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep, int wait); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); @@ -21,6 +21,7 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct list_head *head); int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, struct snd_usb_endpoint *sender, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 41f4b691192..690000db0ec 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -42,6 +42,13 @@ extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; +struct std_mono_table { + unsigned int unitid, control, cmask; + int val_type; + const char *name; + snd_kcontrol_tlv_rw_t *tlv_callback; +}; + /* private_free callback */ static void usb_mixer_elem_free(struct snd_kcontrol *kctl) { @@ -114,6 +121,25 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, } /* + * Create a set of standard UAC controls from a table + */ +static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, + struct std_mono_table *t) +{ + int err; + + while (t->name != NULL) { + err = snd_create_std_mono_ctl(mixer, t->unitid, t->control, + t->cmask, t->val_type, t->name, t->tlv_callback); + if (err < 0) + return err; + t++; + } + + return 0; +} + +/* * Sound Blaster remote control configuration * * format of remote control data: @@ -916,61 +942,6 @@ static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) return 0; } - -/* - * Create mixer for Electrix Ebox-44 - * - * The mixer units from this device are corrupt, and even where they - * are valid they presents mono controls as L and R channels of - * stereo. So we create a good mixer in code. - */ - -static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer) -{ - int err; - - err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, - "Headphone Playback Switch", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, - "Headphone A Mix Playback Volume", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, - "Headphone B Mix Playback Volume", NULL); - if (err < 0) - return err; - - err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, - "Output Playback Switch", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, - "Output A Playback Volume", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, - "Output B Playback Volume", NULL); - if (err < 0) - return err; - - err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, - "Input Capture Switch", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, - "Input A Capture Volume", NULL); - if (err < 0) - return err; - err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, - "Input B Capture Volume", NULL); - if (err < 0) - return err; - - return 0; -} - void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, unsigned char samplerate_id) { @@ -990,6 +961,81 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, } } +/* + * The mixer units for Ebox-44 are corrupt, and even where they + * are valid they presents mono controls as L and R channels of + * stereo. So we provide a good mixer here. + */ +struct std_mono_table ebox44_table[] = { + { + .unitid = 4, + .control = 1, + .cmask = 0x0, + .val_type = USB_MIXER_INV_BOOLEAN, + .name = "Headphone Playback Switch" + }, + { + .unitid = 4, + .control = 2, + .cmask = 0x1, + .val_type = USB_MIXER_S16, + .name = "Headphone A Mix Playback Volume" + }, + { + .unitid = 4, + .control = 2, + .cmask = 0x2, + .val_type = USB_MIXER_S16, + .name = "Headphone B Mix Playback Volume" + }, + + { + .unitid = 7, + .control = 1, + .cmask = 0x0, + .val_type = USB_MIXER_INV_BOOLEAN, + .name = "Output Playback Switch" + }, + { + .unitid = 7, + .control = 2, + .cmask = 0x1, + .val_type = USB_MIXER_S16, + .name = "Output A Playback Volume" + }, + { + .unitid = 7, + .control = 2, + .cmask = 0x2, + .val_type = USB_MIXER_S16, + .name = "Output B Playback Volume" + }, + + { + .unitid = 10, + .control = 1, + .cmask = 0x0, + .val_type = USB_MIXER_INV_BOOLEAN, + .name = "Input Capture Switch" + }, + { + .unitid = 10, + .control = 2, + .cmask = 0x1, + .val_type = USB_MIXER_S16, + .name = "Input A Capture Volume" + }, + { + .unitid = 10, + .control = 2, + .cmask = 0x2, + .val_type = USB_MIXER_S16, + .name = "Input B Capture Volume" + }, + + {} +}; + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -1035,7 +1081,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) break; case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */ - err = snd_ebox44_create_mixer(mixer); + /* detection is disabled in mixer_maps.c */ + err = snd_create_std_mono_table(mixer, ebox44_table); break; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a1298f37942..f782ce19bf5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -212,7 +212,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs) +static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) { int err; @@ -225,7 +225,7 @@ static int start_endpoints(struct snd_usb_substream *subs) snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep); ep->data_subs = subs; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); return err; @@ -236,10 +236,25 @@ static int start_endpoints(struct snd_usb_substream *subs) !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->sync_endpoint; + if (subs->data_endpoint->iface != subs->sync_endpoint->iface || + subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) { + err = usb_set_interface(subs->dev, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx); + if (err < 0) { + snd_printk(KERN_ERR + "%d:%d:%d: cannot set interface (%d)\n", + subs->dev->devnum, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx, err); + return -EIO; + } + } + snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep); ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); return err; @@ -547,7 +562,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - return start_endpoints(subs); + return start_endpoints(subs, 1); return 0; } @@ -1029,6 +1044,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, struct urb *urb) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + struct snd_usb_endpoint *ep = subs->data_endpoint; struct snd_urb_ctx *ctx = urb->context; unsigned int counts, frames, bytes; int i, stride, period_elapsed = 0; @@ -1040,7 +1056,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); for (i = 0; i < ctx->packets; i++) { - counts = ctx->packet_size[i]; + if (ctx->packet_size[i]) + counts = ctx->packet_size[i]; + else + counts = snd_usb_endpoint_next_packet_size(ep); + /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].length = counts * stride; @@ -1091,7 +1111,16 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done += bytes; if (subs->hwptr_done >= runtime->buffer_size * stride) subs->hwptr_done -= runtime->buffer_size * stride; + + /* update delay with exact number of samples queued */ + runtime->delay = subs->last_delay; runtime->delay += frames; + subs->last_delay = runtime->delay; + + /* realign last_frame_number */ + subs->last_frame_number = usb_get_current_frame_number(subs->dev); + subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ + spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer_length = bytes; if (period_elapsed) @@ -1109,12 +1138,32 @@ static void retire_playback_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; int stride = runtime->frame_bits >> 3; int processed = urb->transfer_buffer_length / stride; + int est_delay; + + /* ignore the delay accounting when procssed=0 is given, i.e. + * silent payloads are procssed before handling the actual data + */ + if (!processed) + return; spin_lock_irqsave(&subs->lock, flags); - if (processed > runtime->delay) - runtime->delay = 0; + est_delay = snd_usb_pcm_delay(subs, runtime->rate); + /* update delay with exact number of samples played */ + if (processed > subs->last_delay) + subs->last_delay = 0; else - runtime->delay -= processed; + subs->last_delay -= processed; + runtime->delay = subs->last_delay; + + /* + * Report when delay estimate is off by more than 2ms. + * The error should be lower than 2ms since the estimate relies + * on two reads of a counter updated every ms. + */ + if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) + snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", + est_delay, subs->last_delay); + spin_unlock_irqrestore(&subs->lock, flags); } @@ -1172,7 +1221,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs); + err = start_endpoints(subs, 0); if (err < 0) return err; |