18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Universal Interface for Intel High Definition Audio Codec 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Generic widget tree parser 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/sort.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/ctype.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/bitops.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/leds.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/jack.h> 228c2ecf20Sopenharmony_ci#include <sound/tlv.h> 238c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 248c2ecf20Sopenharmony_ci#include "hda_local.h" 258c2ecf20Sopenharmony_ci#include "hda_auto_parser.h" 268c2ecf20Sopenharmony_ci#include "hda_jack.h" 278c2ecf20Sopenharmony_ci#include "hda_beep.h" 288c2ecf20Sopenharmony_ci#include "hda_generic.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * snd_hda_gen_spec_init - initialize hda_gen_spec struct 338c2ecf20Sopenharmony_ci * @spec: hda_gen_spec object to initialize 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Initialize the given hda_gen_spec object. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ciint snd_hda_gen_spec_init(struct hda_gen_spec *spec) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); 408c2ecf20Sopenharmony_ci snd_array_init(&spec->paths, sizeof(struct nid_path), 8); 418c2ecf20Sopenharmony_ci snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8); 428c2ecf20Sopenharmony_ci mutex_init(&spec->pcm_mutex); 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template 498c2ecf20Sopenharmony_ci * @spec: hda_gen_spec object 508c2ecf20Sopenharmony_ci * @name: name string to override the template, NULL if unchanged 518c2ecf20Sopenharmony_ci * @temp: template for the new kctl 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Add a new kctl (actually snd_kcontrol_new to be instantiated later) 548c2ecf20Sopenharmony_ci * element based on the given snd_kcontrol_new template @temp and the 558c2ecf20Sopenharmony_ci * name string @name to the list in @spec. 568c2ecf20Sopenharmony_ci * Returns the newly created object or NULL as error. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistruct snd_kcontrol_new * 598c2ecf20Sopenharmony_cisnd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, 608c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *temp) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); 638c2ecf20Sopenharmony_ci if (!knew) 648c2ecf20Sopenharmony_ci return NULL; 658c2ecf20Sopenharmony_ci *knew = *temp; 668c2ecf20Sopenharmony_ci if (name) 678c2ecf20Sopenharmony_ci knew->name = kstrdup(name, GFP_KERNEL); 688c2ecf20Sopenharmony_ci else if (knew->name) 698c2ecf20Sopenharmony_ci knew->name = kstrdup(knew->name, GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!knew->name) 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci return knew; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void free_kctls(struct hda_gen_spec *spec) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci if (spec->kctls.list) { 798c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kctl = spec->kctls.list; 808c2ecf20Sopenharmony_ci int i; 818c2ecf20Sopenharmony_ci for (i = 0; i < spec->kctls.used; i++) 828c2ecf20Sopenharmony_ci kfree(kctl[i].name); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci snd_array_free(&spec->kctls); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void snd_hda_gen_spec_free(struct hda_gen_spec *spec) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (!spec) 908c2ecf20Sopenharmony_ci return; 918c2ecf20Sopenharmony_ci free_kctls(spec); 928c2ecf20Sopenharmony_ci snd_array_free(&spec->paths); 938c2ecf20Sopenharmony_ci snd_array_free(&spec->loopback_list); 948c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_GENERIC_LEDS 958c2ecf20Sopenharmony_ci if (spec->led_cdevs[LED_AUDIO_MUTE]) 968c2ecf20Sopenharmony_ci led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]); 978c2ecf20Sopenharmony_ci if (spec->led_cdevs[LED_AUDIO_MICMUTE]) 988c2ecf20Sopenharmony_ci led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]); 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * store user hints 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic void parse_user_hints(struct hda_codec *codec) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 1088c2ecf20Sopenharmony_ci int val; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "jack_detect"); 1118c2ecf20Sopenharmony_ci if (val >= 0) 1128c2ecf20Sopenharmony_ci codec->no_jack_detect = !val; 1138c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); 1148c2ecf20Sopenharmony_ci if (val >= 0) 1158c2ecf20Sopenharmony_ci codec->inv_jack_detect = !!val; 1168c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "trigger_sense"); 1178c2ecf20Sopenharmony_ci if (val >= 0) 1188c2ecf20Sopenharmony_ci codec->no_trigger_sense = !val; 1198c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_eapd"); 1208c2ecf20Sopenharmony_ci if (val >= 0) 1218c2ecf20Sopenharmony_ci codec->inv_eapd = !!val; 1228c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "pcm_format_first"); 1238c2ecf20Sopenharmony_ci if (val >= 0) 1248c2ecf20Sopenharmony_ci codec->pcm_format_first = !!val; 1258c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "sticky_stream"); 1268c2ecf20Sopenharmony_ci if (val >= 0) 1278c2ecf20Sopenharmony_ci codec->no_sticky_stream = !val; 1288c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); 1298c2ecf20Sopenharmony_ci if (val >= 0) 1308c2ecf20Sopenharmony_ci codec->spdif_status_reset = !!val; 1318c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); 1328c2ecf20Sopenharmony_ci if (val >= 0) 1338c2ecf20Sopenharmony_ci codec->pin_amp_workaround = !!val; 1348c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "single_adc_amp"); 1358c2ecf20Sopenharmony_ci if (val >= 0) 1368c2ecf20Sopenharmony_ci codec->single_adc_amp = !!val; 1378c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "power_save_node"); 1388c2ecf20Sopenharmony_ci if (val >= 0) 1398c2ecf20Sopenharmony_ci codec->power_save_node = !!val; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mute"); 1428c2ecf20Sopenharmony_ci if (val >= 0) 1438c2ecf20Sopenharmony_ci spec->suppress_auto_mute = !val; 1448c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mic"); 1458c2ecf20Sopenharmony_ci if (val >= 0) 1468c2ecf20Sopenharmony_ci spec->suppress_auto_mic = !val; 1478c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); 1488c2ecf20Sopenharmony_ci if (val >= 0) 1498c2ecf20Sopenharmony_ci spec->line_in_auto_switch = !!val; 1508c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); 1518c2ecf20Sopenharmony_ci if (val >= 0) 1528c2ecf20Sopenharmony_ci spec->auto_mute_via_amp = !!val; 1538c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "need_dac_fix"); 1548c2ecf20Sopenharmony_ci if (val >= 0) 1558c2ecf20Sopenharmony_ci spec->need_dac_fix = !!val; 1568c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "primary_hp"); 1578c2ecf20Sopenharmony_ci if (val >= 0) 1588c2ecf20Sopenharmony_ci spec->no_primary_hp = !val; 1598c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "multi_io"); 1608c2ecf20Sopenharmony_ci if (val >= 0) 1618c2ecf20Sopenharmony_ci spec->no_multi_io = !val; 1628c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); 1638c2ecf20Sopenharmony_ci if (val >= 0) 1648c2ecf20Sopenharmony_ci spec->multi_cap_vol = !!val; 1658c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); 1668c2ecf20Sopenharmony_ci if (val >= 0) 1678c2ecf20Sopenharmony_ci spec->inv_dmic_split = !!val; 1688c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "indep_hp"); 1698c2ecf20Sopenharmony_ci if (val >= 0) 1708c2ecf20Sopenharmony_ci spec->indep_hp = !!val; 1718c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); 1728c2ecf20Sopenharmony_ci if (val >= 0) 1738c2ecf20Sopenharmony_ci spec->add_stereo_mix_input = !!val; 1748c2ecf20Sopenharmony_ci /* the following two are just for compatibility */ 1758c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); 1768c2ecf20Sopenharmony_ci if (val >= 0) 1778c2ecf20Sopenharmony_ci spec->add_jack_modes = !!val; 1788c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); 1798c2ecf20Sopenharmony_ci if (val >= 0) 1808c2ecf20Sopenharmony_ci spec->add_jack_modes = !!val; 1818c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_jack_modes"); 1828c2ecf20Sopenharmony_ci if (val >= 0) 1838c2ecf20Sopenharmony_ci spec->add_jack_modes = !!val; 1848c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "power_down_unused"); 1858c2ecf20Sopenharmony_ci if (val >= 0) 1868c2ecf20Sopenharmony_ci spec->power_down_unused = !!val; 1878c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "add_hp_mic"); 1888c2ecf20Sopenharmony_ci if (val >= 0) 1898c2ecf20Sopenharmony_ci spec->hp_mic = !!val; 1908c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "hp_mic_detect"); 1918c2ecf20Sopenharmony_ci if (val >= 0) 1928c2ecf20Sopenharmony_ci spec->suppress_hp_mic_detect = !val; 1938c2ecf20Sopenharmony_ci val = snd_hda_get_bool_hint(codec, "vmaster"); 1948c2ecf20Sopenharmony_ci if (val >= 0) 1958c2ecf20Sopenharmony_ci spec->suppress_vmaster = !val; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) 1988c2ecf20Sopenharmony_ci spec->mixer_nid = val; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * pin control value accesses 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#define update_pin_ctl(codec, pin, val) \ 2068c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, pin, 0, \ 2078c2ecf20Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, val) 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* restore the pinctl based on the cached value */ 2108c2ecf20Sopenharmony_cistatic inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* set the pinctl target value and write it if requested */ 2168c2ecf20Sopenharmony_cistatic void set_pin_target(struct hda_codec *codec, hda_nid_t pin, 2178c2ecf20Sopenharmony_ci unsigned int val, bool do_write) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci if (!pin) 2208c2ecf20Sopenharmony_ci return; 2218c2ecf20Sopenharmony_ci val = snd_hda_correct_pin_ctl(codec, pin, val); 2228c2ecf20Sopenharmony_ci snd_hda_codec_set_pin_target(codec, pin, val); 2238c2ecf20Sopenharmony_ci if (do_write) 2248c2ecf20Sopenharmony_ci update_pin_ctl(codec, pin, val); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* set pinctl target values for all given pins */ 2288c2ecf20Sopenharmony_cistatic void set_pin_targets(struct hda_codec *codec, int num_pins, 2298c2ecf20Sopenharmony_ci hda_nid_t *pins, unsigned int val) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int i; 2328c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) 2338c2ecf20Sopenharmony_ci set_pin_target(codec, pins[i], val, false); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * parsing paths 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* return the position of NID in the list, or -1 if not found */ 2418c2ecf20Sopenharmony_cistatic int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int i; 2448c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) 2458c2ecf20Sopenharmony_ci if (list[i] == nid) 2468c2ecf20Sopenharmony_ci return i; 2478c2ecf20Sopenharmony_ci return -1; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* return true if the given NID is contained in the path */ 2518c2ecf20Sopenharmony_cistatic bool is_nid_contained(struct nid_path *path, hda_nid_t nid) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic struct nid_path *get_nid_path(struct hda_codec *codec, 2578c2ecf20Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid, 2588c2ecf20Sopenharmony_ci int anchor_nid) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 2618c2ecf20Sopenharmony_ci struct nid_path *path; 2628c2ecf20Sopenharmony_ci int i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 2658c2ecf20Sopenharmony_ci if (path->depth <= 0) 2668c2ecf20Sopenharmony_ci continue; 2678c2ecf20Sopenharmony_ci if ((!from_nid || path->path[0] == from_nid) && 2688c2ecf20Sopenharmony_ci (!to_nid || path->path[path->depth - 1] == to_nid)) { 2698c2ecf20Sopenharmony_ci if (!anchor_nid || 2708c2ecf20Sopenharmony_ci (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || 2718c2ecf20Sopenharmony_ci (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) 2728c2ecf20Sopenharmony_ci return path; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * snd_hda_get_path_idx - get the index number corresponding to the path 2808c2ecf20Sopenharmony_ci * instance 2818c2ecf20Sopenharmony_ci * @codec: the HDA codec 2828c2ecf20Sopenharmony_ci * @path: nid_path object 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * The returned index starts from 1, i.e. the actual array index with offset 1, 2858c2ecf20Sopenharmony_ci * and zero is handled as an invalid path 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ciint snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 2908c2ecf20Sopenharmony_ci struct nid_path *array = spec->paths.list; 2918c2ecf20Sopenharmony_ci ssize_t idx; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!spec->paths.used) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci idx = path - array; 2968c2ecf20Sopenharmony_ci if (idx < 0 || idx >= spec->paths.used) 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci return idx + 1; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_path_idx); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * snd_hda_get_path_from_idx - get the path instance corresponding to the 3048c2ecf20Sopenharmony_ci * given index number 3058c2ecf20Sopenharmony_ci * @codec: the HDA codec 3068c2ecf20Sopenharmony_ci * @idx: the path index 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistruct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (idx <= 0 || idx > spec->paths.used) 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci return snd_array_elem(&spec->paths, idx - 1); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* check whether the given DAC is already found in any existing paths */ 3198c2ecf20Sopenharmony_cistatic bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 3228c2ecf20Sopenharmony_ci const struct nid_path *path; 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 3268c2ecf20Sopenharmony_ci if (path->path[0] == nid) 3278c2ecf20Sopenharmony_ci return true; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci return false; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* check whether the given two widgets can be connected */ 3338c2ecf20Sopenharmony_cistatic bool is_reachable_path(struct hda_codec *codec, 3348c2ecf20Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci if (!from_nid || !to_nid) 3378c2ecf20Sopenharmony_ci return false; 3388c2ecf20Sopenharmony_ci return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* nid, dir and idx */ 3428c2ecf20Sopenharmony_ci#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/* check whether the given ctl is already assigned in any path elements */ 3458c2ecf20Sopenharmony_cistatic bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 3488c2ecf20Sopenharmony_ci const struct nid_path *path; 3498c2ecf20Sopenharmony_ci int i; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci val &= AMP_VAL_COMPARE_MASK; 3528c2ecf20Sopenharmony_ci snd_array_for_each(&spec->paths, i, path) { 3538c2ecf20Sopenharmony_ci if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) 3548c2ecf20Sopenharmony_ci return true; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci return false; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* check whether a control with the given (nid, dir, idx) was assigned */ 3608c2ecf20Sopenharmony_cistatic bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, 3618c2ecf20Sopenharmony_ci int dir, int idx, int type) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); 3648c2ecf20Sopenharmony_ci return is_ctl_used(codec, val, type); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void print_nid_path(struct hda_codec *codec, 3688c2ecf20Sopenharmony_ci const char *pfx, struct nid_path *path) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci char buf[40]; 3718c2ecf20Sopenharmony_ci char *pos = buf; 3728c2ecf20Sopenharmony_ci int i; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci *pos = 0; 3758c2ecf20Sopenharmony_ci for (i = 0; i < path->depth; i++) 3768c2ecf20Sopenharmony_ci pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", 3778c2ecf20Sopenharmony_ci pos != buf ? ":" : "", 3788c2ecf20Sopenharmony_ci path->path[i]); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* called recursively */ 3848c2ecf20Sopenharmony_cistatic bool __parse_nid_path(struct hda_codec *codec, 3858c2ecf20Sopenharmony_ci hda_nid_t from_nid, hda_nid_t to_nid, 3868c2ecf20Sopenharmony_ci int anchor_nid, struct nid_path *path, 3878c2ecf20Sopenharmony_ci int depth) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci const hda_nid_t *conn; 3908c2ecf20Sopenharmony_ci int i, nums; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (to_nid == anchor_nid) 3938c2ecf20Sopenharmony_ci anchor_nid = 0; /* anchor passed */ 3948c2ecf20Sopenharmony_ci else if (to_nid == (hda_nid_t)(-anchor_nid)) 3958c2ecf20Sopenharmony_ci return false; /* hit the exclusive nid */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci nums = snd_hda_get_conn_list(codec, to_nid, &conn); 3988c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) { 3998c2ecf20Sopenharmony_ci if (conn[i] != from_nid) { 4008c2ecf20Sopenharmony_ci /* special case: when from_nid is 0, 4018c2ecf20Sopenharmony_ci * try to find an empty DAC 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci if (from_nid || 4048c2ecf20Sopenharmony_ci get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || 4058c2ecf20Sopenharmony_ci is_dac_already_used(codec, conn[i])) 4068c2ecf20Sopenharmony_ci continue; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci /* anchor is not requested or already passed? */ 4098c2ecf20Sopenharmony_ci if (anchor_nid <= 0) 4108c2ecf20Sopenharmony_ci goto found; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci if (depth >= MAX_NID_PATH_DEPTH) 4138c2ecf20Sopenharmony_ci return false; 4148c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) { 4158c2ecf20Sopenharmony_ci unsigned int type; 4168c2ecf20Sopenharmony_ci type = get_wcaps_type(get_wcaps(codec, conn[i])); 4178c2ecf20Sopenharmony_ci if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || 4188c2ecf20Sopenharmony_ci type == AC_WID_PIN) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci if (__parse_nid_path(codec, from_nid, conn[i], 4218c2ecf20Sopenharmony_ci anchor_nid, path, depth + 1)) 4228c2ecf20Sopenharmony_ci goto found; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci return false; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci found: 4278c2ecf20Sopenharmony_ci path->path[path->depth] = conn[i]; 4288c2ecf20Sopenharmony_ci path->idx[path->depth + 1] = i; 4298c2ecf20Sopenharmony_ci if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) 4308c2ecf20Sopenharmony_ci path->multi[path->depth + 1] = 1; 4318c2ecf20Sopenharmony_ci path->depth++; 4328c2ecf20Sopenharmony_ci return true; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* 4368c2ecf20Sopenharmony_ci * snd_hda_parse_nid_path - parse the widget path from the given nid to 4378c2ecf20Sopenharmony_ci * the target nid 4388c2ecf20Sopenharmony_ci * @codec: the HDA codec 4398c2ecf20Sopenharmony_ci * @from_nid: the NID where the path start from 4408c2ecf20Sopenharmony_ci * @to_nid: the NID where the path ends at 4418c2ecf20Sopenharmony_ci * @anchor_nid: the anchor indication 4428c2ecf20Sopenharmony_ci * @path: the path object to store the result 4438c2ecf20Sopenharmony_ci * 4448c2ecf20Sopenharmony_ci * Returns true if a matching path is found. 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * The parsing behavior depends on parameters: 4478c2ecf20Sopenharmony_ci * when @from_nid is 0, try to find an empty DAC; 4488c2ecf20Sopenharmony_ci * when @anchor_nid is set to a positive value, only paths through the widget 4498c2ecf20Sopenharmony_ci * with the given value are evaluated. 4508c2ecf20Sopenharmony_ci * when @anchor_nid is set to a negative value, paths through the widget 4518c2ecf20Sopenharmony_ci * with the negative of given value are excluded, only other paths are chosen. 4528c2ecf20Sopenharmony_ci * when @anchor_nid is zero, no special handling about path selection. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, 4558c2ecf20Sopenharmony_ci hda_nid_t to_nid, int anchor_nid, 4568c2ecf20Sopenharmony_ci struct nid_path *path) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { 4598c2ecf20Sopenharmony_ci path->path[path->depth] = to_nid; 4608c2ecf20Sopenharmony_ci path->depth++; 4618c2ecf20Sopenharmony_ci return true; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci return false; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/** 4678c2ecf20Sopenharmony_ci * snd_hda_add_new_path - parse the path between the given NIDs and 4688c2ecf20Sopenharmony_ci * add to the path list 4698c2ecf20Sopenharmony_ci * @codec: the HDA codec 4708c2ecf20Sopenharmony_ci * @from_nid: the NID where the path start from 4718c2ecf20Sopenharmony_ci * @to_nid: the NID where the path ends at 4728c2ecf20Sopenharmony_ci * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * If no valid path is found, returns NULL. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_cistruct nid_path * 4778c2ecf20Sopenharmony_cisnd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, 4788c2ecf20Sopenharmony_ci hda_nid_t to_nid, int anchor_nid) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 4818c2ecf20Sopenharmony_ci struct nid_path *path; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) 4848c2ecf20Sopenharmony_ci return NULL; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* check whether the path has been already added */ 4878c2ecf20Sopenharmony_ci path = get_nid_path(codec, from_nid, to_nid, anchor_nid); 4888c2ecf20Sopenharmony_ci if (path) 4898c2ecf20Sopenharmony_ci return path; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci path = snd_array_new(&spec->paths); 4928c2ecf20Sopenharmony_ci if (!path) 4938c2ecf20Sopenharmony_ci return NULL; 4948c2ecf20Sopenharmony_ci memset(path, 0, sizeof(*path)); 4958c2ecf20Sopenharmony_ci if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) 4968c2ecf20Sopenharmony_ci return path; 4978c2ecf20Sopenharmony_ci /* push back */ 4988c2ecf20Sopenharmony_ci spec->paths.used--; 4998c2ecf20Sopenharmony_ci return NULL; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_new_path); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* clear the given path as invalid so that it won't be picked up later */ 5048c2ecf20Sopenharmony_cistatic void invalidate_nid_path(struct hda_codec *codec, int idx) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); 5078c2ecf20Sopenharmony_ci if (!path) 5088c2ecf20Sopenharmony_ci return; 5098c2ecf20Sopenharmony_ci memset(path, 0, sizeof(*path)); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* return a DAC if paired to the given pin by codec driver */ 5138c2ecf20Sopenharmony_cistatic hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 5168c2ecf20Sopenharmony_ci const hda_nid_t *list = spec->preferred_dacs; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!list) 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci for (; *list; list += 2) 5218c2ecf20Sopenharmony_ci if (*list == pin) 5228c2ecf20Sopenharmony_ci return list[1]; 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/* look for an empty DAC slot */ 5278c2ecf20Sopenharmony_cistatic hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, 5288c2ecf20Sopenharmony_ci bool is_digital) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 5318c2ecf20Sopenharmony_ci bool cap_digital; 5328c2ecf20Sopenharmony_ci int i; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci for (i = 0; i < spec->num_all_dacs; i++) { 5358c2ecf20Sopenharmony_ci hda_nid_t nid = spec->all_dacs[i]; 5368c2ecf20Sopenharmony_ci if (!nid || is_dac_already_used(codec, nid)) 5378c2ecf20Sopenharmony_ci continue; 5388c2ecf20Sopenharmony_ci cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); 5398c2ecf20Sopenharmony_ci if (is_digital != cap_digital) 5408c2ecf20Sopenharmony_ci continue; 5418c2ecf20Sopenharmony_ci if (is_reachable_path(codec, nid, pin)) 5428c2ecf20Sopenharmony_ci return nid; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* replace the channels in the composed amp value with the given number */ 5488c2ecf20Sopenharmony_cistatic unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci val &= ~(0x3U << 16); 5518c2ecf20Sopenharmony_ci val |= chs << 16; 5528c2ecf20Sopenharmony_ci return val; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, 5568c2ecf20Sopenharmony_ci hda_nid_t nid2, int dir) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) 5598c2ecf20Sopenharmony_ci return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); 5608c2ecf20Sopenharmony_ci return (query_amp_caps(codec, nid1, dir) == 5618c2ecf20Sopenharmony_ci query_amp_caps(codec, nid2, dir)); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* look for a widget suitable for assigning a mute switch in the path */ 5658c2ecf20Sopenharmony_cistatic hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, 5668c2ecf20Sopenharmony_ci struct nid_path *path) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci int i; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 5718c2ecf20Sopenharmony_ci if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) 5728c2ecf20Sopenharmony_ci return path->path[i]; 5738c2ecf20Sopenharmony_ci if (i != path->depth - 1 && i != 0 && 5748c2ecf20Sopenharmony_ci nid_has_mute(codec, path->path[i], HDA_INPUT)) 5758c2ecf20Sopenharmony_ci return path->path[i]; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* look for a widget suitable for assigning a volume ctl in the path */ 5818c2ecf20Sopenharmony_cistatic hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, 5828c2ecf20Sopenharmony_ci struct nid_path *path) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 5858c2ecf20Sopenharmony_ci int i; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 5888c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[i]; 5898c2ecf20Sopenharmony_ci if ((spec->out_vol_mask >> nid) & 1) 5908c2ecf20Sopenharmony_ci continue; 5918c2ecf20Sopenharmony_ci if (nid_has_volume(codec, nid, HDA_OUTPUT)) 5928c2ecf20Sopenharmony_ci return nid; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci/* 5988c2ecf20Sopenharmony_ci * path activation / deactivation 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* can have the amp-in capability? */ 6028c2ecf20Sopenharmony_cistatic bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[idx]; 6058c2ecf20Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 6068c2ecf20Sopenharmony_ci unsigned int type = get_wcaps_type(caps); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (!(caps & AC_WCAP_IN_AMP)) 6098c2ecf20Sopenharmony_ci return false; 6108c2ecf20Sopenharmony_ci if (type == AC_WID_PIN && idx > 0) /* only for input pins */ 6118c2ecf20Sopenharmony_ci return false; 6128c2ecf20Sopenharmony_ci return true; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* can have the amp-out capability? */ 6168c2ecf20Sopenharmony_cistatic bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[idx]; 6198c2ecf20Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 6208c2ecf20Sopenharmony_ci unsigned int type = get_wcaps_type(caps); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (!(caps & AC_WCAP_OUT_AMP)) 6238c2ecf20Sopenharmony_ci return false; 6248c2ecf20Sopenharmony_ci if (type == AC_WID_PIN && !idx) /* only for output pins */ 6258c2ecf20Sopenharmony_ci return false; 6268c2ecf20Sopenharmony_ci return true; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* check whether the given (nid,dir,idx) is active */ 6308c2ecf20Sopenharmony_cistatic bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, 6318c2ecf20Sopenharmony_ci unsigned int dir, unsigned int idx) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 6348c2ecf20Sopenharmony_ci int type = get_wcaps_type(get_wcaps(codec, nid)); 6358c2ecf20Sopenharmony_ci const struct nid_path *path; 6368c2ecf20Sopenharmony_ci int i, n; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (nid == codec->core.afg) 6398c2ecf20Sopenharmony_ci return true; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci snd_array_for_each(&spec->paths, n, path) { 6428c2ecf20Sopenharmony_ci if (!path->active) 6438c2ecf20Sopenharmony_ci continue; 6448c2ecf20Sopenharmony_ci if (codec->power_save_node) { 6458c2ecf20Sopenharmony_ci if (!path->stream_enabled) 6468c2ecf20Sopenharmony_ci continue; 6478c2ecf20Sopenharmony_ci /* ignore unplugged paths except for DAC/ADC */ 6488c2ecf20Sopenharmony_ci if (!(path->pin_enabled || path->pin_fixed) && 6498c2ecf20Sopenharmony_ci type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) 6508c2ecf20Sopenharmony_ci continue; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci for (i = 0; i < path->depth; i++) { 6538c2ecf20Sopenharmony_ci if (path->path[i] == nid) { 6548c2ecf20Sopenharmony_ci if (dir == HDA_OUTPUT || idx == -1 || 6558c2ecf20Sopenharmony_ci path->idx[i] == idx) 6568c2ecf20Sopenharmony_ci return true; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci return false; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* check whether the NID is referred by any active paths */ 6658c2ecf20Sopenharmony_ci#define is_active_nid_for_any(codec, nid) \ 6668c2ecf20Sopenharmony_ci is_active_nid(codec, nid, HDA_OUTPUT, -1) 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* get the default amp value for the target state */ 6698c2ecf20Sopenharmony_cistatic int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, 6708c2ecf20Sopenharmony_ci int dir, unsigned int caps, bool enable) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci unsigned int val = 0; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (caps & AC_AMPCAP_NUM_STEPS) { 6758c2ecf20Sopenharmony_ci /* set to 0dB */ 6768c2ecf20Sopenharmony_ci if (enable) 6778c2ecf20Sopenharmony_ci val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { 6808c2ecf20Sopenharmony_ci if (!enable) 6818c2ecf20Sopenharmony_ci val |= HDA_AMP_MUTE; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci return val; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* is this a stereo widget or a stereo-to-mono mix? */ 6878c2ecf20Sopenharmony_cistatic bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 6908c2ecf20Sopenharmony_ci hda_nid_t conn; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (wcaps & AC_WCAP_STEREO) 6938c2ecf20Sopenharmony_ci return true; 6948c2ecf20Sopenharmony_ci if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) 6958c2ecf20Sopenharmony_ci return false; 6968c2ecf20Sopenharmony_ci if (snd_hda_get_num_conns(codec, nid) != 1) 6978c2ecf20Sopenharmony_ci return false; 6988c2ecf20Sopenharmony_ci if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) 6998c2ecf20Sopenharmony_ci return false; 7008c2ecf20Sopenharmony_ci return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* initialize the amp value (only at the first time) */ 7048c2ecf20Sopenharmony_cistatic void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci unsigned int caps = query_amp_caps(codec, nid, dir); 7078c2ecf20Sopenharmony_ci int val = get_amp_val_to_activate(codec, nid, dir, caps, false); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (is_stereo_amps(codec, nid, dir)) 7108c2ecf20Sopenharmony_ci snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); 7118c2ecf20Sopenharmony_ci else 7128c2ecf20Sopenharmony_ci snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* update the amp, doing in stereo or mono depending on NID */ 7168c2ecf20Sopenharmony_cistatic int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, 7178c2ecf20Sopenharmony_ci unsigned int mask, unsigned int val) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci if (is_stereo_amps(codec, nid, dir)) 7208c2ecf20Sopenharmony_ci return snd_hda_codec_amp_stereo(codec, nid, dir, idx, 7218c2ecf20Sopenharmony_ci mask, val); 7228c2ecf20Sopenharmony_ci else 7238c2ecf20Sopenharmony_ci return snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 7248c2ecf20Sopenharmony_ci mask, val); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/* calculate amp value mask we can modify; 7288c2ecf20Sopenharmony_ci * if the given amp is controlled by mixers, don't touch it 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_cistatic unsigned int get_amp_mask_to_modify(struct hda_codec *codec, 7318c2ecf20Sopenharmony_ci hda_nid_t nid, int dir, int idx, 7328c2ecf20Sopenharmony_ci unsigned int caps) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci unsigned int mask = 0xff; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { 7378c2ecf20Sopenharmony_ci if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) 7388c2ecf20Sopenharmony_ci mask &= ~0x80; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci if (caps & AC_AMPCAP_NUM_STEPS) { 7418c2ecf20Sopenharmony_ci if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || 7428c2ecf20Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) 7438c2ecf20Sopenharmony_ci mask &= ~0x7f; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci return mask; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, 7498c2ecf20Sopenharmony_ci int idx, int idx_to_check, bool enable) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci unsigned int caps; 7528c2ecf20Sopenharmony_ci unsigned int mask, val; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci caps = query_amp_caps(codec, nid, dir); 7558c2ecf20Sopenharmony_ci val = get_amp_val_to_activate(codec, nid, dir, caps, enable); 7568c2ecf20Sopenharmony_ci mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); 7578c2ecf20Sopenharmony_ci if (!mask) 7588c2ecf20Sopenharmony_ci return; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci val &= mask; 7618c2ecf20Sopenharmony_ci update_amp(codec, nid, dir, idx, mask, val); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid, 7658c2ecf20Sopenharmony_ci int dir, int idx, int idx_to_check, 7668c2ecf20Sopenharmony_ci bool enable) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci /* check whether the given amp is still used by others */ 7698c2ecf20Sopenharmony_ci if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) 7708c2ecf20Sopenharmony_ci return; 7718c2ecf20Sopenharmony_ci activate_amp(codec, nid, dir, idx, idx_to_check, enable); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void activate_amp_out(struct hda_codec *codec, struct nid_path *path, 7758c2ecf20Sopenharmony_ci int i, bool enable) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[i]; 7788c2ecf20Sopenharmony_ci init_amp(codec, nid, HDA_OUTPUT, 0); 7798c2ecf20Sopenharmony_ci check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic void activate_amp_in(struct hda_codec *codec, struct nid_path *path, 7838c2ecf20Sopenharmony_ci int i, bool enable, bool add_aamix) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 7868c2ecf20Sopenharmony_ci const hda_nid_t *conn; 7878c2ecf20Sopenharmony_ci int n, nums, idx; 7888c2ecf20Sopenharmony_ci int type; 7898c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[i]; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci nums = snd_hda_get_conn_list(codec, nid, &conn); 7928c2ecf20Sopenharmony_ci if (nums < 0) 7938c2ecf20Sopenharmony_ci return; 7948c2ecf20Sopenharmony_ci type = get_wcaps_type(get_wcaps(codec, nid)); 7958c2ecf20Sopenharmony_ci if (type == AC_WID_PIN || 7968c2ecf20Sopenharmony_ci (type == AC_WID_AUD_IN && codec->single_adc_amp)) { 7978c2ecf20Sopenharmony_ci nums = 1; 7988c2ecf20Sopenharmony_ci idx = 0; 7998c2ecf20Sopenharmony_ci } else 8008c2ecf20Sopenharmony_ci idx = path->idx[i]; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (n = 0; n < nums; n++) 8038c2ecf20Sopenharmony_ci init_amp(codec, nid, HDA_INPUT, n); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* here is a little bit tricky in comparison with activate_amp_out(); 8068c2ecf20Sopenharmony_ci * when aa-mixer is available, we need to enable the path as well 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci for (n = 0; n < nums; n++) { 8098c2ecf20Sopenharmony_ci if (n != idx) { 8108c2ecf20Sopenharmony_ci if (conn[n] != spec->mixer_merge_nid) 8118c2ecf20Sopenharmony_ci continue; 8128c2ecf20Sopenharmony_ci /* when aamix is disabled, force to off */ 8138c2ecf20Sopenharmony_ci if (!add_aamix) { 8148c2ecf20Sopenharmony_ci activate_amp(codec, nid, HDA_INPUT, n, n, false); 8158c2ecf20Sopenharmony_ci continue; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/* sync power of each widget in the given path */ 8238c2ecf20Sopenharmony_cistatic hda_nid_t path_power_update(struct hda_codec *codec, 8248c2ecf20Sopenharmony_ci struct nid_path *path, 8258c2ecf20Sopenharmony_ci bool allow_powerdown) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci hda_nid_t nid, changed = 0; 8288c2ecf20Sopenharmony_ci int i, state, power; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci for (i = 0; i < path->depth; i++) { 8318c2ecf20Sopenharmony_ci nid = path->path[i]; 8328c2ecf20Sopenharmony_ci if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) 8338c2ecf20Sopenharmony_ci continue; 8348c2ecf20Sopenharmony_ci if (nid == codec->core.afg) 8358c2ecf20Sopenharmony_ci continue; 8368c2ecf20Sopenharmony_ci if (!allow_powerdown || is_active_nid_for_any(codec, nid)) 8378c2ecf20Sopenharmony_ci state = AC_PWRST_D0; 8388c2ecf20Sopenharmony_ci else 8398c2ecf20Sopenharmony_ci state = AC_PWRST_D3; 8408c2ecf20Sopenharmony_ci power = snd_hda_codec_read(codec, nid, 0, 8418c2ecf20Sopenharmony_ci AC_VERB_GET_POWER_STATE, 0); 8428c2ecf20Sopenharmony_ci if (power != (state | (state << 4))) { 8438c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, 8448c2ecf20Sopenharmony_ci AC_VERB_SET_POWER_STATE, state); 8458c2ecf20Sopenharmony_ci changed = nid; 8468c2ecf20Sopenharmony_ci /* all known codecs seem to be capable to handl 8478c2ecf20Sopenharmony_ci * widgets state even in D3, so far. 8488c2ecf20Sopenharmony_ci * if any new codecs need to restore the widget 8498c2ecf20Sopenharmony_ci * states after D0 transition, call the function 8508c2ecf20Sopenharmony_ci * below. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci#if 0 /* disabled */ 8538c2ecf20Sopenharmony_ci if (state == AC_PWRST_D0) 8548c2ecf20Sopenharmony_ci snd_hdac_regmap_sync_node(&codec->core, nid); 8558c2ecf20Sopenharmony_ci#endif 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci return changed; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci/* do sync with the last power state change */ 8628c2ecf20Sopenharmony_cistatic void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci if (nid) { 8658c2ecf20Sopenharmony_ci msleep(10); 8668c2ecf20Sopenharmony_ci snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/** 8718c2ecf20Sopenharmony_ci * snd_hda_activate_path - activate or deactivate the given path 8728c2ecf20Sopenharmony_ci * @codec: the HDA codec 8738c2ecf20Sopenharmony_ci * @path: the path to activate/deactivate 8748c2ecf20Sopenharmony_ci * @enable: flag to activate or not 8758c2ecf20Sopenharmony_ci * @add_aamix: enable the input from aamix NID 8768c2ecf20Sopenharmony_ci * 8778c2ecf20Sopenharmony_ci * If @add_aamix is set, enable the input from aa-mix NID as well (if any). 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_civoid snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, 8808c2ecf20Sopenharmony_ci bool enable, bool add_aamix) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 8838c2ecf20Sopenharmony_ci int i; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci path->active = enable; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* make sure the widget is powered up */ 8888c2ecf20Sopenharmony_ci if (enable && (spec->power_down_unused || codec->power_save_node)) 8898c2ecf20Sopenharmony_ci path_power_update(codec, path, codec->power_save_node); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci for (i = path->depth - 1; i >= 0; i--) { 8928c2ecf20Sopenharmony_ci hda_nid_t nid = path->path[i]; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (enable && path->multi[i]) 8958c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, nid, 0, 8968c2ecf20Sopenharmony_ci AC_VERB_SET_CONNECT_SEL, 8978c2ecf20Sopenharmony_ci path->idx[i]); 8988c2ecf20Sopenharmony_ci if (has_amp_in(codec, path, i)) 8998c2ecf20Sopenharmony_ci activate_amp_in(codec, path, i, enable, add_aamix); 9008c2ecf20Sopenharmony_ci if (has_amp_out(codec, path, i)) 9018c2ecf20Sopenharmony_ci activate_amp_out(codec, path, i, enable); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_activate_path); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/* if the given path is inactive, put widgets into D3 (only if suitable) */ 9078c2ecf20Sopenharmony_cistatic void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (!(spec->power_down_unused || codec->power_save_node) || path->active) 9128c2ecf20Sopenharmony_ci return; 9138c2ecf20Sopenharmony_ci sync_power_state_change(codec, path_power_update(codec, path, true)); 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/* turn on/off EAPD on the given pin */ 9178c2ecf20Sopenharmony_cistatic void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 9208c2ecf20Sopenharmony_ci if (spec->own_eapd_ctl || 9218c2ecf20Sopenharmony_ci !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci if (spec->keep_eapd_on && !enable) 9248c2ecf20Sopenharmony_ci return; 9258c2ecf20Sopenharmony_ci if (codec->inv_eapd) 9268c2ecf20Sopenharmony_ci enable = !enable; 9278c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, pin, 0, 9288c2ecf20Sopenharmony_ci AC_VERB_SET_EAPD_BTLENABLE, 9298c2ecf20Sopenharmony_ci enable ? 0x02 : 0x00); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/* re-initialize the path specified by the given path index */ 9338c2ecf20Sopenharmony_cistatic void resume_path_from_idx(struct hda_codec *codec, int path_idx) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); 9368c2ecf20Sopenharmony_ci if (path) 9378c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, false); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci/* 9428c2ecf20Sopenharmony_ci * Helper functions for creating mixer ctl elements 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cistatic int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, 9468c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 9478c2ecf20Sopenharmony_cistatic int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, 9488c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 9498c2ecf20Sopenharmony_cistatic int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, 9508c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cienum { 9538c2ecf20Sopenharmony_ci HDA_CTL_WIDGET_VOL, 9548c2ecf20Sopenharmony_ci HDA_CTL_WIDGET_MUTE, 9558c2ecf20Sopenharmony_ci HDA_CTL_BIND_MUTE, 9568c2ecf20Sopenharmony_ci}; 9578c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new control_templates[] = { 9588c2ecf20Sopenharmony_ci HDA_CODEC_VOLUME(NULL, 0, 0, 0), 9598c2ecf20Sopenharmony_ci /* only the put callback is replaced for handling the special mute */ 9608c2ecf20Sopenharmony_ci { 9618c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9628c2ecf20Sopenharmony_ci .subdevice = HDA_SUBDEV_AMP_FLAG, 9638c2ecf20Sopenharmony_ci .info = snd_hda_mixer_amp_switch_info, 9648c2ecf20Sopenharmony_ci .get = snd_hda_mixer_amp_switch_get, 9658c2ecf20Sopenharmony_ci .put = hda_gen_mixer_mute_put, /* replaced */ 9668c2ecf20Sopenharmony_ci .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), 9678c2ecf20Sopenharmony_ci }, 9688c2ecf20Sopenharmony_ci { 9698c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9708c2ecf20Sopenharmony_ci .info = snd_hda_mixer_amp_switch_info, 9718c2ecf20Sopenharmony_ci .get = hda_gen_bind_mute_get, 9728c2ecf20Sopenharmony_ci .put = hda_gen_bind_mute_put, /* replaced */ 9738c2ecf20Sopenharmony_ci .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), 9748c2ecf20Sopenharmony_ci }, 9758c2ecf20Sopenharmony_ci}; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/* add dynamic controls from template */ 9788c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new * 9798c2ecf20Sopenharmony_ciadd_control(struct hda_gen_spec *spec, int type, const char *name, 9808c2ecf20Sopenharmony_ci int cidx, unsigned long val) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); 9858c2ecf20Sopenharmony_ci if (!knew) 9868c2ecf20Sopenharmony_ci return NULL; 9878c2ecf20Sopenharmony_ci knew->index = cidx; 9888c2ecf20Sopenharmony_ci if (get_amp_nid_(val)) 9898c2ecf20Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 9908c2ecf20Sopenharmony_ci knew->private_value = val; 9918c2ecf20Sopenharmony_ci return knew; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int add_control_with_pfx(struct hda_gen_spec *spec, int type, 9958c2ecf20Sopenharmony_ci const char *pfx, const char *dir, 9968c2ecf20Sopenharmony_ci const char *sfx, int cidx, unsigned long val) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 9998c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); 10008c2ecf20Sopenharmony_ci if (!add_control(spec, type, name, cidx, val)) 10018c2ecf20Sopenharmony_ci return -ENOMEM; 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci#define add_pb_vol_ctrl(spec, type, pfx, val) \ 10068c2ecf20Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) 10078c2ecf20Sopenharmony_ci#define add_pb_sw_ctrl(spec, type, pfx, val) \ 10088c2ecf20Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) 10098c2ecf20Sopenharmony_ci#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ 10108c2ecf20Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) 10118c2ecf20Sopenharmony_ci#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ 10128c2ecf20Sopenharmony_ci add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, 10158c2ecf20Sopenharmony_ci unsigned int chs, struct nid_path *path) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci unsigned int val; 10188c2ecf20Sopenharmony_ci if (!path) 10198c2ecf20Sopenharmony_ci return 0; 10208c2ecf20Sopenharmony_ci val = path->ctls[NID_PATH_VOL_CTL]; 10218c2ecf20Sopenharmony_ci if (!val) 10228c2ecf20Sopenharmony_ci return 0; 10238c2ecf20Sopenharmony_ci val = amp_val_replace_channels(val, chs); 10248c2ecf20Sopenharmony_ci return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci/* return the channel bits suitable for the given path->ctls[] */ 10288c2ecf20Sopenharmony_cistatic int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, 10298c2ecf20Sopenharmony_ci int type) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci int chs = 1; /* mono (left only) */ 10328c2ecf20Sopenharmony_ci if (path) { 10338c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid_(path->ctls[type]); 10348c2ecf20Sopenharmony_ci if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) 10358c2ecf20Sopenharmony_ci chs = 3; /* stereo */ 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci return chs; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, 10418c2ecf20Sopenharmony_ci struct nid_path *path) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); 10448c2ecf20Sopenharmony_ci return add_vol_ctl(codec, pfx, cidx, chs, path); 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/* create a mute-switch for the given mixer widget; 10488c2ecf20Sopenharmony_ci * if it has multiple sources (e.g. DAC and loopback), create a bind-mute 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_cistatic int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, 10518c2ecf20Sopenharmony_ci unsigned int chs, struct nid_path *path) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci unsigned int val; 10548c2ecf20Sopenharmony_ci int type = HDA_CTL_WIDGET_MUTE; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (!path) 10578c2ecf20Sopenharmony_ci return 0; 10588c2ecf20Sopenharmony_ci val = path->ctls[NID_PATH_MUTE_CTL]; 10598c2ecf20Sopenharmony_ci if (!val) 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci val = amp_val_replace_channels(val, chs); 10628c2ecf20Sopenharmony_ci if (get_amp_direction_(val) == HDA_INPUT) { 10638c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid_(val); 10648c2ecf20Sopenharmony_ci int nums = snd_hda_get_num_conns(codec, nid); 10658c2ecf20Sopenharmony_ci if (nums > 1) { 10668c2ecf20Sopenharmony_ci type = HDA_CTL_BIND_MUTE; 10678c2ecf20Sopenharmony_ci val |= nums << 19; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic int add_stereo_sw(struct hda_codec *codec, const char *pfx, 10748c2ecf20Sopenharmony_ci int cidx, struct nid_path *path) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); 10778c2ecf20Sopenharmony_ci return add_sw_ctl(codec, pfx, cidx, chs, path); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci/* playback mute control with the software mute bit check */ 10818c2ecf20Sopenharmony_cistatic void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, 10828c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 10858c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (spec->auto_mute_via_amp) { 10888c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 10898c2ecf20Sopenharmony_ci bool enabled = !((spec->mute_bits >> nid) & 1); 10908c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] &= enabled; 10918c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] &= enabled; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, 10968c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci sync_auto_mute_bits(kcontrol, ucontrol); 10998c2ecf20Sopenharmony_ci return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci/* 11038c2ecf20Sopenharmony_ci * Bound mute controls 11048c2ecf20Sopenharmony_ci */ 11058c2ecf20Sopenharmony_ci#define AMP_VAL_IDX_SHIFT 19 11068c2ecf20Sopenharmony_ci#define AMP_VAL_IDX_MASK (0x0f<<19) 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, 11098c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11128c2ecf20Sopenharmony_ci unsigned long pval; 11138c2ecf20Sopenharmony_ci int err; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci mutex_lock(&codec->control_mutex); 11168c2ecf20Sopenharmony_ci pval = kcontrol->private_value; 11178c2ecf20Sopenharmony_ci kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ 11188c2ecf20Sopenharmony_ci err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); 11198c2ecf20Sopenharmony_ci kcontrol->private_value = pval; 11208c2ecf20Sopenharmony_ci mutex_unlock(&codec->control_mutex); 11218c2ecf20Sopenharmony_ci return err; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, 11258c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11288c2ecf20Sopenharmony_ci unsigned long pval; 11298c2ecf20Sopenharmony_ci int i, indices, err = 0, change = 0; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci sync_auto_mute_bits(kcontrol, ucontrol); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci mutex_lock(&codec->control_mutex); 11348c2ecf20Sopenharmony_ci pval = kcontrol->private_value; 11358c2ecf20Sopenharmony_ci indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; 11368c2ecf20Sopenharmony_ci for (i = 0; i < indices; i++) { 11378c2ecf20Sopenharmony_ci kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | 11388c2ecf20Sopenharmony_ci (i << AMP_VAL_IDX_SHIFT); 11398c2ecf20Sopenharmony_ci err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 11408c2ecf20Sopenharmony_ci if (err < 0) 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci change |= err; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci kcontrol->private_value = pval; 11458c2ecf20Sopenharmony_ci mutex_unlock(&codec->control_mutex); 11468c2ecf20Sopenharmony_ci return err < 0 ? err : change; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci/* any ctl assigned to the path with the given index? */ 11508c2ecf20Sopenharmony_cistatic bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); 11538c2ecf20Sopenharmony_ci return path && path->ctls[ctl_type]; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic const char * const channel_name[] = { 11578c2ecf20Sopenharmony_ci "Front", "Surround", "CLFE", "Side", "Back", 11588c2ecf20Sopenharmony_ci}; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci/* give some appropriate ctl name prefix for the given line out channel */ 11618c2ecf20Sopenharmony_cistatic const char *get_line_out_pfx(struct hda_codec *codec, int ch, 11628c2ecf20Sopenharmony_ci int *index, int ctl_type) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 11658c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci *index = 0; 11688c2ecf20Sopenharmony_ci if (cfg->line_outs == 1 && !spec->multi_ios && 11698c2ecf20Sopenharmony_ci !codec->force_pin_prefix && 11708c2ecf20Sopenharmony_ci !cfg->hp_outs && !cfg->speaker_outs) 11718c2ecf20Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* if there is really a single DAC used in the whole output paths, 11748c2ecf20Sopenharmony_ci * use it master (or "PCM" if a vmaster hook is present) 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_ci if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && 11778c2ecf20Sopenharmony_ci !codec->force_pin_prefix && 11788c2ecf20Sopenharmony_ci !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) 11798c2ecf20Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* multi-io channels */ 11828c2ecf20Sopenharmony_ci if (ch >= cfg->line_outs) 11838c2ecf20Sopenharmony_ci goto fixed_name; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci switch (cfg->line_out_type) { 11868c2ecf20Sopenharmony_ci case AUTO_PIN_SPEAKER_OUT: 11878c2ecf20Sopenharmony_ci /* if the primary channel vol/mute is shared with HP volume, 11888c2ecf20Sopenharmony_ci * don't name it as Speaker 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ci if (!ch && cfg->hp_outs && 11918c2ecf20Sopenharmony_ci !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) 11928c2ecf20Sopenharmony_ci break; 11938c2ecf20Sopenharmony_ci if (cfg->line_outs == 1) 11948c2ecf20Sopenharmony_ci return "Speaker"; 11958c2ecf20Sopenharmony_ci if (cfg->line_outs == 2) 11968c2ecf20Sopenharmony_ci return ch ? "Bass Speaker" : "Speaker"; 11978c2ecf20Sopenharmony_ci break; 11988c2ecf20Sopenharmony_ci case AUTO_PIN_HP_OUT: 11998c2ecf20Sopenharmony_ci /* if the primary channel vol/mute is shared with spk volume, 12008c2ecf20Sopenharmony_ci * don't name it as Headphone 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_ci if (!ch && cfg->speaker_outs && 12038c2ecf20Sopenharmony_ci !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) 12048c2ecf20Sopenharmony_ci break; 12058c2ecf20Sopenharmony_ci /* for multi-io case, only the primary out */ 12068c2ecf20Sopenharmony_ci if (ch && spec->multi_ios) 12078c2ecf20Sopenharmony_ci break; 12088c2ecf20Sopenharmony_ci *index = ch; 12098c2ecf20Sopenharmony_ci return "Headphone"; 12108c2ecf20Sopenharmony_ci case AUTO_PIN_LINE_OUT: 12118c2ecf20Sopenharmony_ci /* This deals with the case where one HP or one Speaker or 12128c2ecf20Sopenharmony_ci * one HP + one Speaker need to share the DAC with LO 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci if (!ch) { 12158c2ecf20Sopenharmony_ci bool hp_lo_shared = false, spk_lo_shared = false; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (cfg->speaker_outs) 12188c2ecf20Sopenharmony_ci spk_lo_shared = !path_has_mixer(codec, 12198c2ecf20Sopenharmony_ci spec->speaker_paths[0], ctl_type); 12208c2ecf20Sopenharmony_ci if (cfg->hp_outs) 12218c2ecf20Sopenharmony_ci hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); 12228c2ecf20Sopenharmony_ci if (hp_lo_shared && spk_lo_shared) 12238c2ecf20Sopenharmony_ci return spec->vmaster_mute.hook ? "PCM" : "Master"; 12248c2ecf20Sopenharmony_ci if (hp_lo_shared) 12258c2ecf20Sopenharmony_ci return "Headphone+LO"; 12268c2ecf20Sopenharmony_ci if (spk_lo_shared) 12278c2ecf20Sopenharmony_ci return "Speaker+LO"; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* for a single channel output, we don't have to name the channel */ 12328c2ecf20Sopenharmony_ci if (cfg->line_outs == 1 && !spec->multi_ios) 12338c2ecf20Sopenharmony_ci return "Line Out"; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci fixed_name: 12368c2ecf20Sopenharmony_ci if (ch >= ARRAY_SIZE(channel_name)) { 12378c2ecf20Sopenharmony_ci snd_BUG(); 12388c2ecf20Sopenharmony_ci return "PCM"; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return channel_name[ch]; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci/* 12458c2ecf20Sopenharmony_ci * Parse output paths 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci/* badness definition */ 12498c2ecf20Sopenharmony_cienum { 12508c2ecf20Sopenharmony_ci /* No primary DAC is found for the main output */ 12518c2ecf20Sopenharmony_ci BAD_NO_PRIMARY_DAC = 0x10000, 12528c2ecf20Sopenharmony_ci /* No DAC is found for the extra output */ 12538c2ecf20Sopenharmony_ci BAD_NO_DAC = 0x4000, 12548c2ecf20Sopenharmony_ci /* No possible multi-ios */ 12558c2ecf20Sopenharmony_ci BAD_MULTI_IO = 0x120, 12568c2ecf20Sopenharmony_ci /* No individual DAC for extra output */ 12578c2ecf20Sopenharmony_ci BAD_NO_EXTRA_DAC = 0x102, 12588c2ecf20Sopenharmony_ci /* No individual DAC for extra surrounds */ 12598c2ecf20Sopenharmony_ci BAD_NO_EXTRA_SURR_DAC = 0x101, 12608c2ecf20Sopenharmony_ci /* Primary DAC shared with main surrounds */ 12618c2ecf20Sopenharmony_ci BAD_SHARED_SURROUND = 0x100, 12628c2ecf20Sopenharmony_ci /* No independent HP possible */ 12638c2ecf20Sopenharmony_ci BAD_NO_INDEP_HP = 0x10, 12648c2ecf20Sopenharmony_ci /* Primary DAC shared with main CLFE */ 12658c2ecf20Sopenharmony_ci BAD_SHARED_CLFE = 0x10, 12668c2ecf20Sopenharmony_ci /* Primary DAC shared with extra surrounds */ 12678c2ecf20Sopenharmony_ci BAD_SHARED_EXTRA_SURROUND = 0x10, 12688c2ecf20Sopenharmony_ci /* Volume widget is shared */ 12698c2ecf20Sopenharmony_ci BAD_SHARED_VOL = 0x10, 12708c2ecf20Sopenharmony_ci}; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci/* look for widgets in the given path which are appropriate for 12738c2ecf20Sopenharmony_ci * volume and mute controls, and assign the values to ctls[]. 12748c2ecf20Sopenharmony_ci * 12758c2ecf20Sopenharmony_ci * When no appropriate widget is found in the path, the badness value 12768c2ecf20Sopenharmony_ci * is incremented depending on the situation. The function returns the 12778c2ecf20Sopenharmony_ci * total badness for both volume and mute controls. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_cistatic int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 12828c2ecf20Sopenharmony_ci hda_nid_t nid; 12838c2ecf20Sopenharmony_ci unsigned int val; 12848c2ecf20Sopenharmony_ci int badness = 0; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (!path) 12878c2ecf20Sopenharmony_ci return BAD_SHARED_VOL * 2; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (path->ctls[NID_PATH_VOL_CTL] || 12908c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL]) 12918c2ecf20Sopenharmony_ci return 0; /* already evaluated */ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci nid = look_for_out_vol_nid(codec, path); 12948c2ecf20Sopenharmony_ci if (nid) { 12958c2ecf20Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 12968c2ecf20Sopenharmony_ci if (spec->dac_min_mute) 12978c2ecf20Sopenharmony_ci val |= HDA_AMP_VAL_MIN_MUTE; 12988c2ecf20Sopenharmony_ci if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) 12998c2ecf20Sopenharmony_ci badness += BAD_SHARED_VOL; 13008c2ecf20Sopenharmony_ci else 13018c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = val; 13028c2ecf20Sopenharmony_ci } else 13038c2ecf20Sopenharmony_ci badness += BAD_SHARED_VOL; 13048c2ecf20Sopenharmony_ci nid = look_for_out_mute_nid(codec, path); 13058c2ecf20Sopenharmony_ci if (nid) { 13068c2ecf20Sopenharmony_ci unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); 13078c2ecf20Sopenharmony_ci if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || 13088c2ecf20Sopenharmony_ci nid_has_mute(codec, nid, HDA_OUTPUT)) 13098c2ecf20Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 13108c2ecf20Sopenharmony_ci else 13118c2ecf20Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); 13128c2ecf20Sopenharmony_ci if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) 13138c2ecf20Sopenharmony_ci badness += BAD_SHARED_VOL; 13148c2ecf20Sopenharmony_ci else 13158c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = val; 13168c2ecf20Sopenharmony_ci } else 13178c2ecf20Sopenharmony_ci badness += BAD_SHARED_VOL; 13188c2ecf20Sopenharmony_ci return badness; 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ciconst struct badness_table hda_main_out_badness = { 13228c2ecf20Sopenharmony_ci .no_primary_dac = BAD_NO_PRIMARY_DAC, 13238c2ecf20Sopenharmony_ci .no_dac = BAD_NO_DAC, 13248c2ecf20Sopenharmony_ci .shared_primary = BAD_NO_PRIMARY_DAC, 13258c2ecf20Sopenharmony_ci .shared_surr = BAD_SHARED_SURROUND, 13268c2ecf20Sopenharmony_ci .shared_clfe = BAD_SHARED_CLFE, 13278c2ecf20Sopenharmony_ci .shared_surr_main = BAD_SHARED_SURROUND, 13288c2ecf20Sopenharmony_ci}; 13298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_main_out_badness); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ciconst struct badness_table hda_extra_out_badness = { 13328c2ecf20Sopenharmony_ci .no_primary_dac = BAD_NO_DAC, 13338c2ecf20Sopenharmony_ci .no_dac = BAD_NO_DAC, 13348c2ecf20Sopenharmony_ci .shared_primary = BAD_NO_EXTRA_DAC, 13358c2ecf20Sopenharmony_ci .shared_surr = BAD_SHARED_EXTRA_SURROUND, 13368c2ecf20Sopenharmony_ci .shared_clfe = BAD_SHARED_EXTRA_SURROUND, 13378c2ecf20Sopenharmony_ci .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, 13388c2ecf20Sopenharmony_ci}; 13398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_extra_out_badness); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci/* get the DAC of the primary output corresponding to the given array index */ 13428c2ecf20Sopenharmony_cistatic hda_nid_t get_primary_out(struct hda_codec *codec, int idx) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 13458c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (cfg->line_outs > idx) 13488c2ecf20Sopenharmony_ci return spec->private_dac_nids[idx]; 13498c2ecf20Sopenharmony_ci idx -= cfg->line_outs; 13508c2ecf20Sopenharmony_ci if (spec->multi_ios > idx) 13518c2ecf20Sopenharmony_ci return spec->multi_io[idx].dac; 13528c2ecf20Sopenharmony_ci return 0; 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci/* return the DAC if it's reachable, otherwise zero */ 13568c2ecf20Sopenharmony_cistatic inline hda_nid_t try_dac(struct hda_codec *codec, 13578c2ecf20Sopenharmony_ci hda_nid_t dac, hda_nid_t pin) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci return is_reachable_path(codec, dac, pin) ? dac : 0; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci/* try to assign DACs to pins and return the resultant badness */ 13638c2ecf20Sopenharmony_cistatic int try_assign_dacs(struct hda_codec *codec, int num_outs, 13648c2ecf20Sopenharmony_ci const hda_nid_t *pins, hda_nid_t *dacs, 13658c2ecf20Sopenharmony_ci int *path_idx, 13668c2ecf20Sopenharmony_ci const struct badness_table *bad) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 13698c2ecf20Sopenharmony_ci int i, j; 13708c2ecf20Sopenharmony_ci int badness = 0; 13718c2ecf20Sopenharmony_ci hda_nid_t dac; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (!num_outs) 13748c2ecf20Sopenharmony_ci return 0; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci for (i = 0; i < num_outs; i++) { 13778c2ecf20Sopenharmony_ci struct nid_path *path; 13788c2ecf20Sopenharmony_ci hda_nid_t pin = pins[i]; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (!spec->obey_preferred_dacs) { 13818c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx[i]); 13828c2ecf20Sopenharmony_ci if (path) { 13838c2ecf20Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 13848c2ecf20Sopenharmony_ci continue; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci dacs[i] = get_preferred_dac(codec, pin); 13898c2ecf20Sopenharmony_ci if (dacs[i]) { 13908c2ecf20Sopenharmony_ci if (is_dac_already_used(codec, dacs[i])) 13918c2ecf20Sopenharmony_ci badness += bad->shared_primary; 13928c2ecf20Sopenharmony_ci } else if (spec->obey_preferred_dacs) { 13938c2ecf20Sopenharmony_ci badness += BAD_NO_PRIMARY_DAC; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (!dacs[i]) 13978c2ecf20Sopenharmony_ci dacs[i] = look_for_dac(codec, pin, false); 13988c2ecf20Sopenharmony_ci if (!dacs[i] && !i) { 13998c2ecf20Sopenharmony_ci /* try to steal the DAC of surrounds for the front */ 14008c2ecf20Sopenharmony_ci for (j = 1; j < num_outs; j++) { 14018c2ecf20Sopenharmony_ci if (is_reachable_path(codec, dacs[j], pin)) { 14028c2ecf20Sopenharmony_ci dacs[0] = dacs[j]; 14038c2ecf20Sopenharmony_ci dacs[j] = 0; 14048c2ecf20Sopenharmony_ci invalidate_nid_path(codec, path_idx[j]); 14058c2ecf20Sopenharmony_ci path_idx[j] = 0; 14068c2ecf20Sopenharmony_ci break; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci dac = dacs[i]; 14118c2ecf20Sopenharmony_ci if (!dac) { 14128c2ecf20Sopenharmony_ci if (num_outs > 2) 14138c2ecf20Sopenharmony_ci dac = try_dac(codec, get_primary_out(codec, i), pin); 14148c2ecf20Sopenharmony_ci if (!dac) 14158c2ecf20Sopenharmony_ci dac = try_dac(codec, dacs[0], pin); 14168c2ecf20Sopenharmony_ci if (!dac) 14178c2ecf20Sopenharmony_ci dac = try_dac(codec, get_primary_out(codec, i), pin); 14188c2ecf20Sopenharmony_ci if (dac) { 14198c2ecf20Sopenharmony_ci if (!i) 14208c2ecf20Sopenharmony_ci badness += bad->shared_primary; 14218c2ecf20Sopenharmony_ci else if (i == 1) 14228c2ecf20Sopenharmony_ci badness += bad->shared_surr; 14238c2ecf20Sopenharmony_ci else 14248c2ecf20Sopenharmony_ci badness += bad->shared_clfe; 14258c2ecf20Sopenharmony_ci } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { 14268c2ecf20Sopenharmony_ci dac = spec->private_dac_nids[0]; 14278c2ecf20Sopenharmony_ci badness += bad->shared_surr_main; 14288c2ecf20Sopenharmony_ci } else if (!i) 14298c2ecf20Sopenharmony_ci badness += bad->no_primary_dac; 14308c2ecf20Sopenharmony_ci else 14318c2ecf20Sopenharmony_ci badness += bad->no_dac; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci if (!dac) 14348c2ecf20Sopenharmony_ci continue; 14358c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); 14368c2ecf20Sopenharmony_ci if (!path && !i && spec->mixer_nid) { 14378c2ecf20Sopenharmony_ci /* try with aamix */ 14388c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, 0); 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci if (!path) { 14418c2ecf20Sopenharmony_ci dac = dacs[i] = 0; 14428c2ecf20Sopenharmony_ci badness += bad->no_dac; 14438c2ecf20Sopenharmony_ci } else { 14448c2ecf20Sopenharmony_ci /* print_nid_path(codec, "output", path); */ 14458c2ecf20Sopenharmony_ci path->active = true; 14468c2ecf20Sopenharmony_ci path_idx[i] = snd_hda_get_path_idx(codec, path); 14478c2ecf20Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci return badness; 14528c2ecf20Sopenharmony_ci} 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci/* return NID if the given pin has only a single connection to a certain DAC */ 14558c2ecf20Sopenharmony_cistatic hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 14588c2ecf20Sopenharmony_ci int i; 14598c2ecf20Sopenharmony_ci hda_nid_t nid_found = 0; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci for (i = 0; i < spec->num_all_dacs; i++) { 14628c2ecf20Sopenharmony_ci hda_nid_t nid = spec->all_dacs[i]; 14638c2ecf20Sopenharmony_ci if (!nid || is_dac_already_used(codec, nid)) 14648c2ecf20Sopenharmony_ci continue; 14658c2ecf20Sopenharmony_ci if (is_reachable_path(codec, nid, pin)) { 14668c2ecf20Sopenharmony_ci if (nid_found) 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci nid_found = nid; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci return nid_found; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci/* check whether the given pin can be a multi-io pin */ 14758c2ecf20Sopenharmony_cistatic bool can_be_multiio_pin(struct hda_codec *codec, 14768c2ecf20Sopenharmony_ci unsigned int location, hda_nid_t nid) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci unsigned int defcfg, caps; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, nid); 14818c2ecf20Sopenharmony_ci if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) 14828c2ecf20Sopenharmony_ci return false; 14838c2ecf20Sopenharmony_ci if (location && get_defcfg_location(defcfg) != location) 14848c2ecf20Sopenharmony_ci return false; 14858c2ecf20Sopenharmony_ci caps = snd_hda_query_pin_caps(codec, nid); 14868c2ecf20Sopenharmony_ci if (!(caps & AC_PINCAP_OUT)) 14878c2ecf20Sopenharmony_ci return false; 14888c2ecf20Sopenharmony_ci return true; 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci/* count the number of input pins that are capable to be multi-io */ 14928c2ecf20Sopenharmony_cistatic int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 14958c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 14968c2ecf20Sopenharmony_ci unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 14978c2ecf20Sopenharmony_ci unsigned int location = get_defcfg_location(defcfg); 14988c2ecf20Sopenharmony_ci int type, i; 14998c2ecf20Sopenharmony_ci int num_pins = 0; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 15028c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 15038c2ecf20Sopenharmony_ci if (cfg->inputs[i].type != type) 15048c2ecf20Sopenharmony_ci continue; 15058c2ecf20Sopenharmony_ci if (can_be_multiio_pin(codec, location, 15068c2ecf20Sopenharmony_ci cfg->inputs[i].pin)) 15078c2ecf20Sopenharmony_ci num_pins++; 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci return num_pins; 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci/* 15148c2ecf20Sopenharmony_ci * multi-io helper 15158c2ecf20Sopenharmony_ci * 15168c2ecf20Sopenharmony_ci * When hardwired is set, try to fill ony hardwired pins, and returns 15178c2ecf20Sopenharmony_ci * zero if any pins are filled, non-zero if nothing found. 15188c2ecf20Sopenharmony_ci * When hardwired is off, try to fill possible input pins, and returns 15198c2ecf20Sopenharmony_ci * the badness value. 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_cistatic int fill_multi_ios(struct hda_codec *codec, 15228c2ecf20Sopenharmony_ci hda_nid_t reference_pin, 15238c2ecf20Sopenharmony_ci bool hardwired) 15248c2ecf20Sopenharmony_ci{ 15258c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 15268c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 15278c2ecf20Sopenharmony_ci int type, i, j, num_pins, old_pins; 15288c2ecf20Sopenharmony_ci unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 15298c2ecf20Sopenharmony_ci unsigned int location = get_defcfg_location(defcfg); 15308c2ecf20Sopenharmony_ci int badness = 0; 15318c2ecf20Sopenharmony_ci struct nid_path *path; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci old_pins = spec->multi_ios; 15348c2ecf20Sopenharmony_ci if (old_pins >= 2) 15358c2ecf20Sopenharmony_ci goto end_fill; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci num_pins = count_multiio_pins(codec, reference_pin); 15388c2ecf20Sopenharmony_ci if (num_pins < 2) 15398c2ecf20Sopenharmony_ci goto end_fill; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 15428c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 15438c2ecf20Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 15448c2ecf20Sopenharmony_ci hda_nid_t dac = 0; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (cfg->inputs[i].type != type) 15478c2ecf20Sopenharmony_ci continue; 15488c2ecf20Sopenharmony_ci if (!can_be_multiio_pin(codec, location, nid)) 15498c2ecf20Sopenharmony_ci continue; 15508c2ecf20Sopenharmony_ci for (j = 0; j < spec->multi_ios; j++) { 15518c2ecf20Sopenharmony_ci if (nid == spec->multi_io[j].pin) 15528c2ecf20Sopenharmony_ci break; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci if (j < spec->multi_ios) 15558c2ecf20Sopenharmony_ci continue; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (hardwired) 15588c2ecf20Sopenharmony_ci dac = get_dac_if_single(codec, nid); 15598c2ecf20Sopenharmony_ci else if (!dac) 15608c2ecf20Sopenharmony_ci dac = look_for_dac(codec, nid, false); 15618c2ecf20Sopenharmony_ci if (!dac) { 15628c2ecf20Sopenharmony_ci badness++; 15638c2ecf20Sopenharmony_ci continue; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, nid, 15668c2ecf20Sopenharmony_ci -spec->mixer_nid); 15678c2ecf20Sopenharmony_ci if (!path) { 15688c2ecf20Sopenharmony_ci badness++; 15698c2ecf20Sopenharmony_ci continue; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci /* print_nid_path(codec, "multiio", path); */ 15728c2ecf20Sopenharmony_ci spec->multi_io[spec->multi_ios].pin = nid; 15738c2ecf20Sopenharmony_ci spec->multi_io[spec->multi_ios].dac = dac; 15748c2ecf20Sopenharmony_ci spec->out_paths[cfg->line_outs + spec->multi_ios] = 15758c2ecf20Sopenharmony_ci snd_hda_get_path_idx(codec, path); 15768c2ecf20Sopenharmony_ci spec->multi_ios++; 15778c2ecf20Sopenharmony_ci if (spec->multi_ios >= 2) 15788c2ecf20Sopenharmony_ci break; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci end_fill: 15828c2ecf20Sopenharmony_ci if (badness) 15838c2ecf20Sopenharmony_ci badness = BAD_MULTI_IO; 15848c2ecf20Sopenharmony_ci if (old_pins == spec->multi_ios) { 15858c2ecf20Sopenharmony_ci if (hardwired) 15868c2ecf20Sopenharmony_ci return 1; /* nothing found */ 15878c2ecf20Sopenharmony_ci else 15888c2ecf20Sopenharmony_ci return badness; /* no badness if nothing found */ 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci if (!hardwired && spec->multi_ios < 2) { 15918c2ecf20Sopenharmony_ci /* cancel newly assigned paths */ 15928c2ecf20Sopenharmony_ci spec->paths.used -= spec->multi_ios - old_pins; 15938c2ecf20Sopenharmony_ci spec->multi_ios = old_pins; 15948c2ecf20Sopenharmony_ci return badness; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci /* assign volume and mute controls */ 15988c2ecf20Sopenharmony_ci for (i = old_pins; i < spec->multi_ios; i++) { 15998c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); 16008c2ecf20Sopenharmony_ci badness += assign_out_path_ctls(codec, path); 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci return badness; 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci/* map DACs for all pins in the list if they are single connections */ 16078c2ecf20Sopenharmony_cistatic bool map_singles(struct hda_codec *codec, int outs, 16088c2ecf20Sopenharmony_ci const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 16118c2ecf20Sopenharmony_ci int i; 16128c2ecf20Sopenharmony_ci bool found = false; 16138c2ecf20Sopenharmony_ci for (i = 0; i < outs; i++) { 16148c2ecf20Sopenharmony_ci struct nid_path *path; 16158c2ecf20Sopenharmony_ci hda_nid_t dac; 16168c2ecf20Sopenharmony_ci if (dacs[i]) 16178c2ecf20Sopenharmony_ci continue; 16188c2ecf20Sopenharmony_ci dac = get_dac_if_single(codec, pins[i]); 16198c2ecf20Sopenharmony_ci if (!dac) 16208c2ecf20Sopenharmony_ci continue; 16218c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pins[i], 16228c2ecf20Sopenharmony_ci -spec->mixer_nid); 16238c2ecf20Sopenharmony_ci if (!path && !i && spec->mixer_nid) 16248c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pins[i], 0); 16258c2ecf20Sopenharmony_ci if (path) { 16268c2ecf20Sopenharmony_ci dacs[i] = dac; 16278c2ecf20Sopenharmony_ci found = true; 16288c2ecf20Sopenharmony_ci /* print_nid_path(codec, "output", path); */ 16298c2ecf20Sopenharmony_ci path->active = true; 16308c2ecf20Sopenharmony_ci path_idx[i] = snd_hda_get_path_idx(codec, path); 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci return found; 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic inline bool has_aamix_out_paths(struct hda_gen_spec *spec) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || 16398c2ecf20Sopenharmony_ci spec->aamix_out_paths[2]; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci/* create a new path including aamix if available, and return its index */ 16438c2ecf20Sopenharmony_cistatic int check_aamix_out_path(struct hda_codec *codec, int path_idx) 16448c2ecf20Sopenharmony_ci{ 16458c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 16468c2ecf20Sopenharmony_ci struct nid_path *path; 16478c2ecf20Sopenharmony_ci hda_nid_t path_dac, dac, pin; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 16508c2ecf20Sopenharmony_ci if (!path || !path->depth || 16518c2ecf20Sopenharmony_ci is_nid_contained(path, spec->mixer_nid)) 16528c2ecf20Sopenharmony_ci return 0; 16538c2ecf20Sopenharmony_ci path_dac = path->path[0]; 16548c2ecf20Sopenharmony_ci dac = spec->private_dac_nids[0]; 16558c2ecf20Sopenharmony_ci pin = path->path[path->depth - 1]; 16568c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); 16578c2ecf20Sopenharmony_ci if (!path) { 16588c2ecf20Sopenharmony_ci if (dac != path_dac) 16598c2ecf20Sopenharmony_ci dac = path_dac; 16608c2ecf20Sopenharmony_ci else if (spec->multiout.hp_out_nid[0]) 16618c2ecf20Sopenharmony_ci dac = spec->multiout.hp_out_nid[0]; 16628c2ecf20Sopenharmony_ci else if (spec->multiout.extra_out_nid[0]) 16638c2ecf20Sopenharmony_ci dac = spec->multiout.extra_out_nid[0]; 16648c2ecf20Sopenharmony_ci else 16658c2ecf20Sopenharmony_ci dac = 0; 16668c2ecf20Sopenharmony_ci if (dac) 16678c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dac, pin, 16688c2ecf20Sopenharmony_ci spec->mixer_nid); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci if (!path) 16718c2ecf20Sopenharmony_ci return 0; 16728c2ecf20Sopenharmony_ci /* print_nid_path(codec, "output-aamix", path); */ 16738c2ecf20Sopenharmony_ci path->active = false; /* unused as default */ 16748c2ecf20Sopenharmony_ci path->pin_fixed = true; /* static route */ 16758c2ecf20Sopenharmony_ci return snd_hda_get_path_idx(codec, path); 16768c2ecf20Sopenharmony_ci} 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci/* check whether the independent HP is available with the current config */ 16798c2ecf20Sopenharmony_cistatic bool indep_hp_possible(struct hda_codec *codec) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 16828c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 16838c2ecf20Sopenharmony_ci struct nid_path *path; 16848c2ecf20Sopenharmony_ci int i, idx; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_HP_OUT) 16878c2ecf20Sopenharmony_ci idx = spec->out_paths[0]; 16888c2ecf20Sopenharmony_ci else 16898c2ecf20Sopenharmony_ci idx = spec->hp_paths[0]; 16908c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, idx); 16918c2ecf20Sopenharmony_ci if (!path) 16928c2ecf20Sopenharmony_ci return false; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci /* assume no path conflicts unless aamix is involved */ 16958c2ecf20Sopenharmony_ci if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid)) 16968c2ecf20Sopenharmony_ci return true; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci /* check whether output paths contain aamix */ 16998c2ecf20Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 17008c2ecf20Sopenharmony_ci if (spec->out_paths[i] == idx) 17018c2ecf20Sopenharmony_ci break; 17028c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); 17038c2ecf20Sopenharmony_ci if (path && is_nid_contained(path, spec->mixer_nid)) 17048c2ecf20Sopenharmony_ci return false; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci for (i = 0; i < cfg->speaker_outs; i++) { 17078c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]); 17088c2ecf20Sopenharmony_ci if (path && is_nid_contained(path, spec->mixer_nid)) 17098c2ecf20Sopenharmony_ci return false; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci return true; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci/* fill the empty entries in the dac array for speaker/hp with the 17168c2ecf20Sopenharmony_ci * shared dac pointed by the paths 17178c2ecf20Sopenharmony_ci */ 17188c2ecf20Sopenharmony_cistatic void refill_shared_dacs(struct hda_codec *codec, int num_outs, 17198c2ecf20Sopenharmony_ci hda_nid_t *dacs, int *path_idx) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci struct nid_path *path; 17228c2ecf20Sopenharmony_ci int i; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci for (i = 0; i < num_outs; i++) { 17258c2ecf20Sopenharmony_ci if (dacs[i]) 17268c2ecf20Sopenharmony_ci continue; 17278c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx[i]); 17288c2ecf20Sopenharmony_ci if (!path) 17298c2ecf20Sopenharmony_ci continue; 17308c2ecf20Sopenharmony_ci dacs[i] = path->path[0]; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci} 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci/* fill in the dac_nids table from the parsed pin configuration */ 17358c2ecf20Sopenharmony_cistatic int fill_and_eval_dacs(struct hda_codec *codec, 17368c2ecf20Sopenharmony_ci bool fill_hardwired, 17378c2ecf20Sopenharmony_ci bool fill_mio_first) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 17408c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 17418c2ecf20Sopenharmony_ci int i, err, badness; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci /* set num_dacs once to full for look_for_dac() */ 17448c2ecf20Sopenharmony_ci spec->multiout.num_dacs = cfg->line_outs; 17458c2ecf20Sopenharmony_ci spec->multiout.dac_nids = spec->private_dac_nids; 17468c2ecf20Sopenharmony_ci memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); 17478c2ecf20Sopenharmony_ci memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); 17488c2ecf20Sopenharmony_ci memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); 17498c2ecf20Sopenharmony_ci spec->multi_ios = 0; 17508c2ecf20Sopenharmony_ci snd_array_free(&spec->paths); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci /* clear path indices */ 17538c2ecf20Sopenharmony_ci memset(spec->out_paths, 0, sizeof(spec->out_paths)); 17548c2ecf20Sopenharmony_ci memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); 17558c2ecf20Sopenharmony_ci memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); 17568c2ecf20Sopenharmony_ci memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); 17578c2ecf20Sopenharmony_ci memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); 17588c2ecf20Sopenharmony_ci memset(spec->input_paths, 0, sizeof(spec->input_paths)); 17598c2ecf20Sopenharmony_ci memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); 17608c2ecf20Sopenharmony_ci memset(&spec->digin_path, 0, sizeof(spec->digin_path)); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci badness = 0; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci /* fill hard-wired DACs first */ 17658c2ecf20Sopenharmony_ci if (fill_hardwired) { 17668c2ecf20Sopenharmony_ci bool mapped; 17678c2ecf20Sopenharmony_ci do { 17688c2ecf20Sopenharmony_ci mapped = map_singles(codec, cfg->line_outs, 17698c2ecf20Sopenharmony_ci cfg->line_out_pins, 17708c2ecf20Sopenharmony_ci spec->private_dac_nids, 17718c2ecf20Sopenharmony_ci spec->out_paths); 17728c2ecf20Sopenharmony_ci mapped |= map_singles(codec, cfg->hp_outs, 17738c2ecf20Sopenharmony_ci cfg->hp_pins, 17748c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid, 17758c2ecf20Sopenharmony_ci spec->hp_paths); 17768c2ecf20Sopenharmony_ci mapped |= map_singles(codec, cfg->speaker_outs, 17778c2ecf20Sopenharmony_ci cfg->speaker_pins, 17788c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid, 17798c2ecf20Sopenharmony_ci spec->speaker_paths); 17808c2ecf20Sopenharmony_ci if (!spec->no_multi_io && 17818c2ecf20Sopenharmony_ci fill_mio_first && cfg->line_outs == 1 && 17828c2ecf20Sopenharmony_ci cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 17838c2ecf20Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], true); 17848c2ecf20Sopenharmony_ci if (!err) 17858c2ecf20Sopenharmony_ci mapped = true; 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci } while (mapped); 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, 17918c2ecf20Sopenharmony_ci spec->private_dac_nids, spec->out_paths, 17928c2ecf20Sopenharmony_ci spec->main_out_badness); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci if (!spec->no_multi_io && fill_mio_first && 17958c2ecf20Sopenharmony_ci cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 17968c2ecf20Sopenharmony_ci /* try to fill multi-io first */ 17978c2ecf20Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 17988c2ecf20Sopenharmony_ci if (err < 0) 17998c2ecf20Sopenharmony_ci return err; 18008c2ecf20Sopenharmony_ci /* we don't count badness at this stage yet */ 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 18048c2ecf20Sopenharmony_ci err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, 18058c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid, 18068c2ecf20Sopenharmony_ci spec->hp_paths, 18078c2ecf20Sopenharmony_ci spec->extra_out_badness); 18088c2ecf20Sopenharmony_ci if (err < 0) 18098c2ecf20Sopenharmony_ci return err; 18108c2ecf20Sopenharmony_ci badness += err; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 18138c2ecf20Sopenharmony_ci err = try_assign_dacs(codec, cfg->speaker_outs, 18148c2ecf20Sopenharmony_ci cfg->speaker_pins, 18158c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid, 18168c2ecf20Sopenharmony_ci spec->speaker_paths, 18178c2ecf20Sopenharmony_ci spec->extra_out_badness); 18188c2ecf20Sopenharmony_ci if (err < 0) 18198c2ecf20Sopenharmony_ci return err; 18208c2ecf20Sopenharmony_ci badness += err; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci if (!spec->no_multi_io && 18238c2ecf20Sopenharmony_ci cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 18248c2ecf20Sopenharmony_ci err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 18258c2ecf20Sopenharmony_ci if (err < 0) 18268c2ecf20Sopenharmony_ci return err; 18278c2ecf20Sopenharmony_ci badness += err; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (spec->mixer_nid) { 18318c2ecf20Sopenharmony_ci spec->aamix_out_paths[0] = 18328c2ecf20Sopenharmony_ci check_aamix_out_path(codec, spec->out_paths[0]); 18338c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 18348c2ecf20Sopenharmony_ci spec->aamix_out_paths[1] = 18358c2ecf20Sopenharmony_ci check_aamix_out_path(codec, spec->hp_paths[0]); 18368c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 18378c2ecf20Sopenharmony_ci spec->aamix_out_paths[2] = 18388c2ecf20Sopenharmony_ci check_aamix_out_path(codec, spec->speaker_paths[0]); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci if (!spec->no_multi_io && 18428c2ecf20Sopenharmony_ci cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 18438c2ecf20Sopenharmony_ci if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) 18448c2ecf20Sopenharmony_ci spec->multi_ios = 1; /* give badness */ 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci /* re-count num_dacs and squash invalid entries */ 18478c2ecf20Sopenharmony_ci spec->multiout.num_dacs = 0; 18488c2ecf20Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 18498c2ecf20Sopenharmony_ci if (spec->private_dac_nids[i]) 18508c2ecf20Sopenharmony_ci spec->multiout.num_dacs++; 18518c2ecf20Sopenharmony_ci else { 18528c2ecf20Sopenharmony_ci memmove(spec->private_dac_nids + i, 18538c2ecf20Sopenharmony_ci spec->private_dac_nids + i + 1, 18548c2ecf20Sopenharmony_ci sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); 18558c2ecf20Sopenharmony_ci spec->private_dac_nids[cfg->line_outs - 1] = 0; 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci spec->ext_channel_count = spec->min_channel_count = 18608c2ecf20Sopenharmony_ci spec->multiout.num_dacs * 2; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (spec->multi_ios == 2) { 18638c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 18648c2ecf20Sopenharmony_ci spec->private_dac_nids[spec->multiout.num_dacs++] = 18658c2ecf20Sopenharmony_ci spec->multi_io[i].dac; 18668c2ecf20Sopenharmony_ci } else if (spec->multi_ios) { 18678c2ecf20Sopenharmony_ci spec->multi_ios = 0; 18688c2ecf20Sopenharmony_ci badness += BAD_MULTI_IO; 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (spec->indep_hp && !indep_hp_possible(codec)) 18728c2ecf20Sopenharmony_ci badness += BAD_NO_INDEP_HP; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* re-fill the shared DAC for speaker / headphone */ 18758c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 18768c2ecf20Sopenharmony_ci refill_shared_dacs(codec, cfg->hp_outs, 18778c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid, 18788c2ecf20Sopenharmony_ci spec->hp_paths); 18798c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 18808c2ecf20Sopenharmony_ci refill_shared_dacs(codec, cfg->speaker_outs, 18818c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid, 18828c2ecf20Sopenharmony_ci spec->speaker_paths); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci return badness; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci#define DEBUG_BADNESS 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci#ifdef DEBUG_BADNESS 18908c2ecf20Sopenharmony_ci#define debug_badness(fmt, ...) \ 18918c2ecf20Sopenharmony_ci codec_dbg(codec, fmt, ##__VA_ARGS__) 18928c2ecf20Sopenharmony_ci#else 18938c2ecf20Sopenharmony_ci#define debug_badness(fmt, ...) \ 18948c2ecf20Sopenharmony_ci do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0) 18958c2ecf20Sopenharmony_ci#endif 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci#ifdef DEBUG_BADNESS 18988c2ecf20Sopenharmony_cistatic inline void print_nid_path_idx(struct hda_codec *codec, 18998c2ecf20Sopenharmony_ci const char *pfx, int idx) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci struct nid_path *path; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, idx); 19048c2ecf20Sopenharmony_ci if (path) 19058c2ecf20Sopenharmony_ci print_nid_path(codec, pfx, path); 19068c2ecf20Sopenharmony_ci} 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_cistatic void debug_show_configs(struct hda_codec *codec, 19098c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 19128c2ecf20Sopenharmony_ci static const char * const lo_type[3] = { "LO", "SP", "HP" }; 19138c2ecf20Sopenharmony_ci int i; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", 19168c2ecf20Sopenharmony_ci cfg->line_out_pins[0], cfg->line_out_pins[1], 19178c2ecf20Sopenharmony_ci cfg->line_out_pins[2], cfg->line_out_pins[3], 19188c2ecf20Sopenharmony_ci spec->multiout.dac_nids[0], 19198c2ecf20Sopenharmony_ci spec->multiout.dac_nids[1], 19208c2ecf20Sopenharmony_ci spec->multiout.dac_nids[2], 19218c2ecf20Sopenharmony_ci spec->multiout.dac_nids[3], 19228c2ecf20Sopenharmony_ci lo_type[cfg->line_out_type]); 19238c2ecf20Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) 19248c2ecf20Sopenharmony_ci print_nid_path_idx(codec, " out", spec->out_paths[i]); 19258c2ecf20Sopenharmony_ci if (spec->multi_ios > 0) 19268c2ecf20Sopenharmony_ci debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", 19278c2ecf20Sopenharmony_ci spec->multi_ios, 19288c2ecf20Sopenharmony_ci spec->multi_io[0].pin, spec->multi_io[1].pin, 19298c2ecf20Sopenharmony_ci spec->multi_io[0].dac, spec->multi_io[1].dac); 19308c2ecf20Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) 19318c2ecf20Sopenharmony_ci print_nid_path_idx(codec, " mio", 19328c2ecf20Sopenharmony_ci spec->out_paths[cfg->line_outs + i]); 19338c2ecf20Sopenharmony_ci if (cfg->hp_outs) 19348c2ecf20Sopenharmony_ci debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 19358c2ecf20Sopenharmony_ci cfg->hp_pins[0], cfg->hp_pins[1], 19368c2ecf20Sopenharmony_ci cfg->hp_pins[2], cfg->hp_pins[3], 19378c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid[0], 19388c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid[1], 19398c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid[2], 19408c2ecf20Sopenharmony_ci spec->multiout.hp_out_nid[3]); 19418c2ecf20Sopenharmony_ci for (i = 0; i < cfg->hp_outs; i++) 19428c2ecf20Sopenharmony_ci print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); 19438c2ecf20Sopenharmony_ci if (cfg->speaker_outs) 19448c2ecf20Sopenharmony_ci debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 19458c2ecf20Sopenharmony_ci cfg->speaker_pins[0], cfg->speaker_pins[1], 19468c2ecf20Sopenharmony_ci cfg->speaker_pins[2], cfg->speaker_pins[3], 19478c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid[0], 19488c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid[1], 19498c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid[2], 19508c2ecf20Sopenharmony_ci spec->multiout.extra_out_nid[3]); 19518c2ecf20Sopenharmony_ci for (i = 0; i < cfg->speaker_outs; i++) 19528c2ecf20Sopenharmony_ci print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); 19538c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 19548c2ecf20Sopenharmony_ci print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ci#else 19578c2ecf20Sopenharmony_ci#define debug_show_configs(codec, cfg) /* NOP */ 19588c2ecf20Sopenharmony_ci#endif 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci/* find all available DACs of the codec */ 19618c2ecf20Sopenharmony_cistatic void fill_all_dac_nids(struct hda_codec *codec) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 19648c2ecf20Sopenharmony_ci hda_nid_t nid; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci spec->num_all_dacs = 0; 19678c2ecf20Sopenharmony_ci memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); 19688c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 19698c2ecf20Sopenharmony_ci if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) 19708c2ecf20Sopenharmony_ci continue; 19718c2ecf20Sopenharmony_ci if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { 19728c2ecf20Sopenharmony_ci codec_err(codec, "Too many DACs!\n"); 19738c2ecf20Sopenharmony_ci break; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci spec->all_dacs[spec->num_all_dacs++] = nid; 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci} 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_cistatic int parse_output_paths(struct hda_codec *codec) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 19828c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 19838c2ecf20Sopenharmony_ci struct auto_pin_cfg *best_cfg; 19848c2ecf20Sopenharmony_ci unsigned int val; 19858c2ecf20Sopenharmony_ci int best_badness = INT_MAX; 19868c2ecf20Sopenharmony_ci int badness; 19878c2ecf20Sopenharmony_ci bool fill_hardwired = true, fill_mio_first = true; 19888c2ecf20Sopenharmony_ci bool best_wired = true, best_mio = true; 19898c2ecf20Sopenharmony_ci bool hp_spk_swapped = false; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); 19928c2ecf20Sopenharmony_ci if (!best_cfg) 19938c2ecf20Sopenharmony_ci return -ENOMEM; 19948c2ecf20Sopenharmony_ci *best_cfg = *cfg; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci for (;;) { 19978c2ecf20Sopenharmony_ci badness = fill_and_eval_dacs(codec, fill_hardwired, 19988c2ecf20Sopenharmony_ci fill_mio_first); 19998c2ecf20Sopenharmony_ci if (badness < 0) { 20008c2ecf20Sopenharmony_ci kfree(best_cfg); 20018c2ecf20Sopenharmony_ci return badness; 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", 20048c2ecf20Sopenharmony_ci cfg->line_out_type, fill_hardwired, fill_mio_first, 20058c2ecf20Sopenharmony_ci badness); 20068c2ecf20Sopenharmony_ci debug_show_configs(codec, cfg); 20078c2ecf20Sopenharmony_ci if (badness < best_badness) { 20088c2ecf20Sopenharmony_ci best_badness = badness; 20098c2ecf20Sopenharmony_ci *best_cfg = *cfg; 20108c2ecf20Sopenharmony_ci best_wired = fill_hardwired; 20118c2ecf20Sopenharmony_ci best_mio = fill_mio_first; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci if (!badness) 20148c2ecf20Sopenharmony_ci break; 20158c2ecf20Sopenharmony_ci fill_mio_first = !fill_mio_first; 20168c2ecf20Sopenharmony_ci if (!fill_mio_first) 20178c2ecf20Sopenharmony_ci continue; 20188c2ecf20Sopenharmony_ci fill_hardwired = !fill_hardwired; 20198c2ecf20Sopenharmony_ci if (!fill_hardwired) 20208c2ecf20Sopenharmony_ci continue; 20218c2ecf20Sopenharmony_ci if (hp_spk_swapped) 20228c2ecf20Sopenharmony_ci break; 20238c2ecf20Sopenharmony_ci hp_spk_swapped = true; 20248c2ecf20Sopenharmony_ci if (cfg->speaker_outs > 0 && 20258c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_HP_OUT) { 20268c2ecf20Sopenharmony_ci cfg->hp_outs = cfg->line_outs; 20278c2ecf20Sopenharmony_ci memcpy(cfg->hp_pins, cfg->line_out_pins, 20288c2ecf20Sopenharmony_ci sizeof(cfg->hp_pins)); 20298c2ecf20Sopenharmony_ci cfg->line_outs = cfg->speaker_outs; 20308c2ecf20Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->speaker_pins, 20318c2ecf20Sopenharmony_ci sizeof(cfg->speaker_pins)); 20328c2ecf20Sopenharmony_ci cfg->speaker_outs = 0; 20338c2ecf20Sopenharmony_ci memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); 20348c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; 20358c2ecf20Sopenharmony_ci fill_hardwired = true; 20368c2ecf20Sopenharmony_ci continue; 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci if (cfg->hp_outs > 0 && 20398c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 20408c2ecf20Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 20418c2ecf20Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 20428c2ecf20Sopenharmony_ci sizeof(cfg->speaker_pins)); 20438c2ecf20Sopenharmony_ci cfg->line_outs = cfg->hp_outs; 20448c2ecf20Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->hp_pins, 20458c2ecf20Sopenharmony_ci sizeof(cfg->hp_pins)); 20468c2ecf20Sopenharmony_ci cfg->hp_outs = 0; 20478c2ecf20Sopenharmony_ci memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 20488c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 20498c2ecf20Sopenharmony_ci fill_hardwired = true; 20508c2ecf20Sopenharmony_ci continue; 20518c2ecf20Sopenharmony_ci } 20528c2ecf20Sopenharmony_ci break; 20538c2ecf20Sopenharmony_ci } 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (badness) { 20568c2ecf20Sopenharmony_ci debug_badness("==> restoring best_cfg\n"); 20578c2ecf20Sopenharmony_ci *cfg = *best_cfg; 20588c2ecf20Sopenharmony_ci fill_and_eval_dacs(codec, best_wired, best_mio); 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", 20618c2ecf20Sopenharmony_ci cfg->line_out_type, best_wired, best_mio); 20628c2ecf20Sopenharmony_ci debug_show_configs(codec, cfg); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (cfg->line_out_pins[0]) { 20658c2ecf20Sopenharmony_ci struct nid_path *path; 20668c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); 20678c2ecf20Sopenharmony_ci if (path) 20688c2ecf20Sopenharmony_ci spec->vmaster_nid = look_for_out_vol_nid(codec, path); 20698c2ecf20Sopenharmony_ci if (spec->vmaster_nid) { 20708c2ecf20Sopenharmony_ci snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, 20718c2ecf20Sopenharmony_ci HDA_OUTPUT, spec->vmaster_tlv); 20728c2ecf20Sopenharmony_ci if (spec->dac_min_mute) 20738c2ecf20Sopenharmony_ci spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci /* set initial pinctl targets */ 20788c2ecf20Sopenharmony_ci if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) 20798c2ecf20Sopenharmony_ci val = PIN_HP; 20808c2ecf20Sopenharmony_ci else 20818c2ecf20Sopenharmony_ci val = PIN_OUT; 20828c2ecf20Sopenharmony_ci set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); 20838c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 20848c2ecf20Sopenharmony_ci set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); 20858c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 20868c2ecf20Sopenharmony_ci val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; 20878c2ecf20Sopenharmony_ci set_pin_targets(codec, cfg->speaker_outs, 20888c2ecf20Sopenharmony_ci cfg->speaker_pins, val); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci /* clear indep_hp flag if not available */ 20928c2ecf20Sopenharmony_ci if (spec->indep_hp && !indep_hp_possible(codec)) 20938c2ecf20Sopenharmony_ci spec->indep_hp = 0; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci kfree(best_cfg); 20968c2ecf20Sopenharmony_ci return 0; 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci/* add playback controls from the parsed DAC table */ 21008c2ecf20Sopenharmony_cistatic int create_multi_out_ctls(struct hda_codec *codec, 21018c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg) 21028c2ecf20Sopenharmony_ci{ 21038c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 21048c2ecf20Sopenharmony_ci int i, err, noutputs; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci noutputs = cfg->line_outs; 21078c2ecf20Sopenharmony_ci if (spec->multi_ios > 0 && cfg->line_outs < 3) 21088c2ecf20Sopenharmony_ci noutputs += spec->multi_ios; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci for (i = 0; i < noutputs; i++) { 21118c2ecf20Sopenharmony_ci const char *name; 21128c2ecf20Sopenharmony_ci int index; 21138c2ecf20Sopenharmony_ci struct nid_path *path; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); 21168c2ecf20Sopenharmony_ci if (!path) 21178c2ecf20Sopenharmony_ci continue; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); 21208c2ecf20Sopenharmony_ci if (!name || !strcmp(name, "CLFE")) { 21218c2ecf20Sopenharmony_ci /* Center/LFE */ 21228c2ecf20Sopenharmony_ci err = add_vol_ctl(codec, "Center", 0, 1, path); 21238c2ecf20Sopenharmony_ci if (err < 0) 21248c2ecf20Sopenharmony_ci return err; 21258c2ecf20Sopenharmony_ci err = add_vol_ctl(codec, "LFE", 0, 2, path); 21268c2ecf20Sopenharmony_ci if (err < 0) 21278c2ecf20Sopenharmony_ci return err; 21288c2ecf20Sopenharmony_ci } else { 21298c2ecf20Sopenharmony_ci err = add_stereo_vol(codec, name, index, path); 21308c2ecf20Sopenharmony_ci if (err < 0) 21318c2ecf20Sopenharmony_ci return err; 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); 21358c2ecf20Sopenharmony_ci if (!name || !strcmp(name, "CLFE")) { 21368c2ecf20Sopenharmony_ci err = add_sw_ctl(codec, "Center", 0, 1, path); 21378c2ecf20Sopenharmony_ci if (err < 0) 21388c2ecf20Sopenharmony_ci return err; 21398c2ecf20Sopenharmony_ci err = add_sw_ctl(codec, "LFE", 0, 2, path); 21408c2ecf20Sopenharmony_ci if (err < 0) 21418c2ecf20Sopenharmony_ci return err; 21428c2ecf20Sopenharmony_ci } else { 21438c2ecf20Sopenharmony_ci err = add_stereo_sw(codec, name, index, path); 21448c2ecf20Sopenharmony_ci if (err < 0) 21458c2ecf20Sopenharmony_ci return err; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci return 0; 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_cistatic int create_extra_out(struct hda_codec *codec, int path_idx, 21528c2ecf20Sopenharmony_ci const char *pfx, int cidx) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci struct nid_path *path; 21558c2ecf20Sopenharmony_ci int err; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 21588c2ecf20Sopenharmony_ci if (!path) 21598c2ecf20Sopenharmony_ci return 0; 21608c2ecf20Sopenharmony_ci err = add_stereo_vol(codec, pfx, cidx, path); 21618c2ecf20Sopenharmony_ci if (err < 0) 21628c2ecf20Sopenharmony_ci return err; 21638c2ecf20Sopenharmony_ci err = add_stereo_sw(codec, pfx, cidx, path); 21648c2ecf20Sopenharmony_ci if (err < 0) 21658c2ecf20Sopenharmony_ci return err; 21668c2ecf20Sopenharmony_ci return 0; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci/* add playback controls for speaker and HP outputs */ 21708c2ecf20Sopenharmony_cistatic int create_extra_outs(struct hda_codec *codec, int num_pins, 21718c2ecf20Sopenharmony_ci const int *paths, const char *pfx) 21728c2ecf20Sopenharmony_ci{ 21738c2ecf20Sopenharmony_ci int i; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) { 21768c2ecf20Sopenharmony_ci const char *name; 21778c2ecf20Sopenharmony_ci char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 21788c2ecf20Sopenharmony_ci int err, idx = 0; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) 21818c2ecf20Sopenharmony_ci name = "Bass Speaker"; 21828c2ecf20Sopenharmony_ci else if (num_pins >= 3) { 21838c2ecf20Sopenharmony_ci snprintf(tmp, sizeof(tmp), "%s %s", 21848c2ecf20Sopenharmony_ci pfx, channel_name[i]); 21858c2ecf20Sopenharmony_ci name = tmp; 21868c2ecf20Sopenharmony_ci } else { 21878c2ecf20Sopenharmony_ci name = pfx; 21888c2ecf20Sopenharmony_ci idx = i; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci err = create_extra_out(codec, paths[i], name, idx); 21918c2ecf20Sopenharmony_ci if (err < 0) 21928c2ecf20Sopenharmony_ci return err; 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci return 0; 21958c2ecf20Sopenharmony_ci} 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_cistatic int create_hp_out_ctls(struct hda_codec *codec) 21988c2ecf20Sopenharmony_ci{ 21998c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 22008c2ecf20Sopenharmony_ci return create_extra_outs(codec, spec->autocfg.hp_outs, 22018c2ecf20Sopenharmony_ci spec->hp_paths, 22028c2ecf20Sopenharmony_ci "Headphone"); 22038c2ecf20Sopenharmony_ci} 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_cistatic int create_speaker_out_ctls(struct hda_codec *codec) 22068c2ecf20Sopenharmony_ci{ 22078c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 22088c2ecf20Sopenharmony_ci return create_extra_outs(codec, spec->autocfg.speaker_outs, 22098c2ecf20Sopenharmony_ci spec->speaker_paths, 22108c2ecf20Sopenharmony_ci "Speaker"); 22118c2ecf20Sopenharmony_ci} 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci/* 22148c2ecf20Sopenharmony_ci * independent HP controls 22158c2ecf20Sopenharmony_ci */ 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_cistatic void call_hp_automute(struct hda_codec *codec, 22188c2ecf20Sopenharmony_ci struct hda_jack_callback *jack); 22198c2ecf20Sopenharmony_cistatic int indep_hp_info(struct snd_kcontrol *kcontrol, 22208c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 22218c2ecf20Sopenharmony_ci{ 22228c2ecf20Sopenharmony_ci return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_cistatic int indep_hp_get(struct snd_kcontrol *kcontrol, 22268c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22278c2ecf20Sopenharmony_ci{ 22288c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22298c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 22308c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; 22318c2ecf20Sopenharmony_ci return 0; 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_cistatic void update_aamix_paths(struct hda_codec *codec, bool do_mix, 22358c2ecf20Sopenharmony_ci int nomix_path_idx, int mix_path_idx, 22368c2ecf20Sopenharmony_ci int out_type); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_cistatic int indep_hp_put(struct snd_kcontrol *kcontrol, 22398c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22408c2ecf20Sopenharmony_ci{ 22418c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22428c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 22438c2ecf20Sopenharmony_ci unsigned int select = ucontrol->value.enumerated.item[0]; 22448c2ecf20Sopenharmony_ci int ret = 0; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 22478c2ecf20Sopenharmony_ci if (spec->active_streams) { 22488c2ecf20Sopenharmony_ci ret = -EBUSY; 22498c2ecf20Sopenharmony_ci goto unlock; 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci if (spec->indep_hp_enabled != select) { 22538c2ecf20Sopenharmony_ci hda_nid_t *dacp; 22548c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 22558c2ecf20Sopenharmony_ci dacp = &spec->private_dac_nids[0]; 22568c2ecf20Sopenharmony_ci else 22578c2ecf20Sopenharmony_ci dacp = &spec->multiout.hp_out_nid[0]; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci /* update HP aamix paths in case it conflicts with indep HP */ 22608c2ecf20Sopenharmony_ci if (spec->have_aamix_ctl) { 22618c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 22628c2ecf20Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, 22638c2ecf20Sopenharmony_ci spec->out_paths[0], 22648c2ecf20Sopenharmony_ci spec->aamix_out_paths[0], 22658c2ecf20Sopenharmony_ci spec->autocfg.line_out_type); 22668c2ecf20Sopenharmony_ci else 22678c2ecf20Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, 22688c2ecf20Sopenharmony_ci spec->hp_paths[0], 22698c2ecf20Sopenharmony_ci spec->aamix_out_paths[1], 22708c2ecf20Sopenharmony_ci AUTO_PIN_HP_OUT); 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci spec->indep_hp_enabled = select; 22748c2ecf20Sopenharmony_ci if (spec->indep_hp_enabled) 22758c2ecf20Sopenharmony_ci *dacp = 0; 22768c2ecf20Sopenharmony_ci else 22778c2ecf20Sopenharmony_ci *dacp = spec->alt_dac_nid; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci call_hp_automute(codec, NULL); 22808c2ecf20Sopenharmony_ci ret = 1; 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci unlock: 22838c2ecf20Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 22848c2ecf20Sopenharmony_ci return ret; 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new indep_hp_ctl = { 22888c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22898c2ecf20Sopenharmony_ci .name = "Independent HP", 22908c2ecf20Sopenharmony_ci .info = indep_hp_info, 22918c2ecf20Sopenharmony_ci .get = indep_hp_get, 22928c2ecf20Sopenharmony_ci .put = indep_hp_put, 22938c2ecf20Sopenharmony_ci}; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic int create_indep_hp_ctls(struct hda_codec *codec) 22978c2ecf20Sopenharmony_ci{ 22988c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 22998c2ecf20Sopenharmony_ci hda_nid_t dac; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci if (!spec->indep_hp) 23028c2ecf20Sopenharmony_ci return 0; 23038c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 23048c2ecf20Sopenharmony_ci dac = spec->multiout.dac_nids[0]; 23058c2ecf20Sopenharmony_ci else 23068c2ecf20Sopenharmony_ci dac = spec->multiout.hp_out_nid[0]; 23078c2ecf20Sopenharmony_ci if (!dac) { 23088c2ecf20Sopenharmony_ci spec->indep_hp = 0; 23098c2ecf20Sopenharmony_ci return 0; 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci spec->indep_hp_enabled = false; 23138c2ecf20Sopenharmony_ci spec->alt_dac_nid = dac; 23148c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) 23158c2ecf20Sopenharmony_ci return -ENOMEM; 23168c2ecf20Sopenharmony_ci return 0; 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci/* 23208c2ecf20Sopenharmony_ci * channel mode enum control 23218c2ecf20Sopenharmony_ci */ 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_cistatic int ch_mode_info(struct snd_kcontrol *kcontrol, 23248c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 23258c2ecf20Sopenharmony_ci{ 23268c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23278c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 23288c2ecf20Sopenharmony_ci int chs; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 23318c2ecf20Sopenharmony_ci uinfo->count = 1; 23328c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = spec->multi_ios + 1; 23338c2ecf20Sopenharmony_ci if (uinfo->value.enumerated.item > spec->multi_ios) 23348c2ecf20Sopenharmony_ci uinfo->value.enumerated.item = spec->multi_ios; 23358c2ecf20Sopenharmony_ci chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; 23368c2ecf20Sopenharmony_ci sprintf(uinfo->value.enumerated.name, "%dch", chs); 23378c2ecf20Sopenharmony_ci return 0; 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic int ch_mode_get(struct snd_kcontrol *kcontrol, 23418c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23428c2ecf20Sopenharmony_ci{ 23438c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23448c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 23458c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 23468c2ecf20Sopenharmony_ci (spec->ext_channel_count - spec->min_channel_count) / 2; 23478c2ecf20Sopenharmony_ci return 0; 23488c2ecf20Sopenharmony_ci} 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_cistatic inline struct nid_path * 23518c2ecf20Sopenharmony_ciget_multiio_path(struct hda_codec *codec, int idx) 23528c2ecf20Sopenharmony_ci{ 23538c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 23548c2ecf20Sopenharmony_ci return snd_hda_get_path_from_idx(codec, 23558c2ecf20Sopenharmony_ci spec->out_paths[spec->autocfg.line_outs + idx]); 23568c2ecf20Sopenharmony_ci} 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_cistatic void update_automute_all(struct hda_codec *codec); 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci/* Default value to be passed as aamix argument for snd_hda_activate_path(); 23618c2ecf20Sopenharmony_ci * used for output paths 23628c2ecf20Sopenharmony_ci */ 23638c2ecf20Sopenharmony_cistatic bool aamix_default(struct hda_gen_spec *spec) 23648c2ecf20Sopenharmony_ci{ 23658c2ecf20Sopenharmony_ci return !spec->have_aamix_ctl || spec->aamix_mode; 23668c2ecf20Sopenharmony_ci} 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_cistatic int set_multi_io(struct hda_codec *codec, int idx, bool output) 23698c2ecf20Sopenharmony_ci{ 23708c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 23718c2ecf20Sopenharmony_ci hda_nid_t nid = spec->multi_io[idx].pin; 23728c2ecf20Sopenharmony_ci struct nid_path *path; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci path = get_multiio_path(codec, idx); 23758c2ecf20Sopenharmony_ci if (!path) 23768c2ecf20Sopenharmony_ci return -EINVAL; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci if (path->active == output) 23798c2ecf20Sopenharmony_ci return 0; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci if (output) { 23828c2ecf20Sopenharmony_ci set_pin_target(codec, nid, PIN_OUT, true); 23838c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, true, aamix_default(spec)); 23848c2ecf20Sopenharmony_ci set_pin_eapd(codec, nid, true); 23858c2ecf20Sopenharmony_ci } else { 23868c2ecf20Sopenharmony_ci set_pin_eapd(codec, nid, false); 23878c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, false, aamix_default(spec)); 23888c2ecf20Sopenharmony_ci set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); 23898c2ecf20Sopenharmony_ci path_power_down_sync(codec, path); 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci /* update jack retasking in case it modifies any of them */ 23938c2ecf20Sopenharmony_ci update_automute_all(codec); 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci return 0; 23968c2ecf20Sopenharmony_ci} 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_cistatic int ch_mode_put(struct snd_kcontrol *kcontrol, 23998c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24008c2ecf20Sopenharmony_ci{ 24018c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24028c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 24038c2ecf20Sopenharmony_ci int i, ch; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci ch = ucontrol->value.enumerated.item[0]; 24068c2ecf20Sopenharmony_ci if (ch < 0 || ch > spec->multi_ios) 24078c2ecf20Sopenharmony_ci return -EINVAL; 24088c2ecf20Sopenharmony_ci if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) 24098c2ecf20Sopenharmony_ci return 0; 24108c2ecf20Sopenharmony_ci spec->ext_channel_count = ch * 2 + spec->min_channel_count; 24118c2ecf20Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) 24128c2ecf20Sopenharmony_ci set_multi_io(codec, i, i < ch); 24138c2ecf20Sopenharmony_ci spec->multiout.max_channels = max(spec->ext_channel_count, 24148c2ecf20Sopenharmony_ci spec->const_channel_count); 24158c2ecf20Sopenharmony_ci if (spec->need_dac_fix) 24168c2ecf20Sopenharmony_ci spec->multiout.num_dacs = spec->multiout.max_channels / 2; 24178c2ecf20Sopenharmony_ci return 1; 24188c2ecf20Sopenharmony_ci} 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new channel_mode_enum = { 24218c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24228c2ecf20Sopenharmony_ci .name = "Channel Mode", 24238c2ecf20Sopenharmony_ci .info = ch_mode_info, 24248c2ecf20Sopenharmony_ci .get = ch_mode_get, 24258c2ecf20Sopenharmony_ci .put = ch_mode_put, 24268c2ecf20Sopenharmony_ci}; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic int create_multi_channel_mode(struct hda_codec *codec) 24298c2ecf20Sopenharmony_ci{ 24308c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci if (spec->multi_ios > 0) { 24338c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) 24348c2ecf20Sopenharmony_ci return -ENOMEM; 24358c2ecf20Sopenharmony_ci } 24368c2ecf20Sopenharmony_ci return 0; 24378c2ecf20Sopenharmony_ci} 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci/* 24408c2ecf20Sopenharmony_ci * aamix loopback enable/disable switch 24418c2ecf20Sopenharmony_ci */ 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci#define loopback_mixing_info indep_hp_info 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_cistatic int loopback_mixing_get(struct snd_kcontrol *kcontrol, 24468c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24498c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 24508c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->aamix_mode; 24518c2ecf20Sopenharmony_ci return 0; 24528c2ecf20Sopenharmony_ci} 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_cistatic void update_aamix_paths(struct hda_codec *codec, bool do_mix, 24558c2ecf20Sopenharmony_ci int nomix_path_idx, int mix_path_idx, 24568c2ecf20Sopenharmony_ci int out_type) 24578c2ecf20Sopenharmony_ci{ 24588c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 24598c2ecf20Sopenharmony_ci struct nid_path *nomix_path, *mix_path; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); 24628c2ecf20Sopenharmony_ci mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); 24638c2ecf20Sopenharmony_ci if (!nomix_path || !mix_path) 24648c2ecf20Sopenharmony_ci return; 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci /* if HP aamix path is driven from a different DAC and the 24678c2ecf20Sopenharmony_ci * independent HP mode is ON, can't turn on aamix path 24688c2ecf20Sopenharmony_ci */ 24698c2ecf20Sopenharmony_ci if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && 24708c2ecf20Sopenharmony_ci mix_path->path[0] != spec->alt_dac_nid) 24718c2ecf20Sopenharmony_ci do_mix = false; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci if (do_mix) { 24748c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, nomix_path, false, true); 24758c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, mix_path, true, true); 24768c2ecf20Sopenharmony_ci path_power_down_sync(codec, nomix_path); 24778c2ecf20Sopenharmony_ci } else { 24788c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, mix_path, false, false); 24798c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, nomix_path, true, false); 24808c2ecf20Sopenharmony_ci path_power_down_sync(codec, mix_path); 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci} 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci/* re-initialize the output paths; only called from loopback_mixing_put() */ 24858c2ecf20Sopenharmony_cistatic void update_output_paths(struct hda_codec *codec, int num_outs, 24868c2ecf20Sopenharmony_ci const int *paths) 24878c2ecf20Sopenharmony_ci{ 24888c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 24898c2ecf20Sopenharmony_ci struct nid_path *path; 24908c2ecf20Sopenharmony_ci int i; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci for (i = 0; i < num_outs; i++) { 24938c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, paths[i]); 24948c2ecf20Sopenharmony_ci if (path) 24958c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 24968c2ecf20Sopenharmony_ci spec->aamix_mode); 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci} 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_cistatic int loopback_mixing_put(struct snd_kcontrol *kcontrol, 25018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25028c2ecf20Sopenharmony_ci{ 25038c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25048c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 25058c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 25068c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.enumerated.item[0]; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci if (val == spec->aamix_mode) 25098c2ecf20Sopenharmony_ci return 0; 25108c2ecf20Sopenharmony_ci spec->aamix_mode = val; 25118c2ecf20Sopenharmony_ci if (has_aamix_out_paths(spec)) { 25128c2ecf20Sopenharmony_ci update_aamix_paths(codec, val, spec->out_paths[0], 25138c2ecf20Sopenharmony_ci spec->aamix_out_paths[0], 25148c2ecf20Sopenharmony_ci cfg->line_out_type); 25158c2ecf20Sopenharmony_ci update_aamix_paths(codec, val, spec->hp_paths[0], 25168c2ecf20Sopenharmony_ci spec->aamix_out_paths[1], 25178c2ecf20Sopenharmony_ci AUTO_PIN_HP_OUT); 25188c2ecf20Sopenharmony_ci update_aamix_paths(codec, val, spec->speaker_paths[0], 25198c2ecf20Sopenharmony_ci spec->aamix_out_paths[2], 25208c2ecf20Sopenharmony_ci AUTO_PIN_SPEAKER_OUT); 25218c2ecf20Sopenharmony_ci } else { 25228c2ecf20Sopenharmony_ci update_output_paths(codec, cfg->line_outs, spec->out_paths); 25238c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 25248c2ecf20Sopenharmony_ci update_output_paths(codec, cfg->hp_outs, spec->hp_paths); 25258c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 25268c2ecf20Sopenharmony_ci update_output_paths(codec, cfg->speaker_outs, 25278c2ecf20Sopenharmony_ci spec->speaker_paths); 25288c2ecf20Sopenharmony_ci } 25298c2ecf20Sopenharmony_ci return 1; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new loopback_mixing_enum = { 25338c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25348c2ecf20Sopenharmony_ci .name = "Loopback Mixing", 25358c2ecf20Sopenharmony_ci .info = loopback_mixing_info, 25368c2ecf20Sopenharmony_ci .get = loopback_mixing_get, 25378c2ecf20Sopenharmony_ci .put = loopback_mixing_put, 25388c2ecf20Sopenharmony_ci}; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_cistatic int create_loopback_mixing_ctl(struct hda_codec *codec) 25418c2ecf20Sopenharmony_ci{ 25428c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (!spec->mixer_nid) 25458c2ecf20Sopenharmony_ci return 0; 25468c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) 25478c2ecf20Sopenharmony_ci return -ENOMEM; 25488c2ecf20Sopenharmony_ci spec->have_aamix_ctl = 1; 25498c2ecf20Sopenharmony_ci return 0; 25508c2ecf20Sopenharmony_ci} 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci/* 25538c2ecf20Sopenharmony_ci * shared headphone/mic handling 25548c2ecf20Sopenharmony_ci */ 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_cistatic void call_update_outputs(struct hda_codec *codec); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci/* for shared I/O, change the pin-control accordingly */ 25598c2ecf20Sopenharmony_cistatic void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force) 25608c2ecf20Sopenharmony_ci{ 25618c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 25628c2ecf20Sopenharmony_ci bool as_mic; 25638c2ecf20Sopenharmony_ci unsigned int val; 25648c2ecf20Sopenharmony_ci hda_nid_t pin; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci pin = spec->hp_mic_pin; 25678c2ecf20Sopenharmony_ci as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if (!force) { 25708c2ecf20Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, pin); 25718c2ecf20Sopenharmony_ci if (as_mic) { 25728c2ecf20Sopenharmony_ci if (val & PIN_IN) 25738c2ecf20Sopenharmony_ci return; 25748c2ecf20Sopenharmony_ci } else { 25758c2ecf20Sopenharmony_ci if (val & PIN_OUT) 25768c2ecf20Sopenharmony_ci return; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci } 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci val = snd_hda_get_default_vref(codec, pin); 25818c2ecf20Sopenharmony_ci /* if the HP pin doesn't support VREF and the codec driver gives an 25828c2ecf20Sopenharmony_ci * alternative pin, set up the VREF on that pin instead 25838c2ecf20Sopenharmony_ci */ 25848c2ecf20Sopenharmony_ci if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { 25858c2ecf20Sopenharmony_ci const hda_nid_t vref_pin = spec->shared_mic_vref_pin; 25868c2ecf20Sopenharmony_ci unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); 25878c2ecf20Sopenharmony_ci if (vref_val != AC_PINCTL_VREF_HIZ) 25888c2ecf20Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, vref_pin, 25898c2ecf20Sopenharmony_ci PIN_IN | (as_mic ? vref_val : 0)); 25908c2ecf20Sopenharmony_ci } 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci if (!spec->hp_mic_jack_modes) { 25938c2ecf20Sopenharmony_ci if (as_mic) 25948c2ecf20Sopenharmony_ci val |= PIN_IN; 25958c2ecf20Sopenharmony_ci else 25968c2ecf20Sopenharmony_ci val = PIN_HP; 25978c2ecf20Sopenharmony_ci set_pin_target(codec, pin, val, true); 25988c2ecf20Sopenharmony_ci call_hp_automute(codec, NULL); 25998c2ecf20Sopenharmony_ci } 26008c2ecf20Sopenharmony_ci} 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci/* create a shared input with the headphone out */ 26038c2ecf20Sopenharmony_cistatic int create_hp_mic(struct hda_codec *codec) 26048c2ecf20Sopenharmony_ci{ 26058c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 26068c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 26078c2ecf20Sopenharmony_ci unsigned int defcfg; 26088c2ecf20Sopenharmony_ci hda_nid_t nid; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci if (!spec->hp_mic) { 26118c2ecf20Sopenharmony_ci if (spec->suppress_hp_mic_detect) 26128c2ecf20Sopenharmony_ci return 0; 26138c2ecf20Sopenharmony_ci /* automatic detection: only if no input or a single internal 26148c2ecf20Sopenharmony_ci * input pin is found, try to detect the shared hp/mic 26158c2ecf20Sopenharmony_ci */ 26168c2ecf20Sopenharmony_ci if (cfg->num_inputs > 1) 26178c2ecf20Sopenharmony_ci return 0; 26188c2ecf20Sopenharmony_ci else if (cfg->num_inputs == 1) { 26198c2ecf20Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); 26208c2ecf20Sopenharmony_ci if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) 26218c2ecf20Sopenharmony_ci return 0; 26228c2ecf20Sopenharmony_ci } 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci spec->hp_mic = 0; /* clear once */ 26268c2ecf20Sopenharmony_ci if (cfg->num_inputs >= AUTO_CFG_MAX_INS) 26278c2ecf20Sopenharmony_ci return 0; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci nid = 0; 26308c2ecf20Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0) 26318c2ecf20Sopenharmony_ci nid = cfg->line_out_pins[0]; 26328c2ecf20Sopenharmony_ci else if (cfg->hp_outs > 0) 26338c2ecf20Sopenharmony_ci nid = cfg->hp_pins[0]; 26348c2ecf20Sopenharmony_ci if (!nid) 26358c2ecf20Sopenharmony_ci return 0; 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) 26388c2ecf20Sopenharmony_ci return 0; /* no input */ 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].pin = nid; 26418c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC; 26428c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].is_headphone_mic = 1; 26438c2ecf20Sopenharmony_ci cfg->num_inputs++; 26448c2ecf20Sopenharmony_ci spec->hp_mic = 1; 26458c2ecf20Sopenharmony_ci spec->hp_mic_pin = nid; 26468c2ecf20Sopenharmony_ci /* we can't handle auto-mic together with HP-mic */ 26478c2ecf20Sopenharmony_ci spec->suppress_auto_mic = 1; 26488c2ecf20Sopenharmony_ci codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid); 26498c2ecf20Sopenharmony_ci return 0; 26508c2ecf20Sopenharmony_ci} 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci/* 26538c2ecf20Sopenharmony_ci * output jack mode 26548c2ecf20Sopenharmony_ci */ 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_cistatic int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin); 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_cistatic const char * const out_jack_texts[] = { 26598c2ecf20Sopenharmony_ci "Line Out", "Headphone Out", 26608c2ecf20Sopenharmony_ci}; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_cistatic int out_jack_mode_info(struct snd_kcontrol *kcontrol, 26638c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 26648c2ecf20Sopenharmony_ci{ 26658c2ecf20Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts); 26668c2ecf20Sopenharmony_ci} 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_cistatic int out_jack_mode_get(struct snd_kcontrol *kcontrol, 26698c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26708c2ecf20Sopenharmony_ci{ 26718c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 26728c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 26738c2ecf20Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) 26748c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 1; 26758c2ecf20Sopenharmony_ci else 26768c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 26778c2ecf20Sopenharmony_ci return 0; 26788c2ecf20Sopenharmony_ci} 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_cistatic int out_jack_mode_put(struct snd_kcontrol *kcontrol, 26818c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26828c2ecf20Sopenharmony_ci{ 26838c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 26848c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 26858c2ecf20Sopenharmony_ci unsigned int val; 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; 26888c2ecf20Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) == val) 26898c2ecf20Sopenharmony_ci return 0; 26908c2ecf20Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 26918c2ecf20Sopenharmony_ci return 1; 26928c2ecf20Sopenharmony_ci} 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new out_jack_mode_enum = { 26958c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26968c2ecf20Sopenharmony_ci .info = out_jack_mode_info, 26978c2ecf20Sopenharmony_ci .get = out_jack_mode_get, 26988c2ecf20Sopenharmony_ci .put = out_jack_mode_put, 26998c2ecf20Sopenharmony_ci}; 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_cistatic bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) 27028c2ecf20Sopenharmony_ci{ 27038c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 27048c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *kctl; 27058c2ecf20Sopenharmony_ci int i; 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci snd_array_for_each(&spec->kctls, i, kctl) { 27088c2ecf20Sopenharmony_ci if (!strcmp(kctl->name, name) && kctl->index == idx) 27098c2ecf20Sopenharmony_ci return true; 27108c2ecf20Sopenharmony_ci } 27118c2ecf20Sopenharmony_ci return false; 27128c2ecf20Sopenharmony_ci} 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_cistatic void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, 27158c2ecf20Sopenharmony_ci char *name, size_t name_len) 27168c2ecf20Sopenharmony_ci{ 27178c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 27188c2ecf20Sopenharmony_ci int idx = 0; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); 27218c2ecf20Sopenharmony_ci strlcat(name, " Jack Mode", name_len); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci for (; find_kctl_name(codec, name, idx); idx++) 27248c2ecf20Sopenharmony_ci ; 27258c2ecf20Sopenharmony_ci} 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_cistatic int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) 27288c2ecf20Sopenharmony_ci{ 27298c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 27308c2ecf20Sopenharmony_ci if (spec->add_jack_modes) { 27318c2ecf20Sopenharmony_ci unsigned int pincap = snd_hda_query_pin_caps(codec, pin); 27328c2ecf20Sopenharmony_ci if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) 27338c2ecf20Sopenharmony_ci return 2; 27348c2ecf20Sopenharmony_ci } 27358c2ecf20Sopenharmony_ci return 1; 27368c2ecf20Sopenharmony_ci} 27378c2ecf20Sopenharmony_ci 27388c2ecf20Sopenharmony_cistatic int create_out_jack_modes(struct hda_codec *codec, int num_pins, 27398c2ecf20Sopenharmony_ci hda_nid_t *pins) 27408c2ecf20Sopenharmony_ci{ 27418c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 27428c2ecf20Sopenharmony_ci int i; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) { 27458c2ecf20Sopenharmony_ci hda_nid_t pin = pins[i]; 27468c2ecf20Sopenharmony_ci if (pin == spec->hp_mic_pin) 27478c2ecf20Sopenharmony_ci continue; 27488c2ecf20Sopenharmony_ci if (get_out_jack_num_items(codec, pin) > 1) { 27498c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 27508c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 27518c2ecf20Sopenharmony_ci get_jack_mode_name(codec, pin, name, sizeof(name)); 27528c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, 27538c2ecf20Sopenharmony_ci &out_jack_mode_enum); 27548c2ecf20Sopenharmony_ci if (!knew) 27558c2ecf20Sopenharmony_ci return -ENOMEM; 27568c2ecf20Sopenharmony_ci knew->private_value = pin; 27578c2ecf20Sopenharmony_ci } 27588c2ecf20Sopenharmony_ci } 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci return 0; 27618c2ecf20Sopenharmony_ci} 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci/* 27648c2ecf20Sopenharmony_ci * input jack mode 27658c2ecf20Sopenharmony_ci */ 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ 27688c2ecf20Sopenharmony_ci#define NUM_VREFS 6 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_cistatic const char * const vref_texts[NUM_VREFS] = { 27718c2ecf20Sopenharmony_ci "Line In", "Mic 50pc Bias", "Mic 0V Bias", 27728c2ecf20Sopenharmony_ci "", "Mic 80pc Bias", "Mic 100pc Bias" 27738c2ecf20Sopenharmony_ci}; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_cistatic unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) 27768c2ecf20Sopenharmony_ci{ 27778c2ecf20Sopenharmony_ci unsigned int pincap; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci pincap = snd_hda_query_pin_caps(codec, pin); 27808c2ecf20Sopenharmony_ci pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; 27818c2ecf20Sopenharmony_ci /* filter out unusual vrefs */ 27828c2ecf20Sopenharmony_ci pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); 27838c2ecf20Sopenharmony_ci return pincap; 27848c2ecf20Sopenharmony_ci} 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ 27878c2ecf20Sopenharmony_cistatic int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) 27888c2ecf20Sopenharmony_ci{ 27898c2ecf20Sopenharmony_ci unsigned int i, n = 0; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VREFS; i++) { 27928c2ecf20Sopenharmony_ci if (vref_caps & (1 << i)) { 27938c2ecf20Sopenharmony_ci if (n == item_idx) 27948c2ecf20Sopenharmony_ci return i; 27958c2ecf20Sopenharmony_ci n++; 27968c2ecf20Sopenharmony_ci } 27978c2ecf20Sopenharmony_ci } 27988c2ecf20Sopenharmony_ci return 0; 27998c2ecf20Sopenharmony_ci} 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci/* convert back from the vref ctl index to the enum item index */ 28028c2ecf20Sopenharmony_cistatic int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) 28038c2ecf20Sopenharmony_ci{ 28048c2ecf20Sopenharmony_ci unsigned int i, n = 0; 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VREFS; i++) { 28078c2ecf20Sopenharmony_ci if (i == idx) 28088c2ecf20Sopenharmony_ci return n; 28098c2ecf20Sopenharmony_ci if (vref_caps & (1 << i)) 28108c2ecf20Sopenharmony_ci n++; 28118c2ecf20Sopenharmony_ci } 28128c2ecf20Sopenharmony_ci return 0; 28138c2ecf20Sopenharmony_ci} 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_cistatic int in_jack_mode_info(struct snd_kcontrol *kcontrol, 28168c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 28178c2ecf20Sopenharmony_ci{ 28188c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 28198c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 28208c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), 28238c2ecf20Sopenharmony_ci vref_texts); 28248c2ecf20Sopenharmony_ci /* set the right text */ 28258c2ecf20Sopenharmony_ci strcpy(uinfo->value.enumerated.name, 28268c2ecf20Sopenharmony_ci vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); 28278c2ecf20Sopenharmony_ci return 0; 28288c2ecf20Sopenharmony_ci} 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_cistatic int in_jack_mode_get(struct snd_kcontrol *kcontrol, 28318c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 28328c2ecf20Sopenharmony_ci{ 28338c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 28348c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 28358c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 28368c2ecf20Sopenharmony_ci unsigned int idx; 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; 28398c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); 28408c2ecf20Sopenharmony_ci return 0; 28418c2ecf20Sopenharmony_ci} 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_cistatic int in_jack_mode_put(struct snd_kcontrol *kcontrol, 28448c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 28458c2ecf20Sopenharmony_ci{ 28468c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 28478c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 28488c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 28498c2ecf20Sopenharmony_ci unsigned int val, idx; 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, nid); 28528c2ecf20Sopenharmony_ci idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); 28538c2ecf20Sopenharmony_ci if (idx == ucontrol->value.enumerated.item[0]) 28548c2ecf20Sopenharmony_ci return 0; 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci val &= ~AC_PINCTL_VREFEN; 28578c2ecf20Sopenharmony_ci val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); 28588c2ecf20Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 28598c2ecf20Sopenharmony_ci return 1; 28608c2ecf20Sopenharmony_ci} 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new in_jack_mode_enum = { 28638c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 28648c2ecf20Sopenharmony_ci .info = in_jack_mode_info, 28658c2ecf20Sopenharmony_ci .get = in_jack_mode_get, 28668c2ecf20Sopenharmony_ci .put = in_jack_mode_put, 28678c2ecf20Sopenharmony_ci}; 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_cistatic int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin) 28708c2ecf20Sopenharmony_ci{ 28718c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 28728c2ecf20Sopenharmony_ci int nitems = 0; 28738c2ecf20Sopenharmony_ci if (spec->add_jack_modes) 28748c2ecf20Sopenharmony_ci nitems = hweight32(get_vref_caps(codec, pin)); 28758c2ecf20Sopenharmony_ci return nitems ? nitems : 1; 28768c2ecf20Sopenharmony_ci} 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_cistatic int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) 28798c2ecf20Sopenharmony_ci{ 28808c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 28818c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 28828c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 28838c2ecf20Sopenharmony_ci unsigned int defcfg; 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci if (pin == spec->hp_mic_pin) 28868c2ecf20Sopenharmony_ci return 0; /* already done in create_out_jack_mode() */ 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci /* no jack mode for fixed pins */ 28898c2ecf20Sopenharmony_ci defcfg = snd_hda_codec_get_pincfg(codec, pin); 28908c2ecf20Sopenharmony_ci if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) 28918c2ecf20Sopenharmony_ci return 0; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci /* no multiple vref caps? */ 28948c2ecf20Sopenharmony_ci if (get_in_jack_num_items(codec, pin) <= 1) 28958c2ecf20Sopenharmony_ci return 0; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci get_jack_mode_name(codec, pin, name, sizeof(name)); 28988c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); 28998c2ecf20Sopenharmony_ci if (!knew) 29008c2ecf20Sopenharmony_ci return -ENOMEM; 29018c2ecf20Sopenharmony_ci knew->private_value = pin; 29028c2ecf20Sopenharmony_ci return 0; 29038c2ecf20Sopenharmony_ci} 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci/* 29068c2ecf20Sopenharmony_ci * HP/mic shared jack mode 29078c2ecf20Sopenharmony_ci */ 29088c2ecf20Sopenharmony_cistatic int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, 29098c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 29108c2ecf20Sopenharmony_ci{ 29118c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 29128c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 29138c2ecf20Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 29148c2ecf20Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 29158c2ecf20Sopenharmony_ci const char *text = NULL; 29168c2ecf20Sopenharmony_ci int idx; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 29198c2ecf20Sopenharmony_ci uinfo->count = 1; 29208c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = out_jacks + in_jacks; 29218c2ecf20Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 29228c2ecf20Sopenharmony_ci uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 29238c2ecf20Sopenharmony_ci idx = uinfo->value.enumerated.item; 29248c2ecf20Sopenharmony_ci if (idx < out_jacks) { 29258c2ecf20Sopenharmony_ci if (out_jacks > 1) 29268c2ecf20Sopenharmony_ci text = out_jack_texts[idx]; 29278c2ecf20Sopenharmony_ci else 29288c2ecf20Sopenharmony_ci text = "Headphone Out"; 29298c2ecf20Sopenharmony_ci } else { 29308c2ecf20Sopenharmony_ci idx -= out_jacks; 29318c2ecf20Sopenharmony_ci if (in_jacks > 1) { 29328c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 29338c2ecf20Sopenharmony_ci text = vref_texts[get_vref_idx(vref_caps, idx)]; 29348c2ecf20Sopenharmony_ci } else 29358c2ecf20Sopenharmony_ci text = "Mic In"; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci strcpy(uinfo->value.enumerated.name, text); 29398c2ecf20Sopenharmony_ci return 0; 29408c2ecf20Sopenharmony_ci} 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_cistatic int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid) 29438c2ecf20Sopenharmony_ci{ 29448c2ecf20Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 29458c2ecf20Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 29468c2ecf20Sopenharmony_ci unsigned int val = snd_hda_codec_get_pin_target(codec, nid); 29478c2ecf20Sopenharmony_ci int idx = 0; 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci if (val & PIN_OUT) { 29508c2ecf20Sopenharmony_ci if (out_jacks > 1 && val == PIN_HP) 29518c2ecf20Sopenharmony_ci idx = 1; 29528c2ecf20Sopenharmony_ci } else if (val & PIN_IN) { 29538c2ecf20Sopenharmony_ci idx = out_jacks; 29548c2ecf20Sopenharmony_ci if (in_jacks > 1) { 29558c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 29568c2ecf20Sopenharmony_ci val &= AC_PINCTL_VREFEN; 29578c2ecf20Sopenharmony_ci idx += cvt_from_vref_idx(vref_caps, val); 29588c2ecf20Sopenharmony_ci } 29598c2ecf20Sopenharmony_ci } 29608c2ecf20Sopenharmony_ci return idx; 29618c2ecf20Sopenharmony_ci} 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_cistatic int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol, 29648c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 29658c2ecf20Sopenharmony_ci{ 29668c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 29678c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 29688c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 29698c2ecf20Sopenharmony_ci get_cur_hp_mic_jack_mode(codec, nid); 29708c2ecf20Sopenharmony_ci return 0; 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_cistatic int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol, 29748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 29758c2ecf20Sopenharmony_ci{ 29768c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 29778c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 29788c2ecf20Sopenharmony_ci int out_jacks = get_out_jack_num_items(codec, nid); 29798c2ecf20Sopenharmony_ci int in_jacks = get_in_jack_num_items(codec, nid); 29808c2ecf20Sopenharmony_ci unsigned int val, oldval, idx; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci oldval = get_cur_hp_mic_jack_mode(codec, nid); 29838c2ecf20Sopenharmony_ci idx = ucontrol->value.enumerated.item[0]; 29848c2ecf20Sopenharmony_ci if (oldval == idx) 29858c2ecf20Sopenharmony_ci return 0; 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci if (idx < out_jacks) { 29888c2ecf20Sopenharmony_ci if (out_jacks > 1) 29898c2ecf20Sopenharmony_ci val = idx ? PIN_HP : PIN_OUT; 29908c2ecf20Sopenharmony_ci else 29918c2ecf20Sopenharmony_ci val = PIN_HP; 29928c2ecf20Sopenharmony_ci } else { 29938c2ecf20Sopenharmony_ci idx -= out_jacks; 29948c2ecf20Sopenharmony_ci if (in_jacks > 1) { 29958c2ecf20Sopenharmony_ci unsigned int vref_caps = get_vref_caps(codec, nid); 29968c2ecf20Sopenharmony_ci val = snd_hda_codec_get_pin_target(codec, nid); 29978c2ecf20Sopenharmony_ci val &= ~(AC_PINCTL_VREFEN | PIN_HP); 29988c2ecf20Sopenharmony_ci val |= get_vref_idx(vref_caps, idx) | PIN_IN; 29998c2ecf20Sopenharmony_ci } else 30008c2ecf20Sopenharmony_ci val = snd_hda_get_default_vref(codec, nid) | PIN_IN; 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, nid, val); 30038c2ecf20Sopenharmony_ci call_hp_automute(codec, NULL); 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci return 1; 30068c2ecf20Sopenharmony_ci} 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new hp_mic_jack_mode_enum = { 30098c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 30108c2ecf20Sopenharmony_ci .info = hp_mic_jack_mode_info, 30118c2ecf20Sopenharmony_ci .get = hp_mic_jack_mode_get, 30128c2ecf20Sopenharmony_ci .put = hp_mic_jack_mode_put, 30138c2ecf20Sopenharmony_ci}; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_cistatic int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin) 30168c2ecf20Sopenharmony_ci{ 30178c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 30188c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode", 30218c2ecf20Sopenharmony_ci &hp_mic_jack_mode_enum); 30228c2ecf20Sopenharmony_ci if (!knew) 30238c2ecf20Sopenharmony_ci return -ENOMEM; 30248c2ecf20Sopenharmony_ci knew->private_value = pin; 30258c2ecf20Sopenharmony_ci spec->hp_mic_jack_modes = 1; 30268c2ecf20Sopenharmony_ci return 0; 30278c2ecf20Sopenharmony_ci} 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci/* 30308c2ecf20Sopenharmony_ci * Parse input paths 30318c2ecf20Sopenharmony_ci */ 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci/* add the powersave loopback-list entry */ 30348c2ecf20Sopenharmony_cistatic int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) 30358c2ecf20Sopenharmony_ci{ 30368c2ecf20Sopenharmony_ci struct hda_amp_list *list; 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_ci list = snd_array_new(&spec->loopback_list); 30398c2ecf20Sopenharmony_ci if (!list) 30408c2ecf20Sopenharmony_ci return -ENOMEM; 30418c2ecf20Sopenharmony_ci list->nid = mix; 30428c2ecf20Sopenharmony_ci list->dir = HDA_INPUT; 30438c2ecf20Sopenharmony_ci list->idx = idx; 30448c2ecf20Sopenharmony_ci spec->loopback.amplist = spec->loopback_list.list; 30458c2ecf20Sopenharmony_ci return 0; 30468c2ecf20Sopenharmony_ci} 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci/* return true if either a volume or a mute amp is found for the given 30498c2ecf20Sopenharmony_ci * aamix path; the amp has to be either in the mixer node or its direct leaf 30508c2ecf20Sopenharmony_ci */ 30518c2ecf20Sopenharmony_cistatic bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, 30528c2ecf20Sopenharmony_ci hda_nid_t pin, unsigned int *mix_val, 30538c2ecf20Sopenharmony_ci unsigned int *mute_val) 30548c2ecf20Sopenharmony_ci{ 30558c2ecf20Sopenharmony_ci int idx, num_conns; 30568c2ecf20Sopenharmony_ci const hda_nid_t *list; 30578c2ecf20Sopenharmony_ci hda_nid_t nid; 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); 30608c2ecf20Sopenharmony_ci if (idx < 0) 30618c2ecf20Sopenharmony_ci return false; 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci *mix_val = *mute_val = 0; 30648c2ecf20Sopenharmony_ci if (nid_has_volume(codec, mix_nid, HDA_INPUT)) 30658c2ecf20Sopenharmony_ci *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 30668c2ecf20Sopenharmony_ci if (nid_has_mute(codec, mix_nid, HDA_INPUT)) 30678c2ecf20Sopenharmony_ci *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 30688c2ecf20Sopenharmony_ci if (*mix_val && *mute_val) 30698c2ecf20Sopenharmony_ci return true; 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci /* check leaf node */ 30728c2ecf20Sopenharmony_ci num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); 30738c2ecf20Sopenharmony_ci if (num_conns < idx) 30748c2ecf20Sopenharmony_ci return false; 30758c2ecf20Sopenharmony_ci nid = list[idx]; 30768c2ecf20Sopenharmony_ci if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && 30778c2ecf20Sopenharmony_ci !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) 30788c2ecf20Sopenharmony_ci *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 30798c2ecf20Sopenharmony_ci if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && 30808c2ecf20Sopenharmony_ci !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) 30818c2ecf20Sopenharmony_ci *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci return *mix_val || *mute_val; 30848c2ecf20Sopenharmony_ci} 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci/* create input playback/capture controls for the given pin */ 30878c2ecf20Sopenharmony_cistatic int new_analog_input(struct hda_codec *codec, int input_idx, 30888c2ecf20Sopenharmony_ci hda_nid_t pin, const char *ctlname, int ctlidx, 30898c2ecf20Sopenharmony_ci hda_nid_t mix_nid) 30908c2ecf20Sopenharmony_ci{ 30918c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 30928c2ecf20Sopenharmony_ci struct nid_path *path; 30938c2ecf20Sopenharmony_ci unsigned int mix_val, mute_val; 30948c2ecf20Sopenharmony_ci int err, idx; 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) 30978c2ecf20Sopenharmony_ci return 0; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, mix_nid, 0); 31008c2ecf20Sopenharmony_ci if (!path) 31018c2ecf20Sopenharmony_ci return -EINVAL; 31028c2ecf20Sopenharmony_ci print_nid_path(codec, "loopback", path); 31038c2ecf20Sopenharmony_ci spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci idx = path->idx[path->depth - 1]; 31068c2ecf20Sopenharmony_ci if (mix_val) { 31078c2ecf20Sopenharmony_ci err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); 31088c2ecf20Sopenharmony_ci if (err < 0) 31098c2ecf20Sopenharmony_ci return err; 31108c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = mix_val; 31118c2ecf20Sopenharmony_ci } 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci if (mute_val) { 31148c2ecf20Sopenharmony_ci err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); 31158c2ecf20Sopenharmony_ci if (err < 0) 31168c2ecf20Sopenharmony_ci return err; 31178c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = mute_val; 31188c2ecf20Sopenharmony_ci } 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci path->active = true; 31218c2ecf20Sopenharmony_ci path->stream_enabled = true; /* no DAC/ADC involved */ 31228c2ecf20Sopenharmony_ci err = add_loopback_list(spec, mix_nid, idx); 31238c2ecf20Sopenharmony_ci if (err < 0) 31248c2ecf20Sopenharmony_ci return err; 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci if (spec->mixer_nid != spec->mixer_merge_nid && 31278c2ecf20Sopenharmony_ci !spec->loopback_merge_path) { 31288c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, spec->mixer_nid, 31298c2ecf20Sopenharmony_ci spec->mixer_merge_nid, 0); 31308c2ecf20Sopenharmony_ci if (path) { 31318c2ecf20Sopenharmony_ci print_nid_path(codec, "loopback-merge", path); 31328c2ecf20Sopenharmony_ci path->active = true; 31338c2ecf20Sopenharmony_ci path->pin_fixed = true; /* static route */ 31348c2ecf20Sopenharmony_ci path->stream_enabled = true; /* no DAC/ADC involved */ 31358c2ecf20Sopenharmony_ci spec->loopback_merge_path = 31368c2ecf20Sopenharmony_ci snd_hda_get_path_idx(codec, path); 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci } 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci return 0; 31418c2ecf20Sopenharmony_ci} 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_cistatic int is_input_pin(struct hda_codec *codec, hda_nid_t nid) 31448c2ecf20Sopenharmony_ci{ 31458c2ecf20Sopenharmony_ci unsigned int pincap = snd_hda_query_pin_caps(codec, nid); 31468c2ecf20Sopenharmony_ci return (pincap & AC_PINCAP_IN) != 0; 31478c2ecf20Sopenharmony_ci} 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci/* Parse the codec tree and retrieve ADCs */ 31508c2ecf20Sopenharmony_cistatic int fill_adc_nids(struct hda_codec *codec) 31518c2ecf20Sopenharmony_ci{ 31528c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 31538c2ecf20Sopenharmony_ci hda_nid_t nid; 31548c2ecf20Sopenharmony_ci hda_nid_t *adc_nids = spec->adc_nids; 31558c2ecf20Sopenharmony_ci int max_nums = ARRAY_SIZE(spec->adc_nids); 31568c2ecf20Sopenharmony_ci int nums = 0; 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 31598c2ecf20Sopenharmony_ci unsigned int caps = get_wcaps(codec, nid); 31608c2ecf20Sopenharmony_ci int type = get_wcaps_type(caps); 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) 31638c2ecf20Sopenharmony_ci continue; 31648c2ecf20Sopenharmony_ci adc_nids[nums] = nid; 31658c2ecf20Sopenharmony_ci if (++nums >= max_nums) 31668c2ecf20Sopenharmony_ci break; 31678c2ecf20Sopenharmony_ci } 31688c2ecf20Sopenharmony_ci spec->num_adc_nids = nums; 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci /* copy the detected ADCs to all_adcs[] */ 31718c2ecf20Sopenharmony_ci spec->num_all_adcs = nums; 31728c2ecf20Sopenharmony_ci memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci return nums; 31758c2ecf20Sopenharmony_ci} 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci/* filter out invalid adc_nids that don't give all active input pins; 31788c2ecf20Sopenharmony_ci * if needed, check whether dynamic ADC-switching is available 31798c2ecf20Sopenharmony_ci */ 31808c2ecf20Sopenharmony_cistatic int check_dyn_adc_switch(struct hda_codec *codec) 31818c2ecf20Sopenharmony_ci{ 31828c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 31838c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 31848c2ecf20Sopenharmony_ci unsigned int ok_bits; 31858c2ecf20Sopenharmony_ci int i, n, nums; 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci nums = 0; 31888c2ecf20Sopenharmony_ci ok_bits = 0; 31898c2ecf20Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 31908c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 31918c2ecf20Sopenharmony_ci if (!spec->input_paths[i][n]) 31928c2ecf20Sopenharmony_ci break; 31938c2ecf20Sopenharmony_ci } 31948c2ecf20Sopenharmony_ci if (i >= imux->num_items) { 31958c2ecf20Sopenharmony_ci ok_bits |= (1 << n); 31968c2ecf20Sopenharmony_ci nums++; 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci } 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci if (!ok_bits) { 32018c2ecf20Sopenharmony_ci /* check whether ADC-switch is possible */ 32028c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 32038c2ecf20Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 32048c2ecf20Sopenharmony_ci if (spec->input_paths[i][n]) { 32058c2ecf20Sopenharmony_ci spec->dyn_adc_idx[i] = n; 32068c2ecf20Sopenharmony_ci break; 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci } 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_ci codec_dbg(codec, "enabling ADC switching\n"); 32128c2ecf20Sopenharmony_ci spec->dyn_adc_switch = 1; 32138c2ecf20Sopenharmony_ci } else if (nums != spec->num_adc_nids) { 32148c2ecf20Sopenharmony_ci /* shrink the invalid adcs and input paths */ 32158c2ecf20Sopenharmony_ci nums = 0; 32168c2ecf20Sopenharmony_ci for (n = 0; n < spec->num_adc_nids; n++) { 32178c2ecf20Sopenharmony_ci if (!(ok_bits & (1 << n))) 32188c2ecf20Sopenharmony_ci continue; 32198c2ecf20Sopenharmony_ci if (n != nums) { 32208c2ecf20Sopenharmony_ci spec->adc_nids[nums] = spec->adc_nids[n]; 32218c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 32228c2ecf20Sopenharmony_ci invalidate_nid_path(codec, 32238c2ecf20Sopenharmony_ci spec->input_paths[i][nums]); 32248c2ecf20Sopenharmony_ci spec->input_paths[i][nums] = 32258c2ecf20Sopenharmony_ci spec->input_paths[i][n]; 32268c2ecf20Sopenharmony_ci spec->input_paths[i][n] = 0; 32278c2ecf20Sopenharmony_ci } 32288c2ecf20Sopenharmony_ci } 32298c2ecf20Sopenharmony_ci nums++; 32308c2ecf20Sopenharmony_ci } 32318c2ecf20Sopenharmony_ci spec->num_adc_nids = nums; 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci if (imux->num_items == 1 || 32358c2ecf20Sopenharmony_ci (imux->num_items == 2 && spec->hp_mic)) { 32368c2ecf20Sopenharmony_ci codec_dbg(codec, "reducing to a single ADC\n"); 32378c2ecf20Sopenharmony_ci spec->num_adc_nids = 1; /* reduce to a single ADC */ 32388c2ecf20Sopenharmony_ci } 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci /* single index for individual volumes ctls */ 32418c2ecf20Sopenharmony_ci if (!spec->dyn_adc_switch && spec->multi_cap_vol) 32428c2ecf20Sopenharmony_ci spec->num_adc_nids = 1; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci return 0; 32458c2ecf20Sopenharmony_ci} 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci/* parse capture source paths from the given pin and create imux items */ 32488c2ecf20Sopenharmony_cistatic int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, 32498c2ecf20Sopenharmony_ci int cfg_idx, int num_adcs, 32508c2ecf20Sopenharmony_ci const char *label, int anchor) 32518c2ecf20Sopenharmony_ci{ 32528c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 32538c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 32548c2ecf20Sopenharmony_ci int imux_idx = imux->num_items; 32558c2ecf20Sopenharmony_ci bool imux_added = false; 32568c2ecf20Sopenharmony_ci int c; 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci for (c = 0; c < num_adcs; c++) { 32598c2ecf20Sopenharmony_ci struct nid_path *path; 32608c2ecf20Sopenharmony_ci hda_nid_t adc = spec->adc_nids[c]; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci if (!is_reachable_path(codec, pin, adc)) 32638c2ecf20Sopenharmony_ci continue; 32648c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, adc, anchor); 32658c2ecf20Sopenharmony_ci if (!path) 32668c2ecf20Sopenharmony_ci continue; 32678c2ecf20Sopenharmony_ci print_nid_path(codec, "input", path); 32688c2ecf20Sopenharmony_ci spec->input_paths[imux_idx][c] = 32698c2ecf20Sopenharmony_ci snd_hda_get_path_idx(codec, path); 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci if (!imux_added) { 32728c2ecf20Sopenharmony_ci if (spec->hp_mic_pin == pin) 32738c2ecf20Sopenharmony_ci spec->hp_mic_mux_idx = imux->num_items; 32748c2ecf20Sopenharmony_ci spec->imux_pins[imux->num_items] = pin; 32758c2ecf20Sopenharmony_ci snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); 32768c2ecf20Sopenharmony_ci imux_added = true; 32778c2ecf20Sopenharmony_ci if (spec->dyn_adc_switch) 32788c2ecf20Sopenharmony_ci spec->dyn_adc_idx[imux_idx] = c; 32798c2ecf20Sopenharmony_ci } 32808c2ecf20Sopenharmony_ci } 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci return 0; 32838c2ecf20Sopenharmony_ci} 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci/* 32868c2ecf20Sopenharmony_ci * create playback/capture controls for input pins 32878c2ecf20Sopenharmony_ci */ 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci/* fill the label for each input at first */ 32908c2ecf20Sopenharmony_cistatic int fill_input_pin_labels(struct hda_codec *codec) 32918c2ecf20Sopenharmony_ci{ 32928c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 32938c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 32948c2ecf20Sopenharmony_ci int i; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 32978c2ecf20Sopenharmony_ci hda_nid_t pin = cfg->inputs[i].pin; 32988c2ecf20Sopenharmony_ci const char *label; 32998c2ecf20Sopenharmony_ci int j, idx; 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci if (!is_input_pin(codec, pin)) 33028c2ecf20Sopenharmony_ci continue; 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci label = hda_get_autocfg_input_label(codec, cfg, i); 33058c2ecf20Sopenharmony_ci idx = 0; 33068c2ecf20Sopenharmony_ci for (j = i - 1; j >= 0; j--) { 33078c2ecf20Sopenharmony_ci if (spec->input_labels[j] && 33088c2ecf20Sopenharmony_ci !strcmp(spec->input_labels[j], label)) { 33098c2ecf20Sopenharmony_ci idx = spec->input_label_idxs[j] + 1; 33108c2ecf20Sopenharmony_ci break; 33118c2ecf20Sopenharmony_ci } 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci spec->input_labels[i] = label; 33158c2ecf20Sopenharmony_ci spec->input_label_idxs[i] = idx; 33168c2ecf20Sopenharmony_ci } 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci return 0; 33198c2ecf20Sopenharmony_ci} 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_cistatic int create_input_ctls(struct hda_codec *codec) 33248c2ecf20Sopenharmony_ci{ 33258c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 33268c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg = &spec->autocfg; 33278c2ecf20Sopenharmony_ci hda_nid_t mixer = spec->mixer_nid; 33288c2ecf20Sopenharmony_ci int num_adcs; 33298c2ecf20Sopenharmony_ci int i, err; 33308c2ecf20Sopenharmony_ci unsigned int val; 33318c2ecf20Sopenharmony_ci 33328c2ecf20Sopenharmony_ci num_adcs = fill_adc_nids(codec); 33338c2ecf20Sopenharmony_ci if (num_adcs < 0) 33348c2ecf20Sopenharmony_ci return 0; 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci err = fill_input_pin_labels(codec); 33378c2ecf20Sopenharmony_ci if (err < 0) 33388c2ecf20Sopenharmony_ci return err; 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 33418c2ecf20Sopenharmony_ci hda_nid_t pin; 33428c2ecf20Sopenharmony_ci 33438c2ecf20Sopenharmony_ci pin = cfg->inputs[i].pin; 33448c2ecf20Sopenharmony_ci if (!is_input_pin(codec, pin)) 33458c2ecf20Sopenharmony_ci continue; 33468c2ecf20Sopenharmony_ci 33478c2ecf20Sopenharmony_ci val = PIN_IN; 33488c2ecf20Sopenharmony_ci if (cfg->inputs[i].type == AUTO_PIN_MIC) 33498c2ecf20Sopenharmony_ci val |= snd_hda_get_default_vref(codec, pin); 33508c2ecf20Sopenharmony_ci if (pin != spec->hp_mic_pin && 33518c2ecf20Sopenharmony_ci !snd_hda_codec_get_pin_target(codec, pin)) 33528c2ecf20Sopenharmony_ci set_pin_target(codec, pin, val, false); 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci if (mixer) { 33558c2ecf20Sopenharmony_ci if (is_reachable_path(codec, pin, mixer)) { 33568c2ecf20Sopenharmony_ci err = new_analog_input(codec, i, pin, 33578c2ecf20Sopenharmony_ci spec->input_labels[i], 33588c2ecf20Sopenharmony_ci spec->input_label_idxs[i], 33598c2ecf20Sopenharmony_ci mixer); 33608c2ecf20Sopenharmony_ci if (err < 0) 33618c2ecf20Sopenharmony_ci return err; 33628c2ecf20Sopenharmony_ci } 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci err = parse_capture_source(codec, pin, i, num_adcs, 33668c2ecf20Sopenharmony_ci spec->input_labels[i], -mixer); 33678c2ecf20Sopenharmony_ci if (err < 0) 33688c2ecf20Sopenharmony_ci return err; 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci if (spec->add_jack_modes) { 33718c2ecf20Sopenharmony_ci err = create_in_jack_mode(codec, pin); 33728c2ecf20Sopenharmony_ci if (err < 0) 33738c2ecf20Sopenharmony_ci return err; 33748c2ecf20Sopenharmony_ci } 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_ci /* add stereo mix when explicitly enabled via hint */ 33788c2ecf20Sopenharmony_ci if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { 33798c2ecf20Sopenharmony_ci err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, 33808c2ecf20Sopenharmony_ci "Stereo Mix", 0); 33818c2ecf20Sopenharmony_ci if (err < 0) 33828c2ecf20Sopenharmony_ci return err; 33838c2ecf20Sopenharmony_ci else 33848c2ecf20Sopenharmony_ci spec->suppress_auto_mic = 1; 33858c2ecf20Sopenharmony_ci } 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci return 0; 33888c2ecf20Sopenharmony_ci} 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci/* 33928c2ecf20Sopenharmony_ci * input source mux 33938c2ecf20Sopenharmony_ci */ 33948c2ecf20Sopenharmony_ci 33958c2ecf20Sopenharmony_ci/* get the input path specified by the given adc and imux indices */ 33968c2ecf20Sopenharmony_cistatic struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) 33978c2ecf20Sopenharmony_ci{ 33988c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 33998c2ecf20Sopenharmony_ci if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { 34008c2ecf20Sopenharmony_ci snd_BUG(); 34018c2ecf20Sopenharmony_ci return NULL; 34028c2ecf20Sopenharmony_ci } 34038c2ecf20Sopenharmony_ci if (spec->dyn_adc_switch) 34048c2ecf20Sopenharmony_ci adc_idx = spec->dyn_adc_idx[imux_idx]; 34058c2ecf20Sopenharmony_ci if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { 34068c2ecf20Sopenharmony_ci snd_BUG(); 34078c2ecf20Sopenharmony_ci return NULL; 34088c2ecf20Sopenharmony_ci } 34098c2ecf20Sopenharmony_ci return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); 34108c2ecf20Sopenharmony_ci} 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_cistatic int mux_select(struct hda_codec *codec, unsigned int adc_idx, 34138c2ecf20Sopenharmony_ci unsigned int idx); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_cistatic int mux_enum_info(struct snd_kcontrol *kcontrol, 34168c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 34178c2ecf20Sopenharmony_ci{ 34188c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 34198c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 34208c2ecf20Sopenharmony_ci return snd_hda_input_mux_info(&spec->input_mux, uinfo); 34218c2ecf20Sopenharmony_ci} 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_cistatic int mux_enum_get(struct snd_kcontrol *kcontrol, 34248c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34258c2ecf20Sopenharmony_ci{ 34268c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 34278c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 34288c2ecf20Sopenharmony_ci /* the ctls are created at once with multiple counts */ 34298c2ecf20Sopenharmony_ci unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 34328c2ecf20Sopenharmony_ci return 0; 34338c2ecf20Sopenharmony_ci} 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_cistatic int mux_enum_put(struct snd_kcontrol *kcontrol, 34368c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34378c2ecf20Sopenharmony_ci{ 34388c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 34398c2ecf20Sopenharmony_ci unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 34408c2ecf20Sopenharmony_ci return mux_select(codec, adc_idx, 34418c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0]); 34428c2ecf20Sopenharmony_ci} 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new cap_src_temp = { 34458c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 34468c2ecf20Sopenharmony_ci .name = "Input Source", 34478c2ecf20Sopenharmony_ci .info = mux_enum_info, 34488c2ecf20Sopenharmony_ci .get = mux_enum_get, 34498c2ecf20Sopenharmony_ci .put = mux_enum_put, 34508c2ecf20Sopenharmony_ci}; 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_ci/* 34538c2ecf20Sopenharmony_ci * capture volume and capture switch ctls 34548c2ecf20Sopenharmony_ci */ 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_citypedef int (*put_call_t)(struct snd_kcontrol *kcontrol, 34578c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci/* call the given amp update function for all amps in the imux list at once */ 34608c2ecf20Sopenharmony_cistatic int cap_put_caller(struct snd_kcontrol *kcontrol, 34618c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 34628c2ecf20Sopenharmony_ci put_call_t func, int type) 34638c2ecf20Sopenharmony_ci{ 34648c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 34658c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 34668c2ecf20Sopenharmony_ci const struct hda_input_mux *imux; 34678c2ecf20Sopenharmony_ci struct nid_path *path; 34688c2ecf20Sopenharmony_ci int i, adc_idx, ret, err = 0; 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci imux = &spec->input_mux; 34718c2ecf20Sopenharmony_ci adc_idx = kcontrol->id.index; 34728c2ecf20Sopenharmony_ci mutex_lock(&codec->control_mutex); 34738c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 34748c2ecf20Sopenharmony_ci path = get_input_path(codec, adc_idx, i); 34758c2ecf20Sopenharmony_ci if (!path || !path->ctls[type]) 34768c2ecf20Sopenharmony_ci continue; 34778c2ecf20Sopenharmony_ci kcontrol->private_value = path->ctls[type]; 34788c2ecf20Sopenharmony_ci ret = func(kcontrol, ucontrol); 34798c2ecf20Sopenharmony_ci if (ret < 0) { 34808c2ecf20Sopenharmony_ci err = ret; 34818c2ecf20Sopenharmony_ci break; 34828c2ecf20Sopenharmony_ci } 34838c2ecf20Sopenharmony_ci if (ret > 0) 34848c2ecf20Sopenharmony_ci err = 1; 34858c2ecf20Sopenharmony_ci } 34868c2ecf20Sopenharmony_ci mutex_unlock(&codec->control_mutex); 34878c2ecf20Sopenharmony_ci if (err >= 0 && spec->cap_sync_hook) 34888c2ecf20Sopenharmony_ci spec->cap_sync_hook(codec, kcontrol, ucontrol); 34898c2ecf20Sopenharmony_ci return err; 34908c2ecf20Sopenharmony_ci} 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci/* capture volume ctl callbacks */ 34938c2ecf20Sopenharmony_ci#define cap_vol_info snd_hda_mixer_amp_volume_info 34948c2ecf20Sopenharmony_ci#define cap_vol_get snd_hda_mixer_amp_volume_get 34958c2ecf20Sopenharmony_ci#define cap_vol_tlv snd_hda_mixer_amp_tlv 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_cistatic int cap_vol_put(struct snd_kcontrol *kcontrol, 34988c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34998c2ecf20Sopenharmony_ci{ 35008c2ecf20Sopenharmony_ci return cap_put_caller(kcontrol, ucontrol, 35018c2ecf20Sopenharmony_ci snd_hda_mixer_amp_volume_put, 35028c2ecf20Sopenharmony_ci NID_PATH_VOL_CTL); 35038c2ecf20Sopenharmony_ci} 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new cap_vol_temp = { 35068c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 35078c2ecf20Sopenharmony_ci .name = "Capture Volume", 35088c2ecf20Sopenharmony_ci .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 35098c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ | 35108c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), 35118c2ecf20Sopenharmony_ci .info = cap_vol_info, 35128c2ecf20Sopenharmony_ci .get = cap_vol_get, 35138c2ecf20Sopenharmony_ci .put = cap_vol_put, 35148c2ecf20Sopenharmony_ci .tlv = { .c = cap_vol_tlv }, 35158c2ecf20Sopenharmony_ci}; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci/* capture switch ctl callbacks */ 35188c2ecf20Sopenharmony_ci#define cap_sw_info snd_ctl_boolean_stereo_info 35198c2ecf20Sopenharmony_ci#define cap_sw_get snd_hda_mixer_amp_switch_get 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_cistatic int cap_sw_put(struct snd_kcontrol *kcontrol, 35228c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 35238c2ecf20Sopenharmony_ci{ 35248c2ecf20Sopenharmony_ci return cap_put_caller(kcontrol, ucontrol, 35258c2ecf20Sopenharmony_ci snd_hda_mixer_amp_switch_put, 35268c2ecf20Sopenharmony_ci NID_PATH_MUTE_CTL); 35278c2ecf20Sopenharmony_ci} 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new cap_sw_temp = { 35308c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 35318c2ecf20Sopenharmony_ci .name = "Capture Switch", 35328c2ecf20Sopenharmony_ci .info = cap_sw_info, 35338c2ecf20Sopenharmony_ci .get = cap_sw_get, 35348c2ecf20Sopenharmony_ci .put = cap_sw_put, 35358c2ecf20Sopenharmony_ci}; 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_cistatic int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) 35388c2ecf20Sopenharmony_ci{ 35398c2ecf20Sopenharmony_ci hda_nid_t nid; 35408c2ecf20Sopenharmony_ci int i, depth; 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; 35438c2ecf20Sopenharmony_ci for (depth = 0; depth < 3; depth++) { 35448c2ecf20Sopenharmony_ci if (depth >= path->depth) 35458c2ecf20Sopenharmony_ci return -EINVAL; 35468c2ecf20Sopenharmony_ci i = path->depth - depth - 1; 35478c2ecf20Sopenharmony_ci nid = path->path[i]; 35488c2ecf20Sopenharmony_ci if (!path->ctls[NID_PATH_VOL_CTL]) { 35498c2ecf20Sopenharmony_ci if (nid_has_volume(codec, nid, HDA_OUTPUT)) 35508c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = 35518c2ecf20Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 35528c2ecf20Sopenharmony_ci else if (nid_has_volume(codec, nid, HDA_INPUT)) { 35538c2ecf20Sopenharmony_ci int idx = path->idx[i]; 35548c2ecf20Sopenharmony_ci if (!depth && codec->single_adc_amp) 35558c2ecf20Sopenharmony_ci idx = 0; 35568c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL] = 35578c2ecf20Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 35588c2ecf20Sopenharmony_ci } 35598c2ecf20Sopenharmony_ci } 35608c2ecf20Sopenharmony_ci if (!path->ctls[NID_PATH_MUTE_CTL]) { 35618c2ecf20Sopenharmony_ci if (nid_has_mute(codec, nid, HDA_OUTPUT)) 35628c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = 35638c2ecf20Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 35648c2ecf20Sopenharmony_ci else if (nid_has_mute(codec, nid, HDA_INPUT)) { 35658c2ecf20Sopenharmony_ci int idx = path->idx[i]; 35668c2ecf20Sopenharmony_ci if (!depth && codec->single_adc_amp) 35678c2ecf20Sopenharmony_ci idx = 0; 35688c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL] = 35698c2ecf20Sopenharmony_ci HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci } 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci return 0; 35748c2ecf20Sopenharmony_ci} 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_cistatic bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) 35778c2ecf20Sopenharmony_ci{ 35788c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 35798c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 35808c2ecf20Sopenharmony_ci unsigned int val; 35818c2ecf20Sopenharmony_ci int i; 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci if (!spec->inv_dmic_split) 35848c2ecf20Sopenharmony_ci return false; 35858c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 35868c2ecf20Sopenharmony_ci if (cfg->inputs[i].pin != nid) 35878c2ecf20Sopenharmony_ci continue; 35888c2ecf20Sopenharmony_ci if (cfg->inputs[i].type != AUTO_PIN_MIC) 35898c2ecf20Sopenharmony_ci return false; 35908c2ecf20Sopenharmony_ci val = snd_hda_codec_get_pincfg(codec, nid); 35918c2ecf20Sopenharmony_ci return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; 35928c2ecf20Sopenharmony_ci } 35938c2ecf20Sopenharmony_ci return false; 35948c2ecf20Sopenharmony_ci} 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_ci/* capture switch put callback for a single control with hook call */ 35978c2ecf20Sopenharmony_cistatic int cap_single_sw_put(struct snd_kcontrol *kcontrol, 35988c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 35998c2ecf20Sopenharmony_ci{ 36008c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 36018c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 36028c2ecf20Sopenharmony_ci int ret; 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 36058c2ecf20Sopenharmony_ci if (ret < 0) 36068c2ecf20Sopenharmony_ci return ret; 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci if (spec->cap_sync_hook) 36098c2ecf20Sopenharmony_ci spec->cap_sync_hook(codec, kcontrol, ucontrol); 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci return ret; 36128c2ecf20Sopenharmony_ci} 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_cistatic int add_single_cap_ctl(struct hda_codec *codec, const char *label, 36158c2ecf20Sopenharmony_ci int idx, bool is_switch, unsigned int ctl, 36168c2ecf20Sopenharmony_ci bool inv_dmic) 36178c2ecf20Sopenharmony_ci{ 36188c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 36198c2ecf20Sopenharmony_ci char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 36208c2ecf20Sopenharmony_ci int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; 36218c2ecf20Sopenharmony_ci const char *sfx = is_switch ? "Switch" : "Volume"; 36228c2ecf20Sopenharmony_ci unsigned int chs = inv_dmic ? 1 : 3; 36238c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 36248c2ecf20Sopenharmony_ci 36258c2ecf20Sopenharmony_ci if (!ctl) 36268c2ecf20Sopenharmony_ci return 0; 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_ci if (label) 36298c2ecf20Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 36308c2ecf20Sopenharmony_ci "%s Capture %s", label, sfx); 36318c2ecf20Sopenharmony_ci else 36328c2ecf20Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 36338c2ecf20Sopenharmony_ci "Capture %s", sfx); 36348c2ecf20Sopenharmony_ci knew = add_control(spec, type, tmpname, idx, 36358c2ecf20Sopenharmony_ci amp_val_replace_channels(ctl, chs)); 36368c2ecf20Sopenharmony_ci if (!knew) 36378c2ecf20Sopenharmony_ci return -ENOMEM; 36388c2ecf20Sopenharmony_ci if (is_switch) 36398c2ecf20Sopenharmony_ci knew->put = cap_single_sw_put; 36408c2ecf20Sopenharmony_ci if (!inv_dmic) 36418c2ecf20Sopenharmony_ci return 0; 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci /* Make independent right kcontrol */ 36448c2ecf20Sopenharmony_ci if (label) 36458c2ecf20Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 36468c2ecf20Sopenharmony_ci "Inverted %s Capture %s", label, sfx); 36478c2ecf20Sopenharmony_ci else 36488c2ecf20Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), 36498c2ecf20Sopenharmony_ci "Inverted Capture %s", sfx); 36508c2ecf20Sopenharmony_ci knew = add_control(spec, type, tmpname, idx, 36518c2ecf20Sopenharmony_ci amp_val_replace_channels(ctl, 2)); 36528c2ecf20Sopenharmony_ci if (!knew) 36538c2ecf20Sopenharmony_ci return -ENOMEM; 36548c2ecf20Sopenharmony_ci if (is_switch) 36558c2ecf20Sopenharmony_ci knew->put = cap_single_sw_put; 36568c2ecf20Sopenharmony_ci return 0; 36578c2ecf20Sopenharmony_ci} 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci/* create single (and simple) capture volume and switch controls */ 36608c2ecf20Sopenharmony_cistatic int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, 36618c2ecf20Sopenharmony_ci unsigned int vol_ctl, unsigned int sw_ctl, 36628c2ecf20Sopenharmony_ci bool inv_dmic) 36638c2ecf20Sopenharmony_ci{ 36648c2ecf20Sopenharmony_ci int err; 36658c2ecf20Sopenharmony_ci err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); 36668c2ecf20Sopenharmony_ci if (err < 0) 36678c2ecf20Sopenharmony_ci return err; 36688c2ecf20Sopenharmony_ci err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); 36698c2ecf20Sopenharmony_ci if (err < 0) 36708c2ecf20Sopenharmony_ci return err; 36718c2ecf20Sopenharmony_ci return 0; 36728c2ecf20Sopenharmony_ci} 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci/* create bound capture volume and switch controls */ 36758c2ecf20Sopenharmony_cistatic int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, 36768c2ecf20Sopenharmony_ci unsigned int vol_ctl, unsigned int sw_ctl) 36778c2ecf20Sopenharmony_ci{ 36788c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 36798c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci if (vol_ctl) { 36828c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); 36838c2ecf20Sopenharmony_ci if (!knew) 36848c2ecf20Sopenharmony_ci return -ENOMEM; 36858c2ecf20Sopenharmony_ci knew->index = idx; 36868c2ecf20Sopenharmony_ci knew->private_value = vol_ctl; 36878c2ecf20Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 36888c2ecf20Sopenharmony_ci } 36898c2ecf20Sopenharmony_ci if (sw_ctl) { 36908c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); 36918c2ecf20Sopenharmony_ci if (!knew) 36928c2ecf20Sopenharmony_ci return -ENOMEM; 36938c2ecf20Sopenharmony_ci knew->index = idx; 36948c2ecf20Sopenharmony_ci knew->private_value = sw_ctl; 36958c2ecf20Sopenharmony_ci knew->subdevice = HDA_SUBDEV_AMP_FLAG; 36968c2ecf20Sopenharmony_ci } 36978c2ecf20Sopenharmony_ci return 0; 36988c2ecf20Sopenharmony_ci} 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci/* return the vol ctl when used first in the imux list */ 37018c2ecf20Sopenharmony_cistatic unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) 37028c2ecf20Sopenharmony_ci{ 37038c2ecf20Sopenharmony_ci struct nid_path *path; 37048c2ecf20Sopenharmony_ci unsigned int ctl; 37058c2ecf20Sopenharmony_ci int i; 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci path = get_input_path(codec, 0, idx); 37088c2ecf20Sopenharmony_ci if (!path) 37098c2ecf20Sopenharmony_ci return 0; 37108c2ecf20Sopenharmony_ci ctl = path->ctls[type]; 37118c2ecf20Sopenharmony_ci if (!ctl) 37128c2ecf20Sopenharmony_ci return 0; 37138c2ecf20Sopenharmony_ci for (i = 0; i < idx - 1; i++) { 37148c2ecf20Sopenharmony_ci path = get_input_path(codec, 0, i); 37158c2ecf20Sopenharmony_ci if (path && path->ctls[type] == ctl) 37168c2ecf20Sopenharmony_ci return 0; 37178c2ecf20Sopenharmony_ci } 37188c2ecf20Sopenharmony_ci return ctl; 37198c2ecf20Sopenharmony_ci} 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci/* create individual capture volume and switch controls per input */ 37228c2ecf20Sopenharmony_cistatic int create_multi_cap_vol_ctl(struct hda_codec *codec) 37238c2ecf20Sopenharmony_ci{ 37248c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 37258c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 37268c2ecf20Sopenharmony_ci int i, err, type; 37278c2ecf20Sopenharmony_ci 37288c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 37298c2ecf20Sopenharmony_ci bool inv_dmic; 37308c2ecf20Sopenharmony_ci int idx; 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci idx = imux->items[i].index; 37338c2ecf20Sopenharmony_ci if (idx >= spec->autocfg.num_inputs) 37348c2ecf20Sopenharmony_ci continue; 37358c2ecf20Sopenharmony_ci inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci for (type = 0; type < 2; type++) { 37388c2ecf20Sopenharmony_ci err = add_single_cap_ctl(codec, 37398c2ecf20Sopenharmony_ci spec->input_labels[idx], 37408c2ecf20Sopenharmony_ci spec->input_label_idxs[idx], 37418c2ecf20Sopenharmony_ci type, 37428c2ecf20Sopenharmony_ci get_first_cap_ctl(codec, i, type), 37438c2ecf20Sopenharmony_ci inv_dmic); 37448c2ecf20Sopenharmony_ci if (err < 0) 37458c2ecf20Sopenharmony_ci return err; 37468c2ecf20Sopenharmony_ci } 37478c2ecf20Sopenharmony_ci } 37488c2ecf20Sopenharmony_ci return 0; 37498c2ecf20Sopenharmony_ci} 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_cistatic int create_capture_mixers(struct hda_codec *codec) 37528c2ecf20Sopenharmony_ci{ 37538c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 37548c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 37558c2ecf20Sopenharmony_ci int i, n, nums, err; 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ci if (spec->dyn_adc_switch) 37588c2ecf20Sopenharmony_ci nums = 1; 37598c2ecf20Sopenharmony_ci else 37608c2ecf20Sopenharmony_ci nums = spec->num_adc_nids; 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci if (!spec->auto_mic && imux->num_items > 1) { 37638c2ecf20Sopenharmony_ci struct snd_kcontrol_new *knew; 37648c2ecf20Sopenharmony_ci const char *name; 37658c2ecf20Sopenharmony_ci name = nums > 1 ? "Input Source" : "Capture Source"; 37668c2ecf20Sopenharmony_ci knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); 37678c2ecf20Sopenharmony_ci if (!knew) 37688c2ecf20Sopenharmony_ci return -ENOMEM; 37698c2ecf20Sopenharmony_ci knew->count = nums; 37708c2ecf20Sopenharmony_ci } 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci for (n = 0; n < nums; n++) { 37738c2ecf20Sopenharmony_ci bool multi = false; 37748c2ecf20Sopenharmony_ci bool multi_cap_vol = spec->multi_cap_vol; 37758c2ecf20Sopenharmony_ci bool inv_dmic = false; 37768c2ecf20Sopenharmony_ci int vol, sw; 37778c2ecf20Sopenharmony_ci 37788c2ecf20Sopenharmony_ci vol = sw = 0; 37798c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 37808c2ecf20Sopenharmony_ci struct nid_path *path; 37818c2ecf20Sopenharmony_ci path = get_input_path(codec, n, i); 37828c2ecf20Sopenharmony_ci if (!path) 37838c2ecf20Sopenharmony_ci continue; 37848c2ecf20Sopenharmony_ci parse_capvol_in_path(codec, path); 37858c2ecf20Sopenharmony_ci if (!vol) 37868c2ecf20Sopenharmony_ci vol = path->ctls[NID_PATH_VOL_CTL]; 37878c2ecf20Sopenharmony_ci else if (vol != path->ctls[NID_PATH_VOL_CTL]) { 37888c2ecf20Sopenharmony_ci multi = true; 37898c2ecf20Sopenharmony_ci if (!same_amp_caps(codec, vol, 37908c2ecf20Sopenharmony_ci path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) 37918c2ecf20Sopenharmony_ci multi_cap_vol = true; 37928c2ecf20Sopenharmony_ci } 37938c2ecf20Sopenharmony_ci if (!sw) 37948c2ecf20Sopenharmony_ci sw = path->ctls[NID_PATH_MUTE_CTL]; 37958c2ecf20Sopenharmony_ci else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { 37968c2ecf20Sopenharmony_ci multi = true; 37978c2ecf20Sopenharmony_ci if (!same_amp_caps(codec, sw, 37988c2ecf20Sopenharmony_ci path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) 37998c2ecf20Sopenharmony_ci multi_cap_vol = true; 38008c2ecf20Sopenharmony_ci } 38018c2ecf20Sopenharmony_ci if (is_inv_dmic_pin(codec, spec->imux_pins[i])) 38028c2ecf20Sopenharmony_ci inv_dmic = true; 38038c2ecf20Sopenharmony_ci } 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci if (!multi) 38068c2ecf20Sopenharmony_ci err = create_single_cap_vol_ctl(codec, n, vol, sw, 38078c2ecf20Sopenharmony_ci inv_dmic); 38088c2ecf20Sopenharmony_ci else if (!multi_cap_vol && !inv_dmic) 38098c2ecf20Sopenharmony_ci err = create_bind_cap_vol_ctl(codec, n, vol, sw); 38108c2ecf20Sopenharmony_ci else 38118c2ecf20Sopenharmony_ci err = create_multi_cap_vol_ctl(codec); 38128c2ecf20Sopenharmony_ci if (err < 0) 38138c2ecf20Sopenharmony_ci return err; 38148c2ecf20Sopenharmony_ci } 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci return 0; 38178c2ecf20Sopenharmony_ci} 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci/* 38208c2ecf20Sopenharmony_ci * add mic boosts if needed 38218c2ecf20Sopenharmony_ci */ 38228c2ecf20Sopenharmony_ci 38238c2ecf20Sopenharmony_ci/* check whether the given amp is feasible as a boost volume */ 38248c2ecf20Sopenharmony_cistatic bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, 38258c2ecf20Sopenharmony_ci int dir, int idx) 38268c2ecf20Sopenharmony_ci{ 38278c2ecf20Sopenharmony_ci unsigned int step; 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci if (!nid_has_volume(codec, nid, dir) || 38308c2ecf20Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || 38318c2ecf20Sopenharmony_ci is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) 38328c2ecf20Sopenharmony_ci return false; 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) 38358c2ecf20Sopenharmony_ci >> AC_AMPCAP_STEP_SIZE_SHIFT; 38368c2ecf20Sopenharmony_ci if (step < 0x20) 38378c2ecf20Sopenharmony_ci return false; 38388c2ecf20Sopenharmony_ci return true; 38398c2ecf20Sopenharmony_ci} 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci/* look for a boost amp in a widget close to the pin */ 38428c2ecf20Sopenharmony_cistatic unsigned int look_for_boost_amp(struct hda_codec *codec, 38438c2ecf20Sopenharmony_ci struct nid_path *path) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci unsigned int val = 0; 38468c2ecf20Sopenharmony_ci hda_nid_t nid; 38478c2ecf20Sopenharmony_ci int depth; 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci for (depth = 0; depth < 3; depth++) { 38508c2ecf20Sopenharmony_ci if (depth >= path->depth - 1) 38518c2ecf20Sopenharmony_ci break; 38528c2ecf20Sopenharmony_ci nid = path->path[depth]; 38538c2ecf20Sopenharmony_ci if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { 38548c2ecf20Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 38558c2ecf20Sopenharmony_ci break; 38568c2ecf20Sopenharmony_ci } else if (check_boost_vol(codec, nid, HDA_INPUT, 38578c2ecf20Sopenharmony_ci path->idx[depth])) { 38588c2ecf20Sopenharmony_ci val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], 38598c2ecf20Sopenharmony_ci HDA_INPUT); 38608c2ecf20Sopenharmony_ci break; 38618c2ecf20Sopenharmony_ci } 38628c2ecf20Sopenharmony_ci } 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci return val; 38658c2ecf20Sopenharmony_ci} 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_cistatic int parse_mic_boost(struct hda_codec *codec) 38688c2ecf20Sopenharmony_ci{ 38698c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 38708c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 38718c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 38728c2ecf20Sopenharmony_ci int i; 38738c2ecf20Sopenharmony_ci 38748c2ecf20Sopenharmony_ci if (!spec->num_adc_nids) 38758c2ecf20Sopenharmony_ci return 0; 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 38788c2ecf20Sopenharmony_ci struct nid_path *path; 38798c2ecf20Sopenharmony_ci unsigned int val; 38808c2ecf20Sopenharmony_ci int idx; 38818c2ecf20Sopenharmony_ci char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci idx = imux->items[i].index; 38848c2ecf20Sopenharmony_ci if (idx >= imux->num_items) 38858c2ecf20Sopenharmony_ci continue; 38868c2ecf20Sopenharmony_ci 38878c2ecf20Sopenharmony_ci /* check only line-in and mic pins */ 38888c2ecf20Sopenharmony_ci if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) 38898c2ecf20Sopenharmony_ci continue; 38908c2ecf20Sopenharmony_ci 38918c2ecf20Sopenharmony_ci path = get_input_path(codec, 0, i); 38928c2ecf20Sopenharmony_ci if (!path) 38938c2ecf20Sopenharmony_ci continue; 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci val = look_for_boost_amp(codec, path); 38968c2ecf20Sopenharmony_ci if (!val) 38978c2ecf20Sopenharmony_ci continue; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci /* create a boost control */ 39008c2ecf20Sopenharmony_ci snprintf(boost_label, sizeof(boost_label), 39018c2ecf20Sopenharmony_ci "%s Boost Volume", spec->input_labels[idx]); 39028c2ecf20Sopenharmony_ci if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, 39038c2ecf20Sopenharmony_ci spec->input_label_idxs[idx], val)) 39048c2ecf20Sopenharmony_ci return -ENOMEM; 39058c2ecf20Sopenharmony_ci 39068c2ecf20Sopenharmony_ci path->ctls[NID_PATH_BOOST_CTL] = val; 39078c2ecf20Sopenharmony_ci } 39088c2ecf20Sopenharmony_ci return 0; 39098c2ecf20Sopenharmony_ci} 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_GENERIC_LEDS 39128c2ecf20Sopenharmony_ci/* 39138c2ecf20Sopenharmony_ci * vmaster mute LED hook helpers 39148c2ecf20Sopenharmony_ci */ 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_cistatic int create_mute_led_cdev(struct hda_codec *codec, 39178c2ecf20Sopenharmony_ci int (*callback)(struct led_classdev *, 39188c2ecf20Sopenharmony_ci enum led_brightness), 39198c2ecf20Sopenharmony_ci bool micmute) 39208c2ecf20Sopenharmony_ci{ 39218c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 39228c2ecf20Sopenharmony_ci struct led_classdev *cdev; 39238c2ecf20Sopenharmony_ci int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE; 39248c2ecf20Sopenharmony_ci int err; 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); 39278c2ecf20Sopenharmony_ci if (!cdev) 39288c2ecf20Sopenharmony_ci return -ENOMEM; 39298c2ecf20Sopenharmony_ci 39308c2ecf20Sopenharmony_ci cdev->name = micmute ? "hda::micmute" : "hda::mute"; 39318c2ecf20Sopenharmony_ci cdev->max_brightness = 1; 39328c2ecf20Sopenharmony_ci cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; 39338c2ecf20Sopenharmony_ci cdev->brightness_set_blocking = callback; 39348c2ecf20Sopenharmony_ci cdev->brightness = ledtrig_audio_get(idx); 39358c2ecf20Sopenharmony_ci cdev->flags = LED_CORE_SUSPENDRESUME; 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_ci err = led_classdev_register(&codec->core.dev, cdev); 39388c2ecf20Sopenharmony_ci if (err < 0) 39398c2ecf20Sopenharmony_ci return err; 39408c2ecf20Sopenharmony_ci spec->led_cdevs[idx] = cdev; 39418c2ecf20Sopenharmony_ci return 0; 39428c2ecf20Sopenharmony_ci} 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_cistatic void vmaster_update_mute_led(void *private_data, int enabled) 39458c2ecf20Sopenharmony_ci{ 39468c2ecf20Sopenharmony_ci ledtrig_audio_set(LED_AUDIO_MUTE, enabled ? LED_OFF : LED_ON); 39478c2ecf20Sopenharmony_ci} 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci/** 39508c2ecf20Sopenharmony_ci * snd_dha_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED 39518c2ecf20Sopenharmony_ci * @codec: the HDA codec 39528c2ecf20Sopenharmony_ci * @callback: the callback for LED classdev brightness_set_blocking 39538c2ecf20Sopenharmony_ci */ 39548c2ecf20Sopenharmony_ciint snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, 39558c2ecf20Sopenharmony_ci int (*callback)(struct led_classdev *, 39568c2ecf20Sopenharmony_ci enum led_brightness)) 39578c2ecf20Sopenharmony_ci{ 39588c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 39598c2ecf20Sopenharmony_ci int err; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci if (callback) { 39628c2ecf20Sopenharmony_ci err = create_mute_led_cdev(codec, callback, false); 39638c2ecf20Sopenharmony_ci if (err) { 39648c2ecf20Sopenharmony_ci codec_warn(codec, "failed to create a mute LED cdev\n"); 39658c2ecf20Sopenharmony_ci return err; 39668c2ecf20Sopenharmony_ci } 39678c2ecf20Sopenharmony_ci } 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci if (spec->vmaster_mute.hook) 39708c2ecf20Sopenharmony_ci codec_err(codec, "vmaster hook already present before cdev!\n"); 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_ci spec->vmaster_mute.hook = vmaster_update_mute_led; 39738c2ecf20Sopenharmony_ci spec->vmaster_mute_enum = 1; 39748c2ecf20Sopenharmony_ci return 0; 39758c2ecf20Sopenharmony_ci} 39768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev); 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ci/* 39798c2ecf20Sopenharmony_ci * mic mute LED hook helpers 39808c2ecf20Sopenharmony_ci */ 39818c2ecf20Sopenharmony_cienum { 39828c2ecf20Sopenharmony_ci MICMUTE_LED_ON, 39838c2ecf20Sopenharmony_ci MICMUTE_LED_OFF, 39848c2ecf20Sopenharmony_ci MICMUTE_LED_FOLLOW_CAPTURE, 39858c2ecf20Sopenharmony_ci MICMUTE_LED_FOLLOW_MUTE, 39868c2ecf20Sopenharmony_ci}; 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_cistatic void call_micmute_led_update(struct hda_codec *codec) 39898c2ecf20Sopenharmony_ci{ 39908c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 39918c2ecf20Sopenharmony_ci unsigned int val; 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_ci switch (spec->micmute_led.led_mode) { 39948c2ecf20Sopenharmony_ci case MICMUTE_LED_ON: 39958c2ecf20Sopenharmony_ci val = 1; 39968c2ecf20Sopenharmony_ci break; 39978c2ecf20Sopenharmony_ci case MICMUTE_LED_OFF: 39988c2ecf20Sopenharmony_ci val = 0; 39998c2ecf20Sopenharmony_ci break; 40008c2ecf20Sopenharmony_ci case MICMUTE_LED_FOLLOW_CAPTURE: 40018c2ecf20Sopenharmony_ci val = !!spec->micmute_led.capture; 40028c2ecf20Sopenharmony_ci break; 40038c2ecf20Sopenharmony_ci case MICMUTE_LED_FOLLOW_MUTE: 40048c2ecf20Sopenharmony_ci default: 40058c2ecf20Sopenharmony_ci val = !spec->micmute_led.capture; 40068c2ecf20Sopenharmony_ci break; 40078c2ecf20Sopenharmony_ci } 40088c2ecf20Sopenharmony_ci 40098c2ecf20Sopenharmony_ci if (val == spec->micmute_led.led_value) 40108c2ecf20Sopenharmony_ci return; 40118c2ecf20Sopenharmony_ci spec->micmute_led.led_value = val; 40128c2ecf20Sopenharmony_ci ledtrig_audio_set(LED_AUDIO_MICMUTE, 40138c2ecf20Sopenharmony_ci spec->micmute_led.led_value ? LED_ON : LED_OFF); 40148c2ecf20Sopenharmony_ci} 40158c2ecf20Sopenharmony_ci 40168c2ecf20Sopenharmony_cistatic void update_micmute_led(struct hda_codec *codec, 40178c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, 40188c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40198c2ecf20Sopenharmony_ci{ 40208c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 40218c2ecf20Sopenharmony_ci unsigned int mask; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci if (spec->micmute_led.old_hook) 40248c2ecf20Sopenharmony_ci spec->micmute_led.old_hook(codec, kcontrol, ucontrol); 40258c2ecf20Sopenharmony_ci 40268c2ecf20Sopenharmony_ci if (!ucontrol) 40278c2ecf20Sopenharmony_ci return; 40288c2ecf20Sopenharmony_ci mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 40298c2ecf20Sopenharmony_ci if (!strcmp("Capture Switch", ucontrol->id.name)) { 40308c2ecf20Sopenharmony_ci /* TODO: How do I verify if it's a mono or stereo here? */ 40318c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] || 40328c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1]) 40338c2ecf20Sopenharmony_ci spec->micmute_led.capture |= mask; 40348c2ecf20Sopenharmony_ci else 40358c2ecf20Sopenharmony_ci spec->micmute_led.capture &= ~mask; 40368c2ecf20Sopenharmony_ci call_micmute_led_update(codec); 40378c2ecf20Sopenharmony_ci } 40388c2ecf20Sopenharmony_ci} 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_cistatic int micmute_led_mode_info(struct snd_kcontrol *kcontrol, 40418c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 40428c2ecf20Sopenharmony_ci{ 40438c2ecf20Sopenharmony_ci static const char * const texts[] = { 40448c2ecf20Sopenharmony_ci "On", "Off", "Follow Capture", "Follow Mute", 40458c2ecf20Sopenharmony_ci }; 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); 40488c2ecf20Sopenharmony_ci} 40498c2ecf20Sopenharmony_ci 40508c2ecf20Sopenharmony_cistatic int micmute_led_mode_get(struct snd_kcontrol *kcontrol, 40518c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40528c2ecf20Sopenharmony_ci{ 40538c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 40548c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode; 40578c2ecf20Sopenharmony_ci return 0; 40588c2ecf20Sopenharmony_ci} 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_cistatic int micmute_led_mode_put(struct snd_kcontrol *kcontrol, 40618c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40628c2ecf20Sopenharmony_ci{ 40638c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 40648c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 40658c2ecf20Sopenharmony_ci unsigned int mode; 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci mode = ucontrol->value.enumerated.item[0]; 40688c2ecf20Sopenharmony_ci if (mode > MICMUTE_LED_FOLLOW_MUTE) 40698c2ecf20Sopenharmony_ci mode = MICMUTE_LED_FOLLOW_MUTE; 40708c2ecf20Sopenharmony_ci if (mode == spec->micmute_led.led_mode) 40718c2ecf20Sopenharmony_ci return 0; 40728c2ecf20Sopenharmony_ci spec->micmute_led.led_mode = mode; 40738c2ecf20Sopenharmony_ci call_micmute_led_update(codec); 40748c2ecf20Sopenharmony_ci return 1; 40758c2ecf20Sopenharmony_ci} 40768c2ecf20Sopenharmony_ci 40778c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new micmute_led_mode_ctl = { 40788c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 40798c2ecf20Sopenharmony_ci .name = "Mic Mute-LED Mode", 40808c2ecf20Sopenharmony_ci .info = micmute_led_mode_info, 40818c2ecf20Sopenharmony_ci .get = micmute_led_mode_get, 40828c2ecf20Sopenharmony_ci .put = micmute_led_mode_put, 40838c2ecf20Sopenharmony_ci}; 40848c2ecf20Sopenharmony_ci 40858c2ecf20Sopenharmony_ci/* Set up the capture sync hook for controlling the mic-mute LED */ 40868c2ecf20Sopenharmony_cistatic int add_micmute_led_hook(struct hda_codec *codec) 40878c2ecf20Sopenharmony_ci{ 40888c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE; 40918c2ecf20Sopenharmony_ci spec->micmute_led.capture = 0; 40928c2ecf20Sopenharmony_ci spec->micmute_led.led_value = -1; 40938c2ecf20Sopenharmony_ci spec->micmute_led.old_hook = spec->cap_sync_hook; 40948c2ecf20Sopenharmony_ci spec->cap_sync_hook = update_micmute_led; 40958c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl)) 40968c2ecf20Sopenharmony_ci return -ENOMEM; 40978c2ecf20Sopenharmony_ci return 0; 40988c2ecf20Sopenharmony_ci} 40998c2ecf20Sopenharmony_ci 41008c2ecf20Sopenharmony_ci/** 41018c2ecf20Sopenharmony_ci * snd_dha_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED 41028c2ecf20Sopenharmony_ci * @codec: the HDA codec 41038c2ecf20Sopenharmony_ci * @callback: the callback for LED classdev brightness_set_blocking 41048c2ecf20Sopenharmony_ci * 41058c2ecf20Sopenharmony_ci * Called from the codec drivers for offering the mic mute LED controls. 41068c2ecf20Sopenharmony_ci * This creates a LED classdev and sets up the cap_sync_hook that is called at 41078c2ecf20Sopenharmony_ci * each time when the capture mixer switch changes. 41088c2ecf20Sopenharmony_ci * 41098c2ecf20Sopenharmony_ci * When NULL is passed to @callback, no classdev is created but only the 41108c2ecf20Sopenharmony_ci * LED-trigger is set up. 41118c2ecf20Sopenharmony_ci * 41128c2ecf20Sopenharmony_ci * Returns 0 or a negative error. 41138c2ecf20Sopenharmony_ci */ 41148c2ecf20Sopenharmony_ciint snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, 41158c2ecf20Sopenharmony_ci int (*callback)(struct led_classdev *, 41168c2ecf20Sopenharmony_ci enum led_brightness)) 41178c2ecf20Sopenharmony_ci{ 41188c2ecf20Sopenharmony_ci int err; 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci if (callback) { 41218c2ecf20Sopenharmony_ci err = create_mute_led_cdev(codec, callback, true); 41228c2ecf20Sopenharmony_ci if (err) { 41238c2ecf20Sopenharmony_ci codec_warn(codec, "failed to create a mic-mute LED cdev\n"); 41248c2ecf20Sopenharmony_ci return err; 41258c2ecf20Sopenharmony_ci } 41268c2ecf20Sopenharmony_ci } 41278c2ecf20Sopenharmony_ci 41288c2ecf20Sopenharmony_ci return add_micmute_led_hook(codec); 41298c2ecf20Sopenharmony_ci} 41308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); 41318c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_HDA_GENERIC_LEDS */ 41328c2ecf20Sopenharmony_ci 41338c2ecf20Sopenharmony_ci/* 41348c2ecf20Sopenharmony_ci * parse digital I/Os and set up NIDs in BIOS auto-parse mode 41358c2ecf20Sopenharmony_ci */ 41368c2ecf20Sopenharmony_cistatic void parse_digital(struct hda_codec *codec) 41378c2ecf20Sopenharmony_ci{ 41388c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 41398c2ecf20Sopenharmony_ci struct nid_path *path; 41408c2ecf20Sopenharmony_ci int i, nums; 41418c2ecf20Sopenharmony_ci hda_nid_t dig_nid, pin; 41428c2ecf20Sopenharmony_ci 41438c2ecf20Sopenharmony_ci /* support multiple SPDIFs; the secondary is set up as a follower */ 41448c2ecf20Sopenharmony_ci nums = 0; 41458c2ecf20Sopenharmony_ci for (i = 0; i < spec->autocfg.dig_outs; i++) { 41468c2ecf20Sopenharmony_ci pin = spec->autocfg.dig_out_pins[i]; 41478c2ecf20Sopenharmony_ci dig_nid = look_for_dac(codec, pin, true); 41488c2ecf20Sopenharmony_ci if (!dig_nid) 41498c2ecf20Sopenharmony_ci continue; 41508c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, dig_nid, pin, 0); 41518c2ecf20Sopenharmony_ci if (!path) 41528c2ecf20Sopenharmony_ci continue; 41538c2ecf20Sopenharmony_ci print_nid_path(codec, "digout", path); 41548c2ecf20Sopenharmony_ci path->active = true; 41558c2ecf20Sopenharmony_ci path->pin_fixed = true; /* no jack detection */ 41568c2ecf20Sopenharmony_ci spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); 41578c2ecf20Sopenharmony_ci set_pin_target(codec, pin, PIN_OUT, false); 41588c2ecf20Sopenharmony_ci if (!nums) { 41598c2ecf20Sopenharmony_ci spec->multiout.dig_out_nid = dig_nid; 41608c2ecf20Sopenharmony_ci spec->dig_out_type = spec->autocfg.dig_out_type[0]; 41618c2ecf20Sopenharmony_ci } else { 41628c2ecf20Sopenharmony_ci spec->multiout.follower_dig_outs = spec->follower_dig_outs; 41638c2ecf20Sopenharmony_ci if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1) 41648c2ecf20Sopenharmony_ci break; 41658c2ecf20Sopenharmony_ci spec->follower_dig_outs[nums - 1] = dig_nid; 41668c2ecf20Sopenharmony_ci } 41678c2ecf20Sopenharmony_ci nums++; 41688c2ecf20Sopenharmony_ci } 41698c2ecf20Sopenharmony_ci 41708c2ecf20Sopenharmony_ci if (spec->autocfg.dig_in_pin) { 41718c2ecf20Sopenharmony_ci pin = spec->autocfg.dig_in_pin; 41728c2ecf20Sopenharmony_ci for_each_hda_codec_node(dig_nid, codec) { 41738c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, dig_nid); 41748c2ecf20Sopenharmony_ci if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 41758c2ecf20Sopenharmony_ci continue; 41768c2ecf20Sopenharmony_ci if (!(wcaps & AC_WCAP_DIGITAL)) 41778c2ecf20Sopenharmony_ci continue; 41788c2ecf20Sopenharmony_ci path = snd_hda_add_new_path(codec, pin, dig_nid, 0); 41798c2ecf20Sopenharmony_ci if (path) { 41808c2ecf20Sopenharmony_ci print_nid_path(codec, "digin", path); 41818c2ecf20Sopenharmony_ci path->active = true; 41828c2ecf20Sopenharmony_ci path->pin_fixed = true; /* no jack */ 41838c2ecf20Sopenharmony_ci spec->dig_in_nid = dig_nid; 41848c2ecf20Sopenharmony_ci spec->digin_path = snd_hda_get_path_idx(codec, path); 41858c2ecf20Sopenharmony_ci set_pin_target(codec, pin, PIN_IN, false); 41868c2ecf20Sopenharmony_ci break; 41878c2ecf20Sopenharmony_ci } 41888c2ecf20Sopenharmony_ci } 41898c2ecf20Sopenharmony_ci } 41908c2ecf20Sopenharmony_ci} 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci/* 41948c2ecf20Sopenharmony_ci * input MUX handling 41958c2ecf20Sopenharmony_ci */ 41968c2ecf20Sopenharmony_ci 41978c2ecf20Sopenharmony_cistatic bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci/* select the given imux item; either unmute exclusively or select the route */ 42008c2ecf20Sopenharmony_cistatic int mux_select(struct hda_codec *codec, unsigned int adc_idx, 42018c2ecf20Sopenharmony_ci unsigned int idx) 42028c2ecf20Sopenharmony_ci{ 42038c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 42048c2ecf20Sopenharmony_ci const struct hda_input_mux *imux; 42058c2ecf20Sopenharmony_ci struct nid_path *old_path, *path; 42068c2ecf20Sopenharmony_ci 42078c2ecf20Sopenharmony_ci imux = &spec->input_mux; 42088c2ecf20Sopenharmony_ci if (!imux->num_items) 42098c2ecf20Sopenharmony_ci return 0; 42108c2ecf20Sopenharmony_ci 42118c2ecf20Sopenharmony_ci if (idx >= imux->num_items) 42128c2ecf20Sopenharmony_ci idx = imux->num_items - 1; 42138c2ecf20Sopenharmony_ci if (spec->cur_mux[adc_idx] == idx) 42148c2ecf20Sopenharmony_ci return 0; 42158c2ecf20Sopenharmony_ci 42168c2ecf20Sopenharmony_ci old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); 42178c2ecf20Sopenharmony_ci if (!old_path) 42188c2ecf20Sopenharmony_ci return 0; 42198c2ecf20Sopenharmony_ci if (old_path->active) 42208c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, old_path, false, false); 42218c2ecf20Sopenharmony_ci 42228c2ecf20Sopenharmony_ci spec->cur_mux[adc_idx] = idx; 42238c2ecf20Sopenharmony_ci 42248c2ecf20Sopenharmony_ci if (spec->hp_mic) 42258c2ecf20Sopenharmony_ci update_hp_mic(codec, adc_idx, false); 42268c2ecf20Sopenharmony_ci 42278c2ecf20Sopenharmony_ci if (spec->dyn_adc_switch) 42288c2ecf20Sopenharmony_ci dyn_adc_pcm_resetup(codec, idx); 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci path = get_input_path(codec, adc_idx, idx); 42318c2ecf20Sopenharmony_ci if (!path) 42328c2ecf20Sopenharmony_ci return 0; 42338c2ecf20Sopenharmony_ci if (path->active) 42348c2ecf20Sopenharmony_ci return 0; 42358c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, true, false); 42368c2ecf20Sopenharmony_ci if (spec->cap_sync_hook) 42378c2ecf20Sopenharmony_ci spec->cap_sync_hook(codec, NULL, NULL); 42388c2ecf20Sopenharmony_ci path_power_down_sync(codec, old_path); 42398c2ecf20Sopenharmony_ci return 1; 42408c2ecf20Sopenharmony_ci} 42418c2ecf20Sopenharmony_ci 42428c2ecf20Sopenharmony_ci/* power up/down widgets in the all paths that match with the given NID 42438c2ecf20Sopenharmony_ci * as terminals (either start- or endpoint) 42448c2ecf20Sopenharmony_ci * 42458c2ecf20Sopenharmony_ci * returns the last changed NID, or zero if unchanged. 42468c2ecf20Sopenharmony_ci */ 42478c2ecf20Sopenharmony_cistatic hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, 42488c2ecf20Sopenharmony_ci int pin_state, int stream_state) 42498c2ecf20Sopenharmony_ci{ 42508c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 42518c2ecf20Sopenharmony_ci hda_nid_t last, changed = 0; 42528c2ecf20Sopenharmony_ci struct nid_path *path; 42538c2ecf20Sopenharmony_ci int n; 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci snd_array_for_each(&spec->paths, n, path) { 42568c2ecf20Sopenharmony_ci if (!path->depth) 42578c2ecf20Sopenharmony_ci continue; 42588c2ecf20Sopenharmony_ci if (path->path[0] == nid || 42598c2ecf20Sopenharmony_ci path->path[path->depth - 1] == nid) { 42608c2ecf20Sopenharmony_ci bool pin_old = path->pin_enabled; 42618c2ecf20Sopenharmony_ci bool stream_old = path->stream_enabled; 42628c2ecf20Sopenharmony_ci 42638c2ecf20Sopenharmony_ci if (pin_state >= 0) 42648c2ecf20Sopenharmony_ci path->pin_enabled = pin_state; 42658c2ecf20Sopenharmony_ci if (stream_state >= 0) 42668c2ecf20Sopenharmony_ci path->stream_enabled = stream_state; 42678c2ecf20Sopenharmony_ci if ((!path->pin_fixed && path->pin_enabled != pin_old) 42688c2ecf20Sopenharmony_ci || path->stream_enabled != stream_old) { 42698c2ecf20Sopenharmony_ci last = path_power_update(codec, path, true); 42708c2ecf20Sopenharmony_ci if (last) 42718c2ecf20Sopenharmony_ci changed = last; 42728c2ecf20Sopenharmony_ci } 42738c2ecf20Sopenharmony_ci } 42748c2ecf20Sopenharmony_ci } 42758c2ecf20Sopenharmony_ci return changed; 42768c2ecf20Sopenharmony_ci} 42778c2ecf20Sopenharmony_ci 42788c2ecf20Sopenharmony_ci/* check the jack status for power control */ 42798c2ecf20Sopenharmony_cistatic bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) 42808c2ecf20Sopenharmony_ci{ 42818c2ecf20Sopenharmony_ci if (!is_jack_detectable(codec, pin)) 42828c2ecf20Sopenharmony_ci return true; 42838c2ecf20Sopenharmony_ci return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; 42848c2ecf20Sopenharmony_ci} 42858c2ecf20Sopenharmony_ci 42868c2ecf20Sopenharmony_ci/* power up/down the paths of the given pin according to the jack state; 42878c2ecf20Sopenharmony_ci * power = 0/1 : only power up/down if it matches with the jack state, 42888c2ecf20Sopenharmony_ci * < 0 : force power up/down to follow the jack sate 42898c2ecf20Sopenharmony_ci * 42908c2ecf20Sopenharmony_ci * returns the last changed NID, or zero if unchanged. 42918c2ecf20Sopenharmony_ci */ 42928c2ecf20Sopenharmony_cistatic hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, 42938c2ecf20Sopenharmony_ci int power) 42948c2ecf20Sopenharmony_ci{ 42958c2ecf20Sopenharmony_ci bool on; 42968c2ecf20Sopenharmony_ci 42978c2ecf20Sopenharmony_ci if (!codec->power_save_node) 42988c2ecf20Sopenharmony_ci return 0; 42998c2ecf20Sopenharmony_ci 43008c2ecf20Sopenharmony_ci on = detect_pin_state(codec, pin); 43018c2ecf20Sopenharmony_ci 43028c2ecf20Sopenharmony_ci if (power >= 0 && on != power) 43038c2ecf20Sopenharmony_ci return 0; 43048c2ecf20Sopenharmony_ci return set_path_power(codec, pin, on, -1); 43058c2ecf20Sopenharmony_ci} 43068c2ecf20Sopenharmony_ci 43078c2ecf20Sopenharmony_cistatic void pin_power_callback(struct hda_codec *codec, 43088c2ecf20Sopenharmony_ci struct hda_jack_callback *jack, 43098c2ecf20Sopenharmony_ci bool on) 43108c2ecf20Sopenharmony_ci{ 43118c2ecf20Sopenharmony_ci if (jack && jack->nid) 43128c2ecf20Sopenharmony_ci sync_power_state_change(codec, 43138c2ecf20Sopenharmony_ci set_pin_power_jack(codec, jack->nid, on)); 43148c2ecf20Sopenharmony_ci} 43158c2ecf20Sopenharmony_ci 43168c2ecf20Sopenharmony_ci/* callback only doing power up -- called at first */ 43178c2ecf20Sopenharmony_cistatic void pin_power_up_callback(struct hda_codec *codec, 43188c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 43198c2ecf20Sopenharmony_ci{ 43208c2ecf20Sopenharmony_ci pin_power_callback(codec, jack, true); 43218c2ecf20Sopenharmony_ci} 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_ci/* callback only doing power down -- called at last */ 43248c2ecf20Sopenharmony_cistatic void pin_power_down_callback(struct hda_codec *codec, 43258c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 43268c2ecf20Sopenharmony_ci{ 43278c2ecf20Sopenharmony_ci pin_power_callback(codec, jack, false); 43288c2ecf20Sopenharmony_ci} 43298c2ecf20Sopenharmony_ci 43308c2ecf20Sopenharmony_ci/* set up the power up/down callbacks */ 43318c2ecf20Sopenharmony_cistatic void add_pin_power_ctls(struct hda_codec *codec, int num_pins, 43328c2ecf20Sopenharmony_ci const hda_nid_t *pins, bool on) 43338c2ecf20Sopenharmony_ci{ 43348c2ecf20Sopenharmony_ci int i; 43358c2ecf20Sopenharmony_ci hda_jack_callback_fn cb = 43368c2ecf20Sopenharmony_ci on ? pin_power_up_callback : pin_power_down_callback; 43378c2ecf20Sopenharmony_ci 43388c2ecf20Sopenharmony_ci for (i = 0; i < num_pins && pins[i]; i++) { 43398c2ecf20Sopenharmony_ci if (is_jack_detectable(codec, pins[i])) 43408c2ecf20Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, pins[i], cb); 43418c2ecf20Sopenharmony_ci else 43428c2ecf20Sopenharmony_ci set_path_power(codec, pins[i], true, -1); 43438c2ecf20Sopenharmony_ci } 43448c2ecf20Sopenharmony_ci} 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_ci/* enabled power callback to each available I/O pin with jack detections; 43478c2ecf20Sopenharmony_ci * the digital I/O pins are excluded because of the unreliable detectsion 43488c2ecf20Sopenharmony_ci */ 43498c2ecf20Sopenharmony_cistatic void add_all_pin_power_ctls(struct hda_codec *codec, bool on) 43508c2ecf20Sopenharmony_ci{ 43518c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 43528c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 43538c2ecf20Sopenharmony_ci int i; 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_ci if (!codec->power_save_node) 43568c2ecf20Sopenharmony_ci return; 43578c2ecf20Sopenharmony_ci add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); 43588c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 43598c2ecf20Sopenharmony_ci add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); 43608c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 43618c2ecf20Sopenharmony_ci add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); 43628c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) 43638c2ecf20Sopenharmony_ci add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); 43648c2ecf20Sopenharmony_ci} 43658c2ecf20Sopenharmony_ci 43668c2ecf20Sopenharmony_ci/* sync path power up/down with the jack states of given pins */ 43678c2ecf20Sopenharmony_cistatic void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, 43688c2ecf20Sopenharmony_ci const hda_nid_t *pins) 43698c2ecf20Sopenharmony_ci{ 43708c2ecf20Sopenharmony_ci int i; 43718c2ecf20Sopenharmony_ci 43728c2ecf20Sopenharmony_ci for (i = 0; i < num_pins && pins[i]; i++) 43738c2ecf20Sopenharmony_ci if (is_jack_detectable(codec, pins[i])) 43748c2ecf20Sopenharmony_ci set_pin_power_jack(codec, pins[i], -1); 43758c2ecf20Sopenharmony_ci} 43768c2ecf20Sopenharmony_ci 43778c2ecf20Sopenharmony_ci/* sync path power up/down with pins; called at init and resume */ 43788c2ecf20Sopenharmony_cistatic void sync_all_pin_power_ctls(struct hda_codec *codec) 43798c2ecf20Sopenharmony_ci{ 43808c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 43818c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 43828c2ecf20Sopenharmony_ci int i; 43838c2ecf20Sopenharmony_ci 43848c2ecf20Sopenharmony_ci if (!codec->power_save_node) 43858c2ecf20Sopenharmony_ci return; 43868c2ecf20Sopenharmony_ci sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); 43878c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 43888c2ecf20Sopenharmony_ci sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); 43898c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 43908c2ecf20Sopenharmony_ci sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); 43918c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) 43928c2ecf20Sopenharmony_ci sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); 43938c2ecf20Sopenharmony_ci} 43948c2ecf20Sopenharmony_ci 43958c2ecf20Sopenharmony_ci/* add fake paths if not present yet */ 43968c2ecf20Sopenharmony_cistatic int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, 43978c2ecf20Sopenharmony_ci int num_pins, const hda_nid_t *pins) 43988c2ecf20Sopenharmony_ci{ 43998c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 44008c2ecf20Sopenharmony_ci struct nid_path *path; 44018c2ecf20Sopenharmony_ci int i; 44028c2ecf20Sopenharmony_ci 44038c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) { 44048c2ecf20Sopenharmony_ci if (!pins[i]) 44058c2ecf20Sopenharmony_ci break; 44068c2ecf20Sopenharmony_ci if (get_nid_path(codec, nid, pins[i], 0)) 44078c2ecf20Sopenharmony_ci continue; 44088c2ecf20Sopenharmony_ci path = snd_array_new(&spec->paths); 44098c2ecf20Sopenharmony_ci if (!path) 44108c2ecf20Sopenharmony_ci return -ENOMEM; 44118c2ecf20Sopenharmony_ci memset(path, 0, sizeof(*path)); 44128c2ecf20Sopenharmony_ci path->depth = 2; 44138c2ecf20Sopenharmony_ci path->path[0] = nid; 44148c2ecf20Sopenharmony_ci path->path[1] = pins[i]; 44158c2ecf20Sopenharmony_ci path->active = true; 44168c2ecf20Sopenharmony_ci } 44178c2ecf20Sopenharmony_ci return 0; 44188c2ecf20Sopenharmony_ci} 44198c2ecf20Sopenharmony_ci 44208c2ecf20Sopenharmony_ci/* create fake paths to all outputs from beep */ 44218c2ecf20Sopenharmony_cistatic int add_fake_beep_paths(struct hda_codec *codec) 44228c2ecf20Sopenharmony_ci{ 44238c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 44248c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 44258c2ecf20Sopenharmony_ci hda_nid_t nid = spec->beep_nid; 44268c2ecf20Sopenharmony_ci int err; 44278c2ecf20Sopenharmony_ci 44288c2ecf20Sopenharmony_ci if (!codec->power_save_node || !nid) 44298c2ecf20Sopenharmony_ci return 0; 44308c2ecf20Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); 44318c2ecf20Sopenharmony_ci if (err < 0) 44328c2ecf20Sopenharmony_ci return err; 44338c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 44348c2ecf20Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); 44358c2ecf20Sopenharmony_ci if (err < 0) 44368c2ecf20Sopenharmony_ci return err; 44378c2ecf20Sopenharmony_ci } 44388c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 44398c2ecf20Sopenharmony_ci err = add_fake_paths(codec, nid, cfg->speaker_outs, 44408c2ecf20Sopenharmony_ci cfg->speaker_pins); 44418c2ecf20Sopenharmony_ci if (err < 0) 44428c2ecf20Sopenharmony_ci return err; 44438c2ecf20Sopenharmony_ci } 44448c2ecf20Sopenharmony_ci return 0; 44458c2ecf20Sopenharmony_ci} 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_ci/* power up/down beep widget and its output paths */ 44488c2ecf20Sopenharmony_cistatic void beep_power_hook(struct hda_beep *beep, bool on) 44498c2ecf20Sopenharmony_ci{ 44508c2ecf20Sopenharmony_ci set_path_power(beep->codec, beep->nid, -1, on); 44518c2ecf20Sopenharmony_ci} 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_ci/** 44548c2ecf20Sopenharmony_ci * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 44558c2ecf20Sopenharmony_ci * @codec: the HDA codec 44568c2ecf20Sopenharmony_ci * @pin: NID of pin to fix 44578c2ecf20Sopenharmony_ci */ 44588c2ecf20Sopenharmony_ciint snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) 44598c2ecf20Sopenharmony_ci{ 44608c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 44618c2ecf20Sopenharmony_ci struct nid_path *path; 44628c2ecf20Sopenharmony_ci 44638c2ecf20Sopenharmony_ci path = snd_array_new(&spec->paths); 44648c2ecf20Sopenharmony_ci if (!path) 44658c2ecf20Sopenharmony_ci return -ENOMEM; 44668c2ecf20Sopenharmony_ci memset(path, 0, sizeof(*path)); 44678c2ecf20Sopenharmony_ci path->depth = 1; 44688c2ecf20Sopenharmony_ci path->path[0] = pin; 44698c2ecf20Sopenharmony_ci path->active = true; 44708c2ecf20Sopenharmony_ci path->pin_fixed = true; 44718c2ecf20Sopenharmony_ci path->stream_enabled = true; 44728c2ecf20Sopenharmony_ci return 0; 44738c2ecf20Sopenharmony_ci} 44748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); 44758c2ecf20Sopenharmony_ci 44768c2ecf20Sopenharmony_ci/* 44778c2ecf20Sopenharmony_ci * Jack detections for HP auto-mute and mic-switch 44788c2ecf20Sopenharmony_ci */ 44798c2ecf20Sopenharmony_ci 44808c2ecf20Sopenharmony_ci/* check each pin in the given array; returns true if any of them is plugged */ 44818c2ecf20Sopenharmony_cistatic bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins) 44828c2ecf20Sopenharmony_ci{ 44838c2ecf20Sopenharmony_ci int i; 44848c2ecf20Sopenharmony_ci bool present = false; 44858c2ecf20Sopenharmony_ci 44868c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) { 44878c2ecf20Sopenharmony_ci hda_nid_t nid = pins[i]; 44888c2ecf20Sopenharmony_ci if (!nid) 44898c2ecf20Sopenharmony_ci break; 44908c2ecf20Sopenharmony_ci /* don't detect pins retasked as inputs */ 44918c2ecf20Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) 44928c2ecf20Sopenharmony_ci continue; 44938c2ecf20Sopenharmony_ci if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) 44948c2ecf20Sopenharmony_ci present = true; 44958c2ecf20Sopenharmony_ci } 44968c2ecf20Sopenharmony_ci return present; 44978c2ecf20Sopenharmony_ci} 44988c2ecf20Sopenharmony_ci 44998c2ecf20Sopenharmony_ci/* standard HP/line-out auto-mute helper */ 45008c2ecf20Sopenharmony_cistatic void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, 45018c2ecf20Sopenharmony_ci int *paths, bool mute) 45028c2ecf20Sopenharmony_ci{ 45038c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 45048c2ecf20Sopenharmony_ci int i; 45058c2ecf20Sopenharmony_ci 45068c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) { 45078c2ecf20Sopenharmony_ci hda_nid_t nid = pins[i]; 45088c2ecf20Sopenharmony_ci unsigned int val, oldval; 45098c2ecf20Sopenharmony_ci if (!nid) 45108c2ecf20Sopenharmony_ci break; 45118c2ecf20Sopenharmony_ci 45128c2ecf20Sopenharmony_ci oldval = snd_hda_codec_get_pin_target(codec, nid); 45138c2ecf20Sopenharmony_ci if (oldval & PIN_IN) 45148c2ecf20Sopenharmony_ci continue; /* no mute for inputs */ 45158c2ecf20Sopenharmony_ci 45168c2ecf20Sopenharmony_ci if (spec->auto_mute_via_amp) { 45178c2ecf20Sopenharmony_ci struct nid_path *path; 45188c2ecf20Sopenharmony_ci hda_nid_t mute_nid; 45198c2ecf20Sopenharmony_ci 45208c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, paths[i]); 45218c2ecf20Sopenharmony_ci if (!path) 45228c2ecf20Sopenharmony_ci continue; 45238c2ecf20Sopenharmony_ci mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); 45248c2ecf20Sopenharmony_ci if (!mute_nid) 45258c2ecf20Sopenharmony_ci continue; 45268c2ecf20Sopenharmony_ci if (mute) 45278c2ecf20Sopenharmony_ci spec->mute_bits |= (1ULL << mute_nid); 45288c2ecf20Sopenharmony_ci else 45298c2ecf20Sopenharmony_ci spec->mute_bits &= ~(1ULL << mute_nid); 45308c2ecf20Sopenharmony_ci continue; 45318c2ecf20Sopenharmony_ci } else { 45328c2ecf20Sopenharmony_ci /* don't reset VREF value in case it's controlling 45338c2ecf20Sopenharmony_ci * the amp (see alc861_fixup_asus_amp_vref_0f()) 45348c2ecf20Sopenharmony_ci */ 45358c2ecf20Sopenharmony_ci if (spec->keep_vref_in_automute) 45368c2ecf20Sopenharmony_ci val = oldval & ~PIN_HP; 45378c2ecf20Sopenharmony_ci else 45388c2ecf20Sopenharmony_ci val = 0; 45398c2ecf20Sopenharmony_ci if (!mute) 45408c2ecf20Sopenharmony_ci val |= oldval; 45418c2ecf20Sopenharmony_ci /* here we call update_pin_ctl() so that the pinctl is 45428c2ecf20Sopenharmony_ci * changed without changing the pinctl target value; 45438c2ecf20Sopenharmony_ci * the original target value will be still referred at 45448c2ecf20Sopenharmony_ci * the init / resume again 45458c2ecf20Sopenharmony_ci */ 45468c2ecf20Sopenharmony_ci update_pin_ctl(codec, nid, val); 45478c2ecf20Sopenharmony_ci } 45488c2ecf20Sopenharmony_ci 45498c2ecf20Sopenharmony_ci set_pin_eapd(codec, nid, !mute); 45508c2ecf20Sopenharmony_ci if (codec->power_save_node) { 45518c2ecf20Sopenharmony_ci bool on = !mute; 45528c2ecf20Sopenharmony_ci if (on) 45538c2ecf20Sopenharmony_ci on = detect_pin_state(codec, nid); 45548c2ecf20Sopenharmony_ci set_path_power(codec, nid, on, -1); 45558c2ecf20Sopenharmony_ci } 45568c2ecf20Sopenharmony_ci } 45578c2ecf20Sopenharmony_ci} 45588c2ecf20Sopenharmony_ci 45598c2ecf20Sopenharmony_ci/** 45608c2ecf20Sopenharmony_ci * snd_hda_gen_update_outputs - Toggle outputs muting 45618c2ecf20Sopenharmony_ci * @codec: the HDA codec 45628c2ecf20Sopenharmony_ci * 45638c2ecf20Sopenharmony_ci * Update the mute status of all outputs based on the current jack states. 45648c2ecf20Sopenharmony_ci */ 45658c2ecf20Sopenharmony_civoid snd_hda_gen_update_outputs(struct hda_codec *codec) 45668c2ecf20Sopenharmony_ci{ 45678c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 45688c2ecf20Sopenharmony_ci int *paths; 45698c2ecf20Sopenharmony_ci int on; 45708c2ecf20Sopenharmony_ci 45718c2ecf20Sopenharmony_ci /* Control HP pins/amps depending on master_mute state; 45728c2ecf20Sopenharmony_ci * in general, HP pins/amps control should be enabled in all cases, 45738c2ecf20Sopenharmony_ci * but currently set only for master_mute, just to be safe 45748c2ecf20Sopenharmony_ci */ 45758c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 45768c2ecf20Sopenharmony_ci paths = spec->out_paths; 45778c2ecf20Sopenharmony_ci else 45788c2ecf20Sopenharmony_ci paths = spec->hp_paths; 45798c2ecf20Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), 45808c2ecf20Sopenharmony_ci spec->autocfg.hp_pins, paths, spec->master_mute); 45818c2ecf20Sopenharmony_ci 45828c2ecf20Sopenharmony_ci if (!spec->automute_speaker) 45838c2ecf20Sopenharmony_ci on = 0; 45848c2ecf20Sopenharmony_ci else 45858c2ecf20Sopenharmony_ci on = spec->hp_jack_present | spec->line_jack_present; 45868c2ecf20Sopenharmony_ci on |= spec->master_mute; 45878c2ecf20Sopenharmony_ci spec->speaker_muted = on; 45888c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 45898c2ecf20Sopenharmony_ci paths = spec->out_paths; 45908c2ecf20Sopenharmony_ci else 45918c2ecf20Sopenharmony_ci paths = spec->speaker_paths; 45928c2ecf20Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), 45938c2ecf20Sopenharmony_ci spec->autocfg.speaker_pins, paths, on); 45948c2ecf20Sopenharmony_ci 45958c2ecf20Sopenharmony_ci /* toggle line-out mutes if needed, too */ 45968c2ecf20Sopenharmony_ci /* if LO is a copy of either HP or Speaker, don't need to handle it */ 45978c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || 45988c2ecf20Sopenharmony_ci spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) 45998c2ecf20Sopenharmony_ci return; 46008c2ecf20Sopenharmony_ci if (!spec->automute_lo) 46018c2ecf20Sopenharmony_ci on = 0; 46028c2ecf20Sopenharmony_ci else 46038c2ecf20Sopenharmony_ci on = spec->hp_jack_present; 46048c2ecf20Sopenharmony_ci on |= spec->master_mute; 46058c2ecf20Sopenharmony_ci spec->line_out_muted = on; 46068c2ecf20Sopenharmony_ci paths = spec->out_paths; 46078c2ecf20Sopenharmony_ci do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 46088c2ecf20Sopenharmony_ci spec->autocfg.line_out_pins, paths, on); 46098c2ecf20Sopenharmony_ci} 46108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs); 46118c2ecf20Sopenharmony_ci 46128c2ecf20Sopenharmony_cistatic void call_update_outputs(struct hda_codec *codec) 46138c2ecf20Sopenharmony_ci{ 46148c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 46158c2ecf20Sopenharmony_ci if (spec->automute_hook) 46168c2ecf20Sopenharmony_ci spec->automute_hook(codec); 46178c2ecf20Sopenharmony_ci else 46188c2ecf20Sopenharmony_ci snd_hda_gen_update_outputs(codec); 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_ci /* sync the whole vmaster followers to reflect the new auto-mute status */ 46218c2ecf20Sopenharmony_ci if (spec->auto_mute_via_amp && !codec->bus->shutdown) 46228c2ecf20Sopenharmony_ci snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); 46238c2ecf20Sopenharmony_ci} 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci/** 46268c2ecf20Sopenharmony_ci * snd_hda_gen_hp_automute - standard HP-automute helper 46278c2ecf20Sopenharmony_ci * @codec: the HDA codec 46288c2ecf20Sopenharmony_ci * @jack: jack object, NULL for the whole 46298c2ecf20Sopenharmony_ci */ 46308c2ecf20Sopenharmony_civoid snd_hda_gen_hp_automute(struct hda_codec *codec, 46318c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 46328c2ecf20Sopenharmony_ci{ 46338c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 46348c2ecf20Sopenharmony_ci hda_nid_t *pins = spec->autocfg.hp_pins; 46358c2ecf20Sopenharmony_ci int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); 46368c2ecf20Sopenharmony_ci 46378c2ecf20Sopenharmony_ci /* No detection for the first HP jack during indep-HP mode */ 46388c2ecf20Sopenharmony_ci if (spec->indep_hp_enabled) { 46398c2ecf20Sopenharmony_ci pins++; 46408c2ecf20Sopenharmony_ci num_pins--; 46418c2ecf20Sopenharmony_ci } 46428c2ecf20Sopenharmony_ci 46438c2ecf20Sopenharmony_ci spec->hp_jack_present = detect_jacks(codec, num_pins, pins); 46448c2ecf20Sopenharmony_ci if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) 46458c2ecf20Sopenharmony_ci return; 46468c2ecf20Sopenharmony_ci call_update_outputs(codec); 46478c2ecf20Sopenharmony_ci} 46488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); 46498c2ecf20Sopenharmony_ci 46508c2ecf20Sopenharmony_ci/** 46518c2ecf20Sopenharmony_ci * snd_hda_gen_line_automute - standard line-out-automute helper 46528c2ecf20Sopenharmony_ci * @codec: the HDA codec 46538c2ecf20Sopenharmony_ci * @jack: jack object, NULL for the whole 46548c2ecf20Sopenharmony_ci */ 46558c2ecf20Sopenharmony_civoid snd_hda_gen_line_automute(struct hda_codec *codec, 46568c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 46578c2ecf20Sopenharmony_ci{ 46588c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 46598c2ecf20Sopenharmony_ci 46608c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 46618c2ecf20Sopenharmony_ci return; 46628c2ecf20Sopenharmony_ci /* check LO jack only when it's different from HP */ 46638c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) 46648c2ecf20Sopenharmony_ci return; 46658c2ecf20Sopenharmony_ci 46668c2ecf20Sopenharmony_ci spec->line_jack_present = 46678c2ecf20Sopenharmony_ci detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 46688c2ecf20Sopenharmony_ci spec->autocfg.line_out_pins); 46698c2ecf20Sopenharmony_ci if (!spec->automute_speaker || !spec->detect_lo) 46708c2ecf20Sopenharmony_ci return; 46718c2ecf20Sopenharmony_ci call_update_outputs(codec); 46728c2ecf20Sopenharmony_ci} 46738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); 46748c2ecf20Sopenharmony_ci 46758c2ecf20Sopenharmony_ci/** 46768c2ecf20Sopenharmony_ci * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper 46778c2ecf20Sopenharmony_ci * @codec: the HDA codec 46788c2ecf20Sopenharmony_ci * @jack: jack object, NULL for the whole 46798c2ecf20Sopenharmony_ci */ 46808c2ecf20Sopenharmony_civoid snd_hda_gen_mic_autoswitch(struct hda_codec *codec, 46818c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 46828c2ecf20Sopenharmony_ci{ 46838c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 46848c2ecf20Sopenharmony_ci int i; 46858c2ecf20Sopenharmony_ci 46868c2ecf20Sopenharmony_ci if (!spec->auto_mic) 46878c2ecf20Sopenharmony_ci return; 46888c2ecf20Sopenharmony_ci 46898c2ecf20Sopenharmony_ci for (i = spec->am_num_entries - 1; i > 0; i--) { 46908c2ecf20Sopenharmony_ci hda_nid_t pin = spec->am_entry[i].pin; 46918c2ecf20Sopenharmony_ci /* don't detect pins retasked as outputs */ 46928c2ecf20Sopenharmony_ci if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) 46938c2ecf20Sopenharmony_ci continue; 46948c2ecf20Sopenharmony_ci if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { 46958c2ecf20Sopenharmony_ci mux_select(codec, 0, spec->am_entry[i].idx); 46968c2ecf20Sopenharmony_ci return; 46978c2ecf20Sopenharmony_ci } 46988c2ecf20Sopenharmony_ci } 46998c2ecf20Sopenharmony_ci mux_select(codec, 0, spec->am_entry[0].idx); 47008c2ecf20Sopenharmony_ci} 47018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); 47028c2ecf20Sopenharmony_ci 47038c2ecf20Sopenharmony_ci/* call appropriate hooks */ 47048c2ecf20Sopenharmony_cistatic void call_hp_automute(struct hda_codec *codec, 47058c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 47068c2ecf20Sopenharmony_ci{ 47078c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47088c2ecf20Sopenharmony_ci if (spec->hp_automute_hook) 47098c2ecf20Sopenharmony_ci spec->hp_automute_hook(codec, jack); 47108c2ecf20Sopenharmony_ci else 47118c2ecf20Sopenharmony_ci snd_hda_gen_hp_automute(codec, jack); 47128c2ecf20Sopenharmony_ci} 47138c2ecf20Sopenharmony_ci 47148c2ecf20Sopenharmony_cistatic void call_line_automute(struct hda_codec *codec, 47158c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 47168c2ecf20Sopenharmony_ci{ 47178c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47188c2ecf20Sopenharmony_ci if (spec->line_automute_hook) 47198c2ecf20Sopenharmony_ci spec->line_automute_hook(codec, jack); 47208c2ecf20Sopenharmony_ci else 47218c2ecf20Sopenharmony_ci snd_hda_gen_line_automute(codec, jack); 47228c2ecf20Sopenharmony_ci} 47238c2ecf20Sopenharmony_ci 47248c2ecf20Sopenharmony_cistatic void call_mic_autoswitch(struct hda_codec *codec, 47258c2ecf20Sopenharmony_ci struct hda_jack_callback *jack) 47268c2ecf20Sopenharmony_ci{ 47278c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47288c2ecf20Sopenharmony_ci if (spec->mic_autoswitch_hook) 47298c2ecf20Sopenharmony_ci spec->mic_autoswitch_hook(codec, jack); 47308c2ecf20Sopenharmony_ci else 47318c2ecf20Sopenharmony_ci snd_hda_gen_mic_autoswitch(codec, jack); 47328c2ecf20Sopenharmony_ci} 47338c2ecf20Sopenharmony_ci 47348c2ecf20Sopenharmony_ci/* update jack retasking */ 47358c2ecf20Sopenharmony_cistatic void update_automute_all(struct hda_codec *codec) 47368c2ecf20Sopenharmony_ci{ 47378c2ecf20Sopenharmony_ci call_hp_automute(codec, NULL); 47388c2ecf20Sopenharmony_ci call_line_automute(codec, NULL); 47398c2ecf20Sopenharmony_ci call_mic_autoswitch(codec, NULL); 47408c2ecf20Sopenharmony_ci} 47418c2ecf20Sopenharmony_ci 47428c2ecf20Sopenharmony_ci/* 47438c2ecf20Sopenharmony_ci * Auto-Mute mode mixer enum support 47448c2ecf20Sopenharmony_ci */ 47458c2ecf20Sopenharmony_cistatic int automute_mode_info(struct snd_kcontrol *kcontrol, 47468c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 47478c2ecf20Sopenharmony_ci{ 47488c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 47498c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47508c2ecf20Sopenharmony_ci static const char * const texts3[] = { 47518c2ecf20Sopenharmony_ci "Disabled", "Speaker Only", "Line Out+Speaker" 47528c2ecf20Sopenharmony_ci }; 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci if (spec->automute_speaker_possible && spec->automute_lo_possible) 47558c2ecf20Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); 47568c2ecf20Sopenharmony_ci return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 47578c2ecf20Sopenharmony_ci} 47588c2ecf20Sopenharmony_ci 47598c2ecf20Sopenharmony_cistatic int automute_mode_get(struct snd_kcontrol *kcontrol, 47608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47618c2ecf20Sopenharmony_ci{ 47628c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 47638c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47648c2ecf20Sopenharmony_ci unsigned int val = 0; 47658c2ecf20Sopenharmony_ci if (spec->automute_speaker) 47668c2ecf20Sopenharmony_ci val++; 47678c2ecf20Sopenharmony_ci if (spec->automute_lo) 47688c2ecf20Sopenharmony_ci val++; 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = val; 47718c2ecf20Sopenharmony_ci return 0; 47728c2ecf20Sopenharmony_ci} 47738c2ecf20Sopenharmony_ci 47748c2ecf20Sopenharmony_cistatic int automute_mode_put(struct snd_kcontrol *kcontrol, 47758c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47768c2ecf20Sopenharmony_ci{ 47778c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 47788c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 47798c2ecf20Sopenharmony_ci 47808c2ecf20Sopenharmony_ci switch (ucontrol->value.enumerated.item[0]) { 47818c2ecf20Sopenharmony_ci case 0: 47828c2ecf20Sopenharmony_ci if (!spec->automute_speaker && !spec->automute_lo) 47838c2ecf20Sopenharmony_ci return 0; 47848c2ecf20Sopenharmony_ci spec->automute_speaker = 0; 47858c2ecf20Sopenharmony_ci spec->automute_lo = 0; 47868c2ecf20Sopenharmony_ci break; 47878c2ecf20Sopenharmony_ci case 1: 47888c2ecf20Sopenharmony_ci if (spec->automute_speaker_possible) { 47898c2ecf20Sopenharmony_ci if (!spec->automute_lo && spec->automute_speaker) 47908c2ecf20Sopenharmony_ci return 0; 47918c2ecf20Sopenharmony_ci spec->automute_speaker = 1; 47928c2ecf20Sopenharmony_ci spec->automute_lo = 0; 47938c2ecf20Sopenharmony_ci } else if (spec->automute_lo_possible) { 47948c2ecf20Sopenharmony_ci if (spec->automute_lo) 47958c2ecf20Sopenharmony_ci return 0; 47968c2ecf20Sopenharmony_ci spec->automute_lo = 1; 47978c2ecf20Sopenharmony_ci } else 47988c2ecf20Sopenharmony_ci return -EINVAL; 47998c2ecf20Sopenharmony_ci break; 48008c2ecf20Sopenharmony_ci case 2: 48018c2ecf20Sopenharmony_ci if (!spec->automute_lo_possible || !spec->automute_speaker_possible) 48028c2ecf20Sopenharmony_ci return -EINVAL; 48038c2ecf20Sopenharmony_ci if (spec->automute_speaker && spec->automute_lo) 48048c2ecf20Sopenharmony_ci return 0; 48058c2ecf20Sopenharmony_ci spec->automute_speaker = 1; 48068c2ecf20Sopenharmony_ci spec->automute_lo = 1; 48078c2ecf20Sopenharmony_ci break; 48088c2ecf20Sopenharmony_ci default: 48098c2ecf20Sopenharmony_ci return -EINVAL; 48108c2ecf20Sopenharmony_ci } 48118c2ecf20Sopenharmony_ci call_update_outputs(codec); 48128c2ecf20Sopenharmony_ci return 1; 48138c2ecf20Sopenharmony_ci} 48148c2ecf20Sopenharmony_ci 48158c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new automute_mode_enum = { 48168c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 48178c2ecf20Sopenharmony_ci .name = "Auto-Mute Mode", 48188c2ecf20Sopenharmony_ci .info = automute_mode_info, 48198c2ecf20Sopenharmony_ci .get = automute_mode_get, 48208c2ecf20Sopenharmony_ci .put = automute_mode_put, 48218c2ecf20Sopenharmony_ci}; 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_cistatic int add_automute_mode_enum(struct hda_codec *codec) 48248c2ecf20Sopenharmony_ci{ 48258c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 48268c2ecf20Sopenharmony_ci 48278c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) 48288c2ecf20Sopenharmony_ci return -ENOMEM; 48298c2ecf20Sopenharmony_ci return 0; 48308c2ecf20Sopenharmony_ci} 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci/* 48338c2ecf20Sopenharmony_ci * Check the availability of HP/line-out auto-mute; 48348c2ecf20Sopenharmony_ci * Set up appropriately if really supported 48358c2ecf20Sopenharmony_ci */ 48368c2ecf20Sopenharmony_cistatic int check_auto_mute_availability(struct hda_codec *codec) 48378c2ecf20Sopenharmony_ci{ 48388c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 48398c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 48408c2ecf20Sopenharmony_ci int present = 0; 48418c2ecf20Sopenharmony_ci int i, err; 48428c2ecf20Sopenharmony_ci 48438c2ecf20Sopenharmony_ci if (spec->suppress_auto_mute) 48448c2ecf20Sopenharmony_ci return 0; 48458c2ecf20Sopenharmony_ci 48468c2ecf20Sopenharmony_ci if (cfg->hp_pins[0]) 48478c2ecf20Sopenharmony_ci present++; 48488c2ecf20Sopenharmony_ci if (cfg->line_out_pins[0]) 48498c2ecf20Sopenharmony_ci present++; 48508c2ecf20Sopenharmony_ci if (cfg->speaker_pins[0]) 48518c2ecf20Sopenharmony_ci present++; 48528c2ecf20Sopenharmony_ci if (present < 2) /* need two different output types */ 48538c2ecf20Sopenharmony_ci return 0; 48548c2ecf20Sopenharmony_ci 48558c2ecf20Sopenharmony_ci if (!cfg->speaker_pins[0] && 48568c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 48578c2ecf20Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 48588c2ecf20Sopenharmony_ci sizeof(cfg->speaker_pins)); 48598c2ecf20Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 48608c2ecf20Sopenharmony_ci } 48618c2ecf20Sopenharmony_ci 48628c2ecf20Sopenharmony_ci if (!cfg->hp_pins[0] && 48638c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_HP_OUT) { 48648c2ecf20Sopenharmony_ci memcpy(cfg->hp_pins, cfg->line_out_pins, 48658c2ecf20Sopenharmony_ci sizeof(cfg->hp_pins)); 48668c2ecf20Sopenharmony_ci cfg->hp_outs = cfg->line_outs; 48678c2ecf20Sopenharmony_ci } 48688c2ecf20Sopenharmony_ci 48698c2ecf20Sopenharmony_ci for (i = 0; i < cfg->hp_outs; i++) { 48708c2ecf20Sopenharmony_ci hda_nid_t nid = cfg->hp_pins[i]; 48718c2ecf20Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 48728c2ecf20Sopenharmony_ci continue; 48738c2ecf20Sopenharmony_ci codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid); 48748c2ecf20Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, nid, 48758c2ecf20Sopenharmony_ci call_hp_automute); 48768c2ecf20Sopenharmony_ci spec->detect_hp = 1; 48778c2ecf20Sopenharmony_ci } 48788c2ecf20Sopenharmony_ci 48798c2ecf20Sopenharmony_ci if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { 48808c2ecf20Sopenharmony_ci if (cfg->speaker_outs) 48818c2ecf20Sopenharmony_ci for (i = 0; i < cfg->line_outs; i++) { 48828c2ecf20Sopenharmony_ci hda_nid_t nid = cfg->line_out_pins[i]; 48838c2ecf20Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 48848c2ecf20Sopenharmony_ci continue; 48858c2ecf20Sopenharmony_ci codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid); 48868c2ecf20Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, nid, 48878c2ecf20Sopenharmony_ci call_line_automute); 48888c2ecf20Sopenharmony_ci spec->detect_lo = 1; 48898c2ecf20Sopenharmony_ci } 48908c2ecf20Sopenharmony_ci spec->automute_lo_possible = spec->detect_hp; 48918c2ecf20Sopenharmony_ci } 48928c2ecf20Sopenharmony_ci 48938c2ecf20Sopenharmony_ci spec->automute_speaker_possible = cfg->speaker_outs && 48948c2ecf20Sopenharmony_ci (spec->detect_hp || spec->detect_lo); 48958c2ecf20Sopenharmony_ci 48968c2ecf20Sopenharmony_ci spec->automute_lo = spec->automute_lo_possible; 48978c2ecf20Sopenharmony_ci spec->automute_speaker = spec->automute_speaker_possible; 48988c2ecf20Sopenharmony_ci 48998c2ecf20Sopenharmony_ci if (spec->automute_speaker_possible || spec->automute_lo_possible) { 49008c2ecf20Sopenharmony_ci /* create a control for automute mode */ 49018c2ecf20Sopenharmony_ci err = add_automute_mode_enum(codec); 49028c2ecf20Sopenharmony_ci if (err < 0) 49038c2ecf20Sopenharmony_ci return err; 49048c2ecf20Sopenharmony_ci } 49058c2ecf20Sopenharmony_ci return 0; 49068c2ecf20Sopenharmony_ci} 49078c2ecf20Sopenharmony_ci 49088c2ecf20Sopenharmony_ci/* check whether all auto-mic pins are valid; setup indices if OK */ 49098c2ecf20Sopenharmony_cistatic bool auto_mic_check_imux(struct hda_codec *codec) 49108c2ecf20Sopenharmony_ci{ 49118c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 49128c2ecf20Sopenharmony_ci const struct hda_input_mux *imux; 49138c2ecf20Sopenharmony_ci int i; 49148c2ecf20Sopenharmony_ci 49158c2ecf20Sopenharmony_ci imux = &spec->input_mux; 49168c2ecf20Sopenharmony_ci for (i = 0; i < spec->am_num_entries; i++) { 49178c2ecf20Sopenharmony_ci spec->am_entry[i].idx = 49188c2ecf20Sopenharmony_ci find_idx_in_nid_list(spec->am_entry[i].pin, 49198c2ecf20Sopenharmony_ci spec->imux_pins, imux->num_items); 49208c2ecf20Sopenharmony_ci if (spec->am_entry[i].idx < 0) 49218c2ecf20Sopenharmony_ci return false; /* no corresponding imux */ 49228c2ecf20Sopenharmony_ci } 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ci /* we don't need the jack detection for the first pin */ 49258c2ecf20Sopenharmony_ci for (i = 1; i < spec->am_num_entries; i++) 49268c2ecf20Sopenharmony_ci snd_hda_jack_detect_enable_callback(codec, 49278c2ecf20Sopenharmony_ci spec->am_entry[i].pin, 49288c2ecf20Sopenharmony_ci call_mic_autoswitch); 49298c2ecf20Sopenharmony_ci return true; 49308c2ecf20Sopenharmony_ci} 49318c2ecf20Sopenharmony_ci 49328c2ecf20Sopenharmony_cistatic int compare_attr(const void *ap, const void *bp) 49338c2ecf20Sopenharmony_ci{ 49348c2ecf20Sopenharmony_ci const struct automic_entry *a = ap; 49358c2ecf20Sopenharmony_ci const struct automic_entry *b = bp; 49368c2ecf20Sopenharmony_ci return (int)(a->attr - b->attr); 49378c2ecf20Sopenharmony_ci} 49388c2ecf20Sopenharmony_ci 49398c2ecf20Sopenharmony_ci/* 49408c2ecf20Sopenharmony_ci * Check the availability of auto-mic switch; 49418c2ecf20Sopenharmony_ci * Set up if really supported 49428c2ecf20Sopenharmony_ci */ 49438c2ecf20Sopenharmony_cistatic int check_auto_mic_availability(struct hda_codec *codec) 49448c2ecf20Sopenharmony_ci{ 49458c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 49468c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 49478c2ecf20Sopenharmony_ci unsigned int types; 49488c2ecf20Sopenharmony_ci int i, num_pins; 49498c2ecf20Sopenharmony_ci 49508c2ecf20Sopenharmony_ci if (spec->suppress_auto_mic) 49518c2ecf20Sopenharmony_ci return 0; 49528c2ecf20Sopenharmony_ci 49538c2ecf20Sopenharmony_ci types = 0; 49548c2ecf20Sopenharmony_ci num_pins = 0; 49558c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 49568c2ecf20Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 49578c2ecf20Sopenharmony_ci unsigned int attr; 49588c2ecf20Sopenharmony_ci attr = snd_hda_codec_get_pincfg(codec, nid); 49598c2ecf20Sopenharmony_ci attr = snd_hda_get_input_pin_attr(attr); 49608c2ecf20Sopenharmony_ci if (types & (1 << attr)) 49618c2ecf20Sopenharmony_ci return 0; /* already occupied */ 49628c2ecf20Sopenharmony_ci switch (attr) { 49638c2ecf20Sopenharmony_ci case INPUT_PIN_ATTR_INT: 49648c2ecf20Sopenharmony_ci if (cfg->inputs[i].type != AUTO_PIN_MIC) 49658c2ecf20Sopenharmony_ci return 0; /* invalid type */ 49668c2ecf20Sopenharmony_ci break; 49678c2ecf20Sopenharmony_ci case INPUT_PIN_ATTR_UNUSED: 49688c2ecf20Sopenharmony_ci return 0; /* invalid entry */ 49698c2ecf20Sopenharmony_ci default: 49708c2ecf20Sopenharmony_ci if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 49718c2ecf20Sopenharmony_ci return 0; /* invalid type */ 49728c2ecf20Sopenharmony_ci if (!spec->line_in_auto_switch && 49738c2ecf20Sopenharmony_ci cfg->inputs[i].type != AUTO_PIN_MIC) 49748c2ecf20Sopenharmony_ci return 0; /* only mic is allowed */ 49758c2ecf20Sopenharmony_ci if (!is_jack_detectable(codec, nid)) 49768c2ecf20Sopenharmony_ci return 0; /* no unsol support */ 49778c2ecf20Sopenharmony_ci break; 49788c2ecf20Sopenharmony_ci } 49798c2ecf20Sopenharmony_ci if (num_pins >= MAX_AUTO_MIC_PINS) 49808c2ecf20Sopenharmony_ci return 0; 49818c2ecf20Sopenharmony_ci types |= (1 << attr); 49828c2ecf20Sopenharmony_ci spec->am_entry[num_pins].pin = nid; 49838c2ecf20Sopenharmony_ci spec->am_entry[num_pins].attr = attr; 49848c2ecf20Sopenharmony_ci num_pins++; 49858c2ecf20Sopenharmony_ci } 49868c2ecf20Sopenharmony_ci 49878c2ecf20Sopenharmony_ci if (num_pins < 2) 49888c2ecf20Sopenharmony_ci return 0; 49898c2ecf20Sopenharmony_ci 49908c2ecf20Sopenharmony_ci spec->am_num_entries = num_pins; 49918c2ecf20Sopenharmony_ci /* sort the am_entry in the order of attr so that the pin with a 49928c2ecf20Sopenharmony_ci * higher attr will be selected when the jack is plugged. 49938c2ecf20Sopenharmony_ci */ 49948c2ecf20Sopenharmony_ci sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), 49958c2ecf20Sopenharmony_ci compare_attr, NULL); 49968c2ecf20Sopenharmony_ci 49978c2ecf20Sopenharmony_ci if (!auto_mic_check_imux(codec)) 49988c2ecf20Sopenharmony_ci return 0; 49998c2ecf20Sopenharmony_ci 50008c2ecf20Sopenharmony_ci spec->auto_mic = 1; 50018c2ecf20Sopenharmony_ci spec->num_adc_nids = 1; 50028c2ecf20Sopenharmony_ci spec->cur_mux[0] = spec->am_entry[0].idx; 50038c2ecf20Sopenharmony_ci codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", 50048c2ecf20Sopenharmony_ci spec->am_entry[0].pin, 50058c2ecf20Sopenharmony_ci spec->am_entry[1].pin, 50068c2ecf20Sopenharmony_ci spec->am_entry[2].pin); 50078c2ecf20Sopenharmony_ci 50088c2ecf20Sopenharmony_ci return 0; 50098c2ecf20Sopenharmony_ci} 50108c2ecf20Sopenharmony_ci 50118c2ecf20Sopenharmony_ci/** 50128c2ecf20Sopenharmony_ci * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets 50138c2ecf20Sopenharmony_ci * into power down 50148c2ecf20Sopenharmony_ci * @codec: the HDA codec 50158c2ecf20Sopenharmony_ci * @nid: NID to evalute 50168c2ecf20Sopenharmony_ci * @power_state: target power state 50178c2ecf20Sopenharmony_ci */ 50188c2ecf20Sopenharmony_ciunsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, 50198c2ecf20Sopenharmony_ci hda_nid_t nid, 50208c2ecf20Sopenharmony_ci unsigned int power_state) 50218c2ecf20Sopenharmony_ci{ 50228c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 50238c2ecf20Sopenharmony_ci 50248c2ecf20Sopenharmony_ci if (!spec->power_down_unused && !codec->power_save_node) 50258c2ecf20Sopenharmony_ci return power_state; 50268c2ecf20Sopenharmony_ci if (power_state != AC_PWRST_D0 || nid == codec->core.afg) 50278c2ecf20Sopenharmony_ci return power_state; 50288c2ecf20Sopenharmony_ci if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) 50298c2ecf20Sopenharmony_ci return power_state; 50308c2ecf20Sopenharmony_ci if (is_active_nid_for_any(codec, nid)) 50318c2ecf20Sopenharmony_ci return power_state; 50328c2ecf20Sopenharmony_ci return AC_PWRST_D3; 50338c2ecf20Sopenharmony_ci} 50348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter); 50358c2ecf20Sopenharmony_ci 50368c2ecf20Sopenharmony_ci/* mute all aamix inputs initially; parse up to the first leaves */ 50378c2ecf20Sopenharmony_cistatic void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) 50388c2ecf20Sopenharmony_ci{ 50398c2ecf20Sopenharmony_ci int i, nums; 50408c2ecf20Sopenharmony_ci const hda_nid_t *conn; 50418c2ecf20Sopenharmony_ci bool has_amp; 50428c2ecf20Sopenharmony_ci 50438c2ecf20Sopenharmony_ci nums = snd_hda_get_conn_list(codec, mix, &conn); 50448c2ecf20Sopenharmony_ci has_amp = nid_has_mute(codec, mix, HDA_INPUT); 50458c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) { 50468c2ecf20Sopenharmony_ci if (has_amp) 50478c2ecf20Sopenharmony_ci update_amp(codec, mix, HDA_INPUT, i, 50488c2ecf20Sopenharmony_ci 0xff, HDA_AMP_MUTE); 50498c2ecf20Sopenharmony_ci else if (nid_has_volume(codec, conn[i], HDA_OUTPUT)) 50508c2ecf20Sopenharmony_ci update_amp(codec, conn[i], HDA_OUTPUT, 0, 50518c2ecf20Sopenharmony_ci 0xff, HDA_AMP_MUTE); 50528c2ecf20Sopenharmony_ci } 50538c2ecf20Sopenharmony_ci} 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci/** 50568c2ecf20Sopenharmony_ci * snd_hda_gen_stream_pm - Stream power management callback 50578c2ecf20Sopenharmony_ci * @codec: the HDA codec 50588c2ecf20Sopenharmony_ci * @nid: audio widget 50598c2ecf20Sopenharmony_ci * @on: power on/off flag 50608c2ecf20Sopenharmony_ci * 50618c2ecf20Sopenharmony_ci * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. 50628c2ecf20Sopenharmony_ci */ 50638c2ecf20Sopenharmony_civoid snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) 50648c2ecf20Sopenharmony_ci{ 50658c2ecf20Sopenharmony_ci if (codec->power_save_node) 50668c2ecf20Sopenharmony_ci set_path_power(codec, nid, -1, on); 50678c2ecf20Sopenharmony_ci} 50688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); 50698c2ecf20Sopenharmony_ci 50708c2ecf20Sopenharmony_ci/** 50718c2ecf20Sopenharmony_ci * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and 50728c2ecf20Sopenharmony_ci * set up the hda_gen_spec 50738c2ecf20Sopenharmony_ci * @codec: the HDA codec 50748c2ecf20Sopenharmony_ci * @cfg: Parsed pin configuration 50758c2ecf20Sopenharmony_ci * 50768c2ecf20Sopenharmony_ci * return 1 if successful, 0 if the proper config is not found, 50778c2ecf20Sopenharmony_ci * or a negative error code 50788c2ecf20Sopenharmony_ci */ 50798c2ecf20Sopenharmony_ciint snd_hda_gen_parse_auto_config(struct hda_codec *codec, 50808c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg) 50818c2ecf20Sopenharmony_ci{ 50828c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 50838c2ecf20Sopenharmony_ci int err; 50848c2ecf20Sopenharmony_ci 50858c2ecf20Sopenharmony_ci parse_user_hints(codec); 50868c2ecf20Sopenharmony_ci 50878c2ecf20Sopenharmony_ci if (spec->mixer_nid && !spec->mixer_merge_nid) 50888c2ecf20Sopenharmony_ci spec->mixer_merge_nid = spec->mixer_nid; 50898c2ecf20Sopenharmony_ci 50908c2ecf20Sopenharmony_ci if (cfg != &spec->autocfg) { 50918c2ecf20Sopenharmony_ci spec->autocfg = *cfg; 50928c2ecf20Sopenharmony_ci cfg = &spec->autocfg; 50938c2ecf20Sopenharmony_ci } 50948c2ecf20Sopenharmony_ci 50958c2ecf20Sopenharmony_ci if (!spec->main_out_badness) 50968c2ecf20Sopenharmony_ci spec->main_out_badness = &hda_main_out_badness; 50978c2ecf20Sopenharmony_ci if (!spec->extra_out_badness) 50988c2ecf20Sopenharmony_ci spec->extra_out_badness = &hda_extra_out_badness; 50998c2ecf20Sopenharmony_ci 51008c2ecf20Sopenharmony_ci fill_all_dac_nids(codec); 51018c2ecf20Sopenharmony_ci 51028c2ecf20Sopenharmony_ci if (!cfg->line_outs) { 51038c2ecf20Sopenharmony_ci if (cfg->dig_outs || cfg->dig_in_pin) { 51048c2ecf20Sopenharmony_ci spec->multiout.max_channels = 2; 51058c2ecf20Sopenharmony_ci spec->no_analog = 1; 51068c2ecf20Sopenharmony_ci goto dig_only; 51078c2ecf20Sopenharmony_ci } 51088c2ecf20Sopenharmony_ci if (!cfg->num_inputs && !cfg->dig_in_pin) 51098c2ecf20Sopenharmony_ci return 0; /* can't find valid BIOS pin config */ 51108c2ecf20Sopenharmony_ci } 51118c2ecf20Sopenharmony_ci 51128c2ecf20Sopenharmony_ci if (!spec->no_primary_hp && 51138c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 51148c2ecf20Sopenharmony_ci cfg->line_outs <= cfg->hp_outs) { 51158c2ecf20Sopenharmony_ci /* use HP as primary out */ 51168c2ecf20Sopenharmony_ci cfg->speaker_outs = cfg->line_outs; 51178c2ecf20Sopenharmony_ci memcpy(cfg->speaker_pins, cfg->line_out_pins, 51188c2ecf20Sopenharmony_ci sizeof(cfg->speaker_pins)); 51198c2ecf20Sopenharmony_ci cfg->line_outs = cfg->hp_outs; 51208c2ecf20Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); 51218c2ecf20Sopenharmony_ci cfg->hp_outs = 0; 51228c2ecf20Sopenharmony_ci memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 51238c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 51248c2ecf20Sopenharmony_ci } 51258c2ecf20Sopenharmony_ci 51268c2ecf20Sopenharmony_ci err = parse_output_paths(codec); 51278c2ecf20Sopenharmony_ci if (err < 0) 51288c2ecf20Sopenharmony_ci return err; 51298c2ecf20Sopenharmony_ci err = create_multi_channel_mode(codec); 51308c2ecf20Sopenharmony_ci if (err < 0) 51318c2ecf20Sopenharmony_ci return err; 51328c2ecf20Sopenharmony_ci err = create_multi_out_ctls(codec, cfg); 51338c2ecf20Sopenharmony_ci if (err < 0) 51348c2ecf20Sopenharmony_ci return err; 51358c2ecf20Sopenharmony_ci err = create_hp_out_ctls(codec); 51368c2ecf20Sopenharmony_ci if (err < 0) 51378c2ecf20Sopenharmony_ci return err; 51388c2ecf20Sopenharmony_ci err = create_speaker_out_ctls(codec); 51398c2ecf20Sopenharmony_ci if (err < 0) 51408c2ecf20Sopenharmony_ci return err; 51418c2ecf20Sopenharmony_ci err = create_indep_hp_ctls(codec); 51428c2ecf20Sopenharmony_ci if (err < 0) 51438c2ecf20Sopenharmony_ci return err; 51448c2ecf20Sopenharmony_ci err = create_loopback_mixing_ctl(codec); 51458c2ecf20Sopenharmony_ci if (err < 0) 51468c2ecf20Sopenharmony_ci return err; 51478c2ecf20Sopenharmony_ci err = create_hp_mic(codec); 51488c2ecf20Sopenharmony_ci if (err < 0) 51498c2ecf20Sopenharmony_ci return err; 51508c2ecf20Sopenharmony_ci err = create_input_ctls(codec); 51518c2ecf20Sopenharmony_ci if (err < 0) 51528c2ecf20Sopenharmony_ci return err; 51538c2ecf20Sopenharmony_ci 51548c2ecf20Sopenharmony_ci /* add power-down pin callbacks at first */ 51558c2ecf20Sopenharmony_ci add_all_pin_power_ctls(codec, false); 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_ci spec->const_channel_count = spec->ext_channel_count; 51588c2ecf20Sopenharmony_ci /* check the multiple speaker and headphone pins */ 51598c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 51608c2ecf20Sopenharmony_ci spec->const_channel_count = max(spec->const_channel_count, 51618c2ecf20Sopenharmony_ci cfg->speaker_outs * 2); 51628c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) 51638c2ecf20Sopenharmony_ci spec->const_channel_count = max(spec->const_channel_count, 51648c2ecf20Sopenharmony_ci cfg->hp_outs * 2); 51658c2ecf20Sopenharmony_ci spec->multiout.max_channels = max(spec->ext_channel_count, 51668c2ecf20Sopenharmony_ci spec->const_channel_count); 51678c2ecf20Sopenharmony_ci 51688c2ecf20Sopenharmony_ci err = check_auto_mute_availability(codec); 51698c2ecf20Sopenharmony_ci if (err < 0) 51708c2ecf20Sopenharmony_ci return err; 51718c2ecf20Sopenharmony_ci 51728c2ecf20Sopenharmony_ci err = check_dyn_adc_switch(codec); 51738c2ecf20Sopenharmony_ci if (err < 0) 51748c2ecf20Sopenharmony_ci return err; 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci err = check_auto_mic_availability(codec); 51778c2ecf20Sopenharmony_ci if (err < 0) 51788c2ecf20Sopenharmony_ci return err; 51798c2ecf20Sopenharmony_ci 51808c2ecf20Sopenharmony_ci /* add stereo mix if available and not enabled yet */ 51818c2ecf20Sopenharmony_ci if (!spec->auto_mic && spec->mixer_nid && 51828c2ecf20Sopenharmony_ci spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO && 51838c2ecf20Sopenharmony_ci spec->input_mux.num_items > 1) { 51848c2ecf20Sopenharmony_ci err = parse_capture_source(codec, spec->mixer_nid, 51858c2ecf20Sopenharmony_ci CFG_IDX_MIX, spec->num_all_adcs, 51868c2ecf20Sopenharmony_ci "Stereo Mix", 0); 51878c2ecf20Sopenharmony_ci if (err < 0) 51888c2ecf20Sopenharmony_ci return err; 51898c2ecf20Sopenharmony_ci } 51908c2ecf20Sopenharmony_ci 51918c2ecf20Sopenharmony_ci 51928c2ecf20Sopenharmony_ci err = create_capture_mixers(codec); 51938c2ecf20Sopenharmony_ci if (err < 0) 51948c2ecf20Sopenharmony_ci return err; 51958c2ecf20Sopenharmony_ci 51968c2ecf20Sopenharmony_ci err = parse_mic_boost(codec); 51978c2ecf20Sopenharmony_ci if (err < 0) 51988c2ecf20Sopenharmony_ci return err; 51998c2ecf20Sopenharmony_ci 52008c2ecf20Sopenharmony_ci /* create "Headphone Mic Jack Mode" if no input selection is 52018c2ecf20Sopenharmony_ci * available (or user specifies add_jack_modes hint) 52028c2ecf20Sopenharmony_ci */ 52038c2ecf20Sopenharmony_ci if (spec->hp_mic_pin && 52048c2ecf20Sopenharmony_ci (spec->auto_mic || spec->input_mux.num_items == 1 || 52058c2ecf20Sopenharmony_ci spec->add_jack_modes)) { 52068c2ecf20Sopenharmony_ci err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin); 52078c2ecf20Sopenharmony_ci if (err < 0) 52088c2ecf20Sopenharmony_ci return err; 52098c2ecf20Sopenharmony_ci } 52108c2ecf20Sopenharmony_ci 52118c2ecf20Sopenharmony_ci if (spec->add_jack_modes) { 52128c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 52138c2ecf20Sopenharmony_ci err = create_out_jack_modes(codec, cfg->line_outs, 52148c2ecf20Sopenharmony_ci cfg->line_out_pins); 52158c2ecf20Sopenharmony_ci if (err < 0) 52168c2ecf20Sopenharmony_ci return err; 52178c2ecf20Sopenharmony_ci } 52188c2ecf20Sopenharmony_ci if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 52198c2ecf20Sopenharmony_ci err = create_out_jack_modes(codec, cfg->hp_outs, 52208c2ecf20Sopenharmony_ci cfg->hp_pins); 52218c2ecf20Sopenharmony_ci if (err < 0) 52228c2ecf20Sopenharmony_ci return err; 52238c2ecf20Sopenharmony_ci } 52248c2ecf20Sopenharmony_ci } 52258c2ecf20Sopenharmony_ci 52268c2ecf20Sopenharmony_ci /* add power-up pin callbacks at last */ 52278c2ecf20Sopenharmony_ci add_all_pin_power_ctls(codec, true); 52288c2ecf20Sopenharmony_ci 52298c2ecf20Sopenharmony_ci /* mute all aamix input initially */ 52308c2ecf20Sopenharmony_ci if (spec->mixer_nid) 52318c2ecf20Sopenharmony_ci mute_all_mixer_nid(codec, spec->mixer_nid); 52328c2ecf20Sopenharmony_ci 52338c2ecf20Sopenharmony_ci dig_only: 52348c2ecf20Sopenharmony_ci parse_digital(codec); 52358c2ecf20Sopenharmony_ci 52368c2ecf20Sopenharmony_ci if (spec->power_down_unused || codec->power_save_node) { 52378c2ecf20Sopenharmony_ci if (!codec->power_filter) 52388c2ecf20Sopenharmony_ci codec->power_filter = snd_hda_gen_path_power_filter; 52398c2ecf20Sopenharmony_ci if (!codec->patch_ops.stream_pm) 52408c2ecf20Sopenharmony_ci codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; 52418c2ecf20Sopenharmony_ci } 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_ci if (!spec->no_analog && spec->beep_nid) { 52448c2ecf20Sopenharmony_ci err = snd_hda_attach_beep_device(codec, spec->beep_nid); 52458c2ecf20Sopenharmony_ci if (err < 0) 52468c2ecf20Sopenharmony_ci return err; 52478c2ecf20Sopenharmony_ci if (codec->beep && codec->power_save_node) { 52488c2ecf20Sopenharmony_ci err = add_fake_beep_paths(codec); 52498c2ecf20Sopenharmony_ci if (err < 0) 52508c2ecf20Sopenharmony_ci return err; 52518c2ecf20Sopenharmony_ci codec->beep->power_hook = beep_power_hook; 52528c2ecf20Sopenharmony_ci } 52538c2ecf20Sopenharmony_ci } 52548c2ecf20Sopenharmony_ci 52558c2ecf20Sopenharmony_ci return 1; 52568c2ecf20Sopenharmony_ci} 52578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config); 52588c2ecf20Sopenharmony_ci 52598c2ecf20Sopenharmony_ci 52608c2ecf20Sopenharmony_ci/* 52618c2ecf20Sopenharmony_ci * Build control elements 52628c2ecf20Sopenharmony_ci */ 52638c2ecf20Sopenharmony_ci 52648c2ecf20Sopenharmony_ci/* follower controls for virtual master */ 52658c2ecf20Sopenharmony_cistatic const char * const follower_pfxs[] = { 52668c2ecf20Sopenharmony_ci "Front", "Surround", "Center", "LFE", "Side", 52678c2ecf20Sopenharmony_ci "Headphone", "Speaker", "Mono", "Line Out", 52688c2ecf20Sopenharmony_ci "CLFE", "Bass Speaker", "PCM", 52698c2ecf20Sopenharmony_ci "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", 52708c2ecf20Sopenharmony_ci "Headphone Front", "Headphone Surround", "Headphone CLFE", 52718c2ecf20Sopenharmony_ci "Headphone Side", "Headphone+LO", "Speaker+LO", 52728c2ecf20Sopenharmony_ci NULL, 52738c2ecf20Sopenharmony_ci}; 52748c2ecf20Sopenharmony_ci 52758c2ecf20Sopenharmony_ci/** 52768c2ecf20Sopenharmony_ci * snd_hda_gen_build_controls - Build controls from the parsed results 52778c2ecf20Sopenharmony_ci * @codec: the HDA codec 52788c2ecf20Sopenharmony_ci * 52798c2ecf20Sopenharmony_ci * Pass this to build_controls patch_ops. 52808c2ecf20Sopenharmony_ci */ 52818c2ecf20Sopenharmony_ciint snd_hda_gen_build_controls(struct hda_codec *codec) 52828c2ecf20Sopenharmony_ci{ 52838c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 52848c2ecf20Sopenharmony_ci int err; 52858c2ecf20Sopenharmony_ci 52868c2ecf20Sopenharmony_ci if (spec->kctls.used) { 52878c2ecf20Sopenharmony_ci err = snd_hda_add_new_ctls(codec, spec->kctls.list); 52888c2ecf20Sopenharmony_ci if (err < 0) 52898c2ecf20Sopenharmony_ci return err; 52908c2ecf20Sopenharmony_ci } 52918c2ecf20Sopenharmony_ci 52928c2ecf20Sopenharmony_ci if (spec->multiout.dig_out_nid) { 52938c2ecf20Sopenharmony_ci err = snd_hda_create_dig_out_ctls(codec, 52948c2ecf20Sopenharmony_ci spec->multiout.dig_out_nid, 52958c2ecf20Sopenharmony_ci spec->multiout.dig_out_nid, 52968c2ecf20Sopenharmony_ci spec->pcm_rec[1]->pcm_type); 52978c2ecf20Sopenharmony_ci if (err < 0) 52988c2ecf20Sopenharmony_ci return err; 52998c2ecf20Sopenharmony_ci if (!spec->no_analog) { 53008c2ecf20Sopenharmony_ci err = snd_hda_create_spdif_share_sw(codec, 53018c2ecf20Sopenharmony_ci &spec->multiout); 53028c2ecf20Sopenharmony_ci if (err < 0) 53038c2ecf20Sopenharmony_ci return err; 53048c2ecf20Sopenharmony_ci spec->multiout.share_spdif = 1; 53058c2ecf20Sopenharmony_ci } 53068c2ecf20Sopenharmony_ci } 53078c2ecf20Sopenharmony_ci if (spec->dig_in_nid) { 53088c2ecf20Sopenharmony_ci err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 53098c2ecf20Sopenharmony_ci if (err < 0) 53108c2ecf20Sopenharmony_ci return err; 53118c2ecf20Sopenharmony_ci } 53128c2ecf20Sopenharmony_ci 53138c2ecf20Sopenharmony_ci /* if we have no master control, let's create it */ 53148c2ecf20Sopenharmony_ci if (!spec->no_analog && !spec->suppress_vmaster && 53158c2ecf20Sopenharmony_ci !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 53168c2ecf20Sopenharmony_ci err = snd_hda_add_vmaster(codec, "Master Playback Volume", 53178c2ecf20Sopenharmony_ci spec->vmaster_tlv, follower_pfxs, 53188c2ecf20Sopenharmony_ci "Playback Volume"); 53198c2ecf20Sopenharmony_ci if (err < 0) 53208c2ecf20Sopenharmony_ci return err; 53218c2ecf20Sopenharmony_ci } 53228c2ecf20Sopenharmony_ci if (!spec->no_analog && !spec->suppress_vmaster && 53238c2ecf20Sopenharmony_ci !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 53248c2ecf20Sopenharmony_ci err = __snd_hda_add_vmaster(codec, "Master Playback Switch", 53258c2ecf20Sopenharmony_ci NULL, follower_pfxs, 53268c2ecf20Sopenharmony_ci "Playback Switch", 53278c2ecf20Sopenharmony_ci true, &spec->vmaster_mute.sw_kctl); 53288c2ecf20Sopenharmony_ci if (err < 0) 53298c2ecf20Sopenharmony_ci return err; 53308c2ecf20Sopenharmony_ci if (spec->vmaster_mute.hook) { 53318c2ecf20Sopenharmony_ci snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, 53328c2ecf20Sopenharmony_ci spec->vmaster_mute_enum); 53338c2ecf20Sopenharmony_ci snd_hda_sync_vmaster_hook(&spec->vmaster_mute); 53348c2ecf20Sopenharmony_ci } 53358c2ecf20Sopenharmony_ci } 53368c2ecf20Sopenharmony_ci 53378c2ecf20Sopenharmony_ci free_kctls(spec); /* no longer needed */ 53388c2ecf20Sopenharmony_ci 53398c2ecf20Sopenharmony_ci err = snd_hda_jack_add_kctls(codec, &spec->autocfg); 53408c2ecf20Sopenharmony_ci if (err < 0) 53418c2ecf20Sopenharmony_ci return err; 53428c2ecf20Sopenharmony_ci 53438c2ecf20Sopenharmony_ci return 0; 53448c2ecf20Sopenharmony_ci} 53458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_build_controls); 53468c2ecf20Sopenharmony_ci 53478c2ecf20Sopenharmony_ci 53488c2ecf20Sopenharmony_ci/* 53498c2ecf20Sopenharmony_ci * PCM definitions 53508c2ecf20Sopenharmony_ci */ 53518c2ecf20Sopenharmony_ci 53528c2ecf20Sopenharmony_cistatic void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, 53538c2ecf20Sopenharmony_ci struct hda_codec *codec, 53548c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 53558c2ecf20Sopenharmony_ci int action) 53568c2ecf20Sopenharmony_ci{ 53578c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 53588c2ecf20Sopenharmony_ci if (spec->pcm_playback_hook) 53598c2ecf20Sopenharmony_ci spec->pcm_playback_hook(hinfo, codec, substream, action); 53608c2ecf20Sopenharmony_ci} 53618c2ecf20Sopenharmony_ci 53628c2ecf20Sopenharmony_cistatic void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, 53638c2ecf20Sopenharmony_ci struct hda_codec *codec, 53648c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 53658c2ecf20Sopenharmony_ci int action) 53668c2ecf20Sopenharmony_ci{ 53678c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 53688c2ecf20Sopenharmony_ci if (spec->pcm_capture_hook) 53698c2ecf20Sopenharmony_ci spec->pcm_capture_hook(hinfo, codec, substream, action); 53708c2ecf20Sopenharmony_ci} 53718c2ecf20Sopenharmony_ci 53728c2ecf20Sopenharmony_ci/* 53738c2ecf20Sopenharmony_ci * Analog playback callbacks 53748c2ecf20Sopenharmony_ci */ 53758c2ecf20Sopenharmony_cistatic int playback_pcm_open(struct hda_pcm_stream *hinfo, 53768c2ecf20Sopenharmony_ci struct hda_codec *codec, 53778c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 53788c2ecf20Sopenharmony_ci{ 53798c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 53808c2ecf20Sopenharmony_ci int err; 53818c2ecf20Sopenharmony_ci 53828c2ecf20Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 53838c2ecf20Sopenharmony_ci err = snd_hda_multi_out_analog_open(codec, 53848c2ecf20Sopenharmony_ci &spec->multiout, substream, 53858c2ecf20Sopenharmony_ci hinfo); 53868c2ecf20Sopenharmony_ci if (!err) { 53878c2ecf20Sopenharmony_ci spec->active_streams |= 1 << STREAM_MULTI_OUT; 53888c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 53898c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_OPEN); 53908c2ecf20Sopenharmony_ci } 53918c2ecf20Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 53928c2ecf20Sopenharmony_ci return err; 53938c2ecf20Sopenharmony_ci} 53948c2ecf20Sopenharmony_ci 53958c2ecf20Sopenharmony_cistatic int playback_pcm_prepare(struct hda_pcm_stream *hinfo, 53968c2ecf20Sopenharmony_ci struct hda_codec *codec, 53978c2ecf20Sopenharmony_ci unsigned int stream_tag, 53988c2ecf20Sopenharmony_ci unsigned int format, 53998c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54008c2ecf20Sopenharmony_ci{ 54018c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 54028c2ecf20Sopenharmony_ci int err; 54038c2ecf20Sopenharmony_ci 54048c2ecf20Sopenharmony_ci err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 54058c2ecf20Sopenharmony_ci stream_tag, format, substream); 54068c2ecf20Sopenharmony_ci if (!err) 54078c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 54088c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 54098c2ecf20Sopenharmony_ci return err; 54108c2ecf20Sopenharmony_ci} 54118c2ecf20Sopenharmony_ci 54128c2ecf20Sopenharmony_cistatic int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 54138c2ecf20Sopenharmony_ci struct hda_codec *codec, 54148c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54158c2ecf20Sopenharmony_ci{ 54168c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 54178c2ecf20Sopenharmony_ci int err; 54188c2ecf20Sopenharmony_ci 54198c2ecf20Sopenharmony_ci err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 54208c2ecf20Sopenharmony_ci if (!err) 54218c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 54228c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 54238c2ecf20Sopenharmony_ci return err; 54248c2ecf20Sopenharmony_ci} 54258c2ecf20Sopenharmony_ci 54268c2ecf20Sopenharmony_cistatic int playback_pcm_close(struct hda_pcm_stream *hinfo, 54278c2ecf20Sopenharmony_ci struct hda_codec *codec, 54288c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54298c2ecf20Sopenharmony_ci{ 54308c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 54318c2ecf20Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 54328c2ecf20Sopenharmony_ci spec->active_streams &= ~(1 << STREAM_MULTI_OUT); 54338c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 54348c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLOSE); 54358c2ecf20Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 54368c2ecf20Sopenharmony_ci return 0; 54378c2ecf20Sopenharmony_ci} 54388c2ecf20Sopenharmony_ci 54398c2ecf20Sopenharmony_cistatic int capture_pcm_open(struct hda_pcm_stream *hinfo, 54408c2ecf20Sopenharmony_ci struct hda_codec *codec, 54418c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54428c2ecf20Sopenharmony_ci{ 54438c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); 54448c2ecf20Sopenharmony_ci return 0; 54458c2ecf20Sopenharmony_ci} 54468c2ecf20Sopenharmony_ci 54478c2ecf20Sopenharmony_cistatic int capture_pcm_prepare(struct hda_pcm_stream *hinfo, 54488c2ecf20Sopenharmony_ci struct hda_codec *codec, 54498c2ecf20Sopenharmony_ci unsigned int stream_tag, 54508c2ecf20Sopenharmony_ci unsigned int format, 54518c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54528c2ecf20Sopenharmony_ci{ 54538c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 54548c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 54558c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 54568c2ecf20Sopenharmony_ci return 0; 54578c2ecf20Sopenharmony_ci} 54588c2ecf20Sopenharmony_ci 54598c2ecf20Sopenharmony_cistatic int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 54608c2ecf20Sopenharmony_ci struct hda_codec *codec, 54618c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54628c2ecf20Sopenharmony_ci{ 54638c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, hinfo->nid); 54648c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 54658c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 54668c2ecf20Sopenharmony_ci return 0; 54678c2ecf20Sopenharmony_ci} 54688c2ecf20Sopenharmony_ci 54698c2ecf20Sopenharmony_cistatic int capture_pcm_close(struct hda_pcm_stream *hinfo, 54708c2ecf20Sopenharmony_ci struct hda_codec *codec, 54718c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54728c2ecf20Sopenharmony_ci{ 54738c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); 54748c2ecf20Sopenharmony_ci return 0; 54758c2ecf20Sopenharmony_ci} 54768c2ecf20Sopenharmony_ci 54778c2ecf20Sopenharmony_cistatic int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, 54788c2ecf20Sopenharmony_ci struct hda_codec *codec, 54798c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54808c2ecf20Sopenharmony_ci{ 54818c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 54828c2ecf20Sopenharmony_ci int err = 0; 54838c2ecf20Sopenharmony_ci 54848c2ecf20Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 54858c2ecf20Sopenharmony_ci if (spec->indep_hp && !spec->indep_hp_enabled) 54868c2ecf20Sopenharmony_ci err = -EBUSY; 54878c2ecf20Sopenharmony_ci else 54888c2ecf20Sopenharmony_ci spec->active_streams |= 1 << STREAM_INDEP_HP; 54898c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 54908c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_OPEN); 54918c2ecf20Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 54928c2ecf20Sopenharmony_ci return err; 54938c2ecf20Sopenharmony_ci} 54948c2ecf20Sopenharmony_ci 54958c2ecf20Sopenharmony_cistatic int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, 54968c2ecf20Sopenharmony_ci struct hda_codec *codec, 54978c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 54988c2ecf20Sopenharmony_ci{ 54998c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55008c2ecf20Sopenharmony_ci mutex_lock(&spec->pcm_mutex); 55018c2ecf20Sopenharmony_ci spec->active_streams &= ~(1 << STREAM_INDEP_HP); 55028c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 55038c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLOSE); 55048c2ecf20Sopenharmony_ci mutex_unlock(&spec->pcm_mutex); 55058c2ecf20Sopenharmony_ci return 0; 55068c2ecf20Sopenharmony_ci} 55078c2ecf20Sopenharmony_ci 55088c2ecf20Sopenharmony_cistatic int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 55098c2ecf20Sopenharmony_ci struct hda_codec *codec, 55108c2ecf20Sopenharmony_ci unsigned int stream_tag, 55118c2ecf20Sopenharmony_ci unsigned int format, 55128c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55138c2ecf20Sopenharmony_ci{ 55148c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 55158c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 55168c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 55178c2ecf20Sopenharmony_ci return 0; 55188c2ecf20Sopenharmony_ci} 55198c2ecf20Sopenharmony_ci 55208c2ecf20Sopenharmony_cistatic int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 55218c2ecf20Sopenharmony_ci struct hda_codec *codec, 55228c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55238c2ecf20Sopenharmony_ci{ 55248c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, hinfo->nid); 55258c2ecf20Sopenharmony_ci call_pcm_playback_hook(hinfo, codec, substream, 55268c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 55278c2ecf20Sopenharmony_ci return 0; 55288c2ecf20Sopenharmony_ci} 55298c2ecf20Sopenharmony_ci 55308c2ecf20Sopenharmony_ci/* 55318c2ecf20Sopenharmony_ci * Digital out 55328c2ecf20Sopenharmony_ci */ 55338c2ecf20Sopenharmony_cistatic int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 55348c2ecf20Sopenharmony_ci struct hda_codec *codec, 55358c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55368c2ecf20Sopenharmony_ci{ 55378c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55388c2ecf20Sopenharmony_ci return snd_hda_multi_out_dig_open(codec, &spec->multiout); 55398c2ecf20Sopenharmony_ci} 55408c2ecf20Sopenharmony_ci 55418c2ecf20Sopenharmony_cistatic int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 55428c2ecf20Sopenharmony_ci struct hda_codec *codec, 55438c2ecf20Sopenharmony_ci unsigned int stream_tag, 55448c2ecf20Sopenharmony_ci unsigned int format, 55458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55468c2ecf20Sopenharmony_ci{ 55478c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55488c2ecf20Sopenharmony_ci return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 55498c2ecf20Sopenharmony_ci stream_tag, format, substream); 55508c2ecf20Sopenharmony_ci} 55518c2ecf20Sopenharmony_ci 55528c2ecf20Sopenharmony_cistatic int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 55538c2ecf20Sopenharmony_ci struct hda_codec *codec, 55548c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55558c2ecf20Sopenharmony_ci{ 55568c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55578c2ecf20Sopenharmony_ci return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 55588c2ecf20Sopenharmony_ci} 55598c2ecf20Sopenharmony_ci 55608c2ecf20Sopenharmony_cistatic int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 55618c2ecf20Sopenharmony_ci struct hda_codec *codec, 55628c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55638c2ecf20Sopenharmony_ci{ 55648c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55658c2ecf20Sopenharmony_ci return snd_hda_multi_out_dig_close(codec, &spec->multiout); 55668c2ecf20Sopenharmony_ci} 55678c2ecf20Sopenharmony_ci 55688c2ecf20Sopenharmony_ci/* 55698c2ecf20Sopenharmony_ci * Analog capture 55708c2ecf20Sopenharmony_ci */ 55718c2ecf20Sopenharmony_ci#define alt_capture_pcm_open capture_pcm_open 55728c2ecf20Sopenharmony_ci#define alt_capture_pcm_close capture_pcm_close 55738c2ecf20Sopenharmony_ci 55748c2ecf20Sopenharmony_cistatic int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 55758c2ecf20Sopenharmony_ci struct hda_codec *codec, 55768c2ecf20Sopenharmony_ci unsigned int stream_tag, 55778c2ecf20Sopenharmony_ci unsigned int format, 55788c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55798c2ecf20Sopenharmony_ci{ 55808c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55818c2ecf20Sopenharmony_ci 55828c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], 55838c2ecf20Sopenharmony_ci stream_tag, 0, format); 55848c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 55858c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_PREPARE); 55868c2ecf20Sopenharmony_ci return 0; 55878c2ecf20Sopenharmony_ci} 55888c2ecf20Sopenharmony_ci 55898c2ecf20Sopenharmony_cistatic int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 55908c2ecf20Sopenharmony_ci struct hda_codec *codec, 55918c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 55928c2ecf20Sopenharmony_ci{ 55938c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 55948c2ecf20Sopenharmony_ci 55958c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, 55968c2ecf20Sopenharmony_ci spec->adc_nids[substream->number + 1]); 55978c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, 55988c2ecf20Sopenharmony_ci HDA_GEN_PCM_ACT_CLEANUP); 55998c2ecf20Sopenharmony_ci return 0; 56008c2ecf20Sopenharmony_ci} 56018c2ecf20Sopenharmony_ci 56028c2ecf20Sopenharmony_ci/* 56038c2ecf20Sopenharmony_ci */ 56048c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_playback = { 56058c2ecf20Sopenharmony_ci .substreams = 1, 56068c2ecf20Sopenharmony_ci .channels_min = 2, 56078c2ecf20Sopenharmony_ci .channels_max = 8, 56088c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56098c2ecf20Sopenharmony_ci .ops = { 56108c2ecf20Sopenharmony_ci .open = playback_pcm_open, 56118c2ecf20Sopenharmony_ci .close = playback_pcm_close, 56128c2ecf20Sopenharmony_ci .prepare = playback_pcm_prepare, 56138c2ecf20Sopenharmony_ci .cleanup = playback_pcm_cleanup 56148c2ecf20Sopenharmony_ci }, 56158c2ecf20Sopenharmony_ci}; 56168c2ecf20Sopenharmony_ci 56178c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_capture = { 56188c2ecf20Sopenharmony_ci .substreams = 1, 56198c2ecf20Sopenharmony_ci .channels_min = 2, 56208c2ecf20Sopenharmony_ci .channels_max = 2, 56218c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56228c2ecf20Sopenharmony_ci .ops = { 56238c2ecf20Sopenharmony_ci .open = capture_pcm_open, 56248c2ecf20Sopenharmony_ci .close = capture_pcm_close, 56258c2ecf20Sopenharmony_ci .prepare = capture_pcm_prepare, 56268c2ecf20Sopenharmony_ci .cleanup = capture_pcm_cleanup 56278c2ecf20Sopenharmony_ci }, 56288c2ecf20Sopenharmony_ci}; 56298c2ecf20Sopenharmony_ci 56308c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_alt_playback = { 56318c2ecf20Sopenharmony_ci .substreams = 1, 56328c2ecf20Sopenharmony_ci .channels_min = 2, 56338c2ecf20Sopenharmony_ci .channels_max = 2, 56348c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56358c2ecf20Sopenharmony_ci .ops = { 56368c2ecf20Sopenharmony_ci .open = alt_playback_pcm_open, 56378c2ecf20Sopenharmony_ci .close = alt_playback_pcm_close, 56388c2ecf20Sopenharmony_ci .prepare = alt_playback_pcm_prepare, 56398c2ecf20Sopenharmony_ci .cleanup = alt_playback_pcm_cleanup 56408c2ecf20Sopenharmony_ci }, 56418c2ecf20Sopenharmony_ci}; 56428c2ecf20Sopenharmony_ci 56438c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_analog_alt_capture = { 56448c2ecf20Sopenharmony_ci .substreams = 2, /* can be overridden */ 56458c2ecf20Sopenharmony_ci .channels_min = 2, 56468c2ecf20Sopenharmony_ci .channels_max = 2, 56478c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56488c2ecf20Sopenharmony_ci .ops = { 56498c2ecf20Sopenharmony_ci .open = alt_capture_pcm_open, 56508c2ecf20Sopenharmony_ci .close = alt_capture_pcm_close, 56518c2ecf20Sopenharmony_ci .prepare = alt_capture_pcm_prepare, 56528c2ecf20Sopenharmony_ci .cleanup = alt_capture_pcm_cleanup 56538c2ecf20Sopenharmony_ci }, 56548c2ecf20Sopenharmony_ci}; 56558c2ecf20Sopenharmony_ci 56568c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_digital_playback = { 56578c2ecf20Sopenharmony_ci .substreams = 1, 56588c2ecf20Sopenharmony_ci .channels_min = 2, 56598c2ecf20Sopenharmony_ci .channels_max = 2, 56608c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56618c2ecf20Sopenharmony_ci .ops = { 56628c2ecf20Sopenharmony_ci .open = dig_playback_pcm_open, 56638c2ecf20Sopenharmony_ci .close = dig_playback_pcm_close, 56648c2ecf20Sopenharmony_ci .prepare = dig_playback_pcm_prepare, 56658c2ecf20Sopenharmony_ci .cleanup = dig_playback_pcm_cleanup 56668c2ecf20Sopenharmony_ci }, 56678c2ecf20Sopenharmony_ci}; 56688c2ecf20Sopenharmony_ci 56698c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_digital_capture = { 56708c2ecf20Sopenharmony_ci .substreams = 1, 56718c2ecf20Sopenharmony_ci .channels_min = 2, 56728c2ecf20Sopenharmony_ci .channels_max = 2, 56738c2ecf20Sopenharmony_ci /* NID is set in build_pcms */ 56748c2ecf20Sopenharmony_ci}; 56758c2ecf20Sopenharmony_ci 56768c2ecf20Sopenharmony_ci/* Used by build_pcms to flag that a PCM has no playback stream */ 56778c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream pcm_null_stream = { 56788c2ecf20Sopenharmony_ci .substreams = 0, 56798c2ecf20Sopenharmony_ci .channels_min = 0, 56808c2ecf20Sopenharmony_ci .channels_max = 0, 56818c2ecf20Sopenharmony_ci}; 56828c2ecf20Sopenharmony_ci 56838c2ecf20Sopenharmony_ci/* 56848c2ecf20Sopenharmony_ci * dynamic changing ADC PCM streams 56858c2ecf20Sopenharmony_ci */ 56868c2ecf20Sopenharmony_cistatic bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) 56878c2ecf20Sopenharmony_ci{ 56888c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 56898c2ecf20Sopenharmony_ci hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; 56908c2ecf20Sopenharmony_ci 56918c2ecf20Sopenharmony_ci if (spec->cur_adc && spec->cur_adc != new_adc) { 56928c2ecf20Sopenharmony_ci /* stream is running, let's swap the current ADC */ 56938c2ecf20Sopenharmony_ci __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 56948c2ecf20Sopenharmony_ci spec->cur_adc = new_adc; 56958c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, new_adc, 56968c2ecf20Sopenharmony_ci spec->cur_adc_stream_tag, 0, 56978c2ecf20Sopenharmony_ci spec->cur_adc_format); 56988c2ecf20Sopenharmony_ci return true; 56998c2ecf20Sopenharmony_ci } 57008c2ecf20Sopenharmony_ci return false; 57018c2ecf20Sopenharmony_ci} 57028c2ecf20Sopenharmony_ci 57038c2ecf20Sopenharmony_ci/* analog capture with dynamic dual-adc changes */ 57048c2ecf20Sopenharmony_cistatic int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 57058c2ecf20Sopenharmony_ci struct hda_codec *codec, 57068c2ecf20Sopenharmony_ci unsigned int stream_tag, 57078c2ecf20Sopenharmony_ci unsigned int format, 57088c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 57098c2ecf20Sopenharmony_ci{ 57108c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 57118c2ecf20Sopenharmony_ci spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; 57128c2ecf20Sopenharmony_ci spec->cur_adc_stream_tag = stream_tag; 57138c2ecf20Sopenharmony_ci spec->cur_adc_format = format; 57148c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 57158c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); 57168c2ecf20Sopenharmony_ci return 0; 57178c2ecf20Sopenharmony_ci} 57188c2ecf20Sopenharmony_ci 57198c2ecf20Sopenharmony_cistatic int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 57208c2ecf20Sopenharmony_ci struct hda_codec *codec, 57218c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 57228c2ecf20Sopenharmony_ci{ 57238c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 57248c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 57258c2ecf20Sopenharmony_ci spec->cur_adc = 0; 57268c2ecf20Sopenharmony_ci call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); 57278c2ecf20Sopenharmony_ci return 0; 57288c2ecf20Sopenharmony_ci} 57298c2ecf20Sopenharmony_ci 57308c2ecf20Sopenharmony_cistatic const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { 57318c2ecf20Sopenharmony_ci .substreams = 1, 57328c2ecf20Sopenharmony_ci .channels_min = 2, 57338c2ecf20Sopenharmony_ci .channels_max = 2, 57348c2ecf20Sopenharmony_ci .nid = 0, /* fill later */ 57358c2ecf20Sopenharmony_ci .ops = { 57368c2ecf20Sopenharmony_ci .prepare = dyn_adc_capture_pcm_prepare, 57378c2ecf20Sopenharmony_ci .cleanup = dyn_adc_capture_pcm_cleanup 57388c2ecf20Sopenharmony_ci }, 57398c2ecf20Sopenharmony_ci}; 57408c2ecf20Sopenharmony_ci 57418c2ecf20Sopenharmony_cistatic void fill_pcm_stream_name(char *str, size_t len, const char *sfx, 57428c2ecf20Sopenharmony_ci const char *chip_name) 57438c2ecf20Sopenharmony_ci{ 57448c2ecf20Sopenharmony_ci char *p; 57458c2ecf20Sopenharmony_ci 57468c2ecf20Sopenharmony_ci if (*str) 57478c2ecf20Sopenharmony_ci return; 57488c2ecf20Sopenharmony_ci strlcpy(str, chip_name, len); 57498c2ecf20Sopenharmony_ci 57508c2ecf20Sopenharmony_ci /* drop non-alnum chars after a space */ 57518c2ecf20Sopenharmony_ci for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { 57528c2ecf20Sopenharmony_ci if (!isalnum(p[1])) { 57538c2ecf20Sopenharmony_ci *p = 0; 57548c2ecf20Sopenharmony_ci break; 57558c2ecf20Sopenharmony_ci } 57568c2ecf20Sopenharmony_ci } 57578c2ecf20Sopenharmony_ci strlcat(str, sfx, len); 57588c2ecf20Sopenharmony_ci} 57598c2ecf20Sopenharmony_ci 57608c2ecf20Sopenharmony_ci/* copy PCM stream info from @default_str, and override non-NULL entries 57618c2ecf20Sopenharmony_ci * from @spec_str and @nid 57628c2ecf20Sopenharmony_ci */ 57638c2ecf20Sopenharmony_cistatic void setup_pcm_stream(struct hda_pcm_stream *str, 57648c2ecf20Sopenharmony_ci const struct hda_pcm_stream *default_str, 57658c2ecf20Sopenharmony_ci const struct hda_pcm_stream *spec_str, 57668c2ecf20Sopenharmony_ci hda_nid_t nid) 57678c2ecf20Sopenharmony_ci{ 57688c2ecf20Sopenharmony_ci *str = *default_str; 57698c2ecf20Sopenharmony_ci if (nid) 57708c2ecf20Sopenharmony_ci str->nid = nid; 57718c2ecf20Sopenharmony_ci if (spec_str) { 57728c2ecf20Sopenharmony_ci if (spec_str->substreams) 57738c2ecf20Sopenharmony_ci str->substreams = spec_str->substreams; 57748c2ecf20Sopenharmony_ci if (spec_str->channels_min) 57758c2ecf20Sopenharmony_ci str->channels_min = spec_str->channels_min; 57768c2ecf20Sopenharmony_ci if (spec_str->channels_max) 57778c2ecf20Sopenharmony_ci str->channels_max = spec_str->channels_max; 57788c2ecf20Sopenharmony_ci if (spec_str->rates) 57798c2ecf20Sopenharmony_ci str->rates = spec_str->rates; 57808c2ecf20Sopenharmony_ci if (spec_str->formats) 57818c2ecf20Sopenharmony_ci str->formats = spec_str->formats; 57828c2ecf20Sopenharmony_ci if (spec_str->maxbps) 57838c2ecf20Sopenharmony_ci str->maxbps = spec_str->maxbps; 57848c2ecf20Sopenharmony_ci } 57858c2ecf20Sopenharmony_ci} 57868c2ecf20Sopenharmony_ci 57878c2ecf20Sopenharmony_ci/** 57888c2ecf20Sopenharmony_ci * snd_hda_gen_build_pcms - build PCM streams based on the parsed results 57898c2ecf20Sopenharmony_ci * @codec: the HDA codec 57908c2ecf20Sopenharmony_ci * 57918c2ecf20Sopenharmony_ci * Pass this to build_pcms patch_ops. 57928c2ecf20Sopenharmony_ci */ 57938c2ecf20Sopenharmony_ciint snd_hda_gen_build_pcms(struct hda_codec *codec) 57948c2ecf20Sopenharmony_ci{ 57958c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 57968c2ecf20Sopenharmony_ci struct hda_pcm *info; 57978c2ecf20Sopenharmony_ci bool have_multi_adcs; 57988c2ecf20Sopenharmony_ci 57998c2ecf20Sopenharmony_ci if (spec->no_analog) 58008c2ecf20Sopenharmony_ci goto skip_analog; 58018c2ecf20Sopenharmony_ci 58028c2ecf20Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_analog, 58038c2ecf20Sopenharmony_ci sizeof(spec->stream_name_analog), 58048c2ecf20Sopenharmony_ci " Analog", codec->core.chip_name); 58058c2ecf20Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); 58068c2ecf20Sopenharmony_ci if (!info) 58078c2ecf20Sopenharmony_ci return -ENOMEM; 58088c2ecf20Sopenharmony_ci spec->pcm_rec[0] = info; 58098c2ecf20Sopenharmony_ci 58108c2ecf20Sopenharmony_ci if (spec->multiout.num_dacs > 0) { 58118c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 58128c2ecf20Sopenharmony_ci &pcm_analog_playback, 58138c2ecf20Sopenharmony_ci spec->stream_analog_playback, 58148c2ecf20Sopenharmony_ci spec->multiout.dac_nids[0]); 58158c2ecf20Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 58168c2ecf20Sopenharmony_ci spec->multiout.max_channels; 58178c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && 58188c2ecf20Sopenharmony_ci spec->autocfg.line_outs == 2) 58198c2ecf20Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = 58208c2ecf20Sopenharmony_ci snd_pcm_2_1_chmaps; 58218c2ecf20Sopenharmony_ci } 58228c2ecf20Sopenharmony_ci if (spec->num_adc_nids) { 58238c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 58248c2ecf20Sopenharmony_ci (spec->dyn_adc_switch ? 58258c2ecf20Sopenharmony_ci &dyn_adc_pcm_analog_capture : &pcm_analog_capture), 58268c2ecf20Sopenharmony_ci spec->stream_analog_capture, 58278c2ecf20Sopenharmony_ci spec->adc_nids[0]); 58288c2ecf20Sopenharmony_ci } 58298c2ecf20Sopenharmony_ci 58308c2ecf20Sopenharmony_ci skip_analog: 58318c2ecf20Sopenharmony_ci /* SPDIF for stream index #1 */ 58328c2ecf20Sopenharmony_ci if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 58338c2ecf20Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_digital, 58348c2ecf20Sopenharmony_ci sizeof(spec->stream_name_digital), 58358c2ecf20Sopenharmony_ci " Digital", codec->core.chip_name); 58368c2ecf20Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", 58378c2ecf20Sopenharmony_ci spec->stream_name_digital); 58388c2ecf20Sopenharmony_ci if (!info) 58398c2ecf20Sopenharmony_ci return -ENOMEM; 58408c2ecf20Sopenharmony_ci codec->follower_dig_outs = spec->multiout.follower_dig_outs; 58418c2ecf20Sopenharmony_ci spec->pcm_rec[1] = info; 58428c2ecf20Sopenharmony_ci if (spec->dig_out_type) 58438c2ecf20Sopenharmony_ci info->pcm_type = spec->dig_out_type; 58448c2ecf20Sopenharmony_ci else 58458c2ecf20Sopenharmony_ci info->pcm_type = HDA_PCM_TYPE_SPDIF; 58468c2ecf20Sopenharmony_ci if (spec->multiout.dig_out_nid) 58478c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 58488c2ecf20Sopenharmony_ci &pcm_digital_playback, 58498c2ecf20Sopenharmony_ci spec->stream_digital_playback, 58508c2ecf20Sopenharmony_ci spec->multiout.dig_out_nid); 58518c2ecf20Sopenharmony_ci if (spec->dig_in_nid) 58528c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 58538c2ecf20Sopenharmony_ci &pcm_digital_capture, 58548c2ecf20Sopenharmony_ci spec->stream_digital_capture, 58558c2ecf20Sopenharmony_ci spec->dig_in_nid); 58568c2ecf20Sopenharmony_ci } 58578c2ecf20Sopenharmony_ci 58588c2ecf20Sopenharmony_ci if (spec->no_analog) 58598c2ecf20Sopenharmony_ci return 0; 58608c2ecf20Sopenharmony_ci 58618c2ecf20Sopenharmony_ci /* If the use of more than one ADC is requested for the current 58628c2ecf20Sopenharmony_ci * model, configure a second analog capture-only PCM. 58638c2ecf20Sopenharmony_ci */ 58648c2ecf20Sopenharmony_ci have_multi_adcs = (spec->num_adc_nids > 1) && 58658c2ecf20Sopenharmony_ci !spec->dyn_adc_switch && !spec->auto_mic; 58668c2ecf20Sopenharmony_ci /* Additional Analaog capture for index #2 */ 58678c2ecf20Sopenharmony_ci if (spec->alt_dac_nid || have_multi_adcs) { 58688c2ecf20Sopenharmony_ci fill_pcm_stream_name(spec->stream_name_alt_analog, 58698c2ecf20Sopenharmony_ci sizeof(spec->stream_name_alt_analog), 58708c2ecf20Sopenharmony_ci " Alt Analog", codec->core.chip_name); 58718c2ecf20Sopenharmony_ci info = snd_hda_codec_pcm_new(codec, "%s", 58728c2ecf20Sopenharmony_ci spec->stream_name_alt_analog); 58738c2ecf20Sopenharmony_ci if (!info) 58748c2ecf20Sopenharmony_ci return -ENOMEM; 58758c2ecf20Sopenharmony_ci spec->pcm_rec[2] = info; 58768c2ecf20Sopenharmony_ci if (spec->alt_dac_nid) 58778c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 58788c2ecf20Sopenharmony_ci &pcm_analog_alt_playback, 58798c2ecf20Sopenharmony_ci spec->stream_analog_alt_playback, 58808c2ecf20Sopenharmony_ci spec->alt_dac_nid); 58818c2ecf20Sopenharmony_ci else 58828c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], 58838c2ecf20Sopenharmony_ci &pcm_null_stream, NULL, 0); 58848c2ecf20Sopenharmony_ci if (have_multi_adcs) { 58858c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 58868c2ecf20Sopenharmony_ci &pcm_analog_alt_capture, 58878c2ecf20Sopenharmony_ci spec->stream_analog_alt_capture, 58888c2ecf20Sopenharmony_ci spec->adc_nids[1]); 58898c2ecf20Sopenharmony_ci info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 58908c2ecf20Sopenharmony_ci spec->num_adc_nids - 1; 58918c2ecf20Sopenharmony_ci } else { 58928c2ecf20Sopenharmony_ci setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], 58938c2ecf20Sopenharmony_ci &pcm_null_stream, NULL, 0); 58948c2ecf20Sopenharmony_ci } 58958c2ecf20Sopenharmony_ci } 58968c2ecf20Sopenharmony_ci 58978c2ecf20Sopenharmony_ci return 0; 58988c2ecf20Sopenharmony_ci} 58998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms); 59008c2ecf20Sopenharmony_ci 59018c2ecf20Sopenharmony_ci 59028c2ecf20Sopenharmony_ci/* 59038c2ecf20Sopenharmony_ci * Standard auto-parser initializations 59048c2ecf20Sopenharmony_ci */ 59058c2ecf20Sopenharmony_ci 59068c2ecf20Sopenharmony_ci/* configure the given path as a proper output */ 59078c2ecf20Sopenharmony_cistatic void set_output_and_unmute(struct hda_codec *codec, int path_idx) 59088c2ecf20Sopenharmony_ci{ 59098c2ecf20Sopenharmony_ci struct nid_path *path; 59108c2ecf20Sopenharmony_ci hda_nid_t pin; 59118c2ecf20Sopenharmony_ci 59128c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, path_idx); 59138c2ecf20Sopenharmony_ci if (!path || !path->depth) 59148c2ecf20Sopenharmony_ci return; 59158c2ecf20Sopenharmony_ci pin = path->path[path->depth - 1]; 59168c2ecf20Sopenharmony_ci restore_pin_ctl(codec, pin); 59178c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 59188c2ecf20Sopenharmony_ci aamix_default(codec->spec)); 59198c2ecf20Sopenharmony_ci set_pin_eapd(codec, pin, path->active); 59208c2ecf20Sopenharmony_ci} 59218c2ecf20Sopenharmony_ci 59228c2ecf20Sopenharmony_ci/* initialize primary output paths */ 59238c2ecf20Sopenharmony_cistatic void init_multi_out(struct hda_codec *codec) 59248c2ecf20Sopenharmony_ci{ 59258c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 59268c2ecf20Sopenharmony_ci int i; 59278c2ecf20Sopenharmony_ci 59288c2ecf20Sopenharmony_ci for (i = 0; i < spec->autocfg.line_outs; i++) 59298c2ecf20Sopenharmony_ci set_output_and_unmute(codec, spec->out_paths[i]); 59308c2ecf20Sopenharmony_ci} 59318c2ecf20Sopenharmony_ci 59328c2ecf20Sopenharmony_ci 59338c2ecf20Sopenharmony_cistatic void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) 59348c2ecf20Sopenharmony_ci{ 59358c2ecf20Sopenharmony_ci int i; 59368c2ecf20Sopenharmony_ci 59378c2ecf20Sopenharmony_ci for (i = 0; i < num_outs; i++) 59388c2ecf20Sopenharmony_ci set_output_and_unmute(codec, paths[i]); 59398c2ecf20Sopenharmony_ci} 59408c2ecf20Sopenharmony_ci 59418c2ecf20Sopenharmony_ci/* initialize hp and speaker paths */ 59428c2ecf20Sopenharmony_cistatic void init_extra_out(struct hda_codec *codec) 59438c2ecf20Sopenharmony_ci{ 59448c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 59458c2ecf20Sopenharmony_ci 59468c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) 59478c2ecf20Sopenharmony_ci __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); 59488c2ecf20Sopenharmony_ci if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) 59498c2ecf20Sopenharmony_ci __init_extra_out(codec, spec->autocfg.speaker_outs, 59508c2ecf20Sopenharmony_ci spec->speaker_paths); 59518c2ecf20Sopenharmony_ci} 59528c2ecf20Sopenharmony_ci 59538c2ecf20Sopenharmony_ci/* initialize multi-io paths */ 59548c2ecf20Sopenharmony_cistatic void init_multi_io(struct hda_codec *codec) 59558c2ecf20Sopenharmony_ci{ 59568c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 59578c2ecf20Sopenharmony_ci int i; 59588c2ecf20Sopenharmony_ci 59598c2ecf20Sopenharmony_ci for (i = 0; i < spec->multi_ios; i++) { 59608c2ecf20Sopenharmony_ci hda_nid_t pin = spec->multi_io[i].pin; 59618c2ecf20Sopenharmony_ci struct nid_path *path; 59628c2ecf20Sopenharmony_ci path = get_multiio_path(codec, i); 59638c2ecf20Sopenharmony_ci if (!path) 59648c2ecf20Sopenharmony_ci continue; 59658c2ecf20Sopenharmony_ci if (!spec->multi_io[i].ctl_in) 59668c2ecf20Sopenharmony_ci spec->multi_io[i].ctl_in = 59678c2ecf20Sopenharmony_ci snd_hda_codec_get_pin_target(codec, pin); 59688c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, 59698c2ecf20Sopenharmony_ci aamix_default(spec)); 59708c2ecf20Sopenharmony_ci } 59718c2ecf20Sopenharmony_ci} 59728c2ecf20Sopenharmony_ci 59738c2ecf20Sopenharmony_cistatic void init_aamix_paths(struct hda_codec *codec) 59748c2ecf20Sopenharmony_ci{ 59758c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 59768c2ecf20Sopenharmony_ci 59778c2ecf20Sopenharmony_ci if (!spec->have_aamix_ctl) 59788c2ecf20Sopenharmony_ci return; 59798c2ecf20Sopenharmony_ci if (!has_aamix_out_paths(spec)) 59808c2ecf20Sopenharmony_ci return; 59818c2ecf20Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0], 59828c2ecf20Sopenharmony_ci spec->aamix_out_paths[0], 59838c2ecf20Sopenharmony_ci spec->autocfg.line_out_type); 59848c2ecf20Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0], 59858c2ecf20Sopenharmony_ci spec->aamix_out_paths[1], 59868c2ecf20Sopenharmony_ci AUTO_PIN_HP_OUT); 59878c2ecf20Sopenharmony_ci update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0], 59888c2ecf20Sopenharmony_ci spec->aamix_out_paths[2], 59898c2ecf20Sopenharmony_ci AUTO_PIN_SPEAKER_OUT); 59908c2ecf20Sopenharmony_ci} 59918c2ecf20Sopenharmony_ci 59928c2ecf20Sopenharmony_ci/* set up input pins and loopback paths */ 59938c2ecf20Sopenharmony_cistatic void init_analog_input(struct hda_codec *codec) 59948c2ecf20Sopenharmony_ci{ 59958c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 59968c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->autocfg; 59978c2ecf20Sopenharmony_ci int i; 59988c2ecf20Sopenharmony_ci 59998c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 60008c2ecf20Sopenharmony_ci hda_nid_t nid = cfg->inputs[i].pin; 60018c2ecf20Sopenharmony_ci if (is_input_pin(codec, nid)) 60028c2ecf20Sopenharmony_ci restore_pin_ctl(codec, nid); 60038c2ecf20Sopenharmony_ci 60048c2ecf20Sopenharmony_ci /* init loopback inputs */ 60058c2ecf20Sopenharmony_ci if (spec->mixer_nid) { 60068c2ecf20Sopenharmony_ci resume_path_from_idx(codec, spec->loopback_paths[i]); 60078c2ecf20Sopenharmony_ci resume_path_from_idx(codec, spec->loopback_merge_path); 60088c2ecf20Sopenharmony_ci } 60098c2ecf20Sopenharmony_ci } 60108c2ecf20Sopenharmony_ci} 60118c2ecf20Sopenharmony_ci 60128c2ecf20Sopenharmony_ci/* initialize ADC paths */ 60138c2ecf20Sopenharmony_cistatic void init_input_src(struct hda_codec *codec) 60148c2ecf20Sopenharmony_ci{ 60158c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 60168c2ecf20Sopenharmony_ci struct hda_input_mux *imux = &spec->input_mux; 60178c2ecf20Sopenharmony_ci struct nid_path *path; 60188c2ecf20Sopenharmony_ci int i, c, nums; 60198c2ecf20Sopenharmony_ci 60208c2ecf20Sopenharmony_ci if (spec->dyn_adc_switch) 60218c2ecf20Sopenharmony_ci nums = 1; 60228c2ecf20Sopenharmony_ci else 60238c2ecf20Sopenharmony_ci nums = spec->num_adc_nids; 60248c2ecf20Sopenharmony_ci 60258c2ecf20Sopenharmony_ci for (c = 0; c < nums; c++) { 60268c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 60278c2ecf20Sopenharmony_ci path = get_input_path(codec, c, i); 60288c2ecf20Sopenharmony_ci if (path) { 60298c2ecf20Sopenharmony_ci bool active = path->active; 60308c2ecf20Sopenharmony_ci if (i == spec->cur_mux[c]) 60318c2ecf20Sopenharmony_ci active = true; 60328c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, active, false); 60338c2ecf20Sopenharmony_ci } 60348c2ecf20Sopenharmony_ci } 60358c2ecf20Sopenharmony_ci if (spec->hp_mic) 60368c2ecf20Sopenharmony_ci update_hp_mic(codec, c, true); 60378c2ecf20Sopenharmony_ci } 60388c2ecf20Sopenharmony_ci 60398c2ecf20Sopenharmony_ci if (spec->cap_sync_hook) 60408c2ecf20Sopenharmony_ci spec->cap_sync_hook(codec, NULL, NULL); 60418c2ecf20Sopenharmony_ci} 60428c2ecf20Sopenharmony_ci 60438c2ecf20Sopenharmony_ci/* set right pin controls for digital I/O */ 60448c2ecf20Sopenharmony_cistatic void init_digital(struct hda_codec *codec) 60458c2ecf20Sopenharmony_ci{ 60468c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 60478c2ecf20Sopenharmony_ci int i; 60488c2ecf20Sopenharmony_ci hda_nid_t pin; 60498c2ecf20Sopenharmony_ci 60508c2ecf20Sopenharmony_ci for (i = 0; i < spec->autocfg.dig_outs; i++) 60518c2ecf20Sopenharmony_ci set_output_and_unmute(codec, spec->digout_paths[i]); 60528c2ecf20Sopenharmony_ci pin = spec->autocfg.dig_in_pin; 60538c2ecf20Sopenharmony_ci if (pin) { 60548c2ecf20Sopenharmony_ci restore_pin_ctl(codec, pin); 60558c2ecf20Sopenharmony_ci resume_path_from_idx(codec, spec->digin_path); 60568c2ecf20Sopenharmony_ci } 60578c2ecf20Sopenharmony_ci} 60588c2ecf20Sopenharmony_ci 60598c2ecf20Sopenharmony_ci/* clear unsol-event tags on unused pins; Conexant codecs seem to leave 60608c2ecf20Sopenharmony_ci * invalid unsol tags by some reason 60618c2ecf20Sopenharmony_ci */ 60628c2ecf20Sopenharmony_cistatic void clear_unsol_on_unused_pins(struct hda_codec *codec) 60638c2ecf20Sopenharmony_ci{ 60648c2ecf20Sopenharmony_ci const struct hda_pincfg *pin; 60658c2ecf20Sopenharmony_ci int i; 60668c2ecf20Sopenharmony_ci 60678c2ecf20Sopenharmony_ci snd_array_for_each(&codec->init_pins, i, pin) { 60688c2ecf20Sopenharmony_ci hda_nid_t nid = pin->nid; 60698c2ecf20Sopenharmony_ci if (is_jack_detectable(codec, nid) && 60708c2ecf20Sopenharmony_ci !snd_hda_jack_tbl_get(codec, nid)) 60718c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, nid, 0, 60728c2ecf20Sopenharmony_ci AC_VERB_SET_UNSOLICITED_ENABLE, 0); 60738c2ecf20Sopenharmony_ci } 60748c2ecf20Sopenharmony_ci} 60758c2ecf20Sopenharmony_ci 60768c2ecf20Sopenharmony_ci/** 60778c2ecf20Sopenharmony_ci * snd_hda_gen_init - initialize the generic spec 60788c2ecf20Sopenharmony_ci * @codec: the HDA codec 60798c2ecf20Sopenharmony_ci * 60808c2ecf20Sopenharmony_ci * This can be put as patch_ops init function. 60818c2ecf20Sopenharmony_ci */ 60828c2ecf20Sopenharmony_ciint snd_hda_gen_init(struct hda_codec *codec) 60838c2ecf20Sopenharmony_ci{ 60848c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 60858c2ecf20Sopenharmony_ci 60868c2ecf20Sopenharmony_ci if (spec->init_hook) 60878c2ecf20Sopenharmony_ci spec->init_hook(codec); 60888c2ecf20Sopenharmony_ci 60898c2ecf20Sopenharmony_ci if (!spec->skip_verbs) 60908c2ecf20Sopenharmony_ci snd_hda_apply_verbs(codec); 60918c2ecf20Sopenharmony_ci 60928c2ecf20Sopenharmony_ci init_multi_out(codec); 60938c2ecf20Sopenharmony_ci init_extra_out(codec); 60948c2ecf20Sopenharmony_ci init_multi_io(codec); 60958c2ecf20Sopenharmony_ci init_aamix_paths(codec); 60968c2ecf20Sopenharmony_ci init_analog_input(codec); 60978c2ecf20Sopenharmony_ci init_input_src(codec); 60988c2ecf20Sopenharmony_ci init_digital(codec); 60998c2ecf20Sopenharmony_ci 61008c2ecf20Sopenharmony_ci clear_unsol_on_unused_pins(codec); 61018c2ecf20Sopenharmony_ci 61028c2ecf20Sopenharmony_ci sync_all_pin_power_ctls(codec); 61038c2ecf20Sopenharmony_ci 61048c2ecf20Sopenharmony_ci /* call init functions of standard auto-mute helpers */ 61058c2ecf20Sopenharmony_ci update_automute_all(codec); 61068c2ecf20Sopenharmony_ci 61078c2ecf20Sopenharmony_ci snd_hda_regmap_sync(codec); 61088c2ecf20Sopenharmony_ci 61098c2ecf20Sopenharmony_ci if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) 61108c2ecf20Sopenharmony_ci snd_hda_sync_vmaster_hook(&spec->vmaster_mute); 61118c2ecf20Sopenharmony_ci 61128c2ecf20Sopenharmony_ci hda_call_check_power_status(codec, 0x01); 61138c2ecf20Sopenharmony_ci return 0; 61148c2ecf20Sopenharmony_ci} 61158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_init); 61168c2ecf20Sopenharmony_ci 61178c2ecf20Sopenharmony_ci/** 61188c2ecf20Sopenharmony_ci * snd_hda_gen_free - free the generic spec 61198c2ecf20Sopenharmony_ci * @codec: the HDA codec 61208c2ecf20Sopenharmony_ci * 61218c2ecf20Sopenharmony_ci * This can be put as patch_ops free function. 61228c2ecf20Sopenharmony_ci */ 61238c2ecf20Sopenharmony_civoid snd_hda_gen_free(struct hda_codec *codec) 61248c2ecf20Sopenharmony_ci{ 61258c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); 61268c2ecf20Sopenharmony_ci snd_hda_gen_spec_free(codec->spec); 61278c2ecf20Sopenharmony_ci kfree(codec->spec); 61288c2ecf20Sopenharmony_ci codec->spec = NULL; 61298c2ecf20Sopenharmony_ci} 61308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_free); 61318c2ecf20Sopenharmony_ci 61328c2ecf20Sopenharmony_ci/** 61338c2ecf20Sopenharmony_ci * snd_hda_gen_reboot_notify - Make codec enter D3 before rebooting 61348c2ecf20Sopenharmony_ci * @codec: the HDA codec 61358c2ecf20Sopenharmony_ci * 61368c2ecf20Sopenharmony_ci * This can be put as patch_ops reboot_notify function. 61378c2ecf20Sopenharmony_ci */ 61388c2ecf20Sopenharmony_civoid snd_hda_gen_reboot_notify(struct hda_codec *codec) 61398c2ecf20Sopenharmony_ci{ 61408c2ecf20Sopenharmony_ci /* Make the codec enter D3 to avoid spurious noises from the internal 61418c2ecf20Sopenharmony_ci * speaker during (and after) reboot 61428c2ecf20Sopenharmony_ci */ 61438c2ecf20Sopenharmony_ci snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); 61448c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, codec->core.afg, 0, 61458c2ecf20Sopenharmony_ci AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61468c2ecf20Sopenharmony_ci msleep(10); 61478c2ecf20Sopenharmony_ci} 61488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_reboot_notify); 61498c2ecf20Sopenharmony_ci 61508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 61518c2ecf20Sopenharmony_ci/** 61528c2ecf20Sopenharmony_ci * snd_hda_gen_check_power_status - check the loopback power save state 61538c2ecf20Sopenharmony_ci * @codec: the HDA codec 61548c2ecf20Sopenharmony_ci * @nid: NID to inspect 61558c2ecf20Sopenharmony_ci * 61568c2ecf20Sopenharmony_ci * This can be put as patch_ops check_power_status function. 61578c2ecf20Sopenharmony_ci */ 61588c2ecf20Sopenharmony_ciint snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) 61598c2ecf20Sopenharmony_ci{ 61608c2ecf20Sopenharmony_ci struct hda_gen_spec *spec = codec->spec; 61618c2ecf20Sopenharmony_ci return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 61628c2ecf20Sopenharmony_ci} 61638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); 61648c2ecf20Sopenharmony_ci#endif 61658c2ecf20Sopenharmony_ci 61668c2ecf20Sopenharmony_ci 61678c2ecf20Sopenharmony_ci/* 61688c2ecf20Sopenharmony_ci * the generic codec support 61698c2ecf20Sopenharmony_ci */ 61708c2ecf20Sopenharmony_ci 61718c2ecf20Sopenharmony_cistatic const struct hda_codec_ops generic_patch_ops = { 61728c2ecf20Sopenharmony_ci .build_controls = snd_hda_gen_build_controls, 61738c2ecf20Sopenharmony_ci .build_pcms = snd_hda_gen_build_pcms, 61748c2ecf20Sopenharmony_ci .init = snd_hda_gen_init, 61758c2ecf20Sopenharmony_ci .free = snd_hda_gen_free, 61768c2ecf20Sopenharmony_ci .unsol_event = snd_hda_jack_unsol_event, 61778c2ecf20Sopenharmony_ci .reboot_notify = snd_hda_gen_reboot_notify, 61788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 61798c2ecf20Sopenharmony_ci .check_power_status = snd_hda_gen_check_power_status, 61808c2ecf20Sopenharmony_ci#endif 61818c2ecf20Sopenharmony_ci}; 61828c2ecf20Sopenharmony_ci 61838c2ecf20Sopenharmony_ci/* 61848c2ecf20Sopenharmony_ci * snd_hda_parse_generic_codec - Generic codec parser 61858c2ecf20Sopenharmony_ci * @codec: the HDA codec 61868c2ecf20Sopenharmony_ci */ 61878c2ecf20Sopenharmony_cistatic int snd_hda_parse_generic_codec(struct hda_codec *codec) 61888c2ecf20Sopenharmony_ci{ 61898c2ecf20Sopenharmony_ci struct hda_gen_spec *spec; 61908c2ecf20Sopenharmony_ci int err; 61918c2ecf20Sopenharmony_ci 61928c2ecf20Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 61938c2ecf20Sopenharmony_ci if (!spec) 61948c2ecf20Sopenharmony_ci return -ENOMEM; 61958c2ecf20Sopenharmony_ci snd_hda_gen_spec_init(spec); 61968c2ecf20Sopenharmony_ci codec->spec = spec; 61978c2ecf20Sopenharmony_ci 61988c2ecf20Sopenharmony_ci err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); 61998c2ecf20Sopenharmony_ci if (err < 0) 62008c2ecf20Sopenharmony_ci goto error; 62018c2ecf20Sopenharmony_ci 62028c2ecf20Sopenharmony_ci err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); 62038c2ecf20Sopenharmony_ci if (err < 0) 62048c2ecf20Sopenharmony_ci goto error; 62058c2ecf20Sopenharmony_ci 62068c2ecf20Sopenharmony_ci codec->patch_ops = generic_patch_ops; 62078c2ecf20Sopenharmony_ci return 0; 62088c2ecf20Sopenharmony_ci 62098c2ecf20Sopenharmony_cierror: 62108c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 62118c2ecf20Sopenharmony_ci return err; 62128c2ecf20Sopenharmony_ci} 62138c2ecf20Sopenharmony_ci 62148c2ecf20Sopenharmony_cistatic const struct hda_device_id snd_hda_id_generic[] = { 62158c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), 62168c2ecf20Sopenharmony_ci {} /* terminator */ 62178c2ecf20Sopenharmony_ci}; 62188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); 62198c2ecf20Sopenharmony_ci 62208c2ecf20Sopenharmony_cistatic struct hda_codec_driver generic_driver = { 62218c2ecf20Sopenharmony_ci .id = snd_hda_id_generic, 62228c2ecf20Sopenharmony_ci}; 62238c2ecf20Sopenharmony_ci 62248c2ecf20Sopenharmony_cimodule_hda_codec_driver(generic_driver); 62258c2ecf20Sopenharmony_ci 62268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 62278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic HD-audio codec parser"); 6228