📄 cmipci.c
字号:
static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device){ snd_pcm_t *pcm; int err; err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); pcm->private_data = cm; pcm->private_free = snd_cmipci_pcm_free; pcm->info_flags = 0; strcpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0;}/* * mixer interface: * - CM8338/8738 has a compatible mixer interface with SB16, but * lack of some elements like tone control, i/o gain and AGC. * - Access to native registers: * - A 3D switch * - Output mute switches */static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data){ outb(idx, s->iobase + CM_REG_SB16_ADDR); outb(data, s->iobase + CM_REG_SB16_DATA);}static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx){ unsigned char v; outb(idx, s->iobase + CM_REG_SB16_ADDR); v = inb(s->iobase + CM_REG_SB16_DATA); return v;}/* * general mixer element */typedef struct cmipci_sb_reg { unsigned int left_reg, right_reg; unsigned int left_shift, right_shift; unsigned int mask; unsigned int invert: 1; unsigned int stereo: 1;} cmipci_sb_reg_t;#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \ ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23))#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_volume, \ .get = snd_cmipci_get_volume, .put = snd_cmipci_put_volume, \ .private_value = COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \}#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1)#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0)#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1)#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0)static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val){ r->left_reg = val & 0xff; r->right_reg = (val >> 8) & 0xff; r->left_shift = (val >> 16) & 0x07; r->right_shift = (val >> 19) & 0x07; r->invert = (val >> 22) & 1; r->stereo = (val >> 23) & 1; r->mask = (val >> 24) & 0xff;}static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo){ cmipci_sb_reg_t reg; cmipci_sb_reg_decode(®, kcontrol->private_value); uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = reg.stereo + 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = reg.mask; return 0;} static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; int val; cmipci_sb_reg_decode(®, kcontrol->private_value); spin_lock_irq(&cm->reg_lock); val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[0] = val; if (reg.stereo) { val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[1] = val; } spin_unlock_irq(&cm->reg_lock); return 0;}static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; int change; int left, right, oleft, oright; cmipci_sb_reg_decode(®, kcontrol->private_value); left = ucontrol->value.integer.value[0] & reg.mask; if (reg.invert) left = reg.mask - left; left <<= reg.left_shift; if (reg.stereo) { right = ucontrol->value.integer.value[1] & reg.mask; if (reg.invert) right = reg.mask - right; right <<= reg.right_shift; } else right = 0; spin_lock_irq(&cm->reg_lock); oleft = snd_cmipci_mixer_read(cm, reg.left_reg); left |= oleft & ~(reg.mask << reg.left_shift); change = left != oleft; if (reg.stereo) { if (reg.left_reg != reg.right_reg) { snd_cmipci_mixer_write(cm, reg.left_reg, left); oright = snd_cmipci_mixer_read(cm, reg.right_reg); } else oright = left; right |= oright & ~(reg.mask << reg.right_shift); change |= right != oright; snd_cmipci_mixer_write(cm, reg.right_reg, right); } else snd_cmipci_mixer_write(cm, reg.left_reg, left); spin_unlock_irq(&cm->reg_lock); return change;}/* * input route (left,right) -> (left,right) */#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_input_sw, \ .get = snd_cmipci_get_input_sw, .put = snd_cmipci_put_input_sw, \ .private_value = COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \}static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 4; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;} static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; int val1, val2; cmipci_sb_reg_decode(®, kcontrol->private_value); spin_lock_irq(&cm->reg_lock); val1 = snd_cmipci_mixer_read(cm, reg.left_reg); val2 = snd_cmipci_mixer_read(cm, reg.right_reg); spin_unlock_irq(&cm->reg_lock); ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1; return 0;}static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; int change; int val1, val2, oval1, oval2; cmipci_sb_reg_decode(®, kcontrol->private_value); spin_lock_irq(&cm->reg_lock); oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift; val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift; val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift; val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift; change = val1 != oval1 || val2 != oval2; snd_cmipci_mixer_write(cm, reg.left_reg, val1); snd_cmipci_mixer_write(cm, reg.right_reg, val2); spin_unlock_irq(&cm->reg_lock); return change;}/* * native mixer switches/volumes */#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_native_mixer, \ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \}#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_native_mixer, \ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \}#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_native_mixer, \ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \}#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_cmipci_info_native_mixer, \ .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \}static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ cmipci_sb_reg_t reg; cmipci_sb_reg_decode(®, kcontrol->private_value); uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = reg.stereo + 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = reg.mask; return 0;}static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; unsigned char oreg, val; cmipci_sb_reg_decode(®, kcontrol->private_value); spin_lock_irq(&cm->reg_lock); oreg = inb(cm->iobase + reg.left_reg); val = (oreg >> reg.left_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[0] = val; if (reg.stereo) { val = (oreg >> reg.right_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[1] = val; } spin_unlock_irq(&cm->reg_lock); return 0;}static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; unsigned char oreg, nreg, val; cmipci_sb_reg_decode(®, kcontrol->private_value); spin_lock_irq(&cm->reg_lock); oreg = inb(cm->iobase + reg.left_reg); val = ucontrol->value.integer.value[0] & reg.mask; if (reg.invert) val = reg.mask - val; nreg = oreg & ~(reg.mask << reg.left_shift); nreg |= (val << reg.left_shift); if (reg.stereo) { val = ucontrol->value.integer.value[1] & reg.mask; if (reg.invert) val = reg.mask - val; nreg &= ~(reg.mask << reg.right_shift); nreg |= (val << reg.right_shift); } outb(nreg, cm->iobase + reg.left_reg); spin_unlock_irq(&cm->reg_lock); return (nreg != oreg);}/* * special case - check mixer sensitivity */static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ //cmipci_t *cm = snd_kcontrol_chip(kcontrol); return snd_cmipci_get_native_mixer(kcontrol, ucontrol);}static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ cmipci_t *cm = snd_kcontrol_chip(kcontrol); if (cm->mixer_insensitive) { /* ignored */ return 0; } return snd_cmipci_put_native_mixer(kcontrol, ucontrol);}static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31), //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1), { /* switch with sensitivity */ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", .info = snd_cmipci_info_native_mixer, .get = snd_cmipci_get_native_mixer_sensitive, .put = snd_cmipci_put_native_mixer_sensitive, .private_value = COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0), }, CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0), CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31), CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1), CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5), CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31), CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1), CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1), CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31), CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3), CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3), CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),};/* * other switches */typedef struct snd_cmipci_switch_args { int reg; /* register index */ unsigned int mask; /* mask bits */ unsigned int mask_on; /* mask bits to turn on */ int is_byte: 1; /* byte access? */ int ac3_sensitive: 1; /* access forbidden during non-audio operation? */} snd_cmipci_switch_args_t;static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args){ unsigned int val; cmipci_t *cm = snd_kcontrol_chip(kcontrol); spin_lock_irq(&cm->reg_lock); if (args->ac3_sensitive && cm->mixer_insensitive) { ucontrol->value.integer.value[0] = 0; spin_unlock_irq(&cm->reg_lock); return 0; } if (args->is_byte) val = inb(cm->iobase + args->reg); else val = snd_cmipci_read(cm, args->reg); ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; spin_unlock_irq(&cm->reg_lock); return 0;}static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; snd_assert(args != NULL, return -EINVAL); return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);}static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args){ unsigned int val; int change; cmipci_t *cm = snd_kcontrol_chip(kcontrol); spin_lock_irq(&cm->reg_lock); if (args->ac3_sensitive && cm->mixer_insensitive) { /* ignored */ spin_unlock_irq(&cm->reg_lock); return 0; } if (args->is_byte) val = inb(cm->iobase + args->reg); else val = snd_cmipci_read(cm, args->reg); change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0); if (ch
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -