1 /* 2 * Universal Interface for Intel High Definition Audio Codec 3 * 4 * Generic proc interface 5 * 6 * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 7 * 8 * 9 * This driver is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This driver is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24 #include <sound/driver.h> 25 #include <linux/init.h> 26 #include <linux/pci.h> 27 #include <sound/core.h> 28 #include "hda_codec.h" 29 #include "hda_local.h" 30 31 static const char *get_wid_type_name(unsigned int wid_value) 32 { 33 static char *names[16] = { 34 [AC_WID_AUD_OUT] = "Audio Output", 35 [AC_WID_AUD_IN] = "Audio Input", 36 [AC_WID_AUD_MIX] = "Audio Mixer", 37 [AC_WID_AUD_SEL] = "Audio Selector", 38 [AC_WID_PIN] = "Pin Complex", 39 [AC_WID_POWER] = "Power Widget", 40 [AC_WID_VOL_KNB] = "Volume Knob Widget", 41 [AC_WID_BEEP] = "Beep Generator Widget", 42 [AC_WID_VENDOR] = "Vendor Defined Widget", 43 }; 44 wid_value &= 0xf; 45 if (names[wid_value]) 46 return names[wid_value]; 47 else 48 return "UNKOWN Widget"; 49 } 50 51 static void print_amp_caps(struct snd_info_buffer *buffer, 52 struct hda_codec *codec, hda_nid_t nid, int dir) 53 { 54 unsigned int caps; 55 if (dir == HDA_OUTPUT) 56 caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP); 57 else 58 caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP); 59 if (caps == -1 || caps == 0) { 60 snd_iprintf(buffer, "N/A\n"); 61 return; 62 } 63 snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n", 64 caps & AC_AMPCAP_OFFSET, 65 (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT, 66 (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT, 67 (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); 68 } 69 70 static void print_amp_vals(struct snd_info_buffer *buffer, 71 struct hda_codec *codec, hda_nid_t nid, 72 int dir, int stereo, int indices) 73 { 74 unsigned int val; 75 int i; 76 77 if (dir == HDA_OUTPUT) 78 dir = AC_AMP_GET_OUTPUT; 79 else 80 dir = AC_AMP_GET_INPUT; 81 for (i = 0; i < indices; i++) { 82 snd_iprintf(buffer, " ["); 83 if (stereo) { 84 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 85 AC_AMP_GET_LEFT | dir | i); 86 snd_iprintf(buffer, "0x%02x ", val); 87 } 88 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 89 AC_AMP_GET_RIGHT | dir | i); 90 snd_iprintf(buffer, "0x%02x]", val); 91 } 92 snd_iprintf(buffer, "\n"); 93 } 94 95 static void print_pcm_caps(struct snd_info_buffer *buffer, 96 struct hda_codec *codec, hda_nid_t nid) 97 { 98 unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); 99 unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); 100 if (pcm == -1 || stream == -1) { 101 snd_iprintf(buffer, "N/A\n"); 102 return; 103 } 104 snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n", 105 pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf); 106 } 107 108 static const char *get_jack_location(u32 cfg) 109 { 110 static char *bases[7] = { 111 "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", 112 }; 113 static unsigned char specials_idx[] = { 114 0x07, 0x08, 115 0x17, 0x18, 0x19, 116 0x37, 0x38 117 }; 118 static char *specials[] = { 119 "Rear Panel", "Drive Bar", 120 "Riser", "HDMI", "ATAPI", 121 "Mobile-In", "Mobile-Out" 122 }; 123 int i; 124 cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; 125 if ((cfg & 0x0f) < 7) 126 return bases[cfg & 0x0f]; 127 for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { 128 if (cfg == specials_idx[i]) 129 return specials[i]; 130 } 131 return "UNKNOWN"; 132 } 133 134 static const char *get_jack_connection(u32 cfg) 135 { 136 static char *names[16] = { 137 "Unknown", "1/8", "1/4", "ATAPI", 138 "RCA", "Optical","Digital", "Analog", 139 "DIN", "XLR", "RJ11", "Comb", 140 NULL, NULL, NULL, "Other" 141 }; 142 cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT; 143 if (names[cfg]) 144 return names[cfg]; 145 else 146 return "UNKNOWN"; 147 } 148 149 static const char *get_jack_color(u32 cfg) 150 { 151 static char *names[16] = { 152 "Unknown", "Black", "Grey", "Blue", 153 "Green", "Red", "Orange", "Yellow", 154 "Purple", "Pink", NULL, NULL, 155 NULL, NULL, "White", "Other", 156 }; 157 cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT; 158 if (names[cfg]) 159 return names[cfg]; 160 else 161 return "UNKNOWN"; 162 } 163 164 static void print_pin_caps(struct snd_info_buffer *buffer, 165 struct hda_codec *codec, hda_nid_t nid) 166 { 167 static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; 168 static char *jack_types[16] = { 169 "Line Out", "Speaker", "HP Out", "CD", 170 "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", 171 "Line In", "Aux", "Mic", "Telephony", 172 "SPDIF In", "Digitial In", "Reserved", "Other" 173 }; 174 static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; 175 unsigned int caps; 176 177 caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); 178 snd_iprintf(buffer, " Pincap 0x08%x:", caps); 179 if (caps & AC_PINCAP_IN) 180 snd_iprintf(buffer, " IN"); 181 if (caps & AC_PINCAP_OUT) 182 snd_iprintf(buffer, " OUT"); 183 if (caps & AC_PINCAP_HP_DRV) 184 snd_iprintf(buffer, " HP"); 185 snd_iprintf(buffer, "\n"); 186 caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); 187 snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, 188 jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], 189 jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], 190 jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], 191 get_jack_location(caps)); 192 snd_iprintf(buffer, " Conn = %s, Color = %s\n", 193 get_jack_connection(caps), 194 get_jack_color(caps)); 195 } 196 197 198 static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 199 { 200 struct hda_codec *codec = entry->private_data; 201 char buf[32]; 202 hda_nid_t nid; 203 int i, nodes; 204 205 snd_hda_get_codec_name(codec, buf, sizeof(buf)); 206 snd_iprintf(buffer, "Codec: %s\n", buf); 207 snd_iprintf(buffer, "Address: %d\n", codec->addr); 208 snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); 209 snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); 210 snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); 211 if (! codec->afg) 212 return; 213 snd_iprintf(buffer, "Default PCM: "); 214 print_pcm_caps(buffer, codec, codec->afg); 215 snd_iprintf(buffer, "Default Amp-In caps: "); 216 print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); 217 snd_iprintf(buffer, "Default Amp-Out caps: "); 218 print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); 219 220 nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); 221 if (! nid || nodes < 0) { 222 snd_iprintf(buffer, "Invalid AFG subtree\n"); 223 return; 224 } 225 for (i = 0; i < nodes; i++, nid++) { 226 unsigned int wid_caps = snd_hda_param_read(codec, nid, 227 AC_PAR_AUDIO_WIDGET_CAP); 228 unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; 229 int conn_len = 0; 230 hda_nid_t conn[HDA_MAX_CONNECTIONS]; 231 232 snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, 233 get_wid_type_name(wid_type), wid_caps); 234 if (wid_caps & AC_WCAP_STEREO) 235 snd_iprintf(buffer, " Stereo"); 236 else 237 snd_iprintf(buffer, " Mono"); 238 if (wid_caps & AC_WCAP_DIGITAL) 239 snd_iprintf(buffer, " Digital"); 240 if (wid_caps & AC_WCAP_IN_AMP) 241 snd_iprintf(buffer, " Amp-In"); 242 if (wid_caps & AC_WCAP_OUT_AMP) 243 snd_iprintf(buffer, " Amp-Out"); 244 snd_iprintf(buffer, "\n"); 245 246 if (wid_caps & AC_WCAP_CONN_LIST) 247 conn_len = snd_hda_get_connections(codec, nid, conn, 248 HDA_MAX_CONNECTIONS); 249 250 if (wid_caps & AC_WCAP_IN_AMP) { 251 snd_iprintf(buffer, " Amp-In caps: "); 252 print_amp_caps(buffer, codec, nid, HDA_INPUT); 253 snd_iprintf(buffer, " Amp-In vals: "); 254 print_amp_vals(buffer, codec, nid, HDA_INPUT, 255 wid_caps & AC_WCAP_STEREO, conn_len); 256 } 257 if (wid_caps & AC_WCAP_OUT_AMP) { 258 snd_iprintf(buffer, " Amp-Out caps: "); 259 print_amp_caps(buffer, codec, nid, HDA_OUTPUT); 260 snd_iprintf(buffer, " Amp-Out vals: "); 261 print_amp_vals(buffer, codec, nid, HDA_OUTPUT, 262 wid_caps & AC_WCAP_STEREO, 1); 263 } 264 265 if (wid_type == AC_WID_PIN) { 266 unsigned int pinctls; 267 print_pin_caps(buffer, codec, nid); 268 pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 269 snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); 270 if (pinctls & AC_PINCTL_IN_EN) 271 snd_iprintf(buffer, " IN"); 272 if (pinctls & AC_PINCTL_OUT_EN) 273 snd_iprintf(buffer, " OUT"); 274 if (pinctls & AC_PINCTL_HP_EN) 275 snd_iprintf(buffer, " HP"); 276 snd_iprintf(buffer, "\n"); 277 } 278 279 if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) && 280 (wid_caps & AC_WCAP_FORMAT_OVRD)) { 281 snd_iprintf(buffer, " PCM: "); 282 print_pcm_caps(buffer, codec, nid); 283 } 284 285 if (wid_caps & AC_WCAP_POWER) 286 snd_iprintf(buffer, " Power: 0x%x\n", 287 snd_hda_codec_read(codec, nid, 0, 288 AC_VERB_GET_POWER_STATE, 0)); 289 290 if (wid_caps & AC_WCAP_CONN_LIST) { 291 int c, curr = -1; 292 if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) 293 curr = snd_hda_codec_read(codec, nid, 0, 294 AC_VERB_GET_CONNECT_SEL, 0); 295 snd_iprintf(buffer, " Connection: %d\n", conn_len); 296 snd_iprintf(buffer, " "); 297 for (c = 0; c < conn_len; c++) { 298 snd_iprintf(buffer, " 0x%02x", conn[c]); 299 if (c == curr) 300 snd_iprintf(buffer, "*"); 301 } 302 snd_iprintf(buffer, "\n"); 303 } 304 } 305 } 306 307 /* 308 * create a proc read 309 */ 310 int snd_hda_codec_proc_new(struct hda_codec *codec) 311 { 312 char name[32]; 313 struct snd_info_entry *entry; 314 int err; 315 316 snprintf(name, sizeof(name), "codec#%d", codec->addr); 317 err = snd_card_proc_new(codec->bus->card, name, &entry); 318 if (err < 0) 319 return err; 320 321 snd_info_set_text_ops(entry, codec, print_codec_info); 322 return 0; 323 } 324 325