📄 via82cxxx_audio.c
字号:
* halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given. * * This function halts all audio operations for the given channel * @chan, and then calls via_chan_pcm_fmt to set the audio hardware * to enable or disable stereo. */static int via_chan_set_stereo (struct via_info *card, struct via_channel *chan, int val){ DPRINTK ("ENTER, channels = %d\n", val); via_chan_clear (card, chan); switch (val) { /* mono */ case 1: chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; via_chan_pcm_fmt (chan, 0); break; /* stereo */ case 2: chan->pcm_fmt |= VIA_PCM_FMT_STEREO; via_chan_pcm_fmt (chan, 0); break; /* unknown */ default: printk (KERN_WARNING PFX "unknown number of channels\n"); val = -EINVAL; break; } DPRINTK ("EXIT, returning %d\n", val); 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/** * via_chan_dump_bufs - Display DMA table contents * @chan: Channel whose DMA table will be displayed * * Debugging function which displays the contents of the * scatter-gather DMA table for the given channel @chan. */static void via_chan_dump_bufs (struct via_channel *chan){ int 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, chan->sgtable[i].count & VIA_FLAG ? 1 : 0, chan->sgtable[i].count & VIA_EOL ? 1 : 0); } DPRINTK ("buf_in_use = %d, nextbuf = %d\n", atomic_read (&chan->buf_in_use), atomic_read (&chan->sw_ptr));}#endif /* VIA_CHAN_DUMP_BUFS *//** * via_chan_flush_frag - Flush partially-full playback buffer to hardware * @chan: Channel whose DMA table will be displayed * * Flushes partially-full playback buffer to hardware. */static void via_chan_flush_frag (struct via_channel *chan){ DPRINTK ("ENTER\n"); assert (chan->slop_len > 0); 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_frags) > 0); atomic_dec (&chan->n_frags); DPRINTK ("EXIT\n");}/** * via_chan_maybe_start - Initiate audio hardware DMA operation * @chan: Channel whose DMA is to be started * * Initiate DMA operation, if the DMA engine for the given * channel @chan is not already active. */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); DPRINTK ("starting channel %s\n", chan->name); }}/**************************************************************** * * Interface to ac97-codec module * * *//** * via_ac97_wait_idle - Wait until AC97 codec is not busy * @card: Private info for specified board * * Sleep until the AC97 codec is no longer busy. * Returns the final value read from the SGD * register being polled. */static u8 via_ac97_wait_idle (struct via_info *card){ u8 tmp8; int counter = VIA_COUNTER_LIMIT; DPRINTK ("ENTER/EXIT\n"); assert (card != NULL); assert (card->pdev != NULL); do { udelay (15); tmp8 = inb (card->baseaddr + 0x83); } 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; /* Every time we write to register 80 we cause a transaction. The only safe way to clear the valid bit is to write it at the same time as the command */ 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 + VIA_BASE0_AC97_CTRL)) & (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID)) goto out; } printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); goto err_out;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 + 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); 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 struct file_operations via_mixer_fops = { owner: THIS_MODULE, open: via_mixer_open, llseek: no_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, * 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 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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -