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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
d8a766a16e
commit
59ed1eade1
|
@ -8,6 +8,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm.h>
|
||||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
@ -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.probe = hda_codec_driver_probe;
|
||||
drv->driver.remove = hda_codec_driver_remove;
|
||||
/* TODO: PM and others */
|
||||
drv->driver.pm = &hda_codec_driver_pm;
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
|
||||
|
|
|
@ -1250,6 +1250,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
|
|||
dev->groups = snd_hda_dev_attr_groups;
|
||||
dev_set_name(dev, "hdaudioC%dD%d", bus->card->number, codec_addr);
|
||||
dev_set_drvdata(dev, codec); /* for sysfs */
|
||||
device_enable_async_suspend(dev);
|
||||
|
||||
codec->bus = bus;
|
||||
codec->addr = codec_addr;
|
||||
|
@ -3970,8 +3971,31 @@ static void hda_call_codec_resume(struct hda_codec *codec)
|
|||
codec->in_pm = 0;
|
||||
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 */
|
||||
|
||||
/* 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
|
||||
|
@ -5505,77 +5529,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
|
|||
}
|
||||
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
|
||||
* @bus: the HDA bus
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
* snd_hda_bus_reset - Reset the bus
|
||||
* @bus: HD-audio bus
|
||||
*/
|
||||
int snd_hda_suspend(struct hda_bus *bus)
|
||||
void snd_hda_bus_reset(struct hda_bus *bus)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
ASYNC_DOMAIN_EXCLUSIVE(domain);
|
||||
|
||||
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);
|
||||
#ifdef CONFIG_PM
|
||||
if (hda_codec_is_power_on(codec)) {
|
||||
if (bus->num_codecs > 1)
|
||||
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);
|
||||
}
|
||||
|
||||
if (bus->num_codecs > 1)
|
||||
async_synchronize_full_domain(&domain);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_resume);
|
||||
#endif /* CONFIG_PM */
|
||||
EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
|
||||
|
||||
/*
|
||||
* generic arrays
|
||||
|
|
|
@ -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);
|
||||
void snd_hda_unlock_devices(struct hda_bus *bus);
|
||||
void snd_hda_bus_reset(struct hda_bus *bus);
|
||||
|
||||
/*
|
||||
* power management
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
int snd_hda_suspend(struct hda_bus *bus);
|
||||
int snd_hda_resume(struct hda_bus *bus);
|
||||
#endif
|
||||
extern const struct dev_pm_ops hda_codec_driver_pm;
|
||||
|
||||
static inline
|
||||
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)
|
|||
bus->in_reset = 1;
|
||||
azx_stop_chip(chip);
|
||||
azx_init_chip(chip, true);
|
||||
#ifdef CONFIG_PM
|
||||
if (chip->initialized) {
|
||||
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
|
||||
if (chip->initialized)
|
||||
snd_hda_bus_reset(chip->bus);
|
||||
bus->in_reset = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -772,7 +772,6 @@ static int azx_suspend(struct device *dev)
|
|||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct azx *chip;
|
||||
struct hda_intel *hda;
|
||||
struct azx_pcm *p;
|
||||
|
||||
if (!card)
|
||||
return 0;
|
||||
|
@ -784,10 +783,6 @@ static int azx_suspend(struct device *dev)
|
|||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
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_enter_link_reset(chip);
|
||||
if (chip->irq >= 0) {
|
||||
|
@ -830,7 +825,6 @@ static int azx_resume(struct device *dev)
|
|||
|
||||
azx_init_chip(chip, true);
|
||||
|
||||
snd_hda_resume(chip->bus);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -249,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev)
|
|||
{
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct azx *chip = card->private_data;
|
||||
struct azx_pcm *p;
|
||||
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
|
||||
|
||||
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_enter_link_reset(chip);
|
||||
|
@ -277,7 +272,6 @@ static int hda_tegra_resume(struct device *dev)
|
|||
|
||||
azx_init_chip(chip, 1);
|
||||
|
||||
snd_hda_resume(chip->bus);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue