📄 via82cxxx_audio.c
字号:
} while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0)); if (tmp8 & VIA_CR83_BUSY) printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n"); return tmp8;}/** * via_ac97_read_reg - Read AC97 standard register * @codec: Pointer to generic AC97 codec info * @reg: Index of AC97 register to be read * * Read the value of a single AC97 codec register, * as defined by the Intel AC97 specification. * * Defines the standard AC97 read-register operation * required by the kernel's ac97_codec interface. * * Returns the 16-bit value stored in the specified * register. */static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg){ unsigned long data; struct via_info *card; int counter; DPRINTK ("ENTER\n"); assert (codec != NULL); assert (codec->private_data != NULL); card = codec->private_data; data = (reg << 16) | VIA_CR80_READ; outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); udelay (20); for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { if (inl (card->baseaddr + 0x80) & VIA_CR80_VALID) goto out; udelay (15); } printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); goto err_out;out: data = (unsigned long) inl (card->baseaddr + 0x80); outb (0x02, card->baseaddr + 0x83); if (((data & 0x007F0000) >> 16) == reg) { DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", data, data & 0x0000FFFF); return data & 0x0000FFFF; } printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n", reg, ((data & 0x007F0000) >> 16));err_out: DPRINTK ("EXIT, returning 0\n"); return 0;}/** * via_ac97_write_reg - Write AC97 standard register * @codec: Pointer to generic AC97 codec info * @reg: Index of AC97 register to be written * @value: Value to be written to AC97 register * * Write the value of a single AC97 codec register, * as defined by the Intel AC97 specification. * * Defines the standard AC97 write-register operation * required by the kernel's ac97_codec interface. */static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value){ u32 data; struct via_info *card; int counter; DPRINTK ("ENTER\n"); assert (codec != NULL); assert (codec->private_data != NULL); card = codec->private_data; data = (reg << 16) + value; outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); udelay (10); for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0) goto out; udelay (15); } printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value);out: DPRINTK ("EXIT\n");}static int via_mixer_open (struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); struct via_info *card; struct pci_dev *pdev; struct pci_driver *drvr; DPRINTK ("ENTER\n"); pci_for_each_dev(pdev) { drvr = pci_dev_driver (pdev); if (drvr == &via_driver) { assert (pci_get_drvdata (pdev) != NULL); card = pci_get_drvdata (pdev); if (card->ac97.dev_mixer == minor) goto match; } } DPRINTK ("EXIT, returning -ENODEV\n"); return -ENODEV;match: file->private_data = &card->ac97; DPRINTK ("EXIT, returning 0\n"); return 0;}static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ac97_codec *codec = file->private_data; struct via_info *card; int nonblock = (file->f_flags & O_NONBLOCK); int rc; DPRINTK ("ENTER\n"); assert (codec != NULL); card = codec->private_data; assert (card != NULL); rc = via_syscall_down (card, nonblock); if (rc) goto out; rc = codec->mixer_ioctl(codec, cmd, arg); up (&card->syscall_sem);out: DPRINTK ("EXIT, returning %d\n", rc); return rc;}static loff_t via_llseek(struct file *file, loff_t offset, int origin){ DPRINTK ("ENTER\n"); DPRINTK("EXIT, returning -ESPIPE\n"); return -ESPIPE;}static struct file_operations via_mixer_fops = { owner: THIS_MODULE, open: via_mixer_open, llseek: via_llseek, ioctl: via_mixer_ioctl,};static int __init via_ac97_reset (struct via_info *card){ struct pci_dev *pdev = card->pdev; u8 tmp8; u16 tmp16; DPRINTK ("ENTER\n"); assert (pdev != NULL);#ifndef NDEBUG { u8 r40,r41,r42,r43,r44,r48; pci_read_config_byte (card->pdev, 0x40, &r40); pci_read_config_byte (card->pdev, 0x41, &r41); pci_read_config_byte (card->pdev, 0x42, &r42); 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", r40,r41,r42,r43,r44,r48); spin_lock_irq (&card->lock); DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", inb (card->baseaddr + 0x00), inb (card->baseaddr + 0x01), inb (card->baseaddr + 0x02), inl (card->baseaddr + 0x04), inl (card->baseaddr + 0x0C), inl (card->baseaddr + 0x80), inl (card->baseaddr + 0x84)); spin_unlock_irq (&card->lock); }#endif /* * reset AC97 controller: enable, disable, enable * pause after each command for good luck */ 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);#if 0 /* this breaks on K7M */ /* disable legacy stuff */ pci_write_config_byte (pdev, 0x42, 0x00); udelay(10);#endif /* route FM trap to IRQ, disable FM trap */ pci_write_config_byte (pdev, 0x48, 0x05); udelay(10); /* disable all codec GPI interrupts */ outl (0, pci_resource_start (pdev, 0) + 0x8C); /* WARNING: this line is magic. Remove this * and things break. */ /* enable variable rate, variable rate MIC ADC */ tmp16 = via_ac97_read_reg (&card->ac97, 0x2A); via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0)); pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); if ((tmp8 & (VIA_CR41_AC97_ENABLE | VIA_CR41_AC97_RESET)) == 0) { printk (KERN_ERR PFX "cannot enable AC97 controller, aborting\n"); DPRINTK ("EXIT, tmp8=%X, returning -ENODEV\n", tmp8); return -ENODEV; } DPRINTK ("EXIT, returning 0\n"); return 0;}static void via_ac97_codec_wait (struct ac97_codec *codec){ assert (codec->private_data != NULL); via_ac97_wait_idle (codec->private_data);}static int __init via_ac97_init (struct via_info *card){ int rc; u16 tmp16; DPRINTK ("ENTER\n"); assert (card != NULL); memset (&card->ac97, 0, sizeof (card->ac97)); card->ac97.private_data = card; card->ac97.codec_read = via_ac97_read_reg; card->ac97.codec_write = via_ac97_write_reg; card->ac97.codec_wait = via_ac97_codec_wait; 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"); return -EIO; } rc = via_ac97_reset (card); if (rc) { printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); goto err_out; } if (ac97_probe_codec (&card->ac97) == 0) { printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n"); rc = -EIO; goto err_out; } /* enable variable rate, variable rate MIC ADC */ tmp16 = via_ac97_read_reg (&card->ac97, 0x2A); via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0)); DPRINTK ("EXIT, returning 0\n"); return 0;err_out: unregister_sound_mixer (card->ac97.dev_mixer); DPRINTK("EXIT, returning %d\n", rc); return rc;}static void via_ac97_cleanup (struct via_info *card){ DPRINTK("ENTER\n"); assert (card != NULL); assert (card->ac97.dev_mixer >= 0); unregister_sound_mixer (card->ac97.dev_mixer); DPRINTK("EXIT\n");}/**************************************************************** * * Interrupt-related code * *//** * via_intr_channel - handle an interrupt for a single channel * @chan: handle interrupt for this channel * * This is the "meat" of the interrupt handler, * containing the actions taken each time an interrupt * occurs. All communication and coordination with * userspace takes place here. * * Locking: inside card->lock */static void via_intr_channel (struct via_channel *chan){ u8 status; int n; /* check pertinent bits of status register for action bits */ status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); if (!status) return; /* acknowledge any flagged bits ASAP */ outb (status, chan->iobase); /* grab current h/w ptr value */ n = atomic_read (&chan->hw_ptr); /* sanity check: make sure our h/w ptr doesn't have a weird value */ assert (n >= 0); assert (n < VIA_DMA_BUFFERS); /* 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); atomic_set (&chan->hw_ptr, 0); } else { chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG); atomic_inc (&chan->hw_ptr); } /* accounting crap for SNDCTL_DSP_GETxPTR */ chan->n_irqs++; chan->bytes += VIA_DMA_BUF_SIZE; if (chan->bytes < 0) /* handle overflow of 31-bit value */ chan->bytes = VIA_DMA_BUF_SIZE; /* wake up anyone listening to see when interrupts occur */ if (waitqueue_active (&chan->wait)) wake_up_all (&chan->wait); DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", chan->name, status, (long) inl (chan->iobase + 0x04), atomic_read (&chan->hw_ptr)); /* all following checks only occur when not in mmap(2) mode */ 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), * 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_bufs) == VIA_DMA_BUFFERS) { chan->is_active = 0; via_chan_stop (chan->iobase); } DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name, atomic_read (&chan->n_bufs));}static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct via_info *card = dev_id; u32 status32; /* to minimize interrupt sharing costs, we use the SGD status * shadow register to check the status of all inputs and * outputs with a single 32-bit bus read. If no interrupt * conditions are flagged, we exit immediately */ status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); if (!(status32 & VIA_INTR_MASK)) return; DPRINTK ("intr, status32 == 0x%08X\n", status32); /* synchronize interrupt handling under SMP. this spinlock * goes away completely on UP */ spin_lock (&card->lock); if (status32 & VIA_INTR_OUT) via_intr_channel (&card->ch_out); if (status32 & VIA_INTR_IN) via_intr_channel (&card->ch_in); if (status32 & VIA_INTR_FM) via_intr_channel (&card->ch_fm); spin_unlock (&card->lock);}/** * 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 * * Obtain and reserve IRQ for using in handling audio events. * Also, disable any IRQ-generating resources, to make sure * we don't get interrupts before we want them. */static int via_interrupt_init (struct via_info *card){ DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->pdev != NULL); /* check for sane IRQ number. can this ever happen? */ if (card->pdev->irq < 2) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -