diff options
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 166 |
1 files changed, 107 insertions, 59 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index f782ce19bf5..5c12a3fe8c3 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -71,6 +71,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream unsigned int hwptr_done; subs = (struct snd_usb_substream *)substream->runtime->private_data; + if (subs->stream->chip->shutdown) + return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; substream->runtime->delay = snd_usb_pcm_delay(subs, @@ -82,8 +84,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream /* * find a matching audio format */ -static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format, - unsigned int rate, unsigned int channels) +static struct audioformat *find_format(struct snd_usb_substream *subs) { struct list_head *p; struct audioformat *found = NULL; @@ -92,16 +93,17 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (!(fp->formats & (1uLL << format))) + if (!(fp->formats & (1uLL << subs->pcm_format))) continue; - if (fp->channels != channels) + if (fp->channels != subs->channels) continue; - if (rate < fp->rate_min || rate > fp->rate_max) + if (subs->cur_rate < fp->rate_min || + subs->cur_rate > fp->rate_max) continue; if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { unsigned int i; for (i = 0; i < fp->nr_rates; i++) - if (fp->rate_table[i] == rate) + if (fp->rate_table[i] == subs->cur_rate) break; if (i >= fp->nr_rates) continue; @@ -436,6 +438,38 @@ add_sync_ep: } /* + * configure endpoint params + * + * called during initial setup and upon resume + */ +static int configure_endpoint(struct snd_usb_substream *subs) +{ + int ret; + + /* format changed */ + stop_endpoints(subs, 0, 0, 0); + ret = snd_usb_endpoint_set_params(subs->data_endpoint, + subs->pcm_format, + subs->channels, + subs->period_bytes, + subs->cur_rate, + subs->cur_audiofmt, + subs->sync_endpoint); + if (ret < 0) + return ret; + + if (subs->sync_endpoint) + ret = snd_usb_endpoint_set_params(subs->data_endpoint, + subs->pcm_format, + subs->channels, + subs->period_bytes, + subs->cur_rate, + subs->cur_audiofmt, + NULL); + return ret; +} + +/* * hw_params callback * * allocate a buffer and set the given audio format. @@ -450,63 +484,39 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, { struct snd_usb_substream *subs = substream->runtime->private_data; struct audioformat *fmt; - unsigned int channels, rate, format; - int ret, changed; + int ret; ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); - fmt = find_format(subs, format, rate, channels); + subs->pcm_format = params_format(hw_params); + subs->period_bytes = params_period_bytes(hw_params); + subs->channels = params_channels(hw_params); + subs->cur_rate = params_rate(hw_params); + + fmt = find_format(subs); if (!fmt) { snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n", - format, rate, channels); + subs->pcm_format, subs->cur_rate, subs->channels); return -EINVAL; } - changed = subs->cur_audiofmt != fmt || - subs->period_bytes != params_period_bytes(hw_params) || - subs->cur_rate != rate; - if ((ret = set_format(subs, fmt)) < 0) + down_read(&subs->stream->chip->shutdown_rwsem); + if (subs->stream->chip->shutdown) + ret = -ENODEV; + else + ret = set_format(subs, fmt); + up_read(&subs->stream->chip->shutdown_rwsem); + if (ret < 0) return ret; - if (subs->cur_rate != rate) { - struct usb_host_interface *alts; - struct usb_interface *iface; - iface = usb_ifnum_to_if(subs->dev, fmt->iface); - alts = &iface->altsetting[fmt->altset_idx]; - ret = snd_usb_init_sample_rate(subs->stream->chip, fmt->iface, alts, fmt, rate); - if (ret < 0) - return ret; - subs->cur_rate = rate; - } - - if (changed) { - mutex_lock(&subs->stream->chip->shutdown_mutex); - /* format changed */ - stop_endpoints(subs, 0, 0, 0); - ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt, - subs->sync_endpoint); - if (ret < 0) - goto unlock; - - if (subs->sync_endpoint) - ret = snd_usb_endpoint_set_params(subs->sync_endpoint, - hw_params, fmt, NULL); -unlock: - mutex_unlock(&subs->stream->chip->shutdown_mutex); - } - - if (ret == 0) { - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - } + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + subs->need_setup_ep = true; - return ret; + return 0; } /* @@ -521,10 +531,12 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; - mutex_lock(&subs->stream->chip->shutdown_mutex); - stop_endpoints(subs, 0, 1, 1); - deactivate_endpoints(subs); - mutex_unlock(&subs->stream->chip->shutdown_mutex); + down_read(&subs->stream->chip->shutdown_rwsem); + if (!subs->stream->chip->shutdown) { + stop_endpoints(subs, 0, 1, 1); + deactivate_endpoints(subs); + } + up_read(&subs->stream->chip->shutdown_rwsem); return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -537,14 +549,48 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; + struct usb_host_interface *alts; + struct usb_interface *iface; + int ret; if (! subs->cur_audiofmt) { snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); return -ENXIO; } - if (snd_BUG_ON(!subs->data_endpoint)) - return -EIO; + down_read(&subs->stream->chip->shutdown_rwsem); + if (subs->stream->chip->shutdown) { + ret = -ENODEV; + goto unlock; + } + if (snd_BUG_ON(!subs->data_endpoint)) { + ret = -EIO; + goto unlock; + } + + snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint); + snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); + + ret = set_format(subs, subs->cur_audiofmt); + if (ret < 0) + goto unlock; + + iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); + alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->stream->chip, + subs->cur_audiofmt->iface, + alts, + subs->cur_audiofmt, + subs->cur_rate); + if (ret < 0) + goto unlock; + + if (subs->need_setup_ep) { + ret = configure_endpoint(subs); + if (ret < 0) + goto unlock; + subs->need_setup_ep = false; + } /* some unit conversions in runtime */ subs->data_endpoint->maxframesize = @@ -562,9 +608,11 @@ 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, 1); + ret = start_endpoints(subs, 1); - return 0; + unlock: + up_read(&subs->stream->chip->shutdown_rwsem); + return ret; } static struct snd_pcm_hardware snd_usb_hardware = @@ -617,7 +665,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs, return 0; } /* check whether the period time is >= the data packet interval */ - if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) { + if (subs->speed != USB_SPEED_FULL) { ptime = 125 * (1 << fp->datainterval); if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); @@ -895,7 +943,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre return err; param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) + if (subs->speed == USB_SPEED_FULL) /* full speed devices have fixed data packet interval */ ptmin = 1000; if (ptmin == 1000) |