📄 intel8x0.c
字号:
{ return ioread32(chip->bmaddr + offset);}static inline void iputbyte(struct intel8x0 *chip, u32 offset, u8 val){ iowrite8(val, chip->bmaddr + offset);}static inline void iputword(struct intel8x0 *chip, u32 offset, u16 val){ iowrite16(val, chip->bmaddr + offset);}static inline void iputdword(struct intel8x0 *chip, u32 offset, u32 val){ iowrite32(val, chip->bmaddr + offset);}/* * Lowlevel I/O - AC'97 registers */static inline u16 iagetword(struct intel8x0 *chip, u32 offset){ return ioread16(chip->addr + offset);}static inline void iaputword(struct intel8x0 *chip, u32 offset, u16 val){ iowrite16(val, chip->addr + offset);}/* * Basic I/O *//* * access to AC97 codec via normal i/o (for ICH and SIS7012) */static int snd_intel8x0_codec_semaphore(struct intel8x0 *chip, unsigned int codec){ int time; if (codec > 2) return -EIO; if (chip->in_sdin_init) { /* we don't know the ready bit assignment at the moment */ /* so we check any */ codec = chip->codec_isr_bits; } else { codec = chip->codec_bit[chip->ac97_sdin[codec]]; } /* codec ready ? */ if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0) return -EIO; if (chip->buggy_semaphore) return 0; /* just ignore ... */ /* Anyone holding a semaphore for 1 msec should be shot... */ time = 100; do { if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS)) return 0; udelay(10); } while (time--); /* access to some forbidden (non existant) ac97 registers will not * reset the semaphore. So even if you don't get the semaphore, still * continue the access. We don't need the semaphore anyway. */ snd_printk(KERN_ERR "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n", igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA))); iagetword(chip, 0); /* clear semaphore flag */ /* I don't care about the semaphore */ return -EBUSY;} static void snd_intel8x0_codec_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct intel8x0 *chip = ac97->private_data; if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { if (! chip->in_ac97_init) snd_printk(KERN_ERR "codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); } iaputword(chip, reg + ac97->num * 0x80, val);}static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97, unsigned short reg){ struct intel8x0 *chip = ac97->private_data; unsigned short res; unsigned int tmp; if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { if (! chip->in_ac97_init) snd_printk(KERN_ERR "codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); res = 0xffff; } else { res = iagetword(chip, reg + ac97->num * 0x80); if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) { /* reset RCS and preserve other R/WC bits */ iputdword(chip, ICHREG(GLOB_STA), tmp & ~(chip->codec_ready_bits | ICH_GSCI)); if (! chip->in_ac97_init) snd_printk(KERN_ERR "codec_read %d: read timeout for register 0x%x\n", ac97->num, reg); res = 0xffff; } } return res;}static void __devinit snd_intel8x0_codec_read_test(struct intel8x0 *chip, unsigned int codec){ unsigned int tmp; if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) { iagetword(chip, codec * 0x80); if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) { /* reset RCS and preserve other R/WC bits */ iputdword(chip, ICHREG(GLOB_STA), tmp & ~(chip->codec_ready_bits | ICH_GSCI)); } }}/* * access to AC97 for Ali5455 */static int snd_intel8x0_ali_codec_ready(struct intel8x0 *chip, int mask){ int count = 0; for (count = 0; count < 0x7f; count++) { int val = igetbyte(chip, ICHREG(ALI_CSPSR)); if (val & mask) return 0; } if (! chip->in_ac97_init) snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n"); return -EBUSY;}static int snd_intel8x0_ali_codec_semaphore(struct intel8x0 *chip){ int time = 100; if (chip->buggy_semaphore) return 0; /* just ignore ... */ while (time-- && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY)) udelay(1); if (! time && ! chip->in_ac97_init) snd_printk(KERN_WARNING "ali_codec_semaphore timeout\n"); return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);}static unsigned short snd_intel8x0_ali_codec_read(struct snd_ac97 *ac97, unsigned short reg){ struct intel8x0 *chip = ac97->private_data; unsigned short data = 0xffff; if (snd_intel8x0_ali_codec_semaphore(chip)) goto __err; reg |= ALI_CPR_ADDR_READ; if (ac97->num) reg |= ALI_CPR_ADDR_SECONDARY; iputword(chip, ICHREG(ALI_CPR_ADDR), reg); if (snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_READ_OK)) goto __err; data = igetword(chip, ICHREG(ALI_SPR)); __err: return data;}static void snd_intel8x0_ali_codec_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct intel8x0 *chip = ac97->private_data; if (snd_intel8x0_ali_codec_semaphore(chip)) return; iputword(chip, ICHREG(ALI_CPR), val); if (ac97->num) reg |= ALI_CPR_ADDR_SECONDARY; iputword(chip, ICHREG(ALI_CPR_ADDR), reg); snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_WRITE_OK);}/* * DMA I/O */static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev) { int idx; u32 *bdbar = ichdev->bdbar; unsigned long port = ichdev->reg_offset; iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr); if (ichdev->size == ichdev->fragsize) { ichdev->ack_reload = ichdev->ack = 2; ichdev->fragsize1 = ichdev->fragsize >> 1; for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) { bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf); bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ ichdev->fragsize1 >> ichdev->pos_shift); bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1)); bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */ ichdev->fragsize1 >> ichdev->pos_shift); } ichdev->frags = 2; } else { ichdev->ack_reload = ichdev->ack = 1; ichdev->fragsize1 = ichdev->fragsize; for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) { bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size)); bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ ichdev->fragsize >> ichdev->pos_shift);#if 0 printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);#endif } ichdev->frags = ichdev->size / ichdev->fragsize; } iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK); ichdev->civ = 0; iputbyte(chip, port + ICH_REG_OFF_CIV, 0); ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags; ichdev->position = 0;#if 0 printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n", ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);#endif /* clear interrupts */ iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);}#ifdef __i386__/* * Intel 82443MX running a 100MHz processor system bus has a hardware bug, * which aborts PCI busmaster for audio transfer. A workaround is to set * the pages as non-cached. For details, see the errata in * http://www.intel.com/design/chipsets/specupdt/245051.htm */static void fill_nocache(void *buf, int size, int nocache){ size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; change_page_attr(virt_to_page(buf), size, nocache ? PAGE_KERNEL_NOCACHE : PAGE_KERNEL); global_flush_tlb();}#else#define fill_nocache(buf,size,nocache)#endif/* * Interrupt handler */static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ichdev){ unsigned long port = ichdev->reg_offset; unsigned long flags; int status, civ, i, step; int ack = 0; spin_lock_irqsave(&chip->reg_lock, flags); status = igetbyte(chip, port + ichdev->roff_sr); civ = igetbyte(chip, port + ICH_REG_OFF_CIV); if (!(status & ICH_BCIS)) { step = 0; } else if (civ == ichdev->civ) { // snd_printd("civ same %d\n", civ); step = 1; ichdev->civ++; ichdev->civ &= ICH_REG_LVI_MASK; } else { step = civ - ichdev->civ; if (step < 0) step += ICH_REG_LVI_MASK + 1; // if (step != 1) // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ); ichdev->civ = civ; } ichdev->position += step * ichdev->fragsize1; if (! chip->in_measurement) ichdev->position %= ichdev->size; ichdev->lvi += step; ichdev->lvi &= ICH_REG_LVI_MASK; iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi); for (i = 0; i < step; i++) { ichdev->lvi_frag++; ichdev->lvi_frag %= ichdev->frags; ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);#if 0 printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));#endif if (--ichdev->ack == 0) { ichdev->ack = ichdev->ack_reload; ack = 1; } } spin_unlock_irqrestore(&chip->reg_lock, flags); if (ack && ichdev->substream) { snd_pcm_period_elapsed(ichdev->substream); } iputbyte(chip, port + ichdev->roff_sr, status & (ICH_FIFOE | ICH_BCIS | ICH_LVBCI));}static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id){ struct intel8x0 *chip = dev_id; struct ichdev *ichdev; unsigned int status; unsigned int i; status = igetdword(chip, chip->int_sta_reg); if (status == 0xffffffff) /* we are not yet resumed */ return IRQ_NONE; if ((status & chip->int_sta_mask) == 0) { if (status) { /* ack */ iputdword(chip, chip->int_sta_reg, status); if (! chip->buggy_irq) status = 0; } return IRQ_RETVAL(status); } for (i = 0; i < chip->bdbars_count; i++) { ichdev = &chip->ichd[i]; if (status & ichdev->int_sta_mask) snd_intel8x0_update(chip, ichdev); } /* ack them */ iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask); return IRQ_HANDLED;}/* * PCM part */static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd){ struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); unsigned char val = 0; unsigned long port = ichdev->reg_offset; switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: ichdev->suspended = 0; /* fallthru */ case SNDRV_PCM_TRIGGER_START: val = ICH_IOCE | ICH_STARTBM; break; case SNDRV_PCM_TRIGGER_SUSPEND: ichdev->suspended = 1; /* fallthru */ case SNDRV_PCM_TRIGGER_STOP: val = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: val = ICH_IOCE; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: val = ICH_IOCE | ICH_STARTBM; break; default: return -EINVAL; } iputbyte(chip, port + ICH_REG_OFF_CR, val); if (cmd == SNDRV_PCM_TRIGGER_STOP) { /* wait until DMA stopped */ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ; /* reset whole DMA things */ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); } return 0;}static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd){ struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); unsigned long port = ichdev->reg_offset; static int fiforeg[] = { ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3) }; unsigned int val, fifo; val = igetdword(chip, ICHREG(ALI_DMACR)); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: ichdev->suspended = 0; /* fallthru */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* clear FIFO for synchronization of channels */ fifo = igetdword(chip, fiforeg[ichdev->ali_slot / 4]); fifo &= ~(0xff << (ichdev->ali_slot % 4)); fifo |= 0x83 << (ichdev->ali_slot % 4); iputdword(chip, fiforeg[ichdev->ali_slot / 4], fifo); } iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); val &= ~(1 << (ichdev->ali_slot + 16)); /* clear PAUSE flag */ /* start DMA */ iputdword(chip, ICHREG(ALI_DMACR), val | (1 << ichdev->ali_slot)); break; case SNDRV_PCM_TRIGGER_SUSPEND: ichdev->suspended = 1; /* fallthru */ case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* pause */ iputdword(chip, ICHREG(ALI_DMACR), val | (1 << (ichdev->ali_slot + 16))); iputbyte(chip, port + ICH_REG_OFF_CR, 0); while (igetbyte(chip, port + ICH_REG_OFF_CR)) ; if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) break; /* reset whole DMA things */ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); /* clear interrupts */ iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e); iputdword(chip, ICHREG(ALI_INTERRUPTSR), igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ichdev->int_sta_mask); break; default: return -EINVAL; } return 0;}static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); struct snd_pcm_runtime *runtime = substream->runtime;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -