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"
30 static const char *get_wid_type_name(unsigned int wid_value)
32 static char *names[16] = {
33 [AC_WID_AUD_OUT] = "Audio Output",
34 [AC_WID_AUD_IN] = "Audio Input",
35 [AC_WID_AUD_MIX] = "Audio Mixer",
36 [AC_WID_AUD_SEL] = "Audio Selector",
37 [AC_WID_PIN] = "Pin Complex",
38 [AC_WID_POWER] = "Power Widget",
39 [AC_WID_VOL_KNB] = "Volume Knob Widget",
40 [AC_WID_BEEP] = "Beep Generator Widget",
41 [AC_WID_VENDOR] = "Vendor Defined Widget",
45 return names[wid_value];
47 return "UNKOWN Widget";
50 static void print_amp_caps(snd_info_buffer_t *buffer,
51 struct hda_codec *codec, hda_nid_t nid, int dir)
54 if (dir == HDA_OUTPUT)
55 caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
57 caps = snd_hda_param_read(codec, nid, 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(snd_info_buffer_t *buffer,
70 struct hda_codec *codec, hda_nid_t nid,
71 int dir, int stereo, int indices)
76 if (dir == HDA_OUTPUT)
77 dir = AC_AMP_GET_OUTPUT;
79 dir = AC_AMP_GET_INPUT;
80 for (i = 0; i < indices; i++) {
81 snd_iprintf(buffer, " [");
83 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
84 AC_AMP_GET_LEFT | dir | i);
85 snd_iprintf(buffer, "0x%02x ", val);
87 val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
88 AC_AMP_GET_RIGHT | dir | i);
89 snd_iprintf(buffer, "0x%02x]", val);
91 snd_iprintf(buffer, "\n");
94 static void print_pcm_caps(snd_info_buffer_t *buffer,
95 struct hda_codec *codec, hda_nid_t nid)
97 unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
98 unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
99 if (pcm == -1 || stream == -1) {
100 snd_iprintf(buffer, "N/A\n");
103 snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
104 pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
107 static const char *get_jack_location(u32 cfg)
109 static char *bases[7] = {
110 "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
112 static unsigned char specials_idx[] = {
117 static char *specials[] = {
118 "Rear Panel", "Drive Bar",
119 "Riser", "HDMI", "ATAPI",
120 "Mobile-In", "Mobile-Out"
123 cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
124 if ((cfg & 0x0f) < 7)
125 return bases[cfg & 0x0f];
126 for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
127 if (cfg == specials_idx[i])
133 static const char *get_jack_connection(u32 cfg)
135 static char *names[16] = {
136 "Unknown", "1/8", "1/4", "ATAPI",
137 "RCA", "Optical","Digital", "Analog",
138 "DIN", "XLR", "RJ11", "Comb",
139 NULL, NULL, NULL, "Other"
141 cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
148 static const char *get_jack_color(u32 cfg)
150 static char *names[16] = {
151 "Unknown", "Black", "Grey", "Blue",
152 "Green", "Red", "Orange", "Yellow",
153 "Purple", "Pink", NULL, NULL,
154 NULL, NULL, "White", "Other",
156 cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
163 static void print_pin_caps(snd_info_buffer_t *buffer,
164 struct hda_codec *codec, hda_nid_t nid)
166 static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
167 static char *jack_types[16] = {
168 "Line Out", "Speaker", "HP Out", "CD",
169 "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
170 "Line In", "Aux", "Mic", "Telephony",
171 "SPDIF In", "Digitial In", "Reserved", "Other"
173 static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
176 caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
177 snd_iprintf(buffer, " Pincap 0x08%x:", caps);
178 if (caps & AC_PINCAP_IN)
179 snd_iprintf(buffer, " IN");
180 if (caps & AC_PINCAP_OUT)
181 snd_iprintf(buffer, " OUT");
182 if (caps & AC_PINCAP_HP_DRV)
183 snd_iprintf(buffer, " HP");
184 snd_iprintf(buffer, "\n");
185 caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
186 snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
187 jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
188 jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
189 jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
190 get_jack_location(caps));
191 snd_iprintf(buffer, " Conn = %s, Color = %s\n",
192 get_jack_connection(caps),
193 get_jack_color(caps));
197 static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
199 struct hda_codec *codec = entry->private_data;
204 snd_hda_get_codec_name(codec, buf, sizeof(buf));
205 snd_iprintf(buffer, "Codec: %s\n", buf);
206 snd_iprintf(buffer, "Address: %d\n", codec->addr);
207 snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
208 snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
209 snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
212 snd_iprintf(buffer, "Default PCM: ");
213 print_pcm_caps(buffer, codec, codec->afg);
214 snd_iprintf(buffer, "Default Amp-In caps: ");
215 print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
216 snd_iprintf(buffer, "Default Amp-Out caps: ");
217 print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
219 nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
220 if (! nid || nodes < 0) {
221 snd_iprintf(buffer, "Invalid AFG subtree\n");
224 for (i = 0; i < nodes; i++, nid++) {
225 unsigned int wid_caps = snd_hda_param_read(codec, nid,
226 AC_PAR_AUDIO_WIDGET_CAP);
227 unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
229 hda_nid_t conn[HDA_MAX_CONNECTIONS];
231 snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
232 get_wid_type_name(wid_type), wid_caps);
233 if (wid_caps & AC_WCAP_STEREO)
234 snd_iprintf(buffer, " Stereo");
236 snd_iprintf(buffer, " Mono");
237 if (wid_caps & AC_WCAP_DIGITAL)
238 snd_iprintf(buffer, " Digital");
239 if (wid_caps & AC_WCAP_IN_AMP)
240 snd_iprintf(buffer, " Amp-In");
241 if (wid_caps & AC_WCAP_OUT_AMP)
242 snd_iprintf(buffer, " Amp-Out");
243 snd_iprintf(buffer, "\n");
245 if (wid_caps & AC_WCAP_CONN_LIST)
246 conn_len = snd_hda_get_connections(codec, nid, conn,
247 HDA_MAX_CONNECTIONS);
249 if (wid_caps & AC_WCAP_IN_AMP) {
250 snd_iprintf(buffer, " Amp-In caps: ");
251 print_amp_caps(buffer, codec, nid, HDA_INPUT);
252 snd_iprintf(buffer, " Amp-In vals: ");
253 print_amp_vals(buffer, codec, nid, HDA_INPUT,
254 wid_caps & AC_WCAP_STEREO, conn_len);
256 if (wid_caps & AC_WCAP_OUT_AMP) {
257 snd_iprintf(buffer, " Amp-Out caps: ");
258 print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
259 snd_iprintf(buffer, " Amp-Out vals: ");
260 print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
261 wid_caps & AC_WCAP_STEREO, 1);
264 if (wid_type == AC_WID_PIN) {
265 unsigned int pinctls;
266 print_pin_caps(buffer, codec, nid);
267 pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
268 snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
269 if (pinctls & AC_PINCTL_IN_EN)
270 snd_iprintf(buffer, " IN");
271 if (pinctls & AC_PINCTL_OUT_EN)
272 snd_iprintf(buffer, " OUT");
273 if (pinctls & AC_PINCTL_HP_EN)
274 snd_iprintf(buffer, " HP");
275 snd_iprintf(buffer, "\n");
278 if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
279 (wid_caps & AC_WCAP_FORMAT_OVRD)) {
280 snd_iprintf(buffer, " PCM: ");
281 print_pcm_caps(buffer, codec, nid);
284 if (wid_caps & AC_WCAP_POWER)
285 snd_iprintf(buffer, " Power: 0x%x\n",
286 snd_hda_codec_read(codec, nid, 0,
287 AC_VERB_GET_POWER_STATE, 0));
289 if (wid_caps & AC_WCAP_CONN_LIST) {
291 if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
292 curr = snd_hda_codec_read(codec, nid, 0,
293 AC_VERB_GET_CONNECT_SEL, 0);
294 snd_iprintf(buffer, " Connection: %d\n", conn_len);
295 snd_iprintf(buffer, " ");
296 for (c = 0; c < conn_len; c++) {
297 snd_iprintf(buffer, " 0x%02x", conn[c]);
299 snd_iprintf(buffer, "*");
301 snd_iprintf(buffer, "\n");
309 int snd_hda_codec_proc_new(struct hda_codec *codec)
312 snd_info_entry_t *entry;
315 snprintf(name, sizeof(name), "codec#%d", codec->addr);
316 err = snd_card_proc_new(codec->bus->card, name, &entry);
320 snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);