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 if (dir == HDA_OUTPUT)
56 caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
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");
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);
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)
77 if (dir == HDA_OUTPUT)
78 dir = AC_AMP_GET_OUTPUT;
80 dir = AC_AMP_GET_INPUT;
81 for (i = 0; i < indices; i++) {
82 snd_iprintf(buffer, " [");
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);
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);
92 snd_iprintf(buffer, "\n");
95 static void print_pcm_caps(struct snd_info_buffer *buffer,
96 struct hda_codec *codec, hda_nid_t nid)
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");
104 snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
105 pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
108 static const char *get_jack_location(u32 cfg)
110 static char *bases[7] = {
111 "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
113 static unsigned char specials_idx[] = {
118 static char *specials[] = {
119 "Rear Panel", "Drive Bar",
120 "Riser", "HDMI", "ATAPI",
121 "Mobile-In", "Mobile-Out"
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])
134 static const char *get_jack_connection(u32 cfg)
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"
142 cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
149 static const char *get_jack_color(u32 cfg)
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",
157 cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
164 static void print_pin_caps(struct snd_info_buffer *buffer,
165 struct hda_codec *codec, hda_nid_t nid)
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"
174 static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
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 if (caps & AC_PINCAP_EAPD)
186 snd_iprintf(buffer, " EAPD");
187 if (caps & AC_PINCAP_PRES_DETECT)
188 snd_iprintf(buffer, " Detect");
189 snd_iprintf(buffer, "\n");
190 caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
191 snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
192 jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
193 jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
194 jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
195 get_jack_location(caps));
196 snd_iprintf(buffer, " Conn = %s, Color = %s\n",
197 get_jack_connection(caps),
198 get_jack_color(caps));
202 static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
204 struct hda_codec *codec = entry->private_data;
209 snd_hda_get_codec_name(codec, buf, sizeof(buf));
210 snd_iprintf(buffer, "Codec: %s\n", buf);
211 snd_iprintf(buffer, "Address: %d\n", codec->addr);
212 snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
213 snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
214 snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
217 snd_iprintf(buffer, "Default PCM: ");
218 print_pcm_caps(buffer, codec, codec->afg);
219 snd_iprintf(buffer, "Default Amp-In caps: ");
220 print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
221 snd_iprintf(buffer, "Default Amp-Out caps: ");
222 print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
224 nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
225 if (! nid || nodes < 0) {
226 snd_iprintf(buffer, "Invalid AFG subtree\n");
229 for (i = 0; i < nodes; i++, nid++) {
230 unsigned int wid_caps = snd_hda_param_read(codec, nid,
231 AC_PAR_AUDIO_WIDGET_CAP);
232 unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
234 hda_nid_t conn[HDA_MAX_CONNECTIONS];
236 snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
237 get_wid_type_name(wid_type), wid_caps);
238 if (wid_caps & AC_WCAP_STEREO)
239 snd_iprintf(buffer, " Stereo");
241 snd_iprintf(buffer, " Mono");
242 if (wid_caps & AC_WCAP_DIGITAL)
243 snd_iprintf(buffer, " Digital");
244 if (wid_caps & AC_WCAP_IN_AMP)
245 snd_iprintf(buffer, " Amp-In");
246 if (wid_caps & AC_WCAP_OUT_AMP)
247 snd_iprintf(buffer, " Amp-Out");
248 snd_iprintf(buffer, "\n");
250 if (wid_caps & AC_WCAP_CONN_LIST)
251 conn_len = snd_hda_get_connections(codec, nid, conn,
252 HDA_MAX_CONNECTIONS);
254 if (wid_caps & AC_WCAP_IN_AMP) {
255 snd_iprintf(buffer, " Amp-In caps: ");
256 print_amp_caps(buffer, codec, nid, HDA_INPUT);
257 snd_iprintf(buffer, " Amp-In vals: ");
258 print_amp_vals(buffer, codec, nid, HDA_INPUT,
259 wid_caps & AC_WCAP_STEREO, conn_len);
261 if (wid_caps & AC_WCAP_OUT_AMP) {
262 snd_iprintf(buffer, " Amp-Out caps: ");
263 print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
264 snd_iprintf(buffer, " Amp-Out vals: ");
265 print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
266 wid_caps & AC_WCAP_STEREO, 1);
269 if (wid_type == AC_WID_PIN) {
270 unsigned int pinctls;
271 print_pin_caps(buffer, codec, nid);
272 pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
273 snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
274 if (pinctls & AC_PINCTL_IN_EN)
275 snd_iprintf(buffer, " IN");
276 if (pinctls & AC_PINCTL_OUT_EN)
277 snd_iprintf(buffer, " OUT");
278 if (pinctls & AC_PINCTL_HP_EN)
279 snd_iprintf(buffer, " HP");
280 snd_iprintf(buffer, "\n");
283 if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
284 (wid_caps & AC_WCAP_FORMAT_OVRD)) {
285 snd_iprintf(buffer, " PCM: ");
286 print_pcm_caps(buffer, codec, nid);
289 if (wid_caps & AC_WCAP_POWER)
290 snd_iprintf(buffer, " Power: 0x%x\n",
291 snd_hda_codec_read(codec, nid, 0,
292 AC_VERB_GET_POWER_STATE, 0));
294 if (wid_caps & AC_WCAP_CONN_LIST) {
296 if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
297 curr = snd_hda_codec_read(codec, nid, 0,
298 AC_VERB_GET_CONNECT_SEL, 0);
299 snd_iprintf(buffer, " Connection: %d\n", conn_len);
300 snd_iprintf(buffer, " ");
301 for (c = 0; c < conn_len; c++) {
302 snd_iprintf(buffer, " 0x%02x", conn[c]);
304 snd_iprintf(buffer, "*");
306 snd_iprintf(buffer, "\n");
314 int snd_hda_codec_proc_new(struct hda_codec *codec)
317 struct snd_info_entry *entry;
320 snprintf(name, sizeof(name), "codec#%d", codec->addr);
321 err = snd_card_proc_new(codec->bus->card, name, &entry);
325 snd_info_set_text_ops(entry, codec, print_codec_info);