📄 nec_vrc5477.c
字号:
u32 dmaLength; u32 temp; spin_lock_irqsave(&s->lock, flags); if (!db->stopped) { spin_unlock_irqrestore(&s->lock, flags); return; } /* we should at least have some free space in the buffer */ MIPS_ASSERT(db->count < db->fragTotalSize - db->fragSize * 2); /* clear pending ones */ outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, s->io + VRC5477_INT_CLR); /* enable interrupts */ temp = inl(s->io + VRC5477_INT_MASK); temp |= VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; outl(temp, s->io + VRC5477_INT_MASK); /* setup dma base addr */ outl(db->lbufDma + db->nextIn, s->io + VRC5477_ADC1_BADDR); outl(db->rbufDma + db->nextIn, s->io + VRC5477_ADC2_BADDR); /* setup dma length */ dmaLength = db->fragSize >> 4; outl(dmaLength, s->io + VRC5477_ADC1L); outl(dmaLength, s->io + VRC5477_ADC2L); /* activate dma */ outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC1_CTRL); outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC2_CTRL); /* enable adc slots */ temp = inl(s->io + VRC5477_CTRL); temp |= (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); outl (temp, s->io + VRC5477_CTRL); /* it is time to setup next dma transfer */ temp = db->nextIn + db->fragSize; if (temp >= db->fragTotalSize) { MIPS_ASSERT(temp == db->fragTotalSize); temp = 0; } outl(db->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); outl(db->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); db->stopped = 0; spin_unlock_irqrestore(&s->lock, flags);} /* --------------------------------------------------------------------- */#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1extern inline void dealloc_dmabuf(struct vrc5477_ac97_state *s, struct dmabuf *db){ if (db->lbuf) { MIPS_ASSERT(db->rbuf); pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, db->lbuf, db->lbufDma); pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, db->rbuf, db->rbufDma); db->lbuf = db->rbuf = NULL; } db->nextIn = db->nextOut = 0; db->ready = 0;}static int prog_dmabuf(struct vrc5477_ac97_state *s, struct dmabuf *db, unsigned rate){ int order; unsigned bufsize; if (!db->lbuf) { MIPS_ASSERT(!db->rbuf); db->ready = 0; for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { db->lbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->lbufDma); db->rbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->rbufDma); if (db->lbuf && db->rbuf) break; if (db->lbuf) { MIPS_ASSERT(!db->rbuf); pci_free_consistent(s->dev, PAGE_SIZE << order, db->lbuf, db->lbufDma); } } if (!db->lbuf) { MIPS_ASSERT(!db->rbuf); return -ENOMEM; } db->bufOrder = order; } db->count = 0; db->nextIn = db->nextOut = 0; bufsize = PAGE_SIZE << db->bufOrder; db->fragShift = ld2(rate * 2 / 100); if (db->fragShift < 4) db->fragShift = 4; db->numFrag = bufsize >> db->fragShift; while (db->numFrag < 4 && db->fragShift > 4) { db->fragShift--; db->numFrag = bufsize >> db->fragShift; } db->fragSize = 1 << db->fragShift; db->fragTotalSize = db->numFrag << db->fragShift; memset(db->lbuf, 0, db->fragTotalSize); memset(db->rbuf, 0, db->fragTotalSize); db->ready = 1; return 0;}extern inline int prog_dmabuf_adc(struct vrc5477_ac97_state *s){ stop_adc(s); return prog_dmabuf(s, &s->dma_adc, s->adcRate);}extern inline int prog_dmabuf_dac(struct vrc5477_ac97_state *s){ stop_dac(s); return prog_dmabuf(s, &s->dma_dac, s->dacRate);}/* --------------------------------------------------------------------- *//* hold spinlock for the following! */static inline void vrc5477_ac97_adc_interrupt(struct vrc5477_ac97_state *s){ struct dmabuf* adc = &s->dma_adc; unsigned temp; /* we need two frags avaiable because one is already being used * and the other will be used when next interrupt happens. */ if (adc->count >= adc->fragTotalSize - adc->fragSize) { stop_adc(s); adc->error++; printk(KERN_INFO PFX "adc overrun\n"); return; } /* set the base addr for next DMA transfer */ temp = adc->nextIn + 2*adc->fragSize; if (temp >= adc->fragTotalSize) { MIPS_ASSERT( (temp == adc->fragTotalSize) || (temp == adc->fragTotalSize + adc->fragSize) ); temp -= adc->fragTotalSize; } outl(adc->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); outl(adc->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); /* adjust nextIn */ adc->nextIn += adc->fragSize; if (adc->nextIn >= adc->fragTotalSize) { MIPS_ASSERT(adc->nextIn == adc->fragTotalSize); adc->nextIn = 0; } /* adjust count */ adc->count += adc->fragSize; /* wake up anybody listening */ if (waitqueue_active(&adc->wait)) { wake_up_interruptible(&adc->wait); } }static inline void vrc5477_ac97_dac_interrupt(struct vrc5477_ac97_state *s){ struct dmabuf* dac = &s->dma_dac; unsigned temp; /* next DMA transfer should already started */ MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); /* let us set for next next DMA transfer */ temp = dac->nextOut + dac->fragSize*2; if (temp >= dac->fragTotalSize) { MIPS_ASSERT( (temp == dac->fragTotalSize) || (temp == dac->fragTotalSize + dac->fragSize) ); temp -= dac->fragTotalSize; } outl(dac->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); if (s->dacChannels == 1) { outl(dac->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); } else { outl(dac->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); }#if defined(VRC5477_AC97_VERBOSE_DEBUG) if (*(u16*)(dac->lbuf + dac->nextOut) != outTicket) { printk("assert fail: - %d vs %d\n", *(u16*)(dac->lbuf + dac->nextOut), outTicket); MIPS_ASSERT(1 == 0); }#endif /* adjust nextOut pointer */ dac->nextOut += dac->fragSize; if (dac->nextOut >= dac->fragTotalSize) { MIPS_ASSERT(dac->nextOut == dac->fragTotalSize); dac->nextOut = 0; } /* adjust count */ dac->count -= dac->fragSize; if (dac->count <=0 ) { MIPS_ASSERT(dac->count == 0); MIPS_ASSERT(dac->nextIn == dac->nextOut); /* buffer under run */ stop_dac(s); }#if defined(VRC5477_AC97_VERBOSE_DEBUG) if (dac->count) { outTicket ++; MIPS_ASSERT(*(u16*)(dac->lbuf + dac->nextOut) == outTicket); }#endif /* we cannot have both under run and someone is waiting on us */ MIPS_ASSERT(! (waitqueue_active(&dac->wait) && (dac->count <= 0)) ); /* wake up anybody listening */ if (waitqueue_active(&dac->wait)) wake_up_interruptible(&dac->wait);}static void vrc5477_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)dev_id; u32 irqStatus; u32 adcInterrupts, dacInterrupts; spin_lock(&s->lock); /* get irqStatus and clear the detected ones */ irqStatus = inl(s->io + VRC5477_INT_STATUS); outl(irqStatus, s->io + VRC5477_INT_CLR); /* let us see what we get */ dacInterrupts = VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; adcInterrupts = VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; if (irqStatus & dacInterrupts) { /* we should get both interrupts, but just in case ... */ if (irqStatus & VRC5477_INT_MASK_DAC1END) { vrc5477_ac97_dac_interrupt(s); } if ( (irqStatus & dacInterrupts) != dacInterrupts ) { printk(KERN_WARNING "vrc5477_ac97 : dac interrupts not in sync!!!\n"); stop_dac(s); start_dac(s); } } else if (irqStatus & adcInterrupts) { /* we should get both interrupts, but just in case ... */ if(irqStatus & VRC5477_INT_MASK_ADC1END) { vrc5477_ac97_adc_interrupt(s); } if ( (irqStatus & adcInterrupts) != adcInterrupts ) { printk(KERN_WARNING "vrc5477_ac97 : adc interrupts not in sync!!!\n"); stop_adc(s); start_adc(s); } } spin_unlock(&s->lock);}/* --------------------------------------------------------------------- */static loff_t vrc5477_ac97_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}static int vrc5477_ac97_open_mixdev(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); struct list_head *list; struct vrc5477_ac97_state *s; for (list = devs.next; ; list = list->next) { if (list == &devs) return -ENODEV; s = list_entry(list, struct vrc5477_ac97_state, devs); if (s->codec.dev_mixer == minor) break; } file->private_data = s; return 0;}static int vrc5477_ac97_release_mixdev(struct inode *inode, struct file *file){ return 0;}static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg){ return codec->mixer_ioctl(codec, cmd, arg);}static int vrc5477_ac97_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; struct ac97_codec *codec = &s->codec; return mixdev_ioctl(codec, cmd, arg);}static /*const*/ struct file_operations vrc5477_ac97_mixer_fops = { owner: THIS_MODULE, llseek: vrc5477_ac97_llseek, ioctl: vrc5477_ac97_ioctl_mixdev, open: vrc5477_ac97_open_mixdev, release: vrc5477_ac97_release_mixdev,};/* --------------------------------------------------------------------- */static int drain_dac(struct vrc5477_ac97_state *s, int nonblock){ unsigned long flags; int count, tmo; if (!s->dma_dac.ready) return 0; for (;;) { spin_lock_irqsave(&s->lock, flags); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); if (count <= 0) break; if (signal_pending(current)) break; if (nonblock) return -EBUSY; tmo = 1000 * count / s->dacRate / 2; vrc5477_ac97_delay(tmo); } if (signal_pending(current)) return -ERESTARTSYS; return 0;}/* --------------------------------------------------------------------- */static int inline copy_two_channel_adc_to_user(struct vrc5477_ac97_state *s, char *buffer, int copyCount){ struct dmabuf *db = &s->dma_adc; int bufStart = db->nextOut; for (; copyCount > 0; ) { int i; int count = copyCount; if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; for (i=0; i< count/2; i++) { s->workBuf[i].lchannel = *(u16*)(db->lbuf + bufStart + i*2); s->workBuf[i].rchannel = *(u16*)(db->rbuf + bufStart + i*2); } if (copy_to_user(buffer, s->workBuf, count*2)) { return -1; } copyCount -= count; bufStart += count; MIPS_ASSERT(bufStart <= db->fragTotalSize); buffer += count *2; } return 0;}/* return the total bytes that is copied */static int inline copy_adc_to_user(struct vrc5477_ac97_state *s, char * buffer, size_t count, int avail){ struct dmabuf *db = &s->dma_adc; int copyCount=0; int copyFragCount=0; int totalCopyCount = 0; int totalCopyFragCount = 0; unsigned long flags; /* adjust count to signel channel byte count */ count >>= s->adcChannels - 1; /* we may have to "copy" twice as ring buffer wraps around */ for (; (avail > 0) && (count > 0); ) { /* determine max possible copy count for single channel */ copyCount = count; if (copyCount > avail) { copyCount = avail; } if (copyCount + db->nextOut > db->fragTotalSize) { copyCount = db->fragTotalSize - db->nextOut; MIPS_ASSERT((copyCount % db->fragSize) == 0); } copyFragCount = (copyCount-1) >> db->fragShift; copyFragCount = (copyFragCount+1) << db->fragShift; MIPS_ASSERT(copyFragCount >= copyCount); /* we copy differently based on adc channels */ if (s->adcChannels == 1) { if (copy_to_user(buffer, db->lbuf + db->nextOut, copyCount)) return -1; } else { /* *sigh* we have to mix two streams into one */ if (copy_two_channel_adc_to_user(s, buffer, copyCount)) return -1; } count -= copyCount; totalCopyCount += copyCount; avail -= copyFragCount; totalCopyFragCount += copyFragCount; buffer += copyCount << (s->adcChannels-1); db->nextOut += copyFragCount; if (db->nextOut >= db->fragTotalSize) { MIPS_ASSERT(db->nextOut == db->fragTotalSize); db->nextOut = 0; } MIPS_ASSERT((copyFragCount % db->fragSize) == 0); MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); } spin_lock_irqsave(&s->lock, flags); db->count -= totalCopyFragCount; spin_unlock_irqrestore(&s->lock, flags); return totalCopyCount << (s->adcChannels-1);}static ssize_t vrc5477_ac97_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; struct dmabuf *db = &s->dma_adc; ssize_t ret = 0; unsigned long flags; int copyCount; size_t avail; if (ppos != &file->f_pos) return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; MIPS_ASSERT(db->ready); while (count > 0) { // wait for samples in capture buffer do { spin_lock_irqsave(&s->lock, flags); if (db->stopped) start_adc(s); avail = db->count; spin_unlock_irqrestore(&s->lock, flags); if (avail <= 0) { if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; return ret; } interruptible_sleep_on(&db->wait);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -