Thomas Gleixner | c942fdd | 2019-05-27 08:55:06 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 2 | /* |
| 3 | * hdac_i915.c - routines for sync between HD-A core and i915 display driver |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <linux/init.h> |
| 7 | #include <linux/module.h> |
| 8 | #include <linux/pci.h> |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 9 | #include <sound/core.h> |
| 10 | #include <sound/hdaudio.h> |
| 11 | #include <sound/hda_i915.h> |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 12 | #include <sound/hda_register.h> |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 13 | |
Takashi Iwai | f9b54e1 | 2018-07-11 15:05:06 +0200 | [diff] [blame] | 14 | static struct completion bind_complete; |
| 15 | |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 16 | #define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ |
| 17 | ((pci)->device == 0x0c0c) || \ |
| 18 | ((pci)->device == 0x0d0c) || \ |
| 19 | ((pci)->device == 0x160c)) |
| 20 | |
Takashi Iwai | 78dd5e2 | 2015-10-28 12:26:48 +0100 | [diff] [blame] | 21 | /** |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 22 | * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW |
Takashi Iwai | 78dd5e2 | 2015-10-28 12:26:48 +0100 | [diff] [blame] | 23 | * @bus: HDA core bus |
| 24 | * |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 25 | * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK |
| 26 | * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value) |
| 27 | * are used to convert CDClk (Core Display Clock) to 24MHz BCLK: |
| 28 | * BCLK = CDCLK * M / N |
| 29 | * The values will be lost when the display power well is disabled and need to |
| 30 | * be restored to avoid abnormal playback speed. |
Takashi Iwai | 78dd5e2 | 2015-10-28 12:26:48 +0100 | [diff] [blame] | 31 | * |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 32 | * Call this function at initializing and changing power well, as well as |
| 33 | * at ELD notifier for the hotplug. |
Takashi Iwai | 78dd5e2 | 2015-10-28 12:26:48 +0100 | [diff] [blame] | 34 | */ |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 35 | void snd_hdac_i915_set_bclk(struct hdac_bus *bus) |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 36 | { |
Takashi Iwai | ae891ab | 2018-07-11 15:17:22 +0200 | [diff] [blame] | 37 | struct drm_audio_component *acomp = bus->audio_component; |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 38 | struct pci_dev *pci = to_pci_dev(bus->dev); |
| 39 | int cdclk_freq; |
| 40 | unsigned int bclk_m, bclk_n; |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 41 | |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 42 | if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq) |
| 43 | return; /* only for i915 binding */ |
| 44 | if (!CONTROLLER_IN_GPU(pci)) |
| 45 | return; /* only HSW/BDW */ |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 46 | |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 47 | cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); |
| 48 | switch (cdclk_freq) { |
| 49 | case 337500: |
| 50 | bclk_m = 16; |
| 51 | bclk_n = 225; |
| 52 | break; |
| 53 | |
| 54 | case 450000: |
| 55 | default: /* default CDCLK 450MHz */ |
| 56 | bclk_m = 4; |
| 57 | bclk_n = 75; |
| 58 | break; |
| 59 | |
| 60 | case 540000: |
| 61 | bclk_m = 4; |
| 62 | bclk_n = 90; |
| 63 | break; |
| 64 | |
| 65 | case 675000: |
| 66 | bclk_m = 8; |
| 67 | bclk_n = 225; |
| 68 | break; |
| 69 | } |
| 70 | |
| 71 | snd_hdac_chip_writew(bus, HSW_EM4, bclk_m); |
| 72 | snd_hdac_chip_writew(bus, HSW_EM5, bclk_n); |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 73 | } |
Takashi Iwai | bb03ed2 | 2016-04-21 16:39:17 +0200 | [diff] [blame] | 74 | EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 75 | |
Daniel Vetter | 8857c7d | 2019-02-08 00:27:59 +0100 | [diff] [blame] | 76 | static int i915_component_master_match(struct device *dev, int subcomponent, |
| 77 | void *data) |
Takashi Iwai | e2dc7d7 | 2015-12-01 12:39:38 +0100 | [diff] [blame] | 78 | { |
Daniel Vetter | 8857c7d | 2019-02-08 00:27:59 +0100 | [diff] [blame] | 79 | return !strcmp(dev->driver->name, "i915") && |
| 80 | subcomponent == I915_COMPONENT_AUDIO; |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 81 | } |
| 82 | |
Takashi Iwai | bfa5fb1 | 2016-03-29 15:03:06 +0200 | [diff] [blame] | 83 | /* check whether intel graphics is present */ |
| 84 | static bool i915_gfx_present(void) |
| 85 | { |
Arvind Yadav | 6c5a266 | 2017-07-18 22:35:06 +0530 | [diff] [blame] | 86 | static const struct pci_device_id ids[] = { |
Takashi Iwai | bfa5fb1 | 2016-03-29 15:03:06 +0200 | [diff] [blame] | 87 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), |
| 88 | .class = PCI_BASE_CLASS_DISPLAY << 16, |
| 89 | .class_mask = 0xff << 16 }, |
| 90 | {} |
| 91 | }; |
| 92 | return pci_dev_present(ids); |
| 93 | } |
| 94 | |
Takashi Iwai | f9b54e1 | 2018-07-11 15:05:06 +0200 | [diff] [blame] | 95 | static int i915_master_bind(struct device *dev, |
| 96 | struct drm_audio_component *acomp) |
| 97 | { |
| 98 | complete_all(&bind_complete); |
| 99 | /* clear audio_ops here as it was needed only for completion call */ |
| 100 | acomp->audio_ops = NULL; |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | static const struct drm_audio_component_audio_ops i915_init_ops = { |
| 105 | .master_bind = i915_master_bind |
| 106 | }; |
| 107 | |
Takashi Iwai | 78dd5e2 | 2015-10-28 12:26:48 +0100 | [diff] [blame] | 108 | /** |
| 109 | * snd_hdac_i915_init - Initialize i915 audio component |
| 110 | * @bus: HDA core bus |
| 111 | * |
| 112 | * This function is supposed to be used only by a HD-audio controller |
| 113 | * driver that needs the interaction with i915 graphics. |
| 114 | * |
| 115 | * This function initializes and sets up the audio component to communicate |
| 116 | * with i915 graphics driver. |
| 117 | * |
| 118 | * Returns zero for success or a negative error code. |
| 119 | */ |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 120 | int snd_hdac_i915_init(struct hdac_bus *bus) |
| 121 | { |
Takashi Iwai | ae891ab | 2018-07-11 15:17:22 +0200 | [diff] [blame] | 122 | struct drm_audio_component *acomp; |
Takashi Iwai | a57942b | 2018-07-11 16:23:16 +0200 | [diff] [blame] | 123 | int err; |
Takashi Iwai | d745f5e | 2016-03-21 14:41:58 +0100 | [diff] [blame] | 124 | |
Takashi Iwai | bfa5fb1 | 2016-03-29 15:03:06 +0200 | [diff] [blame] | 125 | if (!i915_gfx_present()) |
| 126 | return -ENODEV; |
| 127 | |
Takashi Iwai | f9b54e1 | 2018-07-11 15:05:06 +0200 | [diff] [blame] | 128 | init_completion(&bind_complete); |
| 129 | |
| 130 | err = snd_hdac_acomp_init(bus, &i915_init_ops, |
Takashi Iwai | a57942b | 2018-07-11 16:23:16 +0200 | [diff] [blame] | 131 | i915_component_master_match, |
| 132 | sizeof(struct i915_audio_component) - sizeof(*acomp)); |
| 133 | if (err < 0) |
| 134 | return err; |
| 135 | acomp = bus->audio_component; |
| 136 | if (!acomp) |
| 137 | return -ENODEV; |
Takashi Iwai | f9b54e1 | 2018-07-11 15:05:06 +0200 | [diff] [blame] | 138 | if (!acomp->ops) { |
Takashi Iwai | a57942b | 2018-07-11 16:23:16 +0200 | [diff] [blame] | 139 | request_module("i915"); |
Takashi Iwai | cfc35f9 | 2019-02-25 16:49:27 +0100 | [diff] [blame] | 140 | /* 60s timeout */ |
Takashi Iwai | b3a5402 | 2018-09-18 18:21:11 +0200 | [diff] [blame] | 141 | wait_for_completion_timeout(&bind_complete, |
Takashi Iwai | cfc35f9 | 2019-02-25 16:49:27 +0100 | [diff] [blame] | 142 | msecs_to_jiffies(60 * 1000)); |
Takashi Iwai | f9b54e1 | 2018-07-11 15:05:06 +0200 | [diff] [blame] | 143 | } |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 144 | if (!acomp->ops) { |
Takashi Iwai | b3a5402 | 2018-09-18 18:21:11 +0200 | [diff] [blame] | 145 | dev_info(bus->dev, "couldn't bind with audio component\n"); |
Takashi Iwai | a57942b | 2018-07-11 16:23:16 +0200 | [diff] [blame] | 146 | snd_hdac_acomp_exit(bus); |
| 147 | return -ENODEV; |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 148 | } |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 149 | return 0; |
Mengdong Lin | 98d8fc6 | 2015-05-19 22:29:30 +0800 | [diff] [blame] | 150 | } |
| 151 | EXPORT_SYMBOL_GPL(snd_hdac_i915_init); |