1d0fa1179SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Universal Interface for Intel High Definition Audio Codec
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Generic proc interface
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/init.h>
114eea3091STakashi Iwai #include <linux/slab.h>
121da177e4SLinus Torvalds #include <sound/core.h>
13cd262518SDavid Henningsson #include <linux/module.h>
14be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
1518612048SAdrian Bunk #include "hda_local.h"
161da177e4SLinus Torvalds
17cd262518SDavid Henningsson static int dump_coef = -1;
18cd262518SDavid Henningsson module_param(dump_coef, int, 0644);
19cd262518SDavid Henningsson MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
20cd262518SDavid Henningsson
219ba17b4dSTakashi Iwai /* always use noncached version */
229ba17b4dSTakashi Iwai #define param_read(codec, nid, parm) \
239ba17b4dSTakashi Iwai snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
249ba17b4dSTakashi Iwai
get_wid_type_name(unsigned int wid_value)251da177e4SLinus Torvalds static const char *get_wid_type_name(unsigned int wid_value)
261da177e4SLinus Torvalds {
27cc75cdfeSTakashi Iwai static const char * const names[16] = {
281da177e4SLinus Torvalds [AC_WID_AUD_OUT] = "Audio Output",
291da177e4SLinus Torvalds [AC_WID_AUD_IN] = "Audio Input",
301da177e4SLinus Torvalds [AC_WID_AUD_MIX] = "Audio Mixer",
311da177e4SLinus Torvalds [AC_WID_AUD_SEL] = "Audio Selector",
321da177e4SLinus Torvalds [AC_WID_PIN] = "Pin Complex",
331da177e4SLinus Torvalds [AC_WID_POWER] = "Power Widget",
341da177e4SLinus Torvalds [AC_WID_VOL_KNB] = "Volume Knob Widget",
351da177e4SLinus Torvalds [AC_WID_BEEP] = "Beep Generator Widget",
361da177e4SLinus Torvalds [AC_WID_VENDOR] = "Vendor Defined Widget",
371da177e4SLinus Torvalds };
383a90274dSTakashi Iwai if (wid_value == -1)
393a90274dSTakashi Iwai return "UNKNOWN Widget";
401da177e4SLinus Torvalds wid_value &= 0xf;
411da177e4SLinus Torvalds if (names[wid_value])
421da177e4SLinus Torvalds return names[wid_value];
431da177e4SLinus Torvalds else
443bc89529STakashi Iwai return "UNKNOWN Widget";
451da177e4SLinus Torvalds }
461da177e4SLinus Torvalds
print_nid_array(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,struct snd_array * array)475b0cb1d8SJaroslav Kysela static void print_nid_array(struct snd_info_buffer *buffer,
485b0cb1d8SJaroslav Kysela struct hda_codec *codec, hda_nid_t nid,
495b0cb1d8SJaroslav Kysela struct snd_array *array)
503911a4c1SJaroslav Kysela {
513911a4c1SJaroslav Kysela int i;
525b0cb1d8SJaroslav Kysela struct hda_nid_item *items = array->list, *item;
533911a4c1SJaroslav Kysela struct snd_kcontrol *kctl;
545b0cb1d8SJaroslav Kysela for (i = 0; i < array->used; i++) {
555b0cb1d8SJaroslav Kysela item = &items[i];
565b0cb1d8SJaroslav Kysela if (item->nid == nid) {
575b0cb1d8SJaroslav Kysela kctl = item->kctl;
583911a4c1SJaroslav Kysela snd_iprintf(buffer,
593911a4c1SJaroslav Kysela " Control: name=\"%s\", index=%i, device=%i\n",
605b0cb1d8SJaroslav Kysela kctl->id.name, kctl->id.index + item->index,
615b0cb1d8SJaroslav Kysela kctl->id.device);
629e3fd871SJaroslav Kysela if (item->flags & HDA_NID_ITEM_AMP)
639e3fd871SJaroslav Kysela snd_iprintf(buffer,
649e3fd871SJaroslav Kysela " ControlAmp: chs=%lu, dir=%s, "
659e3fd871SJaroslav Kysela "idx=%lu, ofs=%lu\n",
669e3fd871SJaroslav Kysela get_amp_channels(kctl),
679e3fd871SJaroslav Kysela get_amp_direction(kctl) ? "Out" : "In",
689e3fd871SJaroslav Kysela get_amp_index(kctl),
699e3fd871SJaroslav Kysela get_amp_offset(kctl));
703911a4c1SJaroslav Kysela }
713911a4c1SJaroslav Kysela }
723911a4c1SJaroslav Kysela }
733911a4c1SJaroslav Kysela
print_nid_pcms(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)743911a4c1SJaroslav Kysela static void print_nid_pcms(struct snd_info_buffer *buffer,
753911a4c1SJaroslav Kysela struct hda_codec *codec, hda_nid_t nid)
763911a4c1SJaroslav Kysela {
77bbbc7e85STakashi Iwai int type;
783911a4c1SJaroslav Kysela struct hda_pcm *cpcm;
79bbbc7e85STakashi Iwai
80bbbc7e85STakashi Iwai list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
813911a4c1SJaroslav Kysela for (type = 0; type < 2; type++) {
823911a4c1SJaroslav Kysela if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
833911a4c1SJaroslav Kysela continue;
843911a4c1SJaroslav Kysela snd_iprintf(buffer, " Device: name=\"%s\", "
853911a4c1SJaroslav Kysela "type=\"%s\", device=%i\n",
863911a4c1SJaroslav Kysela cpcm->name,
873911a4c1SJaroslav Kysela snd_hda_pcm_type_name[cpcm->pcm_type],
883911a4c1SJaroslav Kysela cpcm->pcm->device);
893911a4c1SJaroslav Kysela }
903911a4c1SJaroslav Kysela }
913911a4c1SJaroslav Kysela }
923911a4c1SJaroslav Kysela
print_amp_caps(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,int dir)93c8b6bf9bSTakashi Iwai static void print_amp_caps(struct snd_info_buffer *buffer,
941da177e4SLinus Torvalds struct hda_codec *codec, hda_nid_t nid, int dir)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds unsigned int caps;
979ba17b4dSTakashi Iwai caps = param_read(codec, nid, dir == HDA_OUTPUT ?
987f0e2f8bSJaroslav Kysela AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
991da177e4SLinus Torvalds if (caps == -1 || caps == 0) {
1001da177e4SLinus Torvalds snd_iprintf(buffer, "N/A\n");
1011da177e4SLinus Torvalds return;
1021da177e4SLinus Torvalds }
103d01ce99fSTakashi Iwai snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, "
104d01ce99fSTakashi Iwai "mute=%x\n",
1051da177e4SLinus Torvalds caps & AC_AMPCAP_OFFSET,
1061da177e4SLinus Torvalds (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
1071da177e4SLinus Torvalds (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
1081da177e4SLinus Torvalds (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds
111cc261738STakashi Iwai /* is this a stereo widget or a stereo-to-mono mix? */
is_stereo_amps(struct hda_codec * codec,hda_nid_t nid,int dir,unsigned int wcaps,int indices)112cc261738STakashi Iwai static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
113cc261738STakashi Iwai int dir, unsigned int wcaps, int indices)
114cc261738STakashi Iwai {
115cc261738STakashi Iwai hda_nid_t conn;
116cc261738STakashi Iwai
117cc261738STakashi Iwai if (wcaps & AC_WCAP_STEREO)
118cc261738STakashi Iwai return true;
119cc261738STakashi Iwai /* check for a stereo-to-mono mix; it must be:
120cc261738STakashi Iwai * only a single connection, only for input, and only a mixer widget
121cc261738STakashi Iwai */
122cc261738STakashi Iwai if (indices != 1 || dir != HDA_INPUT ||
123cc261738STakashi Iwai get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
124cc261738STakashi Iwai return false;
125cc261738STakashi Iwai
126cc261738STakashi Iwai if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
127cc261738STakashi Iwai return false;
128cc261738STakashi Iwai /* the connection source is a stereo? */
129cc261738STakashi Iwai wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
130cc261738STakashi Iwai return !!(wcaps & AC_WCAP_STEREO);
131cc261738STakashi Iwai }
132cc261738STakashi Iwai
print_amp_vals(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,int dir,unsigned int wcaps,int indices)133c8b6bf9bSTakashi Iwai static void print_amp_vals(struct snd_info_buffer *buffer,
1341da177e4SLinus Torvalds struct hda_codec *codec, hda_nid_t nid,
135cc261738STakashi Iwai int dir, unsigned int wcaps, int indices)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds unsigned int val;
138cc261738STakashi Iwai bool stereo;
1393e289f16STakashi Iwai int i;
1403e289f16STakashi Iwai
141cc261738STakashi Iwai stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
142cc261738STakashi Iwai
1437f0e2f8bSJaroslav Kysela dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
1443e289f16STakashi Iwai for (i = 0; i < indices; i++) {
1453e289f16STakashi Iwai snd_iprintf(buffer, " [");
146d01ce99fSTakashi Iwai val = snd_hda_codec_read(codec, nid, 0,
147d01ce99fSTakashi Iwai AC_VERB_GET_AMP_GAIN_MUTE,
1483e289f16STakashi Iwai AC_AMP_GET_LEFT | dir | i);
1491da177e4SLinus Torvalds snd_iprintf(buffer, "0x%02x", val);
1502f179721STakashi Iwai if (stereo) {
151d01ce99fSTakashi Iwai val = snd_hda_codec_read(codec, nid, 0,
152d01ce99fSTakashi Iwai AC_VERB_GET_AMP_GAIN_MUTE,
1533e289f16STakashi Iwai AC_AMP_GET_RIGHT | dir | i);
1542f179721STakashi Iwai snd_iprintf(buffer, " 0x%02x", val);
1552f179721STakashi Iwai }
1562f179721STakashi Iwai snd_iprintf(buffer, "]");
1573e289f16STakashi Iwai }
1583e289f16STakashi Iwai snd_iprintf(buffer, "\n");
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
print_pcm_rates(struct snd_info_buffer * buffer,unsigned int pcm)16133deeca3SWu Fengguang static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
16233deeca3SWu Fengguang {
163bf82326fSTakashi Iwai static const unsigned int rates[] = {
164f71ff0d7STakashi Iwai 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
165f71ff0d7STakashi Iwai 96000, 176400, 192000, 384000
166f71ff0d7STakashi Iwai };
167f71ff0d7STakashi Iwai int i;
168d39b4352SWu Fengguang
169b90d7760STakashi Iwai pcm &= AC_SUPPCM_RATES;
170b90d7760STakashi Iwai snd_iprintf(buffer, " rates [0x%x]:", pcm);
171f71ff0d7STakashi Iwai for (i = 0; i < ARRAY_SIZE(rates); i++)
172f71ff0d7STakashi Iwai if (pcm & (1 << i))
173f71ff0d7STakashi Iwai snd_iprintf(buffer, " %d", rates[i]);
174f71ff0d7STakashi Iwai snd_iprintf(buffer, "\n");
175b90d7760STakashi Iwai }
176b90d7760STakashi Iwai
print_pcm_bits(struct snd_info_buffer * buffer,unsigned int pcm)177d39b4352SWu Fengguang static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
178d39b4352SWu Fengguang {
179d39b4352SWu Fengguang char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
180b90d7760STakashi Iwai
181b0e6481aSTakashi Iwai snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff);
182d39b4352SWu Fengguang snd_print_pcm_bits(pcm, buf, sizeof(buf));
183d39b4352SWu Fengguang snd_iprintf(buffer, "%s\n", buf);
184b90d7760STakashi Iwai }
185b90d7760STakashi Iwai
print_pcm_formats(struct snd_info_buffer * buffer,unsigned int streams)186b90d7760STakashi Iwai static void print_pcm_formats(struct snd_info_buffer *buffer,
187b90d7760STakashi Iwai unsigned int streams)
188b90d7760STakashi Iwai {
189b90d7760STakashi Iwai snd_iprintf(buffer, " formats [0x%x]:", streams & 0xf);
190b90d7760STakashi Iwai if (streams & AC_SUPFMT_PCM)
191b90d7760STakashi Iwai snd_iprintf(buffer, " PCM");
192b90d7760STakashi Iwai if (streams & AC_SUPFMT_FLOAT32)
193b90d7760STakashi Iwai snd_iprintf(buffer, " FLOAT");
194b90d7760STakashi Iwai if (streams & AC_SUPFMT_AC3)
195b90d7760STakashi Iwai snd_iprintf(buffer, " AC3");
196b90d7760STakashi Iwai snd_iprintf(buffer, "\n");
197b90d7760STakashi Iwai }
198b90d7760STakashi Iwai
print_pcm_caps(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)199c8b6bf9bSTakashi Iwai static void print_pcm_caps(struct snd_info_buffer *buffer,
2001da177e4SLinus Torvalds struct hda_codec *codec, hda_nid_t nid)
2011da177e4SLinus Torvalds {
2029ba17b4dSTakashi Iwai unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
2039ba17b4dSTakashi Iwai unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
2041da177e4SLinus Torvalds if (pcm == -1 || stream == -1) {
2051da177e4SLinus Torvalds snd_iprintf(buffer, "N/A\n");
2061da177e4SLinus Torvalds return;
2071da177e4SLinus Torvalds }
208b90d7760STakashi Iwai print_pcm_rates(buffer, pcm);
209b90d7760STakashi Iwai print_pcm_bits(buffer, pcm);
210b90d7760STakashi Iwai print_pcm_formats(buffer, stream);
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds
get_jack_connection(u32 cfg)2131da177e4SLinus Torvalds static const char *get_jack_connection(u32 cfg)
2141da177e4SLinus Torvalds {
215cc75cdfeSTakashi Iwai static const char * const names[16] = {
2161da177e4SLinus Torvalds "Unknown", "1/8", "1/4", "ATAPI",
2171da177e4SLinus Torvalds "RCA", "Optical","Digital", "Analog",
2181da177e4SLinus Torvalds "DIN", "XLR", "RJ11", "Comb",
2191da177e4SLinus Torvalds NULL, NULL, NULL, "Other"
2201da177e4SLinus Torvalds };
2211da177e4SLinus Torvalds cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
2221da177e4SLinus Torvalds if (names[cfg])
2231da177e4SLinus Torvalds return names[cfg];
2241da177e4SLinus Torvalds else
2251da177e4SLinus Torvalds return "UNKNOWN";
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds
get_jack_color(u32 cfg)2281da177e4SLinus Torvalds static const char *get_jack_color(u32 cfg)
2291da177e4SLinus Torvalds {
230cc75cdfeSTakashi Iwai static const char * const names[16] = {
2311da177e4SLinus Torvalds "Unknown", "Black", "Grey", "Blue",
2321da177e4SLinus Torvalds "Green", "Red", "Orange", "Yellow",
2331da177e4SLinus Torvalds "Purple", "Pink", NULL, NULL,
2341da177e4SLinus Torvalds NULL, NULL, "White", "Other",
2351da177e4SLinus Torvalds };
2361da177e4SLinus Torvalds cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
2371da177e4SLinus Torvalds if (names[cfg])
2381da177e4SLinus Torvalds return names[cfg];
2391da177e4SLinus Torvalds else
2401da177e4SLinus Torvalds return "UNKNOWN";
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds
243d2c6b63dSTakashi Iwai /*
244d2c6b63dSTakashi Iwai * Parse the pin default config value and returns the string of the
245d2c6b63dSTakashi Iwai * jack location, e.g. "Rear", "Front", etc.
246d2c6b63dSTakashi Iwai */
get_jack_location(u32 cfg)247d2c6b63dSTakashi Iwai static const char *get_jack_location(u32 cfg)
248d2c6b63dSTakashi Iwai {
249cc75cdfeSTakashi Iwai static const char * const bases[7] = {
250d2c6b63dSTakashi Iwai "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
251d2c6b63dSTakashi Iwai };
252cc75cdfeSTakashi Iwai static const unsigned char specials_idx[] = {
253d2c6b63dSTakashi Iwai 0x07, 0x08,
254d2c6b63dSTakashi Iwai 0x17, 0x18, 0x19,
255d2c6b63dSTakashi Iwai 0x37, 0x38
256d2c6b63dSTakashi Iwai };
257cc75cdfeSTakashi Iwai static const char * const specials[] = {
258d2c6b63dSTakashi Iwai "Rear Panel", "Drive Bar",
259d2c6b63dSTakashi Iwai "Riser", "HDMI", "ATAPI",
260d2c6b63dSTakashi Iwai "Mobile-In", "Mobile-Out"
261d2c6b63dSTakashi Iwai };
262d2c6b63dSTakashi Iwai int i;
263d2c6b63dSTakashi Iwai
264d2c6b63dSTakashi Iwai cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
265d2c6b63dSTakashi Iwai if ((cfg & 0x0f) < 7)
266d2c6b63dSTakashi Iwai return bases[cfg & 0x0f];
267d2c6b63dSTakashi Iwai for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
268d2c6b63dSTakashi Iwai if (cfg == specials_idx[i])
269d2c6b63dSTakashi Iwai return specials[i];
270d2c6b63dSTakashi Iwai }
271d2c6b63dSTakashi Iwai return "UNKNOWN";
272d2c6b63dSTakashi Iwai }
273d2c6b63dSTakashi Iwai
274d2c6b63dSTakashi Iwai /*
275d2c6b63dSTakashi Iwai * Parse the pin default config value and returns the string of the
276d2c6b63dSTakashi Iwai * jack connectivity, i.e. external or internal connection.
277d2c6b63dSTakashi Iwai */
get_jack_connectivity(u32 cfg)278d2c6b63dSTakashi Iwai static const char *get_jack_connectivity(u32 cfg)
279d2c6b63dSTakashi Iwai {
280cc75cdfeSTakashi Iwai static const char * const jack_locations[4] = {
281cc75cdfeSTakashi Iwai "Ext", "Int", "Sep", "Oth"
282cc75cdfeSTakashi Iwai };
283d2c6b63dSTakashi Iwai
284d2c6b63dSTakashi Iwai return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
285d2c6b63dSTakashi Iwai }
286d2c6b63dSTakashi Iwai
287d2c6b63dSTakashi Iwai /*
288d2c6b63dSTakashi Iwai * Parse the pin default config value and returns the string of the
289d2c6b63dSTakashi Iwai * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
290d2c6b63dSTakashi Iwai */
get_jack_type(u32 cfg)291d2c6b63dSTakashi Iwai static const char *get_jack_type(u32 cfg)
292d2c6b63dSTakashi Iwai {
293cc75cdfeSTakashi Iwai static const char * const jack_types[16] = {
294d2c6b63dSTakashi Iwai "Line Out", "Speaker", "HP Out", "CD",
295d2c6b63dSTakashi Iwai "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
296d2c6b63dSTakashi Iwai "Line In", "Aux", "Mic", "Telephony",
297d2c6b63dSTakashi Iwai "SPDIF In", "Digital In", "Reserved", "Other"
298d2c6b63dSTakashi Iwai };
299d2c6b63dSTakashi Iwai
300d2c6b63dSTakashi Iwai return jack_types[(cfg & AC_DEFCFG_DEVICE)
301d2c6b63dSTakashi Iwai >> AC_DEFCFG_DEVICE_SHIFT];
302d2c6b63dSTakashi Iwai }
303d2c6b63dSTakashi Iwai
print_pin_caps(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,int * supports_vref)304c8b6bf9bSTakashi Iwai static void print_pin_caps(struct snd_info_buffer *buffer,
305797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid,
306797760abSAndrew Paprocki int *supports_vref)
3071da177e4SLinus Torvalds {
308cc75cdfeSTakashi Iwai static const char * const jack_conns[4] = {
309cc75cdfeSTakashi Iwai "Jack", "N/A", "Fixed", "Both"
310cc75cdfeSTakashi Iwai };
311e97a5167STakashi Iwai unsigned int caps, val;
3121da177e4SLinus Torvalds
3139ba17b4dSTakashi Iwai caps = param_read(codec, nid, AC_PAR_PIN_CAP);
3140481f453SRobin H. Johnson snd_iprintf(buffer, " Pincap 0x%08x:", caps);
3151da177e4SLinus Torvalds if (caps & AC_PINCAP_IN)
3161da177e4SLinus Torvalds snd_iprintf(buffer, " IN");
3171da177e4SLinus Torvalds if (caps & AC_PINCAP_OUT)
3181da177e4SLinus Torvalds snd_iprintf(buffer, " OUT");
3191da177e4SLinus Torvalds if (caps & AC_PINCAP_HP_DRV)
3201da177e4SLinus Torvalds snd_iprintf(buffer, " HP");
3215885492aSTakashi Iwai if (caps & AC_PINCAP_EAPD)
3225885492aSTakashi Iwai snd_iprintf(buffer, " EAPD");
3235885492aSTakashi Iwai if (caps & AC_PINCAP_PRES_DETECT)
3245885492aSTakashi Iwai snd_iprintf(buffer, " Detect");
325797760abSAndrew Paprocki if (caps & AC_PINCAP_BALANCE)
326797760abSAndrew Paprocki snd_iprintf(buffer, " Balanced");
327c4920606STakashi Iwai if (caps & AC_PINCAP_HDMI) {
328c4920606STakashi Iwai /* Realtek uses this bit as a different meaning */
3297639a06cSTakashi Iwai if ((codec->core.vendor_id >> 16) == 0x10ec)
330797760abSAndrew Paprocki snd_iprintf(buffer, " R/L");
331b923528eSWu Fengguang else {
332b923528eSWu Fengguang if (caps & AC_PINCAP_HBR)
333b923528eSWu Fengguang snd_iprintf(buffer, " HBR");
334c4920606STakashi Iwai snd_iprintf(buffer, " HDMI");
335c4920606STakashi Iwai }
336b923528eSWu Fengguang }
337728765b3SWu Fengguang if (caps & AC_PINCAP_DP)
338728765b3SWu Fengguang snd_iprintf(buffer, " DP");
339797760abSAndrew Paprocki if (caps & AC_PINCAP_TRIG_REQ)
340797760abSAndrew Paprocki snd_iprintf(buffer, " Trigger");
341797760abSAndrew Paprocki if (caps & AC_PINCAP_IMP_SENSE)
342797760abSAndrew Paprocki snd_iprintf(buffer, " ImpSense");
3431da177e4SLinus Torvalds snd_iprintf(buffer, "\n");
344797760abSAndrew Paprocki if (caps & AC_PINCAP_VREF) {
345797760abSAndrew Paprocki unsigned int vref =
346797760abSAndrew Paprocki (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
347797760abSAndrew Paprocki snd_iprintf(buffer, " Vref caps:");
348797760abSAndrew Paprocki if (vref & AC_PINCAP_VREF_HIZ)
349797760abSAndrew Paprocki snd_iprintf(buffer, " HIZ");
350797760abSAndrew Paprocki if (vref & AC_PINCAP_VREF_50)
351797760abSAndrew Paprocki snd_iprintf(buffer, " 50");
352797760abSAndrew Paprocki if (vref & AC_PINCAP_VREF_GRD)
353797760abSAndrew Paprocki snd_iprintf(buffer, " GRD");
354797760abSAndrew Paprocki if (vref & AC_PINCAP_VREF_80)
355797760abSAndrew Paprocki snd_iprintf(buffer, " 80");
356797760abSAndrew Paprocki if (vref & AC_PINCAP_VREF_100)
357797760abSAndrew Paprocki snd_iprintf(buffer, " 100");
358797760abSAndrew Paprocki snd_iprintf(buffer, "\n");
359797760abSAndrew Paprocki *supports_vref = 1;
360797760abSAndrew Paprocki } else
361797760abSAndrew Paprocki *supports_vref = 0;
362797760abSAndrew Paprocki if (caps & AC_PINCAP_EAPD) {
363797760abSAndrew Paprocki val = snd_hda_codec_read(codec, nid, 0,
364797760abSAndrew Paprocki AC_VERB_GET_EAPD_BTLENABLE, 0);
365797760abSAndrew Paprocki snd_iprintf(buffer, " EAPD 0x%x:", val);
366797760abSAndrew Paprocki if (val & AC_EAPDBTL_BALANCED)
367797760abSAndrew Paprocki snd_iprintf(buffer, " BALANCED");
368797760abSAndrew Paprocki if (val & AC_EAPDBTL_EAPD)
369797760abSAndrew Paprocki snd_iprintf(buffer, " EAPD");
370797760abSAndrew Paprocki if (val & AC_EAPDBTL_LR_SWAP)
371797760abSAndrew Paprocki snd_iprintf(buffer, " R/L");
372797760abSAndrew Paprocki snd_iprintf(buffer, "\n");
373797760abSAndrew Paprocki }
3741da177e4SLinus Torvalds caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
375b0c95f51STakashi Iwai snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
376b0c95f51STakashi Iwai jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
377d2c6b63dSTakashi Iwai get_jack_type(caps),
378d2c6b63dSTakashi Iwai get_jack_connectivity(caps),
379d2c6b63dSTakashi Iwai get_jack_location(caps));
3801da177e4SLinus Torvalds snd_iprintf(buffer, " Conn = %s, Color = %s\n",
3811da177e4SLinus Torvalds get_jack_connection(caps),
3821da177e4SLinus Torvalds get_jack_color(caps));
383797760abSAndrew Paprocki /* Default association and sequence values refer to default grouping
384797760abSAndrew Paprocki * of pin complexes and their sequence within the group. This is used
385797760abSAndrew Paprocki * for priority and resource allocation.
386797760abSAndrew Paprocki */
387797760abSAndrew Paprocki snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n",
388797760abSAndrew Paprocki (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
389797760abSAndrew Paprocki caps & AC_DEFCFG_SEQUENCE);
390797760abSAndrew Paprocki if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
391797760abSAndrew Paprocki AC_DEFCFG_MISC_NO_PRESENCE) {
392797760abSAndrew Paprocki /* Miscellaneous bit indicates external hardware does not
393797760abSAndrew Paprocki * support presence detection even if the pin complex
394797760abSAndrew Paprocki * indicates it is supported.
395797760abSAndrew Paprocki */
396797760abSAndrew Paprocki snd_iprintf(buffer, " Misc = NO_PRESENCE\n");
397e97a5167STakashi Iwai }
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds
print_pin_ctls(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,int supports_vref)400797760abSAndrew Paprocki static void print_pin_ctls(struct snd_info_buffer *buffer,
401797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid,
402797760abSAndrew Paprocki int supports_vref)
403797760abSAndrew Paprocki {
404797760abSAndrew Paprocki unsigned int pinctls;
405797760abSAndrew Paprocki
406797760abSAndrew Paprocki pinctls = snd_hda_codec_read(codec, nid, 0,
407797760abSAndrew Paprocki AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
408797760abSAndrew Paprocki snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
409797760abSAndrew Paprocki if (pinctls & AC_PINCTL_IN_EN)
410797760abSAndrew Paprocki snd_iprintf(buffer, " IN");
411797760abSAndrew Paprocki if (pinctls & AC_PINCTL_OUT_EN)
412797760abSAndrew Paprocki snd_iprintf(buffer, " OUT");
413797760abSAndrew Paprocki if (pinctls & AC_PINCTL_HP_EN)
414797760abSAndrew Paprocki snd_iprintf(buffer, " HP");
415797760abSAndrew Paprocki if (supports_vref) {
416797760abSAndrew Paprocki int vref = pinctls & AC_PINCTL_VREFEN;
417797760abSAndrew Paprocki switch (vref) {
418797760abSAndrew Paprocki case AC_PINCTL_VREF_HIZ:
419797760abSAndrew Paprocki snd_iprintf(buffer, " VREF_HIZ");
420797760abSAndrew Paprocki break;
421797760abSAndrew Paprocki case AC_PINCTL_VREF_50:
422797760abSAndrew Paprocki snd_iprintf(buffer, " VREF_50");
423797760abSAndrew Paprocki break;
424797760abSAndrew Paprocki case AC_PINCTL_VREF_GRD:
425797760abSAndrew Paprocki snd_iprintf(buffer, " VREF_GRD");
426797760abSAndrew Paprocki break;
427797760abSAndrew Paprocki case AC_PINCTL_VREF_80:
428797760abSAndrew Paprocki snd_iprintf(buffer, " VREF_80");
429797760abSAndrew Paprocki break;
430797760abSAndrew Paprocki case AC_PINCTL_VREF_100:
431797760abSAndrew Paprocki snd_iprintf(buffer, " VREF_100");
432797760abSAndrew Paprocki break;
433797760abSAndrew Paprocki }
434797760abSAndrew Paprocki }
435797760abSAndrew Paprocki snd_iprintf(buffer, "\n");
436797760abSAndrew Paprocki }
437797760abSAndrew Paprocki
print_vol_knob(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)438797760abSAndrew Paprocki static void print_vol_knob(struct snd_info_buffer *buffer,
439797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
440797760abSAndrew Paprocki {
4419ba17b4dSTakashi Iwai unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
442797760abSAndrew Paprocki snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
443797760abSAndrew Paprocki (cap >> 7) & 1, cap & 0x7f);
444797760abSAndrew Paprocki cap = snd_hda_codec_read(codec, nid, 0,
445797760abSAndrew Paprocki AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
446797760abSAndrew Paprocki snd_iprintf(buffer, "direct=%d, val=%d\n",
447797760abSAndrew Paprocki (cap >> 7) & 1, cap & 0x7f);
448797760abSAndrew Paprocki }
449797760abSAndrew Paprocki
print_audio_io(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,unsigned int wid_type)450797760abSAndrew Paprocki static void print_audio_io(struct snd_info_buffer *buffer,
451797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid,
452797760abSAndrew Paprocki unsigned int wid_type)
453797760abSAndrew Paprocki {
4543911a4c1SJaroslav Kysela int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
455797760abSAndrew Paprocki snd_iprintf(buffer,
456797760abSAndrew Paprocki " Converter: stream=%d, channel=%d\n",
457797760abSAndrew Paprocki (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
458797760abSAndrew Paprocki conv & AC_CONV_CHANNEL);
459797760abSAndrew Paprocki
460797760abSAndrew Paprocki if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
461797760abSAndrew Paprocki int sdi = snd_hda_codec_read(codec, nid, 0,
462797760abSAndrew Paprocki AC_VERB_GET_SDI_SELECT, 0);
463797760abSAndrew Paprocki snd_iprintf(buffer, " SDI-Select: %d\n",
464797760abSAndrew Paprocki sdi & AC_SDI_SELECT);
465797760abSAndrew Paprocki }
466797760abSAndrew Paprocki }
467797760abSAndrew Paprocki
print_digital_conv(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)468797760abSAndrew Paprocki static void print_digital_conv(struct snd_info_buffer *buffer,
469797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
470797760abSAndrew Paprocki {
471797760abSAndrew Paprocki unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
472797760abSAndrew Paprocki AC_VERB_GET_DIGI_CONVERT_1, 0);
47361525979SWang Xingchao unsigned char digi2 = digi1 >> 8;
47461525979SWang Xingchao unsigned char digi3 = digi1 >> 16;
47561525979SWang Xingchao
476797760abSAndrew Paprocki snd_iprintf(buffer, " Digital:");
477797760abSAndrew Paprocki if (digi1 & AC_DIG1_ENABLE)
478797760abSAndrew Paprocki snd_iprintf(buffer, " Enabled");
479797760abSAndrew Paprocki if (digi1 & AC_DIG1_V)
480797760abSAndrew Paprocki snd_iprintf(buffer, " Validity");
481797760abSAndrew Paprocki if (digi1 & AC_DIG1_VCFG)
482797760abSAndrew Paprocki snd_iprintf(buffer, " ValidityCfg");
483797760abSAndrew Paprocki if (digi1 & AC_DIG1_EMPHASIS)
484797760abSAndrew Paprocki snd_iprintf(buffer, " Preemphasis");
485797760abSAndrew Paprocki if (digi1 & AC_DIG1_COPYRIGHT)
486088c820bSWang Xingchao snd_iprintf(buffer, " Non-Copyright");
487797760abSAndrew Paprocki if (digi1 & AC_DIG1_NONAUDIO)
488797760abSAndrew Paprocki snd_iprintf(buffer, " Non-Audio");
489797760abSAndrew Paprocki if (digi1 & AC_DIG1_PROFESSIONAL)
490797760abSAndrew Paprocki snd_iprintf(buffer, " Pro");
491797760abSAndrew Paprocki if (digi1 & AC_DIG1_LEVEL)
492797760abSAndrew Paprocki snd_iprintf(buffer, " GenLevel");
49361525979SWang Xingchao if (digi3 & AC_DIG3_KAE)
49461525979SWang Xingchao snd_iprintf(buffer, " KAE");
495797760abSAndrew Paprocki snd_iprintf(buffer, "\n");
496a1855d80STakashi Iwai snd_iprintf(buffer, " Digital category: 0x%x\n",
49761525979SWang Xingchao digi2 & AC_DIG2_CC);
49861525979SWang Xingchao snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
49961525979SWang Xingchao digi3 & AC_DIG3_ICT);
500797760abSAndrew Paprocki }
501797760abSAndrew Paprocki
get_pwr_state(u32 state)502797760abSAndrew Paprocki static const char *get_pwr_state(u32 state)
503797760abSAndrew Paprocki {
504167d2d55STakashi Iwai static const char * const buf[] = {
505167d2d55STakashi Iwai "D0", "D1", "D2", "D3", "D3cold"
506797760abSAndrew Paprocki };
507167d2d55STakashi Iwai if (state < ARRAY_SIZE(buf))
508797760abSAndrew Paprocki return buf[state];
509797760abSAndrew Paprocki return "UNKNOWN";
510797760abSAndrew Paprocki }
511797760abSAndrew Paprocki
print_power_state(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)512797760abSAndrew Paprocki static void print_power_state(struct snd_info_buffer *buffer,
513797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
514797760abSAndrew Paprocki {
515cc75cdfeSTakashi Iwai static const char * const names[] = {
51683d605fdSWu Fengguang [ilog2(AC_PWRST_D0SUP)] = "D0",
51783d605fdSWu Fengguang [ilog2(AC_PWRST_D1SUP)] = "D1",
51883d605fdSWu Fengguang [ilog2(AC_PWRST_D2SUP)] = "D2",
51983d605fdSWu Fengguang [ilog2(AC_PWRST_D3SUP)] = "D3",
52083d605fdSWu Fengguang [ilog2(AC_PWRST_D3COLDSUP)] = "D3cold",
52183d605fdSWu Fengguang [ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold",
52283d605fdSWu Fengguang [ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP",
52383d605fdSWu Fengguang [ilog2(AC_PWRST_EPSS)] = "EPSS",
52483d605fdSWu Fengguang };
52583d605fdSWu Fengguang
5269ba17b4dSTakashi Iwai int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
527797760abSAndrew Paprocki int pwr = snd_hda_codec_read(codec, nid, 0,
528797760abSAndrew Paprocki AC_VERB_GET_POWER_STATE, 0);
5291d260d7bSTakashi Iwai if (sup != -1) {
5301d260d7bSTakashi Iwai int i;
5311d260d7bSTakashi Iwai
5321d260d7bSTakashi Iwai snd_iprintf(buffer, " Power states: ");
5331d260d7bSTakashi Iwai for (i = 0; i < ARRAY_SIZE(names); i++) {
5341d260d7bSTakashi Iwai if (sup & (1U << i))
5351d260d7bSTakashi Iwai snd_iprintf(buffer, " %s", names[i]);
5361d260d7bSTakashi Iwai }
5371d260d7bSTakashi Iwai snd_iprintf(buffer, "\n");
5381d260d7bSTakashi Iwai }
53983d605fdSWu Fengguang
540ce63f3baSWang Xingchao snd_iprintf(buffer, " Power: setting=%s, actual=%s",
541797760abSAndrew Paprocki get_pwr_state(pwr & AC_PWRST_SETTING),
542797760abSAndrew Paprocki get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
543797760abSAndrew Paprocki AC_PWRST_ACTUAL_SHIFT));
544ce63f3baSWang Xingchao if (pwr & AC_PWRST_ERROR)
545ce63f3baSWang Xingchao snd_iprintf(buffer, ", Error");
546ce63f3baSWang Xingchao if (pwr & AC_PWRST_CLK_STOP_OK)
547ce63f3baSWang Xingchao snd_iprintf(buffer, ", Clock-stop-OK");
548ce63f3baSWang Xingchao if (pwr & AC_PWRST_SETTING_RESET)
549ce63f3baSWang Xingchao snd_iprintf(buffer, ", Setting-reset");
550ce63f3baSWang Xingchao snd_iprintf(buffer, "\n");
551797760abSAndrew Paprocki }
552797760abSAndrew Paprocki
print_unsol_cap(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)553797760abSAndrew Paprocki static void print_unsol_cap(struct snd_info_buffer *buffer,
554797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
555797760abSAndrew Paprocki {
556797760abSAndrew Paprocki int unsol = snd_hda_codec_read(codec, nid, 0,
557797760abSAndrew Paprocki AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
558797760abSAndrew Paprocki snd_iprintf(buffer,
559797760abSAndrew Paprocki " Unsolicited: tag=%02x, enabled=%d\n",
560797760abSAndrew Paprocki unsol & AC_UNSOL_TAG,
561797760abSAndrew Paprocki (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
562797760abSAndrew Paprocki }
563797760abSAndrew Paprocki
can_dump_coef(struct hda_codec * codec)564cd262518SDavid Henningsson static inline bool can_dump_coef(struct hda_codec *codec)
565cd262518SDavid Henningsson {
566cd262518SDavid Henningsson switch (dump_coef) {
567cd262518SDavid Henningsson case 0: return false;
568cd262518SDavid Henningsson case 1: return true;
569cd262518SDavid Henningsson default: return codec->dump_coef;
570cd262518SDavid Henningsson }
571cd262518SDavid Henningsson }
572cd262518SDavid Henningsson
print_proc_caps(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)573797760abSAndrew Paprocki static void print_proc_caps(struct snd_info_buffer *buffer,
574797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
575797760abSAndrew Paprocki {
576cd262518SDavid Henningsson unsigned int i, ncoeff, oldindex;
5779ba17b4dSTakashi Iwai unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
578cd262518SDavid Henningsson ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
579797760abSAndrew Paprocki snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
580cd262518SDavid Henningsson proc_caps & AC_PCAP_BENIGN, ncoeff);
581cd262518SDavid Henningsson
582cd262518SDavid Henningsson if (!can_dump_coef(codec))
583cd262518SDavid Henningsson return;
584cd262518SDavid Henningsson
585cd262518SDavid Henningsson /* Note: This is racy - another process could run in parallel and change
586cd262518SDavid Henningsson the coef index too. */
587cd262518SDavid Henningsson oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
588cd262518SDavid Henningsson for (i = 0; i < ncoeff; i++) {
589cd262518SDavid Henningsson unsigned int val;
590cd262518SDavid Henningsson snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
591cd262518SDavid Henningsson val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
592cd262518SDavid Henningsson 0);
593cd262518SDavid Henningsson snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val);
594cd262518SDavid Henningsson }
595cd262518SDavid Henningsson snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
596797760abSAndrew Paprocki }
597797760abSAndrew Paprocki
print_conn_list(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,unsigned int wid_type,hda_nid_t * conn,int conn_len)598797760abSAndrew Paprocki static void print_conn_list(struct snd_info_buffer *buffer,
599797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid,
600797760abSAndrew Paprocki unsigned int wid_type, hda_nid_t *conn,
601797760abSAndrew Paprocki int conn_len)
602797760abSAndrew Paprocki {
603797760abSAndrew Paprocki int c, curr = -1;
6048b2c7a5cSWang Xingchao const hda_nid_t *list;
6058b2c7a5cSWang Xingchao int cache_len;
606797760abSAndrew Paprocki
60707a1e813STakashi Iwai if (conn_len > 1 &&
60807a1e813STakashi Iwai wid_type != AC_WID_AUD_MIX &&
60907a1e813STakashi Iwai wid_type != AC_WID_VOL_KNB &&
61007a1e813STakashi Iwai wid_type != AC_WID_POWER)
611797760abSAndrew Paprocki curr = snd_hda_codec_read(codec, nid, 0,
612797760abSAndrew Paprocki AC_VERB_GET_CONNECT_SEL, 0);
613797760abSAndrew Paprocki snd_iprintf(buffer, " Connection: %d\n", conn_len);
614797760abSAndrew Paprocki if (conn_len > 0) {
615797760abSAndrew Paprocki snd_iprintf(buffer, " ");
616797760abSAndrew Paprocki for (c = 0; c < conn_len; c++) {
617797760abSAndrew Paprocki snd_iprintf(buffer, " 0x%02x", conn[c]);
618797760abSAndrew Paprocki if (c == curr)
619797760abSAndrew Paprocki snd_iprintf(buffer, "*");
620797760abSAndrew Paprocki }
621797760abSAndrew Paprocki snd_iprintf(buffer, "\n");
622797760abSAndrew Paprocki }
6238b2c7a5cSWang Xingchao
6248b2c7a5cSWang Xingchao /* Get Cache connections info */
6258b2c7a5cSWang Xingchao cache_len = snd_hda_get_conn_list(codec, nid, &list);
626f4d77031SDan Carpenter if (cache_len >= 0 && (cache_len != conn_len ||
627f4d77031SDan Carpenter memcmp(list, conn, conn_len) != 0)) {
6288b2c7a5cSWang Xingchao snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
6298b2c7a5cSWang Xingchao if (cache_len > 0) {
6308b2c7a5cSWang Xingchao snd_iprintf(buffer, " ");
6318b2c7a5cSWang Xingchao for (c = 0; c < cache_len; c++)
6328b2c7a5cSWang Xingchao snd_iprintf(buffer, " 0x%02x", list[c]);
6338b2c7a5cSWang Xingchao snd_iprintf(buffer, "\n");
6348b2c7a5cSWang Xingchao }
6358b2c7a5cSWang Xingchao }
636797760abSAndrew Paprocki }
637797760abSAndrew Paprocki
print_gpio(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)638797760abSAndrew Paprocki static void print_gpio(struct snd_info_buffer *buffer,
639797760abSAndrew Paprocki struct hda_codec *codec, hda_nid_t nid)
640797760abSAndrew Paprocki {
641797760abSAndrew Paprocki unsigned int gpio =
6429ba17b4dSTakashi Iwai param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
643797760abSAndrew Paprocki unsigned int enable, direction, wake, unsol, sticky, data;
644797760abSAndrew Paprocki int i, max;
645797760abSAndrew Paprocki snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
646797760abSAndrew Paprocki "unsolicited=%d, wake=%d\n",
647797760abSAndrew Paprocki gpio & AC_GPIO_IO_COUNT,
648797760abSAndrew Paprocki (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
649797760abSAndrew Paprocki (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
650797760abSAndrew Paprocki (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
651797760abSAndrew Paprocki (gpio & AC_GPIO_WAKE) ? 1 : 0);
652797760abSAndrew Paprocki max = gpio & AC_GPIO_IO_COUNT;
653c4dc5071STakashi Iwai if (!max || max > 8)
654c4dc5071STakashi Iwai return;
655797760abSAndrew Paprocki enable = snd_hda_codec_read(codec, nid, 0,
656797760abSAndrew Paprocki AC_VERB_GET_GPIO_MASK, 0);
657797760abSAndrew Paprocki direction = snd_hda_codec_read(codec, nid, 0,
658797760abSAndrew Paprocki AC_VERB_GET_GPIO_DIRECTION, 0);
659797760abSAndrew Paprocki wake = snd_hda_codec_read(codec, nid, 0,
660797760abSAndrew Paprocki AC_VERB_GET_GPIO_WAKE_MASK, 0);
661797760abSAndrew Paprocki unsol = snd_hda_codec_read(codec, nid, 0,
662797760abSAndrew Paprocki AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
663797760abSAndrew Paprocki sticky = snd_hda_codec_read(codec, nid, 0,
664797760abSAndrew Paprocki AC_VERB_GET_GPIO_STICKY_MASK, 0);
665797760abSAndrew Paprocki data = snd_hda_codec_read(codec, nid, 0,
666797760abSAndrew Paprocki AC_VERB_GET_GPIO_DATA, 0);
667797760abSAndrew Paprocki for (i = 0; i < max; ++i)
668797760abSAndrew Paprocki snd_iprintf(buffer,
669797760abSAndrew Paprocki " IO[%d]: enable=%d, dir=%d, wake=%d, "
67085639646STakashi Iwai "sticky=%d, data=%d, unsol=%d\n", i,
671797760abSAndrew Paprocki (enable & (1<<i)) ? 1 : 0,
672797760abSAndrew Paprocki (direction & (1<<i)) ? 1 : 0,
673797760abSAndrew Paprocki (wake & (1<<i)) ? 1 : 0,
674797760abSAndrew Paprocki (sticky & (1<<i)) ? 1 : 0,
67585639646STakashi Iwai (data & (1<<i)) ? 1 : 0,
67685639646STakashi Iwai (unsol & (1<<i)) ? 1 : 0);
677797760abSAndrew Paprocki /* FIXME: add GPO and GPI pin information */
6785b0cb1d8SJaroslav Kysela print_nid_array(buffer, codec, nid, &codec->mixers);
6795b0cb1d8SJaroslav Kysela print_nid_array(buffer, codec, nid, &codec->nids);
680797760abSAndrew Paprocki }
6811da177e4SLinus Torvalds
print_dpmst_connections(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid,int dev_num)682*19bb4f78SKai Vehmanen static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
683*19bb4f78SKai Vehmanen hda_nid_t nid, int dev_num)
684*19bb4f78SKai Vehmanen {
685*19bb4f78SKai Vehmanen int c, conn_len, curr, dev_id_saved;
686*19bb4f78SKai Vehmanen hda_nid_t *conn;
687*19bb4f78SKai Vehmanen
688*19bb4f78SKai Vehmanen conn_len = snd_hda_get_num_raw_conns(codec, nid);
689*19bb4f78SKai Vehmanen if (conn_len <= 0)
690*19bb4f78SKai Vehmanen return;
691*19bb4f78SKai Vehmanen
692*19bb4f78SKai Vehmanen conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
693*19bb4f78SKai Vehmanen if (!conn)
694*19bb4f78SKai Vehmanen return;
695*19bb4f78SKai Vehmanen
696*19bb4f78SKai Vehmanen dev_id_saved = snd_hda_get_dev_select(codec, nid);
697*19bb4f78SKai Vehmanen
698*19bb4f78SKai Vehmanen snd_hda_set_dev_select(codec, nid, dev_num);
699*19bb4f78SKai Vehmanen curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
700*19bb4f78SKai Vehmanen if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
701*19bb4f78SKai Vehmanen goto out;
702*19bb4f78SKai Vehmanen
703*19bb4f78SKai Vehmanen for (c = 0; c < conn_len; c++) {
704*19bb4f78SKai Vehmanen snd_iprintf(buffer, " 0x%02x", conn[c]);
705*19bb4f78SKai Vehmanen if (c == curr)
706*19bb4f78SKai Vehmanen snd_iprintf(buffer, "*");
707*19bb4f78SKai Vehmanen }
708*19bb4f78SKai Vehmanen
709*19bb4f78SKai Vehmanen out:
710*19bb4f78SKai Vehmanen kfree(conn);
711*19bb4f78SKai Vehmanen snd_hda_set_dev_select(codec, nid, dev_id_saved);
712*19bb4f78SKai Vehmanen }
713*19bb4f78SKai Vehmanen
print_device_list(struct snd_info_buffer * buffer,struct hda_codec * codec,hda_nid_t nid)7147a624ea5SMengdong Lin static void print_device_list(struct snd_info_buffer *buffer,
7157a624ea5SMengdong Lin struct hda_codec *codec, hda_nid_t nid)
7167a624ea5SMengdong Lin {
7177a624ea5SMengdong Lin int i, curr = -1;
7187a624ea5SMengdong Lin u8 dev_list[AC_MAX_DEV_LIST_LEN];
7197a624ea5SMengdong Lin int devlist_len;
7207a624ea5SMengdong Lin
7217a624ea5SMengdong Lin devlist_len = snd_hda_get_devices(codec, nid, dev_list,
7227a624ea5SMengdong Lin AC_MAX_DEV_LIST_LEN);
7237a624ea5SMengdong Lin snd_iprintf(buffer, " Devices: %d\n", devlist_len);
7247a624ea5SMengdong Lin if (devlist_len <= 0)
7257a624ea5SMengdong Lin return;
7267a624ea5SMengdong Lin
7277a624ea5SMengdong Lin curr = snd_hda_codec_read(codec, nid, 0,
7287a624ea5SMengdong Lin AC_VERB_GET_DEVICE_SEL, 0);
7297a624ea5SMengdong Lin
7307a624ea5SMengdong Lin for (i = 0; i < devlist_len; i++) {
7317a624ea5SMengdong Lin if (i == curr)
7327a624ea5SMengdong Lin snd_iprintf(buffer, " *");
7337a624ea5SMengdong Lin else
7347a624ea5SMengdong Lin snd_iprintf(buffer, " ");
7357a624ea5SMengdong Lin
7367a624ea5SMengdong Lin snd_iprintf(buffer,
737*19bb4f78SKai Vehmanen "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
7387a624ea5SMengdong Lin !!(dev_list[i] & AC_DE_PD),
7397a624ea5SMengdong Lin !!(dev_list[i] & AC_DE_ELDV),
7407a624ea5SMengdong Lin !!(dev_list[i] & AC_DE_IA));
741*19bb4f78SKai Vehmanen
742*19bb4f78SKai Vehmanen print_dpmst_connections(buffer, codec, nid, i);
743*19bb4f78SKai Vehmanen
744*19bb4f78SKai Vehmanen snd_iprintf(buffer, " ]\n");
7457a624ea5SMengdong Lin }
7467a624ea5SMengdong Lin }
7477a624ea5SMengdong Lin
print_codec_core_info(struct hdac_device * codec,struct snd_info_buffer * buffer)7487639a06cSTakashi Iwai static void print_codec_core_info(struct hdac_device *codec,
749d01ce99fSTakashi Iwai struct snd_info_buffer *buffer)
7501da177e4SLinus Torvalds {
751812a2ccaSTakashi Iwai snd_iprintf(buffer, "Codec: ");
752812a2ccaSTakashi Iwai if (codec->vendor_name && codec->chip_name)
753812a2ccaSTakashi Iwai snd_iprintf(buffer, "%s %s\n",
754812a2ccaSTakashi Iwai codec->vendor_name, codec->chip_name);
755812a2ccaSTakashi Iwai else
756812a2ccaSTakashi Iwai snd_iprintf(buffer, "Not Set\n");
7571da177e4SLinus Torvalds snd_iprintf(buffer, "Address: %d\n", codec->addr);
75879c944adSJaroslav Kysela if (codec->afg)
75979c944adSJaroslav Kysela snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
76079c944adSJaroslav Kysela codec->afg_function_id, codec->afg_unsol);
76179c944adSJaroslav Kysela if (codec->mfg)
76279c944adSJaroslav Kysela snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
76379c944adSJaroslav Kysela codec->mfg_function_id, codec->mfg_unsol);
764234b4346SPascal de Bruijn snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
765234b4346SPascal de Bruijn snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
7661da177e4SLinus Torvalds snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
767e25c05f1SJonathan Phenix
768e25c05f1SJonathan Phenix if (codec->mfg)
769e25c05f1SJonathan Phenix snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
770e25c05f1SJonathan Phenix else
771e25c05f1SJonathan Phenix snd_iprintf(buffer, "No Modem Function Group found\n");
7727639a06cSTakashi Iwai }
773e25c05f1SJonathan Phenix
print_codec_info(struct snd_info_entry * entry,struct snd_info_buffer * buffer)7747639a06cSTakashi Iwai static void print_codec_info(struct snd_info_entry *entry,
7757639a06cSTakashi Iwai struct snd_info_buffer *buffer)
7767639a06cSTakashi Iwai {
7777639a06cSTakashi Iwai struct hda_codec *codec = entry->private_data;
7787639a06cSTakashi Iwai hda_nid_t nid, fg;
7797639a06cSTakashi Iwai int i, nodes;
7807639a06cSTakashi Iwai
7817639a06cSTakashi Iwai print_codec_core_info(&codec->core, buffer);
7827639a06cSTakashi Iwai fg = codec->core.afg;
7837639a06cSTakashi Iwai if (!fg)
784ec9e1c5cSTakashi Iwai return;
785cb53c626STakashi Iwai snd_hda_power_up(codec);
786b90d7760STakashi Iwai snd_iprintf(buffer, "Default PCM:\n");
7877639a06cSTakashi Iwai print_pcm_caps(buffer, codec, fg);
7881da177e4SLinus Torvalds snd_iprintf(buffer, "Default Amp-In caps: ");
7897639a06cSTakashi Iwai print_amp_caps(buffer, codec, fg, HDA_INPUT);
7901da177e4SLinus Torvalds snd_iprintf(buffer, "Default Amp-Out caps: ");
7917639a06cSTakashi Iwai print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
7927639a06cSTakashi Iwai snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
7937639a06cSTakashi Iwai print_power_state(buffer, codec, fg);
7941da177e4SLinus Torvalds
7957639a06cSTakashi Iwai nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
7961da177e4SLinus Torvalds if (! nid || nodes < 0) {
7971da177e4SLinus Torvalds snd_iprintf(buffer, "Invalid AFG subtree\n");
798cb53c626STakashi Iwai snd_hda_power_down(codec);
7991da177e4SLinus Torvalds return;
8001da177e4SLinus Torvalds }
801797760abSAndrew Paprocki
8027639a06cSTakashi Iwai print_gpio(buffer, codec, fg);
8032d34e1b3STakashi Iwai if (codec->proc_widget_hook)
8047639a06cSTakashi Iwai codec->proc_widget_hook(buffer, codec, fg);
805797760abSAndrew Paprocki
8061da177e4SLinus Torvalds for (i = 0; i < nodes; i++, nid++) {
807d01ce99fSTakashi Iwai unsigned int wid_caps =
8089ba17b4dSTakashi Iwai param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
809a22d543aSTakashi Iwai unsigned int wid_type = get_wcaps_type(wid_caps);
8104eea3091STakashi Iwai hda_nid_t *conn = NULL;
811797760abSAndrew Paprocki int conn_len = 0;
8123e289f16STakashi Iwai
8131da177e4SLinus Torvalds snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
8141da177e4SLinus Torvalds get_wid_type_name(wid_type), wid_caps);
815c4920606STakashi Iwai if (wid_caps & AC_WCAP_STEREO) {
816fd72d008SWu Fengguang unsigned int chans = get_wcaps_channels(wid_caps);
817c4920606STakashi Iwai if (chans == 2)
8181da177e4SLinus Torvalds snd_iprintf(buffer, " Stereo");
8191da177e4SLinus Torvalds else
820c4920606STakashi Iwai snd_iprintf(buffer, " %d-Channels", chans);
821c4920606STakashi Iwai } else
8221da177e4SLinus Torvalds snd_iprintf(buffer, " Mono");
8231da177e4SLinus Torvalds if (wid_caps & AC_WCAP_DIGITAL)
8241da177e4SLinus Torvalds snd_iprintf(buffer, " Digital");
8251da177e4SLinus Torvalds if (wid_caps & AC_WCAP_IN_AMP)
8261da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-In");
8271da177e4SLinus Torvalds if (wid_caps & AC_WCAP_OUT_AMP)
8281da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-Out");
829797760abSAndrew Paprocki if (wid_caps & AC_WCAP_STRIPE)
830797760abSAndrew Paprocki snd_iprintf(buffer, " Stripe");
831797760abSAndrew Paprocki if (wid_caps & AC_WCAP_LR_SWAP)
832797760abSAndrew Paprocki snd_iprintf(buffer, " R/L");
833c4920606STakashi Iwai if (wid_caps & AC_WCAP_CP_CAPS)
834c4920606STakashi Iwai snd_iprintf(buffer, " CP");
8351da177e4SLinus Torvalds snd_iprintf(buffer, "\n");
8361da177e4SLinus Torvalds
8375b0cb1d8SJaroslav Kysela print_nid_array(buffer, codec, nid, &codec->mixers);
8385b0cb1d8SJaroslav Kysela print_nid_array(buffer, codec, nid, &codec->nids);
8393911a4c1SJaroslav Kysela print_nid_pcms(buffer, codec, nid);
8403911a4c1SJaroslav Kysela
841e1716139STakashi Iwai /* volume knob is a special widget that always have connection
842e1716139STakashi Iwai * list
843e1716139STakashi Iwai */
844e1716139STakashi Iwai if (wid_type == AC_WID_VOL_KNB)
845e1716139STakashi Iwai wid_caps |= AC_WCAP_CONN_LIST;
846e1716139STakashi Iwai
8474eea3091STakashi Iwai if (wid_caps & AC_WCAP_CONN_LIST) {
8484eea3091STakashi Iwai conn_len = snd_hda_get_num_raw_conns(codec, nid);
8494eea3091STakashi Iwai if (conn_len > 0) {
8506da2ec56SKees Cook conn = kmalloc_array(conn_len,
8516da2ec56SKees Cook sizeof(hda_nid_t),
8524eea3091STakashi Iwai GFP_KERNEL);
8534eea3091STakashi Iwai if (!conn)
8544eea3091STakashi Iwai return;
8554eea3091STakashi Iwai if (snd_hda_get_raw_connections(codec, nid, conn,
8564eea3091STakashi Iwai conn_len) < 0)
8574eea3091STakashi Iwai conn_len = 0;
8584eea3091STakashi Iwai }
8594eea3091STakashi Iwai }
8603e289f16STakashi Iwai
8611da177e4SLinus Torvalds if (wid_caps & AC_WCAP_IN_AMP) {
8621da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-In caps: ");
8631da177e4SLinus Torvalds print_amp_caps(buffer, codec, nid, HDA_INPUT);
8641da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-In vals: ");
8654f32456eSMichael Karcher if (wid_type == AC_WID_PIN ||
8664f32456eSMichael Karcher (codec->single_adc_amp &&
8674f32456eSMichael Karcher wid_type == AC_WID_AUD_IN))
8681da177e4SLinus Torvalds print_amp_vals(buffer, codec, nid, HDA_INPUT,
869cc261738STakashi Iwai wid_caps, 1);
8704f32456eSMichael Karcher else
8714f32456eSMichael Karcher print_amp_vals(buffer, codec, nid, HDA_INPUT,
872cc261738STakashi Iwai wid_caps, conn_len);
8731da177e4SLinus Torvalds }
8741da177e4SLinus Torvalds if (wid_caps & AC_WCAP_OUT_AMP) {
8751da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-Out caps: ");
8761da177e4SLinus Torvalds print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
8771da177e4SLinus Torvalds snd_iprintf(buffer, " Amp-Out vals: ");
8789421f954STakashi Iwai if (wid_type == AC_WID_PIN &&
8799421f954STakashi Iwai codec->pin_amp_workaround)
8809421f954STakashi Iwai print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
881cc261738STakashi Iwai wid_caps, conn_len);
8829421f954STakashi Iwai else
8831da177e4SLinus Torvalds print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
884cc261738STakashi Iwai wid_caps, 1);
8851da177e4SLinus Torvalds }
8861da177e4SLinus Torvalds
887e97a5167STakashi Iwai switch (wid_type) {
888797760abSAndrew Paprocki case AC_WID_PIN: {
889797760abSAndrew Paprocki int supports_vref;
890797760abSAndrew Paprocki print_pin_caps(buffer, codec, nid, &supports_vref);
891797760abSAndrew Paprocki print_pin_ctls(buffer, codec, nid, supports_vref);
892e97a5167STakashi Iwai break;
893797760abSAndrew Paprocki }
894e97a5167STakashi Iwai case AC_WID_VOL_KNB:
895797760abSAndrew Paprocki print_vol_knob(buffer, codec, nid);
896e97a5167STakashi Iwai break;
897e97a5167STakashi Iwai case AC_WID_AUD_OUT:
898e97a5167STakashi Iwai case AC_WID_AUD_IN:
899797760abSAndrew Paprocki print_audio_io(buffer, codec, nid, wid_type);
900797760abSAndrew Paprocki if (wid_caps & AC_WCAP_DIGITAL)
901797760abSAndrew Paprocki print_digital_conv(buffer, codec, nid);
902e97a5167STakashi Iwai if (wid_caps & AC_WCAP_FORMAT_OVRD) {
903b90d7760STakashi Iwai snd_iprintf(buffer, " PCM:\n");
9041da177e4SLinus Torvalds print_pcm_caps(buffer, codec, nid);
9051da177e4SLinus Torvalds }
906e97a5167STakashi Iwai break;
907e97a5167STakashi Iwai }
9081da177e4SLinus Torvalds
909797760abSAndrew Paprocki if (wid_caps & AC_WCAP_UNSOL_CAP)
910797760abSAndrew Paprocki print_unsol_cap(buffer, codec, nid);
911b7027cc2STakashi Iwai
912797760abSAndrew Paprocki if (wid_caps & AC_WCAP_POWER)
913797760abSAndrew Paprocki print_power_state(buffer, codec, nid);
914797760abSAndrew Paprocki
915797760abSAndrew Paprocki if (wid_caps & AC_WCAP_DELAY)
916797760abSAndrew Paprocki snd_iprintf(buffer, " Delay: %d samples\n",
917797760abSAndrew Paprocki (wid_caps & AC_WCAP_DELAY) >>
918797760abSAndrew Paprocki AC_WCAP_DELAY_SHIFT);
919797760abSAndrew Paprocki
9207a624ea5SMengdong Lin if (wid_type == AC_WID_PIN && codec->dp_mst)
9217a624ea5SMengdong Lin print_device_list(buffer, codec, nid);
9227a624ea5SMengdong Lin
923797760abSAndrew Paprocki if (wid_caps & AC_WCAP_CONN_LIST)
924797760abSAndrew Paprocki print_conn_list(buffer, codec, nid, wid_type,
925797760abSAndrew Paprocki conn, conn_len);
926797760abSAndrew Paprocki
927797760abSAndrew Paprocki if (wid_caps & AC_WCAP_PROC_WID)
928797760abSAndrew Paprocki print_proc_caps(buffer, codec, nid);
929797760abSAndrew Paprocki
930daead538STakashi Iwai if (codec->proc_widget_hook)
931daead538STakashi Iwai codec->proc_widget_hook(buffer, codec, nid);
9324eea3091STakashi Iwai
9334eea3091STakashi Iwai kfree(conn);
934e1716139STakashi Iwai }
935cb53c626STakashi Iwai snd_hda_power_down(codec);
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds
9381da177e4SLinus Torvalds /*
9391da177e4SLinus Torvalds * create a proc read
9401da177e4SLinus Torvalds */
snd_hda_codec_proc_new(struct hda_codec * codec)9411da177e4SLinus Torvalds int snd_hda_codec_proc_new(struct hda_codec *codec)
9421da177e4SLinus Torvalds {
9431da177e4SLinus Torvalds char name[32];
9441da177e4SLinus Torvalds
9457639a06cSTakashi Iwai snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
94647f2769bSTakashi Iwai return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
9471da177e4SLinus Torvalds }
9481da177e4SLinus Torvalds
949