[alsa-devel] [PATCH v4 1/1] ALSA: hda - add runtime PM support

Lin, Mengdong mengdong.lin at intel.com
Thu Aug 23 11:25:45 CEST 2012


Please ignore this patch, '#include <linux/pm_runtime.h>' should be removed from hda_codec.c
I'll re-send the patch.

Thanks
Mengdong

> -----Original Message-----
> From: Lin, Mengdong
> Sent: Thursday, August 23, 2012 5:17 PM
> To: alsa-devel at alsa-project.org
> Cc: tiwai at suse.de; Lin, Mengdong
> Subject: [PATCH v4 1/1] ALSA: hda - add runtime PM support
> 
> From: Mengdong Lin <mengdong.lin at intel.com>
> 
> Runtime PM can bring more power saving:
> - When the controller is suspended, its parent device will also have a chance
>   to suspend.
> - PCI subsystem can choose the lowest power state the controller can signal
>   wake up from. This state can be D3cold on platforms with ACPI PM
> support.
> And runtime PM can provide a gerneral sysfs interface for a system policy
> manager.
> 
> Runtime PM support is based on current HDA power saving implementation.
> The user can enable runtime PM on platfroms that provide acceptable
> latency on transition from D3 to D0.
> 
> Details:
> - When both power saving and runtime PM are enabled:
>   -- If a codec supports 'stop-clock' in D3, it will request suspending the
>      controller after it enters D3 and request resuming the controller before
>      back to D0. Thus the controller will be suspended only when all codecs
> are
>      suspended and support stop-clock in D3.
>   -- User IO operations and HW wakeup signal can resume the controller
> back to
>      D0.
> - If runtime PM is disabled, power saving just works as before.
> - If power saving is disabled, the controller won't be suspended because the
>   power usage counter can never be 0.
> 
> More about 'stop-clock' feature:
> If a codec can support targeted pass-through operations in D3 state when
> there is no BCLK present on the link, it will set CLKSTOP flag in the supported
> power states and report PS-ClkStopOk when entering D3 state. Please refer
> to HDA spec section 7.3.3.10 Power state and 7.3.4.12 Supported Power
> State.
> 
> Signed-off-by: Mengdong Lin <mengdong.lin at intel.com>
> ---
>  sound/pci/hda/hda_codec.c |   20 ++++++++++--
>  sound/pci/hda/hda_codec.h |    5 ++-
>  sound/pci/hda/hda_intel.c |   75
> +++++++++++++++++++++++++++++++++++----------
>  3 files changed, 81 insertions(+), 19 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index
> f2f5347..72e6328 100644
> --- a/sound/pci/hda/hda_codec.c
> +++ b/sound/pci/hda/hda_codec.c
> @@ -26,6 +26,7 @@
>  #include <linux/pci.h>
>  #include <linux/mutex.h>
>  #include <linux/module.h>
> +#include <linux/pm_runtime.h>
>  #include <sound/core.h>
>  #include "hda_codec.h"
>  #include <sound/asoundef.h>
> @@ -1209,6 +1210,9 @@ static void snd_hda_codec_free(struct hda_codec
> *codec)
>  	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);
> 
> @@ -1317,6 +1321,12 @@ int /*__devinit*/ snd_hda_codec_new(struct
> hda_bus *bus,
>  					   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, @@ -3514,6
> +3524,8 @@ static void hda_set_power_state(struct hda_codec *codec,
> hda_nid_t fg,
>  	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;
> @@ -3536,6 +3548,10 @@ static void hda_set_power_state(struct
> hda_codec *codec, hda_nid_t fg,
>  		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
> @@ -4385,7 +4401,7 @@ static void hda_power_work(struct work_struct
> *work)
> 
>  	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) @@ -4440,7
> +4456,7 @@ static void __snd_hda_power_up(struct hda_codec *codec,
> bool wait_power_down)
>  	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);
> diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index
> fe6206b..b384c721 100644
> --- a/sound/pci/hda/hda_codec.h
> +++ b/sound/pci/hda/hda_codec.h
> @@ -612,7 +612,7 @@ struct hda_bus_ops {
>  	void (*bus_reset)(struct hda_bus *bus);  #ifdef
> CONFIG_SND_HDA_POWER_SAVE
>  	/* notify power-up/down from codec to controller */
> -	void (*pm_notify)(struct hda_bus *bus);
> +	void (*pm_notify)(struct hda_bus *bus, struct hda_codec *codec);
>  #endif
>  };
> 
> @@ -870,6 +870,9 @@ struct hda_codec {
>  	unsigned long power_off_acct;
>  	unsigned long power_jiffies;
>  	spinlock_t power_lock;
> +
> +	unsigned int d3_stop_clk:1;	/* support D3 operation without BCLK */
> +	unsigned int d3_stop_clk_ok:1; /* BCLK can stop */
>  #endif
> 
>  	/* codec-specific additional proc output */ diff --git
> a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index
> b6a4ea7..2baa3ee 100644
> --- a/sound/pci/hda/hda_intel.c
> +++ b/sound/pci/hda/hda_intel.c
> @@ -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>
> @@ -1022,7 +1023,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 */
> @@ -1278,6 +1279,9 @@ static irqreturn_t azx_interrupt(int irq, void
> *dev_id)
>  	u8 sd_status;
>  	int i, ok;
> 
> +	if (chip->pci->dev.power.runtime_status != RPM_ACTIVE)
> +		return IRQ_NONE;
> +
>  	spin_lock(&chip->reg_lock);
> 
>  	if (chip->disabled) {
> @@ -2390,23 +2394,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);
> @@ -2511,7 +2509,35 @@ 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);
> +
> +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;
> +}
> +
> +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_suspend(dev)
> @@ -3201,6 +3227,15 @@ static void power_down_all_codecs(struct azx
> *chip)  #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)  { @@ -3238,6
> +3273,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;
> 
> @@ -3289,6 +3327,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);
> @@ -3303,6 +3342,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);
> --
> 1.7.9.5



More information about the Alsa-devel mailing list