aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-01-24 11:23:35 -0500
committerTakashi Iwai <tiwai@suse.de>2013-01-24 11:23:35 -0500
commit9419ab6b72325e20789a61004cf68dc9e909a009 (patch)
treef9c7a7ddaec6208153716ae798e4374ae49f06a0 /sound
parent25368c47aee6d909923001918041f2e94bfa02ef (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')
-rw-r--r--sound/pci/hda/hda_codec.c39
-rw-r--r--sound/pci/hda/hda_codec.h7
-rw-r--r--sound/pci/hda/patch_conexant.c2
-rw-r--r--sound/pci/hda/patch_sigmatel.c2
4 files changed, 32 insertions, 18 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
1277static unsigned int hda_set_power_state(struct hda_codec *codec, 1277static unsigned int hda_set_power_state(struct hda_codec *codec,
1278 unsigned int power_state); 1278 unsigned int power_state);
1279static 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)
3649EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); 3652EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache);
3650 3653
3651void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, 3654void 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}
3677EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); 3674EXPORT_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 */
3719static 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))
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index cc73287341de..fbedcf3c9d0b 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -886,6 +886,10 @@ struct hda_codec {
886 spinlock_t power_lock; 886 spinlock_t power_lock;
887#endif 887#endif
888 888
889 /* filter the requested power state per nid */
890 unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
891 unsigned int power_state);
892
889 /* codec-specific additional proc output */ 893 /* codec-specific additional proc output */
890 void (*proc_widget_hook)(struct snd_info_buffer *buffer, 894 void (*proc_widget_hook)(struct snd_info_buffer *buffer,
891 struct hda_codec *codec, hda_nid_t nid); 895 struct hda_codec *codec, hda_nid_t nid);
@@ -1047,8 +1051,7 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
1047void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); 1051void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
1048void snd_hda_bus_reboot_notify(struct hda_bus *bus); 1052void snd_hda_bus_reboot_notify(struct hda_bus *bus);
1049void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, 1053void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
1050 unsigned int power_state, 1054 unsigned int power_state);
1051 bool eapd_workaround);
1052 1055
1053int snd_hda_lock_devices(struct hda_bus *bus); 1056int snd_hda_lock_devices(struct hda_bus *bus);
1054void snd_hda_unlock_devices(struct hda_bus *bus); 1057void snd_hda_unlock_devices(struct hda_bus *bus);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index d98d470b0f26..7d941ef54172 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -435,7 +435,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
435 /* partial workaround for "azx_get_response timeout" */ 435 /* partial workaround for "azx_get_response timeout" */
436 if (power_state == AC_PWRST_D0) 436 if (power_state == AC_PWRST_D0)
437 msleep(10); 437 msleep(10);
438 snd_hda_codec_set_power_to_all(codec, fg, power_state, true); 438 snd_hda_codec_set_power_to_all(codec, fg, power_state);
439} 439}
440 440
441static int conexant_init(struct hda_codec *codec) 441static int conexant_init(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 0aa0ceba0806..5895d8f9a546 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -3724,7 +3724,7 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg,
3724 } 3724 }
3725 snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, 3725 snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
3726 afg_power_state); 3726 afg_power_state);
3727 snd_hda_codec_set_power_to_all(codec, fg, power_state, true); 3727 snd_hda_codec_set_power_to_all(codec, fg, power_state);
3728} 3728}
3729#else 3729#else
3730#define stac_suspend NULL 3730#define stac_suspend NULL