⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 via82cxxx_audio.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	} 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 + -