📄 cmpci.c
字号:
unsigned char freq;} rate_lookup[] ={ { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, { 48000, (44100 + 48000) /2, 48000, 7 }};static void set_dac_rate(struct cm_state *s, unsigned rate){ unsigned long flags; unsigned char freq = 4, val; int i; if (rate > 48000) rate = 48000; if (rate < 5512) rate = 5512; for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } s->ratedac = rate; freq <<= 2; spin_lock_irqsave(&s->lock, flags); val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0x1c; outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1); spin_unlock_irqrestore(&s->lock, flags);}static void set_adc_rate(struct cm_state *s, unsigned rate){ unsigned long flags; unsigned char freq = 4, val; int i; if (rate > 48000) rate = 48000; if (rate < 5512) rate = 5512; for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } s->rateadc = rate; freq <<= 5; spin_lock_irqsave(&s->lock, flags); val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0xe0; outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1); spin_unlock_irqrestore(&s->lock, flags);}/* --------------------------------------------------------------------- */extern inline void stop_adc(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); /* disable channel */ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); s->enable &= ~CM_CENABLE_RE; /* disable interrupt */ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* reset */ outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); spin_unlock_irqrestore(&s->lock, flags);} extern inline void stop_dac(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); /* disable channel */ s->enable &= ~CM_CENABLE_PE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable interrupt */ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* reset */ outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); spin_unlock_irqrestore(&s->lock, flags);} static void start_dac(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); s->enable |= CM_CENABLE_PE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } spin_unlock_irqrestore(&s->lock, flags);} static void start_adc(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) && s->dma_adc.ready) { outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); s->enable |= CM_CENABLE_RE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } spin_unlock_irqrestore(&s->lock, flags);} /* --------------------------------------------------------------------- */#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1static void dealloc_dmabuf(struct dmabuf *db){ struct page *pstart, *pend; if (db->rawbuf) { /* undo marking the pages as reserved */ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) mem_map_unreserve(pstart); free_pages((unsigned long)db->rawbuf, db->buforder); } db->rawbuf = NULL; db->mapped = db->ready = 0;}/* Ch0 is used for playback, Ch1 is used for recording */static int prog_dmabuf(struct cm_state *s, unsigned rec){ struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; unsigned rate = rec ? s->rateadc : s->ratedac; int order; unsigned bytepersec; unsigned bufs; struct page *pstart, *pend; unsigned char fmt; unsigned long flags; spin_lock_irqsave(&s->lock, flags); fmt = s->fmt; if (rec) { s->enable &= ~CM_CENABLE_RE; fmt >>= CM_CFMT_ADCSHIFT; } else { s->enable &= ~CM_CENABLE_PE; fmt >>= CM_CFMT_DACSHIFT; } outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); spin_unlock_irqrestore(&s->lock, flags); fmt &= CM_CFMT_MASK; db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) mem_map_reserve(pstart); } bytepersec = rate << sample_shift[fmt]; bufs = PAGE_SIZE << db->buforder; if (db->ossfragshift) { if ((1000 << db->ossfragshift) < bytepersec) db->fragshift = ld2(bytepersec/1000); else db->fragshift = db->ossfragshift; } else { db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); if (db->fragshift < 3) db->fragshift = 3; } db->numfrag = bufs >> db->fragshift; while (db->numfrag < 4 && db->fragshift > 3) { db->fragshift--; db->numfrag = bufs >> db->fragshift; } db->fragsize = 1 << db->fragshift; if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; /* to make fragsize >= 4096 */#if 0 if(s->modem) { while (db->fragsize < 4096 && db->numfrag >= 4) { db->fragsize *= 2; db->fragshift++; db->numfrag /= 2; } }#endif db->fragsamples = db->fragsize >> sample_shift[fmt]; db->dmasize = db->numfrag << db->fragshift; db->dmasamples = db->dmasize >> sample_shift[fmt]; memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); spin_lock_irqsave(&s->lock, flags); if (rec) { set_dmaadc(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]); /* program sample counts */ outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); } else { set_dmadac(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]); /* program sample counts */ outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); } spin_unlock_irqrestore(&s->lock, flags); db->ready = 1; return 0;}extern __inline__ void clear_advance(struct cm_state *s){ unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; unsigned char *buf = s->dma_dac.rawbuf; unsigned bsize = s->dma_dac.dmasize; unsigned bptr = s->dma_dac.swptr; unsigned len = s->dma_dac.fragsize; if (bptr + len > bsize) { unsigned x = bsize - bptr; memset(buf + bptr, c, x); bptr = 0; len -= x; } memset(buf + bptr, c, len); outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);}/* call with spinlock held! */static void cm_update_ptr(struct cm_state *s){ unsigned hwptr; int diff; /* update ADC pointer */ if (s->dma_adc.ready) { hwptr = (s->dma_adc.dmasize - get_dmaadc(s)) % s->dma_adc.dmasize; diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; s->dma_adc.count += diff; if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) wake_up(&s->dma_adc.wait); if (!s->dma_adc.mapped) { if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { s->enable &= ~CM_CENABLE_RE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); s->dma_adc.error++; } } } /* update DAC pointer */ if (s->dma_dac.ready) { hwptr = (s->dma_dac.dmasize - get_dmadac(s)) % s->dma_dac.dmasize; diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; s->dma_dac.hwptr = hwptr; s->dma_dac.total_bytes += diff; if (s->dma_dac.mapped) { s->dma_dac.count += diff; if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) wake_up(&s->dma_dac.wait); } else { s->dma_dac.count -= diff; if (s->dma_dac.count <= 0) { s->enable &= ~CM_CENABLE_PE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); s->dma_dac.error++; } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { clear_advance(s); s->dma_dac.endcleared = 1; } if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) wake_up(&s->dma_dac.wait); } }}/* hold spinlock for the following! */static void cm_handle_midi(struct cm_state *s){ unsigned char ch; int wake; wake = 0; while (!(inb(s->iomidi+1) & 0x80)) { ch = inb(s->iomidi); if (s->midi.icnt < MIDIINBUF) { s->midi.ibuf[s->midi.iwr] = ch; s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; s->midi.icnt++; } wake = 1; } if (wake) wake_up(&s->midi.iwait); wake = 0; while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { outb(s->midi.obuf[s->midi.ord], s->iomidi); s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; s->midi.ocnt--; if (s->midi.ocnt < MIDIOUTBUF-16) wake = 1; } if (wake) wake_up(&s->midi.owait);}static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct cm_state *s = (struct cm_state *)dev_id; unsigned int intsrc, intstat; /* fastpath out, to ease interrupt sharing */ intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); if (!(intsrc & 0x80000000)) return; spin_lock(&s->lock); intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* acknowledge interrupt */ if (intsrc & CM_INT_CH0) { outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); udelay(10); outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); } if (intsrc & CM_INT_CH1) { outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); udelay(10); outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); } cm_update_ptr(s); cm_handle_midi(s); spin_unlock(&s->lock);}static void cm_midi_timer(unsigned long data){ struct cm_state *s = (struct cm_state *)data; unsigned long flags; spin_lock_irqsave(&s->lock, flags); cm_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); s->midi.timer.expires = jiffies+1; add_timer(&s->midi.timer);}/* --------------------------------------------------------------------- */static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";#ifdef CONFIG_SOUND_CMPCI /* support multiple chips */#define VALIDATE_STATE(s)#else#define VALIDATE_STATE(s) \({ \ if (!(s) || (s)->magic != CM_MAGIC) { \ printk(invalid_magic); \ return -ENXIO; \ } \})#endif/* --------------------------------------------------------------------- */#define MT_4 1#define MT_5MUTE 2#define MT_4MUTEMONO 3#define MT_6MUTE 4#define MT_5MUTEMONO 5static const struct { unsigned left; unsigned right; unsigned type; unsigned rec; unsigned play;} mixtable[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 }, [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 }, [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }};#ifdef OSS_DOCUMENTED_MIXER_SEMANTICSstatic int return_mixval(struct cm_state *s, unsigned i, int *arg){ unsigned long flags; unsigned char l, r, rl, rr; spin_lock_irqsave(&s->lock, flags); l = rdmixer(s, mixtable[i].left); r = rdmixer(s, mixtable[i].right); spin_unlock_irqrestore(&s->lock, flags); switch (mixtable[i].type) { case MT_4: r &= 0xf; l &= 0xf; rl = 10 + 6 * (l & 15); rr = 10 + 6 * (r & 15); break; case MT_4MUTEMONO: rl = 55 - 3 * (l & 15); if (r & 0x10) rl += 45; rr = rl; r = l; break; case MT_5MUTEMONO: r = l; rl = 100 - 3 * ((l >> 3) & 31); rr = rl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -