diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-01-24 11:32:56 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-01-24 11:55:52 -0500 |
commit | 55196fffc951059bb89f97ba53203acb9f87a6f1 (patch) | |
tree | 8201219132adc1e47d988c586c82a1c5d6f1d6a3 /sound/pci | |
parent | 9040d102da5635abc306372bb4dbffaba92c478e (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.c | 73 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.h | 1 |
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, | |||
700 | void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, | 704 | void 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 | } |
722 | EXPORT_SYMBOL_HDA(snd_hda_activate_path); | 735 | EXPORT_SYMBOL_HDA(snd_hda_activate_path); |
723 | 736 | ||
737 | /* if the given path is inactive, put widgets into D3 (only if suitable) */ | ||
738 | static 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 */ |
725 | static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) | 765 | static 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 */ | ||
3901 | static 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 | } |
3985 | EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); | 4046 | EXPORT_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 */ |