| .. | .. |
|---|
| 1 | | -// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | +// SPDX-License-Identifier: MIT |
|---|
| 2 | 2 | #include <linux/pci.h> |
|---|
| 3 | 3 | #include <linux/acpi.h> |
|---|
| 4 | 4 | #include <linux/slab.h> |
|---|
| .. | .. |
|---|
| 49 | 49 | bool optimus_flags_detected; |
|---|
| 50 | 50 | bool optimus_skip_dsm; |
|---|
| 51 | 51 | acpi_handle dhandle; |
|---|
| 52 | | - acpi_handle rom_handle; |
|---|
| 53 | 52 | } nouveau_dsm_priv; |
|---|
| 54 | 53 | |
|---|
| 55 | 54 | bool nouveau_is_optimus(void) { |
|---|
| .. | .. |
|---|
| 212 | 211 | .get_client_id = nouveau_dsm_get_client_id, |
|---|
| 213 | 212 | }; |
|---|
| 214 | 213 | |
|---|
| 215 | | -/* |
|---|
| 216 | | - * Firmware supporting Windows 8 or later do not use _DSM to put the device into |
|---|
| 217 | | - * D3cold, they instead rely on disabling power resources on the parent. |
|---|
| 218 | | - */ |
|---|
| 219 | | -static bool nouveau_pr3_present(struct pci_dev *pdev) |
|---|
| 220 | | -{ |
|---|
| 221 | | - struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); |
|---|
| 222 | | - struct acpi_device *parent_adev; |
|---|
| 223 | | - |
|---|
| 224 | | - if (!parent_pdev) |
|---|
| 225 | | - return false; |
|---|
| 226 | | - |
|---|
| 227 | | - if (!parent_pdev->bridge_d3) { |
|---|
| 228 | | - /* |
|---|
| 229 | | - * Parent PCI bridge is currently not power managed. |
|---|
| 230 | | - * Since userspace can change these afterwards to be on |
|---|
| 231 | | - * the safe side we stick with _DSM and prevent usage of |
|---|
| 232 | | - * _PR3 from the bridge. |
|---|
| 233 | | - */ |
|---|
| 234 | | - pci_d3cold_disable(pdev); |
|---|
| 235 | | - return false; |
|---|
| 236 | | - } |
|---|
| 237 | | - |
|---|
| 238 | | - parent_adev = ACPI_COMPANION(&parent_pdev->dev); |
|---|
| 239 | | - if (!parent_adev) |
|---|
| 240 | | - return false; |
|---|
| 241 | | - |
|---|
| 242 | | - return parent_adev->power.flags.power_resources && |
|---|
| 243 | | - acpi_has_method(parent_adev->handle, "_PR3"); |
|---|
| 244 | | -} |
|---|
| 245 | | - |
|---|
| 246 | 214 | static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out, |
|---|
| 247 | 215 | bool *has_mux, bool *has_opt, |
|---|
| 248 | 216 | bool *has_opt_flags, bool *has_pr3) |
|---|
| .. | .. |
|---|
| 250 | 218 | acpi_handle dhandle; |
|---|
| 251 | 219 | bool supports_mux; |
|---|
| 252 | 220 | int optimus_funcs; |
|---|
| 221 | + struct pci_dev *parent_pdev; |
|---|
| 222 | + |
|---|
| 223 | + *has_pr3 = false; |
|---|
| 224 | + parent_pdev = pci_upstream_bridge(pdev); |
|---|
| 225 | + if (parent_pdev) { |
|---|
| 226 | + if (parent_pdev->bridge_d3) |
|---|
| 227 | + *has_pr3 = pci_pr3_present(parent_pdev); |
|---|
| 228 | + else |
|---|
| 229 | + pci_d3cold_disable(pdev); |
|---|
| 230 | + } |
|---|
| 253 | 231 | |
|---|
| 254 | 232 | dhandle = ACPI_HANDLE(&pdev->dev); |
|---|
| 255 | 233 | if (!dhandle) |
|---|
| .. | .. |
|---|
| 270 | 248 | *has_mux = supports_mux; |
|---|
| 271 | 249 | *has_opt = !!optimus_funcs; |
|---|
| 272 | 250 | *has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS); |
|---|
| 273 | | - *has_pr3 = false; |
|---|
| 274 | 251 | |
|---|
| 275 | 252 | if (optimus_funcs) { |
|---|
| 276 | 253 | uint32_t result; |
|---|
| .. | .. |
|---|
| 280 | 257 | (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", |
|---|
| 281 | 258 | (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", |
|---|
| 282 | 259 | (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); |
|---|
| 283 | | - |
|---|
| 284 | | - *has_pr3 = nouveau_pr3_present(pdev); |
|---|
| 285 | 260 | } |
|---|
| 286 | 261 | } |
|---|
| 287 | 262 | |
|---|
| .. | .. |
|---|
| 384 | 359 | void nouveau_unregister_dsm_handler(void) {} |
|---|
| 385 | 360 | void nouveau_switcheroo_optimus_dsm(void) {} |
|---|
| 386 | 361 | #endif |
|---|
| 387 | | - |
|---|
| 388 | | -/* retrieve the ROM in 4k blocks */ |
|---|
| 389 | | -static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, |
|---|
| 390 | | - int offset, int len) |
|---|
| 391 | | -{ |
|---|
| 392 | | - acpi_status status; |
|---|
| 393 | | - union acpi_object rom_arg_elements[2], *obj; |
|---|
| 394 | | - struct acpi_object_list rom_arg; |
|---|
| 395 | | - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; |
|---|
| 396 | | - |
|---|
| 397 | | - rom_arg.count = 2; |
|---|
| 398 | | - rom_arg.pointer = &rom_arg_elements[0]; |
|---|
| 399 | | - |
|---|
| 400 | | - rom_arg_elements[0].type = ACPI_TYPE_INTEGER; |
|---|
| 401 | | - rom_arg_elements[0].integer.value = offset; |
|---|
| 402 | | - |
|---|
| 403 | | - rom_arg_elements[1].type = ACPI_TYPE_INTEGER; |
|---|
| 404 | | - rom_arg_elements[1].integer.value = len; |
|---|
| 405 | | - |
|---|
| 406 | | - status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); |
|---|
| 407 | | - if (ACPI_FAILURE(status)) { |
|---|
| 408 | | - pr_info("failed to evaluate ROM got %s\n", |
|---|
| 409 | | - acpi_format_exception(status)); |
|---|
| 410 | | - return -ENODEV; |
|---|
| 411 | | - } |
|---|
| 412 | | - obj = (union acpi_object *)buffer.pointer; |
|---|
| 413 | | - len = min(len, (int)obj->buffer.length); |
|---|
| 414 | | - memcpy(bios+offset, obj->buffer.pointer, len); |
|---|
| 415 | | - kfree(buffer.pointer); |
|---|
| 416 | | - return len; |
|---|
| 417 | | -} |
|---|
| 418 | | - |
|---|
| 419 | | -bool nouveau_acpi_rom_supported(struct device *dev) |
|---|
| 420 | | -{ |
|---|
| 421 | | - acpi_status status; |
|---|
| 422 | | - acpi_handle dhandle, rom_handle; |
|---|
| 423 | | - |
|---|
| 424 | | - dhandle = ACPI_HANDLE(dev); |
|---|
| 425 | | - if (!dhandle) |
|---|
| 426 | | - return false; |
|---|
| 427 | | - |
|---|
| 428 | | - status = acpi_get_handle(dhandle, "_ROM", &rom_handle); |
|---|
| 429 | | - if (ACPI_FAILURE(status)) |
|---|
| 430 | | - return false; |
|---|
| 431 | | - |
|---|
| 432 | | - nouveau_dsm_priv.rom_handle = rom_handle; |
|---|
| 433 | | - return true; |
|---|
| 434 | | -} |
|---|
| 435 | | - |
|---|
| 436 | | -int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) |
|---|
| 437 | | -{ |
|---|
| 438 | | - return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); |
|---|
| 439 | | -} |
|---|
| 440 | 362 | |
|---|
| 441 | 363 | void * |
|---|
| 442 | 364 | nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) |
|---|