diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-08-10 11:21:45 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2007-10-16 09:58:46 -0400 |
commit | cb53c626e1145edf1d619bc4953f6293d3a77ace (patch) | |
tree | 715c2ef3d56a5ac7c79498800e888f562c1aa961 /sound/pci/hda/patch_analog.c | |
parent | cca3b3718ca96dca51daf1129ac03003bcede751 (diff) |
[ALSA] hda-intel - Add POWER_SAVE option
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>
Diffstat (limited to 'sound/pci/hda/patch_analog.c')
-rw-r--r-- | sound/pci/hda/patch_analog.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index f9390a544ea4..53cfa0da4964 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c | |||
@@ -73,6 +73,10 @@ struct ad198x_spec { | |||
73 | struct snd_kcontrol_new *kctl_alloc; | 73 | struct snd_kcontrol_new *kctl_alloc; |
74 | struct hda_input_mux private_imux; | 74 | struct hda_input_mux private_imux; |
75 | hda_nid_t private_dac_nids[4]; | 75 | hda_nid_t private_dac_nids[4]; |
76 | |||
77 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
78 | struct hda_loopback_check loopback; | ||
79 | #endif | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | /* | 82 | /* |
@@ -144,6 +148,14 @@ static int ad198x_build_controls(struct hda_codec *codec) | |||
144 | return 0; | 148 | return 0; |
145 | } | 149 | } |
146 | 150 | ||
151 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
152 | static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) | ||
153 | { | ||
154 | struct ad198x_spec *spec = codec->spec; | ||
155 | return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); | ||
156 | } | ||
157 | #endif | ||
158 | |||
147 | /* | 159 | /* |
148 | * Analog playback callbacks | 160 | * Analog playback callbacks |
149 | */ | 161 | */ |
@@ -323,6 +335,9 @@ static struct hda_codec_ops ad198x_patch_ops = { | |||
323 | .build_pcms = ad198x_build_pcms, | 335 | .build_pcms = ad198x_build_pcms, |
324 | .init = ad198x_init, | 336 | .init = ad198x_init, |
325 | .free = ad198x_free, | 337 | .free = ad198x_free, |
338 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
339 | .check_power_status = ad198x_check_power_status, | ||
340 | #endif | ||
326 | }; | 341 | }; |
327 | 342 | ||
328 | 343 | ||
@@ -736,6 +751,17 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { | |||
736 | {} | 751 | {} |
737 | }; | 752 | }; |
738 | 753 | ||
754 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
755 | static struct hda_amp_list ad1986a_loopbacks[] = { | ||
756 | { 0x13, HDA_OUTPUT, 0 }, /* Mic */ | ||
757 | { 0x14, HDA_OUTPUT, 0 }, /* Phone */ | ||
758 | { 0x15, HDA_OUTPUT, 0 }, /* CD */ | ||
759 | { 0x16, HDA_OUTPUT, 0 }, /* Aux */ | ||
760 | { 0x17, HDA_OUTPUT, 0 }, /* Line */ | ||
761 | { } /* end */ | ||
762 | }; | ||
763 | #endif | ||
764 | |||
739 | static int patch_ad1986a(struct hda_codec *codec) | 765 | static int patch_ad1986a(struct hda_codec *codec) |
740 | { | 766 | { |
741 | struct ad198x_spec *spec; | 767 | struct ad198x_spec *spec; |
@@ -759,6 +785,9 @@ static int patch_ad1986a(struct hda_codec *codec) | |||
759 | spec->mixers[0] = ad1986a_mixers; | 785 | spec->mixers[0] = ad1986a_mixers; |
760 | spec->num_init_verbs = 1; | 786 | spec->num_init_verbs = 1; |
761 | spec->init_verbs[0] = ad1986a_init_verbs; | 787 | spec->init_verbs[0] = ad1986a_init_verbs; |
788 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
789 | spec->loopback.amplist = ad1986a_loopbacks; | ||
790 | #endif | ||
762 | 791 | ||
763 | codec->patch_ops = ad198x_patch_ops; | 792 | codec->patch_ops = ad198x_patch_ops; |
764 | 793 | ||
@@ -944,6 +973,13 @@ static struct hda_verb ad1983_init_verbs[] = { | |||
944 | { } /* end */ | 973 | { } /* end */ |
945 | }; | 974 | }; |
946 | 975 | ||
976 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
977 | static struct hda_amp_list ad1983_loopbacks[] = { | ||
978 | { 0x12, HDA_OUTPUT, 0 }, /* Mic */ | ||
979 | { 0x13, HDA_OUTPUT, 0 }, /* Line */ | ||
980 | { } /* end */ | ||
981 | }; | ||
982 | #endif | ||
947 | 983 | ||
948 | static int patch_ad1983(struct hda_codec *codec) | 984 | static int patch_ad1983(struct hda_codec *codec) |
949 | { | 985 | { |
@@ -968,6 +1004,9 @@ static int patch_ad1983(struct hda_codec *codec) | |||
968 | spec->num_init_verbs = 1; | 1004 | spec->num_init_verbs = 1; |
969 | spec->init_verbs[0] = ad1983_init_verbs; | 1005 | spec->init_verbs[0] = ad1983_init_verbs; |
970 | spec->spdif_route = 0; | 1006 | spec->spdif_route = 0; |
1007 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
1008 | spec->loopback.amplist = ad1983_loopbacks; | ||
1009 | #endif | ||
971 | 1010 | ||
972 | codec->patch_ops = ad198x_patch_ops; | 1011 | codec->patch_ops = ad198x_patch_ops; |
973 | 1012 | ||
@@ -1091,6 +1130,17 @@ static struct hda_verb ad1981_init_verbs[] = { | |||
1091 | { } /* end */ | 1130 | { } /* end */ |
1092 | }; | 1131 | }; |
1093 | 1132 | ||
1133 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
1134 | static struct hda_amp_list ad1981_loopbacks[] = { | ||
1135 | { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ | ||
1136 | { 0x13, HDA_OUTPUT, 0 }, /* Line */ | ||
1137 | { 0x1b, HDA_OUTPUT, 0 }, /* Aux */ | ||
1138 | { 0x1c, HDA_OUTPUT, 0 }, /* Mic */ | ||
1139 | { 0x1d, HDA_OUTPUT, 0 }, /* CD */ | ||
1140 | { } /* end */ | ||
1141 | }; | ||
1142 | #endif | ||
1143 | |||
1094 | /* | 1144 | /* |
1095 | * Patch for HP nx6320 | 1145 | * Patch for HP nx6320 |
1096 | * | 1146 | * |
@@ -1350,6 +1400,9 @@ static int patch_ad1981(struct hda_codec *codec) | |||
1350 | spec->num_init_verbs = 1; | 1400 | spec->num_init_verbs = 1; |
1351 | spec->init_verbs[0] = ad1981_init_verbs; | 1401 | spec->init_verbs[0] = ad1981_init_verbs; |
1352 | spec->spdif_route = 0; | 1402 | spec->spdif_route = 0; |
1403 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
1404 | spec->loopback.amplist = ad1981_loopbacks; | ||
1405 | #endif | ||
1353 | 1406 | ||
1354 | codec->patch_ops = ad198x_patch_ops; | 1407 | codec->patch_ops = ad198x_patch_ops; |
1355 | 1408 | ||
@@ -2103,6 +2156,15 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) | |||
2103 | snd_hda_sequence_write(codec, ad1988_laptop_hp_off); | 2156 | snd_hda_sequence_write(codec, ad1988_laptop_hp_off); |
2104 | } | 2157 | } |
2105 | 2158 | ||
2159 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
2160 | static struct hda_amp_list ad1988_loopbacks[] = { | ||
2161 | { 0x20, HDA_INPUT, 0 }, /* Front Mic */ | ||
2162 | { 0x20, HDA_INPUT, 1 }, /* Line */ | ||
2163 | { 0x20, HDA_INPUT, 4 }, /* Mic */ | ||
2164 | { 0x20, HDA_INPUT, 6 }, /* CD */ | ||
2165 | { } /* end */ | ||
2166 | }; | ||
2167 | #endif | ||
2106 | 2168 | ||
2107 | /* | 2169 | /* |
2108 | * Automatic parse of I/O pins from the BIOS configuration | 2170 | * Automatic parse of I/O pins from the BIOS configuration |
@@ -2647,6 +2709,9 @@ static int patch_ad1988(struct hda_codec *codec) | |||
2647 | codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; | 2709 | codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; |
2648 | break; | 2710 | break; |
2649 | } | 2711 | } |
2712 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
2713 | spec->loopback.amplist = ad1988_loopbacks; | ||
2714 | #endif | ||
2650 | 2715 | ||
2651 | return 0; | 2716 | return 0; |
2652 | } | 2717 | } |
@@ -2803,6 +2868,16 @@ static struct hda_verb ad1884_init_verbs[] = { | |||
2803 | { } /* end */ | 2868 | { } /* end */ |
2804 | }; | 2869 | }; |
2805 | 2870 | ||
2871 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
2872 | static struct hda_amp_list ad1884_loopbacks[] = { | ||
2873 | { 0x20, HDA_INPUT, 0 }, /* Front Mic */ | ||
2874 | { 0x20, HDA_INPUT, 1 }, /* Mic */ | ||
2875 | { 0x20, HDA_INPUT, 2 }, /* CD */ | ||
2876 | { 0x20, HDA_INPUT, 4 }, /* Docking */ | ||
2877 | { } /* end */ | ||
2878 | }; | ||
2879 | #endif | ||
2880 | |||
2806 | static int patch_ad1884(struct hda_codec *codec) | 2881 | static int patch_ad1884(struct hda_codec *codec) |
2807 | { | 2882 | { |
2808 | struct ad198x_spec *spec; | 2883 | struct ad198x_spec *spec; |
@@ -2827,6 +2902,9 @@ static int patch_ad1884(struct hda_codec *codec) | |||
2827 | spec->num_init_verbs = 1; | 2902 | spec->num_init_verbs = 1; |
2828 | spec->init_verbs[0] = ad1884_init_verbs; | 2903 | spec->init_verbs[0] = ad1884_init_verbs; |
2829 | spec->spdif_route = 0; | 2904 | spec->spdif_route = 0; |
2905 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
2906 | spec->loopback.amplist = ad1884_loopbacks; | ||
2907 | #endif | ||
2830 | 2908 | ||
2831 | codec->patch_ops = ad198x_patch_ops; | 2909 | codec->patch_ops = ad198x_patch_ops; |
2832 | 2910 | ||
@@ -3208,6 +3286,16 @@ static struct hda_verb ad1882_init_verbs[] = { | |||
3208 | { } /* end */ | 3286 | { } /* end */ |
3209 | }; | 3287 | }; |
3210 | 3288 | ||
3289 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
3290 | static struct hda_amp_list ad1882_loopbacks[] = { | ||
3291 | { 0x20, HDA_INPUT, 0 }, /* Front Mic */ | ||
3292 | { 0x20, HDA_INPUT, 1 }, /* Mic */ | ||
3293 | { 0x20, HDA_INPUT, 4 }, /* Line */ | ||
3294 | { 0x20, HDA_INPUT, 6 }, /* CD */ | ||
3295 | { } /* end */ | ||
3296 | }; | ||
3297 | #endif | ||
3298 | |||
3211 | /* models */ | 3299 | /* models */ |
3212 | enum { | 3300 | enum { |
3213 | AD1882_3STACK, | 3301 | AD1882_3STACK, |
@@ -3246,6 +3334,9 @@ static int patch_ad1882(struct hda_codec *codec) | |||
3246 | spec->num_init_verbs = 1; | 3334 | spec->num_init_verbs = 1; |
3247 | spec->init_verbs[0] = ad1882_init_verbs; | 3335 | spec->init_verbs[0] = ad1882_init_verbs; |
3248 | spec->spdif_route = 0; | 3336 | spec->spdif_route = 0; |
3337 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
3338 | spec->loopback.amplist = ad1882_loopbacks; | ||
3339 | #endif | ||
3249 | 3340 | ||
3250 | codec->patch_ops = ad198x_patch_ops; | 3341 | codec->patch_ops = ad198x_patch_ops; |
3251 | 3342 | ||