CHROMIUM: sound / hda: Fix D0 power state checking.
authorShawn Nematbakhsh <shawnn@google.com>
Fri, 30 Nov 2012 20:35:27 +0000 (12:35 -0800)
committerGerrit <chrome-bot@google.com>
Sat, 1 Dec 2012 01:58:33 +0000 (17:58 -0800)
Power state checking for HDA cards is broken. According to the HDA spec, the
lower four bits of the power state register define the "set" power state and
the next four bits define the "actual" state. Subsequent bits have different
meanings. When we check the power state of our card during transition to D0,
we read the entire register and compare to AC_PWRST_D0 (0). This leads to
timeout of the wait loop and ~500ms wasted during resume if some upper
status bits are set.

Solution is to mask off all upper bits and compare only get + set state. If
these states are 0, it means the transition has completed.

This is fixed differently in the mainstream kernel repo, but there are some
extra dependencies involved. Therefore, this patch should NOT be carried
forward through the next kernel rebase.

DO NOT CARRY FORWARD.

TEST=Run power_Resume test on HDA card that reports PS-ClkStopOk status.
Confirm that resume time is no longer ~500 ms.
BUG=chrome-os-partner:15729

Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Change-Id: I73dbe59ac8c19bad5c0eabdee5116da49d936e72
Reviewed-on: https://gerrit.chromium.org/gerrit/39012
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Commit-Ready: Shawn Nematbakhsh <shawnn@google.com>
Tested-by: Shawn Nematbakhsh <shawnn@google.com>
sound/pci/hda/hda_codec.c

index ed4e7e6..e0f1038 100644 (file)
@@ -3434,13 +3434,16 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
 
        if (power_state == AC_PWRST_D0) {
                unsigned long end_time;
-               int state;
+               int state, actual_state, set_state;
                /* wait until the codec reachs to D0 */
                end_time = jiffies + msecs_to_jiffies(500);
                do {
                        state = snd_hda_codec_read(codec, fg, 0,
                                                   AC_VERB_GET_POWER_STATE, 0);
-                       if (state == power_state)
+                       actual_state = (state >> 4) & 0x0f;
+                       set_state = state & 0x0f;
+                       if (actual_state == set_state &&
+                           actual_state == power_state)
                                break;
                        msleep(1);
                } while (time_after_eq(end_time, jiffies));