Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.4.1 /drivers/sound/via82cxxx_audio.c

Filename:/drivers/sound/via82cxxx_audio.c
Lines Added:433
Lines Deleted:274
Also changed in: (Previous) 2.4.1-pre12  2.4.1-pre11  2.4.0-ac12  2.4.0-ac11  2.4.0-ac10  2.4.0-ac9 
(Following) 2.4.1-ac1  2.4.1-ac2  2.4.1-ac3  2.4.1-ac4  2.4.1-ac5  2.4.1-ac6 

Location
[  2.4.1
  [  drivers
    [  sound
       o  via82cxxx_audio.c

Patch

diff -u --recursive --new-file v2.4.0/linux/drivers/sound/via82cxxx_audio.c linux/drivers/sound/via82cxxx_audio.c
--- v2.4.0/linux/drivers/sound/via82cxxx_audio.c   Fri Dec 29 20:25:47 2000
+++ linux/drivers/sound/via82cxxx_audio.c   Wed Jan 24 15:30:43 2001
@@ -15,7 +15,7 @@
  */
 
 
-#define VIA_VERSION   "1.1.14"
+#define VIA_VERSION   "1.1.14a"
 
 
 #include <linux/config.h>
@@ -76,8 +76,16 @@
 #define VIA_COUNTER_LIMIT   100000
 
 /* size of DMA buffers */
-#define VIA_DMA_BUFFERS      16
-#define VIA_DMA_BUF_SIZE   PAGE_SIZE
+#define VIA_MAX_BUFFER_DMA_PAGES   32
+
+/* buffering default values in ms */
+#define VIA_DEFAULT_FRAG_TIME      20
+#define VIA_DEFAULT_BUFFER_TIME      500
+
+#define VIA_MAX_FRAG_SIZE      PAGE_SIZE
+#define VIA_MIN_FRAG_SIZE      64
+
+#define VIA_MIN_FRAG_NUMBER      2   
 
 #ifndef AC97_PCM_LR_ADC_RATE
 #  define AC97_PCM_LR_ADC_RATE AC97_PCM_LR_DAC_RATE
@@ -102,7 +110,6 @@
 #define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
 #define VIA_BASE0_PCM_OUT_CHAN_CTRL   0x01
 #define VIA_BASE0_PCM_OUT_CHAN_TYPE   0x02
-#define VIA_BASE0_PCM_OUT_BLOCK_COUNT   0x0C
 
 #define VIA_BASE0_PCM_IN_CHAN      0x10 /* input PCM from user */
 #define VIA_BASE0_PCM_IN_CHAN_STATUS   0x10
@@ -114,6 +121,7 @@
 #define VIA_PCM_CONTROL         0x01
 #define VIA_PCM_TYPE         0x02
 #define VIA_PCM_TABLE_ADDR      0x04
+#define VIA_PCM_BLOCK_COUNT      0x0C
 
 /* XXX unused DMA channel for FM PCM data */
 #define VIA_BASE0_FM_OUT_CHAN      0x20
@@ -223,14 +231,14 @@
 };
 
 
-struct via_sgd_data {
+struct via_buffer_pgtbl {
    dma_addr_t handle;
    void *cpuaddr;
 };
 
 
 struct via_channel {
-   atomic_t n_bufs;
+   atomic_t n_frags;
    atomic_t hw_ptr;
    wait_queue_head_t wait;
 
@@ -246,11 +254,14 @@
    u8 pcm_fmt;      /* VIA_PCM_FMT_xxx */
 
    unsigned rate;      /* sample rate */
+   unsigned int frag_size;
+   unsigned int frag_number;
 
    volatile struct via_sgd_table *sgtable;
    dma_addr_t sgt_handle;
 
-   struct via_sgd_data sgbuf [VIA_DMA_BUFFERS];
+   unsigned int page_number;
+   struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES];
 
    long iobase;
 
@@ -301,17 +312,16 @@
 static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
 static int via_dsp_open (struct inode *inode, struct file *file);
 static int via_dsp_release(struct inode *inode, struct file *file);
-#ifdef VIA_SUPPORT_MMAP
 static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);
-#endif
 
 static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
 static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
 static u8 via_ac97_wait_idle (struct via_info *card);
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan);
-static void via_chan_clear (struct via_channel *chan);
+static void via_chan_clear (struct via_info *card, struct via_channel *chan);
 static void via_chan_pcm_fmt (struct via_channel *chan, int reset);
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan);
 
 #ifdef VIA_PROC_FS
 static int via_init_proc (void);
@@ -569,25 +579,53 @@
    chan->pcm_fmt = VIA_PCM_FMT_MASK;
    chan->is_enabled = 1;
 
-   if (chan->is_record)
-      atomic_set (&chan->n_bufs, 0);
-   else
-      atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
+   chan->frag_number = 0;
+        chan->frag_size = 0;
+   atomic_set(&chan->n_frags, 0);
    atomic_set (&chan->hw_ptr, 0);
 }
 
+/**
+ *      via_chan_init - Initialize PCM channel
+ *      @card: Private audio chip info
+ *      @chan: Channel to be initialized
+ *
+ *      Performs some of the preparations necessary to begin
+ *      using a PCM channel.
+ *
+ *      Currently the preparations consist in 
+ *      setting the
+ *      PCM channel to a known state.
+ */
+
+
+static void via_chan_init (struct via_info *card, struct via_channel *chan)
+{
+
+        DPRINTK ("ENTER\n");
+
+   /* bzero channel structure, and init members to defaults */
+        via_chan_init_defaults (card, chan);
+
+        /* stop any existing channel output */
+        via_chan_clear (card, chan);
+        via_chan_status_clear (chan->iobase);
+        via_chan_pcm_fmt (chan, 1);
+
+   DPRINTK ("EXIT\n");
+}
 
 /**
- *   via_chan_init - Initialize PCM channel
+ *   via_chan_buffer_init - Initialize PCM channel buffer
  *   @card: Private audio chip info
  *   @chan: Channel to be initialized
  *
- *   Performs all the preparations necessary to begin
+ *   Performs some of the preparations necessary to begin
  *   using a PCM channel.
  *
  *   Currently the preparations include allocating the
- *   scatter-gather DMA table and buffers, setting the
- *   PCM channel to a known state, and passing the
+ *   scatter-gather DMA table and buffers,
+ *   and passing the
  *   address of the DMA table to the hardware.
  *
  *   Note that special care is taken when passing the
@@ -596,18 +634,21 @@
  *   always "take" the address.
  */
 
-static int via_chan_init (struct via_info *card, struct via_channel *chan)
+static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan)
 {
+   int page, offset;
    int i;
 
    DPRINTK ("ENTER\n");
 
-   /* bzero channel structure, and init members to defaults */
-   via_chan_init_defaults (card, chan);
+   if (chan->sgtable != NULL) {
+      DPRINTK ("EXIT\n");
+      return 0;
+   }
 
    /* alloc DMA-able memory for scatter-gather table */
    chan->sgtable = pci_alloc_consistent (card->pdev,
-      (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+      (sizeof (struct via_sgd_table) * chan->frag_number),
       &chan->sgt_handle);
    if (!chan->sgtable) {
       printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");
@@ -616,45 +657,54 @@
    }
 
    memset ((void*)chan->sgtable, 0,
-      (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS));
+      (sizeof (struct via_sgd_table) * chan->frag_number));
 
    /* alloc DMA-able memory for scatter-gather buffers */
-   for (i = 0; i < VIA_DMA_BUFFERS; i++) {
-      chan->sgbuf[i].cpuaddr =
-         pci_alloc_consistent (card->pdev, VIA_DMA_BUF_SIZE,
-                     &chan->sgbuf[i].handle);
 
-      if (!chan->sgbuf[i].cpuaddr)
-         goto err_out_nomem;
+   chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + 
+             (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0);
+
+   for (i = 0; i < chan->page_number; i++) {
+      chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE,
+                     &chan->pgtbl[i].handle);
 
-      if (i < (VIA_DMA_BUFFERS - 1))
-         chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
-      else
-         chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
-      chan->sgtable[i].addr = cpu_to_le32 (chan->sgbuf[i].handle);
+      if (!chan->pgtbl[i].cpuaddr) {
+         chan->page_number = i;
+         goto err_out_nomem;
+      }
 
 #ifndef VIA_NDEBUG
-      memset (chan->sgbuf[i].cpuaddr, 0xBC, VIA_DMA_BUF_SIZE);
+                memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);
 #endif
 
 #if 1
-      DPRINTK ("dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)\n",
-          i, (long)chan->sgbuf[i].handle,
-          (long)chan->sgtable[i].addr,
-          virt_to_phys(chan->sgbuf[i].cpuaddr),
-          chan->sgbuf[i].cpuaddr);
+                DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n",
+         i, (long)chan->pgtbl[i].handle,
+         virt_to_phys(chan->pgtbl[i].cpuaddr),
+         chan->pgtbl[i].cpuaddr);
 #endif
-
-      assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
    }
 
-   /* stop any existing channel output */
-   via_chan_clear (chan);
-   via_chan_status_clear (chan->iobase);
-   via_chan_pcm_fmt (chan, 1);
+   for (i = 0; i < chan->frag_number; i++) {
+
+      page = i / (PAGE_SIZE / chan->frag_size);
+      offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;
+
+      chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
+      chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);
+
+#if 1
+      DPRINTK ("dmabuf #%d (32(h)=%lx)\n",
+          i,
+          (long)chan->sgtable[i].addr);
+#endif
+   }   
+
+   /* overwrite the last buffer information */
+   chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
 
    /* set location of DMA-able scatter-gather info table */
-   DPRINTK("outl (0x%X, 0x%04lX)\n",
+   DPRINTK ("outl (0x%X, 0x%04lX)\n",
       cpu_to_le32 (chan->sgt_handle),
       chan->iobase + VIA_PCM_TABLE_ADDR);
 
@@ -664,7 +714,7 @@
    udelay (20);
    via_ac97_wait_idle (card);
 
-   DPRINTK("inl (0x%lX) = %x\n",
+   DPRINTK ("inl (0x%lX) = %x\n",
       chan->iobase + VIA_PCM_TABLE_ADDR,
       inl(chan->iobase + VIA_PCM_TABLE_ADDR));
 
@@ -673,7 +723,7 @@
 
 err_out_nomem:
    printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");
-   via_chan_free (card, chan);
+   via_chan_buffer_free (card, chan);
    DPRINTK ("EXIT\n");
    return -ENOMEM;
 }
@@ -695,8 +745,6 @@
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan)
 {
-   int i;
-
    DPRINTK ("ENTER\n");
 
    synchronize_irq();
@@ -710,23 +758,33 @@
 
    spin_unlock_irq (&card->lock);
 
+   DPRINTK ("EXIT\n");
+}
+
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan)
+{
+   int i;
+
+        DPRINTK ("ENTER\n");
+
    /* zero location of DMA-able scatter-gather info table */
    via_ac97_wait_idle(card);
    outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);
 
-   for (i = 0; i < VIA_DMA_BUFFERS; i++)
-      if (chan->sgbuf[i].cpuaddr) {
-         assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
-         pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE,
-                    chan->sgbuf[i].cpuaddr,
-                    chan->sgbuf[i].handle);
-         chan->sgbuf[i].cpuaddr = NULL;
-         chan->sgbuf[i].handle = 0;
+   for (i = 0; i < chan->page_number; i++)
+      if (chan->pgtbl[i].cpuaddr) {
+         pci_free_consistent (card->pdev, PAGE_SIZE,
+                    chan->pgtbl[i].cpuaddr,
+                    chan->pgtbl[i].handle);
+         chan->pgtbl[i].cpuaddr = NULL;
+         chan->pgtbl[i].handle = 0;
       }
 
+   chan->page_number = 0;
+
    if (chan->sgtable) {
       pci_free_consistent (card->pdev,
-         (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+         (sizeof (struct via_sgd_table) * chan->frag_number),
          (void*)chan->sgtable, chan->sgt_handle);
       chan->sgtable = NULL;
    }
@@ -771,11 +829,11 @@
    if (!chan->is_record)
       chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
 
-   outb (chan->pcm_fmt, chan->iobase + 2);
+   outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
 
    DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
        chan->pcm_fmt,
-       inb (chan->iobase + 2));
+       inb (chan->iobase + VIA_PCM_TYPE));
 }
 
 
@@ -787,10 +845,11 @@
  *   all software pointers which track DMA operation.
  */
 
-static void via_chan_clear (struct via_channel *chan)
+static void via_chan_clear (struct via_info *card, struct via_channel *chan)
 {
    DPRINTK ("ENTER\n");
    via_chan_stop (chan->iobase);
+   via_chan_buffer_free(card, chan);
    chan->is_active = 0;
    chan->is_mapped = 0;
    chan->is_enabled = 1;
@@ -798,10 +857,6 @@
    chan->sw_ptr = 0;
    chan->n_irqs = 0;
    atomic_set (&chan->hw_ptr, 0);
-   if (chan->is_record)
-      atomic_set (&chan->n_bufs, 0);
-   else
-      atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
    DPRINTK ("EXIT\n");
 }
 
@@ -826,7 +881,7 @@
 {
    DPRINTK ("ENTER, requested rate = %d\n", val);
 
-   via_chan_clear (chan);
+   via_chan_clear (card, chan);
 
    val = via_set_rate (&card->ac97, chan, val);
 
@@ -858,7 +913,7 @@
         val == AFMT_S16_LE ? "AFMT_S16_LE" :
        "unknown");
 
-   via_chan_clear (chan);
+   via_chan_clear (card, chan);
 
    assert (val != AFMT_QUERY); /* this case is handled elsewhere */
 
@@ -907,7 +962,7 @@
 {
    DPRINTK ("ENTER, channels = %d\n", val);
 
-   via_chan_clear (chan);
+   via_chan_clear (card, chan);
 
    switch (val) {
 
@@ -934,6 +989,78 @@
    return val;
 }
 
+static int via_chan_set_buffering (struct via_info *card,
+                                struct via_channel *chan, int val)
+{
+   int shift;
+
+        DPRINTK ("ENTER\n");
+
+   /* in both cases the buffer cannot be changed */
+   if (chan->is_active || chan->is_mapped) { 
+      DPRINTK ("EXIT\n");
+      return -EINVAL;
+   }
+
+   /* called outside SETFRAGMENT */
+   /* set defaults or do nothing */
+   if (val < 0) {
+
+      if (chan->frag_size && chan->frag_number)
+         goto out;
+
+      DPRINTK ("\n");
+
+      chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate *
+               ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) *
+               ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
+
+      shift = 0;
+      while (chan->frag_size) {
+         chan->frag_size >>= 1;
+         shift++;
+      }
+      chan->frag_size = 1 << shift;
+
+      chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME);
+
+      DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number);
+   } else {
+      chan->frag_size = 1 << (val & 0xFFFF);
+      chan->frag_number = (val >> 16) & 0xFFFF;
+
+      DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number);
+   }
+
+   /* quake3 wants frag_number to be a power of two */
+   shift = 0;
+   while (chan->frag_number) {
+      chan->frag_number >>= 1;
+      shift++;
+   }
+   chan->frag_number = 1 << shift;
+
+   if (chan->frag_size > VIA_MAX_FRAG_SIZE)
+      chan->frag_size = VIA_MAX_FRAG_SIZE;
+   else if (chan->frag_size < VIA_MIN_FRAG_SIZE)
+      chan->frag_size = VIA_MIN_FRAG_SIZE;
+
+   if (chan->frag_number < VIA_MIN_FRAG_NUMBER)
+                chan->frag_number = VIA_MIN_FRAG_NUMBER;
+
+   if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES)
+      chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size;
+
+out:
+   if (chan->is_record)
+      atomic_set (&chan->n_frags, 0);
+   else
+      atomic_set (&chan->n_frags, chan->frag_number);
+
+   DPRINTK ("EXIT\n");
+
+   return 0;
+}
 
 #ifdef VIA_CHAN_DUMP_BUFS
 /**
@@ -948,7 +1075,7 @@
 {
    int i;
 
-   for (i = 0; i < VIA_DMA_BUFFERS; i++) {
+   for (i = 0; i < chan->frag_number; i++) {
       DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
           i, chan->sgtable[i].addr,
           chan->sgtable[i].count & 0x00FFFFFF,
@@ -975,15 +1102,15 @@
 
    assert (chan->slop_len > 0);
 
-   if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+   if (chan->sw_ptr == (chan->frag_number - 1))
       chan->sw_ptr = 0;
    else
       chan->sw_ptr++;
 
    chan->slop_len = 0;
 
-   assert (atomic_read (&chan->n_bufs) > 0);
-   atomic_dec (&chan->n_bufs);
+   assert (atomic_read (&chan->n_frags) > 0);
+   atomic_dec (&chan->n_frags);
 
    DPRINTK ("EXIT\n");
 }
@@ -1003,7 +1130,7 @@
    if (!chan->is_active && chan->is_enabled) {
       chan->is_active = 1;
       sg_begin (chan);
-      DPRINTK("starting channel %s\n", chan->name);
+      DPRINTK ("starting channel %s\n", chan->name);
    }
 }
 
@@ -1213,7 +1340,7 @@
 {
    DPRINTK ("ENTER\n");
 
-   DPRINTK("EXIT, returning -ESPIPE\n");
+   DPRINTK ("EXIT, returning -ESPIPE\n");
    return -ESPIPE;
 }
 
@@ -1245,7 +1372,7 @@
       pci_read_config_byte (card->pdev, 0x43, &r43);
       pci_read_config_byte (card->pdev, 0x44, &r44);
       pci_read_config_byte (card->pdev, 0x48, &r48);
-      DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+      DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
          r40,r41,r42,r43,r44,r48);
 
       spin_lock_irq (&card->lock);
@@ -1334,7 +1461,7 @@
    card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
    if (card->ac97.dev_mixer < 0) {
       printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
-      DPRINTK("EXIT, returning -EIO\n");
+      DPRINTK ("EXIT, returning -EIO\n");
       return -EIO;
    }
 
@@ -1359,21 +1486,21 @@
 
 err_out:
    unregister_sound_mixer (card->ac97.dev_mixer);
-   DPRINTK("EXIT, returning %d\n", rc);
+   DPRINTK ("EXIT, returning %d\n", rc);
    return rc;
 }
 
 
 static void via_ac97_cleanup (struct via_info *card)
 {
-   DPRINTK("ENTER\n");
+   DPRINTK ("ENTER\n");
 
    assert (card != NULL);
    assert (card->ac97.dev_mixer >= 0);
 
    unregister_sound_mixer (card->ac97.dev_mixer);
 
-   DPRINTK("EXIT\n");
+   DPRINTK ("EXIT\n");
 }
 
 
@@ -1414,24 +1541,24 @@
 
    /* sanity check: make sure our h/w ptr doesn't have a weird value */
    assert (n >= 0);
-   assert (n < VIA_DMA_BUFFERS);
+   assert (n < chan->frag_number);
 
    /* reset SGD data structure in memory to reflect a full buffer,
     * and advance the h/w ptr, wrapping around to zero if needed
     */
-   if (n == (VIA_DMA_BUFFERS - 1)) {
-      chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_EOL);
+   if (n == (chan->frag_number - 1)) {
+      chan->sgtable[n].count = (chan->frag_size | VIA_EOL);
       atomic_set (&chan->hw_ptr, 0);
    } else {
-      chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG);
+      chan->sgtable[n].count = (chan->frag_size | VIA_FLAG);
       atomic_inc (&chan->hw_ptr);
    }
 
    /* accounting crap for SNDCTL_DSP_GETxPTR */
    chan->n_irqs++;
-   chan->bytes += VIA_DMA_BUF_SIZE;
+   chan->bytes += chan->frag_size;
    if (chan->bytes < 0) /* handle overflow of 31-bit value */
-      chan->bytes = VIA_DMA_BUF_SIZE;
+      chan->bytes = chan->frag_size;
 
    /* wake up anyone listening to see when interrupts occur */
    if (waitqueue_active (&chan->wait))
@@ -1445,25 +1572,25 @@
    if (chan->is_mapped)
       return;
 
-   /* If we are recording, then n_bufs represents the number
-    * of buffers waiting to be handled by userspace.
-    * If we are playback, then n_bufs represents the number
-    * of buffers remaining to be filled by userspace.
-    * We increment here.  If we reach max buffers (VIA_DMA_BUFFERS),
+   /* If we are recording, then n_frags represents the number
+    * of fragments waiting to be handled by userspace.
+    * If we are playback, then n_frags represents the number
+    * of fragments remaining to be filled by userspace.
+    * We increment here.  If we reach max number of fragments,
     * this indicates an underrun/overrun.  For this case under OSS,
     * we stop the record/playback process.
     */
-   if (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS)
-      atomic_inc (&chan->n_bufs);
-   assert (atomic_read (&chan->n_bufs) <= VIA_DMA_BUFFERS);
+   if (atomic_read (&chan->n_frags) < chan->frag_number)
+      atomic_inc (&chan->n_frags);
+   assert (atomic_read (&chan->n_frags) <= chan->frag_number);
 
-   if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) {
+   if (atomic_read (&chan->n_frags) == chan->frag_number) {
       chan->is_active = 0;
       via_chan_stop (chan->iobase);
    }
 
-   DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name,
-       atomic_read (&chan->n_bufs));
+   DPRINTK ("%s intr, channel n_frags == %d\n", chan->name,
+       atomic_read (&chan->n_frags));
 }
 
 
@@ -1618,9 +1745,7 @@
    poll:      via_dsp_poll,
    llseek:    via_llseek,
    ioctl:      via_dsp_ioctl,
-#ifdef VIA_SUPPORT_MMAP
    mmap:      via_dsp_mmap,
-#endif
 };
 
 
@@ -1668,7 +1793,6 @@
 }
 
 
-#ifdef VIA_SUPPORT_MMAP
 static struct page * via_mm_nopage (struct vm_area_struct * vma,
                 unsigned long address, int write_access)
 {
@@ -1685,8 +1809,6 @@
        address,
        write_access);
 
-   assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
         if (address > vma->vm_end) {
       DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");
       return NOPAGE_SIGBUS; /* Disallow mremap */
@@ -1702,7 +1824,7 @@
 
 #ifndef VIA_NDEBUG
    {
-   unsigned long max_bufs = VIA_DMA_BUFFERS;
+   unsigned long max_bufs = chan->frag_number;
    if (rd && wr) max_bufs *= 2;
    /* via_dsp_mmap() should ensure this */
    assert (pgoff < max_bufs);
@@ -1711,17 +1833,17 @@
 
    /* if full-duplex (read+write) and we have two sets of bufs,
     * then the playback buffers come first, sez soundcard.c */
-   if (pgoff >= VIA_DMA_BUFFERS) {
-      pgoff -= VIA_DMA_BUFFERS;
+   if (pgoff >= chan->page_number) {
+      pgoff -= chan->page_number;
       chan = &card->ch_in;
    } else if (!wr)
       chan = &card->ch_in;
 
-   assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0);
+   assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0);
 
-   dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr);
+   dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr);
    DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",
-       dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr);
+       dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr);
    get_page (dmapage);
    return dmapage;
 }
@@ -1761,16 +1883,18 @@
        vma->vm_end - vma->vm_start,
        vma->vm_pgoff);
 
-   assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
    max_size = 0;
-   if (file->f_mode & FMODE_READ) {
+   if (vma->vm_flags & VM_READ) {
       rd = 1;
-      max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+      via_chan_set_buffering(card, &card->ch_in, -1);
+      via_chan_buffer_init (card, &card->ch_in);
+      max_size += card->ch_in.page_number << PAGE_SHIFT;
    }
-   if (file->f_mode & FMODE_WRITE) {
+   if (vma->vm_flags & VM_WRITE) {
       wr = 1;
-      max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+      via_chan_set_buffering(card, &card->ch_out, -1);
+      via_chan_buffer_init (card, &card->ch_out);
+      max_size += card->ch_out.page_number << PAGE_SHIFT;
    }
 
    start = vma->vm_start;
@@ -1802,10 +1926,9 @@
    rc = 0;
 
 out:
-   DPRINTK("EXIT, returning %d\n", rc);
+   DPRINTK ("EXIT, returning %d\n", rc);
    return rc;
 }
-#endif /* VIA_SUPPORT_MMAP */
 
 
 static ssize_t via_dsp_do_read (struct via_info *card,
@@ -1831,13 +1954,13 @@
     */
    n = chan->sw_ptr;
 
-   /* n_bufs represents the number of buffers waiting
+   /* n_frags represents the number of fragments waiting
     * to be copied to userland.  sleep until at least
     * one buffer has been read from the audio hardware.
     */
-   tmp = atomic_read (&chan->n_bufs);
+   tmp = atomic_read (&chan->n_frags);
    assert (tmp >= 0);
-   assert (tmp <= VIA_DMA_BUFFERS);
+   assert (tmp <= chan->frag_number);
    while (tmp == 0) {
       if (nonblock || !chan->is_active)
          return -EAGAIN;
@@ -1848,18 +1971,18 @@
       if (signal_pending (current))
          return -ERESTARTSYS;
 
-      tmp = atomic_read (&chan->n_bufs);
+      tmp = atomic_read (&chan->n_frags);
    }
 
    /* Now that we have a buffer we can read from, send
     * as much as sample data possible to userspace.
     */
-   while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
-      size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+   while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+      size_t slop_left = chan->frag_size - chan->slop_len;
 
       size = (count < slop_left) ? count : slop_left;
       if (copy_to_user (userbuf,
-              chan->sgbuf[n].cpuaddr + chan->slop_len,
+              chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + n % (PAGE_SIZE / chan->frag_size) + chan->slop_l+
en,
               size))
          return -EFAULT;
 
@@ -1871,7 +1994,7 @@
    /* If we didn't copy the buffer completely to userspace,
     * stop now.
     */
-   if (chan->slop_len < VIA_DMA_BUF_SIZE)
+   if (chan->slop_len < chan->frag_size)
       goto out;
 
    /*
@@ -1882,20 +2005,20 @@
    /* advance channel software pointer to point to
     * the next buffer from which we will copy
     */
-   if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+   if (chan->sw_ptr == (chan->frag_number - 1))
       chan->sw_ptr = 0;
    else
       chan->sw_ptr++;
 
    /* mark one less buffer waiting to be processed */
-   assert (atomic_read (&chan->n_bufs) > 0);
-   atomic_dec (&chan->n_bufs);
+   assert (atomic_read (&chan->n_frags) > 0);
+   atomic_dec (&chan->n_frags);
 
    /* we are at a block boundary, there is no fragment data */
    chan->slop_len = 0;
 
-   DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
-      n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+   DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+      n, chan->sw_ptr, atomic_read (&chan->n_frags));
 
    DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
        inb (card->baseaddr + 0x00),
@@ -1941,12 +2064,18 @@
       goto out_up;
    }
 
+   via_chan_set_buffering(card, &card->ch_in, -1);
+        rc = via_chan_buffer_init (card, &card->ch_in);
+
+   if (rc)
+      goto out_up;
+
    rc = via_dsp_do_read (card, buffer, count, nonblock);
 
 out_up:
    up (&card->syscall_sem);
 out:
-   DPRINTK("EXIT, returning %ld\n",(long) rc);
+   DPRINTK ("EXIT, returning %ld\n",(long) rc);
    return rc;
 }
 
@@ -1966,40 +2095,40 @@
    if (current->need_resched)
       schedule ();
 
-   /* grab current channel software pointer.  In the case of
-    * playback, this is pointing to the next buffer that
+   /* grab current channel fragment pointer.  In the case of
+    * playback, this is pointing to the next fragment that
     * should receive data from userland.
     */
    n = chan->sw_ptr;
 
-   /* n_bufs represents the number of buffers remaining
+   /* n_frags represents the number of fragments remaining
     * to be filled by userspace.  Sleep until
-    * at least one buffer is available for our use.
+    * at least one fragment is available for our use.
     */
-   tmp = atomic_read (&chan->n_bufs);
+   tmp = atomic_read (&chan->n_frags);
    assert (tmp >= 0);
-   assert (tmp <= VIA_DMA_BUFFERS);
+   assert (tmp <= chan->frag_number);
    while (tmp == 0) {
       if (nonblock || !chan->is_enabled)
          return -EAGAIN;
 
-      DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
+      DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
       interruptible_sleep_on (&chan->wait);
 
       if (signal_pending (current))
          return -ERESTARTSYS;
 
-      tmp = atomic_read (&chan->n_bufs);
+      tmp = atomic_read (&chan->n_frags);
    }
 
-   /* Now that we have a buffer we can write to, fill it up
+   /* Now that we have at least one fragment we can write to, fill the buffer
     * as much as possible with data from userspace.
     */
-   while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
-      size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+   while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+      size_t slop_left = chan->frag_size - chan->slop_len;
 
       size = (count < slop_left) ? count : slop_left;
-      if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len,
+      if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * c+
han->frag_size + chan->slop_len,
                 userbuf, size))
          return -EFAULT;
 
@@ -2009,36 +2138,36 @@
    }
 
    /* If we didn't fill up the buffer with data, stop now.
-    * Put a 'stop' marker in the DMA table too, to tell the
-    * audio hardware to stop if it gets here.
-    */
-   if (chan->slop_len < VIA_DMA_BUF_SIZE) {
+         * Put a 'stop' marker in the DMA table too, to tell the
+         * audio hardware to stop if it gets here.
+         */
+   if (chan->slop_len < chan->frag_size) {
       sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);
       goto out;
    }
 
    /*
-    * If we get to this point, we have filled a buffer with
-    * audio data, flush the buffer to audio hardware.
-    */
+         * If we get to this point, we have filled a buffer with
+         * audio data, flush the buffer to audio hardware.
+         */
 
    /* Record the true size for the audio hardware to notice */
-   if (n == (VIA_DMA_BUFFERS - 1))
-      sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
-   else
-      sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
+        if (n == (chan->frag_number - 1))
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
+        else
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
 
    /* advance channel software pointer to point to
     * the next buffer we will fill with data
     */
-   if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+   if (chan->sw_ptr == (chan->frag_number - 1))
       chan->sw_ptr = 0;
    else
       chan->sw_ptr++;
 
    /* mark one less buffer as being available for userspace consumption */
-   assert (atomic_read (&chan->n_bufs) > 0);
-   atomic_dec (&chan->n_bufs);
+   assert (atomic_read (&chan->n_frags) > 0);
+   atomic_dec (&chan->n_frags);
 
    /* we are at a block boundary, there is no fragment data */
    chan->slop_len = 0;
@@ -2046,8 +2175,8 @@
    /* if SGD has not yet been started, start it */
    via_chan_maybe_start (chan);
 
-   DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
-      n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+   DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+      n, chan->sw_ptr, atomic_read (&chan->n_frags));
 
    DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
        inb (card->baseaddr + 0x00),
@@ -2093,12 +2222,18 @@
       goto out_up;
    }
 
+   via_chan_set_buffering(card, &card->ch_out, -1);
+   rc = via_chan_buffer_init (card, &card->ch_out);
+
+   if (rc)
+      goto out_up;
+
    rc = via_dsp_do_write (card, buffer, count, nonblock);
 
 out_up:
    up (&card->syscall_sem);
 out:
-   DPRINTK("EXIT, returning %ld\n",(long) rc);
+   DPRINTK ("EXIT, returning %ld\n",(long) rc);
    return rc;
 }
 
@@ -2117,23 +2252,27 @@
    rd = (file->f_mode & FMODE_READ);
    wr = (file->f_mode & FMODE_WRITE);
 
-   if (wr && (atomic_read (&card->ch_out.n_bufs) == 0)) {
+   if (wr && (atomic_read (&card->ch_out.n_frags) == 0)) {
       assert (card->ch_out.is_active);
                 poll_wait(file, &card->ch_out.wait, wait);
    }
         if (rd) {
       /* XXX is it ok, spec-wise, to start DMA here? */
+      if (!card->ch_in.is_active) {
+         via_chan_set_buffering(card, &card->ch_in, -1);
+         via_chan_buffer_init(card, &card->ch_in);
+      }
       via_chan_maybe_start (&card->ch_in);
-      if (atomic_read (&card->ch_in.n_bufs) == 0)
+      if (atomic_read (&card->ch_in.n_frags) == 0)
                    poll_wait(file, &card->ch_in.wait, wait);
    }
 
-   if (wr && (atomic_read (&card->ch_out.n_bufs) > 0))
+   if (wr && ((atomic_read (&card->ch_out.n_frags) > 0) || !card->ch_out.is_active))
       mask |= POLLOUT | POLLWRNORM;
-   if (rd && (atomic_read (&card->ch_in.n_bufs) > 0))
+   if (rd && (atomic_read (&card->ch_in.n_frags) > 0))
       mask |= POLLIN | POLLRDNORM;
 
-   DPRINTK("EXIT, returning %u\n", mask);
+   DPRINTK ("EXIT, returning %u\n", mask);
    return mask;
 }
 
@@ -2158,12 +2297,12 @@
    if (chan->slop_len > 0)
       via_chan_flush_frag (chan);
 
-   if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS)
+   if (atomic_read (&chan->n_frags) == chan->frag_number)
       goto out;
 
    via_chan_maybe_start (chan);
 
-   while (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) {
+   while (atomic_read (&chan->n_frags) < chan->frag_number) {
       if (nonblock) {
          DPRINTK ("EXIT, returning -EAGAIN\n");
          return -EAGAIN;
@@ -2178,7 +2317,7 @@
       pci_read_config_byte (card->pdev, 0x43, &r43);
       pci_read_config_byte (card->pdev, 0x44, &r44);
       pci_read_config_byte (card->pdev, 0x48, &r48);
-      DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+      DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
          r40,r41,r42,r43,r44,r48);
 
       DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2195,7 +2334,7 @@
          printk (KERN_ERR "sleeping but not active\n");
 #endif
 
-      DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_bufs));
+      DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags));
       interruptible_sleep_on (&chan->wait);
 
       if (signal_pending (current)) {
@@ -2213,7 +2352,7 @@
       pci_read_config_byte (card->pdev, 0x43, &r43);
       pci_read_config_byte (card->pdev, 0x44, &r44);
       pci_read_config_byte (card->pdev, 0x48, &r48);
-      DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+      DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
          r40,r41,r42,r43,r44,r48);
 
       DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2225,7 +2364,7 @@
           inl (card->baseaddr + 0x80),
           inl (card->baseaddr + 0x84));
 
-      DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_bufs));
+      DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags));
    }
 #endif
 
@@ -2252,21 +2391,23 @@
 {
    audio_buf_info info;
 
-   info.fragstotal = VIA_DMA_BUFFERS;
-   info.fragsize = VIA_DMA_BUF_SIZE;
+   via_chan_set_buffering(card, chan, -1);
+
+   info.fragstotal = chan->frag_number;
+   info.fragsize = chan->frag_size;
 
    /* number of full fragments we can read/write without blocking */
-   info.fragments = atomic_read (&chan->n_bufs);
+   info.fragments = atomic_read (&chan->n_frags);
 
-   if ((chan->slop_len > 0) && (info.fragments > 0))
+   if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0))
       info.fragments--;
 
    /* number of bytes that can be read or written immediately
     * without blocking.
     */
-   info.bytes = (info.fragments * VIA_DMA_BUF_SIZE);
-   if (chan->slop_len > 0)
-      info.bytes += VIA_DMA_BUF_SIZE - chan->slop_len;
+   info.bytes = (info.fragments * chan->frag_size);
+   if (chan->slop_len % chan->frag_size > 0)
+      info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size);
 
    DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
       info.fragstotal,
@@ -2305,8 +2446,8 @@
 
    if (chan->is_active) {
       unsigned long extra;
-      info.ptr = atomic_read (&chan->hw_ptr) * VIA_DMA_BUF_SIZE;
-      extra = VIA_DMA_BUF_SIZE - inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+      info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size;
+      extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
       info.ptr += extra;
       info.bytes += extra;
    } else {
@@ -2386,13 +2527,13 @@
 
    /* OSS API version.  XXX unverified */
    case OSS_GETVERSION:
-      DPRINTK("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
+      DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
       rc = put_user (SOUND_VERSION, (int *)arg);
       break;
 
    /* list of supported PCM data formats */
    case SNDCTL_DSP_GETFMTS:
-      DPRINTK("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
+      DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
                 rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
       break;
 
@@ -2402,20 +2543,19 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_SETFMT, val==%d\n", val);
+      DPRINTK ("DSP_SETFMT, val==%d\n", val);
       if (val != AFMT_QUERY) {
          rc = 0;
 
-         if (rc == 0 && rd)
+         if (rd)
             rc = via_chan_set_fmt (card, &card->ch_in, val);
-         if (rc == 0 && wr)
+
+         if (rc >= 0 && wr)
             rc = via_chan_set_fmt (card, &card->ch_out, val);
 
-         if (rc <= 0) {
-            if (rc == 0)
-               rc = -EINVAL;
+         if (rc < 0)
             break;
-         }
+
          val = rc;
       } else {
          if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
@@ -2424,7 +2564,7 @@
          else
             val = AFMT_U8;
       }
-      DPRINTK("SETFMT EXIT, returning %d\n", val);
+      DPRINTK ("SETFMT EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
       break;
 
@@ -2434,18 +2574,19 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_CHANNELS, val==%d\n", val);
+      DPRINTK ("DSP_CHANNELS, val==%d\n", val);
       if (val != 0) {
          rc = 0;
-         if (rc == 0 && rd)
+
+         if (rd)
             rc = via_chan_set_stereo (card, &card->ch_in, val);
-         if (rc == 0 && wr)
+
+         if (rc >= 0 && wr)
             rc = via_chan_set_stereo (card, &card->ch_out, val);
-         if (rc <= 0) {
-            if (rc == 0)
-               rc = -EINVAL;
+
+         if (rc < 0)
             break;
-         }
+
          val = rc;
       } else {
          if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
@@ -2454,7 +2595,7 @@
          else
             val = 1;
       }
-      DPRINTK("CHANNELS EXIT, returning %d\n", val);
+      DPRINTK ("CHANNELS EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
       break;
 
@@ -2464,21 +2605,21 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_STEREO, val==%d\n", val);
+      DPRINTK ("DSP_STEREO, val==%d\n", val);
       rc = 0;
 
-      if (rc == 0 && rd)
+      if (rd)
          rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
-      if (rc == 0 && wr)
+      if (rc >= 0 && wr)
          rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
 
-      if (rc <= 0) {
-         if (rc == 0)
-            rc = -EINVAL;
+      if (rc < 0)
          break;
-      }
-      DPRINTK("STEREO EXIT, returning %d\n", val);
-                rc = 0;
+
+      val = rc - 1;
+
+      DPRINTK ("STEREO EXIT, returning %d\n", val);
+      rc = put_user(val, (int *) arg);
       break;
 
    /* query or set sampling rate */
@@ -2487,7 +2628,7 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_SPEED, val==%d\n", val);
+      DPRINTK ("DSP_SPEED, val==%d\n", val);
       if (val < 0) {
          rc = -EINVAL;
          break;
@@ -2495,16 +2636,14 @@
       if (val > 0) {
          rc = 0;
 
-         if (rc == 0 && rd)
+         if (rd)
             rc = via_chan_set_speed (card, &card->ch_in, val);
-         if (rc == 0 && wr)
+         if (rc >= 0 && wr)
             rc = via_chan_set_speed (card, &card->ch_out, val);
 
-         if (rc <= 0) {
-            if (rc == 0)
-               rc = -EINVAL;
+         if (rc < 0)
             break;
-         }
+
          val = rc;
       } else {
          if (rd)
@@ -2514,7 +2653,7 @@
          else
             val = 0;
       }
-      DPRINTK("SPEED EXIT, returning %d\n", val);
+      DPRINTK ("SPEED EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
       break;
 
@@ -2522,7 +2661,7 @@
    case SNDCTL_DSP_SYNC:
       DPRINTK ("DSP_SYNC\n");
       if (wr) {
-         DPRINTK("SYNC EXIT (after calling via_dsp_drain_playback)\n");
+         DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n");
          rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
       }
       break;
@@ -2531,12 +2670,19 @@
         case SNDCTL_DSP_RESET:
       DPRINTK ("DSP_RESET\n");
       if (rd) {
-         via_chan_clear (&card->ch_in);
+         via_chan_clear (card, &card->ch_in);
          via_chan_pcm_fmt (&card->ch_in, 1);
+         card->ch_in.frag_number = 0;
+         card->ch_in.frag_size = 0;
+         atomic_set(&card->ch_in.n_frags, 0);
       }
+
       if (wr) {
-         via_chan_clear (&card->ch_out);
+         via_chan_clear (card, &card->ch_out);
          via_chan_pcm_fmt (&card->ch_out, 1);
+         card->ch_out.frag_number = 0;
+         card->ch_out.frag_size = 0;
+         atomic_set(&card->ch_out.n_frags, 0);
       }
 
       rc = 0;
@@ -2544,40 +2690,47 @@
 
    /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
    case SNDCTL_DSP_GETCAPS:
-      DPRINTK("DSP_GETCAPS\n");
+      DPRINTK ("DSP_GETCAPS\n");
       rc = put_user(VIA_DSP_CAP, (int *)arg);
       break;
 
-   /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+   /* obtain buffer fragment size */
    case SNDCTL_DSP_GETBLKSIZE:
-      DPRINTK("DSP_GETBLKSIZE\n");
-      rc = put_user(VIA_DMA_BUF_SIZE, (int *)arg);
+      DPRINTK ("DSP_GETBLKSIZE\n");
+
+      if (rd) {
+         via_chan_set_buffering(card, &card->ch_in, -1);
+         rc = put_user(card->ch_in.frag_size, (int *)arg);
+      } else if (wr) {
+         via_chan_set_buffering(card, &card->ch_out, -1);
+         rc = put_user(card->ch_out.frag_size, (int *)arg);
+      }
       break;
 
    /* obtain information about input buffering */
    case SNDCTL_DSP_GETISPACE:
-      DPRINTK("DSP_GETISPACE\n");
+      DPRINTK ("DSP_GETISPACE\n");
       if (rd)
          rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
       break;
 
    /* obtain information about output buffering */
    case SNDCTL_DSP_GETOSPACE:
-      DPRINTK("DSP_GETOSPACE\n");
+      DPRINTK ("DSP_GETOSPACE\n");
       if (wr)
          rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
       break;
 
    /* obtain information about input hardware pointer */
    case SNDCTL_DSP_GETIPTR:
-      DPRINTK("DSP_GETIPTR\n");
+      DPRINTK ("DSP_GETIPTR\n");
       if (rd)
          rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg);
       break;
 
    /* obtain information about output hardware pointer */
    case SNDCTL_DSP_GETOPTR:
-      DPRINTK("DSP_GETOPTR\n");
+      DPRINTK ("DSP_GETOPTR\n");
       if (wr)
          rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg);
       break;
@@ -2585,25 +2738,29 @@
    /* return number of bytes remaining to be played by DMA engine */
    case SNDCTL_DSP_GETODELAY:
       {
-      DPRINTK("DSP_GETODELAY\n");
+      DPRINTK ("DSP_GETODELAY\n");
 
       chan = &card->ch_out;
 
       if (!wr)
          break;
 
-      val = VIA_DMA_BUFFERS - atomic_read (&chan->n_bufs);
+      if (chan->is_active) {
 
-      if (val > 0) {
-         val *= VIA_DMA_BUF_SIZE;
-         val -= VIA_DMA_BUF_SIZE -
-                inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
-      }
-      val += chan->slop_len;
+         val = chan->frag_number - atomic_read (&chan->n_frags);
 
-      assert (val <= (VIA_DMA_BUF_SIZE * VIA_DMA_BUFFERS));
+         if (val > 0) {
+            val *= chan->frag_size;
+            val -= chan->frag_size -
+                   inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+         }
+         val += chan->slop_len % chan->frag_size;
+      } else
+         val = 0;
 
-      DPRINTK("GETODELAY EXIT, val = %d bytes\n", val);
+      assert (val <= (chan->frag_size * chan->frag_number));
+
+      DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val);
                 rc = put_user (val, (int *)arg);
       break;
       }
@@ -2617,7 +2774,7 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
+      DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
          rd, wr, card->ch_in.is_active, card->ch_out.is_active,
          card->ch_in.is_enabled, card->ch_out.is_enabled);
 
@@ -2625,6 +2782,7 @@
 
       if (rd)
          rc = via_dsp_ioctl_trigger (&card->ch_in, val);
+
       if (!rc && wr)
          rc = via_dsp_ioctl_trigger (&card->ch_out, val);
 
@@ -2634,7 +2792,7 @@
     * with O_RDWR, this is mainly a no-op that always returns success.
     */
    case SNDCTL_DSP_SETDUPLEX:
-      DPRINTK("DSP_SETDUPLEX\n");
+      DPRINTK ("DSP_SETDUPLEX\n");
       if (!rd || !wr)
          break;
       rc = 0;
@@ -2646,7 +2804,13 @@
          rc = -EFAULT;
          break;
       }
-      DPRINTK("DSP_SETFRAGMENT, val==%d\n", val);
+      DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val);
+
+      if (rd)
+         rc = via_chan_set_buffering(card, &card->ch_in, val);
+
+      if (wr)
+         rc = via_chan_set_buffering(card, &card->ch_out, val);   
 
       DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
           val & 0xFFFF,
@@ -2654,13 +2818,12 @@
           (val >> 16) & 0xFFFF,
           (val >> 16) & 0xFFFF);
 
-      /* just to shut up some programs */
       rc = 0;
       break;
 
    /* inform device of an upcoming pause in input (or output). */
    case SNDCTL_DSP_POST:
-      DPRINTK("DSP_POST\n");
+      DPRINTK ("DSP_POST\n");
       if (wr) {
          if (card->ch_out.slop_len > 0)
             via_chan_flush_frag (&card->ch_out);
@@ -2678,15 +2841,14 @@
    }
 
    up (&card->syscall_sem);
-   DPRINTK("EXIT, returning %d\n", rc);
+   DPRINTK ("EXIT, returning %d\n", rc);
    return rc;
 }
 
 
 static int via_dsp_open (struct inode *inode, struct file *file)
 {
-   int rc, minor = MINOR(inode->i_rdev);
-   int got_read_chan = 0;
+   int minor = MINOR(inode->i_rdev);
    struct via_info *card;
    struct pci_dev *pdev;
    struct via_channel *chan;
@@ -2733,17 +2895,13 @@
    }
 
    file->private_data = card;
-   DPRINTK("file->f_mode == 0x%x\n", file->f_mode);
+   DPRINTK ("file->f_mode == 0x%x\n", file->f_mode);
 
    /* handle input from analog source */
    if (file->f_mode & FMODE_READ) {
       chan = &card->ch_in;
 
-      rc = via_chan_init (card, chan);
-      if (rc)
-         goto err_out;
-
-      got_read_chan = 1;
+      via_chan_init (card, chan);
 
       /* why is this forced to 16-bit stereo in all drivers? */
       chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
@@ -2756,30 +2914,28 @@
    if (file->f_mode & FMODE_WRITE) {
       chan = &card->ch_out;
 
-      rc = via_chan_init (card, chan);
-      if (rc)
-         goto err_out_read_chan;
+      via_chan_init (card, chan);
 
-      if ((minor & 0xf) == SND_DEV_DSP16) {
-         chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
-         via_set_rate (&card->ac97, chan, 44100);
+      if (file->f_mode & FMODE_READ) {
+         /* if in duplex mode make the recording and playback channels
+            have the same settings */
+         chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+         via_chan_pcm_fmt (chan, 0);
+                        via_set_rate (&card->ac97, chan, 44100);
       } else {
-         via_set_rate (&card->ac97, chan, 8000);
+          if ((minor & 0xf) == SND_DEV_DSP16) {
+            chan->pcm_fmt = VIA_PCM_FMT_16BIT;
+            via_chan_pcm_fmt (chan, 0);
+            via_set_rate (&card->ac97, chan, 44100);
+         } else {
+            via_chan_pcm_fmt (chan, 0);
+            via_set_rate (&card->ac97, chan, 8000);
+         }
       }
-
-      via_chan_pcm_fmt (chan, 0);
    }
 
    DPRINTK ("EXIT, returning 0\n");
    return 0;
-
-err_out_read_chan:
-   if (got_read_chan)
-      via_chan_free (card, &card->ch_in);
-err_out:
-   up (&card->open_sem);
-   DPRINTK("ERROR EXIT, returning %d\n", rc);
-   return rc;
 }
 
 
@@ -2807,15 +2963,18 @@
          printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
 
       via_chan_free (card, &card->ch_out);
+      via_chan_buffer_free(card, &card->ch_out);
    }
 
-   if (file->f_mode & FMODE_READ)
+   if (file->f_mode & FMODE_READ) {
       via_chan_free (card, &card->ch_in);
+      via_chan_buffer_free (card, &card->ch_in);
+   }
 
    up (&card->syscall_sem);
    up (&card->open_sem);
 
-   DPRINTK("EXIT, returning 0\n");
+   DPRINTK ("EXIT, returning 0\n");
    return 0;
 }
 
@@ -2934,9 +3093,9 @@
       tmp &= 0xF0;
       tmp |= pdev->irq;
       pci_write_config_byte (pdev, 0x3C, tmp);
-      DPRINTK("new 0x3c==0x%02x\n", tmp);
+      DPRINTK ("new 0x3c==0x%02x\n", tmp);
    } else {
-      DPRINTK("IRQ reg 0x3c==0x%02x, irq==%d\n",
+      DPRINTK ("IRQ reg 0x3c==0x%02x, irq==%d\n",
          tmp, tmp & 0x0F);
    }
 
@@ -3036,12 +3195,12 @@
 
 static void __exit cleanup_via82cxxx_audio(void)
 {
-   DPRINTK("ENTER\n");
+   DPRINTK ("ENTER\n");
 
    pci_unregister_driver (&via_driver);
    via_cleanup_proc ();
 
-   DPRINTK("EXIT\n");
+   DPRINTK ("EXIT\n");
 }
 
 
@@ -3133,7 +3292,7 @@
 
       );
 
-   DPRINTK("EXIT, returning %d\n", len);
+   DPRINTK ("EXIT, returning %d\n", len);
    return len;
 
 #undef YN


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.