Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / sound / pci / ac97 / ac97_proc.c
index 2080463..4d523df 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <sound/driver.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
+
 #include <sound/core.h>
 #include <sound/ac97_codec.h>
 #include <sound/asoundef.h>
  * proc interface
  */
 
-static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+static void snd_ac97_proc_read_functions(struct snd_ac97 *ac97, struct snd_info_buffer *buffer)
+{
+       int header = 0, function;
+       unsigned short info, sense_info;
+       static const char *function_names[12] = {
+               "Master Out", "AUX Out", "Center/LFE Out", "SPDIF Out",
+               "Phone In", "Mic 1", "Mic 2", "Line In", "CD In", "Video In",
+               "Aux In", "Mono Out"
+       };
+       static const char *locations[8] = {
+               "Rear I/O Panel", "Front Panel", "Motherboard", "Dock/External",
+               "reserved", "reserved", "reserved", "NC/unused"
+       };
+
+       for (function = 0; function < 12; ++function) {
+               snd_ac97_write(ac97, AC97_FUNC_SELECT, function << 1);
+               info = snd_ac97_read(ac97, AC97_FUNC_INFO);
+               if (!(info & 0x0001))
+                       continue;
+               if (!header) {
+                       snd_iprintf(buffer, "\n                    Gain     Inverted  Buffer delay  Location\n");
+                       header = 1;
+               }
+               sense_info = snd_ac97_read(ac97, AC97_SENSE_INFO);
+               snd_iprintf(buffer, "%-17s: %3d.%d dBV    %c      %2d/fs         %s\n",
+                           function_names[function],
+                           (info & 0x8000 ? -1 : 1) * ((info & 0x7000) >> 12) * 3 / 2,
+                           ((info & 0x0800) >> 11) * 5,
+                           info & 0x0400 ? 'X' : '-',
+                           (info & 0x03e0) >> 5,
+                           locations[sense_info >> 13]);
+       }
+}
+
+static const char *snd_ac97_stereo_enhancements[] =
+{
+  /*   0 */ "No 3D Stereo Enhancement",
+  /*   1 */ "Analog Devices Phat Stereo",
+  /*   2 */ "Creative Stereo Enhancement",
+  /*   3 */ "National Semi 3D Stereo Enhancement",
+  /*   4 */ "YAMAHA Ymersion",
+  /*   5 */ "BBE 3D Stereo Enhancement",
+  /*   6 */ "Crystal Semi 3D Stereo Enhancement",
+  /*   7 */ "Qsound QXpander",
+  /*   8 */ "Spatializer 3D Stereo Enhancement",
+  /*   9 */ "SRS 3D Stereo Enhancement",
+  /*  10 */ "Platform Tech 3D Stereo Enhancement",
+  /*  11 */ "AKM 3D Audio",
+  /*  12 */ "Aureal Stereo Enhancement",
+  /*  13 */ "Aztech 3D Enhancement",
+  /*  14 */ "Binaura 3D Audio Enhancement",
+  /*  15 */ "ESS Technology Stereo Enhancement",
+  /*  16 */ "Harman International VMAx",
+  /*  17 */ "Nvidea/IC Ensemble/KS Waves 3D Stereo Enhancement",
+  /*  18 */ "Philips Incredible Sound",
+  /*  19 */ "Texas Instruments 3D Stereo Enhancement",
+  /*  20 */ "VLSI Technology 3D Stereo Enhancement",
+  /*  21 */ "TriTech 3D Stereo Enhancement",
+  /*  22 */ "Realtek 3D Stereo Enhancement",
+  /*  23 */ "Samsung 3D Stereo Enhancement",
+  /*  24 */ "Wolfson Microelectronics 3D Enhancement",
+  /*  25 */ "Delta Integration 3D Enhancement",
+  /*  26 */ "SigmaTel 3D Enhancement",
+  /*  27 */ "IC Ensemble/KS Waves",
+  /*  28 */ "Rockwell 3D Stereo Enhancement",
+  /*  29 */ "Reserved 29",
+  /*  30 */ "Reserved 30",
+  /*  31 */ "Reserved 31"
+};
+
+static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffer *buffer, int subidx)
 {
        char name[64];
        unsigned short val, tmp, ext, mext;
-       static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" };
+       static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=10/11" };
        static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" };
        static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
+       static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" };
 
        snd_ac97_get_name(NULL, ac97->id, name, 0);
        snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
+
        if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
                goto __modem;
 
+        snd_iprintf(buffer, "PCI Subsys Vendor: 0x%04x\n",
+                   ac97->subsystem_vendor);
+        snd_iprintf(buffer, "PCI Subsys Device: 0x%04x\n\n",
+                    ac97->subsystem_device);
+
+       if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+               val = snd_ac97_read(ac97, AC97_INT_PAGING);
+               snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+                                    AC97_PAGE_MASK, AC97_PAGE_1);
+               tmp = snd_ac97_read(ac97, AC97_CODEC_CLASS_REV);
+               snd_iprintf(buffer, "Revision         : 0x%02x\n", tmp & 0xff);
+               snd_iprintf(buffer, "Compat. Class    : 0x%02x\n", (tmp >> 8) & 0x1f);
+               snd_iprintf(buffer, "Subsys. Vendor ID: 0x%04x\n",
+                           snd_ac97_read(ac97, AC97_PCI_SVID));
+               snd_iprintf(buffer, "Subsys. ID       : 0x%04x\n\n",
+                           snd_ac97_read(ac97, AC97_PCI_SID));
+               snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+                                    AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+       }
+
        // val = snd_ac97_read(ac97, AC97_RESET);
        val = ac97->caps;
        snd_iprintf(buffer, "Capabilities     :%s%s%s%s%s%s\n",
@@ -88,6 +182,9 @@ static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, in
                    val & 0x0200 ? "Mic" : "MIX",
                    val & 0x0100 ? "Mic2" : "Mic1",
                    val & 0x0080 ? "on" : "off");
+       if (ac97->ext_id & AC97_EI_DRA)
+               snd_iprintf(buffer, "Double rate slots: %s\n",
+                           double_rate_slots[(val >> 10) & 3]);
 
        ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
        if (ext == 0)
@@ -185,6 +282,14 @@ static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, in
                        }
                }
        }
+       if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+               val = snd_ac97_read(ac97, AC97_INT_PAGING);
+               snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+                                    AC97_PAGE_MASK, AC97_PAGE_1);
+               snd_ac97_proc_read_functions(ac97, buffer);
+               snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+                                    AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+       }
 
 
       __modem:
@@ -231,13 +336,13 @@ static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, in
        }
 }
 
-static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
-       ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
+       struct snd_ac97 *ac97 = entry->private_data;
        
+       mutex_lock(&ac97->page_mutex);
        if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {        // Analog Devices AD1881/85/86
                int idx;
-               down(&ac97->spec.ad18xx.mutex);
                for (idx = 0; idx < 3; idx++)
                        if (ac97->spec.ad18xx.id[idx]) {
                                /* select single codec */
@@ -248,7 +353,6 @@ static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buff
                        }
                /* select all codecs */
                snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
-               up(&ac97->spec.ad18xx.mutex);
                
                snd_iprintf(buffer, "\nAD18XX configuration\n");
                snd_iprintf(buffer, "Unchained        : 0x%04x,0x%04x,0x%04x\n",
@@ -262,9 +366,29 @@ static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buff
        } else {
                snd_ac97_proc_read_main(ac97, buffer, 0);
        }
+       mutex_unlock(&ac97->page_mutex);
+}
+
+#ifdef CONFIG_SND_DEBUG
+/* direct register write for debugging */
+static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_ac97 *ac97 = entry->private_data;
+       char line[64];
+       unsigned int reg, val;
+       mutex_lock(&ac97->page_mutex);
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               if (sscanf(line, "%x %x", &reg, &val) != 2)
+                       continue;
+               /* register must be even */
+               if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff)
+                       snd_ac97_write_cache(ac97, reg, val);
+       }
+       mutex_unlock(&ac97->page_mutex);
 }
+#endif
 
-static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+static void snd_ac97_proc_regs_read_main(struct snd_ac97 *ac97, struct snd_info_buffer *buffer, int subidx)
 {
        int reg, val;
 
@@ -274,15 +398,15 @@ static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffe
        }
 }
 
-static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, 
-                                   snd_info_buffer_t * buffer)
+static void snd_ac97_proc_regs_read(struct snd_info_entry *entry, 
+                                   struct snd_info_buffer *buffer)
 {
-       ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
+       struct snd_ac97 *ac97 = entry->private_data;
 
+       mutex_lock(&ac97->page_mutex);
        if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {        // Analog Devices AD1881/85/86
 
                int idx;
-               down(&ac97->spec.ad18xx.mutex);
                for (idx = 0; idx < 3; idx++)
                        if (ac97->spec.ad18xx.id[idx]) {
                                /* select single codec */
@@ -292,15 +416,15 @@ static void snd_ac97_proc_regs_read(snd_info_entry_t *entry,
                        }
                /* select all codecs */
                snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
-               up(&ac97->spec.ad18xx.mutex);
        } else {
                snd_ac97_proc_regs_read_main(ac97, buffer, 0);
        }       
+       mutex_unlock(&ac97->page_mutex);
 }
 
-void snd_ac97_proc_init(ac97_t * ac97)
+void snd_ac97_proc_init(struct snd_ac97 * ac97)
 {
-       snd_info_entry_t *entry;
+       struct snd_info_entry *entry;
        char name[32];
        const char *prefix;
 
@@ -319,6 +443,11 @@ void snd_ac97_proc_init(ac97_t * ac97)
        sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
        if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
                snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read);
+#ifdef CONFIG_SND_DEBUG
+               entry->mode |= S_IWUSR;
+               entry->c.text.write_size = 1024;
+               entry->c.text.write = snd_ac97_proc_regs_write;
+#endif
                if (snd_info_register(entry) < 0) {
                        snd_info_free_entry(entry);
                        entry = NULL;
@@ -327,7 +456,7 @@ void snd_ac97_proc_init(ac97_t * ac97)
        ac97->proc_regs = entry;
 }
 
-void snd_ac97_proc_done(ac97_t * ac97)
+void snd_ac97_proc_done(struct snd_ac97 * ac97)
 {
        if (ac97->proc_regs) {
                snd_info_unregister(ac97->proc_regs);
@@ -339,9 +468,9 @@ void snd_ac97_proc_done(ac97_t * ac97)
        }
 }
 
-void snd_ac97_bus_proc_init(ac97_bus_t * bus)
+void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
 {
-       snd_info_entry_t *entry;
+       struct snd_info_entry *entry;
        char name[32];
 
        sprintf(name, "codec97#%d", bus->num);
@@ -355,7 +484,7 @@ void snd_ac97_bus_proc_init(ac97_bus_t * bus)
        bus->proc = entry;
 }
 
-void snd_ac97_bus_proc_done(ac97_bus_t * bus)
+void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus)
 {
        if (bus->proc) {
                snd_info_unregister(bus->proc);