📄 hda_intel.c
字号:
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, ®); 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, ®); 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, ®); 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 + -