aboutsummaryrefslogtreecommitdiffstats
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 06:16:32 -0500
commit0e24dbb7f44877a3565ee461d2209b0942b1a753 (patch)
tree1fdef5cb4b6247a677311754fd868ae7ee01e200
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>
-rw-r--r--sound/pci/hda/hda_codec.c47
-rw-r--r--sound/pci/hda/hda_codec.h5
2 files changed, 50 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);
96 96
97#ifdef CONFIG_PM 97#ifdef CONFIG_PM
98#define codec_in_pm(codec) ((codec)->in_pm) 98#define codec_in_pm(codec) ((codec)->in_pm)
99static void hda_pm_work(struct work_struct *work);
99static void hda_power_work(struct work_struct *work); 100static void hda_power_work(struct work_struct *work);
100static void hda_keep_power_on(struct hda_codec *codec); 101static void hda_keep_power_on(struct hda_codec *codec);
101#define hda_codec_is_power_on(codec) ((codec)->power_on) 102#define hda_codec_is_power_on(codec) ((codec)->power_on)
@@ -839,6 +840,12 @@ static int snd_hda_bus_free(struct hda_bus *bus)
839 bus->ops.private_free(bus); 840 bus->ops.private_free(bus);
840 if (bus->workq) 841 if (bus->workq)
841 destroy_workqueue(bus->workq); 842 destroy_workqueue(bus->workq);
843
844#ifdef CONFIG_PM
845 if (bus->pm_wq)
846 destroy_workqueue(bus->pm_wq);
847#endif
848
842 kfree(bus); 849 kfree(bus);
843 return 0; 850 return 0;
844} 851}
@@ -883,6 +890,9 @@ int snd_hda_bus_new(struct snd_card *card,
883 .dev_register = snd_hda_bus_dev_register, 890 .dev_register = snd_hda_bus_dev_register,
884 .dev_free = snd_hda_bus_dev_free, 891 .dev_free = snd_hda_bus_dev_free,
885 }; 892 };
893#ifdef CONFIG_PM
894 char wqname[16];
895#endif
886 896
887 if (snd_BUG_ON(!temp)) 897 if (snd_BUG_ON(!temp))
888 return -EINVAL; 898 return -EINVAL;
@@ -919,6 +929,16 @@ int snd_hda_bus_new(struct snd_card *card,
919 return -ENOMEM; 929 return -ENOMEM;
920 } 930 }
921 931
932#ifdef CONFIG_PM
933 sprintf(wqname, "hda-pm-wq-%d", card->number);
934 bus->pm_wq = create_workqueue(wqname);
935 if (!bus->pm_wq) {
936 snd_printk(KERN_ERR "cannot create PM workqueue\n");
937 snd_hda_bus_free(bus);
938 return -ENOMEM;
939 }
940#endif
941
922 err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); 942 err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
923 if (err < 0) { 943 if (err < 0) {
924 snd_hda_bus_free(bus); 944 snd_hda_bus_free(bus);
@@ -1388,6 +1408,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
1388 kfree(codec->chip_name); 1408 kfree(codec->chip_name);
1389 kfree(codec->modelname); 1409 kfree(codec->modelname);
1390 kfree(codec->wcaps); 1410 kfree(codec->wcaps);
1411 codec->bus->num_codecs--;
1391 kfree(codec); 1412 kfree(codec);
1392} 1413}
1393 1414
@@ -1453,6 +1474,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
1453#ifdef CONFIG_PM 1474#ifdef CONFIG_PM
1454 spin_lock_init(&codec->power_lock); 1475 spin_lock_init(&codec->power_lock);
1455 INIT_DELAYED_WORK(&codec->power_work, hda_power_work); 1476 INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
1477 INIT_WORK(&codec->pm_work, hda_pm_work);
1456 /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. 1478 /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
1457 * the caller has to power down appropriatley after initialization 1479 * the caller has to power down appropriatley after initialization
1458 * phase. 1480 * phase.
@@ -1469,6 +1491,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
1469 } 1491 }
1470 1492
1471 list_add_tail(&codec->list, &bus->codec_list); 1493 list_add_tail(&codec->list, &bus->codec_list);
1494 bus->num_codecs++;
1495#ifdef CONFIG_PM
1496 workqueue_set_max_active(bus->pm_wq, bus->num_codecs);
1497#endif
1498
1472 bus->caddr_tbl[codec_addr] = codec; 1499 bus->caddr_tbl[codec_addr] = codec;
1473 1500
1474 codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, 1501 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,
5088 return 0; 5115 return 0;
5089} 5116}
5090EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); 5117EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
5118
5119static void hda_pm_work(struct work_struct *work)
5120{
5121 struct hda_codec *codec =
5122 container_of(work, struct hda_codec, pm_work);
5123
5124 hda_call_codec_suspend(codec, false);
5125}
5091#endif 5126#endif
5092 5127
5093/* 5128/*
@@ -5663,9 +5698,17 @@ int snd_hda_suspend(struct hda_bus *bus)
5663 5698
5664 list_for_each_entry(codec, &bus->codec_list, list) { 5699 list_for_each_entry(codec, &bus->codec_list, list) {
5665 cancel_delayed_work_sync(&codec->jackpoll_work); 5700 cancel_delayed_work_sync(&codec->jackpoll_work);
5666 if (hda_codec_is_power_on(codec)) 5701 if (hda_codec_is_power_on(codec)) {
5667 hda_call_codec_suspend(codec, false); 5702 if (bus->num_codecs > 1)
5703 queue_work(bus->pm_wq, &codec->pm_work);
5704 else
5705 hda_call_codec_suspend(codec, false);
5706 }
5668 } 5707 }
5708
5709 if (bus->num_codecs > 1)
5710 flush_workqueue(bus->pm_wq);
5711
5669 return 0; 5712 return 0;
5670} 5713}
5671EXPORT_SYMBOL_HDA(snd_hda_suspend); 5714EXPORT_SYMBOL_HDA(snd_hda_suspend);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index a3e5f45d74c1..9a2798a0313c 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -673,6 +673,7 @@ struct hda_bus {
673 673
674 /* codec linked list */ 674 /* codec linked list */
675 struct list_head codec_list; 675 struct list_head codec_list;
676 unsigned int num_codecs;
676 /* link caddr -> codec */ 677 /* link caddr -> codec */
677 struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; 678 struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
678 679
@@ -683,6 +684,9 @@ struct hda_bus {
683 struct hda_bus_unsolicited *unsol; 684 struct hda_bus_unsolicited *unsol;
684 char workq_name[16]; 685 char workq_name[16];
685 struct workqueue_struct *workq; /* common workqueue for codecs */ 686 struct workqueue_struct *workq; /* common workqueue for codecs */
687#ifdef CONFIG_PM
688 struct workqueue_struct *pm_wq; /* workqueue to parallel codec PM */
689#endif
686 690
687 /* assigned PCMs */ 691 /* assigned PCMs */
688 DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); 692 DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
@@ -917,6 +921,7 @@ struct hda_codec {
917 unsigned long power_off_acct; 921 unsigned long power_off_acct;
918 unsigned long power_jiffies; 922 unsigned long power_jiffies;
919 spinlock_t power_lock; 923 spinlock_t power_lock;
924 struct work_struct pm_work; /* task to parallel multi-codec PM */
920#endif 925#endif
921 926
922 /* filter the requested power state per nid */ 927 /* filter the requested power state per nid */