[ALSA] hda-intel - Add POWER_SAVE option
authorTakashi Iwai <tiwai@suse.de>
Fri, 10 Aug 2007 15:21:45 +0000 (17:21 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 16 Oct 2007 13:58:46 +0000 (15:58 +0200)
Added CONFIG_SND_HDA_POWER_SAVE kconfig.  It's an experimental option
to achieve an aggressive power-saving.  With this option, the driver
will turn on/off the power of each codec and controller chip dynamically
on demand.
The patch introduces a new module option 'power_save'.  It specifies
the second of time-out for automatic power-down.  As default, it's
10 seconds.  Setting 0 means to suppress the power-saving feature.
The codec may have analog-input loopbacks, which are usually represented
by mixer elements such as 'Mic Playback Switch' or 'CD Playback Switch'.
When these are on, we cannot turn off the mixer and the codec chip has
to be kept on.  For bookkeeping these states, a new codec-callback is
introduced.
For the bus-controller side, a new callback pm_notify is introduced,
which can be used to turn on/off the contoller appropriately.
Note that this power-saving might cause slight click-noise at
power-on/off.  Also, it might take some time to wake up the codec, and
might even drop some tones at the very beginning.  This seems to be the
side-effect of turning off the controller chip.
This turn-off of the controller can be disabled by undefining
HDA_POWER_SAVE_RESET_CONTOLLER in hda_intel.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
sound/pci/Kconfig
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index ff7a689..9554140 100644 (file)
@@ -581,6 +581,14 @@ config SND_HDA_GENERIC
          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
index 0435293..9a3b728 100644 (file)
 #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
@@ -60,6 +67,13 @@ static struct hda_vendor_id hda_vendor_ids[] = {
 #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
@@ -77,12 +91,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 +118,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;
 }
 
@@ -505,6 +523,9 @@ 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);
+#endif
        list_del(&codec->list);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
@@ -551,6 +572,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;
 
@@ -855,7 +885,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
        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)
 {
@@ -879,7 +909,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
                }
        }
 }
-#endif /* CONFIG_PM */
+#endif /* SND_HDA_NEEDS_RESUME */
 
 /*
  * AMP control callbacks
@@ -945,6 +975,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);
@@ -953,6 +984,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;
 }
 
@@ -1025,6 +1057,7 @@ 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,
                                                  HDA_AMP_MUTE,
@@ -1035,7 +1068,11 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                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;
 }
 
@@ -1502,7 +1539,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 /*
  * command cache
  */
@@ -1528,6 +1565,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) {
@@ -1538,6 +1576,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;
 }
 
@@ -1572,7 +1611,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
@@ -1580,24 +1619,70 @@ 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);
 
-       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
@@ -1611,28 +1696,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;
 }
 
@@ -2078,7 +2159,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;
@@ -2101,6 +2182,89 @@ 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)
+               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
@@ -2605,41 +2769,32 @@ 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);
+               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
index 92938d2..1ffffaa 100644 (file)
 #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
  */
@@ -412,6 +416,10 @@ struct hda_bus_ops {
        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 */
@@ -473,10 +481,13 @@ struct hda_codec_ops {
        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 */
@@ -573,6 +584,12 @@ struct hda_codec {
        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 */
@@ -617,7 +634,7 @@ void snd_hda_sequence_write(struct hda_codec *codec,
 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,
@@ -662,4 +679,15 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
 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 */
index 91cd9b9..819c804 100644 (file)
@@ -70,6 +70,13 @@ struct hda_gspec {
        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
 };
 
 /*
@@ -682,11 +689,33 @@ static int parse_input(struct hda_codec *codec)
        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;
@@ -700,6 +729,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
        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;
@@ -707,6 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
        } 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;
@@ -765,7 +798,7 @@ static int create_output_mixers(struct hda_codec *codec, const char **names)
        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;
        }
@@ -782,7 +815,7 @@ static int build_output_controls(struct hda_codec *codec)
        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);
@@ -818,7 +851,7 @@ static int build_input_controls(struct hda_codec *codec)
        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;
@@ -884,7 +917,8 @@ static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
                        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)
@@ -1020,6 +1054,14 @@ static int build_generic_pcms(struct hda_codec *codec)
        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
+
 
 /*
  */
@@ -1027,6 +1069,9 @@ static struct hda_codec_ops generic_patch_ops = {
        .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
 };
 
 /*
index ebb442d..7be3a9b 100644 (file)
@@ -75,6 +75,7 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
 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;
@@ -102,6 +103,18 @@ MODULE_DESCRIPTION("Intel HDA driver");
 #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
  */
 #define ICH6_REG_GCAP                  0x00
@@ -345,6 +358,7 @@ struct azx {
 
        /* flags */
        int position_fix;
+       unsigned int running :1;
        unsigned int initialized :1;
        unsigned int single_cmd :1;
        unsigned int polling_mode :1;
@@ -665,6 +679,9 @@ static unsigned int azx_get_response(struct hda_codec *codec)
                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)
@@ -790,19 +807,12 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
 
 
 /*
- * 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, &reg);
-       pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8);
+       if (chip->initialized)
+               return;
 
        /* reset controller */
        azx_reset(chip);
@@ -819,22 +829,45 @@ static void azx_init_chip(struct azx *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, 
-                                    &reg);
-               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, &reg);
-               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;
         }
 }
@@ -1007,6 +1040,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
        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)
@@ -1128,9 +1164,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
                                   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;
        }
@@ -1159,6 +1197,7 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
        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;
 }
@@ -1459,6 +1498,48 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
 }
 
 
+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
@@ -1473,7 +1554,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
        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);
@@ -1506,8 +1587,12 @@ static int azx_resume(struct pci_dev *pci)
                        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;
 }
@@ -1521,20 +1606,9 @@ static int azx_free(struct azx *chip)
 {
        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) {
@@ -1720,10 +1794,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        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");
@@ -1750,6 +1823,19 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        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)
 {
@@ -1800,6 +1886,8 @@ static int __devinit azx_probe(struct pci_dev *pci,
        }
 
        pci_set_drvdata(pci, card);
+       chip->running = 1;
+       power_down_all_codecs(chip);
 
        return err;
 }
index 35ea0cf..a79d0ed 100644 (file)
@@ -86,7 +86,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
                             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
 
@@ -366,4 +366,27 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
  */
 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 */
index ccd1918..e94944f 100644 (file)
@@ -262,6 +262,7 @@ static void print_codec_info(struct snd_info_entry *entry,
 
        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: ");
@@ -272,6 +273,7 @@ static void print_codec_info(struct snd_info_entry *entry,
        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++) {
@@ -359,6 +361,7 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, "\n");
                }
        }
+       snd_hda_power_down(codec);
 }
 
 /*
index f9390a5..53cfa0d 100644 (file)
@@ -73,6 +73,10 @@ struct ad198x_spec {
        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
 };
 
 /*
@@ -144,6 +148,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
        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
  */
@@ -323,6 +335,9 @@ static struct hda_codec_ops ad198x_patch_ops = {
        .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
 };
 
 
@@ -736,6 +751,17 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        {}
 };
 
+#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;
@@ -759,6 +785,9 @@ static int patch_ad1986a(struct hda_codec *codec)
        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;
 
@@ -944,6 +973,13 @@ static struct hda_verb ad1983_init_verbs[] = {
        { } /* 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)
 {
@@ -968,6 +1004,9 @@ 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;
 
@@ -1091,6 +1130,17 @@ static struct hda_verb ad1981_init_verbs[] = {
        { } /* 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
  *
@@ -1350,6 +1400,9 @@ static int patch_ad1981(struct hda_codec *codec)
        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;
 
@@ -2103,6 +2156,15 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
                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
@@ -2647,6 +2709,9 @@ static int patch_ad1988(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
                break;
        }
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1988_loopbacks;
+#endif
 
        return 0;
 }
@@ -2803,6 +2868,16 @@ static struct hda_verb ad1884_init_verbs[] = {
        { } /* 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;
@@ -2827,6 +2902,9 @@ static int patch_ad1884(struct hda_codec *codec)
        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;
 
@@ -3208,6 +3286,16 @@ static struct hda_verb ad1882_init_verbs[] = {
        { } /* 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,
@@ -3246,6 +3334,9 @@ static int patch_ad1882(struct hda_codec *codec)
        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;
 
index ebbabeb..b3d3916 100644 (file)
@@ -240,6 +240,10 @@ struct alc_spec {
        /* 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
 };
 
 /*
@@ -264,6 +268,9 @@ struct alc_config_preset {
        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
 };
 
 
@@ -621,6 +628,9 @@ static void setup_preset(struct alc_spec *spec,
 
        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 */
@@ -1287,11 +1297,13 @@ static struct hda_verb alc880_volume_init_verbs[] = {
         * 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)
@@ -1836,8 +1848,8 @@ static struct hda_verb alc880_lg_init_verbs[] = {
        {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},
@@ -1939,7 +1951,7 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
        {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},
@@ -1979,6 +1991,24 @@ static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
                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
  */
@@ -2005,6 +2035,14 @@ static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
                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
  */
@@ -2236,6 +2274,9 @@ static struct hda_codec_ops alc_patch_ops = {
        .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
 };
 
 
@@ -2860,6 +2901,9 @@ static struct alc_config_preset alc880_presets[] = {
                .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 },
@@ -3343,6 +3387,10 @@ static int patch_alc880(struct hda_codec *codec)
        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;
 }
@@ -3691,12 +3739,12 @@ static struct hda_verb alc260_init_verbs[] = {
        /* 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)},
@@ -3741,12 +3789,12 @@ static struct hda_verb alc260_hp_init_verbs[] = {
        /* 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))},
@@ -3791,12 +3839,12 @@ static struct hda_verb alc260_hp_3013_init_verbs[] = {
        /* 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))},
@@ -4418,11 +4466,12 @@ static struct hda_verb alc260_volume_init_verbs[] = {
         * 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)
@@ -4499,6 +4548,17 @@ static void alc260_auto_init(struct hda_codec *codec)
        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
  */
@@ -4698,6 +4758,10 @@ static int patch_alc260(struct hda_codec *codec)
        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;
 }
@@ -5223,17 +5287,17 @@ static struct hda_verb alc882_auto_init_verbs[] = {
        {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)
@@ -5322,6 +5386,10 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
        { } /* 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
@@ -5659,6 +5727,10 @@ static int patch_alc882(struct hda_codec *codec)
        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;
 }
@@ -6242,11 +6314,12 @@ static struct hda_verb alc883_init_verbs[] = {
        {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},
@@ -6515,17 +6588,17 @@ static struct hda_verb alc883_auto_init_verbs[] = {
        {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)
@@ -6588,6 +6661,10 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
        { } /* 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
@@ -7029,6 +7106,10 @@ static int patch_alc883(struct hda_codec *codec)
        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;
 }
@@ -7186,17 +7267,17 @@ static struct hda_verb alc262_init_verbs[] = {
        {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)
@@ -7565,17 +7646,17 @@ static struct hda_verb alc262_volume_init_verbs[] = {
        {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)
@@ -7626,19 +7707,19 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
        {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)
@@ -7713,20 +7794,20 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
        {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)
         */
@@ -7796,6 +7877,10 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
        { }
 };
 
+#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
@@ -8098,6 +8183,10 @@ static int patch_alc262(struct hda_codec *codec)
        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;
 }
@@ -8507,6 +8596,10 @@ static void alc268_auto_init(struct hda_codec *codec)
        alc268_auto_init_analog_input(codec);
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc883_loopbacks       alc880_loopbacks
+#endif
+
 /*
  * configuration and preset
  */
@@ -9556,6 +9649,16 @@ static void alc861_auto_init(struct hda_codec *codec)
        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
@@ -9753,6 +9856,10 @@ static int patch_alc861(struct hda_codec *codec)
        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;
 }
@@ -10035,11 +10142,11 @@ static struct hda_verb alc861vd_volume_init_verbs[] = {
         * 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)},
@@ -10266,6 +10373,10 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re
                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
@@ -10688,6 +10799,10 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        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;
 }
@@ -10968,11 +11083,11 @@ static struct hda_verb alc662_init_verbs[] = {
        {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)},
@@ -11041,11 +11156,11 @@ static struct hda_verb alc662_auto_init_verbs[] = {
         * 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)
@@ -11132,6 +11247,10 @@ static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
                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
@@ -11534,6 +11653,10 @@ static int patch_alc662(struct hda_codec *codec)
        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;
 }
index bf5d91b..4a98139 100644 (file)
@@ -1946,7 +1946,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        }
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 {
        stac92xx_set_config_regs(codec);
@@ -1963,7 +1963,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .init = stac92xx_init,
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        .resume = stac92xx_resume,
 #endif
 };
@@ -2460,7 +2460,7 @@ static struct hda_codec_ops stac9872_patch_ops = {
        .build_pcms = stac92xx_build_pcms,
        .init = stac92xx_init,
        .free = stac92xx_free,
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        .resume = stac92xx_resume,
 #endif
 };
index 6c734f0..33b5e1f 100644 (file)
@@ -115,6 +115,10 @@ struct via_spec {
        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] = {
@@ -305,15 +309,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
        {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)
@@ -543,6 +547,14 @@ static int via_init(struct hda_codec *codec)
        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 = {
@@ -550,6 +562,9 @@ 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 */
@@ -738,6 +753,16 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
        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;
@@ -831,6 +856,9 @@ static int patch_vt1708(struct hda_codec *codec)
        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;
 }
@@ -871,15 +899,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
        {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)
@@ -1227,6 +1255,16 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        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;
@@ -1269,6 +1307,9 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        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;
 }
@@ -1359,6 +1400,9 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
        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;
 }