sched: fix fair preempt check
[safe/jmp/linux-2.6] / sound / pci / hda / hda_codec.c
index 1d31da4..ba1ab73 100644 (file)
@@ -19,7 +19,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <sound/initval.h>
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
-
+#include "hda_patch.h" /* codec presets */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* define this option here to hide as static */
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+                "(in second, 0 = disable).");
+#endif
 
 /*
  * vendor / preset table
@@ -45,20 +52,61 @@ struct hda_vendor_id {
 
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
-       { 0x10ec, "Realtek" },
+       { 0x1002, "ATI" },
        { 0x1057, "Motorola" },
+       { 0x1095, "Silicon Image" },
+       { 0x10ec, "Realtek" },
        { 0x1106, "VIA" },
+       { 0x111d, "IDT" },
+       { 0x11c1, "LSI" },
        { 0x11d4, "Analog Devices" },
        { 0x13f6, "C-Media" },
        { 0x14f1, "Conexant" },
+       { 0x17e8, "Chrontel" },
+       { 0x1854, "LG" },
+       { 0x1aec, "Wolfson Microelectronics" },
        { 0x434d, "C-Media" },
        { 0x8384, "SigmaTel" },
        {} /* terminator */
 };
 
-/* codec presets */
-#include "hda_patch.h"
+static const struct hda_codec_preset *hda_preset_tables[] = {
+#ifdef CONFIG_SND_HDA_CODEC_REALTEK
+       snd_hda_preset_realtek,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
+       snd_hda_preset_cmedia,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_ANALOG
+       snd_hda_preset_analog,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
+       snd_hda_preset_sigmatel,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_SI3054
+       snd_hda_preset_si3054,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
+       snd_hda_preset_atihdmi,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
+       snd_hda_preset_conexant,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_VIA
+       snd_hda_preset_via,
+#endif
+#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+       snd_hda_preset_nvhdmi,
+#endif
+       NULL
+};
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void hda_power_work(struct work_struct *work);
+static void hda_keep_power_on(struct hda_codec *codec);
+#else
+static inline void hda_keep_power_on(struct hda_codec *codec) {}
+#endif
 
 /**
  * snd_hda_codec_read - send a command and get the response
@@ -77,12 +125,14 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int verb, unsigned int parm)
 {
        unsigned int res;
+       snd_hda_power_up(codec);
        mutex_lock(&codec->bus->cmd_mutex);
        if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
                res = codec->bus->ops.get_response(codec);
        else
                res = (unsigned int)-1;
        mutex_unlock(&codec->bus->cmd_mutex);
+       snd_hda_power_down(codec);
        return res;
 }
 
@@ -102,9 +152,11 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
                         unsigned int verb, unsigned int parm)
 {
        int err;
+       snd_hda_power_up(codec);
        mutex_lock(&codec->bus->cmd_mutex);
        err = codec->bus->ops.command(codec, nid, direct, verb, parm);
        mutex_unlock(&codec->bus->cmd_mutex);
+       snd_hda_power_down(codec);
        return err;
 }
 
@@ -137,6 +189,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
        unsigned int parm;
 
        parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
+       if (parm == -1)
+               return 0;
        *start_id = (parm >> 16) & 0x7fff;
        return (int)(parm & 0x7fff);
 }
@@ -161,7 +215,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
        unsigned int shift, num_elems, mask;
        hda_nid_t prev_nid;
 
-       snd_assert(conn_list && max_conns > 0, return -EINVAL);
+       if (snd_BUG_ON(!conn_list || max_conns <= 0))
+               return -EINVAL;
 
        parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
        if (parm & AC_CLIST_LONG) {
@@ -263,7 +318,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
 }
 
 /*
- * process queueud unsolicited events
+ * process queued unsolicited events
  */
 static void process_unsol_events(struct work_struct *work)
 {
@@ -357,8 +412,10 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
                .dev_free = snd_hda_bus_dev_free,
        };
 
-       snd_assert(temp, return -EINVAL);
-       snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+       if (snd_BUG_ON(!temp))
+               return -EINVAL;
+       if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response))
+               return -EINVAL;
 
        if (busp)
                *busp = NULL;
