// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2010 Red Hat Inc. * Author : Dave Airlie <airlied@redhat.com> * * ATPX support for both Intel/ATI
*/ #include <linux/vga_switcheroo.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/pci.h> #include <linux/delay.h>
if (params) {
atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;
atpx_arg_elements[1].buffer.length = params->length;
atpx_arg_elements[1].buffer.pointer = params->pointer;
} else { /* We need a second fake parameter */
atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;
atpx_arg_elements[1].integer.value = 0;
}
status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
/* Fail only if calling the method fails and ATPX is supported */ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
pr_err("failed to evaluate ATPX got %s\n",
acpi_format_exception(status));
kfree(buffer.pointer); return NULL;
}
if (atpx->functions.px_params) { union acpi_object *info; struct atpx_px_params output;
size_t size;
info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL); if (!info) return -EIO;
memset(&output, 0, sizeof(output));
size = *(u16 *) info->buffer.pointer; if (size < 10) {
pr_err("ATPX buffer is too small: %zu\n", size);
kfree(info); return -EINVAL;
}
size = min(sizeof(output), size);
memcpy(&output, info->buffer.pointer, size);
valid_bits = output.flags & output.valid_flags;
kfree(info);
}
/* if separate mux flag is set, mux controls are required */ if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {
atpx->functions.i2c_mux_cntl = true;
atpx->functions.disp_mux_cntl = true;
} /* if any outputs are muxed, mux controls are required */ if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |
ATPX_TV_SIGNAL_MUXED |
ATPX_DFP_SIGNAL_MUXED))
atpx->functions.disp_mux_cntl = true;
/* some bioses set these bits rather than flagging power_cntl as supported */ if (valid_bits & (ATPX_DYNAMIC_PX_SUPPORTED |
ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED))
atpx->functions.power_cntl = true;
atpx->is_hybrid = false; if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) { if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {
pr_warn("ATPX Hybrid Graphics, forcing to ATPX\n");
atpx->functions.power_cntl = true;
atpx->is_hybrid = false;
} else {
pr_notice("ATPX Hybrid Graphics\n"); /* * Disable legacy PM methods only when pcie port PM is usable, * otherwise the device might fail to power off or power on.
*/
atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
atpx->is_hybrid = true;
}
}
atpx->dgpu_req_power_for_displays = false; if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)
atpx->dgpu_req_power_for_displays = true;
return 0;
}
/** * amdgpu_atpx_verify_interface - verify ATPX * * @atpx: amdgpu atpx struct * * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function * to initialize ATPX and determine what features are supported * (all asics). * returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
{ union acpi_object *info; struct atpx_verify_interface output;
size_t size; int err = 0;
info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL); if (!info) return -EIO;
memset(&output, 0, sizeof(output));
size = *(u16 *) info->buffer.pointer; if (size < 8) {
pr_err("ATPX buffer is too small: %zu\n", size);
err = -EINVAL; goto out;
}
size = min(sizeof(output), size);
/** * amdgpu_atpx_set_discrete_state - power up/down discrete GPU * * @atpx: atpx info struct * @state: discrete GPU state (0 = power down, 1 = power up) * * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to * power down/up the discrete GPU (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state)
{ struct acpi_buffer params; union acpi_object *info; struct atpx_power_control input;
if (atpx->functions.power_cntl) {
input.size = 3;
input.dgpu_state = state;
params.length = input.size;
params.pointer = &input;
info = amdgpu_atpx_call(atpx->handle,
ATPX_FUNCTION_POWER_CONTROL,
¶ms); if (!info) return -EIO;
kfree(info);
/* 200ms delay is required after off */ if (state == 0)
msleep(200);
} return 0;
}
/** * amdgpu_atpx_switch_disp_mux - switch display mux * * @atpx: atpx info struct * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) * * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to * switch the display mux between the discrete GPU and integrated GPU * (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id)
{ struct acpi_buffer params; union acpi_object *info; struct atpx_mux input;
if (atpx->functions.disp_mux_cntl) {
input.size = 4;
input.mux = mux_id;
params.length = input.size;
params.pointer = &input;
info = amdgpu_atpx_call(atpx->handle,
ATPX_FUNCTION_DISPLAY_MUX_CONTROL,
¶ms); if (!info) return -EIO;
kfree(info);
} return 0;
}
/** * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux * * @atpx: atpx info struct * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) * * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to * switch the i2c/hpd mux between the discrete GPU and integrated GPU * (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id)
{ struct acpi_buffer params; union acpi_object *info; struct atpx_mux input;
if (atpx->functions.i2c_mux_cntl) {
input.size = 4;
input.mux = mux_id;
params.length = input.size;
params.pointer = &input;
info = amdgpu_atpx_call(atpx->handle,
ATPX_FUNCTION_I2C_MUX_CONTROL,
¶ms); if (!info) return -EIO;
kfree(info);
} return 0;
}
/** * amdgpu_atpx_switch_start - notify the sbios of a GPU switch * * @atpx: atpx info struct * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) * * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX * function to notify the sbios that a switch between the discrete GPU and * integrated GPU has begun (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id)
{ struct acpi_buffer params; union acpi_object *info; struct atpx_mux input;
if (atpx->functions.switch_start) {
input.size = 4;
input.mux = mux_id;
params.length = input.size;
params.pointer = &input;
info = amdgpu_atpx_call(atpx->handle,
ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,
¶ms); if (!info) return -EIO;
kfree(info);
} return 0;
}
/** * amdgpu_atpx_switch_end - notify the sbios of a GPU switch * * @atpx: atpx info struct * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) * * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX * function to notify the sbios that a switch between the discrete GPU and * integrated GPU has ended (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id)
{ struct acpi_buffer params; union acpi_object *info; struct atpx_mux input;
if (atpx->functions.switch_end) {
input.size = 4;
input.mux = mux_id;
params.length = input.size;
params.pointer = &input;
info = amdgpu_atpx_call(atpx->handle,
ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,
¶ms); if (!info) return -EIO;
kfree(info);
} return 0;
}
/** * amdgpu_atpx_switchto - switch to the requested GPU * * @id: GPU to switch to * * Execute the necessary ATPX functions to switch between the discrete GPU and * integrated GPU (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)
{
u16 gpu_id;
if (id == VGA_SWITCHEROO_IGD)
gpu_id = ATPX_INTEGRATED_GPU; else
gpu_id = ATPX_DISCRETE_GPU;
/** * amdgpu_atpx_power_state - power down/up the requested GPU * * @id: GPU to power down/up * @state: requested power state (0 = off, 1 = on) * * Execute the necessary ATPX function to power down/up the discrete GPU * (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_power_state(enum vga_switcheroo_client_id id, enum vga_switcheroo_state state)
{ /* on w500 ACPI can't change intel gpu state */ if (id == VGA_SWITCHEROO_IGD) return 0;
/** * amdgpu_atpx_init - verify the ATPX interface * * Verify the ATPX interface (all asics). * Returns 0 on success, error on failure.
*/ staticint amdgpu_atpx_init(void)
{ int r;
/* set up the ATPX handle */
r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx); if (r) return r;
/* validate the atpx setup */
r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx); if (r) return r;
return 0;
}
/** * amdgpu_atpx_get_client_id - get the client id * * @pdev: pci device * * look up whether we are the integrated or discrete GPU (all asics). * Returns the client id.
*/ staticenum vga_switcheroo_client_id amdgpu_atpx_get_client_id(struct pci_dev *pdev)
{ if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev)) return VGA_SWITCHEROO_IGD; else return VGA_SWITCHEROO_DIS;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.