via82cxxx_audio.c
来自「linux 内核源代码」· C语言 代码 · 共 2,515 行 · 第 1/5 页
C
2,515 行
int counter; DPRINTK ("ENTER\n"); assert (codec != NULL); assert (codec->private_data != NULL); card = codec->private_data; spin_lock(&card->ac97_lock); 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: spin_unlock(&card->ac97_lock); DPRINTK ("EXIT\n");}static int via_mixer_open (struct inode *inode, struct file *file){ int minor = iminor(inode); struct via_info *card; struct pci_dev *pdev = NULL; struct pci_driver *drvr; DPRINTK ("ENTER\n"); while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { 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: pci_dev_put(pdev); file->private_data = card->ac97; DPRINTK ("EXIT, returning 0\n"); return nonseekable_open(inode, file);}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; #if 0 /* * Intercept volume control on 8233 and 8235 */ if(card->volume) { switch(cmd) { case SOUND_MIXER_READ_VOLUME: return card->mixer_vol; case SOUND_MIXER_WRITE_VOLUME: { int v; if(get_user(v, (int *)arg)) { rc = -EFAULT; goto out; } card->mixer_vol = v; } } } #endif rc = codec->mixer_ioctl(codec, cmd, arg); mutex_unlock(&card->syscall_mutex);out: DPRINTK ("EXIT, returning %d\n", rc); return rc;}static const struct file_operations via_mixer_fops = { .owner = THIS_MODULE, .open = via_mixer_open, .llseek = no_llseek, .ioctl = via_mixer_ioctl,};static int __devinit 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, * 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_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(card->legacy) {#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 */ 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); 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 __devinit via_ac97_init (struct via_info *card){ int rc; u16 tmp16; DPRINTK ("ENTER\n"); assert (card != NULL); card->ac97 = ac97_alloc_codec(); if(card->ac97 == NULL) return -ENOMEM; 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"); ac97_release_codec(card->ac97); return -EIO; } rc = via_ac97_reset (card); if (rc) { printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); goto err_out; } mdelay(10); 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 */ 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;err_out: unregister_sound_mixer (card->ac97->dev_mixer); DPRINTK ("EXIT, returning %d\n", rc); ac97_release_codec(card->ac97); 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); ac97_release_codec(card->ac97); DPRINTK ("EXIT\n");}/**************************************************************** * * Interrupt-related code * *//** * via_intr_channel - handle an interrupt for a single channel * @card: unused * @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_info *card, 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); if (!chan->sgtable) /* XXX: temporary solution */ return; /* 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 < 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 == (chan->frag_number - 1)) { chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL); atomic_set (&chan->hw_ptr, 0); } else { chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG); atomic_inc (&chan->hw_ptr); } /* accounting crap for SNDCTL_DSP_GETxPTR */ chan->n_irqs++; chan->bytes += chan->frag_size; /* FIXME - signed overflow is undefined */ if (chan->bytes < 0) /* handle overflow of 31-bit value */ chan->bytes = chan->frag_size; /* all following checks only occur when not in mmap(2) mode */ if (!chan->is_mapped) { /* 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_frags) < chan->frag_number) atomic_inc (&chan->n_frags); assert (atomic_read (&chan->n_frags) <= chan->frag_number); if (atomic_read (&chan->n_frags) == chan->frag_number) { chan->is_active = 0; via_chan_stop (chan->iobase); } } /* wake up anyone listening to see when interrupts occur */ 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)); DPRINTK ("%s intr, channel n_frags == %d, missed %d\n", chan->name, atomic_read (&chan->n_frags), missed);}static irqreturn_t via_interrupt(int irq, void *dev_id){ 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)) {#ifdef CONFIG_MIDI_VIA82CXXX if (card->midi_devc) uart401intr(irq, card->midi_devc);#endif return IRQ_HANDLED; } 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, &card->ch_out); if (status32 & VIA_INTR_IN) via_intr_channel (card, &card->ch_in); if (status32 & VIA_INTR_FM) via_intr_channel (card, &card->ch_fm); spin_unlock (&card->lock); return IRQ_HANDLED;}static irqreturn_t via_new_interrupt(int irq, void *dev_id){ 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_NEW_INTR_MASK)) return IRQ_NONE; /* * goes away completely on UP */ spin_lock (&card->lock); via_intr_channel (card, &card->ch_out); via_intr_channel (card, &card->ch_in); via_intr_channel (card, &card->ch_fm); spin_unlock (&card->lock); return IRQ_HANDLED;}/** * 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){ u8 tmp8; DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->pdev != NULL); /* check for sane IRQ number. can this ever happen? */ if (card->pdev->irq < 2) { printk (KERN_ERR PFX "insane IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EIO\n"); return -EIO; } /* VIA requires this is done */ pci_write_config_byte(card->pdev, PCI_INTERRUPT_LINE, card->pdev->irq); if(card->legacy) { /* 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) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?