summaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorMengdong Lin <mengdong.lin@intel.com>2013-11-26 23:00:51 -0500
committerTakashi Iwai <tiwai@suse.de>2013-11-27 12:16:32 +0100
commit0e24dbb7f44877a3565ee461d2209b0942b1a753 (patch)
tree1fdef5cb4b6247a677311754fd868ae7ee01e200 /sound/pci/hda/hda_codec.c
parent3e9bc58fef3b7a1ee95b36c26671198a985293c7 (diff)
ALSA: hda - suspend codecs in parallel
The time to suspend a single codec may be several hundreds of ms. When runtime power saving is disabled, driver suspend time can be long especially when there are more than one codec on the bus. To reduce driver suspend time, this patch creates a work queue for the bus, and suspends the codecs in parallel if there are multiple codecs on the bus. [fixed cosmetic issues by tiwai] Signed-off-by: Mengdong Lin <mengdong.lin@intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c47
1 files changed, 45 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 6deb8d1852cc..85886220ee74 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -96,6 +96,7 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
#ifdef CONFIG_PM
#define codec_in_pm(codec) ((codec)->in_pm)
+static void hda_pm_work(struct work_struct *work);
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)
@@ -839,6 +840,12 @@ static int snd_hda_bus_free(struct hda_bus *bus)
bus->ops.private_free(bus);
if (bus->workq)
destroy_workqueue(bus->workq);
+
+#ifdef CONFIG_PM
+ if (bus->pm_wq)
+ destroy_workqueue(bus->pm_wq);
+#endif
+
kfree(bus);
return 0;
}
@@ -883,6 +890,9 @@ int snd_hda_bus_new(struct snd_card *card,
.dev_register = snd_hda_bus_dev_register,
.dev_free = snd_hda_bus_dev_free,
};
+#ifdef CONFIG_PM
+ char wqname[16];
+#endif
if (snd_BUG_ON(!temp))
return -EINVAL;
@@ -919,6 +929,16 @@ int snd_hda_bus_new(struct snd_card *card,
return -ENOMEM;
}
+#ifdef CONFIG_PM
+ sprintf(wqname, "hda-pm-wq-%d", card->number);
+ bus->pm_wq = create_workqueue(wqname);
+ if (!bus->pm_wq) {
+ snd_printk(KERN_ERR "cannot create PM workqueue\n");
+ snd_hda_bus_free(bus);
+ return -ENOMEM;
+ }
+#endif
+
err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
if (err < 0) {
snd_hda_bus_free(bus);
@@ -1388,6 +1408,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
kfree(codec->chip_name);
kfree(codec->modelname);
kfree(codec->wcaps);
+ codec->bus->num_codecs--;
kfree(codec);
}
@@ -1453,6 +1474,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
#ifdef CONFIG_PM
spin_lock_init(&codec->power_lock);
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
+ INIT_WORK(&codec->pm_work, hda_pm_work);
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
* the caller has to power down appropriatley after initialization
* phase.
@@ -1469,6 +1491,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
}
list_add_tail(&codec->list, &bus->codec_list);
+ bus->num_codecs++;
+#ifdef CONFIG_PM
+ workqueue_set_max_active(bus->pm_wq, bus->num_codecs);
+#endif
+
bus->caddr_tbl[codec_addr] = codec;
codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
@@ -5088,6 +5115,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
/*
@@ -5663,9 +5698,17 @@ int snd_hda_suspend(struct hda_bus *bus)
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 (bus->num_codecs > 1)
+ queue_work(bus->pm_wq, &codec->pm_work);
+ else
+ hda_call_codec_suspend(codec, false);
+ }
}
+
+ if (bus->num_codecs > 1)
+ flush_workqueue(bus->pm_wq);
+
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_suspend);