diff options
| -rw-r--r-- | sound/pci/hda/Kconfig | 10 | ||||
| -rw-r--r-- | sound/pci/hda/Makefile | 2 | ||||
| -rw-r--r-- | sound/pci/hda/hda_i915.c | 75 | ||||
| -rw-r--r-- | sound/pci/hda/hda_i915.h | 35 | ||||
| -rw-r--r-- | sound/pci/hda/hda_intel.c | 60 |
5 files changed, 179 insertions, 3 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44bcf81..c5a872ca7703 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig | |||
| @@ -152,6 +152,16 @@ config SND_HDA_CODEC_HDMI | |||
| 152 | snd-hda-codec-hdmi. | 152 | snd-hda-codec-hdmi. |
| 153 | This module is automatically loaded at probing. | 153 | This module is automatically loaded at probing. |
| 154 | 154 | ||
| 155 | config SND_HDA_I915 | ||
| 156 | bool "Build Display HD-audio controller/codec power well support for i915 cards" | ||
| 157 | depends on DRM_I915 | ||
| 158 | help | ||
| 159 | Say Y here to include full HDMI and DisplayPort HD-audio controller/codec | ||
| 160 | power-well support for Intel Haswell graphics cards based on the i915 driver. | ||
| 161 | |||
| 162 | Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise | ||
| 163 | the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode. | ||
| 164 | |||
| 155 | config SND_HDA_CODEC_CIRRUS | 165 | config SND_HDA_CODEC_CIRRUS |
| 156 | bool "Build Cirrus Logic codec support" | 166 | bool "Build Cirrus Logic codec support" |
| 157 | default y | 167 | default y |
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a251497a1f..c091438286a3 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile | |||
| @@ -1,4 +1,6 @@ | |||
| 1 | snd-hda-intel-objs := hda_intel.o | 1 | snd-hda-intel-objs := hda_intel.o |
| 2 | # for haswell power well | ||
| 3 | snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o | ||
| 2 | 4 | ||
| 3 | snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o | 5 | snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o |
| 4 | snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o | 6 | snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o |
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 000000000000..76c13d5b3ca0 --- /dev/null +++ b/sound/pci/hda/hda_i915.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* | ||
| 2 | * hda_i915.c - routines for Haswell HDA controller power well support | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License as published by the Free | ||
| 6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 7 | * any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| 11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 12 | * for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program; if not, write to the Free Software Foundation, | ||
| 16 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <sound/core.h> | ||
| 22 | #include <drm/i915_powerwell.h> | ||
| 23 | #include "hda_i915.h" | ||
| 24 | |||
| 25 | static void (*get_power)(void); | ||
| 26 | static void (*put_power)(void); | ||
| 27 | |||
| 28 | void hda_display_power(bool enable) | ||
| 29 | { | ||
| 30 | if (!get_power || !put_power) | ||
| 31 | return; | ||
| 32 | |||
| 33 | snd_printdd("HDA display power %s \n", | ||
| 34 | enable ? "Enable" : "Disable"); | ||
| 35 | if (enable) | ||
| 36 | get_power(); | ||
| 37 | else | ||
| 38 | put_power(); | ||
| 39 | } | ||
| 40 | |||
| 41 | int hda_i915_init(void) | ||
| 42 | { | ||
| 43 | int err = 0; | ||
| 44 | |||
| 45 | get_power = symbol_request(i915_request_power_well); | ||
| 46 | if (!get_power) { | ||
| 47 | snd_printk(KERN_WARNING "hda-i915: get_power symbol get fail\n"); | ||
| 48 | return -ENODEV; | ||
| 49 | } | ||
| 50 | |||
| 51 | put_power = symbol_request(i915_release_power_well); | ||
| 52 | if (!put_power) { | ||
| 53 | symbol_put(i915_request_power_well); | ||
| 54 | get_power = NULL; | ||
| 55 | return -ENODEV; | ||
| 56 | } | ||
| 57 | |||
| 58 | snd_printd("HDA driver get symbol successfully from i915 module\n"); | ||
| 59 | |||
| 60 | return err; | ||
| 61 | } | ||
| 62 | |||
| 63 | int hda_i915_exit(void) | ||
| 64 | { | ||
| 65 | if (get_power) { | ||
| 66 | symbol_put(i915_request_power_well); | ||
| 67 | get_power = NULL; | ||
| 68 | } | ||
| 69 | if (put_power) { | ||
| 70 | symbol_put(i915_release_power_well); | ||
| 71 | put_power = NULL; | ||
| 72 | } | ||
| 73 | |||
| 74 | return 0; | ||
| 75 | } | ||
diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 000000000000..5a63da2c53e5 --- /dev/null +++ b/sound/pci/hda/hda_i915.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * This program is free software; you can redistribute it and/or modify it | ||
| 3 | * under the terms of the GNU General Public License as published by the Free | ||
| 4 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 5 | * any later version. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 8 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 9 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 10 | * more details. | ||
| 11 | * | ||
| 12 | * You should have received a copy of the GNU General Public License along with | ||
| 13 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
| 14 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 15 | */ | ||
| 16 | #ifndef __SOUND_HDA_I915_H | ||
| 17 | #define __SOUND_HDA_I915_H | ||
| 18 | |||
| 19 | #ifdef CONFIG_SND_HDA_I915 | ||
| 20 | void hda_display_power(bool enable); | ||
| 21 | int hda_i915_init(void); | ||
| 22 | int hda_i915_exit(void); | ||
| 23 | #else | ||
| 24 | static inline void hda_display_power(bool enable) {} | ||
| 25 | static inline int hda_i915_init(void) | ||
| 26 | { | ||
| 27 | return -ENODEV; | ||
| 28 | } | ||
| 29 | static inline int hda_i915_exit(void) | ||
| 30 | { | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #endif | ||
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 @@ | |||
| 62 | #include <linux/vga_switcheroo.h> | 62 | #include <linux/vga_switcheroo.h> |
| 63 | #include <linux/firmware.h> | 63 | #include <linux/firmware.h> |
| 64 | #include "hda_codec.h" | 64 | #include "hda_codec.h" |
| 65 | #include "hda_i915.h" | ||
| 65 | 66 | ||
| 66 | 67 | ||
| 67 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | 68 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; |
| @@ -541,6 +542,10 @@ struct azx { | |||
| 541 | /* for pending irqs */ | 542 | /* for pending irqs */ |
| 542 | struct work_struct irq_pending_work; | 543 | struct work_struct irq_pending_work; |
| 543 | 544 | ||
| 545 | #ifdef CONFIG_SND_HDA_I915 | ||
| 546 | struct work_struct probe_work; | ||
| 547 | #endif | ||
| 548 | |||
| 544 | /* reboot notifier (for mysterious hangup problem at power-down) */ | 549 | /* reboot notifier (for mysterious hangup problem at power-down) */ |
| 545 | struct notifier_block reboot_notifier; | 550 | struct notifier_block reboot_notifier; |
| 546 | 551 | ||
| @@ -594,6 +599,7 @@ enum { | |||
| 594 | #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ | 599 | #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ |
| 595 | #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ | 600 | #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ |
| 596 | #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ | 601 | #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ |
| 602 | #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well support */ | ||
| 597 | 603 | ||
| 598 | /* quirks for Intel PCH */ | 604 | /* quirks for Intel PCH */ |
| 599 | #define AZX_DCAPS_INTEL_PCH_NOPM \ | 605 | #define AZX_DCAPS_INTEL_PCH_NOPM \ |
| @@ -2900,6 +2906,8 @@ static int azx_suspend(struct device *dev) | |||
| 2900 | pci_disable_device(pci); | 2906 | pci_disable_device(pci); |
| 2901 | pci_save_state(pci); | 2907 | pci_save_state(pci); |
| 2902 | pci_set_power_state(pci, PCI_D3hot); | 2908 | pci_set_power_state(pci, PCI_D3hot); |
| 2909 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) | ||
| 2910 | hda_display_power(false); | ||
| 2903 | return 0; | 2911 | return 0; |
| 2904 | } | 2912 | } |
| 2905 | 2913 | ||
| @@ -2912,6 +2920,8 @@ static int azx_resume(struct device *dev) | |||
| 2912 | if (chip->disabled) | 2920 | if (chip->disabled) |
| 2913 | return 0; | 2921 | return 0; |
| 2914 | 2922 | ||
| 2923 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) | ||
| 2924 | hda_display_power(true); | ||
| 2915 | pci_set_power_state(pci, PCI_D0); | 2925 | pci_set_power_state(pci, PCI_D0); |
| 2916 | pci_restore_state(pci); | 2926 | pci_restore_state(pci); |
| 2917 | if (pci_enable_device(pci) < 0) { | 2927 | if (pci_enable_device(pci) < 0) { |
| @@ -2944,6 +2954,8 @@ static int azx_runtime_suspend(struct device *dev) | |||
| 2944 | 2954 | ||
| 2945 | azx_stop_chip(chip); | 2955 | azx_stop_chip(chip); |
| 2946 | azx_clear_irq_pending(chip); | 2956 | azx_clear_irq_pending(chip); |
| 2957 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) | ||
| 2958 | hda_display_power(false); | ||
| 2947 | return 0; | 2959 | return 0; |
| 2948 | } | 2960 | } |
| 2949 | 2961 | ||
| @@ -2952,6 +2964,8 @@ static int azx_runtime_resume(struct device *dev) | |||
| 2952 | struct snd_card *card = dev_get_drvdata(dev); | 2964 | struct snd_card *card = dev_get_drvdata(dev); |
| 2953 | struct azx *chip = card->private_data; | 2965 | struct azx *chip = card->private_data; |
| 2954 | 2966 | ||
| 2967 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) | ||
| 2968 | hda_display_power(true); | ||
| 2955 | azx_init_pci(chip); | 2969 | azx_init_pci(chip); |
| 2956 | azx_init_chip(chip, 1); | 2970 | azx_init_chip(chip, 1); |
| 2957 | return 0; | 2971 | return 0; |
| @@ -3176,6 +3190,10 @@ static int azx_free(struct azx *chip) | |||
| 3176 | if (chip->fw) | 3190 | if (chip->fw) |
| 3177 | release_firmware(chip->fw); | 3191 | release_firmware(chip->fw); |
| 3178 | #endif | 3192 | #endif |
| 3193 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { | ||
| 3194 | hda_display_power(false); | ||
| 3195 | hda_i915_exit(); | ||
| 3196 | } | ||
| 3179 | kfree(chip); | 3197 | kfree(chip); |
| 3180 | 3198 | ||
| 3181 | return 0; | 3199 | return 0; |
| @@ -3401,6 +3419,13 @@ static void azx_check_snoop_available(struct azx *chip) | |||
| 3401 | } | 3419 | } |
| 3402 | } | 3420 | } |
| 3403 | 3421 | ||
| 3422 | #ifdef CONFIG_SND_HDA_I915 | ||
| 3423 | static void azx_probe_work(struct work_struct *work) | ||
| 3424 | { | ||
| 3425 | azx_probe_continue(container_of(work, struct azx, probe_work)); | ||
| 3426 | } | ||
| 3427 | #endif | ||
| 3428 | |||
| 3404 | /* | 3429 | /* |
| 3405 | * constructor | 3430 | * constructor |
| 3406 | */ | 3431 | */ |
| @@ -3476,7 +3501,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, | |||
| 3476 | return err; | 3501 | return err; |
| 3477 | } | 3502 | } |
| 3478 | 3503 | ||
| 3504 | #ifdef CONFIG_SND_HDA_I915 | ||
| 3505 | /* continue probing in work context as may trigger request module */ | ||
| 3506 | INIT_WORK(&chip->probe_work, azx_probe_work); | ||
| 3507 | #endif | ||
| 3508 | |||
| 3479 | *rchip = chip; | 3509 | *rchip = chip; |
| 3510 | |||
| 3480 | return 0; | 3511 | return 0; |
| 3481 | } | 3512 | } |
| 3482 | 3513 | ||
| @@ -3747,6 +3778,16 @@ static int azx_probe(struct pci_dev *pci, | |||
| 3747 | } | 3778 | } |
| 3748 | #endif /* CONFIG_SND_HDA_PATCH_LOADER */ | 3779 | #endif /* CONFIG_SND_HDA_PATCH_LOADER */ |
| 3749 | 3780 | ||
| 3781 | /* continue probing in work context, avoid request_module deadlock */ | ||
| 3782 | if (probe_now && (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)) { | ||
| 3783 | #ifdef CONFIG_SND_HDA_I915 | ||
| 3784 | probe_now = false; | ||
| 3785 | schedule_work(&chip->probe_work); | ||
| 3786 | #else | ||
| 3787 | snd_printk(KERN_ERR SFX "Haswell must build in CONFIG_SND_HDA_I915\n"); | ||
| 3788 | #endif | ||
| 3789 | } | ||
| 3790 | |||
| 3750 | if (probe_now) { | 3791 | if (probe_now) { |
| 3751 | err = azx_probe_continue(chip); | 3792 | err = azx_probe_continue(chip); |
| 3752 | if (err < 0) | 3793 | if (err < 0) |
| @@ -3769,6 +3810,16 @@ static int azx_probe_continue(struct azx *chip) | |||
| 3769 | int dev = chip->dev_index; | 3810 | int dev = chip->dev_index; |
| 3770 | int err; | 3811 | int err; |
| 3771 | 3812 | ||
| 3813 | /* Request power well for Haswell HDA controller and codec */ | ||
| 3814 | if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { | ||
| 3815 | err = hda_i915_init(); | ||
| 3816 | if (err < 0) { | ||
| 3817 | snd_printk(KERN_ERR SFX "Error request power-well from i915\n"); | ||
| 3818 | goto out_free; | ||
| 3819 | } | ||
| 3820 | hda_display_power(true); | ||
| 3821 | } | ||
| 3822 | |||
| 3772 | err = azx_first_init(chip); | 3823 | err = azx_first_init(chip); |
| 3773 | if (err < 0) | 3824 | if (err < 0) |
| 3774 | goto out_free; | 3825 | goto out_free; |
| @@ -3863,11 +3914,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { | |||
| 3863 | .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, | 3914 | .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, |
| 3864 | /* Haswell */ | 3915 | /* Haswell */ |
| 3865 | { PCI_DEVICE(0x8086, 0x0a0c), | 3916 | { PCI_DEVICE(0x8086, 0x0a0c), |
| 3866 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, | 3917 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | |
| 3918 | AZX_DCAPS_I915_POWERWELL }, | ||
| 3867 | { PCI_DEVICE(0x8086, 0x0c0c), | 3919 | { PCI_DEVICE(0x8086, 0x0c0c), |
| 3868 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, | 3920 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | |
| 3921 | AZX_DCAPS_I915_POWERWELL }, | ||
| 3869 | { PCI_DEVICE(0x8086, 0x0d0c), | 3922 | { PCI_DEVICE(0x8086, 0x0d0c), |
| 3870 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, | 3923 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | |
| 3924 | AZX_DCAPS_I915_POWERWELL }, | ||
| 3871 | /* 5 Series/3400 */ | 3925 | /* 5 Series/3400 */ |
| 3872 | { PCI_DEVICE(0x8086, 0x3b56), | 3926 | { PCI_DEVICE(0x8086, 0x3b56), |
| 3873 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, | 3927 | .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, |
