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 proc interface
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 <sound/core.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <sound/hda_codec.h>
1562306a36Sopenharmony_ci#include "hda_local.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int dump_coef = -1;
1862306a36Sopenharmony_cimodule_param(dump_coef, int, 0644);
1962306a36Sopenharmony_ciMODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* always use noncached version */
2262306a36Sopenharmony_ci#define param_read(codec, nid, parm) \
2362306a36Sopenharmony_ci	snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic const char *get_wid_type_name(unsigned int wid_value)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	static const char * const names[16] = {
2862306a36Sopenharmony_ci		[AC_WID_AUD_OUT] = "Audio Output",
2962306a36Sopenharmony_ci		[AC_WID_AUD_IN] = "Audio Input",
3062306a36Sopenharmony_ci		[AC_WID_AUD_MIX] = "Audio Mixer",
3162306a36Sopenharmony_ci		[AC_WID_AUD_SEL] = "Audio Selector",
3262306a36Sopenharmony_ci		[AC_WID_PIN] = "Pin Complex",
3362306a36Sopenharmony_ci		[AC_WID_POWER] = "Power Widget",
3462306a36Sopenharmony_ci		[AC_WID_VOL_KNB] = "Volume Knob Widget",
3562306a36Sopenharmony_ci		[AC_WID_BEEP] = "Beep Generator Widget",
3662306a36Sopenharmony_ci		[AC_WID_VENDOR] = "Vendor Defined Widget",
3762306a36Sopenharmony_ci	};
3862306a36Sopenharmony_ci	if (wid_value == -1)
3962306a36Sopenharmony_ci		return "UNKNOWN Widget";
4062306a36Sopenharmony_ci	wid_value &= 0xf;
4162306a36Sopenharmony_ci	if (names[wid_value])
4262306a36Sopenharmony_ci		return names[wid_value];
4362306a36Sopenharmony_ci	else
4462306a36Sopenharmony_ci		return "UNKNOWN Widget";
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void print_nid_array(struct snd_info_buffer *buffer,
4862306a36Sopenharmony_ci			    struct hda_codec *codec, hda_nid_t nid,
4962306a36Sopenharmony_ci			    struct snd_array *array)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci	struct hda_nid_item *items = array->list, *item;
5362306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
5462306a36Sopenharmony_ci	for (i = 0; i < array->used; i++) {
5562306a36Sopenharmony_ci		item = &items[i];
5662306a36Sopenharmony_ci		if (item->nid == nid) {
5762306a36Sopenharmony_ci			kctl = item->kctl;
5862306a36Sopenharmony_ci			snd_iprintf(buffer,
5962306a36Sopenharmony_ci			  "  Control: name=\"%s\", index=%i, device=%i\n",
6062306a36Sopenharmony_ci			  kctl->id.name, kctl->id.index + item->index,
6162306a36Sopenharmony_ci			  kctl->id.device);
6262306a36Sopenharmony_ci			if (item->flags & HDA_NID_ITEM_AMP)
6362306a36Sopenharmony_ci				snd_iprintf(buffer,
6462306a36Sopenharmony_ci				  "    ControlAmp: chs=%lu, dir=%s, "
6562306a36Sopenharmony_ci				  "idx=%lu, ofs=%lu\n",
6662306a36Sopenharmony_ci				  get_amp_channels(kctl),
6762306a36Sopenharmony_ci				  get_amp_direction(kctl) ? "Out" : "In",
6862306a36Sopenharmony_ci				  get_amp_index(kctl),
6962306a36Sopenharmony_ci				  get_amp_offset(kctl));
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void print_nid_pcms(struct snd_info_buffer *buffer,
7562306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int type;
7862306a36Sopenharmony_ci	struct hda_pcm *cpcm;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
8162306a36Sopenharmony_ci		for (type = 0; type < 2; type++) {
8262306a36Sopenharmony_ci			if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
8362306a36Sopenharmony_ci				continue;
8462306a36Sopenharmony_ci			snd_iprintf(buffer, "  Device: name=\"%s\", "
8562306a36Sopenharmony_ci				    "type=\"%s\", device=%i\n",
8662306a36Sopenharmony_ci				    cpcm->name,
8762306a36Sopenharmony_ci				    snd_hda_pcm_type_name[cpcm->pcm_type],
8862306a36Sopenharmony_ci				    cpcm->pcm->device);
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void print_amp_caps(struct snd_info_buffer *buffer,
9462306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid, int dir)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned int caps;
9762306a36Sopenharmony_ci	caps = param_read(codec, nid, dir == HDA_OUTPUT ?
9862306a36Sopenharmony_ci			  AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
9962306a36Sopenharmony_ci	if (caps == -1 || caps == 0) {
10062306a36Sopenharmony_ci		snd_iprintf(buffer, "N/A\n");
10162306a36Sopenharmony_ci		return;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci	snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, "
10462306a36Sopenharmony_ci		    "mute=%x\n",
10562306a36Sopenharmony_ci		    caps & AC_AMPCAP_OFFSET,
10662306a36Sopenharmony_ci		    (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
10762306a36Sopenharmony_ci		    (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
10862306a36Sopenharmony_ci		    (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* is this a stereo widget or a stereo-to-mono mix? */
11262306a36Sopenharmony_cistatic bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
11362306a36Sopenharmony_ci			   int dir, unsigned int wcaps, int indices)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	hda_nid_t conn;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (wcaps & AC_WCAP_STEREO)
11862306a36Sopenharmony_ci		return true;
11962306a36Sopenharmony_ci	/* check for a stereo-to-mono mix; it must be:
12062306a36Sopenharmony_ci	 * only a single connection, only for input, and only a mixer widget
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	if (indices != 1 || dir != HDA_INPUT ||
12362306a36Sopenharmony_ci	    get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
12462306a36Sopenharmony_ci		return false;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
12762306a36Sopenharmony_ci		return false;
12862306a36Sopenharmony_ci	/* the connection source is a stereo? */
12962306a36Sopenharmony_ci	wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
13062306a36Sopenharmony_ci	return !!(wcaps & AC_WCAP_STEREO);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void print_amp_vals(struct snd_info_buffer *buffer,
13462306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid,
13562306a36Sopenharmony_ci			   int dir, unsigned int wcaps, int indices)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned int val;
13862306a36Sopenharmony_ci	bool stereo;
13962306a36Sopenharmony_ci	int i;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
14462306a36Sopenharmony_ci	for (i = 0; i < indices; i++) {
14562306a36Sopenharmony_ci		snd_iprintf(buffer, " [");
14662306a36Sopenharmony_ci		val = snd_hda_codec_read(codec, nid, 0,
14762306a36Sopenharmony_ci					 AC_VERB_GET_AMP_GAIN_MUTE,
14862306a36Sopenharmony_ci					 AC_AMP_GET_LEFT | dir | i);
14962306a36Sopenharmony_ci		snd_iprintf(buffer, "0x%02x", val);
15062306a36Sopenharmony_ci		if (stereo) {
15162306a36Sopenharmony_ci			val = snd_hda_codec_read(codec, nid, 0,
15262306a36Sopenharmony_ci						 AC_VERB_GET_AMP_GAIN_MUTE,
15362306a36Sopenharmony_ci						 AC_AMP_GET_RIGHT | dir | i);
15462306a36Sopenharmony_ci			snd_iprintf(buffer, " 0x%02x", val);
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci		snd_iprintf(buffer, "]");
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	static const unsigned int rates[] = {
16462306a36Sopenharmony_ci		8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
16562306a36Sopenharmony_ci		96000, 176400, 192000, 384000
16662306a36Sopenharmony_ci	};
16762306a36Sopenharmony_ci	int i;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	pcm &= AC_SUPPCM_RATES;
17062306a36Sopenharmony_ci	snd_iprintf(buffer, "    rates [0x%x]:", pcm);
17162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rates); i++)
17262306a36Sopenharmony_ci		if (pcm & (1 << i))
17362306a36Sopenharmony_ci			snd_iprintf(buffer,  " %d", rates[i]);
17462306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	snd_iprintf(buffer, "    bits [0x%x]:", (pcm >> 16) & 0xff);
18262306a36Sopenharmony_ci	snd_print_pcm_bits(pcm, buf, sizeof(buf));
18362306a36Sopenharmony_ci	snd_iprintf(buffer, "%s\n", buf);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void print_pcm_formats(struct snd_info_buffer *buffer,
18762306a36Sopenharmony_ci			      unsigned int streams)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	snd_iprintf(buffer, "    formats [0x%x]:", streams & 0xf);
19062306a36Sopenharmony_ci	if (streams & AC_SUPFMT_PCM)
19162306a36Sopenharmony_ci		snd_iprintf(buffer, " PCM");
19262306a36Sopenharmony_ci	if (streams & AC_SUPFMT_FLOAT32)
19362306a36Sopenharmony_ci		snd_iprintf(buffer, " FLOAT");
19462306a36Sopenharmony_ci	if (streams & AC_SUPFMT_AC3)
19562306a36Sopenharmony_ci		snd_iprintf(buffer, " AC3");
19662306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void print_pcm_caps(struct snd_info_buffer *buffer,
20062306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
20362306a36Sopenharmony_ci	unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
20462306a36Sopenharmony_ci	if (pcm == -1 || stream == -1) {
20562306a36Sopenharmony_ci		snd_iprintf(buffer, "N/A\n");
20662306a36Sopenharmony_ci		return;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	print_pcm_rates(buffer, pcm);
20962306a36Sopenharmony_ci	print_pcm_bits(buffer, pcm);
21062306a36Sopenharmony_ci	print_pcm_formats(buffer, stream);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const char *get_jack_connection(u32 cfg)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	static const char * const names[16] = {
21662306a36Sopenharmony_ci		"Unknown", "1/8", "1/4", "ATAPI",
21762306a36Sopenharmony_ci		"RCA", "Optical","Digital", "Analog",
21862306a36Sopenharmony_ci		"DIN", "XLR", "RJ11", "Comb",
21962306a36Sopenharmony_ci		NULL, NULL, NULL, "Other"
22062306a36Sopenharmony_ci	};
22162306a36Sopenharmony_ci	cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
22262306a36Sopenharmony_ci	if (names[cfg])
22362306a36Sopenharmony_ci		return names[cfg];
22462306a36Sopenharmony_ci	else
22562306a36Sopenharmony_ci		return "UNKNOWN";
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic const char *get_jack_color(u32 cfg)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	static const char * const names[16] = {
23162306a36Sopenharmony_ci		"Unknown", "Black", "Grey", "Blue",
23262306a36Sopenharmony_ci		"Green", "Red", "Orange", "Yellow",
23362306a36Sopenharmony_ci		"Purple", "Pink", NULL, NULL,
23462306a36Sopenharmony_ci		NULL, NULL, "White", "Other",
23562306a36Sopenharmony_ci	};
23662306a36Sopenharmony_ci	cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
23762306a36Sopenharmony_ci	if (names[cfg])
23862306a36Sopenharmony_ci		return names[cfg];
23962306a36Sopenharmony_ci	else
24062306a36Sopenharmony_ci		return "UNKNOWN";
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/*
24462306a36Sopenharmony_ci * Parse the pin default config value and returns the string of the
24562306a36Sopenharmony_ci * jack location, e.g. "Rear", "Front", etc.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic const char *get_jack_location(u32 cfg)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	static const char * const bases[7] = {
25062306a36Sopenharmony_ci		"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
25162306a36Sopenharmony_ci	};
25262306a36Sopenharmony_ci	static const unsigned char specials_idx[] = {
25362306a36Sopenharmony_ci		0x07, 0x08,
25462306a36Sopenharmony_ci		0x17, 0x18, 0x19,
25562306a36Sopenharmony_ci		0x37, 0x38
25662306a36Sopenharmony_ci	};
25762306a36Sopenharmony_ci	static const char * const specials[] = {
25862306a36Sopenharmony_ci		"Rear Panel", "Drive Bar",
25962306a36Sopenharmony_ci		"Riser", "HDMI", "ATAPI",
26062306a36Sopenharmony_ci		"Mobile-In", "Mobile-Out"
26162306a36Sopenharmony_ci	};
26262306a36Sopenharmony_ci	int i;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
26562306a36Sopenharmony_ci	if ((cfg & 0x0f) < 7)
26662306a36Sopenharmony_ci		return bases[cfg & 0x0f];
26762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
26862306a36Sopenharmony_ci		if (cfg == specials_idx[i])
26962306a36Sopenharmony_ci			return specials[i];
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	return "UNKNOWN";
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci * Parse the pin default config value and returns the string of the
27662306a36Sopenharmony_ci * jack connectivity, i.e. external or internal connection.
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_cistatic const char *get_jack_connectivity(u32 cfg)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	static const char * const jack_locations[4] = {
28162306a36Sopenharmony_ci		"Ext", "Int", "Sep", "Oth"
28262306a36Sopenharmony_ci	};
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * Parse the pin default config value and returns the string of the
28962306a36Sopenharmony_ci * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic const char *get_jack_type(u32 cfg)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	static const char * const jack_types[16] = {
29462306a36Sopenharmony_ci		"Line Out", "Speaker", "HP Out", "CD",
29562306a36Sopenharmony_ci		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
29662306a36Sopenharmony_ci		"Line In", "Aux", "Mic", "Telephony",
29762306a36Sopenharmony_ci		"SPDIF In", "Digital In", "Reserved", "Other"
29862306a36Sopenharmony_ci	};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return jack_types[(cfg & AC_DEFCFG_DEVICE)
30162306a36Sopenharmony_ci				>> AC_DEFCFG_DEVICE_SHIFT];
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void print_pin_caps(struct snd_info_buffer *buffer,
30562306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid,
30662306a36Sopenharmony_ci			   int *supports_vref)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	static const char * const jack_conns[4] = {
30962306a36Sopenharmony_ci		"Jack", "N/A", "Fixed", "Both"
31062306a36Sopenharmony_ci	};
31162306a36Sopenharmony_ci	unsigned int caps, val;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	caps = param_read(codec, nid, AC_PAR_PIN_CAP);
31462306a36Sopenharmony_ci	snd_iprintf(buffer, "  Pincap 0x%08x:", caps);
31562306a36Sopenharmony_ci	if (caps & AC_PINCAP_IN)
31662306a36Sopenharmony_ci		snd_iprintf(buffer, " IN");
31762306a36Sopenharmony_ci	if (caps & AC_PINCAP_OUT)
31862306a36Sopenharmony_ci		snd_iprintf(buffer, " OUT");
31962306a36Sopenharmony_ci	if (caps & AC_PINCAP_HP_DRV)
32062306a36Sopenharmony_ci		snd_iprintf(buffer, " HP");
32162306a36Sopenharmony_ci	if (caps & AC_PINCAP_EAPD)
32262306a36Sopenharmony_ci		snd_iprintf(buffer, " EAPD");
32362306a36Sopenharmony_ci	if (caps & AC_PINCAP_PRES_DETECT)
32462306a36Sopenharmony_ci		snd_iprintf(buffer, " Detect");
32562306a36Sopenharmony_ci	if (caps & AC_PINCAP_BALANCE)
32662306a36Sopenharmony_ci		snd_iprintf(buffer, " Balanced");
32762306a36Sopenharmony_ci	if (caps & AC_PINCAP_HDMI) {
32862306a36Sopenharmony_ci		/* Realtek uses this bit as a different meaning */
32962306a36Sopenharmony_ci		if ((codec->core.vendor_id >> 16) == 0x10ec)
33062306a36Sopenharmony_ci			snd_iprintf(buffer, " R/L");
33162306a36Sopenharmony_ci		else {
33262306a36Sopenharmony_ci			if (caps & AC_PINCAP_HBR)
33362306a36Sopenharmony_ci				snd_iprintf(buffer, " HBR");
33462306a36Sopenharmony_ci			snd_iprintf(buffer, " HDMI");
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	if (caps & AC_PINCAP_DP)
33862306a36Sopenharmony_ci		snd_iprintf(buffer, " DP");
33962306a36Sopenharmony_ci	if (caps & AC_PINCAP_TRIG_REQ)
34062306a36Sopenharmony_ci		snd_iprintf(buffer, " Trigger");
34162306a36Sopenharmony_ci	if (caps & AC_PINCAP_IMP_SENSE)
34262306a36Sopenharmony_ci		snd_iprintf(buffer, " ImpSense");
34362306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
34462306a36Sopenharmony_ci	if (caps & AC_PINCAP_VREF) {
34562306a36Sopenharmony_ci		unsigned int vref =
34662306a36Sopenharmony_ci			(caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
34762306a36Sopenharmony_ci		snd_iprintf(buffer, "    Vref caps:");
34862306a36Sopenharmony_ci		if (vref & AC_PINCAP_VREF_HIZ)
34962306a36Sopenharmony_ci			snd_iprintf(buffer, " HIZ");
35062306a36Sopenharmony_ci		if (vref & AC_PINCAP_VREF_50)
35162306a36Sopenharmony_ci			snd_iprintf(buffer, " 50");
35262306a36Sopenharmony_ci		if (vref & AC_PINCAP_VREF_GRD)
35362306a36Sopenharmony_ci			snd_iprintf(buffer, " GRD");
35462306a36Sopenharmony_ci		if (vref & AC_PINCAP_VREF_80)
35562306a36Sopenharmony_ci			snd_iprintf(buffer, " 80");
35662306a36Sopenharmony_ci		if (vref & AC_PINCAP_VREF_100)
35762306a36Sopenharmony_ci			snd_iprintf(buffer, " 100");
35862306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
35962306a36Sopenharmony_ci		*supports_vref = 1;
36062306a36Sopenharmony_ci	} else
36162306a36Sopenharmony_ci		*supports_vref = 0;
36262306a36Sopenharmony_ci	if (caps & AC_PINCAP_EAPD) {
36362306a36Sopenharmony_ci		val = snd_hda_codec_read(codec, nid, 0,
36462306a36Sopenharmony_ci					 AC_VERB_GET_EAPD_BTLENABLE, 0);
36562306a36Sopenharmony_ci		snd_iprintf(buffer, "  EAPD 0x%x:", val);
36662306a36Sopenharmony_ci		if (val & AC_EAPDBTL_BALANCED)
36762306a36Sopenharmony_ci			snd_iprintf(buffer, " BALANCED");
36862306a36Sopenharmony_ci		if (val & AC_EAPDBTL_EAPD)
36962306a36Sopenharmony_ci			snd_iprintf(buffer, " EAPD");
37062306a36Sopenharmony_ci		if (val & AC_EAPDBTL_LR_SWAP)
37162306a36Sopenharmony_ci			snd_iprintf(buffer, " R/L");
37262306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
37562306a36Sopenharmony_ci	snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
37662306a36Sopenharmony_ci		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
37762306a36Sopenharmony_ci		    get_jack_type(caps),
37862306a36Sopenharmony_ci		    get_jack_connectivity(caps),
37962306a36Sopenharmony_ci		    get_jack_location(caps));
38062306a36Sopenharmony_ci	snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
38162306a36Sopenharmony_ci		    get_jack_connection(caps),
38262306a36Sopenharmony_ci		    get_jack_color(caps));
38362306a36Sopenharmony_ci	/* Default association and sequence values refer to default grouping
38462306a36Sopenharmony_ci	 * of pin complexes and their sequence within the group. This is used
38562306a36Sopenharmony_ci	 * for priority and resource allocation.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
38862306a36Sopenharmony_ci		    (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
38962306a36Sopenharmony_ci		    caps & AC_DEFCFG_SEQUENCE);
39062306a36Sopenharmony_ci	if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
39162306a36Sopenharmony_ci	    AC_DEFCFG_MISC_NO_PRESENCE) {
39262306a36Sopenharmony_ci		/* Miscellaneous bit indicates external hardware does not
39362306a36Sopenharmony_ci		 * support presence detection even if the pin complex
39462306a36Sopenharmony_ci		 * indicates it is supported.
39562306a36Sopenharmony_ci		 */
39662306a36Sopenharmony_ci		snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void print_pin_ctls(struct snd_info_buffer *buffer,
40162306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid,
40262306a36Sopenharmony_ci			   int supports_vref)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned int pinctls;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	pinctls = snd_hda_codec_read(codec, nid, 0,
40762306a36Sopenharmony_ci				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
40862306a36Sopenharmony_ci	snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
40962306a36Sopenharmony_ci	if (pinctls & AC_PINCTL_IN_EN)
41062306a36Sopenharmony_ci		snd_iprintf(buffer, " IN");
41162306a36Sopenharmony_ci	if (pinctls & AC_PINCTL_OUT_EN)
41262306a36Sopenharmony_ci		snd_iprintf(buffer, " OUT");
41362306a36Sopenharmony_ci	if (pinctls & AC_PINCTL_HP_EN)
41462306a36Sopenharmony_ci		snd_iprintf(buffer, " HP");
41562306a36Sopenharmony_ci	if (supports_vref) {
41662306a36Sopenharmony_ci		int vref = pinctls & AC_PINCTL_VREFEN;
41762306a36Sopenharmony_ci		switch (vref) {
41862306a36Sopenharmony_ci		case AC_PINCTL_VREF_HIZ:
41962306a36Sopenharmony_ci			snd_iprintf(buffer, " VREF_HIZ");
42062306a36Sopenharmony_ci			break;
42162306a36Sopenharmony_ci		case AC_PINCTL_VREF_50:
42262306a36Sopenharmony_ci			snd_iprintf(buffer, " VREF_50");
42362306a36Sopenharmony_ci			break;
42462306a36Sopenharmony_ci		case AC_PINCTL_VREF_GRD:
42562306a36Sopenharmony_ci			snd_iprintf(buffer, " VREF_GRD");
42662306a36Sopenharmony_ci			break;
42762306a36Sopenharmony_ci		case AC_PINCTL_VREF_80:
42862306a36Sopenharmony_ci			snd_iprintf(buffer, " VREF_80");
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci		case AC_PINCTL_VREF_100:
43162306a36Sopenharmony_ci			snd_iprintf(buffer, " VREF_100");
43262306a36Sopenharmony_ci			break;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void print_vol_knob(struct snd_info_buffer *buffer,
43962306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
44262306a36Sopenharmony_ci	snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
44362306a36Sopenharmony_ci		    (cap >> 7) & 1, cap & 0x7f);
44462306a36Sopenharmony_ci	cap = snd_hda_codec_read(codec, nid, 0,
44562306a36Sopenharmony_ci				 AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
44662306a36Sopenharmony_ci	snd_iprintf(buffer, "direct=%d, val=%d\n",
44762306a36Sopenharmony_ci		    (cap >> 7) & 1, cap & 0x7f);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void print_audio_io(struct snd_info_buffer *buffer,
45162306a36Sopenharmony_ci			   struct hda_codec *codec, hda_nid_t nid,
45262306a36Sopenharmony_ci			   unsigned int wid_type)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
45562306a36Sopenharmony_ci	snd_iprintf(buffer,
45662306a36Sopenharmony_ci		    "  Converter: stream=%d, channel=%d\n",
45762306a36Sopenharmony_ci		    (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
45862306a36Sopenharmony_ci		    conv & AC_CONV_CHANNEL);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
46162306a36Sopenharmony_ci		int sdi = snd_hda_codec_read(codec, nid, 0,
46262306a36Sopenharmony_ci					     AC_VERB_GET_SDI_SELECT, 0);
46362306a36Sopenharmony_ci		snd_iprintf(buffer, "  SDI-Select: %d\n",
46462306a36Sopenharmony_ci			    sdi & AC_SDI_SELECT);
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic void print_digital_conv(struct snd_info_buffer *buffer,
46962306a36Sopenharmony_ci			       struct hda_codec *codec, hda_nid_t nid)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
47262306a36Sopenharmony_ci						AC_VERB_GET_DIGI_CONVERT_1, 0);
47362306a36Sopenharmony_ci	unsigned char digi2 = digi1 >> 8;
47462306a36Sopenharmony_ci	unsigned char digi3 = digi1 >> 16;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	snd_iprintf(buffer, "  Digital:");
47762306a36Sopenharmony_ci	if (digi1 & AC_DIG1_ENABLE)
47862306a36Sopenharmony_ci		snd_iprintf(buffer, " Enabled");
47962306a36Sopenharmony_ci	if (digi1 & AC_DIG1_V)
48062306a36Sopenharmony_ci		snd_iprintf(buffer, " Validity");
48162306a36Sopenharmony_ci	if (digi1 & AC_DIG1_VCFG)
48262306a36Sopenharmony_ci		snd_iprintf(buffer, " ValidityCfg");
48362306a36Sopenharmony_ci	if (digi1 & AC_DIG1_EMPHASIS)
48462306a36Sopenharmony_ci		snd_iprintf(buffer, " Preemphasis");
48562306a36Sopenharmony_ci	if (digi1 & AC_DIG1_COPYRIGHT)
48662306a36Sopenharmony_ci		snd_iprintf(buffer, " Non-Copyright");
48762306a36Sopenharmony_ci	if (digi1 & AC_DIG1_NONAUDIO)
48862306a36Sopenharmony_ci		snd_iprintf(buffer, " Non-Audio");
48962306a36Sopenharmony_ci	if (digi1 & AC_DIG1_PROFESSIONAL)
49062306a36Sopenharmony_ci		snd_iprintf(buffer, " Pro");
49162306a36Sopenharmony_ci	if (digi1 & AC_DIG1_LEVEL)
49262306a36Sopenharmony_ci		snd_iprintf(buffer, " GenLevel");
49362306a36Sopenharmony_ci	if (digi3 & AC_DIG3_KAE)
49462306a36Sopenharmony_ci		snd_iprintf(buffer, " KAE");
49562306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
49662306a36Sopenharmony_ci	snd_iprintf(buffer, "  Digital category: 0x%x\n",
49762306a36Sopenharmony_ci		    digi2 & AC_DIG2_CC);
49862306a36Sopenharmony_ci	snd_iprintf(buffer, "  IEC Coding Type: 0x%x\n",
49962306a36Sopenharmony_ci			digi3 & AC_DIG3_ICT);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic const char *get_pwr_state(u32 state)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	static const char * const buf[] = {
50562306a36Sopenharmony_ci		"D0", "D1", "D2", "D3", "D3cold"
50662306a36Sopenharmony_ci	};
50762306a36Sopenharmony_ci	if (state < ARRAY_SIZE(buf))
50862306a36Sopenharmony_ci		return buf[state];
50962306a36Sopenharmony_ci	return "UNKNOWN";
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic void print_power_state(struct snd_info_buffer *buffer,
51362306a36Sopenharmony_ci			      struct hda_codec *codec, hda_nid_t nid)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	static const char * const names[] = {
51662306a36Sopenharmony_ci		[ilog2(AC_PWRST_D0SUP)]		= "D0",
51762306a36Sopenharmony_ci		[ilog2(AC_PWRST_D1SUP)]		= "D1",
51862306a36Sopenharmony_ci		[ilog2(AC_PWRST_D2SUP)]		= "D2",
51962306a36Sopenharmony_ci		[ilog2(AC_PWRST_D3SUP)]		= "D3",
52062306a36Sopenharmony_ci		[ilog2(AC_PWRST_D3COLDSUP)]	= "D3cold",
52162306a36Sopenharmony_ci		[ilog2(AC_PWRST_S3D3COLDSUP)]	= "S3D3cold",
52262306a36Sopenharmony_ci		[ilog2(AC_PWRST_CLKSTOP)]	= "CLKSTOP",
52362306a36Sopenharmony_ci		[ilog2(AC_PWRST_EPSS)]		= "EPSS",
52462306a36Sopenharmony_ci	};
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
52762306a36Sopenharmony_ci	int pwr = snd_hda_codec_read(codec, nid, 0,
52862306a36Sopenharmony_ci				     AC_VERB_GET_POWER_STATE, 0);
52962306a36Sopenharmony_ci	if (sup != -1) {
53062306a36Sopenharmony_ci		int i;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		snd_iprintf(buffer, "  Power states: ");
53362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(names); i++) {
53462306a36Sopenharmony_ci			if (sup & (1U << i))
53562306a36Sopenharmony_ci				snd_iprintf(buffer, " %s", names[i]);
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	snd_iprintf(buffer, "  Power: setting=%s, actual=%s",
54162306a36Sopenharmony_ci		    get_pwr_state(pwr & AC_PWRST_SETTING),
54262306a36Sopenharmony_ci		    get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
54362306a36Sopenharmony_ci				  AC_PWRST_ACTUAL_SHIFT));
54462306a36Sopenharmony_ci	if (pwr & AC_PWRST_ERROR)
54562306a36Sopenharmony_ci		snd_iprintf(buffer, ", Error");
54662306a36Sopenharmony_ci	if (pwr & AC_PWRST_CLK_STOP_OK)
54762306a36Sopenharmony_ci		snd_iprintf(buffer, ", Clock-stop-OK");
54862306a36Sopenharmony_ci	if (pwr & AC_PWRST_SETTING_RESET)
54962306a36Sopenharmony_ci		snd_iprintf(buffer, ", Setting-reset");
55062306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic void print_unsol_cap(struct snd_info_buffer *buffer,
55462306a36Sopenharmony_ci			      struct hda_codec *codec, hda_nid_t nid)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	int unsol = snd_hda_codec_read(codec, nid, 0,
55762306a36Sopenharmony_ci				       AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
55862306a36Sopenharmony_ci	snd_iprintf(buffer,
55962306a36Sopenharmony_ci		    "  Unsolicited: tag=%02x, enabled=%d\n",
56062306a36Sopenharmony_ci		    unsol & AC_UNSOL_TAG,
56162306a36Sopenharmony_ci		    (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic inline bool can_dump_coef(struct hda_codec *codec)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	switch (dump_coef) {
56762306a36Sopenharmony_ci	case 0: return false;
56862306a36Sopenharmony_ci	case 1: return true;
56962306a36Sopenharmony_ci	default: return codec->dump_coef;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void print_proc_caps(struct snd_info_buffer *buffer,
57462306a36Sopenharmony_ci			    struct hda_codec *codec, hda_nid_t nid)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	unsigned int i, ncoeff, oldindex;
57762306a36Sopenharmony_ci	unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
57862306a36Sopenharmony_ci	ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
57962306a36Sopenharmony_ci	snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
58062306a36Sopenharmony_ci		    proc_caps & AC_PCAP_BENIGN, ncoeff);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (!can_dump_coef(codec))
58362306a36Sopenharmony_ci		return;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* Note: This is racy - another process could run in parallel and change
58662306a36Sopenharmony_ci	   the coef index too. */
58762306a36Sopenharmony_ci	oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
58862306a36Sopenharmony_ci	for (i = 0; i < ncoeff; i++) {
58962306a36Sopenharmony_ci		unsigned int val;
59062306a36Sopenharmony_ci		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
59162306a36Sopenharmony_ci		val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
59262306a36Sopenharmony_ci					 0);
59362306a36Sopenharmony_ci		snd_iprintf(buffer, "    Coeff 0x%02x: 0x%04x\n", i, val);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic void print_conn_list(struct snd_info_buffer *buffer,
59962306a36Sopenharmony_ci			    struct hda_codec *codec, hda_nid_t nid,
60062306a36Sopenharmony_ci			    unsigned int wid_type, hda_nid_t *conn,
60162306a36Sopenharmony_ci			    int conn_len)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	int c, curr = -1;
60462306a36Sopenharmony_ci	const hda_nid_t *list;
60562306a36Sopenharmony_ci	int cache_len;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (conn_len > 1 &&
60862306a36Sopenharmony_ci	    wid_type != AC_WID_AUD_MIX &&
60962306a36Sopenharmony_ci	    wid_type != AC_WID_VOL_KNB &&
61062306a36Sopenharmony_ci	    wid_type != AC_WID_POWER)
61162306a36Sopenharmony_ci		curr = snd_hda_codec_read(codec, nid, 0,
61262306a36Sopenharmony_ci					  AC_VERB_GET_CONNECT_SEL, 0);
61362306a36Sopenharmony_ci	snd_iprintf(buffer, "  Connection: %d\n", conn_len);
61462306a36Sopenharmony_ci	if (conn_len > 0) {
61562306a36Sopenharmony_ci		snd_iprintf(buffer, "    ");
61662306a36Sopenharmony_ci		for (c = 0; c < conn_len; c++) {
61762306a36Sopenharmony_ci			snd_iprintf(buffer, " 0x%02x", conn[c]);
61862306a36Sopenharmony_ci			if (c == curr)
61962306a36Sopenharmony_ci				snd_iprintf(buffer, "*");
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* Get Cache connections info */
62562306a36Sopenharmony_ci	cache_len = snd_hda_get_conn_list(codec, nid, &list);
62662306a36Sopenharmony_ci	if (cache_len >= 0 && (cache_len != conn_len ||
62762306a36Sopenharmony_ci			      memcmp(list, conn, conn_len) != 0)) {
62862306a36Sopenharmony_ci		snd_iprintf(buffer, "  In-driver Connection: %d\n", cache_len);
62962306a36Sopenharmony_ci		if (cache_len > 0) {
63062306a36Sopenharmony_ci			snd_iprintf(buffer, "    ");
63162306a36Sopenharmony_ci			for (c = 0; c < cache_len; c++)
63262306a36Sopenharmony_ci				snd_iprintf(buffer, " 0x%02x", list[c]);
63362306a36Sopenharmony_ci			snd_iprintf(buffer, "\n");
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic void print_gpio(struct snd_info_buffer *buffer,
63962306a36Sopenharmony_ci		       struct hda_codec *codec, hda_nid_t nid)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	unsigned int gpio =
64262306a36Sopenharmony_ci		param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
64362306a36Sopenharmony_ci	unsigned int enable, direction, wake, unsol, sticky, data;
64462306a36Sopenharmony_ci	int i, max;
64562306a36Sopenharmony_ci	snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
64662306a36Sopenharmony_ci		    "unsolicited=%d, wake=%d\n",
64762306a36Sopenharmony_ci		    gpio & AC_GPIO_IO_COUNT,
64862306a36Sopenharmony_ci		    (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
64962306a36Sopenharmony_ci		    (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
65062306a36Sopenharmony_ci		    (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
65162306a36Sopenharmony_ci		    (gpio & AC_GPIO_WAKE) ? 1 : 0);
65262306a36Sopenharmony_ci	max = gpio & AC_GPIO_IO_COUNT;
65362306a36Sopenharmony_ci	if (!max || max > 8)
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci	enable = snd_hda_codec_read(codec, nid, 0,
65662306a36Sopenharmony_ci				    AC_VERB_GET_GPIO_MASK, 0);
65762306a36Sopenharmony_ci	direction = snd_hda_codec_read(codec, nid, 0,
65862306a36Sopenharmony_ci				       AC_VERB_GET_GPIO_DIRECTION, 0);
65962306a36Sopenharmony_ci	wake = snd_hda_codec_read(codec, nid, 0,
66062306a36Sopenharmony_ci				  AC_VERB_GET_GPIO_WAKE_MASK, 0);
66162306a36Sopenharmony_ci	unsol  = snd_hda_codec_read(codec, nid, 0,
66262306a36Sopenharmony_ci				    AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
66362306a36Sopenharmony_ci	sticky = snd_hda_codec_read(codec, nid, 0,
66462306a36Sopenharmony_ci				    AC_VERB_GET_GPIO_STICKY_MASK, 0);
66562306a36Sopenharmony_ci	data = snd_hda_codec_read(codec, nid, 0,
66662306a36Sopenharmony_ci				  AC_VERB_GET_GPIO_DATA, 0);
66762306a36Sopenharmony_ci	for (i = 0; i < max; ++i)
66862306a36Sopenharmony_ci		snd_iprintf(buffer,
66962306a36Sopenharmony_ci			    "  IO[%d]: enable=%d, dir=%d, wake=%d, "
67062306a36Sopenharmony_ci			    "sticky=%d, data=%d, unsol=%d\n", i,
67162306a36Sopenharmony_ci			    (enable & (1<<i)) ? 1 : 0,
67262306a36Sopenharmony_ci			    (direction & (1<<i)) ? 1 : 0,
67362306a36Sopenharmony_ci			    (wake & (1<<i)) ? 1 : 0,
67462306a36Sopenharmony_ci			    (sticky & (1<<i)) ? 1 : 0,
67562306a36Sopenharmony_ci			    (data & (1<<i)) ? 1 : 0,
67662306a36Sopenharmony_ci			    (unsol & (1<<i)) ? 1 : 0);
67762306a36Sopenharmony_ci	/* FIXME: add GPO and GPI pin information */
67862306a36Sopenharmony_ci	print_nid_array(buffer, codec, nid, &codec->mixers);
67962306a36Sopenharmony_ci	print_nid_array(buffer, codec, nid, &codec->nids);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
68362306a36Sopenharmony_ci				    hda_nid_t nid, int dev_num)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	int c, conn_len, curr, dev_id_saved;
68662306a36Sopenharmony_ci	hda_nid_t *conn;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	conn_len = snd_hda_get_num_raw_conns(codec, nid);
68962306a36Sopenharmony_ci	if (conn_len <= 0)
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
69362306a36Sopenharmony_ci	if (!conn)
69462306a36Sopenharmony_ci		return;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	dev_id_saved = snd_hda_get_dev_select(codec, nid);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	snd_hda_set_dev_select(codec, nid, dev_num);
69962306a36Sopenharmony_ci	curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
70062306a36Sopenharmony_ci	if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
70162306a36Sopenharmony_ci		goto out;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	for (c = 0; c < conn_len; c++) {
70462306a36Sopenharmony_ci		snd_iprintf(buffer, " 0x%02x", conn[c]);
70562306a36Sopenharmony_ci		if (c == curr)
70662306a36Sopenharmony_ci			snd_iprintf(buffer, "*");
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ciout:
71062306a36Sopenharmony_ci	kfree(conn);
71162306a36Sopenharmony_ci	snd_hda_set_dev_select(codec, nid, dev_id_saved);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic void print_device_list(struct snd_info_buffer *buffer,
71562306a36Sopenharmony_ci			    struct hda_codec *codec, hda_nid_t nid)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	int i, curr = -1;
71862306a36Sopenharmony_ci	u8 dev_list[AC_MAX_DEV_LIST_LEN];
71962306a36Sopenharmony_ci	int devlist_len;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	devlist_len = snd_hda_get_devices(codec, nid, dev_list,
72262306a36Sopenharmony_ci					AC_MAX_DEV_LIST_LEN);
72362306a36Sopenharmony_ci	snd_iprintf(buffer, "  Devices: %d\n", devlist_len);
72462306a36Sopenharmony_ci	if (devlist_len <= 0)
72562306a36Sopenharmony_ci		return;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	curr = snd_hda_codec_read(codec, nid, 0,
72862306a36Sopenharmony_ci				AC_VERB_GET_DEVICE_SEL, 0);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	for (i = 0; i < devlist_len; i++) {
73162306a36Sopenharmony_ci		if (i == curr)
73262306a36Sopenharmony_ci			snd_iprintf(buffer, "    *");
73362306a36Sopenharmony_ci		else
73462306a36Sopenharmony_ci			snd_iprintf(buffer, "     ");
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		snd_iprintf(buffer,
73762306a36Sopenharmony_ci			"Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
73862306a36Sopenharmony_ci			!!(dev_list[i] & AC_DE_PD),
73962306a36Sopenharmony_ci			!!(dev_list[i] & AC_DE_ELDV),
74062306a36Sopenharmony_ci			!!(dev_list[i] & AC_DE_IA));
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		print_dpmst_connections(buffer, codec, nid, i);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		snd_iprintf(buffer, " ]\n");
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic void print_codec_core_info(struct hdac_device *codec,
74962306a36Sopenharmony_ci				  struct snd_info_buffer *buffer)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	snd_iprintf(buffer, "Codec: ");
75262306a36Sopenharmony_ci	if (codec->vendor_name && codec->chip_name)
75362306a36Sopenharmony_ci		snd_iprintf(buffer, "%s %s\n",
75462306a36Sopenharmony_ci			    codec->vendor_name, codec->chip_name);
75562306a36Sopenharmony_ci	else
75662306a36Sopenharmony_ci		snd_iprintf(buffer, "Not Set\n");
75762306a36Sopenharmony_ci	snd_iprintf(buffer, "Address: %d\n", codec->addr);
75862306a36Sopenharmony_ci	if (codec->afg)
75962306a36Sopenharmony_ci		snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
76062306a36Sopenharmony_ci			codec->afg_function_id, codec->afg_unsol);
76162306a36Sopenharmony_ci	if (codec->mfg)
76262306a36Sopenharmony_ci		snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
76362306a36Sopenharmony_ci			codec->mfg_function_id, codec->mfg_unsol);
76462306a36Sopenharmony_ci	snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
76562306a36Sopenharmony_ci	snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
76662306a36Sopenharmony_ci	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (codec->mfg)
76962306a36Sopenharmony_ci		snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
77062306a36Sopenharmony_ci	else
77162306a36Sopenharmony_ci		snd_iprintf(buffer, "No Modem Function Group found\n");
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic void print_codec_info(struct snd_info_entry *entry,
77562306a36Sopenharmony_ci			     struct snd_info_buffer *buffer)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct hda_codec *codec = entry->private_data;
77862306a36Sopenharmony_ci	hda_nid_t nid, fg;
77962306a36Sopenharmony_ci	int i, nodes;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	print_codec_core_info(&codec->core, buffer);
78262306a36Sopenharmony_ci	fg = codec->core.afg;
78362306a36Sopenharmony_ci	if (!fg)
78462306a36Sopenharmony_ci		return;
78562306a36Sopenharmony_ci	snd_hda_power_up(codec);
78662306a36Sopenharmony_ci	snd_iprintf(buffer, "Default PCM:\n");
78762306a36Sopenharmony_ci	print_pcm_caps(buffer, codec, fg);
78862306a36Sopenharmony_ci	snd_iprintf(buffer, "Default Amp-In caps: ");
78962306a36Sopenharmony_ci	print_amp_caps(buffer, codec, fg, HDA_INPUT);
79062306a36Sopenharmony_ci	snd_iprintf(buffer, "Default Amp-Out caps: ");
79162306a36Sopenharmony_ci	print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
79262306a36Sopenharmony_ci	snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
79362306a36Sopenharmony_ci	print_power_state(buffer, codec, fg);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
79662306a36Sopenharmony_ci	if (! nid || nodes < 0) {
79762306a36Sopenharmony_ci		snd_iprintf(buffer, "Invalid AFG subtree\n");
79862306a36Sopenharmony_ci		snd_hda_power_down(codec);
79962306a36Sopenharmony_ci		return;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	print_gpio(buffer, codec, fg);
80362306a36Sopenharmony_ci	if (codec->proc_widget_hook)
80462306a36Sopenharmony_ci		codec->proc_widget_hook(buffer, codec, fg);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	for (i = 0; i < nodes; i++, nid++) {
80762306a36Sopenharmony_ci		unsigned int wid_caps =
80862306a36Sopenharmony_ci			param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
80962306a36Sopenharmony_ci		unsigned int wid_type = get_wcaps_type(wid_caps);
81062306a36Sopenharmony_ci		hda_nid_t *conn = NULL;
81162306a36Sopenharmony_ci		int conn_len = 0;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
81462306a36Sopenharmony_ci			    get_wid_type_name(wid_type), wid_caps);
81562306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_STEREO) {
81662306a36Sopenharmony_ci			unsigned int chans = get_wcaps_channels(wid_caps);
81762306a36Sopenharmony_ci			if (chans == 2)
81862306a36Sopenharmony_ci				snd_iprintf(buffer, " Stereo");
81962306a36Sopenharmony_ci			else
82062306a36Sopenharmony_ci				snd_iprintf(buffer, " %d-Channels", chans);
82162306a36Sopenharmony_ci		} else
82262306a36Sopenharmony_ci			snd_iprintf(buffer, " Mono");
82362306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_DIGITAL)
82462306a36Sopenharmony_ci			snd_iprintf(buffer, " Digital");
82562306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_IN_AMP)
82662306a36Sopenharmony_ci			snd_iprintf(buffer, " Amp-In");
82762306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_OUT_AMP)
82862306a36Sopenharmony_ci			snd_iprintf(buffer, " Amp-Out");
82962306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_STRIPE)
83062306a36Sopenharmony_ci			snd_iprintf(buffer, " Stripe");
83162306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_LR_SWAP)
83262306a36Sopenharmony_ci			snd_iprintf(buffer, " R/L");
83362306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_CP_CAPS)
83462306a36Sopenharmony_ci			snd_iprintf(buffer, " CP");
83562306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		print_nid_array(buffer, codec, nid, &codec->mixers);
83862306a36Sopenharmony_ci		print_nid_array(buffer, codec, nid, &codec->nids);
83962306a36Sopenharmony_ci		print_nid_pcms(buffer, codec, nid);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		/* volume knob is a special widget that always have connection
84262306a36Sopenharmony_ci		 * list
84362306a36Sopenharmony_ci		 */
84462306a36Sopenharmony_ci		if (wid_type == AC_WID_VOL_KNB)
84562306a36Sopenharmony_ci			wid_caps |= AC_WCAP_CONN_LIST;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_CONN_LIST) {
84862306a36Sopenharmony_ci			conn_len = snd_hda_get_num_raw_conns(codec, nid);
84962306a36Sopenharmony_ci			if (conn_len > 0) {
85062306a36Sopenharmony_ci				conn = kmalloc_array(conn_len,
85162306a36Sopenharmony_ci						     sizeof(hda_nid_t),
85262306a36Sopenharmony_ci						     GFP_KERNEL);
85362306a36Sopenharmony_ci				if (!conn)
85462306a36Sopenharmony_ci					return;
85562306a36Sopenharmony_ci				if (snd_hda_get_raw_connections(codec, nid, conn,
85662306a36Sopenharmony_ci								conn_len) < 0)
85762306a36Sopenharmony_ci					conn_len = 0;
85862306a36Sopenharmony_ci			}
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_IN_AMP) {
86262306a36Sopenharmony_ci			snd_iprintf(buffer, "  Amp-In caps: ");
86362306a36Sopenharmony_ci			print_amp_caps(buffer, codec, nid, HDA_INPUT);
86462306a36Sopenharmony_ci			snd_iprintf(buffer, "  Amp-In vals: ");
86562306a36Sopenharmony_ci			if (wid_type == AC_WID_PIN ||
86662306a36Sopenharmony_ci			    (codec->single_adc_amp &&
86762306a36Sopenharmony_ci			     wid_type == AC_WID_AUD_IN))
86862306a36Sopenharmony_ci				print_amp_vals(buffer, codec, nid, HDA_INPUT,
86962306a36Sopenharmony_ci					       wid_caps, 1);
87062306a36Sopenharmony_ci			else
87162306a36Sopenharmony_ci				print_amp_vals(buffer, codec, nid, HDA_INPUT,
87262306a36Sopenharmony_ci					       wid_caps, conn_len);
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_OUT_AMP) {
87562306a36Sopenharmony_ci			snd_iprintf(buffer, "  Amp-Out caps: ");
87662306a36Sopenharmony_ci			print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
87762306a36Sopenharmony_ci			snd_iprintf(buffer, "  Amp-Out vals: ");
87862306a36Sopenharmony_ci			if (wid_type == AC_WID_PIN &&
87962306a36Sopenharmony_ci			    codec->pin_amp_workaround)
88062306a36Sopenharmony_ci				print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
88162306a36Sopenharmony_ci					       wid_caps, conn_len);
88262306a36Sopenharmony_ci			else
88362306a36Sopenharmony_ci				print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
88462306a36Sopenharmony_ci					       wid_caps, 1);
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		switch (wid_type) {
88862306a36Sopenharmony_ci		case AC_WID_PIN: {
88962306a36Sopenharmony_ci			int supports_vref;
89062306a36Sopenharmony_ci			print_pin_caps(buffer, codec, nid, &supports_vref);
89162306a36Sopenharmony_ci			print_pin_ctls(buffer, codec, nid, supports_vref);
89262306a36Sopenharmony_ci			break;
89362306a36Sopenharmony_ci		}
89462306a36Sopenharmony_ci		case AC_WID_VOL_KNB:
89562306a36Sopenharmony_ci			print_vol_knob(buffer, codec, nid);
89662306a36Sopenharmony_ci			break;
89762306a36Sopenharmony_ci		case AC_WID_AUD_OUT:
89862306a36Sopenharmony_ci		case AC_WID_AUD_IN:
89962306a36Sopenharmony_ci			print_audio_io(buffer, codec, nid, wid_type);
90062306a36Sopenharmony_ci			if (wid_caps & AC_WCAP_DIGITAL)
90162306a36Sopenharmony_ci				print_digital_conv(buffer, codec, nid);
90262306a36Sopenharmony_ci			if (wid_caps & AC_WCAP_FORMAT_OVRD) {
90362306a36Sopenharmony_ci				snd_iprintf(buffer, "  PCM:\n");
90462306a36Sopenharmony_ci				print_pcm_caps(buffer, codec, nid);
90562306a36Sopenharmony_ci			}
90662306a36Sopenharmony_ci			break;
90762306a36Sopenharmony_ci		}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_UNSOL_CAP)
91062306a36Sopenharmony_ci			print_unsol_cap(buffer, codec, nid);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_POWER)
91362306a36Sopenharmony_ci			print_power_state(buffer, codec, nid);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_DELAY)
91662306a36Sopenharmony_ci			snd_iprintf(buffer, "  Delay: %d samples\n",
91762306a36Sopenharmony_ci				    (wid_caps & AC_WCAP_DELAY) >>
91862306a36Sopenharmony_ci				    AC_WCAP_DELAY_SHIFT);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		if (wid_type == AC_WID_PIN && codec->dp_mst)
92162306a36Sopenharmony_ci			print_device_list(buffer, codec, nid);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_CONN_LIST)
92462306a36Sopenharmony_ci			print_conn_list(buffer, codec, nid, wid_type,
92562306a36Sopenharmony_ci					conn, conn_len);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		if (wid_caps & AC_WCAP_PROC_WID)
92862306a36Sopenharmony_ci			print_proc_caps(buffer, codec, nid);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		if (codec->proc_widget_hook)
93162306a36Sopenharmony_ci			codec->proc_widget_hook(buffer, codec, nid);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		kfree(conn);
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci	snd_hda_power_down(codec);
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci/*
93962306a36Sopenharmony_ci * create a proc read
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_ciint snd_hda_codec_proc_new(struct hda_codec *codec)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	char name[32];
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
94662306a36Sopenharmony_ci	return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
949