aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/pci/Kconfig8
-rw-r--r--sound/pci/hda/hda_codec.c239
-rw-r--r--sound/pci/hda/hda_codec.h32
-rw-r--r--sound/pci/hda/hda_generic.c55
-rw-r--r--sound/pci/hda/hda_intel.c158
-rw-r--r--sound/pci/hda/hda_local.h25
-rw-r--r--sound/pci/hda/hda_proc.c3
-rw-r--r--sound/pci/hda/patch_analog.c91
-rw-r--r--sound/pci/hda/patch_realtek.c307
-rw-r--r--sound/pci/hda/patch_sigmatel.c6
-rw-r--r--sound/pci/hda/patch_via.c68
11 files changed, 800 insertions, 192 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index ff7a689c229..9554140f0b0 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -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
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 04352930867..9a3b72824f8 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -33,6 +33,13 @@
#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
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 92938d2a52e..1ffffaa3a30 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -26,6 +26,10 @@
#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 */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 91cd9b9ea5d..819c804a579 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -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
};
/*
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index ebb442dcc02..7be3a9b5533 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -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;
}
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 35ea0cf37a2..a79d0ed5469 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -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 */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index ccd19180e54..e94944f34ff 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -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);
}
/*
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index f9390a544ea..53cfa0da496 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -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;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ebbabeb3293..b3d3916c8ec 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -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;
}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index bf5d91b63d1..4a981399abd 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -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
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 6c734f07e5b..33b5e1ffa81 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -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;
}