📄 cmpci.c
字号:
unsigned long flags; fmt = s->fmt; if (rec) { stop_adc(s); fmt >>= CM_CFMT_ADCSHIFT; } else { stop_dac(s); fmt >>= CM_CFMT_DACSHIFT; } 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 = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) break; if (!db->rawbuf || !db->dmaaddr) return -ENOMEM; db->buforder = order; /* 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++) SetPageReserved(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 */ 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) { if (s->status & DO_DUAL_DAC) set_dmadac1(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); else set_dmaadc(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); /* program sample counts */ set_countdac(s, db->fragsamples); } else { set_dmadac(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); /* program sample counts */ set_countdac(s, db->fragsamples); } spin_unlock_irqrestore(&s->lock, flags); db->enabled = 1; db->ready = 1; return 0;}static 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 char *buf1 = s->dma_adc.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); if (s->status & DO_DUAL_DAC) memset(buf1 + bptr, c, x); bptr = 0; len -= x; } memset(buf + bptr, c, len); if (s->status & DO_DUAL_DAC) memset(buf1 + bptr, c, len);}/* 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) { if (s->status & DO_DUAL_DAC) { /* the dac part will finish for this */ } else { hwptr = 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))) { pause_adc(s); s->dma_adc.error++; } } } } /* update DAC pointer */ if (s->dma_dac.ready) { hwptr = 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->status & DO_DUAL_DAC) { s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; } if (s->dma_dac.mapped) { s->dma_dac.count += diff; if (s->status & DO_DUAL_DAC) s->dma_adc.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->status & DO_DUAL_DAC) s->dma_adc.count -= diff; if (s->dma_dac.count <= 0) { pause_dac(s); 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->status & DO_DUAL_DAC) s->dma_adc.endcleared = 1; } if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) wake_up(&s->dma_dac.wait); } }}static irqreturn_t cm_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct cm_state *s = (struct cm_state *)dev_id; unsigned int intsrc, intstat; unsigned char mask = 0; /* fastpath out, to ease interrupt sharing */ intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); if (!(intsrc & 0x80000000)) return IRQ_NONE; spin_lock(&s->lock); intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* acknowledge interrupt */ if (intsrc & ADCINT) mask |= ENADCINT; if (intsrc & DACINT) mask |= ENDACINT; outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); cm_update_ptr(s); spin_unlock(&s->lock);#ifdef CONFIG_SOUND_CMPCI_MIDI if (intsrc & 0x00010000) { // UART interrupt if (s->midi_devc && intchk_mpu401((void *)s->midi_devc)) mpuintr(irq, (void *)s->midi_devc, regs); else inb(s->iomidi);// dummy read }#endif return IRQ_HANDLED;}/* --------------------------------------------------------------------- */static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";#define VALIDATE_STATE(s) \({ \ if (!(s) || (s)->magic != CM_MAGIC) { \ printk(invalid_magic); \ return -ENXIO; \ } \})/* --------------------------------------------------------------------- */#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, 0x06 }, [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x18 }, [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 }, [SOUND_MIXER_LINE1] = { DSP_MIX_AUXVOL_L, DSP_MIX_AUXVOL_R, MT_5MUTE, 0x80, 0x60 }, [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x00, 0x01 }};static const unsigned char volidx[SOUND_MIXER_NRDEVICES] ={ [SOUND_MIXER_CD] = 1, [SOUND_MIXER_LINE] = 2, [SOUND_MIXER_MIC] = 3, [SOUND_MIXER_SYNTH] = 4, [SOUND_MIXER_VOLUME] = 5, [SOUND_MIXER_PCM] = 6, [SOUND_MIXER_LINE1] = 7, [SOUND_MIXER_SPEAKER]= 8};static unsigned mixer_outmask(struct cm_state *s){ unsigned long flags; int i, j, k; spin_lock_irqsave(&s->lock, flags); j = rdmixer(s, DSP_MIX_OUTMIXIDX); spin_unlock_irqrestore(&s->lock, flags); for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (j & mixtable[i].play) k |= 1 << i; return k;}static unsigned mixer_recmask(struct cm_state *s){ unsigned long flags; int i, j, k; spin_lock_irqsave(&s->lock, flags); j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); spin_unlock_irqrestore(&s->lock, flags); for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (j & mixtable[i].rec) k |= 1 << i; return k;}static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg){ unsigned long flags; int i, val, j; unsigned char l, r, rl, rr; void __user *argp = (void __user *)arg; int __user *p = argp; VALIDATE_STATE(s); if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, "cmpci", sizeof(info.id)); strlcpy(info.name, "C-Media PCI", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user(argp, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, "cmpci", sizeof(info.id)); strlcpy(info.name, "C-Media cmpci", sizeof(info.name)); if (copy_to_user(argp, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, p); if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) return -EINVAL; if (_SIOC_DIR(cmd) == _SIOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ val = mixer_recmask(s); return put_user(val, p); case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ val = mixer_outmask(s); return put_user(val, p); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].type) val |= 1 << i; return put_user(val, p); case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].rec) val |= 1 << i; return put_user(val, p); case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].play) val |= 1 << i; return put_user(val, p); case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) val |= 1 << i; return put_user(val, p); case SOUND_MIXER_CAPS: return put_user(0, p); default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) return -EINVAL; if (!volidx[i]) return -EINVAL; return put_user(s->mix.vol[volidx[i]-1], p); } } if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) return -EINVAL; s->mix.modcnt++; switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ if (get_user(val, p)) return -EFAULT; i = generic_hweight32(val); for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(val & (1 << i))) continue; if (!mixtable[i].rec) { val &= ~(1 << i); continue; } j |= mixtable[i].rec; } spin_lock_irqsave(&s->lock, flags); wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1) | (j & 0x80)); spin_unlock_irqrestore(&s->lock, flags); return 0; case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ if (get_user(val, p)) return -EFAULT; for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(val & (1 << i))) continue; if (!mixtable[i].play) { val &= ~(1 << i); continue; } j |= mixtable[i].play; } spin_lock_irqsave(&s->lock, flags); wrmixer(s, DSP_MIX_OUTMIXIDX, j); spin_unlock_irqrestore(&s->lock, flags); return 0; default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) return -EINVAL; if (get_user(val, p)) return -EFAULT; l = val & 0xff; r = (val >> 8) & 0xff; if (l > 100) l = 100; if (r > 100) r = 100; spin_lock_irqsave(&s->lock, flags); switch (mixtable[i].type) { case MT_4: if (l >= 10) l -= 10; if (r >= 10) r -= 10; frobindir(s, mixtable[i].left, 0xf0, l / 6); frobindir(s, mixtable[i].right, 0xf0, l / 6); break; case MT_4MUTEMONO: rl = (l < 4 ? 0 : (l - 5) / 3) & 31; rr = (rl >> 2) & 7; wrmixer(s, mixtable[i].left, rl<<3); if (i == SOUND_MIXER_MIC) maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); break; case MT_5MUTEMONO: rl = l < 4 ? 0 : (l - 5) / 3; wrmixer(s, mixtable[i].left, rl<<3); l = rdmixer(s, DSP_MIX_OUTMIXIDX) & ~mixtable[i].play; r = rl ? mixtable[i].play : 0; wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); /* for recording */ if (i == SOUND_MIXER_MIC) { if (s->chip_version >= 37) { rr = rl >> 1; maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, (rr&0x07)<<1); frobindir(s, DSP_MIX_EXTENSION, ~0x01, rr>>3); } else { rr = rl >> 2; maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); } } break; case MT_5MUTE: rl = l < 4 ? 0 : (l - 5) / 3; rr = r < 4 ? 0 : (r - 5) / 3; wrmixer(s, mixtable[i].left, rl<<3); wrmixer(s, mixtable[i].right, rr<<3); l = rdmixer(s, DSP_MIX_OUTMIXIDX); l &= ~mixtable[i].play; r = (rl|rr) ? mixtable[i].play : 0; wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); break; case MT_6MUTE: if (l < 6) rl = 0x00; else rl = l * 2 / 3; if (r < 6) rr = 0x00; else rr = r * 2 / 3; wrmixer(s, mixtable[i].left, rl); wrmixer(s, mixtable[i].right, rr); break; } spin_unlock_irqrestore(&s->lock, flags); if (!volidx[i]) return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -