brintos

brintos / linux-shallow public Read only

0
0
Text · 17.1 KiB · 375f020 Raw
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					&params);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					&params);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					&params);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					&params);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					&params);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