[alsa-devel] [RFC PATCH 1/1] ALSA: hda - add a timer to find and suspend idle codecs

Takashi Iwai tiwai at suse.de
Tue Aug 14 12:26:49 CEST 2012


At Tue, 14 Aug 2012 13:56:43 -0400,
mengdong.lin at intel.com wrote:
> 
> From: Mengdong Lin <mengdong.lin at intel.com>
> 
> (sorry, this patch is resent with title updated)
> 
> 
> This patch sets up a timer to monitor changes of the parameter 'power_save'
> and suspend any idle codecs if power save is re-enabled by the user.
> 
> A codec won't be suspended if it becomes idle when power save is disabled. And it
> will remain unsuspended if power save is enabled later but no further user
> operations comes. So a timer is used here to find and suspend such idle codecs
> when power save is re-enabled.
> 
> Now the timer has a fixed interval of 1 second.
> 
> Signed-off-by: Mengdong Lin <mengdong.lin at intel.com>

Is watching the codec power up/down the only purpose?
If so, the time for codec power on/off can be already checked in
/sys/class/sound/hwC*D*/power_{on|off}_acct files.
These are used by powertop, for example.


Takashi


> ---
>  sound/pci/hda/hda_codec.c |  109 +++++++++++++++++++++++++++++++++++++++++++++
>  sound/pci/hda/hda_codec.h |    9 ++++
>  sound/pci/hda/hda_intel.c |    2 +-
>  3 files changed, 119 insertions(+), 1 deletion(-)
> 
> diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
> index 629131a..6bd8873 100644
> --- a/sound/pci/hda/hda_codec.c
> +++ b/sound/pci/hda/hda_codec.c
> @@ -98,6 +98,8 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
>  static void hda_power_work(struct work_struct *work);
>  static void hda_keep_power_on(struct hda_codec *codec);
>  #define hda_codec_is_power_on(codec)	((codec)->power_on)
> +#define POWER_SAVE_MONITOR_INTERVAL 1000
> +static void power_save_monitor_timeout(unsigned long data);
>  #else
>  static inline void hda_keep_power_on(struct hda_codec *codec) {}
>  #define hda_codec_is_power_on(codec)	1
> @@ -685,6 +687,9 @@ static int snd_hda_bus_free(struct hda_bus *bus)
>  
>  	if (!bus)
>  		return 0;
> +
> +	snd_hda_power_save_monitor_stop(bus);
> +
>  	if (bus->workq)
>  		flush_workqueue(bus->workq);
>  	if (bus->unsol)
> @@ -4558,6 +4563,106 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
>  	return 0;
>  }
>  EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
> +
> +#define BUS_POWER_SAVE(bus) \
> +	((bus)->power_save && *(bus)->power_save ?  1 : 0)
> +
> +static void power_save_monitor_timeout(unsigned long data)
> +{
> +	int ret;
> +	struct hda_bus *bus = (struct hda_bus *)data;
> +	struct list_head *codec_list = &bus->codec_list;
> +	struct hda_codec *codec, *next;
> +
> +	if (BUS_POWER_SAVE(bus) == bus->power_save_enabled)
> +		goto out;
> +
> +	bus->power_save_enabled = BUS_POWER_SAVE(bus);
> +	if (!bus->power_save_enabled) {
> +		snd_printd("hda-bus: power save disabled\n");
> +		goto out;
> +	}
> +
> +	snd_printd("hda-bus: power save enabled\n");
> +	list_for_each_entry_safe(codec, next, codec_list, list) {
> +		spin_lock(&codec->power_lock);
> +		if (codec->power_on
> +			&& !codec->power_count
> +			&& !codec->power_transition) {
> +			snd_printd("hda codec %d idle\n", codec->addr);
> +			codec->power_transition = -1; /* avoid reentrance */
> +			queue_delayed_work(codec->bus->workq,
> +				&codec->power_work,
> +				msecs_to_jiffies(power_save(codec) * 1000));
> +		}
> +		spin_unlock(&codec->power_lock);
> +	}
> +
> +out:
> +	ret = mod_timer(&bus->power_save_timer,
> +				jiffies + \
> +				msecs_to_jiffies(POWER_SAVE_MONITOR_INTERVAL));
> +	if (ret)
> +		snd_printd("hda-bus: error in mod_timer\n");
> +
> +	return;
> +}
> +
> +/**
> + * snd_hda_power_save_monitor_start - Setup a timer to monitor the changes of
> + *    power_save parameter.
> + * @bus: HD-audio bus
> + *
> + * Setup a timer to monitor whether parameter power_save is changed by the user.
> + * A codec won't be suspended if it becomes idle when power save is disabled,
> + * and can remain unsuspended after power save is enabled later.
> + * This monitor will find and suspened such idle codecs when power save is
> + * re-enabled.
> + */
> +void  snd_hda_power_save_monitor_start(struct hda_bus *bus)
> +{
> +	int ret;
> +
> +	if (bus->power_save_timer_inited) {
> +		snd_printd("hda-bus: power save timer already inited\n");
> +		return;
> +	}
> +
> +	setup_timer(&bus->power_save_timer,
> +				power_save_monitor_timeout, (unsigned long)bus);
> +	bus->power_save_timer_inited = 1;
> +
> +	snd_printd("hda-bus: start power save monitor\n");
> +	ret = mod_timer(&bus->power_save_timer,
> +			jiffies + \
> +			msecs_to_jiffies(POWER_SAVE_MONITOR_INTERVAL));
> +	if (ret)
> +		snd_printd("hda-bus: error in mod_timer\n");
> +
> +	return;
> +}
> +EXPORT_SYMBOL_HDA(snd_hda_power_save_monitor_start);
> +
> +/**
> + * snd_hda_power_save_monitor_stop - Delete the timer to monitor the changes of
> + *    power_save parameter.
> + * @bus: HD-audio bus
> + */
> +void snd_hda_power_save_monitor_stop(struct hda_bus *bus)
> +{
> +	int ret;
> +
> +	if (bus->power_save_timer_inited) {
> +		snd_printd("hda-bus: stop power save monitor\n");
> +		ret = del_timer_sync(&bus->power_save_timer);
> +		if (ret < 0)
> +			snd_printd("hda-bus: error in del_timer_sync\n");
> +		bus->power_save_timer_inited = 0;
> +	}
> +
> +	return;
> +}
> +EXPORT_SYMBOL_HDA(snd_hda_power_save_monitor_stop);
>  #endif
>  
>  /*
> @@ -5045,6 +5150,8 @@ int snd_hda_suspend(struct hda_bus *bus)
>  {
>  	struct hda_codec *codec;
>  
> +	snd_hda_power_save_monitor_stop(bus);
> +
>  	list_for_each_entry(codec, &bus->codec_list, list) {
>  		if (hda_codec_is_power_on(codec))
>  			hda_call_codec_suspend(codec);
> @@ -5069,6 +5176,8 @@ int snd_hda_resume(struct hda_bus *bus)
>  	list_for_each_entry(codec, &bus->codec_list, list) {
>  		hda_call_codec_resume(codec);
>  	}
> +
> +	snd_hda_power_save_monitor_start(bus);
>  	return 0;
>  }
>  EXPORT_SYMBOL_HDA(snd_hda_resume);
> diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
> index c422d33..8b88ec5 100644
> --- a/sound/pci/hda/hda_codec.h
> +++ b/sound/pci/hda/hda_codec.h
> @@ -653,6 +653,8 @@ struct hda_bus {
>  	struct hda_bus_unsolicited *unsol;
>  	char workq_name[16];
>  	struct workqueue_struct *workq;	/* common workqueue for codecs */
> +	/* timer to monitor changes of power_save parameter */
> +	struct timer_list power_save_timer;
>  
>  	/* assigned PCMs */
>  	DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
> @@ -667,6 +669,9 @@ struct hda_bus {
>  	unsigned int response_reset:1;	/* controller was reset */
>  	unsigned int in_reset:1;	/* during reset operation */
>  	unsigned int power_keep_link_on:1; /* don't power off HDA link */
> +	/* power save flags */
> +	unsigned int power_save_enabled:1;	/* enabled at last check */
> +	unsigned int power_save_timer_inited:1;	/* timer is initialized */
>  };
>  
>  /*
> @@ -1062,10 +1067,14 @@ void snd_hda_power_up(struct hda_codec *codec);
>  void snd_hda_power_up_d3wait(struct hda_codec *codec);
>  void snd_hda_power_down(struct hda_codec *codec);
>  void snd_hda_update_power_acct(struct hda_codec *codec);
> +extern void snd_hda_power_save_monitor_start(struct hda_bus *bus);
> +extern void snd_hda_power_save_monitor_stop(struct hda_bus *bus);
>  #else
>  static inline void snd_hda_power_up(struct hda_codec *codec) {}
>  static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) {}
>  static inline void snd_hda_power_down(struct hda_codec *codec) {}
> +#define snd_hda_power_save_monitor_start(bus) {}
> +#define snd_hda_power_save_monitor_stop(bus) {}
>  #endif
>  
>  #ifdef CONFIG_SND_HDA_PATCH_LOADER
> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
> index c8aced1..fe015d0 100644
> --- a/sound/pci/hda/hda_intel.c
> +++ b/sound/pci/hda/hda_intel.c
> @@ -3236,7 +3236,7 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip)
>  	chip->running = 1;
>  	power_down_all_codecs(chip);
>  	azx_notifier_register(chip);
> -
> +	snd_hda_power_save_monitor_start(chip->bus);
>  	return 0;
>  
>  out_free:
> -- 
> 1.7.9.5
> 


More information about the Alsa-devel mailing list