📄 ac97_codec.c
字号:
/* no lock version - see snd_ac97_updat_bits() */int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value){ int change; unsigned short old, new; old = snd_ac97_read_cache(ac97, reg); new = (old & ~mask) | value; change = old != new; if (change) { ac97->regs[reg] = new; ac97->bus->ops->write(ac97, reg, new); } return change;}static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value){ int change; unsigned short old, new, cfg; down(&ac97->page_mutex); old = ac97->spec.ad18xx.pcmreg[codec]; new = (old & ~mask) | value; change = old != new; if (change) { down(&ac97->reg_mutex); cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG); ac97->spec.ad18xx.pcmreg[codec] = new; /* select single codec */ ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, (cfg & ~0x7000) | ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); /* update PCM bits */ ac97->bus->ops->write(ac97, AC97_PCM, new); /* select all codecs */ ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, cfg | 0x7000); up(&ac97->reg_mutex); } up(&ac97->page_mutex); return change;}/* * */static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ static char *texts[8] = { "Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 2; uinfo->value.enumerated.items = 8; if (uinfo->value.enumerated.item > 7) uinfo->value.enumerated.item = 7; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; val = snd_ac97_read_cache(ac97, AC97_REC_SEL); ucontrol->value.enumerated.item[0] = (val >> 8) & 7; ucontrol->value.enumerated.item[1] = (val >> 0) & 7; return 0;}static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; if (ucontrol->value.enumerated.item[0] > 7 || ucontrol->value.enumerated.item[1] > 7) return -EINVAL; val = (ucontrol->value.enumerated.item[0] << 8) | (ucontrol->value.enumerated.item[1] << 0); return snd_ac97_update(ac97, AC97_REC_SEL, val);}#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \ .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \ .private_value = reg | (shift << 8) | (invert << 24) }static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ static char *texts1[2] = { "pre 3D", "post 3D" }; static char *texts2[2] = { "Mix", "Mic" }; static char *texts3[2] = { "Mic1", "Mic2" }; char **texts = NULL; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; switch (reg) { case AC97_GENERAL_PURPOSE: switch (shift) { case 15: texts = texts1; break; case 9: texts = texts2; break; case 8: texts = texts3; break; } } uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 2; if (uinfo->value.enumerated.item > 1) uinfo->value.enumerated.item = 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; val = (snd_ac97_read_cache(ac97, reg) >> shift) & 1; if (invert) val ^= 1; ucontrol->value.enumerated.item[0] = val; return 0;}static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; if (ucontrol->value.enumerated.item[0] > 1) return -EINVAL; val = !!ucontrol->value.enumerated.item[0]; if (invert) val = !val; return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift);}int snd_ac97_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = shift == rshift ? 1 : 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}int snd_ac97_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask; if (shift != rshift) ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; if (shift != rshift) ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; } return 0;}int snd_ac97_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; unsigned short val, val2, val_mask; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; val_mask = mask << shift; val = val << shift; if (shift != rshift) { val2 = (ucontrol->value.integer.value[1] & mask); if (invert) val2 = mask - val2; val_mask |= mask << rshift; val |= val2 << rshift; } return snd_ac97_update_bits(ac97, reg, val_mask, val);}#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \ .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }static int snd_ac97_getput_page(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, int (*func)(snd_kcontrol_t *, snd_ctl_elem_value_t *)){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int err; if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && (reg >= 0x60 && reg < 0x70)) { unsigned short page_save; unsigned short page = (kcontrol->private_value >> 25) & 0x0f; down(&ac97->page_mutex); /* lock paging */ page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); err = func(kcontrol, ucontrol); snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); up(&ac97->page_mutex); /* unlock paging */ } else err = func(kcontrol, ucontrol); return err;}/* for rev2.3 paging */int snd_ac97_page_get_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_get_volsw);}/* for rev2.3 paging */int snd_ac97_page_put_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_put_volsw);}static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = {AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)};static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = {AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)};static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = {AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)};static const snd_kcontrol_new_t snd_ac97_controls_mic_boost = AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);static const snd_kcontrol_new_t snd_ac97_control_capture_src = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", .info = snd_ac97_info_mux, .get = snd_ac97_get_mux, .put = snd_ac97_put_mux,};static const snd_kcontrol_new_t snd_ac97_control_capture_vol =AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = {AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1),AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0)};typedef enum { AC97_GENERAL_PCM_OUT = 0, AC97_GENERAL_STEREO_ENHANCEMENT, AC97_GENERAL_3D, AC97_GENERAL_LOUDNESS, AC97_GENERAL_MONO, AC97_GENERAL_MIC, AC97_GENERAL_LOOPBACK} ac97_general_index_t;static const snd_kcontrol_new_t snd_ac97_controls_general[7] = {AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0),AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0),AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0),AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)};const snd_kcontrol_new_t snd_ac97_controls_3d[2] = {AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)};static const snd_kcontrol_new_t snd_ac97_controls_center[2] = {AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1),AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1)};static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = {AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)};static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = {AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),};static const snd_kcontrol_new_t snd_ac97_control_eapd =AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);/* change the existing EAPD control as inverted */static void set_inv_eapd(ac97_t *ac97, snd_kcontrol_t *kctl){ kctl->private_value = AC97_SINGLE_VALUE(AC97_POWERDOWN, 15, 1, 0); snd_ac97_update_bits(ac97, AC97_POWERDOWN, (1<<15), (1<<15)); /* EAPD up */ ac97->scaps |= AC97_SCAP_INV_EAPD;}static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0;} static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_NONAUDIO | IEC958_AES0_CON_EMPHASIS_5015 | IEC958_AES0_CON_NOT_COPYRIGHT; ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL; ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; return 0;} static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ /* FIXME: AC'97 spec doesn't say which bits are used for what */ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_FS | IEC958_AES0_PRO_EMPHASIS_5015; return 0;}static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); down(&ac97->reg_mutex); ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff; ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff; ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff; up(&ac97->reg_mutex); return 0;} static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned int new = 0; unsigned short val = 0; int change; new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); switch (new & IEC958_AES0_PRO_FS) { case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break; case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break; default: val |= 1<<12; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -