📄 hda_codec.c
字号:
codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID); setup_fg_nodes(codec); if (! codec->afg && ! codec->mfg) { snd_printdd("hda_codec: no AFG or MFG node found\n"); snd_hda_codec_free(codec); return -ENODEV; } if (! codec->subsystem_id) { hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; codec->subsystem_id = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_SUBSYSTEM_ID, 0); } codec->preset = find_codec_preset(codec); if (! *bus->card->mixername) snd_hda_get_codec_name(codec, bus->card->mixername, sizeof(bus->card->mixername)); if (codec->preset && codec->preset->patch) err = codec->preset->patch(codec); else err = snd_hda_parse_generic_codec(codec); if (err < 0) { snd_hda_codec_free(codec); return err; } snd_hda_codec_proc_new(codec); sprintf(component, "HDA:%08x", codec->vendor_id); snd_component_add(codec->bus->card, component); if (codecp) *codecp = codec; return 0;}/** * snd_hda_codec_setup_stream - set up the codec for streaming * @codec: the CODEC to set up * @nid: the NID to set up * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. * @channel_id: channel id to pass, zero based. * @format: stream format. */void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format){ if (! nid) return; snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | channel_id); msleep(1); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);}/* * amp access functions *//* FIXME: more better hash key? */#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))#define INFO_AMP_CAPS (1<<0)#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))/* initialize the hash table */static void init_amp_hash(struct hda_codec *codec){ memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash)); codec->num_amp_entries = 0;}/* query the hash. allocate an entry if not found. */static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key){ u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash); u16 cur = codec->amp_hash[idx]; struct hda_amp_info *info; while (cur != 0xffff) { info = &codec->amp_info[cur]; if (info->key == key) return info; cur = info->next; } /* add a new hash entry */ if (codec->num_amp_entries >= ARRAY_SIZE(codec->amp_info)) { snd_printk(KERN_ERR "hda_codec: Tooooo many amps!\n"); return NULL; } cur = codec->num_amp_entries++; info = &codec->amp_info[cur]; info->key = key; info->status = 0; /* not initialized yet */ info->next = codec->amp_hash[idx]; codec->amp_hash[idx] = cur; return info;}/* * query AMP capabilities for the given widget and direction */static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction){ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); if (! info) return 0; if (! (info->status & INFO_AMP_CAPS)) { if (!(snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD)) nid = codec->afg; info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); info->status |= INFO_AMP_CAPS; } return info->amp_caps;}/* * read the current volume to info * if the cache exists, read the cache value. */static unsigned int get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index){ u32 val, parm; if (info->status & INFO_AMP_VOL(ch)) return info->vol[ch]; parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; parm |= index; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); info->vol[ch] = val & 0xff; info->status |= INFO_AMP_VOL(ch); return info->vol[ch];}/* * write the current volume in info to the h/w and update the cache */static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, int val){ u32 parm; parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; parm |= val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); info->vol[ch] = val;}/* * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index){ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); if (! info) return 0; return get_vol_mute(codec, info, nid, ch, direction, index);}/* * update the AMP value, mask = bit mask to set, val = the value */static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val){ struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); if (! info) return 0; val &= mask; val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; if (info->vol[ch] == val && ! codec->in_resume) return 0; put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1;}/* * AMP control callbacks *//* retrieve parameters from private_value */#define get_amp_nid(kc) ((kc)->private_value & 0xffff)#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)/* volume */int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); u16 nid = get_amp_nid(kcontrol); u8 chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); u32 caps; caps = query_amp_caps(codec, nid, dir); caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; /* num steps */ if (! caps) { printk(KERN_WARNING "hda_codec: num_steps = 0 for NID=0x%x\n", nid); return -EINVAL; } uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chs == 3 ? 2 : 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = caps; return 0;}int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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) & 0x7f; if (chs & 2) *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; return 0;}int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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; if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 0x7f, *valp); valp++; } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 0x7f, *valp); return change;}/* switch */int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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) & 0x80) ? 0 : 1; if (chs & 2) *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80) ? 0 : 1; return 0;}int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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; if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 0x80, *valp ? 0 : 0x80); valp++; } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 0x80, *valp ? 0 : 0x80); 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(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned long pval; int err; down(&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; up(&codec->spdif_mutex); return err;}int snd_hda_mixer_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned long pval; int i, indices, err = 0, change = 0; down(&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; up(&codec->spdif_mutex); return err < 0 ? err : change;}/* * SPDIF out controls */static int snd_hda_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_hda_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; return 0;}static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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 |= 1 << 6; if (sbits & IEC958_AES0_NONAUDIO) val |= 1 << 5; if (sbits & IEC958_AES0_PROFESSIONAL) { if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) val |= 1 << 3; } else { if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) val |= 1 << 3; if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) val |= 1 << 4; if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) val |= 1 << 7; 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 & (1 << 5)) sbits |= IEC958_AES0_NONAUDIO; if (val & (1 << 6)) sbits |= IEC958_AES0_PROFESSIONAL; if (sbits & IEC958_AES0_PROFESSIONAL) { if (sbits & (1 << 3)) sbits |= IEC958_AES0_PRO_EMPHASIS_5015; } else { if (val & (1 << 3)) sbits |= IEC958_AES0_CON_EMPHASIS_5015; if (! (val & (1 << 4))) sbits |= IEC958_AES0_CON_NOT_COPYRIGHT; if (val & (1 << 7)) sbits |= (IEC958_AES1_CON_ORIGINAL << 8); sbits |= val & (0x7f << 8); } return sbits;}static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; int change; down(&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 || codec->in_resume) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8); } up(&codec->spdif_mutex); return change;}static int snd_hda_spdif_out_switch_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_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = codec->spdif_ctls & 1; return 0;}static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned short val; int change; down(&codec->spdif_mutex); val = codec->spdif_ctls & ~1; if (ucontrol->value.integer.value[0]) val |= 1; change = codec->spdif_ctls != val; if (change || codec->in_resume) { codec->spdif_ctls = val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -