diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_acpi.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 105 |
1 files changed, 68 insertions, 37 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index db76b94e6e26..f2ad17aa33f0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c | |||
@@ -45,6 +45,8 @@ | |||
45 | static struct nouveau_dsm_priv { | 45 | static struct nouveau_dsm_priv { |
46 | bool dsm_detected; | 46 | bool dsm_detected; |
47 | bool optimus_detected; | 47 | bool optimus_detected; |
48 | bool optimus_flags_detected; | ||
49 | bool optimus_skip_dsm; | ||
48 | acpi_handle dhandle; | 50 | acpi_handle dhandle; |
49 | acpi_handle rom_handle; | 51 | acpi_handle rom_handle; |
50 | } nouveau_dsm_priv; | 52 | } nouveau_dsm_priv; |
@@ -57,9 +59,6 @@ bool nouveau_is_v1_dsm(void) { | |||
57 | return nouveau_dsm_priv.dsm_detected; | 59 | return nouveau_dsm_priv.dsm_detected; |
58 | } | 60 | } |
59 | 61 | ||
60 | #define NOUVEAU_DSM_HAS_MUX 0x1 | ||
61 | #define NOUVEAU_DSM_HAS_OPT 0x2 | ||
62 | |||
63 | #ifdef CONFIG_VGA_SWITCHEROO | 62 | #ifdef CONFIG_VGA_SWITCHEROO |
64 | static const char nouveau_dsm_muid[] = { | 63 | static const char nouveau_dsm_muid[] = { |
65 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, | 64 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, |
@@ -110,7 +109,7 @@ static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t * | |||
110 | * requirements on the fourth parameter, so a private implementation | 109 | * requirements on the fourth parameter, so a private implementation |
111 | * instead of using acpi_check_dsm(). | 110 | * instead of using acpi_check_dsm(). |
112 | */ | 111 | */ |
113 | static int nouveau_check_optimus_dsm(acpi_handle handle) | 112 | static int nouveau_dsm_get_optimus_functions(acpi_handle handle) |
114 | { | 113 | { |
115 | int result; | 114 | int result; |
116 | 115 | ||
@@ -125,7 +124,9 @@ static int nouveau_check_optimus_dsm(acpi_handle handle) | |||
125 | * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. | 124 | * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. |
126 | * If the n-th bit is enabled, function n is supported | 125 | * If the n-th bit is enabled, function n is supported |
127 | */ | 126 | */ |
128 | return result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS); | 127 | if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS)) |
128 | return result; | ||
129 | return 0; | ||
129 | } | 130 | } |
130 | 131 | ||
131 | static int nouveau_dsm(acpi_handle handle, int func, int arg) | 132 | static int nouveau_dsm(acpi_handle handle, int func, int arg) |
@@ -212,26 +213,55 @@ static const struct vga_switcheroo_handler nouveau_dsm_handler = { | |||
212 | .get_client_id = nouveau_dsm_get_client_id, | 213 | .get_client_id = nouveau_dsm_get_client_id, |
213 | }; | 214 | }; |
214 | 215 | ||
215 | static int nouveau_dsm_pci_probe(struct pci_dev *pdev) | 216 | /* |
217 | * Firmware supporting Windows 8 or later do not use _DSM to put the device into | ||
218 | * D3cold, they instead rely on disabling power resources on the parent. | ||
219 | */ | ||
220 | static bool nouveau_pr3_present(struct pci_dev *pdev) | ||
221 | { | ||
222 | struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); | ||
223 | struct acpi_device *parent_adev; | ||
224 | |||
225 | if (!parent_pdev) | ||
226 | return false; | ||
227 | |||
228 | parent_adev = ACPI_COMPANION(&parent_pdev->dev); | ||
229 | if (!parent_adev) | ||
230 | return false; | ||
231 | |||
232 | return acpi_has_method(parent_adev->handle, "_PR3"); | ||
233 | } | ||
234 | |||
235 | static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out, | ||
236 | bool *has_mux, bool *has_opt, | ||
237 | bool *has_opt_flags, bool *has_pr3) | ||
216 | { | 238 | { |
217 | acpi_handle dhandle; | 239 | acpi_handle dhandle; |
218 | int retval = 0; | 240 | bool supports_mux; |
241 | int optimus_funcs; | ||
219 | 242 | ||
220 | dhandle = ACPI_HANDLE(&pdev->dev); | 243 | dhandle = ACPI_HANDLE(&pdev->dev); |
221 | if (!dhandle) | 244 | if (!dhandle) |
222 | return false; | 245 | return; |
223 | 246 | ||
224 | if (!acpi_has_method(dhandle, "_DSM")) | 247 | if (!acpi_has_method(dhandle, "_DSM")) |
225 | return false; | 248 | return; |
249 | |||
250 | supports_mux = acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, | ||
251 | 1 << NOUVEAU_DSM_POWER); | ||
252 | optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle); | ||
226 | 253 | ||
227 | if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, | 254 | /* Does not look like a Nvidia device. */ |
228 | 1 << NOUVEAU_DSM_POWER)) | 255 | if (!supports_mux && !optimus_funcs) |
229 | retval |= NOUVEAU_DSM_HAS_MUX; | 256 | return; |
230 | 257 | ||
231 | if (nouveau_check_optimus_dsm(dhandle)) | 258 | *dhandle_out = dhandle; |
232 | retval |= NOUVEAU_DSM_HAS_OPT; | 259 | *has_mux = supports_mux; |
260 | *has_opt = !!optimus_funcs; | ||
261 | *has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS); | ||
262 | *has_pr3 = false; | ||
233 | 263 | ||
234 | if (retval & NOUVEAU_DSM_HAS_OPT) { | 264 | if (optimus_funcs) { |
235 | uint32_t result; | 265 | uint32_t result; |
236 | nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, | 266 | nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, |
237 | &result); | 267 | &result); |
@@ -239,11 +269,9 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev) | |||
239 | (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", | 269 | (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", |
240 | (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", | 270 | (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", |
241 | (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); | 271 | (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); |
242 | } | ||
243 | if (retval) | ||
244 | nouveau_dsm_priv.dhandle = dhandle; | ||
245 | 272 | ||
246 | return retval; | 273 | *has_pr3 = nouveau_pr3_present(pdev); |
274 | } | ||
247 | } | 275 | } |
248 | 276 | ||
249 | static bool nouveau_dsm_detect(void) | 277 | static bool nouveau_dsm_detect(void) |
@@ -251,11 +279,13 @@ static bool nouveau_dsm_detect(void) | |||
251 | char acpi_method_name[255] = { 0 }; | 279 | char acpi_method_name[255] = { 0 }; |
252 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; | 280 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; |
253 | struct pci_dev *pdev = NULL; | 281 | struct pci_dev *pdev = NULL; |
254 | int has_dsm = 0; | 282 | acpi_handle dhandle = NULL; |
255 | int has_optimus = 0; | 283 | bool has_mux = false; |
284 | bool has_optimus = false; | ||
285 | bool has_optimus_flags = false; | ||
286 | bool has_power_resources = false; | ||
256 | int vga_count = 0; | 287 | int vga_count = 0; |
257 | bool guid_valid; | 288 | bool guid_valid; |
258 | int retval; | ||
259 | bool ret = false; | 289 | bool ret = false; |
260 | 290 | ||
261 | /* lookup the MXM GUID */ | 291 | /* lookup the MXM GUID */ |
@@ -268,32 +298,32 @@ static bool nouveau_dsm_detect(void) | |||
268 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | 298 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { |
269 | vga_count++; | 299 | vga_count++; |
270 | 300 | ||
271 | retval = nouveau_dsm_pci_probe(pdev); | 301 | nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus, |
272 | if (retval & NOUVEAU_DSM_HAS_MUX) | 302 | &has_optimus_flags, &has_power_resources); |
273 | has_dsm |= 1; | ||
274 | if (retval & NOUVEAU_DSM_HAS_OPT) | ||
275 | has_optimus = 1; | ||
276 | } | 303 | } |
277 | 304 | ||
278 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { | 305 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { |
279 | vga_count++; | 306 | vga_count++; |
280 | 307 | ||
281 | retval = nouveau_dsm_pci_probe(pdev); | 308 | nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus, |
282 | if (retval & NOUVEAU_DSM_HAS_MUX) | 309 | &has_optimus_flags, &has_power_resources); |
283 | has_dsm |= 1; | ||
284 | if (retval & NOUVEAU_DSM_HAS_OPT) | ||
285 | has_optimus = 1; | ||
286 | } | 310 | } |
287 | 311 | ||
288 | /* find the optimus DSM or the old v1 DSM */ | 312 | /* find the optimus DSM or the old v1 DSM */ |
289 | if (has_optimus == 1) { | 313 | if (has_optimus) { |
314 | nouveau_dsm_priv.dhandle = dhandle; | ||
290 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, | 315 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, |
291 | &buffer); | 316 | &buffer); |
292 | printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", | 317 | printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", |
293 | acpi_method_name); | 318 | acpi_method_name); |
319 | if (has_power_resources) | ||
320 | pr_info("nouveau: detected PR support, will not use DSM\n"); | ||
294 | nouveau_dsm_priv.optimus_detected = true; | 321 | nouveau_dsm_priv.optimus_detected = true; |
322 | nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags; | ||
323 | nouveau_dsm_priv.optimus_skip_dsm = has_power_resources; | ||
295 | ret = true; | 324 | ret = true; |
296 | } else if (vga_count == 2 && has_dsm && guid_valid) { | 325 | } else if (vga_count == 2 && has_mux && guid_valid) { |
326 | nouveau_dsm_priv.dhandle = dhandle; | ||
297 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, | 327 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, |
298 | &buffer); | 328 | &buffer); |
299 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", | 329 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", |
@@ -321,11 +351,12 @@ void nouveau_register_dsm_handler(void) | |||
321 | void nouveau_switcheroo_optimus_dsm(void) | 351 | void nouveau_switcheroo_optimus_dsm(void) |
322 | { | 352 | { |
323 | u32 result = 0; | 353 | u32 result = 0; |
324 | if (!nouveau_dsm_priv.optimus_detected) | 354 | if (!nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.optimus_skip_dsm) |
325 | return; | 355 | return; |
326 | 356 | ||
327 | nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, | 357 | if (nouveau_dsm_priv.optimus_flags_detected) |
328 | 0x3, &result); | 358 | nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, |
359 | 0x3, &result); | ||
329 | 360 | ||
330 | nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, | 361 | nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, |
331 | NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); | 362 | NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); |