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