ALSA: usb-audio: Move configuration to prepare.
authorDylan Reid <dgreid@chromium.org>
Tue, 18 Sep 2012 16:49:48 +0000 (09:49 -0700)
committerTakashi Iwai <tiwai@suse.de>
Wed, 19 Sep 2012 06:08:11 +0000 (08:08 +0200)
Move interface and endpoint configuration from hw_params to prepare
callback.  During system suspend/resume when the USB device power isn't
cycled the interface and endpoint configuration need to be set before
audio playback can continue.  Resume involves another call to prepare
but not to hw_params, moving it here allows a playing stream to continue
after resume.

Signed-off-by: Dylan Reid <dgreid@chromium.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/card.h
sound/usb/pcm.c

index 23b6f23bd36a0532557fe0114de513acf7960a8c..6cc883c3567dff7f7a2b24640f9da9c8045608d2 100644 (file)
@@ -107,6 +107,8 @@ struct snd_usb_substream {
        int interface;  /* current interface */
        int endpoint;   /* assigned endpoint */
        struct audioformat *cur_audiofmt;       /* current audioformat pointer (for hw_params callback) */
+       snd_pcm_format_t pcm_format;    /* current audio format (for hw_params callback) */
+       unsigned int channels;          /* current number of channels (for hw_params callback) */
        unsigned int cur_rate;          /* current rate (for hw_params callback) */
        unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
        unsigned int altset_idx;     /* USB data format: index of alternate setting */
index 62ab4fd5880af08c4e6e5fc77239c97d8ed0f9a9..ae783d40f55aa31e0995999f330052367c646f0a 100644 (file)
@@ -82,8 +82,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 +91,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;
@@ -435,6 +435,42 @@ add_sync_ep:
        return 0;
 }
 
+/*
+ * configure endpoint params
+ *
+ * called  during initial setup and upon resume
+ */
+static int configure_endpoint(struct snd_usb_substream *subs)
+{
+       int ret;
+
+       mutex_lock(&subs->stream->chip->shutdown_mutex);
+       /* 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)
+               goto unlock;
+
+       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);
+
+unlock:
+       mutex_unlock(&subs->stream->chip->shutdown_mutex);
+       return ret;
+}
+
 /*
  * hw_params callback
  *
@@ -450,75 +486,32 @@ 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)
                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) {
-               subs->period_bytes = params_period_bytes(hw_params);
+       subs->interface = fmt->iface;
+       subs->altset_idx = fmt->altset_idx;
 
-               mutex_lock(&subs->stream->chip->shutdown_mutex);
-               /* format changed */
-               stop_endpoints(subs, 0, 0, 0);
-               ret = snd_usb_endpoint_set_params(subs->data_endpoint,
-                                                 format,
-                                                 channels,
-                                                 subs->period_bytes,
-                                                 rate,
-                                                 fmt,
-                                                 subs->sync_endpoint);
-               if (ret < 0)
-                       goto unlock;
-
-               if (subs->sync_endpoint)
-                       ret = snd_usb_endpoint_set_params(subs->data_endpoint,
-                                                         format,
-                                                         channels,
-                                                         subs->period_bytes,
-                                                         rate,
-                                                         fmt,
-                                                         NULL);
-unlock:
-               mutex_unlock(&subs->stream->chip->shutdown_mutex);
-       }
-
-       if (ret == 0) {
-               subs->interface = fmt->iface;
-               subs->altset_idx = fmt->altset_idx;
-       }
-
-       return ret;
+       return 0;
 }
 
 /*
@@ -549,6 +542,9 @@ 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");
@@ -558,6 +554,24 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
        if (snd_BUG_ON(!subs->data_endpoint))
                return -EIO;
 
+       ret = set_format(subs, subs->cur_audiofmt);
+       if (ret < 0)
+               return ret;
+
+       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)
+               return ret;
+
+       ret = configure_endpoint(subs);
+       if (ret < 0)
+               return ret;
+
        /* some unit conversions in runtime */
        subs->data_endpoint->maxframesize =
                bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);