]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/usb/usbaudio.c
[ALSA] Introduce slots option to snd module
[linux-2.6-omap-h63xx.git] / sound / usb / usbaudio.c
index 67202b9eeb77c09bf4c3d10fb372c5582e92394c..967b823eace083bbad39e4fae5378243b61dbbe7 100644 (file)
@@ -123,7 +123,6 @@ struct audioformat {
        unsigned int rate_min, rate_max;        /* min/max rates */
        unsigned int nr_rates;          /* number of rate table entries */
        unsigned int *rate_table;       /* rate table */
-       unsigned int needs_knot;        /* any unusual rates? */
 };
 
 struct snd_usb_substream;
@@ -186,6 +185,7 @@ struct snd_usb_substream {
        u64 formats;                    /* format bitmasks (all or'ed) */
        unsigned int num_formats;               /* number of supported audio formats (list) */
        struct list_head fmt_list;      /* format list */
+       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
        spinlock_t lock;
 
        struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
@@ -253,7 +253,7 @@ static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
                                    struct urb *urb)
 {
        unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
        urb->iso_frame_desc[0].length = 3;
@@ -275,7 +275,7 @@ static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
                                       struct urb *urb)
 {
        unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
        urb->iso_frame_desc[0].length = 4;
@@ -313,7 +313,7 @@ static int prepare_capture_urb(struct snd_usb_substream *subs,
                               struct urb *urb)
 {
        int i, offs;
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        offs = 0;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
@@ -324,16 +324,6 @@ static int prepare_capture_urb(struct snd_usb_substream *subs,
        }
        urb->transfer_buffer_length = offs;
        urb->number_of_packets = ctx->packets;
-#if 0 // for check
-       if (! urb->bandwidth) {
-               int bustime;
-               bustime = usb_check_bandwidth(urb->dev, urb);
-               if (bustime < 0)
-                       return bustime;
-               printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
-               usb_claim_bandwidth(urb->dev, urb, bustime, 1);
-       }
-#endif // for check
        return 0;
 }
 
@@ -391,6 +381,16 @@ static int retire_capture_urb(struct snd_usb_substream *subs,
        return 0;
 }
 
+/*
+ * Process after capture complete when paused.  Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+                                    struct snd_pcm_runtime *runtime,
+                                    struct urb *urb)
+{
+       return 0;
+}
+
 
 /*
  * prepare urb for full speed playback sync pipe
@@ -402,7 +402,7 @@ static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
                                     struct snd_pcm_runtime *runtime,
                                     struct urb *urb)
 {
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
        urb->iso_frame_desc[0].length = 3;
@@ -420,7 +420,7 @@ static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
                                        struct snd_pcm_runtime *runtime,
                                        struct urb *urb)
 {
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
        urb->iso_frame_desc[0].length = 4;
@@ -493,13 +493,13 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
 }
 
 /*
- * Prepare urb for streaming before playback starts.
+ * Prepare urb for streaming before playback starts or when paused.
  *
- * We don't yet have data, so we send a frame of silence.
+ * We don't have any data, so we send a frame of silence.
  */
-static int prepare_startup_playback_urb(struct snd_usb_substream *subs,
-                                       struct snd_pcm_runtime *runtime,
-                                       struct urb *urb)
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
 {
        unsigned int i, offs, counts;
        struct snd_urb_ctx *ctx = urb->context;
@@ -537,7 +537,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
        unsigned int counts;
        unsigned long flags;
        int period_elapsed = 0;
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
 
        stride = runtime->frame_bits >> 3;
 
@@ -622,7 +622,7 @@ static int retire_playback_urb(struct snd_usb_substream *subs,
  */
 static struct snd_urb_ops audio_urb_ops[2] = {
        {
-               .prepare =      prepare_startup_playback_urb,
+               .prepare =      prepare_nodata_playback_urb,
                .retire =       retire_playback_urb,
                .prepare_sync = prepare_playback_sync_urb,
                .retire_sync =  retire_playback_sync_urb,
@@ -637,7 +637,7 @@ static struct snd_urb_ops audio_urb_ops[2] = {
 
 static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
        {
-               .prepare =      prepare_startup_playback_urb,
+               .prepare =      prepare_nodata_playback_urb,
                .retire =       retire_playback_urb,
                .prepare_sync = prepare_playback_sync_urb_hs,
                .retire_sync =  retire_playback_sync_urb_hs,
@@ -655,7 +655,7 @@ static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
  */
 static void snd_complete_urb(struct urb *urb)
 {
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
        struct snd_usb_substream *subs = ctx->subs;
        struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
        int err = 0;
@@ -678,7 +678,7 @@ static void snd_complete_urb(struct urb *urb)
  */
 static void snd_complete_sync_urb(struct urb *urb)
 {
-       struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+       struct snd_urb_ctx *ctx = urb->context;
        struct snd_usb_substream *subs = ctx->subs;
        struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
        int err = 0;
@@ -925,10 +925,14 @@ static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                subs->ops.prepare = prepare_playback_urb;
                return 0;
        case SNDRV_PCM_TRIGGER_STOP:
                return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.prepare = prepare_nodata_playback_urb;
+               return 0;
        default:
                return -EINVAL;
        }
@@ -944,9 +948,16 @@ static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+               subs->ops.retire = retire_capture_urb;
                return start_urbs(subs, substream->runtime);
        case SNDRV_PCM_TRIGGER_STOP:
                return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.retire = retire_paused_capture_urb;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->ops.retire = retire_capture_urb;
+               return 0;
        default:
                return -EINVAL;
        }
@@ -1297,7 +1308,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 
        /* close the old interface */
        if (subs->interface >= 0 && subs->interface != fmt->iface) {
-               usb_set_interface(subs->dev, subs->interface, 0);
+               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
+                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
+                               dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EIO;
+               }
                subs->interface = -1;
                subs->format = 0;
        }
@@ -1408,7 +1423,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 static int snd_usb_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *hw_params)
 {
-       struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+       struct snd_usb_substream *subs = substream->runtime->private_data;
        struct audioformat *fmt;
        unsigned int channels, rate, format;
        int ret, changed;
@@ -1464,7 +1479,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
  */
 static int snd_usb_hw_free(struct snd_pcm_substream *substream)
 {
-       struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+       struct snd_usb_substream *subs = substream->runtime->private_data;
 
        subs->cur_audiofmt = NULL;
        subs->cur_rate = 0;
@@ -1505,33 +1520,20 @@ 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) {
-               subs->ops.prepare = prepare_startup_playback_urb;
+               subs->ops.prepare = prepare_nodata_playback_urb;
                return start_urbs(subs, runtime);
        } else
                return 0;
 }
 
-static struct snd_pcm_hardware snd_usb_playback =
-{
-       .info =                 SNDRV_PCM_INFO_MMAP |
-                               SNDRV_PCM_INFO_MMAP_VALID |
-                               SNDRV_PCM_INFO_BATCH |
-                               SNDRV_PCM_INFO_INTERLEAVED |
-                               SNDRV_PCM_INFO_BLOCK_TRANSFER,
-       .buffer_bytes_max =     1024 * 1024,
-       .period_bytes_min =     64,
-       .period_bytes_max =     512 * 1024,
-       .periods_min =          2,
-       .periods_max =          1024,
-};
-
-static struct snd_pcm_hardware snd_usb_capture =
+static struct snd_pcm_hardware snd_usb_hardware =
 {
        .info =                 SNDRV_PCM_INFO_MMAP |
                                SNDRV_PCM_INFO_MMAP_VALID |
                                SNDRV_PCM_INFO_BATCH |
                                SNDRV_PCM_INFO_INTERLEAVED |
-                               SNDRV_PCM_INFO_BLOCK_TRANSFER,
+                               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                               SNDRV_PCM_INFO_PAUSE,
        .buffer_bytes_max =     1024 * 1024,
        .period_bytes_min =     64,
        .period_bytes_max =     512 * 1024,
@@ -1762,7 +1764,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
                channels[f->format] |= (1 << f->channels);
                rates[f->format] |= f->rates;
                /* needs knot? */
-               if (f->needs_knot)
+               if (f->rates & SNDRV_PCM_RATE_KNOT)
                        goto __out;
        }
        /* check whether channels and rates match for all formats */
@@ -1810,28 +1812,33 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
 static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
                                  struct snd_usb_substream *subs)
 {
-       struct list_head *p;
-       struct snd_pcm_hw_constraint_list constraints_rates;
+       struct audioformat *fp;
+       int count = 0, needs_knot = 0;
        int err;
 
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-
-               if (!fp->needs_knot)
-                       continue;
-
-               constraints_rates.count = fp->nr_rates;
-               constraints_rates.list = fp->rate_table;
-               constraints_rates.mask = 0;
-
-               err = snd_pcm_hw_constraint_list(runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_rates);
-
-               if (err < 0)
-                       return err;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+                       return 0;
+               count += fp->nr_rates;
+               if (fp->rates & SNDRV_PCM_RATE_KNOT)
+                       needs_knot = 1;
        }
+       if (!needs_knot)
+               return 0;
+
+       subs->rate_list.count = count;
+       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+       subs->rate_list.mask = 0;
+       count = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               int i;
+               for (i = 0; i < fp->nr_rates; i++)
+                       subs->rate_list.list[count++] = fp->rate_table[i];
+       }
+       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                        &subs->rate_list);
+       if (err < 0)
+               return err;
 
        return 0;
 }
@@ -1874,6 +1881,9 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
        }
 
        /* set the period time minimum 1ms */
+       /* FIXME: high-speed mode allows 125us minimum period, but many parts
+        * in the current code assume the 1ms period.
+        */
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
                                     1000 * MIN_PACKS_URB,
                                     /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX);
@@ -1904,8 +1914,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
        return 0;
 }
 
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction,
-                           struct snd_pcm_hardware *hw)
+static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
 {
        struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1913,7 +1922,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction,
 
        subs->interface = -1;
        subs->format = 0;
-       runtime->hw = *hw;
+       runtime->hw = snd_usb_hardware;
        runtime->private_data = subs;
        subs->pcm_substream = substream;
        return setup_hw_info(runtime, subs);
@@ -1934,7 +1943,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 
 static int snd_usb_playback_open(struct snd_pcm_substream *substream)
 {
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback);
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
 }
 
 static int snd_usb_playback_close(struct snd_pcm_substream *substream)
@@ -1944,7 +1953,7 @@ static int snd_usb_playback_close(struct snd_pcm_substream *substream)
 
 static int snd_usb_capture_open(struct snd_pcm_substream *substream)
 {
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture);
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
 }
 
 static int snd_usb_capture_close(struct snd_pcm_substream *substream)
@@ -2231,6 +2240,7 @@ static void free_substream(struct snd_usb_substream *subs)
                kfree(fp->rate_table);
                kfree(fp);
        }
+       kfree(subs->rate_list.list);
 }
 
 
@@ -2343,7 +2353,9 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
                        return 1;
                break;
        case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-               return 1;
+               if (device_setup[chip->index] == 0x00 ||
+                   fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+                       return 1;
        }
        return 0;
 }
@@ -2444,7 +2456,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
                                    unsigned char *fmt, int offset)
 {
        int nr_rates = fmt[offset];
-       int found;
+
        if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
                snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
                                   chip->dev->devnum, fp->iface, fp->altsetting);
@@ -2455,40 +2467,36 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
                /*
                 * build the rate table and bitmap flags
                 */
-               int r, idx, c;
-               /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
-               static unsigned int conv_rates[] = {
-                       5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
-                       64000, 88200, 96000, 176400, 192000
-               };
+               int r, idx;
+               unsigned int nonzero_rates = 0;
+
                fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
                if (fp->rate_table == NULL) {
                        snd_printk(KERN_ERR "cannot malloc\n");
                        return -1;
                }
 
-               fp->needs_knot = 0;
                fp->nr_rates = nr_rates;
                fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
                for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
-                       unsigned int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
+                       unsigned int rate = combine_triple(&fmt[idx]);
+                       /* C-Media CM6501 mislabels its 96 kHz altsetting */
+                       if (rate == 48000 && nr_rates == 1 &&
+                           chip->usb_id == USB_ID(0x0d8c, 0x0201) &&
+                           fp->altsetting == 5 && fp->maxpacksize == 392)
+                               rate = 96000;
+                       fp->rate_table[r] = rate;
+                       nonzero_rates |= rate;
                        if (rate < fp->rate_min)
                                fp->rate_min = rate;
                        else if (rate > fp->rate_max)
                                fp->rate_max = rate;
-                       found = 0;
-                       for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
-                               if (rate == conv_rates[c]) {
-                                       found = 1;
-                                       fp->rates |= (1 << c);
-                                       break;
-                               }
-                       }
-                       if (!found)
-                               fp->needs_knot = 1;
+                       fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+               }
+               if (!nonzero_rates) {
+                       hwc_debug("All rates were zero. Skipping format!\n");
+                       return -1;
                }
-               if (fp->needs_knot)
-                       fp->rates |= SNDRV_PCM_RATE_KNOT;
        } else {
                /* continuous rates */
                fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
@@ -2511,7 +2519,18 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
                 *        but we give normal PCM format to get the existing
                 *        apps working...
                 */
-               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+               switch (chip->usb_id) {
+
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+                       if (device_setup[chip->index] == 0x00 && 
+                           fp->altsetting == 6)
+                               pcm_format = SNDRV_PCM_FORMAT_S16_BE;
+                       else
+                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+                       break;
+               default:
+                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+               }
        } else {
                pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
                if (pcm_format < 0)
@@ -2825,6 +2844,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
                        /* skip non-supported classes */
                        continue;
                }
+               if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
+                       snd_printk(KERN_ERR "low speed audio streaming not supported\n");
+                       continue;
+               }
                if (! parse_audio_endpoints(chip, j)) {
                        usb_set_interface(dev, j, 0); /* reset the current interface */
                        usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
@@ -2844,7 +2867,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        struct audioformat *fp;
        struct usb_host_interface *alts;
        int stream, err;
-       int *rate_table = NULL;
+       unsigned *rate_table = NULL;
 
        fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
        if (! fp) {
@@ -3051,6 +3074,58 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip,
        return 0;
 }
 
+/*
+ * Create a stream for an Edirol UA-101 interface.
+ * Copy, paste and modify from Edirol UA-1000
+ */
+static int create_ua101_quirk(struct snd_usb_audio *chip,
+                              struct usb_interface *iface,
+                              const struct snd_usb_audio_quirk *quirk)
+{
+       static const struct audioformat ua101_format = {
+               .format = SNDRV_PCM_FORMAT_S32_LE,
+               .fmt_type = USB_FORMAT_TYPE_I,
+               .altsetting = 1,
+               .altset_idx = 1,
+               .attributes = 0,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+       };
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct audioformat *fp;
+       int stream, err;
+
+       if (iface->num_altsetting != 2)
+               return -ENXIO;
+       alts = &iface->altsetting[1];
+       altsd = get_iface_desc(alts);
+       if (alts->extralen != 18 || alts->extra[1] != USB_DT_CS_INTERFACE ||
+           altsd->bNumEndpoints != 1)
+               return -ENXIO;
+
+       fp = kmemdup(&ua101_format, sizeof(*fp), GFP_KERNEL);
+       if (!fp)
+               return -ENOMEM;
+
+       fp->channels = alts->extra[11];
+       fp->iface = altsd->bInterfaceNumber;
+       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+       fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]);
+
+       stream = (fp->endpoint & USB_DIR_IN)
+               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       err = add_audio_endpoint(chip, stream, fp);
+       if (err < 0) {
+               kfree(fp);
+               return err;
+       }
+       /* FIXME: playback must be synchronized to capture */
+       usb_set_interface(chip->dev, fp->iface, 0);
+       return 0;
+}
+
 static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                                struct usb_interface *iface,
                                const struct snd_usb_audio_quirk *quirk);
@@ -3180,6 +3255,11 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
 static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
                                         int iface, int altno)
 {
+       /* Reset ALL ifaces to 0 altsetting.
+        * Call it for every possible altsetting of every interface.
+        */
+       usb_set_interface(chip->dev, iface, 0);
+
        if (device_setup[chip->index] & AUDIOPHILE_SET) {
                if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
                    && altno != 6)
@@ -3232,6 +3312,7 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
                [QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
                [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+               [QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
        };
 
        if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -3280,6 +3361,7 @@ static void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
 
 static int snd_usb_audio_free(struct snd_usb_audio *chip)
 {
+       usb_chip[chip->index] = NULL;
        kfree(chip);
        return 0;
 }
@@ -3308,7 +3390,8 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 
        *rchip = NULL;
 
-       if (snd_usb_get_speed(dev) != USB_SPEED_FULL &&
+       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
+           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
            snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
                snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
                return -ENXIO;
@@ -3382,7 +3465,9 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
                usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
 
        strlcat(card->longname,
-               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" : ", high speed",
+               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
+               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
+               ", high speed",
                sizeof(card->longname));
 
        snd_usb_audio_create_proc(chip);
@@ -3541,7 +3626,6 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
                list_for_each(p, &chip->mixer_list) {
                        snd_usb_mixer_disconnect(p);
                }
-               usb_chip[chip->index] = NULL;
                mutex_unlock(&register_mutex);
                snd_card_free_when_closed(card);
        } else {
@@ -3577,8 +3661,7 @@ static int __init snd_usb_audio_init(void)
                printk(KERN_WARNING "invalid nrpacks value.\n");
                return -EINVAL;
        }
-       usb_register(&usb_audio_driver);
-       return 0;
+       return usb_register(&usb_audio_driver);
 }