#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)
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;
}
.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;
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);
kfree(codec->chip_name);
kfree(codec->modelname);
kfree(codec->wcaps);
+ codec->bus->num_codecs--;
kfree(codec);
}
#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.
}
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,
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
/*
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);
/* codec linked list */
struct list_head codec_list;
+ unsigned int num_codecs;
/* link caddr -> codec */
struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
struct hda_bus_unsolicited *unsol;
char workq_name[16];
struct workqueue_struct *workq; /* common workqueue for codecs */
+#ifdef CONFIG_PM
+ struct workqueue_struct *pm_wq; /* workqueue to parallel codec PM */
+#endif
/* assigned PCMs */
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
unsigned long power_off_acct;
unsigned long power_jiffies;
spinlock_t power_lock;
+ struct work_struct pm_work; /* task to parallel multi-codec PM */
#endif
/* filter the requested power state per nid */