📄 i810_audio.c
字号:
if(card->pci_id == PCI_DEVICE_ID_SI_7012) I810_IOWRITEB( I810_IOREADB(card, PI_PICB), card, PI_PICB ); else I810_IOWRITEB( I810_IOREADB(card, PI_SR), card, PI_SR ); I810_IOWRITEL( I810_IOREADL(card, GLOB_STA) & INT_PI, card, GLOB_STA);}static void stop_adc(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __stop_adc(state); spin_unlock_irqrestore(&card->lock, flags);}static inline void __start_adc(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { dmabuf->enable |= ADC_RUNNING; // Interrupt enable, LVI enable, DMA enable I810_IOWRITEB(0x10 | 0x04 | 0x01, state->card, PI_CR); }}static void start_adc(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __start_adc(state); spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */static inline void __stop_dac(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; dmabuf->enable &= ~DAC_RUNNING; I810_IOWRITEB(0, card, PO_CR); // wait for the card to acknowledge shutdown while( I810_IOREADB(card, PO_CR) != 0 ) ; // now clear any latent interrupt bits (like the halt bit) if(card->pci_id == PCI_DEVICE_ID_SI_7012) I810_IOWRITEB( I810_IOREADB(card, PO_PICB), card, PO_PICB ); else I810_IOWRITEB( I810_IOREADB(card, PO_SR), card, PO_SR ); I810_IOWRITEL( I810_IOREADL(card, GLOB_STA) & INT_PO, card, GLOB_STA);}static void stop_dac(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __stop_dac(state); spin_unlock_irqrestore(&card->lock, flags);} static inline void __start_dac(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { dmabuf->enable |= DAC_RUNNING; // Interrupt enable, LVI enable, DMA enable I810_IOWRITEB(0x10 | 0x04 | 0x01, state->card, PO_CR); }}static void start_dac(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __start_dac(state); spin_unlock_irqrestore(&card->lock, flags);}#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1/* allocate DMA buffer, playback and recording buffer should be allocated separately */static int alloc_dmabuf(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; void *rawbuf= NULL; int order, size; struct page *page, *pend; /* If we don't have any oss frag params, then use our default ones */ if(dmabuf->ossmaxfrags == 0) dmabuf->ossmaxfrags = 4; if(dmabuf->ossfragsize == 0) dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags; size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) return 0; /* alloc enough to satisfy the oss params */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { if ( (PAGE_SIZE<<order) > size ) continue; if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, PAGE_SIZE << order, &dmabuf->dma_handle))) break; } if (!rawbuf) return -ENOMEM;#ifdef DEBUG printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf);#endif dmabuf->ready = dmabuf->mapped = 0; dmabuf->rawbuf = rawbuf; dmabuf->buforder = order; /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(rawbuf); page <= pend; page++) SetPageReserved(page); return 0;}/* free DMA buffer */static void dealloc_dmabuf(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct page *page, *pend; if (dmabuf->rawbuf) { /* undo marking the pages as reserved */ pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) ClearPageReserved(page); pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder, dmabuf->rawbuf, dmabuf->dma_handle); } dmabuf->rawbuf = NULL; dmabuf->mapped = dmabuf->ready = 0;}static int prog_dmabuf(struct i810_state *state, unsigned rec){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_channel *c; struct sg_item *sg; unsigned long flags; int ret; unsigned fragint; int i; spin_lock_irqsave(&state->card->lock, flags); if(dmabuf->enable & DAC_RUNNING) __stop_dac(state); if(dmabuf->enable & ADC_RUNNING) __stop_adc(state); dmabuf->total_bytes = 0; dmabuf->count = dmabuf->error = 0; dmabuf->swptr = dmabuf->hwptr = 0; spin_unlock_irqrestore(&state->card->lock, flags); /* allocate DMA buffer, let alloc_dmabuf determine if we are already * allocated well enough or if we should replace the current buffer * (assuming one is already allocated, if it isn't, then allocate it). */ if ((ret = alloc_dmabuf(state))) return ret; /* FIXME: figure out all this OSS fragment stuff */ /* I did, it now does what it should according to the OSS API. DL */ /* We may not have realloced our dmabuf, but the fragment size to * fragment number ratio may have changed, so go ahead and reprogram * things */ dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; dmabuf->numfrag = SG_LEN; dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag; dmabuf->fragsamples = dmabuf->fragsize >> 1; dmabuf->fragshift = ffs(dmabuf->fragsize) - 1; dmabuf->userfragsize = dmabuf->ossfragsize; dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize; memset(dmabuf->rawbuf, 0, dmabuf->dmasize); if(dmabuf->ossmaxfrags == 4) { fragint = 8; } else if (dmabuf->ossmaxfrags == 8) { fragint = 4; } else if (dmabuf->ossmaxfrags == 16) { fragint = 2; } else { fragint = 1; } /* * Now set up the ring */ if(dmabuf->read_channel) c = dmabuf->read_channel; else c = dmabuf->write_channel; while(c != NULL) { sg=&c->sg[0]; /* * Load up 32 sg entries and take an interrupt at half * way (we might want more interrupts later..) */ for(i=0;i<dmabuf->numfrag;i++) { sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i; // the card will always be doing 16bit stereo sg->control=dmabuf->fragsamples; if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) sg->control <<= 1; sg->control|=CON_BUFPAD; // set us up to get IOC interrupts as often as needed to // satisfy numfrag requirements, no more if( ((i+1) % fragint) == 0) { sg->control|=CON_IOC; } sg++; } spin_lock_irqsave(&state->card->lock, flags); I810_IOWRITEB(2, state->card, c->port+OFF_CR); /* reset DMA machine */ while( I810_IOREADB(state->card, c->port+OFF_CR) & 0x02 ) ; I810_IOWRITEL((u32)state->card->chandma + c->num*sizeof(struct i810_channel), state->card, c->port+OFF_BDBAR); CIV_TO_LVI(state->card, c->port, 0); spin_unlock_irqrestore(&state->card->lock, flags); if(c != dmabuf->write_channel) c = dmabuf->write_channel; else c = NULL; } /* set the ready flag for the dma buffer */ dmabuf->ready = 1;#ifdef DEBUG printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " "fragsize = %d dmasize = %d\n", dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, dmabuf->dmasize);#endif return 0;}static void __i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; int x, port; int trigger; int count, fragsize; void (*start)(struct i810_state *); count = dmabuf->count; if (rec) { port = dmabuf->read_channel->port; trigger = PCM_ENABLE_INPUT; start = __start_adc; count = dmabuf->dmasize - count; } else { port = dmabuf->write_channel->port; trigger = PCM_ENABLE_OUTPUT; start = __start_dac; } /* Do not process partial fragments. */ fragsize = dmabuf->fragsize; if (count < fragsize) return; /* if we are currently stopped, then our CIV is actually set to our * *last* sg segment and we are ready to wrap to the next. However, * if we set our LVI to the last sg segment, then it won't wrap to * the next sg segment, it won't even get a start. So, instead, when * we are stopped, we set both the LVI value and also we increment * the CIV value to the next sg segment to be played so that when * we call start, things will operate properly. Since the CIV can't * be written to directly for this purpose, we set the LVI to CIV + 1 * temporarily. Once the engine has started we set the LVI to its * final value. */ if (!dmabuf->enable && dmabuf->ready) { if (!(dmabuf->trigger & trigger)) return; CIV_TO_LVI(state->card, port, 1); start(state); while (!(I810_IOREADB(state->card, port + OFF_CR) & ((1<<4) | (1<<2)))) ; } /* MASKP2(swptr, fragsize) - 1 is the tail of our transfer */ x = MODULOP2(MASKP2(dmabuf->swptr, fragsize) - 1, dmabuf->dmasize); x >>= dmabuf->fragshift; I810_IOWRITEB(x, state->card, port + OFF_LVI);}static void i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; if(!dmabuf->ready) return; spin_lock_irqsave(&state->card->lock, flags); __i810_update_lvi(state, rec); spin_unlock_irqrestore(&state->card->lock, flags);}/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */static void i810_update_ptr(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned hwptr; unsigned fragmask, dmamask; int diff; fragmask = MASKP2(~0, dmabuf->fragsize); dmamask = MODULOP2(~0, dmabuf->dmasize); /* error handling and process wake up for ADC */ if (dmabuf->enable == ADC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 1) & fragmask; diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count += diff; if (dmabuf->count > dmabuf->dmasize) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a read */ /* only give an error if we went past the */ /* last valid sg entry */ if (GET_CIV(state->card, PI_BASE) != GET_LVI(state->card, PI_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on read\n"); dmabuf->error++; } } if (diff) wake_up(&dmabuf->wait); } /* error handling and process wake up for DAC */ if (dmabuf->enable == DAC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 0) & fragmask; diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count -= diff; if (dmabuf->count < 0) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a write */ /* only give an error if we went past the */ /* last valid sg entry */ if (GET_CIV(state->card, PO_BASE) != GET_LVI(state->card, PO_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); printk("i810_audio: CIV %d, LVI %d, hwptr %x, " "count %d\n", GET_CIV(state->card, PO_BASE), GET_LVI(state->card, PO_BASE), dmabuf->hwptr, dmabuf->count); dmabuf->error++; } } if (diff) wake_up(&dmabuf->wait); }}static inline int i810_get_free_write_space(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int free; i810_update_ptr(state); // catch underruns during playback if (dmabuf->count < 0) { dmabuf->count = 0; dmabuf->swptr = dmabuf->hwptr; } free = dmabuf->dmasize - dmabuf->count; if(free < 0) return(0); return(free);}static inline int i810_get_available_read_data(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int avail; i810_update_ptr(state); // catch overruns during record if (dmabuf->count > dmabuf->dmasize) { dmabuf->count = dmabuf->dmasize; dmabuf->swptr = dmabuf->hwptr; } avail = dmabuf->count; if(avail < 0) return(0); return(avail);}static inline void fill_partial_frag(struct dmabuf *dmabuf){ unsigned fragsize; unsigned swptr, len; fragsize = dmabuf->fragsize; swptr = dmabuf->swptr; len = fragsize - MODULOP2(dmabuf->swptr, fragsize); if (len == fragsize) return; memset(dmabuf->rawbuf + swptr, '\0', len); dmabuf->swptr = MODULOP2(swptr + len, dmabuf->dmasize); dmabuf->count += len;}static int drain_dac(struct i810_state *state, int signals_allowed){ DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -