Hi, A patch for the via82cxxx_audio sound driver against 2.4.1-pre8. It includes: 1. Support for variable fragment size and variable fragment number 2. Fixes for the SPEED, STEREO, CHANNELS, FMT ioctls when in read & write mode 2.1 Mmaped sound is now fully functional. The patch is fairly tested (xmms, quake3, oss-test) but it might still have some bugs. Also there is almost no documentation update... sorry. The remaining issue with the driver (that I can see) are the click/pops that happen when playback is started/stopped. I guess some of the channel setting/resetting is just a bit too harsh. Rui Sousa
--- linux-2.4.1-pre8/drivers/sound/via82cxxx_audio.c Sat Dec 30 05:25:47 2000 +++ linux-2.4.1-pre8.new/drivers/sound/via82cxxx_audio.c Thu Jan 18 21:22:17 +2001 @@ -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_len, 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)) * chan->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