📄 cmpci.c
字号:
static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); set_fmt_unlocked(s,mask,data); spin_unlock_irqrestore(&s->lock, flags);}static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data){ outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); udelay(10);}static struct { unsigned rate; unsigned lower; unsigned upper; 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_spdifout_unlocked(struct cm_state *s, unsigned rate){ if (rate == 48000 || rate == 44100) { // SPDIFI48K SPDF_ACc97 maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0); // ENSPDOUT maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80); // SPDF_1 SPD2DAC maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240); // CDPLAY if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); s->status |= DO_SPDIF_OUT; } else { maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0); maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0); if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); s->status &= ~DO_SPDIF_OUT; }}static void set_spdifout(struct cm_state *s, unsigned rate){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); set_spdifout_unlocked(s,rate); spin_unlock_irqrestore(&s->lock, flags);}/* find parity for bit 4~30 */static unsigned parity(unsigned data){ unsigned parity = 0; int counter = 4; data >>= 4; // start from bit 4 while (counter <= 30) { if (data & 1) parity++; data >>= 1; counter++; } return parity & 1;}static void set_ac3_unlocked(struct cm_state *s, unsigned rate){ /* enable AC3 */ if (rate == 48000 || rate == 44100) { // mute DAC maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40); // AC3EN for 037, 0x10 maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); // AC3EN for 039, 0x04 maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04); if (s->capability & CAN_AC3_HW) { // SPD24SEL for 037, 0x02 // SPD24SEL for 039, 0x20, but cannot be set maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); s->status |= DO_AC3_HW; if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); } else { // SPD32SEL for 037 & 039, 0x20 maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20); // set 176K sample rate to fix 033 HW bug if (s->chip_version == 33) { if (rate == 48000) maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); else maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); } s->status |= DO_AC3_SW; } } else { maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0); maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0); maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0); maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); if (s->chip_version == 33) maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); s->status &= ~DO_AC3; } s->spdif_counter = 0;}static void set_ac3(struct cm_state *s, unsigned rate){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); set_spdifout_unlocked(s, rate); set_ac3_unlocked(s,rate); spin_unlock_irqrestore(&s->lock, flags);}static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size){ int i = size / 2; unsigned long data; unsigned long *dst = (unsigned long *) dest; unsigned short *src = (unsigned short *)source; do { data = (unsigned long) *src++; data <<= 12; // ok for 16-bit data if (s->spdif_counter == 2 || s->spdif_counter == 3) data |= 0x40000000; // indicate AC-3 raw data if (parity(data)) data |= 0x80000000; // parity if (s->spdif_counter == 0) data |= 3; // preamble 'M' else if (s->spdif_counter & 1) data |= 5; // odd, 'W' else data |= 9; // even, 'M' *dst++ = data; s->spdif_counter++; if (s->spdif_counter == 384) s->spdif_counter = 0; } while (--i);}static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate){ unsigned char freq = 4; int i; if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; 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 <<= 2; maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq);}static void set_adc_rate(struct cm_state *s, unsigned rate){ unsigned long flags; unsigned char freq = 4; int i; if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; 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 <<= 2; spin_lock_irqsave(&s->lock, flags); maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); spin_unlock_irqrestore(&s->lock, flags);}static void set_dac_rate(struct cm_state *s, unsigned rate){ unsigned long flags; unsigned char freq = 4; int i; if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; 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 <<= 5; spin_lock_irqsave(&s->lock, flags); maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq); if (s->curr_channels <= 2) set_spdifout_unlocked(s, rate); if (s->status & DO_DUAL_DAC) set_adc_rate_unlocked(s, rate); spin_unlock_irqrestore(&s->lock, flags);}/* --------------------------------------------------------------------- */static inline void reset_adc(struct cm_state *s){ /* reset bus master */ 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);}static inline void reset_dac(struct cm_state *s){ /* reset bus master */ outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); if (s->status & DO_DUAL_DAC) reset_adc(s);}static inline void pause_adc(struct cm_state *s){ maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4);}static inline void pause_dac(struct cm_state *s){ maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8); if (s->status & DO_DUAL_DAC) pause_adc(s);}static inline void disable_adc(struct cm_state *s){ /* disable channel */ s->enable &= ~CM_ENABLE_CH0; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); reset_adc(s);}static inline void disable_dac(struct cm_state *s){ /* disable channel */ s->enable &= ~CM_ENABLE_CH1; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); reset_dac(s); if (s->status & DO_DUAL_DAC) disable_adc(s);}static inline void enable_adc(struct cm_state *s){ if (!(s->enable & CM_ENABLE_CH0)) { /* enable channel */ s->enable |= CM_ENABLE_CH0; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0);}static inline void enable_dac_unlocked(struct cm_state *s){ if (!(s->enable & CM_ENABLE_CH1)) { /* enable channel */ s->enable |= CM_ENABLE_CH1; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0); if (s->status & DO_DUAL_DAC) enable_adc(s);}static inline void enable_dac(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); enable_dac_unlocked(s); spin_unlock_irqrestore(&s->lock, flags);}static inline void stop_adc_unlocked(struct cm_state *s){ if (s->enable & CM_ENABLE_CH0) { /* disable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0); disable_adc(s); }}static inline void stop_adc(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); stop_adc_unlocked(s); spin_unlock_irqrestore(&s->lock, flags);}static inline void stop_dac_unlocked(struct cm_state *s){ if (s->enable & CM_ENABLE_CH1) { /* disable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0); disable_dac(s); } if (s->status & DO_DUAL_DAC) stop_adc_unlocked(s);}static inline void stop_dac(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); stop_dac_unlocked(s); spin_unlock_irqrestore(&s->lock, flags);}static void start_adc_unlocked(struct cm_state *s){ if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) && s->dma_adc.ready) { /* enable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); enable_adc(s); }}static void start_adc(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); start_adc_unlocked(s); spin_unlock_irqrestore(&s->lock, flags);} static void start_dac1_unlocked(struct cm_state *s){ if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { /* enable interrupt */// maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); enable_dac_unlocked(s); }}static void start_dac_unlocked(struct cm_state *s){ if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { /* enable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2); enable_dac_unlocked(s); } if (s->status & DO_DUAL_DAC) start_dac1_unlocked(s);}static void start_dac(struct cm_state *s){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); start_dac_unlocked(s); spin_unlock_irqrestore(&s->lock, flags);} static int prog_dmabuf(struct cm_state *s, unsigned rec);static int set_dac_channels(struct cm_state *s, int channels){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); if ((channels > 2) && (channels <= s->max_channels) && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { set_spdifout_unlocked(s, 0); if (s->capability & CAN_MULTI_CH_HW) { // NXCHG maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80); // CHB3D or CHB3D5C maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20); // CHB3D6C maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0); // ENCENTER maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0); s->status |= DO_MULTI_CH_HW; } else if (s->capability & CAN_DUAL_DAC) { unsigned char fmtm = ~0, fmts = 0; ssize_t ret; // ENDBDAC, turn on double DAC mode // XCHGDAC, CH0 -> back, CH1->front maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0); s->status |= DO_DUAL_DAC; // prepare secondary buffer spin_unlock_irqrestore(&s->lock, flags); ret = prog_dmabuf(s, 1); if (ret) return ret; spin_lock_irqsave(&s->lock, flags); // copy the hw state fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -