/* * Universal Interface for Intel High Definition Audio Codec * * Generic widget tree parser * * Copyright (c) 2004 Takashi Iwai * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_beep.h" #include "hda_generic.h" /** * snd_hda_gen_spec_init - initialize hda_gen_spec struct * @spec: hda_gen_spec object to initialize * * Initialize the given hda_gen_spec object. */ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8); mutex_init(&spec->pcm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); /** * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template * @spec: hda_gen_spec object * @name: name string to override the template, NULL if unchanged * @temp: template for the new kctl * * Add a new kctl (actually snd_kcontrol_new to be instantiated later) * element based on the given snd_kcontrol_new template @temp and the * name string @name to the list in @spec. * Returns the newly created object or NULL as error. */ struct snd_kcontrol_new * snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, const struct snd_kcontrol_new *temp) { struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); if (!knew) return NULL; *knew = *temp; if (name) knew->name = kstrdup(name, GFP_KERNEL); else if (knew->name) knew->name = kstrdup(knew->name, GFP_KERNEL); if (!knew->name) return NULL; return knew; } EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl); static void free_kctls(struct hda_gen_spec *spec) { if (spec->kctls.list) { struct snd_kcontrol_new *kctl = spec->kctls.list; int i; for (i = 0; i < spec->kctls.used; i++) kfree(kctl[i].name); } snd_array_free(&spec->kctls); } static void snd_hda_gen_spec_free(struct hda_gen_spec *spec) { if (!spec) return; free_kctls(spec); snd_array_free(&spec->paths); snd_array_free(&spec->loopback_list); } /* * store user hints */ static void parse_user_hints(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int val; val = snd_hda_get_bool_hint(codec, "jack_detect"); if (val >= 0) codec->no_jack_detect = !val; val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); if (val >= 0) codec->inv_jack_detect = !!val; val = snd_hda_get_bool_hint(codec, "trigger_sense"); if (val >= 0) codec->no_trigger_sense = !val; val = snd_hda_get_bool_hint(codec, "inv_eapd"); if (val >= 0) codec->inv_eapd = !!val; val = snd_hda_get_bool_hint(codec, "pcm_format_first"); if (val >= 0) codec->pcm_format_first = !!val; val = snd_hda_get_bool_hint(codec, "sticky_stream"); if (val >= 0) codec->no_sticky_stream = !val; val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); if (val >= 0) codec->spdif_status_reset = !!val; val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); if (val >= 0) codec->pin_amp_workaround = !!val; val = snd_hda_get_bool_hint(codec, "single_adc_amp"); if (val >= 0) codec->single_adc_amp = !!val; val = snd_hda_get_bool_hint(codec, "power_save_node"); if (val >= 0) codec->power_save_node = !!val; val = snd_hda_get_bool_hint(codec, "auto_mute"); if (val >= 0) spec->suppress_auto_mute = !val; val = snd_hda_get_bool_hint(codec, "auto_mic"); if (val >= 0) spec->suppress_auto_mic = !val; val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); if (val >= 0) spec->line_in_auto_switch = !!val; val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); if (val >= 0) spec->auto_mute_via_amp = !!val; val = snd_hda_get_bool_hint(codec, "need_dac_fix"); if (val >= 0) spec->need_dac_fix = !!val; val = snd_hda_get_bool_hint(codec, "primary_hp"); if (val >= 0) spec->no_primary_hp = !val; val = snd_hda_get_bool_hint(codec, "multi_io"); if (val >= 0) spec->no_multi_io = !val; val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); if (val >= 0) spec->multi_cap_vol = !!val; val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); if (val >= 0) spec->inv_dmic_split = !!val; val = snd_hda_get_bool_hint(codec, "indep_hp"); if (val >= 0) spec->indep_hp = !!val; val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); if (val >= 0) spec->add_stereo_mix_input = !!val; /* the following two are just for compatibility */ val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); if (val >= 0) spec->add_jack_modes = !!val; val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); if (val >= 0) spec->add_jack_modes = !!val; val = snd_hda_get_bool_hint(codec, "add_jack_modes"); if (val >= 0) spec->add_jack_modes = !!val; val = snd_hda_get_bool_hint(codec, "power_down_unused"); if (val >= 0) spec->power_down_unused = !!val; val = snd_hda_get_bool_hint(codec, "add_hp_mic"); if (val >= 0) spec->hp_mic = !!val; val = snd_hda_get_bool_hint(codec, "hp_mic_detect"); if (val >= 0) spec->suppress_hp_mic_detect = !val; val = snd_hda_get_bool_hint(codec, "vmaster"); if (val >= 0) spec->suppress_vmaster = !val; if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; } /* * pin control value accesses */ #define update_pin_ctl(codec, pin, val) \ snd_hda_codec_write_cache(codec, pin, 0, \ AC_VERB_SET_PIN_WIDGET_CONTROL, val) /* restore the pinctl based on the cached value */ static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) { update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); } /* set the pinctl target value and write it if requested */ static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool do_write) { if (!pin) return; val = snd_hda_correct_pin_ctl(codec, pin, val); snd_hda_codec_set_pin_target(codec, pin, val); if (do_write) update_pin_ctl(codec, pin, val); } /* set pinctl target values for all given pins */ static void set_pin_targets(struct hda_codec *codec, int num_pins, hda_nid_t *pins, unsigned int val) { int i; for (i = 0; i < num_pins; i++) set_pin_target(codec, pins[i], val, false); } /* * parsing paths */ /* return the position of NID in the list, or -1 if not found */ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { int i; for (i = 0; i < nums; i++) if (list[i] == nid) return i; return -1; } /* return true if the given NID is contained in the path */ static bool is_nid_contained(struct nid_path *path, hda_nid_t nid) { return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; } static struct nid_path *get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int anchor_nid) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; int i; snd_array_for_each(&spec->paths, i, path) { if (path->depth <= 0) continue; if ((!from_nid || path->path[0] == from_nid) && (!to_nid || path->path[path->depth - 1] == to_nid)) { if (!anchor_nid || (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) return path; } } return NULL; } /** * snd_hda_get_path_idx - get the index number corresponding to the path * instance * @codec: the HDA codec * @path: nid_path object * * The returned index starts from 1, i.e. the actual array index with offset 1, * and zero is handled as an invalid path */ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; struct nid_path *array = spec->paths.list; ssize_t idx; if (!spec->paths.used) return 0; idx = path - array; if (idx < 0 || idx >= spec->paths.used) return 0; return idx + 1; } EXPORT_SYMBOL_GPL(snd_hda_get_path_idx); /** * snd_hda_get_path_from_idx - get the path instance corresponding to the * given index number * @codec: the HDA codec * @idx: the path index */ struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) { struct hda_gen_spec *spec = codec->spec; if (idx <= 0 || idx > spec->paths.used) return NULL; return snd_array_elem(&spec->paths, idx - 1); } EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx); /* check whether the given DAC is already found in any existing paths */ static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; const struct nid_path *path; int i; snd_array_for_each(&spec->paths, i, path) { if (path->path[0] == nid) return true; } return false; } /* check whether the given two widgets can be connected */ static bool is_reachable_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) { if (!from_nid || !to_nid) return false; return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; } /* nid, dir and idx */ #define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) /* check whether the given ctl is already assigned in any path elements */ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) { struct hda_gen_spec *spec = codec->spec; const struct nid_path *path; int i; val &= AMP_VAL_COMPARE_MASK; snd_array_for_each(&spec->paths, i, path) { if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) return true; } return false; } /* check whether a control with the given (nid, dir, idx) was assigned */ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int type) { unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); return is_ctl_used(codec, val, type); } static void print_nid_path(struct hda_codec *codec, const char *pfx, struct nid_path *path) { char buf[40]; char *pos = buf; int i; *pos = 0; for (i = 0; i < path->depth; i++) pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", pos != buf ? ":" : "", path->path[i]); codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); } /* called recursively */ static bool __parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int anchor_nid, struct nid_path *path, int depth) { const hda_nid_t *conn; int i, nums; if (to_nid == anchor_nid) anchor_nid = 0; /* anchor passed */ else if (to_nid == (hda_nid_t)(-anchor_nid)) return false; /* hit the exclusive nid */ nums = snd_hda_get_conn_list(codec, to_nid, &conn); for (i = 0; i < nums; i++) { if (conn[i] != from_nid) { /* special case: when from_nid is 0, * try to find an empty DAC */ if (from_nid || get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || is_dac_already_used(codec, conn[i])) continue; } /* anchor is not requested or already passed? */ if (anchor_nid <= 0) goto found; } if (depth >= MAX_NID_PATH_DEPTH) return false; for (i = 0; i < nums; i++) { unsigned int type; type = get_wcaps_type(get_wcaps(codec, conn[i])); if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || type == AC_WID_PIN) continue; if (__parse_nid_path(codec, from_nid, conn[i], anchor_nid, path, depth + 1)) goto found; } return false; found: path->path[path->depth] = conn[i]; path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; path->depth++; return true; } /* * snd_hda_parse_nid_path - parse the widget path from the given nid to * the target nid * @codec: the HDA codec * @from_nid: the NID where the path start from * @to_nid: the NID where the path ends at * @anchor_nid: the anchor indication * @path: the path object to store the result * * Returns true if a matching path is found. * * The parsing behavior depends on parameters: * when @from_nid is 0, try to find an empty DAC; * when @anchor_nid is set to a positive value, only paths through the widget * with the given value are evaluated. * when @anchor_nid is set to a negative value, paths through the widget * with the negative of given value are excluded, only other paths are chosen. * when @anchor_nid is zero, no special handling about path selection. */ static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int anchor_nid, struct nid_path *path) { if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { path->path[path->depth] = to_nid; path->depth++; return true; } return false; } /** * snd_hda_add_new_path - parse the path between the given NIDs and * add to the path list * @codec: the HDA codec * @from_nid: the NID where the path start from * @to_nid: the NID where the path ends at * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() * * If no valid path is found, returns NULL. */ struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int anchor_nid) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) return NULL; /* check whether the path has been already added */ path = get_nid_path(codec, from_nid, to_nid, anchor_nid); if (path) return path; path = snd_array_new(&spec->paths); if (!path) return NULL; memset(path, 0, sizeof(*path)); if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) return path; /* push back */ spec->paths.used--; return NULL; } EXPORT_SYMBOL_GPL(snd_hda_add_new_path); /* clear the given path as invalid so that it won't be picked up later */ static void invalidate_nid_path(struct hda_codec *codec, int idx) { struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); if (!path) return; memset(path, 0, sizeof(*path)); } /* return a DAC if paired to the given pin by codec driver */ static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; const hda_nid_t *list = spec->preferred_dacs; if (!list) return 0; for (; *list; list += 2) if (*list == pin) return list[1]; return 0; } /* look for an empty DAC slot */ static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, bool is_digital) { struct hda_gen_spec *spec = codec->spec; bool cap_digital; int i; for (i = 0; i < spec->num_all_dacs; i++) { hda_nid_t nid = spec->all_dacs[i]; if (!nid || is_dac_already_used(codec, nid)) continue; cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); if (is_digital != cap_digital) continue; if (is_reachable_path(codec, nid, pin)) return nid; } return 0; } /* replace the channels in the composed amp value with the given number */ static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) { val &= ~(0x3U << 16); val |= chs << 16; return val; } static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, hda_nid_t nid2, int dir) { if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); return (query_amp_caps(codec, nid1, dir) == query_amp_caps(codec, nid2, dir)); } /* look for a widget suitable for assigning a mute switch in the path */ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, struct nid_path *path) { int i; for (i = path->depth - 1; i >= 0; i--) { if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) return path->path[i]; if (i != path->depth - 1 && i != 0 && nid_has_mute(codec, path->path[i], HDA_INPUT)) return path->path[i]; } return 0; } /* look for a widget suitable for assigning a volume ctl in the path */ static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; int i; for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; if ((spec->out_vol_mask >> nid) & 1) continue; if (nid_has_volume(codec, nid, HDA_OUTPUT)) return nid; } return 0; } /* * path activation / deactivation */ /* can have the amp-in capability? */ static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { hda_nid_t nid = path->path[idx]; unsigned int caps = get_wcaps(codec, nid); unsigned int type = get_wcaps_type(caps); if (!(caps & AC_WCAP_IN_AMP)) return false; if (type == AC_WID_PIN && idx > 0) /* only for input pins */ return false; return true; } /* can have the amp-out capability? */ static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) { hda_nid_t nid = path->path[idx]; unsigned int caps = get_wcaps(codec, nid); unsigned int type = get_wcaps_type(caps); if (!(caps & AC_WCAP_OUT_AMP)) return false; if (type == AC_WID_PIN && !idx) /* only for output pins */ return false; return true; } /* check whether the given (nid,dir,idx) is active */ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, unsigned int dir, unsigned int idx) { struct hda_gen_spec *spec = codec->spec; int type = get_wcaps_type(get_wcaps(codec, nid)); const struct nid_path *path; int i, n; if (nid == codec->core.afg) return true; snd_array_for_each(&spec->paths, n, path) { if (!path->active) continue; if (codec->power_save_node) { if (!path->stream_enabled) continue; /* ignore unplugged paths except for DAC/ADC */ if (!(path->pin_enabled || path->pin_fixed) && type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) continue; } for (i = 0; i < path->depth; i++) { if (path->path[i] == nid) { if (dir == HDA_OUTPUT || idx == -1 || path->idx[i] == idx) return true; break; } } } return false; } /* check whether the NID is referred by any active paths */ #define is_active_nid_for_any(codec, nid) \ is_active_nid(codec, nid, HDA_OUTPUT, -1) /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps, bool enable) { unsigned int val = 0; if (caps & AC_AMPCAP_NUM_STEPS) { /* set to 0dB */ if (enable) val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { if (!enable) val |= HDA_AMP_MUTE; } return val; } /* is this a stereo widget or a stereo-to-mono mix? */ static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int wcaps = get_wcaps(codec, nid); hda_nid_t conn; if (wcaps & AC_WCAP_STEREO) return true; if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) return false; if (snd_hda_get_num_conns(codec, nid) != 1) return false; if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) return false; return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); } /* initialize the amp value (only at the first time) */ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { unsigned int caps = query_amp_caps(codec, nid, dir); int val = get_amp_val_to_activate(codec, nid, dir, caps, false); if (is_stereo_amps(codec, nid, dir)) snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); else snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); } /* update the amp, doing in stereo or mono depending on NID */ static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, unsigned int mask, unsigned int val) { if (is_stereo_amps(codec, nid, dir)) return snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); else return snd_hda_codec_amp_update(codec, nid, 0, dir, idx, mask, val); } /* calculate amp value mask we can modify; * if the given amp is controlled by mixers, don't touch it */ static unsigned int get_amp_mask_to_modify(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, unsigned int caps) { unsigned int mask = 0xff; if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) mask &= ~0x80; } if (caps & AC_AMPCAP_NUM_STEPS) { if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) mask &= ~0x7f; } return mask; } static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int idx_to_check, bool enable) { unsigned int caps; unsigned int mask, val; caps = query_amp_caps(codec, nid, dir); val = get_amp_val_to_activate(codec, nid, dir, caps, enable); mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); if (!mask) return; val &= mask; update_amp(codec, nid, dir, idx, mask, val); } static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int idx_to_check, bool enable) { /* check whether the given amp is still used by others */ if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) return; activate_amp(codec, nid, dir, idx, idx_to_check, enable); } static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, int i, bool enable) { hda_nid_t nid = path->path[i]; init_amp(codec, nid, HDA_OUTPUT, 0); check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); } static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, int i, bool enable, bool add_aamix) { struct hda_gen_spec *spec = codec->spec; const hda_nid_t *conn; int n, nums, idx; int type; hda_nid_t nid = path->path[i]; nums = snd_hda_get_conn_list(codec, nid, &conn); if (nums < 0) return; type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_PIN || (type == AC_WID_AUD_IN && codec->single_adc_amp)) { nums = 1; idx = 0; } else idx = path->idx[i]; for (n = 0; n < nums; n++) init_amp(codec, nid, HDA_INPUT, n); /* here is a little bit tricky in comparison with activate_amp_out(); * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { if (n != idx) { if (conn[n] != spec->mixer_merge_nid) continue; /* when aamix is disabled, force to off */ if (!add_aamix) { activate_amp(codec, nid, HDA_INPUT, n, n, false); continue; } } check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable); } } /* sync power of each widget in the the given path */ static hda_nid_t path_power_update(struct hda_codec *codec, struct nid_path *path, bool allow_powerdown) { hda_nid_t nid, changed = 0; int i, state, power; for (i = 0; i < path->depth; i++) { nid = path->path[i]; if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) continue; if (nid == codec->core.afg) continue; if (!allow_powerdown || is_active_nid_for_any(codec, nid)) state = AC_PWRST_D0; else state = AC_PWRST_D3; power = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); if (power != (state | (state << 4))) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, state); changed = nid; /* all known codecs seem to be capable to handl * widgets state even in D3, so far. * if any new codecs need to restore the widget * states after D0 transition, call the function * below. */ #if 0 /* disabled */ if (state == AC_PWRST_D0) snd_hdac_regmap_sync_node(&codec->core, nid); #endif } } return changed; } /* do sync with the last power state change */ static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) { if (nid) { msleep(10); snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); } } /** * snd_hda_activate_path - activate or deactivate the given path * @codec: the HDA codec * @path: the path to activate/deactivate * @enable: flag to activate or not * @add_aamix: enable the input from aamix NID * * If @add_aamix is set, enable the input from aa-mix NID as well (if any). */ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix) { struct hda_gen_spec *spec = codec->spec; int i; path->active = enable; /* make sure the widget is powered up */ if (enable && (spec->power_down_unused || codec->power_save_node)) path_power_update(codec, path, codec->power_save_node); for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; if (enable && path->multi[i]) snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) activate_amp_in(codec, path, i, enable, add_aamix); if (has_amp_out(codec, path, i)) activate_amp_out(codec, path, i, enable); } } EXPORT_SYMBOL_GPL(snd_hda_activate_path); /* if the given path is inactive, put widgets into D3 (only if suitable) */ static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; if (!(spec->power_down_unused || codec->power_save_node) || path->active) return; sync_power_state_change(codec, path_power_update(codec, path, true)); } /* turn on/off EAPD on the given pin */ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) { struct hda_gen_spec *spec = codec->spec; if (spec->own_eapd_ctl || !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) return; if (spec->keep_eapd_on && !enable) return; if (codec->inv_eapd) enable = !enable; snd_hda_codec_write_cache(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, enable ? 0x02 : 0x00); } /* re-initialize the path specified by the given path index */ static void resume_path_from_idx(struct hda_codec *codec, int path_idx) { struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); if (path) snd_hda_activate_path(codec, path, path->active, false); } /* * Helper functions for creating mixer ctl elements */ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); enum { HDA_CTL_WIDGET_VOL, HDA_CTL_WIDGET_MUTE, HDA_CTL_BIND_MUTE, }; static const struct snd_kcontrol_new control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), /* only the put callback is replaced for handling the special mute */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = hda_gen_mixer_mute_put, /* replaced */ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = snd_hda_mixer_amp_switch_info, .get = hda_gen_bind_mute_get, .put = hda_gen_bind_mute_put, /* replaced */ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), }, }; /* add dynamic controls from template */ static struct snd_kcontrol_new * add_control(struct hda_gen_spec *spec, int type, const char *name, int cidx, unsigned long val) { struct snd_kcontrol_new *knew; knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); if (!knew) return NULL; knew->index = cidx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return knew; } static int add_control_with_pfx(struct hda_gen_spec *spec, int type, const char *pfx, const char *dir, const char *sfx, int cidx, unsigned long val) { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); if (!add_control(spec, type, name, cidx, val)) return -ENOMEM; return 0; } #define add_pb_vol_ctrl(spec, type, pfx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) #define add_pb_sw_ctrl(spec, type, pfx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) #define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, unsigned int chs, struct nid_path *path) { unsigned int val; if (!path) return 0; val = path->ctls[NID_PATH_VOL_CTL]; if (!val) return 0; val = amp_val_replace_channels(val, chs); return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); } /* return the channel bits suitable for the given path->ctls[] */ static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, int type) { int chs = 1; /* mono (left only) */ if (path) { hda_nid_t nid = get_amp_nid_(path->ctls[type]); if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) chs = 3; /* stereo */ } return chs; } static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, struct nid_path *path) { int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); return add_vol_ctl(codec, pfx, cidx, chs, path); } /* create a mute-switch for the given mixer widget; * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, unsigned int chs, struct nid_path *path) { unsigned int val; int type = HDA_CTL_WIDGET_MUTE; if (!path) return 0; val = path->ctls[NID_PATH_MUTE_CTL]; if (!val) return 0; val = amp_val_replace_channels(val, chs); if (get_amp_direction_(val) == HDA_INPUT) { hda_nid_t nid = get_amp_nid_(val); int nums = snd_hda_get_num_conns(codec, nid); if (nums > 1) { type = HDA_CTL_BIND_MUTE; val |= nums << 19; } } return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } static int add_stereo_sw(struct hda_codec *codec, const char *pfx, int cidx, struct nid_path *path) { int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); return add_sw_ctl(codec, pfx, cidx, chs, path); } /* playback mute control with the software mute bit check */ static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; if (spec->auto_mute_via_amp) { hda_nid_t nid = get_amp_nid(kcontrol); bool enabled = !((spec->mute_bits >> nid) & 1); ucontrol->value.integer.value[0] &= enabled; ucontrol->value.integer.value[1] &= enabled; } } static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { sync_auto_mute_bits(kcontrol, ucontrol); return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } /* * Bound mute controls */ #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) static int hda_gen_bind_mute_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->control_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->control_mutex); return err; } static int hda_gen_bind_mute_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; sync_auto_mute_bits(kcontrol, ucontrol); mutex_lock(&codec->control_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->control_mutex); return err < 0 ? err : change; } /* any ctl assigned to the path with the given index? */ static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) { struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); return path && path->ctls[ctl_type]; } static const char * const channel_name[4] = { "Front", "Surround", "CLFE", "Side" }; /* give some appropriate ctl name prefix for the given line out channel */ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, int *index, int ctl_type) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && !codec->force_pin_prefix && !cfg->hp_outs && !cfg->speaker_outs) return spec->vmaster_mute.hook ? "PCM" : "Master"; /* if there is really a single DAC used in the whole output paths, * use it master (or "PCM" if a vmaster hook is present) */ if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && !codec->force_pin_prefix && !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) return spec->vmaster_mute.hook ? "PCM" : "Master"; /* multi-io channels */ if (ch >= cfg->line_outs) return channel_name[ch]; switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: /* if the primary channel vol/mute is shared with HP volume, * don't name it as Speaker */ if (!ch && cfg->hp_outs && !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) break; if (cfg->line_outs == 1) return "Speaker"; if (cfg->line_outs == 2) return ch ? "Bass Speaker" : "Speaker"; break; case AUTO_PIN_HP_OUT: /* if the primary channel vol/mute is shared with spk volume, * don't name it as Headphone */ if (!ch && cfg->speaker_outs && !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) break; /* for multi-io case, only the primary out */ if (ch && spec->multi_ios) break; *index = ch; return "Headphone"; case AUTO_PIN_LINE_OUT: /* This deals with the case where we have two DACs and * one LO, one HP and one Speaker */ if (!ch && cfg->speaker_outs && cfg->hp_outs) { bool hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); bool spk_lo_shared = !path_has_mixer(codec, spec->speaker_paths[0], ctl_type); if (hp_lo_shared && spk_lo_shared) return spec->vmaster_mute.hook ? "PCM" : "Master"; if (hp_lo_shared) return "Headphone+LO"; if (spk_lo_shared) return "Speaker+LO"; } } /* for a single channel output, we don't have to name the channel */ if (cfg->line_outs == 1 && !spec->multi_ios) return "Line Out"; if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); return "PCM"; } return channel_name[ch]; } /* * Parse output paths */ /* badness definition */ enum { /* No primary DAC is found for the main output */ BAD_NO_PRIMARY_DAC = 0x10000, /* No DAC is found for the extra output */ BAD_NO_DAC = 0x4000, /* No possible multi-ios */ BAD_MULTI_IO = 0x120, /* No individual DAC for extra output */ BAD_NO_EXTRA_DAC = 0x102, /* No individual DAC for extra surrounds */ BAD_NO_EXTRA_SURR_DAC = 0x101, /* Primary DAC shared with main surrounds */ BAD_SHARED_SURROUND = 0x100, /* No independent HP possible */ BAD_NO_INDEP_HP = 0x10, /* Primary DAC shared with main CLFE */ BAD_SHARED_CLFE = 0x10, /* Primary DAC shared with extra surrounds */ BAD_SHARED_EXTRA_SURROUND = 0x10, /* Volume widget is shared */ BAD_SHARED_VOL = 0x10, }; /* look for widgets in the given path which are appropriate for * volume and mute controls, and assign the values to ctls[]. * * When no appropriate widget is found in the path, the badness value * is incremented depending on the situation. The function returns the * total badness for both volume and mute controls. */ static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid; unsigned int val; int badness = 0; if (!path) return BAD_SHARED_VOL * 2; if (path->ctls[NID_PATH_VOL_CTL] || path->ctls[NID_PATH_MUTE_CTL]) return 0; /* already evaluated */ nid = look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); if (spec->dac_min_mute) val |= HDA_AMP_VAL_MIN_MUTE; if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) badness += BAD_SHARED_VOL; else path->ctls[NID_PATH_VOL_CTL] = val; } else badness += BAD_SHARED_VOL; nid = look_for_out_mute_nid(codec, path); if (nid) { unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || nid_has_mute(codec, nid, HDA_OUTPUT)) val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) badness += BAD_SHARED_VOL; else path->ctls[NID_PATH_MUTE_CTL] = val; } else badness += BAD_SHARED_VOL; return badness; } const struct badness_table hda_main_out_badness = { .no_primary_dac = BAD_NO_PRIMARY_DAC, .no_dac = BAD_NO_DAC, .shared_primary = BAD_NO_PRIMARY_DAC, .shared_surr = BAD_SHARED_SURROUND, .shared_clfe = BAD_SHARED_CLFE, .shared_surr_main = BAD_SHARED_SURROUND, }; EXPORT_SYMBOL_GPL(hda_main_out_badness); const struct badness_table hda_extra_out_badness = { .no_primary_dac = BAD_NO_DAC, .no_dac = BAD_NO_DAC, .shared_primary = BAD_NO_EXTRA_DAC, .shared_surr = BAD_SHARED_EXTRA_SURROUND, .shared_clfe = BAD_SHARED_EXTRA_SURROUND, .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, }; EXPORT_SYMBOL_GPL(hda_extra_out_badness); /* get the DAC of the primary output corresponding to the given array index */ static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; if (cfg->line_outs > idx) return spec->private_dac_nids[idx]; idx -= cfg->line_outs; if (spec->multi_ios > idx) return spec->multi_io[idx].dac; return 0; } /* return the DAC if it's reachable, otherwise zero */ static inline hda_nid_t try_dac(struct hda_codec *codec, hda_nid_t dac, hda_nid_t pin) { return is_reachable_path(codec, dac, pin) ? dac : 0; } /* try to assign DACs to pins and return the resultant badness */ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx, const struct badness_table *bad) { struct hda_gen_spec *spec = codec->spec; int i, j; int badness = 0; hda_nid_t dac; if (!num_outs) return 0; for (i = 0; i < num_outs; i++) { struct nid_path *path; hda_nid_t pin = pins[i]; path = snd_hda_get_path_from_idx(codec, path_idx[i]); if (path) { badness += assign_out_path_ctls(codec, path); continue; } dacs[i] = get_preferred_dac(codec, pin); if (dacs[i]) { if (is_dac_already_used(codec, dacs[i])) badness += bad->shared_primary; } if (!dacs[i]) dacs[i] = look_for_dac(codec, pin, false); if (!dacs[i] && !i) { /* try to steal the DAC of surrounds for the front */ for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; invalidate_nid_path(codec, path_idx[j]); path_idx[j] = 0; break; } } } dac = dacs[i]; if (!dac) { if (num_outs > 2) dac = try_dac(codec, get_primary_out(codec, i), pin); if (!dac) dac = try_dac(codec, dacs[0], pin); if (!dac) dac = try_dac(codec, get_primary_out(codec, i), pin); if (dac) { if (!i) badness += bad->shared_primary; else if (i == 1) badness += bad->shared_surr; else badness += bad->shared_clfe; } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { dac = spec->private_dac_nids[0]; badness += bad->shared_surr_main; } else if (!i) badness += bad->no_primary_dac; else badness += bad->no_dac; } if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); if (!path && !i && spec->mixer_nid) { /* try with aamix */ path = snd_hda_add_new_path(codec, dac, pin, 0); } if (!path) { dac = dacs[i] = 0; badness += bad->no_dac; } else { /* print_nid_path(codec, "output", path); */ path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); badness += assign_out_path_ctls(codec, path); } } return badness; } /* return NID if the given pin has only a single connection to a certain DAC */ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; int i; hda_nid_t nid_found = 0; for (i = 0; i < spec->num_all_dacs; i++) { hda_nid_t nid = spec->all_dacs[i]; if (!nid || is_dac_already_used(codec, nid)) continue; if (is_reachable_path(codec, nid, pin)) { if (nid_found) return 0; nid_found = nid; } } return nid_found; } /* check whether the given pin can be a multi-io pin */ static bool can_be_multiio_pin(struct hda_codec *codec, unsigned int location, hda_nid_t nid) { unsigned int defcfg, caps; defcfg = snd_hda_codec_get_pincfg(codec, nid); if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) return false; if (location && get_defcfg_location(defcfg) != location) return false; caps = snd_hda_query_pin_caps(codec, nid); if (!(caps & AC_PINCAP_OUT)) return false; return true; } /* count the number of input pins that are capable to be multi-io */ static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int type, i; int num_pins = 0; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { if (cfg->inputs[i].type != type) continue; if (can_be_multiio_pin(codec, location, cfg->inputs[i].pin)) num_pins++; } } return num_pins; } /* * multi-io helper * * When hardwired is set, try to fill ony hardwired pins, and returns * zero if any pins are filled, non-zero if nothing found. * When hardwired is off, try to fill possible input pins, and returns * the badness value. */ static int fill_multi_ios(struct hda_codec *codec, hda_nid_t reference_pin, bool hardwired) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int type, i, j, num_pins, old_pins; unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int badness = 0; struct nid_path *path; old_pins = spec->multi_ios; if (old_pins >= 2) goto end_fill; num_pins = count_multiio_pins(codec, reference_pin); if (num_pins < 2) goto end_fill; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0; if (cfg->inputs[i].type != type) continue; if (!can_be_multiio_pin(codec, location, nid)) continue; for (j = 0; j < spec->multi_ios; j++) { if (nid == spec->multi_io[j].pin) break; } if (j < spec->multi_ios) continue; if (hardwired) dac = get_dac_if_single(codec, nid); else if (!dac) dac = look_for_dac(codec, nid, false); if (!dac) { badness++; continue; } path = snd_hda_add_new_path(codec, dac, nid, -spec->mixer_nid); if (!path) { badness++; continue; } /* print_nid_path(codec, "multiio", path); */ spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->out_paths[cfg->line_outs + spec->multi_ios] = snd_hda_get_path_idx(codec, path); spec->multi_ios++; if (spec->multi_ios >= 2) break; } } end_fill: if (badness) badness = BAD_MULTI_IO; if (old_pins == spec->multi_ios) { if (hardwired) return 1; /* nothing found */ else return badness; /* no badness if nothing found */ } if (!hardwired && spec->multi_ios < 2) { /* cancel newly assigned paths */ spec->paths.used -= spec->multi_ios - old_pins; spec->multi_ios = old_pins; return badness; } /* assign volume and mute controls */ for (i = old_pins; i < spec->multi_ios; i++) { path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); badness += assign_out_path_ctls(codec, path); } return badness; } /* map DACs for all pins in the list if they are single connections */ static bool map_singles(struct hda_codec *codec, int outs, const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) { struct hda_gen_spec *spec = codec->spec; int i; bool found = false; for (i = 0; i < outs; i++) { struct nid_path *path; hda_nid_t dac; if (dacs[i]) continue; dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pins[i], -spec->mixer_nid); if (!path && !i && spec->mixer_nid) path = snd_hda_add_new_path(codec, dac, pins[i], 0); if (path) { dacs[i] = dac; found = true; /* print_nid_path(codec, "output", path); */ path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); } } return found; } static inline bool has_aamix_out_paths(struct hda_gen_spec *spec) { return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || spec->aamix_out_paths[2]; } /* create a new path including aamix if available, and return its index */ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; hda_nid_t path_dac, dac, pin; path = snd_hda_get_path_from_idx(codec, path_idx); if (!path || !path->depth || is_nid_contained(path, spec->mixer_nid)) return 0; path_dac = path->path[0]; dac = spec->private_dac_nids[0]; pin = path->path[path->depth - 1]; path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); if (!path) { if (dac != path_dac) dac = path_dac; else if (spec->multiout.hp_out_nid[0]) dac = spec->multiout.hp_out_nid[0]; else if (spec->multiout.extra_out_nid[0]) dac = spec->multiout.extra_out_nid[0]; else dac = 0; if (dac) path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); } if (!path) return 0; /* print_nid_path(codec, "output-aamix", path); */ path->active = false; /* unused as default */ path->pin_fixed = true; /* static route */ return snd_hda_get_path_idx(codec, path); } /* check whether the independent HP is available with the current config */ static bool indep_hp_possible(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; struct nid_path *path; int i, idx; if (cfg->line_out_type == AUTO_PIN_HP_OUT) idx = spec->out_paths[0]; else idx = spec->hp_paths[0]; path = snd_hda_get_path_from_idx(codec, idx); if (!path) return false; /* assume no path conflicts unless aamix is involved */ if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid)) return true; /* check whether output paths contain aamix */ for (i = 0; i < cfg->line_outs; i++) { if (spec->out_paths[i] == idx) break; path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (path && is_nid_contained(path, spec->mixer_nid)) return false; } for (i = 0; i < cfg->speaker_outs; i++) { path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]); if (path && is_nid_contained(path, spec->mixer_nid)) return false; } return true; } /* fill the empty entries in the dac array for speaker/hp with the * shared dac pointed by the paths */ static void refill_shared_dacs(struct hda_codec *codec, int num_outs, hda_nid_t *dacs, int *path_idx) { struct nid_path *path; int i; for (i = 0; i < num_outs; i++) { if (dacs[i]) continue; path = snd_hda_get_path_from_idx(codec, path_idx[i]); if (!path) continue; dacs[i] = path->path[0]; } } /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired, bool fill_mio_first) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, err, badness; /* set num_dacs once to full for look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; spec->multiout.dac_nids = spec->private_dac_nids; memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; snd_array_free(&spec->paths); /* clear path indices */ memset(spec->out_paths, 0, sizeof(spec->out_paths)); memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); memset(spec->input_paths, 0, sizeof(spec->input_paths)); memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); memset(&spec->digin_path, 0, sizeof(spec->digin_path)); badness = 0; /* fill hard-wired DACs first */ if (fill_hardwired) { bool mapped; do { mapped = map_singles(codec, cfg->line_outs, cfg->line_out_pins, spec->private_dac_nids, spec->out_paths); mapped |= map_singles(codec, cfg->hp_outs, cfg->hp_pins, spec->multiout.hp_out_nid, spec->hp_paths); mapped |= map_singles(codec, cfg->speaker_outs, cfg->speaker_pins, spec->multiout.extra_out_nid, spec->speaker_paths); if (!spec->no_multi_io && fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], true); if (!err) mapped = true; } } while (mapped); } badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, spec->private_dac_nids, spec->out_paths, spec->main_out_badness); if (!spec->no_multi_io && fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; /* we don't count badness at this stage yet */ } if (cfg->line_out_type != AUTO_PIN_HP_OUT) { err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, spec->multiout.hp_out_nid, spec->hp_paths, spec->extra_out_badness); if (err < 0) return err; badness += err; } if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = try_assign_dacs(codec, cfg->speaker_outs, cfg->speaker_pins, spec->multiout.extra_out_nid, spec->speaker_paths, spec->extra_out_badness); if (err < 0) return err; badness += err; } if (!spec->no_multi_io && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; badness += err; } if (spec->mixer_nid) { spec->aamix_out_paths[0] = check_aamix_out_path(codec, spec->out_paths[0]); if (cfg->line_out_type != AUTO_PIN_HP_OUT) spec->aamix_out_paths[1] = check_aamix_out_path(codec, spec->hp_paths[0]); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) spec->aamix_out_paths[2] = check_aamix_out_path(codec, spec->speaker_paths[0]); } if (!spec->no_multi_io && cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */ /* re-count num_dacs and squash invalid entries */ spec->multiout.num_dacs = 0; for (i = 0; i < cfg->line_outs; i++) { if (spec->private_dac_nids[i]) spec->multiout.num_dacs++; else { memmove(spec->private_dac_nids + i, spec->private_dac_nids + i + 1, sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); spec->private_dac_nids[cfg->line_outs - 1] = 0; } } spec->ext_channel_count = spec->min_channel_count = spec->multiout.num_dacs * 2; if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) spec->private_dac_nids[spec->multiout.num_dacs++] = spec->multi_io[i].dac; } else if (spec->multi_ios) { spec->multi_ios = 0; badness += BAD_MULTI_IO; } if (spec->indep_hp && !indep_hp_possible(codec)) badness += BAD_NO_INDEP_HP; /* re-fill the shared DAC for speaker / headphone */ if (cfg->line_out_type != AUTO_PIN_HP_OUT) refill_shared_dacs(codec, cfg->hp_outs, spec->multiout.hp_out_nid, spec->hp_paths); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) refill_shared_dacs(codec, cfg->speaker_outs, spec->multiout.extra_out_nid, spec->speaker_paths); return badness; } #define DEBUG_BADNESS #ifdef DEBUG_BADNESS #define debug_badness(fmt, ...) \ codec_dbg(codec, fmt, ##__VA_ARGS__) #else #define debug_badness(fmt, ...) \ do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0) #endif #ifdef DEBUG_BADNESS static inline void print_nid_path_idx(struct hda_codec *codec, const char *pfx, int idx) { struct nid_path *path; path = snd_hda_get_path_from_idx(codec, idx); if (path) print_nid_path(codec, pfx, path); } static void debug_show_configs(struct hda_codec *codec, struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; static const char * const lo_type[3] = { "LO", "SP", "HP" }; int i; debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], spec->multiout.dac_nids[3], lo_type[cfg->line_out_type]); for (i = 0; i < cfg->line_outs; i++) print_nid_path_idx(codec, " out", spec->out_paths[i]); if (spec->multi_ios > 0) debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", spec->multi_ios, spec->multi_io[0].pin, spec->multi_io[1].pin, spec->multi_io[0].dac, spec->multi_io[1].dac); for (i = 0; i < spec->multi_ios; i++) print_nid_path_idx(codec, " mio", spec->out_paths[cfg->line_outs + i]); if (cfg->hp_outs) debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->hp_pins[0], cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[3], spec->multiout.hp_out_nid[0], spec->multiout.hp_out_nid[1], spec->multiout.hp_out_nid[2], spec->multiout.hp_out_nid[3]); for (i = 0; i < cfg->hp_outs; i++) print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); if (cfg->speaker_outs) debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->speaker_pins[0], cfg->speaker_pins[1], cfg->speaker_pins[2], cfg->speaker_pins[3], spec->multiout.extra_out_nid[0], spec->multiout.extra_out_nid[1], spec->multiout.extra_out_nid[2], spec->multiout.extra_out_nid[3]); for (i = 0; i < cfg->speaker_outs; i++) print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); for (i = 0; i < 3; i++) print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); } #else #define debug_show_configs(codec, cfg) /* NOP */ #endif /* find all available DACs of the codec */ static void fill_all_dac_nids(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid; spec->num_all_dacs = 0; memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) continue; if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { codec_err(codec, "Too many DACs!\n"); break; } spec->all_dacs[spec->num_all_dacs++] = nid; } } static int parse_output_paths(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *best_cfg; unsigned int val; int best_badness = INT_MAX; int badness; bool fill_hardwired = true, fill_mio_first = true; bool best_wired = true, best_mio = true; bool hp_spk_swapped = false; best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM; *best_cfg = *cfg; for (;;) { badness = fill_and_eval_dacs(codec, fill_hardwired, fill_mio_first); if (badness < 0) { kfree(best_cfg); return badness; } debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", cfg->line_out_type, fill_hardwired, fill_mio_first, badness); debug_show_configs(codec, cfg); if (badness < best_badness) { best_badness = badness; *best_cfg = *cfg; best_wired = fill_hardwired; best_mio = fill_mio_first; } if (!badness) break; fill_mio_first = !fill_mio_first; if (!fill_mio_first) continue; fill_hardwired = !fill_hardwired; if (!fill_hardwired) continue; if (hp_spk_swapped) break; hp_spk_swapped = true; if (cfg->speaker_outs > 0 && cfg->line_out_type == AUTO_PIN_HP_OUT) { cfg->hp_outs = cfg->line_outs; memcpy(cfg->hp_pins, cfg->line_out_pins, sizeof(cfg->hp_pins)); cfg->line_outs = cfg->speaker_outs; memcpy(cfg->line_out_pins, cfg->speaker_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = 0; memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; fill_hardwired = true; continue; } if (cfg->hp_outs > 0 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { cfg->speaker_outs = cfg->line_outs; memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->line_outs = cfg->hp_outs; memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = 0; memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); cfg->line_out_type = AUTO_PIN_HP_OUT; fill_hardwired = true; continue; } break; } if (badness) { debug_badness("==> restoring best_cfg\n"); *cfg = *best_cfg; fill_and_eval_dacs(codec, best_wired, best_mio); } debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", cfg->line_out_type, best_wired, best_mio); debug_show_configs(codec, cfg); if (cfg->line_out_pins[0]) { struct nid_path *path; path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); if (spec->vmaster_nid) { snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, spec->vmaster_tlv); if (spec->dac_min_mute) spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE; } } /* set initial pinctl targets */ if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) val = PIN_HP; else val = PIN_OUT; set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); if (cfg->line_out_type != AUTO_PIN_HP_OUT) set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; set_pin_targets(codec, cfg->speaker_outs, cfg->speaker_pins, val); } /* clear indep_hp flag if not available */ if (spec->indep_hp && !indep_hp_possible(codec)) spec->indep_hp = 0; kfree(best_cfg); return 0; } /* add playback controls from the parsed DAC table */ static int create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; int i, err, noutputs; noutputs = cfg->line_outs; if (spec->multi_ios > 0 && cfg->line_outs < 3) noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { const char *name; int index; struct nid_path *path; path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (!path) continue; name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ err = add_vol_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; err = add_vol_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; } else { err = add_stereo_vol(codec, name, index, path); if (err < 0) return err; } name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); if (!name || !strcmp(name, "CLFE")) { err = add_sw_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; err = add_sw_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; } else { err = add_stereo_sw(codec, name, index, path); if (err < 0) return err; } } return 0; } static int create_extra_out(struct hda_codec *codec, int path_idx, const char *pfx, int cidx) { struct nid_path *path; int err; path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return 0; err = add_stereo_vol(codec, pfx, cidx, path); if (err < 0) return err; err = add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; return 0; } /* add playback controls for speaker and HP outputs */ static int create_extra_outs(struct hda_codec *codec, int num_pins, const int *paths, const char *pfx) { int i; for (i = 0; i < num_pins; i++) { const char *name; char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int err, idx = 0; if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) name = "Bass Speaker"; else if (num_pins >= 3) { snprintf(tmp, sizeof(tmp), "%s %s", pfx, channel_name[i]); name = tmp; } else { name = pfx; idx = i; } err = create_extra_out(codec, paths[i], name, idx); if (err < 0) return err; } return 0; } static int create_hp_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.hp_outs, spec->hp_paths, "Headphone"); } static int create_speaker_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.speaker_outs, spec->speaker_paths, "Speaker"); } /* * independent HP controls */ static void call_hp_automute(struct hda_codec *codec, struct hda_jack_callback *jack); static int indep_hp_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { return snd_hda_enum_bool_helper_info(kcontrol, uinfo); } static int indep_hp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; return 0; } static void update_aamix_paths(struct hda_codec *codec, bool do_mix, int nomix_path_idx, int mix_path_idx, int out_type); static int indep_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; unsigned int select = ucontrol->value.enumerated.item[0]; int ret = 0; mutex_lock(&spec->pcm_mutex); if (spec->active_streams) { ret = -EBUSY; goto unlock; } if (spec->indep_hp_enabled != select) { hda_nid_t *dacp; if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) dacp = &spec->private_dac_nids[0]; else dacp = &spec->multiout.hp_out_nid[0]; /* update HP aamix paths in case it conflicts with indep HP */ if (spec->have_aamix_ctl) { if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0], spec->aamix_out_paths[0], spec->autocfg.line_out_type); else update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0], spec->aamix_out_paths[1], AUTO_PIN_HP_OUT); } spec->indep_hp_enabled = select; if (spec->indep_hp_enabled) *dacp = 0; else *dacp = spec->alt_dac_nid; call_hp_automute(codec, NULL); ret = 1; } unlock: mutex_unlock(&spec->pcm_mutex); return ret; } static const struct snd_kcontrol_new indep_hp_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Independent HP", .info = indep_hp_info, .get = indep_hp_get, .put = indep_hp_put, }; static int create_indep_hp_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; hda_nid_t dac; if (!spec->indep_hp) return 0; if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) dac = spec->multiout.dac_nids[0]; else dac = spec->multiout.hp_out_nid[0]; if (!dac) { spec->indep_hp = 0; return 0; } spec->indep_hp_enabled = false; spec->alt_dac_nid = dac; if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) return -ENOMEM; return 0; } /* * channel mode enum control */ static int ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; int chs; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = spec->multi_ios + 1; if (uinfo->value.enumerated.item > spec->multi_ios) uinfo->value.enumerated.item = spec->multi_ios; chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; sprintf(uinfo->value.enumerated.name, "%dch", chs); return 0; } static int ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - spec->min_channel_count) / 2; return 0; } static inline struct nid_path * get_multiio_path(struct hda_codec *codec, int idx) { struct hda_gen_spec *spec = codec->spec; return snd_hda_get_path_from_idx(codec, spec->out_paths[spec->autocfg.line_outs + idx]); } static void update_automute_all(struct hda_codec *codec); /* Default value to be passed as aamix argument for snd_hda_activate_path(); * used for output paths */ static bool aamix_default(struct hda_gen_spec *spec) { return !spec->have_aamix_ctl || spec->aamix_mode; } static int set_multi_io(struct hda_codec *codec, int idx, bool output) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid = spec->multi_io[idx].pin; struct nid_path *path; path = get_multiio_path(codec, idx); if (!path) return -EINVAL; if (path->active == output) return 0; if (output) { set_pin_target(codec, nid, PIN_OUT, true); snd_hda_activate_path(codec, path, true, aamix_default(spec)); set_pin_eapd(codec, nid, true); } else { set_pin_eapd(codec, nid, false); snd_hda_activate_path(codec, path, false, aamix_default(spec)); set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); path_power_down_sync(codec, path); } /* update jack retasking in case it modifies any of them */ update_automute_all(codec); return 0; } static int ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; int i, ch; ch = ucontrol->value.enumerated.item[0]; if (ch < 0 || ch > spec->multi_ios) return -EINVAL; if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) return 0; spec->ext_channel_count = ch * 2 + spec->min_channel_count; for (i = 0; i < spec->multi_ios; i++) set_multi_io(codec, i, i < ch); spec->multiout.max_channels = max(spec->ext_channel_count, spec->const_channel_count); if (spec->need_dac_fix) spec->multiout.num_dacs = spec->multiout.max_channels / 2; return 1; } static const struct snd_kcontrol_new channel_mode_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channel Mode", .info = ch_mode_info, .get = ch_mode_get, .put = ch_mode_put, }; static int create_multi_channel_mode(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (spec->multi_ios > 0) { if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) return -ENOMEM; } return 0; } /* * aamix loopback enable/disable switch */ #define loopback_mixing_info indep_hp_info static int loopback_mixing_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->aamix_mode; return 0; } static void update_aamix_paths(struct hda_codec *codec, bool do_mix, int nomix_path_idx, int mix_path_idx, int out_type) { struct hda_gen_spec *spec = codec->spec; struct nid_path *nomix_path, *mix_path; nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); if (!nomix_path || !mix_path) return; /* if HP aamix path is driven from a different DAC and the * independent HP mode is ON, can't turn on aamix path */ if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && mix_path->path[0] != spec->alt_dac_nid) do_mix = false; if (do_mix) { snd_hda_activate_path(codec, nomix_path, false, true); snd_hda_activate_path(codec, mix_path, true, true); path_power_down_sync(codec, nomix_path); } else { snd_hda_activate_path(codec, mix_path, false, false); snd_hda_activate_path(codec, nomix_path, true, false); path_power_down_sync(codec, mix_path); } } /* re-initialize the output paths; only called from loopback_mixing_put() */ static void update_output_paths(struct hda_codec *codec, int num_outs, const int *paths) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; int i; for (i = 0; i < num_outs; i++) { path = snd_hda_get_path_from_idx(codec, paths[i]); if (path) snd_hda_activate_path(codec, path, path->active, spec->aamix_mode); } } static int loopback_mixing_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int val = ucontrol->value.enumerated.item[0]; if (val == spec->aamix_mode) return 0; spec->aamix_mode = val; if (has_aamix_out_paths(spec)) { update_aamix_paths(codec, val, spec->out_paths[0], spec->aamix_out_paths[0], cfg->line_out_type); update_aamix_paths(codec, val, spec->hp_paths[0], spec->aamix_out_paths[1], AUTO_PIN_HP_OUT); update_aamix_paths(codec, val, spec->speaker_paths[0], spec->aamix_out_paths[2], AUTO_PIN_SPEAKER_OUT); } else { update_output_paths(codec, cfg->line_outs, spec->out_paths); if (cfg->line_out_type != AUTO_PIN_HP_OUT) update_output_paths(codec, cfg->hp_outs, spec->hp_paths); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) update_output_paths(codec, cfg->speaker_outs, spec->speaker_paths); } return 1; } static const struct snd_kcontrol_new loopback_mixing_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Loopback Mixing", .info = loopback_mixing_info, .get = loopback_mixing_get, .put = loopback_mixing_put, }; static int create_loopback_mixing_ctl(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (!spec->mixer_nid) return 0; if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) return -ENOMEM; spec->have_aamix_ctl = 1; return 0; } /* * shared headphone/mic handling */ static void call_update_outputs(struct hda_codec *codec); /* for shared I/O, change the pin-control accordingly */ static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force) { struct hda_gen_spec *spec = codec->spec; bool as_mic; unsigned int val; hda_nid_t pin; pin = spec->hp_mic_pin; as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx; if (!force) { val = snd_hda_codec_get_pin_target(codec, pin); if (as_mic) { if (val & PIN_IN) return; } else { if (val & PIN_OUT) return; } } val = snd_hda_get_default_vref(codec, pin); /* if the HP pin doesn't support VREF and the codec driver gives an * alternative pin, set up the VREF on that pin instead */ if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { const hda_nid_t vref_pin = spec->shared_mic_vref_pin; unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); if (vref_val != AC_PINCTL_VREF_HIZ) snd_hda_set_pin_ctl_cache(codec, vref_pin, PIN_IN | (as_mic ? vref_val : 0)); } if (!spec->hp_mic_jack_modes) { if (as_mic) val |= PIN_IN; else val = PIN_HP; set_pin_target(codec, pin, val, true); call_hp_automute(codec, NULL); } } /* create a shared input with the headphone out */ static int create_hp_mic(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int defcfg; hda_nid_t nid; if (!spec->hp_mic) { if (spec->suppress_hp_mic_detect) return 0; /* automatic detection: only if no input or a single internal * input pin is found, try to detect the shared hp/mic */ if (cfg->num_inputs > 1) return 0; else if (cfg->num_inputs == 1) { defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) return 0; } } spec->hp_mic = 0; /* clear once */ if (cfg->num_inputs >= AUTO_CFG_MAX_INS) return 0; nid = 0; if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0) nid = cfg->line_out_pins[0]; else if (cfg->hp_outs > 0) nid = cfg->hp_pins[0]; if (!nid) return 0; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) return 0; /* no input */ cfg->inputs[cfg->num_inputs].pin = nid; cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC; cfg->inputs[cfg->num_inputs].is_headphone_mic = 1; cfg->num_inputs++; spec->hp_mic = 1; spec->hp_mic_pin = nid; /* we can't handle auto-mic together with HP-mic */ spec->suppress_auto_mic = 1; codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid); return 0; } /* * output jack mode */ static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin); static const char * const out_jack_texts[] = { "Line Out", "Headphone Out", }; static int out_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts); } static int out_jack_mode_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; if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) ucontrol->value.enumerated.item[0] = 1; else ucontrol->value.enumerated.item[0] = 0; return 0; } static int out_jack_mode_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; val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; if (snd_hda_codec_get_pin_target(codec, nid) == val) return 0; snd_hda_set_pin_ctl_cache(codec, nid, val); return 1; } static const struct snd_kcontrol_new out_jack_mode_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = out_jack_mode_info, .get = out_jack_mode_get, .put = out_jack_mode_put, }; static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) { struct hda_gen_spec *spec = codec->spec; const struct snd_kcontrol_new *kctl; int i; snd_array_for_each(&spec->kctls, i, kctl) { if (!strcmp(kctl->name, name) && kctl->index == idx) return true; } return false; } static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, char *name, size_t name_len) { struct hda_gen_spec *spec = codec->spec; int idx = 0; snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); strlcat(name, " Jack Mode", name_len); for (; find_kctl_name(codec, name, idx); idx++) ; } static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; if (spec->add_jack_modes) { unsigned int pincap = snd_hda_query_pin_caps(codec, pin); if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) return 2; } return 1; } static int create_out_jack_modes(struct hda_codec *codec, int num_pins, hda_nid_t *pins) { struct hda_gen_spec *spec = codec->spec; int i; for (i = 0; i < num_pins; i++) { hda_nid_t pin = pins[i]; if (pin == spec->hp_mic_pin) continue; if (get_out_jack_num_items(codec, pin) > 1) { struct snd_kcontrol_new *knew; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; get_jack_mode_name(codec, pin, name, sizeof(name)); knew = snd_hda_gen_add_kctl(spec, name, &out_jack_mode_enum); if (!knew) return -ENOMEM; knew->private_value = pin; } } return 0; } /* * input jack mode */ /* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ #define NUM_VREFS 6 static const char * const vref_texts[NUM_VREFS] = { "Line In", "Mic 50pc Bias", "Mic 0V Bias", "", "Mic 80pc Bias", "Mic 100pc Bias" }; static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) { unsigned int pincap; pincap = snd_hda_query_pin_caps(codec, pin); pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; /* filter out unusual vrefs */ pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); return pincap; } /* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) { unsigned int i, n = 0; for (i = 0; i < NUM_VREFS; i++) { if (vref_caps & (1 << i)) { if (n == item_idx) return i; n++; } } return 0; } /* convert back from the vref ctl index to the enum item index */ static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) { unsigned int i, n = 0; for (i = 0; i < NUM_VREFS; i++) { if (i == idx) return n; if (vref_caps & (1 << i)) n++; } return 0; } static int in_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; unsigned int vref_caps = get_vref_caps(codec, nid); snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), vref_texts); /* set the right text */ strcpy(uinfo->value.enumerated.name, vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); return 0; } static int in_jack_mode_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 int vref_caps = get_vref_caps(codec, nid); unsigned int idx; idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); return 0; } static int in_jack_mode_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 vref_caps = get_vref_caps(codec, nid); unsigned int val, idx; val = snd_hda_codec_get_pin_target(codec, nid); idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); if (idx == ucontrol->value.enumerated.item[0]) return 0; val &= ~AC_PINCTL_VREFEN; val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); snd_hda_set_pin_ctl_cache(codec, nid, val); return 1; } static const struct snd_kcontrol_new in_jack_mode_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = in_jack_mode_info, .get = in_jack_mode_get, .put = in_jack_mode_put, }; static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; int nitems = 0; if (spec->add_jack_modes) nitems = hweight32(get_vref_caps(codec, pin)); return nitems ? nitems : 1; } static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; struct snd_kcontrol_new *knew; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; unsigned int defcfg; if (pin == spec->hp_mic_pin) return 0; /* already done in create_out_jack_mode() */ /* no jack mode for fixed pins */ defcfg = snd_hda_codec_get_pincfg(codec, pin); if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) return 0; /* no multiple vref caps? */ if (get_in_jack_num_items(codec, pin) <= 1) return 0; get_jack_mode_name(codec, pin, name, sizeof(name)); knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); if (!knew) return -ENOMEM; knew->private_value = pin; return 0; } /* * HP/mic shared jack mode */ static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; int out_jacks = get_out_jack_num_items(codec, nid); int in_jacks = get_in_jack_num_items(codec, nid); const char *text = NULL; int idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = out_jacks + in_jacks; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; idx = uinfo->value.enumerated.item; if (idx < out_jacks) { if (out_jacks > 1) text = out_jack_texts[idx]; else text = "Headphone Out"; } else { idx -= out_jacks; if (in_jacks > 1) { unsigned int vref_caps = get_vref_caps(codec, nid); text = vref_texts[get_vref_idx(vref_caps, idx)]; } else text = "Mic In"; } strcpy(uinfo->value.enumerated.name, text); return 0; } static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid) { int out_jacks = get_out_jack_num_items(codec, nid); int in_jacks = get_in_jack_num_items(codec, nid); unsigned int val = snd_hda_codec_get_pin_target(codec, nid); int idx = 0; if (val & PIN_OUT) { if (out_jacks > 1 && val == PIN_HP) idx = 1; } else if (val & PIN_IN) { idx = out_jacks; if (in_jacks > 1) { unsigned int vref_caps = get_vref_caps(codec, nid); val &= AC_PINCTL_VREFEN; idx += cvt_from_vref_idx(vref_caps, val); } } return idx; } static int hp_mic_jack_mode_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; ucontrol->value.enumerated.item[0] = get_cur_hp_mic_jack_mode(codec, nid); return 0; } static int hp_mic_jack_mode_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; int out_jacks = get_out_jack_num_items(codec, nid); int in_jacks = get_in_jack_num_items(codec, nid); unsigned int val, oldval, idx; oldval = get_cur_hp_mic_jack_mode(codec, nid); idx = ucontrol->value.enumerated.item[0]; if (oldval == idx) return 0; if (idx < out_jacks) { if (out_jacks > 1) val = idx ? PIN_HP : PIN_OUT; else val = PIN_HP; } else { idx -= out_jacks; if (in_jacks > 1) { unsigned int vref_caps = get_vref_caps(codec, nid); val = snd_hda_codec_get_pin_target(codec, nid); val &= ~(AC_PINCTL_VREFEN | PIN_HP); val |= get_vref_idx(vref_caps, idx) | PIN_IN; } else val = snd_hda_get_default_vref(codec, nid) | PIN_IN; } snd_hda_set_pin_ctl_cache(codec, nid, val); call_hp_automute(codec, NULL); return 1; } static const struct snd_kcontrol_new hp_mic_jack_mode_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = hp_mic_jack_mode_info, .get = hp_mic_jack_mode_get, .put = hp_mic_jack_mode_put, }; static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; struct snd_kcontrol_new *knew; knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode", &hp_mic_jack_mode_enum); if (!knew) return -ENOMEM; knew->private_value = pin; spec->hp_mic_jack_modes = 1; return 0; } /* * Parse input paths */ /* add the powersave loopback-list entry */ static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) { struct hda_amp_list *list; list = snd_array_new(&spec->loopback_list); if (!list) return -ENOMEM; list->nid = mix; list->dir = HDA_INPUT; list->idx = idx; spec->loopback.amplist = spec->loopback_list.list; return 0; } /* return true if either a volume or a mute amp is found for the given * aamix path; the amp has to be either in the mixer node or its direct leaf */ static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, hda_nid_t pin, unsigned int *mix_val, unsigned int *mute_val) { int idx, num_conns; const hda_nid_t *list; hda_nid_t nid; idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); if (idx < 0) return false; *mix_val = *mute_val = 0; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); if (nid_has_mute(codec, mix_nid, HDA_INPUT)) *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); if (*mix_val && *mute_val) return true; /* check leaf node */ num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); if (num_conns < idx) return false; nid = list[idx]; if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); return *mix_val || *mute_val; } /* create input playback/capture controls for the given pin */ static int new_analog_input(struct hda_codec *codec, int input_idx, hda_nid_t pin, const char *ctlname, int ctlidx, hda_nid_t mix_nid) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; unsigned int mix_val, mute_val; int err, idx; if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) return 0; path = snd_hda_add_new_path(codec, pin, mix_nid, 0); if (!path) return -EINVAL; print_nid_path(codec, "loopback", path); spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); idx = path->idx[path->depth - 1]; if (mix_val) { err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); if (err < 0) return err; path->ctls[NID_PATH_VOL_CTL] = mix_val; } if (mute_val) { err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); if (err < 0) return err; path->ctls[NID_PATH_MUTE_CTL] = mute_val; } path->active = true; path->stream_enabled = true; /* no DAC/ADC involved */ err = add_loopback_list(spec, mix_nid, idx); if (err < 0) return err; if (spec->mixer_nid != spec->mixer_merge_nid && !spec->loopback_merge_path) { path = snd_hda_add_new_path(codec, spec->mixer_nid, spec->mixer_merge_nid, 0); if (path) { print_nid_path(codec, "loopback-merge", path); path->active = true; path->pin_fixed = true; /* static route */ path->stream_enabled = true; /* no DAC/ADC involved */ spec->loopback_merge_path = snd_hda_get_path_idx(codec, path); } } return 0; } static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); return (pincap & AC_PINCAP_IN) != 0; } /* Parse the codec tree and retrieve ADCs */ static int fill_adc_nids(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid; hda_nid_t *adc_nids = spec->adc_nids; int max_nums = ARRAY_SIZE(spec->adc_nids); int nums = 0; for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps); if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) continue; adc_nids[nums] = nid; if (++nums >= max_nums) break; } spec->num_adc_nids = nums; /* copy the detected ADCs to all_adcs[] */ spec->num_all_adcs = nums; memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); return nums; } /* filter out invalid adc_nids that don't give all active input pins; * if needed, check whether dynamic ADC-switching is available */ static int check_dyn_adc_switch(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; unsigned int ok_bits; int i, n, nums; nums = 0; ok_bits = 0; for (n = 0; n < spec->num_adc_nids; n++) { for (i = 0; i < imux->num_items; i++) { if (!spec->input_paths[i][n]) break; } if (i >= imux->num_items) { ok_bits |= (1 << n); nums++; } } if (!ok_bits) { /* check whether ADC-switch is possible */ for (i = 0; i < imux->num_items; i++) { for (n = 0; n < spec->num_adc_nids; n++) { if (spec->input_paths[i][n]) { spec->dyn_adc_idx[i] = n; break; } } } codec_dbg(codec, "enabling ADC switching\n"); spec->dyn_adc_switch = 1; } else if (nums != spec->num_adc_nids) { /* shrink the invalid adcs and input paths */ nums = 0; for (n = 0; n < spec->num_adc_nids; n++) { if (!(ok_bits & (1 << n))) continue; if (n != nums) { spec->adc_nids[nums] = spec->adc_nids[n]; for (i = 0; i < imux->num_items; i++) { invalidate_nid_path(codec, spec->input_paths[i][nums]); spec->input_paths[i][nums] = spec->input_paths[i][n]; spec->input_paths[i][n] = 0; } } nums++; } spec->num_adc_nids = nums; } if (imux->num_items == 1 || (imux->num_items == 2 && spec->hp_mic)) { codec_dbg(codec, "reducing to a single ADC\n"); spec->num_adc_nids = 1; /* reduce to a single ADC */ } /* single index for individual volumes ctls */ if (!spec->dyn_adc_switch && spec->multi_cap_vol) spec->num_adc_nids = 1; return 0; } /* parse capture source paths from the given pin and create imux items */ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, int cfg_idx, int num_adcs, const char *label, int anchor) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; int imux_idx = imux->num_items; bool imux_added = false; int c; for (c = 0; c < num_adcs; c++) { struct nid_path *path; hda_nid_t adc = spec->adc_nids[c]; if (!is_reachable_path(codec, pin, adc)) continue; path = snd_hda_add_new_path(codec, pin, adc, anchor); if (!path) continue; print_nid_path(codec, "input", path); spec->input_paths[imux_idx][c] = snd_hda_get_path_idx(codec, path); if (!imux_added) { if (spec->hp_mic_pin == pin) spec->hp_mic_mux_idx = imux->num_items; spec->imux_pins[imux->num_items] = pin; snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); imux_added = true; if (spec->dyn_adc_switch) spec->dyn_adc_idx[imux_idx] = c; } } return 0; } /* * create playback/capture controls for input pins */ /* fill the label for each input at first */ static int fill_input_pin_labels(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; int i; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin = cfg->inputs[i].pin; const char *label; int j, idx; if (!is_input_pin(codec, pin)) continue; label = hda_get_autocfg_input_label(codec, cfg, i); idx = 0; for (j = i - 1; j >= 0; j--) { if (spec->input_labels[j] && !strcmp(spec->input_labels[j], label)) { idx = spec->input_label_idxs[j] + 1; break; } } spec->input_labels[i] = label; spec->input_label_idxs[i] = idx; } return 0; } #define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ static int create_input_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t mixer = spec->mixer_nid; int num_adcs; int i, err; unsigned int val; num_adcs = fill_adc_nids(codec); if (num_adcs < 0) return 0; err = fill_input_pin_labels(codec); if (err < 0) return err; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; pin = cfg->inputs[i].pin; if (!is_input_pin(codec, pin)) continue; val = PIN_IN; if (cfg->inputs[i].type == AUTO_PIN_MIC) val |= snd_hda_get_default_vref(codec, pin); if (pin != spec->hp_mic_pin && !snd_hda_codec_get_pin_target(codec, pin)) set_pin_target(codec, pin, val, false); if (mixer) { if (is_reachable_path(codec, pin, mixer)) { err = new_analog_input(codec, i, pin, spec->input_labels[i], spec->input_label_idxs[i], mixer); if (err < 0) return err; } } err = parse_capture_source(codec, pin, i, num_adcs, spec->input_labels[i], -mixer); if (err < 0) return err; if (spec->add_jack_modes) { err = create_in_jack_mode(codec, pin); if (err < 0) return err; } } /* add stereo mix when explicitly enabled via hint */ if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, "Stereo Mix", 0); if (err < 0) return err; else spec->suppress_auto_mic = 1; } return 0; } /* * input source mux */ /* get the input path specified by the given adc and imux indices */ static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) { struct hda_gen_spec *spec = codec->spec; if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { snd_BUG(); return NULL; } if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { snd_BUG(); return NULL; } return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); } static int mux_select(struct hda_codec *codec, unsigned int adc_idx, unsigned int idx); static int mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; return snd_hda_input_mux_info(&spec->input_mux, uinfo); } static int mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; /* the ctls are created at once with multiple counts */ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; return 0; } static int mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return mux_select(codec, adc_idx, ucontrol->value.enumerated.item[0]); } static const struct snd_kcontrol_new cap_src_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", .info = mux_enum_info, .get = mux_enum_get, .put = mux_enum_put, }; /* * capture volume and capture switch ctls */ typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); /* call the given amp update function for all amps in the imux list at once */ static int cap_put_caller(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, put_call_t func, int type) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; const struct hda_input_mux *imux; struct nid_path *path; int i, adc_idx, err = 0; imux = &spec->input_mux; adc_idx = kcontrol->id.index; mutex_lock(&codec->control_mutex); for (i = 0; i < imux->num_items; i++) { path = get_input_path(codec, adc_idx, i); if (!path || !path->ctls[type]) continue; kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); if (err < 0) break; } mutex_unlock(&codec->control_mutex); if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec, kcontrol, ucontrol); return err; } /* capture volume ctl callbacks */ #define cap_vol_info snd_hda_mixer_amp_volume_info #define cap_vol_get snd_hda_mixer_amp_volume_get #define cap_vol_tlv snd_hda_mixer_amp_tlv static int cap_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return cap_put_caller(kcontrol, ucontrol, snd_hda_mixer_amp_volume_put, NID_PATH_VOL_CTL); } static const struct snd_kcontrol_new cap_vol_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Volume", .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), .info = cap_vol_info, .get = cap_vol_get, .put = cap_vol_put, .tlv = { .c = cap_vol_tlv }, }; /* capture switch ctl callbacks */ #define cap_sw_info snd_ctl_boolean_stereo_info #define cap_sw_get snd_hda_mixer_amp_switch_get static int cap_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return cap_put_caller(kcontrol, ucontrol, snd_hda_mixer_amp_switch_put, NID_PATH_MUTE_CTL); } static const struct snd_kcontrol_new cap_sw_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Switch", .info = cap_sw_info, .get = cap_sw_get, .put = cap_sw_put, }; static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) { hda_nid_t nid; int i, depth; path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; for (depth = 0; depth < 3; depth++) { if (depth >= path->depth) return -EINVAL; i = path->depth - depth - 1; nid = path->path[i]; if (!path->ctls[NID_PATH_VOL_CTL]) { if (nid_has_volume(codec, nid, HDA_OUTPUT)) path->ctls[NID_PATH_VOL_CTL] = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else if (nid_has_volume(codec, nid, HDA_INPUT)) { int idx = path->idx[i]; if (!depth && codec->single_adc_amp) idx = 0; path->ctls[NID_PATH_VOL_CTL] = HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); } } if (!path->ctls[NID_PATH_MUTE_CTL]) { if (nid_has_mute(codec, nid, HDA_OUTPUT)) path->ctls[NID_PATH_MUTE_CTL] = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else if (nid_has_mute(codec, nid, HDA_INPUT)) { int idx = path->idx[i]; if (!depth && codec->single_adc_amp) idx = 0; path->ctls[NID_PATH_MUTE_CTL] = HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); } } } return 0; } static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int val; int i; if (!spec->inv_dmic_split) return false; for (i = 0; i < cfg->num_inputs; i++) { if (cfg->inputs[i].pin != nid) continue; if (cfg->inputs[i].type != AUTO_PIN_MIC) return false; val = snd_hda_codec_get_pincfg(codec, nid); return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; } return false; } /* capture switch put callback for a single control with hook call */ static int cap_single_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; int ret; ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); if (ret < 0) return ret; if (spec->cap_sync_hook) spec->cap_sync_hook(codec, kcontrol, ucontrol); return ret; } static int add_single_cap_ctl(struct hda_codec *codec, const char *label, int idx, bool is_switch, unsigned int ctl, bool inv_dmic) { struct hda_gen_spec *spec = codec->spec; char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; unsigned int chs = inv_dmic ? 1 : 3; struct snd_kcontrol_new *knew; if (!ctl) return 0; if (label) snprintf(tmpname, sizeof(tmpname), "%s Capture %s", label, sfx); else snprintf(tmpname, sizeof(tmpname), "Capture %s", sfx); knew = add_control(spec, type, tmpname, idx, amp_val_replace_channels(ctl, chs)); if (!knew) return -ENOMEM; if (is_switch) knew->put = cap_single_sw_put; if (!inv_dmic) return 0; /* Make independent right kcontrol */ if (label) snprintf(tmpname, sizeof(tmpname), "Inverted %s Capture %s", label, sfx); else snprintf(tmpname, sizeof(tmpname), "Inverted Capture %s", sfx); knew = add_control(spec, type, tmpname, idx, amp_val_replace_channels(ctl, 2)); if (!knew) return -ENOMEM; if (is_switch) knew->put = cap_single_sw_put; return 0; } /* create single (and simple) capture volume and switch controls */ static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, unsigned int vol_ctl, unsigned int sw_ctl, bool inv_dmic) { int err; err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); if (err < 0) return err; err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); if (err < 0) return err; return 0; } /* create bound capture volume and switch controls */ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, unsigned int vol_ctl, unsigned int sw_ctl) { struct hda_gen_spec *spec = codec->spec; struct snd_kcontrol_new *knew; if (vol_ctl) { knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); if (!knew) return -ENOMEM; knew->index = idx; knew->private_value = vol_ctl; knew->subdevice = HDA_SUBDEV_AMP_FLAG; } if (sw_ctl) { knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); if (!knew) return -ENOMEM; knew->index = idx; knew->private_value = sw_ctl; knew->subdevice = HDA_SUBDEV_AMP_FLAG; } return 0; } /* return the vol ctl when used first in the imux list */ static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) { struct nid_path *path; unsigned int ctl; int i; path = get_input_path(codec, 0, idx); if (!path) return 0; ctl = path->ctls[type]; if (!ctl) return 0; for (i = 0; i < idx - 1; i++) { path = get_input_path(codec, 0, i); if (path && path->ctls[type] == ctl) return 0; } return ctl; } /* create individual capture volume and switch controls per input */ static int create_multi_cap_vol_ctl(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; int i, err, type; for (i = 0; i < imux->num_items; i++) { bool inv_dmic; int idx; idx = imux->items[i].index; if (idx >= spec->autocfg.num_inputs) continue; inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); for (type = 0; type < 2; type++) { err = add_single_cap_ctl(codec, spec->input_labels[idx], spec->input_label_idxs[idx], type, get_first_cap_ctl(codec, i, type), inv_dmic); if (err < 0) return err; } } return 0; } static int create_capture_mixers(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; int i, n, nums, err; if (spec->dyn_adc_switch) nums = 1; else nums = spec->num_adc_nids; if (!spec->auto_mic && imux->num_items > 1) { struct snd_kcontrol_new *knew; const char *name; name = nums > 1 ? "Input Source" : "Capture Source"; knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; } for (n = 0; n < nums; n++) { bool multi = false; bool multi_cap_vol = spec->multi_cap_vol; bool inv_dmic = false; int vol, sw; vol = sw = 0; for (i = 0; i < imux->num_items; i++) { struct nid_path *path; path = get_input_path(codec, n, i); if (!path) continue; parse_capvol_in_path(codec, path); if (!vol) vol = path->ctls[NID_PATH_VOL_CTL]; else if (vol != path->ctls[NID_PATH_VOL_CTL]) { multi = true; if (!same_amp_caps(codec, vol, path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) multi_cap_vol = true; } if (!sw) sw = path->ctls[NID_PATH_MUTE_CTL]; else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { multi = true; if (!same_amp_caps(codec, sw, path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) multi_cap_vol = true; } if (is_inv_dmic_pin(codec, spec->imux_pins[i])) inv_dmic = true; } if (!multi) err = create_single_cap_vol_ctl(codec, n, vol, sw, inv_dmic); else if (!multi_cap_vol && !inv_dmic) err = create_bind_cap_vol_ctl(codec, n, vol, sw); else err = create_multi_cap_vol_ctl(codec); if (err < 0) return err; } return 0; } /* * add mic boosts if needed */ /* check whether the given amp is feasible as a boost volume */ static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { unsigned int step; if (!nid_has_volume(codec, nid, dir) || is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) return false; step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; if (step < 0x20) return false; return true; } /* look for a boost amp in a widget close to the pin */ static unsigned int look_for_boost_amp(struct hda_codec *codec, struct nid_path *path) { unsigned int val = 0; hda_nid_t nid; int depth; for (depth = 0; depth < 3; depth++) { if (depth >= path->depth - 1) break; nid = path->path[depth]; if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); break; } else if (check_boost_vol(codec, nid, HDA_INPUT, path->idx[depth])) { val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], HDA_INPUT); break; } } return val; } static int parse_mic_boost(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; struct hda_input_mux *imux = &spec->input_mux; int i; if (!spec->num_adc_nids) return 0; for (i = 0; i < imux->num_items; i++) { struct nid_path *path; unsigned int val; int idx; char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; idx = imux->items[i].index; if (idx >= imux->num_items) continue; /* check only line-in and mic pins */ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) continue; path = get_input_path(codec, 0, i); if (!path) continue; val = look_for_boost_amp(codec, path); if (!val) continue; /* create a boost control */ snprintf(boost_label, sizeof(boost_label), "%s Boost Volume", spec->input_labels[idx]); if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, spec->input_label_idxs[idx], val)) return -ENOMEM; path->ctls[NID_PATH_BOOST_CTL] = val; } return 0; } /* * mic mute LED hook helpers */ enum { MICMUTE_LED_ON, MICMUTE_LED_OFF, MICMUTE_LED_FOLLOW_CAPTURE, MICMUTE_LED_FOLLOW_MUTE, }; static void call_micmute_led_update(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; unsigned int val; switch (spec->micmute_led.led_mode) { case MICMUTE_LED_ON: val = 1; break; case MICMUTE_LED_OFF: val = 0; break; case MICMUTE_LED_FOLLOW_CAPTURE: val = !!spec->micmute_led.capture; break; case MICMUTE_LED_FOLLOW_MUTE: default: val = !spec->micmute_led.capture; break; } if (val == spec->micmute_led.led_value) return; spec->micmute_led.led_value = val; if (spec->micmute_led.update) spec->micmute_led.update(codec); } static void update_micmute_led(struct hda_codec *codec, struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_gen_spec *spec = codec->spec; unsigned int mask; if (spec->micmute_led.old_hook) spec->micmute_led.old_hook(codec, kcontrol, ucontrol); if (!ucontrol) return; mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); if (!strcmp("Capture Switch", ucontrol->id.name)) { /* TODO: How do I verify if it's a mono or stereo here? */ if (ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]) spec->micmute_led.capture |= mask; else spec->micmute_led.capture &= ~mask; call_micmute_led_update(codec); } } static int micmute_led_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { static const char * const texts[] = { "On", "Off", "Follow Capture", "Follow Mute", }; return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int micmute_led_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode; return 0; } static int micmute_led_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; unsigned int mode; mode = ucontrol->value.enumerated.item[0]; if (mode > MICMUTE_LED_FOLLOW_MUTE) mode = MICMUTE_LED_FOLLOW_MUTE; if (mode == spec->micmute_led.led_mode) return 0; spec->micmute_led.led_mode = mode; call_micmute_led_update(codec); return 1; } static const struct snd_kcontrol_new micmute_led_mode_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic Mute-LED Mode", .info = micmute_led_mode_info, .get = micmute_led_mode_get, .put = micmute_led_mode_put, }; /** * snd_hda_gen_add_micmute_led - helper for setting up mic mute LED hook * @codec: the HDA codec * @hook: the callback for updating LED * * Called from the codec drivers for offering the mic mute LED controls. * When established, it sets up cap_sync_hook and triggers the callback at * each time when the capture mixer switch changes. The callback is supposed * to update the LED accordingly. * * Returns 0 if the hook is established or a negative error code. */ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, void (*hook)(struct hda_codec *)) { struct hda_gen_spec *spec = codec->spec; spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE; spec->micmute_led.capture = 0; spec->micmute_led.led_value = 0; spec->micmute_led.old_hook = spec->cap_sync_hook; spec->micmute_led.update = hook; spec->cap_sync_hook = update_micmute_led; if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl)) return -ENOMEM; return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); /* * parse digital I/Os and set up NIDs in BIOS auto-parse mode */ static void parse_digital(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; int i, nums; hda_nid_t dig_nid, pin; /* support multiple SPDIFs; the secondary is set up as a slave */ nums = 0; for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; path = snd_hda_add_new_path(codec, dig_nid, pin, 0); if (!path) continue; print_nid_path(codec, "digout", path); path->active = true; path->pin_fixed = true; /* no jack detection */ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_OUT, false); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; } else { spec->multiout.slave_dig_outs = spec->slave_dig_outs; if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) break; spec->slave_dig_outs[nums - 1] = dig_nid; } nums++; } if (spec->autocfg.dig_in_pin) { pin = spec->autocfg.dig_in_pin; for_each_hda_codec_node(dig_nid, codec) { unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; if (!(wcaps & AC_WCAP_DIGITAL)) continue; path = snd_hda_add_new_path(codec, pin, dig_nid, 0); if (path) { print_nid_path(codec, "digin", path); path->active = true; path->pin_fixed = true; /* no jack */ spec->dig_in_nid = dig_nid; spec->digin_path = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_IN, false); break; } } } } /* * input MUX handling */ static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); /* select the given imux item; either unmute exclusively or select the route */ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, unsigned int idx) { struct hda_gen_spec *spec = codec->spec; const struct hda_input_mux *imux; struct nid_path *old_path, *path; imux = &spec->input_mux; if (!imux->num_items) return 0; if (idx >= imux->num_items) idx = imux->num_items - 1; if (spec->cur_mux[adc_idx] == idx) return 0; old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); if (!old_path) return 0; if (old_path->active) snd_hda_activate_path(codec, old_path, false, false); spec->cur_mux[adc_idx] = idx; if (spec->hp_mic) update_hp_mic(codec, adc_idx, false); if (spec->dyn_adc_switch) dyn_adc_pcm_resetup(codec, idx); path = get_input_path(codec, adc_idx, idx); if (!path) return 0; if (path->active) return 0; snd_hda_activate_path(codec, path, true, false); if (spec->cap_sync_hook) spec->cap_sync_hook(codec, NULL, NULL); path_power_down_sync(codec, old_path); return 1; } /* power up/down widgets in the all paths that match with the given NID * as terminals (either start- or endpoint) * * returns the last changed NID, or zero if unchanged. */ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, int pin_state, int stream_state) { struct hda_gen_spec *spec = codec->spec; hda_nid_t last, changed = 0; struct nid_path *path; int n; snd_array_for_each(&spec->paths, n, path) { if (!path->depth) continue; if (path->path[0] == nid || path->path[path->depth - 1] == nid) { bool pin_old = path->pin_enabled; bool stream_old = path->stream_enabled; if (pin_state >= 0) path->pin_enabled = pin_state; if (stream_state >= 0) path->stream_enabled = stream_state; if ((!path->pin_fixed && path->pin_enabled != pin_old) || path->stream_enabled != stream_old) { last = path_power_update(codec, path, true); if (last) changed = last; } } } return changed; } /* check the jack status for power control */ static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) { if (!is_jack_detectable(codec, pin)) return true; return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; } /* power up/down the paths of the given pin according to the jack state; * power = 0/1 : only power up/down if it matches with the jack state, * < 0 : force power up/down to follow the jack sate * * returns the last changed NID, or zero if unchanged. */ static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, int power) { bool on; if (!codec->power_save_node) return 0; on = detect_pin_state(codec, pin); if (power >= 0 && on != power) return 0; return set_path_power(codec, pin, on, -1); } static void pin_power_callback(struct hda_codec *codec, struct hda_jack_callback *jack, bool on) { if (jack && jack->nid) sync_power_state_change(codec, set_pin_power_jack(codec, jack->nid, on)); } /* callback only doing power up -- called at first */ static void pin_power_up_callback(struct hda_codec *codec, struct hda_jack_callback *jack) { pin_power_callback(codec, jack, true); } /* callback only doing power down -- called at last */ static void pin_power_down_callback(struct hda_codec *codec, struct hda_jack_callback *jack) { pin_power_callback(codec, jack, false); } /* set up the power up/down callbacks */ static void add_pin_power_ctls(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, bool on) { int i; hda_jack_callback_fn cb = on ? pin_power_up_callback : pin_power_down_callback; for (i = 0; i < num_pins && pins[i]; i++) { if (is_jack_detectable(codec, pins[i])) snd_hda_jack_detect_enable_callback(codec, pins[i], cb); else set_path_power(codec, pins[i], true, -1); } } /* enabled power callback to each available I/O pin with jack detections; * the digital I/O pins are excluded because of the unreliable detectsion */ static void add_all_pin_power_ctls(struct hda_codec *codec, bool on) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; if (!codec->power_save_node) return; add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); if (cfg->line_out_type != AUTO_PIN_HP_OUT) add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); for (i = 0; i < cfg->num_inputs; i++) add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); } /* sync path power up/down with the jack states of given pins */ static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, const hda_nid_t *pins) { int i; for (i = 0; i < num_pins && pins[i]; i++) if (is_jack_detectable(codec, pins[i])) set_pin_power_jack(codec, pins[i], -1); } /* sync path power up/down with pins; called at init and resume */ static void sync_all_pin_power_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; if (!codec->power_save_node) return; sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); if (cfg->line_out_type != AUTO_PIN_HP_OUT) sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); for (i = 0; i < cfg->num_inputs; i++) sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); } /* add fake paths if not present yet */ static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, int num_pins, const hda_nid_t *pins) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; int i; for (i = 0; i < num_pins; i++) { if (!pins[i]) break; if (get_nid_path(codec, nid, pins[i], 0)) continue; path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); path->depth = 2; path->path[0] = nid; path->path[1] = pins[i]; path->active = true; } return 0; } /* create fake paths to all outputs from beep */ static int add_fake_beep_paths(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid = spec->beep_nid; int err; if (!codec->power_save_node || !nid) return 0; err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); if (err < 0) return err; if (cfg->line_out_type != AUTO_PIN_HP_OUT) { err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); if (err < 0) return err; } if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = add_fake_paths(codec, nid, cfg->speaker_outs, cfg->speaker_pins); if (err < 0) return err; } return 0; } /* power up/down beep widget and its output paths */ static void beep_power_hook(struct hda_beep *beep, bool on) { set_path_power(beep->codec, beep->nid, -1, on); } /** * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 * @codec: the HDA codec * @pin: NID of pin to fix */ int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); path->depth = 1; path->path[0] = pin; path->active = true; path->pin_fixed = true; path->stream_enabled = true; return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); /* * Jack detections for HP auto-mute and mic-switch */ /* check each pin in the given array; returns true if any of them is plugged */ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) { int i; bool present = false; for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; if (!nid) break; /* don't detect pins retasked as inputs */ if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) continue; if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) present = true; } return present; } /* standard HP/line-out auto-mute helper */ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, int *paths, bool mute) { struct hda_gen_spec *spec = codec->spec; int i; for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; unsigned int val, oldval; if (!nid) break; oldval = snd_hda_codec_get_pin_target(codec, nid); if (oldval & PIN_IN) continue; /* no mute for inputs */ if (spec->auto_mute_via_amp) { struct nid_path *path; hda_nid_t mute_nid; path = snd_hda_get_path_from_idx(codec, paths[i]); if (!path) continue; mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); if (!mute_nid) continue; if (mute) spec->mute_bits |= (1ULL << mute_nid); else spec->mute_bits &= ~(1ULL << mute_nid); continue; } else { /* don't reset VREF value in case it's controlling * the amp (see alc861_fixup_asus_amp_vref_0f()) */ if (spec->keep_vref_in_automute) val = oldval & ~PIN_HP; else val = 0; if (!mute) val |= oldval; /* here we call update_pin_ctl() so that the pinctl is * changed without changing the pinctl target value; * the original target value will be still referred at * the init / resume again */ update_pin_ctl(codec, nid, val); } set_pin_eapd(codec, nid, !mute); if (codec->power_save_node) { bool on = !mute; if (on) on = detect_pin_state(codec, nid); set_path_power(codec, nid, on, -1); } } } /** * snd_hda_gen_update_outputs - Toggle outputs muting * @codec: the HDA codec * * Update the mute status of all outputs based on the current jack states. */ void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int *paths; int on; /* Control HP pins/amps depending on master_mute state; * in general, HP pins/amps control should be enabled in all cases, * but currently set only for master_mute, just to be safe */ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) paths = spec->out_paths; else paths = spec->hp_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), spec->autocfg.hp_pins, paths, spec->master_mute); if (!spec->automute_speaker) on = 0; else on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; spec->speaker_muted = on; if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) paths = spec->out_paths; else paths = spec->speaker_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), spec->autocfg.speaker_pins, paths, on); /* toggle line-out mutes if needed, too */ /* if LO is a copy of either HP or Speaker, don't need to handle it */ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) return; if (!spec->automute_lo) on = 0; else on = spec->hp_jack_present; on |= spec->master_mute; spec->line_out_muted = on; paths = spec->out_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins, paths, on); } EXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs); static void call_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (spec->automute_hook) spec->automute_hook(codec); else snd_hda_gen_update_outputs(codec); /* sync the whole vmaster slaves to reflect the new auto-mute status */ if (spec->auto_mute_via_amp && !codec->bus->shutdown) snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); } /** * snd_hda_gen_hp_automute - standard HP-automute helper * @codec: the HDA codec * @jack: jack object, NULL for the whole */ void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; hda_nid_t *pins = spec->autocfg.hp_pins; int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); /* No detection for the first HP jack during indep-HP mode */ if (spec->indep_hp_enabled) { pins++; num_pins--; } spec->hp_jack_present = detect_jacks(codec, num_pins, pins); if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) return; call_update_outputs(codec); } EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); /** * snd_hda_gen_line_automute - standard line-out-automute helper * @codec: the HDA codec * @jack: jack object, NULL for the whole */ void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) return; /* check LO jack only when it's different from HP */ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) return; spec->line_jack_present = detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins); if (!spec->automute_speaker || !spec->detect_lo) return; call_update_outputs(codec); } EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); /** * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper * @codec: the HDA codec * @jack: jack object, NULL for the whole */ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; int i; if (!spec->auto_mic) return; for (i = spec->am_num_entries - 1; i > 0; i--) { hda_nid_t pin = spec->am_entry[i].pin; /* don't detect pins retasked as outputs */ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) continue; if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { mux_select(codec, 0, spec->am_entry[i].idx); return; } } mux_select(codec, 0, spec->am_entry[0].idx); } EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); /* call appropriate hooks */ static void call_hp_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->hp_automute_hook) spec->hp_automute_hook(codec, jack); else snd_hda_gen_hp_automute(codec, jack); } static void call_line_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->line_automute_hook) spec->line_automute_hook(codec, jack); else snd_hda_gen_line_automute(codec, jack); } static void call_mic_autoswitch(struct hda_codec *codec, struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->mic_autoswitch_hook) spec->mic_autoswitch_hook(codec, jack); else snd_hda_gen_mic_autoswitch(codec, jack); } /* update jack retasking */ static void update_automute_all(struct hda_codec *codec) { call_hp_automute(codec, NULL); call_line_automute(codec, NULL); call_mic_autoswitch(codec, NULL); } /* * Auto-Mute mode mixer enum support */ static int automute_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; static const char * const texts3[] = { "Disabled", "Speaker Only", "Line Out+Speaker" }; if (spec->automute_speaker_possible && spec->automute_lo_possible) return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); return snd_hda_enum_bool_helper_info(kcontrol, uinfo); } static int automute_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; unsigned int val = 0; if (spec->automute_speaker) val++; if (spec->automute_lo) val++; ucontrol->value.enumerated.item[0] = val; return 0; } static int automute_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; switch (ucontrol->value.enumerated.item[0]) { case 0: if (!spec->automute_speaker && !spec->automute_lo) return 0; spec->automute_speaker = 0; spec->automute_lo = 0; break; case 1: if (spec->automute_speaker_possible) { if (!spec->automute_lo && spec->automute_speaker) return 0; spec->automute_speaker = 1; spec->automute_lo = 0; } else if (spec->automute_lo_possible) { if (spec->automute_lo) return 0; spec->automute_lo = 1; } else return -EINVAL; break; case 2: if (!spec->automute_lo_possible || !spec->automute_speaker_possible) return -EINVAL; if (spec->automute_speaker && spec->automute_lo) return 0; spec->automute_speaker = 1; spec->automute_lo = 1; break; default: return -EINVAL; } call_update_outputs(codec); return 1; } static const struct snd_kcontrol_new automute_mode_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Auto-Mute Mode", .info = automute_mode_info, .get = automute_mode_get, .put = automute_mode_put, }; static int add_automute_mode_enum(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) return -ENOMEM; return 0; } /* * Check the availability of HP/line-out auto-mute; * Set up appropriately if really supported */ static int check_auto_mute_availability(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int present = 0; int i, err; if (spec->suppress_auto_mute) return 0; if (cfg->hp_pins[0]) present++; if (cfg->line_out_pins[0]) present++; if (cfg->speaker_pins[0]) present++; if (present < 2) /* need two different output types */ return 0; if (!cfg->speaker_pins[0] && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = cfg->line_outs; } if (!cfg->hp_pins[0] && cfg->line_out_type == AUTO_PIN_HP_OUT) { memcpy(cfg->hp_pins, cfg->line_out_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = cfg->line_outs; } for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; if (!is_jack_detectable(codec, nid)) continue; codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, call_hp_automute); spec->detect_hp = 1; } if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { if (cfg->speaker_outs) for (i = 0; i < cfg->line_outs; i++) { hda_nid_t nid = cfg->line_out_pins[i]; if (!is_jack_detectable(codec, nid)) continue; codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, call_line_automute); spec->detect_lo = 1; } spec->automute_lo_possible = spec->detect_hp; } spec->automute_speaker_possible = cfg->speaker_outs && (spec->detect_hp || spec->detect_lo); spec->automute_lo = spec->automute_lo_possible; spec->automute_speaker = spec->automute_speaker_possible; if (spec->automute_speaker_possible || spec->automute_lo_possible) { /* create a control for automute mode */ err = add_automute_mode_enum(codec); if (err < 0) return err; } return 0; } /* check whether all auto-mic pins are valid; setup indices if OK */ static bool auto_mic_check_imux(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; const struct hda_input_mux *imux; int i; imux = &spec->input_mux; for (i = 0; i < spec->am_num_entries; i++) { spec->am_entry[i].idx = find_idx_in_nid_list(spec->am_entry[i].pin, spec->imux_pins, imux->num_items); if (spec->am_entry[i].idx < 0) return false; /* no corresponding imux */ } /* we don't need the jack detection for the first pin */ for (i = 1; i < spec->am_num_entries; i++) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, call_mic_autoswitch); return true; } static int compare_attr(const void *ap, const void *bp) { const struct automic_entry *a = ap; const struct automic_entry *b = bp; return (int)(a->attr - b->attr); } /* * Check the availability of auto-mic switch; * Set up if really supported */ static int check_auto_mic_availability(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int types; int i, num_pins; if (spec->suppress_auto_mic) return 0; types = 0; num_pins = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; unsigned int attr; attr = snd_hda_codec_get_pincfg(codec, nid); attr = snd_hda_get_input_pin_attr(attr); if (types & (1 << attr)) return 0; /* already occupied */ switch (attr) { case INPUT_PIN_ATTR_INT: if (cfg->inputs[i].type != AUTO_PIN_MIC) return 0; /* invalid type */ break; case INPUT_PIN_ATTR_UNUSED: return 0; /* invalid entry */ default: if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) return 0; /* invalid type */ if (!spec->line_in_auto_switch && cfg->inputs[i].type != AUTO_PIN_MIC) return 0; /* only mic is allowed */ if (!is_jack_detectable(codec, nid)) return 0; /* no unsol support */ break; } if (num_pins >= MAX_AUTO_MIC_PINS) return 0; types |= (1 << attr); spec->am_entry[num_pins].pin = nid; spec->am_entry[num_pins].attr = attr; num_pins++; } if (num_pins < 2) return 0; spec->am_num_entries = num_pins; /* sort the am_entry in the order of attr so that the pin with a * higher attr will be selected when the jack is plugged. */ sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), compare_attr, NULL); if (!auto_mic_check_imux(codec)) return 0; spec->auto_mic = 1; spec->num_adc_nids = 1; spec->cur_mux[0] = spec->am_entry[0].idx; codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", spec->am_entry[0].pin, spec->am_entry[1].pin, spec->am_entry[2].pin); return 0; } /** * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets * into power down * @codec: the HDA codec * @nid: NID to evalute * @power_state: target power state */ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { struct hda_gen_spec *spec = codec->spec; if (!spec->power_down_unused && !codec->power_save_node) return power_state; if (power_state != AC_PWRST_D0 || nid == codec->core.afg) return power_state; if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) return power_state; if (is_active_nid_for_any(codec, nid)) return power_state; return AC_PWRST_D3; } EXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter); /* mute all aamix inputs initially; parse up to the first leaves */ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) { int i, nums; const hda_nid_t *conn; bool has_amp; nums = snd_hda_get_conn_list(codec, mix, &conn); has_amp = nid_has_mute(codec, mix, HDA_INPUT); for (i = 0; i < nums; i++) { if (has_amp) update_amp(codec, mix, HDA_INPUT, i, 0xff, HDA_AMP_MUTE); else if (nid_has_volume(codec, conn[i], HDA_OUTPUT)) update_amp(codec, conn[i], HDA_OUTPUT, 0, 0xff, HDA_AMP_MUTE); } } /** * snd_hda_gen_stream_pm - Stream power management callback * @codec: the HDA codec * @nid: audio widget * @on: power on/off flag * * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. */ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) { if (codec->power_save_node) set_path_power(codec, nid, -1, on); } EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); /** * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and * set up the hda_gen_spec * @codec: the HDA codec * @cfg: Parsed pin configuration * * return 1 if successful, 0 if the proper config is not found, * or a negative error code */ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; int err; parse_user_hints(codec); if (spec->mixer_nid && !spec->mixer_merge_nid) spec->mixer_merge_nid = spec->mixer_nid; if (cfg != &spec->autocfg) { spec->autocfg = *cfg; cfg = &spec->autocfg; } if (!spec->main_out_badness) spec->main_out_badness = &hda_main_out_badness; if (!spec->extra_out_badness) spec->extra_out_badness = &hda_extra_out_badness; fill_all_dac_nids(codec); if (!cfg->line_outs) { if (cfg->dig_outs || cfg->dig_in_pin) { spec->multiout.max_channels = 2; spec->no_analog = 1; goto dig_only; } if (!cfg->num_inputs && !cfg->dig_in_pin) return 0; /* can't find valid BIOS pin config */ } if (!spec->no_primary_hp && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->line_outs <= cfg->hp_outs) { /* use HP as primary out */ cfg->speaker_outs = cfg->line_outs; memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->line_outs = cfg->hp_outs; memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = 0; memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); cfg->line_out_type = AUTO_PIN_HP_OUT; } err = parse_output_paths(codec); if (err < 0) return err; err = create_multi_channel_mode(codec); if (err < 0) return err; err = create_multi_out_ctls(codec, cfg); if (err < 0) return err; err = create_hp_out_ctls(codec); if (err < 0) return err; err = create_speaker_out_ctls(codec); if (err < 0) return err; err = create_indep_hp_ctls(codec); if (err < 0) return err; err = create_loopback_mixing_ctl(codec); if (err < 0) return err; err = create_hp_mic(codec); if (err < 0) return err; err = create_input_ctls(codec); if (err < 0) return err; /* add power-down pin callbacks at first */ add_all_pin_power_ctls(codec, false); spec->const_channel_count = spec->ext_channel_count; /* check the multiple speaker and headphone pins */ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) spec->const_channel_count = max(spec->const_channel_count, cfg->speaker_outs * 2); if (cfg->line_out_type != AUTO_PIN_HP_OUT) spec->const_channel_count = max(spec->const_channel_count, cfg->hp_outs * 2); spec->multiout.max_channels = max(spec->ext_channel_count, spec->const_channel_count); err = check_auto_mute_availability(codec); if (err < 0) return err; err = check_dyn_adc_switch(codec); if (err < 0) return err; err = check_auto_mic_availability(codec); if (err < 0) return err; /* add stereo mix if available and not enabled yet */ if (!spec->auto_mic && spec->mixer_nid && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO && spec->input_mux.num_items > 1) { err = parse_capture_source(codec, spec->mixer_nid, CFG_IDX_MIX, spec->num_all_adcs, "Stereo Mix", 0); if (err < 0) return err; } err = create_capture_mixers(codec); if (err < 0) return err; err = parse_mic_boost(codec); if (err < 0) return err; /* create "Headphone Mic Jack Mode" if no input selection is * available (or user specifies add_jack_modes hint) */ if (spec->hp_mic_pin && (spec->auto_mic || spec->input_mux.num_items == 1 || spec->add_jack_modes)) { err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin); if (err < 0) return err; } if (spec->add_jack_modes) { if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = create_out_jack_modes(codec, cfg->line_outs, cfg->line_out_pins); if (err < 0) return err; } if (cfg->line_out_type != AUTO_PIN_HP_OUT) { err = create_out_jack_modes(codec, cfg->hp_outs, cfg->hp_pins); if (err < 0) return err; } } /* add power-up pin callbacks at last */ add_all_pin_power_ctls(codec, true); /* mute all aamix input initially */ if (spec->mixer_nid) mute_all_mixer_nid(codec, spec->mixer_nid); dig_only: parse_digital(codec); if (spec->power_down_unused || codec->power_save_node) { if (!codec->power_filter) codec->power_filter = snd_hda_gen_path_power_filter; if (!codec->patch_ops.stream_pm) codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; } if (!spec->no_analog && spec->beep_nid) { err = snd_hda_attach_beep_device(codec, spec->beep_nid); if (err < 0) return err; if (codec->beep && codec->power_save_node) { err = add_fake_beep_paths(codec); if (err < 0) return err; codec->beep->power_hook = beep_power_hook; } } return 1; } EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config); /* * Build control elements */ /* slave controls for virtual master */ static const char * const slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "Headphone", "Speaker", "Mono", "Line Out", "CLFE", "Bass Speaker", "PCM", "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", "Headphone Front", "Headphone Surround", "Headphone CLFE", "Headphone Side", "Headphone+LO", "Speaker+LO", NULL, }; /** * snd_hda_gen_build_controls - Build controls from the parsed results * @codec: the HDA codec * * Pass this to build_controls patch_ops. */ int snd_hda_gen_build_controls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int err; if (spec->kctls.used) { err = snd_hda_add_new_ctls(codec, spec->kctls.list); if (err < 0) return err; } if (spec->multiout.dig_out_nid) { err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, spec->multiout.dig_out_nid, spec->pcm_rec[1]->pcm_type); if (err < 0) return err; if (!spec->no_analog) { err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); if (err < 0) return err; spec->multiout.share_spdif = 1; } } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); if (err < 0) return err; } /* if we have no master control, let's create it */ if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { err = snd_hda_add_vmaster(codec, "Master Playback Volume", spec->vmaster_tlv, slave_pfxs, "Playback Volume"); if (err < 0) return err; } if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = __snd_hda_add_vmaster(codec, "Master Playback Switch", NULL, slave_pfxs, "Playback Switch", true, &spec->vmaster_mute.sw_kctl); if (err < 0) return err; if (spec->vmaster_mute.hook) { snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, spec->vmaster_mute_enum); snd_hda_sync_vmaster_hook(&spec->vmaster_mute); } } free_kctls(spec); /* no longer needed */ err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_build_controls); /* * PCM definitions */ static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) { struct hda_gen_spec *spec = codec->spec; if (spec->pcm_playback_hook) spec->pcm_playback_hook(hinfo, codec, substream, action); } static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) { struct hda_gen_spec *spec = codec->spec; if (spec->pcm_capture_hook) spec->pcm_capture_hook(hinfo, codec, substream, action); } /* * Analog playback callbacks */ static int playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; int err; mutex_lock(&spec->pcm_mutex); err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); if (!err) { spec->active_streams |= 1 << STREAM_MULTI_OUT; call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); } mutex_unlock(&spec->pcm_mutex); return err; } static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; int err; err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); if (!err) call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return err; } static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; int err; err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); if (!err) call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return err; } static int playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_MULTI_OUT); call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; } static int capture_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); return 0; } static int capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return 0; } static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { snd_hda_codec_cleanup_stream(codec, hinfo->nid); call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return 0; } static int capture_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); return 0; } static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; int err = 0; mutex_lock(&spec->pcm_mutex); if (spec->indep_hp && !spec->indep_hp_enabled) err = -EBUSY; else spec->active_streams |= 1 << STREAM_INDEP_HP; call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); mutex_unlock(&spec->pcm_mutex); return err; } static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_INDEP_HP); call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; } static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return 0; } static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { snd_hda_codec_cleanup_stream(codec, hinfo->nid); call_pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return 0; } /* * Digital out */ static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; return snd_hda_multi_out_dig_open(codec, &spec->multiout); } static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream); } static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); } static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; return snd_hda_multi_out_dig_close(codec, &spec->multiout); } /* * Analog capture */ #define alt_capture_pcm_open capture_pcm_open #define alt_capture_pcm_close capture_pcm_close static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return 0; } static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number + 1]); call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return 0; } /* */ static const struct hda_pcm_stream pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, /* NID is set in build_pcms */ .ops = { .open = playback_pcm_open, .close = playback_pcm_close, .prepare = playback_pcm_prepare, .cleanup = playback_pcm_cleanup }, }; static const struct hda_pcm_stream pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ .ops = { .open = capture_pcm_open, .close = capture_pcm_close, .prepare = capture_pcm_prepare, .cleanup = capture_pcm_cleanup }, }; static const struct hda_pcm_stream pcm_analog_alt_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ .ops = { .open = alt_playback_pcm_open, .close = alt_playback_pcm_close, .prepare = alt_playback_pcm_prepare, .cleanup = alt_playback_pcm_cleanup }, }; static const struct hda_pcm_stream pcm_analog_alt_capture = { .substreams = 2, /* can be overridden */ .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ .ops = { .open = alt_capture_pcm_open, .close = alt_capture_pcm_close, .prepare = alt_capture_pcm_prepare, .cleanup = alt_capture_pcm_cleanup }, }; static const struct hda_pcm_stream pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ .ops = { .open = dig_playback_pcm_open, .close = dig_playback_pcm_close, .prepare = dig_playback_pcm_prepare, .cleanup = dig_playback_pcm_cleanup }, }; static const struct hda_pcm_stream pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ }; /* Used by build_pcms to flag that a PCM has no playback stream */ static const struct hda_pcm_stream pcm_null_stream = { .substreams = 0, .channels_min = 0, .channels_max = 0, }; /* * dynamic changing ADC PCM streams */ static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) { struct hda_gen_spec *spec = codec->spec; hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; if (spec->cur_adc && spec->cur_adc != new_adc) { /* stream is running, let's swap the current ADC */ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); spec->cur_adc = new_adc; snd_hda_codec_setup_stream(codec, new_adc, spec->cur_adc_stream_tag, 0, spec->cur_adc_format); return true; } return false; } /* analog capture with dynamic dual-adc changes */ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; spec->cur_adc_stream_tag = stream_tag; spec->cur_adc_format = format; snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return 0; } static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = 0; call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return 0; } static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, .nid = 0, /* fill later */ .ops = { .prepare = dyn_adc_capture_pcm_prepare, .cleanup = dyn_adc_capture_pcm_cleanup }, }; static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, const char *chip_name) { char *p; if (*str) return; strlcpy(str, chip_name, len); /* drop non-alnum chars after a space */ for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { if (!isalnum(p[1])) { *p = 0; break; } } strlcat(str, sfx, len); } /* copy PCM stream info from @default_str, and override non-NULL entries * from @spec_str and @nid */ static void setup_pcm_stream(struct hda_pcm_stream *str, const struct hda_pcm_stream *default_str, const struct hda_pcm_stream *spec_str, hda_nid_t nid) { *str = *default_str; if (nid) str->nid = nid; if (spec_str) { if (spec_str->substreams) str->substreams = spec_str->substreams; if (spec_str->channels_min) str->channels_min = spec_str->channels_min; if (spec_str->channels_max) str->channels_max = spec_str->channels_max; if (spec_str->rates) str->rates = spec_str->rates; if (spec_str->formats) str->formats = spec_str->formats; if (spec_str->maxbps) str->maxbps = spec_str->maxbps; } } /** * snd_hda_gen_build_pcms - build PCM streams based on the parsed results * @codec: the HDA codec * * Pass this to build_pcms patch_ops. */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_pcm *info; bool have_multi_adcs; if (spec->no_analog) goto skip_analog; fill_pcm_stream_name(spec->stream_name_analog, sizeof(spec->stream_name_analog), " Analog", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); if (!info) return -ENOMEM; spec->pcm_rec[0] = info; if (spec->multiout.num_dacs > 0) { setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], &pcm_analog_playback, spec->stream_analog_playback, spec->multiout.dac_nids[0]); info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && spec->autocfg.line_outs == 2) info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = snd_pcm_2_1_chmaps; } if (spec->num_adc_nids) { setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], (spec->dyn_adc_switch ? &dyn_adc_pcm_analog_capture : &pcm_analog_capture), spec->stream_analog_capture, spec->adc_nids[0]); } skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { fill_pcm_stream_name(spec->stream_name_digital, sizeof(spec->stream_name_digital), " Digital", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_digital); if (!info) return -ENOMEM; codec->slave_dig_outs = spec->multiout.slave_dig_outs; spec->pcm_rec[1] = info; if (spec->dig_out_type) info->pcm_type = spec->dig_out_type; else info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], &pcm_digital_playback, spec->stream_digital_playback, spec->multiout.dig_out_nid); if (spec->dig_in_nid) setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], &pcm_digital_capture, spec->stream_digital_capture, spec->dig_in_nid); } if (spec->no_analog) return 0; /* If the use of more than one ADC is requested for the current * model, configure a second analog capture-only PCM. */ have_multi_adcs = (spec->num_adc_nids > 1) && !spec->dyn_adc_switch && !spec->auto_mic; /* Additional Analaog capture for index #2 */ if (spec->alt_dac_nid || have_multi_adcs) { fill_pcm_stream_name(spec->stream_name_alt_analog, sizeof(spec->stream_name_alt_analog), " Alt Analog", codec->core.chip_name); info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_alt_analog); if (!info) return -ENOMEM; spec->pcm_rec[2] = info; if (spec->alt_dac_nid) setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], &pcm_analog_alt_playback, spec->stream_analog_alt_playback, spec->alt_dac_nid); else setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], &pcm_null_stream, NULL, 0); if (have_multi_adcs) { setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], &pcm_analog_alt_capture, spec->stream_analog_alt_capture, spec->adc_nids[1]); info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids - 1; } else { setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], &pcm_null_stream, NULL, 0); } } return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms); /* * Standard auto-parser initializations */ /* configure the given path as a proper output */ static void set_output_and_unmute(struct hda_codec *codec, int path_idx) { struct nid_path *path; hda_nid_t pin; path = snd_hda_get_path_from_idx(codec, path_idx); if (!path || !path->depth) return; pin = path->path[path->depth - 1]; restore_pin_ctl(codec, pin); snd_hda_activate_path(codec, path, path->active, aamix_default(codec->spec)); set_pin_eapd(codec, pin, path->active); } /* initialize primary output paths */ static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int i; for (i = 0; i < spec->autocfg.line_outs; i++) set_output_and_unmute(codec, spec->out_paths[i]); } static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) { int i; for (i = 0; i < num_outs; i++) set_output_and_unmute(codec, paths[i]); } /* initialize hp and speaker paths */ static void init_extra_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, spec->speaker_paths); } /* initialize multi-io paths */ static void init_multi_io(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int i; for (i = 0; i < spec->multi_ios; i++) { hda_nid_t pin = spec->multi_io[i].pin; struct nid_path *path; path = get_multiio_path(codec, i); if (!path) continue; if (!spec->multi_io[i].ctl_in) spec->multi_io[i].ctl_in = snd_hda_codec_get_pin_target(codec, pin); snd_hda_activate_path(codec, path, path->active, aamix_default(spec)); } } static void init_aamix_paths(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (!spec->have_aamix_ctl) return; if (!has_aamix_out_paths(spec)) return; update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0], spec->aamix_out_paths[0], spec->autocfg.line_out_type); update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0], spec->aamix_out_paths[1], AUTO_PIN_HP_OUT); update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0], spec->aamix_out_paths[2], AUTO_PIN_SPEAKER_OUT); } /* set up input pins and loopback paths */ static void init_analog_input(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; if (is_input_pin(codec, nid)) restore_pin_ctl(codec, nid); /* init loopback inputs */ if (spec->mixer_nid) { resume_path_from_idx(codec, spec->loopback_paths[i]); resume_path_from_idx(codec, spec->loopback_merge_path); } } } /* initialize ADC paths */ static void init_input_src(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; struct nid_path *path; int i, c, nums; if (spec->dyn_adc_switch) nums = 1; else nums = spec->num_adc_nids; for (c = 0; c < nums; c++) { for (i = 0; i < imux->num_items; i++) { path = get_input_path(codec, c, i); if (path) { bool active = path->active; if (i == spec->cur_mux[c]) active = true; snd_hda_activate_path(codec, path, active, false); } } if (spec->hp_mic) update_hp_mic(codec, c, true); } if (spec->cap_sync_hook) spec->cap_sync_hook(codec, NULL, NULL); } /* set right pin controls for digital I/O */ static void init_digital(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int i; hda_nid_t pin; for (i = 0; i < spec->autocfg.dig_outs; i++) set_output_and_unmute(codec, spec->digout_paths[i]); pin = spec->autocfg.dig_in_pin; if (pin) { restore_pin_ctl(codec, pin); resume_path_from_idx(codec, spec->digin_path); } } /* clear unsol-event tags on unused pins; Conexant codecs seem to leave * invalid unsol tags by some reason */ static void clear_unsol_on_unused_pins(struct hda_codec *codec) { const struct hda_pincfg *pin; int i; snd_array_for_each(&codec->init_pins, i, pin) { hda_nid_t nid = pin->nid; if (is_jack_detectable(codec, nid) && !snd_hda_jack_tbl_get(codec, nid)) snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, 0); } } /** * snd_hda_gen_init - initialize the generic spec * @codec: the HDA codec * * This can be put as patch_ops init function. */ int snd_hda_gen_init(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; if (spec->init_hook) spec->init_hook(codec); if (!spec->skip_verbs) snd_hda_apply_verbs(codec); init_multi_out(codec); init_extra_out(codec); init_multi_io(codec); init_aamix_paths(codec); init_analog_input(codec); init_input_src(codec); init_digital(codec); clear_unsol_on_unused_pins(codec); sync_all_pin_power_ctls(codec); /* call init functions of standard auto-mute helpers */ update_automute_all(codec); regcache_sync(codec->core.regmap); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); hda_call_check_power_status(codec, 0x01); return 0; } EXPORT_SYMBOL_GPL(snd_hda_gen_init); /** * snd_hda_gen_free - free the generic spec * @codec: the HDA codec * * This can be put as patch_ops free function. */ void snd_hda_gen_free(struct hda_codec *codec) { snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); snd_hda_gen_spec_free(codec->spec); kfree(codec->spec); codec->spec = NULL; } EXPORT_SYMBOL_GPL(snd_hda_gen_free); /** * snd_hda_gen_reboot_notify - Make codec enter D3 before rebooting * @codec: the HDA codec * * This can be put as patch_ops reboot_notify function. */ void snd_hda_gen_reboot_notify(struct hda_codec *codec) { /* Make the codec enter D3 to avoid spurious noises from the internal * speaker during (and after) reboot */ snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); msleep(10); } EXPORT_SYMBOL_GPL(snd_hda_gen_reboot_notify); #ifdef CONFIG_PM /** * snd_hda_gen_check_power_status - check the loopback power save state * @codec: the HDA codec * @nid: NID to inspect * * This can be put as patch_ops check_power_status function. */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); #endif /* * the generic codec support */ static const struct hda_codec_ops generic_patch_ops = { .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, .reboot_notify = snd_hda_gen_reboot_notify, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, #endif }; /* * snd_hda_parse_generic_codec - Generic codec parser * @codec: the HDA codec */ static int snd_hda_parse_generic_codec(struct hda_codec *codec) { struct hda_gen_spec *spec; int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; snd_hda_gen_spec_init(spec); codec->spec = spec; err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); if (err < 0) goto error; err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); if (err < 0) goto error; codec->patch_ops = generic_patch_ops; return 0; error: snd_hda_gen_free(codec); return err; } static const struct hda_device_id snd_hda_id_generic[] = { HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); static struct hda_codec_driver generic_driver = { .id = snd_hda_id_generic, }; module_hda_codec_driver(generic_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic HD-audio codec parser");