Merge branch 'for-linus' of git://gitorious.org/linux-omap-dss2/linux
[safe/jmp/linux-2.6] / sound / pci / hda / hda_codec.c
index 0cf2424..f98b47c 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/tlv.h>
 #include <sound/initval.h>
 #include "hda_local.h"
+#include "hda_beep.h"
 #include <sound/hda_hwdep.h>
 
 /*
@@ -44,10 +45,12 @@ struct hda_vendor_id {
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1002, "ATI" },
+       { 0x1013, "Cirrus Logic" },
        { 0x1057, "Motorola" },
        { 0x1095, "Silicon Image" },
        { 0x10de, "Nvidia" },
        { 0x10ec, "Realtek" },
+       { 0x1102, "Creative" },
        { 0x1106, "VIA" },
        { 0x111d, "IDT" },
        { 0x11c1, "LSI" },
@@ -91,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec);
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 #endif
 
+/**
+ * snd_hda_get_jack_location - Give a location string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
 const char *snd_hda_get_jack_location(u32 cfg)
 {
        static char *bases[7] = {
@@ -118,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg)
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
 
+/**
+ * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
 const char *snd_hda_get_jack_connectivity(u32 cfg)
 {
        static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
@@ -126,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
 
+/**
+ * snd_hda_get_jack_type - Give a type string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
 const char *snd_hda_get_jack_type(u32 cfg)
 {
        static char *jack_types[16] = {
@@ -149,7 +173,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
 {
        u32 val;
 
-       val = (u32)(codec->addr & 0x0f) << 28;
+       if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
+           (verb & ~0xfff) || (parm & ~0xffff)) {
+               printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
+                      codec->addr, direct, nid, verb, parm);
+               return ~0;
+       }
+
+       val = (u32)codec->addr << 28;
        val |= (u32)direct << 27;
        val |= (u32)nid << 20;
        val |= verb << 8;
@@ -157,6 +188,42 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
        return val;
 }
 
+/*
+ * Send and receive a verb
+ */
+static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
+                          unsigned int *res)
+{
+       struct hda_bus *bus = codec->bus;
+       int err;
+
+       if (cmd == ~0)
+               return -1;
+
+       if (res)
+               *res = -1;
+ again:
+       snd_hda_power_up(codec);
+       mutex_lock(&bus->cmd_mutex);
+       err = bus->ops.command(bus, cmd);
+       if (!err && res)
+               *res = bus->ops.get_response(bus, codec->addr);
+       mutex_unlock(&bus->cmd_mutex);
+       snd_hda_power_down(codec);
+       if (res && *res == -1 && bus->rirb_error) {
+               if (bus->response_reset) {
+                       snd_printd("hda_codec: resetting BUS due to "
+                                  "fatal communication error\n");
+                       bus->ops.bus_reset(bus);
+               }
+               goto again;
+       }
+       /* clear reset-flag when the communication gets recovered */
+       if (!err)
+               bus->response_reset = 0;
+       return err;
+}
+
 /**
  * snd_hda_codec_read - send a command and get the response
  * @codec: the HDA codec
@@ -173,18 +240,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                int direct,
                                unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
+       unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
        unsigned int res;
-
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       if (!bus->ops.command(bus, res))
-               res = bus->ops.get_response(bus);
-       else
-               res = (unsigned int)-1;
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
+       codec_exec_verb(codec, cmd, &res);
        return res;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_read);
@@ -204,17 +262,10 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
                         unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
+       unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
        unsigned int res;
-       int err;
-
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       err = bus->ops.command(bus, res);
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
-       return err;
+       return codec_exec_verb(codec, cmd,
+                              codec->bus->sync_write ? &res : NULL);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
@@ -273,11 +324,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
        unsigned int parm;
        int i, conn_len, conns;
        unsigned int shift, num_elems, mask;
+       unsigned int wcaps;
        hda_nid_t prev_nid;
 
        if (snd_BUG_ON(!conn_list || max_conns <= 0))
                return -EINVAL;
 
+       wcaps = get_wcaps(codec, nid);
+       if (!(wcaps & AC_WCAP_CONN_LIST) &&
+           get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
+               snd_printk(KERN_WARNING "hda_codec: "
+                          "connection list not available for 0x%x\n", nid);
+               return -EINVAL;
+       }
+
        parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
        if (parm & AC_CLIST_LONG) {
                /* long form */
@@ -298,6 +358,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                /* single connection */
                parm = snd_hda_codec_read(codec, nid, 0,
                                          AC_VERB_GET_CONNECT_LIST, 0);
+               if (parm == -1 && codec->bus->rirb_error)
+                       return -EIO;
                conn_list[0] = parm & mask;
                return 1;
        }
@@ -309,11 +371,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                int range_val;
                hda_nid_t val, n;
 
-               if (i % num_elems == 0)
+               if (i % num_elems == 0) {
                        parm = snd_hda_codec_read(codec, nid, 0,
                                                  AC_VERB_GET_CONNECT_LIST, i);
+                       if (parm == -1 && codec->bus->rirb_error)
+                               return -EIO;
+               }
                range_val = !!(parm & (1 << (shift-1))); /* ranges */
                val = parm & mask;
+               if (val == 0) {
+                       snd_printk(KERN_WARNING "hda_codec: "
+                                  "invalid CONNECT_LIST verb %x[%i]:%x\n",
+                                   nid, i, parm);
+                       return 0;
+               }
                parm >>= shift;
                if (range_val) {
                        /* ranges between the previous and this one */
@@ -466,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
        struct hda_codec *codec;
        list_for_each_entry(codec, &bus->codec_list, list) {
                snd_hda_hwdep_add_sysfs(codec);
+               snd_hda_hwdep_add_power_sysfs(codec);
        }
        return 0;
 }
@@ -487,7 +559,6 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
 {
        struct hda_bus *bus;
        int err;
-       char qname[8];
        static struct snd_device_ops dev_ops = {
                .dev_register = snd_hda_bus_dev_register,
                .dev_free = snd_hda_bus_dev_free,
@@ -517,10 +588,12 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
        mutex_init(&bus->cmd_mutex);
        INIT_LIST_HEAD(&bus->codec_list);
 
-       snprintf(qname, sizeof(qname), "hda%d", card->number);
-       bus->workq = create_workqueue(qname);
+       snprintf(bus->workq_name, sizeof(bus->workq_name),
+                "hd-audio%d", card->number);
+       bus->workq = create_singlethread_workqueue(bus->workq_name);
        if (!bus->workq) {
-               snd_printk(KERN_ERR "cannot create workqueue %s\n", qname);
+               snd_printk(KERN_ERR "cannot create workqueue %s\n",
+                          bus->workq_name);
                kfree(bus);
                return -ENOMEM;
        }
@@ -612,7 +685,10 @@ static int get_codec_name(struct hda_codec *codec)
        const struct hda_vendor_id *c;
        const char *vendor = NULL;
        u16 vendor_id = codec->vendor_id >> 16;
-       char tmp[16], name[32];
+       char tmp[16];
+
+       if (codec->vendor_name)
+               goto get_chip_name;
 
        for (c = hda_vendor_ids; c->id; c++) {
                if (c->id == vendor_id) {
@@ -624,14 +700,21 @@ static int get_codec_name(struct hda_codec *codec)
                sprintf(tmp, "Generic %04x", vendor_id);
                vendor = tmp;
        }
+       codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
+       if (!codec->vendor_name)
+               return -ENOMEM;
+
+ get_chip_name:
+       if (codec->chip_name)
+               return 0;
+
        if (codec->preset && codec->preset->name)
-               snprintf(name, sizeof(name), "%s %s", vendor,
-                        codec->preset->name);
-       else
-               snprintf(name, sizeof(name), "%s ID %x", vendor,
-                        codec->vendor_id & 0xffff);
-       codec->name = kstrdup(name, GFP_KERNEL);
-       if (!codec->name)
+               codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
+       else {
+               sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
+               codec->chip_name = kstrdup(tmp, GFP_KERNEL);
+       }
+       if (!codec->chip_name)
                return -ENOMEM;
        return 0;
 }
@@ -641,19 +724,21 @@ static int get_codec_name(struct hda_codec *codec)
  */
 static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
 {
-       int i, total_nodes;
+       int i, total_nodes, function_id;
        hda_nid_t nid;
 
        total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
        for (i = 0; i < total_nodes; i++, nid++) {
-               unsigned int func;
-               func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE);
-               switch (func & 0xff) {
+               function_id = snd_hda_param_read(codec, nid,
+                                               AC_PAR_FUNCTION_TYPE) & 0xff;
+               switch (function_id) {
                case AC_GRP_AUDIO_FUNCTION:
                        codec->afg = nid;
+                       codec->function_id = function_id;
                        break;
                case AC_GRP_MODEM_FUNCTION:
                        codec->mfg = nid;
+                       codec->function_id = function_id;
                        break;
                default:
                        break;
@@ -681,11 +766,157 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
        return 0;
 }
 
+/* read all pin default configurations and save codec->init_pins */
+static int read_pin_defaults(struct hda_codec *codec)
+{
+       int i;
+       hda_nid_t nid = codec->start_nid;
+
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               struct hda_pincfg *pin;
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wcaps);
+               if (wid_type != AC_WID_PIN)
+                       continue;
+               pin = snd_array_new(&codec->init_pins);
+               if (!pin)
+                       return -ENOMEM;
+               pin->nid = nid;
+               pin->cfg = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+       }
+       return 0;
+}
+
+/* look up the given pin config list and return the item matching with NID */
+static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
+                                        struct snd_array *array,
+                                        hda_nid_t nid)
+{
+       int i;
+       for (i = 0; i < array->used; i++) {
+               struct hda_pincfg *pin = snd_array_elem(array, i);
+               if (pin->nid == nid)
+                       return pin;
+       }
+       return NULL;
+}
+
+/* write a config value for the given NID */
+static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
+                      unsigned int cfg)
+{
+       int i;
+       for (i = 0; i < 4; i++) {
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
+                                   cfg & 0xff);
+               cfg >>= 8;
+       }
+}
+
+/* set the current pin config value for the given NID.
+ * the value is cached, and read via snd_hda_codec_get_pincfg()
+ */
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+                      hda_nid_t nid, unsigned int cfg)
+{
+       struct hda_pincfg *pin;
+       unsigned int oldcfg;
+
+       oldcfg = snd_hda_codec_get_pincfg(codec, nid);
+       pin = look_up_pincfg(codec, list, nid);
+       if (!pin) {
+               pin = snd_array_new(list);
+               if (!pin)
+                       return -ENOMEM;
+               pin->nid = nid;
+       }
+       pin->cfg = cfg;
+
+       /* change only when needed; e.g. if the pincfg is already present
+        * in user_pins[], don't write it
+        */
+       cfg = snd_hda_codec_get_pincfg(codec, nid);
+       if (oldcfg != cfg)
+               set_pincfg(codec, nid, cfg);
+       return 0;
+}
+
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
+int snd_hda_codec_set_pincfg(struct hda_codec *codec,
+                            hda_nid_t nid, unsigned int cfg)
+{
+       return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
+
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_pincfg *pin;
+
+#ifdef CONFIG_SND_HDA_HWDEP
+       pin = look_up_pincfg(codec, &codec->user_pins, nid);
+       if (pin)
+               return pin->cfg;
+#endif
+       pin = look_up_pincfg(codec, &codec->driver_pins, nid);
+       if (pin)
+               return pin->cfg;
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (pin)
+               return pin->cfg;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
+
+/* restore all current pin configs */
+static void restore_pincfgs(struct hda_codec *codec)
+{
+       int i;
+       for (i = 0; i < codec->init_pins.used; i++) {
+               struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+               set_pincfg(codec, pin->nid,
+                          snd_hda_codec_get_pincfg(codec, pin->nid));
+       }
+}
 
 static void init_hda_cache(struct hda_cache_rec *cache,
                           unsigned int record_size);
 static void free_hda_cache(struct hda_cache_rec *cache);
 
+/* restore the initial pin cfgs and release all pincfg lists */
+static void restore_init_pincfgs(struct hda_codec *codec)
+{
+       /* first free driver_pins and user_pins, then call restore_pincfg
+        * so that only the values in init_pins are restored
+        */
+       snd_array_free(&codec->driver_pins);
+#ifdef CONFIG_SND_HDA_HWDEP
+       snd_array_free(&codec->user_pins);
+#endif
+       restore_pincfgs(codec);
+       snd_array_free(&codec->init_pins);
+}
+
 /*
  * codec destructor
  */
@@ -693,6 +924,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 {
        if (!codec)
                return;
+       restore_init_pincfgs(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
        flush_workqueue(codec->bus->workq);
@@ -705,12 +937,16 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        module_put(codec->owner);
        free_hda_cache(&codec->amp_cache);
        free_hda_cache(&codec->cmd_cache);
-       kfree(codec->name);
+       kfree(codec->vendor_name);
+       kfree(codec->chip_name);
        kfree(codec->modelname);
        kfree(codec->wcaps);
        kfree(codec);
 }
 
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+                               unsigned int power_state);
+
 /**
  * snd_hda_codec_new - create a HDA codec
  * @bus: the bus to assign
@@ -720,7 +956,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
  * Returns 0 if successful, or a negative error code.
  */
 int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                                   int do_init, struct hda_codec **codecp)
