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