aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMengdong Lin <mengdong.lin@intel.com>2012-08-23 05:32:30 -0400
committerTakashi Iwai <tiwai@suse.de>2012-08-23 08:21:32 -0400
commitb8dfc4624162c0547d7f36a9df48da2d9b4bd58a (patch)
tree55f32cd8537eb3db947282823182ed0dcc41da0a
parent8a5354140a86b6d4057793a9ed28d29ac8ce6ba6 (diff)
ALSA: hda - add runtime PM support
Runtime PM can bring more power saving: - When the controller is suspended, its parent device will also have a chance to suspend. - PCI subsystem can choose the lowest power state the controller can signal wake up from. This state can be D3cold on platforms with ACPI PM support. And runtime PM can provide a gerneral sysfs interface for a system policy manager. Runtime PM support is based on current HDA power saving implementation. The user can enable runtime PM on platfroms that provide acceptable latency on transition from D3 to D0. Details: - When both power saving and runtime PM are enabled: -- If a codec supports 'stop-clock' in D3, it will request suspending the controller after it enters D3 and request resuming the controller before back to D0. Thus the controller will be suspended only when all codecs are suspended and support stop-clock in D3. -- User IO operations and HW wakeup signal can resume the controller back to D0. - If runtime PM is disabled, power saving just works as before. - If power saving is disabled, the controller won't be suspended because the power usage counter can never be 0. More about 'stop-clock' feature: If a codec can support targeted pass-through operations in D3 state when there is no BCLK present on the link, it will set CLKSTOP flag in the supported power states and report PS-ClkStopOk when entering D3 state. Please refer to HDA spec section 7.3.3.10 Power state and 7.3.4.12 Supported Power State. [Fixed CONFIG_PM_RUNTIME dependency in hda_intel.c by tiwai] Signed-off-by: Mengdong Lin <mengdong.lin@intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/pci/hda/hda_codec.c19
-rw-r--r--sound/pci/hda/hda_codec.h5
-rw-r--r--sound/pci/hda/hda_intel.c83
3 files changed, 87 insertions, 20 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 4efd2711acd9..0de1f76733d2 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1209,6 +1209,9 @@ static void snd_hda_codec_free(struct hda_codec *codec)
1209 kfree(codec); 1209 kfree(codec);
1210} 1210}
1211 1211
1212static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
1213 hda_nid_t fg, unsigned int power_state);
1214
1212static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 1215static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
1213 unsigned int power_state); 1216 unsigned int power_state);
1214 1217
@@ -1317,6 +1320,12 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
1317 AC_VERB_GET_SUBSYSTEM_ID, 0); 1320 AC_VERB_GET_SUBSYSTEM_ID, 0);
1318 } 1321 }
1319 1322
1323 codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec,
1324 codec->afg ? codec->afg : codec->mfg,
1325 AC_PWRST_CLKSTOP);
1326 if (!codec->d3_stop_clk)
1327 bus->power_keep_link_on = 1;
1328
1320 /* power-up all before initialization */ 1329 /* power-up all before initialization */
1321 hda_set_power_state(codec, 1330 hda_set_power_state(codec,
1322 codec->afg ? codec->afg : codec->mfg, 1331 codec->afg ? codec->afg : codec->mfg,
@@ -3535,6 +3544,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
3535 int count; 3544 int count;
3536 unsigned int state; 3545 unsigned int state;
3537 3546
3547 codec->d3_stop_clk_ok = 0;
3548
3538 if (codec->patch_ops.set_power_state) { 3549 if (codec->patch_ops.set_power_state) {
3539 codec->patch_ops.set_power_state(codec, fg, power_state); 3550 codec->patch_ops.set_power_state(codec, fg, power_state);
3540 return; 3551 return;
@@ -3557,6 +3568,10 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
3557 if (!(state & AC_PWRST_ERROR)) 3568 if (!(state & AC_PWRST_ERROR))
3558 break; 3569 break;
3559 } 3570 }
3571
3572 if ((power_state == AC_PWRST_D3)
3573 && codec->d3_stop_clk && (state & AC_PWRST_CLK_STOP_OK))
3574 codec->d3_stop_clk_ok = 1;
3560} 3575}
3561 3576
3562#ifdef CONFIG_SND_HDA_HWDEP 3577#ifdef CONFIG_SND_HDA_HWDEP
@@ -4408,7 +4423,7 @@ static void hda_power_work(struct work_struct *work)
4408 4423
4409 hda_call_codec_suspend(codec); 4424 hda_call_codec_suspend(codec);
4410 if (bus->ops.pm_notify) 4425 if (bus->ops.pm_notify)
4411 bus->ops.pm_notify(bus); 4426 bus->ops.pm_notify(bus, codec);
4412} 4427}
4413 4428
4414static void hda_keep_power_on(struct hda_codec *codec) 4429static void hda_keep_power_on(struct hda_codec *codec)
@@ -4466,7 +4481,7 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
4466 spin_unlock(&codec->power_lock); 4481 spin_unlock(&codec->power_lock);
4467 4482
4468 if (bus->ops.pm_notify) 4483 if (bus->ops.pm_notify)
4469 bus->ops.pm_notify(bus); 4484 bus->ops.pm_notify(bus, codec);
4470 hda_call_codec_resume(codec); 4485 hda_call_codec_resume(codec);
4471 4486
4472 spin_lock(&codec->power_lock); 4487 spin_lock(&codec->power_lock);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 92ecb2b2a05c..13c834f20440 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -616,7 +616,7 @@ struct hda_bus_ops {
616 void (*bus_reset)(struct hda_bus *bus); 616 void (*bus_reset)(struct hda_bus *bus);
617#ifdef CONFIG_SND_HDA_POWER_SAVE 617#ifdef CONFIG_SND_HDA_POWER_SAVE
618 /* notify power-up/down from codec to controller */ 618 /* notify power-up/down from codec to controller */
619 void (*pm_notify)(struct hda_bus *bus); 619 void (*pm_notify)(struct hda_bus *bus, struct hda_codec *codec);
620#endif 620#endif
621}; 621};
622 622
@@ -875,6 +875,9 @@ struct hda_codec {
875 unsigned long power_off_acct; 875 unsigned long power_off_acct;
876 unsigned long power_jiffies; 876 unsigned long power_jiffies;
877 spinlock_t power_lock; 877 spinlock_t power_lock;
878
879 unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
880 unsigned int d3_stop_clk_ok:1; /* BCLK can stop */
878#endif 881#endif
879 882
880 /* codec-specific additional proc output */ 883 /* codec-specific additional proc output */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 209bea435442..726f4208bd05 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -46,6 +46,7 @@
46#include <linux/mutex.h> 46#include <linux/mutex.h>
47#include <linux/reboot.h> 47#include <linux/reboot.h>
48#include <linux/io.h> 48#include <linux/io.h>
49#include <linux/pm_runtime.h>
49#ifdef CONFIG_X86 50#ifdef CONFIG_X86
50/* for snoop control */ 51/* for snoop control */
51#include <asm/pgtable.h> 52#include <asm/pgtable.h>
@@ -1032,7 +1033,7 @@ static unsigned int azx_get_response(struct hda_bus *bus,
1032} 1033}
1033 1034
1034#ifdef CONFIG_SND_HDA_POWER_SAVE 1035#ifdef CONFIG_SND_HDA_POWER_SAVE
1035static void azx_power_notify(struct hda_bus *bus); 1036static void azx_power_notify(struct hda_bus *bus, struct hda_codec *codec);
1036#endif 1037#endif
1037 1038
1038/* reset codec link */ 1039/* reset codec link */
@@ -1288,6 +1289,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
1288 u8 sd_status; 1289 u8 sd_status;
1289 int i, ok; 1290 int i, ok;
1290 1291
1292#ifdef CONFIG_PM_RUNTIME
1293 if (chip->pci->dev.power.runtime_status != RPM_ACTIVE)
1294 return IRQ_NONE;
1295#endif
1296
1291 spin_lock(&chip->reg_lock); 1297 spin_lock(&chip->reg_lock);
1292 1298
1293 if (chip->disabled) { 1299 if (chip->disabled) {
@@ -2400,23 +2406,17 @@ static void azx_stop_chip(struct azx *chip)
2400 2406
2401#ifdef CONFIG_SND_HDA_POWER_SAVE 2407#ifdef CONFIG_SND_HDA_POWER_SAVE
2402/* power-up/down the controller */ 2408/* power-up/down the controller */
2403static void azx_power_notify(struct hda_bus *bus) 2409static void azx_power_notify(struct hda_bus *bus, struct hda_codec *codec)
2404{ 2410{
2405 struct azx *chip = bus->private_data; 2411 struct azx *chip = bus->private_data;
2406 struct hda_codec *c;
2407 int power_on = 0;
2408 2412
2409 list_for_each_entry(c, &bus->codec_list, list) { 2413 if (bus->power_keep_link_on || !codec->d3_stop_clk_ok)
2410 if (c->power_on) { 2414 return;
2411 power_on = 1; 2415
2412 break; 2416 if (codec->power_on)
2413 } 2417 pm_runtime_get_sync(&chip->pci->dev);
2414 } 2418 else
2415 if (power_on) 2419 pm_runtime_put_sync(&chip->pci->dev);
2416 azx_init_chip(chip, 1);
2417 else if (chip->running && power_save_controller &&
2418 !bus->power_keep_link_on)
2419 azx_stop_chip(chip);
2420} 2420}
2421 2421
2422static DEFINE_MUTEX(card_list_lock); 2422static DEFINE_MUTEX(card_list_lock);
@@ -2520,11 +2520,43 @@ static int azx_resume(struct device *dev)
2520 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 2520 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2521 return 0; 2521 return 0;
2522} 2522}
2523static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume); 2523#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
2524
2525#ifdef CONFIG_PM_RUNTIME
2526static int azx_runtime_suspend(struct device *dev)
2527{
2528 struct snd_card *card = dev_get_drvdata(dev);
2529 struct azx *chip = card->private_data;
2530
2531 if (!power_save_controller)
2532 return -EAGAIN;
2533
2534 azx_stop_chip(chip);
2535 azx_clear_irq_pending(chip);
2536 return 0;
2537}
2538
2539static int azx_runtime_resume(struct device *dev)
2540{
2541 struct snd_card *card = dev_get_drvdata(dev);
2542 struct azx *chip = card->private_data;
2543
2544 azx_init_pci(chip);
2545 azx_init_chip(chip, 1);
2546 return 0;
2547}
2548#endif /* CONFIG_PM_RUNTIME */
2549
2550#ifdef CONFIG_PM
2551static const struct dev_pm_ops azx_pm = {
2552 SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
2553 SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, NULL)
2554};
2555
2524#define AZX_PM_OPS &azx_pm 2556#define AZX_PM_OPS &azx_pm
2525#else 2557#else
2526#define AZX_PM_OPS NULL 2558#define AZX_PM_OPS NULL
2527#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ 2559#endif /* CONFIG_PM */
2528 2560
2529 2561
2530/* 2562/*
@@ -3239,6 +3271,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
3239} 3271}
3240#endif 3272#endif
3241 3273
3274static void rpm_get_all_codecs(struct azx *chip)
3275{
3276 struct hda_codec *codec;
3277
3278 list_for_each_entry(codec, &chip->bus->codec_list, list) {
3279 pm_runtime_get_noresume(&chip->pci->dev);
3280 }
3281}
3282
3242static int __devinit azx_probe(struct pci_dev *pci, 3283static int __devinit azx_probe(struct pci_dev *pci,
3243 const struct pci_device_id *pci_id) 3284 const struct pci_device_id *pci_id)
3244{ 3285{
@@ -3290,6 +3331,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
3290 3331
3291 pci_set_drvdata(pci, card); 3332 pci_set_drvdata(pci, card);
3292 3333
3334 if (pci_dev_run_wake(pci))
3335 pm_runtime_put_noidle(&pci->dev);
3336
3293 dev++; 3337 dev++;
3294 return 0; 3338 return 0;
3295 3339
@@ -3342,6 +3386,7 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip)
3342 goto out_free; 3386 goto out_free;
3343 3387
3344 chip->running = 1; 3388 chip->running = 1;
3389 rpm_get_all_codecs(chip); /* all codecs are active */
3345 power_down_all_codecs(chip); 3390 power_down_all_codecs(chip);
3346 azx_notifier_register(chip); 3391 azx_notifier_register(chip);
3347 azx_add_card_list(chip); 3392 azx_add_card_list(chip);
@@ -3356,6 +3401,10 @@ out_free:
3356static void __devexit azx_remove(struct pci_dev *pci) 3401static void __devexit azx_remove(struct pci_dev *pci)
3357{ 3402{
3358 struct snd_card *card = pci_get_drvdata(pci); 3403 struct snd_card *card = pci_get_drvdata(pci);
3404
3405 if (pci_dev_run_wake(pci))
3406 pm_runtime_get_noresume(&pci->dev);
3407
3359 if (card) 3408 if (card)
3360 snd_card_free(card); 3409 snd_card_free(card);
3361 pci_set_drvdata(pci, NULL); 3410 pci_set_drvdata(pci, NULL);