📄 hda_codec.c
字号:
hda_nid_t nid = get_amp_nid(kcontrol); int dir = get_amp_direction(kcontrol); u32 caps, val1, val2; if (size < 4 * sizeof(unsigned int)) return -ENOMEM; caps = query_amp_caps(codec, nid, dir); val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; val2 = (val2 + 1) * 25; val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 = ((int)val1) * ((int)val2); if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) return -EFAULT; if (put_user(2 * sizeof(unsigned int), _tlv + 1)) return -EFAULT; if (put_user(val1, _tlv + 2)) return -EFAULT; if (put_user(val2, _tlv + 3)) return -EFAULT; return 0;}/* switch */int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int chs = get_amp_channels(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = chs == 3 ? 2 : 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); long *valp = ucontrol->value.integer.value; if (chs & 1) *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & HDA_AMP_MUTE) ? 0 : 1; if (chs & 2) *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & HDA_AMP_MUTE) ? 0 : 1; return 0;}int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); long *valp = ucontrol->value.integer.value; int change = 0; snd_hda_power_up(codec); if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); valp++; } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE);#ifdef CONFIG_SND_HDA_POWER_SAVE if (codec->patch_ops.check_power_status) codec->patch_ops.check_power_status(codec, nid);#endif snd_hda_power_down(codec); return change;}/* * bound volume controls * * bind multiple volumes (# indices, from 0) */#define AMP_VAL_IDX_SHIFT 19#define AMP_VAL_IDX_MASK (0x0f<<19)int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned long pval; int err; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ pval = kcontrol->private_value; kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); kcontrol->private_value = pval; mutex_unlock(&codec->spdif_mutex); return err;}int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned long pval; int i, indices, err = 0, change = 0; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ pval = kcontrol->private_value; indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; for (i = 0; i < indices; i++) { kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | (i << AMP_VAL_IDX_SHIFT); err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); if (err < 0) break; change |= err; } kcontrol->private_value = pval; mutex_unlock(&codec->spdif_mutex); return err < 0 ? err : change;}/* * generic bound volume/swtich controls */int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_bind_ctls *c; int err; c = (struct hda_bind_ctls *)kcontrol->private_value; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ kcontrol->private_value = *c->values; err = c->ops->info(kcontrol, uinfo); kcontrol->private_value = (long)c; mutex_unlock(&codec->spdif_mutex); return err;}int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_bind_ctls *c; int err; c = (struct hda_bind_ctls *)kcontrol->private_value; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ kcontrol->private_value = *c->values; err = c->ops->get(kcontrol, ucontrol); kcontrol->private_value = (long)c; mutex_unlock(&codec->spdif_mutex); return err;}int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_bind_ctls *c; unsigned long *vals; int err = 0, change = 0; c = (struct hda_bind_ctls *)kcontrol->private_value; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ for (vals = c->values; *vals; vals++) { kcontrol->private_value = *vals; err = c->ops->put(kcontrol, ucontrol); if (err < 0) break; change |= err; } kcontrol->private_value = (long)c; mutex_unlock(&codec->spdif_mutex); return err < 0 ? err : change;}int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_bind_ctls *c; int err; c = (struct hda_bind_ctls *)kcontrol->private_value; mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ kcontrol->private_value = *c->values; err = c->ops->tlv(kcontrol, op_flag, size, tlv); kcontrol->private_value = (long)c; mutex_unlock(&codec->spdif_mutex); return err;}struct hda_ctl_ops snd_hda_bind_vol = { .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = snd_hda_mixer_amp_volume_put, .tlv = snd_hda_mixer_amp_tlv};struct hda_ctl_ops snd_hda_bind_sw = { .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = snd_hda_mixer_amp_switch_put, .tlv = snd_hda_mixer_amp_tlv};/* * SPDIF out controls */static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0;}static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *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; return 0;}static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_5015; return 0;}static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff; ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff; ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff; return 0;}/* convert from SPDIF status bits to HDA SPDIF bits * bit 0 (DigEn) is always set zero (to be filled later) */static unsigned short convert_from_spdif_status(unsigned int sbits){ unsigned short val = 0; if (sbits & IEC958_AES0_PROFESSIONAL) val |= AC_DIG1_PROFESSIONAL; if (sbits & IEC958_AES0_NONAUDIO) val |= AC_DIG1_NONAUDIO; if (sbits & IEC958_AES0_PROFESSIONAL) { if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) val |= AC_DIG1_EMPHASIS; } else { if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) val |= AC_DIG1_EMPHASIS; if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) val |= AC_DIG1_COPYRIGHT; if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) val |= AC_DIG1_LEVEL; val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); } return val;}/* convert to SPDIF status bits from HDA SPDIF bits */static unsigned int convert_to_spdif_status(unsigned short val){ unsigned int sbits = 0; if (val & AC_DIG1_NONAUDIO) sbits |= IEC958_AES0_NONAUDIO; if (val & AC_DIG1_PROFESSIONAL) sbits |= IEC958_AES0_PROFESSIONAL; if (sbits & IEC958_AES0_PROFESSIONAL) { if (sbits & AC_DIG1_EMPHASIS) sbits |= IEC958_AES0_PRO_EMPHASIS_5015; } else { if (val & AC_DIG1_EMPHASIS) sbits |= IEC958_AES0_CON_EMPHASIS_5015; if (!(val & AC_DIG1_COPYRIGHT)) sbits |= IEC958_AES0_CON_NOT_COPYRIGHT; if (val & AC_DIG1_LEVEL) sbits |= (IEC958_AES1_CON_ORIGINAL << 8); sbits |= val & (0x7f << 8); } return sbits;}static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); codec->spdif_status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | ((unsigned int)ucontrol->value.iec958.status[3] << 24); val = convert_from_spdif_status(codec->spdif_status); val |= codec->spdif_ctls & 1; change = codec->spdif_ctls != val; codec->spdif_ctls = val; if (change) { snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8); } mutex_unlock(&codec->spdif_mutex); return change;}#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_infostatic int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE; return 0;}static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); val = codec->spdif_ctls & ~AC_DIG1_ENABLE; if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; change = codec->spdif_ctls != val; if (change) { codec->spdif_ctls = val; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); /* unmute amp switch (if any) */ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && (val & AC_DIG1_ENABLE)) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); } mutex_unlock(&codec->spdif_mutex); return change;}static struct snd_kcontrol_new dig_mixes[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_cmask_get, }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_pmask_get, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_default_get, .put = snd_hda_spdif_default_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), .info = snd_hda_spdif_out_switch_info, .get = snd_hda_spdif_out_switch_get, .put = snd_hda_spdif_out_switch_put, }, { } /* end */};/** * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls * @codec: the HDA codec * @nid: audio out widget NID * * Creates controls related with the SPDIF output. * Called from each patch supporting the SPDIF out. * * Returns 0 if successful, or a negative error code. */int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid){ int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; } codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); return 0;}/* * SPDIF input */#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_infostatic int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = codec->spdif_in_enable; return 0;}static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned int val = !!ucontrol->value.integer.value[0]; int change; mutex_lock(&codec->spdif_mutex); change = codec->spdif_in_enable != val; if (change) { codec->spdif_in_enable = val; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val); } mutex_unlock(&codec->spdif_mutex); return change;}static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; unsigned int sbits; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; ucontrol->value.iec958.status[2] = sbits >> 16;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -