From: Steve L. <slo...@us...> - 2002-03-25 22:58:19
|
Update of /cvsroot/linux-mips/linux/drivers/sound In directory usw-pr-cvs1:/tmp/cvs-serv1301/drivers/sound Modified Files: au1000.c Log Message: Some changes to Au1000 sound driver: - Added mutex locks around read/write methods, to prevent simultaneous access on SMP or preemptible kernels. - Removed the counter/pointer fragment aligning at the end of read/write methods, for madplay. - Use coherent DMA. Index: au1000.c =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/sound/au1000.c,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- au1000.c 15 Feb 2002 19:47:28 -0000 1.9 +++ au1000.c 25 Mar 2002 22:58:15 -0000 1.10 @@ -42,6 +42,13 @@ * * Revision history * 06.27.2001 Initial version + * 03.20.2002 Added mutex locks around read/write methods, to prevent + * simultaneous access on SMP or preemptible kernels. Also + * removed the counter/pointer fragment aligning at the end + * of read/write methods [stevel]. + * 03.21.2002 Add support for coherent DMA on the audio read/write DMA + * channels [stevel]. + * */ #include <linux/version.h> #include <linux/module.h> @@ -73,6 +80,7 @@ #define AU1000_DEBUG #undef AU1000_VERBOSE_DEBUG +#define USE_COHERENT_DMA #define AU1000_MODULE_NAME "Au1000 audio" #define PFX AU1000_MODULE_NAME @@ -110,12 +118,13 @@ #endif /* AU1000_DEBUG */ struct ac97_codec codec; - unsigned codec_base_caps; // AC'97 reg 00h, "Reset Register" + unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register" unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID" int no_vra; // do not use VRA spinlock_t lock; struct semaphore open_sem; + struct semaphore sem; mode_t open_mode; wait_queue_head_t open_wait; @@ -136,7 +145,7 @@ unsigned numfrag; // # of DMA fragments in DMA buffer unsigned fragshift; void *nextIn; // ptr to next-in to DMA buffer - void *nextOut; // ptr to next-out from DMA buffer + void *nextOut;// ptr to next-out from DMA buffer int count; // current byte count in DMA buffer unsigned total_bytes; // total bytes written or read unsigned error; // over/underrun @@ -185,6 +194,34 @@ } +#ifdef USE_COHERENT_DMA +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + void* ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, + get_order(size)); + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + free_pages((unsigned long)va, get_order(size)); +} +#else +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + return pci_alloc_consistent(NULL, size, dma_handle); +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + pci_free_consistent(NULL, size, va, dma_handle); +} +#endif + /* --------------------------------------------------------------------- */ static void au1000_delay(int msec) @@ -304,6 +341,7 @@ /* --------------------------------------------------------------------- */ +/* stop the ADC before calling */ static void set_adc_rate(struct au1000_state *s, unsigned rate) { struct dmabuf *adc = &s->dma_adc; @@ -348,6 +386,7 @@ dac->sample_rate = dac_rate; } +/* stop the DAC before calling */ static void set_dac_rate(struct au1000_state *s, unsigned rate) { struct dmabuf *dac = &s->dma_dac; @@ -561,8 +600,7 @@ (PAGE_SIZE << db->buforder) - 1); for (page = virt_to_page(db->rawbuf); page <= pend; page++) mem_map_unreserve(page); - pci_free_consistent(NULL, PAGE_SIZE << db->buforder, - db->rawbuf, db->dmaaddr); + dma_free(PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); } db->rawbuf = db->nextIn = db->nextOut = NULL; db->mapped = db->ready = 0; @@ -580,10 +618,8 @@ db->ready = db->mapped = 0; for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = - pci_alloc_consistent(NULL, - PAGE_SIZE << order, - &db->dmaaddr))) + if ((db->rawbuf = dma_alloc(PAGE_SIZE << order, + &db->dmaaddr))) break; if (!db->rawbuf) return -ENOMEM; @@ -595,7 +631,7 @@ for (page = virt_to_page(db->rawbuf); page <= pend; page++) mem_map_reserve(page); } - + db->cnt_factor = 1; if (db->sample_size == 8) db->cnt_factor *= 2; @@ -686,6 +722,8 @@ return; } + spin_lock(&s->lock); + if (buff_done != (DMA_D0 | DMA_D1)) { dac->nextOut += dac->dma_fragsize; if (dac->nextOut >= dac->rawbuf + dac->dmasize) @@ -699,9 +737,16 @@ dac->count -= dac->dma_fragsize; dac->total_bytes += dac->dma_fragsize; - if (dac->count <= 0) + if (dac->count <= 0) { +#ifdef AU1000_VERBOSE_DEBUG + dbg("dac underrun"); +#endif + spin_unlock(&s->lock); stop_dac(s); - else if (buff_done == DMA_D0) { + spin_lock(&s->lock); + dac->count = 0; + dac->nextIn = dac->nextOut; + } else if (buff_done == DMA_D0) { clear_dma_done0(dac->dmanr); // clear DMA done bit set_dma_count0(dac->dmanr, dac->dma_fragsize>>1); set_dma_addr0(dac->dmanr, newptr); @@ -714,7 +759,9 @@ } } else { // both done bits set, we missed an interrupt + spin_unlock(&s->lock); stop_dac(s); + spin_lock(&s->lock); dac->nextOut += 2*dac->dma_fragsize; if (dac->nextOut >= dac->rawbuf + dac->dmasize) @@ -723,14 +770,18 @@ dac->count -= 2*dac->dma_fragsize; dac->total_bytes += 2*dac->dma_fragsize; - if (dac->count > 0) + if (dac->count > 0) { + spin_unlock(&s->lock); start_dac(s); + spin_lock(&s->lock); + } } - /* wake up anybody listening */ if (waitqueue_active(&dac->wait)) - wake_up_interruptible(&dac->wait); + wake_up(&dac->wait); + + spin_unlock(&s->lock); } @@ -752,9 +803,12 @@ return; } + spin_lock(&s->lock); + if (buff_done != (DMA_D0 | DMA_D1)) { if (adc->count + adc->dma_fragsize > adc->dmasize) { // Overrun. Stop ADC and log the error + spin_unlock(&s->lock); stop_adc(s); adc->error++; err("adc overrun"); @@ -786,12 +840,15 @@ } } else { // both done bits set, we missed an interrupt + spin_unlock(&s->lock); stop_adc(s); - + spin_lock(&s->lock); + if (adc->count + 2*adc->dma_fragsize > adc->dmasize) { // Overrun. Log the error adc->error++; err("adc overrun"); + spin_unlock(&s->lock); return; } @@ -802,12 +859,16 @@ adc->count += 2*adc->dma_fragsize; adc->total_bytes += 2*adc->dma_fragsize; + spin_unlock(&s->lock); start_adc(s); + spin_lock(&s->lock); } /* wake up anybody listening */ if (waitqueue_active(&adc->wait)) - wake_up_interruptible(&adc->wait); + wake_up(&adc->wait); + + spin_unlock(&s->lock); } /* --------------------------------------------------------------------- */ @@ -1050,9 +1111,10 @@ { struct au1000_state *s = (struct au1000_state *)file->private_data; struct dmabuf *db = &s->dma_adc; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; - int cnt, remainder, usercnt, avail; + int cnt, usercnt, avail; if (ppos != &file->f_pos) return -ESPIPE; @@ -1064,6 +1126,9 @@ count *= db->cnt_factor; + down(&s->sem); + add_wait_queue(&db->wait, &wait); + while (count > 0) { // wait for samples in ADC dma buffer do { @@ -1071,19 +1136,23 @@ start_adc(s); spin_lock_irqsave(&s->lock, flags); avail = db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (avail <= 0) { if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto out; } - interruptible_sleep_on(&db->wait); + up(&s->sem); + schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; - return ret; + goto out2; } + down(&s->sem); } } while (avail <= 0); @@ -1093,16 +1162,15 @@ avail : count, 1)) < 0) { if (!ret) ret = -EFAULT; - return ret; + goto out; } spin_lock_irqsave(&s->lock, flags); db->count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - db->nextOut += cnt; if (db->nextOut >= db->rawbuf + db->dmasize) db->nextOut -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); count -= cnt; usercnt = cnt / db->cnt_factor; @@ -1110,25 +1178,11 @@ ret += usercnt; } // while (count > 0) - /* - * See if the dma buffer count after this read call is - * aligned on a dma_fragsize boundary. If not, read from - * buffer until we reach a boundary, and let's hope this - * is just the last remainder of an audio record. If not - * it means the user is not reading in fragsize chunks, in - * which case it's his/her fault that there are audio gaps - * in their record. - */ - spin_lock_irqsave(&s->lock, flags); - remainder = db->count % db->dma_fragsize; - if (remainder) { - db->nextOut += remainder; - if (db->nextOut >= db->rawbuf + db->dmasize) - db->nextOut -= db->dmasize; - db->count -= remainder; - } - spin_unlock_irqrestore(&s->lock, flags); - +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1137,10 +1191,15 @@ { struct au1000_state *s = (struct au1000_state *)file->private_data; struct dmabuf *db = &s->dma_dac; + DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; unsigned long flags; - int cnt, remainder, usercnt, avail; + int cnt, usercnt, avail; +#ifdef AU1000_VERBOSE_DEBUG + dbg("write: count=%d", count); +#endif + if (ppos != &file->f_pos) return -ESPIPE; if (db->mapped) @@ -1150,45 +1209,51 @@ count *= db->cnt_factor; + down(&s->sem); + add_wait_queue(&db->wait, &wait); + while (count > 0) { // wait for space in playback buffer do { spin_lock_irqsave(&s->lock, flags); avail = (int) db->dmasize - db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&s->lock, flags); if (avail <= 0) { if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto out; } - interruptible_sleep_on(&db->wait); + up(&s->sem); + schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; - return ret; + goto out2; } + down(&s->sem); } } while (avail <= 0); - // copy to nextIn + // copy from user to nextIn if ((cnt = copy_dmabuf_user(db, (char *) buffer, count > avail ? avail : count, 0)) < 0) { if (!ret) ret = -EFAULT; - return ret; + goto out; } spin_lock_irqsave(&s->lock, flags); db->count += cnt; - spin_unlock_irqrestore(&s->lock, flags); - if (db->stopped) - start_dac(s); - db->nextIn += cnt; if (db->nextIn >= db->rawbuf + db->dmasize) db->nextIn -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + if (db->stopped) + start_dac(s); count -= cnt; usercnt = cnt / db->cnt_factor; @@ -1196,27 +1261,11 @@ ret += usercnt; } // while (count > 0) - /* - * See if the dma buffer count after this write call is - * aligned on a dma_fragsize boundary. If not, fill buffer - * with silence to the next boundary, and let's hope this - * is just the last remainder of an audio playback. If not - * it means the user is not sending us fragsize chunks, in - * which case it's his/her fault that there are audio gaps - * in their playback. - */ - spin_lock_irqsave(&s->lock, flags); - remainder = db->count % db->dma_fragsize; - if (remainder) { - int fill_cnt = db->dma_fragsize - remainder; - memset(db->nextIn, 0, fill_cnt); - db->nextIn += fill_cnt; - if (db->nextIn >= db->rawbuf + db->dmasize) - db->nextIn -= db->dmasize; - db->count += fill_cnt; - } - spin_unlock_irqrestore(&s->lock, flags); - +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1241,6 +1290,7 @@ } spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize) mask |= POLLIN | POLLRDNORM; @@ -1265,35 +1315,40 @@ struct au1000_state *s = (struct au1000_state *)file->private_data; struct dmabuf *db; unsigned long size; - + int ret = 0; + dbg(__FUNCTION__); lock_kernel(); + down(&s->sem); if (vma->vm_flags & VM_WRITE) db = &s->dma_dac; else if (vma->vm_flags & VM_READ) db = &s->dma_adc; else { - unlock_kernel(); - return -EINVAL; + ret = -EINVAL; + goto out; } if (vma->vm_pgoff != 0) { - unlock_kernel(); - return -EINVAL; + ret = -EINVAL; + goto out; } size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << db->buforder)) { - unlock_kernel(); - return -EINVAL; + ret = -EINVAL; + goto out; } if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; + ret = -EAGAIN; + goto out; } + vma->vm_flags &= ~VM_IO; db->mapped = 1; +out: + up(&s->sem); unlock_kernel(); - return 0; + return ret; } @@ -1368,9 +1423,9 @@ break; } if (count < sizeof(ioctl_str) / sizeof(ioctl_str[0])) - dbg("ioctl %s, arg=0x%x", ioctl_str[count].str, arg); + dbg("ioctl %s, arg=0x%lx", ioctl_str[count].str, arg); else - dbg("ioctl 0x%s unknown, arg=0x%x", cmd, arg); + dbg("ioctl 0x%x unknown, arg=0x%lx", cmd, arg); #endif switch (cmd) { @@ -1609,6 +1664,9 @@ s->dma_dac.cnt_factor; abinfo.fragstotal = s->dma_dac.numfrag; abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; +#ifdef AU1000_VERBOSE_DEBUG + dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments); +#endif return copy_to_user((void *) arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; @@ -1844,6 +1902,7 @@ s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&s->open_sem); + init_MUTEX(&s->sem); return 0; } @@ -1976,7 +2035,20 @@ s->dma_dac.irq = get_dma_done_irq(s->dma_dac.dmanr); s->dma_adc.irq = get_dma_done_irq(s->dma_adc.dmanr); - +#ifdef USE_COHERENT_DMA + // enable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC); +#else + // disable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) | DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) | DMA_NC); +#endif + if (request_irq(s->dma_dac.irq, dac_dma_interrupt, SA_INTERRUPT, "audio DAC", s)) { err("Can't get DAC irq #%d", s->dma_dac.irq); |