summaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_intel.c
diff options
context:
space:
mode:
authorWang Xingchao <xingchao.wang@linux.intel.com>2013-05-30 22:07:10 +0800
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-06-06 17:31:56 +0200
commit99a2008d0b32d72dfc2a54e7be1eb698dd2e3bd6 (patch)
treea4966a9b0e4c03e329875c17e998959e8a394e1b /sound/pci/hda/hda_intel.c
parent5c90680e42b08a1e4a6800ca02e75ad201f8037f (diff)
ALSA: hda - Add power-welll support for haswell HDA
For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving. There's deadlock when request_module(i915) in azx_probe. It looks like: device_lock(audio pci device) -> azx_probe -> module_request (or symbol_request) -> modprobe (userspace) -> i915 init -> drm_pci_init -> pci_register_driver -> bus_add_driver -> driver_attach -> which in turn tries all locks on pci bus, and when it tries the one on the audio device, it will deadlock. This patch introduce a work to store remaining probe stuff, and let request_module run in safe work context. Signed-off-by: Wang Xingchao <xingchao.wang@linux.intel.com> Reviewed-by: Takashi Iwai <tiwai@suse.de> Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com> Reviewed-by: David Henningsson <david.henningsson@canonical.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r--sound/pci/hda/hda_intel.c60
1 files changed, 57 insertions, 3 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3e3126ba8276..35e9f8b010a7 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -62,6 +62,7 @@
#include <linux/vga_switcheroo.h>
#include <linux/firmware.h>
#include "hda_codec.h"
+#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -541,6 +542,10 @@ struct azx {
/* for pending irqs */
struct work_struct irq_pending_work;
+#ifdef CONFIG_SND_HDA_I915
+ struct work_struct probe_work;
+#endif
+
/* reboot notifier (for mysterious hangup problem at power-down) */
struct notifier_block reboot_notifier;
@@ -594,6 +599,7 @@ enum {
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
+#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well support */
/* quirks for Intel PCH */
#define AZX_DCAPS_INTEL_PCH_NOPM \
@@ -2900,6 +2906,8 @@ static int azx_suspend(struct device *dev)
pci_disable_device(pci);
pci_save_state(pci);
pci_set_power_state(pci, PCI_D3hot);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(false);
return 0;
}
@@ -2912,6 +2920,8 @@ static int azx_resume(struct device *dev)
if (chip->disabled)
return 0;
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(true);
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
@@ -2944,6 +2954,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip);
azx_clear_irq_pending(chip);
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(false);
return 0;
}
@@ -2952,6 +2964,8 @@ static int azx_runtime_resume(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ hda_display_power(true);
azx_init_pci(chip);
azx_init_chip(chip, 1);
return 0;
@@ -3176,6 +3190,10 @@ static int azx_free(struct azx *chip)
if (chip->fw)
release_firmware(chip->fw);
#endif
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ hda_display_power(false);
+ hda_i915_exit();
+ }
kfree(chip);
return 0;
@@ -3401,6 +3419,13 @@ static void azx_check_snoop_available(struct azx *chip)
}
}
+#ifdef CONFIG_SND_HDA_I915
+static void azx_probe_work(struct work_struct *work)
+{
+ azx_probe_continue(container_of(work, struct azx, probe_work));
+}
+#endif
+
/*
* constructor
*/
@@ -3476,7 +3501,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
return err;
}
+#ifdef CONFIG_SND_HDA_I915
+ /* continue probing in work context as may trigger request module */
+ INIT_WORK(&chip->probe_work, azx_probe_work);
+#endif
+
*rchip = chip;
+
return 0;
}
@@ -3747,6 +3778,16 @@ static int azx_probe(struct pci_dev *pci,
}
#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+ /* continue probing in work context, avoid request_module deadlock */
+ if (probe_now && (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)) {
+#ifdef CONFIG_SND_HDA_I915
+ probe_now = false;
+ schedule_work(&chip->probe_work);
+#else
+ snd_printk(KERN_ERR SFX "Haswell must build in CONFIG_SND_HDA_I915\n");
+#endif
+ }
+
if (probe_now) {
err = azx_probe_continue(chip);
if (err < 0)
@@ -3769,6 +3810,16 @@ static int azx_probe_continue(struct azx *chip)
int dev = chip->dev_index;
int err;
+ /* Request power well for Haswell HDA controller and codec */
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ err = hda_i915_init();
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
+ goto out_free;
+ }
+ hda_display_power(true);
+ }
+
err = azx_first_init(chip);
if (err < 0)
goto out_free;
@@ -3863,11 +3914,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+ AZX_DCAPS_I915_POWERWELL },
{ PCI_DEVICE(0x8086, 0x0c0c),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+ AZX_DCAPS_I915_POWERWELL },
{ PCI_DEVICE(0x8086, 0x0d0c),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+ AZX_DCAPS_I915_POWERWELL },
/* 5 Series/3400 */
{ PCI_DEVICE(0x8086, 0x3b56),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },