[alsa-devel] [PATCH] ALSA: hda - do delayed suspend for HD-audio devices

mengdong.lin at intel.com mengdong.lin at intel.com
Thu Dec 5 02:04:25 CET 2013


From: Mengdong Lin <mengdong.lin at intel.com>

HD-Audio codecs tends to take a long time to supend and is time-consuming part
in driver suspend. In this patch, codec suspending and the left operations are
delayed to a worker thead, so that they can be done asynchronously in parallel
with suspending of other devices. And azx_suspend_late() is defined as PM
.suspend_late ops to wait for completion of the delayed suspending.

Credit goes to Liam Girdwood, who suggested this workaround.

Signed-off-by: Mengdong Lin <mengdong.lin at intel.com>

diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 80c5f14..aed4cdf 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -545,6 +545,11 @@ struct azx {
 
 	struct work_struct probe_work;
 
+#ifdef CONFIG_PM
+	struct workqueue_struct *pm_wq; /* workqueue to delay controller PM */
+	struct work_struct suspend_work; /* task to delay controller suspend */
+#endif
+
 	/* reboot notifier (for mysterious hangup problem at power-down) */
 	struct notifier_block reboot_notifier;
 
@@ -2908,20 +2913,11 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
 /*
  * power management
  */
-static int azx_suspend(struct device *dev)
-{
-	struct pci_dev *pci = to_pci_dev(dev);
-	struct snd_card *card = dev_get_drvdata(dev);
-	struct azx *chip = card->private_data;
-	struct azx_pcm *p;
 
-	if (chip->disabled)
-		return 0;
+static int azx_suspend_continue(struct azx *chip)
+{
+	struct pci_dev *pci = chip->pci;
 
-	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	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);
@@ -2937,6 +2933,41 @@ static int azx_suspend(struct device *dev)
 	pci_set_power_state(pci, PCI_D3hot);
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
 		hda_display_power(false);
+
+	return 0;
+}
+
+static void azx_suspend_work(struct work_struct *work)
+{
+	azx_suspend_continue(container_of(work, struct azx, suspend_work));
+}
+
+
+static int azx_suspend(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+	struct azx_pcm *p;
+
+	if (chip->disabled)
+		return 0;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	azx_clear_irq_pending(chip);
+	list_for_each_entry(p, &chip->pcm_list, list)
+		snd_pcm_suspend_all(p->pcm);
+
+	queue_work(chip->pm_wq, &chip->suspend_work);
+	return 0;
+}
+
+static int azx_suspend_late(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+
+	flush_workqueue(chip->pm_wq);
+
 	return 0;
 }
 
@@ -3057,6 +3088,7 @@ static int azx_runtime_idle(struct device *dev)
 #ifdef CONFIG_PM
 static const struct dev_pm_ops azx_pm = {
 	SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+	.suspend_late = azx_suspend_late,
 	SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
 };
 
@@ -3272,6 +3304,12 @@ static int azx_free(struct azx *chip)
 		hda_display_power(false);
 		hda_i915_exit();
 	}
+
+#ifdef CONFIG_PM
+	if (chip->pm_wq)
+		destroy_workqueue(chip->pm_wq);
+#endif
+
 	kfree(chip);
 
 	return 0;
@@ -3515,6 +3553,9 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 	};
 	struct azx *chip;
 	int err;
+#ifdef CONFIG_PM
+	char wqname[24];
+#endif
 
 	*rchip = NULL;
 
@@ -3581,6 +3622,18 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 	/* continue probing in work context as may trigger request module */
 	INIT_WORK(&chip->probe_work, azx_probe_work);
 
+#ifdef CONFIG_PM
+	sprintf(wqname, "hda-azx-pm-wq%d", card->number);
+	chip->pm_wq = create_workqueue("wqname");
+	if (!chip->pm_wq) {
+		snd_printk(KERN_ERR "cannot create PM workqueue\n");
+		azx_free(chip);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&chip->suspend_work, azx_suspend_work);
+#endif
+
 	*rchip = chip;
 
 	return 0;
-- 
1.8.1.2



More information about the Alsa-devel mailing list