Say Y here to enable the generic HD-audio codec parser
          in snd-hda-intel driver.
 
+config SND_HDA_POWER_SAVE
+       bool "Aggressive power-saving on HD-audio"
+       depends on SND_HDA_INTEL && EXPERIMENTAL
+       help
+         Say Y here to enable more aggressive power-saving mode on
+         HD-audio driver.  The power-saving timeout can be configured
+         via power_save option or over sysfs on-the-fly.
+
 config SND_HDSP
        tristate "RME Hammerfall DSP Audio"
        depends on SND
 
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* define this option here to hide as static */
+static int power_save = 10;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+                "(in second, 0 = disable).");
+#endif
 
 /*
  * vendor / preset table
 #include "hda_patch.h"
 
 
+#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
  * @codec: the HDA codec
                                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;
 }
 
                         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;
 }
 
 {
        if (!codec)
                return;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       cancel_delayed_work(&codec->power_work);
+#endif
        list_del(&codec->list);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
        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;
 
        return ret;
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 /* resume the all amp commands from the cache */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
                }
        }
 }
-#endif /* CONFIG_PM */
+#endif /* SND_HDA_NEEDS_RESUME */
 
 /*
  * AMP control callbacks
        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);
        if (chs & 2)
                change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
                                                   0x7f, *valp);
+       snd_hda_power_down(codec);
        return change;
 }
 
        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,
                                                  HDA_AMP_MUTE,
                change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
                                                   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;
 }
 
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 /*
  * command cache
  */
                              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) {
                        c->val = parm;
        }
        mutex_unlock(&codec->bus->cmd_mutex);
+       snd_hda_power_down(codec);
        return err;
 }
 
                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
 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);
 
-       nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
-       for (nid = nid_start; nid < nodes + nid_start; nid++) {
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
                if (get_wcaps(codec, nid) & AC_WCAP_POWER)
                        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);
+#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 {
+               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
 {
        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;
 }
 
  */
 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;
        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)
+               return;
+
+       hda_call_codec_suspend(codec);
+       codec->power_on = 0;
+       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)
+               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);
+}
+
+void snd_hda_power_down(struct hda_codec *codec)
+{
+       --codec->power_count;
+       if (!codec->power_on)
+               return;
+       if (power_save)
+               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
 {
        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);
+               hda_call_codec_suspend(codec);
        }
        return 0;
 }
 
+#ifndef CONFIG_SND_HDA_POWER_SAVE
 /**
  * snd_hda_resume - resume the codecs
  * @bus: the HDA bus
  * @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);
-               }
+               hda_call_codec_resume(codec);
        }
        return 0;
 }
+#endif /* !CONFIG_SND_HDA_POWER_SAVE */
 
 #endif
 
 #include <sound/pcm.h>
 #include <sound/hwdep.h>
 
+#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
+#define SND_HDA_NEEDS_RESUME   /* resume control code is required */
+#endif
+
 /*
  * nodes
  */
        unsigned int (*get_response)(struct hda_codec *codec);
        /* free the private data */
        void (*private_free)(struct hda_bus *);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       /* notify power-up/down from codec to contoller */
+       void (*pm_notify)(struct hda_codec *codec);
+#endif
 };
 
 /* template to pass to the bus constructor */
        int (*init)(struct hda_codec *codec);
        void (*free)(struct hda_codec *codec);
        void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        int (*suspend)(struct hda_codec *codec, pm_message_t state);
        int (*resume)(struct hda_codec *codec);
 #endif
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
+#endif
 };
 
 /* record for amp information cache */
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
 
        struct snd_hwdep *hwdep;        /* assigned hwdep device */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       int power_on;           /* current (global) power-state */
+       int power_count;        /* current (global) power refcount */
+       struct delayed_work power_work; /* delayed task for powerdown */
+#endif
 };
 
 /* direction */
 int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
 
 /* cached write */
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm);
 void snd_hda_sequence_write_cache(struct hda_codec *codec,
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
+/*
+ * power saving
+ */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+void snd_hda_power_up(struct hda_codec *codec);
+void snd_hda_power_down(struct hda_codec *codec);
+#else
+static inline void snd_hda_power_up(struct hda_codec *codec) {}
+static inline void snd_hda_power_down(struct hda_codec *codec) {}
+#endif
+
 #endif /* __SOUND_HDA_CODEC_H */
 
        struct hda_pcm pcm_rec;         /* PCM information */
 
        struct list_head nid_list;      /* list of widgets */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define MAX_LOOPBACK_AMPS      7
+       struct hda_loopback_check loopback;
+       int num_loopbacks;
+       struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
+#endif
 };
 
 /*
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
+                              int dir, int idx)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct hda_amp_list *p;
+
+       if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
+               snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
+               return;
+       }
+       p = &spec->loopback_list[spec->num_loopbacks++];
+       p->nid = nid;
+       p->dir = dir;
+       p->idx = idx;
+       spec->loopback.amplist = spec->loopback_list;
+}
+#else
+#define add_input_loopback(codec,nid,dir,idx)
+#endif
+
 /*
  * create mixer controls if possible
  */
 static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
-                       unsigned int index, const char *type, const char *dir_sfx)
+                       unsigned int index, const char *type,
+                       const char *dir_sfx, int is_loopback)
 {
        char name[32];
        int err;
        if ((node->wid_caps & AC_WCAP_IN_AMP) &&
            (node->amp_in_caps & AC_AMPCAP_MUTE)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
+               if (is_loopback)
+                       add_input_loopback(codec, node->nid, HDA_INPUT, index);
                snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
                if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
                        return err;
        } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
                   (node->amp_out_caps & AC_AMPCAP_MUTE)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
+               if (is_loopback)
+                       add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
                snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
                if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
                        return err;
        for (i = 0; i < spec->pcm_vol_nodes; i++) {
                err = create_mixer(codec, spec->pcm_vol[i].node,
                                   spec->pcm_vol[i].index,
-                                  names[i], "Playback");
+                                  names[i], "Playback", 0);
                if (err < 0)
                        return err;
        }
        case 1:
                return create_mixer(codec, spec->pcm_vol[0].node,
                                    spec->pcm_vol[0].index,
-                                   "Master", "Playback");
+                                   "Master", "Playback", 0);
        case 2:
                if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
                        return create_output_mixers(codec, types_speaker);
        if (spec->input_mux.num_items == 1) {
                err = create_mixer(codec, adc_node,
                                   spec->input_mux.items[0].index,
-                                  NULL, "Capture");
+                                  NULL, "Capture", 0);
                if (err < 0)
                        return err;
                return 0;
                        return err;
                else if (err >= 1) {
                        if (err == 1) {
-                               err = create_mixer(codec, node, i, type, "Playback");
+                               err = create_mixer(codec, node, i, type,
+                                                  "Playback", 1);
                                if (err < 0)
                                        return err;
                                if (err > 0)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_gspec *spec = codec->spec;
+       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
 
 /*
  */
        .build_controls = build_generic_controls,
        .build_pcms = build_generic_pcms,
        .free = snd_hda_generic_free,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .check_power_status = generic_check_power_status,
+#endif
 };
 
 /*
 
 module_param(enable_msi, int, 0);
 MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
 
+/* power_save option is defined in hda_codec.c */
 
 /* just for backward compatibility */
 static int enable;
 
 #define SFX    "hda-intel: "
 
+/*
+ * build flags
+ */
+
+/*
+ * reset the HD-audio controller in power save mode.
+ * this may give more power-saving, but will take longer time to
+ * wake up.
+ */
+#define HDA_POWER_SAVE_RESET_CONTROLLER
+
+
 /*
  * registers
  */
 
        /* flags */
        int position_fix;
+       unsigned int running :1;
        unsigned int initialized :1;
        unsigned int single_cmd :1;
        unsigned int polling_mode :1;
                return azx_rirb_get_response(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void azx_power_notify(struct hda_codec *codec);
+#endif
 
 /* reset codec link */
 static int azx_reset(struct azx *chip)
 
 
 /*
- * initialize the chip
+ * reset and start the controller registers
  */
 static void azx_init_chip(struct azx *chip)
 {
-       unsigned char reg;
-
-       /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
-        * TCSEL == Traffic Class Select Register, which sets PCI express QOS
-        * Ensuring these bits are 0 clears playback static on some HD Audio
-        * codecs
-        */
-       pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, ®);
-       pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8);
+       if (chip->initialized)
+               return;
 
        /* reset controller */
        azx_reset(chip);
        azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
        azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
 
+       chip->initialized = 1;
+}
+
+/*
+ * initialize the PCI registers
+ */
+/* update bits in a PCI register byte */
+static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
+                           unsigned char mask, unsigned char val)
+{
+       unsigned char data;
+
+       pci_read_config_byte(pci, reg, &data);
+       data &= ~mask;
+       data |= (val & mask);
+       pci_write_config_byte(pci, reg, data);
+}
+
+static void azx_init_pci(struct azx *chip)
+{
+       /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+        * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+        * Ensuring these bits are 0 clears playback static on some HD Audio
+        * codecs
+        */
+       update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
+
        switch (chip->driver_type) {
        case AZX_DRIVER_ATI:
                /* For ATI SB450 azalia HD audio, we need to enable snoop */
-               pci_read_config_byte(chip->pci,
-                                    ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
-                                    ®);
-               pci_write_config_byte(chip->pci,
-                                     ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
-                                     (reg & 0xf8) |
-                                     ATI_SB450_HDAUDIO_ENABLE_SNOOP);
+               update_pci_byte(chip->pci,
+                               ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+                               0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
                break;
        case AZX_DRIVER_NVIDIA:
                /* For NVIDIA HDA, enable snoop */
-               pci_read_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, ®);
-               pci_write_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR,
-                                     (reg & 0xf0) | NVIDIA_HDA_ENABLE_COHBITS);
+               update_pci_byte(chip->pci,
+                               NVIDIA_HDA_TRANSREG_ADDR,
+                               0x0f, NVIDIA_HDA_ENABLE_COHBITS);
                break;
         }
 }
        bus_temp.pci = chip->pci;
        bus_temp.ops.command = azx_send_cmd;
        bus_temp.ops.get_response = azx_get_response;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       bus_temp.ops.pm_notify = azx_power_notify;
+#endif
 
        err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
        if (err < 0)
                                   128);
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
                                   128);
+       snd_hda_power_up(apcm->codec);
        err = hinfo->ops.open(hinfo, apcm->codec, substream);
        if (err < 0) {
                azx_release_device(azx_dev);
+               snd_hda_power_down(apcm->codec);
                mutex_unlock(&chip->open_mutex);
                return err;
        }
        spin_unlock_irqrestore(&chip->reg_lock, flags);
        azx_release_device(azx_dev);
        hinfo->ops.close(hinfo, apcm->codec, substream);
+       snd_hda_power_down(apcm->codec);
        mutex_unlock(&chip->open_mutex);
        return 0;
 }
 }
 
 
+static void azx_stop_chip(struct azx *chip)
+{
+       if (chip->initialized)
+               return;
+
+       /* disable interrupts */
+       azx_int_disable(chip);
+       azx_int_clear(chip);
+
+       /* disable CORB/RIRB */
+       azx_free_cmd_io(chip);
+
+       /* disable position buffer */
+       azx_writel(chip, DPLBASE, 0);
+       azx_writel(chip, DPUBASE, 0);
+
+       chip->initialized = 0;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* power-up/down the controller */
+static void azx_power_notify(struct hda_codec *codec)
+{
+       struct azx *chip = codec->bus->private_data;
+       struct hda_codec *c;
+       int power_on = 0;
+
+       list_for_each_entry(c, &codec->bus->codec_list, list) {
+               if (c->power_on) {
+                       power_on = 1;
+                       break;
+               }
+       }
+       if (power_on)
+               azx_init_chip(chip);
+#ifdef HDA_POWER_SAVE_RESET_CONTROLLER
+       else if (chip->running)
+               azx_stop_chip(chip);
+#endif
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
 #ifdef CONFIG_PM
 /*
  * power management
        for (i = 0; i < chip->pcm_devs; i++)
                snd_pcm_suspend_all(chip->pcm[i]);
        snd_hda_suspend(chip->bus, state);
-       azx_free_cmd_io(chip);
+       azx_stop_chip(chip);
        if (chip->irq >= 0) {
                synchronize_irq(chip->irq);
                free_irq(chip->irq, chip);
                        chip->msi = 0;
        if (azx_acquire_irq(chip, 1) < 0)
                return -EIO;
+       azx_init_pci(chip);
+#ifndef CONFIG_SND_HDA_POWER_SAVE
+       /* the explicit resume is needed only when POWER_SAVE isn't set */
        azx_init_chip(chip);
        snd_hda_resume(chip->bus);
+#endif
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
        return 0;
 }
 {
        if (chip->initialized) {
                int i;
-
                for (i = 0; i < chip->num_streams; i++)
                        azx_stream_stop(chip, &chip->azx_dev[i]);
-
-               /* disable interrupts */
-               azx_int_disable(chip);
-               azx_int_clear(chip);
-
-               /* disable CORB/RIRB */
-               azx_free_cmd_io(chip);
-
-               /* disable position buffer */
-               azx_writel(chip, DPLBASE, 0);
-               azx_writel(chip, DPUBASE, 0);
+               azx_stop_chip(chip);
        }
 
        if (chip->irq >= 0) {
        azx_init_stream(chip);
 
        /* initialize chip */
+       azx_init_pci(chip);
        azx_init_chip(chip);
 
-       chip->initialized = 1;
-
        /* codec detection */
        if (!chip->codec_mask) {
                snd_printk(KERN_ERR SFX "no codecs found!\n");
        return err;
 }
 
+static void power_down_all_codecs(struct azx *chip)
+{
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       /* The codecs were powered up in snd_hda_codec_new().
+        * Now all initialization done, so turn them down if possible
+        */
+       struct hda_codec *codec;
+       list_for_each_entry(codec, &chip->bus->codec_list, list) {
+               snd_hda_power_down(codec);
+       }
+#endif
+}
+
 static int __devinit azx_probe(struct pci_dev *pci,
                               const struct pci_device_id *pci_id)
 {
        }
 
        pci_set_drvdata(pci, card);
+       chip->running = 1;
+       power_down_all_codecs(chip);
 
        return err;
 }
 
                             int direction, int idx, int mask, int val);
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                             int dir, int idx, int mask, int val);
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 #endif
 
  */
 int snd_hda_create_hwdep(struct hda_codec *codec);
 
+/*
+ * power-management
+ */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+void snd_hda_schedule_power_save(struct hda_codec *codec);
+
+struct hda_amp_list {
+       hda_nid_t nid;
+       unsigned char dir;
+       unsigned char idx;
+};
+
+struct hda_loopback_check {
+       struct hda_amp_list *amplist;
+       int power_on;
+};
+
+int snd_hda_check_amp_list_power(struct hda_codec *codec,
+                                struct hda_loopback_check *check,
+                                hda_nid_t nid);
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
 #endif /* __SOUND_HDA_LOCAL_H */
 
 
        if (! codec->afg)
                return;
+       snd_hda_power_up(codec);
        snd_iprintf(buffer, "Default PCM:\n");
        print_pcm_caps(buffer, codec, codec->afg);
        snd_iprintf(buffer, "Default Amp-In caps: ");
        nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
        if (! nid || nodes < 0) {
                snd_iprintf(buffer, "Invalid AFG subtree\n");
+               snd_hda_power_down(codec);
                return;
        }
        for (i = 0; i < nodes; i++, nid++) {
                        snd_iprintf(buffer, "\n");
                }
        }
+       snd_hda_power_down(codec);
 }
 
 /*
 
        struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[4];
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
 };
 
 /*
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
 /*
  * Analog playback callbacks
  */
        .build_pcms = ad198x_build_pcms,
        .init = ad198x_init,
        .free = ad198x_free,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .check_power_status = ad198x_check_power_status,
+#endif
 };
 
 
        {}
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1986a_loopbacks[] = {
+       { 0x13, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x14, HDA_OUTPUT, 0 }, /* Phone */
+       { 0x15, HDA_OUTPUT, 0 }, /* CD */
+       { 0x16, HDA_OUTPUT, 0 }, /* Aux */
+       { 0x17, HDA_OUTPUT, 0 }, /* Line */
+       { } /* end */
+};
+#endif
+
 static int patch_ad1986a(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        spec->mixers[0] = ad1986a_mixers;
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1986a_init_verbs;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1986a_loopbacks;
+#endif
 
        codec->patch_ops = ad198x_patch_ops;
 
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1983_loopbacks[] = {
+       { 0x12, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x13, HDA_OUTPUT, 0 }, /* Line */
+       { } /* end */
+};
+#endif
 
 static int patch_ad1983(struct hda_codec *codec)
 {
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1983_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1983_loopbacks;
+#endif
 
        codec->patch_ops = ad198x_patch_ops;
 
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1981_loopbacks[] = {
+       { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
+       { 0x13, HDA_OUTPUT, 0 }, /* Line */
+       { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
+       { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x1d, HDA_OUTPUT, 0 }, /* CD */
+       { } /* end */
+};
+#endif
+
 /*
  * Patch for HP nx6320
  *
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1981_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1981_loopbacks;
+#endif
 
        codec->patch_ops = ad198x_patch_ops;
 
                snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
 } 
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1988_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Line */
+       { 0x20, HDA_INPUT, 4 }, /* Mic */
+       { 0x20, HDA_INPUT, 6 }, /* CD */
+       { } /* end */
+};
+#endif
 
 /*
  * Automatic parse of I/O pins from the BIOS configuration
                codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
                break;
        }
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1988_loopbacks;
+#endif
 
        return 0;
 }
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1884_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Mic */
+       { 0x20, HDA_INPUT, 2 }, /* CD */
+       { 0x20, HDA_INPUT, 4 }, /* Docking */
+       { } /* end */
+};
+#endif
+
 static int patch_ad1884(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1884_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1884_loopbacks;
+#endif
 
        codec->patch_ops = ad198x_patch_ops;
 
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1882_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Mic */
+       { 0x20, HDA_INPUT, 4 }, /* Line */
+       { 0x20, HDA_INPUT, 6 }, /* CD */
+       { } /* end */
+};
+#endif
+
 /* models */
 enum {
        AD1882_3STACK,
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1882_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1882_loopbacks;
+#endif
 
        codec->patch_ops = ad198x_patch_ops;
 
 
        /* for pin sensing */
        unsigned int sense_updated: 1;
        unsigned int jack_present: 1;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
 };
 
 /*
        const struct hda_input_mux *input_mux;
        void (*unsol_event)(struct hda_codec *, unsigned int);
        void (*init_hook)(struct hda_codec *);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_amp_list *loopbacks;
+#endif
 };
 
 
 
        spec->unsol_event = preset->unsol_event;
        spec->init_hook = preset->init_hook;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = preset->loopbacks;
+#endif
 }
 
 /* Enable GPIO mask and set output */
         * panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
 
        /*
         * Set up output mixers (0x0c - 0x0f)
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
        /* mute all amp mixer inputs */
        {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
        /* line-in to input */
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
        {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
        /* speaker-out */
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
                alc880_lg_lw_automute(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list alc880_loopbacks[] = {
+       { 0x0b, HDA_INPUT, 0 },
+       { 0x0b, HDA_INPUT, 1 },
+       { 0x0b, HDA_INPUT, 2 },
+       { 0x0b, HDA_INPUT, 3 },
+       { 0x0b, HDA_INPUT, 4 },
+       { } /* end */
+};
+
+static struct hda_amp_list alc880_lg_loopbacks[] = {
+       { 0x0b, HDA_INPUT, 1 },
+       { 0x0b, HDA_INPUT, 6 },
+       { 0x0b, HDA_INPUT, 7 },
+       { } /* end */
+};
+#endif
+
 /*
  * Common callbacks
  */
                spec->unsol_event(codec, res);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
 /*
  * Analog playback callbacks
  */
        .init = alc_init,
        .free = alc_free,
        .unsol_event = alc_unsol_event,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .check_power_status = alc_check_power_status,
+#endif
 };
 
 
                .input_mux = &alc880_lg_capture_source,
                .unsol_event = alc880_lg_unsol_event,
                .init_hook = alc880_lg_automute,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               .loopbacks = alc880_lg_loopbacks,
+#endif
        },
        [ALC880_LG_LW] = {
                .mixers = { alc880_lg_lw_mixer },
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC880_AUTO)
                spec->init_hook = alc880_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc880_loopbacks;
+#endif
 
        return 0;
 }
        /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
         * Line In 2 = 0x03
         */
-       /* mute CD */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-       /* mute Line In */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       /* mute Mic */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       /* mute analog inputs */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
        /* mute Front out path */
        {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
        /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
         * Line In 2 = 0x03
         */
-       /* unmute CD */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
-       /* unmute Line In */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       /* unmute Mic */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* mute analog inputs */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
        /* Unmute Front out path */
        {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
        /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
         * Line In 2 = 0x03
         */
-       /* unmute CD */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
-       /* unmute Line In */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       /* unmute Mic */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* mute analog inputs */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
        /* Unmute Front out path */
        {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       /* mute analog inputs */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x08 - 0x0a)
        alc260_auto_init_analog_input(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list alc260_loopbacks[] = {
+       { 0x07, HDA_INPUT, 0 },
+       { 0x07, HDA_INPUT, 1 },
+       { 0x07, HDA_INPUT, 2 },
+       { 0x07, HDA_INPUT, 3 },
+       { 0x07, HDA_INPUT, 4 },
+       { } /* end */
+};
+#endif
+
 /*
  * ALC260 configurations
  */
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC260_AUTO)
                spec->init_hook = alc260_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc260_loopbacks;
+#endif
 
        return 0;
 }
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x0c - 0x0f)
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc882_loopbacks       alc880_loopbacks
+#endif
+
 /* pcm configuration: identiacal with ALC880 */
 #define alc882_pcm_analog_playback     alc880_pcm_analog_playback
 #define alc882_pcm_analog_capture      alc880_pcm_analog_capture
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC882_AUTO)
                spec->init_hook = alc882_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc882_loopbacks;
+#endif
 
        return 0;
 }
        {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
        {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       /* mute analog input loopbacks */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /* Front Pin: output 0 (0x0c) */
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x0c - 0x0f)
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc883_loopbacks       alc880_loopbacks
+#endif
+
 /* pcm configuration: identiacal with ALC880 */
 #define alc883_pcm_analog_playback     alc880_pcm_analog_playback
 #define alc883_pcm_analog_capture      alc880_pcm_analog_capture
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC883_AUTO)
                spec->init_hook = alc883_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc883_loopbacks;
+#endif
 
        return 0;
 }
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x0c - 0x0e)
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x0c - 0x0f)
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for
         * front panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
-        {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+        {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
        
        /*
         * Set up output mixers (0x0c - 0x0e)
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for front
         * panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
        /*
         * Set up output mixers (0x0c - 0x0e)
         */
        { }
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc262_loopbacks       alc880_loopbacks
+#endif
+
 /* pcm configuration: identiacal with ALC880 */
 #define alc262_pcm_analog_playback     alc880_pcm_analog_playback
 #define alc262_pcm_analog_capture      alc880_pcm_analog_capture
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC262_AUTO)
                spec->init_hook = alc262_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc262_loopbacks;
+#endif
                
        return 0;
 }
        alc268_auto_init_analog_input(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc883_loopbacks       alc880_loopbacks
+#endif
+
 /*
  * configuration and preset
  */
        alc861_auto_init_analog_input(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list alc861_loopbacks[] = {
+       { 0x15, HDA_INPUT, 0 },
+       { 0x15, HDA_INPUT, 1 },
+       { 0x15, HDA_INPUT, 2 },
+       { 0x15, HDA_INPUT, 3 },
+       { } /* end */
+};
+#endif
+
 
 /*
  * configuration and preset
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC861_AUTO)
                spec->init_hook = alc861_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc861_loopbacks;
+#endif
                
        return 0;
 }
         * the analog-loopback mixer widget
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
                alc861vd_dallas_automute(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc861vd_loopbacks     alc880_loopbacks
+#endif
+
 /* pcm configuration: identiacal with ALC880 */
 #define alc861vd_pcm_analog_playback   alc880_pcm_analog_playback
 #define alc861vd_pcm_analog_capture    alc880_pcm_analog_capture
 
        if (board_config == ALC861VD_AUTO)
                spec->init_hook = alc861vd_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc861vd_loopbacks;
+#endif
 
        return 0;
 }
        {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
        /* Front mixer: unmute input/output amp left and right (volume = 0) */
 
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
         * panel mic (mic 2)
         */
        /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x0c - 0x0f)
                alc662_lenovo_101e_ispeaker_automute(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc662_loopbacks       alc880_loopbacks
+#endif
+
 
 /* pcm configuration: identiacal with ALC880 */
 #define alc662_pcm_analog_playback     alc880_pcm_analog_playback
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC662_AUTO)
                spec->init_hook = alc662_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!spec->loopback.amplist)
