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

📄 hda_intel.c

📁 一个Linux下的软猫驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
	azx_writeb(chip, CORBSIZE, 0x02);	/* set the corb write pointer to 0 */	azx_writew(chip, CORBWP, 0);	/* reset the corb hw read pointer */	azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);	/* enable corb dma */	azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);	/* RIRB set up */	chip->rirb.addr = chip->rb.addr + 2048;	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);	azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));	/* set the rirb size to 256 entries (ULI requires explicitly) */	azx_writeb(chip, RIRBSIZE, 0x02);	/* reset the rirb hw write pointer */	azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);	/* set N=1, get RIRB response interrupt for new entry */	azx_writew(chip, RINTCNT, 1);	/* enable rirb dma and response irq */#ifdef USE_CORB_RIRB	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);#else	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN);#endif	chip->rirb.rp = chip->rirb.cmds = 0;}static void azx_free_cmd_io(azx_t *chip){	/* disable ringbuffer DMAs */	azx_writeb(chip, RIRBCTL, 0);	azx_writeb(chip, CORBCTL, 0);}/* send a command */static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,			unsigned int verb, unsigned int para){	azx_t *chip = codec->bus->private_data;	unsigned int wp;	u32 val;	val = (u32)(codec->addr & 0x0f) << 28;	val |= (u32)direct << 27;	val |= (u32)nid << 20;	val |= verb << 8;	val |= para;	/* add command to corb */	wp = azx_readb(chip, CORBWP);	wp++;	wp %= ICH6_MAX_CORB_ENTRIES;	spin_lock_irq(&chip->reg_lock);	chip->rirb.cmds++;	chip->corb.buf[wp] = cpu_to_le32(val);	azx_writel(chip, CORBWP, wp);	spin_unlock_irq(&chip->reg_lock);	return 0;}#define ICH6_RIRB_EX_UNSOL_EV	(1<<4)/* retrieve RIRB entry - called from interrupt handler */static void azx_update_rirb(azx_t *chip){	unsigned int rp, wp;	u32 res, res_ex;	wp = azx_readb(chip, RIRBWP);	if (wp == chip->rirb.wp)		return;	chip->rirb.wp = wp;			while (chip->rirb.rp != wp) {		chip->rirb.rp++;		chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;		rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */		res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);		res = le32_to_cpu(chip->rirb.buf[rp]);		if (res_ex & ICH6_RIRB_EX_UNSOL_EV)			snd_hda_queue_unsol_event(chip->bus, res, res_ex);		else if (chip->rirb.cmds) {			chip->rirb.cmds--;			chip->rirb.res = res;		}	}}/* receive a response */static unsigned int azx_get_response(struct hda_codec *codec){	azx_t *chip = codec->bus->private_data;	int timeout = 50;	while (chip->rirb.cmds) {		if (! --timeout) {			snd_printk(KERN_ERR "azx_get_response timeout\n");			chip->rirb.rp = azx_readb(chip, RIRBWP);			chip->rirb.cmds = 0;//printk(KERN_DEBUG"%s: timeout codec=%p\n", __FUNCTION__, codec);			return -1;		}		msleep(1);	}	return chip->rirb.res; /* the last value */}#else/* * Use the single immediate command instead of CORB/RIRB for simplicity * * Note: according to Intel, this is not preferred use.  The command was *       intended for the BIOS only, and may get confused with unsolicited *       responses.  So, we shouldn't use it for normal operation from the *       driver. *       I left the codes, however, for debugging/testing purposes. */#define azx_alloc_cmd_io(chip)	0#define azx_init_cmd_io(chip)#define azx_free_cmd_io(chip)/* send a command */static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,			unsigned int verb, unsigned int para){	azx_t *chip = codec->bus->private_data;	u32 val;	int timeout = 50;	val = (u32)(codec->addr & 0x0f) << 28;	val |= (u32)direct << 27;	val |= (u32)nid << 20;	val |= verb << 8;	val |= para;	while (timeout--) {		/* check ICB busy bit */		if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {			/* Clear IRV valid bit */			azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID);			azx_writel(chip, IC, val);			azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY);			return 0;		}		udelay(1);	}	snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val);	return -EIO;}/* receive a response */static unsigned int azx_get_response(struct hda_codec *codec){	azx_t *chip = codec->bus->private_data;	int timeout = 50;	while (timeout--) {		/* check IRV busy bit */		if (azx_readw(chip, IRS) & ICH6_IRS_VALID)			return azx_readl(chip, IR);		udelay(1);	}	snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS));	return (unsigned int)-1;}#define azx_update_rirb(chip)#endif /* USE_CORB_RIRB *//* reset codec link */static int azx_reset(azx_t *chip){	int count;	/* reset controller */	azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);	count = 50;	while (azx_readb(chip, GCTL) && --count)		msleep(1);	/* delay for >= 100us for codec PLL to settle per spec	 * Rev 0.9 section 5.5.1	 */	msleep(1);	/* Bring controller out of reset */	azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);	count = 50;	while (! azx_readb(chip, GCTL) && --count)		msleep(1);	/* Brent Chartrand said to wait >= 540us for codecs to intialize */	msleep(1);	/* check to see if controller is ready */	if (! azx_readb(chip, GCTL)) {		snd_printd("azx_reset: controller not ready!\n");		return -EBUSY;	}	/* Accept unsolicited responses */	azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);	/* detect codecs */	if (! chip->codec_mask) {		chip->codec_mask = azx_readw(chip, STATESTS);		snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);	}	return 0;}/* * Lowlevel interface */  /* enable interrupts */static void azx_int_enable(azx_t *chip){	/* enable controller CIE and GIE */	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |		   ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);}/* disable interrupts */static void azx_int_disable(azx_t *chip){	int i;	/* disable interrupts in stream descriptor */	for (i = 0; i < chip->num_streams; i++) {		azx_dev_t *azx_dev = &chip->azx_dev[i];		azx_sd_writeb(azx_dev, SD_CTL,			      azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);	}	/* disable SIE for all streams */	azx_writeb(chip, INTCTL, 0);	/* disable controller CIE and GIE */	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &		   ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));}/* clear interrupts */static void azx_int_clear(azx_t *chip){	int i;	/* clear stream status */	for (i = 0; i < chip->num_streams; i++) {		azx_dev_t *azx_dev = &chip->azx_dev[i];		azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);	}	/* clear STATESTS */	azx_writeb(chip, STATESTS, STATESTS_INT_MASK);	/* clear rirb status */	azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);	/* clear int status */	azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);}/* start a stream */static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev){//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, azx_dev);	/* enable SIE */	azx_writeb(chip, INTCTL,		   azx_readb(chip, INTCTL) | (1 << azx_dev->index));	/* set DMA start and interrupt mask */	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |		      SD_CTL_DMA_START | SD_INT_MASK);}/* stop a stream */static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev){//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, azx_dev);	/* stop DMA */	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &		      ~(SD_CTL_DMA_START | SD_INT_MASK));	azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */	/* disable SIE */	azx_writeb(chip, INTCTL,		   azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));}/* * initialize the chip */static void azx_init_chip(azx_t *chip){	unsigned char reg;	/* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)	 * TCSEL == Traffic Class Select Register, which sets PCI express QOS	 * Ensuring these bits are 0 clears playback static on some HD Audio codecs	 */	pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, &reg);	pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8);	/* reset controller */	azx_reset(chip);	/* initialize interrupts */	azx_int_clear(chip);	azx_int_enable(chip);	/* initialize the codec command I/O */	azx_init_cmd_io(chip);	/* program the position buffer */	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);	azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));	switch (chip->driver_type) {	case AZX_DRIVER_ATI:		/* For ATI SB450 azalia HD audio, we need to enable snoop */		pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 				     &reg);		pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 				      (reg & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP);		break;	case AZX_DRIVER_NVIDIA:		/* For NVIDIA HDA, enable snoop */		pci_read_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, &reg);		pci_write_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR,				      (reg & 0xf0) | NVIDIA_HDA_ENABLE_COHBITS);		break;        }}/* * interrupt handler */static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs){	azx_t *chip = dev_id;	azx_dev_t *azx_dev;	u32 status;	int i;	spin_lock(&chip->reg_lock);	status = azx_readl(chip, INTSTS);	if (status == 0) {		spin_unlock(&chip->reg_lock);		return IRQ_NONE;	}		for (i = 0; i < chip->num_streams; i++) {		azx_dev = &chip->azx_dev[i];		if (status & azx_dev->sd_int_sta_mask) {			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);#if 1			if (azx_dev->substream && azx_dev->running) {				azx_dev->period_updating = 1;				spin_unlock(&chip->reg_lock);				snd_pcm_period_elapsed(azx_dev->substream);				spin_lock(&chip->reg_lock);				azx_dev->period_updating = 0;			}#endif		}	}	/* clear rirb int */	status = azx_readb(chip, RIRBSTS);	if (status & RIRB_INT_MASK) {		if (status & RIRB_INT_RESPONSE)			azx_update_rirb(chip);		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);	}#if 0	/* clear state status int */	if (azx_readb(chip, STATESTS) & 0x04)		azx_writeb(chip, STATESTS, 0x04);#endif	spin_unlock(&chip->reg_lock);		return IRQ_HANDLED;}/* * set up BDL entries */static void azx_setup_periods(azx_dev_t *azx_dev){	u32 *bdl = azx_dev->bdl;	dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;	int idx;//printk(KERN_DEBUG"%s: %p frags=%d fragsize=%d dma_addr=0x%lx\n", __FUNCTION__, azx_dev, azx_dev->frags, azx_dev->fragsize, (unsigned long) dma_addr);	/* reset BDL address */	azx_sd_writel(azx_dev, SD_BDLPL, 0);	azx_sd_writel(azx_dev, SD_BDLPU, 0);	/* program the initial BDL entries */	for (idx = 0; idx < azx_dev->frags; idx++) {		unsigned int off = idx << 2; /* 4 dword step */		dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;		/* program the address field of the BDL entry */		bdl[off] = cpu_to_le32((u32)addr);		bdl[off+1] = cpu_to_le32(upper_32bit(addr));		/* program the size field of the BDL entry */		bdl[off+2] = cpu_to_le32(azx_dev->fragsize);//XXX if(azx_dev->substream->pcm->dev_class != SNDRV_PCM_CLASS_MODEM)		/* program the IOC to enable interrupt when buffer completes */		bdl[off+3] = cpu_to_le32(0x01);	}}/* * set up the SD for streaming */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -