📄 cmpci.c
字号:
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, CDPLAY); s->status &= ~DO_AC3; } s->spdif_counter = 0;}static void set_line_as_rear(struct cm_state *s, int use_line_as_rear){ if (!(s->capability & CAN_LINE_AS_REAR)) return; if (use_line_as_rear) { maskb(s->iobase + CODEC_CMI_MIXER1, ~0, SPK4); s->status |= DO_LINE_AS_REAR; } else { maskb(s->iobase + CODEC_CMI_MIXER1, ~SPK4, 0); s->status &= ~DO_LINE_AS_REAR; }}static void set_line_as_bass(struct cm_state *s, int use_line_as_bass){ if (!(s->capability & CAN_LINE_AS_BASS)) return; if (use_line_as_bass) { maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, CB2LIN); s->status |= DO_LINE_AS_BASS; } else { maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CB2LIN, 0); s->status &= ~DO_LINE_AS_BASS; }}static void set_mic_as_bass(struct cm_state *s, int use_mic_as_bass){ if (!(s->capability & CAN_MIC_AS_BASS)) return; if (use_mic_as_bass) { maskb(s->iobase + CODEC_CMI_MISC, ~0, 0x04); s->status |= DO_MIC_AS_BASS; } else { maskb(s->iobase + CODEC_CMI_MISC, ~0x04, 0); s->status &= ~DO_MIC_AS_BASS; }}static void set_hw_copy(struct cm_state *s, int hw_copy){ if (s->max_channels > 2 && hw_copy) maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, N4SPK3D); else maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~N4SPK3D, 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 int trans_ac3(struct cm_state *s, void *dest, const char __user *source, int size){ int i = size / 2; unsigned long data; unsigned short data16; unsigned long *dst = (unsigned long *) dest; unsigned short __user *src = (unsigned short __user *)source; int err; do { if ((err = __get_user(data16, src++))) return err; data = (unsigned long)le16_to_cpu(data16); 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++ = cpu_to_le32(data); s->spdif_counter++; if (s->spdif_counter == 384) s->spdif_counter = 0; } while (--i); return 0;}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 <<= CM_FREQ_ADCSHIFT; maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, 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 <<= CM_FREQ_ADCSHIFT; spin_lock_irqsave(&s->lock, flags); maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, 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 <<= CM_FREQ_DACSHIFT; spin_lock_irqsave(&s->lock, flags); maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~DSFC, freq); spin_unlock_irqrestore(&s->lock, flags); if (s->curr_channels <= 2 && spdif_out) set_spdifout(s, rate); if (s->status & DO_DUAL_DAC) set_dac1_rate(s, rate);}/* --------------------------------------------------------------------- */static inline void reset_adc(struct cm_state *s){ /* reset bus master */ outb(s->enable | RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2);}static inline void reset_dac(struct cm_state *s){ /* reset bus master */ outb(s->enable | RSTDAC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~RSTDAC, 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, PAUSEADC);}static inline void pause_dac(struct cm_state *s){ maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, PAUSEDAC); if (s->status & DO_DUAL_DAC) pause_adc(s);}static inline void disable_adc(struct cm_state *s){ /* disable channel */ s->enable &= ~ENADC; 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 &= ~ENDAC; 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 & ENADC)) { /* enable channel */ s->enable |= ENADC; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEADC, 0);}static inline void enable_dac_unlocked(struct cm_state *s){ if (!(s->enable & ENDAC)) { /* enable channel */ s->enable |= ENDAC; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEDAC, 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 & ENADC) { /* disable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENADCINT, 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 & ENDAC) { /* disable interrupt */ maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENDACINT, 0); disable_dac(s); } if (s->status & DO_DUAL_DAC) stop_dac1_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 inline 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, ENADCINT); 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, ENADCINT); 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, ENDACINT); 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; static unsigned int fmmute = 0; 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, NXCHG); // CHB3D or CHB3D5C maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), channels > 4 ? CHB3D5C : CHB3D); // CHB3D6C maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, channels == 6 ? CHB3D6C : 0); // ENCENTER maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~ENCENTER, channels == 6 ? ENCENTER : 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, ENDBDAC|XCHGDAC); // mute FM fmmute = inb(s->iobase + CODEC_CMI_MIXER1) & FMMUTE; maskb(s->iobase + CODEC_CMI_MIXER1, ~0, FMMUTE); 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); // the HW only support 16-bit stereo fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; set_fmt_unlocked(s, fmtm, fmts); set_adc_rate_unlocked(s, s->ratedac); } // disable 4 speaker mode (analog duplicate) set_hw_copy(s, 0); s->curr_channels = channels; // enable jack redirect set_line_as_rear(s, use_line_as_rear); if (channels > 4) { set_line_as_bass(s, use_line_as_bass); set_mic_as_bass(s, use_mic_as_bass); } } else { if (s->status & DO_MULTI_CH_HW) { maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~NXCHG, 0); maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), 0); maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, 0); } else if (s->status & DO_DUAL_DAC) { maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~ENDBDAC, 0); maskb(s->iobase + CODEC_CMI_MIXER1, ~FMMUTE, fmmute); } // enable 4 speaker mode (analog duplicate) set_hw_copy(s, hw_copy); s->status &= ~DO_MULTI_CH; s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; // disable jack redirect set_line_as_rear(s, hw_copy ? use_line_as_rear : 0); set_line_as_bass(s, 0); set_mic_as_bass(s, 0); } spin_unlock_irqrestore(&s->lock, flags); return s->curr_channels;}/* --------------------------------------------------------------------- */#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1static void dealloc_dmabuf(struct cm_state *s, 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++) ClearPageReserved(pstart); pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); } db->rawbuf = NULL; db->mapped = db->ready = 0;}/* Ch1 is used for playback, Ch0 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -