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