Attached is a patch against kernel 2.4.6-pre1 which includes fixes for
via audio. It -should- patch against kernel 2.4.3 or later, though.
I'm interested in feedback from people having via audio problems, if
this patch fixes them. I am of course also interested in general
testing, to ensure I did not break anything in the process of fixing
things. :)
-- Jeff Garzik | Andre the Giant has a posse. Building 1024 | MandrakeSoft | --------------6B6658E9390F2079982C3562 Content-Type: text/plain; charset=us-ascii; name="via-pre1.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="via-pre1.patch"Index: linux_2_4/drivers/sound/via82cxxx_audio.c diff -u linux_2_4/drivers/sound/via82cxxx_audio.c:1.1.1.19 linux_2_4/drivers/sound/via82cxxx_audio.c:1.1.1.19.2.2 --- linux_2_4/drivers/sound/via82cxxx_audio.c:1.1.1.19 Mon Jun 4 19:43:41 2001 +++ linux_2_4/drivers/sound/via82cxxx_audio.c Thu Jun 7 13:04:28 2001 @@ -15,7 +15,7 @@ */ -#define VIA_VERSION "1.1.14b" +#define VIA_VERSION "1.1.15-pre1" #include <linux/config.h> @@ -92,6 +92,7 @@ #endif /* 82C686 function 5 (audio codec) PCI configuration registers */ +#define VIA_ACLINK_STATUS 0x40 #define VIA_ACLINK_CTRL 0x41 #define VIA_FUNC_ENABLE 0x42 #define VIA_PNP_CONTROL 0x43 @@ -187,6 +188,7 @@ /* controller base 0 register bitmasks */ #define VIA_INT_DISABLE_MASK (~(0x01|0x02)) #define VIA_SGD_STOPPED (1 << 2) +#define VIA_SGD_PAUSED (1 << 6) #define VIA_SGD_ACTIVE (1 << 7) #define VIA_SGD_TERMINATE (1 << 6) #define VIA_SGD_FLAG (1 << 0) @@ -381,7 +383,7 @@ * */ -static inline void via_chan_stop (int iobase) +static inline void via_chan_stop (long iobase) { if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE) outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL); @@ -402,7 +404,7 @@ * */ -static inline void via_chan_status_clear (int iobase) +static inline void via_chan_status_clear (long iobase) { u8 tmp = inb (iobase + VIA_PCM_STATUS); @@ -425,6 +427,19 @@ } +static int sg_active (long iobase) +{ + u8 tmp = inb (iobase + VIA_PCM_STATUS); + if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) { + printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n"); + return 0; + } + if (tmp & VIA_SGD_ACTIVE) + return 1; + return 0; +} + + /**************************************************************** * * Miscellaneous debris @@ -467,6 +482,8 @@ static void via_stop_everything (struct via_info *card) { + u8 tmp, new_tmp; + DPRINTK ("ENTER\n"); assert (card != NULL); @@ -486,11 +503,32 @@ via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); /* - * clear any enabled interrupt bits, reset to 8-bit mono PCM mode + * clear any enabled interrupt bits */ - outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); - outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); - outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + tmp = inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + + udelay(10); + + /* + * clear any existing flags + */ + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + DPRINTK ("EXIT\n"); } @@ -515,6 +553,8 @@ DPRINTK ("ENTER, rate = %d\n", rate); + if (chan->rate == rate) + goto out; if (card->locked_rate) { chan->rate = 48000; goto out; @@ -762,17 +802,17 @@ { DPRINTK ("ENTER\n"); - synchronize_irq(); - spin_lock_irq (&card->lock); /* stop any existing channel output */ + via_chan_status_clear (chan->iobase); via_chan_stop (chan->iobase); via_chan_status_clear (chan->iobase); - via_chan_pcm_fmt (chan, 1); spin_unlock_irq (&card->lock); + synchronize_irq(); + DPRINTK ("EXIT\n"); } @@ -840,7 +880,7 @@ /* if we are recording, enable recording fifo bit */ if (chan->is_record) chan->pcm_fmt |= VIA_PCM_REC_FIFO; - /* set interrupt select bits where applicable (PCM & FM out channels) */ + /* set interrupt select bits where applicable (PCM in & out channels) */ if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; @@ -1142,6 +1182,8 @@ static inline void via_chan_maybe_start (struct via_channel *chan) { + assert (chan->is_active == sg_active(chan->iobase)); + if (!chan->is_active && chan->is_enabled) { chan->is_active = 1; sg_begin (chan); @@ -1405,20 +1447,43 @@ #endif /* - * reset AC97 controller: enable, disable, enable - * pause after each command for good luck + * Reset AC97 controller: enable, disable, enable, + * pausing after each command for good luck. Only + * do this if the codec is not ready, because it causes + * loud pops and such due to such a hard codec reset. */ - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, VIA_CR41_AC97_ENABLE | - VIA_CR41_AC97_RESET | VIA_CR41_AC97_WAKEUP); - udelay (100); - - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); - udelay (100); - - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, - VIA_CR41_AC97_ENABLE | VIA_CR41_PCM_ENABLE | - VIA_CR41_VRA | VIA_CR41_AC97_RESET); - udelay (100); + pci_read_config_byte (pdev, VIA_ACLINK_STATUS, &tmp8); + if ((tmp8 & VIA_CR40_AC97_READY) == 0) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_AC97_RESET | + VIA_CR41_AC97_WAKEUP); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above + */ + pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); + if (((tmp8 & VIA_CR41_VRA) == 0) || + ((tmp8 & VIA_CR41_AC97_ENABLE) == 0) || + ((tmp8 & VIA_CR41_PCM_ENABLE) == 0) || + ((tmp8 & VIA_CR41_AC97_RESET) == 0)) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } #if 0 /* this breaks on K7M */ /* disable legacy stuff */ @@ -1435,20 +1500,10 @@ /* WARNING: this line is magic. Remove this * and things break. */ - /* enable variable rate, variable rate MIC ADC */ - /* - * If we cannot enable VRA, we have a locked-rate codec. - * We try again to enable VRA before assuming so, however. - */ + /* enable variable rate */ tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); - if ((tmp16 & 1) == 0) { + if ((tmp16 & 1) == 0) via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); - if ((tmp16 & 1) == 0) { - card->locked_rate = 1; - printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); - } - } DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1496,10 +1551,24 @@ goto err_out; } - /* enable variable rate, variable rate MIC ADC */ + /* enable variable rate */ tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + /* + * If we cannot enable VRA, we have a locked-rate codec. + * We try again to enable VRA before assuming so, however. + */ + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + if ((tmp16 & 1) == 0) { + via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + if ((tmp16 & 1) == 0) { + card->locked_rate = 1; + printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); + } + } + DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1649,47 +1718,6 @@ /** - * via_interrupt_disable - Disable all interrupt-generating sources - * @card: Private info for specified board - * - * Disables all interrupt-generation flags in the Via - * audio hardware registers. - */ - -static void via_interrupt_disable (struct via_info *card) -{ - u8 tmp8; - unsigned long flags; - - DPRINTK ("ENTER\n"); - - assert (card != NULL); - - spin_lock_irqsave (&card->lock, flags); - - pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); - if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { - tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; - pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); - } - - outb (inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE) & - VIA_INT_DISABLE_MASK, - card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); - outb (inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE) & - VIA_INT_DISABLE_MASK, - card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); - outb (inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE) & - VIA_INT_DISABLE_MASK, - card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); - - spin_unlock_irqrestore (&card->lock, flags); - - DPRINTK ("EXIT\n"); -} - - -/** * via_interrupt_init - Initialize interrupt handling * @card: Private info for specified board * @@ -1700,6 +1728,8 @@ static int via_interrupt_init (struct via_info *card) { + u8 tmp8; + DPRINTK ("ENTER\n"); assert (card != NULL); @@ -1713,6 +1743,13 @@ return -EIO; } + /* make sure FM irq is not routed to us */ + pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); + if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { + tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; + pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + } + if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", card->pdev->irq); @@ -1720,38 +1757,11 @@ return -EBUSY; } - /* we don't want interrupts until we're opened */ - via_interrupt_disable (card); - DPRINTK ("EXIT, returning 0\n"); return 0; } -/** - * via_interrupt_cleanup - Shutdown driver interrupt handling - * @card: Private info for specified board - * - * Disable any potential interrupt sources in the Via audio - * hardware, and then release (un-reserve) the IRQ line - * in the kernel core. - */ - -static void via_interrupt_cleanup (struct via_info *card) -{ - DPRINTK ("ENTER\n"); - - assert (card != NULL); - assert (card->pdev != NULL); - - via_interrupt_disable (card); - - free_irq (card->pdev->irq, card); - - DPRINTK ("EXIT\n"); -} - - /**************************************************************** * * OSS DSP device @@ -1983,17 +1993,16 @@ tmp = atomic_read (&chan->n_frags); assert (tmp >= 0); assert (tmp <= chan->frag_number); - while (tmp == 0) { + if (tmp == 0) { + int ret; if (nonblock || !chan->is_active) return -EAGAIN; DPRINTK ("Sleeping on block %d\n", n); - interruptible_sleep_on (&chan->wait); - - if (signal_pending (current)) - return -ERESTARTSYS; - - tmp = atomic_read (&chan->n_frags); + ret = wait_event_interruptible(chan->wait, + (tmp = atomic_read(&chan->n_frags))); + if (ret < 0) + return ret; } /* Now that we have a buffer we can read from, send @@ -2001,10 +2010,12 @@ */ while ((count > 0) && (chan->slop_len < chan->frag_size)) { size_t slop_left = chan->frag_size - chan->slop_len; + void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr; + unsigned ofs = n % (PAGE_SIZE / chan->frag_size); size = (count < slop_left) ? count : slop_left; if (copy_to_user (userbuf, - chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + n % (PAGE_SIZE / chan->frag_size) + chan->slop_len, + base + ofs + chan->slop_len, size)) return -EFAULT; @@ -2130,17 +2141,16 @@ tmp = atomic_read (&chan->n_frags); assert (tmp >= 0); assert (tmp <= chan->frag_number); - while (tmp == 0) { + if (tmp == 0) { + int ret; if (nonblock || !chan->is_enabled) return -EAGAIN; 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_frags); + ret = wait_event_interruptible(chan->wait, + (tmp = atomic_read(&chan->n_frags))); + if (ret < 0) + return ret; } /* Now that we have at least one fragment we can write to, fill the buffer @@ -2263,36 +2273,30 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait) { struct via_info *card; - unsigned int mask = 0, rd, wr; + struct via_channel *chan; + unsigned int mask = 0; DPRINTK ("ENTER\n"); assert (file != NULL); card = file->private_data; assert (card != NULL); - - rd = (file->f_mode & FMODE_READ); - wr = (file->f_mode & FMODE_WRITE); - 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_frags) == 0) - poll_wait(file, &card->ch_in.wait, wait); + if (file->f_mode & FMODE_READ) { + chan = &card->ch_in; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLIN | POLLRDNORM; } - 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_frags) > 0)) - mask |= POLLIN | POLLRDNORM; + if (file->f_mode & FMODE_WRITE) { + chan = &card->ch_out; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLOUT | POLLWRNORM; + } DPRINTK ("EXIT, returning %u\n", mask); return mask; @@ -2324,7 +2328,8 @@ via_chan_maybe_start (chan); - while (atomic_read (&chan->n_frags) < chan->frag_number) { + if (atomic_read (&chan->n_frags) < chan->frag_number) { + int ret; if (nonblock) { DPRINTK ("EXIT, returning -EAGAIN\n"); return -EAGAIN; @@ -2357,11 +2362,11 @@ #endif DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags)); - interruptible_sleep_on (&chan->wait); - - if (signal_pending (current)) { + ret = wait_event_interruptible(chan->wait, + (atomic_read (&chan->n_frags) >= chan->frag_number)); + if (ret < 0) { DPRINTK ("EXIT, returning -ERESTARTSYS\n"); - return -ERESTARTSYS; + return ret; } } @@ -2950,7 +2955,7 @@ via_chan_pcm_fmt (chan, 0); via_set_rate (&card->ac97, chan, 44100); } else { - via_chan_pcm_fmt (chan, 0); + via_chan_pcm_fmt (chan, 1); via_set_rate (&card->ac97, chan, 8000); } } @@ -3020,25 +3024,19 @@ if (printed_version++ == 0) printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); - if (pci_enable_device (pdev)) { - rc = -EIO; - goto err_out_none; - } - - if (!request_region (pci_resource_start (pdev, 0), - pci_resource_len (pdev, 0), - VIA_MODULE_NAME)) { - printk (KERN_ERR PFX "unable to obtain I/O resources, aborting\n"); - rc = -EBUSY; + rc = pci_enable_device (pdev); + if (rc) goto err_out; - } + rc = pci_request_regions (pdev, "via82cxxx_audio"); + if (rc) + goto err_out; card = kmalloc (sizeof (*card), GFP_KERNEL); if (!card) { printk (KERN_ERR PFX "out of memory, aborting\n"); rc = -ENOMEM; - goto err_out_none; + goto err_out_res; } pci_set_drvdata (pdev, card); @@ -3143,8 +3128,8 @@ #endif kfree (card); -err_out_none: - release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0)); +err_out_res: + pci_release_regions (pdev); err_out: pci_set_drvdata (pdev, NULL); DPRINTK ("EXIT - returning %d\n", rc); @@ -3162,12 +3147,12 @@ card = pci_get_drvdata (pdev); assert (card != NULL); - via_interrupt_cleanup (card); + free_irq (card->pdev->irq, card); via_card_cleanup_proc (card); via_dsp_cleanup (card); via_ac97_cleanup (card); - release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0)); + pci_release_regions (pdev); #ifndef VIA_NDEBUG memset (card, 0xAB, sizeof (*card)); /* poison memory */
--------------6B6658E9390F2079982C3562--
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/