📄 hda_intel.c
字号:
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){ /* 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){ /* 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 (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; } } } /* 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; /* 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); /* program the IOC to enable interrupt when buffer completes */ bdl[off+3] = cpu_to_le32(0x01); }}/* * set up the SD for streaming */static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev){ unsigned char val; int timeout; /* make sure the run bit is zero for SD */ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START); /* reset stream */ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET); udelay(3); timeout = 300; while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && --timeout) ; val &= ~SD_CTL_STREAM_RESET; azx_sd_writeb(azx_dev, SD_CTL, val); udelay(3); timeout = 300; /* waiting for hardware to report that the stream is out of reset */ while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && --timeout) ; /* program the stream_tag */ azx_sd_writel(azx_dev, SD_CTL, (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) | (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT)); /* program the length of samples in cyclic buffer */ azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize); /* program the stream format */ /* this value needs to be the same as the one programmed */ azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val); /* program the stream LVI (last valid index) of the BDL */ azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1); /* program the BDL address */ /* lower BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); /* upper BDL address */ azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); /* enable the position buffer */ if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); /* set the interrupt enable bits in the descriptor control register */ azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); return 0;}/* * Codec initialization */static int __devinit azx_codec_create(azx_t *chip, const char *model){ struct hda_bus_template bus_temp; int c, codecs, err; memset(&bus_temp, 0, sizeof(bus_temp)); bus_temp.private_data = chip; bus_temp.modelname = model; bus_temp.pci = chip->pci; bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0) return err; codecs = 0; for (c = 0; c < AZX_MAX_CODECS; c++) { if (chip->codec_mask & (1 << c)) { err = snd_hda_codec_new(chip->bus, c, NULL); if (err < 0) continue; codecs++; } } if (! codecs) { snd_printk(KERN_ERR SFX "no codecs initialized\n"); return -ENXIO; } return 0;}/* * PCM support *//* assign a stream for the PCM */static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream){ int dev, i, nums; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { dev = chip->playback_index_offset; nums = chip->playback_streams; } else { dev = chip->capture_index_offset; nums = chip->capture_streams; } for (i = 0; i < nums; i++, dev++) if (! chip->azx_dev[dev].opened) { chip->azx_dev[dev].opened = 1; return &chip->azx_dev[dev]; } return NULL;}/* release the assigned stream */static inline void azx_release_device(azx_dev_t *azx_dev){ azx_dev->opened = 0;}static snd_pcm_hardware_t azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE /*|*/ /*SNDRV_PCM_INFO_RESUME*/), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, .periods_min = 2, .periods_max = AZX_MAX_FRAG, .fifo_size = 0,};struct azx_pcm { azx_t *chip; struct hda_codec *codec; struct hda_pcm_stream *hinfo[2];};static int azx_pcm_open(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; azx_t *chip = apcm->chip; azx_dev_t *azx_dev; snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; int err; down(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream->stream); if (azx_dev == NULL) { up(&chip->open_mutex); return -EBUSY; } runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; runtime->hw.formats = hinfo->formats; runtime->hw.rates = hinfo->rates; snd_pcm_limit_hw_rates(runtime); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) { azx_release_device(azx_dev); up(&chip->open_mutex); return err; } spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = substream; azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); runtime->private_data = azx_dev; up(&chip->open_mutex); return 0;}static int azx_pcm_close(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); unsigned long flags; down(&chip->open_mutex); spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = NULL; azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); hinfo->ops.close(hinfo, apcm->codec, substream); up(&chip->open_mutex); return 0;}static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int azx_pcm_hw_free(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_dev_t *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; /* reset BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0); azx_sd_writel(azx_dev, SD_CTL, 0); hinfo->ops.cleanup(hinfo, apcm->codec, substream); return snd_pcm_lib_free_pages(substream);}static int azx_pcm_prepare(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; snd_pcm_runtime_t *runtime = substream->runtime; azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); azx_dev->fragsize = snd_pcm_lib_period_bytes(substream); azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize; azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, hinfo->maxbps); if (! azx_dev->format_val) { snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", runtime->rate, runtime->channels, runtime->format); return -EINVAL; } snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n", azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); azx_setup_periods(azx_dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -