Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: hda - Yet another fix for D3 stop-clock refcounting

The call of pm_notify callback in snd_hda_codec_free() should be with
the check of the current state whether pm_notify(false) is called or
not, instead of codec->power_on check.

For improving the code readability and fixing this inconsistency,
codec->d3_stop_clk_ok is renamed to codec->pm_down_notified, and this
flag is set only when runtime PM down is called. The new name reflects
to a more direct purpose of the flag.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

+23 -20
+22 -19
sound/pci/hda/hda_codec.c
··· 1206 1206 if (codec->patch_ops.free) 1207 1207 codec->patch_ops.free(codec); 1208 1208 #ifdef CONFIG_PM 1209 - if (codec->power_on) 1209 + if (!codec->pm_down_notified) /* cancel leftover refcounts */ 1210 1210 hda_call_pm_notify(codec->bus, false); 1211 1211 #endif 1212 1212 module_put(codec->owner); ··· 1222 1222 static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, 1223 1223 hda_nid_t fg, unsigned int power_state); 1224 1224 1225 - static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 1225 + static unsigned int hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 1226 1226 unsigned int power_state); 1227 1227 1228 1228 /** ··· 3564 3564 } 3565 3565 3566 3566 /* 3567 - * set power state of the codec 3567 + * set power state of the codec, and return the power state 3568 3568 */ 3569 - static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 3570 - unsigned int power_state) 3569 + static unsigned int hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 3570 + unsigned int power_state) 3571 3571 { 3572 3572 int count; 3573 3573 unsigned int state; 3574 - 3575 - #ifdef CONFIG_PM 3576 - codec->d3_stop_clk_ok = 0; 3577 - #endif 3578 3574 3579 3575 /* this delay seems necessary to avoid click noise at power-down */ 3580 3576 if (power_state == AC_PWRST_D3) { ··· 3595 3599 break; 3596 3600 } 3597 3601 3598 - #ifdef CONFIG_PM 3599 - if (!codec->bus->power_keep_link_on && power_state == AC_PWRST_D3 3600 - && codec->d3_stop_clk && (state & AC_PWRST_CLK_STOP_OK)) 3601 - codec->d3_stop_clk_ok = 1; 3602 - #endif 3602 + return state; 3603 3603 } 3604 3604 3605 3605 #ifdef CONFIG_SND_HDA_HWDEP ··· 3612 3620 #ifdef CONFIG_PM 3613 3621 /* 3614 3622 * call suspend and power-down; used both from PM and power-save 3623 + * this function returns the power state in the end 3615 3624 */ 3616 - static void hda_call_codec_suspend(struct hda_codec *codec) 3625 + static unsigned int hda_call_codec_suspend(struct hda_codec *codec) 3617 3626 { 3627 + unsigned int state; 3628 + 3618 3629 if (codec->patch_ops.suspend) 3619 3630 codec->patch_ops.suspend(codec); 3620 3631 hda_cleanup_all_streams(codec); 3621 - hda_set_power_state(codec, 3632 + state = hda_set_power_state(codec, 3622 3633 codec->afg ? codec->afg : codec->mfg, 3623 3634 AC_PWRST_D3); 3624 3635 cancel_delayed_work(&codec->power_work); ··· 3632 3637 codec->power_transition = 0; 3633 3638 codec->power_jiffies = jiffies; 3634 3639 spin_unlock(&codec->power_lock); 3640 + return state; 3635 3641 } 3636 3642 3637 3643 /* ··· 4434 4438 struct hda_codec *codec = 4435 4439 container_of(work, struct hda_codec, power_work.work); 4436 4440 struct hda_bus *bus = codec->bus; 4441 + unsigned int state; 4437 4442 4438 4443 spin_lock(&codec->power_lock); 4439 4444 if (codec->power_transition > 0) { /* during power-up sequence? */ ··· 4448 4451 } 4449 4452 spin_unlock(&codec->power_lock); 4450 4453 4451 - hda_call_codec_suspend(codec); 4452 - if (codec->d3_stop_clk_ok) 4454 + state = hda_call_codec_suspend(codec); 4455 + codec->pm_down_notified = 0; 4456 + if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) { 4457 + codec->pm_down_notified = 1; 4453 4458 hda_call_pm_notify(bus, false); 4459 + } 4454 4460 } 4455 4461 4456 4462 static void hda_keep_power_on(struct hda_codec *codec) ··· 4510 4510 codec->power_transition = 1; /* avoid reentrance */ 4511 4511 spin_unlock(&codec->power_lock); 4512 4512 4513 - if (codec->d3_stop_clk_ok) /* flag set at suspend */ 4513 + if (codec->pm_down_notified) { 4514 + codec->pm_down_notified = 0; 4514 4515 hda_call_pm_notify(bus, true); 4516 + } 4517 + 4515 4518 hda_call_codec_resume(codec); 4516 4519 4517 4520 spin_lock(&codec->power_lock);
+1 -1
sound/pci/hda/hda_codec.h
··· 868 868 #ifdef CONFIG_PM 869 869 unsigned int power_on :1; /* current (global) power-state */ 870 870 unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ 871 - unsigned int d3_stop_clk_ok:1; /* BCLK can stop */ 871 + unsigned int pm_down_notified:1; /* PM notified to controller */ 872 872 int power_transition; /* power-state in transition */ 873 873 int power_count; /* current (global) power refcount */ 874 874 struct delayed_work power_work; /* delayed task for powerdown */