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/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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
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)
|
|
||||||
async_schedule_domain(hda_async_suspend, codec,
|
|
||||||
&domain);
|
|
||||||
else
|
|
||||||
hda_call_codec_suspend(codec, false);
|
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,
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue