diff options
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 83 |
1 files changed, 66 insertions, 17 deletions
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 @@ #include <linux/mutex.h> #include <linux/reboot.h> #include <linux/io.h> +#include <linux/pm_runtime.h> #ifdef CONFIG_X86 /* for snoop control */ #include <asm/pgtable.h> @@ -1032,7 +1033,7 @@ static unsigned int azx_get_response(struct hda_bus *bus, } #ifdef CONFIG_SND_HDA_POWER_SAVE -static void azx_power_notify(struct hda_bus *bus); +static void azx_power_notify(struct hda_bus *bus, struct hda_codec *codec); #endif /* reset codec link */ @@ -1288,6 +1289,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) u8 sd_status; int i, ok; +#ifdef CONFIG_PM_RUNTIME + if (chip->pci->dev.power.runtime_status != RPM_ACTIVE) + return IRQ_NONE; +#endif + spin_lock(&chip->reg_lock); if (chip->disabled) { @@ -2400,23 +2406,17 @@ static void azx_stop_chip(struct azx *chip) #ifdef CONFIG_SND_HDA_POWER_SAVE /* power-up/down the controller */ -static void azx_power_notify(struct hda_bus *bus) +static void azx_power_notify(struct hda_bus *bus, struct hda_codec *codec) { struct azx *chip = bus->private_data; - struct hda_codec *c; - int power_on = 0; - list_for_each_entry(c, &bus->codec_list, list) { - if (c->power_on) { - power_on = 1; - break; - } - } - if (power_on) - azx_init_chip(chip, 1); - else if (chip->running && power_save_controller && - !bus->power_keep_link_on) - azx_stop_chip(chip); + if (bus->power_keep_link_on || !codec->d3_stop_clk_ok) + return; + + if (codec->power_on) + pm_runtime_get_sync(&chip->pci->dev); + else + pm_runtime_put_sync(&chip->pci->dev); } static DEFINE_MUTEX(card_list_lock); @@ -2520,11 +2520,43 @@ static int azx_resume(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume); +#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ + +#ifdef CONFIG_PM_RUNTIME +static int azx_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + if (!power_save_controller) + return -EAGAIN; + + azx_stop_chip(chip); + azx_clear_irq_pending(chip); + return 0; +} + +static int azx_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + azx_init_pci(chip); + azx_init_chip(chip, 1); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM +static const struct dev_pm_ops azx_pm = { + SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, NULL) +}; + #define AZX_PM_OPS &azx_pm #else #define AZX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ +#endif /* CONFIG_PM */ /* @@ -3239,6 +3271,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context) } #endif +static void rpm_get_all_codecs(struct azx *chip) +{ + struct hda_codec *codec; + + list_for_each_entry(codec, &chip->bus->codec_list, list) { + pm_runtime_get_noresume(&chip->pci->dev); + } +} + static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -3290,6 +3331,9 @@ static int __devinit azx_probe(struct pci_dev *pci, pci_set_drvdata(pci, card); + if (pci_dev_run_wake(pci)) + pm_runtime_put_noidle(&pci->dev); + dev++; return 0; @@ -3342,6 +3386,7 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip) goto out_free; chip->running = 1; + rpm_get_all_codecs(chip); /* all codecs are active */ power_down_all_codecs(chip); azx_notifier_register(chip); azx_add_card_list(chip); @@ -3356,6 +3401,10 @@ out_free: static void __devexit azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); + + if (pci_dev_run_wake(pci)) + pm_runtime_get_noresume(&pci->dev); + if (card) snd_card_free(card); pci_set_drvdata(pci, NULL); |