/** * 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. */ static int 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) { printk("ATPX buffer is too small: %zu\n", size); err = -EINVAL; goto out; } size = min(sizeof(output), size); memcpy(&output, info->buffer.pointer, size); /* TODO: check version? */ printk("ATPX version %u, functions 0x%08x\n", output.version, output.function_bits); amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits); out: kfree(info); return err; }
/** * amdgpu_atpx_validate_functions - validate ATPX functions * * @atpx: amdgpu atpx struct * * Validate that required functions are enabled (all asics). * returns 0 on success, error on failure. */ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) { u32 valid_bits = 0; 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) { printk("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) { printk("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; } return 0; }
/** * amdgpu_atpx_validate_functions - validate ATPX functions * * @atpx: amdgpu atpx struct * * Validate that required functions are enabled (all asics). * returns 0 on success, error on failure. */ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) { /* make sure required functions are enabled */ /* dGPU power control is required */ atpx->functions.power_cntl = true; if (atpx->functions.px_params) { union acpi_object *info; struct atpx_px_params output; size_t size; u32 valid_bits; 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) { printk("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; /* 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; 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. */ static int 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_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. */ static int 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_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. */ static int 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); } return 0; }