+               spec->loopback.amplist = alc662_loopbacks;
+#endif
 
        return 0;
 }
 
        }
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 {
        stac92xx_set_config_regs(codec);
        .init = stac92xx_init,
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        .resume = stac92xx_resume,
 #endif
 };
        .build_pcms = stac92xx_build_pcms,
        .init = stac92xx_init,
        .free = stac92xx_free,
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        .resume = stac92xx_resume,
 #endif
 };
 
        struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[4];  
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
 };
 
 static hda_nid_t vt1708_adc_nids[2] = {
        {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         */
        /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output mixers (0x19 - 0x1b)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct via_spec *spec = codec->spec;
+       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
 /*
  */
 static struct hda_codec_ops via_patch_ops = {
        .build_pcms = via_build_pcms,
        .init = via_init,
        .free = via_free,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .check_power_status = via_check_power_status,
+#endif
 };
 
 /* fill in the dac_nids table from the parsed pin configuration */
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708_loopbacks[] = {
+       { 0x17, HDA_INPUT, 1 },
+       { 0x17, HDA_INPUT, 2 },
+       { 0x17, HDA_INPUT, 3 },
+       { 0x17, HDA_INPUT, 4 },
+       { } /* end */
+};
+#endif
+
 static int vt1708_parse_auto_config(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        codec->patch_ops = via_patch_ops;
 
        codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1708_loopbacks;
+#endif
 
        return 0;
 }
        {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
         * mixer widget
         */
        /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
        /*
         * Set up output selector (0x1a, 0x1b, 0x29)
        return 1;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1709_loopbacks[] = {
+       { 0x18, HDA_INPUT, 1 },
+       { 0x18, HDA_INPUT, 2 },
+       { 0x18, HDA_INPUT, 3 },
+       { 0x18, HDA_INPUT, 4 },
+       { } /* end */
+};
+#endif
+
 static int patch_vt1709_10ch(struct hda_codec *codec)
 {
        struct via_spec *spec;
        codec->patch_ops = via_patch_ops;
 
        codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1709_loopbacks;
+#endif
 
        return 0;
 }
        codec->patch_ops = via_patch_ops;
 
        codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1709_loopbacks;
+#endif
 
        return 0;
 }