162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Universal Interface for Intel High Definition Audio Codec 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic widget tree parser 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/sort.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/ctype.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/bitops.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/leds.h> 2062306a36Sopenharmony_ci#include <sound/core.h> 2162306a36Sopenharmony_ci#include <sound/jack.h> 2262306a36Sopenharmony_ci#include <sound/tlv.h> 2362306a36Sopenharmony_ci#include <sound/hda_codec.h> 2462306a36Sopenharmony_ci#include "hda_local.h" 2562306a36Sopenharmony_ci#include "hda_auto_parser.h" 2662306a36Sopenharmony_ci#include "hda_jack.h" 2762306a36Sopenharmony_ci#include "hda_beep.h" 2862306a36Sopenharmony_ci#include "hda_generic.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/** 3262306a36Sopenharmony_ci * snd_hda_gen_spec_init - initialize hda_gen_spec struct 3362306a36Sopenharmony_ci * @spec: hda_gen_spec object to initialize 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Initialize the given hda_gen_spec object. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ciint snd_hda_gen_spec_init(struct hda_gen_spec *spec) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); 4062306a36Sopenharmony_ci snd_array_init(&spec->paths, sizeof(struct nid_path), 8); 4162306a36Sopenharmony_ci snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8); 4262306a36Sopenharmony_ci mutex_init(&spec->pcm_mutex); 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template 4962306a36Sopenharmony_ci * @spec: hda_gen_spec object 5062306a36Sopenharmony_ci * @name: name string to override the template, NULL if unchanged 5162306a36Sopenharmony_ci * @temp: template for the new kctl 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Add a new kctl (actually snd_kcontrol_new to be instantiated later) 5462306a36Sopenharmony_ci * element based on the given snd_kcontrol_new template @temp and the 5562306a36Sopenharmony_ci * name string @name to the list in @spec. 5662306a36Sopenharmony_ci * Returns the newly created object or NULL as error. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistruct snd_kcontrol_new * 5962306a36Sopenharmony_cisnd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, 6062306a36Sopenharmony_ci const struct snd_kcontrol_new *temp) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); 6362306a36Sopenharmony_ci if (!knew) 6462306a36Sopenharmony_ci return NULL; 6562306a36Sopenharmony_ci *knew = *temp; 6662306a36Sopenharmony_ci if (name) 6762306a36Sopenharmony_ci knew->name = kstrdup(name, GFP_KERNEL); 6862306a36Sopenharmony_ci else if (knew->name) 6962306a36Sopenharmony_ci knew->name = kstrdup(knew->name, GFP_KERNEL); 7062306a36Sopenharmony_ci if (!knew->name) 7162306a36Sopenharmony_ci return NULL; 7262306a36Sopenharmony_ci return knew; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void free_kctls(struct hda_gen_spec *spec) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (spec->kctls.list) { 7962306a36Sopenharmony_ci struct snd_kcontrol_new *kctl = spec->kctls.list; 8062306a36Sopenharmony_ci int i; 8162306a36Sopenharmony_ci for (i = 0; i < spec->kctls.used; i++) 8262306a36Sopenharmony_ci kfree(kctl[i].name); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci snd_array_free(&spec->kctls); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void snd_hda_gen_spec_free(struct hda_gen_spec *spec) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci if (!spec) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci free_kctls(spec); 9262306a36Sopenharmony_ci snd_array_free(&spec->paths); 9362306a36Sopenharmony_ci snd_array_free(&spec->loopback_list); 9462306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_GENERIC_LEDS 9562306a36Sopenharmony_ci if (spec->led_cdevs[LED_AUDIO_MUTE]) 9662306a36Sopenharmony_ci led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]); 9762306a36Sopenharmony_ci if (spec->led_cdevs[LED_AUDIO_MICMUTE]) 9862306a36Sopenharmony_ci led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]); 9962306a36Sopenharmony_ci#endif 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * store user hints 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic void parse_user_hints(struct hda_codec *codec) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 10862306a36Sopenharmony_ci int val; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "jack_detect"); 11162306a36Sopenharmony_ci if (val >= 0) 11262306a36Sopenharmony_ci codec->no_jack_detect = !val; 11362306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); 11462306a36Sopenharmony_ci if (val >= 0) 11562306a36Sopenharmony_ci codec->inv_jack_detect = !!val; 11662306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "trigger_sense"); 11762306a36Sopenharmony_ci if (val >= 0) 11862306a36Sopenharmony_ci codec->no_trigger_sense = !val; 11962306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_eapd"); 12062306a36Sopenharmony_ci if (val >= 0) 12162306a36Sopenharmony_ci codec->inv_eapd = !!val; 12262306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "pcm_format_first"); 12362306a36Sopenharmony_ci if (val >= 0) 12462306a36Sopenharmony_ci codec->pcm_format_first = !!val; 12562306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "sticky_stream"); 12662306a36Sopenharmony_ci if (val >= 0) 12762306a36Sopenharmony_ci codec->no_sticky_stream = !val; 12862306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); 12962306a36Sopenharmony_ci if (val >= 0) 13062306a36Sopenharmony_ci codec->spdif_status_reset = !!val; 13162306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); 13262306a36Sopenharmony_ci if (val >= 0) 13362306a36Sopenharmony_ci codec->pin_amp_workaround = !!val; 13462306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "single_adc_amp"); 13562306a36Sopenharmony_ci if (val >= 0) 13662306a36Sopenharmony_ci codec->single_adc_amp = !!val; 13762306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "power_save_node"); 13862306a36Sopenharmony_ci if (val >= 0) 13962306a36Sopenharmony_ci codec->power_save_node = !!val; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mute"); 14262306a36Sopenharmony_ci if (val >= 0) 14362306a36Sopenharmony_ci spec->suppress_auto_mute = !val; 14462306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mic"); 14562306a36Sopenharmony_ci if (val >= 0) 14662306a36Sopenharmony_ci spec->suppress_auto_mic = !val; 14762306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); 14862306a36Sopenharmony_ci if (val >= 0) 14962306a36Sopenharmony_ci spec->line_in_auto_switch = !!val; 15062306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); 15162306a36Sopenharmony_ci if (val >= 0) 15262306a36Sopenharmony_ci spec->auto_mute_via_amp = !!val; 15362306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "need_dac_fix"); 15462306a36Sopenharmony_ci if (val >= 0) 15562306a36Sopenharmony_ci spec->need_dac_fix = !!val; 15662306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "primary_hp"); 15762306a36Sopenharmony_ci if (val >= 0) 15862306a36Sopenharmony_ci spec->no_primary_hp = !val; 15962306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "multi_io"); 16062306a36Sopenharmony_ci if (val >= 0) 16162306a36Sopenharmony_ci spec->no_multi_io = !val; 16262306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); 16362306a36Sopenharmony_ci if (val >= 0) 16462306a36Sopenharmony_ci spec->multi_cap_vol = !!val; 16562306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); 16662306a36Sopenharmony_ci if (val >= 0) 16762306a36Sopenharmony_ci spec->inv_dmic_split = !!val; 16862306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "indep_hp"); 16962306a36Sopenharmony_ci if (val >= 0) 17062306a36Sopenharmony_ci spec->indep_hp = !!val; 17162306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); 17262306a36Sopenharmony_ci if (val >= 0) 17362306a36Sopenharmony_ci spec->add_stereo_mix_input = !!val; 17462306a36Sopenharmony_ci /* the following two are just for compatibility */ 17562306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); 17662306a36Sopenharmony_ci if (val >= 0) 17762306a36Sopenharmony_ci spec->add_jack_modes = !!val; 17862306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); 17962306a36Sopenharmony_ci if (val >= 0) 18062306a36Sopenharmony_ci spec->add_jack_modes = !!val; 18162306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_jack_modes"); 18262306a36Sopenharmony_ci if (val >= 0) 18362306a36Sopenharmony_ci spec->add_jack_modes = !!val; 18462306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "power_down_unused"); 18562306a36Sopenharmony_ci if (val >= 0) 18662306a36Sopenharmony_ci spec->power_down_unused = !!val; 18762306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_hp_mic"); 18862306a36Sopenharmony_ci if (val >= 0) 18962306a36Sopenharmony_ci spec->hp_mic = !!val; 19062306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "hp_mic_detect"); 19162306a36Sopenharmony_ci if (val >= 0) 19262306a36Sopenharmony_ci spec->suppress_hp_mic_detect = !val; 19362306a36Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "vmaster"); 19462306a36Sopenharmony_ci if (val >= 0) 19562306a36Sopenharmony_ci spec->suppress_vmaster = !val; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) 19862306a36Sopenharmony_ci spec->mixer_nid = val; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * pin control value accesses 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci#define update_pin_ctl(codec, pin, val) \ 20662306a36Sopenharmony_ci snd_hda_codec_write_cache(codec, pin, 0, \ 20762306a36Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, val) 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* restore the pinctl based on the cached value */ 21062306a36Sopenharmony_cistatic inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* set the pinctl target value and write it if requested */ 21662306a36Sopenharmony_cistatic void set_pin_target(struct hda_codec *codec, hda_nid_t pin, 21762306a36Sopenharmony_ci unsigned int val, bool do_write) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (!pin) 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci val = snd_hda_correct_pin_ctl(codec, pin, val); 22262306a36Sopenharmony_ci snd_hda_codec_set_pin_target(codec, pin, val); 22362306a36Sopenharmony_ci if (do_write) 22462306a36Sopenharmony_ci update_pin_ctl(codec, pin, val); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* set pinctl target values for all given pins */ 22862306a36Sopenharmony_cistatic void set_pin_targets(struct hda_codec *codec, int num_pins, 22962306a36Sopenharmony_ci hda_nid_t *pins, unsigned int val) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int i; 23262306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) 23362306a36Sopenharmony_ci set_pin_target(codec, pins[i], val, false); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * parsing paths 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* return the position of NID in the list, or -1 if not found */ 24162306a36Sopenharmony_cistatic int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int i; 24462306a36Sopenharmony_ci for (i = 0; i < nums; i++) 24562306a36Sopenharmony_ci if (list[i] == nid) 24662306a36Sopenharmony_ci return i; 24762306a36Sopenharmony_ci return -1; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* return true if the given NID is contained in the path */ 25162306a36Sopenharmony_cistatic bool is_nid_contained(struct nid_path *path, hda_nid_t nid) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct nid_path *get_nid_path(struct hda_codec *codec, 25762306a36Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid, 25862306a36Sopenharmony_ci int anchor_nid) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 26162306a36Sopenharmony_ci struct nid_path *path; 26262306a36Sopenharmony_ci int i; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 26562306a36Sopenharmony_ci if (path->depth <= 0) 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci if ((!from_nid || path->path[0] == from_nid) && 26862306a36Sopenharmony_ci (!to_nid || path->path[path->depth - 1] == to_nid)) { 26962306a36Sopenharmony_ci if (!anchor_nid || 27062306a36Sopenharmony_ci (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || 27162306a36Sopenharmony_ci (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) 27262306a36Sopenharmony_ci return path; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci return NULL; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/** 27962306a36Sopenharmony_ci * snd_hda_get_path_idx - get the index number corresponding to the path 28062306a36Sopenharmony_ci * instance 28162306a36Sopenharmony_ci * @codec: the HDA codec 28262306a36Sopenharmony_ci * @path: nid_path object 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * The returned index starts from 1, i.e. the actual array index with offset 1, 28562306a36Sopenharmony_ci * and zero is handled as an invalid path 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ciint snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 29062306a36Sopenharmony_ci struct nid_path *array = spec->paths.list; 29162306a36Sopenharmony_ci ssize_t idx; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!spec->paths.used) 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci idx = path - array; 29662306a36Sopenharmony_ci if (idx < 0 || idx >= spec->paths.used) 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci return idx + 1; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_path_idx); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/** 30362306a36Sopenharmony_ci * snd_hda_get_path_from_idx - get the path instance corresponding to the 30462306a36Sopenharmony_ci * given index number 30562306a36Sopenharmony_ci * @codec: the HDA codec 30662306a36Sopenharmony_ci * @idx: the path index 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistruct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (idx <= 0 || idx > spec->paths.used) 31362306a36Sopenharmony_ci return NULL; 31462306a36Sopenharmony_ci return snd_array_elem(&spec->paths, idx - 1); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* check whether the given DAC is already found in any existing paths */ 31962306a36Sopenharmony_cistatic bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 32262306a36Sopenharmony_ci const struct nid_path *path; 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 32662306a36Sopenharmony_ci if (path->path[0] == nid) 32762306a36Sopenharmony_ci return true; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci return false; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* check whether the given two widgets can be connected */ 33362306a36Sopenharmony_cistatic bool is_reachable_path(struct hda_codec *codec, 33462306a36Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci if (!from_nid || !to_nid) 33762306a36Sopenharmony_ci return false; 33862306a36Sopenharmony_ci return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* nid, dir and idx */ 34262306a36Sopenharmony_ci#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* check whether the given ctl is already assigned in any path elements */ 34562306a36Sopenharmony_cistatic bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 34862306a36Sopenharmony_ci const struct nid_path *path; 34962306a36Sopenharmony_ci int i; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci val &= AMP_VAL_COMPARE_MASK; 35262306a36Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 35362306a36Sopenharmony_ci if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) 35462306a36Sopenharmony_ci return true; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci return false; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* check whether a control with the given (nid, dir, idx) was assigned */ 36062306a36Sopenharmony_cistatic bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, 36162306a36Sopenharmony_ci int dir, int idx, int type) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); 36462306a36Sopenharmony_ci return is_ctl_used(codec, val, type); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void print_nid_path(struct hda_codec *codec, 36862306a36Sopenharmony_ci const char *pfx, struct nid_path *path) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci char buf[40]; 37162306a36Sopenharmony_ci char *pos = buf; 37262306a36Sopenharmony_ci int i; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci *pos = 0; 37562306a36Sopenharmony_ci for (i = 0; i < path->depth; i++) 37662306a36Sopenharmony_ci pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", 37762306a36Sopenharmony_ci pos != buf ? ":" : "", 37862306a36Sopenharmony_ci path->path[i]); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* called recursively */ 38462306a36Sopenharmony_cistatic bool __parse_nid_path(struct hda_codec *codec, 38562306a36Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid, 38662306a36Sopenharmony_ci int anchor_nid, struct nid_path *path, 38762306a36Sopenharmony_ci int depth) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci const hda_nid_t *conn; 39062306a36Sopenharmony_ci int i, nums; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (to_nid == anchor_nid) 39362306a36Sopenharmony_ci anchor_nid = 0; /* anchor passed */ 39462306a36Sopenharmony_ci else if (to_nid == (hda_nid_t)(-anchor_nid)) 39562306a36Sopenharmony_ci return false; /* hit the exclusive nid */ 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci nums = snd_hda_get_conn_list(codec, to_nid, &conn); 39862306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 39962306a36Sopenharmony_ci if (conn[i] != from_nid) { 40062306a36Sopenharmony_ci /* special case: when from_nid is 0, 40162306a36Sopenharmony_ci * try to find an empty DAC 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci if (from_nid || 40462306a36Sopenharmony_ci get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || 40562306a36Sopenharmony_ci is_dac_already_used(codec, conn[i])) 40662306a36Sopenharmony_ci continue; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci /* anchor is not requested or already passed? */ 40962306a36Sopenharmony_ci if (anchor_nid <= 0) 41062306a36Sopenharmony_ci goto found; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (depth >= MAX_NID_PATH_DEPTH) 41362306a36Sopenharmony_ci return false; 41462306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 41562306a36Sopenharmony_ci unsigned int type; 41662306a36Sopenharmony_ci type = get_wcaps_type(get_wcaps(codec, conn[i])); 41762306a36Sopenharmony_ci if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || 41862306a36Sopenharmony_ci type == AC_WID_PIN) 41962306a36Sopenharmony_ci continue; 42062306a36Sopenharmony_ci if (__parse_nid_path(codec, from_nid, conn[i], 42162306a36Sopenharmony_ci anchor_nid, path, depth + 1)) 42262306a36Sopenharmony_ci goto found; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci return false; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci found: 42762306a36Sopenharmony_ci path->path[path->depth] = conn[i]; 42862306a36Sopenharmony_ci path->idx[path->depth + 1] = i; 42962306a36Sopenharmony_ci if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) 43062306a36Sopenharmony_ci path->multi[path->depth + 1] = 1; 43162306a36Sopenharmony_ci path->depth++; 43262306a36Sopenharmony_ci return true; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* 43662306a36Sopenharmony_ci * snd_hda_parse_nid_path - parse the widget path from the given nid to 43762306a36Sopenharmony_ci * the target nid 43862306a36Sopenharmony_ci * @codec: the HDA codec 43962306a36Sopenharmony_ci * @from_nid: the NID where the path start from 44062306a36Sopenharmony_ci * @to_nid: the NID where the path ends at 44162306a36Sopenharmony_ci * @anchor_nid: the anchor indication 44262306a36Sopenharmony_ci * @path: the path object to store the result 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * Returns true if a matching path is found. 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * The parsing behavior depends on parameters: 44762306a36Sopenharmony_ci * when @from_nid is 0, try to find an empty DAC; 44862306a36Sopenharmony_ci * when @anchor_nid is set to a positive value, only paths through the widget 44962306a36Sopenharmony_ci * with the given value are evaluated. 45062306a36Sopenharmony_ci * when @anchor_nid is set to a negative value, paths through the widget 45162306a36Sopenharmony_ci * with the negative of given value are excluded, only other paths are chosen. 45262306a36Sopenharmony_ci * when @anchor_nid is zero, no special handling about path selection. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, 45562306a36Sopenharmony_ci hda_nid_t to_nid, int anchor_nid, 45662306a36Sopenharmony_ci struct nid_path *path) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { 45962306a36Sopenharmony_ci path->path[path->depth] = to_nid; 46062306a36Sopenharmony_ci path->depth++; 46162306a36Sopenharmony_ci return true; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci return false; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * snd_hda_add_new_path - parse the path between the given NIDs and 46862306a36Sopenharmony_ci * add to the path list 46962306a36Sopenharmony_ci * @codec: the HDA codec 47062306a36Sopenharmony_ci * @from_nid: the NID where the path start from 47162306a36Sopenharmony_ci * @to_nid: the NID where the path ends at 47262306a36Sopenharmony_ci * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * If no valid path is found, returns NULL. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_cistruct nid_path * 47762306a36Sopenharmony_cisnd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, 47862306a36Sopenharmony_ci hda_nid_t to_nid, int anchor_nid) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 48162306a36Sopenharmony_ci struct nid_path *path; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) 48462306a36Sopenharmony_ci return NULL; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* check whether the path has been already added */ 48762306a36Sopenharmony_ci path = get_nid_path(codec, from_nid, to_nid, anchor_nid); 48862306a36Sopenharmony_ci if (path) 48962306a36Sopenharmony_ci return path; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci path = snd_array_new(&spec->paths); 49262306a36Sopenharmony_ci if (!path) 49362306a36Sopenharmony_ci return NULL; 49462306a36Sopenharmony_ci memset(path, 0, sizeof(*path)); 49562306a36Sopenharmony_ci if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) 49662306a36Sopenharmony_ci return path; 49762306a36Sopenharmony_ci /* push back */ 49862306a36Sopenharmony_ci spec->paths.used--; 49962306a36Sopenharmony_ci return NULL; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_new_path); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* clear the given path as invalid so that it won't be picked up later */ 50462306a36Sopenharmony_cistatic void invalidate_nid_path(struct hda_codec *codec, int idx) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); 50762306a36Sopenharmony_ci if (!path) 50862306a36Sopenharmony_ci return; 50962306a36Sopenharmony_ci memset(path, 0, sizeof(*path)); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* return a DAC if paired to the given pin by codec driver */ 51362306a36Sopenharmony_cistatic hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 51662306a36Sopenharmony_ci const hda_nid_t *list = spec->preferred_dacs; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!list) 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci for (; *list; list += 2) 52162306a36Sopenharmony_ci if (*list == pin) 52262306a36Sopenharmony_ci return list[1]; 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* look for an empty DAC slot */ 52762306a36Sopenharmony_cistatic hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, 52862306a36Sopenharmony_ci bool is_digital) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 53162306a36Sopenharmony_ci bool cap_digital; 53262306a36Sopenharmony_ci int i; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci for (i = 0; i < spec->num_all_dacs; i++) { 53562306a36Sopenharmony_ci hda_nid_t nid = spec->all_dacs[i]; 53662306a36Sopenharmony_ci if (!nid || is_dac_already_used(codec, nid)) 53762306a36Sopenharmony_ci continue; 53862306a36Sopenharmony_ci cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); 53962306a36Sopenharmony_ci if (is_digital != cap_digital) 54062306a36Sopenharmony_ci continue; 54162306a36Sopenharmony_ci if (is_reachable_path(codec, nid, pin)) 54262306a36Sopenharmony_ci return nid; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* replace the channels in the composed amp value with the given number */ 54862306a36Sopenharmony_cistatic unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci val &= ~(0x3U << 16); 55162306a36Sopenharmony_ci val |= chs << 16; 55262306a36Sopenharmony_ci return val; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, 55662306a36Sopenharmony_ci hda_nid_t nid2, int dir) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) 55962306a36Sopenharmony_ci return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); 56062306a36Sopenharmony_ci return (query_amp_caps(codec, nid1, dir) == 56162306a36Sopenharmony_ci query_amp_caps(codec, nid2, dir)); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* look for a widget suitable for assigning a mute switch in the path */ 56562306a36Sopenharmony_cistatic hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, 56662306a36Sopenharmony_ci struct nid_path *path) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci int i; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 57162306a36Sopenharmony_ci if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) 57262306a36Sopenharmony_ci return path->path[i]; 57362306a36Sopenharmony_ci if (i != path->depth - 1 && i != 0 && 57462306a36Sopenharmony_ci nid_has_mute(codec, path->path[i], HDA_INPUT)) 57562306a36Sopenharmony_ci return path->path[i]; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* look for a widget suitable for assigning a volume ctl in the path */ 58162306a36Sopenharmony_cistatic hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, 58262306a36Sopenharmony_ci struct nid_path *path) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 58562306a36Sopenharmony_ci int i; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 58862306a36Sopenharmony_ci hda_nid_t nid = path->path[i]; 58962306a36Sopenharmony_ci if ((spec->out_vol_mask >> nid) & 1) 59062306a36Sopenharmony_ci continue; 59162306a36Sopenharmony_ci if (nid_has_volume(codec, nid, HDA_OUTPUT)) 59262306a36Sopenharmony_ci return nid; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* 59862306a36Sopenharmony_ci * path activation / deactivation 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* can have the amp-in capability? */ 60262306a36Sopenharmony_cistatic bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci hda_nid_t nid = path->path[idx]; 60562306a36Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 60662306a36Sopenharmony_ci unsigned int type = get_wcaps_type(caps); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!(caps & AC_WCAP_IN_AMP)) 60962306a36Sopenharmony_ci return false; 61062306a36Sopenharmony_ci if (type == AC_WID_PIN && idx > 0) /* only for input pins */ 61162306a36Sopenharmony_ci return false; 61262306a36Sopenharmony_ci return true; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* can have the amp-out capability? */ 61662306a36Sopenharmony_cistatic bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci hda_nid_t nid = path->path[idx]; 61962306a36Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 62062306a36Sopenharmony_ci unsigned int type = get_wcaps_type(caps); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!(caps & AC_WCAP_OUT_AMP)) 62362306a36Sopenharmony_ci return false; 62462306a36Sopenharmony_ci if (type == AC_WID_PIN && !idx) /* only for output pins */ 62562306a36Sopenharmony_ci return false; 62662306a36Sopenharmony_ci return true; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* check whether the given (nid,dir,idx) is active */ 63062306a36Sopenharmony_cistatic bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, 63162306a36Sopenharmony_ci unsigned int dir, unsigned int idx) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 63462306a36Sopenharmony_ci int type = get_wcaps_type(get_wcaps(codec, nid)); 63562306a36Sopenharmony_ci const struct nid_path *path; 63662306a36Sopenharmony_ci int i, n; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (nid == codec->core.afg) 63962306a36Sopenharmony_ci return true; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci snd_array_for_each(&spec->paths, n, path) { 64262306a36Sopenharmony_ci if (!path->active) 64362306a36Sopenharmony_ci continue; 64462306a36Sopenharmony_ci if (codec->power_save_node) { 64562306a36Sopenharmony_ci if (!path->stream_enabled) 64662306a36Sopenharmony_ci continue; 64762306a36Sopenharmony_ci /* ignore unplugged paths except for DAC/ADC */ 64862306a36Sopenharmony_ci if (!(path->pin_enabled || path->pin_fixed) && 64962306a36Sopenharmony_ci type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) 65062306a36Sopenharmony_ci continue; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci for (i = 0; i < path->depth; i++) { 65362306a36Sopenharmony_ci if (path->path[i] == nid) { 65462306a36Sopenharmony_ci if (dir == HDA_OUTPUT || idx == -1 || 65562306a36Sopenharmony_ci path->idx[i] == idx) 65662306a36Sopenharmony_ci return true; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci return false; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* check whether the NID is referred by any active paths */ 66562306a36Sopenharmony_ci#define is_active_nid_for_any(codec, nid) \ 66662306a36Sopenharmony_ci is_active_nid(codec, nid, HDA_OUTPUT, -1) 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* get the default amp value for the target state */ 66962306a36Sopenharmony_cistatic int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, 67062306a36Sopenharmony_ci int dir, unsigned int caps, bool enable) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci unsigned int val = 0; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (caps & AC_AMPCAP_NUM_STEPS) { 67562306a36Sopenharmony_ci /* set to 0dB */ 67662306a36Sopenharmony_ci if (enable) 67762306a36Sopenharmony_ci val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { 68062306a36Sopenharmony_ci if (!enable) 68162306a36Sopenharmony_ci val |= HDA_AMP_MUTE; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci return val; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* is this a stereo widget or a stereo-to-mono mix? */ 68762306a36Sopenharmony_cistatic bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 69062306a36Sopenharmony_ci hda_nid_t conn; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (wcaps & AC_WCAP_STEREO) 69362306a36Sopenharmony_ci return true; 69462306a36Sopenharmony_ci if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) 69562306a36Sopenharmony_ci return false; 69662306a36Sopenharmony_ci if (snd_hda_get_num_conns(codec, nid) != 1) 69762306a36Sopenharmony_ci return false; 69862306a36Sopenharmony_ci if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) 69962306a36Sopenharmony_ci return false; 70062306a36Sopenharmony_ci return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/* initialize the amp value (only at the first time) */ 70462306a36Sopenharmony_cistatic void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci unsigned int caps = query_amp_caps(codec, nid, dir); 70762306a36Sopenharmony_ci int val = get_amp_val_to_activate(codec, nid, dir, caps, false); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (is_stereo_amps(codec, nid, dir)) 71062306a36Sopenharmony_ci snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); 71162306a36Sopenharmony_ci else 71262306a36Sopenharmony_ci snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* update the amp, doing in stereo or mono depending on NID */ 71662306a36Sopenharmony_cistatic int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, 71762306a36Sopenharmony_ci unsigned int mask, unsigned int val) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci if (is_stereo_amps(codec, nid, dir)) 72062306a36Sopenharmony_ci return snd_hda_codec_amp_stereo(codec, nid, dir, idx, 72162306a36Sopenharmony_ci mask, val); 72262306a36Sopenharmony_ci else 72362306a36Sopenharmony_ci return snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 72462306a36Sopenharmony_ci mask, val); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/* calculate amp value mask we can modify; 72862306a36Sopenharmony_ci * if the given amp is controlled by mixers, don't touch it 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic unsigned int get_amp_mask_to_modify(struct hda_codec *codec, 73162306a36Sopenharmony_ci hda_nid_t nid, int dir, int idx, 73262306a36Sopenharmony_ci unsigned int caps) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci unsigned int mask = 0xff; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { 73762306a36Sopenharmony_ci if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) 73862306a36Sopenharmony_ci mask &= ~0x80; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci if (caps & AC_AMPCAP_NUM_STEPS) { 74162306a36Sopenharmony_ci if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || 74262306a36Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) 74362306a36Sopenharmony_ci mask &= ~0x7f; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci return mask; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, 74962306a36Sopenharmony_ci int idx, int idx_to_check, bool enable) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci unsigned int caps; 75262306a36Sopenharmony_ci unsigned int mask, val; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci caps = query_amp_caps(codec, nid, dir); 75562306a36Sopenharmony_ci val = get_amp_val_to_activate(codec, nid, dir, caps, enable); 75662306a36Sopenharmony_ci mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); 75762306a36Sopenharmony_ci if (!mask) 75862306a36Sopenharmony_ci return; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci val &= mask; 76162306a36Sopenharmony_ci update_amp(codec, nid, dir, idx, mask, val); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid, 76562306a36Sopenharmony_ci int dir, int idx, int idx_to_check, 76662306a36Sopenharmony_ci bool enable) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci /* check whether the given amp is still used by others */ 76962306a36Sopenharmony_ci if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) 77062306a36Sopenharmony_ci return; 77162306a36Sopenharmony_ci activate_amp(codec, nid, dir, idx, idx_to_check, enable); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void activate_amp_out(struct hda_codec *codec, struct nid_path *path, 77562306a36Sopenharmony_ci int i, bool enable) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci hda_nid_t nid = path->path[i]; 77862306a36Sopenharmony_ci init_amp(codec, nid, HDA_OUTPUT, 0); 77962306a36Sopenharmony_ci check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic void activate_amp_in(struct hda_codec *codec, struct nid_path *path, 78362306a36Sopenharmony_ci int i, bool enable, bool add_aamix) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 78662306a36Sopenharmony_ci const hda_nid_t *conn; 78762306a36Sopenharmony_ci int n, nums, idx; 78862306a36Sopenharmony_ci int type; 78962306a36Sopenharmony_ci hda_nid_t nid = path->path[i]; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci nums = snd_hda_get_conn_list(codec, nid, &conn); 79262306a36Sopenharmony_ci if (nums < 0) 79362306a36Sopenharmony_ci return; 79462306a36Sopenharmony_ci type = get_wcaps_type(get_wcaps(codec, nid)); 79562306a36Sopenharmony_ci if (type == AC_WID_PIN || 79662306a36Sopenharmony_ci (type == AC_WID_AUD_IN && codec->single_adc_amp)) { 79762306a36Sopenharmony_ci nums = 1; 79862306a36Sopenharmony_ci idx = 0; 79962306a36Sopenharmony_ci } else 80062306a36Sopenharmony_ci idx = path->idx[i]; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (n = 0; n < nums; n++) 80362306a36Sopenharmony_ci init_amp(codec, nid, HDA_INPUT, n); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* here is a little bit tricky in comparison with activate_amp_out(); 80662306a36Sopenharmony_ci * when aa-mixer is available, we need to enable the path as well 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci for (n = 0; n < nums; n++) { 80962306a36Sopenharmony_ci if (n != idx) { 81062306a36Sopenharmony_ci if (conn[n] != spec->mixer_merge_nid) 81162306a36Sopenharmony_ci continue; 81262306a36Sopenharmony_ci /* when aamix is disabled, force to off */ 81362306a36Sopenharmony_ci if (!add_aamix) { 81462306a36Sopenharmony_ci activate_amp(codec, nid, HDA_INPUT, n, n, false); 81562306a36Sopenharmony_ci continue; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable); 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* sync power of each widget in the given path */ 82362306a36Sopenharmony_cistatic hda_nid_t path_power_update(struct hda_codec *codec, 82462306a36Sopenharmony_ci struct nid_path *path, 82562306a36Sopenharmony_ci bool allow_powerdown) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci hda_nid_t nid, changed = 0; 82862306a36Sopenharmony_ci int i, state, power; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci for (i = 0; i < path->depth; i++) { 83162306a36Sopenharmony_ci nid = path->path[i]; 83262306a36Sopenharmony_ci if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) 83362306a36Sopenharmony_ci continue; 83462306a36Sopenharmony_ci if (nid == codec->core.afg) 83562306a36Sopenharmony_ci continue; 83662306a36Sopenharmony_ci if (!allow_powerdown || is_active_nid_for_any(codec, nid)) 83762306a36Sopenharmony_ci state = AC_PWRST_D0; 83862306a36Sopenharmony_ci else 83962306a36Sopenharmony_ci state = AC_PWRST_D3; 84062306a36Sopenharmony_ci power = snd_hda_codec_read(codec, nid, 0, 84162306a36Sopenharmony_ci AC_VERB_GET_POWER_STATE, 0); 84262306a36Sopenharmony_ci if (power != (state | (state << 4))) { 84362306a36Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, 84462306a36Sopenharmony_ci AC_VERB_SET_POWER_STATE, state); 84562306a36Sopenharmony_ci changed = nid; 84662306a36Sopenharmony_ci /* all known codecs seem to be capable to handl 84762306a36Sopenharmony_ci * widgets state even in D3, so far. 84862306a36Sopenharmony_ci * if any new codecs need to restore the widget 84962306a36Sopenharmony_ci * states after D0 transition, call the function 85062306a36Sopenharmony_ci * below. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci#if 0 /* disabled */ 85362306a36Sopenharmony_ci if (state == AC_PWRST_D0) 85462306a36Sopenharmony_ci snd_hdac_regmap_sync_node(&codec->core, nid); 85562306a36Sopenharmony_ci#endif 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci return changed; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* do sync with the last power state change */ 86262306a36Sopenharmony_cistatic void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci if (nid) { 86562306a36Sopenharmony_ci msleep(10); 86662306a36Sopenharmony_ci snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/** 87162306a36Sopenharmony_ci * snd_hda_activate_path - activate or deactivate the given path 87262306a36Sopenharmony_ci * @codec: the HDA codec 87362306a36Sopenharmony_ci * @path: the path to activate/deactivate 87462306a36Sopenharmony_ci * @enable: flag to activate or not 87562306a36Sopenharmony_ci * @add_aamix: enable the input from aamix NID 87662306a36Sopenharmony_ci * 87762306a36Sopenharmony_ci * If @add_aamix is set, enable the input from aa-mix NID as well (if any). 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_civoid snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, 88062306a36Sopenharmony_ci bool enable, bool add_aamix) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 88362306a36Sopenharmony_ci int i; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci path->active = enable; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* make sure the widget is powered up */ 88862306a36Sopenharmony_ci if (enable && (spec->power_down_unused || codec->power_save_node)) 88962306a36Sopenharmony_ci path_power_update(codec, path, codec->power_save_node); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 89262306a36Sopenharmony_ci hda_nid_t nid = path->path[i]; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (enable && path->multi[i]) 89562306a36Sopenharmony_ci snd_hda_codec_write_cache(codec, nid, 0, 89662306a36Sopenharmony_ci AC_VERB_SET_CONNECT_SEL, 89762306a36Sopenharmony_ci path->idx[i]); 89862306a36Sopenharmony_ci if (has_amp_in(codec, path, i)) 89962306a36Sopenharmony_ci activate_amp_in(codec, path, i, enable, add_aamix); 90062306a36Sopenharmony_ci if (has_amp_out(codec, path, i)) 90162306a36Sopenharmony_ci activate_amp_out(codec, path, i, enable); 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_activate_path); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/* if the given path is inactive, put widgets into D3 (only if suitable) */ 90762306a36Sopenharmony_cistatic void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (!(spec->power_down_unused || codec->power_save_node) || path->active) 91262306a36Sopenharmony_ci return; 91362306a36Sopenharmony_ci sync_power_state_change(codec, path_power_update(codec, path, true)); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/* turn on/off EAPD on the given pin */ 91762306a36Sopenharmony_cistatic void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 92062306a36Sopenharmony_ci if (spec->own_eapd_ctl || 92162306a36Sopenharmony_ci !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) 92262306a36Sopenharmony_ci return; 92362306a36Sopenharmony_ci if (spec->keep_eapd_on && !enable) 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci if (codec->inv_eapd) 92662306a36Sopenharmony_ci enable = !enable; 92762306a36Sopenharmony_ci snd_hda_codec_write_cache(codec, pin, 0, 92862306a36Sopenharmony_ci AC_VERB_SET_EAPD_BTLENABLE, 92962306a36Sopenharmony_ci enable ? 0x02 : 0x00); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/* re-initialize the path specified by the given path index */ 93362306a36Sopenharmony_cistatic void resume_path_from_idx(struct hda_codec *codec, int path_idx) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); 93662306a36Sopenharmony_ci if (path) 93762306a36Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, false); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/* 94262306a36Sopenharmony_ci * Helper functions for creating mixer ctl elements 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, 94662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 94762306a36Sopenharmony_cistatic int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, 94862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 94962306a36Sopenharmony_cistatic int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, 95062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cienum { 95362306a36Sopenharmony_ci HDA_CTL_WIDGET_VOL, 95462306a36Sopenharmony_ci HDA_CTL_WIDGET_MUTE, 95562306a36Sopenharmony_ci HDA_CTL_BIND_MUTE, 95662306a36Sopenharmony_ci}; 95762306a36Sopenharmony_cistatic const struct snd_kcontrol_new control_templates[] = { 95862306a36Sopenharmony_ci HDA_CODEC_VOLUME(NULL, 0, 0, 0), 95962306a36Sopenharmony_ci /* only the put callback is replaced for handling the special mute */ 96062306a36Sopenharmony_ci { 96162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 96262306a36Sopenharmony_ci .subdevice = HDA_SUBDEV_AMP_FLAG, 96362306a36Sopenharmony_ci .info = snd_hda_mixer_amp_switch_info, 96462306a36Sopenharmony_ci .get = snd_hda_mixer_amp_switch_get, 96562306a36Sopenharmony_ci .put = hda_gen_mixer_mute_put, /* replaced */ 96662306a36Sopenharmony_ci .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), 96762306a36Sopenharmony_ci }, 96862306a36Sopenharmony_ci { 96962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 97062306a36Sopenharmony_ci .info = snd_hda_mixer_amp_switch_info, 97162306a36Sopenharmony_ci .get = hda_gen_bind_mute_get, 97262306a36Sopenharmony_ci .put = hda_gen_bind_mute_put, /* replaced */ 97362306a36Sopenharmony_ci .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), 97462306a36Sopenharmony_ci }, 97562306a36Sopenharmony_ci}; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* add dynamic controls from template */ 97862306a36Sopenharmony_cistatic struct snd_kcontrol_new * 97962306a36Sopenharmony_ciadd_control(struct hda_gen_spec *spec, int type, const char *name, 98062306a36Sopenharmony_ci int cidx, unsigned long val) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); 98562306a36Sopenharmony_ci if (!knew) 98662306a36Sopenharmony_ci return NULL; 98762306a36Sopenharmony_ci knew->index = cidx; 98862306a36Sopenharmony_ci if (get_amp_nid_(val)) 98962306a36Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 99062306a36Sopenharmony_ci if (knew->access == 0) 99162306a36Sopenharmony_ci knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 99262306a36Sopenharmony_ci knew->private_value = val; 99362306a36Sopenharmony_ci return knew; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic int add_control_with_pfx(struct hda_gen_spec *spec, int type, 99762306a36Sopenharmony_ci const char *pfx, const char *dir, 99862306a36Sopenharmony_ci const char *sfx, int cidx, unsigned long val) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 100162306a36Sopenharmony_ci int len; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); 100462306a36Sopenharmony_ci if (snd_BUG_ON(len >= sizeof(name))) 100562306a36Sopenharmony_ci return -EINVAL; 100662306a36Sopenharmony_ci if (!add_control(spec, type, name, cidx, val)) 100762306a36Sopenharmony_ci return -ENOMEM; 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci#define add_pb_vol_ctrl(spec, type, pfx, val) \ 101262306a36Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) 101362306a36Sopenharmony_ci#define add_pb_sw_ctrl(spec, type, pfx, val) \ 101462306a36Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) 101562306a36Sopenharmony_ci#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ 101662306a36Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) 101762306a36Sopenharmony_ci#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ 101862306a36Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, 102162306a36Sopenharmony_ci unsigned int chs, struct nid_path *path) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci unsigned int val; 102462306a36Sopenharmony_ci if (!path) 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci val = path->ctls[NID_PATH_VOL_CTL]; 102762306a36Sopenharmony_ci if (!val) 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci val = amp_val_replace_channels(val, chs); 103062306a36Sopenharmony_ci return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci/* return the channel bits suitable for the given path->ctls[] */ 103462306a36Sopenharmony_cistatic int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, 103562306a36Sopenharmony_ci int type) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci int chs = 1; /* mono (left only) */ 103862306a36Sopenharmony_ci if (path) { 103962306a36Sopenharmony_ci hda_nid_t nid = get_amp_nid_(path->ctls[type]); 104062306a36Sopenharmony_ci if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) 104162306a36Sopenharmony_ci chs = 3; /* stereo */ 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci return chs; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, 104762306a36Sopenharmony_ci struct nid_path *path) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); 105062306a36Sopenharmony_ci return add_vol_ctl(codec, pfx, cidx, chs, path); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* create a mute-switch for the given mixer widget; 105462306a36Sopenharmony_ci * if it has multiple sources (e.g. DAC and loopback), create a bind-mute 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_cistatic int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, 105762306a36Sopenharmony_ci unsigned int chs, struct nid_path *path) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci unsigned int val; 106062306a36Sopenharmony_ci int type = HDA_CTL_WIDGET_MUTE; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (!path) 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci val = path->ctls[NID_PATH_MUTE_CTL]; 106562306a36Sopenharmony_ci if (!val) 106662306a36Sopenharmony_ci return 0; 106762306a36Sopenharmony_ci val = amp_val_replace_channels(val, chs); 106862306a36Sopenharmony_ci if (get_amp_direction_(val) == HDA_INPUT) { 106962306a36Sopenharmony_ci hda_nid_t nid = get_amp_nid_(val); 107062306a36Sopenharmony_ci int nums = snd_hda_get_num_conns(codec, nid); 107162306a36Sopenharmony_ci if (nums > 1) { 107262306a36Sopenharmony_ci type = HDA_CTL_BIND_MUTE; 107362306a36Sopenharmony_ci val |= nums << 19; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic int add_stereo_sw(struct hda_codec *codec, const char *pfx, 108062306a36Sopenharmony_ci int cidx, struct nid_path *path) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); 108362306a36Sopenharmony_ci return add_sw_ctl(codec, pfx, cidx, chs, path); 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci/* playback mute control with the software mute bit check */ 108762306a36Sopenharmony_cistatic void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, 108862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 109162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (spec->auto_mute_via_amp) { 109462306a36Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 109562306a36Sopenharmony_ci bool enabled = !((spec->mute_bits >> nid) & 1); 109662306a36Sopenharmony_ci ucontrol->value.integer.value[0] &= enabled; 109762306a36Sopenharmony_ci ucontrol->value.integer.value[1] &= enabled; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, 110262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci sync_auto_mute_bits(kcontrol, ucontrol); 110562306a36Sopenharmony_ci return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/* 110962306a36Sopenharmony_ci * Bound mute controls 111062306a36Sopenharmony_ci */ 111162306a36Sopenharmony_ci#define AMP_VAL_IDX_SHIFT 19 111262306a36Sopenharmony_ci#define AMP_VAL_IDX_MASK (0x0f<<19) 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, 111562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 111862306a36Sopenharmony_ci unsigned long pval; 111962306a36Sopenharmony_ci int err; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci mutex_lock(&codec->control_mutex); 112262306a36Sopenharmony_ci pval = kcontrol->private_value; 112362306a36Sopenharmony_ci kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ 112462306a36Sopenharmony_ci err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); 112562306a36Sopenharmony_ci kcontrol->private_value = pval; 112662306a36Sopenharmony_ci mutex_unlock(&codec->control_mutex); 112762306a36Sopenharmony_ci return err; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, 113162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 113462306a36Sopenharmony_ci unsigned long pval; 113562306a36Sopenharmony_ci int i, indices, err = 0, change = 0; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci sync_auto_mute_bits(kcontrol, ucontrol); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci mutex_lock(&codec->control_mutex); 114062306a36Sopenharmony_ci pval = kcontrol->private_value; 114162306a36Sopenharmony_ci indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; 114262306a36Sopenharmony_ci for (i = 0; i < indices; i++) { 114362306a36Sopenharmony_ci kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | 114462306a36Sopenharmony_ci (i << AMP_VAL_IDX_SHIFT); 114562306a36Sopenharmony_ci err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 114662306a36Sopenharmony_ci if (err < 0) 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci change |= err; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci kcontrol->private_value = pval; 115162306a36Sopenharmony_ci mutex_unlock(&codec->control_mutex); 115262306a36Sopenharmony_ci return err < 0 ? err : change; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/* any ctl assigned to the path with the given index? */ 115662306a36Sopenharmony_cistatic bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); 115962306a36Sopenharmony_ci return path && path->ctls[ctl_type]; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic const char * const channel_name[] = { 116362306a36Sopenharmony_ci "Front", "Surround", "CLFE", "Side", "Back", 116462306a36Sopenharmony_ci}; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/* give some appropriate ctl name prefix for the given line out channel */ 116762306a36Sopenharmony_cistatic const char *get_line_out_pfx(struct hda_codec *codec, int ch, 116862306a36Sopenharmony_ci int *index, int ctl_type) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 117162306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci *index = 0; 117462306a36Sopenharmony_ci if (cfg->line_outs == 1 && !spec->multi_ios && 117562306a36Sopenharmony_ci !codec->force_pin_prefix && 117662306a36Sopenharmony_ci !cfg->hp_outs && !cfg->speaker_outs) 117762306a36Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* if there is really a single DAC used in the whole output paths, 118062306a36Sopenharmony_ci * use it master (or "PCM" if a vmaster hook is present) 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && 118362306a36Sopenharmony_ci !codec->force_pin_prefix && 118462306a36Sopenharmony_ci !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) 118562306a36Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* multi-io channels */ 118862306a36Sopenharmony_ci if (ch >= cfg->line_outs) 118962306a36Sopenharmony_ci goto fixed_name; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci switch (cfg->line_out_type) { 119262306a36Sopenharmony_ci case AUTO_PIN_SPEAKER_OUT: 119362306a36Sopenharmony_ci /* if the primary channel vol/mute is shared with HP volume, 119462306a36Sopenharmony_ci * don't name it as Speaker 119562306a36Sopenharmony_ci */ 119662306a36Sopenharmony_ci if (!ch && cfg->hp_outs && 119762306a36Sopenharmony_ci !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci if (cfg->line_outs == 1) 120062306a36Sopenharmony_ci return "Speaker"; 120162306a36Sopenharmony_ci if (cfg->line_outs == 2) 120262306a36Sopenharmony_ci return ch ? "Bass Speaker" : "Speaker"; 120362306a36Sopenharmony_ci break; 120462306a36Sopenharmony_ci case AUTO_PIN_HP_OUT: 120562306a36Sopenharmony_ci /* if the primary channel vol/mute is shared with spk volume, 120662306a36Sopenharmony_ci * don't name it as Headphone 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci if (!ch && cfg->speaker_outs && 120962306a36Sopenharmony_ci !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) 121062306a36Sopenharmony_ci break; 121162306a36Sopenharmony_ci /* for multi-io case, only the primary out */ 121262306a36Sopenharmony_ci if (ch && spec->multi_ios) 121362306a36Sopenharmony_ci break; 121462306a36Sopenharmony_ci *index = ch; 121562306a36Sopenharmony_ci return "Headphone"; 121662306a36Sopenharmony_ci case AUTO_PIN_LINE_OUT: 121762306a36Sopenharmony_ci /* This deals with the case where one HP or one Speaker or 121862306a36Sopenharmony_ci * one HP + one Speaker need to share the DAC with LO 121962306a36Sopenharmony_ci */ 122062306a36Sopenharmony_ci if (!ch) { 122162306a36Sopenharmony_ci bool hp_lo_shared = false, spk_lo_shared = false; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (cfg->speaker_outs) 122462306a36Sopenharmony_ci spk_lo_shared = !path_has_mixer(codec, 122562306a36Sopenharmony_ci spec->speaker_paths[0], ctl_type); 122662306a36Sopenharmony_ci if (cfg->hp_outs) 122762306a36Sopenharmony_ci hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); 122862306a36Sopenharmony_ci if (hp_lo_shared && spk_lo_shared) 122962306a36Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 123062306a36Sopenharmony_ci if (hp_lo_shared) 123162306a36Sopenharmony_ci return "Headphone+LO"; 123262306a36Sopenharmony_ci if (spk_lo_shared) 123362306a36Sopenharmony_ci return "Speaker+LO"; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* for a single channel output, we don't have to name the channel */ 123862306a36Sopenharmony_ci if (cfg->line_outs == 1 && !spec->multi_ios) 123962306a36Sopenharmony_ci return "Line Out"; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci fixed_name: 124262306a36Sopenharmony_ci if (ch >= ARRAY_SIZE(channel_name)) { 124362306a36Sopenharmony_ci snd_BUG(); 124462306a36Sopenharmony_ci return "PCM"; 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return channel_name[ch]; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/* 125162306a36Sopenharmony_ci * Parse output paths 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci/* badness definition */ 125562306a36Sopenharmony_cienum { 125662306a36Sopenharmony_ci /* No primary DAC is found for the main output */ 125762306a36Sopenharmony_ci BAD_NO_PRIMARY_DAC = 0x10000, 125862306a36Sopenharmony_ci /* No DAC is found for the extra output */ 125962306a36Sopenharmony_ci BAD_NO_DAC = 0x4000, 126062306a36Sopenharmony_ci /* No possible multi-ios */ 126162306a36Sopenharmony_ci BAD_MULTI_IO = 0x120, 126262306a36Sopenharmony_ci /* No individual DAC for extra output */ 126362306a36Sopenharmony_ci BAD_NO_EXTRA_DAC = 0x102, 126462306a36Sopenharmony_ci /* No individual DAC for extra surrounds */ 126562306a36Sopenharmony_ci BAD_NO_EXTRA_SURR_DAC = 0x101, 126662306a36Sopenharmony_ci /* Primary DAC shared with main surrounds */ 126762306a36Sopenharmony_ci BAD_SHARED_SURROUND = 0x100, 126862306a36Sopenharmony_ci /* No independent HP possible */ 126962306a36Sopenharmony_ci BAD_NO_INDEP_HP = 0x10, 127062306a36Sopenharmony_ci /* Primary DAC shared with main CLFE */ 127162306a36Sopenharmony_ci BAD_SHARED_CLFE = 0x10, 127262306a36Sopenharmony_ci /* Primary DAC shared with extra surrounds */ 127362306a36Sopenharmony_ci BAD_SHARED_EXTRA_SURROUND = 0x10, 127462306a36Sopenharmony_ci /* Volume widget is shared */ 127562306a36Sopenharmony_ci BAD_SHARED_VOL = 0x10, 127662306a36Sopenharmony_ci}; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci/* look for widgets in the given path which are appropriate for 127962306a36Sopenharmony_ci * volume and mute controls, and assign the values to ctls[]. 128062306a36Sopenharmony_ci * 128162306a36Sopenharmony_ci * When no appropriate widget is found in the path, the badness value 128262306a36Sopenharmony_ci * is incremented depending on the situation. The function returns the 128362306a36Sopenharmony_ci * total badness for both volume and mute controls. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_cistatic int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 128862306a36Sopenharmony_ci hda_nid_t nid; 128962306a36Sopenharmony_ci unsigned int val; 129062306a36Sopenharmony_ci int badness = 0; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (!path) 129362306a36Sopenharmony_ci return BAD_SHARED_VOL * 2; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (path->ctls[NID_PATH_VOL_CTL] || 129662306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL]) 129762306a36Sopenharmony_ci return 0; /* already evaluated */ 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci nid = look_for_out_vol_nid(codec, path); 130062306a36Sopenharmony_ci if (nid) { 130162306a36Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 130262306a36Sopenharmony_ci if (spec->dac_min_mute) 130362306a36Sopenharmony_ci val |= HDA_AMP_VAL_MIN_MUTE; 130462306a36Sopenharmony_ci if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) 130562306a36Sopenharmony_ci badness += BAD_SHARED_VOL; 130662306a36Sopenharmony_ci else 130762306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = val; 130862306a36Sopenharmony_ci } else 130962306a36Sopenharmony_ci badness += BAD_SHARED_VOL; 131062306a36Sopenharmony_ci nid = look_for_out_mute_nid(codec, path); 131162306a36Sopenharmony_ci if (nid) { 131262306a36Sopenharmony_ci unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); 131362306a36Sopenharmony_ci if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || 131462306a36Sopenharmony_ci nid_has_mute(codec, nid, HDA_OUTPUT)) 131562306a36Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 131662306a36Sopenharmony_ci else 131762306a36Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); 131862306a36Sopenharmony_ci if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) 131962306a36Sopenharmony_ci badness += BAD_SHARED_VOL; 132062306a36Sopenharmony_ci else 132162306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = val; 132262306a36Sopenharmony_ci } else 132362306a36Sopenharmony_ci badness += BAD_SHARED_VOL; 132462306a36Sopenharmony_ci return badness; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ciconst struct badness_table hda_main_out_badness = { 132862306a36Sopenharmony_ci .no_primary_dac = BAD_NO_PRIMARY_DAC, 132962306a36Sopenharmony_ci .no_dac = BAD_NO_DAC, 133062306a36Sopenharmony_ci .shared_primary = BAD_NO_PRIMARY_DAC, 133162306a36Sopenharmony_ci .shared_surr = BAD_SHARED_SURROUND, 133262306a36Sopenharmony_ci .shared_clfe = BAD_SHARED_CLFE, 133362306a36Sopenharmony_ci .shared_surr_main = BAD_SHARED_SURROUND, 133462306a36Sopenharmony_ci}; 133562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_main_out_badness); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ciconst struct badness_table hda_extra_out_badness = { 133862306a36Sopenharmony_ci .no_primary_dac = BAD_NO_DAC, 133962306a36Sopenharmony_ci .no_dac = BAD_NO_DAC, 134062306a36Sopenharmony_ci .shared_primary = BAD_NO_EXTRA_DAC, 134162306a36Sopenharmony_ci .shared_surr = BAD_SHARED_EXTRA_SURROUND, 134262306a36Sopenharmony_ci .shared_clfe = BAD_SHARED_EXTRA_SURROUND, 134362306a36Sopenharmony_ci .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, 134462306a36Sopenharmony_ci}; 134562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_extra_out_badness); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci/* get the DAC of the primary output corresponding to the given array index */ 134862306a36Sopenharmony_cistatic hda_nid_t get_primary_out(struct hda_codec *codec, int idx) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 135162306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (cfg->line_outs > idx) 135462306a36Sopenharmony_ci return spec->private_dac_nids[idx]; 135562306a36Sopenharmony_ci idx -= cfg->line_outs; 135662306a36Sopenharmony_ci if (spec->multi_ios > idx) 135762306a36Sopenharmony_ci return spec->multi_io[idx].dac; 135862306a36Sopenharmony_ci return 0; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci/* return the DAC if it's reachable, otherwise zero */ 136262306a36Sopenharmony_cistatic inline hda_nid_t try_dac(struct hda_codec *codec, 136362306a36Sopenharmony_ci hda_nid_t dac, hda_nid_t pin) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci return is_reachable_path(codec, dac, pin) ? dac : 0; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci/* try to assign DACs to pins and return the resultant badness */ 136962306a36Sopenharmony_cistatic int try_assign_dacs(struct hda_codec *codec, int num_outs, 137062306a36Sopenharmony_ci const hda_nid_t *pins, hda_nid_t *dacs, 137162306a36Sopenharmony_ci int *path_idx, 137262306a36Sopenharmony_ci const struct badness_table *bad) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 137562306a36Sopenharmony_ci int i, j; 137662306a36Sopenharmony_ci int badness = 0; 137762306a36Sopenharmony_ci hda_nid_t dac; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (!num_outs) 138062306a36Sopenharmony_ci return 0; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci for (i = 0; i < num_outs; i++) { 138362306a36Sopenharmony_ci struct nid_path *path; 138462306a36Sopenharmony_ci hda_nid_t pin = pins[i]; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (!spec->obey_preferred_dacs) { 138762306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx[i]); 138862306a36Sopenharmony_ci if (path) { 138962306a36Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 139062306a36Sopenharmony_ci continue; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci dacs[i] = get_preferred_dac(codec, pin); 139562306a36Sopenharmony_ci if (dacs[i]) { 139662306a36Sopenharmony_ci if (is_dac_already_used(codec, dacs[i])) 139762306a36Sopenharmony_ci badness += bad->shared_primary; 139862306a36Sopenharmony_ci } else if (spec->obey_preferred_dacs) { 139962306a36Sopenharmony_ci badness += BAD_NO_PRIMARY_DAC; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (!dacs[i]) 140362306a36Sopenharmony_ci dacs[i] = look_for_dac(codec, pin, false); 140462306a36Sopenharmony_ci if (!dacs[i] && !i) { 140562306a36Sopenharmony_ci /* try to steal the DAC of surrounds for the front */ 140662306a36Sopenharmony_ci for (j = 1; j < num_outs; j++) { 140762306a36Sopenharmony_ci if (is_reachable_path(codec, dacs[j], pin)) { 140862306a36Sopenharmony_ci dacs[0] = dacs[j]; 140962306a36Sopenharmony_ci dacs[j] = 0; 141062306a36Sopenharmony_ci invalidate_nid_path(codec, path_idx[j]); 141162306a36Sopenharmony_ci path_idx[j] = 0; 141262306a36Sopenharmony_ci break; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci dac = dacs[i]; 141762306a36Sopenharmony_ci if (!dac) { 141862306a36Sopenharmony_ci if (num_outs > 2) 141962306a36Sopenharmony_ci dac = try_dac(codec, get_primary_out(codec, i), pin); 142062306a36Sopenharmony_ci if (!dac) 142162306a36Sopenharmony_ci dac = try_dac(codec, dacs[0], pin); 142262306a36Sopenharmony_ci if (!dac) 142362306a36Sopenharmony_ci dac = try_dac(codec, get_primary_out(codec, i), pin); 142462306a36Sopenharmony_ci if (dac) { 142562306a36Sopenharmony_ci if (!i) 142662306a36Sopenharmony_ci badness += bad->shared_primary; 142762306a36Sopenharmony_ci else if (i == 1) 142862306a36Sopenharmony_ci badness += bad->shared_surr; 142962306a36Sopenharmony_ci else 143062306a36Sopenharmony_ci badness += bad->shared_clfe; 143162306a36Sopenharmony_ci } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { 143262306a36Sopenharmony_ci dac = spec->private_dac_nids[0]; 143362306a36Sopenharmony_ci badness += bad->shared_surr_main; 143462306a36Sopenharmony_ci } else if (!i) 143562306a36Sopenharmony_ci badness += bad->no_primary_dac; 143662306a36Sopenharmony_ci else 143762306a36Sopenharmony_ci badness += bad->no_dac; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci if (!dac) 144062306a36Sopenharmony_ci continue; 144162306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); 144262306a36Sopenharmony_ci if (!path && !i && spec->mixer_nid) { 144362306a36Sopenharmony_ci /* try with aamix */ 144462306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, 0); 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci if (!path) { 144762306a36Sopenharmony_ci dacs[i] = 0; 144862306a36Sopenharmony_ci badness += bad->no_dac; 144962306a36Sopenharmony_ci } else { 145062306a36Sopenharmony_ci /* print_nid_path(codec, "output", path); */ 145162306a36Sopenharmony_ci path->active = true; 145262306a36Sopenharmony_ci path_idx[i] = snd_hda_get_path_idx(codec, path); 145362306a36Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci return badness; 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci/* return NID if the given pin has only a single connection to a certain DAC */ 146162306a36Sopenharmony_cistatic hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 146462306a36Sopenharmony_ci int i; 146562306a36Sopenharmony_ci hda_nid_t nid_found = 0; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci for (i = 0; i < spec->num_all_dacs; i++) { 146862306a36Sopenharmony_ci hda_nid_t nid = spec->all_dacs[i]; 146962306a36Sopenharmony_ci if (!nid || is_dac_already_used(codec, nid)) 147062306a36Sopenharmony_ci continue; 147162306a36Sopenharmony_ci if (is_reachable_path(codec, nid, pin)) { 147262306a36Sopenharmony_ci if (nid_found) 147362306a36Sopenharmony_ci return 0; 147462306a36Sopenharmony_ci nid_found = nid; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci return nid_found; 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci/* check whether the given pin can be a multi-io pin */ 148162306a36Sopenharmony_cistatic bool can_be_multiio_pin(struct hda_codec *codec, 148262306a36Sopenharmony_ci unsigned int location, hda_nid_t nid) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci unsigned int defcfg, caps; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, nid); 148762306a36Sopenharmony_ci if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) 148862306a36Sopenharmony_ci return false; 148962306a36Sopenharmony_ci if (location && get_defcfg_location(defcfg) != location) 149062306a36Sopenharmony_ci return false; 149162306a36Sopenharmony_ci caps = snd_hda_query_pin_caps(codec, nid); 149262306a36Sopenharmony_ci if (!(caps & AC_PINCAP_OUT)) 149362306a36Sopenharmony_ci return false; 149462306a36Sopenharmony_ci return true; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci/* count the number of input pins that are capable to be multi-io */ 149862306a36Sopenharmony_cistatic int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 150162306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 150262306a36Sopenharmony_ci unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 150362306a36Sopenharmony_ci unsigned int location = get_defcfg_location(defcfg); 150462306a36Sopenharmony_ci int type, i; 150562306a36Sopenharmony_ci int num_pins = 0; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 150862306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 150962306a36Sopenharmony_ci if (cfg->inputs[i].type != type) 151062306a36Sopenharmony_ci continue; 151162306a36Sopenharmony_ci if (can_be_multiio_pin(codec, location, 151262306a36Sopenharmony_ci cfg->inputs[i].pin)) 151362306a36Sopenharmony_ci num_pins++; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci return num_pins; 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci/* 152062306a36Sopenharmony_ci * multi-io helper 152162306a36Sopenharmony_ci * 152262306a36Sopenharmony_ci * When hardwired is set, try to fill ony hardwired pins, and returns 152362306a36Sopenharmony_ci * zero if any pins are filled, non-zero if nothing found. 152462306a36Sopenharmony_ci * When hardwired is off, try to fill possible input pins, and returns 152562306a36Sopenharmony_ci * the badness value. 152662306a36Sopenharmony_ci */ 152762306a36Sopenharmony_cistatic int fill_multi_ios(struct hda_codec *codec, 152862306a36Sopenharmony_ci hda_nid_t reference_pin, 152962306a36Sopenharmony_ci bool hardwired) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 153262306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 153362306a36Sopenharmony_ci int type, i, j, num_pins, old_pins; 153462306a36Sopenharmony_ci unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 153562306a36Sopenharmony_ci unsigned int location = get_defcfg_location(defcfg); 153662306a36Sopenharmony_ci int badness = 0; 153762306a36Sopenharmony_ci struct nid_path *path; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci old_pins = spec->multi_ios; 154062306a36Sopenharmony_ci if (old_pins >= 2) 154162306a36Sopenharmony_ci goto end_fill; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci num_pins = count_multiio_pins(codec, reference_pin); 154462306a36Sopenharmony_ci if (num_pins < 2) 154562306a36Sopenharmony_ci goto end_fill; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 154862306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 154962306a36Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 155062306a36Sopenharmony_ci hda_nid_t dac = 0; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (cfg->inputs[i].type != type) 155362306a36Sopenharmony_ci continue; 155462306a36Sopenharmony_ci if (!can_be_multiio_pin(codec, location, nid)) 155562306a36Sopenharmony_ci continue; 155662306a36Sopenharmony_ci for (j = 0; j < spec->multi_ios; j++) { 155762306a36Sopenharmony_ci if (nid == spec->multi_io[j].pin) 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci if (j < spec->multi_ios) 156162306a36Sopenharmony_ci continue; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (hardwired) 156462306a36Sopenharmony_ci dac = get_dac_if_single(codec, nid); 156562306a36Sopenharmony_ci else if (!dac) 156662306a36Sopenharmony_ci dac = look_for_dac(codec, nid, false); 156762306a36Sopenharmony_ci if (!dac) { 156862306a36Sopenharmony_ci badness++; 156962306a36Sopenharmony_ci continue; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, nid, 157262306a36Sopenharmony_ci -spec->mixer_nid); 157362306a36Sopenharmony_ci if (!path) { 157462306a36Sopenharmony_ci badness++; 157562306a36Sopenharmony_ci continue; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci /* print_nid_path(codec, "multiio", path); */ 157862306a36Sopenharmony_ci spec->multi_io[spec->multi_ios].pin = nid; 157962306a36Sopenharmony_ci spec->multi_io[spec->multi_ios].dac = dac; 158062306a36Sopenharmony_ci spec->out_paths[cfg->line_outs + spec->multi_ios] = 158162306a36Sopenharmony_ci snd_hda_get_path_idx(codec, path); 158262306a36Sopenharmony_ci spec->multi_ios++; 158362306a36Sopenharmony_ci if (spec->multi_ios >= 2) 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci end_fill: 158862306a36Sopenharmony_ci if (badness) 158962306a36Sopenharmony_ci badness = BAD_MULTI_IO; 159062306a36Sopenharmony_ci if (old_pins == spec->multi_ios) { 159162306a36Sopenharmony_ci if (hardwired) 159262306a36Sopenharmony_ci return 1; /* nothing found */ 159362306a36Sopenharmony_ci else 159462306a36Sopenharmony_ci return badness; /* no badness if nothing found */ 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci if (!hardwired && spec->multi_ios < 2) { 159762306a36Sopenharmony_ci /* cancel newly assigned paths */ 159862306a36Sopenharmony_ci spec->paths.used -= spec->multi_ios - old_pins; 159962306a36Sopenharmony_ci spec->multi_ios = old_pins; 160062306a36Sopenharmony_ci return badness; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* assign volume and mute controls */ 160462306a36Sopenharmony_ci for (i = old_pins; i < spec->multi_ios; i++) { 160562306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); 160662306a36Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return badness; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci/* map DACs for all pins in the list if they are single connections */ 161362306a36Sopenharmony_cistatic bool map_singles(struct hda_codec *codec, int outs, 161462306a36Sopenharmony_ci const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 161762306a36Sopenharmony_ci int i; 161862306a36Sopenharmony_ci bool found = false; 161962306a36Sopenharmony_ci for (i = 0; i < outs; i++) { 162062306a36Sopenharmony_ci struct nid_path *path; 162162306a36Sopenharmony_ci hda_nid_t dac; 162262306a36Sopenharmony_ci if (dacs[i]) 162362306a36Sopenharmony_ci continue; 162462306a36Sopenharmony_ci dac = get_dac_if_single(codec, pins[i]); 162562306a36Sopenharmony_ci if (!dac) 162662306a36Sopenharmony_ci continue; 162762306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pins[i], 162862306a36Sopenharmony_ci -spec->mixer_nid); 162962306a36Sopenharmony_ci if (!path && !i && spec->mixer_nid) 163062306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pins[i], 0); 163162306a36Sopenharmony_ci if (path) { 163262306a36Sopenharmony_ci dacs[i] = dac; 163362306a36Sopenharmony_ci found = true; 163462306a36Sopenharmony_ci /* print_nid_path(codec, "output", path); */ 163562306a36Sopenharmony_ci path->active = true; 163662306a36Sopenharmony_ci path_idx[i] = snd_hda_get_path_idx(codec, path); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci return found; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic inline bool has_aamix_out_paths(struct hda_gen_spec *spec) 164362306a36Sopenharmony_ci{ 164462306a36Sopenharmony_ci return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || 164562306a36Sopenharmony_ci spec->aamix_out_paths[2]; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/* create a new path including aamix if available, and return its index */ 164962306a36Sopenharmony_cistatic int check_aamix_out_path(struct hda_codec *codec, int path_idx) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 165262306a36Sopenharmony_ci struct nid_path *path; 165362306a36Sopenharmony_ci hda_nid_t path_dac, dac, pin; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 165662306a36Sopenharmony_ci if (!path || !path->depth || 165762306a36Sopenharmony_ci is_nid_contained(path, spec->mixer_nid)) 165862306a36Sopenharmony_ci return 0; 165962306a36Sopenharmony_ci path_dac = path->path[0]; 166062306a36Sopenharmony_ci dac = spec->private_dac_nids[0]; 166162306a36Sopenharmony_ci pin = path->path[path->depth - 1]; 166262306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); 166362306a36Sopenharmony_ci if (!path) { 166462306a36Sopenharmony_ci if (dac != path_dac) 166562306a36Sopenharmony_ci dac = path_dac; 166662306a36Sopenharmony_ci else if (spec->multiout.hp_out_nid[0]) 166762306a36Sopenharmony_ci dac = spec->multiout.hp_out_nid[0]; 166862306a36Sopenharmony_ci else if (spec->multiout.extra_out_nid[0]) 166962306a36Sopenharmony_ci dac = spec->multiout.extra_out_nid[0]; 167062306a36Sopenharmony_ci else 167162306a36Sopenharmony_ci dac = 0; 167262306a36Sopenharmony_ci if (dac) 167362306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, 167462306a36Sopenharmony_ci spec->mixer_nid); 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci if (!path) 167762306a36Sopenharmony_ci return 0; 167862306a36Sopenharmony_ci /* print_nid_path(codec, "output-aamix", path); */ 167962306a36Sopenharmony_ci path->active = false; /* unused as default */ 168062306a36Sopenharmony_ci path->pin_fixed = true; /* static route */ 168162306a36Sopenharmony_ci return snd_hda_get_path_idx(codec, path); 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci/* check whether the independent HP is available with the current config */ 168562306a36Sopenharmony_cistatic bool indep_hp_possible(struct hda_codec *codec) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 168862306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 168962306a36Sopenharmony_ci struct nid_path *path; 169062306a36Sopenharmony_ci int i, idx; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_HP_OUT) 169362306a36Sopenharmony_ci idx = spec->out_paths[0]; 169462306a36Sopenharmony_ci else 169562306a36Sopenharmony_ci idx = spec->hp_paths[0]; 169662306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, idx); 169762306a36Sopenharmony_ci if (!path) 169862306a36Sopenharmony_ci return false; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci /* assume no path conflicts unless aamix is involved */ 170162306a36Sopenharmony_ci if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid)) 170262306a36Sopenharmony_ci return true; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci /* check whether output paths contain aamix */ 170562306a36Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 170662306a36Sopenharmony_ci if (spec->out_paths[i] == idx) 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); 170962306a36Sopenharmony_ci if (path && is_nid_contained(path, spec->mixer_nid)) 171062306a36Sopenharmony_ci return false; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci for (i = 0; i < cfg->speaker_outs; i++) { 171362306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]); 171462306a36Sopenharmony_ci if (path && is_nid_contained(path, spec->mixer_nid)) 171562306a36Sopenharmony_ci return false; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci return true; 171962306a36Sopenharmony_ci} 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci/* fill the empty entries in the dac array for speaker/hp with the 172262306a36Sopenharmony_ci * shared dac pointed by the paths 172362306a36Sopenharmony_ci */ 172462306a36Sopenharmony_cistatic void refill_shared_dacs(struct hda_codec *codec, int num_outs, 172562306a36Sopenharmony_ci hda_nid_t *dacs, int *path_idx) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci struct nid_path *path; 172862306a36Sopenharmony_ci int i; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci for (i = 0; i < num_outs; i++) { 173162306a36Sopenharmony_ci if (dacs[i]) 173262306a36Sopenharmony_ci continue; 173362306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx[i]); 173462306a36Sopenharmony_ci if (!path) 173562306a36Sopenharmony_ci continue; 173662306a36Sopenharmony_ci dacs[i] = path->path[0]; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci/* fill in the dac_nids table from the parsed pin configuration */ 174162306a36Sopenharmony_cistatic int fill_and_eval_dacs(struct hda_codec *codec, 174262306a36Sopenharmony_ci bool fill_hardwired, 174362306a36Sopenharmony_ci bool fill_mio_first) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 174662306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 174762306a36Sopenharmony_ci int i, err, badness; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci /* set num_dacs once to full for look_for_dac() */ 175062306a36Sopenharmony_ci spec->multiout.num_dacs = cfg->line_outs; 175162306a36Sopenharmony_ci spec->multiout.dac_nids = spec->private_dac_nids; 175262306a36Sopenharmony_ci memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); 175362306a36Sopenharmony_ci memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); 175462306a36Sopenharmony_ci memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); 175562306a36Sopenharmony_ci spec->multi_ios = 0; 175662306a36Sopenharmony_ci snd_array_free(&spec->paths); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci /* clear path indices */ 175962306a36Sopenharmony_ci memset(spec->out_paths, 0, sizeof(spec->out_paths)); 176062306a36Sopenharmony_ci memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); 176162306a36Sopenharmony_ci memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); 176262306a36Sopenharmony_ci memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); 176362306a36Sopenharmony_ci memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); 176462306a36Sopenharmony_ci memset(spec->input_paths, 0, sizeof(spec->input_paths)); 176562306a36Sopenharmony_ci memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); 176662306a36Sopenharmony_ci memset(&spec->digin_path, 0, sizeof(spec->digin_path)); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci badness = 0; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci /* fill hard-wired DACs first */ 177162306a36Sopenharmony_ci if (fill_hardwired) { 177262306a36Sopenharmony_ci bool mapped; 177362306a36Sopenharmony_ci do { 177462306a36Sopenharmony_ci mapped = map_singles(codec, cfg->line_outs, 177562306a36Sopenharmony_ci cfg->line_out_pins, 177662306a36Sopenharmony_ci spec->private_dac_nids, 177762306a36Sopenharmony_ci spec->out_paths); 177862306a36Sopenharmony_ci mapped |= map_singles(codec, cfg->hp_outs, 177962306a36Sopenharmony_ci cfg->hp_pins, 178062306a36Sopenharmony_ci spec->multiout.hp_out_nid, 178162306a36Sopenharmony_ci spec->hp_paths); 178262306a36Sopenharmony_ci mapped |= map_singles(codec, cfg->speaker_outs, 178362306a36Sopenharmony_ci cfg->speaker_pins, 178462306a36Sopenharmony_ci spec->multiout.extra_out_nid, 178562306a36Sopenharmony_ci spec->speaker_paths); 178662306a36Sopenharmony_ci if (!spec->no_multi_io && 178762306a36Sopenharmony_ci fill_mio_first && cfg->line_outs == 1 && 178862306a36Sopenharmony_ci cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 178962306a36Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], true); 179062306a36Sopenharmony_ci if (!err) 179162306a36Sopenharmony_ci mapped = true; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci } while (mapped); 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, 179762306a36Sopenharmony_ci spec->private_dac_nids, spec->out_paths, 179862306a36Sopenharmony_ci spec->main_out_badness); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (!spec->no_multi_io && fill_mio_first && 180162306a36Sopenharmony_ci cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 180262306a36Sopenharmony_ci /* try to fill multi-io first */ 180362306a36Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 180462306a36Sopenharmony_ci if (err < 0) 180562306a36Sopenharmony_ci return err; 180662306a36Sopenharmony_ci /* we don't count badness at this stage yet */ 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 181062306a36Sopenharmony_ci err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, 181162306a36Sopenharmony_ci spec->multiout.hp_out_nid, 181262306a36Sopenharmony_ci spec->hp_paths, 181362306a36Sopenharmony_ci spec->extra_out_badness); 181462306a36Sopenharmony_ci if (err < 0) 181562306a36Sopenharmony_ci return err; 181662306a36Sopenharmony_ci badness += err; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 181962306a36Sopenharmony_ci err = try_assign_dacs(codec, cfg->speaker_outs, 182062306a36Sopenharmony_ci cfg->speaker_pins, 182162306a36Sopenharmony_ci spec->multiout.extra_out_nid, 182262306a36Sopenharmony_ci spec->speaker_paths, 182362306a36Sopenharmony_ci spec->extra_out_badness); 182462306a36Sopenharmony_ci if (err < 0) 182562306a36Sopenharmony_ci return err; 182662306a36Sopenharmony_ci badness += err; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci if (!spec->no_multi_io && 182962306a36Sopenharmony_ci cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 183062306a36Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 183162306a36Sopenharmony_ci if (err < 0) 183262306a36Sopenharmony_ci return err; 183362306a36Sopenharmony_ci badness += err; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (spec->mixer_nid) { 183762306a36Sopenharmony_ci spec->aamix_out_paths[0] = 183862306a36Sopenharmony_ci check_aamix_out_path(codec, spec->out_paths[0]); 183962306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 184062306a36Sopenharmony_ci spec->aamix_out_paths[1] = 184162306a36Sopenharmony_ci check_aamix_out_path(codec, spec->hp_paths[0]); 184262306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 184362306a36Sopenharmony_ci spec->aamix_out_paths[2] = 184462306a36Sopenharmony_ci check_aamix_out_path(codec, spec->speaker_paths[0]); 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (!spec->no_multi_io && 184862306a36Sopenharmony_ci cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 184962306a36Sopenharmony_ci if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) 185062306a36Sopenharmony_ci spec->multi_ios = 1; /* give badness */ 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci /* re-count num_dacs and squash invalid entries */ 185362306a36Sopenharmony_ci spec->multiout.num_dacs = 0; 185462306a36Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 185562306a36Sopenharmony_ci if (spec->private_dac_nids[i]) 185662306a36Sopenharmony_ci spec->multiout.num_dacs++; 185762306a36Sopenharmony_ci else { 185862306a36Sopenharmony_ci memmove(spec->private_dac_nids + i, 185962306a36Sopenharmony_ci spec->private_dac_nids + i + 1, 186062306a36Sopenharmony_ci sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); 186162306a36Sopenharmony_ci spec->private_dac_nids[cfg->line_outs - 1] = 0; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci spec->ext_channel_count = spec->min_channel_count = 186662306a36Sopenharmony_ci spec->multiout.num_dacs * 2; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (spec->multi_ios == 2) { 186962306a36Sopenharmony_ci for (i = 0; i < 2; i++) 187062306a36Sopenharmony_ci spec->private_dac_nids[spec->multiout.num_dacs++] = 187162306a36Sopenharmony_ci spec->multi_io[i].dac; 187262306a36Sopenharmony_ci } else if (spec->multi_ios) { 187362306a36Sopenharmony_ci spec->multi_ios = 0; 187462306a36Sopenharmony_ci badness += BAD_MULTI_IO; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci if (spec->indep_hp && !indep_hp_possible(codec)) 187862306a36Sopenharmony_ci badness += BAD_NO_INDEP_HP; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* re-fill the shared DAC for speaker / headphone */ 188162306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 188262306a36Sopenharmony_ci refill_shared_dacs(codec, cfg->hp_outs, 188362306a36Sopenharmony_ci spec->multiout.hp_out_nid, 188462306a36Sopenharmony_ci spec->hp_paths); 188562306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 188662306a36Sopenharmony_ci refill_shared_dacs(codec, cfg->speaker_outs, 188762306a36Sopenharmony_ci spec->multiout.extra_out_nid, 188862306a36Sopenharmony_ci spec->speaker_paths); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci return badness; 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci#define DEBUG_BADNESS 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci#ifdef DEBUG_BADNESS 189662306a36Sopenharmony_ci#define debug_badness(fmt, ...) \ 189762306a36Sopenharmony_ci codec_dbg(codec, fmt, ##__VA_ARGS__) 189862306a36Sopenharmony_ci#else 189962306a36Sopenharmony_ci#define debug_badness(fmt, ...) \ 190062306a36Sopenharmony_ci do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0) 190162306a36Sopenharmony_ci#endif 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci#ifdef DEBUG_BADNESS 190462306a36Sopenharmony_cistatic inline void print_nid_path_idx(struct hda_codec *codec, 190562306a36Sopenharmony_ci const char *pfx, int idx) 190662306a36Sopenharmony_ci{ 190762306a36Sopenharmony_ci struct nid_path *path; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, idx); 191062306a36Sopenharmony_ci if (path) 191162306a36Sopenharmony_ci print_nid_path(codec, pfx, path); 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic void debug_show_configs(struct hda_codec *codec, 191562306a36Sopenharmony_ci struct auto_pin_cfg *cfg) 191662306a36Sopenharmony_ci{ 191762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 191862306a36Sopenharmony_ci static const char * const lo_type[3] = { "LO", "SP", "HP" }; 191962306a36Sopenharmony_ci int i; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", 192262306a36Sopenharmony_ci cfg->line_out_pins[0], cfg->line_out_pins[1], 192362306a36Sopenharmony_ci cfg->line_out_pins[2], cfg->line_out_pins[3], 192462306a36Sopenharmony_ci spec->multiout.dac_nids[0], 192562306a36Sopenharmony_ci spec->multiout.dac_nids[1], 192662306a36Sopenharmony_ci spec->multiout.dac_nids[2], 192762306a36Sopenharmony_ci spec->multiout.dac_nids[3], 192862306a36Sopenharmony_ci lo_type[cfg->line_out_type]); 192962306a36Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) 193062306a36Sopenharmony_ci print_nid_path_idx(codec, " out", spec->out_paths[i]); 193162306a36Sopenharmony_ci if (spec->multi_ios > 0) 193262306a36Sopenharmony_ci debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", 193362306a36Sopenharmony_ci spec->multi_ios, 193462306a36Sopenharmony_ci spec->multi_io[0].pin, spec->multi_io[1].pin, 193562306a36Sopenharmony_ci spec->multi_io[0].dac, spec->multi_io[1].dac); 193662306a36Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) 193762306a36Sopenharmony_ci print_nid_path_idx(codec, " mio", 193862306a36Sopenharmony_ci spec->out_paths[cfg->line_outs + i]); 193962306a36Sopenharmony_ci if (cfg->hp_outs) 194062306a36Sopenharmony_ci debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 194162306a36Sopenharmony_ci cfg->hp_pins[0], cfg->hp_pins[1], 194262306a36Sopenharmony_ci cfg->hp_pins[2], cfg->hp_pins[3], 194362306a36Sopenharmony_ci spec->multiout.hp_out_nid[0], 194462306a36Sopenharmony_ci spec->multiout.hp_out_nid[1], 194562306a36Sopenharmony_ci spec->multiout.hp_out_nid[2], 194662306a36Sopenharmony_ci spec->multiout.hp_out_nid[3]); 194762306a36Sopenharmony_ci for (i = 0; i < cfg->hp_outs; i++) 194862306a36Sopenharmony_ci print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); 194962306a36Sopenharmony_ci if (cfg->speaker_outs) 195062306a36Sopenharmony_ci debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 195162306a36Sopenharmony_ci cfg->speaker_pins[0], cfg->speaker_pins[1], 195262306a36Sopenharmony_ci cfg->speaker_pins[2], cfg->speaker_pins[3], 195362306a36Sopenharmony_ci spec->multiout.extra_out_nid[0], 195462306a36Sopenharmony_ci spec->multiout.extra_out_nid[1], 195562306a36Sopenharmony_ci spec->multiout.extra_out_nid[2], 195662306a36Sopenharmony_ci spec->multiout.extra_out_nid[3]); 195762306a36Sopenharmony_ci for (i = 0; i < cfg->speaker_outs; i++) 195862306a36Sopenharmony_ci print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); 195962306a36Sopenharmony_ci for (i = 0; i < 3; i++) 196062306a36Sopenharmony_ci print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); 196162306a36Sopenharmony_ci} 196262306a36Sopenharmony_ci#else 196362306a36Sopenharmony_ci#define debug_show_configs(codec, cfg) /* NOP */ 196462306a36Sopenharmony_ci#endif 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci/* find all available DACs of the codec */ 196762306a36Sopenharmony_cistatic void fill_all_dac_nids(struct hda_codec *codec) 196862306a36Sopenharmony_ci{ 196962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 197062306a36Sopenharmony_ci hda_nid_t nid; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci spec->num_all_dacs = 0; 197362306a36Sopenharmony_ci memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); 197462306a36Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 197562306a36Sopenharmony_ci if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) 197662306a36Sopenharmony_ci continue; 197762306a36Sopenharmony_ci if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { 197862306a36Sopenharmony_ci codec_err(codec, "Too many DACs!\n"); 197962306a36Sopenharmony_ci break; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci spec->all_dacs[spec->num_all_dacs++] = nid; 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_cistatic int parse_output_paths(struct hda_codec *codec) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 198862306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 198962306a36Sopenharmony_ci struct auto_pin_cfg *best_cfg; 199062306a36Sopenharmony_ci unsigned int val; 199162306a36Sopenharmony_ci int best_badness = INT_MAX; 199262306a36Sopenharmony_ci int badness; 199362306a36Sopenharmony_ci bool fill_hardwired = true, fill_mio_first = true; 199462306a36Sopenharmony_ci bool best_wired = true, best_mio = true; 199562306a36Sopenharmony_ci bool hp_spk_swapped = false; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); 199862306a36Sopenharmony_ci if (!best_cfg) 199962306a36Sopenharmony_ci return -ENOMEM; 200062306a36Sopenharmony_ci *best_cfg = *cfg; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci for (;;) { 200362306a36Sopenharmony_ci badness = fill_and_eval_dacs(codec, fill_hardwired, 200462306a36Sopenharmony_ci fill_mio_first); 200562306a36Sopenharmony_ci if (badness < 0) { 200662306a36Sopenharmony_ci kfree(best_cfg); 200762306a36Sopenharmony_ci return badness; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", 201062306a36Sopenharmony_ci cfg->line_out_type, fill_hardwired, fill_mio_first, 201162306a36Sopenharmony_ci badness); 201262306a36Sopenharmony_ci debug_show_configs(codec, cfg); 201362306a36Sopenharmony_ci if (badness < best_badness) { 201462306a36Sopenharmony_ci best_badness = badness; 201562306a36Sopenharmony_ci *best_cfg = *cfg; 201662306a36Sopenharmony_ci best_wired = fill_hardwired; 201762306a36Sopenharmony_ci best_mio = fill_mio_first; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci if (!badness) 202062306a36Sopenharmony_ci break; 202162306a36Sopenharmony_ci fill_mio_first = !fill_mio_first; 202262306a36Sopenharmony_ci if (!fill_mio_first) 202362306a36Sopenharmony_ci continue; 202462306a36Sopenharmony_ci fill_hardwired = !fill_hardwired; 202562306a36Sopenharmony_ci if (!fill_hardwired) 202662306a36Sopenharmony_ci continue; 202762306a36Sopenharmony_ci if (hp_spk_swapped) 202862306a36Sopenharmony_ci break; 202962306a36Sopenharmony_ci hp_spk_swapped = true; 203062306a36Sopenharmony_ci if (cfg->speaker_outs > 0 && 203162306a36Sopenharmony_ci cfg->line_out_type == AUTO_PIN_HP_OUT) { 203262306a36Sopenharmony_ci cfg->hp_outs = cfg->line_outs; 203362306a36Sopenharmony_ci memcpy(cfg->hp_pins, cfg->line_out_pins, 203462306a36Sopenharmony_ci sizeof(cfg->hp_pins)); 203562306a36Sopenharmony_ci cfg->line_outs = cfg->speaker_outs; 203662306a36Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->speaker_pins, 203762306a36Sopenharmony_ci sizeof(cfg->speaker_pins)); 203862306a36Sopenharmony_ci cfg->speaker_outs = 0; 203962306a36Sopenharmony_ci memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); 204062306a36Sopenharmony_ci cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; 204162306a36Sopenharmony_ci fill_hardwired = true; 204262306a36Sopenharmony_ci continue; 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci if (cfg->hp_outs > 0 && 204562306a36Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 204662306a36Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 204762306a36Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 204862306a36Sopenharmony_ci sizeof(cfg->speaker_pins)); 204962306a36Sopenharmony_ci cfg->line_outs = cfg->hp_outs; 205062306a36Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->hp_pins, 205162306a36Sopenharmony_ci sizeof(cfg->hp_pins)); 205262306a36Sopenharmony_ci cfg->hp_outs = 0; 205362306a36Sopenharmony_ci memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 205462306a36Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 205562306a36Sopenharmony_ci fill_hardwired = true; 205662306a36Sopenharmony_ci continue; 205762306a36Sopenharmony_ci } 205862306a36Sopenharmony_ci break; 205962306a36Sopenharmony_ci } 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci if (badness) { 206262306a36Sopenharmony_ci debug_badness("==> restoring best_cfg\n"); 206362306a36Sopenharmony_ci *cfg = *best_cfg; 206462306a36Sopenharmony_ci fill_and_eval_dacs(codec, best_wired, best_mio); 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", 206762306a36Sopenharmony_ci cfg->line_out_type, best_wired, best_mio); 206862306a36Sopenharmony_ci debug_show_configs(codec, cfg); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci if (cfg->line_out_pins[0]) { 207162306a36Sopenharmony_ci struct nid_path *path; 207262306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); 207362306a36Sopenharmony_ci if (path) 207462306a36Sopenharmony_ci spec->vmaster_nid = look_for_out_vol_nid(codec, path); 207562306a36Sopenharmony_ci if (spec->vmaster_nid) { 207662306a36Sopenharmony_ci snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, 207762306a36Sopenharmony_ci HDA_OUTPUT, spec->vmaster_tlv); 207862306a36Sopenharmony_ci if (spec->dac_min_mute) 207962306a36Sopenharmony_ci spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* set initial pinctl targets */ 208462306a36Sopenharmony_ci if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) 208562306a36Sopenharmony_ci val = PIN_HP; 208662306a36Sopenharmony_ci else 208762306a36Sopenharmony_ci val = PIN_OUT; 208862306a36Sopenharmony_ci set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); 208962306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 209062306a36Sopenharmony_ci set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); 209162306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 209262306a36Sopenharmony_ci val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; 209362306a36Sopenharmony_ci set_pin_targets(codec, cfg->speaker_outs, 209462306a36Sopenharmony_ci cfg->speaker_pins, val); 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci /* clear indep_hp flag if not available */ 209862306a36Sopenharmony_ci if (spec->indep_hp && !indep_hp_possible(codec)) 209962306a36Sopenharmony_ci spec->indep_hp = 0; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci kfree(best_cfg); 210262306a36Sopenharmony_ci return 0; 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci/* add playback controls from the parsed DAC table */ 210662306a36Sopenharmony_cistatic int create_multi_out_ctls(struct hda_codec *codec, 210762306a36Sopenharmony_ci const struct auto_pin_cfg *cfg) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 211062306a36Sopenharmony_ci int i, err, noutputs; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci noutputs = cfg->line_outs; 211362306a36Sopenharmony_ci if (spec->multi_ios > 0 && cfg->line_outs < 3) 211462306a36Sopenharmony_ci noutputs += spec->multi_ios; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci for (i = 0; i < noutputs; i++) { 211762306a36Sopenharmony_ci const char *name; 211862306a36Sopenharmony_ci int index; 211962306a36Sopenharmony_ci struct nid_path *path; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); 212262306a36Sopenharmony_ci if (!path) 212362306a36Sopenharmony_ci continue; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); 212662306a36Sopenharmony_ci if (!name || !strcmp(name, "CLFE")) { 212762306a36Sopenharmony_ci /* Center/LFE */ 212862306a36Sopenharmony_ci err = add_vol_ctl(codec, "Center", 0, 1, path); 212962306a36Sopenharmony_ci if (err < 0) 213062306a36Sopenharmony_ci return err; 213162306a36Sopenharmony_ci err = add_vol_ctl(codec, "LFE", 0, 2, path); 213262306a36Sopenharmony_ci if (err < 0) 213362306a36Sopenharmony_ci return err; 213462306a36Sopenharmony_ci } else { 213562306a36Sopenharmony_ci err = add_stereo_vol(codec, name, index, path); 213662306a36Sopenharmony_ci if (err < 0) 213762306a36Sopenharmony_ci return err; 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); 214162306a36Sopenharmony_ci if (!name || !strcmp(name, "CLFE")) { 214262306a36Sopenharmony_ci err = add_sw_ctl(codec, "Center", 0, 1, path); 214362306a36Sopenharmony_ci if (err < 0) 214462306a36Sopenharmony_ci return err; 214562306a36Sopenharmony_ci err = add_sw_ctl(codec, "LFE", 0, 2, path); 214662306a36Sopenharmony_ci if (err < 0) 214762306a36Sopenharmony_ci return err; 214862306a36Sopenharmony_ci } else { 214962306a36Sopenharmony_ci err = add_stereo_sw(codec, name, index, path); 215062306a36Sopenharmony_ci if (err < 0) 215162306a36Sopenharmony_ci return err; 215262306a36Sopenharmony_ci } 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci return 0; 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_cistatic int create_extra_out(struct hda_codec *codec, int path_idx, 215862306a36Sopenharmony_ci const char *pfx, int cidx) 215962306a36Sopenharmony_ci{ 216062306a36Sopenharmony_ci struct nid_path *path; 216162306a36Sopenharmony_ci int err; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 216462306a36Sopenharmony_ci if (!path) 216562306a36Sopenharmony_ci return 0; 216662306a36Sopenharmony_ci err = add_stereo_vol(codec, pfx, cidx, path); 216762306a36Sopenharmony_ci if (err < 0) 216862306a36Sopenharmony_ci return err; 216962306a36Sopenharmony_ci err = add_stereo_sw(codec, pfx, cidx, path); 217062306a36Sopenharmony_ci if (err < 0) 217162306a36Sopenharmony_ci return err; 217262306a36Sopenharmony_ci return 0; 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci/* add playback controls for speaker and HP outputs */ 217662306a36Sopenharmony_cistatic int create_extra_outs(struct hda_codec *codec, int num_pins, 217762306a36Sopenharmony_ci const int *paths, const char *pfx) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci int i; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 218262306a36Sopenharmony_ci const char *name; 218362306a36Sopenharmony_ci char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 218462306a36Sopenharmony_ci int err, idx = 0; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) 218762306a36Sopenharmony_ci name = "Bass Speaker"; 218862306a36Sopenharmony_ci else if (num_pins >= 3) { 218962306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), "%s %s", 219062306a36Sopenharmony_ci pfx, channel_name[i]); 219162306a36Sopenharmony_ci name = tmp; 219262306a36Sopenharmony_ci } else { 219362306a36Sopenharmony_ci name = pfx; 219462306a36Sopenharmony_ci idx = i; 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci err = create_extra_out(codec, paths[i], name, idx); 219762306a36Sopenharmony_ci if (err < 0) 219862306a36Sopenharmony_ci return err; 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci return 0; 220162306a36Sopenharmony_ci} 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_cistatic int create_hp_out_ctls(struct hda_codec *codec) 220462306a36Sopenharmony_ci{ 220562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 220662306a36Sopenharmony_ci return create_extra_outs(codec, spec->autocfg.hp_outs, 220762306a36Sopenharmony_ci spec->hp_paths, 220862306a36Sopenharmony_ci "Headphone"); 220962306a36Sopenharmony_ci} 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_cistatic int create_speaker_out_ctls(struct hda_codec *codec) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 221462306a36Sopenharmony_ci return create_extra_outs(codec, spec->autocfg.speaker_outs, 221562306a36Sopenharmony_ci spec->speaker_paths, 221662306a36Sopenharmony_ci "Speaker"); 221762306a36Sopenharmony_ci} 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci/* 222062306a36Sopenharmony_ci * independent HP controls 222162306a36Sopenharmony_ci */ 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_cistatic void call_hp_automute(struct hda_codec *codec, 222462306a36Sopenharmony_ci struct hda_jack_callback *jack); 222562306a36Sopenharmony_cistatic int indep_hp_info(struct snd_kcontrol *kcontrol, 222662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 222962306a36Sopenharmony_ci} 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_cistatic int indep_hp_get(struct snd_kcontrol *kcontrol, 223262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 223562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 223662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; 223762306a36Sopenharmony_ci return 0; 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_cistatic void update_aamix_paths(struct hda_codec *codec, bool do_mix, 224162306a36Sopenharmony_ci int nomix_path_idx, int mix_path_idx, 224262306a36Sopenharmony_ci int out_type); 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_cistatic int indep_hp_put(struct snd_kcontrol *kcontrol, 224562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 224862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 224962306a36Sopenharmony_ci unsigned int select = ucontrol->value.enumerated.item[0]; 225062306a36Sopenharmony_ci int ret = 0; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 225362306a36Sopenharmony_ci if (spec->active_streams) { 225462306a36Sopenharmony_ci ret = -EBUSY; 225562306a36Sopenharmony_ci goto unlock; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (spec->indep_hp_enabled != select) { 225962306a36Sopenharmony_ci hda_nid_t *dacp; 226062306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 226162306a36Sopenharmony_ci dacp = &spec->private_dac_nids[0]; 226262306a36Sopenharmony_ci else 226362306a36Sopenharmony_ci dacp = &spec->multiout.hp_out_nid[0]; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* update HP aamix paths in case it conflicts with indep HP */ 226662306a36Sopenharmony_ci if (spec->have_aamix_ctl) { 226762306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 226862306a36Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, 226962306a36Sopenharmony_ci spec->out_paths[0], 227062306a36Sopenharmony_ci spec->aamix_out_paths[0], 227162306a36Sopenharmony_ci spec->autocfg.line_out_type); 227262306a36Sopenharmony_ci else 227362306a36Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, 227462306a36Sopenharmony_ci spec->hp_paths[0], 227562306a36Sopenharmony_ci spec->aamix_out_paths[1], 227662306a36Sopenharmony_ci AUTO_PIN_HP_OUT); 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci spec->indep_hp_enabled = select; 228062306a36Sopenharmony_ci if (spec->indep_hp_enabled) 228162306a36Sopenharmony_ci *dacp = 0; 228262306a36Sopenharmony_ci else 228362306a36Sopenharmony_ci *dacp = spec->alt_dac_nid; 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci call_hp_automute(codec, NULL); 228662306a36Sopenharmony_ci ret = 1; 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci unlock: 228962306a36Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 229062306a36Sopenharmony_ci return ret; 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic const struct snd_kcontrol_new indep_hp_ctl = { 229462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 229562306a36Sopenharmony_ci .name = "Independent HP", 229662306a36Sopenharmony_ci .info = indep_hp_info, 229762306a36Sopenharmony_ci .get = indep_hp_get, 229862306a36Sopenharmony_ci .put = indep_hp_put, 229962306a36Sopenharmony_ci}; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic int create_indep_hp_ctls(struct hda_codec *codec) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 230562306a36Sopenharmony_ci hda_nid_t dac; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci if (!spec->indep_hp) 230862306a36Sopenharmony_ci return 0; 230962306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 231062306a36Sopenharmony_ci dac = spec->multiout.dac_nids[0]; 231162306a36Sopenharmony_ci else 231262306a36Sopenharmony_ci dac = spec->multiout.hp_out_nid[0]; 231362306a36Sopenharmony_ci if (!dac) { 231462306a36Sopenharmony_ci spec->indep_hp = 0; 231562306a36Sopenharmony_ci return 0; 231662306a36Sopenharmony_ci } 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci spec->indep_hp_enabled = false; 231962306a36Sopenharmony_ci spec->alt_dac_nid = dac; 232062306a36Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) 232162306a36Sopenharmony_ci return -ENOMEM; 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci/* 232662306a36Sopenharmony_ci * channel mode enum control 232762306a36Sopenharmony_ci */ 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic int ch_mode_info(struct snd_kcontrol *kcontrol, 233062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 233362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 233462306a36Sopenharmony_ci int chs; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 233762306a36Sopenharmony_ci uinfo->count = 1; 233862306a36Sopenharmony_ci uinfo->value.enumerated.items = spec->multi_ios + 1; 233962306a36Sopenharmony_ci if (uinfo->value.enumerated.item > spec->multi_ios) 234062306a36Sopenharmony_ci uinfo->value.enumerated.item = spec->multi_ios; 234162306a36Sopenharmony_ci chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; 234262306a36Sopenharmony_ci sprintf(uinfo->value.enumerated.name, "%dch", chs); 234362306a36Sopenharmony_ci return 0; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic int ch_mode_get(struct snd_kcontrol *kcontrol, 234762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 234862306a36Sopenharmony_ci{ 234962306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 235062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 235162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 235262306a36Sopenharmony_ci (spec->ext_channel_count - spec->min_channel_count) / 2; 235362306a36Sopenharmony_ci return 0; 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_cistatic inline struct nid_path * 235762306a36Sopenharmony_ciget_multiio_path(struct hda_codec *codec, int idx) 235862306a36Sopenharmony_ci{ 235962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 236062306a36Sopenharmony_ci return snd_hda_get_path_from_idx(codec, 236162306a36Sopenharmony_ci spec->out_paths[spec->autocfg.line_outs + idx]); 236262306a36Sopenharmony_ci} 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_cistatic void update_automute_all(struct hda_codec *codec); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci/* Default value to be passed as aamix argument for snd_hda_activate_path(); 236762306a36Sopenharmony_ci * used for output paths 236862306a36Sopenharmony_ci */ 236962306a36Sopenharmony_cistatic bool aamix_default(struct hda_gen_spec *spec) 237062306a36Sopenharmony_ci{ 237162306a36Sopenharmony_ci return !spec->have_aamix_ctl || spec->aamix_mode; 237262306a36Sopenharmony_ci} 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_cistatic int set_multi_io(struct hda_codec *codec, int idx, bool output) 237562306a36Sopenharmony_ci{ 237662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 237762306a36Sopenharmony_ci hda_nid_t nid = spec->multi_io[idx].pin; 237862306a36Sopenharmony_ci struct nid_path *path; 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci path = get_multiio_path(codec, idx); 238162306a36Sopenharmony_ci if (!path) 238262306a36Sopenharmony_ci return -EINVAL; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci if (path->active == output) 238562306a36Sopenharmony_ci return 0; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci if (output) { 238862306a36Sopenharmony_ci set_pin_target(codec, nid, PIN_OUT, true); 238962306a36Sopenharmony_ci snd_hda_activate_path(codec, path, true, aamix_default(spec)); 239062306a36Sopenharmony_ci set_pin_eapd(codec, nid, true); 239162306a36Sopenharmony_ci } else { 239262306a36Sopenharmony_ci set_pin_eapd(codec, nid, false); 239362306a36Sopenharmony_ci snd_hda_activate_path(codec, path, false, aamix_default(spec)); 239462306a36Sopenharmony_ci set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); 239562306a36Sopenharmony_ci path_power_down_sync(codec, path); 239662306a36Sopenharmony_ci } 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci /* update jack retasking in case it modifies any of them */ 239962306a36Sopenharmony_ci update_automute_all(codec); 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci return 0; 240262306a36Sopenharmony_ci} 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_cistatic int ch_mode_put(struct snd_kcontrol *kcontrol, 240562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 240862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 240962306a36Sopenharmony_ci int i, ch; 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci ch = ucontrol->value.enumerated.item[0]; 241262306a36Sopenharmony_ci if (ch < 0 || ch > spec->multi_ios) 241362306a36Sopenharmony_ci return -EINVAL; 241462306a36Sopenharmony_ci if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) 241562306a36Sopenharmony_ci return 0; 241662306a36Sopenharmony_ci spec->ext_channel_count = ch * 2 + spec->min_channel_count; 241762306a36Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) 241862306a36Sopenharmony_ci set_multi_io(codec, i, i < ch); 241962306a36Sopenharmony_ci spec->multiout.max_channels = max(spec->ext_channel_count, 242062306a36Sopenharmony_ci spec->const_channel_count); 242162306a36Sopenharmony_ci if (spec->need_dac_fix) 242262306a36Sopenharmony_ci spec->multiout.num_dacs = spec->multiout.max_channels / 2; 242362306a36Sopenharmony_ci return 1; 242462306a36Sopenharmony_ci} 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_cistatic const struct snd_kcontrol_new channel_mode_enum = { 242762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 242862306a36Sopenharmony_ci .name = "Channel Mode", 242962306a36Sopenharmony_ci .info = ch_mode_info, 243062306a36Sopenharmony_ci .get = ch_mode_get, 243162306a36Sopenharmony_ci .put = ch_mode_put, 243262306a36Sopenharmony_ci}; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_cistatic int create_multi_channel_mode(struct hda_codec *codec) 243562306a36Sopenharmony_ci{ 243662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci if (spec->multi_ios > 0) { 243962306a36Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) 244062306a36Sopenharmony_ci return -ENOMEM; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci return 0; 244362306a36Sopenharmony_ci} 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci/* 244662306a36Sopenharmony_ci * aamix loopback enable/disable switch 244762306a36Sopenharmony_ci */ 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci#define loopback_mixing_info indep_hp_info 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_cistatic int loopback_mixing_get(struct snd_kcontrol *kcontrol, 245262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 245362306a36Sopenharmony_ci{ 245462306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 245562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 245662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->aamix_mode; 245762306a36Sopenharmony_ci return 0; 245862306a36Sopenharmony_ci} 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_cistatic void update_aamix_paths(struct hda_codec *codec, bool do_mix, 246162306a36Sopenharmony_ci int nomix_path_idx, int mix_path_idx, 246262306a36Sopenharmony_ci int out_type) 246362306a36Sopenharmony_ci{ 246462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 246562306a36Sopenharmony_ci struct nid_path *nomix_path, *mix_path; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); 246862306a36Sopenharmony_ci mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); 246962306a36Sopenharmony_ci if (!nomix_path || !mix_path) 247062306a36Sopenharmony_ci return; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci /* if HP aamix path is driven from a different DAC and the 247362306a36Sopenharmony_ci * independent HP mode is ON, can't turn on aamix path 247462306a36Sopenharmony_ci */ 247562306a36Sopenharmony_ci if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && 247662306a36Sopenharmony_ci mix_path->path[0] != spec->alt_dac_nid) 247762306a36Sopenharmony_ci do_mix = false; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci if (do_mix) { 248062306a36Sopenharmony_ci snd_hda_activate_path(codec, nomix_path, false, true); 248162306a36Sopenharmony_ci snd_hda_activate_path(codec, mix_path, true, true); 248262306a36Sopenharmony_ci path_power_down_sync(codec, nomix_path); 248362306a36Sopenharmony_ci } else { 248462306a36Sopenharmony_ci snd_hda_activate_path(codec, mix_path, false, false); 248562306a36Sopenharmony_ci snd_hda_activate_path(codec, nomix_path, true, false); 248662306a36Sopenharmony_ci path_power_down_sync(codec, mix_path); 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci} 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci/* re-initialize the output paths; only called from loopback_mixing_put() */ 249162306a36Sopenharmony_cistatic void update_output_paths(struct hda_codec *codec, int num_outs, 249262306a36Sopenharmony_ci const int *paths) 249362306a36Sopenharmony_ci{ 249462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 249562306a36Sopenharmony_ci struct nid_path *path; 249662306a36Sopenharmony_ci int i; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci for (i = 0; i < num_outs; i++) { 249962306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, paths[i]); 250062306a36Sopenharmony_ci if (path) 250162306a36Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 250262306a36Sopenharmony_ci spec->aamix_mode); 250362306a36Sopenharmony_ci } 250462306a36Sopenharmony_ci} 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_cistatic int loopback_mixing_put(struct snd_kcontrol *kcontrol, 250762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 251062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 251162306a36Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 251262306a36Sopenharmony_ci unsigned int val = ucontrol->value.enumerated.item[0]; 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci if (val == spec->aamix_mode) 251562306a36Sopenharmony_ci return 0; 251662306a36Sopenharmony_ci spec->aamix_mode = val; 251762306a36Sopenharmony_ci if (has_aamix_out_paths(spec)) { 251862306a36Sopenharmony_ci update_aamix_paths(codec, val, spec->out_paths[0], 251962306a36Sopenharmony_ci spec->aamix_out_paths[0], 252062306a36Sopenharmony_ci cfg->line_out_type); 252162306a36Sopenharmony_ci update_aamix_paths(codec, val, spec->hp_paths[0], 252262306a36Sopenharmony_ci spec->aamix_out_paths[1], 252362306a36Sopenharmony_ci AUTO_PIN_HP_OUT); 252462306a36Sopenharmony_ci update_aamix_paths(codec, val, spec->speaker_paths[0], 252562306a36Sopenharmony_ci spec->aamix_out_paths[2], 252662306a36Sopenharmony_ci AUTO_PIN_SPEAKER_OUT); 252762306a36Sopenharmony_ci } else { 252862306a36Sopenharmony_ci update_output_paths(codec, cfg->line_outs, spec->out_paths); 252962306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 253062306a36Sopenharmony_ci update_output_paths(codec, cfg->hp_outs, spec->hp_paths); 253162306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 253262306a36Sopenharmony_ci update_output_paths(codec, cfg->speaker_outs, 253362306a36Sopenharmony_ci spec->speaker_paths); 253462306a36Sopenharmony_ci } 253562306a36Sopenharmony_ci return 1; 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic const struct snd_kcontrol_new loopback_mixing_enum = { 253962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 254062306a36Sopenharmony_ci .name = "Loopback Mixing", 254162306a36Sopenharmony_ci .info = loopback_mixing_info, 254262306a36Sopenharmony_ci .get = loopback_mixing_get, 254362306a36Sopenharmony_ci .put = loopback_mixing_put, 254462306a36Sopenharmony_ci}; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_cistatic int create_loopback_mixing_ctl(struct hda_codec *codec) 254762306a36Sopenharmony_ci{ 254862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci if (!spec->mixer_nid) 255162306a36Sopenharmony_ci return 0; 255262306a36Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) 255362306a36Sopenharmony_ci return -ENOMEM; 255462306a36Sopenharmony_ci spec->have_aamix_ctl = 1; 255562306a36Sopenharmony_ci return 0; 255662306a36Sopenharmony_ci} 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci/* 255962306a36Sopenharmony_ci * shared headphone/mic handling 256062306a36Sopenharmony_ci */ 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_cistatic void call_update_outputs(struct hda_codec *codec); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci/* for shared I/O, change the pin-control accordingly */ 256562306a36Sopenharmony_cistatic void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force) 256662306a36Sopenharmony_ci{ 256762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 256862306a36Sopenharmony_ci bool as_mic; 256962306a36Sopenharmony_ci unsigned int val; 257062306a36Sopenharmony_ci hda_nid_t pin; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci pin = spec->hp_mic_pin; 257362306a36Sopenharmony_ci as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci if (!force) { 257662306a36Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, pin); 257762306a36Sopenharmony_ci if (as_mic) { 257862306a36Sopenharmony_ci if (val & PIN_IN) 257962306a36Sopenharmony_ci return; 258062306a36Sopenharmony_ci } else { 258162306a36Sopenharmony_ci if (val & PIN_OUT) 258262306a36Sopenharmony_ci return; 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci val = snd_hda_get_default_vref(codec, pin); 258762306a36Sopenharmony_ci /* if the HP pin doesn't support VREF and the codec driver gives an 258862306a36Sopenharmony_ci * alternative pin, set up the VREF on that pin instead 258962306a36Sopenharmony_ci */ 259062306a36Sopenharmony_ci if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { 259162306a36Sopenharmony_ci const hda_nid_t vref_pin = spec->shared_mic_vref_pin; 259262306a36Sopenharmony_ci unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); 259362306a36Sopenharmony_ci if (vref_val != AC_PINCTL_VREF_HIZ) 259462306a36Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, vref_pin, 259562306a36Sopenharmony_ci PIN_IN | (as_mic ? vref_val : 0)); 259662306a36Sopenharmony_ci } 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci if (!spec->hp_mic_jack_modes) { 259962306a36Sopenharmony_ci if (as_mic) 260062306a36Sopenharmony_ci val |= PIN_IN; 260162306a36Sopenharmony_ci else 260262306a36Sopenharmony_ci val = PIN_HP; 260362306a36Sopenharmony_ci set_pin_target(codec, pin, val, true); 260462306a36Sopenharmony_ci call_hp_automute(codec, NULL); 260562306a36Sopenharmony_ci } 260662306a36Sopenharmony_ci} 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci/* create a shared input with the headphone out */ 260962306a36Sopenharmony_cistatic int create_hp_mic(struct hda_codec *codec) 261062306a36Sopenharmony_ci{ 261162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 261262306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 261362306a36Sopenharmony_ci unsigned int defcfg; 261462306a36Sopenharmony_ci hda_nid_t nid; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci if (!spec->hp_mic) { 261762306a36Sopenharmony_ci if (spec->suppress_hp_mic_detect) 261862306a36Sopenharmony_ci return 0; 261962306a36Sopenharmony_ci /* automatic detection: only if no input or a single internal 262062306a36Sopenharmony_ci * input pin is found, try to detect the shared hp/mic 262162306a36Sopenharmony_ci */ 262262306a36Sopenharmony_ci if (cfg->num_inputs > 1) 262362306a36Sopenharmony_ci return 0; 262462306a36Sopenharmony_ci else if (cfg->num_inputs == 1) { 262562306a36Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); 262662306a36Sopenharmony_ci if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) 262762306a36Sopenharmony_ci return 0; 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci spec->hp_mic = 0; /* clear once */ 263262306a36Sopenharmony_ci if (cfg->num_inputs >= AUTO_CFG_MAX_INS) 263362306a36Sopenharmony_ci return 0; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci nid = 0; 263662306a36Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0) 263762306a36Sopenharmony_ci nid = cfg->line_out_pins[0]; 263862306a36Sopenharmony_ci else if (cfg->hp_outs > 0) 263962306a36Sopenharmony_ci nid = cfg->hp_pins[0]; 264062306a36Sopenharmony_ci if (!nid) 264162306a36Sopenharmony_ci return 0; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) 264462306a36Sopenharmony_ci return 0; /* no input */ 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci cfg->inputs[cfg->num_inputs].pin = nid; 264762306a36Sopenharmony_ci cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC; 264862306a36Sopenharmony_ci cfg->inputs[cfg->num_inputs].is_headphone_mic = 1; 264962306a36Sopenharmony_ci cfg->num_inputs++; 265062306a36Sopenharmony_ci spec->hp_mic = 1; 265162306a36Sopenharmony_ci spec->hp_mic_pin = nid; 265262306a36Sopenharmony_ci /* we can't handle auto-mic together with HP-mic */ 265362306a36Sopenharmony_ci spec->suppress_auto_mic = 1; 265462306a36Sopenharmony_ci codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid); 265562306a36Sopenharmony_ci return 0; 265662306a36Sopenharmony_ci} 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci/* 265962306a36Sopenharmony_ci * output jack mode 266062306a36Sopenharmony_ci */ 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_cistatic int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_cistatic const char * const out_jack_texts[] = { 266562306a36Sopenharmony_ci "Line Out", "Headphone Out", 266662306a36Sopenharmony_ci}; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_cistatic int out_jack_mode_info(struct snd_kcontrol *kcontrol, 266962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 267062306a36Sopenharmony_ci{ 267162306a36Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts); 267262306a36Sopenharmony_ci} 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_cistatic int out_jack_mode_get(struct snd_kcontrol *kcontrol, 267562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 267662306a36Sopenharmony_ci{ 267762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 267862306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 267962306a36Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) 268062306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 1; 268162306a36Sopenharmony_ci else 268262306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 268362306a36Sopenharmony_ci return 0; 268462306a36Sopenharmony_ci} 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_cistatic int out_jack_mode_put(struct snd_kcontrol *kcontrol, 268762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 268862306a36Sopenharmony_ci{ 268962306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 269062306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 269162306a36Sopenharmony_ci unsigned int val; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; 269462306a36Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) == val) 269562306a36Sopenharmony_ci return 0; 269662306a36Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 269762306a36Sopenharmony_ci return 1; 269862306a36Sopenharmony_ci} 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_cistatic const struct snd_kcontrol_new out_jack_mode_enum = { 270162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 270262306a36Sopenharmony_ci .info = out_jack_mode_info, 270362306a36Sopenharmony_ci .get = out_jack_mode_get, 270462306a36Sopenharmony_ci .put = out_jack_mode_put, 270562306a36Sopenharmony_ci}; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_cistatic bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) 270862306a36Sopenharmony_ci{ 270962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 271062306a36Sopenharmony_ci const struct snd_kcontrol_new *kctl; 271162306a36Sopenharmony_ci int i; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci snd_array_for_each(&spec->kctls, i, kctl) { 271462306a36Sopenharmony_ci if (!strcmp(kctl->name, name) && kctl->index == idx) 271562306a36Sopenharmony_ci return true; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci return false; 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_cistatic void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, 272162306a36Sopenharmony_ci char *name, size_t name_len) 272262306a36Sopenharmony_ci{ 272362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 272462306a36Sopenharmony_ci int idx = 0; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); 272762306a36Sopenharmony_ci strlcat(name, " Jack Mode", name_len); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci for (; find_kctl_name(codec, name, idx); idx++) 273062306a36Sopenharmony_ci ; 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) 273462306a36Sopenharmony_ci{ 273562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 273662306a36Sopenharmony_ci if (spec->add_jack_modes) { 273762306a36Sopenharmony_ci unsigned int pincap = snd_hda_query_pin_caps(codec, pin); 273862306a36Sopenharmony_ci if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) 273962306a36Sopenharmony_ci return 2; 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci return 1; 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_cistatic int create_out_jack_modes(struct hda_codec *codec, int num_pins, 274562306a36Sopenharmony_ci hda_nid_t *pins) 274662306a36Sopenharmony_ci{ 274762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 274862306a36Sopenharmony_ci int i; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 275162306a36Sopenharmony_ci hda_nid_t pin = pins[i]; 275262306a36Sopenharmony_ci if (pin == spec->hp_mic_pin) 275362306a36Sopenharmony_ci continue; 275462306a36Sopenharmony_ci if (get_out_jack_num_items(codec, pin) > 1) { 275562306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 275662306a36Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 275762306a36Sopenharmony_ci get_jack_mode_name(codec, pin, name, sizeof(name)); 275862306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, 275962306a36Sopenharmony_ci &out_jack_mode_enum); 276062306a36Sopenharmony_ci if (!knew) 276162306a36Sopenharmony_ci return -ENOMEM; 276262306a36Sopenharmony_ci knew->private_value = pin; 276362306a36Sopenharmony_ci } 276462306a36Sopenharmony_ci } 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci return 0; 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/* 277062306a36Sopenharmony_ci * input jack mode 277162306a36Sopenharmony_ci */ 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ 277462306a36Sopenharmony_ci#define NUM_VREFS 6 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_cistatic const char * const vref_texts[NUM_VREFS] = { 277762306a36Sopenharmony_ci "Line In", "Mic 50pc Bias", "Mic 0V Bias", 277862306a36Sopenharmony_ci "", "Mic 80pc Bias", "Mic 100pc Bias" 277962306a36Sopenharmony_ci}; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_cistatic unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) 278262306a36Sopenharmony_ci{ 278362306a36Sopenharmony_ci unsigned int pincap; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci pincap = snd_hda_query_pin_caps(codec, pin); 278662306a36Sopenharmony_ci pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; 278762306a36Sopenharmony_ci /* filter out unusual vrefs */ 278862306a36Sopenharmony_ci pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); 278962306a36Sopenharmony_ci return pincap; 279062306a36Sopenharmony_ci} 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ 279362306a36Sopenharmony_cistatic int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci unsigned int i, n = 0; 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci for (i = 0; i < NUM_VREFS; i++) { 279862306a36Sopenharmony_ci if (vref_caps & (1 << i)) { 279962306a36Sopenharmony_ci if (n == item_idx) 280062306a36Sopenharmony_ci return i; 280162306a36Sopenharmony_ci n++; 280262306a36Sopenharmony_ci } 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci return 0; 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci/* convert back from the vref ctl index to the enum item index */ 280862306a36Sopenharmony_cistatic int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) 280962306a36Sopenharmony_ci{ 281062306a36Sopenharmony_ci unsigned int i, n = 0; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci for (i = 0; i < NUM_VREFS; i++) { 281362306a36Sopenharmony_ci if (i == idx) 281462306a36Sopenharmony_ci return n; 281562306a36Sopenharmony_ci if (vref_caps & (1 << i)) 281662306a36Sopenharmony_ci n++; 281762306a36Sopenharmony_ci } 281862306a36Sopenharmony_ci return 0; 281962306a36Sopenharmony_ci} 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_cistatic int in_jack_mode_info(struct snd_kcontrol *kcontrol, 282262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 282362306a36Sopenharmony_ci{ 282462306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 282562306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 282662306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), 282962306a36Sopenharmony_ci vref_texts); 283062306a36Sopenharmony_ci /* set the right text */ 283162306a36Sopenharmony_ci strcpy(uinfo->value.enumerated.name, 283262306a36Sopenharmony_ci vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); 283362306a36Sopenharmony_ci return 0; 283462306a36Sopenharmony_ci} 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_cistatic int in_jack_mode_get(struct snd_kcontrol *kcontrol, 283762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 283862306a36Sopenharmony_ci{ 283962306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 284062306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 284162306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 284262306a36Sopenharmony_ci unsigned int idx; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; 284562306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); 284662306a36Sopenharmony_ci return 0; 284762306a36Sopenharmony_ci} 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_cistatic int in_jack_mode_put(struct snd_kcontrol *kcontrol, 285062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 285162306a36Sopenharmony_ci{ 285262306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 285362306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 285462306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 285562306a36Sopenharmony_ci unsigned int val, idx; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, nid); 285862306a36Sopenharmony_ci idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); 285962306a36Sopenharmony_ci if (idx == ucontrol->value.enumerated.item[0]) 286062306a36Sopenharmony_ci return 0; 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci val &= ~AC_PINCTL_VREFEN; 286362306a36Sopenharmony_ci val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); 286462306a36Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 286562306a36Sopenharmony_ci return 1; 286662306a36Sopenharmony_ci} 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_cistatic const struct snd_kcontrol_new in_jack_mode_enum = { 286962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 287062306a36Sopenharmony_ci .info = in_jack_mode_info, 287162306a36Sopenharmony_ci .get = in_jack_mode_get, 287262306a36Sopenharmony_ci .put = in_jack_mode_put, 287362306a36Sopenharmony_ci}; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_cistatic int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin) 287662306a36Sopenharmony_ci{ 287762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 287862306a36Sopenharmony_ci int nitems = 0; 287962306a36Sopenharmony_ci if (spec->add_jack_modes) 288062306a36Sopenharmony_ci nitems = hweight32(get_vref_caps(codec, pin)); 288162306a36Sopenharmony_ci return nitems ? nitems : 1; 288262306a36Sopenharmony_ci} 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_cistatic int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) 288562306a36Sopenharmony_ci{ 288662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 288762306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 288862306a36Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 288962306a36Sopenharmony_ci unsigned int defcfg; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci if (pin == spec->hp_mic_pin) 289262306a36Sopenharmony_ci return 0; /* already done in create_out_jack_mode() */ 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci /* no jack mode for fixed pins */ 289562306a36Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, pin); 289662306a36Sopenharmony_ci if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) 289762306a36Sopenharmony_ci return 0; 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci /* no multiple vref caps? */ 290062306a36Sopenharmony_ci if (get_in_jack_num_items(codec, pin) <= 1) 290162306a36Sopenharmony_ci return 0; 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci get_jack_mode_name(codec, pin, name, sizeof(name)); 290462306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); 290562306a36Sopenharmony_ci if (!knew) 290662306a36Sopenharmony_ci return -ENOMEM; 290762306a36Sopenharmony_ci knew->private_value = pin; 290862306a36Sopenharmony_ci return 0; 290962306a36Sopenharmony_ci} 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci/* 291262306a36Sopenharmony_ci * HP/mic shared jack mode 291362306a36Sopenharmony_ci */ 291462306a36Sopenharmony_cistatic int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, 291562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 291662306a36Sopenharmony_ci{ 291762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 291862306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 291962306a36Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 292062306a36Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 292162306a36Sopenharmony_ci const char *text = NULL; 292262306a36Sopenharmony_ci int idx; 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 292562306a36Sopenharmony_ci uinfo->count = 1; 292662306a36Sopenharmony_ci uinfo->value.enumerated.items = out_jacks + in_jacks; 292762306a36Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 292862306a36Sopenharmony_ci uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 292962306a36Sopenharmony_ci idx = uinfo->value.enumerated.item; 293062306a36Sopenharmony_ci if (idx < out_jacks) { 293162306a36Sopenharmony_ci if (out_jacks > 1) 293262306a36Sopenharmony_ci text = out_jack_texts[idx]; 293362306a36Sopenharmony_ci else 293462306a36Sopenharmony_ci text = "Headphone Out"; 293562306a36Sopenharmony_ci } else { 293662306a36Sopenharmony_ci idx -= out_jacks; 293762306a36Sopenharmony_ci if (in_jacks > 1) { 293862306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 293962306a36Sopenharmony_ci text = vref_texts[get_vref_idx(vref_caps, idx)]; 294062306a36Sopenharmony_ci } else 294162306a36Sopenharmony_ci text = "Mic In"; 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci strcpy(uinfo->value.enumerated.name, text); 294562306a36Sopenharmony_ci return 0; 294662306a36Sopenharmony_ci} 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_cistatic int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 295162306a36Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 295262306a36Sopenharmony_ci unsigned int val = snd_hda_codec_get_pin_target(codec, nid); 295362306a36Sopenharmony_ci int idx = 0; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci if (val & PIN_OUT) { 295662306a36Sopenharmony_ci if (out_jacks > 1 && val == PIN_HP) 295762306a36Sopenharmony_ci idx = 1; 295862306a36Sopenharmony_ci } else if (val & PIN_IN) { 295962306a36Sopenharmony_ci idx = out_jacks; 296062306a36Sopenharmony_ci if (in_jacks > 1) { 296162306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 296262306a36Sopenharmony_ci val &= AC_PINCTL_VREFEN; 296362306a36Sopenharmony_ci idx += cvt_from_vref_idx(vref_caps, val); 296462306a36Sopenharmony_ci } 296562306a36Sopenharmony_ci } 296662306a36Sopenharmony_ci return idx; 296762306a36Sopenharmony_ci} 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_cistatic int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol, 297062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 297162306a36Sopenharmony_ci{ 297262306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 297362306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 297462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 297562306a36Sopenharmony_ci get_cur_hp_mic_jack_mode(codec, nid); 297662306a36Sopenharmony_ci return 0; 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_cistatic int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol, 298062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 298362306a36Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 298462306a36Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 298562306a36Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 298662306a36Sopenharmony_ci unsigned int val, oldval, idx; 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci oldval = get_cur_hp_mic_jack_mode(codec, nid); 298962306a36Sopenharmony_ci idx = ucontrol->value.enumerated.item[0]; 299062306a36Sopenharmony_ci if (oldval == idx) 299162306a36Sopenharmony_ci return 0; 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci if (idx < out_jacks) { 299462306a36Sopenharmony_ci if (out_jacks > 1) 299562306a36Sopenharmony_ci val = idx ? PIN_HP : PIN_OUT; 299662306a36Sopenharmony_ci else 299762306a36Sopenharmony_ci val = PIN_HP; 299862306a36Sopenharmony_ci } else { 299962306a36Sopenharmony_ci idx -= out_jacks; 300062306a36Sopenharmony_ci if (in_jacks > 1) { 300162306a36Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 300262306a36Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, nid); 300362306a36Sopenharmony_ci val &= ~(AC_PINCTL_VREFEN | PIN_HP); 300462306a36Sopenharmony_ci val |= get_vref_idx(vref_caps, idx) | PIN_IN; 300562306a36Sopenharmony_ci } else 300662306a36Sopenharmony_ci val = snd_hda_get_default_vref(codec, nid) | PIN_IN; 300762306a36Sopenharmony_ci } 300862306a36Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 300962306a36Sopenharmony_ci call_hp_automute(codec, NULL); 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci return 1; 301262306a36Sopenharmony_ci} 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_cistatic const struct snd_kcontrol_new hp_mic_jack_mode_enum = { 301562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 301662306a36Sopenharmony_ci .info = hp_mic_jack_mode_info, 301762306a36Sopenharmony_ci .get = hp_mic_jack_mode_get, 301862306a36Sopenharmony_ci .put = hp_mic_jack_mode_put, 301962306a36Sopenharmony_ci}; 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_cistatic int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin) 302262306a36Sopenharmony_ci{ 302362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 302462306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode", 302762306a36Sopenharmony_ci &hp_mic_jack_mode_enum); 302862306a36Sopenharmony_ci if (!knew) 302962306a36Sopenharmony_ci return -ENOMEM; 303062306a36Sopenharmony_ci knew->private_value = pin; 303162306a36Sopenharmony_ci spec->hp_mic_jack_modes = 1; 303262306a36Sopenharmony_ci return 0; 303362306a36Sopenharmony_ci} 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci/* 303662306a36Sopenharmony_ci * Parse input paths 303762306a36Sopenharmony_ci */ 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci/* add the powersave loopback-list entry */ 304062306a36Sopenharmony_cistatic int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) 304162306a36Sopenharmony_ci{ 304262306a36Sopenharmony_ci struct hda_amp_list *list; 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci list = snd_array_new(&spec->loopback_list); 304562306a36Sopenharmony_ci if (!list) 304662306a36Sopenharmony_ci return -ENOMEM; 304762306a36Sopenharmony_ci list->nid = mix; 304862306a36Sopenharmony_ci list->dir = HDA_INPUT; 304962306a36Sopenharmony_ci list->idx = idx; 305062306a36Sopenharmony_ci spec->loopback.amplist = spec->loopback_list.list; 305162306a36Sopenharmony_ci return 0; 305262306a36Sopenharmony_ci} 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci/* return true if either a volume or a mute amp is found for the given 305562306a36Sopenharmony_ci * aamix path; the amp has to be either in the mixer node or its direct leaf 305662306a36Sopenharmony_ci */ 305762306a36Sopenharmony_cistatic bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, 305862306a36Sopenharmony_ci hda_nid_t pin, unsigned int *mix_val, 305962306a36Sopenharmony_ci unsigned int *mute_val) 306062306a36Sopenharmony_ci{ 306162306a36Sopenharmony_ci int idx, num_conns; 306262306a36Sopenharmony_ci const hda_nid_t *list; 306362306a36Sopenharmony_ci hda_nid_t nid; 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); 306662306a36Sopenharmony_ci if (idx < 0) 306762306a36Sopenharmony_ci return false; 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci *mix_val = *mute_val = 0; 307062306a36Sopenharmony_ci if (nid_has_volume(codec, mix_nid, HDA_INPUT)) 307162306a36Sopenharmony_ci *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 307262306a36Sopenharmony_ci if (nid_has_mute(codec, mix_nid, HDA_INPUT)) 307362306a36Sopenharmony_ci *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 307462306a36Sopenharmony_ci if (*mix_val && *mute_val) 307562306a36Sopenharmony_ci return true; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci /* check leaf node */ 307862306a36Sopenharmony_ci num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); 307962306a36Sopenharmony_ci if (num_conns < idx) 308062306a36Sopenharmony_ci return false; 308162306a36Sopenharmony_ci nid = list[idx]; 308262306a36Sopenharmony_ci if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && 308362306a36Sopenharmony_ci !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) 308462306a36Sopenharmony_ci *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 308562306a36Sopenharmony_ci if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && 308662306a36Sopenharmony_ci !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) 308762306a36Sopenharmony_ci *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci return *mix_val || *mute_val; 309062306a36Sopenharmony_ci} 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci/* create input playback/capture controls for the given pin */ 309362306a36Sopenharmony_cistatic int new_analog_input(struct hda_codec *codec, int input_idx, 309462306a36Sopenharmony_ci hda_nid_t pin, const char *ctlname, int ctlidx, 309562306a36Sopenharmony_ci hda_nid_t mix_nid) 309662306a36Sopenharmony_ci{ 309762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 309862306a36Sopenharmony_ci struct nid_path *path; 309962306a36Sopenharmony_ci unsigned int mix_val, mute_val; 310062306a36Sopenharmony_ci int err, idx; 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) 310362306a36Sopenharmony_ci return 0; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, mix_nid, 0); 310662306a36Sopenharmony_ci if (!path) 310762306a36Sopenharmony_ci return -EINVAL; 310862306a36Sopenharmony_ci print_nid_path(codec, "loopback", path); 310962306a36Sopenharmony_ci spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci idx = path->idx[path->depth - 1]; 311262306a36Sopenharmony_ci if (mix_val) { 311362306a36Sopenharmony_ci err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); 311462306a36Sopenharmony_ci if (err < 0) 311562306a36Sopenharmony_ci return err; 311662306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = mix_val; 311762306a36Sopenharmony_ci } 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci if (mute_val) { 312062306a36Sopenharmony_ci err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); 312162306a36Sopenharmony_ci if (err < 0) 312262306a36Sopenharmony_ci return err; 312362306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = mute_val; 312462306a36Sopenharmony_ci } 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ci path->active = true; 312762306a36Sopenharmony_ci path->stream_enabled = true; /* no DAC/ADC involved */ 312862306a36Sopenharmony_ci err = add_loopback_list(spec, mix_nid, idx); 312962306a36Sopenharmony_ci if (err < 0) 313062306a36Sopenharmony_ci return err; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci if (spec->mixer_nid != spec->mixer_merge_nid && 313362306a36Sopenharmony_ci !spec->loopback_merge_path) { 313462306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, spec->mixer_nid, 313562306a36Sopenharmony_ci spec->mixer_merge_nid, 0); 313662306a36Sopenharmony_ci if (path) { 313762306a36Sopenharmony_ci print_nid_path(codec, "loopback-merge", path); 313862306a36Sopenharmony_ci path->active = true; 313962306a36Sopenharmony_ci path->pin_fixed = true; /* static route */ 314062306a36Sopenharmony_ci path->stream_enabled = true; /* no DAC/ADC involved */ 314162306a36Sopenharmony_ci spec->loopback_merge_path = 314262306a36Sopenharmony_ci snd_hda_get_path_idx(codec, path); 314362306a36Sopenharmony_ci } 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci return 0; 314762306a36Sopenharmony_ci} 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_cistatic int is_input_pin(struct hda_codec *codec, hda_nid_t nid) 315062306a36Sopenharmony_ci{ 315162306a36Sopenharmony_ci unsigned int pincap = snd_hda_query_pin_caps(codec, nid); 315262306a36Sopenharmony_ci return (pincap & AC_PINCAP_IN) != 0; 315362306a36Sopenharmony_ci} 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci/* Parse the codec tree and retrieve ADCs */ 315662306a36Sopenharmony_cistatic int fill_adc_nids(struct hda_codec *codec) 315762306a36Sopenharmony_ci{ 315862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 315962306a36Sopenharmony_ci hda_nid_t nid; 316062306a36Sopenharmony_ci hda_nid_t *adc_nids = spec->adc_nids; 316162306a36Sopenharmony_ci int max_nums = ARRAY_SIZE(spec->adc_nids); 316262306a36Sopenharmony_ci int nums = 0; 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 316562306a36Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 316662306a36Sopenharmony_ci int type = get_wcaps_type(caps); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) 316962306a36Sopenharmony_ci continue; 317062306a36Sopenharmony_ci adc_nids[nums] = nid; 317162306a36Sopenharmony_ci if (++nums >= max_nums) 317262306a36Sopenharmony_ci break; 317362306a36Sopenharmony_ci } 317462306a36Sopenharmony_ci spec->num_adc_nids = nums; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci /* copy the detected ADCs to all_adcs[] */ 317762306a36Sopenharmony_ci spec->num_all_adcs = nums; 317862306a36Sopenharmony_ci memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci return nums; 318162306a36Sopenharmony_ci} 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci/* filter out invalid adc_nids that don't give all active input pins; 318462306a36Sopenharmony_ci * if needed, check whether dynamic ADC-switching is available 318562306a36Sopenharmony_ci */ 318662306a36Sopenharmony_cistatic int check_dyn_adc_switch(struct hda_codec *codec) 318762306a36Sopenharmony_ci{ 318862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 318962306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 319062306a36Sopenharmony_ci unsigned int ok_bits; 319162306a36Sopenharmony_ci int i, n, nums; 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci nums = 0; 319462306a36Sopenharmony_ci ok_bits = 0; 319562306a36Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 319662306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 319762306a36Sopenharmony_ci if (!spec->input_paths[i][n]) 319862306a36Sopenharmony_ci break; 319962306a36Sopenharmony_ci } 320062306a36Sopenharmony_ci if (i >= imux->num_items) { 320162306a36Sopenharmony_ci ok_bits |= (1 << n); 320262306a36Sopenharmony_ci nums++; 320362306a36Sopenharmony_ci } 320462306a36Sopenharmony_ci } 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci if (!ok_bits) { 320762306a36Sopenharmony_ci /* check whether ADC-switch is possible */ 320862306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 320962306a36Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 321062306a36Sopenharmony_ci if (spec->input_paths[i][n]) { 321162306a36Sopenharmony_ci spec->dyn_adc_idx[i] = n; 321262306a36Sopenharmony_ci break; 321362306a36Sopenharmony_ci } 321462306a36Sopenharmony_ci } 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci codec_dbg(codec, "enabling ADC switching\n"); 321862306a36Sopenharmony_ci spec->dyn_adc_switch = 1; 321962306a36Sopenharmony_ci } else if (nums != spec->num_adc_nids) { 322062306a36Sopenharmony_ci /* shrink the invalid adcs and input paths */ 322162306a36Sopenharmony_ci nums = 0; 322262306a36Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 322362306a36Sopenharmony_ci if (!(ok_bits & (1 << n))) 322462306a36Sopenharmony_ci continue; 322562306a36Sopenharmony_ci if (n != nums) { 322662306a36Sopenharmony_ci spec->adc_nids[nums] = spec->adc_nids[n]; 322762306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 322862306a36Sopenharmony_ci invalidate_nid_path(codec, 322962306a36Sopenharmony_ci spec->input_paths[i][nums]); 323062306a36Sopenharmony_ci spec->input_paths[i][nums] = 323162306a36Sopenharmony_ci spec->input_paths[i][n]; 323262306a36Sopenharmony_ci spec->input_paths[i][n] = 0; 323362306a36Sopenharmony_ci } 323462306a36Sopenharmony_ci } 323562306a36Sopenharmony_ci nums++; 323662306a36Sopenharmony_ci } 323762306a36Sopenharmony_ci spec->num_adc_nids = nums; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci if (imux->num_items == 1 || 324162306a36Sopenharmony_ci (imux->num_items == 2 && spec->hp_mic)) { 324262306a36Sopenharmony_ci codec_dbg(codec, "reducing to a single ADC\n"); 324362306a36Sopenharmony_ci spec->num_adc_nids = 1; /* reduce to a single ADC */ 324462306a36Sopenharmony_ci } 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci /* single index for individual volumes ctls */ 324762306a36Sopenharmony_ci if (!spec->dyn_adc_switch && spec->multi_cap_vol) 324862306a36Sopenharmony_ci spec->num_adc_nids = 1; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci return 0; 325162306a36Sopenharmony_ci} 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci/* parse capture source paths from the given pin and create imux items */ 325462306a36Sopenharmony_cistatic int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, 325562306a36Sopenharmony_ci int cfg_idx, int num_adcs, 325662306a36Sopenharmony_ci const char *label, int anchor) 325762306a36Sopenharmony_ci{ 325862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 325962306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 326062306a36Sopenharmony_ci int imux_idx = imux->num_items; 326162306a36Sopenharmony_ci bool imux_added = false; 326262306a36Sopenharmony_ci int c; 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci for (c = 0; c < num_adcs; c++) { 326562306a36Sopenharmony_ci struct nid_path *path; 326662306a36Sopenharmony_ci hda_nid_t adc = spec->adc_nids[c]; 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci if (!is_reachable_path(codec, pin, adc)) 326962306a36Sopenharmony_ci continue; 327062306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, adc, anchor); 327162306a36Sopenharmony_ci if (!path) 327262306a36Sopenharmony_ci continue; 327362306a36Sopenharmony_ci print_nid_path(codec, "input", path); 327462306a36Sopenharmony_ci spec->input_paths[imux_idx][c] = 327562306a36Sopenharmony_ci snd_hda_get_path_idx(codec, path); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci if (!imux_added) { 327862306a36Sopenharmony_ci if (spec->hp_mic_pin == pin) 327962306a36Sopenharmony_ci spec->hp_mic_mux_idx = imux->num_items; 328062306a36Sopenharmony_ci spec->imux_pins[imux->num_items] = pin; 328162306a36Sopenharmony_ci snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); 328262306a36Sopenharmony_ci imux_added = true; 328362306a36Sopenharmony_ci if (spec->dyn_adc_switch) 328462306a36Sopenharmony_ci spec->dyn_adc_idx[imux_idx] = c; 328562306a36Sopenharmony_ci } 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci return 0; 328962306a36Sopenharmony_ci} 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci/* 329262306a36Sopenharmony_ci * create playback/capture controls for input pins 329362306a36Sopenharmony_ci */ 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci/* fill the label for each input at first */ 329662306a36Sopenharmony_cistatic int fill_input_pin_labels(struct hda_codec *codec) 329762306a36Sopenharmony_ci{ 329862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 329962306a36Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 330062306a36Sopenharmony_ci int i; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 330362306a36Sopenharmony_ci hda_nid_t pin = cfg->inputs[i].pin; 330462306a36Sopenharmony_ci const char *label; 330562306a36Sopenharmony_ci int j, idx; 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci if (!is_input_pin(codec, pin)) 330862306a36Sopenharmony_ci continue; 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci label = hda_get_autocfg_input_label(codec, cfg, i); 331162306a36Sopenharmony_ci idx = 0; 331262306a36Sopenharmony_ci for (j = i - 1; j >= 0; j--) { 331362306a36Sopenharmony_ci if (spec->input_labels[j] && 331462306a36Sopenharmony_ci !strcmp(spec->input_labels[j], label)) { 331562306a36Sopenharmony_ci idx = spec->input_label_idxs[j] + 1; 331662306a36Sopenharmony_ci break; 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci } 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci spec->input_labels[i] = label; 332162306a36Sopenharmony_ci spec->input_label_idxs[i] = idx; 332262306a36Sopenharmony_ci } 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci return 0; 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_cistatic int create_input_ctls(struct hda_codec *codec) 333062306a36Sopenharmony_ci{ 333162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 333262306a36Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 333362306a36Sopenharmony_ci hda_nid_t mixer = spec->mixer_nid; 333462306a36Sopenharmony_ci int num_adcs; 333562306a36Sopenharmony_ci int i, err; 333662306a36Sopenharmony_ci unsigned int val; 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci num_adcs = fill_adc_nids(codec); 333962306a36Sopenharmony_ci if (num_adcs < 0) 334062306a36Sopenharmony_ci return 0; 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci err = fill_input_pin_labels(codec); 334362306a36Sopenharmony_ci if (err < 0) 334462306a36Sopenharmony_ci return err; 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 334762306a36Sopenharmony_ci hda_nid_t pin; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci pin = cfg->inputs[i].pin; 335062306a36Sopenharmony_ci if (!is_input_pin(codec, pin)) 335162306a36Sopenharmony_ci continue; 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci val = PIN_IN; 335462306a36Sopenharmony_ci if (cfg->inputs[i].type == AUTO_PIN_MIC) 335562306a36Sopenharmony_ci val |= snd_hda_get_default_vref(codec, pin); 335662306a36Sopenharmony_ci if (pin != spec->hp_mic_pin && 335762306a36Sopenharmony_ci !snd_hda_codec_get_pin_target(codec, pin)) 335862306a36Sopenharmony_ci set_pin_target(codec, pin, val, false); 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci if (mixer) { 336162306a36Sopenharmony_ci if (is_reachable_path(codec, pin, mixer)) { 336262306a36Sopenharmony_ci err = new_analog_input(codec, i, pin, 336362306a36Sopenharmony_ci spec->input_labels[i], 336462306a36Sopenharmony_ci spec->input_label_idxs[i], 336562306a36Sopenharmony_ci mixer); 336662306a36Sopenharmony_ci if (err < 0) 336762306a36Sopenharmony_ci return err; 336862306a36Sopenharmony_ci } 336962306a36Sopenharmony_ci } 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci err = parse_capture_source(codec, pin, i, num_adcs, 337262306a36Sopenharmony_ci spec->input_labels[i], -mixer); 337362306a36Sopenharmony_ci if (err < 0) 337462306a36Sopenharmony_ci return err; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci if (spec->add_jack_modes) { 337762306a36Sopenharmony_ci err = create_in_jack_mode(codec, pin); 337862306a36Sopenharmony_ci if (err < 0) 337962306a36Sopenharmony_ci return err; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci } 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_ci /* add stereo mix when explicitly enabled via hint */ 338462306a36Sopenharmony_ci if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { 338562306a36Sopenharmony_ci err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, 338662306a36Sopenharmony_ci "Stereo Mix", 0); 338762306a36Sopenharmony_ci if (err < 0) 338862306a36Sopenharmony_ci return err; 338962306a36Sopenharmony_ci else 339062306a36Sopenharmony_ci spec->suppress_auto_mic = 1; 339162306a36Sopenharmony_ci } 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci return 0; 339462306a36Sopenharmony_ci} 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci/* 339862306a36Sopenharmony_ci * input source mux 339962306a36Sopenharmony_ci */ 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci/* get the input path specified by the given adc and imux indices */ 340262306a36Sopenharmony_cistatic struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) 340362306a36Sopenharmony_ci{ 340462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 340562306a36Sopenharmony_ci if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { 340662306a36Sopenharmony_ci snd_BUG(); 340762306a36Sopenharmony_ci return NULL; 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci if (spec->dyn_adc_switch) 341062306a36Sopenharmony_ci adc_idx = spec->dyn_adc_idx[imux_idx]; 341162306a36Sopenharmony_ci if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { 341262306a36Sopenharmony_ci snd_BUG(); 341362306a36Sopenharmony_ci return NULL; 341462306a36Sopenharmony_ci } 341562306a36Sopenharmony_ci return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); 341662306a36Sopenharmony_ci} 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cistatic int mux_select(struct hda_codec *codec, unsigned int adc_idx, 341962306a36Sopenharmony_ci unsigned int idx); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_cistatic int mux_enum_info(struct snd_kcontrol *kcontrol, 342262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 342362306a36Sopenharmony_ci{ 342462306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 342562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 342662306a36Sopenharmony_ci return snd_hda_input_mux_info(&spec->input_mux, uinfo); 342762306a36Sopenharmony_ci} 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_cistatic int mux_enum_get(struct snd_kcontrol *kcontrol, 343062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 343162306a36Sopenharmony_ci{ 343262306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 343362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 343462306a36Sopenharmony_ci /* the ctls are created at once with multiple counts */ 343562306a36Sopenharmony_ci unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 343862306a36Sopenharmony_ci return 0; 343962306a36Sopenharmony_ci} 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_cistatic int mux_enum_put(struct snd_kcontrol *kcontrol, 344262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 344362306a36Sopenharmony_ci{ 344462306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 344562306a36Sopenharmony_ci unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 344662306a36Sopenharmony_ci return mux_select(codec, adc_idx, 344762306a36Sopenharmony_ci ucontrol->value.enumerated.item[0]); 344862306a36Sopenharmony_ci} 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_cistatic const struct snd_kcontrol_new cap_src_temp = { 345162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 345262306a36Sopenharmony_ci .name = "Input Source", 345362306a36Sopenharmony_ci .info = mux_enum_info, 345462306a36Sopenharmony_ci .get = mux_enum_get, 345562306a36Sopenharmony_ci .put = mux_enum_put, 345662306a36Sopenharmony_ci}; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci/* 345962306a36Sopenharmony_ci * capture volume and capture switch ctls 346062306a36Sopenharmony_ci */ 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_citypedef int (*put_call_t)(struct snd_kcontrol *kcontrol, 346362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci/* call the given amp update function for all amps in the imux list at once */ 346662306a36Sopenharmony_cistatic int cap_put_caller(struct snd_kcontrol *kcontrol, 346762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 346862306a36Sopenharmony_ci put_call_t func, int type) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 347162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 347262306a36Sopenharmony_ci const struct hda_input_mux *imux; 347362306a36Sopenharmony_ci struct nid_path *path; 347462306a36Sopenharmony_ci int i, adc_idx, ret, err = 0; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci imux = &spec->input_mux; 347762306a36Sopenharmony_ci adc_idx = kcontrol->id.index; 347862306a36Sopenharmony_ci mutex_lock(&codec->control_mutex); 347962306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 348062306a36Sopenharmony_ci path = get_input_path(codec, adc_idx, i); 348162306a36Sopenharmony_ci if (!path || !path->ctls[type]) 348262306a36Sopenharmony_ci continue; 348362306a36Sopenharmony_ci kcontrol->private_value = path->ctls[type]; 348462306a36Sopenharmony_ci ret = func(kcontrol, ucontrol); 348562306a36Sopenharmony_ci if (ret < 0) { 348662306a36Sopenharmony_ci err = ret; 348762306a36Sopenharmony_ci break; 348862306a36Sopenharmony_ci } 348962306a36Sopenharmony_ci if (ret > 0) 349062306a36Sopenharmony_ci err = 1; 349162306a36Sopenharmony_ci } 349262306a36Sopenharmony_ci mutex_unlock(&codec->control_mutex); 349362306a36Sopenharmony_ci if (err >= 0 && spec->cap_sync_hook) 349462306a36Sopenharmony_ci spec->cap_sync_hook(codec, kcontrol, ucontrol); 349562306a36Sopenharmony_ci return err; 349662306a36Sopenharmony_ci} 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci/* capture volume ctl callbacks */ 349962306a36Sopenharmony_ci#define cap_vol_info snd_hda_mixer_amp_volume_info 350062306a36Sopenharmony_ci#define cap_vol_get snd_hda_mixer_amp_volume_get 350162306a36Sopenharmony_ci#define cap_vol_tlv snd_hda_mixer_amp_tlv 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_cistatic int cap_vol_put(struct snd_kcontrol *kcontrol, 350462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 350562306a36Sopenharmony_ci{ 350662306a36Sopenharmony_ci return cap_put_caller(kcontrol, ucontrol, 350762306a36Sopenharmony_ci snd_hda_mixer_amp_volume_put, 350862306a36Sopenharmony_ci NID_PATH_VOL_CTL); 350962306a36Sopenharmony_ci} 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_cistatic const struct snd_kcontrol_new cap_vol_temp = { 351262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 351362306a36Sopenharmony_ci .name = "Capture Volume", 351462306a36Sopenharmony_ci .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 351562306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ | 351662306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), 351762306a36Sopenharmony_ci .info = cap_vol_info, 351862306a36Sopenharmony_ci .get = cap_vol_get, 351962306a36Sopenharmony_ci .put = cap_vol_put, 352062306a36Sopenharmony_ci .tlv = { .c = cap_vol_tlv }, 352162306a36Sopenharmony_ci}; 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci/* capture switch ctl callbacks */ 352462306a36Sopenharmony_ci#define cap_sw_info snd_ctl_boolean_stereo_info 352562306a36Sopenharmony_ci#define cap_sw_get snd_hda_mixer_amp_switch_get 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_cistatic int cap_sw_put(struct snd_kcontrol *kcontrol, 352862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 352962306a36Sopenharmony_ci{ 353062306a36Sopenharmony_ci return cap_put_caller(kcontrol, ucontrol, 353162306a36Sopenharmony_ci snd_hda_mixer_amp_switch_put, 353262306a36Sopenharmony_ci NID_PATH_MUTE_CTL); 353362306a36Sopenharmony_ci} 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_cistatic const struct snd_kcontrol_new cap_sw_temp = { 353662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 353762306a36Sopenharmony_ci .name = "Capture Switch", 353862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 353962306a36Sopenharmony_ci .info = cap_sw_info, 354062306a36Sopenharmony_ci .get = cap_sw_get, 354162306a36Sopenharmony_ci .put = cap_sw_put, 354262306a36Sopenharmony_ci}; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_cistatic int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) 354562306a36Sopenharmony_ci{ 354662306a36Sopenharmony_ci hda_nid_t nid; 354762306a36Sopenharmony_ci int i, depth; 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; 355062306a36Sopenharmony_ci for (depth = 0; depth < 3; depth++) { 355162306a36Sopenharmony_ci if (depth >= path->depth) 355262306a36Sopenharmony_ci return -EINVAL; 355362306a36Sopenharmony_ci i = path->depth - depth - 1; 355462306a36Sopenharmony_ci nid = path->path[i]; 355562306a36Sopenharmony_ci if (!path->ctls[NID_PATH_VOL_CTL]) { 355662306a36Sopenharmony_ci if (nid_has_volume(codec, nid, HDA_OUTPUT)) 355762306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = 355862306a36Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 355962306a36Sopenharmony_ci else if (nid_has_volume(codec, nid, HDA_INPUT)) { 356062306a36Sopenharmony_ci int idx = path->idx[i]; 356162306a36Sopenharmony_ci if (!depth && codec->single_adc_amp) 356262306a36Sopenharmony_ci idx = 0; 356362306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = 356462306a36Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci } 356762306a36Sopenharmony_ci if (!path->ctls[NID_PATH_MUTE_CTL]) { 356862306a36Sopenharmony_ci if (nid_has_mute(codec, nid, HDA_OUTPUT)) 356962306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = 357062306a36Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 357162306a36Sopenharmony_ci else if (nid_has_mute(codec, nid, HDA_INPUT)) { 357262306a36Sopenharmony_ci int idx = path->idx[i]; 357362306a36Sopenharmony_ci if (!depth && codec->single_adc_amp) 357462306a36Sopenharmony_ci idx = 0; 357562306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = 357662306a36Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 357762306a36Sopenharmony_ci } 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci } 358062306a36Sopenharmony_ci return 0; 358162306a36Sopenharmony_ci} 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_cistatic bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) 358462306a36Sopenharmony_ci{ 358562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 358662306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 358762306a36Sopenharmony_ci unsigned int val; 358862306a36Sopenharmony_ci int i; 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci if (!spec->inv_dmic_split) 359162306a36Sopenharmony_ci return false; 359262306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 359362306a36Sopenharmony_ci if (cfg->inputs[i].pin != nid) 359462306a36Sopenharmony_ci continue; 359562306a36Sopenharmony_ci if (cfg->inputs[i].type != AUTO_PIN_MIC) 359662306a36Sopenharmony_ci return false; 359762306a36Sopenharmony_ci val = snd_hda_codec_get_pincfg(codec, nid); 359862306a36Sopenharmony_ci return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; 359962306a36Sopenharmony_ci } 360062306a36Sopenharmony_ci return false; 360162306a36Sopenharmony_ci} 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci/* capture switch put callback for a single control with hook call */ 360462306a36Sopenharmony_cistatic int cap_single_sw_put(struct snd_kcontrol *kcontrol, 360562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 360662306a36Sopenharmony_ci{ 360762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 360862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 360962306a36Sopenharmony_ci int ret; 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 361262306a36Sopenharmony_ci if (ret < 0) 361362306a36Sopenharmony_ci return ret; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci if (spec->cap_sync_hook) 361662306a36Sopenharmony_ci spec->cap_sync_hook(codec, kcontrol, ucontrol); 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_ci return ret; 361962306a36Sopenharmony_ci} 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_cistatic int add_single_cap_ctl(struct hda_codec *codec, const char *label, 362262306a36Sopenharmony_ci int idx, bool is_switch, unsigned int ctl, 362362306a36Sopenharmony_ci bool inv_dmic) 362462306a36Sopenharmony_ci{ 362562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 362662306a36Sopenharmony_ci char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 362762306a36Sopenharmony_ci int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; 362862306a36Sopenharmony_ci const char *sfx = is_switch ? "Switch" : "Volume"; 362962306a36Sopenharmony_ci unsigned int chs = inv_dmic ? 1 : 3; 363062306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci if (!ctl) 363362306a36Sopenharmony_ci return 0; 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci if (label) 363662306a36Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 363762306a36Sopenharmony_ci "%s Capture %s", label, sfx); 363862306a36Sopenharmony_ci else 363962306a36Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 364062306a36Sopenharmony_ci "Capture %s", sfx); 364162306a36Sopenharmony_ci knew = add_control(spec, type, tmpname, idx, 364262306a36Sopenharmony_ci amp_val_replace_channels(ctl, chs)); 364362306a36Sopenharmony_ci if (!knew) 364462306a36Sopenharmony_ci return -ENOMEM; 364562306a36Sopenharmony_ci if (is_switch) { 364662306a36Sopenharmony_ci knew->put = cap_single_sw_put; 364762306a36Sopenharmony_ci if (spec->mic_mute_led) 364862306a36Sopenharmony_ci knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; 364962306a36Sopenharmony_ci } 365062306a36Sopenharmony_ci if (!inv_dmic) 365162306a36Sopenharmony_ci return 0; 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci /* Make independent right kcontrol */ 365462306a36Sopenharmony_ci if (label) 365562306a36Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 365662306a36Sopenharmony_ci "Inverted %s Capture %s", label, sfx); 365762306a36Sopenharmony_ci else 365862306a36Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 365962306a36Sopenharmony_ci "Inverted Capture %s", sfx); 366062306a36Sopenharmony_ci knew = add_control(spec, type, tmpname, idx, 366162306a36Sopenharmony_ci amp_val_replace_channels(ctl, 2)); 366262306a36Sopenharmony_ci if (!knew) 366362306a36Sopenharmony_ci return -ENOMEM; 366462306a36Sopenharmony_ci if (is_switch) { 366562306a36Sopenharmony_ci knew->put = cap_single_sw_put; 366662306a36Sopenharmony_ci if (spec->mic_mute_led) 366762306a36Sopenharmony_ci knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; 366862306a36Sopenharmony_ci } 366962306a36Sopenharmony_ci return 0; 367062306a36Sopenharmony_ci} 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci/* create single (and simple) capture volume and switch controls */ 367362306a36Sopenharmony_cistatic int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, 367462306a36Sopenharmony_ci unsigned int vol_ctl, unsigned int sw_ctl, 367562306a36Sopenharmony_ci bool inv_dmic) 367662306a36Sopenharmony_ci{ 367762306a36Sopenharmony_ci int err; 367862306a36Sopenharmony_ci err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); 367962306a36Sopenharmony_ci if (err < 0) 368062306a36Sopenharmony_ci return err; 368162306a36Sopenharmony_ci err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); 368262306a36Sopenharmony_ci if (err < 0) 368362306a36Sopenharmony_ci return err; 368462306a36Sopenharmony_ci return 0; 368562306a36Sopenharmony_ci} 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_ci/* create bound capture volume and switch controls */ 368862306a36Sopenharmony_cistatic int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, 368962306a36Sopenharmony_ci unsigned int vol_ctl, unsigned int sw_ctl) 369062306a36Sopenharmony_ci{ 369162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 369262306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci if (vol_ctl) { 369562306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); 369662306a36Sopenharmony_ci if (!knew) 369762306a36Sopenharmony_ci return -ENOMEM; 369862306a36Sopenharmony_ci knew->index = idx; 369962306a36Sopenharmony_ci knew->private_value = vol_ctl; 370062306a36Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 370162306a36Sopenharmony_ci } 370262306a36Sopenharmony_ci if (sw_ctl) { 370362306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); 370462306a36Sopenharmony_ci if (!knew) 370562306a36Sopenharmony_ci return -ENOMEM; 370662306a36Sopenharmony_ci knew->index = idx; 370762306a36Sopenharmony_ci knew->private_value = sw_ctl; 370862306a36Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 370962306a36Sopenharmony_ci if (spec->mic_mute_led) 371062306a36Sopenharmony_ci knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; 371162306a36Sopenharmony_ci } 371262306a36Sopenharmony_ci return 0; 371362306a36Sopenharmony_ci} 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci/* return the vol ctl when used first in the imux list */ 371662306a36Sopenharmony_cistatic unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) 371762306a36Sopenharmony_ci{ 371862306a36Sopenharmony_ci struct nid_path *path; 371962306a36Sopenharmony_ci unsigned int ctl; 372062306a36Sopenharmony_ci int i; 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci path = get_input_path(codec, 0, idx); 372362306a36Sopenharmony_ci if (!path) 372462306a36Sopenharmony_ci return 0; 372562306a36Sopenharmony_ci ctl = path->ctls[type]; 372662306a36Sopenharmony_ci if (!ctl) 372762306a36Sopenharmony_ci return 0; 372862306a36Sopenharmony_ci for (i = 0; i < idx - 1; i++) { 372962306a36Sopenharmony_ci path = get_input_path(codec, 0, i); 373062306a36Sopenharmony_ci if (path && path->ctls[type] == ctl) 373162306a36Sopenharmony_ci return 0; 373262306a36Sopenharmony_ci } 373362306a36Sopenharmony_ci return ctl; 373462306a36Sopenharmony_ci} 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci/* create individual capture volume and switch controls per input */ 373762306a36Sopenharmony_cistatic int create_multi_cap_vol_ctl(struct hda_codec *codec) 373862306a36Sopenharmony_ci{ 373962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 374062306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 374162306a36Sopenharmony_ci int i, err, type; 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 374462306a36Sopenharmony_ci bool inv_dmic; 374562306a36Sopenharmony_ci int idx; 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci idx = imux->items[i].index; 374862306a36Sopenharmony_ci if (idx >= spec->autocfg.num_inputs) 374962306a36Sopenharmony_ci continue; 375062306a36Sopenharmony_ci inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); 375162306a36Sopenharmony_ci 375262306a36Sopenharmony_ci for (type = 0; type < 2; type++) { 375362306a36Sopenharmony_ci err = add_single_cap_ctl(codec, 375462306a36Sopenharmony_ci spec->input_labels[idx], 375562306a36Sopenharmony_ci spec->input_label_idxs[idx], 375662306a36Sopenharmony_ci type, 375762306a36Sopenharmony_ci get_first_cap_ctl(codec, i, type), 375862306a36Sopenharmony_ci inv_dmic); 375962306a36Sopenharmony_ci if (err < 0) 376062306a36Sopenharmony_ci return err; 376162306a36Sopenharmony_ci } 376262306a36Sopenharmony_ci } 376362306a36Sopenharmony_ci return 0; 376462306a36Sopenharmony_ci} 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_cistatic int create_capture_mixers(struct hda_codec *codec) 376762306a36Sopenharmony_ci{ 376862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 376962306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 377062306a36Sopenharmony_ci int i, n, nums, err; 377162306a36Sopenharmony_ci 377262306a36Sopenharmony_ci if (spec->dyn_adc_switch) 377362306a36Sopenharmony_ci nums = 1; 377462306a36Sopenharmony_ci else 377562306a36Sopenharmony_ci nums = spec->num_adc_nids; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci if (!spec->auto_mic && imux->num_items > 1) { 377862306a36Sopenharmony_ci struct snd_kcontrol_new *knew; 377962306a36Sopenharmony_ci const char *name; 378062306a36Sopenharmony_ci name = nums > 1 ? "Input Source" : "Capture Source"; 378162306a36Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); 378262306a36Sopenharmony_ci if (!knew) 378362306a36Sopenharmony_ci return -ENOMEM; 378462306a36Sopenharmony_ci knew->count = nums; 378562306a36Sopenharmony_ci } 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci for (n = 0; n < nums; n++) { 378862306a36Sopenharmony_ci bool multi = false; 378962306a36Sopenharmony_ci bool multi_cap_vol = spec->multi_cap_vol; 379062306a36Sopenharmony_ci bool inv_dmic = false; 379162306a36Sopenharmony_ci int vol, sw; 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_ci vol = sw = 0; 379462306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 379562306a36Sopenharmony_ci struct nid_path *path; 379662306a36Sopenharmony_ci path = get_input_path(codec, n, i); 379762306a36Sopenharmony_ci if (!path) 379862306a36Sopenharmony_ci continue; 379962306a36Sopenharmony_ci parse_capvol_in_path(codec, path); 380062306a36Sopenharmony_ci if (!vol) 380162306a36Sopenharmony_ci vol = path->ctls[NID_PATH_VOL_CTL]; 380262306a36Sopenharmony_ci else if (vol != path->ctls[NID_PATH_VOL_CTL]) { 380362306a36Sopenharmony_ci multi = true; 380462306a36Sopenharmony_ci if (!same_amp_caps(codec, vol, 380562306a36Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) 380662306a36Sopenharmony_ci multi_cap_vol = true; 380762306a36Sopenharmony_ci } 380862306a36Sopenharmony_ci if (!sw) 380962306a36Sopenharmony_ci sw = path->ctls[NID_PATH_MUTE_CTL]; 381062306a36Sopenharmony_ci else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { 381162306a36Sopenharmony_ci multi = true; 381262306a36Sopenharmony_ci if (!same_amp_caps(codec, sw, 381362306a36Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) 381462306a36Sopenharmony_ci multi_cap_vol = true; 381562306a36Sopenharmony_ci } 381662306a36Sopenharmony_ci if (is_inv_dmic_pin(codec, spec->imux_pins[i])) 381762306a36Sopenharmony_ci inv_dmic = true; 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci if (!multi) 382162306a36Sopenharmony_ci err = create_single_cap_vol_ctl(codec, n, vol, sw, 382262306a36Sopenharmony_ci inv_dmic); 382362306a36Sopenharmony_ci else if (!multi_cap_vol && !inv_dmic) 382462306a36Sopenharmony_ci err = create_bind_cap_vol_ctl(codec, n, vol, sw); 382562306a36Sopenharmony_ci else 382662306a36Sopenharmony_ci err = create_multi_cap_vol_ctl(codec); 382762306a36Sopenharmony_ci if (err < 0) 382862306a36Sopenharmony_ci return err; 382962306a36Sopenharmony_ci } 383062306a36Sopenharmony_ci 383162306a36Sopenharmony_ci return 0; 383262306a36Sopenharmony_ci} 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci/* 383562306a36Sopenharmony_ci * add mic boosts if needed 383662306a36Sopenharmony_ci */ 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci/* check whether the given amp is feasible as a boost volume */ 383962306a36Sopenharmony_cistatic bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, 384062306a36Sopenharmony_ci int dir, int idx) 384162306a36Sopenharmony_ci{ 384262306a36Sopenharmony_ci unsigned int step; 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci if (!nid_has_volume(codec, nid, dir) || 384562306a36Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || 384662306a36Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) 384762306a36Sopenharmony_ci return false; 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) 385062306a36Sopenharmony_ci >> AC_AMPCAP_STEP_SIZE_SHIFT; 385162306a36Sopenharmony_ci if (step < 0x20) 385262306a36Sopenharmony_ci return false; 385362306a36Sopenharmony_ci return true; 385462306a36Sopenharmony_ci} 385562306a36Sopenharmony_ci 385662306a36Sopenharmony_ci/* look for a boost amp in a widget close to the pin */ 385762306a36Sopenharmony_cistatic unsigned int look_for_boost_amp(struct hda_codec *codec, 385862306a36Sopenharmony_ci struct nid_path *path) 385962306a36Sopenharmony_ci{ 386062306a36Sopenharmony_ci unsigned int val = 0; 386162306a36Sopenharmony_ci hda_nid_t nid; 386262306a36Sopenharmony_ci int depth; 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci for (depth = 0; depth < 3; depth++) { 386562306a36Sopenharmony_ci if (depth >= path->depth - 1) 386662306a36Sopenharmony_ci break; 386762306a36Sopenharmony_ci nid = path->path[depth]; 386862306a36Sopenharmony_ci if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { 386962306a36Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 387062306a36Sopenharmony_ci break; 387162306a36Sopenharmony_ci } else if (check_boost_vol(codec, nid, HDA_INPUT, 387262306a36Sopenharmony_ci path->idx[depth])) { 387362306a36Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], 387462306a36Sopenharmony_ci HDA_INPUT); 387562306a36Sopenharmony_ci break; 387662306a36Sopenharmony_ci } 387762306a36Sopenharmony_ci } 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci return val; 388062306a36Sopenharmony_ci} 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_cistatic int parse_mic_boost(struct hda_codec *codec) 388362306a36Sopenharmony_ci{ 388462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 388562306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 388662306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 388762306a36Sopenharmony_ci int i; 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci if (!spec->num_adc_nids) 389062306a36Sopenharmony_ci return 0; 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 389362306a36Sopenharmony_ci struct nid_path *path; 389462306a36Sopenharmony_ci unsigned int val; 389562306a36Sopenharmony_ci int idx; 389662306a36Sopenharmony_ci char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci idx = imux->items[i].index; 389962306a36Sopenharmony_ci if (idx >= imux->num_items) 390062306a36Sopenharmony_ci continue; 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci /* check only line-in and mic pins */ 390362306a36Sopenharmony_ci if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) 390462306a36Sopenharmony_ci continue; 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci path = get_input_path(codec, 0, i); 390762306a36Sopenharmony_ci if (!path) 390862306a36Sopenharmony_ci continue; 390962306a36Sopenharmony_ci 391062306a36Sopenharmony_ci val = look_for_boost_amp(codec, path); 391162306a36Sopenharmony_ci if (!val) 391262306a36Sopenharmony_ci continue; 391362306a36Sopenharmony_ci 391462306a36Sopenharmony_ci /* create a boost control */ 391562306a36Sopenharmony_ci snprintf(boost_label, sizeof(boost_label), 391662306a36Sopenharmony_ci "%s Boost Volume", spec->input_labels[idx]); 391762306a36Sopenharmony_ci if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, 391862306a36Sopenharmony_ci spec->input_label_idxs[idx], val)) 391962306a36Sopenharmony_ci return -ENOMEM; 392062306a36Sopenharmony_ci 392162306a36Sopenharmony_ci path->ctls[NID_PATH_BOOST_CTL] = val; 392262306a36Sopenharmony_ci } 392362306a36Sopenharmony_ci return 0; 392462306a36Sopenharmony_ci} 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_GENERIC_LEDS 392762306a36Sopenharmony_ci/* 392862306a36Sopenharmony_ci * vmaster mute LED hook helpers 392962306a36Sopenharmony_ci */ 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_cistatic int create_mute_led_cdev(struct hda_codec *codec, 393262306a36Sopenharmony_ci int (*callback)(struct led_classdev *, 393362306a36Sopenharmony_ci enum led_brightness), 393462306a36Sopenharmony_ci bool micmute) 393562306a36Sopenharmony_ci{ 393662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 393762306a36Sopenharmony_ci struct led_classdev *cdev; 393862306a36Sopenharmony_ci int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE; 393962306a36Sopenharmony_ci int err; 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); 394262306a36Sopenharmony_ci if (!cdev) 394362306a36Sopenharmony_ci return -ENOMEM; 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_ci cdev->name = micmute ? "hda::micmute" : "hda::mute"; 394662306a36Sopenharmony_ci cdev->max_brightness = 1; 394762306a36Sopenharmony_ci cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; 394862306a36Sopenharmony_ci cdev->brightness_set_blocking = callback; 394962306a36Sopenharmony_ci cdev->brightness = ledtrig_audio_get(idx); 395062306a36Sopenharmony_ci cdev->flags = LED_CORE_SUSPENDRESUME; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci err = led_classdev_register(&codec->core.dev, cdev); 395362306a36Sopenharmony_ci if (err < 0) 395462306a36Sopenharmony_ci return err; 395562306a36Sopenharmony_ci spec->led_cdevs[idx] = cdev; 395662306a36Sopenharmony_ci return 0; 395762306a36Sopenharmony_ci} 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci/** 396062306a36Sopenharmony_ci * snd_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED 396162306a36Sopenharmony_ci * @codec: the HDA codec 396262306a36Sopenharmony_ci * @callback: the callback for LED classdev brightness_set_blocking 396362306a36Sopenharmony_ci */ 396462306a36Sopenharmony_ciint snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, 396562306a36Sopenharmony_ci int (*callback)(struct led_classdev *, 396662306a36Sopenharmony_ci enum led_brightness)) 396762306a36Sopenharmony_ci{ 396862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 396962306a36Sopenharmony_ci int err; 397062306a36Sopenharmony_ci 397162306a36Sopenharmony_ci if (callback) { 397262306a36Sopenharmony_ci err = create_mute_led_cdev(codec, callback, false); 397362306a36Sopenharmony_ci if (err) { 397462306a36Sopenharmony_ci codec_warn(codec, "failed to create a mute LED cdev\n"); 397562306a36Sopenharmony_ci return err; 397662306a36Sopenharmony_ci } 397762306a36Sopenharmony_ci } 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci if (spec->vmaster_mute.hook) 398062306a36Sopenharmony_ci codec_err(codec, "vmaster hook already present before cdev!\n"); 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci spec->vmaster_mute_led = 1; 398362306a36Sopenharmony_ci return 0; 398462306a36Sopenharmony_ci} 398562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev); 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci/** 398862306a36Sopenharmony_ci * snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED 398962306a36Sopenharmony_ci * @codec: the HDA codec 399062306a36Sopenharmony_ci * @callback: the callback for LED classdev brightness_set_blocking 399162306a36Sopenharmony_ci * 399262306a36Sopenharmony_ci * Called from the codec drivers for offering the mic mute LED controls. 399362306a36Sopenharmony_ci * This creates a LED classdev and sets up the cap_sync_hook that is called at 399462306a36Sopenharmony_ci * each time when the capture mixer switch changes. 399562306a36Sopenharmony_ci * 399662306a36Sopenharmony_ci * When NULL is passed to @callback, no classdev is created but only the 399762306a36Sopenharmony_ci * LED-trigger is set up. 399862306a36Sopenharmony_ci * 399962306a36Sopenharmony_ci * Returns 0 or a negative error. 400062306a36Sopenharmony_ci */ 400162306a36Sopenharmony_ciint snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, 400262306a36Sopenharmony_ci int (*callback)(struct led_classdev *, 400362306a36Sopenharmony_ci enum led_brightness)) 400462306a36Sopenharmony_ci{ 400562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 400662306a36Sopenharmony_ci int err; 400762306a36Sopenharmony_ci 400862306a36Sopenharmony_ci if (callback) { 400962306a36Sopenharmony_ci err = create_mute_led_cdev(codec, callback, true); 401062306a36Sopenharmony_ci if (err) { 401162306a36Sopenharmony_ci codec_warn(codec, "failed to create a mic-mute LED cdev\n"); 401262306a36Sopenharmony_ci return err; 401362306a36Sopenharmony_ci } 401462306a36Sopenharmony_ci } 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci spec->mic_mute_led = 1; 401762306a36Sopenharmony_ci return 0; 401862306a36Sopenharmony_ci} 401962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); 402062306a36Sopenharmony_ci#endif /* CONFIG_SND_HDA_GENERIC_LEDS */ 402162306a36Sopenharmony_ci 402262306a36Sopenharmony_ci/* 402362306a36Sopenharmony_ci * parse digital I/Os and set up NIDs in BIOS auto-parse mode 402462306a36Sopenharmony_ci */ 402562306a36Sopenharmony_cistatic void parse_digital(struct hda_codec *codec) 402662306a36Sopenharmony_ci{ 402762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 402862306a36Sopenharmony_ci struct nid_path *path; 402962306a36Sopenharmony_ci int i, nums; 403062306a36Sopenharmony_ci hda_nid_t dig_nid, pin; 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci /* support multiple SPDIFs; the secondary is set up as a follower */ 403362306a36Sopenharmony_ci nums = 0; 403462306a36Sopenharmony_ci for (i = 0; i < spec->autocfg.dig_outs; i++) { 403562306a36Sopenharmony_ci pin = spec->autocfg.dig_out_pins[i]; 403662306a36Sopenharmony_ci dig_nid = look_for_dac(codec, pin, true); 403762306a36Sopenharmony_ci if (!dig_nid) 403862306a36Sopenharmony_ci continue; 403962306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, dig_nid, pin, 0); 404062306a36Sopenharmony_ci if (!path) 404162306a36Sopenharmony_ci continue; 404262306a36Sopenharmony_ci print_nid_path(codec, "digout", path); 404362306a36Sopenharmony_ci path->active = true; 404462306a36Sopenharmony_ci path->pin_fixed = true; /* no jack detection */ 404562306a36Sopenharmony_ci spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); 404662306a36Sopenharmony_ci set_pin_target(codec, pin, PIN_OUT, false); 404762306a36Sopenharmony_ci if (!nums) { 404862306a36Sopenharmony_ci spec->multiout.dig_out_nid = dig_nid; 404962306a36Sopenharmony_ci spec->dig_out_type = spec->autocfg.dig_out_type[0]; 405062306a36Sopenharmony_ci } else { 405162306a36Sopenharmony_ci spec->multiout.follower_dig_outs = spec->follower_dig_outs; 405262306a36Sopenharmony_ci if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1) 405362306a36Sopenharmony_ci break; 405462306a36Sopenharmony_ci spec->follower_dig_outs[nums - 1] = dig_nid; 405562306a36Sopenharmony_ci } 405662306a36Sopenharmony_ci nums++; 405762306a36Sopenharmony_ci } 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci if (spec->autocfg.dig_in_pin) { 406062306a36Sopenharmony_ci pin = spec->autocfg.dig_in_pin; 406162306a36Sopenharmony_ci for_each_hda_codec_node(dig_nid, codec) { 406262306a36Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, dig_nid); 406362306a36Sopenharmony_ci if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 406462306a36Sopenharmony_ci continue; 406562306a36Sopenharmony_ci if (!(wcaps & AC_WCAP_DIGITAL)) 406662306a36Sopenharmony_ci continue; 406762306a36Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, dig_nid, 0); 406862306a36Sopenharmony_ci if (path) { 406962306a36Sopenharmony_ci print_nid_path(codec, "digin", path); 407062306a36Sopenharmony_ci path->active = true; 407162306a36Sopenharmony_ci path->pin_fixed = true; /* no jack */ 407262306a36Sopenharmony_ci spec->dig_in_nid = dig_nid; 407362306a36Sopenharmony_ci spec->digin_path = snd_hda_get_path_idx(codec, path); 407462306a36Sopenharmony_ci set_pin_target(codec, pin, PIN_IN, false); 407562306a36Sopenharmony_ci break; 407662306a36Sopenharmony_ci } 407762306a36Sopenharmony_ci } 407862306a36Sopenharmony_ci } 407962306a36Sopenharmony_ci} 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci/* 408362306a36Sopenharmony_ci * input MUX handling 408462306a36Sopenharmony_ci */ 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_cistatic bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_ci/* select the given imux item; either unmute exclusively or select the route */ 408962306a36Sopenharmony_cistatic int mux_select(struct hda_codec *codec, unsigned int adc_idx, 409062306a36Sopenharmony_ci unsigned int idx) 409162306a36Sopenharmony_ci{ 409262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 409362306a36Sopenharmony_ci const struct hda_input_mux *imux; 409462306a36Sopenharmony_ci struct nid_path *old_path, *path; 409562306a36Sopenharmony_ci 409662306a36Sopenharmony_ci imux = &spec->input_mux; 409762306a36Sopenharmony_ci if (!imux->num_items) 409862306a36Sopenharmony_ci return 0; 409962306a36Sopenharmony_ci 410062306a36Sopenharmony_ci if (idx >= imux->num_items) 410162306a36Sopenharmony_ci idx = imux->num_items - 1; 410262306a36Sopenharmony_ci if (spec->cur_mux[adc_idx] == idx) 410362306a36Sopenharmony_ci return 0; 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); 410662306a36Sopenharmony_ci if (!old_path) 410762306a36Sopenharmony_ci return 0; 410862306a36Sopenharmony_ci if (old_path->active) 410962306a36Sopenharmony_ci snd_hda_activate_path(codec, old_path, false, false); 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_ci spec->cur_mux[adc_idx] = idx; 411262306a36Sopenharmony_ci 411362306a36Sopenharmony_ci if (spec->hp_mic) 411462306a36Sopenharmony_ci update_hp_mic(codec, adc_idx, false); 411562306a36Sopenharmony_ci 411662306a36Sopenharmony_ci if (spec->dyn_adc_switch) 411762306a36Sopenharmony_ci dyn_adc_pcm_resetup(codec, idx); 411862306a36Sopenharmony_ci 411962306a36Sopenharmony_ci path = get_input_path(codec, adc_idx, idx); 412062306a36Sopenharmony_ci if (!path) 412162306a36Sopenharmony_ci return 0; 412262306a36Sopenharmony_ci if (path->active) 412362306a36Sopenharmony_ci return 0; 412462306a36Sopenharmony_ci snd_hda_activate_path(codec, path, true, false); 412562306a36Sopenharmony_ci if (spec->cap_sync_hook) 412662306a36Sopenharmony_ci spec->cap_sync_hook(codec, NULL, NULL); 412762306a36Sopenharmony_ci path_power_down_sync(codec, old_path); 412862306a36Sopenharmony_ci return 1; 412962306a36Sopenharmony_ci} 413062306a36Sopenharmony_ci 413162306a36Sopenharmony_ci/* power up/down widgets in the all paths that match with the given NID 413262306a36Sopenharmony_ci * as terminals (either start- or endpoint) 413362306a36Sopenharmony_ci * 413462306a36Sopenharmony_ci * returns the last changed NID, or zero if unchanged. 413562306a36Sopenharmony_ci */ 413662306a36Sopenharmony_cistatic hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, 413762306a36Sopenharmony_ci int pin_state, int stream_state) 413862306a36Sopenharmony_ci{ 413962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 414062306a36Sopenharmony_ci hda_nid_t last, changed = 0; 414162306a36Sopenharmony_ci struct nid_path *path; 414262306a36Sopenharmony_ci int n; 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ci snd_array_for_each(&spec->paths, n, path) { 414562306a36Sopenharmony_ci if (!path->depth) 414662306a36Sopenharmony_ci continue; 414762306a36Sopenharmony_ci if (path->path[0] == nid || 414862306a36Sopenharmony_ci path->path[path->depth - 1] == nid) { 414962306a36Sopenharmony_ci bool pin_old = path->pin_enabled; 415062306a36Sopenharmony_ci bool stream_old = path->stream_enabled; 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci if (pin_state >= 0) 415362306a36Sopenharmony_ci path->pin_enabled = pin_state; 415462306a36Sopenharmony_ci if (stream_state >= 0) 415562306a36Sopenharmony_ci path->stream_enabled = stream_state; 415662306a36Sopenharmony_ci if ((!path->pin_fixed && path->pin_enabled != pin_old) 415762306a36Sopenharmony_ci || path->stream_enabled != stream_old) { 415862306a36Sopenharmony_ci last = path_power_update(codec, path, true); 415962306a36Sopenharmony_ci if (last) 416062306a36Sopenharmony_ci changed = last; 416162306a36Sopenharmony_ci } 416262306a36Sopenharmony_ci } 416362306a36Sopenharmony_ci } 416462306a36Sopenharmony_ci return changed; 416562306a36Sopenharmony_ci} 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_ci/* check the jack status for power control */ 416862306a36Sopenharmony_cistatic bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) 416962306a36Sopenharmony_ci{ 417062306a36Sopenharmony_ci if (!is_jack_detectable(codec, pin)) 417162306a36Sopenharmony_ci return true; 417262306a36Sopenharmony_ci return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; 417362306a36Sopenharmony_ci} 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_ci/* power up/down the paths of the given pin according to the jack state; 417662306a36Sopenharmony_ci * power = 0/1 : only power up/down if it matches with the jack state, 417762306a36Sopenharmony_ci * < 0 : force power up/down to follow the jack sate 417862306a36Sopenharmony_ci * 417962306a36Sopenharmony_ci * returns the last changed NID, or zero if unchanged. 418062306a36Sopenharmony_ci */ 418162306a36Sopenharmony_cistatic hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, 418262306a36Sopenharmony_ci int power) 418362306a36Sopenharmony_ci{ 418462306a36Sopenharmony_ci bool on; 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci if (!codec->power_save_node) 418762306a36Sopenharmony_ci return 0; 418862306a36Sopenharmony_ci 418962306a36Sopenharmony_ci on = detect_pin_state(codec, pin); 419062306a36Sopenharmony_ci 419162306a36Sopenharmony_ci if (power >= 0 && on != power) 419262306a36Sopenharmony_ci return 0; 419362306a36Sopenharmony_ci return set_path_power(codec, pin, on, -1); 419462306a36Sopenharmony_ci} 419562306a36Sopenharmony_ci 419662306a36Sopenharmony_cistatic void pin_power_callback(struct hda_codec *codec, 419762306a36Sopenharmony_ci struct hda_jack_callback *jack, 419862306a36Sopenharmony_ci bool on) 419962306a36Sopenharmony_ci{ 420062306a36Sopenharmony_ci if (jack && jack->nid) 420162306a36Sopenharmony_ci sync_power_state_change(codec, 420262306a36Sopenharmony_ci set_pin_power_jack(codec, jack->nid, on)); 420362306a36Sopenharmony_ci} 420462306a36Sopenharmony_ci 420562306a36Sopenharmony_ci/* callback only doing power up -- called at first */ 420662306a36Sopenharmony_cistatic void pin_power_up_callback(struct hda_codec *codec, 420762306a36Sopenharmony_ci struct hda_jack_callback *jack) 420862306a36Sopenharmony_ci{ 420962306a36Sopenharmony_ci pin_power_callback(codec, jack, true); 421062306a36Sopenharmony_ci} 421162306a36Sopenharmony_ci 421262306a36Sopenharmony_ci/* callback only doing power down -- called at last */ 421362306a36Sopenharmony_cistatic void pin_power_down_callback(struct hda_codec *codec, 421462306a36Sopenharmony_ci struct hda_jack_callback *jack) 421562306a36Sopenharmony_ci{ 421662306a36Sopenharmony_ci pin_power_callback(codec, jack, false); 421762306a36Sopenharmony_ci} 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci/* set up the power up/down callbacks */ 422062306a36Sopenharmony_cistatic void add_pin_power_ctls(struct hda_codec *codec, int num_pins, 422162306a36Sopenharmony_ci const hda_nid_t *pins, bool on) 422262306a36Sopenharmony_ci{ 422362306a36Sopenharmony_ci int i; 422462306a36Sopenharmony_ci hda_jack_callback_fn cb = 422562306a36Sopenharmony_ci on ? pin_power_up_callback : pin_power_down_callback; 422662306a36Sopenharmony_ci 422762306a36Sopenharmony_ci for (i = 0; i < num_pins && pins[i]; i++) { 422862306a36Sopenharmony_ci if (is_jack_detectable(codec, pins[i])) 422962306a36Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, pins[i], cb); 423062306a36Sopenharmony_ci else 423162306a36Sopenharmony_ci set_path_power(codec, pins[i], true, -1); 423262306a36Sopenharmony_ci } 423362306a36Sopenharmony_ci} 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci/* enabled power callback to each available I/O pin with jack detections; 423662306a36Sopenharmony_ci * the digital I/O pins are excluded because of the unreliable detectsion 423762306a36Sopenharmony_ci */ 423862306a36Sopenharmony_cistatic void add_all_pin_power_ctls(struct hda_codec *codec, bool on) 423962306a36Sopenharmony_ci{ 424062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 424162306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 424262306a36Sopenharmony_ci int i; 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci if (!codec->power_save_node) 424562306a36Sopenharmony_ci return; 424662306a36Sopenharmony_ci add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); 424762306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 424862306a36Sopenharmony_ci add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); 424962306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 425062306a36Sopenharmony_ci add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); 425162306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) 425262306a36Sopenharmony_ci add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); 425362306a36Sopenharmony_ci} 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_ci/* sync path power up/down with the jack states of given pins */ 425662306a36Sopenharmony_cistatic void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, 425762306a36Sopenharmony_ci const hda_nid_t *pins) 425862306a36Sopenharmony_ci{ 425962306a36Sopenharmony_ci int i; 426062306a36Sopenharmony_ci 426162306a36Sopenharmony_ci for (i = 0; i < num_pins && pins[i]; i++) 426262306a36Sopenharmony_ci if (is_jack_detectable(codec, pins[i])) 426362306a36Sopenharmony_ci set_pin_power_jack(codec, pins[i], -1); 426462306a36Sopenharmony_ci} 426562306a36Sopenharmony_ci 426662306a36Sopenharmony_ci/* sync path power up/down with pins; called at init and resume */ 426762306a36Sopenharmony_cistatic void sync_all_pin_power_ctls(struct hda_codec *codec) 426862306a36Sopenharmony_ci{ 426962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 427062306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 427162306a36Sopenharmony_ci int i; 427262306a36Sopenharmony_ci 427362306a36Sopenharmony_ci if (!codec->power_save_node) 427462306a36Sopenharmony_ci return; 427562306a36Sopenharmony_ci sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); 427662306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 427762306a36Sopenharmony_ci sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); 427862306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 427962306a36Sopenharmony_ci sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); 428062306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) 428162306a36Sopenharmony_ci sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); 428262306a36Sopenharmony_ci} 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci/* add fake paths if not present yet */ 428562306a36Sopenharmony_cistatic int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, 428662306a36Sopenharmony_ci int num_pins, const hda_nid_t *pins) 428762306a36Sopenharmony_ci{ 428862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 428962306a36Sopenharmony_ci struct nid_path *path; 429062306a36Sopenharmony_ci int i; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 429362306a36Sopenharmony_ci if (!pins[i]) 429462306a36Sopenharmony_ci break; 429562306a36Sopenharmony_ci if (get_nid_path(codec, nid, pins[i], 0)) 429662306a36Sopenharmony_ci continue; 429762306a36Sopenharmony_ci path = snd_array_new(&spec->paths); 429862306a36Sopenharmony_ci if (!path) 429962306a36Sopenharmony_ci return -ENOMEM; 430062306a36Sopenharmony_ci memset(path, 0, sizeof(*path)); 430162306a36Sopenharmony_ci path->depth = 2; 430262306a36Sopenharmony_ci path->path[0] = nid; 430362306a36Sopenharmony_ci path->path[1] = pins[i]; 430462306a36Sopenharmony_ci path->active = true; 430562306a36Sopenharmony_ci } 430662306a36Sopenharmony_ci return 0; 430762306a36Sopenharmony_ci} 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci/* create fake paths to all outputs from beep */ 431062306a36Sopenharmony_cistatic int add_fake_beep_paths(struct hda_codec *codec) 431162306a36Sopenharmony_ci{ 431262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 431362306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 431462306a36Sopenharmony_ci hda_nid_t nid = spec->beep_nid; 431562306a36Sopenharmony_ci int err; 431662306a36Sopenharmony_ci 431762306a36Sopenharmony_ci if (!codec->power_save_node || !nid) 431862306a36Sopenharmony_ci return 0; 431962306a36Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); 432062306a36Sopenharmony_ci if (err < 0) 432162306a36Sopenharmony_ci return err; 432262306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 432362306a36Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); 432462306a36Sopenharmony_ci if (err < 0) 432562306a36Sopenharmony_ci return err; 432662306a36Sopenharmony_ci } 432762306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 432862306a36Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->speaker_outs, 432962306a36Sopenharmony_ci cfg->speaker_pins); 433062306a36Sopenharmony_ci if (err < 0) 433162306a36Sopenharmony_ci return err; 433262306a36Sopenharmony_ci } 433362306a36Sopenharmony_ci return 0; 433462306a36Sopenharmony_ci} 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci/* power up/down beep widget and its output paths */ 433762306a36Sopenharmony_cistatic void beep_power_hook(struct hda_beep *beep, bool on) 433862306a36Sopenharmony_ci{ 433962306a36Sopenharmony_ci set_path_power(beep->codec, beep->nid, -1, on); 434062306a36Sopenharmony_ci} 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci/** 434362306a36Sopenharmony_ci * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 434462306a36Sopenharmony_ci * @codec: the HDA codec 434562306a36Sopenharmony_ci * @pin: NID of pin to fix 434662306a36Sopenharmony_ci */ 434762306a36Sopenharmony_ciint snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) 434862306a36Sopenharmony_ci{ 434962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 435062306a36Sopenharmony_ci struct nid_path *path; 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci path = snd_array_new(&spec->paths); 435362306a36Sopenharmony_ci if (!path) 435462306a36Sopenharmony_ci return -ENOMEM; 435562306a36Sopenharmony_ci memset(path, 0, sizeof(*path)); 435662306a36Sopenharmony_ci path->depth = 1; 435762306a36Sopenharmony_ci path->path[0] = pin; 435862306a36Sopenharmony_ci path->active = true; 435962306a36Sopenharmony_ci path->pin_fixed = true; 436062306a36Sopenharmony_ci path->stream_enabled = true; 436162306a36Sopenharmony_ci return 0; 436262306a36Sopenharmony_ci} 436362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); 436462306a36Sopenharmony_ci 436562306a36Sopenharmony_ci/* 436662306a36Sopenharmony_ci * Jack detections for HP auto-mute and mic-switch 436762306a36Sopenharmony_ci */ 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci/* check each pin in the given array; returns true if any of them is plugged */ 437062306a36Sopenharmony_cistatic bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins) 437162306a36Sopenharmony_ci{ 437262306a36Sopenharmony_ci int i; 437362306a36Sopenharmony_ci bool present = false; 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 437662306a36Sopenharmony_ci hda_nid_t nid = pins[i]; 437762306a36Sopenharmony_ci if (!nid) 437862306a36Sopenharmony_ci break; 437962306a36Sopenharmony_ci /* don't detect pins retasked as inputs */ 438062306a36Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) 438162306a36Sopenharmony_ci continue; 438262306a36Sopenharmony_ci if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) 438362306a36Sopenharmony_ci present = true; 438462306a36Sopenharmony_ci } 438562306a36Sopenharmony_ci return present; 438662306a36Sopenharmony_ci} 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci/* standard HP/line-out auto-mute helper */ 438962306a36Sopenharmony_cistatic void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, 439062306a36Sopenharmony_ci int *paths, bool mute) 439162306a36Sopenharmony_ci{ 439262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 439362306a36Sopenharmony_ci int i; 439462306a36Sopenharmony_ci 439562306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 439662306a36Sopenharmony_ci hda_nid_t nid = pins[i]; 439762306a36Sopenharmony_ci unsigned int val, oldval; 439862306a36Sopenharmony_ci if (!nid) 439962306a36Sopenharmony_ci break; 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci oldval = snd_hda_codec_get_pin_target(codec, nid); 440262306a36Sopenharmony_ci if (oldval & PIN_IN) 440362306a36Sopenharmony_ci continue; /* no mute for inputs */ 440462306a36Sopenharmony_ci 440562306a36Sopenharmony_ci if (spec->auto_mute_via_amp) { 440662306a36Sopenharmony_ci struct nid_path *path; 440762306a36Sopenharmony_ci hda_nid_t mute_nid; 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, paths[i]); 441062306a36Sopenharmony_ci if (!path) 441162306a36Sopenharmony_ci continue; 441262306a36Sopenharmony_ci mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); 441362306a36Sopenharmony_ci if (!mute_nid) 441462306a36Sopenharmony_ci continue; 441562306a36Sopenharmony_ci if (mute) 441662306a36Sopenharmony_ci spec->mute_bits |= (1ULL << mute_nid); 441762306a36Sopenharmony_ci else 441862306a36Sopenharmony_ci spec->mute_bits &= ~(1ULL << mute_nid); 441962306a36Sopenharmony_ci continue; 442062306a36Sopenharmony_ci } else { 442162306a36Sopenharmony_ci /* don't reset VREF value in case it's controlling 442262306a36Sopenharmony_ci * the amp (see alc861_fixup_asus_amp_vref_0f()) 442362306a36Sopenharmony_ci */ 442462306a36Sopenharmony_ci if (spec->keep_vref_in_automute) 442562306a36Sopenharmony_ci val = oldval & ~PIN_HP; 442662306a36Sopenharmony_ci else 442762306a36Sopenharmony_ci val = 0; 442862306a36Sopenharmony_ci if (!mute) 442962306a36Sopenharmony_ci val |= oldval; 443062306a36Sopenharmony_ci /* here we call update_pin_ctl() so that the pinctl is 443162306a36Sopenharmony_ci * changed without changing the pinctl target value; 443262306a36Sopenharmony_ci * the original target value will be still referred at 443362306a36Sopenharmony_ci * the init / resume again 443462306a36Sopenharmony_ci */ 443562306a36Sopenharmony_ci update_pin_ctl(codec, nid, val); 443662306a36Sopenharmony_ci } 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_ci set_pin_eapd(codec, nid, !mute); 443962306a36Sopenharmony_ci if (codec->power_save_node) { 444062306a36Sopenharmony_ci bool on = !mute; 444162306a36Sopenharmony_ci if (on) 444262306a36Sopenharmony_ci on = detect_pin_state(codec, nid); 444362306a36Sopenharmony_ci set_path_power(codec, nid, on, -1); 444462306a36Sopenharmony_ci } 444562306a36Sopenharmony_ci } 444662306a36Sopenharmony_ci} 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_ci/** 444962306a36Sopenharmony_ci * snd_hda_gen_update_outputs - Toggle outputs muting 445062306a36Sopenharmony_ci * @codec: the HDA codec 445162306a36Sopenharmony_ci * 445262306a36Sopenharmony_ci * Update the mute status of all outputs based on the current jack states. 445362306a36Sopenharmony_ci */ 445462306a36Sopenharmony_civoid snd_hda_gen_update_outputs(struct hda_codec *codec) 445562306a36Sopenharmony_ci{ 445662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 445762306a36Sopenharmony_ci int *paths; 445862306a36Sopenharmony_ci int on; 445962306a36Sopenharmony_ci 446062306a36Sopenharmony_ci /* Control HP pins/amps depending on master_mute state; 446162306a36Sopenharmony_ci * in general, HP pins/amps control should be enabled in all cases, 446262306a36Sopenharmony_ci * but currently set only for master_mute, just to be safe 446362306a36Sopenharmony_ci */ 446462306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 446562306a36Sopenharmony_ci paths = spec->out_paths; 446662306a36Sopenharmony_ci else 446762306a36Sopenharmony_ci paths = spec->hp_paths; 446862306a36Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), 446962306a36Sopenharmony_ci spec->autocfg.hp_pins, paths, spec->master_mute); 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci if (!spec->automute_speaker) 447262306a36Sopenharmony_ci on = 0; 447362306a36Sopenharmony_ci else 447462306a36Sopenharmony_ci on = spec->hp_jack_present | spec->line_jack_present; 447562306a36Sopenharmony_ci on |= spec->master_mute; 447662306a36Sopenharmony_ci spec->speaker_muted = on; 447762306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 447862306a36Sopenharmony_ci paths = spec->out_paths; 447962306a36Sopenharmony_ci else 448062306a36Sopenharmony_ci paths = spec->speaker_paths; 448162306a36Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), 448262306a36Sopenharmony_ci spec->autocfg.speaker_pins, paths, on); 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci /* toggle line-out mutes if needed, too */ 448562306a36Sopenharmony_ci /* if LO is a copy of either HP or Speaker, don't need to handle it */ 448662306a36Sopenharmony_ci if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || 448762306a36Sopenharmony_ci spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) 448862306a36Sopenharmony_ci return; 448962306a36Sopenharmony_ci if (!spec->automute_lo) 449062306a36Sopenharmony_ci on = 0; 449162306a36Sopenharmony_ci else 449262306a36Sopenharmony_ci on = spec->hp_jack_present; 449362306a36Sopenharmony_ci on |= spec->master_mute; 449462306a36Sopenharmony_ci spec->line_out_muted = on; 449562306a36Sopenharmony_ci paths = spec->out_paths; 449662306a36Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 449762306a36Sopenharmony_ci spec->autocfg.line_out_pins, paths, on); 449862306a36Sopenharmony_ci} 449962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs); 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_cistatic void call_update_outputs(struct hda_codec *codec) 450262306a36Sopenharmony_ci{ 450362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 450462306a36Sopenharmony_ci if (spec->automute_hook) 450562306a36Sopenharmony_ci spec->automute_hook(codec); 450662306a36Sopenharmony_ci else 450762306a36Sopenharmony_ci snd_hda_gen_update_outputs(codec); 450862306a36Sopenharmony_ci 450962306a36Sopenharmony_ci /* sync the whole vmaster followers to reflect the new auto-mute status */ 451062306a36Sopenharmony_ci if (spec->auto_mute_via_amp && !codec->bus->shutdown) 451162306a36Sopenharmony_ci snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); 451262306a36Sopenharmony_ci} 451362306a36Sopenharmony_ci 451462306a36Sopenharmony_ci/** 451562306a36Sopenharmony_ci * snd_hda_gen_hp_automute - standard HP-automute helper 451662306a36Sopenharmony_ci * @codec: the HDA codec 451762306a36Sopenharmony_ci * @jack: jack object, NULL for the whole 451862306a36Sopenharmony_ci */ 451962306a36Sopenharmony_civoid snd_hda_gen_hp_automute(struct hda_codec *codec, 452062306a36Sopenharmony_ci struct hda_jack_callback *jack) 452162306a36Sopenharmony_ci{ 452262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 452362306a36Sopenharmony_ci hda_nid_t *pins = spec->autocfg.hp_pins; 452462306a36Sopenharmony_ci int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); 452562306a36Sopenharmony_ci 452662306a36Sopenharmony_ci /* No detection for the first HP jack during indep-HP mode */ 452762306a36Sopenharmony_ci if (spec->indep_hp_enabled) { 452862306a36Sopenharmony_ci pins++; 452962306a36Sopenharmony_ci num_pins--; 453062306a36Sopenharmony_ci } 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci spec->hp_jack_present = detect_jacks(codec, num_pins, pins); 453362306a36Sopenharmony_ci if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) 453462306a36Sopenharmony_ci return; 453562306a36Sopenharmony_ci call_update_outputs(codec); 453662306a36Sopenharmony_ci} 453762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci/** 454062306a36Sopenharmony_ci * snd_hda_gen_line_automute - standard line-out-automute helper 454162306a36Sopenharmony_ci * @codec: the HDA codec 454262306a36Sopenharmony_ci * @jack: jack object, NULL for the whole 454362306a36Sopenharmony_ci */ 454462306a36Sopenharmony_civoid snd_hda_gen_line_automute(struct hda_codec *codec, 454562306a36Sopenharmony_ci struct hda_jack_callback *jack) 454662306a36Sopenharmony_ci{ 454762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 454862306a36Sopenharmony_ci 454962306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 455062306a36Sopenharmony_ci return; 455162306a36Sopenharmony_ci /* check LO jack only when it's different from HP */ 455262306a36Sopenharmony_ci if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) 455362306a36Sopenharmony_ci return; 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_ci spec->line_jack_present = 455662306a36Sopenharmony_ci detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 455762306a36Sopenharmony_ci spec->autocfg.line_out_pins); 455862306a36Sopenharmony_ci if (!spec->automute_speaker || !spec->detect_lo) 455962306a36Sopenharmony_ci return; 456062306a36Sopenharmony_ci call_update_outputs(codec); 456162306a36Sopenharmony_ci} 456262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci/** 456562306a36Sopenharmony_ci * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper 456662306a36Sopenharmony_ci * @codec: the HDA codec 456762306a36Sopenharmony_ci * @jack: jack object, NULL for the whole 456862306a36Sopenharmony_ci */ 456962306a36Sopenharmony_civoid snd_hda_gen_mic_autoswitch(struct hda_codec *codec, 457062306a36Sopenharmony_ci struct hda_jack_callback *jack) 457162306a36Sopenharmony_ci{ 457262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 457362306a36Sopenharmony_ci int i; 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_ci if (!spec->auto_mic) 457662306a36Sopenharmony_ci return; 457762306a36Sopenharmony_ci 457862306a36Sopenharmony_ci for (i = spec->am_num_entries - 1; i > 0; i--) { 457962306a36Sopenharmony_ci hda_nid_t pin = spec->am_entry[i].pin; 458062306a36Sopenharmony_ci /* don't detect pins retasked as outputs */ 458162306a36Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) 458262306a36Sopenharmony_ci continue; 458362306a36Sopenharmony_ci if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { 458462306a36Sopenharmony_ci mux_select(codec, 0, spec->am_entry[i].idx); 458562306a36Sopenharmony_ci return; 458662306a36Sopenharmony_ci } 458762306a36Sopenharmony_ci } 458862306a36Sopenharmony_ci mux_select(codec, 0, spec->am_entry[0].idx); 458962306a36Sopenharmony_ci} 459062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_ci/* call appropriate hooks */ 459362306a36Sopenharmony_cistatic void call_hp_automute(struct hda_codec *codec, 459462306a36Sopenharmony_ci struct hda_jack_callback *jack) 459562306a36Sopenharmony_ci{ 459662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 459762306a36Sopenharmony_ci if (spec->hp_automute_hook) 459862306a36Sopenharmony_ci spec->hp_automute_hook(codec, jack); 459962306a36Sopenharmony_ci else 460062306a36Sopenharmony_ci snd_hda_gen_hp_automute(codec, jack); 460162306a36Sopenharmony_ci} 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_cistatic void call_line_automute(struct hda_codec *codec, 460462306a36Sopenharmony_ci struct hda_jack_callback *jack) 460562306a36Sopenharmony_ci{ 460662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 460762306a36Sopenharmony_ci if (spec->line_automute_hook) 460862306a36Sopenharmony_ci spec->line_automute_hook(codec, jack); 460962306a36Sopenharmony_ci else 461062306a36Sopenharmony_ci snd_hda_gen_line_automute(codec, jack); 461162306a36Sopenharmony_ci} 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_cistatic void call_mic_autoswitch(struct hda_codec *codec, 461462306a36Sopenharmony_ci struct hda_jack_callback *jack) 461562306a36Sopenharmony_ci{ 461662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 461762306a36Sopenharmony_ci if (spec->mic_autoswitch_hook) 461862306a36Sopenharmony_ci spec->mic_autoswitch_hook(codec, jack); 461962306a36Sopenharmony_ci else 462062306a36Sopenharmony_ci snd_hda_gen_mic_autoswitch(codec, jack); 462162306a36Sopenharmony_ci} 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_ci/* update jack retasking */ 462462306a36Sopenharmony_cistatic void update_automute_all(struct hda_codec *codec) 462562306a36Sopenharmony_ci{ 462662306a36Sopenharmony_ci call_hp_automute(codec, NULL); 462762306a36Sopenharmony_ci call_line_automute(codec, NULL); 462862306a36Sopenharmony_ci call_mic_autoswitch(codec, NULL); 462962306a36Sopenharmony_ci} 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci/* 463262306a36Sopenharmony_ci * Auto-Mute mode mixer enum support 463362306a36Sopenharmony_ci */ 463462306a36Sopenharmony_cistatic int automute_mode_info(struct snd_kcontrol *kcontrol, 463562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 463662306a36Sopenharmony_ci{ 463762306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 463862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 463962306a36Sopenharmony_ci static const char * const texts3[] = { 464062306a36Sopenharmony_ci "Disabled", "Speaker Only", "Line Out+Speaker" 464162306a36Sopenharmony_ci }; 464262306a36Sopenharmony_ci 464362306a36Sopenharmony_ci if (spec->automute_speaker_possible && spec->automute_lo_possible) 464462306a36Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); 464562306a36Sopenharmony_ci return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 464662306a36Sopenharmony_ci} 464762306a36Sopenharmony_ci 464862306a36Sopenharmony_cistatic int automute_mode_get(struct snd_kcontrol *kcontrol, 464962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 465062306a36Sopenharmony_ci{ 465162306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 465262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 465362306a36Sopenharmony_ci unsigned int val = 0; 465462306a36Sopenharmony_ci if (spec->automute_speaker) 465562306a36Sopenharmony_ci val++; 465662306a36Sopenharmony_ci if (spec->automute_lo) 465762306a36Sopenharmony_ci val++; 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = val; 466062306a36Sopenharmony_ci return 0; 466162306a36Sopenharmony_ci} 466262306a36Sopenharmony_ci 466362306a36Sopenharmony_cistatic int automute_mode_put(struct snd_kcontrol *kcontrol, 466462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 466562306a36Sopenharmony_ci{ 466662306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 466762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci switch (ucontrol->value.enumerated.item[0]) { 467062306a36Sopenharmony_ci case 0: 467162306a36Sopenharmony_ci if (!spec->automute_speaker && !spec->automute_lo) 467262306a36Sopenharmony_ci return 0; 467362306a36Sopenharmony_ci spec->automute_speaker = 0; 467462306a36Sopenharmony_ci spec->automute_lo = 0; 467562306a36Sopenharmony_ci break; 467662306a36Sopenharmony_ci case 1: 467762306a36Sopenharmony_ci if (spec->automute_speaker_possible) { 467862306a36Sopenharmony_ci if (!spec->automute_lo && spec->automute_speaker) 467962306a36Sopenharmony_ci return 0; 468062306a36Sopenharmony_ci spec->automute_speaker = 1; 468162306a36Sopenharmony_ci spec->automute_lo = 0; 468262306a36Sopenharmony_ci } else if (spec->automute_lo_possible) { 468362306a36Sopenharmony_ci if (spec->automute_lo) 468462306a36Sopenharmony_ci return 0; 468562306a36Sopenharmony_ci spec->automute_lo = 1; 468662306a36Sopenharmony_ci } else 468762306a36Sopenharmony_ci return -EINVAL; 468862306a36Sopenharmony_ci break; 468962306a36Sopenharmony_ci case 2: 469062306a36Sopenharmony_ci if (!spec->automute_lo_possible || !spec->automute_speaker_possible) 469162306a36Sopenharmony_ci return -EINVAL; 469262306a36Sopenharmony_ci if (spec->automute_speaker && spec->automute_lo) 469362306a36Sopenharmony_ci return 0; 469462306a36Sopenharmony_ci spec->automute_speaker = 1; 469562306a36Sopenharmony_ci spec->automute_lo = 1; 469662306a36Sopenharmony_ci break; 469762306a36Sopenharmony_ci default: 469862306a36Sopenharmony_ci return -EINVAL; 469962306a36Sopenharmony_ci } 470062306a36Sopenharmony_ci call_update_outputs(codec); 470162306a36Sopenharmony_ci return 1; 470262306a36Sopenharmony_ci} 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_cistatic const struct snd_kcontrol_new automute_mode_enum = { 470562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 470662306a36Sopenharmony_ci .name = "Auto-Mute Mode", 470762306a36Sopenharmony_ci .info = automute_mode_info, 470862306a36Sopenharmony_ci .get = automute_mode_get, 470962306a36Sopenharmony_ci .put = automute_mode_put, 471062306a36Sopenharmony_ci}; 471162306a36Sopenharmony_ci 471262306a36Sopenharmony_cistatic int add_automute_mode_enum(struct hda_codec *codec) 471362306a36Sopenharmony_ci{ 471462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) 471762306a36Sopenharmony_ci return -ENOMEM; 471862306a36Sopenharmony_ci return 0; 471962306a36Sopenharmony_ci} 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci/* 472262306a36Sopenharmony_ci * Check the availability of HP/line-out auto-mute; 472362306a36Sopenharmony_ci * Set up appropriately if really supported 472462306a36Sopenharmony_ci */ 472562306a36Sopenharmony_cistatic int check_auto_mute_availability(struct hda_codec *codec) 472662306a36Sopenharmony_ci{ 472762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 472862306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 472962306a36Sopenharmony_ci int present = 0; 473062306a36Sopenharmony_ci int i, err; 473162306a36Sopenharmony_ci 473262306a36Sopenharmony_ci if (spec->suppress_auto_mute) 473362306a36Sopenharmony_ci return 0; 473462306a36Sopenharmony_ci 473562306a36Sopenharmony_ci if (cfg->hp_pins[0]) 473662306a36Sopenharmony_ci present++; 473762306a36Sopenharmony_ci if (cfg->line_out_pins[0]) 473862306a36Sopenharmony_ci present++; 473962306a36Sopenharmony_ci if (cfg->speaker_pins[0]) 474062306a36Sopenharmony_ci present++; 474162306a36Sopenharmony_ci if (present < 2) /* need two different output types */ 474262306a36Sopenharmony_ci return 0; 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci if (!cfg->speaker_pins[0] && 474562306a36Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 474662306a36Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 474762306a36Sopenharmony_ci sizeof(cfg->speaker_pins)); 474862306a36Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 474962306a36Sopenharmony_ci } 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_ci if (!cfg->hp_pins[0] && 475262306a36Sopenharmony_ci cfg->line_out_type == AUTO_PIN_HP_OUT) { 475362306a36Sopenharmony_ci memcpy(cfg->hp_pins, cfg->line_out_pins, 475462306a36Sopenharmony_ci sizeof(cfg->hp_pins)); 475562306a36Sopenharmony_ci cfg->hp_outs = cfg->line_outs; 475662306a36Sopenharmony_ci } 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci for (i = 0; i < cfg->hp_outs; i++) { 475962306a36Sopenharmony_ci hda_nid_t nid = cfg->hp_pins[i]; 476062306a36Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 476162306a36Sopenharmony_ci continue; 476262306a36Sopenharmony_ci codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid); 476362306a36Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, nid, 476462306a36Sopenharmony_ci call_hp_automute); 476562306a36Sopenharmony_ci spec->detect_hp = 1; 476662306a36Sopenharmony_ci } 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { 476962306a36Sopenharmony_ci if (cfg->speaker_outs) 477062306a36Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 477162306a36Sopenharmony_ci hda_nid_t nid = cfg->line_out_pins[i]; 477262306a36Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 477362306a36Sopenharmony_ci continue; 477462306a36Sopenharmony_ci codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid); 477562306a36Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, nid, 477662306a36Sopenharmony_ci call_line_automute); 477762306a36Sopenharmony_ci spec->detect_lo = 1; 477862306a36Sopenharmony_ci } 477962306a36Sopenharmony_ci spec->automute_lo_possible = spec->detect_hp; 478062306a36Sopenharmony_ci } 478162306a36Sopenharmony_ci 478262306a36Sopenharmony_ci spec->automute_speaker_possible = cfg->speaker_outs && 478362306a36Sopenharmony_ci (spec->detect_hp || spec->detect_lo); 478462306a36Sopenharmony_ci 478562306a36Sopenharmony_ci spec->automute_lo = spec->automute_lo_possible; 478662306a36Sopenharmony_ci spec->automute_speaker = spec->automute_speaker_possible; 478762306a36Sopenharmony_ci 478862306a36Sopenharmony_ci if (spec->automute_speaker_possible || spec->automute_lo_possible) { 478962306a36Sopenharmony_ci /* create a control for automute mode */ 479062306a36Sopenharmony_ci err = add_automute_mode_enum(codec); 479162306a36Sopenharmony_ci if (err < 0) 479262306a36Sopenharmony_ci return err; 479362306a36Sopenharmony_ci } 479462306a36Sopenharmony_ci return 0; 479562306a36Sopenharmony_ci} 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci/* check whether all auto-mic pins are valid; setup indices if OK */ 479862306a36Sopenharmony_cistatic bool auto_mic_check_imux(struct hda_codec *codec) 479962306a36Sopenharmony_ci{ 480062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 480162306a36Sopenharmony_ci const struct hda_input_mux *imux; 480262306a36Sopenharmony_ci int i; 480362306a36Sopenharmony_ci 480462306a36Sopenharmony_ci imux = &spec->input_mux; 480562306a36Sopenharmony_ci for (i = 0; i < spec->am_num_entries; i++) { 480662306a36Sopenharmony_ci spec->am_entry[i].idx = 480762306a36Sopenharmony_ci find_idx_in_nid_list(spec->am_entry[i].pin, 480862306a36Sopenharmony_ci spec->imux_pins, imux->num_items); 480962306a36Sopenharmony_ci if (spec->am_entry[i].idx < 0) 481062306a36Sopenharmony_ci return false; /* no corresponding imux */ 481162306a36Sopenharmony_ci } 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci /* we don't need the jack detection for the first pin */ 481462306a36Sopenharmony_ci for (i = 1; i < spec->am_num_entries; i++) 481562306a36Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, 481662306a36Sopenharmony_ci spec->am_entry[i].pin, 481762306a36Sopenharmony_ci call_mic_autoswitch); 481862306a36Sopenharmony_ci return true; 481962306a36Sopenharmony_ci} 482062306a36Sopenharmony_ci 482162306a36Sopenharmony_cistatic int compare_attr(const void *ap, const void *bp) 482262306a36Sopenharmony_ci{ 482362306a36Sopenharmony_ci const struct automic_entry *a = ap; 482462306a36Sopenharmony_ci const struct automic_entry *b = bp; 482562306a36Sopenharmony_ci return (int)(a->attr - b->attr); 482662306a36Sopenharmony_ci} 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci/* 482962306a36Sopenharmony_ci * Check the availability of auto-mic switch; 483062306a36Sopenharmony_ci * Set up if really supported 483162306a36Sopenharmony_ci */ 483262306a36Sopenharmony_cistatic int check_auto_mic_availability(struct hda_codec *codec) 483362306a36Sopenharmony_ci{ 483462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 483562306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 483662306a36Sopenharmony_ci unsigned int types; 483762306a36Sopenharmony_ci int i, num_pins; 483862306a36Sopenharmony_ci 483962306a36Sopenharmony_ci if (spec->suppress_auto_mic) 484062306a36Sopenharmony_ci return 0; 484162306a36Sopenharmony_ci 484262306a36Sopenharmony_ci types = 0; 484362306a36Sopenharmony_ci num_pins = 0; 484462306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 484562306a36Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 484662306a36Sopenharmony_ci unsigned int attr; 484762306a36Sopenharmony_ci attr = snd_hda_codec_get_pincfg(codec, nid); 484862306a36Sopenharmony_ci attr = snd_hda_get_input_pin_attr(attr); 484962306a36Sopenharmony_ci if (types & (1 << attr)) 485062306a36Sopenharmony_ci return 0; /* already occupied */ 485162306a36Sopenharmony_ci switch (attr) { 485262306a36Sopenharmony_ci case INPUT_PIN_ATTR_INT: 485362306a36Sopenharmony_ci if (cfg->inputs[i].type != AUTO_PIN_MIC) 485462306a36Sopenharmony_ci return 0; /* invalid type */ 485562306a36Sopenharmony_ci break; 485662306a36Sopenharmony_ci case INPUT_PIN_ATTR_UNUSED: 485762306a36Sopenharmony_ci return 0; /* invalid entry */ 485862306a36Sopenharmony_ci default: 485962306a36Sopenharmony_ci if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 486062306a36Sopenharmony_ci return 0; /* invalid type */ 486162306a36Sopenharmony_ci if (!spec->line_in_auto_switch && 486262306a36Sopenharmony_ci cfg->inputs[i].type != AUTO_PIN_MIC) 486362306a36Sopenharmony_ci return 0; /* only mic is allowed */ 486462306a36Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 486562306a36Sopenharmony_ci return 0; /* no unsol support */ 486662306a36Sopenharmony_ci break; 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci if (num_pins >= MAX_AUTO_MIC_PINS) 486962306a36Sopenharmony_ci return 0; 487062306a36Sopenharmony_ci types |= (1 << attr); 487162306a36Sopenharmony_ci spec->am_entry[num_pins].pin = nid; 487262306a36Sopenharmony_ci spec->am_entry[num_pins].attr = attr; 487362306a36Sopenharmony_ci num_pins++; 487462306a36Sopenharmony_ci } 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci if (num_pins < 2) 487762306a36Sopenharmony_ci return 0; 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_ci spec->am_num_entries = num_pins; 488062306a36Sopenharmony_ci /* sort the am_entry in the order of attr so that the pin with a 488162306a36Sopenharmony_ci * higher attr will be selected when the jack is plugged. 488262306a36Sopenharmony_ci */ 488362306a36Sopenharmony_ci sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), 488462306a36Sopenharmony_ci compare_attr, NULL); 488562306a36Sopenharmony_ci 488662306a36Sopenharmony_ci if (!auto_mic_check_imux(codec)) 488762306a36Sopenharmony_ci return 0; 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci spec->auto_mic = 1; 489062306a36Sopenharmony_ci spec->num_adc_nids = 1; 489162306a36Sopenharmony_ci spec->cur_mux[0] = spec->am_entry[0].idx; 489262306a36Sopenharmony_ci codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", 489362306a36Sopenharmony_ci spec->am_entry[0].pin, 489462306a36Sopenharmony_ci spec->am_entry[1].pin, 489562306a36Sopenharmony_ci spec->am_entry[2].pin); 489662306a36Sopenharmony_ci 489762306a36Sopenharmony_ci return 0; 489862306a36Sopenharmony_ci} 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_ci/** 490162306a36Sopenharmony_ci * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets 490262306a36Sopenharmony_ci * into power down 490362306a36Sopenharmony_ci * @codec: the HDA codec 490462306a36Sopenharmony_ci * @nid: NID to evalute 490562306a36Sopenharmony_ci * @power_state: target power state 490662306a36Sopenharmony_ci */ 490762306a36Sopenharmony_ciunsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, 490862306a36Sopenharmony_ci hda_nid_t nid, 490962306a36Sopenharmony_ci unsigned int power_state) 491062306a36Sopenharmony_ci{ 491162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 491262306a36Sopenharmony_ci 491362306a36Sopenharmony_ci if (!spec->power_down_unused && !codec->power_save_node) 491462306a36Sopenharmony_ci return power_state; 491562306a36Sopenharmony_ci if (power_state != AC_PWRST_D0 || nid == codec->core.afg) 491662306a36Sopenharmony_ci return power_state; 491762306a36Sopenharmony_ci if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) 491862306a36Sopenharmony_ci return power_state; 491962306a36Sopenharmony_ci if (is_active_nid_for_any(codec, nid)) 492062306a36Sopenharmony_ci return power_state; 492162306a36Sopenharmony_ci return AC_PWRST_D3; 492262306a36Sopenharmony_ci} 492362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter); 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_ci/* mute all aamix inputs initially; parse up to the first leaves */ 492662306a36Sopenharmony_cistatic void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) 492762306a36Sopenharmony_ci{ 492862306a36Sopenharmony_ci int i, nums; 492962306a36Sopenharmony_ci const hda_nid_t *conn; 493062306a36Sopenharmony_ci bool has_amp; 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci nums = snd_hda_get_conn_list(codec, mix, &conn); 493362306a36Sopenharmony_ci has_amp = nid_has_mute(codec, mix, HDA_INPUT); 493462306a36Sopenharmony_ci for (i = 0; i < nums; i++) { 493562306a36Sopenharmony_ci if (has_amp) 493662306a36Sopenharmony_ci update_amp(codec, mix, HDA_INPUT, i, 493762306a36Sopenharmony_ci 0xff, HDA_AMP_MUTE); 493862306a36Sopenharmony_ci else if (nid_has_volume(codec, conn[i], HDA_OUTPUT)) 493962306a36Sopenharmony_ci update_amp(codec, conn[i], HDA_OUTPUT, 0, 494062306a36Sopenharmony_ci 0xff, HDA_AMP_MUTE); 494162306a36Sopenharmony_ci } 494262306a36Sopenharmony_ci} 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci/** 494562306a36Sopenharmony_ci * snd_hda_gen_stream_pm - Stream power management callback 494662306a36Sopenharmony_ci * @codec: the HDA codec 494762306a36Sopenharmony_ci * @nid: audio widget 494862306a36Sopenharmony_ci * @on: power on/off flag 494962306a36Sopenharmony_ci * 495062306a36Sopenharmony_ci * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. 495162306a36Sopenharmony_ci */ 495262306a36Sopenharmony_civoid snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) 495362306a36Sopenharmony_ci{ 495462306a36Sopenharmony_ci if (codec->power_save_node) 495562306a36Sopenharmony_ci set_path_power(codec, nid, -1, on); 495662306a36Sopenharmony_ci} 495762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); 495862306a36Sopenharmony_ci 495962306a36Sopenharmony_ci/** 496062306a36Sopenharmony_ci * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and 496162306a36Sopenharmony_ci * set up the hda_gen_spec 496262306a36Sopenharmony_ci * @codec: the HDA codec 496362306a36Sopenharmony_ci * @cfg: Parsed pin configuration 496462306a36Sopenharmony_ci * 496562306a36Sopenharmony_ci * return 1 if successful, 0 if the proper config is not found, 496662306a36Sopenharmony_ci * or a negative error code 496762306a36Sopenharmony_ci */ 496862306a36Sopenharmony_ciint snd_hda_gen_parse_auto_config(struct hda_codec *codec, 496962306a36Sopenharmony_ci struct auto_pin_cfg *cfg) 497062306a36Sopenharmony_ci{ 497162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 497262306a36Sopenharmony_ci int err; 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci parse_user_hints(codec); 497562306a36Sopenharmony_ci 497662306a36Sopenharmony_ci if (spec->vmaster_mute_led || spec->mic_mute_led) 497762306a36Sopenharmony_ci snd_ctl_led_request(); 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci if (spec->mixer_nid && !spec->mixer_merge_nid) 498062306a36Sopenharmony_ci spec->mixer_merge_nid = spec->mixer_nid; 498162306a36Sopenharmony_ci 498262306a36Sopenharmony_ci if (cfg != &spec->autocfg) { 498362306a36Sopenharmony_ci spec->autocfg = *cfg; 498462306a36Sopenharmony_ci cfg = &spec->autocfg; 498562306a36Sopenharmony_ci } 498662306a36Sopenharmony_ci 498762306a36Sopenharmony_ci if (!spec->main_out_badness) 498862306a36Sopenharmony_ci spec->main_out_badness = &hda_main_out_badness; 498962306a36Sopenharmony_ci if (!spec->extra_out_badness) 499062306a36Sopenharmony_ci spec->extra_out_badness = &hda_extra_out_badness; 499162306a36Sopenharmony_ci 499262306a36Sopenharmony_ci fill_all_dac_nids(codec); 499362306a36Sopenharmony_ci 499462306a36Sopenharmony_ci if (!cfg->line_outs) { 499562306a36Sopenharmony_ci if (cfg->dig_outs || cfg->dig_in_pin) { 499662306a36Sopenharmony_ci spec->multiout.max_channels = 2; 499762306a36Sopenharmony_ci spec->no_analog = 1; 499862306a36Sopenharmony_ci goto dig_only; 499962306a36Sopenharmony_ci } 500062306a36Sopenharmony_ci if (!cfg->num_inputs && !cfg->dig_in_pin) 500162306a36Sopenharmony_ci return 0; /* can't find valid BIOS pin config */ 500262306a36Sopenharmony_ci } 500362306a36Sopenharmony_ci 500462306a36Sopenharmony_ci if (!spec->no_primary_hp && 500562306a36Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 500662306a36Sopenharmony_ci cfg->line_outs <= cfg->hp_outs) { 500762306a36Sopenharmony_ci /* use HP as primary out */ 500862306a36Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 500962306a36Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 501062306a36Sopenharmony_ci sizeof(cfg->speaker_pins)); 501162306a36Sopenharmony_ci cfg->line_outs = cfg->hp_outs; 501262306a36Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); 501362306a36Sopenharmony_ci cfg->hp_outs = 0; 501462306a36Sopenharmony_ci memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 501562306a36Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 501662306a36Sopenharmony_ci } 501762306a36Sopenharmony_ci 501862306a36Sopenharmony_ci err = parse_output_paths(codec); 501962306a36Sopenharmony_ci if (err < 0) 502062306a36Sopenharmony_ci return err; 502162306a36Sopenharmony_ci err = create_multi_channel_mode(codec); 502262306a36Sopenharmony_ci if (err < 0) 502362306a36Sopenharmony_ci return err; 502462306a36Sopenharmony_ci err = create_multi_out_ctls(codec, cfg); 502562306a36Sopenharmony_ci if (err < 0) 502662306a36Sopenharmony_ci return err; 502762306a36Sopenharmony_ci err = create_hp_out_ctls(codec); 502862306a36Sopenharmony_ci if (err < 0) 502962306a36Sopenharmony_ci return err; 503062306a36Sopenharmony_ci err = create_speaker_out_ctls(codec); 503162306a36Sopenharmony_ci if (err < 0) 503262306a36Sopenharmony_ci return err; 503362306a36Sopenharmony_ci err = create_indep_hp_ctls(codec); 503462306a36Sopenharmony_ci if (err < 0) 503562306a36Sopenharmony_ci return err; 503662306a36Sopenharmony_ci err = create_loopback_mixing_ctl(codec); 503762306a36Sopenharmony_ci if (err < 0) 503862306a36Sopenharmony_ci return err; 503962306a36Sopenharmony_ci err = create_hp_mic(codec); 504062306a36Sopenharmony_ci if (err < 0) 504162306a36Sopenharmony_ci return err; 504262306a36Sopenharmony_ci err = create_input_ctls(codec); 504362306a36Sopenharmony_ci if (err < 0) 504462306a36Sopenharmony_ci return err; 504562306a36Sopenharmony_ci 504662306a36Sopenharmony_ci /* add power-down pin callbacks at first */ 504762306a36Sopenharmony_ci add_all_pin_power_ctls(codec, false); 504862306a36Sopenharmony_ci 504962306a36Sopenharmony_ci spec->const_channel_count = spec->ext_channel_count; 505062306a36Sopenharmony_ci /* check the multiple speaker and headphone pins */ 505162306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 505262306a36Sopenharmony_ci spec->const_channel_count = max(spec->const_channel_count, 505362306a36Sopenharmony_ci cfg->speaker_outs * 2); 505462306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 505562306a36Sopenharmony_ci spec->const_channel_count = max(spec->const_channel_count, 505662306a36Sopenharmony_ci cfg->hp_outs * 2); 505762306a36Sopenharmony_ci spec->multiout.max_channels = max(spec->ext_channel_count, 505862306a36Sopenharmony_ci spec->const_channel_count); 505962306a36Sopenharmony_ci 506062306a36Sopenharmony_ci err = check_auto_mute_availability(codec); 506162306a36Sopenharmony_ci if (err < 0) 506262306a36Sopenharmony_ci return err; 506362306a36Sopenharmony_ci 506462306a36Sopenharmony_ci err = check_dyn_adc_switch(codec); 506562306a36Sopenharmony_ci if (err < 0) 506662306a36Sopenharmony_ci return err; 506762306a36Sopenharmony_ci 506862306a36Sopenharmony_ci err = check_auto_mic_availability(codec); 506962306a36Sopenharmony_ci if (err < 0) 507062306a36Sopenharmony_ci return err; 507162306a36Sopenharmony_ci 507262306a36Sopenharmony_ci /* add stereo mix if available and not enabled yet */ 507362306a36Sopenharmony_ci if (!spec->auto_mic && spec->mixer_nid && 507462306a36Sopenharmony_ci spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO && 507562306a36Sopenharmony_ci spec->input_mux.num_items > 1) { 507662306a36Sopenharmony_ci err = parse_capture_source(codec, spec->mixer_nid, 507762306a36Sopenharmony_ci CFG_IDX_MIX, spec->num_all_adcs, 507862306a36Sopenharmony_ci "Stereo Mix", 0); 507962306a36Sopenharmony_ci if (err < 0) 508062306a36Sopenharmony_ci return err; 508162306a36Sopenharmony_ci } 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci 508462306a36Sopenharmony_ci err = create_capture_mixers(codec); 508562306a36Sopenharmony_ci if (err < 0) 508662306a36Sopenharmony_ci return err; 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_ci err = parse_mic_boost(codec); 508962306a36Sopenharmony_ci if (err < 0) 509062306a36Sopenharmony_ci return err; 509162306a36Sopenharmony_ci 509262306a36Sopenharmony_ci /* create "Headphone Mic Jack Mode" if no input selection is 509362306a36Sopenharmony_ci * available (or user specifies add_jack_modes hint) 509462306a36Sopenharmony_ci */ 509562306a36Sopenharmony_ci if (spec->hp_mic_pin && 509662306a36Sopenharmony_ci (spec->auto_mic || spec->input_mux.num_items == 1 || 509762306a36Sopenharmony_ci spec->add_jack_modes)) { 509862306a36Sopenharmony_ci err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin); 509962306a36Sopenharmony_ci if (err < 0) 510062306a36Sopenharmony_ci return err; 510162306a36Sopenharmony_ci } 510262306a36Sopenharmony_ci 510362306a36Sopenharmony_ci if (spec->add_jack_modes) { 510462306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 510562306a36Sopenharmony_ci err = create_out_jack_modes(codec, cfg->line_outs, 510662306a36Sopenharmony_ci cfg->line_out_pins); 510762306a36Sopenharmony_ci if (err < 0) 510862306a36Sopenharmony_ci return err; 510962306a36Sopenharmony_ci } 511062306a36Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 511162306a36Sopenharmony_ci err = create_out_jack_modes(codec, cfg->hp_outs, 511262306a36Sopenharmony_ci cfg->hp_pins); 511362306a36Sopenharmony_ci if (err < 0) 511462306a36Sopenharmony_ci return err; 511562306a36Sopenharmony_ci } 511662306a36Sopenharmony_ci } 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_ci /* add power-up pin callbacks at last */ 511962306a36Sopenharmony_ci add_all_pin_power_ctls(codec, true); 512062306a36Sopenharmony_ci 512162306a36Sopenharmony_ci /* mute all aamix input initially */ 512262306a36Sopenharmony_ci if (spec->mixer_nid) 512362306a36Sopenharmony_ci mute_all_mixer_nid(codec, spec->mixer_nid); 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci dig_only: 512662306a36Sopenharmony_ci parse_digital(codec); 512762306a36Sopenharmony_ci 512862306a36Sopenharmony_ci if (spec->power_down_unused || codec->power_save_node) { 512962306a36Sopenharmony_ci if (!codec->power_filter) 513062306a36Sopenharmony_ci codec->power_filter = snd_hda_gen_path_power_filter; 513162306a36Sopenharmony_ci if (!codec->patch_ops.stream_pm) 513262306a36Sopenharmony_ci codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; 513362306a36Sopenharmony_ci } 513462306a36Sopenharmony_ci 513562306a36Sopenharmony_ci if (!spec->no_analog && spec->beep_nid) { 513662306a36Sopenharmony_ci err = snd_hda_attach_beep_device(codec, spec->beep_nid); 513762306a36Sopenharmony_ci if (err < 0) 513862306a36Sopenharmony_ci return err; 513962306a36Sopenharmony_ci if (codec->beep && codec->power_save_node) { 514062306a36Sopenharmony_ci err = add_fake_beep_paths(codec); 514162306a36Sopenharmony_ci if (err < 0) 514262306a36Sopenharmony_ci return err; 514362306a36Sopenharmony_ci codec->beep->power_hook = beep_power_hook; 514462306a36Sopenharmony_ci } 514562306a36Sopenharmony_ci } 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci return 1; 514862306a36Sopenharmony_ci} 514962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config); 515062306a36Sopenharmony_ci 515162306a36Sopenharmony_ci 515262306a36Sopenharmony_ci/* 515362306a36Sopenharmony_ci * Build control elements 515462306a36Sopenharmony_ci */ 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci/* follower controls for virtual master */ 515762306a36Sopenharmony_cistatic const char * const follower_pfxs[] = { 515862306a36Sopenharmony_ci "Front", "Surround", "Center", "LFE", "Side", 515962306a36Sopenharmony_ci "Headphone", "Speaker", "Mono", "Line Out", 516062306a36Sopenharmony_ci "CLFE", "Bass Speaker", "PCM", 516162306a36Sopenharmony_ci "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", 516262306a36Sopenharmony_ci "Headphone Front", "Headphone Surround", "Headphone CLFE", 516362306a36Sopenharmony_ci "Headphone Side", "Headphone+LO", "Speaker+LO", 516462306a36Sopenharmony_ci NULL, 516562306a36Sopenharmony_ci}; 516662306a36Sopenharmony_ci 516762306a36Sopenharmony_ci/** 516862306a36Sopenharmony_ci * snd_hda_gen_build_controls - Build controls from the parsed results 516962306a36Sopenharmony_ci * @codec: the HDA codec 517062306a36Sopenharmony_ci * 517162306a36Sopenharmony_ci * Pass this to build_controls patch_ops. 517262306a36Sopenharmony_ci */ 517362306a36Sopenharmony_ciint snd_hda_gen_build_controls(struct hda_codec *codec) 517462306a36Sopenharmony_ci{ 517562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 517662306a36Sopenharmony_ci int err; 517762306a36Sopenharmony_ci 517862306a36Sopenharmony_ci if (spec->kctls.used) { 517962306a36Sopenharmony_ci err = snd_hda_add_new_ctls(codec, spec->kctls.list); 518062306a36Sopenharmony_ci if (err < 0) 518162306a36Sopenharmony_ci return err; 518262306a36Sopenharmony_ci } 518362306a36Sopenharmony_ci 518462306a36Sopenharmony_ci if (spec->multiout.dig_out_nid) { 518562306a36Sopenharmony_ci err = snd_hda_create_dig_out_ctls(codec, 518662306a36Sopenharmony_ci spec->multiout.dig_out_nid, 518762306a36Sopenharmony_ci spec->multiout.dig_out_nid, 518862306a36Sopenharmony_ci spec->pcm_rec[1]->pcm_type); 518962306a36Sopenharmony_ci if (err < 0) 519062306a36Sopenharmony_ci return err; 519162306a36Sopenharmony_ci if (!spec->no_analog) { 519262306a36Sopenharmony_ci err = snd_hda_create_spdif_share_sw(codec, 519362306a36Sopenharmony_ci &spec->multiout); 519462306a36Sopenharmony_ci if (err < 0) 519562306a36Sopenharmony_ci return err; 519662306a36Sopenharmony_ci spec->multiout.share_spdif = 1; 519762306a36Sopenharmony_ci } 519862306a36Sopenharmony_ci } 519962306a36Sopenharmony_ci if (spec->dig_in_nid) { 520062306a36Sopenharmony_ci err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 520162306a36Sopenharmony_ci if (err < 0) 520262306a36Sopenharmony_ci return err; 520362306a36Sopenharmony_ci } 520462306a36Sopenharmony_ci 520562306a36Sopenharmony_ci /* if we have no master control, let's create it */ 520662306a36Sopenharmony_ci if (!spec->no_analog && !spec->suppress_vmaster && 520762306a36Sopenharmony_ci !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 520862306a36Sopenharmony_ci err = snd_hda_add_vmaster(codec, "Master Playback Volume", 520962306a36Sopenharmony_ci spec->vmaster_tlv, follower_pfxs, 521062306a36Sopenharmony_ci "Playback Volume", 0); 521162306a36Sopenharmony_ci if (err < 0) 521262306a36Sopenharmony_ci return err; 521362306a36Sopenharmony_ci } 521462306a36Sopenharmony_ci if (!spec->no_analog && !spec->suppress_vmaster && 521562306a36Sopenharmony_ci !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 521662306a36Sopenharmony_ci err = __snd_hda_add_vmaster(codec, "Master Playback Switch", 521762306a36Sopenharmony_ci NULL, follower_pfxs, 521862306a36Sopenharmony_ci "Playback Switch", true, 521962306a36Sopenharmony_ci spec->vmaster_mute_led ? 522062306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0, 522162306a36Sopenharmony_ci &spec->vmaster_mute.sw_kctl); 522262306a36Sopenharmony_ci if (err < 0) 522362306a36Sopenharmony_ci return err; 522462306a36Sopenharmony_ci if (spec->vmaster_mute.hook) { 522562306a36Sopenharmony_ci snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute); 522662306a36Sopenharmony_ci snd_hda_sync_vmaster_hook(&spec->vmaster_mute); 522762306a36Sopenharmony_ci } 522862306a36Sopenharmony_ci } 522962306a36Sopenharmony_ci 523062306a36Sopenharmony_ci free_kctls(spec); /* no longer needed */ 523162306a36Sopenharmony_ci 523262306a36Sopenharmony_ci err = snd_hda_jack_add_kctls(codec, &spec->autocfg); 523362306a36Sopenharmony_ci if (err < 0) 523462306a36Sopenharmony_ci return err; 523562306a36Sopenharmony_ci 523662306a36Sopenharmony_ci return 0; 523762306a36Sopenharmony_ci} 523862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_build_controls); 523962306a36Sopenharmony_ci 524062306a36Sopenharmony_ci 524162306a36Sopenharmony_ci/* 524262306a36Sopenharmony_ci * PCM definitions 524362306a36Sopenharmony_ci */ 524462306a36Sopenharmony_ci 524562306a36Sopenharmony_cistatic void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, 524662306a36Sopenharmony_ci struct hda_codec *codec, 524762306a36Sopenharmony_ci struct snd_pcm_substream *substream, 524862306a36Sopenharmony_ci int action) 524962306a36Sopenharmony_ci{ 525062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 525162306a36Sopenharmony_ci if (spec->pcm_playback_hook) 525262306a36Sopenharmony_ci spec->pcm_playback_hook(hinfo, codec, substream, action); 525362306a36Sopenharmony_ci} 525462306a36Sopenharmony_ci 525562306a36Sopenharmony_cistatic void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, 525662306a36Sopenharmony_ci struct hda_codec *codec, 525762306a36Sopenharmony_ci struct snd_pcm_substream *substream, 525862306a36Sopenharmony_ci int action) 525962306a36Sopenharmony_ci{ 526062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 526162306a36Sopenharmony_ci if (spec->pcm_capture_hook) 526262306a36Sopenharmony_ci spec->pcm_capture_hook(hinfo, codec, substream, action); 526362306a36Sopenharmony_ci} 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci/* 526662306a36Sopenharmony_ci * Analog playback callbacks 526762306a36Sopenharmony_ci */ 526862306a36Sopenharmony_cistatic int playback_pcm_open(struct hda_pcm_stream *hinfo, 526962306a36Sopenharmony_ci struct hda_codec *codec, 527062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 527162306a36Sopenharmony_ci{ 527262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 527362306a36Sopenharmony_ci int err; 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 527662306a36Sopenharmony_ci err = snd_hda_multi_out_analog_open(codec, 527762306a36Sopenharmony_ci &spec->multiout, substream, 527862306a36Sopenharmony_ci hinfo); 527962306a36Sopenharmony_ci if (!err) { 528062306a36Sopenharmony_ci spec->active_streams |= 1 << STREAM_MULTI_OUT; 528162306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 528262306a36Sopenharmony_ci HDA_GEN_PCM_ACT_OPEN); 528362306a36Sopenharmony_ci } 528462306a36Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 528562306a36Sopenharmony_ci return err; 528662306a36Sopenharmony_ci} 528762306a36Sopenharmony_ci 528862306a36Sopenharmony_cistatic int playback_pcm_prepare(struct hda_pcm_stream *hinfo, 528962306a36Sopenharmony_ci struct hda_codec *codec, 529062306a36Sopenharmony_ci unsigned int stream_tag, 529162306a36Sopenharmony_ci unsigned int format, 529262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 529362306a36Sopenharmony_ci{ 529462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 529562306a36Sopenharmony_ci int err; 529662306a36Sopenharmony_ci 529762306a36Sopenharmony_ci err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 529862306a36Sopenharmony_ci stream_tag, format, substream); 529962306a36Sopenharmony_ci if (!err) 530062306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 530162306a36Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 530262306a36Sopenharmony_ci return err; 530362306a36Sopenharmony_ci} 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_cistatic int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 530662306a36Sopenharmony_ci struct hda_codec *codec, 530762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 530862306a36Sopenharmony_ci{ 530962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 531062306a36Sopenharmony_ci int err; 531162306a36Sopenharmony_ci 531262306a36Sopenharmony_ci err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 531362306a36Sopenharmony_ci if (!err) 531462306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 531562306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 531662306a36Sopenharmony_ci return err; 531762306a36Sopenharmony_ci} 531862306a36Sopenharmony_ci 531962306a36Sopenharmony_cistatic int playback_pcm_close(struct hda_pcm_stream *hinfo, 532062306a36Sopenharmony_ci struct hda_codec *codec, 532162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 532262306a36Sopenharmony_ci{ 532362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 532462306a36Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 532562306a36Sopenharmony_ci spec->active_streams &= ~(1 << STREAM_MULTI_OUT); 532662306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 532762306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLOSE); 532862306a36Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 532962306a36Sopenharmony_ci return 0; 533062306a36Sopenharmony_ci} 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_cistatic int capture_pcm_open(struct hda_pcm_stream *hinfo, 533362306a36Sopenharmony_ci struct hda_codec *codec, 533462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 533562306a36Sopenharmony_ci{ 533662306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); 533762306a36Sopenharmony_ci return 0; 533862306a36Sopenharmony_ci} 533962306a36Sopenharmony_ci 534062306a36Sopenharmony_cistatic int capture_pcm_prepare(struct hda_pcm_stream *hinfo, 534162306a36Sopenharmony_ci struct hda_codec *codec, 534262306a36Sopenharmony_ci unsigned int stream_tag, 534362306a36Sopenharmony_ci unsigned int format, 534462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 534562306a36Sopenharmony_ci{ 534662306a36Sopenharmony_ci snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 534762306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 534862306a36Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 534962306a36Sopenharmony_ci return 0; 535062306a36Sopenharmony_ci} 535162306a36Sopenharmony_ci 535262306a36Sopenharmony_cistatic int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 535362306a36Sopenharmony_ci struct hda_codec *codec, 535462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 535562306a36Sopenharmony_ci{ 535662306a36Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, hinfo->nid); 535762306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 535862306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 535962306a36Sopenharmony_ci return 0; 536062306a36Sopenharmony_ci} 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_cistatic int capture_pcm_close(struct hda_pcm_stream *hinfo, 536362306a36Sopenharmony_ci struct hda_codec *codec, 536462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 536562306a36Sopenharmony_ci{ 536662306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); 536762306a36Sopenharmony_ci return 0; 536862306a36Sopenharmony_ci} 536962306a36Sopenharmony_ci 537062306a36Sopenharmony_cistatic int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, 537162306a36Sopenharmony_ci struct hda_codec *codec, 537262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 537362306a36Sopenharmony_ci{ 537462306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 537562306a36Sopenharmony_ci int err = 0; 537662306a36Sopenharmony_ci 537762306a36Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 537862306a36Sopenharmony_ci if (spec->indep_hp && !spec->indep_hp_enabled) 537962306a36Sopenharmony_ci err = -EBUSY; 538062306a36Sopenharmony_ci else 538162306a36Sopenharmony_ci spec->active_streams |= 1 << STREAM_INDEP_HP; 538262306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 538362306a36Sopenharmony_ci HDA_GEN_PCM_ACT_OPEN); 538462306a36Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 538562306a36Sopenharmony_ci return err; 538662306a36Sopenharmony_ci} 538762306a36Sopenharmony_ci 538862306a36Sopenharmony_cistatic int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, 538962306a36Sopenharmony_ci struct hda_codec *codec, 539062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 539162306a36Sopenharmony_ci{ 539262306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 539362306a36Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 539462306a36Sopenharmony_ci spec->active_streams &= ~(1 << STREAM_INDEP_HP); 539562306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 539662306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLOSE); 539762306a36Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 539862306a36Sopenharmony_ci return 0; 539962306a36Sopenharmony_ci} 540062306a36Sopenharmony_ci 540162306a36Sopenharmony_cistatic int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 540262306a36Sopenharmony_ci struct hda_codec *codec, 540362306a36Sopenharmony_ci unsigned int stream_tag, 540462306a36Sopenharmony_ci unsigned int format, 540562306a36Sopenharmony_ci struct snd_pcm_substream *substream) 540662306a36Sopenharmony_ci{ 540762306a36Sopenharmony_ci snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 540862306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 540962306a36Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 541062306a36Sopenharmony_ci return 0; 541162306a36Sopenharmony_ci} 541262306a36Sopenharmony_ci 541362306a36Sopenharmony_cistatic int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 541462306a36Sopenharmony_ci struct hda_codec *codec, 541562306a36Sopenharmony_ci struct snd_pcm_substream *substream) 541662306a36Sopenharmony_ci{ 541762306a36Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, hinfo->nid); 541862306a36Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 541962306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 542062306a36Sopenharmony_ci return 0; 542162306a36Sopenharmony_ci} 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ci/* 542462306a36Sopenharmony_ci * Digital out 542562306a36Sopenharmony_ci */ 542662306a36Sopenharmony_cistatic int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 542762306a36Sopenharmony_ci struct hda_codec *codec, 542862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 542962306a36Sopenharmony_ci{ 543062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 543162306a36Sopenharmony_ci return snd_hda_multi_out_dig_open(codec, &spec->multiout); 543262306a36Sopenharmony_ci} 543362306a36Sopenharmony_ci 543462306a36Sopenharmony_cistatic int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 543562306a36Sopenharmony_ci struct hda_codec *codec, 543662306a36Sopenharmony_ci unsigned int stream_tag, 543762306a36Sopenharmony_ci unsigned int format, 543862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 543962306a36Sopenharmony_ci{ 544062306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 544162306a36Sopenharmony_ci return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 544262306a36Sopenharmony_ci stream_tag, format, substream); 544362306a36Sopenharmony_ci} 544462306a36Sopenharmony_ci 544562306a36Sopenharmony_cistatic int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 544662306a36Sopenharmony_ci struct hda_codec *codec, 544762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 544862306a36Sopenharmony_ci{ 544962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 545062306a36Sopenharmony_ci return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 545162306a36Sopenharmony_ci} 545262306a36Sopenharmony_ci 545362306a36Sopenharmony_cistatic int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 545462306a36Sopenharmony_ci struct hda_codec *codec, 545562306a36Sopenharmony_ci struct snd_pcm_substream *substream) 545662306a36Sopenharmony_ci{ 545762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 545862306a36Sopenharmony_ci return snd_hda_multi_out_dig_close(codec, &spec->multiout); 545962306a36Sopenharmony_ci} 546062306a36Sopenharmony_ci 546162306a36Sopenharmony_ci/* 546262306a36Sopenharmony_ci * Analog capture 546362306a36Sopenharmony_ci */ 546462306a36Sopenharmony_ci#define alt_capture_pcm_open capture_pcm_open 546562306a36Sopenharmony_ci#define alt_capture_pcm_close capture_pcm_close 546662306a36Sopenharmony_ci 546762306a36Sopenharmony_cistatic int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 546862306a36Sopenharmony_ci struct hda_codec *codec, 546962306a36Sopenharmony_ci unsigned int stream_tag, 547062306a36Sopenharmony_ci unsigned int format, 547162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 547262306a36Sopenharmony_ci{ 547362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], 547662306a36Sopenharmony_ci stream_tag, 0, format); 547762306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 547862306a36Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 547962306a36Sopenharmony_ci return 0; 548062306a36Sopenharmony_ci} 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_cistatic int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 548362306a36Sopenharmony_ci struct hda_codec *codec, 548462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 548562306a36Sopenharmony_ci{ 548662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 548762306a36Sopenharmony_ci 548862306a36Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, 548962306a36Sopenharmony_ci spec->adc_nids[substream->number + 1]); 549062306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 549162306a36Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 549262306a36Sopenharmony_ci return 0; 549362306a36Sopenharmony_ci} 549462306a36Sopenharmony_ci 549562306a36Sopenharmony_ci/* 549662306a36Sopenharmony_ci */ 549762306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_playback = { 549862306a36Sopenharmony_ci .substreams = 1, 549962306a36Sopenharmony_ci .channels_min = 2, 550062306a36Sopenharmony_ci .channels_max = 8, 550162306a36Sopenharmony_ci /* NID is set in build_pcms */ 550262306a36Sopenharmony_ci .ops = { 550362306a36Sopenharmony_ci .open = playback_pcm_open, 550462306a36Sopenharmony_ci .close = playback_pcm_close, 550562306a36Sopenharmony_ci .prepare = playback_pcm_prepare, 550662306a36Sopenharmony_ci .cleanup = playback_pcm_cleanup 550762306a36Sopenharmony_ci }, 550862306a36Sopenharmony_ci}; 550962306a36Sopenharmony_ci 551062306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_capture = { 551162306a36Sopenharmony_ci .substreams = 1, 551262306a36Sopenharmony_ci .channels_min = 2, 551362306a36Sopenharmony_ci .channels_max = 2, 551462306a36Sopenharmony_ci /* NID is set in build_pcms */ 551562306a36Sopenharmony_ci .ops = { 551662306a36Sopenharmony_ci .open = capture_pcm_open, 551762306a36Sopenharmony_ci .close = capture_pcm_close, 551862306a36Sopenharmony_ci .prepare = capture_pcm_prepare, 551962306a36Sopenharmony_ci .cleanup = capture_pcm_cleanup 552062306a36Sopenharmony_ci }, 552162306a36Sopenharmony_ci}; 552262306a36Sopenharmony_ci 552362306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_alt_playback = { 552462306a36Sopenharmony_ci .substreams = 1, 552562306a36Sopenharmony_ci .channels_min = 2, 552662306a36Sopenharmony_ci .channels_max = 2, 552762306a36Sopenharmony_ci /* NID is set in build_pcms */ 552862306a36Sopenharmony_ci .ops = { 552962306a36Sopenharmony_ci .open = alt_playback_pcm_open, 553062306a36Sopenharmony_ci .close = alt_playback_pcm_close, 553162306a36Sopenharmony_ci .prepare = alt_playback_pcm_prepare, 553262306a36Sopenharmony_ci .cleanup = alt_playback_pcm_cleanup 553362306a36Sopenharmony_ci }, 553462306a36Sopenharmony_ci}; 553562306a36Sopenharmony_ci 553662306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_alt_capture = { 553762306a36Sopenharmony_ci .substreams = 2, /* can be overridden */ 553862306a36Sopenharmony_ci .channels_min = 2, 553962306a36Sopenharmony_ci .channels_max = 2, 554062306a36Sopenharmony_ci /* NID is set in build_pcms */ 554162306a36Sopenharmony_ci .ops = { 554262306a36Sopenharmony_ci .open = alt_capture_pcm_open, 554362306a36Sopenharmony_ci .close = alt_capture_pcm_close, 554462306a36Sopenharmony_ci .prepare = alt_capture_pcm_prepare, 554562306a36Sopenharmony_ci .cleanup = alt_capture_pcm_cleanup 554662306a36Sopenharmony_ci }, 554762306a36Sopenharmony_ci}; 554862306a36Sopenharmony_ci 554962306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_digital_playback = { 555062306a36Sopenharmony_ci .substreams = 1, 555162306a36Sopenharmony_ci .channels_min = 2, 555262306a36Sopenharmony_ci .channels_max = 2, 555362306a36Sopenharmony_ci /* NID is set in build_pcms */ 555462306a36Sopenharmony_ci .ops = { 555562306a36Sopenharmony_ci .open = dig_playback_pcm_open, 555662306a36Sopenharmony_ci .close = dig_playback_pcm_close, 555762306a36Sopenharmony_ci .prepare = dig_playback_pcm_prepare, 555862306a36Sopenharmony_ci .cleanup = dig_playback_pcm_cleanup 555962306a36Sopenharmony_ci }, 556062306a36Sopenharmony_ci}; 556162306a36Sopenharmony_ci 556262306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_digital_capture = { 556362306a36Sopenharmony_ci .substreams = 1, 556462306a36Sopenharmony_ci .channels_min = 2, 556562306a36Sopenharmony_ci .channels_max = 2, 556662306a36Sopenharmony_ci /* NID is set in build_pcms */ 556762306a36Sopenharmony_ci}; 556862306a36Sopenharmony_ci 556962306a36Sopenharmony_ci/* Used by build_pcms to flag that a PCM has no playback stream */ 557062306a36Sopenharmony_cistatic const struct hda_pcm_stream pcm_null_stream = { 557162306a36Sopenharmony_ci .substreams = 0, 557262306a36Sopenharmony_ci .channels_min = 0, 557362306a36Sopenharmony_ci .channels_max = 0, 557462306a36Sopenharmony_ci}; 557562306a36Sopenharmony_ci 557662306a36Sopenharmony_ci/* 557762306a36Sopenharmony_ci * dynamic changing ADC PCM streams 557862306a36Sopenharmony_ci */ 557962306a36Sopenharmony_cistatic bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) 558062306a36Sopenharmony_ci{ 558162306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 558262306a36Sopenharmony_ci hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; 558362306a36Sopenharmony_ci 558462306a36Sopenharmony_ci if (spec->cur_adc && spec->cur_adc != new_adc) { 558562306a36Sopenharmony_ci /* stream is running, let's swap the current ADC */ 558662306a36Sopenharmony_ci __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 558762306a36Sopenharmony_ci spec->cur_adc = new_adc; 558862306a36Sopenharmony_ci snd_hda_codec_setup_stream(codec, new_adc, 558962306a36Sopenharmony_ci spec->cur_adc_stream_tag, 0, 559062306a36Sopenharmony_ci spec->cur_adc_format); 559162306a36Sopenharmony_ci return true; 559262306a36Sopenharmony_ci } 559362306a36Sopenharmony_ci return false; 559462306a36Sopenharmony_ci} 559562306a36Sopenharmony_ci 559662306a36Sopenharmony_ci/* analog capture with dynamic dual-adc changes */ 559762306a36Sopenharmony_cistatic int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 559862306a36Sopenharmony_ci struct hda_codec *codec, 559962306a36Sopenharmony_ci unsigned int stream_tag, 560062306a36Sopenharmony_ci unsigned int format, 560162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 560262306a36Sopenharmony_ci{ 560362306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 560462306a36Sopenharmony_ci spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; 560562306a36Sopenharmony_ci spec->cur_adc_stream_tag = stream_tag; 560662306a36Sopenharmony_ci spec->cur_adc_format = format; 560762306a36Sopenharmony_ci snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 560862306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); 560962306a36Sopenharmony_ci return 0; 561062306a36Sopenharmony_ci} 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_cistatic int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 561362306a36Sopenharmony_ci struct hda_codec *codec, 561462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 561562306a36Sopenharmony_ci{ 561662306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 561762306a36Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 561862306a36Sopenharmony_ci spec->cur_adc = 0; 561962306a36Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); 562062306a36Sopenharmony_ci return 0; 562162306a36Sopenharmony_ci} 562262306a36Sopenharmony_ci 562362306a36Sopenharmony_cistatic const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { 562462306a36Sopenharmony_ci .substreams = 1, 562562306a36Sopenharmony_ci .channels_min = 2, 562662306a36Sopenharmony_ci .channels_max = 2, 562762306a36Sopenharmony_ci .nid = 0, /* fill later */ 562862306a36Sopenharmony_ci .ops = { 562962306a36Sopenharmony_ci .prepare = dyn_adc_capture_pcm_prepare, 563062306a36Sopenharmony_ci .cleanup = dyn_adc_capture_pcm_cleanup 563162306a36Sopenharmony_ci }, 563262306a36Sopenharmony_ci}; 563362306a36Sopenharmony_ci 563462306a36Sopenharmony_cistatic void fill_pcm_stream_name(char *str, size_t len, const char *sfx, 563562306a36Sopenharmony_ci const char *chip_name) 563662306a36Sopenharmony_ci{ 563762306a36Sopenharmony_ci char *p; 563862306a36Sopenharmony_ci 563962306a36Sopenharmony_ci if (*str) 564062306a36Sopenharmony_ci return; 564162306a36Sopenharmony_ci strscpy(str, chip_name, len); 564262306a36Sopenharmony_ci 564362306a36Sopenharmony_ci /* drop non-alnum chars after a space */ 564462306a36Sopenharmony_ci for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { 564562306a36Sopenharmony_ci if (!isalnum(p[1])) { 564662306a36Sopenharmony_ci *p = 0; 564762306a36Sopenharmony_ci break; 564862306a36Sopenharmony_ci } 564962306a36Sopenharmony_ci } 565062306a36Sopenharmony_ci strlcat(str, sfx, len); 565162306a36Sopenharmony_ci} 565262306a36Sopenharmony_ci 565362306a36Sopenharmony_ci/* copy PCM stream info from @default_str, and override non-NULL entries 565462306a36Sopenharmony_ci * from @spec_str and @nid 565562306a36Sopenharmony_ci */ 565662306a36Sopenharmony_cistatic void setup_pcm_stream(struct hda_pcm_stream *str, 565762306a36Sopenharmony_ci const struct hda_pcm_stream *default_str, 565862306a36Sopenharmony_ci const struct hda_pcm_stream *spec_str, 565962306a36Sopenharmony_ci hda_nid_t nid) 566062306a36Sopenharmony_ci{ 566162306a36Sopenharmony_ci *str = *default_str; 566262306a36Sopenharmony_ci if (nid) 566362306a36Sopenharmony_ci str->nid = nid; 566462306a36Sopenharmony_ci if (spec_str) { 566562306a36Sopenharmony_ci if (spec_str->substreams) 566662306a36Sopenharmony_ci str->substreams = spec_str->substreams; 566762306a36Sopenharmony_ci if (spec_str->channels_min) 566862306a36Sopenharmony_ci str->channels_min = spec_str->channels_min; 566962306a36Sopenharmony_ci if (spec_str->channels_max) 567062306a36Sopenharmony_ci str->channels_max = spec_str->channels_max; 567162306a36Sopenharmony_ci if (spec_str->rates) 567262306a36Sopenharmony_ci str->rates = spec_str->rates; 567362306a36Sopenharmony_ci if (spec_str->formats) 567462306a36Sopenharmony_ci str->formats = spec_str->formats; 567562306a36Sopenharmony_ci if (spec_str->maxbps) 567662306a36Sopenharmony_ci str->maxbps = spec_str->maxbps; 567762306a36Sopenharmony_ci } 567862306a36Sopenharmony_ci} 567962306a36Sopenharmony_ci 568062306a36Sopenharmony_ci/** 568162306a36Sopenharmony_ci * snd_hda_gen_build_pcms - build PCM streams based on the parsed results 568262306a36Sopenharmony_ci * @codec: the HDA codec 568362306a36Sopenharmony_ci * 568462306a36Sopenharmony_ci * Pass this to build_pcms patch_ops. 568562306a36Sopenharmony_ci */ 568662306a36Sopenharmony_ciint snd_hda_gen_build_pcms(struct hda_codec *codec) 568762306a36Sopenharmony_ci{ 568862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 568962306a36Sopenharmony_ci struct hda_pcm *info; 569062306a36Sopenharmony_ci bool have_multi_adcs; 569162306a36Sopenharmony_ci 569262306a36Sopenharmony_ci if (spec->no_analog) 569362306a36Sopenharmony_ci goto skip_analog; 569462306a36Sopenharmony_ci 569562306a36Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_analog, 569662306a36Sopenharmony_ci sizeof(spec->stream_name_analog), 569762306a36Sopenharmony_ci " Analog", codec->core.chip_name); 569862306a36Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); 569962306a36Sopenharmony_ci if (!info) 570062306a36Sopenharmony_ci return -ENOMEM; 570162306a36Sopenharmony_ci spec->pcm_rec[0] = info; 570262306a36Sopenharmony_ci 570362306a36Sopenharmony_ci if (spec->multiout.num_dacs > 0) { 570462306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 570562306a36Sopenharmony_ci &pcm_analog_playback, 570662306a36Sopenharmony_ci spec->stream_analog_playback, 570762306a36Sopenharmony_ci spec->multiout.dac_nids[0]); 570862306a36Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 570962306a36Sopenharmony_ci spec->multiout.max_channels; 571062306a36Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && 571162306a36Sopenharmony_ci spec->autocfg.line_outs == 2) 571262306a36Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = 571362306a36Sopenharmony_ci snd_pcm_2_1_chmaps; 571462306a36Sopenharmony_ci } 571562306a36Sopenharmony_ci if (spec->num_adc_nids) { 571662306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 571762306a36Sopenharmony_ci (spec->dyn_adc_switch ? 571862306a36Sopenharmony_ci &dyn_adc_pcm_analog_capture : &pcm_analog_capture), 571962306a36Sopenharmony_ci spec->stream_analog_capture, 572062306a36Sopenharmony_ci spec->adc_nids[0]); 572162306a36Sopenharmony_ci } 572262306a36Sopenharmony_ci 572362306a36Sopenharmony_ci skip_analog: 572462306a36Sopenharmony_ci /* SPDIF for stream index #1 */ 572562306a36Sopenharmony_ci if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 572662306a36Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_digital, 572762306a36Sopenharmony_ci sizeof(spec->stream_name_digital), 572862306a36Sopenharmony_ci " Digital", codec->core.chip_name); 572962306a36Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", 573062306a36Sopenharmony_ci spec->stream_name_digital); 573162306a36Sopenharmony_ci if (!info) 573262306a36Sopenharmony_ci return -ENOMEM; 573362306a36Sopenharmony_ci codec->follower_dig_outs = spec->multiout.follower_dig_outs; 573462306a36Sopenharmony_ci spec->pcm_rec[1] = info; 573562306a36Sopenharmony_ci if (spec->dig_out_type) 573662306a36Sopenharmony_ci info->pcm_type = spec->dig_out_type; 573762306a36Sopenharmony_ci else 573862306a36Sopenharmony_ci info->pcm_type = HDA_PCM_TYPE_SPDIF; 573962306a36Sopenharmony_ci if (spec->multiout.dig_out_nid) 574062306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 574162306a36Sopenharmony_ci &pcm_digital_playback, 574262306a36Sopenharmony_ci spec->stream_digital_playback, 574362306a36Sopenharmony_ci spec->multiout.dig_out_nid); 574462306a36Sopenharmony_ci if (spec->dig_in_nid) 574562306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 574662306a36Sopenharmony_ci &pcm_digital_capture, 574762306a36Sopenharmony_ci spec->stream_digital_capture, 574862306a36Sopenharmony_ci spec->dig_in_nid); 574962306a36Sopenharmony_ci } 575062306a36Sopenharmony_ci 575162306a36Sopenharmony_ci if (spec->no_analog) 575262306a36Sopenharmony_ci return 0; 575362306a36Sopenharmony_ci 575462306a36Sopenharmony_ci /* If the use of more than one ADC is requested for the current 575562306a36Sopenharmony_ci * model, configure a second analog capture-only PCM. 575662306a36Sopenharmony_ci */ 575762306a36Sopenharmony_ci have_multi_adcs = (spec->num_adc_nids > 1) && 575862306a36Sopenharmony_ci !spec->dyn_adc_switch && !spec->auto_mic; 575962306a36Sopenharmony_ci /* Additional Analaog capture for index #2 */ 576062306a36Sopenharmony_ci if (spec->alt_dac_nid || have_multi_adcs) { 576162306a36Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_alt_analog, 576262306a36Sopenharmony_ci sizeof(spec->stream_name_alt_analog), 576362306a36Sopenharmony_ci " Alt Analog", codec->core.chip_name); 576462306a36Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", 576562306a36Sopenharmony_ci spec->stream_name_alt_analog); 576662306a36Sopenharmony_ci if (!info) 576762306a36Sopenharmony_ci return -ENOMEM; 576862306a36Sopenharmony_ci spec->pcm_rec[2] = info; 576962306a36Sopenharmony_ci if (spec->alt_dac_nid) 577062306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 577162306a36Sopenharmony_ci &pcm_analog_alt_playback, 577262306a36Sopenharmony_ci spec->stream_analog_alt_playback, 577362306a36Sopenharmony_ci spec->alt_dac_nid); 577462306a36Sopenharmony_ci else 577562306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 577662306a36Sopenharmony_ci &pcm_null_stream, NULL, 0); 577762306a36Sopenharmony_ci if (have_multi_adcs) { 577862306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 577962306a36Sopenharmony_ci &pcm_analog_alt_capture, 578062306a36Sopenharmony_ci spec->stream_analog_alt_capture, 578162306a36Sopenharmony_ci spec->adc_nids[1]); 578262306a36Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 578362306a36Sopenharmony_ci spec->num_adc_nids - 1; 578462306a36Sopenharmony_ci } else { 578562306a36Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 578662306a36Sopenharmony_ci &pcm_null_stream, NULL, 0); 578762306a36Sopenharmony_ci } 578862306a36Sopenharmony_ci } 578962306a36Sopenharmony_ci 579062306a36Sopenharmony_ci return 0; 579162306a36Sopenharmony_ci} 579262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms); 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci 579562306a36Sopenharmony_ci/* 579662306a36Sopenharmony_ci * Standard auto-parser initializations 579762306a36Sopenharmony_ci */ 579862306a36Sopenharmony_ci 579962306a36Sopenharmony_ci/* configure the given path as a proper output */ 580062306a36Sopenharmony_cistatic void set_output_and_unmute(struct hda_codec *codec, int path_idx) 580162306a36Sopenharmony_ci{ 580262306a36Sopenharmony_ci struct nid_path *path; 580362306a36Sopenharmony_ci hda_nid_t pin; 580462306a36Sopenharmony_ci 580562306a36Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 580662306a36Sopenharmony_ci if (!path || !path->depth) 580762306a36Sopenharmony_ci return; 580862306a36Sopenharmony_ci pin = path->path[path->depth - 1]; 580962306a36Sopenharmony_ci restore_pin_ctl(codec, pin); 581062306a36Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 581162306a36Sopenharmony_ci aamix_default(codec->spec)); 581262306a36Sopenharmony_ci set_pin_eapd(codec, pin, path->active); 581362306a36Sopenharmony_ci} 581462306a36Sopenharmony_ci 581562306a36Sopenharmony_ci/* initialize primary output paths */ 581662306a36Sopenharmony_cistatic void init_multi_out(struct hda_codec *codec) 581762306a36Sopenharmony_ci{ 581862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 581962306a36Sopenharmony_ci int i; 582062306a36Sopenharmony_ci 582162306a36Sopenharmony_ci for (i = 0; i < spec->autocfg.line_outs; i++) 582262306a36Sopenharmony_ci set_output_and_unmute(codec, spec->out_paths[i]); 582362306a36Sopenharmony_ci} 582462306a36Sopenharmony_ci 582562306a36Sopenharmony_ci 582662306a36Sopenharmony_cistatic void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) 582762306a36Sopenharmony_ci{ 582862306a36Sopenharmony_ci int i; 582962306a36Sopenharmony_ci 583062306a36Sopenharmony_ci for (i = 0; i < num_outs; i++) 583162306a36Sopenharmony_ci set_output_and_unmute(codec, paths[i]); 583262306a36Sopenharmony_ci} 583362306a36Sopenharmony_ci 583462306a36Sopenharmony_ci/* initialize hp and speaker paths */ 583562306a36Sopenharmony_cistatic void init_extra_out(struct hda_codec *codec) 583662306a36Sopenharmony_ci{ 583762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 583862306a36Sopenharmony_ci 583962306a36Sopenharmony_ci if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) 584062306a36Sopenharmony_ci __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); 584162306a36Sopenharmony_ci if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) 584262306a36Sopenharmony_ci __init_extra_out(codec, spec->autocfg.speaker_outs, 584362306a36Sopenharmony_ci spec->speaker_paths); 584462306a36Sopenharmony_ci} 584562306a36Sopenharmony_ci 584662306a36Sopenharmony_ci/* initialize multi-io paths */ 584762306a36Sopenharmony_cistatic void init_multi_io(struct hda_codec *codec) 584862306a36Sopenharmony_ci{ 584962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 585062306a36Sopenharmony_ci int i; 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) { 585362306a36Sopenharmony_ci hda_nid_t pin = spec->multi_io[i].pin; 585462306a36Sopenharmony_ci struct nid_path *path; 585562306a36Sopenharmony_ci path = get_multiio_path(codec, i); 585662306a36Sopenharmony_ci if (!path) 585762306a36Sopenharmony_ci continue; 585862306a36Sopenharmony_ci if (!spec->multi_io[i].ctl_in) 585962306a36Sopenharmony_ci spec->multi_io[i].ctl_in = 586062306a36Sopenharmony_ci snd_hda_codec_get_pin_target(codec, pin); 586162306a36Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 586262306a36Sopenharmony_ci aamix_default(spec)); 586362306a36Sopenharmony_ci } 586462306a36Sopenharmony_ci} 586562306a36Sopenharmony_ci 586662306a36Sopenharmony_cistatic void init_aamix_paths(struct hda_codec *codec) 586762306a36Sopenharmony_ci{ 586862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 586962306a36Sopenharmony_ci 587062306a36Sopenharmony_ci if (!spec->have_aamix_ctl) 587162306a36Sopenharmony_ci return; 587262306a36Sopenharmony_ci if (!has_aamix_out_paths(spec)) 587362306a36Sopenharmony_ci return; 587462306a36Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0], 587562306a36Sopenharmony_ci spec->aamix_out_paths[0], 587662306a36Sopenharmony_ci spec->autocfg.line_out_type); 587762306a36Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0], 587862306a36Sopenharmony_ci spec->aamix_out_paths[1], 587962306a36Sopenharmony_ci AUTO_PIN_HP_OUT); 588062306a36Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0], 588162306a36Sopenharmony_ci spec->aamix_out_paths[2], 588262306a36Sopenharmony_ci AUTO_PIN_SPEAKER_OUT); 588362306a36Sopenharmony_ci} 588462306a36Sopenharmony_ci 588562306a36Sopenharmony_ci/* set up input pins and loopback paths */ 588662306a36Sopenharmony_cistatic void init_analog_input(struct hda_codec *codec) 588762306a36Sopenharmony_ci{ 588862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 588962306a36Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 589062306a36Sopenharmony_ci int i; 589162306a36Sopenharmony_ci 589262306a36Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 589362306a36Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 589462306a36Sopenharmony_ci if (is_input_pin(codec, nid)) 589562306a36Sopenharmony_ci restore_pin_ctl(codec, nid); 589662306a36Sopenharmony_ci 589762306a36Sopenharmony_ci /* init loopback inputs */ 589862306a36Sopenharmony_ci if (spec->mixer_nid) { 589962306a36Sopenharmony_ci resume_path_from_idx(codec, spec->loopback_paths[i]); 590062306a36Sopenharmony_ci resume_path_from_idx(codec, spec->loopback_merge_path); 590162306a36Sopenharmony_ci } 590262306a36Sopenharmony_ci } 590362306a36Sopenharmony_ci} 590462306a36Sopenharmony_ci 590562306a36Sopenharmony_ci/* initialize ADC paths */ 590662306a36Sopenharmony_cistatic void init_input_src(struct hda_codec *codec) 590762306a36Sopenharmony_ci{ 590862306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 590962306a36Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 591062306a36Sopenharmony_ci struct nid_path *path; 591162306a36Sopenharmony_ci int i, c, nums; 591262306a36Sopenharmony_ci 591362306a36Sopenharmony_ci if (spec->dyn_adc_switch) 591462306a36Sopenharmony_ci nums = 1; 591562306a36Sopenharmony_ci else 591662306a36Sopenharmony_ci nums = spec->num_adc_nids; 591762306a36Sopenharmony_ci 591862306a36Sopenharmony_ci for (c = 0; c < nums; c++) { 591962306a36Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 592062306a36Sopenharmony_ci path = get_input_path(codec, c, i); 592162306a36Sopenharmony_ci if (path) { 592262306a36Sopenharmony_ci bool active = path->active; 592362306a36Sopenharmony_ci if (i == spec->cur_mux[c]) 592462306a36Sopenharmony_ci active = true; 592562306a36Sopenharmony_ci snd_hda_activate_path(codec, path, active, false); 592662306a36Sopenharmony_ci } 592762306a36Sopenharmony_ci } 592862306a36Sopenharmony_ci if (spec->hp_mic) 592962306a36Sopenharmony_ci update_hp_mic(codec, c, true); 593062306a36Sopenharmony_ci } 593162306a36Sopenharmony_ci 593262306a36Sopenharmony_ci if (spec->cap_sync_hook) 593362306a36Sopenharmony_ci spec->cap_sync_hook(codec, NULL, NULL); 593462306a36Sopenharmony_ci} 593562306a36Sopenharmony_ci 593662306a36Sopenharmony_ci/* set right pin controls for digital I/O */ 593762306a36Sopenharmony_cistatic void init_digital(struct hda_codec *codec) 593862306a36Sopenharmony_ci{ 593962306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 594062306a36Sopenharmony_ci int i; 594162306a36Sopenharmony_ci hda_nid_t pin; 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci for (i = 0; i < spec->autocfg.dig_outs; i++) 594462306a36Sopenharmony_ci set_output_and_unmute(codec, spec->digout_paths[i]); 594562306a36Sopenharmony_ci pin = spec->autocfg.dig_in_pin; 594662306a36Sopenharmony_ci if (pin) { 594762306a36Sopenharmony_ci restore_pin_ctl(codec, pin); 594862306a36Sopenharmony_ci resume_path_from_idx(codec, spec->digin_path); 594962306a36Sopenharmony_ci } 595062306a36Sopenharmony_ci} 595162306a36Sopenharmony_ci 595262306a36Sopenharmony_ci/* clear unsol-event tags on unused pins; Conexant codecs seem to leave 595362306a36Sopenharmony_ci * invalid unsol tags by some reason 595462306a36Sopenharmony_ci */ 595562306a36Sopenharmony_cistatic void clear_unsol_on_unused_pins(struct hda_codec *codec) 595662306a36Sopenharmony_ci{ 595762306a36Sopenharmony_ci const struct hda_pincfg *pin; 595862306a36Sopenharmony_ci int i; 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ci snd_array_for_each(&codec->init_pins, i, pin) { 596162306a36Sopenharmony_ci hda_nid_t nid = pin->nid; 596262306a36Sopenharmony_ci if (is_jack_detectable(codec, nid) && 596362306a36Sopenharmony_ci !snd_hda_jack_tbl_get(codec, nid)) 596462306a36Sopenharmony_ci snd_hda_codec_write_cache(codec, nid, 0, 596562306a36Sopenharmony_ci AC_VERB_SET_UNSOLICITED_ENABLE, 0); 596662306a36Sopenharmony_ci } 596762306a36Sopenharmony_ci} 596862306a36Sopenharmony_ci 596962306a36Sopenharmony_ci/** 597062306a36Sopenharmony_ci * snd_hda_gen_init - initialize the generic spec 597162306a36Sopenharmony_ci * @codec: the HDA codec 597262306a36Sopenharmony_ci * 597362306a36Sopenharmony_ci * This can be put as patch_ops init function. 597462306a36Sopenharmony_ci */ 597562306a36Sopenharmony_ciint snd_hda_gen_init(struct hda_codec *codec) 597662306a36Sopenharmony_ci{ 597762306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 597862306a36Sopenharmony_ci 597962306a36Sopenharmony_ci if (spec->init_hook) 598062306a36Sopenharmony_ci spec->init_hook(codec); 598162306a36Sopenharmony_ci 598262306a36Sopenharmony_ci if (!spec->skip_verbs) 598362306a36Sopenharmony_ci snd_hda_apply_verbs(codec); 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_ci init_multi_out(codec); 598662306a36Sopenharmony_ci init_extra_out(codec); 598762306a36Sopenharmony_ci init_multi_io(codec); 598862306a36Sopenharmony_ci init_aamix_paths(codec); 598962306a36Sopenharmony_ci init_analog_input(codec); 599062306a36Sopenharmony_ci init_input_src(codec); 599162306a36Sopenharmony_ci init_digital(codec); 599262306a36Sopenharmony_ci 599362306a36Sopenharmony_ci clear_unsol_on_unused_pins(codec); 599462306a36Sopenharmony_ci 599562306a36Sopenharmony_ci sync_all_pin_power_ctls(codec); 599662306a36Sopenharmony_ci 599762306a36Sopenharmony_ci /* call init functions of standard auto-mute helpers */ 599862306a36Sopenharmony_ci update_automute_all(codec); 599962306a36Sopenharmony_ci 600062306a36Sopenharmony_ci snd_hda_regmap_sync(codec); 600162306a36Sopenharmony_ci 600262306a36Sopenharmony_ci if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) 600362306a36Sopenharmony_ci snd_hda_sync_vmaster_hook(&spec->vmaster_mute); 600462306a36Sopenharmony_ci 600562306a36Sopenharmony_ci hda_call_check_power_status(codec, 0x01); 600662306a36Sopenharmony_ci return 0; 600762306a36Sopenharmony_ci} 600862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_init); 600962306a36Sopenharmony_ci 601062306a36Sopenharmony_ci/** 601162306a36Sopenharmony_ci * snd_hda_gen_free - free the generic spec 601262306a36Sopenharmony_ci * @codec: the HDA codec 601362306a36Sopenharmony_ci * 601462306a36Sopenharmony_ci * This can be put as patch_ops free function. 601562306a36Sopenharmony_ci */ 601662306a36Sopenharmony_civoid snd_hda_gen_free(struct hda_codec *codec) 601762306a36Sopenharmony_ci{ 601862306a36Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); 601962306a36Sopenharmony_ci snd_hda_gen_spec_free(codec->spec); 602062306a36Sopenharmony_ci kfree(codec->spec); 602162306a36Sopenharmony_ci codec->spec = NULL; 602262306a36Sopenharmony_ci} 602362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_free); 602462306a36Sopenharmony_ci 602562306a36Sopenharmony_ci#ifdef CONFIG_PM 602662306a36Sopenharmony_ci/** 602762306a36Sopenharmony_ci * snd_hda_gen_check_power_status - check the loopback power save state 602862306a36Sopenharmony_ci * @codec: the HDA codec 602962306a36Sopenharmony_ci * @nid: NID to inspect 603062306a36Sopenharmony_ci * 603162306a36Sopenharmony_ci * This can be put as patch_ops check_power_status function. 603262306a36Sopenharmony_ci */ 603362306a36Sopenharmony_ciint snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) 603462306a36Sopenharmony_ci{ 603562306a36Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 603662306a36Sopenharmony_ci return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 603762306a36Sopenharmony_ci} 603862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); 603962306a36Sopenharmony_ci#endif 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci 604262306a36Sopenharmony_ci/* 604362306a36Sopenharmony_ci * the generic codec support 604462306a36Sopenharmony_ci */ 604562306a36Sopenharmony_ci 604662306a36Sopenharmony_cistatic const struct hda_codec_ops generic_patch_ops = { 604762306a36Sopenharmony_ci .build_controls = snd_hda_gen_build_controls, 604862306a36Sopenharmony_ci .build_pcms = snd_hda_gen_build_pcms, 604962306a36Sopenharmony_ci .init = snd_hda_gen_init, 605062306a36Sopenharmony_ci .free = snd_hda_gen_free, 605162306a36Sopenharmony_ci .unsol_event = snd_hda_jack_unsol_event, 605262306a36Sopenharmony_ci#ifdef CONFIG_PM 605362306a36Sopenharmony_ci .check_power_status = snd_hda_gen_check_power_status, 605462306a36Sopenharmony_ci#endif 605562306a36Sopenharmony_ci}; 605662306a36Sopenharmony_ci 605762306a36Sopenharmony_ci/* 605862306a36Sopenharmony_ci * snd_hda_parse_generic_codec - Generic codec parser 605962306a36Sopenharmony_ci * @codec: the HDA codec 606062306a36Sopenharmony_ci */ 606162306a36Sopenharmony_cistatic int snd_hda_parse_generic_codec(struct hda_codec *codec) 606262306a36Sopenharmony_ci{ 606362306a36Sopenharmony_ci struct hda_gen_spec *spec; 606462306a36Sopenharmony_ci int err; 606562306a36Sopenharmony_ci 606662306a36Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 606762306a36Sopenharmony_ci if (!spec) 606862306a36Sopenharmony_ci return -ENOMEM; 606962306a36Sopenharmony_ci snd_hda_gen_spec_init(spec); 607062306a36Sopenharmony_ci codec->spec = spec; 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); 607362306a36Sopenharmony_ci if (err < 0) 607462306a36Sopenharmony_ci goto error; 607562306a36Sopenharmony_ci 607662306a36Sopenharmony_ci err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); 607762306a36Sopenharmony_ci if (err < 0) 607862306a36Sopenharmony_ci goto error; 607962306a36Sopenharmony_ci 608062306a36Sopenharmony_ci codec->patch_ops = generic_patch_ops; 608162306a36Sopenharmony_ci return 0; 608262306a36Sopenharmony_ci 608362306a36Sopenharmony_cierror: 608462306a36Sopenharmony_ci snd_hda_gen_free(codec); 608562306a36Sopenharmony_ci return err; 608662306a36Sopenharmony_ci} 608762306a36Sopenharmony_ci 608862306a36Sopenharmony_cistatic const struct hda_device_id snd_hda_id_generic[] = { 608962306a36Sopenharmony_ci HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), 609062306a36Sopenharmony_ci {} /* terminator */ 609162306a36Sopenharmony_ci}; 609262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); 609362306a36Sopenharmony_ci 609462306a36Sopenharmony_cistatic struct hda_codec_driver generic_driver = { 609562306a36Sopenharmony_ci .id = snd_hda_id_generic, 609662306a36Sopenharmony_ci}; 609762306a36Sopenharmony_ci 609862306a36Sopenharmony_cimodule_hda_codec_driver(generic_driver); 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 610162306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic HD-audio codec parser"); 6102