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);
210 snd_iprintf(buffer, "Default PCM: ");
211 print_pcm_caps(buffer, codec, codec->afg);
212 snd_iprintf(buffer, "Default Amp-In caps: ");
213 print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
214 snd_iprintf(buffer, "Default Amp-Out caps: ");
215 print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
217 nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
218 if (! nid || nodes < 0) {
219 snd_iprintf(buffer, "Invalid AFG subtree\n");
222 for (i = 0; i < nodes; i++, nid++) {
223 unsigned int wid_caps = snd_hda_param_read(codec, nid,
224 AC_PAR_AUDIO_WIDGET_CAP);
225 unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
227 hda_nid_t conn[HDA_MAX_CONNECTIONS];
229 snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
230 get_wid_type_name(wid_type), wid_caps);
231 if (wid_caps & AC_WCAP_STEREO)
232 snd_iprintf(buffer, " Stereo");
234 snd_iprintf(buffer, " Mono");
235 if (wid_caps & AC_WCAP_DIGITAL)
236 snd_iprintf(buffer, " Digital");
237 if (wid_caps & AC_WCAP_IN_AMP)
238 snd_iprintf(buffer, " Amp-In");
239 if (wid_caps & AC_WCAP_OUT_AMP)
240 snd_iprintf(buffer, " Amp-Out");
241 snd_iprintf(buffer, "\n");
243 if (wid_caps & AC_WCAP_CONN_LIST)
244 conn_len = snd_hda_get_connections(codec, nid, conn,
245 HDA_MAX_CONNECTIONS);
247 if (wid_caps & AC_WCAP_IN_AMP) {
248 snd_iprintf(buffer, " Amp-In caps: ");
249 print_amp_caps(buffer, codec, nid, HDA_INPUT);
250 snd_iprintf(buffer, " Amp-In vals: ");
251 print_amp_vals(buffer, codec, nid, HDA_INPUT,
252 wid_caps & AC_WCAP_STEREO, conn_len);
254 if (wid_caps & AC_WCAP_OUT_AMP) {
255 snd_iprintf(buffer, " Amp-Out caps: ");
256 print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
257 snd_iprintf(buffer, " Amp-Out vals: ");
258 print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
259 wid_caps & AC_WCAP_STEREO, 1);
262 if (wid_type == AC_WID_PIN) {
263 unsigned int pinctls;
264 print_pin_caps(buffer, codec, nid);
265 pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
266 snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
267 if (pinctls & AC_PINCTL_IN_EN)
268 snd_iprintf(buffer, " IN");
269 if (pinctls & AC_PINCTL_OUT_EN)
270 snd_iprintf(buffer, " OUT");
271 if (pinctls & AC_PINCTL_HP_EN)
272 snd_iprintf(buffer, " HP");
273 snd_iprintf(buffer, "\n");
276 if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
277 (wid_caps & AC_WCAP_FORMAT_OVRD)) {
278 snd_iprintf(buffer, " PCM: ");
279 print_pcm_caps(buffer, codec, nid);
282 if (wid_caps & AC_WCAP_CONN_LIST) {
284 if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
285 curr = snd_hda_codec_read(codec, nid, 0,
286 AC_VERB_GET_CONNECT_SEL, 0);
287 snd_iprintf(buffer, " Connection: %d\n", conn_len);
288 snd_iprintf(buffer, " ");
289 for (c = 0; c < conn_len; c++) {
290 snd_iprintf(buffer, " 0x%02x", conn[c]);
292 snd_iprintf(buffer, "*");
294 snd_iprintf(buffer, "\n");
302 int snd_hda_codec_proc_new(struct hda_codec *codec)
305 snd_info_entry_t *entry;
308 snprintf(name, sizeof(name), "codec#%d", codec->addr);
309 err = snd_card_proc_new(codec->bus->card, name, &entry);
313 snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);