Commit 59ed1ead authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Move codec suspend/resume to codec driver

This patch moves the suspend/resume mechanisms down to each codec
driver level, as we have a proper codec driver bound on the bus now.
Then we get the asynchronous PM gratis without fiddling much in the
driver level.

As a soft-landing transition, implement the common suspend/resume pm
ops for hda_codec_driver and keep the each codec driver intact.  Only
the callers of suspend/resume in the controller side (azx_suspend()
and azx_resume()) are removed.

Another involved place is azx_bus_reset() calling the temporary
suspend and resume as a hackish method of bus reset.  The HD-audio
core provide a helper function snd_hda_bus_reset() instead.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent d8a766a1
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/pm.h>
#include <sound/core.h> #include <sound/core.h>
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.h" #include "hda_local.h"
...@@ -138,7 +139,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, ...@@ -138,7 +139,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
drv->driver.bus = &snd_hda_bus_type; drv->driver.bus = &snd_hda_bus_type;
drv->driver.probe = hda_codec_driver_probe; drv->driver.probe = hda_codec_driver_probe;
drv->driver.remove = hda_codec_driver_remove; drv->driver.remove = hda_codec_driver_remove;
/* TODO: PM and others */ drv->driver.pm = &hda_codec_driver_pm;
return driver_register(&drv->driver); return driver_register(&drv->driver);
} }
EXPORT_SYMBOL_GPL(__hda_codec_driver_register); EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
......
...@@ -1250,6 +1250,7 @@ int snd_hda_codec_new(struct hda_bus *bus, ...@@ -1250,6 +1250,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
dev->groups = snd_hda_dev_attr_groups; dev->groups = snd_hda_dev_attr_groups;
dev_set_name(dev, "hdaudioC%dD%d", bus->card->number, codec_addr); dev_set_name(dev, "hdaudioC%dD%d", bus->card->number, codec_addr);
dev_set_drvdata(dev, codec); /* for sysfs */ dev_set_drvdata(dev, codec); /* for sysfs */
device_enable_async_suspend(dev);
codec->bus = bus; codec->bus = bus;
codec->addr = codec_addr; codec->addr = codec_addr;
...@@ -3970,8 +3971,31 @@ static void hda_call_codec_resume(struct hda_codec *codec) ...@@ -3970,8 +3971,31 @@ static void hda_call_codec_resume(struct hda_codec *codec)
codec->in_pm = 0; codec->in_pm = 0;
snd_hda_power_down(codec); /* flag down before returning */ snd_hda_power_down(codec); /* flag down before returning */
} }
static int hda_codec_driver_suspend(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
int i;
cancel_delayed_work_sync(&codec->jackpoll_work);
for (i = 0; i < codec->num_pcms; i++)
snd_pcm_suspend_all(codec->pcm_info[i].pcm);
hda_call_codec_suspend(codec, false);
return 0;
}
static int hda_codec_driver_resume(struct device *dev)
{
hda_call_codec_resume(dev_to_hda_codec(dev));
return 0;
}
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
/* referred in hda_bind.c */
const struct dev_pm_ops hda_codec_driver_pm = {
SET_SYSTEM_SLEEP_PM_OPS(hda_codec_driver_suspend,
hda_codec_driver_resume)
};
/** /**
* snd_hda_build_controls - build mixer controls * snd_hda_build_controls - build mixer controls
...@@ -5505,77 +5529,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec, ...@@ -5505,77 +5529,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
} }
EXPORT_SYMBOL_GPL(snd_hda_add_imux_item); EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
#ifdef CONFIG_PM
/*
* power management
*/
static void hda_async_suspend(void *data, async_cookie_t cookie)
{
hda_call_codec_suspend(data, false);
}
static void hda_async_resume(void *data, async_cookie_t cookie)
{
hda_call_codec_resume(data);
}
/** /**
* snd_hda_suspend - suspend the codecs * snd_hda_bus_reset - Reset the bus
* @bus: the HDA bus * @bus: HD-audio bus
*
* Returns 0 if successful.
*/ */
int snd_hda_suspend(struct hda_bus *bus) void snd_hda_bus_reset(struct hda_bus *bus)
{ {
struct hda_codec *codec; struct hda_codec *codec;
ASYNC_DOMAIN_EXCLUSIVE(domain);
list_for_each_entry(codec, &bus->codec_list, list) { list_for_each_entry(codec, &bus->codec_list, list) {
/* FIXME: maybe a better way needed for forced reset */
cancel_delayed_work_sync(&codec->jackpoll_work); cancel_delayed_work_sync(&codec->jackpoll_work);
#ifdef CONFIG_PM
if (hda_codec_is_power_on(codec)) { if (hda_codec_is_power_on(codec)) {
if (bus->num_codecs > 1) hda_call_codec_suspend(codec, false);
async_schedule_domain(hda_async_suspend, codec,
&domain);
else
hda_call_codec_suspend(codec, false);
}
}
if (bus->num_codecs > 1)
async_synchronize_full_domain(&domain);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_suspend);
/**
* snd_hda_resume - resume the codecs
* @bus: the HDA bus
*
* Returns 0 if successful.
*/
int snd_hda_resume(struct hda_bus *bus)
{
struct hda_codec *codec;
ASYNC_DOMAIN_EXCLUSIVE(domain);
list_for_each_entry(codec, &bus->codec_list, list) {
if (bus->num_codecs > 1)
async_schedule_domain(hda_async_resume, codec, &domain);
else
hda_call_codec_resume(codec); hda_call_codec_resume(codec);
}
#endif
} }
if (bus->num_codecs > 1)
async_synchronize_full_domain(&domain);
return 0;
} }
EXPORT_SYMBOL_GPL(snd_hda_resume); EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
#endif /* CONFIG_PM */
/* /*
* generic arrays * generic arrays
......
...@@ -567,14 +567,12 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, ...@@ -567,14 +567,12 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
int snd_hda_lock_devices(struct hda_bus *bus); int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
/* /*
* power management * power management
*/ */
#ifdef CONFIG_PM extern const struct dev_pm_ops hda_codec_driver_pm;
int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
static inline static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
......
...@@ -1780,15 +1780,8 @@ static void azx_bus_reset(struct hda_bus *bus) ...@@ -1780,15 +1780,8 @@ static void azx_bus_reset(struct hda_bus *bus)
bus->in_reset = 1; bus->in_reset = 1;
azx_stop_chip(chip); azx_stop_chip(chip);
azx_init_chip(chip, true); azx_init_chip(chip, true);
#ifdef CONFIG_PM if (chip->initialized)
if (chip->initialized) { snd_hda_bus_reset(chip->bus);
struct azx_pcm *p;
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
snd_hda_suspend(chip->bus);
snd_hda_resume(chip->bus);
}
#endif
bus->in_reset = 0; bus->in_reset = 0;
} }
......
...@@ -772,7 +772,6 @@ static int azx_suspend(struct device *dev) ...@@ -772,7 +772,6 @@ static int azx_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev); struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip; struct azx *chip;
struct hda_intel *hda; struct hda_intel *hda;
struct azx_pcm *p;
if (!card) if (!card)
return 0; return 0;
...@@ -784,10 +783,6 @@ static int azx_suspend(struct device *dev) ...@@ -784,10 +783,6 @@ static int azx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
azx_clear_irq_pending(chip); azx_clear_irq_pending(chip);
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip); azx_stop_chip(chip);
azx_enter_link_reset(chip); azx_enter_link_reset(chip);
if (chip->irq >= 0) { if (chip->irq >= 0) {
...@@ -830,7 +825,6 @@ static int azx_resume(struct device *dev) ...@@ -830,7 +825,6 @@ static int azx_resume(struct device *dev)
azx_init_chip(chip, true); azx_init_chip(chip, true);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0; return 0;
} }
......
...@@ -249,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev) ...@@ -249,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev)
{ {
struct snd_card *card = dev_get_drvdata(dev); struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data; struct azx *chip = card->private_data;
struct azx_pcm *p;
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip); azx_stop_chip(chip);
azx_enter_link_reset(chip); azx_enter_link_reset(chip);
...@@ -277,7 +272,6 @@ static int hda_tegra_resume(struct device *dev) ...@@ -277,7 +272,6 @@ static int hda_tegra_resume(struct device *dev)
azx_init_chip(chip, 1); azx_init_chip(chip, 1);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment