ALSA: hda - Add power state filtering
authorTakashi Iwai <tiwai@suse.de>
Thu, 24 Jan 2013 16:23:35 +0000 (17:23 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 24 Jan 2013 16:23:35 +0000 (17:23 +0100)
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>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_sigmatel.c

index e4e0501ab84a3c8bf2ffa37613191cc68541e980..19ff923b243167912744133ee407d6f1e3cb6d73 100644 (file)
@@ -1276,6 +1276,8 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
 
 static unsigned int hda_set_power_state(struct hda_codec *codec,
                                unsigned int power_state);
+static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
+                                        unsigned int power_state);
 
 /**
  * snd_hda_codec_new - create a HDA codec
@@ -1396,6 +1398,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
 #endif
        codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
                                        AC_PWRST_EPSS);
+       codec->power_filter = default_power_filter;
 
        /* power-up all before initialization */
        hda_set_power_state(codec, AC_PWRST_D0);
@@ -3649,29 +3652,23 @@ void snd_hda_codec_flush_cache(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache);
 
 void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
-                                   unsigned int power_state,
-                                   bool eapd_workaround)
+                                   unsigned int power_state)
 {
        hda_nid_t nid = codec->start_nid;
        int i;
 
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int state = power_state;
                if (!(wcaps & AC_WCAP_POWER))
                        continue;
-               /* don't power down the widget if it controls eapd and
-                * EAPD_BTLENABLE is set.
-                */
-               if (eapd_workaround && power_state == AC_PWRST_D3 &&
-                   get_wcaps_type(wcaps) == AC_WID_PIN &&
-                   (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
-                       int eapd = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_EAPD_BTLENABLE, 0);
-                       if (eapd & 0x02)
+               if (codec->power_filter) {
+                       state = codec->power_filter(codec, nid, power_state);
+                       if (state != power_state && power_state == AC_PWRST_D3)
                                continue;
                }
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
-                                   power_state);
+                                   state);
        }
 }
 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,
        return state;
 }
 
+/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */
+static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
+                                        unsigned int power_state)
+{
+       if (power_state == AC_PWRST_D3 &&
+           get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
+           (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+               int eapd = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_EAPD_BTLENABLE, 0);
+               if (eapd & 0x02)
+                       return AC_PWRST_D0;
+       }
+       return power_state;
+}
+
 /*
  * set power state of the codec, and return the power state
  */
@@ -3743,8 +3755,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
                        snd_hda_codec_read(codec, fg, 0,
                                           AC_VERB_SET_POWER_STATE,
                                           power_state);
-                       snd_hda_codec_set_power_to_all(codec, fg, power_state,
-                                                      true);
+                       snd_hda_codec_set_power_to_all(codec, fg, power_state);
                }
                state = hda_sync_power_state(codec, fg, power_state);
                if (!(state & AC_PWRST_ERROR))
index cc73287341dec72ff670986fcd52d8faa2b855b9..fbedcf3c9d0b2aae8bc5c65b2d72f32330f44e86 100644 (file)
@@ -886,6 +886,10 @@ struct hda_codec {
        spinlock_t power_lock;
 #endif
 
+       /* filter the requested power state per nid */
+       unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
+                                    unsigned int power_state);
+
        /* codec-specific additional proc output */
        void (*proc_widget_hook)(struct snd_info_buffer *buffer,
                                 struct hda_codec *codec, hda_nid_t nid);
@@ -1047,8 +1051,7 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
 void snd_hda_bus_reboot_notify(struct hda_bus *bus);
 void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
-                                   unsigned int power_state,
-                                   bool eapd_workaround);
+                                   unsigned int power_state);
 
 int snd_hda_lock_devices(struct hda_bus *bus);
 void snd_hda_unlock_devices(struct hda_bus *bus);
index d98d470b0f26598ab3f81290e46b4312a873d3c6..7d941ef54172b81058dd0d722f051459cab6e7d9 100644 (file)
@@ -435,7 +435,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
        /* partial workaround for "azx_get_response timeout" */
        if (power_state == AC_PWRST_D0)
                msleep(10);
-       snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+       snd_hda_codec_set_power_to_all(codec, fg, power_state);
 }
 
 static int conexant_init(struct hda_codec *codec)
index 0aa0ceba0806217fca35f8ed58e46b323c52e7a5..5895d8f9a546d53eb6cd60b23c1ba186199cc5da 100644 (file)
@@ -3724,7 +3724,7 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg,
        }
        snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
                        afg_power_state);
-       snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+       snd_hda_codec_set_power_to_all(codec, fg, power_state);
 }
 #else
 #define stac_suspend           NULL