au1550_ac97.c
来自「linux 内核源代码」· C语言 代码 · 共 2,130 行 · 第 1/4 页
C
2,130 行
ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3); ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4); } au_writel(ac97_config, PSC_AC97CFG); au_sync(); ac97_config |= PSC_AC97CFG_DE_ENABLE; au_writel(ac97_config, PSC_AC97CFG); au_sync(); /* Wait for Device ready. */ do { stat = au_readl(PSC_AC97STAT); au_sync(); } while ((stat & PSC_AC97STAT_DR) == 0);}static voidset_recv_slots(int num_channels){ u32 ac97_config, stat; ac97_config = au_readl(PSC_AC97CFG); au_sync(); ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE); au_writel(ac97_config, PSC_AC97CFG); au_sync(); /* Always enable slots 3 and 4 (stereo). Slot 6 is * optional Mic ADC, which we don't support yet. */ ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3); ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4); au_writel(ac97_config, PSC_AC97CFG); au_sync(); ac97_config |= PSC_AC97CFG_DE_ENABLE; au_writel(ac97_config, PSC_AC97CFG); au_sync(); /* Wait for Device ready. */ do { stat = au_readl(PSC_AC97STAT); au_sync(); } while ((stat & PSC_AC97STAT_DR) == 0);}/* Hold spinlock for both start_dac() and start_adc() calls */static voidstart_dac(struct au1550_state *s){ struct dmabuf *db = &s->dma_dac; if (!db->stopped) return; set_xmit_slots(db->num_channels); au_writel(PSC_AC97PCR_TC, PSC_AC97PCR); au_sync(); au_writel(PSC_AC97PCR_TS, PSC_AC97PCR); au_sync(); au1xxx_dbdma_start(db->dmanr); db->stopped = 0;}static voidstart_adc(struct au1550_state *s){ struct dmabuf *db = &s->dma_adc; int i; if (!db->stopped) return; /* Put two buffers on the ring to get things started. */ for (i=0; i<2; i++) { au1xxx_dbdma_put_dest(db->dmanr, db->nextIn, db->dma_fragsize); db->nextIn += db->dma_fragsize; if (db->nextIn >= db->rawbuf + db->dmasize) db->nextIn -= db->dmasize; } set_recv_slots(db->num_channels); au1xxx_dbdma_start(db->dmanr); au_writel(PSC_AC97PCR_RC, PSC_AC97PCR); au_sync(); au_writel(PSC_AC97PCR_RS, PSC_AC97PCR); au_sync(); db->stopped = 0;}static intprog_dmabuf(struct au1550_state *s, struct dmabuf *db){ unsigned user_bytes_per_sec; unsigned bufs; unsigned rate = db->sample_rate; if (!db->rawbuf) { db->ready = db->mapped = 0; db->buforder = 5; /* 32 * PAGE_SIZE */ db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL); if (!db->rawbuf) return -ENOMEM; } db->cnt_factor = 1; if (db->sample_size == 8) db->cnt_factor *= 2; if (db->num_channels == 1) db->cnt_factor *= 2; db->cnt_factor *= db->src_factor; db->count = 0; db->dma_qcount = 0; db->nextIn = db->nextOut = db->rawbuf; db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels; db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ? 2 : db->num_channels); user_bytes_per_sec = rate * db->user_bytes_per_sample; bufs = PAGE_SIZE << db->buforder; if (db->ossfragshift) { if ((1000 << db->ossfragshift) < user_bytes_per_sec) db->fragshift = ld2(user_bytes_per_sec/1000); else db->fragshift = db->ossfragshift; } else { db->fragshift = ld2(user_bytes_per_sec / 100 / (db->subdivision ? db->subdivision : 1)); if (db->fragshift < 3) db->fragshift = 3; } db->fragsize = 1 << db->fragshift; db->dma_fragsize = db->fragsize * db->cnt_factor; db->numfrag = bufs / db->dma_fragsize; while (db->numfrag < 4 && db->fragshift > 3) { db->fragshift--; db->fragsize = 1 << db->fragshift; db->dma_fragsize = db->fragsize * db->cnt_factor; db->numfrag = bufs / db->dma_fragsize; } if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; db->dmasize = db->dma_fragsize * db->numfrag; memset(db->rawbuf, 0, bufs); pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n", rate, db->sample_size, db->num_channels); pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n", db->fragsize, db->cnt_factor, db->dma_fragsize); pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize); db->ready = 1; return 0;}static intprog_dmabuf_adc(struct au1550_state *s){ stop_adc(s); return prog_dmabuf(s, &s->dma_adc);}static intprog_dmabuf_dac(struct au1550_state *s){ stop_dac(s); return prog_dmabuf(s, &s->dma_dac);}static void dac_dma_interrupt(int irq, void *dev_id){ struct au1550_state *s = (struct au1550_state *) dev_id; struct dmabuf *db = &s->dma_dac; u32 ac97c_stat; spin_lock(&s->lock); ac97c_stat = au_readl(PSC_AC97STAT); if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) pr_debug("AC97C status = 0x%08x\n", ac97c_stat); db->dma_qcount--; if (db->count >= db->fragsize) { if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut, db->fragsize) == 0) { err("qcount < 2 and no ring room!"); } db->nextOut += db->fragsize; if (db->nextOut >= db->rawbuf + db->dmasize) db->nextOut -= db->dmasize; db->count -= db->fragsize; db->total_bytes += db->dma_fragsize; db->dma_qcount++; } /* wake up anybody listening */ if (waitqueue_active(&db->wait)) wake_up(&db->wait); spin_unlock(&s->lock);}static void adc_dma_interrupt(int irq, void *dev_id){ struct au1550_state *s = (struct au1550_state *)dev_id; struct dmabuf *dp = &s->dma_adc; u32 obytes; char *obuf; spin_lock(&s->lock); /* Pull the buffer from the dma queue. */ au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes); if ((dp->count + obytes) > dp->dmasize) { /* Overrun. Stop ADC and log the error */ spin_unlock(&s->lock); stop_adc(s); dp->error++; err("adc overrun"); return; } /* Put a new empty buffer on the destination DMA. */ au1xxx_dbdma_put_dest(dp->dmanr, dp->nextIn, dp->dma_fragsize); dp->nextIn += dp->dma_fragsize; if (dp->nextIn >= dp->rawbuf + dp->dmasize) dp->nextIn -= dp->dmasize; dp->count += obytes; dp->total_bytes += obytes; /* wake up anybody listening */ if (waitqueue_active(&dp->wait)) wake_up(&dp->wait); spin_unlock(&s->lock);}static loff_tau1550_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}static intau1550_open_mixdev(struct inode *inode, struct file *file){ file->private_data = &au1550_state; return 0;}static intau1550_release_mixdev(struct inode *inode, struct file *file){ return 0;}static intmixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg){ return codec->mixer_ioctl(codec, cmd, arg);}static intau1550_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct au1550_state *s = (struct au1550_state *)file->private_data; struct ac97_codec *codec = s->codec; return mixdev_ioctl(codec, cmd, arg);}static /*const */ struct file_operations au1550_mixer_fops = { owner:THIS_MODULE, llseek:au1550_llseek, ioctl:au1550_ioctl_mixdev, open:au1550_open_mixdev, release:au1550_release_mixdev,};static intdrain_dac(struct au1550_state *s, int nonblock){ unsigned long flags; int count, tmo; if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) return 0; for (;;) { spin_lock_irqsave(&s->lock, flags); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); if (count <= s->dma_dac.fragsize) break; if (signal_pending(current)) break; if (nonblock) return -EBUSY; tmo = 1000 * count / (s->no_vra ? 48000 : s->dma_dac.sample_rate); tmo /= s->dma_dac.dma_bytes_per_sample; au1550_delay(tmo); } if (signal_pending(current)) return -ERESTARTSYS; return 0;}static inline u8 S16_TO_U8(s16 ch){ return (u8) (ch >> 8) + 0x80;}static inline s16 U8_TO_S16(u8 ch){ return (s16) (ch - 0x80) << 8;}/* * Translates user samples to dma buffer suitable for AC'97 DAC data: * If mono, copy left channel to right channel in dma buffer. * If 8 bit samples, cvt to 16-bit before writing to dma buffer. * If interpolating (no VRA), duplicate every audio frame src_factor times. */static inttranslate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf, int dmacount){ int sample, i; int interp_bytes_per_sample; int num_samples; int mono = (db->num_channels == 1); char usersample[12]; s16 ch, dmasample[6]; if (db->sample_size == 16 && !mono && db->src_factor == 1) { /* no translation necessary, just copy */ if (copy_from_user(dmabuf, userbuf, dmacount)) return -EFAULT; return dmacount; } interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; num_samples = dmacount / interp_bytes_per_sample; for (sample = 0; sample < num_samples; sample++) { if (copy_from_user(usersample, userbuf, db->user_bytes_per_sample)) { return -EFAULT; } for (i = 0; i < db->num_channels; i++) { if (db->sample_size == 8) ch = U8_TO_S16(usersample[i]); else ch = *((s16 *) (&usersample[i * 2])); dmasample[i] = ch; if (mono) dmasample[i + 1] = ch; /* right channel */ } /* duplicate every audio frame src_factor times */ for (i = 0; i < db->src_factor; i++) memcpy(dmabuf, dmasample, db->dma_bytes_per_sample); userbuf += db->user_bytes_per_sample; dmabuf += interp_bytes_per_sample; } return num_samples * interp_bytes_per_sample;}/* * Translates AC'97 ADC samples to user buffer: * If mono, send only left channel to user buffer. * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. * If decimating (no VRA), skip over src_factor audio frames. */static inttranslate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf, int dmacount){ int sample, i; int interp_bytes_per_sample; int num_samples; int mono = (db->num_channels == 1); char usersample[12]; if (db->sample_size == 16 && !mono && db->src_factor == 1) { /* no translation necessary, just copy */ if (copy_to_user(userbuf, dmabuf, dmacount)) return -EFAULT; return dmacount; } interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; num_samples = dmacount / interp_bytes_per_sample; for (sample = 0; sample < num_samples; sample++) { for (i = 0; i < db->num_channels; i++) { if (db->sample_size == 8) usersample[i] = S16_TO_U8(*((s16 *) (&dmabuf[i * 2]))); else *((s16 *) (&usersample[i * 2])) = *((s16 *) (&dmabuf[i * 2])); } if (copy_to_user(userbuf, usersample, db->user_bytes_per_sample)) { return -EFAULT; } userbuf += db->user_bytes_per_sample; dmabuf += interp_bytes_per_sample; } return num_samples * interp_bytes_per_sample;}/* * Copy audio data to/from user buffer from/to dma buffer, taking care * that we wrap when reading/writing the dma buffer. Returns actual byte * count written to or read from the dma buffer. */static intcopy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user){ char *bufptr = to_user ? db->nextOut : db->nextIn; char *bufend = db->rawbuf + db->dmasize; int cnt, ret; if (bufptr + count > bufend) { int partial = (int) (bufend - bufptr); if (to_user) { if ((cnt = translate_to_user(db, userbuf, bufptr, partial)) < 0) return cnt; ret = cnt; if ((cnt = translate_to_user(db, userbuf + partial, db->rawbuf, count - partial)) < 0) return cnt; ret += cnt; } else { if ((cnt = translate_from_user(db, bufptr, userbuf, partial)) < 0) return cnt; ret = cnt; if ((cnt = translate_from_user(db, db->rawbuf, userbuf + partial, count - partial)) < 0) return cnt; ret += cnt; } } else { if (to_user) ret = translate_to_user(db, userbuf, bufptr, count); else ret = translate_from_user(db, bufptr, userbuf, count); } return ret;}static ssize_tau1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct au1550_state *s = (struct au1550_state *)file->private_data; struct dmabuf *db = &s->dma_adc; DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; int cnt, usercnt, avail; if (db->mapped) return -ENXIO; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; ret = 0; count *= db->cnt_factor; mutex_lock(&s->sem); add_wait_queue(&db->wait, &wait); while (count > 0) { /* wait for samples in ADC dma buffer */ do { spin_lock_irqsave(&s->lock, flags); if (db->stopped) start_adc(s); 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?