diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-01-24 11:23:35 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-01-24 11:23:35 -0500 |
commit | 9419ab6b72325e20789a61004cf68dc9e909a009 (patch) | |
tree | f9c7a7ddaec6208153716ae798e4374ae49f06a0 /sound/pci/hda/hda_codec.c | |
parent | 25368c47aee6d909923001918041f2e94bfa02ef (diff) |
ALSA: hda - Add power state filtering
Add a hook to struct hda_codec for filtering the target power state of
each widget when powering up/down. The current hackish EAPD check is
implemented as the default hook pointer, too.
This allows codec drivers to implement own power filter. In the
upcoming changes, the generic parser will have the better power filter
based on the active paths.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 39 |
1 files changed, 25 insertions, 14 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e4e0501ab84a..19ff923b2431 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -1276,6 +1276,8 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, | |||
1276 | 1276 | ||
1277 | static unsigned int hda_set_power_state(struct hda_codec *codec, | 1277 | static unsigned int hda_set_power_state(struct hda_codec *codec, |
1278 | unsigned int power_state); | 1278 | unsigned int power_state); |
1279 | static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, | ||
1280 | unsigned int power_state); | ||
1279 | 1281 | ||
1280 | /** | 1282 | /** |
1281 | * snd_hda_codec_new - create a HDA codec | 1283 | * snd_hda_codec_new - create a HDA codec |
@@ -1396,6 +1398,7 @@ int snd_hda_codec_new(struct hda_bus *bus, | |||
1396 | #endif | 1398 | #endif |
1397 | codec->epss = snd_hda_codec_get_supported_ps(codec, fg, | 1399 | codec->epss = snd_hda_codec_get_supported_ps(codec, fg, |
1398 | AC_PWRST_EPSS); | 1400 | AC_PWRST_EPSS); |
1401 | codec->power_filter = default_power_filter; | ||
1399 | 1402 | ||
1400 | /* power-up all before initialization */ | 1403 | /* power-up all before initialization */ |
1401 | hda_set_power_state(codec, AC_PWRST_D0); | 1404 | hda_set_power_state(codec, AC_PWRST_D0); |
@@ -3649,29 +3652,23 @@ void snd_hda_codec_flush_cache(struct hda_codec *codec) | |||
3649 | EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); | 3652 | EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); |
3650 | 3653 | ||
3651 | void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, | 3654 | void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, |
3652 | unsigned int power_state, | 3655 | unsigned int power_state) |
3653 | bool eapd_workaround) | ||
3654 | { | 3656 | { |
3655 | hda_nid_t nid = codec->start_nid; | 3657 | hda_nid_t nid = codec->start_nid; |
3656 | int i; | 3658 | int i; |
3657 | 3659 | ||
3658 | for (i = 0; i < codec->num_nodes; i++, nid++) { | 3660 | for (i = 0; i < codec->num_nodes; i++, nid++) { |
3659 | unsigned int wcaps = get_wcaps(codec, nid); | 3661 | unsigned int wcaps = get_wcaps(codec, nid); |
3662 | unsigned int state = power_state; | ||
3660 | if (!(wcaps & AC_WCAP_POWER)) | 3663 | if (!(wcaps & AC_WCAP_POWER)) |
3661 | continue; | 3664 | continue; |
3662 | /* don't power down the widget if it controls eapd and | 3665 | if (codec->power_filter) { |
3663 | * EAPD_BTLENABLE is set. | 3666 | state = codec->power_filter(codec, nid, power_state); |
3664 | */ | 3667 | if (state != power_state && power_state == AC_PWRST_D3) |
3665 | if (eapd_workaround && power_state == AC_PWRST_D3 && | ||
3666 | get_wcaps_type(wcaps) == AC_WID_PIN && | ||
3667 | (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { | ||
3668 | int eapd = snd_hda_codec_read(codec, nid, 0, | ||
3669 | AC_VERB_GET_EAPD_BTLENABLE, 0); | ||
3670 | if (eapd & 0x02) | ||
3671 | continue; | 3668 | continue; |
3672 | } | 3669 | } |
3673 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, | 3670 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, |
3674 | power_state); | 3671 | state); |
3675 | } | 3672 | } |
3676 | } | 3673 | } |
3677 | EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); | 3674 | EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); |
@@ -3718,6 +3715,21 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, | |||
3718 | return state; | 3715 | return state; |
3719 | } | 3716 | } |
3720 | 3717 | ||
3718 | /* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */ | ||
3719 | static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, | ||
3720 | unsigned int power_state) | ||
3721 | { | ||
3722 | if (power_state == AC_PWRST_D3 && | ||
3723 | get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && | ||
3724 | (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { | ||
3725 | int eapd = snd_hda_codec_read(codec, nid, 0, | ||
3726 | AC_VERB_GET_EAPD_BTLENABLE, 0); | ||
3727 | if (eapd & 0x02) | ||
3728 | return AC_PWRST_D0; | ||
3729 | } | ||
3730 | return power_state; | ||
3731 | } | ||
3732 | |||
3721 | /* | 3733 | /* |
3722 | * set power state of the codec, and return the power state | 3734 | * set power state of the codec, and return the power state |
3723 | */ | 3735 | */ |
@@ -3743,8 +3755,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, | |||
3743 | snd_hda_codec_read(codec, fg, 0, | 3755 | snd_hda_codec_read(codec, fg, 0, |
3744 | AC_VERB_SET_POWER_STATE, | 3756 | AC_VERB_SET_POWER_STATE, |
3745 | power_state); | 3757 | power_state); |
3746 | snd_hda_codec_set_power_to_all(codec, fg, power_state, | 3758 | snd_hda_codec_set_power_to_all(codec, fg, power_state); |
3747 | true); | ||
3748 | } | 3759 | } |
3749 | state = hda_sync_power_state(codec, fg, power_state); | 3760 | state = hda_sync_power_state(codec, fg, power_state); |
3750 | if (!(state & AC_PWRST_ERROR)) | 3761 | if (!(state & AC_PWRST_ERROR)) |