[alsa-devel] [RFC PATCH] ALSA: hda - suspend codecs in parallel

Takashi Iwai tiwai at suse.de
Mon Nov 25 11:51:16 CET 2013


At Sun, 24 Nov 2013 22:33:41 -0500,
mengdong.lin at intel.com wrote:
> 
> From: Mengdong Lin <mengdong.lin at intel.com>
> 
> The time to suspend a single codec may be several hundreds of ms. When runtime
> power saving is not enabled, driver suspend time can be long especially there
> are more than one codec on the bus.
> 
> To reduce driver suspend time, this patch creates a work queue for each codec
> and will suspend the codecs in parallel, if there are multiple codecs on bus.
> 
> Signed-off-by: Mengdong Lin <mengdong.lin at intel.com>
> 
> diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
> index cb0c776..c9092a1 100644
> --- a/sound/pci/hda/hda_codec.c
> +++ b/sound/pci/hda/hda_codec.c
> @@ -97,6 +97,7 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
>  #ifdef CONFIG_PM
>  #define codec_in_pm(codec)	((codec)->in_pm)
>  static void hda_power_work(struct work_struct *work);
> +static void hda_pm_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)
>  static inline void hda_call_pm_notify(struct hda_bus *bus, bool power_up)
> @@ -1355,6 +1356,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
>  #ifdef CONFIG_PM
>  	if (!codec->pm_down_notified) /* cancel leftover refcounts */
>  		hda_call_pm_notify(codec->bus, false);
> +	if (codec->pm_wq)
> +		destroy_workqueue(codec->pm_wq);
>  #endif
>  	module_put(codec->owner);
>  	free_hda_cache(&codec->amp_cache);
> @@ -1434,6 +1437,13 @@ int snd_hda_codec_new(struct hda_bus *bus,
>  	 */
>  	hda_keep_power_on(codec);
>  	hda_call_pm_notify(bus, true);
> +
> +	codec->pm_wq = create_workqueue("hda_codec_wq");
> +	if (!codec->pm_wq) {
> +		snd_hda_codec_free(codec);
> +		return -ENOMEM;
> +	}
> +	INIT_WORK(&codec->pm_work, hda_pm_work);
>  #endif
>  
>  	if (codec->bus->modelname) {
> @@ -1445,6 +1455,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
>  	}
>  
>  	list_add_tail(&codec->list, &bus->codec_list);
> +	bus->num_codecs++;
>  	bus->caddr_tbl[codec_addr] = codec;
>  
>  	codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
> @@ -5032,6 +5043,14 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
>  	return 0;
>  }
>  EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
> +
> +static void hda_pm_work(struct work_struct *work)
> +{
> +	struct hda_codec *codec =
> +		container_of(work, struct hda_codec, pm_work);
> +
> +	hda_call_codec_suspend(codec, false);
> +}
>  #endif
>  
>  /*
> @@ -5595,11 +5614,24 @@ EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
>  int snd_hda_suspend(struct hda_bus *bus)
>  {
>  	struct hda_codec *codec;
> +	unsigned int mask = 0;
>  
>  	list_for_each_entry(codec, &bus->codec_list, list) {
>  		cancel_delayed_work_sync(&codec->jackpoll_work);
> -		if (hda_codec_is_power_on(codec))
> -			hda_call_codec_suspend(codec, false);
> +		if (hda_codec_is_power_on(codec)) {
> +			if (codec->epss || bus->num_codecs == 1)
> +				hda_call_codec_suspend(codec, false);

What's the reason for codec->epss check exceptionally?

> +			else {
> +				mask |= 1 << codec->addr;
> +				queue_work(codec->pm_wq, &codec->pm_work);
> +			}
> +		}
> +	}
> +
> +	list_for_each_entry(codec, &bus->codec_list, list) {
> +		if (mask & (1 << codec->addr))
> +			flush_work(&codec->pm_work);
> +
>  	}

I thought that the recent workqueue is performed asynchronously as
default (unless an ordered workqueue is used), so it should be enough
to have a single workqueue in the bus, and flush it.


thanks,

Takashi


More information about the Alsa-devel mailing list