patch-2.4.15 linux/drivers/sound/via82cxxx_audio.c
Next file: linux/drivers/sound/vwsnd.c
Previous file: linux/drivers/sound/trident.h
Back to the patch index
Back to the overall index
- Lines: 892
- Date:
Fri Nov 9 13:45:35 2001
- Orig file:
v2.4.14/linux/drivers/sound/via82cxxx_audio.c
- Orig date:
Tue Oct 9 17:06:52 2001
diff -u --recursive --new-file v2.4.14/linux/drivers/sound/via82cxxx_audio.c linux/drivers/sound/via82cxxx_audio.c
@@ -15,7 +15,7 @@
*/
-#define VIA_VERSION "1.1.14b"
+#define VIA_VERSION "1.9.1"
#include <linux/config.h>
@@ -88,9 +88,10 @@
#define VIA_MAX_FRAG_SIZE PAGE_SIZE
#define VIA_MIN_FRAG_SIZE 64
-#define VIA_MIN_FRAG_NUMBER 2
+#define VIA_MIN_FRAG_NUMBER 2
/* 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
@@ -188,6 +189,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)
@@ -353,7 +355,10 @@
static struct pci_device_id via_pci_tbl[] __initdata = {
- { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }
};
MODULE_DEVICE_TABLE(pci,via_pci_tbl);
@@ -387,7 +392,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);
@@ -408,7 +413,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);
@@ -431,6 +436,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
@@ -451,6 +469,14 @@
static inline int via_syscall_down (struct via_info *card, int nonblock)
{
+ /* Thomas Sailer:
+ * EAGAIN is supposed to be used if IO is pending,
+ * not if there is contention on some internal
+ * synchronization primitive which should be
+ * held only for a short time anyway
+ */
+ nonblock = 0;
+
if (nonblock) {
if (down_trylock (&card->syscall_sem))
return -EAGAIN;
@@ -473,6 +499,8 @@
static void via_stop_everything (struct via_info *card)
{
+ u8 tmp, new_tmp;
+
DPRINTK ("ENTER\n");
assert (card != NULL);
@@ -492,11 +520,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");
}
@@ -521,6 +570,8 @@
DPRINTK ("ENTER, rate = %d\n", rate);
+ if (chan->rate == rate)
+ goto out;
if (card->locked_rate) {
chan->rate = 48000;
goto out;
@@ -614,7 +665,7 @@
* Performs some of the preparations necessary to begin
* using a PCM channel.
*
- * Currently the preparations consist in
+ * Currently the preparations consist in
* setting the
* PCM channel to a known state.
*/
@@ -682,7 +733,7 @@
/* alloc DMA-able memory for scatter-gather buffers */
- chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE +
+ 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++) {
@@ -719,7 +770,7 @@
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);
@@ -766,17 +817,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");
}
@@ -844,7 +895,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;
@@ -1017,7 +1068,7 @@
DPRINTK ("ENTER\n");
/* in both cases the buffer cannot be changed */
- if (chan->is_active || chan->is_mapped) {
+ if (chan->is_active || chan->is_mapped) {
DPRINTK ("EXIT\n");
return -EINVAL;
}
@@ -1147,6 +1198,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);
@@ -1227,10 +1280,12 @@
data = (reg << 16) | VIA_CR80_READ | VIA_CR80_VALID;
outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
+ udelay (20);
+
for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
- udelay (1);
- if ((((data = inl(card->baseaddr + 0x80)) &
- (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID))
+ udelay (1);
+ if ((((data = inl(card->baseaddr + VIA_BASE0_AC97_CTRL)) &
+ (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID))
goto out;
}
@@ -1240,9 +1295,11 @@
out:
/* Once the valid bit has become set, we must wait a complete AC97
frame before the data has settled. */
- udelay(25);
- data = (unsigned long) inl (card->baseaddr + 0x80);
-
+ udelay(25);
+ data = (unsigned long) inl (card->baseaddr + VIA_BASE0_AC97_CTRL);
+
+ outb (0x02, card->baseaddr + 0x83);
+
if (((data & 0x007F0000) >> 16) == reg) {
DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n",
data, data & 0x0000FFFF);
@@ -1370,6 +1427,7 @@
static int __init via_ac97_reset (struct via_info *card)
{
struct pci_dev *pdev = card->pdev;
+ u8 tmp8;
u16 tmp16;
DPRINTK ("ENTER\n");
@@ -1403,20 +1461,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 */
@@ -1433,20 +1514,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;
@@ -1494,10 +1565,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;
@@ -1632,7 +1717,7 @@
uart401intr(irq, card->midi_devc, regs);
#endif
return;
- }
+ }
DPRINTK ("intr, status32 == 0x%08X\n", status32);
/* synchronize interrupt handling under SMP. this spinlock
@@ -1652,47 +1737,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
*
@@ -1703,6 +1747,8 @@
static int via_interrupt_init (struct via_info *card)
{
+ u8 tmp8;
+
DPRINTK ("ENTER\n");
assert (card != NULL);
@@ -1716,6 +1762,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);
@@ -1723,38 +1776,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
@@ -1958,18 +1984,27 @@
char *userbuf, size_t count,
int nonblock)
{
+ DECLARE_WAITQUEUE(wait, current);
const char *orig_userbuf = userbuf;
struct via_channel *chan = &card->ch_in;
size_t size;
int n, tmp;
+ ssize_t ret = 0;
/* if SGD has not yet been started, start it */
via_chan_maybe_start (chan);
handle_one_block:
/* just to be a nice neighbor */
- if (current->need_resched)
+ /* Thomas Sailer:
+ * But also to ourselves, release semaphore if we do so */
+ if (current->need_resched) {
+ up(&card->syscall_sem);
schedule ();
+ ret = via_syscall_down (card, nonblock);
+ if (ret)
+ goto out;
+ }
/* grab current channel software pointer. In the case of
* recording, this is pointing to the next buffer that
@@ -1981,33 +2016,53 @@
* to be copied to userland. sleep until at least
* one buffer has been read from the audio hardware.
*/
- tmp = atomic_read (&chan->n_frags);
- assert (tmp >= 0);
- assert (tmp <= chan->frag_number);
- while (tmp == 0) {
- if (nonblock || !chan->is_active)
- return -EAGAIN;
+ add_wait_queue(&chan->wait, &wait);
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ tmp = atomic_read (&chan->n_frags);
+ assert (tmp >= 0);
+ assert (tmp <= chan->frag_number);
+ if (tmp)
+ break;
+ if (nonblock || !chan->is_active) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ up(&card->syscall_sem);
DPRINTK ("Sleeping on block %d\n", n);
- interruptible_sleep_on (&chan->wait);
+ schedule();
- if (signal_pending (current))
- return -ERESTARTSYS;
+ ret = via_syscall_down (card, nonblock);
+ if (ret)
+ break;
- tmp = atomic_read (&chan->n_frags);
+ if (signal_pending (current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&chan->wait, &wait);
+ if (ret)
+ goto out;
/* 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 < 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,
- size))
- return -EFAULT;
+ base + ofs + chan->slop_len,
+ size)) {
+ ret = -EFAULT;
+ goto out;
+ }
count -= size;
chan->slop_len += size;
@@ -2056,7 +2111,7 @@
goto handle_one_block;
out:
- return userbuf - orig_userbuf;
+ return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret;
}
@@ -2107,16 +2162,25 @@
const char *userbuf, size_t count,
int nonblock)
{
+ DECLARE_WAITQUEUE(wait, current);
const char *orig_userbuf = userbuf;
struct via_channel *chan = &card->ch_out;
volatile struct via_sgd_table *sgtable = chan->sgtable;
size_t size;
int n, tmp;
+ ssize_t ret = 0;
handle_one_block:
/* just to be a nice neighbor */
- if (current->need_resched)
+ /* Thomas Sailer:
+ * But also to ourselves, release semaphore if we do so */
+ if (current->need_resched) {
+ up(&card->syscall_sem);
schedule ();
+ ret = via_syscall_down (card, nonblock);
+ if (ret)
+ goto out;
+ }
/* grab current channel fragment pointer. In the case of
* playback, this is pointing to the next fragment that
@@ -2128,21 +2192,37 @@
* to be filled by userspace. Sleep until
* at least one fragment is available for our use.
*/
- tmp = atomic_read (&chan->n_frags);
- assert (tmp >= 0);
- assert (tmp <= chan->frag_number);
- while (tmp == 0) {
- if (nonblock || !chan->is_enabled)
- return -EAGAIN;
+ add_wait_queue(&chan->wait, &wait);
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ tmp = atomic_read (&chan->n_frags);
+ assert (tmp >= 0);
+ assert (tmp <= chan->frag_number);
+ if (tmp)
+ break;
+ if (nonblock || !chan->is_active) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ up(&card->syscall_sem);
DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
- interruptible_sleep_on (&chan->wait);
+ schedule();
- if (signal_pending (current))
- return -ERESTARTSYS;
+ ret = via_syscall_down (card, nonblock);
+ if (ret)
+ break;
- tmp = atomic_read (&chan->n_frags);
+ if (signal_pending (current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&chan->wait, &wait);
+ if (ret)
+ goto out;
/* Now that we have at least one fragment we can write to, fill the buffer
* as much as possible with data from userspace.
@@ -2152,8 +2232,10 @@
size = (count < slop_left) ? count : slop_left;
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;
+ userbuf, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
count -= size;
chan->slop_len += size;
@@ -2264,7 +2346,8 @@
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");
@@ -2272,28 +2355,21 @@
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;
@@ -2315,6 +2391,9 @@
static int via_dsp_drain_playback (struct via_info *card,
struct via_channel *chan, int nonblock)
{
+ DECLARE_WAITQUEUE(wait, current);
+ int ret = 0;
+
DPRINTK ("ENTER, nonblock = %d\n", nonblock);
if (chan->slop_len > 0)
@@ -2325,10 +2404,16 @@
via_chan_maybe_start (chan);
- while (atomic_read (&chan->n_frags) < chan->frag_number) {
+ add_wait_queue(&chan->wait, &wait);
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (atomic_read (&chan->n_frags) >= chan->frag_number)
+ break;
+
if (nonblock) {
DPRINTK ("EXIT, returning -EAGAIN\n");
- return -EAGAIN;
+ ret = -EAGAIN;
+ break;
}
#ifdef VIA_DEBUG
@@ -2357,14 +2442,22 @@
printk (KERN_ERR "sleeping but not active\n");
#endif
+ up(&card->syscall_sem);
+
DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags));
- interruptible_sleep_on (&chan->wait);
+ schedule();
+
+ if ((ret = via_syscall_down (card, nonblock)))
+ break;
if (signal_pending (current)) {
DPRINTK ("EXIT, returning -ERESTARTSYS\n");
- return -ERESTARTSYS;
+ ret = -ERESTARTSYS;
+ break;
}
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&chan->wait, &wait);
#ifdef VIA_DEBUG
{
@@ -2392,8 +2485,8 @@
#endif
out:
- DPRINTK ("EXIT, returning 0\n");
- return 0;
+ DPRINTK ("EXIT, returning %d\n", ret);
+ return ret;
}
@@ -2695,7 +2788,6 @@
DPRINTK ("DSP_RESET\n");
if (rd) {
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);
@@ -2703,7 +2795,6 @@
if (wr) {
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);
@@ -2712,6 +2803,11 @@
rc = 0;
break;
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ rc = 0;
+ break;
+
/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
case SNDCTL_DSP_GETCAPS:
DPRINTK ("DSP_GETCAPS\n");
@@ -2812,6 +2908,15 @@
break;
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+ if ((file->f_mode & FMODE_READ) && card->ch_in.is_enabled)
+ val |= PCM_ENABLE_INPUT;
+ if ((file->f_mode & FMODE_WRITE) && card->ch_out.is_enabled)
+ val |= PCM_ENABLE_OUTPUT;
+ rc = put_user(val, (int *)arg);
+ break;
+
/* Enable full duplex. Since we do this as soon as we are opened
* with O_RDWR, this is mainly a no-op that always returns success.
*/
@@ -2834,7 +2939,7 @@
rc = via_chan_set_buffering(card, &card->ch_in, val);
if (wr)
- rc = via_chan_set_buffering(card, &card->ch_out, val);
+ 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,
@@ -2952,7 +3057,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);
}
}
@@ -3127,7 +3232,7 @@
pci_read_config_byte (pdev, 0x43, &r43);
card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2);
}
-
+
card->midi_info.irq = -pdev->irq;
if (probe_uart401(& card->midi_info, THIS_MODULE))
{
@@ -3181,10 +3286,10 @@
#ifdef CONFIG_MIDI_VIA82CXXX
if (card->midi_info.io_base)
- unload_uart401(&card->midi_info);
+ unload_uart401(&card->midi_info);
#endif
- via_interrupt_cleanup (card);
+ free_irq (card->pdev->irq, card);
via_card_cleanup_proc (card);
via_dsp_cleanup (card);
via_ac97_cleanup (card);
@@ -3197,8 +3302,8 @@
pci_set_drvdata (pdev, NULL);
pci_release_regions (pdev);
- pci_set_power_state (pdev, 3); /* ...zzzzzz */
pci_disable_device (pdev);
+ pci_set_power_state (pdev, 3); /* ...zzzzzz */
DPRINTK ("EXIT\n");
return;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)