Merge branch 'for-2.6.25' of master.kernel.org:/pub/scm/linux/kernel/git/jwboyer...
[sfrench/cifs-2.6.git] / sound / pci / hda / hda_proc.c
index e94944f34ffdcfffc4c8c9385ba268899ce824c2..5633f77f8f3b74885f36ff33dec5481771096871 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
 #include "hda_codec.h"
@@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg)
 }
 
 static void print_pin_caps(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid)
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int *supports_vref)
 {
        static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
        static char *jack_types[16] = {
@@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
                "SPDIF In", "Digitial In", "Reserved", "Other"
        };
        static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
-       unsigned int caps;
+       unsigned int caps, val;
 
        caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
        snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
@@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
                snd_iprintf(buffer, " EAPD");
        if (caps & AC_PINCAP_PRES_DETECT)
                snd_iprintf(buffer, " Detect");
+       if (caps & AC_PINCAP_BALANCE)
+               snd_iprintf(buffer, " Balanced");
+       if (caps & AC_PINCAP_LR_SWAP)
+               snd_iprintf(buffer, " R/L");
+       if (caps & AC_PINCAP_TRIG_REQ)
+               snd_iprintf(buffer, " Trigger");
+       if (caps & AC_PINCAP_IMP_SENSE)
+               snd_iprintf(buffer, " ImpSense");
        snd_iprintf(buffer, "\n");
+       if (caps & AC_PINCAP_VREF) {
+               unsigned int vref =
+                       (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+               snd_iprintf(buffer, "    Vref caps:");
+               if (vref & AC_PINCAP_VREF_HIZ)
+                       snd_iprintf(buffer, " HIZ");
+               if (vref & AC_PINCAP_VREF_50)
+                       snd_iprintf(buffer, " 50");
+               if (vref & AC_PINCAP_VREF_GRD)
+                       snd_iprintf(buffer, " GRD");
+               if (vref & AC_PINCAP_VREF_80)
+                       snd_iprintf(buffer, " 80");
+               if (vref & AC_PINCAP_VREF_100)
+                       snd_iprintf(buffer, " 100");
+               snd_iprintf(buffer, "\n");
+               *supports_vref = 1;
+       } else
+               *supports_vref = 0;
+       if (caps & AC_PINCAP_EAPD) {
+               val = snd_hda_codec_read(codec, nid, 0,
+                                        AC_VERB_GET_EAPD_BTLENABLE, 0);
+               snd_iprintf(buffer, "  EAPD 0x%x:", val);
+               if (val & AC_EAPDBTL_BALANCED)
+                       snd_iprintf(buffer, " BALANCED");
+               if (val & AC_EAPDBTL_EAPD)
+                       snd_iprintf(buffer, " EAPD");
+               if (val & AC_EAPDBTL_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
+               snd_iprintf(buffer, "\n");
+       }
        caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
        snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
                    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
@@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
        snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
                    get_jack_connection(caps),
                    get_jack_color(caps));
+       /* Default association and sequence values refer to default grouping
+        * of pin complexes and their sequence within the group. This is used
+        * for priority and resource allocation.
+        */
+       snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
+                   (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
+                   caps & AC_DEFCFG_SEQUENCE);
+       if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
+           AC_DEFCFG_MISC_NO_PRESENCE) {
+               /* Miscellaneous bit indicates external hardware does not
+                * support presence detection even if the pin complex
+                * indicates it is supported.
+                */
+               snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
+       }
+}
+
+static void print_pin_ctls(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int supports_vref)
+{
+       unsigned int pinctls;
+
+       pinctls = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+       if (pinctls & AC_PINCTL_IN_EN)
+               snd_iprintf(buffer, " IN");
+       if (pinctls & AC_PINCTL_OUT_EN)
+               snd_iprintf(buffer, " OUT");
+       if (pinctls & AC_PINCTL_HP_EN)
+               snd_iprintf(buffer, " HP");
+       if (supports_vref) {
+               int vref = pinctls & AC_PINCTL_VREFEN;
+               switch (vref) {
+               case AC_PINCTL_VREF_HIZ:
+                       snd_iprintf(buffer, " VREF_HIZ");
+                       break;
+               case AC_PINCTL_VREF_50:
+                       snd_iprintf(buffer, " VREF_50");
+                       break;
+               case AC_PINCTL_VREF_GRD:
+                       snd_iprintf(buffer, " VREF_GRD");
+                       break;
+               case AC_PINCTL_VREF_80:
+                       snd_iprintf(buffer, " VREF_80");
+                       break;
+               case AC_PINCTL_VREF_100:
+                       snd_iprintf(buffer, " VREF_100");
+                       break;
+               }
+       }
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_vol_knob(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int cap = snd_hda_param_read(codec, nid,
+                                             AC_PAR_VOL_KNB_CAP);
+       snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
+                   (cap >> 7) & 1, cap & 0x7f);
+       cap = snd_hda_codec_read(codec, nid, 0,
+                                AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+       snd_iprintf(buffer, "direct=%d, val=%d\n",
+                   (cap >> 7) & 1, cap & 0x7f);
+}
+
+static void print_audio_io(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          unsigned int wid_type)
+{
+       int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       snd_iprintf(buffer,
+                   "  Converter: stream=%d, channel=%d\n",
+                   (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
+                   conv & AC_CONV_CHANNEL);
+
+       if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
+               int sdi = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_SDI_SELECT, 0);
+               snd_iprintf(buffer, "  SDI-Select: %d\n",
+                           sdi & AC_SDI_SELECT);
+       }
+}
+
+static void print_digital_conv(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_DIGI_CONVERT_1, 0);
+       unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_DIGI_CONVERT_2, 0);
+       snd_iprintf(buffer, "  Digital:");
+       if (digi1 & AC_DIG1_ENABLE)
+               snd_iprintf(buffer, " Enabled");
+       if (digi1 & AC_DIG1_V)
+               snd_iprintf(buffer, " Validity");
+       if (digi1 & AC_DIG1_VCFG)
+               snd_iprintf(buffer, " ValidityCfg");
+       if (digi1 & AC_DIG1_EMPHASIS)
+               snd_iprintf(buffer, " Preemphasis");
+       if (digi1 & AC_DIG1_COPYRIGHT)
+               snd_iprintf(buffer, " Copyright");
+       if (digi1 & AC_DIG1_NONAUDIO)
+               snd_iprintf(buffer, " Non-Audio");
+       if (digi1 & AC_DIG1_PROFESSIONAL)
+               snd_iprintf(buffer, " Pro");
+       if (digi1 & AC_DIG1_LEVEL)
+               snd_iprintf(buffer, " GenLevel");
+       snd_iprintf(buffer, "\n");
+       snd_iprintf(buffer, "  Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+}
+
+static const char *get_pwr_state(u32 state)
+{
+       static const char *buf[4] = {
+               "D0", "D1", "D2", "D3"
+       };
+       if (state < 4)
+               return buf[state];
+       return "UNKNOWN";
+}
+
+static void print_power_state(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       int pwr = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_POWER_STATE, 0);
+       snd_iprintf(buffer, "  Power: setting=%s, actual=%s\n",
+                   get_pwr_state(pwr & AC_PWRST_SETTING),
+                   get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
+                                 AC_PWRST_ACTUAL_SHIFT));
+}
+
+static void print_unsol_cap(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       int unsol = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
+       snd_iprintf(buffer,
+                   "  Unsolicited: tag=%02x, enabled=%d\n",
+                   unsol & AC_UNSOL_TAG,
+                   (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
+}
+
+static void print_proc_caps(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int proc_caps = snd_hda_param_read(codec, nid,
+                                                   AC_PAR_PROC_CAP);
+       snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
+                   proc_caps & AC_PCAP_BENIGN,
+                   (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
 }
 
+static void print_conn_list(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           unsigned int wid_type, hda_nid_t *conn,
+                           int conn_len)
+{
+       int c, curr = -1;
+
+       if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+               curr = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
+       if (conn_len > 0) {
+               snd_iprintf(buffer, "    ");
+               for (c = 0; c < conn_len; c++) {
+                       snd_iprintf(buffer, " 0x%02x", conn[c]);
+                       if (c == curr)
+                               snd_iprintf(buffer, "*");
+               }
+               snd_iprintf(buffer, "\n");
+       }
+}
+
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       int coeff = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_PROC_COEF, 0);
+       snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
+       coeff = snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_COEF_INDEX, 0);
+       snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
+}
+
+static void print_gpio(struct snd_info_buffer *buffer,
+                      struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int gpio =
+               snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+       unsigned int enable, direction, wake, unsol, sticky, data;
+       int i, max;
+       snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
+                   "unsolicited=%d, wake=%d\n",
+                   gpio & AC_GPIO_IO_COUNT,
+                   (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
+                   (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+                   (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
+                   (gpio & AC_GPIO_WAKE) ? 1 : 0);
+       max = gpio & AC_GPIO_IO_COUNT;
+       enable = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_MASK, 0);
+       direction = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_GPIO_DIRECTION, 0);
+       wake = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_WAKE_MASK, 0);
+       unsol  = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
+       sticky = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_STICKY_MASK, 0);
+       data = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_DATA, 0);
+       for (i = 0; i < max; ++i)
+               snd_iprintf(buffer,
+                           "  IO[%d]: enable=%d, dir=%d, wake=%d, "
+                           "sticky=%d, data=%d\n", i,
+                           (enable & (1<<i)) ? 1 : 0,
+                           (direction & (1<<i)) ? 1 : 0,
+                           (wake & (1<<i)) ? 1 : 0,
+                           (sticky & (1<<i)) ? 1 : 0,
+                           (data & (1<<i)) ? 1 : 0);
+       /* FIXME: add GPO and GPI pin information */
+}
 
 static void print_codec_info(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
@@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry,
                snd_hda_power_down(codec);
                return;
        }
+
+       print_gpio(buffer, codec, codec->afg);
+
        for (i = 0; i < nodes; i++, nid++) {
                unsigned int wid_caps =
                        snd_hda_param_read(codec, nid,
                                           AC_PAR_AUDIO_WIDGET_CAP);
                unsigned int wid_type =
                        (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-               int conn_len = 0; 
                hda_nid_t conn[HDA_MAX_CONNECTIONS];
+               int conn_len = 0;
 
                snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
                            get_wid_type_name(wid_type), wid_caps);
@@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, " Amp-In");
                if (wid_caps & AC_WCAP_OUT_AMP)
                        snd_iprintf(buffer, " Amp-Out");
+               if (wid_caps & AC_WCAP_STRIPE)
+                       snd_iprintf(buffer, " Stripe");
+               if (wid_caps & AC_WCAP_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
                snd_iprintf(buffer, "\n");
 
+               /* volume knob is a special widget that always have connection
+                * list
+                */
+               if (wid_type == AC_WID_VOL_KNB)
+                       wid_caps |= AC_WCAP_CONN_LIST;
+
                if (wid_caps & AC_WCAP_CONN_LIST)
                        conn_len = snd_hda_get_connections(codec, nid, conn,
                                                           HDA_MAX_CONNECTIONS);
@@ -308,7 +584,8 @@ static void print_codec_info(struct snd_info_entry *entry,
                        print_amp_caps(buffer, codec, nid, HDA_INPUT);
                        snd_iprintf(buffer, "  Amp-In vals: ");
                        print_amp_vals(buffer, codec, nid, HDA_INPUT,
-                                      wid_caps & AC_WCAP_STEREO, conn_len);
+                                      wid_caps & AC_WCAP_STEREO,
+                                      wid_type == AC_WID_PIN ? 1 : conn_len);
                }
                if (wid_caps & AC_WCAP_OUT_AMP) {
                        snd_iprintf(buffer, "  Amp-Out caps: ");
@@ -318,48 +595,49 @@ static void print_codec_info(struct snd_info_entry *entry,
                                       wid_caps & AC_WCAP_STEREO, 1);
                }
 
-               if (wid_type == AC_WID_PIN) {
-                       unsigned int pinctls;
-                       print_pin_caps(buffer, codec, nid);
-                       pinctls = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                                    0);
-                       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
-                       if (pinctls & AC_PINCTL_IN_EN)
-                               snd_iprintf(buffer, " IN");
-                       if (pinctls & AC_PINCTL_OUT_EN)
-                               snd_iprintf(buffer, " OUT");
-                       if (pinctls & AC_PINCTL_HP_EN)
-                               snd_iprintf(buffer, " HP");
-                       snd_iprintf(buffer, "\n");
+               switch (wid_type) {
+               case AC_WID_PIN: {
+                       int supports_vref;
+                       print_pin_caps(buffer, codec, nid, &supports_vref);
+                       print_pin_ctls(buffer, codec, nid, supports_vref);
+                       break;
                }
-
-               if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
-                   (wid_caps & AC_WCAP_FORMAT_OVRD)) {
-                       snd_iprintf(buffer, "  PCM:\n");
-                       print_pcm_caps(buffer, codec, nid);
+               case AC_WID_VOL_KNB:
+                       print_vol_knob(buffer, codec, nid);
+                       break;
+               case AC_WID_AUD_OUT:
+               case AC_WID_AUD_IN:
+                       print_audio_io(buffer, codec, nid, wid_type);
+                       if (wid_caps & AC_WCAP_DIGITAL)
+                               print_digital_conv(buffer, codec, nid);
+                       if (wid_caps & AC_WCAP_FORMAT_OVRD) {
+                               snd_iprintf(buffer, "  PCM:\n");
+                               print_pcm_caps(buffer, codec, nid);
+                       }
+                       break;
                }
 
+               if (wid_caps & AC_WCAP_UNSOL_CAP)
+                       print_unsol_cap(buffer, codec, nid);
+
                if (wid_caps & AC_WCAP_POWER)
-                       snd_iprintf(buffer, "  Power: 0x%x\n",
-                                   snd_hda_codec_read(codec, nid, 0,
-                                                      AC_VERB_GET_POWER_STATE,
-                                                      0));
-
-               if (wid_caps & AC_WCAP_CONN_LIST) {
-                       int c, curr = -1;
-                       if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
-                               curr = snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_CONNECT_SEL, 0);
-                       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
-                       snd_iprintf(buffer, "    ");
-                       for (c = 0; c < conn_len; c++) {
-                               snd_iprintf(buffer, " 0x%02x", conn[c]);
-                               if (c == curr)
-                                       snd_iprintf(buffer, "*");
-                       }
-                       snd_iprintf(buffer, "\n");
-               }
+                       print_power_state(buffer, codec, nid);
+
+               if (wid_caps & AC_WCAP_DELAY)
+                       snd_iprintf(buffer, "  Delay: %d samples\n",
+                                   (wid_caps & AC_WCAP_DELAY) >>
+                                   AC_WCAP_DELAY_SHIFT);
+
+               if (wid_caps & AC_WCAP_CONN_LIST)
+                       print_conn_list(buffer, codec, nid, wid_type,
+                                       conn, conn_len);
+
+               if (wid_caps & AC_WCAP_PROC_WID)
+                       print_proc_caps(buffer, codec, nid);
+
+               /* NID 0x20 == Realtek Define Registers */
+               if (codec->vendor_id == 0x10ec && nid == 0x20)
+                       print_realtek_coef(buffer, codec, nid);
        }
        snd_hda_power_down(codec);
 }