diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-08-28 12:59:20 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-08-30 10:48:55 -0400 |
commit | 432c641e013d6e294e2ddf06d32a610eb1d4d856 (patch) | |
tree | 09cb193f39c9c4b6fa7126a49c291707449f6dbc /sound/pci/hda | |
parent | 68467f51c1b578ad98593bf5dee4337bd8d7798d (diff) |
ALSA: hda - Fix D3 clock stop check for codecs with own set_power_state op
When a codec provides its own set_power_state op, the D3-clock-stop
isn't checked correctly. And the recent changes for repeating the
state-setting operation isn't applied to such a codec, too.
This patch fixes these issues by moving the call of codec's own op to
the place where the generic power-set operation is done, and move the
power-state synchronization code out of
snd_hda_set_power_state_to_all() so that it can be called always at
the end of power-up/down sequence, and updates the D3 clock-stop flag
properly.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 61 |
1 files changed, 37 insertions, 24 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 90b34e830415..409f5ecd0a71 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -3518,20 +3518,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, | |||
3518 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, | 3518 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, |
3519 | power_state); | 3519 | power_state); |
3520 | } | 3520 | } |
3521 | |||
3522 | if (power_state == AC_PWRST_D0) { | ||
3523 | unsigned long end_time; | ||
3524 | int state; | ||
3525 | /* wait until the codec reachs to D0 */ | ||
3526 | end_time = jiffies + msecs_to_jiffies(500); | ||
3527 | do { | ||
3528 | state = snd_hda_codec_read(codec, fg, 0, | ||
3529 | AC_VERB_GET_POWER_STATE, 0); | ||
3530 | if (state == power_state) | ||
3531 | break; | ||
3532 | msleep(1); | ||
3533 | } while (time_after_eq(end_time, jiffies)); | ||
3534 | } | ||
3535 | } | 3521 | } |
3536 | EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); | 3522 | EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); |
3537 | 3523 | ||
@@ -3552,6 +3538,32 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg | |||
3552 | } | 3538 | } |
3553 | 3539 | ||
3554 | /* | 3540 | /* |
3541 | * wait until the state is reached, returns the current state | ||
3542 | */ | ||
3543 | static unsigned int hda_sync_power_state(struct hda_codec *codec, | ||
3544 | hda_nid_t fg, | ||
3545 | unsigned int power_state) | ||
3546 | { | ||
3547 | unsigned long end_time = jiffies + msecs_to_jiffies(500); | ||
3548 | unsigned int state, actual_state; | ||
3549 | |||
3550 | for (;;) { | ||
3551 | state = snd_hda_codec_read(codec, fg, 0, | ||
3552 | AC_VERB_GET_POWER_STATE, 0); | ||
3553 | if (state & AC_PWRST_ERROR) | ||
3554 | break; | ||
3555 | actual_state = (state >> 4) & 0x0f; | ||
3556 | if (actual_state == power_state) | ||
3557 | break; | ||
3558 | if (time_after_eq(jiffies, end_time)) | ||
3559 | break; | ||
3560 | /* wait until the codec reachs to the target state */ | ||
3561 | msleep(1); | ||
3562 | } | ||
3563 | return state; | ||
3564 | } | ||
3565 | |||
3566 | /* | ||
3555 | * set power state of the codec | 3567 | * set power state of the codec |
3556 | */ | 3568 | */ |
3557 | static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | 3569 | static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, |
@@ -3564,11 +3576,6 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | |||
3564 | codec->d3_stop_clk_ok = 0; | 3576 | codec->d3_stop_clk_ok = 0; |
3565 | #endif | 3577 | #endif |
3566 | 3578 | ||
3567 | if (codec->patch_ops.set_power_state) { | ||
3568 | codec->patch_ops.set_power_state(codec, fg, power_state); | ||
3569 | return; | ||
3570 | } | ||
3571 | |||
3572 | /* this delay seems necessary to avoid click noise at power-down */ | 3579 | /* this delay seems necessary to avoid click noise at power-down */ |
3573 | if (power_state == AC_PWRST_D3) { | 3580 | if (power_state == AC_PWRST_D3) { |
3574 | /* transition time less than 10ms for power down */ | 3581 | /* transition time less than 10ms for power down */ |
@@ -3577,11 +3584,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | |||
3577 | 3584 | ||
3578 | /* repeat power states setting at most 10 times*/ | 3585 | /* repeat power states setting at most 10 times*/ |
3579 | for (count = 0; count < 10; count++) { | 3586 | for (count = 0; count < 10; count++) { |
3580 | snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, | 3587 | if (codec->patch_ops.set_power_state) |
3581 | power_state); | 3588 | codec->patch_ops.set_power_state(codec, fg, |
3582 | snd_hda_codec_set_power_to_all(codec, fg, power_state, true); | 3589 | power_state); |
3583 | state = snd_hda_codec_read(codec, fg, 0, | 3590 | else { |
3584 | AC_VERB_GET_POWER_STATE, 0); | 3591 | snd_hda_codec_read(codec, fg, 0, |
3592 | AC_VERB_SET_POWER_STATE, | ||
3593 | power_state); | ||
3594 | snd_hda_codec_set_power_to_all(codec, fg, power_state, | ||
3595 | true); | ||
3596 | } | ||
3597 | state = hda_sync_power_state(codec, fg, power_state); | ||
3585 | if (!(state & AC_PWRST_ERROR)) | 3598 | if (!(state & AC_PWRST_ERROR)) |
3586 | break; | 3599 | break; |
3587 | } | 3600 | } |