681 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2010 Red Hat Inc.4 * Author : Dave Airlie <airlied@redhat.com>5 *6 * ATPX support for both Intel/ATI7 */8#include <linux/vga_switcheroo.h>9#include <linux/slab.h>10#include <linux/acpi.h>11#include <linux/pci.h>12#include <linux/delay.h>13 14#include "amdgpu.h"15#include "amd_acpi.h"16 17#define AMDGPU_PX_QUIRK_FORCE_ATPX (1 << 0)18 19struct amdgpu_px_quirk {20 u32 chip_vendor;21 u32 chip_device;22 u32 subsys_vendor;23 u32 subsys_device;24 u32 px_quirk_flags;25};26 27struct amdgpu_atpx_functions {28 bool px_params;29 bool power_cntl;30 bool disp_mux_cntl;31 bool i2c_mux_cntl;32 bool switch_start;33 bool switch_end;34 bool disp_connectors_mapping;35 bool disp_detection_ports;36};37 38struct amdgpu_atpx {39 acpi_handle handle;40 struct amdgpu_atpx_functions functions;41 bool is_hybrid;42 bool dgpu_req_power_for_displays;43};44 45static struct amdgpu_atpx_priv {46 bool atpx_detected;47 bool bridge_pm_usable;48 unsigned int quirks;49 /* handle for device - and atpx */50 acpi_handle dhandle;51 acpi_handle other_handle;52 struct amdgpu_atpx atpx;53} amdgpu_atpx_priv;54 55struct atpx_verify_interface {56 u16 size; /* structure size in bytes (includes size field) */57 u16 version; /* version */58 u32 function_bits; /* supported functions bit vector */59} __packed;60 61struct atpx_px_params {62 u16 size; /* structure size in bytes (includes size field) */63 u32 valid_flags; /* which flags are valid */64 u32 flags; /* flags */65} __packed;66 67struct atpx_power_control {68 u16 size;69 u8 dgpu_state;70} __packed;71 72struct atpx_mux {73 u16 size;74 u16 mux;75} __packed;76 77bool amdgpu_has_atpx(void)78{79 return amdgpu_atpx_priv.atpx_detected;80}81 82bool amdgpu_has_atpx_dgpu_power_cntl(void)83{84 return amdgpu_atpx_priv.atpx.functions.power_cntl;85}86 87bool amdgpu_is_atpx_hybrid(void)88{89 return amdgpu_atpx_priv.atpx.is_hybrid;90}91 92bool amdgpu_atpx_dgpu_req_power_for_displays(void)93{94 return amdgpu_atpx_priv.atpx.dgpu_req_power_for_displays;95}96 97#if defined(CONFIG_ACPI)98void *amdgpu_atpx_get_dhandle(void)99{100 return amdgpu_atpx_priv.dhandle;101}102#endif103 104/**105 * amdgpu_atpx_call - call an ATPX method106 *107 * @handle: acpi handle108 * @function: the ATPX function to execute109 * @params: ATPX function params110 *111 * Executes the requested ATPX function (all asics).112 * Returns a pointer to the acpi output buffer.113 */114static union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function,115 struct acpi_buffer *params)116{117 acpi_status status;118 union acpi_object atpx_arg_elements[2];119 struct acpi_object_list atpx_arg;120 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };121 122 atpx_arg.count = 2;123 atpx_arg.pointer = &atpx_arg_elements[0];124 125 atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;126 atpx_arg_elements[0].integer.value = function;127 128 if (params) {129 atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;130 atpx_arg_elements[1].buffer.length = params->length;131 atpx_arg_elements[1].buffer.pointer = params->pointer;132 } else {133 /* We need a second fake parameter */134 atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;135 atpx_arg_elements[1].integer.value = 0;136 }137 138 status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);139 140 /* Fail only if calling the method fails and ATPX is supported */141 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {142 pr_err("failed to evaluate ATPX got %s\n",143 acpi_format_exception(status));144 kfree(buffer.pointer);145 return NULL;146 }147 148 return buffer.pointer;149}150 151/**152 * amdgpu_atpx_parse_functions - parse supported functions153 *154 * @f: supported functions struct155 * @mask: supported functions mask from ATPX156 *157 * Use the supported functions mask from ATPX function158 * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions159 * are supported (all asics).160 */161static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mask)162{163 f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED;164 f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED;165 f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED;166 f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED;167 f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED;168 f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED;169 f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED;170 f->disp_detection_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED;171}172 173/**174 * amdgpu_atpx_validate - validate ATPX functions175 *176 * @atpx: amdgpu atpx struct177 *178 * Validate that required functions are enabled (all asics).179 * returns 0 on success, error on failure.180 */181static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)182{183 u32 valid_bits = 0;184 185 if (atpx->functions.px_params) {186 union acpi_object *info;187 struct atpx_px_params output;188 size_t size;189 190 info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL);191 if (!info)192 return -EIO;193 194 memset(&output, 0, sizeof(output));195 196 size = *(u16 *) info->buffer.pointer;197 if (size < 10) {198 pr_err("ATPX buffer is too small: %zu\n", size);199 kfree(info);200 return -EINVAL;201 }202 size = min(sizeof(output), size);203 204 memcpy(&output, info->buffer.pointer, size);205 206 valid_bits = output.flags & output.valid_flags;207 208 kfree(info);209 }210 211 /* if separate mux flag is set, mux controls are required */212 if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {213 atpx->functions.i2c_mux_cntl = true;214 atpx->functions.disp_mux_cntl = true;215 }216 /* if any outputs are muxed, mux controls are required */217 if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |218 ATPX_TV_SIGNAL_MUXED |219 ATPX_DFP_SIGNAL_MUXED))220 atpx->functions.disp_mux_cntl = true;221 222 223 /* some bioses set these bits rather than flagging power_cntl as supported */224 if (valid_bits & (ATPX_DYNAMIC_PX_SUPPORTED |225 ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED))226 atpx->functions.power_cntl = true;227 228 atpx->is_hybrid = false;229 if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) {230 if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {231 pr_warn("ATPX Hybrid Graphics, forcing to ATPX\n");232 atpx->functions.power_cntl = true;233 atpx->is_hybrid = false;234 } else {235 pr_notice("ATPX Hybrid Graphics\n");236 /*237 * Disable legacy PM methods only when pcie port PM is usable,238 * otherwise the device might fail to power off or power on.239 */240 atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;241 atpx->is_hybrid = true;242 }243 }244 245 atpx->dgpu_req_power_for_displays = false;246 if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)247 atpx->dgpu_req_power_for_displays = true;248 249 return 0;250}251 252/**253 * amdgpu_atpx_verify_interface - verify ATPX254 *255 * @atpx: amdgpu atpx struct256 *257 * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function258 * to initialize ATPX and determine what features are supported259 * (all asics).260 * returns 0 on success, error on failure.261 */262static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)263{264 union acpi_object *info;265 struct atpx_verify_interface output;266 size_t size;267 int err = 0;268 269 info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL);270 if (!info)271 return -EIO;272 273 memset(&output, 0, sizeof(output));274 275 size = *(u16 *) info->buffer.pointer;276 if (size < 8) {277 pr_err("ATPX buffer is too small: %zu\n", size);278 err = -EINVAL;279 goto out;280 }281 size = min(sizeof(output), size);282 283 memcpy(&output, info->buffer.pointer, size);284 285 /* TODO: check version? */286 pr_notice("ATPX version %u, functions 0x%08x\n",287 output.version, output.function_bits);288 289 amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits);290 291out:292 kfree(info);293 return err;294}295 296/**297 * amdgpu_atpx_set_discrete_state - power up/down discrete GPU298 *299 * @atpx: atpx info struct300 * @state: discrete GPU state (0 = power down, 1 = power up)301 *302 * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to303 * power down/up the discrete GPU (all asics).304 * Returns 0 on success, error on failure.305 */306static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state)307{308 struct acpi_buffer params;309 union acpi_object *info;310 struct atpx_power_control input;311 312 if (atpx->functions.power_cntl) {313 input.size = 3;314 input.dgpu_state = state;315 params.length = input.size;316 params.pointer = &input;317 info = amdgpu_atpx_call(atpx->handle,318 ATPX_FUNCTION_POWER_CONTROL,319 ¶ms);320 if (!info)321 return -EIO;322 kfree(info);323 324 /* 200ms delay is required after off */325 if (state == 0)326 msleep(200);327 }328 return 0;329}330 331/**332 * amdgpu_atpx_switch_disp_mux - switch display mux333 *334 * @atpx: atpx info struct335 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)336 *337 * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to338 * switch the display mux between the discrete GPU and integrated GPU339 * (all asics).340 * Returns 0 on success, error on failure.341 */342static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id)343{344 struct acpi_buffer params;345 union acpi_object *info;346 struct atpx_mux input;347 348 if (atpx->functions.disp_mux_cntl) {349 input.size = 4;350 input.mux = mux_id;351 params.length = input.size;352 params.pointer = &input;353 info = amdgpu_atpx_call(atpx->handle,354 ATPX_FUNCTION_DISPLAY_MUX_CONTROL,355 ¶ms);356 if (!info)357 return -EIO;358 kfree(info);359 }360 return 0;361}362 363/**364 * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux365 *366 * @atpx: atpx info struct367 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)368 *369 * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to370 * switch the i2c/hpd mux between the discrete GPU and integrated GPU371 * (all asics).372 * Returns 0 on success, error on failure.373 */374static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id)375{376 struct acpi_buffer params;377 union acpi_object *info;378 struct atpx_mux input;379 380 if (atpx->functions.i2c_mux_cntl) {381 input.size = 4;382 input.mux = mux_id;383 params.length = input.size;384 params.pointer = &input;385 info = amdgpu_atpx_call(atpx->handle,386 ATPX_FUNCTION_I2C_MUX_CONTROL,387 ¶ms);388 if (!info)389 return -EIO;390 kfree(info);391 }392 return 0;393}394 395/**396 * amdgpu_atpx_switch_start - notify the sbios of a GPU switch397 *398 * @atpx: atpx info struct399 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)400 *401 * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX402 * function to notify the sbios that a switch between the discrete GPU and403 * integrated GPU has begun (all asics).404 * Returns 0 on success, error on failure.405 */406static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id)407{408 struct acpi_buffer params;409 union acpi_object *info;410 struct atpx_mux input;411 412 if (atpx->functions.switch_start) {413 input.size = 4;414 input.mux = mux_id;415 params.length = input.size;416 params.pointer = &input;417 info = amdgpu_atpx_call(atpx->handle,418 ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,419 ¶ms);420 if (!info)421 return -EIO;422 kfree(info);423 }424 return 0;425}426 427/**428 * amdgpu_atpx_switch_end - notify the sbios of a GPU switch429 *430 * @atpx: atpx info struct431 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)432 *433 * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX434 * function to notify the sbios that a switch between the discrete GPU and435 * integrated GPU has ended (all asics).436 * Returns 0 on success, error on failure.437 */438static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id)439{440 struct acpi_buffer params;441 union acpi_object *info;442 struct atpx_mux input;443 444 if (atpx->functions.switch_end) {445 input.size = 4;446 input.mux = mux_id;447 params.length = input.size;448 params.pointer = &input;449 info = amdgpu_atpx_call(atpx->handle,450 ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,451 ¶ms);452 if (!info)453 return -EIO;454 kfree(info);455 }456 return 0;457}458 459/**460 * amdgpu_atpx_switchto - switch to the requested GPU461 *462 * @id: GPU to switch to463 *464 * Execute the necessary ATPX functions to switch between the discrete GPU and465 * integrated GPU (all asics).466 * Returns 0 on success, error on failure.467 */468static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)469{470 u16 gpu_id;471 472 if (id == VGA_SWITCHEROO_IGD)473 gpu_id = ATPX_INTEGRATED_GPU;474 else475 gpu_id = ATPX_DISCRETE_GPU;476 477 amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id);478 amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id);479 amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id);480 amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id);481 482 return 0;483}484 485/**486 * amdgpu_atpx_power_state - power down/up the requested GPU487 *488 * @id: GPU to power down/up489 * @state: requested power state (0 = off, 1 = on)490 *491 * Execute the necessary ATPX function to power down/up the discrete GPU492 * (all asics).493 * Returns 0 on success, error on failure.494 */495static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id,496 enum vga_switcheroo_state state)497{498 /* on w500 ACPI can't change intel gpu state */499 if (id == VGA_SWITCHEROO_IGD)500 return 0;501 502 amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state);503 return 0;504}505 506/**507 * amdgpu_atpx_pci_probe_handle - look up the ATPX handle508 *509 * @pdev: pci device510 *511 * Look up the ATPX handles (all asics).512 * Returns true if the handles are found, false if not.513 */514static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev)515{516 acpi_handle dhandle, atpx_handle;517 acpi_status status;518 519 dhandle = ACPI_HANDLE(&pdev->dev);520 if (!dhandle)521 return false;522 523 status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);524 if (ACPI_FAILURE(status)) {525 amdgpu_atpx_priv.other_handle = dhandle;526 return false;527 }528 amdgpu_atpx_priv.dhandle = dhandle;529 amdgpu_atpx_priv.atpx.handle = atpx_handle;530 return true;531}532 533/**534 * amdgpu_atpx_init - verify the ATPX interface535 *536 * Verify the ATPX interface (all asics).537 * Returns 0 on success, error on failure.538 */539static int amdgpu_atpx_init(void)540{541 int r;542 543 /* set up the ATPX handle */544 r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx);545 if (r)546 return r;547 548 /* validate the atpx setup */549 r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx);550 if (r)551 return r;552 553 return 0;554}555 556/**557 * amdgpu_atpx_get_client_id - get the client id558 *559 * @pdev: pci device560 *561 * look up whether we are the integrated or discrete GPU (all asics).562 * Returns the client id.563 */564static enum vga_switcheroo_client_id amdgpu_atpx_get_client_id(struct pci_dev *pdev)565{566 if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev))567 return VGA_SWITCHEROO_IGD;568 else569 return VGA_SWITCHEROO_DIS;570}571 572static const struct vga_switcheroo_handler amdgpu_atpx_handler = {573 .switchto = amdgpu_atpx_switchto,574 .power_state = amdgpu_atpx_power_state,575 .get_client_id = amdgpu_atpx_get_client_id,576};577 578static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = {579 /* HG _PR3 doesn't seem to work on this A+A weston board */580 { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },581 { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },582 { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },583 { 0x1002, 0x699f, 0x1028, 0x0814, AMDGPU_PX_QUIRK_FORCE_ATPX },584 { 0x1002, 0x6900, 0x1025, 0x125A, AMDGPU_PX_QUIRK_FORCE_ATPX },585 { 0x1002, 0x6900, 0x17AA, 0x3806, AMDGPU_PX_QUIRK_FORCE_ATPX },586 { 0, 0, 0, 0, 0 },587};588 589static void amdgpu_atpx_get_quirks(struct pci_dev *pdev)590{591 const struct amdgpu_px_quirk *p = amdgpu_px_quirk_list;592 593 /* Apply PX quirks */594 while (p && p->chip_device != 0) {595 if (pdev->vendor == p->chip_vendor &&596 pdev->device == p->chip_device &&597 pdev->subsystem_vendor == p->subsys_vendor &&598 pdev->subsystem_device == p->subsys_device) {599 amdgpu_atpx_priv.quirks |= p->px_quirk_flags;600 break;601 }602 ++p;603 }604}605 606/**607 * amdgpu_atpx_detect - detect whether we have PX608 *609 * Check if we have a PX system (all asics).610 * Returns true if we have a PX system, false if not.611 */612static bool amdgpu_atpx_detect(void)613{614 char acpi_method_name[255] = { 0 };615 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};616 struct pci_dev *pdev = NULL;617 bool has_atpx = false;618 int vga_count = 0;619 bool d3_supported = false;620 struct pci_dev *parent_pdev;621 622 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {623 vga_count++;624 625 has_atpx |= amdgpu_atpx_pci_probe_handle(pdev);626 627 parent_pdev = pci_upstream_bridge(pdev);628 d3_supported |= parent_pdev && parent_pdev->bridge_d3;629 amdgpu_atpx_get_quirks(pdev);630 }631 632 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {633 vga_count++;634 635 has_atpx |= amdgpu_atpx_pci_probe_handle(pdev);636 637 parent_pdev = pci_upstream_bridge(pdev);638 d3_supported |= parent_pdev && parent_pdev->bridge_d3;639 amdgpu_atpx_get_quirks(pdev);640 }641 642 if (has_atpx && vga_count == 2) {643 acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer);644 pr_info("vga_switcheroo: detected switching method %s handle\n",645 acpi_method_name);646 amdgpu_atpx_priv.atpx_detected = true;647 amdgpu_atpx_priv.bridge_pm_usable = d3_supported;648 amdgpu_atpx_init();649 return true;650 }651 return false;652}653 654/**655 * amdgpu_register_atpx_handler - register with vga_switcheroo656 *657 * Register the PX callbacks with vga_switcheroo (all asics).658 */659void amdgpu_register_atpx_handler(void)660{661 bool r;662 enum vga_switcheroo_handler_flags_t handler_flags = 0;663 664 /* detect if we have any ATPX + 2 VGA in the system */665 r = amdgpu_atpx_detect();666 if (!r)667 return;668 669 vga_switcheroo_register_handler(&amdgpu_atpx_handler, handler_flags);670}671 672/**673 * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo674 *675 * Unregister the PX callbacks with vga_switcheroo (all asics).676 */677void amdgpu_unregister_atpx_handler(void)678{679 vga_switcheroo_unregister_handler();680}681