@@ -409,6 +466,10 @@ find_codec_preset(struct hda_codec *codec)
        for (tbl = hda_preset_tables; *tbl; tbl++) {
                for (preset = *tbl; preset->id; preset++) {
                        u32 mask = preset->mask;
+                       if (preset->afg && preset->afg != codec->afg)
+                               continue;
+                       if (preset->mfg && preset->mfg != codec->mfg)
+                               continue;
                        if (!mask)
                                mask = ~0;
                        if (preset->id == (codec->vendor_id & mask) &&
@@ -496,7 +557,7 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
 
 static void init_hda_cache(struct hda_cache_rec *cache,
                           unsigned int record_size);
-static inline void free_hda_cache(struct hda_cache_rec *cache);
+static void free_hda_cache(struct hda_cache_rec *cache);
 
 /*
  * codec destructor
@@ -505,6 +566,10 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 {
        if (!codec)
                return;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       cancel_delayed_work(&codec->power_work);
+       flush_scheduled_work();
+#endif
        list_del(&codec->list);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
@@ -527,11 +592,13 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
                                struct hda_codec **codecp)
 {
        struct hda_codec *codec;
-       char component[13];
+       char component[31];
        int err;
 
-       snd_assert(bus, return -EINVAL);
-       snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+       if (snd_BUG_ON(!bus))
+               return -EINVAL;
+       if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+               return -EINVAL;
 
        if (bus->caddr_tbl[codec_addr]) {
                snd_printk(KERN_ERR "hda_codec: "
@@ -551,6 +618,15 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
+       /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
+        * the caller has to power down appropriatley after initialization
+        * phase.
+        */
+       hda_keep_power_on(codec);
+#endif
+
        list_add_tail(&codec->list, &bus->codec_list);
        bus->caddr_tbl[codec_addr] = codec;
 
@@ -593,24 +669,19 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
                snd_hda_get_codec_name(codec, bus->card->mixername,
                                       sizeof(bus->card->mixername));
 
-#ifdef CONFIG_SND_HDA_GENERIC
        if (is_generic_config(codec)) {
                err = snd_hda_parse_generic_codec(codec);
                goto patched;
        }
-#endif
        if (codec->preset && codec->preset->patch) {
                err = codec->preset->patch(codec);
                goto patched;
        }
 
        /* call the default parser */
-#ifdef CONFIG_SND_HDA_GENERIC
        err = snd_hda_parse_generic_codec(codec);
-#else
-       printk(KERN_ERR "hda-codec: No codec parser is available\n");
-       err = -ENODEV;
-#endif
+       if (err < 0)
+               printk(KERN_ERR "hda-codec: No codec parser is available\n");
 
  patched:
        if (err < 0) {
@@ -626,7 +697,7 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        snd_hda_create_hwdep(codec);
 #endif
 
-       sprintf(component, "HDA:%08x", codec->vendor_id);
+       sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
        snd_component_add(codec->bus->card, component);
 
        if (codecp)
@@ -658,6 +729,19 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 }
 
+void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (!nid)
+               return;
+
+       snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+#if 0 /* keep the format */
+       msleep(1);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+#endif
+}
+
 /*
  * amp access functions
  */
@@ -676,7 +760,7 @@ static void __devinit init_hda_cache(struct hda_cache_rec *cache,
        cache->record_size = record_size;
 }
 
-static inline void free_hda_cache(struct hda_cache_rec *cache)
+static void free_hda_cache(struct hda_cache_rec *cache)
 {
        kfree(cache->buffer);
 }
@@ -737,7 +821,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
 /*
  * query AMP capabilities for the given widget and direction
  */
-static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
        struct hda_amp_info *info;
 
@@ -842,7 +926,20 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
        return 1;
 }
 
-#ifdef CONFIG_PM
+/*
+ * update the AMP stereo 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)
+{
+       int ch, ret = 0;
+       for (ch = 0; ch < 2; ch++)
+               ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
+                                               idx, mask, val);
+       return ret;
+}
+
+#ifdef SND_HDA_NEEDS_RESUME
 /* resume the all amp commands from the cache */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
@@ -866,16 +963,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
                }
        }
 }
-#endif /* CONFIG_PM */
-
-/*
- * AMP control callbacks
- */
-/* retrieve parameters from private_value */
-#define get_amp_nid(kc)                ((kc)->private_value & 0xffff)
-#define get_amp_channels(kc)   (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc)  (((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc)      (((kc)->private_value >> 19) & 0xf)
+#endif /* SND_HDA_NEEDS_RESUME */
 
 /* volume */
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
@@ -892,7 +980,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
        if (!caps) {
                printk(KERN_WARNING "hda_codec: "
-                      "num_steps = 0 for NID=0x%x\n", nid);
+                      "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
+                      kcontrol->id.name);
                return -EINVAL;
        }
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -913,9 +1002,11 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
        long *valp = ucontrol->value.integer.value;
 
        if (chs & 1)
-               *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+               *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx)
+                       & HDA_AMP_VOLMASK;
        if (chs & 2)
-               *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+               *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx)
+                       & HDA_AMP_VOLMASK;
        return 0;
 }
 
@@ -930,6 +1021,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
        long *valp = ucontrol->value.integer.value;
        int change = 0;
 
+       snd_hda_power_up(codec);
        if (chs & 1) {
                change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
                                                  0x7f, *valp);
@@ -938,6 +1030,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
        if (chs & 2)
                change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
                                                   0x7f, *valp);
+       snd_hda_power_down(codec);
        return change;
 }
 
@@ -967,6 +1060,80 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        return 0;
 }
 
+/*
+ * set (static) TLV for virtual master volume; recalculated as max 0dB
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int *tlv)
+{
+       u32 caps;
+       int nums, step;
+
+       caps = query_amp_caps(codec, nid, dir);
+       nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+       step = (step + 1) * 25;
+       tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
+       tlv[1] = 2 * sizeof(unsigned int);
+       tlv[2] = -nums * step;
+       tlv[3] = step;
+}
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *
+_snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                       const char *name, int idx)
+{
+       struct snd_ctl_elem_id id;
+       memset(&id, 0, sizeof(id));
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       id.index = idx;
+       strcpy(id.name, name);
+       return snd_ctl_find_id(codec->bus->card, &id);
+}
+
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                                           const char *name)
+{
+       return _snd_hda_find_mixer_ctl(codec, name, 0);
+}
+
+/* create a virtual master control and add slaves */
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                       unsigned int *tlv, const char **slaves)
+{
+       struct snd_kcontrol *kctl;
+       const char **s;
+       int err;
+
+       for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++)
+               ;
+       if (!*s) {
+               snd_printdd("No slave found for %s\n", name);
+               return 0;
+       }
+       kctl = snd_ctl_make_virtual_master(name, tlv);
+       if (!kctl)
+               return -ENOMEM;
+       err = snd_ctl_add(codec->bus->card, 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;
+               }
+               err = snd_ctl_add_slave(kctl, sctl);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
@@ -992,10 +1159,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 
        if (chs & 1)
                *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
-                          0x80) ? 0 : 1;
+                          HDA_AMP_MUTE) ? 0 : 1;
        if (chs & 2)
                *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) &
-                        0x80) ? 0 : 1;
+                        HDA_AMP_MUTE) ? 0 : 1;
        return 0;
 }
 
@@ -1010,15 +1177,22 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
        long *valp = ucontrol->value.integer.value;
        int change = 0;
 
+       snd_hda_power_up(codec);
        if (chs & 1) {
                change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
-                                                 0x80, *valp ? 0 : 0x80);
+                                                 HDA_AMP_MUTE,
+                                                 *valp ? 0 : HDA_AMP_MUTE);
                valp++;
        }
        if (chs & 2)
                change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
-                                                  0x80, *valp ? 0 : 0x80);
-       
+                                                  HDA_AMP_MUTE,
+                                                  *valp ? 0 : HDA_AMP_MUTE);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (codec->patch_ops.check_power_status)
+               codec->patch_ops.check_power_status(codec, nid);
+#endif
+       snd_hda_power_down(codec);
        return change;
 }
 
@@ -1080,8 +1254,8 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
        struct hda_bind_ctls *c;
        int err;
 
-       c = (struct hda_bind_ctls *)kcontrol->private_value;
        mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->info(kcontrol, uinfo);
        kcontrol->private_value = (long)c;
@@ -1096,8 +1270,8 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
        struct hda_bind_ctls *c;
        int err;
 
-       c = (struct hda_bind_ctls *)kcontrol->private_value;
        mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->get(kcontrol, ucontrol);
        kcontrol->private_value = (long)c;
@@ -1113,8 +1287,8 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
        unsigned long *vals;
        int err = 0, change = 0;
 
-       c = (struct hda_bind_ctls *)kcontrol->private_value;
        mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       c = (struct hda_bind_ctls *)kcontrol->private_value;
        for (vals = c->values; *vals; vals++) {
                kcontrol->private_value = *vals;
                err = c->ops->put(kcontrol, ucontrol);
@@ -1134,8 +1308,8 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        struct hda_bind_ctls *c;
        int err;
 
-       c = (struct hda_bind_ctls *)kcontrol->private_value;
        mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->tlv(kcontrol, op_flag, size, tlv);
        kcontrol->private_value = (long)c;
@@ -1256,6 +1430,29 @@ static unsigned int convert_to_spdif_status(unsigned short val)
        return sbits;
 }
 
+/* set digital convert verbs both for the given NID and its slaves */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+                       int verb, int val)
+{
+       hda_nid_t *d;
+
+       snd_hda_codec_write(codec, nid, 0, verb, val);
+       d = codec->slave_dig_outs;
+       if (!d)
+               return;
+       for (; *d; d++)
+               snd_hda_codec_write(codec, *d, 0, verb, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+                                      int dig1, int dig2)
+{
+       if (dig1 != -1)
+               set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
+       if (dig2 != -1)
+               set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
+}
+
 static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
@@ -1274,14 +1471,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
        change = codec->spdif_ctls != val;
        codec->spdif_ctls = val;
 
-       if (change) {
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_2,
-                                         val >> 8);
-       }
+       if (change)
+               set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
 
        mutex_unlock(&codec->spdif_mutex);
        return change;
@@ -1313,17 +1504,12 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
        change = codec->spdif_ctls != val;
        if (change) {
                codec->spdif_ctls = val;
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
+               set_dig_out_convert(codec, nid, val & 0xff, -1);
                /* unmute amp switch (if any) */
                if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
-                   (val & AC_DIG1_ENABLE)) {
-                       snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
-                                                0x80, 0x00);
-                       snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
-                                                0x80, 0x00);
-               }
+                   (val & AC_DIG1_ENABLE))
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                                HDA_AMP_MUTE, 0);
        }
        mutex_unlock(&codec->spdif_mutex);
        return change;
@@ -1361,6 +1547,8 @@ static struct snd_kcontrol_new dig_mixes[] = {
        { } /* end */
 };
 
+#define SPDIF_MAX_IDX  4       /* 4 instances should be enough to probe */
+
 /**
  * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
  * @codec: the HDA codec
@@ -1376,21 +1564,70 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        int err;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_new *dig_mix;
+       int idx;
 
+       for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
+               if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
+                                            idx))
+                       break;
+       }
+       if (idx >= SPDIF_MAX_IDX) {
+               printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
+               return -EBUSY;
+       }
        for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
+               kctl->id.index = idx;
                kctl->private_value = nid;
                err = snd_ctl_add(codec->bus->card, kctl);
                if (err < 0)
                        return err;
        }
        codec->spdif_ctls =
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+               snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_DIGI_CONVERT_1, 0);
        codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
        return 0;
 }
 
 /*
+ * SPDIF sharing with analog output
+ */
+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = mout->share_spdif;
+       return 0;
+}
+
+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+       mout->share_spdif = !!ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static struct snd_kcontrol_new spdif_share_sw = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "IEC958 Default PCM Playback Switch",
+       .info = snd_ctl_boolean_mono_info,
+       .get = spdif_share_sw_get,
+       .put = spdif_share_sw_put,
+};
+
+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_ctl_add(codec->bus->card,
+                          snd_ctl_new1(&spdif_share_sw, mout));
+}
+
+/*
  * SPDIF input
  */
 
@@ -1432,7 +1669,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
        unsigned short val;
        unsigned int sbits;
 
-       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
        sbits = convert_to_spdif_status(val);
        ucontrol->value.iec958.status[0] = sbits;
        ucontrol->value.iec958.status[1] = sbits >> 8;
@@ -1474,7 +1711,17 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        int err;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_new *dig_mix;
+       int idx;
 
+       for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
+               if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
+                                            idx))
+                       break;
+       }
+       if (idx >= SPDIF_MAX_IDX) {
+               printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
+               return -EBUSY;
+       }
        for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
                kctl->private_value = nid;
@@ -1483,12 +1730,13 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
                        return err;
        }
        codec->spdif_in_enable =
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &
+               snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_DIGI_CONVERT_1, 0) &
                AC_DIG1_ENABLE;
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 /*
  * command cache
  */
@@ -1514,6 +1762,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm)
 {
        int err;
+       snd_hda_power_up(codec);
        mutex_lock(&codec->bus->cmd_mutex);
        err = codec->bus->ops.command(codec, nid, direct, verb, parm);
        if (!err) {
@@ -1524,6 +1773,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                        c->val = parm;
        }
        mutex_unlock(&codec->bus->cmd_mutex);
+       snd_hda_power_down(codec);
        return err;
 }
 
@@ -1558,7 +1808,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
                snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
                                          seq->param);
 }
-#endif /* CONFIG_PM */
+#endif /* SND_HDA_NEEDS_RESUME */
 
 /*
  * set power state of the codec
@@ -1566,24 +1816,95 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                unsigned int power_state)
 {
-       hda_nid_t nid, nid_start;
-       int nodes;
+       hda_nid_t nid;
+       int i;
 
        snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
                            power_state);
+       msleep(10); /* partial workaround for "azx_get_response timeout" */
 
-       nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
-       for (nid = nid_start; nid < nodes + nid_start; nid++) {
-               if (get_wcaps(codec, nid) & AC_WCAP_POWER)
+       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 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);
+                               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)
+                                               continue;
+                               }
+                       }
                        snd_hda_codec_write(codec, nid, 0,
                                            AC_VERB_SET_POWER_STATE,
                                            power_state);
+               }
        }
 
-       if (power_state == AC_PWRST_D0)
+       if (power_state == AC_PWRST_D0) {
+               unsigned long end_time;
+               int state;
                msleep(10);
+               /* wait until the codec reachs to D0 */
+               end_time = jiffies + msecs_to_jiffies(500);
+               do {
+                       state = snd_hda_codec_read(codec, fg, 0,
+                                                  AC_VERB_GET_POWER_STATE, 0);
+                       if (state == power_state)
+                               break;
+                       msleep(1);
+               } while (time_after_eq(end_time, jiffies));
+       }
 }
 
+#ifdef SND_HDA_NEEDS_RESUME
+/*
+ * call suspend and power-down; used both from PM and power-save
+ */
+static void hda_call_codec_suspend(struct hda_codec *codec)
+{
+       if (codec->patch_ops.suspend)
+               codec->patch_ops.suspend(codec, PMSG_SUSPEND);
+       hda_set_power_state(codec,
+                           codec->afg ? codec->afg : codec->mfg,
+                           AC_PWRST_D3);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       cancel_delayed_work(&codec->power_work);
+       codec->power_on = 0;
+       codec->power_transition = 0;
+#endif
+}
+
+/*
+ * kick up codec; used both from PM and power-save
+ */
+static void hda_call_codec_resume(struct hda_codec *codec)
+{
+       hda_set_power_state(codec,
+                           codec->afg ? codec->afg : codec->mfg,
+                           AC_PWRST_D0);
+       if (codec->patch_ops.resume)
+               codec->patch_ops.resume(codec);
+       else {
+               if (codec->patch_ops.init)
+                       codec->patch_ops.init(codec);
+               snd_hda_codec_resume_amp(codec);
+               snd_hda_codec_resume_cache(codec);
+       }
+}
+#endif /* SND_HDA_NEEDS_RESUME */
+
 
 /**
  * snd_hda_build_controls - build mixer controls
@@ -1597,28 +1918,24 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
 {
        struct hda_codec *codec;
 
-       /* build controls */
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               int err;
-               if (!codec->patch_ops.build_controls)
-                       continue;
-               err = codec->patch_ops.build_controls(codec);
-               if (err < 0)
-                       return err;
-       }
-
-       /* initialize */
        list_for_each_entry(codec, &bus->codec_list, list) {
-               int err;
+               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);
-               if (!codec->patch_ops.init)
-                       continue;
-               err = codec->patch_ops.init(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;
 }
 
@@ -1915,7 +2232,7 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
                                   struct hda_codec *codec,
                                   struct snd_pcm_substream *substream)
 {
-       snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
        return 0;
 }
 
@@ -1934,11 +2251,13 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
        if (info->ops.close == NULL)
                info->ops.close = hda_pcm_default_open_close;
        if (info->ops.prepare == NULL) {
-               snd_assert(info->nid, return -EINVAL);
+               if (snd_BUG_ON(!info->nid))
+                       return -EINVAL;
                info->ops.prepare = hda_pcm_default_prepare;
        }
        if (info->ops.cleanup == NULL) {
-               snd_assert(info->nid, return -EINVAL);
+               if (snd_BUG_ON(!info->nid))
+                       return -EINVAL;
                info->ops.cleanup = hda_pcm_default_cleanup;
        }
        return 0;
@@ -2033,7 +2352,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
        if (!tbl)
                return -1;
        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)
@@ -2064,7 +2383,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
  */
 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;
@@ -2087,6 +2406,93 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+                               unsigned int power_state);
+
+static void hda_power_work(struct work_struct *work)
+{
+       struct hda_codec *codec =
+               container_of(work, struct hda_codec, power_work.work);
+
+       if (!codec->power_on || codec->power_count) {
+               codec->power_transition = 0;
+               return;
+       }
+
+       hda_call_codec_suspend(codec);
+       if (codec->bus->ops.pm_notify)
+               codec->bus->ops.pm_notify(codec);
+}
+
+static void hda_keep_power_on(struct hda_codec *codec)
+{
+       codec->power_count++;
+       codec->power_on = 1;
+}
+
+void snd_hda_power_up(struct hda_codec *codec)
+{
+       codec->power_count++;
+       if (codec->power_on || codec->power_transition)
+               return;
+
+       codec->power_on = 1;
+       if (codec->bus->ops.pm_notify)
+               codec->bus->ops.pm_notify(codec);
+       hda_call_codec_resume(codec);
+       cancel_delayed_work(&codec->power_work);
+       codec->power_transition = 0;
+}
+
+void snd_hda_power_down(struct hda_codec *codec)
+{
+       --codec->power_count;
+       if (!codec->power_on || codec->power_count || codec->power_transition)
+               return;
+       if (power_save) {
+               codec->power_transition = 1; /* avoid reentrance */
+               schedule_delayed_work(&codec->power_work,
+                                     msecs_to_jiffies(power_save * 1000));
+       }
+}
+
+int snd_hda_check_amp_list_power(struct hda_codec *codec,
+                                struct hda_loopback_check *check,
+                                hda_nid_t nid)
+{
+       struct hda_amp_list *p;
+       int ch, v;
+
+       if (!check->amplist)
+               return 0;
+       for (p = check->amplist; p->nid; p++) {
+               if (p->nid == nid)
+                       break;
+       }
+       if (!p->nid)
+               return 0; /* nothing changed */
+
+       for (p = check->amplist; p->nid; p++) {
+               for (ch = 0; ch < 2; ch++) {
+                       v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+                                                  p->idx);
+                       if (!(v & HDA_AMP_MUTE) && v > 0) {
+                               if (!check->power_on) {
+                                       check->power_on = 1;
+                                       snd_hda_power_up(codec);
+                               }
+                               return 1;
+                       }
+               }
+       }
+       if (check->power_on) {
+               check->power_on = 0;
+               snd_hda_power_down(codec);
+       }
+       return 0;
+}
+#endif
 
 /*
  * Channel mode helper
@@ -2132,7 +2538,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
        unsigned int mode;
 
        mode = ucontrol->value.enumerated.item[0];
-       snd_assert(mode < num_chmodes, return -EINVAL);
+       if (mode >= num_chmodes)
+               return -EINVAL;
        if (*max_channelsp == chmode[mode].channels)
                return 0;
        /* change the current channel setting */
@@ -2153,6 +2560,8 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = imux->num_items;
+       if (!imux->num_items)
+               return 0;
        index = uinfo->value.enumerated.item;
        if (index >= imux->num_items)
                index = imux->num_items - 1;
@@ -2168,6 +2577,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
 {
        unsigned int idx;
 
+       if (!imux->num_items)
+               return 0;
        idx = ucontrol->value.enumerated.item[0];
        if (idx >= imux->num_items)
                idx = imux->num_items - 1;
@@ -2189,14 +2600,31 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
                                 unsigned int stream_tag, unsigned int format)
 {
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               set_dig_out_convert(codec, nid, 
+                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+                                   -1);
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+       if (codec->slave_dig_outs) {
+               hda_nid_t *d;
+               for (d = codec->slave_dig_outs; *d; d++)
+                       snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+                                                  format);
+       }
        /* turn on again (if needed) */
-       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & 0xff);
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               set_dig_out_convert(codec, nid,
+                                   codec->spdif_ctls & 0xff, -1);
+}
+
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       snd_hda_codec_cleanup_stream(codec, nid);
+       if (codec->slave_dig_outs) {
+               hda_nid_t *d;
+               for (d = codec->slave_dig_outs; *d; d++)
+                       snd_hda_codec_cleanup_stream(codec, *d);
+       }
 }
 
 /*
@@ -2208,7 +2636,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
        mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
                /* already opened as analog dup; reset it once */
-               snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
        mout->dig_out_used = HDA_DIG_EXCLUSIVE;
        mutex_unlock(&codec->spdif_mutex);
        return 0;
@@ -2243,9 +2671,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
-                                 struct snd_pcm_substream *substream)
-{
-       substream->runtime->hw.channels_max = mout->max_channels;
+                                 struct snd_pcm_substream *substream,
+                                 struct hda_pcm_stream *hinfo)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       runtime->hw.channels_max = mout->max_channels;
+       if (mout->dig_out_nid) {
+               if (!mout->analog_rates) {
+                       mout->analog_rates = hinfo->rates;
+                       mout->analog_formats = hinfo->formats;
+                       mout->analog_maxbps = hinfo->maxbps;
+               } else {
+                       runtime->hw.rates = mout->analog_rates;
+                       runtime->hw.formats = mout->analog_formats;
+                       hinfo->maxbps = mout->analog_maxbps;
+               }
+               if (!mout->spdif_rates) {
+                       snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
+                                                   &mout->spdif_rates,
+                                                   &mout->spdif_formats,
+                                                   &mout->spdif_maxbps);
+               }
+               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;
+               }
+               mutex_unlock(&codec->spdif_mutex);
+       }
        return snd_pcm_hw_constraint_step(substream->runtime, 0,
                                          SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 }
@@ -2265,7 +2720,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
        int i;
 
        mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+       if (mout->dig_out_nid && mout->share_spdif &&
+           mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
                if (chs == 2 &&
                    snd_hda_is_supported_format(codec, mout->dig_out_nid,
                                                format) &&
@@ -2275,8 +2731,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                                             stream_tag, format);
                } else {
                        mout->dig_out_used = 0;
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  0, 0, 0);
+                       cleanup_dig_out_stream(codec, mout->dig_out_nid);
                }
        }
        mutex_unlock(&codec->spdif_mutex);
@@ -2284,13 +2739,14 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
        /* front */
        snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
                                   0, format);
-       if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
+       if (!mout->no_share_stream &&
+           mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
                /* headphone out will just decode front left/right (stereo) */
                snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
                                           0, format);
        /* extra outputs copied from front */
        for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (mout->extra_out_nid[i])
+               if (!mout->no_share_stream && mout->extra_out_nid[i])
                        snd_hda_codec_setup_stream(codec,
                                                   mout->extra_out_nid[i],
                                                   stream_tag, 0, format);
@@ -2300,7 +2756,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                if (chs >= (i + 1) * 2) /* independent out */
                        snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
                                                   i * 2, format);
-               else /* copy front */
+               else if (!mout->no_share_stream) /* copy front */
                        snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
                                                   0, format);
        }
@@ -2317,17 +2773,16 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
        int i;
 
        for (i = 0; i < mout->num_dacs; i++)
-               snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+               snd_hda_codec_cleanup_stream(codec, nids[i]);
        if (mout->hp_nid)
-               snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+               snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
        for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
                if (mout->extra_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->extra_out_nid[i],
-                                                  0, 0, 0);
+                       snd_hda_codec_cleanup_stream(codec,
+                                                    mout->extra_out_nid[i]);
        mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-               snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
                mout->dig_out_used = 0;
        }
        mutex_unlock(&codec->spdif_mutex);
@@ -2335,7 +2790,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 }
 
 /*
- * Helper for automatic ping configuration
+ * Helper for automatic pin configuration
  */
 
 static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
@@ -2393,20 +2848,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                                 struct auto_pin_cfg *cfg,
                                 hda_nid_t *ignore_nids)
 {
-       hda_nid_t nid, nid_start;
-       int nodes;
+       hda_nid_t nid, end_nid;
        short seq, assoc_line_out, assoc_speaker;
        short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
        short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+       short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
 
        memset(cfg, 0, sizeof(*cfg));
 
        memset(sequences_line_out, 0, sizeof(sequences_line_out));
        memset(sequences_speaker, 0, sizeof(sequences_speaker));
+       memset(sequences_hp, 0, sizeof(sequences_hp));
        assoc_line_out = assoc_speaker = 0;
 
-       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
-       for (nid = nid_start; nid < nodes + nid_start; nid++) {
+       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;
@@ -2429,6 +2885,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                case AC_JACK_LINE_OUT:
                        seq = get_defcfg_sequence(def_conf);
                        assoc = get_defcfg_association(def_conf);
+
+                       if (!(wid_caps & AC_WCAP_STEREO))
+                               if (!cfg->mono_out_pin)
+                                       cfg->mono_out_pin = nid;
                        if (!assoc)
                                continue;
                        if (!assoc_line_out)
@@ -2457,9 +2917,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        cfg->speaker_outs++;
                        break;
                case AC_JACK_HP_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
                        if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
                                continue;
                        cfg->hp_pins[cfg->hp_outs] = nid;
+                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
                        cfg->hp_outs++;
                        break;
                case AC_JACK_MIC_IN: {
@@ -2498,12 +2961,53 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                }
        }
 
+       /* FIX-UP:
+        * If no line-out is defined but multiple HPs are found,
+        * some of them might be the real line-outs.
+        */
+       if (!cfg->line_outs && cfg->hp_outs > 1) {
+               int i = 0;
+               while (i < cfg->hp_outs) {
+                       /* The real HPs should have the sequence 0x0f */
+                       if ((sequences_hp[i] & 0x0f) == 0x0f) {
+                               i++;
+                               continue;
+                       }
+                       /* Move it to the line-out table */
+                       cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+                       sequences_line_out[cfg->line_outs] = sequences_hp[i];
+                       cfg->line_outs++;
+                       cfg->hp_outs--;
+                       memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+                               sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+                       memmove(sequences_hp + i - 1, sequences_hp + i,
+                               sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
+               }
+       }
+
        /* sort by sequence */
        sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
                              cfg->line_outs);
        sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
                              cfg->speaker_outs);
+       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+                             cfg->hp_outs);
        
+       /* if we have only one mic, make it AUTO_PIN_MIC */
+       if (!cfg->input_pins[AUTO_PIN_MIC] &&
+           cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
+               cfg->input_pins[AUTO_PIN_MIC] =
+                       cfg->input_pins[AUTO_PIN_FRONT_MIC];
+               cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
+       }
+       /* ditto for line-in */
+       if (!cfg->input_pins[AUTO_PIN_LINE] &&
+           cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
+               cfg->input_pins[AUTO_PIN_LINE] =
+                       cfg->input_pins[AUTO_PIN_FRONT_LINE];
+               cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
+       }
+
        /*
         * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
         * as a primary output
@@ -2557,6 +3061,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                   cfg->hp_outs, cfg->hp_pins[0],
                   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);
        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],
@@ -2591,13 +3096,12 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
 {
        struct hda_codec *codec;
 
-       /* FIXME: should handle power widget capabilities */
        list_for_each_entry(codec, &bus->codec_list, list) {
-               if (codec->patch_ops.suspend)
-                       codec->patch_ops.suspend(codec, state);
-               hda_set_power_state(codec,
-                                   codec->afg ? codec->afg : codec->mfg,
-                                   AC_PWRST_D3);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               if (!codec->power_on)
+                       continue;
+#endif
+               hda_call_codec_suspend(codec);
        }
        return 0;
 }
@@ -2608,24 +3112,30 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
  * @state: resume state
  *
  * Returns 0 if successful.
+ *
+ * This fucntion is defined only when POWER_SAVE isn't set.
+ * In the power-save mode, the codec is resumed dynamically.
  */
 int snd_hda_resume(struct hda_bus *bus)
 {
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               hda_set_power_state(codec,
-                                   codec->afg ? codec->afg : codec->mfg,
-                                   AC_PWRST_D0);
-               if (codec->patch_ops.resume)
-                       codec->patch_ops.resume(codec);
-               else {
-                       codec->patch_ops.init(codec);
-                       snd_hda_codec_resume_amp(codec);
-                       snd_hda_codec_resume_cache(codec);
-               }
+               if (snd_hda_codec_needs_resume(codec))
+                       hda_call_codec_resume(codec);
        }
        return 0;
 }
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+int snd_hda_codecs_inuse(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
 
+       list_for_each_entry(codec, &bus->codec_list, list) {
+               if (snd_hda_codec_needs_resume(codec))
+                       return 1;
+       }
+       return 0;
+}
+#endif
 #endif