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