+                                   struct hda_codec **codecp)
 {
        struct hda_codec *codec;
        char component[31];
@@ -749,7 +985,9 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        mutex_init(&codec->control_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-       snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
+       snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
+       snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        if (codec->bus->modelname) {
                codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
                if (!codec->modelname) {
@@ -786,15 +1024,18 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        setup_fg_nodes(codec);
        if (!codec->afg && !codec->mfg) {
                snd_printdd("hda_codec: no AFG or MFG node found\n");
-               snd_hda_codec_free(codec);
-               return -ENODEV;
+               err = -ENODEV;
+               goto error;
        }
 
-       if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
+       err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
+       if (err < 0) {
                snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
-               snd_hda_codec_free(codec);
-               return -ENOMEM;
+               goto error;
        }
+       err = read_pin_defaults(codec);
+       if (err < 0)
+               goto error;
 
        if (!codec->subsystem_id) {
                hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
@@ -802,16 +1043,12 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
                        snd_hda_codec_read(codec, nid, 0,
                                           AC_VERB_GET_SUBSYSTEM_ID, 0);
        }
-       if (bus->modelname)
-               codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
 
-       if (do_init) {
-               err = snd_hda_codec_configure(codec);
-               if (err < 0) {
-                       snd_hda_codec_free(codec);
-                       return err;
-               }
-       }
+       /* power-up all before initialization */
+       hda_set_power_state(codec,
+                           codec->afg ? codec->afg : codec->mfg,
+                           AC_PWRST_D0);
+
        snd_hda_codec_proc_new(codec);
 
        snd_hda_create_hwdep(codec);
@@ -823,23 +1060,32 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        if (codecp)
                *codecp = codec;
        return 0;
+
+ error:
+       snd_hda_codec_free(codec);
+       return err;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
 int snd_hda_codec_configure(struct hda_codec *codec)
 {
        int err;
 
        codec->preset = find_codec_preset(codec);
-       if (!codec->name) {
+       if (!codec->vendor_name || !codec->chip_name) {
                err = get_codec_name(codec);
                if (err < 0)
                        return err;
        }
-       /* audio codec should override the mixer name */
-       if (codec->afg || !*codec->bus->card->mixername)
-               strlcpy(codec->bus->card->mixername, codec->name,
-                       sizeof(codec->bus->card->mixername));
 
        if (is_generic_config(codec)) {
                err = snd_hda_parse_generic_codec(codec);
@@ -858,8 +1104,14 @@ int snd_hda_codec_configure(struct hda_codec *codec)
  patched:
        if (!err && codec->patch_ops.unsol_event)
                err = init_unsol_queue(codec->bus);
+       /* audio codec should override the mixer name */
+       if (!err && (codec->afg || !*codec->bus->card->mixername))
+               snprintf(codec->bus->card->mixername,
+                        sizeof(codec->bus->card->mixername),
+                        "%s %s", codec->vendor_name, codec->chip_name);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
 
 /**
  * snd_hda_codec_setup_stream - set up the codec for streaming
@@ -886,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
+/**
+ * snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ */
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
@@ -906,6 +1163,9 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 /* FIXME: more better hash key? */
 #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
+#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
+#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
 #define INFO_AMP_CAPS  (1<<0)
 #define INFO_AMP_VOL(ch)       (1 << (1 + (ch)))
 
@@ -958,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
        return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 
-/*
- * query AMP capabilities for the given widget and direction
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
@@ -982,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
 {
@@ -996,6 +1278,81 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
+               unsigned int (*func)(struct hda_codec *, hda_nid_t))
+{
+       struct hda_amp_info *info;
+
+       info = get_alloc_amp_hash(codec, key);
+       if (!info)
+               return 0;
+       if (!info->head.val) {
+               info->head.val |= INFO_AMP_CAPS;
+               info->amp_caps = func(codec, nid);
+       }
+       return info->amp_caps;
+}
+
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+}
+
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+                              read_pin_cap);
+}
+EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+       u32 pincap;
+
+       if (!codec->no_trigger_sense) {
+               pincap = snd_hda_query_pin_caps(codec, nid);
+               if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+                       snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+       }
+       return snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_PIN_SENSE, 0);
+}
+EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status.
+ */
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+        u32 sense = snd_hda_pin_sense(codec, nid);
+        return !!(sense & AC_PINSENSE_PRESENCE);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+
 /*
  * read the current volume to info
  * if the cache exists, read the cache value.
@@ -1036,8 +1393,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        info->vol[ch] = val;
 }
 
-/*
- * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+/**
+ * snd_hda_codec_amp_read - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ *
+ * Read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
 int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                           int direction, int index)
@@ -1050,8 +1414,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
-/*
- * update the AMP value, mask = bit mask to set, val = the value
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
  */
 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
                             int direction, int idx, int mask, int val)
@@ -1070,8 +1444,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
-/*
- * update the AMP stereo with the same mask and value
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
  */
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                             int direction, int idx, int mask, int val)
@@ -1085,7 +1468,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 #ifdef SND_HDA_NEEDS_RESUME
-/* resume the all amp commands from the cache */
+/**
+ * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Resume the all amp commands from the cache.
+ */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
        struct hda_amp_info *buffer = codec->amp_cache.buf.list;
@@ -1111,7 +1499,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
-/* volume */
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
 {
@@ -1167,6 +1560,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
                                        HDA_AMP_VOLMASK, val);
 }
 
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1186,6 +1585,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1210,6 +1615,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
+/**
+ * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                          unsigned int size, unsigned int __user *_tlv)
 {
@@ -1239,8 +1650,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
-/*
- * set (static) TLV for virtual master volume; recalculated as max 0dB
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
  */
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
                             unsigned int *tlv)
@@ -1268,10 +1687,19 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
        memset(&id, 0, sizeof(id));
        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
        id.index = idx;
+       if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+               return NULL;
        strcpy(id.name, name);
        return snd_ctl_find_id(codec->bus->card, &id);
 }
 
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
                                            const char *name)
 {
@@ -1279,37 +1707,116 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
-/* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+/**
+ * snd_hda_ctl-add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+                   struct snd_kcontrol *kctl)
 {
        int err;
-       struct snd_kcontrol **knewp;
+       struct hda_nid_item *item;
 
+       if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+               if (nid == 0)
+                       nid = kctl->id.subdevice & 0xffff;
+               kctl->id.subdevice = 0;
+       }
        err = snd_ctl_add(codec->bus->card, kctl);
        if (err < 0)
                return err;
-       knewp = snd_array_new(&codec->mixers);
-       if (!knewp)
+       item = snd_array_new(&codec->mixers);
+       if (!item)
                return -ENOMEM;
-       *knewp = kctl;
+       item->kctl = kctl;
+       item->nid = nid;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
-#ifdef CONFIG_SND_HDA_RECONFIG
-/* Clear all controls assigned to the given codec */
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
        int i;
-       struct snd_kcontrol **kctls = codec->mixers.list;
+       struct hda_nid_item *items = codec->mixers.list;
        for (i = 0; i < codec->mixers.used; i++)
-               snd_ctl_remove(codec->bus->card, kctls[i]);
+               snd_ctl_remove(codec->bus->card, items[i].kctl);
        snd_array_free(&codec->mixers);
 }
 
-void snd_hda_codec_reset(struct hda_codec *codec)
+/* pseudo device locking
+ * toggle card->shutdown to allow/disallow the device access (as a hack)
+ */
+static int hda_lock_devices(struct snd_card *card)
 {
-       int i;
+       spin_lock(&card->files_lock);
+       if (card->shutdown) {
+               spin_unlock(&card->files_lock);
+               return -EINVAL;
+       }
+       card->shutdown = 1;
+       spin_unlock(&card->files_lock);
+       return 0;
+}
+
+static void hda_unlock_devices(struct snd_card *card)
+{
+       spin_lock(&card->files_lock);
+       card->shutdown = 0;
+       spin_unlock(&card->files_lock);
+}
+
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY.  If successfully freed,
+ * returns zero.
+ */
+int snd_hda_codec_reset(struct hda_codec *codec)
+{
+       struct snd_card *card = codec->bus->card;
+       int i, pcm;
+
+       if (hda_lock_devices(card) < 0)
+               return -EBUSY;
+       /* check whether the codec isn't used by any mixer or PCM streams */
+       if (!list_empty(&card->ctl_files)) {
+               hda_unlock_devices(card);
+               return -EBUSY;
+       }
+       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+               if (!cpcm->pcm)
+                       continue;
+               if (cpcm->pcm->streams[0].substream_opened ||
+                   cpcm->pcm->streams[1].substream_opened) {
+                       hda_unlock_devices(card);
+                       return -EBUSY;
+               }
+       }
+
+       /* OK, let it free */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
@@ -1319,8 +1826,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
        /* relase PCMs */
        for (i = 0; i < codec->num_pcms; i++) {
                if (codec->pcm_info[i].pcm) {
-                       snd_device_free(codec->bus->card,
-                                       codec->pcm_info[i].pcm);
+                       snd_device_free(card, codec->pcm_info[i].pcm);
                        clear_bit(codec->pcm_info[i].device,
                                  codec->bus->pcm_dev_bits);
                }
@@ -1333,15 +1839,39 @@ void snd_hda_codec_reset(struct hda_codec *codec)
        free_hda_cache(&codec->cmd_cache);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+       /* free only driver_pins so that init_pins + user_pins are restored */
+       snd_array_free(&codec->driver_pins);
+       restore_pincfgs(codec);
        codec->num_pcms = 0;
        codec->pcm_info = NULL;
        codec->preset = NULL;
+       memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+       codec->slave_dig_outs = NULL;
+       codec->spdif_status_reset = 0;
        module_put(codec->owner);
        codec->owner = NULL;
+
+       /* allow device access again */
+       hda_unlock_devices(card);
+       return 0;
 }
-#endif /* CONFIG_SND_HDA_RECONFIG */
 
-/* create a virtual master control and add slaves */
+/**
+ * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @slaves: slave control names (optional)
+ *
+ * Create a virtual master control with the given name.  The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @slaves is a NULL-terminated array of strings, each of which is a
+ * slave control name.  All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
                        unsigned int *tlv, const char **slaves)
 {
@@ -1358,27 +1888,37 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        kctl = snd_ctl_make_virtual_master(name, tlv);
        if (!kctl)
                return -ENOMEM;
-       err = snd_hda_ctl_add(codec, kctl);
+       err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
        
        for (s = slaves; *s; s++) {
                struct snd_kcontrol *sctl;
-
-               sctl = snd_hda_find_mixer_ctl(codec, *s);
-               if (!sctl) {
-                       snd_printdd("Cannot find slave %s, skipped\n", *s);
-                       continue;
+               int i = 0;
+               for (;;) {
+                       sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
+                       if (!sctl) {
+                               if (!i)
+                                       snd_printdd("Cannot find slave %s, "
+                                                   "skipped\n", *s);
+                               break;
+                       }
+                       err = snd_ctl_add_slave(kctl, sctl);
+                       if (err < 0)
+                               return err;
+                       i++;
                }
-               err = snd_ctl_add_slave(kctl, sctl);
-               if (err < 0)
-                       return err;
        }
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
-/* switch */
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
 {
@@ -1392,6 +1932,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1412,6 +1958,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1443,6 +1995,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
+ *
+ * This function calls snd_hda_enable_beep_device(), which behaves differently
+ * depending on beep_mode option.
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       snd_hda_enable_beep_device(codec, *valp);
+       return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
 /*
  * bound volume controls
  *
@@ -1452,6 +2023,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 #define AMP_VAL_IDX_SHIFT      19
 #define AMP_VAL_IDX_MASK       (0x0f<<19)
 
+/**
+ * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
@@ -1469,6 +2046,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
+/**
+ * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
@@ -1493,8 +2076,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
-/*
- * generic bound volume/swtich controls
+/**
+ * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
  */
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
@@ -1513,6 +2099,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
+/**
+ * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
@@ -1530,6 +2122,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
+/**
+ * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
@@ -1553,6 +2151,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
+/**
+ * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() macro.
+ */
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                           unsigned int size, unsigned int __user *tlv)
 {
@@ -1836,7 +2440,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
                        return -ENOMEM;
                kctl->id.index = idx;
                kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, nid, kctl);
                if (err < 0)
                        return err;
        }
