📄 cmpci.c
字号:
#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC)#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */#define DO_SPDIF_OUT 0x00000100#define DO_SPDIF_IN 0x00000200#define DO_SPDIF_LOOP 0x00000400#define DO_BIGENDIAN_W 0x00001000 /* used in PowerPC */#define DO_BIGENDIAN_R 0x00002000 /* used in PowerPC */static LIST_HEAD(devs);static int mpuio = 0;static int fmio = 0;static int joystick = 0;static int spdif_inverse = 0;static int spdif_loop = 0;static int spdif_out = 0;static int use_line_as_rear = 0;static int use_line_as_bass = 0;static int use_mic_as_bass = 0;static int mic_boost = 0;static int hw_copy = 0;MODULE_PARM(mpuio, "i");MODULE_PARM(fmio, "i");MODULE_PARM(joystick, "i");MODULE_PARM(spdif_inverse, "i");MODULE_PARM(spdif_loop, "i");MODULE_PARM(spdif_out, "i");MODULE_PARM(use_line_as_rear, "i");MODULE_PARM(use_line_as_bass, "i");MODULE_PARM(use_mic_as_bass, "i");MODULE_PARM(mic_boost, "i");MODULE_PARM(hw_copy, "i");MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable");MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable");MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver");MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal");MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly");MODULE_PARM_DESC(spdif_out, "(1/0) Send PCM to S/PDIF-out (PCM volume will not function)");MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out");MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center");MODULE_PARM_DESC(use_mic_as_bass, "(1/0) Use mic-in jack as bass/center");MODULE_PARM_DESC(mic_boost, "(1/0) Enable microphone boost");MODULE_PARM_DESC(hw_copy, "Copy front channel to surround channel");/* --------------------------------------------------------------------- */static inline unsigned ld2(unsigned int x){ unsigned exp=16,l=5,r=0; static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; /* num: 2, 4, 16, 256, 65536 */ /* exp: 1, 2, 4, 8, 16 */ while(l--) { if( x >= num[l] ) { if(num[l]>2) x >>= exp; r+=exp; } exp>>=1; } return r;}/* --------------------------------------------------------------------- */static void maskb(unsigned int addr, unsigned int mask, unsigned int value){ outb((inb(addr) & mask) | value, addr);}static void maskw(unsigned int addr, unsigned int mask, unsigned int value){ outw((inw(addr) & mask) | value, addr);}static void maskl(unsigned int addr, unsigned int mask, unsigned int value){ outl((inl(addr) & mask) | value, addr);}static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count){ if (addr) outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC0, 0);}static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count){ outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, CHADC0);}static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count){ outl(addr, s->iobase + CODEC_CMI_DAC_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC1, 0); if (s->status & DO_DUAL_DAC) set_dmadac1(s, 0, count);}static void set_countadc(struct cm_state *s, unsigned count){ outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2 + 2);}static void set_countdac(struct cm_state *s, unsigned count){ outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2 + 2); if (s->status & DO_DUAL_DAC) set_countadc(s, count);}static unsigned get_dmadac(struct cm_state *s){ unsigned int curr_addr; curr_addr = inw(s->iobase + CODEC_CMI_DAC_FRAME2) + 1; curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_dac.dmasize - curr_addr; return curr_addr;}static unsigned get_dmaadc(struct cm_state *s){ unsigned int curr_addr; curr_addr = inw(s->iobase + CODEC_CMI_ADC_FRAME2) + 1; curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_adc.dmasize - curr_addr; return curr_addr;}static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data){ unsigned char regval, pseudo; // pseudo register if (idx == DSP_MIX_AUXVOL_L) { data >>= 4; data &= 0x0f; regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0x0f; outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); return; } if (idx == DSP_MIX_AUXVOL_R) { data &= 0xf0; regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0xf0; outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); return; } outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); // pseudo bits if (idx == DSP_MIX_OUTMIXIDX) { pseudo = data & ~0x1f; pseudo >>= 1; regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x30; outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); } if (idx == DSP_MIX_ADCMIXIDX_L) { pseudo = data & 0x80; pseudo >>= 1; regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x40; outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); } if (idx == DSP_MIX_ADCMIXIDX_R) { pseudo = data & 0x80; regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x80; outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); } outb(data, s->iobase + CODEC_SB16_DATA); udelay(10);}static unsigned char rdmixer(struct cm_state *s, unsigned char idx){ unsigned char v, pseudo; // pseudo register if (idx == DSP_MIX_AUXVOL_L) { v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0x0f; v <<= 4; return v; } if (idx == DSP_MIX_AUXVOL_L) { v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0xf0; return v; } outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); v = inb(s->iobase + CODEC_SB16_DATA); udelay(10); // pseudo bits if (idx == DSP_MIX_OUTMIXIDX) { pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x30; pseudo <<= 1; v |= pseudo; } if (idx == DSP_MIX_ADCMIXIDX_L) { pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x40; pseudo <<= 1; v |= pseudo; } if (idx == DSP_MIX_ADCMIXIDX_R) { pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x80; v |= pseudo; } return v;}static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data){ if (mask && s->chip_version > 0) { /* 8338 cannot keep this */ s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); udelay(10); } s->fmt = (s->fmt & mask) | data; outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); udelay(10);}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_spdif_copyright(struct cm_state *s, int spdif_copyright){ /* enable SPDIF-in Copyright */ maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~SPDCOPYRHT, spdif_copyright ? SPDCOPYRHT : 0);}static void set_spdif_loop(struct cm_state *s, int spdif_loop){ /* enable SPDIF loop */ if (spdif_loop) { s->status |= DO_SPDIF_LOOP; /* turn on spdif-in to spdif-out */ maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, SPDFLOOP); } else { s->status &= ~DO_SPDIF_LOOP; /* turn off spdif-in to spdif-out */ maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDFLOOP, 0); }}static void set_spdif_monitor(struct cm_state *s, int channel){ // SPDO2DAC maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDO2DAC, channel == 2 ? SPDO2DAC : 0); // CDPLAY if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, channel ? CDPLAY : 0);}static void set_spdifout_level(struct cm_state *s, int level5v){ /* SPDO5V */ if (s->chip_version > 0) maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~SPDO5V, level5v ? SPDO5V : 0);}static void set_spdifin_inverse(struct cm_state *s, int spdif_inverse){ if (s->chip_version == 0) /* 8338 has not this feature */ return; if (spdif_inverse) { /* turn on spdif-in inverse */ if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_CHFORMAT, ~0, INVSPDIFI); else maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); } else { /* turn off spdif-ininverse */ if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_CHFORMAT, ~INVSPDIFI, 0); else maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); }}static void set_spdifin_channel2(struct cm_state *s, int channel2){ /* SELSPDIFI2 */ if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MISC_CTRL + 1, ~SELSPDIFI2, channel2 ? SELSPDIFI2 : 0);}static void set_spdifin_valid(struct cm_state *s, int valid){ /* SPDVALID */ maskb(s->iobase + CODEC_CMI_MISC, ~SPDVALID, valid ? SPDVALID : 0);}static void set_spdifout_unlocked(struct cm_state *s, unsigned rate){ if (rate != 48000 && rate != 44100) rate = 0; if (rate == 48000 || rate == 44100) { set_spdif_loop(s, 0); // SPDF_1 maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); // SPDIFI48K SPDF_AC97 maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); if (s->chip_version >= 55) // SPD32KFMT maskb(s->iobase + CODEC_CMI_MISC_CTRL2, ~SPD32KFMT, rate == 48000 ? SPD32KFMT : 0); if (s->chip_version > 0) // ENSPDOUT maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, ENSPDOUT); // monitor SPDIF out set_spdif_monitor(s, 2); s->status |= DO_SPDIF_OUT; } else { maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~ENSPDOUT, 0); // monitor none set_spdif_monitor(s, 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);}static void set_spdifin_unlocked(struct cm_state *s, unsigned rate){ if (rate == 48000 || rate == 44100) { // SPDF_1 maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); // SPDIFI48K SPDF_AC97 maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); s->status |= DO_SPDIF_IN; } else { maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); s->status &= ~DO_SPDIF_IN; }}static void set_spdifin(struct cm_state *s, unsigned rate){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); set_spdifin_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){ if (!(s->capability & CAN_AC3)) return; /* enable AC3 */ if (rate && rate != 44100) rate = 48000; if (rate == 48000 || rate == 44100) { // mute DAC maskb(s->iobase + CODEC_CMI_MIXER1, ~0, WSMUTE); if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0, MUTECH1); // AC3EN for 039, 0x04 if (s->chip_version >= 39) { maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, AC3_EN); if (s->chip_version == 55) maskb(s->iobase + CODEC_CMI_SPDIF_CTRL, ~2, 0); // AC3EN for 037, 0x10 } else if (s->chip_version == 37) maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); if (s->capability & CAN_AC3_HW) { // SPD24SEL for 039, 0x20, but cannot be set if (s->chip_version == 39) maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, SPD24SEL); // SPD24SEL for 037, 0x02 else if (s->chip_version == 37) maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, 0); s->status |= DO_AC3_HW; } else { // SPD32SEL for 037 & 039 maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, SPD32SEL); // 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, ~WSMUTE, 0); if (s->chip_version >= 39) maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~MUTECH1, 0); maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~(SPD24SEL|0x12), 0); maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~(SPD32SEL|AC3_EN), 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -