2 * Universal Interface for Intel High Definition Audio Codec
4 * Generic proc interface
6 * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
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.
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.
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
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"
31 static const char *get_wid_type_name(unsigned int wid_value)
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",
46 return names[wid_value];
48 return "UNKOWN Widget";
51 static void print_amp_caps(struct snd_info_buffer *buffer,
52 struct hda_codec *codec, hda_nid_t nid, int dir)
55 caps = snd_hda_param_read(codec, nid,
57 AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
58 if (caps == -1 || caps == 0) {
59 snd_iprintf(buffer, "N/A\n");
62 snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n",
63 caps & AC_AMPCAP_OFFSET,
64 (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
65 (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
66 (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
69 static void print_amp_vals(struct snd_info_buffer *buffer,
70 struct hda_codec *codec, hda_nid_t nid,
71 int dir, int stereo, int indices)
76 dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
77 for (i = 0; i < indices; i++) {
78 snd_iprintf(buffer, " [");
80 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
81 AC_AMP_GET_LEFT | dir | i);
82 snd_iprintf(buffer, "0x%02x ", val);
84 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
85 AC_AMP_GET_RIGHT | dir | i);
86 snd_iprintf(buffer, "0x%02x]", val);
88 snd_iprintf(buffer, "\n");
91 static void print_pcm_caps(struct snd_info_buffer *buffer,
92 struct hda_codec *codec, hda_nid_t nid)
94 unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
95 unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
96 if (pcm == -1 || stream == -1) {
97 snd_iprintf(buffer, "N/A\n");
100 snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
101 pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
104 static const char *get_jack_location(u32 cfg)
106 static char *bases[7] = {
107 "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
109 static unsigned char specials_idx[] = {
114 static char *specials[] = {
115 "Rear Panel", "Drive Bar",
116 "Riser", "HDMI", "ATAPI",
117 "Mobile-In", "Mobile-Out"
120 cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
121 if ((cfg & 0x0f) < 7)
122 return bases[cfg & 0x0f];
123 for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
124 if (cfg == specials_idx[i])
130 static const char *get_jack_connection(u32 cfg)
132 static char *names[16] = {
133 "Unknown", "1/8", "1/4", "ATAPI",
134 "RCA", "Optical","Digital", "Analog",
135 "DIN", "XLR", "RJ11", "Comb",
136 NULL, NULL, NULL, "Other"
138 cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
145 static const char *get_jack_color(u32 cfg)
147 static char *names[16] = {
148 "Unknown", "Black", "Grey", "Blue",
149 "Green", "Red", "Orange", "Yellow",
150 "Purple", "Pink", NULL, NULL,
151 NULL, NULL, "White", "Other",
153 cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
160 static void print_pin_caps(struct snd_info_buffer *buffer,
161 struct hda_codec *codec, hda_nid_t nid)
163 static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
164 static char *jack_types[16] = {
165 "Line Out", "Speaker", "HP Out", "CD",
166 "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
167 "Line In", "Aux", "Mic", "Telephony",
168 "SPDIF In", "Digitial In", "Reserved", "Other"
170 static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
173 caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
174 snd_iprintf(buffer, " Pincap 0x08%x:", caps);
175 if (caps & AC_PINCAP_IN)
176 snd_iprintf(buffer, " IN");
177 if (caps & AC_PINCAP_OUT)
178 snd_iprintf(buffer, " OUT");
179 if (caps & AC_PINCAP_HP_DRV)
180 snd_iprintf(buffer, " HP");
181 if (caps & AC_PINCAP_EAPD)
182 snd_iprintf(buffer, " EAPD");
183 if (caps & AC_PINCAP_PRES_DETECT)
184 snd_iprintf(buffer, " Detect");
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));
198 static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
200 struct hda_codec *codec = entry->private_data;
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);
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);
220 nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
221 if (! nid || nodes < 0) {
222 snd_iprintf(buffer, "Invalid AFG subtree\n");
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;
230 hda_nid_t conn[HDA_MAX_CONNECTIONS];
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");
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");
246 if (wid_caps & AC_WCAP_CONN_LIST)
247 conn_len = snd_hda_get_connections(codec, nid, conn,
248 HDA_MAX_CONNECTIONS);
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);
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);
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");
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);
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));
290 if (wid_caps & AC_WCAP_CONN_LIST) {
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]);
300 snd_iprintf(buffer, "*");
302 snd_iprintf(buffer, "\n");
310 int snd_hda_codec_proc_new(struct hda_codec *codec)
313 struct snd_info_entry *entry;
316 snprintf(name, sizeof(name), "codec#%d", codec->addr);
317 err = snd_card_proc_new(codec->bus->card, name, &entry);
321 snd_info_set_text_ops(entry, codec, print_codec_info);