📄 ac97_codec.c
字号:
change = snd_ac97_update_bits_nolock(ac97, reg, mask, value); up(&ac97->reg_mutex); return change;}/* 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); } set_bit(reg, ac97->reg_accessed); 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;}/* * Controls */int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = e->shift_l == e->shift_r ? 1 : 2; uinfo->value.enumerated.items = e->mask; if (uinfo->value.enumerated.item > e->mask - 1) uinfo->value.enumerated.item = e->mask - 1; strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]); return 0;}int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; unsigned short val, bitmask; for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) ; val = snd_ac97_read_cache(ac97, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1); return 0;}int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; unsigned short val; unsigned short mask, bitmask; for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) ; if (ucontrol->value.enumerated.item[0] > e->mask - 1) return -EINVAL; val = ucontrol->value.enumerated.item[0] << e->shift_l; mask = (bitmask - 1) << e->shift_l; if (e->shift_l != e->shift_r) { if (ucontrol->value.enumerated.item[1] > e->mask - 1) return -EINVAL; val |= ucontrol->value.enumerated.item[1] << e->shift_r; mask |= (bitmask - 1) << e->shift_r; } return snd_ac97_update_bits(ac97, e->reg, mask, val);}/* save/restore ac97 v2.3 paging */static int snd_ac97_page_save(ac97_t *ac97, int reg, snd_kcontrol_t *kcontrol){ int page_save = -1; if ((kcontrol->private_value & (1<<25)) && (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && (reg >= 0x60 && reg < 0x70)) { unsigned short page = (kcontrol->private_value >> 26) & 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); } return page_save;}static void snd_ac97_page_restore(ac97_t *ac97, int page_save){ if (page_save >= 0) { snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); up(&ac97->page_mutex); /* unlock paging */ }}/* volume and switch controls */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; int page_save; page_save = snd_ac97_page_save(ac97, reg, kcontrol); 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]; } snd_ac97_page_restore(ac97, page_save); 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; int err, page_save; unsigned short val, val2, val_mask; page_save = snd_ac97_page_save(ac97, reg, kcontrol); 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; } err = snd_ac97_update_bits(ac97, reg, val_mask, val); snd_ac97_page_restore(ac97, page_save); return err;}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 char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};static const char* std_3d_path[] = {"pre 3D", "post 3D"};static const char* std_mix[] = {"Mix", "Mic"};static const char* std_mic[] = {"Mic1", "Mic2"};static const struct ac97_enum std_enum[] = {AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel),AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path),AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix),AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic),};static const snd_kcontrol_new_t snd_ac97_control_capture_src = AC97_ENUM("Capture Source", std_enum[0]); 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("PCM Out Path & Mute", std_enum[1]),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("Mono Output Select", std_enum[2]),AC97_ENUM("Mic Select", std_enum[3]),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_control_eapd =AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);static const snd_kcontrol_new_t snd_ac97_controls_modem_switches[2] = {AC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0),AC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0)};/* 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; } if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) val |= 1<<3; } else { new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24); if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) val |= 1<<3; if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT)) val |= 1<<2; val |= ((new >> 8) & 0xff) << 4; // category + original switch ((new >> 24) & 0xff) { case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; default: val |= 1<<12; break; } } down(&ac97->reg_mutex); change = ac97->spdif_status != new; ac97->spdif_status = new; if (ac97->flags & AC97_CS_SPDIF) { int x = (val >> 12) & 0x03; switch (x) { case 0: x = 1; break; // 44.1 case 2: x = 0; break; // 48.0 default: x = 0; break; // illegal. } change |= snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12))); } else if (ac97->flags & AC97_CX_SPDIF) { int v; v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT; v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM; change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC, AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, v); } else { unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS); snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ change |= snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -