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

📄 atiixp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	int timeout = 1000;	while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {		if (! timeout--) {			snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");			return -EBUSY;		}		udelay(1);	}	return 0;}static unsigned short snd_atiixp_codec_read(struct atiixp *chip, unsigned short codec, unsigned short reg){	unsigned int data;	int timeout;	if (snd_atiixp_acquire_codec(chip) < 0)		return 0xffff;	data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |		ATI_REG_PHYS_OUT_ADDR_EN |		ATI_REG_PHYS_OUT_RW |		codec;	atiixp_write(chip, PHYS_OUT_ADDR, data);	if (snd_atiixp_acquire_codec(chip) < 0)		return 0xffff;	timeout = 1000;	do {		data = atiixp_read(chip, PHYS_IN_ADDR);		if (data & ATI_REG_PHYS_IN_READ_FLAG)			return data >> ATI_REG_PHYS_IN_DATA_SHIFT;		udelay(1);	} while (--timeout);	/* time out may happen during reset */	if (reg < 0x7c)		snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);	return 0xffff;}static void snd_atiixp_codec_write(struct atiixp *chip, unsigned short codec,				   unsigned short reg, unsigned short val){	unsigned int data;    	if (snd_atiixp_acquire_codec(chip) < 0)		return;	data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) |		((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |		ATI_REG_PHYS_OUT_ADDR_EN | codec;	atiixp_write(chip, PHYS_OUT_ADDR, data);}static unsigned short snd_atiixp_ac97_read(struct snd_ac97 *ac97,					   unsigned short reg){	struct atiixp *chip = ac97->private_data;	return snd_atiixp_codec_read(chip, ac97->num, reg);    }static void snd_atiixp_ac97_write(struct snd_ac97 *ac97, unsigned short reg,				  unsigned short val){	struct atiixp *chip = ac97->private_data;	snd_atiixp_codec_write(chip, ac97->num, reg, val);}/* * reset AC link */static int snd_atiixp_aclink_reset(struct atiixp *chip){	int timeout;	/* reset powerdoewn */	if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0))		udelay(10);	/* perform a software reset */	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET);	atiixp_read(chip, CMD);	udelay(10);	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0);    	timeout = 10;	while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) {		/* do a hard reset */		atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,			      ATI_REG_CMD_AC_SYNC);		atiixp_read(chip, CMD);		mdelay(1);		atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);		if (--timeout) {			snd_printk(KERN_ERR "atiixp: codec reset timeout\n");			break;		}	}	/* deassert RESET and assert SYNC to make sure */	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,		      ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET);	return 0;}#ifdef CONFIG_PMstatic int snd_atiixp_aclink_down(struct atiixp *chip){	// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */	//	return -EBUSY;	atiixp_update(chip, CMD,		     ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET,		     ATI_REG_CMD_POWERDOWN);	return 0;}#endif/* * auto-detection of codecs * * the IXP chip can generate interrupts for the non-existing codecs. * NEW_FRAME interrupt is used to make sure that the interrupt is generated * even if all three codecs are connected. */#define ALL_CODEC_NOT_READY \	    (ATI_REG_ISR_CODEC0_NOT_READY |\	     ATI_REG_ISR_CODEC1_NOT_READY |\	     ATI_REG_ISR_CODEC2_NOT_READY)#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)static int ac97_probing_bugs(struct pci_dev *pci){	const struct snd_pci_quirk *q;	q = snd_pci_quirk_lookup(pci, atiixp_quirks);	if (q) {		snd_printdd(KERN_INFO "Atiixp quirk for %s.  "			    "Forcing codec %d\n", q->name, q->value);		return q->value;	}	/* this hardware doesn't need workarounds.  Probe for codec */	return -1;}static int snd_atiixp_codec_detect(struct atiixp *chip){	int timeout;	chip->codec_not_ready_bits = 0;	if (ac97_codec == -1)		ac97_codec = ac97_probing_bugs(chip->pci);	if (ac97_codec >= 0) {		chip->codec_not_ready_bits |= 			CODEC_CHECK_BITS ^ (1 << (ac97_codec + 10));		return 0;	}	atiixp_write(chip, IER, CODEC_CHECK_BITS);	/* wait for the interrupts */	timeout = 50;	while (timeout-- > 0) {		mdelay(1);		if (chip->codec_not_ready_bits)			break;	}	atiixp_write(chip, IER, 0); /* disable irqs */	if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {		snd_printk(KERN_ERR "atiixp: no codec detected!\n");		return -ENXIO;	}	return 0;}/* * enable DMA and irqs */static int snd_atiixp_chip_start(struct atiixp *chip){	unsigned int reg;	/* set up spdif, enable burst mode */	reg = atiixp_read(chip, CMD);	reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT;	reg |= ATI_REG_CMD_BURST_EN;	atiixp_write(chip, CMD, reg);	reg = atiixp_read(chip, SPDF_CMD);	reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH);	atiixp_write(chip, SPDF_CMD, reg);	/* clear all interrupt source */	atiixp_write(chip, ISR, 0xffffffff);	/* enable irqs */	atiixp_write(chip, IER,		     ATI_REG_IER_IO_STATUS_EN |		     ATI_REG_IER_IN_XRUN_EN |		     ATI_REG_IER_OUT_XRUN_EN |		     ATI_REG_IER_SPDF_XRUN_EN |		     ATI_REG_IER_SPDF_STATUS_EN);	return 0;}/* * disable DMA and IRQs */static int snd_atiixp_chip_stop(struct atiixp *chip){	/* clear interrupt source */	atiixp_write(chip, ISR, atiixp_read(chip, ISR));	/* disable irqs */	atiixp_write(chip, IER, 0);	return 0;}/* * PCM section *//* * pointer callback simplly reads XXX_DMA_DT_CUR register as the current * position.  when SG-buffer is implemented, the offset must be calculated * correctly... */static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substream){	struct atiixp *chip = snd_pcm_substream_chip(substream);	struct snd_pcm_runtime *runtime = substream->runtime;	struct atiixp_dma *dma = runtime->private_data;	unsigned int curptr;	int timeout = 1000;	while (timeout--) {		curptr = readl(chip->remap_addr + dma->ops->dt_cur);		if (curptr < dma->buf_addr)			continue;		curptr -= dma->buf_addr;		if (curptr >= dma->buf_bytes)			continue;		return bytes_to_frames(runtime, curptr);	}	snd_printd("atiixp: invalid DMA pointer read 0x%x (buf=%x)\n",		   readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);	return 0;}/* * XRUN detected, and stop the PCM substream */static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma){	if (! dma->substream || ! dma->running)		return;	snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);	snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);}/* * the period ack.  update the substream. */static void snd_atiixp_update_dma(struct atiixp *chip, struct atiixp_dma *dma){	if (! dma->substream || ! dma->running)		return;	snd_pcm_period_elapsed(dma->substream);}/* set BUS_BUSY interrupt bit if any DMA is running *//* call with spinlock held */static void snd_atiixp_check_bus_busy(struct atiixp *chip){	unsigned int bus_busy;	if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN |				      ATI_REG_CMD_RECEIVE_EN |				      ATI_REG_CMD_SPDF_OUT_EN))		bus_busy = ATI_REG_IER_SET_BUS_BUSY;	else		bus_busy = 0;	atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy);}/* common trigger callback * calling the lowlevel callbacks in it */static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd){	struct atiixp *chip = snd_pcm_substream_chip(substream);	struct atiixp_dma *dma = substream->runtime->private_data;	int err = 0;	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);	spin_lock(&chip->reg_lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:	case SNDRV_PCM_TRIGGER_RESUME:		dma->ops->enable_transfer(chip, 1);		dma->running = 1;		dma->suspended = 0;		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_SUSPEND:		dma->ops->enable_transfer(chip, 0);		dma->running = 0;		dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;		break;	default:		err = -EINVAL;		break;	}	if (! err) {		snd_atiixp_check_bus_busy(chip);		if (cmd == SNDRV_PCM_TRIGGER_STOP) {			dma->ops->flush_dma(chip);			snd_atiixp_check_bus_busy(chip);		}	}	spin_unlock(&chip->reg_lock);	return err;}/* * lowlevel callbacks for each DMA type * * every callback is supposed to be called in chip->reg_lock spinlock *//* flush FIFO of analog OUT DMA */static void atiixp_out_flush_dma(struct atiixp *chip){	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH);}/* enable/disable analog OUT DMA */static void atiixp_out_enable_dma(struct atiixp *chip, int on){	unsigned int data;	data = atiixp_read(chip, CMD);	if (on) {		if (data & ATI_REG_CMD_OUT_DMA_EN)			return;		atiixp_out_flush_dma(chip);		data |= ATI_REG_CMD_OUT_DMA_EN;	} else		data &= ~ATI_REG_CMD_OUT_DMA_EN;	atiixp_write(chip, CMD, data);}/* start/stop transfer over OUT DMA */static void atiixp_out_enable_transfer(struct atiixp *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN,		      on ? ATI_REG_CMD_SEND_EN : 0);}/* enable/disable analog IN DMA */static void atiixp_in_enable_dma(struct atiixp *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN,		      on ? ATI_REG_CMD_IN_DMA_EN : 0);}/* start/stop analog IN DMA */static void atiixp_in_enable_transfer(struct atiixp *chip, int on){	if (on) {		unsigned int data = atiixp_read(chip, CMD);		if (! (data & ATI_REG_CMD_RECEIVE_EN)) {			data |= ATI_REG_CMD_RECEIVE_EN;#if 0 /* FIXME: this causes the endless loop */			/* wait until slot 3/4 are finished */			while ((atiixp_read(chip, COUNTER) &				ATI_REG_COUNTER_SLOT) != 5)				;#endif			atiixp_write(chip, CMD, data);		}	} else		atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0);}/* flush FIFO of analog IN DMA */static void atiixp_in_flush_dma(struct atiixp *chip){	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH);}/* enable/disable SPDIF OUT DMA */static void atiixp_spdif_enable_dma(struct atiixp *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN,		      on ? ATI_REG_CMD_SPDF_DMA_EN : 0);}/* start/stop SPDIF OUT DMA */static void atiixp_spdif_enable_transfer(struct atiixp *chip, int on){	unsigned int data;	data = atiixp_read(chip, CMD);	if (on)		data |= ATI_REG_CMD_SPDF_OUT_EN;	else		data &= ~ATI_REG_CMD_SPDF_OUT_EN;	atiixp_write(chip, CMD, data);}/* flush FIFO of SPDIF OUT DMA */static void atiixp_spdif_flush_dma(struct atiixp *chip){	int timeout;	/* DMA off, transfer on */	atiixp_spdif_enable_dma(chip, 0);	atiixp_spdif_enable_transfer(chip, 1);		timeout = 100;	do {		if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED))			break;		udelay(1);

⌨️ 快捷键说明

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