ALSA: hda - add runtime PM support
[pandora-kernel.git] / sound / pci / hda / hda_intel.c
index 209bea4..726f420 100644 (file)
@@ -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);