📄 cs4281.c
字号:
{ unsigned long va; unsigned count; int c; stop_dac(s); s->dma_dac.type = CS_TYPE_DAC; if ((c = prog_dmabuf(s, &s->dma_dac))) return c; memset(s->dma_dac.rawbuf, (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, s->dma_dac.dmasize); va = virt_to_bus(s->dma_dac.rawbuf); count = s->dma_dac.dmasize; if (s->prop_dac. fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) count /= 2; // 16-bit. if (s->prop_dac.channels > 1) count /= 2; // Assume stereo. writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", count, (unsigned) va)); s->dma_dac.ready = 1; return 0;}static void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c){ if (bptr + len > bsize) { unsigned x = bsize - bptr; memset(((char *) buf) + bptr, c, x); bptr = 0; len -= x; } CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO "cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n", (unsigned) c, (unsigned) ((char *) buf) + bptr, len)); memset(((char *) buf) + bptr, c, len);}// call with spinlock held! static void cs4281_update_ptr(struct cs4281_state *s){ int diff; unsigned hwptr, va; // update ADC pointer if (s->ena & FMODE_READ) { hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. va = virt_to_bus(s->dma_adc.rawbuf); hwptr -= (unsigned) va; 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 > s->dma_adc.dmasize) s->dma_adc.count = s->dma_adc.dmasize; if (s->dma_adc.mapped) { if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) wake_up(&s-> dma_adc. wait); } else { if (s->dma_adc.count > 0) wake_up(&s->dma_adc.wait); } CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", (unsigned) s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); } // update DAC pointer // // check for end of buffer, means that we are going to wait for another interrupt // to allow silence to fill the fifos on the part, to keep pops down to a minimum. // if (s->ena & FMODE_WRITE) { hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. va = virt_to_bus(s->dma_dac.rawbuf); hwptr -= (unsigned) va; 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 >= s->dma_dac.fragsize) { s->dma_dac.wakeup = 1; wake_up(&s->dma_dac.wait); if (s->dma_dac.count > s->dma_dac.dmasize) s->dma_dac.count &= s->dma_dac.dmasize - 1; } } else { s->dma_dac.count -= diff; if (s->dma_dac.count <= 0) { // // fill with silence, and do not shut down the DAC. // Continue to play silence until the _release. // CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO "cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n", (unsigned) (s->prop_dac. fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, (unsigned) s->dma_dac. rawbuf, s->dma_dac.dmasize)); memset(s->dma_dac.rawbuf, (s->prop_dac. fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, s->dma_dac.dmasize); } else if (s->dma_dac.count <= (signed) s->dma_dac.fragsize && !s->dma_dac.endcleared) { clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, s->dma_dac.fragsize, (s->prop_dac. fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0); s->dma_dac.endcleared = 1; } if (s->dma_dac.count < (signed) s->dma_dac.dmasize) wake_up(&s->dma_dac.wait); } CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", (unsigned) s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); }}// --------------------------------------------------------------------- static void prog_codec(struct cs4281_state *s, unsigned type){ unsigned long flags; unsigned temp1, format; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: prog_codec()+ \n")); spin_lock_irqsave(&s->lock, flags); if (type == CS_TYPE_ADC) { temp1 = readl(s->pBA0 + BA0_DCR1); writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. // program sampling rates // Note, for CS4281, capture & play rates can be set independently. cs4281_record_rate(s, s->prop_adc.rate); // program ADC parameters format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; if (s->prop_adc. fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? format |= DMRn_BEND; if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) format |= DMRn_USIGN; // Unsigned. } else format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned if (s->prop_adc.channels < 2) format |= DMRn_MONO; writel(format, s->pBA0 + BA0_DMR1); CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", (format & DMRn_SIZE8) ? "8" : "16", (format & DMRn_USIGN) ? "Unsigned" : "Signed", (format & DMRn_MONO) ? "Mono" : "Stereo", s->prop_adc.rate, format)); s->ena &= ~FMODE_READ; // not capturing data yet } if (type == CS_TYPE_DAC) { temp1 = readl(s->pBA0 + BA0_DCR0); writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. // program sampling rates // Note, for CS4281, capture & play rates can be set independently. cs4281_play_rate(s, s->prop_dac.rate); // program DAC parameters format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; if (s->prop_dac. fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) format |= DMRn_BEND; // Big Endian. if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) format |= DMRn_USIGN; // Unsigned. } else format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned if (s->prop_dac.channels < 2) format |= DMRn_MONO; writel(format, s->pBA0 + BA0_DMR0); CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", (format & DMRn_SIZE8) ? "8" : "16", (format & DMRn_USIGN) ? "Unsigned" : "Signed", (format & DMRn_MONO) ? "Mono" : "Stereo", s->prop_dac.rate, format)); s->ena &= ~FMODE_WRITE; // not capturing data yet } spin_unlock_irqrestore(&s->lock, flags); CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: prog_codec()- \n"));}// --------------------------------------------------------------------- static const char invalid_magic[] = KERN_CRIT "cs4281: invalid magic value\n";#define VALIDATE_STATE(s) \({ \ if (!(s) || (s)->magic != CS4281_MAGIC) { \ printk(invalid_magic); \ return -ENXIO; \ } \})// --------------------------------------------------------------------- static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, unsigned long arg){ // Index to mixer_src[] is value of AC97 Input Mux Select Reg. // Value of array member is recording source Device ID Mask. static const unsigned int mixer_src[8] = { SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 }; // Index of mixtable1[] member is Device ID // and must be <= SOUND_MIXER_NRDEVICES. // Value of array member is index into s->mix.vol[] static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_PCM] = 1, // voice [SOUND_MIXER_LINE1] = 2, // AUX [SOUND_MIXER_CD] = 3, // CD [SOUND_MIXER_LINE] = 4, // Line [SOUND_MIXER_SYNTH] = 5, // FM [SOUND_MIXER_MIC] = 6, // Mic [SOUND_MIXER_SPEAKER] = 7, // Speaker [SOUND_MIXER_RECLEV] = 8, // Recording level [SOUND_MIXER_VOLUME] = 9 // Master Volume }; static const unsigned mixreg[] = { BA0_AC97_PCM_OUT_VOLUME, BA0_AC97_AUX_VOLUME, BA0_AC97_CD_VOLUME, BA0_AC97_LINE_IN_VOLUME }; unsigned char l, r, rl, rr, vidx; unsigned char attentbl[11] = { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; unsigned temp1; int i, val; VALIDATE_STATE(s); CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs4281: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", (unsigned) s, cmd));#if CSDEBUG printioctl(cmd);#endif#if CSDEBUG_INTERFACE if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || (cmd == SOUND_MIXER_CS_SETDBGMASK) || (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || (cmd == SOUND_MIXER_CS_SETDBGLEVEL)) { switch (cmd) { case SOUND_MIXER_CS_GETDBGMASK: return put_user(cs_debugmask, (unsigned long *) arg); case SOUND_MIXER_CS_GETDBGLEVEL: return put_user(cs_debuglevel, (unsigned long *) arg); case SOUND_MIXER_CS_SETDBGMASK: if (get_user(val, (unsigned long *) arg)) return -EFAULT; cs_debugmask = val; return 0; case SOUND_MIXER_CS_SETDBGLEVEL: if (get_user(val, (unsigned long *) arg)) return -EFAULT; cs_debuglevel = val; return 0; default: CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); return 0; } }#endif if (cmd == SOUND_MIXER_PRIVATE1) { // enable/disable/query mixer preamp if (get_user(val, (int *) arg)) return -EFAULT; if (val != -1) { cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); } cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); val = (temp1 & 0x40) ? 1 : 0; return put_user(val, (int *) arg); } if (cmd == SOUND_MIXER_PRIVATE2) { // enable/disable/query spatializer if (get_user(val, (int *) arg)) return -EFAULT; if (val != -1) { temp1 = (val & 0x3f) >> 2; cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &temp1); cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, temp1 | 0x2000); } cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); return put_user((temp1 << 2) | 3, (int *) arg); } if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "CS4281", sizeof(info.id)); strncpy(info.name, "Crystal CS4281", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *) arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "CS4281", sizeof(info.id)); strncpy(info.name, "Crystal CS4281", sizeof(info.name)); if (copy_to_user((void *) arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *) arg); if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) return -EINVAL; // If ioctl has only the SIOC_READ bit(bit 31) // on, process the only-read commands. if (_SIOC_DIR(cmd) == _SIOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT, &temp1); return put_user(mixer_src[temp1 & 7], (int *) arg); case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER, (int *) arg); case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | SOUND_MASK_LINE1, (int *) arg); case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, (int *) arg); case SOUND_MIXER_CAPS: return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); default: i = _IOC_NR(cmd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -