[PATCH] timer initialization cleanup: DEFINE_TIMER
[pandora-kernel.git] / sound / usb / usbaudio.c
index a756950..bfbec58 100644 (file)
 #include <sound/driver.h>
 #include <linux/bitops.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/usb.h>
+#include <linux/vmalloc.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/info.h>
@@ -79,7 +81,7 @@ module_param_array(vid, int, NULL, 0444);
 MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
 module_param_array(pid, int, NULL, 0444);
 MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
-module_param(nrpacks, int, 0444);
+module_param(nrpacks, int, 0644);
 MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
 module_param(async_unlink, bool, 0444);
 MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
@@ -97,7 +99,7 @@ MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
 
 #define MAX_PACKS      10
 #define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS       5       /* max. 20ms long packets */
+#define MAX_URBS       8
 #define SYNC_URBS      4       /* always four urbs for sync */
 #define MIN_PACKS_URB  1       /* minimum 1 packet per urb */
 
@@ -126,11 +128,10 @@ struct audioformat {
 
 struct snd_urb_ctx {
        struct urb *urb;
+       unsigned int buffer_size;       /* size of data buffer, if data URB */
        snd_usb_substream_t *subs;
        int index;      /* index for urb array */
        int packets;    /* number of packets per urb */
-       int transfer;   /* transferred size */
-       char *buf;      /* buffer for capture */
 };
 
 struct snd_urb_ops {
@@ -153,6 +154,7 @@ struct snd_usb_substream {
        unsigned int format;     /* USB data format */
        unsigned int datapipe;   /* the data i/o pipe */
        unsigned int syncpipe;   /* 1 - async out or adaptive in */
+       unsigned int datainterval;      /* log_2 of data packet interval */
        unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
        unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
        unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
@@ -164,12 +166,11 @@ struct snd_usb_substream {
        unsigned int curframesize;      /* current packet size in frames (for capture) */
        unsigned int fill_max: 1;       /* fill max packet size always */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
+       unsigned int packs_per_ms;      /* packets per millisecond (for playback) */
 
        unsigned int running: 1;        /* running status */
 
-       unsigned int hwptr;                     /* free frame position in the buffer (only for playback) */
        unsigned int hwptr_done;                        /* processed frame position in the buffer */
-       unsigned int transfer_sched;            /* scheduled frames since last period (for playback) */
        unsigned int transfer_done;             /* processed frames since last period update */
        unsigned long active_mask;      /* bitmask of active urbs */
        unsigned long unlink_mask;      /* bitmask of unlinked urbs */
@@ -177,13 +178,14 @@ struct snd_usb_substream {
        unsigned int nurbs;                     /* # urbs */
        snd_urb_ctx_t dataurb[MAX_URBS];        /* data urb table */
        snd_urb_ctx_t syncurb[SYNC_URBS];       /* sync urb table */
-       char syncbuf[SYNC_URBS * 4];    /* sync buffer; it's so small - let's get static */
-       char *tmpbuf;                   /* temporary buffer for playback */
+       char *syncbuf;                          /* sync buffer for all sync URBs */
+       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
 
        u64 formats;                    /* format bitmasks (all or'ed) */
        unsigned int num_formats;               /* number of supported audio formats (list) */
        struct list_head fmt_list;      /* format list */
        spinlock_t lock;
+       struct tasklet_struct start_period_elapsed;     /* for start trigger */
 
        struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
 };
@@ -212,7 +214,7 @@ static snd_usb_audio_t *usb_chip[SNDRV_CARDS];
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
  */
-inline static unsigned get_usb_full_speed_rate(unsigned int rate)
+static inline unsigned get_usb_full_speed_rate(unsigned int rate)
 {
        return ((rate << 13) + 62) / 125;
 }
@@ -221,19 +223,19 @@ inline static unsigned get_usb_full_speed_rate(unsigned int rate)
  * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
  * this will overflow at approx 4 MHz
  */
-inline static unsigned get_usb_high_speed_rate(unsigned int rate)
+static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 {
        return ((rate << 10) + 62) / 125;
 }
 
 /* convert our full speed USB rate into sampling rate in Hz */
-inline static unsigned get_full_speed_hz(unsigned int usb_rate)
+static inline unsigned get_full_speed_hz(unsigned int usb_rate)
 {
        return (usb_rate * 125 + (1 << 12)) >> 13;
 }
 
 /* convert our high speed USB rate into sampling rate in Hz */
-inline static unsigned get_high_speed_hz(unsigned int usb_rate)
+static inline unsigned get_high_speed_hz(unsigned int usb_rate)
 {
        return (usb_rate * 125 + (1 << 9)) >> 10;
 }
@@ -310,27 +312,17 @@ static int prepare_capture_urb(snd_usb_substream_t *subs,
                               struct urb *urb)
 {
        int i, offs;
-       unsigned long flags;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
        offs = 0;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->number_of_packets = 0;
-       spin_lock_irqsave(&subs->lock, flags);
        for (i = 0; i < ctx->packets; i++) {
                urb->iso_frame_desc[i].offset = offs;
                urb->iso_frame_desc[i].length = subs->curpacksize;
                offs += subs->curpacksize;
-               urb->number_of_packets++;
-               subs->transfer_sched += subs->curframesize;
-               if (subs->transfer_sched >= runtime->period_size) {
-                       subs->transfer_sched -= runtime->period_size;
-                       break;
-               }
        }
-       spin_unlock_irqrestore(&subs->lock, flags);
-       urb->transfer_buffer = ctx->buf;
        urb->transfer_buffer_length = offs;
+       urb->number_of_packets = ctx->packets;
 #if 0 // for check
        if (! urb->bandwidth) {
                int bustime;
@@ -358,6 +350,7 @@ static int retire_capture_urb(snd_usb_substream_t *subs,
        unsigned char *cp;
        int i;
        unsigned int stride, len, oldptr;
+       int period_elapsed = 0;
 
        stride = runtime->frame_bits >> 3;
 
@@ -377,6 +370,10 @@ static int retire_capture_urb(snd_usb_substream_t *subs,
                if (subs->hwptr_done >= runtime->buffer_size)
                        subs->hwptr_done -= runtime->buffer_size;
                subs->transfer_done += len;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+               }
                spin_unlock_irqrestore(&subs->lock, flags);
                /* copy a data chunk */
                if (oldptr + len > runtime->buffer_size) {
@@ -387,15 +384,9 @@ static int retire_capture_urb(snd_usb_substream_t *subs,
                } else {
                        memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
                }
-               /* update the pointer, call callback if necessary */
-               spin_lock_irqsave(&subs->lock, flags);
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-                       snd_pcm_period_elapsed(subs->pcm_substream);
-               } else
-                       spin_unlock_irqrestore(&subs->lock, flags);
        }
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
        return 0;
 }
 
@@ -491,12 +482,10 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs,
 /*
  * prepare urb for playback data pipe
  *
- * we copy the data directly from the pcm buffer.
- * the current position to be copied is held in hwptr field.
- * since a urb can handle only a single linear buffer, if the total
- * transferred area overflows the buffer boundary, we cannot send
- * it directly from the buffer.  thus the data is once copied to
- * a temporary buffer and urb points to that.
+ * Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
  */
 static int prepare_playback_urb(snd_usb_substream_t *subs,
                                snd_pcm_runtime_t *runtime,
@@ -505,6 +494,7 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
        int i, stride, offs;
        unsigned int counts;
        unsigned long flags;
+       int period_elapsed = 0;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
        stride = runtime->frame_bits >> 3;
@@ -518,7 +508,8 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
                if (subs->fill_max)
                        counts = subs->maxframesize; /* fixed */
                else {
-                       subs->phase = (subs->phase & 0xffff) + subs->freqm;
+                       subs->phase = (subs->phase & 0xffff)
+                               + (subs->freqm << subs->datainterval);
                        counts = subs->phase >> 16;
                        if (counts > subs->maxframesize)
                                counts = subs->maxframesize;
@@ -528,80 +519,85 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
                urb->iso_frame_desc[i].length = counts * stride;
                offs += counts;
                urb->number_of_packets++;
-               subs->transfer_sched += counts;
-               if (subs->transfer_sched >= runtime->period_size) {
-                       subs->transfer_sched -= runtime->period_size;
+               subs->transfer_done += counts;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
                        if (subs->fmt_type == USB_FORMAT_TYPE_II) {
-                               if (subs->transfer_sched > 0) {
-                                       /* FIXME: fill-max mode is not supported yet */
-                                       offs -= subs->transfer_sched;
-                                       counts -= subs->transfer_sched;
-                                       urb->iso_frame_desc[i].length = counts * stride;
-                                       subs->transfer_sched = 0;
+                               if (subs->transfer_done > 0) {
+                                       /* FIXME: fill-max mode is not
+                                        * supported yet */
+                                       offs -= subs->transfer_done;
+                                       counts -= subs->transfer_done;
+                                       urb->iso_frame_desc[i].length =
+                                               counts * stride;
+                                       subs->transfer_done = 0;
                                }
                                i++;
                                if (i < ctx->packets) {
                                        /* add a transfer delimiter */
-                                       urb->iso_frame_desc[i].offset = offs * stride;
+                                       urb->iso_frame_desc[i].offset =
+                                               offs * stride;
                                        urb->iso_frame_desc[i].length = 0;
                                        urb->number_of_packets++;
                                }
+                               break;
                        }
-                       break;
                }
+               /* finish at the frame boundary at/after the period boundary */
+               if (period_elapsed &&
+                   (i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1)
+                       break;
        }
-       if (subs->hwptr + offs > runtime->buffer_size) {
-               /* err, the transferred area goes over buffer boundary.
-                * copy the data to the temp buffer.
-                */
-               int len;
-               len = runtime->buffer_size - subs->hwptr;
-               urb->transfer_buffer = subs->tmpbuf;
-               memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride);
-               memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride);
-               subs->hwptr += offs;
-               subs->hwptr -= runtime->buffer_size;
+       if (subs->hwptr_done + offs > runtime->buffer_size) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int len = runtime->buffer_size - subs->hwptr_done;
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done * stride,
+                      len * stride);
+               memcpy(urb->transfer_buffer + len * stride,
+                      runtime->dma_area,
+                      (offs - len) * stride);
        } else {
-               /* set the buffer pointer */
-               urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
-               subs->hwptr += offs;
-               if (subs->hwptr == runtime->buffer_size)
-                       subs->hwptr = 0;
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done * stride,
+                      offs * stride);
        }
+       subs->hwptr_done += offs;
+       if (subs->hwptr_done >= runtime->buffer_size)
+               subs->hwptr_done -= runtime->buffer_size;
        spin_unlock_irqrestore(&subs->lock, flags);
        urb->transfer_buffer_length = offs * stride;
-       ctx->transfer = offs;
-
+       if (period_elapsed) {
+               if (likely(subs->running))
+                       snd_pcm_period_elapsed(subs->pcm_substream);
+               else
+                       tasklet_hi_schedule(&subs->start_period_elapsed);
+       }
        return 0;
 }
 
 /*
  * process after playback data complete
- *
- * update the current position and call callback if a period is processed.
+ * - nothing to do
  */
 static int retire_playback_urb(snd_usb_substream_t *subs,
                               snd_pcm_runtime_t *runtime,
                               struct urb *urb)
 {
-       unsigned long flags;
-       snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
-
-       spin_lock_irqsave(&subs->lock, flags);
-       subs->transfer_done += ctx->transfer;
-       subs->hwptr_done += ctx->transfer;
-       ctx->transfer = 0;
-       if (subs->hwptr_done >= runtime->buffer_size)
-               subs->hwptr_done -= runtime->buffer_size;
-       if (subs->transfer_done >= runtime->period_size) {
-               subs->transfer_done -= runtime->period_size;
-               spin_unlock_irqrestore(&subs->lock, flags);
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       } else
-               spin_unlock_irqrestore(&subs->lock, flags);
        return 0;
 }
 
+/*
+ * Delay the snd_pcm_period_elapsed() call until after the start trigger
+ * callback so that we're not longer in the substream's lock.
+ */
+static void start_period_elapsed(unsigned long data)
+{
+       snd_usb_substream_t *subs = (snd_usb_substream_t *)data;
+       snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
 
 /*
  */
@@ -681,6 +677,42 @@ static void snd_complete_sync_urb(struct urb *urb, struct pt_regs *regs)
 }
 
 
+/* get the physical page pointer at the given offset */
+static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs,
+                                            unsigned long offset)
+{
+       void *pageptr = subs->runtime->dma_area + offset;
+       return vmalloc_to_page(pageptr);
+}
+
+/* allocate virtual buffer; may be called more than once */
+static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)
+{
+       snd_pcm_runtime_t *runtime = subs->runtime;
+       if (runtime->dma_area) {
+               if (runtime->dma_bytes >= size)
+                       return 0; /* already large enough */
+               vfree_nocheck(runtime->dma_area);
+       }
+       runtime->dma_area = vmalloc_nocheck(size);
+       if (! runtime->dma_area)
+               return -ENOMEM;
+       runtime->dma_bytes = size;
+       return 0;
+}
+
+/* free virtual buffer; may be called more than once */
+static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs)
+{
+       snd_pcm_runtime_t *runtime = subs->runtime;
+       if (runtime->dma_area) {
+               vfree_nocheck(runtime->dma_area);
+               runtime->dma_area = NULL;
+       }
+       return 0;
+}
+
+
 /*
  * unlink active urbs.
  */
@@ -703,10 +735,9 @@ static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep)
                if (test_bit(i, &subs->active_mask)) {
                        if (! test_and_set_bit(i, &subs->unlink_mask)) {
                                struct urb *u = subs->dataurb[i].urb;
-                               if (async) {
-                                       u->transfer_flags |= URB_ASYNC_UNLINK;
+                               if (async)
                                        usb_unlink_urb(u);
-                               else
+                               else
                                        usb_kill_urb(u);
                        }
                }
@@ -716,10 +747,9 @@ static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep)
                        if (test_bit(i+16, &subs->active_mask)) {
                                if (! test_and_set_bit(i+16, &subs->unlink_mask)) {
                                        struct urb *u = subs->syncurb[i].urb;
-                                       if (async) {
-                                               u->transfer_flags |= URB_ASYNC_UNLINK;
+                                       if (async)
                                                usb_unlink_urb(u);
-                                       else
+                                       else
                                                usb_kill_urb(u);
                                }
                        }
@@ -790,7 +820,7 @@ static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
  */
 static int wait_clear_urbs(snd_usb_substream_t *subs)
 {
-       int timeout = HZ;
+       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
        unsigned int i;
        int alive;
 
@@ -810,7 +840,7 @@ static int wait_clear_urbs(snd_usb_substream_t *subs)
                        break;
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule_timeout(1);
-       } while (--timeout > 0);
+       } while (time_before(jiffies, end_time));
        if (alive)
                snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
        return 0;
@@ -822,8 +852,14 @@ static int wait_clear_urbs(snd_usb_substream_t *subs)
  */
 static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream)
 {
-       snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;
-       return subs->hwptr_done;
+       snd_usb_substream_t *subs;
+       snd_pcm_uframes_t hwptr_done;
+       
+       subs = (snd_usb_substream_t *)substream->runtime->private_data;
+       spin_lock(&subs->lock);
+       hwptr_done = subs->hwptr_done;
+       spin_unlock(&subs->lock);
+       return hwptr_done;
 }
 
 
@@ -856,11 +892,13 @@ static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 static void release_urb_ctx(snd_urb_ctx_t *u)
 {
        if (u->urb) {
+               if (u->buffer_size)
+                       usb_buffer_free(u->subs->dev, u->buffer_size,
+                                       u->urb->transfer_buffer,
+                                       u->urb->transfer_dma);
                usb_free_urb(u->urb);
                u->urb = NULL;
        }
-       kfree(u->buf);
-       u->buf = NULL;
 }
 
 /*
@@ -878,8 +916,9 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force)
                release_urb_ctx(&subs->dataurb[i]);
        for (i = 0; i < SYNC_URBS; i++)
                release_urb_ctx(&subs->syncurb[i]);
-       kfree(subs->tmpbuf);
-       subs->tmpbuf = NULL;
+       usb_buffer_free(subs->dev, SYNC_URBS * 4,
+                       subs->syncbuf, subs->sync_dma);
+       subs->syncbuf = NULL;
        subs->nurbs = 0;
 }
 
@@ -891,7 +930,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
 {
        unsigned int maxsize, n, i;
        int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       unsigned int npacks[MAX_URBS], urb_packs, total_packs;
+       unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms;
 
        /* calculate the frequency in 16.16 format */
        if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
@@ -899,40 +938,59 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
        else
                subs->freqn = get_usb_high_speed_rate(rate);
        subs->freqm = subs->freqn;
-       subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */
-       subs->phase = 0;
-
-       /* calculate the max. size of packet */
-       maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) >> 16;
-       if (subs->maxpacksize && maxsize > subs->maxpacksize) {
-               //snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n",
-               //         maxsize, subs->maxpacksize);
+       /* calculate max. frequency */
+       if (subs->maxpacksize) {
+               /* whatever fits into a max. size packet */
                maxsize = subs->maxpacksize;
+               subs->freqmax = (maxsize / (frame_bits >> 3))
+                               << (16 - subs->datainterval);
+       } else {
+               /* no max. packet size: just take 25% higher than nominal */
+               subs->freqmax = subs->freqn + (subs->freqn >> 2);
+               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
+                               >> (16 - subs->datainterval);
        }
+       subs->phase = 0;
 
        if (subs->fill_max)
                subs->curpacksize = subs->maxpacksize;
        else
                subs->curpacksize = maxsize;
 
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-               urb_packs = nrpacks;
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+               packs_per_ms = 8 >> subs->datainterval;
        else
-               urb_packs = nrpacks * 8;
+               packs_per_ms = 1;
+       subs->packs_per_ms = packs_per_ms;
 
-       /* allocate a temporary buffer for playback */
        if (is_playback) {
-               subs->tmpbuf = kmalloc(maxsize * urb_packs, GFP_KERNEL);
-               if (! subs->tmpbuf) {
-                       snd_printk(KERN_ERR "cannot malloc tmpbuf\n");
-                       return -ENOMEM;
-               }
-       }
+               urb_packs = nrpacks;
+               urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB);
+               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
+       } else
+               urb_packs = 1;
+       urb_packs *= packs_per_ms;
 
        /* decide how many packets to be used */
-       total_packs = (period_bytes + maxsize - 1) / maxsize;
-       if (total_packs < 2 * MIN_PACKS_URB)
-               total_packs = 2 * MIN_PACKS_URB;
+       if (is_playback) {
+               unsigned int minsize;
+               /* determine how small a packet can be */
+               minsize = (subs->freqn >> (16 - subs->datainterval))
+                         * (frame_bits >> 3);
+               /* with sync from device, assume it can be 12% lower */
+               if (subs->syncpipe)
+                       minsize -= minsize >> 3;
+               minsize = max(minsize, 1u);
+               total_packs = (period_bytes + minsize - 1) / minsize;
+               /* round up to multiple of packs_per_ms */
+               total_packs = (total_packs + packs_per_ms - 1)
+                               & ~(packs_per_ms - 1);
+               /* we need at least two URBs for queueing */
+               if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms)
+                       total_packs = 2 * MIN_PACKS_URB * packs_per_ms;
+       } else {
+               total_packs = MAX_URBS * urb_packs;
+       }
        subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
        if (subs->nurbs > MAX_URBS) {
                /* too much... */
@@ -951,7 +1009,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
                subs->nurbs = 2;
                npacks[0] = (total_packs + 1) / 2;
                npacks[1] = total_packs - npacks[0];
-       } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB) {
+       } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) {
                /* the last packet is too small.. */
                if (subs->nurbs > 2) {
                        /* merge to the first one */
@@ -970,49 +1028,45 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
                snd_urb_ctx_t *u = &subs->dataurb[i];
                u->index = i;
                u->subs = subs;
-               u->transfer = 0;
                u->packets = npacks[i];
+               u->buffer_size = maxsize * u->packets;
                if (subs->fmt_type == USB_FORMAT_TYPE_II)
                        u->packets++; /* for transfer delimiter */
-               if (! is_playback) {
-                       /* allocate a capture buffer per urb */
-                       u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL);
-                       if (! u->buf) {
-                               release_substream_urbs(subs, 0);
-                               return -ENOMEM;
-                       }
-               }
                u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
-               if (! u->urb) {
-                       release_substream_urbs(subs, 0);
-                       return -ENOMEM;
-               }
-               u->urb->dev = subs->dev;
+               if (! u->urb)
+                       goto out_of_memory;
+               u->urb->transfer_buffer =
+                       usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
+                                        &u->urb->transfer_dma);
+               if (! u->urb->transfer_buffer)
+                       goto out_of_memory;
                u->urb->pipe = subs->datapipe;
-               u->urb->transfer_flags = URB_ISO_ASAP;
-               u->urb->number_of_packets = u->packets;
-               u->urb->interval = 1;
+               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+               u->urb->interval = 1 << subs->datainterval;
                u->urb->context = u;
                u->urb->complete = snd_usb_complete_callback(snd_complete_urb);
        }
 
        if (subs->syncpipe) {
                /* allocate and initialize sync urbs */
+               subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
+                                                GFP_KERNEL, &subs->sync_dma);
+               if (! subs->syncbuf)
+                       goto out_of_memory;
                for (i = 0; i < SYNC_URBS; i++) {
                        snd_urb_ctx_t *u = &subs->syncurb[i];
                        u->index = i;
                        u->subs = subs;
                        u->packets = 1;
                        u->urb = usb_alloc_urb(1, GFP_KERNEL);
-                       if (! u->urb) {
-                               release_substream_urbs(subs, 0);
-                               return -ENOMEM;
-                       }
+                       if (! u->urb)
+                               goto out_of_memory;
                        u->urb->transfer_buffer = subs->syncbuf + i * 4;
+                       u->urb->transfer_dma = subs->sync_dma + i * 4;
                        u->urb->transfer_buffer_length = 4;
-                       u->urb->dev = subs->dev;
                        u->urb->pipe = subs->syncpipe;
-                       u->urb->transfer_flags = URB_ISO_ASAP;
+                       u->urb->transfer_flags = URB_ISO_ASAP |
+                                                URB_NO_TRANSFER_DMA_MAP;
                        u->urb->number_of_packets = 1;
                        u->urb->interval = 1 << subs->syncinterval;
                        u->urb->context = u;
@@ -1020,6 +1074,10 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
                }
        }
        return 0;
+
+out_of_memory:
+       release_substream_urbs(subs, 0);
+       return -ENOMEM;
 }
 
 
@@ -1195,6 +1253,12 @@ static int set_format(snd_usb_substream_t *subs, struct audioformat *fmt)
                subs->datapipe = usb_sndisocpipe(dev, ep);
        else
                subs->datapipe = usb_rcvisocpipe(dev, ep);
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH &&
+           get_endpoint(alts, 0)->bInterval >= 1 &&
+           get_endpoint(alts, 0)->bInterval <= 4)
+               subs->datainterval = get_endpoint(alts, 0)->bInterval - 1;
+       else
+               subs->datainterval = 0;
        subs->syncpipe = subs->syncinterval = 0;
        subs->maxpacksize = fmt->maxpacksize;
        subs->fill_max = 0;
@@ -1282,7 +1346,8 @@ static int snd_usb_hw_params(snd_pcm_substream_t *substream,
        unsigned int channels, rate, format;
        int ret, changed;
 
-       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+       ret = snd_pcm_alloc_vmalloc_buffer(substream,
+                                          params_buffer_bytes(hw_params));
        if (ret < 0)
                return ret;
 
@@ -1338,7 +1403,7 @@ static int snd_usb_hw_free(snd_pcm_substream_t *substream)
        subs->cur_rate = 0;
        subs->period_bytes = 0;
        release_substream_urbs(subs, 0);
-       return snd_pcm_lib_free_pages(substream);
+       return snd_pcm_free_vmalloc_buffer(substream);
 }
 
 /*
@@ -1361,9 +1426,7 @@ static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream)
        subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
 
        /* reset the pointer */
-       subs->hwptr = 0;
        subs->hwptr_done = 0;
-       subs->transfer_sched = 0;
        subs->transfer_done = 0;
        subs->phase = 0;
 
@@ -1379,7 +1442,7 @@ static snd_pcm_hardware_t snd_usb_playback =
        .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
-       .buffer_bytes_max =     (128*1024),
+       .buffer_bytes_max =     (256*1024),
        .period_bytes_min =     64,
        .period_bytes_max =     (128*1024),
        .periods_min =          2,
@@ -1391,7 +1454,7 @@ static snd_pcm_hardware_t snd_usb_capture =
        .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
-       .buffer_bytes_max =     (128*1024),
+       .buffer_bytes_max =     (256*1024),
        .period_bytes_min =     64,
        .period_bytes_max =     (128*1024),
        .periods_min =          2,
@@ -1783,6 +1846,7 @@ static snd_pcm_ops_t snd_usb_playback_ops = {
        .prepare =      snd_usb_pcm_prepare,
        .trigger =      snd_usb_pcm_trigger,
        .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_get_vmalloc_page,
 };
 
 static snd_pcm_ops_t snd_usb_capture_ops = {
@@ -1794,6 +1858,7 @@ static snd_pcm_ops_t snd_usb_capture_ops = {
        .prepare =      snd_usb_pcm_prepare,
        .trigger =      snd_usb_pcm_trigger,
        .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_get_vmalloc_page,
 };
 
 
@@ -2010,6 +2075,9 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat
 
        INIT_LIST_HEAD(&subs->fmt_list);
        spin_lock_init(&subs->lock);
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               tasklet_init(&subs->start_period_elapsed, start_period_elapsed,
+                            (unsigned long)subs);
 
        subs->stream = as;
        subs->direction = stream;
@@ -2018,10 +2086,6 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat
                subs->ops = audio_urb_ops[stream];
        else
                subs->ops = audio_urb_ops_high_speed[stream];
-       snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream,
-                                     SNDRV_DMA_TYPE_CONTINUOUS,
-                                     snd_dma_continuous_data(GFP_KERNEL),
-                                     64 * 1024, 128 * 1024);
        snd_pcm_set_ops(as->pcm, stream,
                        stream == SNDRV_PCM_STREAM_PLAYBACK ?
                        &snd_usb_playback_ops : &snd_usb_capture_ops);
@@ -2067,7 +2131,6 @@ static void snd_usb_audio_pcm_free(snd_pcm_t *pcm)
        snd_usb_stream_t *stream = pcm->private_data;
        if (stream) {
                stream->pcm = NULL;
-               snd_pcm_lib_preallocate_free_for_all(pcm);
                snd_usb_audio_stream_free(stream);
        }
 }
@@ -2397,10 +2460,9 @@ static int parse_audio_format(snd_usb_audio_t *chip, struct audioformat *fp,
        if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
            chip->usb_id == USB_ID(0x041e, 0x3020)) {
                if (fmt[3] == USB_FORMAT_TYPE_I &&
-                   stream == SNDRV_PCM_STREAM_PLAYBACK &&
                    fp->rates != SNDRV_PCM_RATE_48000 &&
                    fp->rates != SNDRV_PCM_RATE_96000)
-                       return -1; /* use 48k only */
+                       return -1;
        }
 #endif
        return 0;
@@ -2492,8 +2554,10 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no)
                fp->altset_idx = i;
                fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
                fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-               /* FIXME: decode wMaxPacketSize of high bandwith endpoints */
                fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+               if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+                       fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+                                       * (fp->maxpacksize & 0x7ff);
                fp->attributes = csep[3];
 
                /* some quirks for attributes here */
@@ -2723,7 +2787,8 @@ static int create_standard_interface_quirk(snd_usb_audio_t *chip,
  * to detect the sample rate is by looking at wMaxPacketSize.
  */
 static int create_ua700_ua25_quirk(snd_usb_audio_t *chip,
-                                  struct usb_interface *iface)
+                                  struct usb_interface *iface,
+                                  const snd_usb_audio_quirk_t *quirk)
 {
        static const struct audioformat ua_format = {
                .format = SNDRV_PCM_FORMAT_S24_3LE,
@@ -2814,7 +2879,9 @@ static int create_ua700_ua25_quirk(snd_usb_audio_t *chip,
 /*
  * Create a stream for an Edirol UA-1000 interface.
  */
-static int create_ua1000_quirk(snd_usb_audio_t *chip, struct usb_interface *iface)
+static int create_ua1000_quirk(snd_usb_audio_t *chip,
+                              struct usb_interface *iface,
+                              const snd_usb_audio_quirk_t *quirk)
 {
        static const struct audioformat ua1000_format = {
                .format = SNDRV_PCM_FORMAT_S32_LE,
@@ -2891,6 +2958,13 @@ static int create_composite_quirk(snd_usb_audio_t *chip,
        return 0;
 }
 
+static int ignore_interface_quirk(snd_usb_audio_t *chip,
+                                 struct usb_interface *iface,
+                                 const snd_usb_audio_quirk_t *quirk)
+{
+       return 0;
+}
+
 
 /*
  * boot quirks
@@ -2926,8 +3000,6 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
 
 static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
 {
-#if 0
-       /* TODO: enable this when high speed synchronization actually works */
        u8 buf = 1;
 
        snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
@@ -2939,7 +3011,6 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
                                1, 2000, NULL, 0, 1000);
                return -ENODEV;
        }
-#endif
        return 0;
 }
 
@@ -2956,28 +3027,28 @@ static int snd_usb_create_quirk(snd_usb_audio_t *chip,
                                struct usb_interface *iface,
                                const snd_usb_audio_quirk_t *quirk)
 {
-       switch (quirk->type) {
-       case QUIRK_MIDI_FIXED_ENDPOINT:
-       case QUIRK_MIDI_YAMAHA:
-       case QUIRK_MIDI_MIDIMAN:
-       case QUIRK_MIDI_NOVATION:
-       case QUIRK_MIDI_MOTU:
-       case QUIRK_MIDI_EMAGIC:
-               return snd_usb_create_midi_interface(chip, iface, quirk);
-       case QUIRK_COMPOSITE:
-               return create_composite_quirk(chip, iface, quirk);
-       case QUIRK_AUDIO_FIXED_ENDPOINT:
-               return create_fixed_stream_quirk(chip, iface, quirk);
-       case QUIRK_AUDIO_STANDARD_INTERFACE:
-       case QUIRK_MIDI_STANDARD_INTERFACE:
-               return create_standard_interface_quirk(chip, iface, quirk);
-       case QUIRK_AUDIO_EDIROL_UA700_UA25:
-               return create_ua700_ua25_quirk(chip, iface);
-       case QUIRK_AUDIO_EDIROL_UA1000:
-               return create_ua1000_quirk(chip, iface);
-       case QUIRK_IGNORE_INTERFACE:
-               return 0;
-       default:
+       typedef int (*quirk_func_t)(snd_usb_audio_t *, struct usb_interface *,
+                                   const snd_usb_audio_quirk_t *);
+       static const quirk_func_t quirk_funcs[] = {
+               [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+               [QUIRK_COMPOSITE] = create_composite_quirk,
+               [QUIRK_MIDI_STANDARD_INTERFACE] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_FIXED_ENDPOINT] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_YAMAHA] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_MIDIMAN] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_NOVATION] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_RAW] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_EMAGIC] = snd_usb_create_midi_interface,
+               [QUIRK_MIDI_MIDITECH] = snd_usb_create_midi_interface,
+               [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_interface_quirk,
+               [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+               [QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
+               [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+       };
+
+       if (quirk->type < QUIRK_TYPE_COUNT) {
+               return quirk_funcs[quirk->type](chip, iface, quirk);
+       } else {
                snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
                return -ENXIO;
        }