📄 atiixp.c
字号:
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 + -