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

📄 hda_intel.c

📁 LINUX 2.6.17.4的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	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_single_get_response(struct hda_codec *codec){	struct azx *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;}/* * The below are the main callbacks from hda_codec. * * They are just the skeleton to call sub-callbacks according to the * current setting of chip->single_cmd. *//* send a command */static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,			int direct, unsigned int verb,			unsigned int para){	struct azx *chip = codec->bus->private_data;	if (chip->single_cmd)		return azx_single_send_cmd(codec, nid, direct, verb, para);	else		return azx_corb_send_cmd(codec, nid, direct, verb, para);}/* get a response */static unsigned int azx_get_response(struct hda_codec *codec){	struct azx *chip = codec->bus->private_data;	if (chip->single_cmd)		return azx_single_get_response(codec);	else		return azx_rirb_get_response(codec);}/* reset codec link */static int azx_reset(struct azx *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(struct azx *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(struct azx *chip){	int i;	/* disable interrupts in stream descriptor */	for (i = 0; i < chip->num_streams; i++) {		struct azx_dev *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(struct azx *chip){	int i;	/* clear stream status */	for (i = 0; i < chip->num_streams; i++) {		struct azx_dev *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(struct azx *chip, struct azx_dev *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(struct azx *chip, struct azx_dev *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(struct azx *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 */	if (! chip->single_cmd)		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){	struct azx *chip = dev_id;	struct azx_dev *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_intr++;				spin_unlock(&chip->reg_lock);				snd_pcm_period_elapsed(azx_dev->substream);				spin_lock(&chip->reg_lock);			}		}	}	/* clear rirb int */	status = azx_readb(chip, RIRBSTS);	if (status & RIRB_INT_MASK) {		if (! chip->single_cmd && (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(struct azx_dev *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(struct azx *chip, struct azx_dev *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(struct azx *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)) & probe_mask) {			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 struct azx_dev *azx_assign_device(struct azx *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(struct azx_dev *azx_dev){	azx_dev->opened = 0;}static struct snd_pcm_hardware 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 {	struct azx *chip;	struct hda_codec *codec;	struct hda_pcm_stream *hinfo[2];};static int azx_pcm_open(struct snd_pcm_substream *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	struct azx *chip = apcm->chip;	struct azx_dev *azx_dev;	struct snd_pcm_runtime *runtime = substream->runtime;	unsigned long flags;	int err;	mutex_lock(&chip->open_mutex);	azx_dev = azx_assign_device(chip, substream->stream);	if (azx_dev == NULL) {		mutex_unlock(&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);		mutex_unlock(&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;	mutex_unlock(&chip->open_mutex);	return 0;}static int azx_pcm_close(struct snd_pcm_substream *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	struct azx *chip = apcm->chip;	struct azx_dev *azx_dev = get_azx_dev(substream);	unsigned long flags;	mutex_lock(&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);	mutex_unlock(&chip->open_mutex);	return 0;}static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int azx_pcm_hw_free(struct snd_pcm_substream *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct azx_dev *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(struct snd_pcm_substream *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct azx *chip = apcm->chip;	struct azx_dev *azx_dev = get_azx_dev(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	struct snd_pcm_runtime *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,

⌨️ 快捷键说明

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