@@ -1875,14 +2479,19 @@ static struct snd_kcontrol_new spdif_share_sw = {
        .put = spdif_share_sw_put,
 };
 
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
 int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
                                  struct hda_multi_out *mout)
 {
        if (!mout->dig_out_nid)
                return 0;
        /* ATTENTION: here mout is passed as private_data, instead of codec */
-       return snd_hda_ctl_add(codec,
-                          snd_ctl_new1(&spdif_share_sw, mout));
+       return snd_hda_ctl_add(codec, mout->dig_out_nid,
+                             snd_ctl_new1(&spdif_share_sw, mout));
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
@@ -1983,8 +2592,10 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        }
        for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
+               if (!kctl)
+                       return -ENOMEM;
                kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, nid, kctl);
                if (err < 0)
                        return err;
        }
@@ -2021,28 +2632,31 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
-       unsigned int res;
-       int err;
+       int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+       struct hda_cache_head *c;
+       u32 key;
 
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       err = bus->ops.command(bus, res);
-       if (!err) {
-               struct hda_cache_head *c;
-               u32 key = build_cmd_cache_key(nid, verb);
-               c = get_alloc_hash(&codec->cmd_cache, key);
-               if (c)
-                       c->val = parm;
-       }
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
-       return err;
+       if (err < 0)
+               return err;
+       /* parm may contain the verb stuff for get/set amp */
+       verb = verb | (parm >> 8);
+       parm &= 0xff;
+       key = build_cmd_cache_key(nid, verb);
+       mutex_lock(&codec->bus->cmd_mutex);
+       c = get_alloc_hash(&codec->cmd_cache, key);
+       if (c)
+               c->val = parm;
+       mutex_unlock(&codec->bus->cmd_mutex);
+       return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
-/* resume the all commands from the cache */
+/**
+ * snd_hda_codec_resume_cache - Resume the all commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Execute all verbs recorded in the command caches to resume.
+ */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
        struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
@@ -2086,30 +2700,34 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
        hda_nid_t nid;
        int i;
 
-       snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+       /* this delay seems necessary to avoid click noise at power-down */
+       if (power_state == AC_PWRST_D3)
+               msleep(100);
+       snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
                            power_state);
-       msleep(10); /* partial workaround for "azx_get_response timeout" */
+       /* partial workaround for "azx_get_response timeout" */
+       if (power_state == AC_PWRST_D0)
+               msleep(10);
 
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                unsigned int wcaps = get_wcaps(codec, nid);
                if (wcaps & AC_WCAP_POWER) {
-                       unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
-                               AC_WCAP_TYPE_SHIFT;
-                       if (wid_type == AC_WID_PIN) {
+                       unsigned int wid_type = get_wcaps_type(wcaps);
+                       if (power_state == AC_PWRST_D3 &&
+                           wid_type == AC_WID_PIN) {
                                unsigned int pincap;
                                /*
                                 * don't power down the widget if it controls
                                 * eapd and EAPD_BTLENABLE is set.
                                 */
-                               pincap = snd_hda_param_read(codec, nid,
-                                                           AC_PAR_PIN_CAP);
+                               pincap = snd_hda_query_pin_caps(codec, nid);
                                if (pincap & AC_PINCAP_EAPD) {
                                        int eapd = snd_hda_codec_read(codec,
                                                nid, 0,
                                                AC_VERB_GET_EAPD_BTLENABLE, 0);
                                        eapd &= 0x02;
-                                       if (power_state == AC_PWRST_D3 && eapd)
+                                       if (eapd)
                                                continue;
                                }
                        }
@@ -2158,9 +2776,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       snd_hda_update_power_acct(codec);
        cancel_delayed_work(&codec->power_work);
        codec->power_on = 0;
        codec->power_transition = 0;
+       codec->power_jiffies = jiffies;
 #endif
 }
 
@@ -2172,6 +2792,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        hda_set_power_state(codec,
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D0);
+       restore_pincfgs(codec); /* restore all current pin configs */
        hda_exec_init_verbs(codec);
        if (codec->patch_ops.resume)
                codec->patch_ops.resume(codec);
@@ -2199,8 +2820,16 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
 
        list_for_each_entry(codec, &bus->codec_list, list) {
                int err = snd_hda_codec_build_controls(codec);
-               if (err < 0)
-                       return err;
+               if (err < 0) {
+                       printk(KERN_ERR "hda_codec: cannot build controls"
+                              "for #%d (error %d)\n", codec->addr, err); 
+                       err = snd_hda_codec_reset(codec);
+                       if (err < 0) {
+                               printk(KERN_ERR
+                                      "hda_codec: cannot revert codec\n");
+                               return err;
+                       }
+               }
        }
        return 0;
 }
@@ -2209,19 +2838,12 @@ EXPORT_SYMBOL_HDA(snd_hda_build_controls);
 int snd_hda_codec_build_controls(struct hda_codec *codec)
 {
        int err = 0;
-       /* fake as if already powered-on */
-       hda_keep_power_on(codec);
-       /* then fire up */
-       hda_set_power_state(codec,
-                           codec->afg ? codec->afg : codec->mfg,
-                           AC_PWRST_D0);
        hda_exec_init_verbs(codec);
        /* continue to initialize... */
        if (codec->patch_ops.init)
                err = codec->patch_ops.init(codec);
        if (!err && codec->patch_ops.build_controls)
                err = codec->patch_ops.build_controls(codec);
-       snd_hda_power_down(codec);
        if (err < 0)
                return err;
        return 0;
@@ -2301,7 +2923,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
        case 20:
        case 24:
        case 32:
-               if (maxbps >= 32)
+               if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
                        val |= 0x40;
                else if (maxbps >= 24)
                        val |= 0x30;
@@ -2318,6 +2940,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int val = 0;
+       if (nid != codec->afg &&
+           (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+       if (!val || val == -1)
+               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+       if (!val || val == -1)
+               return 0;
+       return val;
+}
+
+static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+                              get_pcm_param);
+}
+
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+       if (!streams || streams == -1)
+               streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+       if (!streams || streams == -1)
+               return 0;
+       return streams;
+}
+
+static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+                              get_stream_param);
+}
+
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
  * @codec: the HDA codec
@@ -2334,18 +2991,10 @@ EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 {
-       int i;
-       unsigned int val, streams;
+       unsigned int i, val, wcaps;
 
-       val = 0;
-       if (nid != codec->afg &&
-           (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
-               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
-               if (val == -1)
-                       return -EIO;
-       }
-       if (!val)
-               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+       wcaps = get_wcaps(codec, nid);
+       val = query_pcm_param(codec, nid);
 
        if (ratesp) {
                u32 rates = 0;
@@ -2353,24 +3002,23 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                        if (val & (1 << i))
                                rates |= rate_bits[i].alsa_bits;
                }
+               if (rates == 0) {
+                       snd_printk(KERN_ERR "hda_codec: rates == 0 "
+                                  "(nid=0x%x, val=0x%x, ovrd=%i)\n",
+                                       nid, val,
+                                       (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+                       return -EIO;
+               }
                *ratesp = rates;
        }
 
        if (formatsp || bpsp) {
                u64 formats = 0;
-               unsigned int bps;
-               unsigned int wcaps;
+               unsigned int streams, bps;
 
-               wcaps = get_wcaps(codec, nid);
-               streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
-               if (streams == -1)
+               streams = query_stream_param(codec, nid);
+               if (!streams)
                        return -EIO;
-               if (!streams) {
-                       streams = snd_hda_param_read(codec, codec->afg,
-                                                    AC_PAR_STREAM);
-                       if (streams == -1)
-                               return -EIO;
-               }
 
                bps = 0;
                if (streams & AC_SUPFMT_PCM) {
@@ -2402,11 +3050,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                        bps = 20;
                        }
                }
-               else if (streams == AC_SUPFMT_FLOAT32) {
-                       /* should be exclusive */
+               if (streams & AC_SUPFMT_FLOAT32) {
                        formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
-                       bps = 32;
-               } else if (streams == AC_SUPFMT_AC3) {
+                       if (!bps)
+                               bps = 32;
+               }
+               if (streams == AC_SUPFMT_AC3) {
                        /* should be exclusive */
                        /* temporary hack: we have still no proper support
                         * for the direct AC3 stream...
@@ -2414,6 +3063,15 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                        formats |= SNDRV_PCM_FMTBIT_U8;
                        bps = 8;
                }
+               if (formats == 0) {
+                       snd_printk(KERN_ERR "hda_codec: formats == 0 "
+                                  "(nid=0x%x, val=0x%x, ovrd=%i, "
+                                  "streams=0x%x)\n",
+                                       nid, val,
+                                       (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+                                       streams);
+                       return -EIO;
+               }
                if (formatsp)
                        *formatsp = formats;
                if (bpsp)
@@ -2424,8 +3082,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 }
 
 /**
- * snd_hda_is_supported_format - check whether the given node supports
- * the format val
+ * snd_hda_is_supported_format - Check the validity of the format
+ * @codec: HD-audio codec
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
  *
  * Returns 1 if supported, 0 if not.
  */
@@ -2435,17 +3097,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        int i;
        unsigned int val = 0, rate, stream;
 
-       if (nid != codec->afg &&
-           (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
-               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
-               if (val == -1)
-                       return 0;
-       }
-       if (!val) {
-               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
-               if (val == -1)
-                       return 0;
-       }
+       val = query_pcm_param(codec, nid);
+       if (!val)
+               return 0;
 
        rate = format & 0xff00;
        for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
@@ -2457,12 +3111,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        if (i >= AC_PAR_PCM_RATE_BITS)
                return 0;
 
-       stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
-       if (stream == -1)
-               return 0;
-       if (!stream && nid != codec->afg)
-               stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
-       if (!stream || stream == -1)
+       stream = query_stream_param(codec, nid);
+       if (!stream)
                return 0;
 
        if (stream & AC_SUPFMT_PCM) {
@@ -2529,12 +3179,16 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
 static int set_pcm_default_values(struct hda_codec *codec,
                                  struct hda_pcm_stream *info)
 {
+       int err;
+
        /* query support PCM information from the given NID */
        if (info->nid && (!info->rates || !info->formats)) {
-               snd_hda_query_supported_pcm(codec, info->nid,
+               err = snd_hda_query_supported_pcm(codec, info->nid,
                                info->rates ? NULL : &info->rates,
                                info->formats ? NULL : &info->formats,
                                info->maxbps ? NULL : &info->maxbps);
+               if (err < 0)
+                       return err;
        }
        if (info->ops.open == NULL)
                info->ops.open = hda_pcm_default_open_close;
@@ -2553,53 +3207,36 @@ static int set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+       "Audio", "SPDIF", "HDMI", "Modem"
+};
+
 /*
  * get the empty PCM device number to assign
  */
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 {
-       static const char *dev_name[HDA_PCM_NTYPES] = {
-               "Audio", "SPDIF", "HDMI", "Modem"
-       };
-       /* starting device index for each PCM type */
-       static int dev_idx[HDA_PCM_NTYPES] = {
-               [HDA_PCM_TYPE_AUDIO] = 0,
-               [HDA_PCM_TYPE_SPDIF] = 1,
-               [HDA_PCM_TYPE_HDMI] = 3,
-               [HDA_PCM_TYPE_MODEM] = 6
+       /* audio device indices; not linear to keep compatibility */
+       static int audio_idx[HDA_PCM_NTYPES][5] = {
+               [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+               [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+               [HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
+               [HDA_PCM_TYPE_MODEM] = { 6, -1 },
        };
-       /* normal audio device indices; not linear to keep compatibility */
-       static int audio_idx[4] = { 0, 2, 4, 5 };
-       int i, dev;
-
-       switch (type) {
-       case HDA_PCM_TYPE_AUDIO:
-               for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
-                       dev = audio_idx[i];
-                       if (!test_bit(dev, bus->pcm_dev_bits))
-                               break;
-               }
-               if (i >= ARRAY_SIZE(audio_idx)) {
-                       snd_printk(KERN_WARNING "Too many audio devices\n");
-                       return -EAGAIN;
-               }
-               break;
-       case HDA_PCM_TYPE_SPDIF:
-       case HDA_PCM_TYPE_HDMI:
-       case HDA_PCM_TYPE_MODEM:
-               dev = dev_idx[type];
-               if (test_bit(dev, bus->pcm_dev_bits)) {
-                       snd_printk(KERN_WARNING "%s already defined\n",
-                                  dev_name[type]);
-                       return -EAGAIN;
-               }
-               break;
-       default:
+       int i;
+
+       if (type >= HDA_PCM_NTYPES) {
                snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
                return -EINVAL;
        }
-       set_bit(dev, bus->pcm_dev_bits);
-       return dev;
+
+       for (i = 0; audio_idx[type][i] >= 0 ; i++)
+               if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+                       return audio_idx[type][i];
+
+       snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+       return -EAGAIN;
 }
 
 /*
@@ -2634,24 +3271,36 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
                if (!codec->patch_ops.build_pcms)
                        return 0;
                err = codec->patch_ops.build_pcms(codec);
-               if (err < 0)
-                       return err;
+               if (err < 0) {
+                       printk(KERN_ERR "hda_codec: cannot build PCMs"
+                              "for #%d (error %d)\n", codec->addr, err); 
+                       err = snd_hda_codec_reset(codec);
+                       if (err < 0) {
+                               printk(KERN_ERR
+                                      "hda_codec: cannot revert codec\n");
+                               return err;
+                       }
+               }
        }
        for (pcm = 0; pcm < codec->num_pcms; pcm++) {
                struct hda_pcm *cpcm = &codec->pcm_info[pcm];
                int dev;
 
                if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-                       return 0; /* no substreams assigned */
+                       continue; /* no substreams assigned */
 
                if (!cpcm->pcm) {
                        dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
                        if (dev < 0)
-                               return 0;
+                               continue; /* no fatal error */
                        cpcm->device = dev;
                        err = snd_hda_attach_pcm(codec, cpcm);
-                       if (err < 0)
-                               return err;
+                       if (err < 0) {
+                               printk(KERN_ERR "hda_codec: cannot attach "
+                                      "PCM stream %d for codec #%d\n",
+                                      dev, codec->addr);
+                               continue; /* no fatal error */
+                       }
                }
        }
        return 0;
@@ -2793,7 +3442,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
        tbl = q;
 
        if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
                char tmp[10];
                const char *model = NULL;
                if (models)
@@ -2825,14 +3474,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
  */
 int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 {
-       int err;
+       int err;
 
        for (; knew->name; knew++) {
                struct snd_kcontrol *kctl;
                kctl = snd_ctl_new1(knew, codec);
                if (!kctl)
                        return -ENOMEM;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, 0, kctl);
                if (err < 0) {
                        if (!codec->addr)
                                return err;
@@ -2840,7 +3489,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->id.device = codec->addr;
-                       err = snd_hda_ctl_add(codec, kctl);
+                       err = snd_hda_ctl_add(codec, 0, kctl);
                        if (err < 0)
                                return err;
                }
@@ -2873,8 +3522,27 @@ static void hda_keep_power_on(struct hda_codec *codec)
 {
        codec->power_count++;
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
+}
+
+/* update the power on/off account with the current jiffies */
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+       unsigned long delta = jiffies - codec->power_jiffies;
+       if (codec->power_on)
+               codec->power_on_acct += delta;
+       else
+               codec->power_off_acct += delta;
+       codec->power_jiffies += delta;
 }
 
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */ 
 void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
@@ -2883,7 +3551,9 @@ void snd_hda_power_up(struct hda_codec *codec)
        if (codec->power_on || codec->power_transition)
                return;
 
+       snd_hda_update_power_acct(codec);
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
@@ -2895,9 +3565,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
 #define power_save(codec)      \
        ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 
-#define power_save(codec)      \
-       ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */ 
 void snd_hda_power_down(struct hda_codec *codec)
 {
        --codec->power_count;
@@ -2911,6 +3585,19 @@ void snd_hda_power_down(struct hda_codec *codec)
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list.  If it's in the list,
+ * check the current AMP status, and update the the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */ 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
                                 hda_nid_t nid)
@@ -2952,6 +3639,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 /*
  * Channel mode helper
  */
+
+/**
+ * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_info(struct hda_codec *codec,
                         struct snd_ctl_elem_info *uinfo,
                         const struct hda_channel_mode *chmode,
@@ -2968,6 +3659,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
+/**
+ * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_get(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
                        const struct hda_channel_mode *chmode,
@@ -2986,6 +3680,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
+/**
+ * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_put(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
                        const struct hda_channel_mode *chmode,
@@ -3010,6 +3707,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 /*
  * input MUX helper
  */
+
+/**
+ * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_info(const struct hda_input_mux *imux,
                           struct snd_ctl_elem_info *uinfo)
 {
@@ -3028,6 +3729,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 }
 EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
+/**
+ * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
                          struct snd_ctl_elem_value *ucontrol,
@@ -3087,8 +3791,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
        }
 }
 
-/*
- * open the digital out in the exclusive mode
+/**
+ * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
+
+       if (!bus)
+               return;
+       list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               if (!codec->power_on)
+                       continue;
+#endif
+               if (codec->patch_ops.reboot_notify)
+                       codec->patch_ops.reboot_notify(codec);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
  */
 int snd_hda_multi_out_dig_open(struct hda_codec *codec,
                               struct hda_multi_out *mout)
@@ -3103,6 +3828,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ */
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
                                  unsigned int stream_tag,
@@ -3116,8 +3844,21 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
-/*
- * release the digital out
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ */
+int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
+                                 struct hda_multi_out *mout)
+{
+       mutex_lock(&codec->spdif_mutex);
+       cleanup_dig_out_stream(codec, mout->dig_out_nid);
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
+
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
  */
 int snd_hda_multi_out_dig_close(struct hda_codec *codec,
                                struct hda_multi_out *mout)
@@ -3129,8 +3870,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
-/*
- * set up more restrictions for analog out
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as slave, open the digital
+ * outputs, too.
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
@@ -3157,10 +3902,16 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
                }
                mutex_lock(&codec->spdif_mutex);
                if (mout->share_spdif) {
-                       runtime->hw.rates &= mout->spdif_rates;
-                       runtime->hw.formats &= mout->spdif_formats;
-                       if (mout->spdif_maxbps < hinfo->maxbps)
-                               hinfo->maxbps = mout->spdif_maxbps;
+                       if ((runtime->hw.rates & mout->spdif_rates) &&
+                           (runtime->hw.formats & mout->spdif_formats)) {
+                               runtime->hw.rates &= mout->spdif_rates;
+                               runtime->hw.formats &= mout->spdif_formats;
+                               if (mout->spdif_maxbps < hinfo->maxbps)
+                                       hinfo->maxbps = mout->spdif_maxbps;
+                       } else {
+                               mout->share_spdif = 0;
+                               /* FIXME: need notify? */
+                       }
                }
                mutex_unlock(&codec->spdif_mutex);
        }
@@ -3169,9 +3920,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
  */
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                                     struct hda_multi_out *mout,
@@ -3228,8 +3981,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
-/*
- * clean up the setting for analog out
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
  */
 int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
                                     struct hda_multi_out *mout)
@@ -3330,8 +4083,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
        end_nid = codec->start_nid + codec->num_nodes;
        for (nid = codec->start_nid; nid < end_nid; nid++) {
                unsigned int wid_caps = get_wcaps(codec, nid);
-               unsigned int wid_type =
-                       (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               unsigned int wid_type = get_wcaps_type(wid_caps);
                unsigned int def_conf;
                short assoc, loc;
 
@@ -3342,8 +4094,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                if (ignore_nids && is_in_nid_list(nid, ignore_nids))
                        continue;
 
-               def_conf = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
                if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
                        continue;
                loc = get_defcfg_location(def_conf);
@@ -3419,10 +4170,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        cfg->input_pins[AUTO_PIN_AUX] = nid;
                        break;
                case AC_JACK_SPDIF_OUT:
-                       cfg->dig_out_pin = nid;
+               case AC_JACK_DIG_OTHER_OUT:
+                       if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+                               continue;
+                       cfg->dig_out_pins[cfg->dig_outs] = nid;
+                       cfg->dig_out_type[cfg->dig_outs] =
+                               (loc == AC_JACK_LOC_HDMI) ?
+                               HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+                       cfg->dig_outs++;
                        break;
                case AC_JACK_SPDIF_IN:
+               case AC_JACK_DIG_OTHER_IN:
                        cfg->dig_in_pin = nid;
+                       if (loc == AC_JACK_LOC_HDMI)
+                               cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+                       else
+                               cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
                        break;
                }
        }
@@ -3528,6 +4291,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                   cfg->hp_pins[1], cfg->hp_pins[2],
                   cfg->hp_pins[3], cfg->hp_pins[4]);
        snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+       if (cfg->dig_outs)
+               snd_printd("   dig-out=0x%x/0x%x\n",
+                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
        snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
                   " cd=0x%x, aux=0x%x\n",
                   cfg->input_pins[AUTO_PIN_MIC],
@@ -3536,6 +4302,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                   cfg->input_pins[AUTO_PIN_FRONT_LINE],
                   cfg->input_pins[AUTO_PIN_CD],
                   cfg->input_pins[AUTO_PIN_AUX]);
+       if (cfg->dig_in_pin)
+               snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
 
        return 0;
 }
@@ -3556,11 +4324,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
 /**
  * snd_hda_suspend - suspend the codecs
  * @bus: the HDA bus
- * @state: suspsend state
  *
  * Returns 0 if successful.
  */
-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+int snd_hda_suspend(struct hda_bus *bus)
 {
        struct hda_codec *codec;
 
@@ -3601,8 +4368,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
  * generic arrays
  */
 
-/* get a new element from the given array
- * if it exceeds the pre-allocated array size, re-allocate the array
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ * 
+ * Get a new element from the given array.  If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
  */
 void *snd_array_new(struct snd_array *array)
 {
@@ -3626,7 +4399,10 @@ void *snd_array_new(struct snd_array *array)
 }
 EXPORT_SYMBOL_HDA(snd_array_new);
 
-/* free the given array elements */
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
 void snd_array_free(struct snd_array *array)
 {
        kfree(array->list);
@@ -3636,7 +4412,12 @@ void snd_array_free(struct snd_array *array)
 }
 EXPORT_SYMBOL_HDA(snd_array_free);
 
-/*
+/**
+ * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
  * used by hda_proc.c and hda_eld.c
  */
 void snd_print_pcm_rates(int pcm, char *buf, int buflen)
@@ -3655,6 +4436,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
 }
 EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
 
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
 void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 {
        static unsigned int bits[] = { 8, 16, 20, 24, 32 };