--- /dev/null
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - Use own workqueue
+Patch-mainline: 2.6.29-rc2
+References: bnc#502733
+
+snd-hda-intel driver used schedule_work() fot the delayed DMA pointer
+updates, but this has several potential problems:
+- it may block other eventsd works longer
+- it may deadlock when probing fails and flush_scheduled_work() is
+ called during probe callback (as probe callback itself could be
+ invoked from eventd)
+
+This patch adds an own workq for each driver instance to solve these
+problems.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+ sound/pci/hda/hda_beep.c | 1 -
+ sound/pci/hda/hda_codec.c | 24 ++++++++++++++++++------
+ sound/pci/hda/hda_codec.h | 2 ++
+ sound/pci/hda/hda_intel.c | 6 +++---
+ 4 files changed, 23 insertions(+), 10 deletions(-)
+
+--- a/sound/pci/hda/hda_beep.c
++++ b/sound/pci/hda/hda_beep.c
+@@ -134,7 +134,6 @@
+ struct hda_beep *beep = codec->beep;
+ if (beep) {
+ cancel_work_sync(&beep->beep_work);
+- flush_scheduled_work();
+
+ input_unregister_device(beep->dev);
+ kfree(beep);
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -310,7 +310,7 @@
+ unsol->queue[wp] = res;
+ unsol->queue[wp + 1] = res_ex;
+
+- schedule_work(&unsol->work);
++ queue_work(bus->workq, &unsol->work);
+
+ return 0;
+ }
+@@ -373,15 +373,17 @@
+
+ if (!bus)
+ return 0;
+- if (bus->unsol) {
+- flush_scheduled_work();
++ if (bus->workq)
++ flush_workqueue(bus->workq);
++ if (bus->unsol)
+ kfree(bus->unsol);
+- }
+ list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
+ snd_hda_codec_free(codec);
+ }
+ if (bus->ops.private_free)
+ bus->ops.private_free(bus);
++ if (bus->workq)
++ destroy_workqueue(bus->workq);
+ kfree(bus);
+ return 0;
+ }
+@@ -431,6 +433,16 @@
+ mutex_init(&bus->cmd_mutex);
+ INIT_LIST_HEAD(&bus->codec_list);
+
++ snprintf(bus->workq_name, sizeof(bus->workq_name),
++ "hd-audio%d", card->number);
++ bus->workq = create_singlethread_workqueue(bus->workq_name);
++ if (!bus->workq) {
++ snd_printk(KERN_ERR "cannot create workqueue %s\n",
++ bus->workq_name);
++ kfree(bus);
++ return -ENOMEM;
++ }
++
+ err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
+ if (err < 0) {
+ snd_hda_bus_free(bus);
+@@ -564,7 +576,7 @@
+ return;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ cancel_delayed_work(&codec->power_work);
+- flush_scheduled_work();
++ flush_workqueue(codec->bus->workq);
+ #endif
+ list_del(&codec->list);
+ codec->bus->caddr_tbl[codec->addr] = NULL;
+@@ -2533,7 +2545,7 @@
+ return;
+ if (power_save) {
+ codec->power_transition = 1; /* avoid reentrance */
+- schedule_delayed_work(&codec->power_work,
++ queue_delayed_work(codec->bus->workq, &codec->power_work,
+ msecs_to_jiffies(power_save * 1000));
+ }
+ }
+--- a/sound/pci/hda/hda_codec.h
++++ b/sound/pci/hda/hda_codec.h
+@@ -580,6 +580,8 @@
+
+ /* unsolicited event queue */
+ struct hda_bus_unsolicited *unsol;
++ char workq_name[16];
++ struct workqueue_struct *workq; /* common workqueue for codecs */
+
+ struct snd_info_entry *proc;
+
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -986,10 +986,11 @@
+ spin_unlock(&chip->reg_lock);
+ snd_pcm_period_elapsed(azx_dev->substream);
+ spin_lock(&chip->reg_lock);
+- } else {
++ } else if (chip->bus && chip->bus->workq) {
+ /* bogus IRQ, process it later */
+ azx_dev->irq_pending = 1;
+- schedule_work(&chip->irq_pending_work);
++ queue_work(chip->bus->workq,
++ &chip->irq_pending_work);
+ }
+ }
+ }
+@@ -1708,7 +1709,6 @@
+ for (i = 0; i < chip->num_streams; i++)
+ chip->azx_dev[i].irq_pending = 0;
+ spin_unlock_irq(&chip->reg_lock);
+- flush_scheduled_work();
+ }
+
+ static struct snd_pcm_ops azx_pcm_ops = {