aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-01-24 11:32:56 -0500
committerTakashi Iwai <tiwai@suse.de>2013-01-24 11:55:52 -0500
commit55196fffc951059bb89f97ba53203acb9f87a6f1 (patch)
tree8201219132adc1e47d988c586c82a1c5d6f1d6a3 /sound/pci
parent9040d102da5635abc306372bb4dbffaba92c478e (diff)
ALSA: hda - Implement path-based power filter to the generic parser
This patch adds a better power filter hook for powering down unused widgets in the generic parser. The feature is enabled by setting hda_gen_spec.power_down_unused flag. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/hda_generic.c73
-rw-r--r--sound/pci/hda/hda_generic.h1
2 files changed, 68 insertions, 6 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 31ffd663f7d1..19d014a6a40e 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -24,6 +24,7 @@
24#include <linux/slab.h> 24#include <linux/slab.h>
25#include <linux/export.h> 25#include <linux/export.h>
26#include <linux/sort.h> 26#include <linux/sort.h>
27#include <linux/delay.h>
27#include <linux/ctype.h> 28#include <linux/ctype.h>
28#include <linux/string.h> 29#include <linux/string.h>
29#include <linux/bitops.h> 30#include <linux/bitops.h>
@@ -153,6 +154,9 @@ static void parse_user_hints(struct hda_codec *codec)
153 val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); 154 val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
154 if (val >= 0) 155 if (val >= 0)
155 spec->add_in_jack_modes = !!val; 156 spec->add_in_jack_modes = !!val;
157 val = snd_hda_get_bool_hint(codec, "power_down_unused");
158 if (val >= 0)
159 spec->power_down_unused = !!val;
156 160
157 if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) 161 if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
158 spec->mixer_nid = val; 162 spec->mixer_nid = val;
@@ -700,14 +704,23 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
700void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, 704void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
701 bool enable, bool add_aamix) 705 bool enable, bool add_aamix)
702{ 706{
707 struct hda_gen_spec *spec = codec->spec;
703 int i; 708 int i;
704 709
705 if (!enable) 710 if (!enable)
706 path->active = false; 711 path->active = false;
707 712
708 for (i = path->depth - 1; i >= 0; i--) { 713 for (i = path->depth - 1; i >= 0; i--) {
714 hda_nid_t nid = path->path[i];
715 if (enable && spec->power_down_unused) {
716 /* make sure the widget is powered up */
717 if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0))
718 snd_hda_codec_write(codec, nid, 0,
719 AC_VERB_SET_POWER_STATE,
720 AC_PWRST_D0);
721 }
709 if (enable && path->multi[i]) 722 if (enable && path->multi[i])
710 snd_hda_codec_write_cache(codec, path->path[i], 0, 723 snd_hda_codec_write_cache(codec, nid, 0,
711 AC_VERB_SET_CONNECT_SEL, 724 AC_VERB_SET_CONNECT_SEL,
712 path->idx[i]); 725 path->idx[i]);
713 if (has_amp_in(codec, path, i)) 726 if (has_amp_in(codec, path, i))
@@ -721,6 +734,33 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
721} 734}
722EXPORT_SYMBOL_HDA(snd_hda_activate_path); 735EXPORT_SYMBOL_HDA(snd_hda_activate_path);
723 736
737/* if the given path is inactive, put widgets into D3 (only if suitable) */
738static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
739{
740 struct hda_gen_spec *spec = codec->spec;
741 bool changed;
742 int i;
743
744 if (!spec->power_down_unused || path->active)
745 return;
746
747 for (i = 0; i < path->depth; i++) {
748 hda_nid_t nid = path->path[i];
749 if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3)) {
750 snd_hda_codec_write(codec, nid, 0,
751 AC_VERB_SET_POWER_STATE,
752 AC_PWRST_D3);
753 changed = true;
754 }
755 }
756
757 if (changed) {
758 msleep(10);
759 snd_hda_codec_read(codec, path->path[0], 0,
760 AC_VERB_GET_POWER_STATE, 0);
761 }
762}
763
724/* turn on/off EAPD on the given pin */ 764/* turn on/off EAPD on the given pin */
725static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) 765static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
726{ 766{
@@ -2007,6 +2047,7 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output)
2007 set_pin_eapd(codec, nid, false); 2047 set_pin_eapd(codec, nid, false);
2008 snd_hda_activate_path(codec, path, false, true); 2048 snd_hda_activate_path(codec, path, false, true);
2009 set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); 2049 set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
2050 path_power_down_sync(codec, path);
2010 } 2051 }
2011 2052
2012 /* update jack retasking in case it modifies any of them */ 2053 /* update jack retasking in case it modifies any of them */
@@ -2093,9 +2134,11 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
2093 if (do_mix) { 2134 if (do_mix) {
2094 snd_hda_activate_path(codec, nomix_path, false, true); 2135 snd_hda_activate_path(codec, nomix_path, false, true);
2095 snd_hda_activate_path(codec, mix_path, true, true); 2136 snd_hda_activate_path(codec, mix_path, true, true);
2137 path_power_down_sync(codec, nomix_path);
2096 } else { 2138 } else {
2097 snd_hda_activate_path(codec, mix_path, false, true); 2139 snd_hda_activate_path(codec, mix_path, false, true);
2098 snd_hda_activate_path(codec, nomix_path, true, true); 2140 snd_hda_activate_path(codec, nomix_path, true, true);
2141 path_power_down_sync(codec, mix_path);
2099 } 2142 }
2100} 2143}
2101 2144
@@ -3356,7 +3399,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
3356{ 3399{
3357 struct hda_gen_spec *spec = codec->spec; 3400 struct hda_gen_spec *spec = codec->spec;
3358 const struct hda_input_mux *imux; 3401 const struct hda_input_mux *imux;
3359 struct nid_path *path; 3402 struct nid_path *old_path, *path;
3360 3403
3361 imux = &spec->input_mux; 3404 imux = &spec->input_mux;
3362 if (!imux->num_items) 3405 if (!imux->num_items)
@@ -3367,11 +3410,11 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
3367 if (spec->cur_mux[adc_idx] == idx) 3410 if (spec->cur_mux[adc_idx] == idx)
3368 return 0; 3411 return 0;
3369 3412
3370 path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); 3413 old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
3371 if (!path) 3414 if (!old_path)
3372 return 0; 3415 return 0;
3373 if (path->active) 3416 if (old_path->active)
3374 snd_hda_activate_path(codec, path, false, false); 3417 snd_hda_activate_path(codec, old_path, false, false);
3375 3418
3376 spec->cur_mux[adc_idx] = idx; 3419 spec->cur_mux[adc_idx] = idx;
3377 3420
@@ -3389,6 +3432,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
3389 snd_hda_activate_path(codec, path, true, false); 3432 snd_hda_activate_path(codec, path, true, false);
3390 if (spec->cap_sync_hook) 3433 if (spec->cap_sync_hook)
3391 spec->cap_sync_hook(codec, NULL); 3434 spec->cap_sync_hook(codec, NULL);
3435 path_power_down_sync(codec, old_path);
3392 return 1; 3436 return 1;
3393} 3437}
3394 3438
@@ -3853,6 +3897,20 @@ static int check_auto_mic_availability(struct hda_codec *codec)
3853 return 0; 3897 return 0;
3854} 3898}
3855 3899
3900/* power_filter hook; make inactive widgets into power down */
3901static unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
3902 hda_nid_t nid,
3903 unsigned int power_state)
3904{
3905 if (power_state != AC_PWRST_D0)
3906 return power_state;
3907 if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
3908 return power_state;
3909 if (is_active_nid(codec, nid, HDA_OUTPUT, 0))
3910 return power_state;
3911 return AC_PWRST_D3;
3912}
3913
3856 3914
3857/* 3915/*
3858 * Parse the given BIOS configuration and set up the hda_gen_spec 3916 * Parse the given BIOS configuration and set up the hda_gen_spec
@@ -3980,6 +4038,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
3980 dig_only: 4038 dig_only:
3981 parse_digital(codec); 4039 parse_digital(codec);
3982 4040
4041 if (spec->power_down_unused)
4042 codec->power_filter = snd_hda_gen_path_power_filter;
4043
3983 return 1; 4044 return 1;
3984} 4045}
3985EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); 4046EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config);
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index d226856b2dfa..065fcc77b860 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -211,6 +211,7 @@ struct hda_gen_spec {
211 unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ 211 unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
212 unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ 212 unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
213 unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */ 213 unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
214 unsigned int power_down_unused:1; /* power down unused widgets */
214 215
215 /* other internal flags */ 216 /* other internal flags */
216 unsigned int no_analog:1; /* digital I/O only */ 217 unsigned int no_analog:1; /* digital I/O only */