kfree(codec);
}
+static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
+ hda_nid_t fg, unsigned int power_state);
+
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
+ codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_CLKSTOP);
+ if (!codec->d3_stop_clk)
+ bus->power_keep_link_on = 1;
+
/* power-up all before initialization */
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
int count;
unsigned int state;
+ codec->d3_stop_clk_ok = 0;
+
if (codec->patch_ops.set_power_state) {
codec->patch_ops.set_power_state(codec, fg, power_state);
return;
if (!(state & AC_PWRST_ERROR))
break;
}
+
+ if ((power_state == AC_PWRST_D3)
+ && codec->d3_stop_clk && (state & AC_PWRST_CLK_STOP_OK))
+ codec->d3_stop_clk_ok = 1;
}
#ifdef CONFIG_SND_HDA_HWDEP
hda_call_codec_suspend(codec);
if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ bus->ops.pm_notify(bus, codec);
}
static void hda_keep_power_on(struct hda_codec *codec)
spin_unlock(&codec->power_lock);
if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ bus->ops.pm_notify(bus, codec);
hda_call_codec_resume(codec);
spin_lock(&codec->power_lock);
#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>
}
#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 */
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) {
#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);
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 */
/*
}
#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)
{
pci_set_drvdata(pci, card);
+ if (pci_dev_run_wake(pci))
+ pm_runtime_put_noidle(&pci->dev);
+
dev++;
return 0;
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);
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);