1f50a7f3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d38ceaf9SAlex Deucher /*
3d38ceaf9SAlex Deucher  * Copyright (c) 2010 Red Hat Inc.
4d38ceaf9SAlex Deucher  * Author : Dave Airlie <[email protected]>
5d38ceaf9SAlex Deucher  *
6d38ceaf9SAlex Deucher  * ATPX support for both Intel/ATI
7d38ceaf9SAlex Deucher  */
8d38ceaf9SAlex Deucher #include <linux/vga_switcheroo.h>
9d38ceaf9SAlex Deucher #include <linux/slab.h>
10d38ceaf9SAlex Deucher #include <linux/acpi.h>
11d38ceaf9SAlex Deucher #include <linux/pci.h>
12f81eb1a3SAlex Deucher #include <linux/delay.h>
13d38ceaf9SAlex Deucher 
1464cf26f0SIsabella Basso #include "amdgpu.h"
1566dc0dddSRex Zhu #include "amd_acpi.h"
16d38ceaf9SAlex Deucher 
17052c2990SAlex Deucher #define AMDGPU_PX_QUIRK_FORCE_ATPX  (1 << 0)
18052c2990SAlex Deucher 
19052c2990SAlex Deucher struct amdgpu_px_quirk {
20052c2990SAlex Deucher 	u32 chip_vendor;
21052c2990SAlex Deucher 	u32 chip_device;
22052c2990SAlex Deucher 	u32 subsys_vendor;
23052c2990SAlex Deucher 	u32 subsys_device;
24052c2990SAlex Deucher 	u32 px_quirk_flags;
25052c2990SAlex Deucher };
26052c2990SAlex Deucher 
27d38ceaf9SAlex Deucher struct amdgpu_atpx_functions {
28d38ceaf9SAlex Deucher 	bool px_params;
29d38ceaf9SAlex Deucher 	bool power_cntl;
30d38ceaf9SAlex Deucher 	bool disp_mux_cntl;
31d38ceaf9SAlex Deucher 	bool i2c_mux_cntl;
32d38ceaf9SAlex Deucher 	bool switch_start;
33d38ceaf9SAlex Deucher 	bool switch_end;
34d38ceaf9SAlex Deucher 	bool disp_connectors_mapping;
35aa03c075SLyude Paul 	bool disp_detection_ports;
36d38ceaf9SAlex Deucher };
37d38ceaf9SAlex Deucher 
38d38ceaf9SAlex Deucher struct amdgpu_atpx {
39d38ceaf9SAlex Deucher 	acpi_handle handle;
40d38ceaf9SAlex Deucher 	struct amdgpu_atpx_functions functions;
412f5af82eSAlex Deucher 	bool is_hybrid;
42efc83cf4SAlex Deucher 	bool dgpu_req_power_for_displays;
43d38ceaf9SAlex Deucher };
44d38ceaf9SAlex Deucher 
45d38ceaf9SAlex Deucher static struct amdgpu_atpx_priv {
46d38ceaf9SAlex Deucher 	bool atpx_detected;
471db4496fSPeter Wu 	bool bridge_pm_usable;
48052c2990SAlex Deucher 	unsigned int quirks;
49d38ceaf9SAlex Deucher 	/* handle for device - and atpx */
50d38ceaf9SAlex Deucher 	acpi_handle dhandle;
51d38ceaf9SAlex Deucher 	acpi_handle other_handle;
52d38ceaf9SAlex Deucher 	struct amdgpu_atpx atpx;
53d38ceaf9SAlex Deucher } amdgpu_atpx_priv;
54d38ceaf9SAlex Deucher 
55d38ceaf9SAlex Deucher struct atpx_verify_interface {
56d38ceaf9SAlex Deucher 	u16 size;		/* structure size in bytes (includes size field) */
57d38ceaf9SAlex Deucher 	u16 version;		/* version */
58d38ceaf9SAlex Deucher 	u32 function_bits;	/* supported functions bit vector */
59d38ceaf9SAlex Deucher } __packed;
60d38ceaf9SAlex Deucher 
61d38ceaf9SAlex Deucher struct atpx_px_params {
62d38ceaf9SAlex Deucher 	u16 size;		/* structure size in bytes (includes size field) */
63d38ceaf9SAlex Deucher 	u32 valid_flags;	/* which flags are valid */
64d38ceaf9SAlex Deucher 	u32 flags;		/* flags */
65d38ceaf9SAlex Deucher } __packed;
66d38ceaf9SAlex Deucher 
67d38ceaf9SAlex Deucher struct atpx_power_control {
68d38ceaf9SAlex Deucher 	u16 size;
69d38ceaf9SAlex Deucher 	u8 dgpu_state;
70d38ceaf9SAlex Deucher } __packed;
71d38ceaf9SAlex Deucher 
72d38ceaf9SAlex Deucher struct atpx_mux {
73d38ceaf9SAlex Deucher 	u16 size;
74d38ceaf9SAlex Deucher 	u16 mux;
75d38ceaf9SAlex Deucher } __packed;
76d38ceaf9SAlex Deucher 
amdgpu_has_atpx(void)77*07867a78SSrinivasan Shanmugam bool amdgpu_has_atpx(void)
78*07867a78SSrinivasan Shanmugam {
79d38ceaf9SAlex Deucher 	return amdgpu_atpx_priv.atpx_detected;
80d38ceaf9SAlex Deucher }
81d38ceaf9SAlex Deucher 
amdgpu_has_atpx_dgpu_power_cntl(void)82*07867a78SSrinivasan Shanmugam bool amdgpu_has_atpx_dgpu_power_cntl(void)
83*07867a78SSrinivasan Shanmugam {
84a78fe133SAlex Deucher 	return amdgpu_atpx_priv.atpx.functions.power_cntl;
85a78fe133SAlex Deucher }
86a78fe133SAlex Deucher 
amdgpu_is_atpx_hybrid(void)87*07867a78SSrinivasan Shanmugam bool amdgpu_is_atpx_hybrid(void)
88*07867a78SSrinivasan Shanmugam {
892f5af82eSAlex Deucher 	return amdgpu_atpx_priv.atpx.is_hybrid;
902f5af82eSAlex Deucher }
912f5af82eSAlex Deucher 
92d38ceaf9SAlex Deucher /**
93d38ceaf9SAlex Deucher  * amdgpu_atpx_call - call an ATPX method
94d38ceaf9SAlex Deucher  *
95d38ceaf9SAlex Deucher  * @handle: acpi handle
96d38ceaf9SAlex Deucher  * @function: the ATPX function to execute
97d38ceaf9SAlex Deucher  * @params: ATPX function params
98d38ceaf9SAlex Deucher  *
99d38ceaf9SAlex Deucher  * Executes the requested ATPX function (all asics).
100d38ceaf9SAlex Deucher  * Returns a pointer to the acpi output buffer.
101d38ceaf9SAlex Deucher  */
amdgpu_atpx_call(acpi_handle handle,int function,struct acpi_buffer * params)102d38ceaf9SAlex Deucher static union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function,
103d38ceaf9SAlex Deucher 					   struct acpi_buffer *params)
104d38ceaf9SAlex Deucher {
105d38ceaf9SAlex Deucher 	acpi_status status;
106d38ceaf9SAlex Deucher 	union acpi_object atpx_arg_elements[2];
107d38ceaf9SAlex Deucher 	struct acpi_object_list atpx_arg;
108d38ceaf9SAlex Deucher 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
109d38ceaf9SAlex Deucher 
110d38ceaf9SAlex Deucher 	atpx_arg.count = 2;
111d38ceaf9SAlex Deucher 	atpx_arg.pointer = &atpx_arg_elements[0];
112d38ceaf9SAlex Deucher 
113d38ceaf9SAlex Deucher 	atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
114d38ceaf9SAlex Deucher 	atpx_arg_elements[0].integer.value = function;
115d38ceaf9SAlex Deucher 
116d38ceaf9SAlex Deucher 	if (params) {
117d38ceaf9SAlex Deucher 		atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;
118d38ceaf9SAlex Deucher 		atpx_arg_elements[1].buffer.length = params->length;
119d38ceaf9SAlex Deucher 		atpx_arg_elements[1].buffer.pointer = params->pointer;
120d38ceaf9SAlex Deucher 	} else {
121d38ceaf9SAlex Deucher 		/* We need a second fake parameter */
122d38ceaf9SAlex Deucher 		atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;
123d38ceaf9SAlex Deucher 		atpx_arg_elements[1].integer.value = 0;
124d38ceaf9SAlex Deucher 	}
125d38ceaf9SAlex Deucher 
126d38ceaf9SAlex Deucher 	status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
127d38ceaf9SAlex Deucher 
128d38ceaf9SAlex Deucher 	/* Fail only if calling the method fails and ATPX is supported */
129d38ceaf9SAlex Deucher 	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
130*07867a78SSrinivasan Shanmugam 		pr_err("failed to evaluate ATPX got %s\n",
131d38ceaf9SAlex Deucher 		       acpi_format_exception(status));
132d38ceaf9SAlex Deucher 		kfree(buffer.pointer);
133d38ceaf9SAlex Deucher 		return NULL;
134d38ceaf9SAlex Deucher 	}
135d38ceaf9SAlex Deucher 
136d38ceaf9SAlex Deucher 	return buffer.pointer;
137d38ceaf9SAlex Deucher }
138d38ceaf9SAlex Deucher 
139d38ceaf9SAlex Deucher /**
140d38ceaf9SAlex Deucher  * amdgpu_atpx_parse_functions - parse supported functions
141d38ceaf9SAlex Deucher  *
142d38ceaf9SAlex Deucher  * @f: supported functions struct
143d38ceaf9SAlex Deucher  * @mask: supported functions mask from ATPX
144d38ceaf9SAlex Deucher  *
145d38ceaf9SAlex Deucher  * Use the supported functions mask from ATPX function
146d38ceaf9SAlex Deucher  * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions
147d38ceaf9SAlex Deucher  * are supported (all asics).
148d38ceaf9SAlex Deucher  */
amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions * f,u32 mask)149d38ceaf9SAlex Deucher static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mask)
150d38ceaf9SAlex Deucher {
151d38ceaf9SAlex Deucher 	f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED;
152d38ceaf9SAlex Deucher 	f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED;
153d38ceaf9SAlex Deucher 	f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED;
154d38ceaf9SAlex Deucher 	f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED;
155d38ceaf9SAlex Deucher 	f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED;
156d38ceaf9SAlex Deucher 	f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED;
157d38ceaf9SAlex Deucher 	f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED;
158aa03c075SLyude Paul 	f->disp_detection_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED;
159d38ceaf9SAlex Deucher }
160d38ceaf9SAlex Deucher 
161d38ceaf9SAlex Deucher /**
162bbe04decSIsabella Basso  * amdgpu_atpx_validate - validate ATPX functions
163d38ceaf9SAlex Deucher  *
164d38ceaf9SAlex Deucher  * @atpx: amdgpu atpx struct
165d38ceaf9SAlex Deucher  *
166d38ceaf9SAlex Deucher  * Validate that required functions are enabled (all asics).
167d38ceaf9SAlex Deucher  * returns 0 on success, error on failure.
168d38ceaf9SAlex Deucher  */
amdgpu_atpx_validate(struct amdgpu_atpx * atpx)169d38ceaf9SAlex Deucher static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
170d38ceaf9SAlex Deucher {
1718d45f80eSAlex Deucher 	u32 valid_bits = 0;
172e9bef455SAlex Deucher 
173d38ceaf9SAlex Deucher 	if (atpx->functions.px_params) {
174d38ceaf9SAlex Deucher 		union acpi_object *info;
175d38ceaf9SAlex Deucher 		struct atpx_px_params output;
176d38ceaf9SAlex Deucher 		size_t size;
177d38ceaf9SAlex Deucher 
178d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL);
179d38ceaf9SAlex Deucher 		if (!info)
180d38ceaf9SAlex Deucher 			return -EIO;
181d38ceaf9SAlex Deucher 
182d38ceaf9SAlex Deucher 		memset(&output, 0, sizeof(output));
183d38ceaf9SAlex Deucher 
184d38ceaf9SAlex Deucher 		size = *(u16 *) info->buffer.pointer;
185d38ceaf9SAlex Deucher 		if (size < 10) {
186*07867a78SSrinivasan Shanmugam 			pr_err("ATPX buffer is too small: %zu\n", size);
187d38ceaf9SAlex Deucher 			kfree(info);
188d38ceaf9SAlex Deucher 			return -EINVAL;
189d38ceaf9SAlex Deucher 		}
190d38ceaf9SAlex Deucher 		size = min(sizeof(output), size);
191d38ceaf9SAlex Deucher 
192d38ceaf9SAlex Deucher 		memcpy(&output, info->buffer.pointer, size);
193d38ceaf9SAlex Deucher 
194d38ceaf9SAlex Deucher 		valid_bits = output.flags & output.valid_flags;
1958d45f80eSAlex Deucher 
1968d45f80eSAlex Deucher 		kfree(info);
1978d45f80eSAlex Deucher 	}
1988d45f80eSAlex Deucher 
199d38ceaf9SAlex Deucher 	/* if separate mux flag is set, mux controls are required */
200d38ceaf9SAlex Deucher 	if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {
201d38ceaf9SAlex Deucher 		atpx->functions.i2c_mux_cntl = true;
202d38ceaf9SAlex Deucher 		atpx->functions.disp_mux_cntl = true;
203d38ceaf9SAlex Deucher 	}
204d38ceaf9SAlex Deucher 	/* if any outputs are muxed, mux controls are required */
205d38ceaf9SAlex Deucher 	if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |
206d38ceaf9SAlex Deucher 			  ATPX_TV_SIGNAL_MUXED |
207d38ceaf9SAlex Deucher 			  ATPX_DFP_SIGNAL_MUXED))
208d38ceaf9SAlex Deucher 		atpx->functions.disp_mux_cntl = true;
209d38ceaf9SAlex Deucher 
2108d45f80eSAlex Deucher 
2118d45f80eSAlex Deucher 	/* some bioses set these bits rather than flagging power_cntl as supported */
2128d45f80eSAlex Deucher 	if (valid_bits & (ATPX_DYNAMIC_PX_SUPPORTED |
2138d45f80eSAlex Deucher 			  ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED))
2148d45f80eSAlex Deucher 		atpx->functions.power_cntl = true;
2158d45f80eSAlex Deucher 
2162f5af82eSAlex Deucher 	atpx->is_hybrid = false;
2175c614792SAlex Deucher 	if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) {
218052c2990SAlex Deucher 		if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {
219*07867a78SSrinivasan Shanmugam 			pr_warn("ATPX Hybrid Graphics, forcing to ATPX\n");
220052c2990SAlex Deucher 			atpx->functions.power_cntl = true;
221052c2990SAlex Deucher 			atpx->is_hybrid = false;
222052c2990SAlex Deucher 		} else {
223*07867a78SSrinivasan Shanmugam 			pr_notice("ATPX Hybrid Graphics\n");
2241db4496fSPeter Wu 			/*
2251db4496fSPeter Wu 			 * Disable legacy PM methods only when pcie port PM is usable,
2261db4496fSPeter Wu 			 * otherwise the device might fail to power off or power on.
2271db4496fSPeter Wu 			 */
2281db4496fSPeter Wu 			atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
2292f5af82eSAlex Deucher 			atpx->is_hybrid = true;
2305c614792SAlex Deucher 		}
231052c2990SAlex Deucher 	}
2325c614792SAlex Deucher 
233efc83cf4SAlex Deucher 	atpx->dgpu_req_power_for_displays = false;
234efc83cf4SAlex Deucher 	if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)
235efc83cf4SAlex Deucher 		atpx->dgpu_req_power_for_displays = true;
236efc83cf4SAlex Deucher 
237d38ceaf9SAlex Deucher 	return 0;
238d38ceaf9SAlex Deucher }
239d38ceaf9SAlex Deucher 
240d38ceaf9SAlex Deucher /**
241d38ceaf9SAlex Deucher  * amdgpu_atpx_verify_interface - verify ATPX
242d38ceaf9SAlex Deucher  *
243d38ceaf9SAlex Deucher  * @atpx: amdgpu atpx struct
244d38ceaf9SAlex Deucher  *
245d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function
246d38ceaf9SAlex Deucher  * to initialize ATPX and determine what features are supported
247d38ceaf9SAlex Deucher  * (all asics).
248d38ceaf9SAlex Deucher  * returns 0 on success, error on failure.
249d38ceaf9SAlex Deucher  */
amdgpu_atpx_verify_interface(struct amdgpu_atpx * atpx)250d38ceaf9SAlex Deucher static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
251d38ceaf9SAlex Deucher {
252d38ceaf9SAlex Deucher 	union acpi_object *info;
253d38ceaf9SAlex Deucher 	struct atpx_verify_interface output;
254d38ceaf9SAlex Deucher 	size_t size;
255d38ceaf9SAlex Deucher 	int err = 0;
256d38ceaf9SAlex Deucher 
257d38ceaf9SAlex Deucher 	info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL);
258d38ceaf9SAlex Deucher 	if (!info)
259d38ceaf9SAlex Deucher 		return -EIO;
260d38ceaf9SAlex Deucher 
261d38ceaf9SAlex Deucher 	memset(&output, 0, sizeof(output));
262d38ceaf9SAlex Deucher 
263d38ceaf9SAlex Deucher 	size = *(u16 *) info->buffer.pointer;
264d38ceaf9SAlex Deucher 	if (size < 8) {
265*07867a78SSrinivasan Shanmugam 		pr_err("ATPX buffer is too small: %zu\n", size);
266d38ceaf9SAlex Deucher 		err = -EINVAL;
267d38ceaf9SAlex Deucher 		goto out;
268d38ceaf9SAlex Deucher 	}
269d38ceaf9SAlex Deucher 	size = min(sizeof(output), size);
270d38ceaf9SAlex Deucher 
271d38ceaf9SAlex Deucher 	memcpy(&output, info->buffer.pointer, size);
272d38ceaf9SAlex Deucher 
273d38ceaf9SAlex Deucher 	/* TODO: check version? */
274*07867a78SSrinivasan Shanmugam 	pr_notice("ATPX version %u, functions 0x%08x\n",
275d38ceaf9SAlex Deucher 		  output.version, output.function_bits);
276d38ceaf9SAlex Deucher 
277d38ceaf9SAlex Deucher 	amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits);
278d38ceaf9SAlex Deucher 
279d38ceaf9SAlex Deucher out:
280d38ceaf9SAlex Deucher 	kfree(info);
281d38ceaf9SAlex Deucher 	return err;
282d38ceaf9SAlex Deucher }
283d38ceaf9SAlex Deucher 
284d38ceaf9SAlex Deucher /**
285d38ceaf9SAlex Deucher  * amdgpu_atpx_set_discrete_state - power up/down discrete GPU
286d38ceaf9SAlex Deucher  *
287d38ceaf9SAlex Deucher  * @atpx: atpx info struct
288d38ceaf9SAlex Deucher  * @state: discrete GPU state (0 = power down, 1 = power up)
289d38ceaf9SAlex Deucher  *
290d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to
291d38ceaf9SAlex Deucher  * power down/up the discrete GPU (all asics).
292d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
293d38ceaf9SAlex Deucher  */
amdgpu_atpx_set_discrete_state(struct amdgpu_atpx * atpx,u8 state)294d38ceaf9SAlex Deucher static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state)
295d38ceaf9SAlex Deucher {
296d38ceaf9SAlex Deucher 	struct acpi_buffer params;
297d38ceaf9SAlex Deucher 	union acpi_object *info;
298d38ceaf9SAlex Deucher 	struct atpx_power_control input;
299d38ceaf9SAlex Deucher 
300d38ceaf9SAlex Deucher 	if (atpx->functions.power_cntl) {
301d38ceaf9SAlex Deucher 		input.size = 3;
302d38ceaf9SAlex Deucher 		input.dgpu_state = state;
303d38ceaf9SAlex Deucher 		params.length = input.size;
304d38ceaf9SAlex Deucher 		params.pointer = &input;
305d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle,
306d38ceaf9SAlex Deucher 					ATPX_FUNCTION_POWER_CONTROL,
307d38ceaf9SAlex Deucher 					&params);
308d38ceaf9SAlex Deucher 		if (!info)
309d38ceaf9SAlex Deucher 			return -EIO;
310d38ceaf9SAlex Deucher 		kfree(info);
311f81eb1a3SAlex Deucher 
312f81eb1a3SAlex Deucher 		/* 200ms delay is required after off */
313f81eb1a3SAlex Deucher 		if (state == 0)
314f81eb1a3SAlex Deucher 			msleep(200);
315d38ceaf9SAlex Deucher 	}
316d38ceaf9SAlex Deucher 	return 0;
317d38ceaf9SAlex Deucher }
318d38ceaf9SAlex Deucher 
319d38ceaf9SAlex Deucher /**
320d38ceaf9SAlex Deucher  * amdgpu_atpx_switch_disp_mux - switch display mux
321d38ceaf9SAlex Deucher  *
322d38ceaf9SAlex Deucher  * @atpx: atpx info struct
323d38ceaf9SAlex Deucher  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
324d38ceaf9SAlex Deucher  *
325d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to
326d38ceaf9SAlex Deucher  * switch the display mux between the discrete GPU and integrated GPU
327d38ceaf9SAlex Deucher  * (all asics).
328d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
329d38ceaf9SAlex Deucher  */
amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx * atpx,u16 mux_id)330d38ceaf9SAlex Deucher static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id)
331d38ceaf9SAlex Deucher {
332d38ceaf9SAlex Deucher 	struct acpi_buffer params;
333d38ceaf9SAlex Deucher 	union acpi_object *info;
334d38ceaf9SAlex Deucher 	struct atpx_mux input;
335d38ceaf9SAlex Deucher 
336d38ceaf9SAlex Deucher 	if (atpx->functions.disp_mux_cntl) {
337d38ceaf9SAlex Deucher 		input.size = 4;
338d38ceaf9SAlex Deucher 		input.mux = mux_id;
339d38ceaf9SAlex Deucher 		params.length = input.size;
340d38ceaf9SAlex Deucher 		params.pointer = &input;
341d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle,
342d38ceaf9SAlex Deucher 					ATPX_FUNCTION_DISPLAY_MUX_CONTROL,
343d38ceaf9SAlex Deucher 					&params);
344d38ceaf9SAlex Deucher 		if (!info)
345d38ceaf9SAlex Deucher 			return -EIO;
346d38ceaf9SAlex Deucher 		kfree(info);
347d38ceaf9SAlex Deucher 	}
348d38ceaf9SAlex Deucher 	return 0;
349d38ceaf9SAlex Deucher }
350d38ceaf9SAlex Deucher 
351d38ceaf9SAlex Deucher /**
352d38ceaf9SAlex Deucher  * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux
353d38ceaf9SAlex Deucher  *
354d38ceaf9SAlex Deucher  * @atpx: atpx info struct
355d38ceaf9SAlex Deucher  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
356d38ceaf9SAlex Deucher  *
357d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to
358d38ceaf9SAlex Deucher  * switch the i2c/hpd mux between the discrete GPU and integrated GPU
359d38ceaf9SAlex Deucher  * (all asics).
360d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
361d38ceaf9SAlex Deucher  */
amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx * atpx,u16 mux_id)362d38ceaf9SAlex Deucher static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id)
363d38ceaf9SAlex Deucher {
364d38ceaf9SAlex Deucher 	struct acpi_buffer params;
365d38ceaf9SAlex Deucher 	union acpi_object *info;
366d38ceaf9SAlex Deucher 	struct atpx_mux input;
367d38ceaf9SAlex Deucher 
368d38ceaf9SAlex Deucher 	if (atpx->functions.i2c_mux_cntl) {
369d38ceaf9SAlex Deucher 		input.size = 4;
370d38ceaf9SAlex Deucher 		input.mux = mux_id;
371d38ceaf9SAlex Deucher 		params.length = input.size;
372d38ceaf9SAlex Deucher 		params.pointer = &input;
373d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle,
374d38ceaf9SAlex Deucher 					ATPX_FUNCTION_I2C_MUX_CONTROL,
375d38ceaf9SAlex Deucher 					&params);
376d38ceaf9SAlex Deucher 		if (!info)
377d38ceaf9SAlex Deucher 			return -EIO;
378d38ceaf9SAlex Deucher 		kfree(info);
379d38ceaf9SAlex Deucher 	}
380d38ceaf9SAlex Deucher 	return 0;
381d38ceaf9SAlex Deucher }
382d38ceaf9SAlex Deucher 
383d38ceaf9SAlex Deucher /**
384d38ceaf9SAlex Deucher  * amdgpu_atpx_switch_start - notify the sbios of a GPU switch
385d38ceaf9SAlex Deucher  *
386d38ceaf9SAlex Deucher  * @atpx: atpx info struct
387d38ceaf9SAlex Deucher  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
388d38ceaf9SAlex Deucher  *
389d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX
390d38ceaf9SAlex Deucher  * function to notify the sbios that a switch between the discrete GPU and
391d38ceaf9SAlex Deucher  * integrated GPU has begun (all asics).
392d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
393d38ceaf9SAlex Deucher  */
amdgpu_atpx_switch_start(struct amdgpu_atpx * atpx,u16 mux_id)394d38ceaf9SAlex Deucher static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id)
395d38ceaf9SAlex Deucher {
396d38ceaf9SAlex Deucher 	struct acpi_buffer params;
397d38ceaf9SAlex Deucher 	union acpi_object *info;
398d38ceaf9SAlex Deucher 	struct atpx_mux input;
399d38ceaf9SAlex Deucher 
400d38ceaf9SAlex Deucher 	if (atpx->functions.switch_start) {
401d38ceaf9SAlex Deucher 		input.size = 4;
402d38ceaf9SAlex Deucher 		input.mux = mux_id;
403d38ceaf9SAlex Deucher 		params.length = input.size;
404d38ceaf9SAlex Deucher 		params.pointer = &input;
405d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle,
406d38ceaf9SAlex Deucher 					ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,
407d38ceaf9SAlex Deucher 					&params);
408d38ceaf9SAlex Deucher 		if (!info)
409d38ceaf9SAlex Deucher 			return -EIO;
410d38ceaf9SAlex Deucher 		kfree(info);
411d38ceaf9SAlex Deucher 	}
412d38ceaf9SAlex Deucher 	return 0;
413d38ceaf9SAlex Deucher }
414d38ceaf9SAlex Deucher 
415d38ceaf9SAlex Deucher /**
416d38ceaf9SAlex Deucher  * amdgpu_atpx_switch_end - notify the sbios of a GPU switch
417d38ceaf9SAlex Deucher  *
418d38ceaf9SAlex Deucher  * @atpx: atpx info struct
419d38ceaf9SAlex Deucher  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
420d38ceaf9SAlex Deucher  *
421d38ceaf9SAlex Deucher  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX
422d38ceaf9SAlex Deucher  * function to notify the sbios that a switch between the discrete GPU and
423d38ceaf9SAlex Deucher  * integrated GPU has ended (all asics).
424d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
425d38ceaf9SAlex Deucher  */
amdgpu_atpx_switch_end(struct amdgpu_atpx * atpx,u16 mux_id)426d38ceaf9SAlex Deucher static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id)
427d38ceaf9SAlex Deucher {
428d38ceaf9SAlex Deucher 	struct acpi_buffer params;
429d38ceaf9SAlex Deucher 	union acpi_object *info;
430d38ceaf9SAlex Deucher 	struct atpx_mux input;
431d38ceaf9SAlex Deucher 
432d38ceaf9SAlex Deucher 	if (atpx->functions.switch_end) {
433d38ceaf9SAlex Deucher 		input.size = 4;
434d38ceaf9SAlex Deucher 		input.mux = mux_id;
435d38ceaf9SAlex Deucher 		params.length = input.size;
436d38ceaf9SAlex Deucher 		params.pointer = &input;
437d38ceaf9SAlex Deucher 		info = amdgpu_atpx_call(atpx->handle,
438d38ceaf9SAlex Deucher 					ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,
439d38ceaf9SAlex Deucher 					&params);
440d38ceaf9SAlex Deucher 		if (!info)
441d38ceaf9SAlex Deucher 			return -EIO;
442d38ceaf9SAlex Deucher 		kfree(info);
443d38ceaf9SAlex Deucher 	}
444d38ceaf9SAlex Deucher 	return 0;
445d38ceaf9SAlex Deucher }
446d38ceaf9SAlex Deucher 
447d38ceaf9SAlex Deucher /**
448d38ceaf9SAlex Deucher  * amdgpu_atpx_switchto - switch to the requested GPU
449d38ceaf9SAlex Deucher  *
450d38ceaf9SAlex Deucher  * @id: GPU to switch to
451d38ceaf9SAlex Deucher  *
452d38ceaf9SAlex Deucher  * Execute the necessary ATPX functions to switch between the discrete GPU and
453d38ceaf9SAlex Deucher  * integrated GPU (all asics).
454d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
455d38ceaf9SAlex Deucher  */
amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)456d38ceaf9SAlex Deucher static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)
457d38ceaf9SAlex Deucher {
458d38ceaf9SAlex Deucher 	u16 gpu_id;
459d38ceaf9SAlex Deucher 
460d38ceaf9SAlex Deucher 	if (id == VGA_SWITCHEROO_IGD)
461d38ceaf9SAlex Deucher 		gpu_id = ATPX_INTEGRATED_GPU;
462d38ceaf9SAlex Deucher 	else
463d38ceaf9SAlex Deucher 		gpu_id = ATPX_DISCRETE_GPU;
464d38ceaf9SAlex Deucher 
465d38ceaf9SAlex Deucher 	amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id);
466d38ceaf9SAlex Deucher 	amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id);
467d38ceaf9SAlex Deucher 	amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id);
468d38ceaf9SAlex Deucher 	amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id);
469d38ceaf9SAlex Deucher 
470d38ceaf9SAlex Deucher 	return 0;
471d38ceaf9SAlex Deucher }
472d38ceaf9SAlex Deucher 
473d38ceaf9SAlex Deucher /**
474d38ceaf9SAlex Deucher  * amdgpu_atpx_power_state - power down/up the requested GPU
475d38ceaf9SAlex Deucher  *
476d38ceaf9SAlex Deucher  * @id: GPU to power down/up
477d38ceaf9SAlex Deucher  * @state: requested power state (0 = off, 1 = on)
478d38ceaf9SAlex Deucher  *
479d38ceaf9SAlex Deucher  * Execute the necessary ATPX function to power down/up the discrete GPU
480d38ceaf9SAlex Deucher  * (all asics).
481d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
482d38ceaf9SAlex Deucher  */
amdgpu_atpx_power_state(enum vga_switcheroo_client_id id,enum vga_switcheroo_state state)483d38ceaf9SAlex Deucher static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id,
484d38ceaf9SAlex Deucher 				   enum vga_switcheroo_state state)
485d38ceaf9SAlex Deucher {
486d38ceaf9SAlex Deucher 	/* on w500 ACPI can't change intel gpu state */
487d38ceaf9SAlex Deucher 	if (id == VGA_SWITCHEROO_IGD)
488d38ceaf9SAlex Deucher 		return 0;
489d38ceaf9SAlex Deucher 
490d38ceaf9SAlex Deucher 	amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state);
491d38ceaf9SAlex Deucher 	return 0;
492d38ceaf9SAlex Deucher }
493d38ceaf9SAlex Deucher 
494d38ceaf9SAlex Deucher /**
495d38ceaf9SAlex Deucher  * amdgpu_atpx_pci_probe_handle - look up the ATPX handle
496d38ceaf9SAlex Deucher  *
497d38ceaf9SAlex Deucher  * @pdev: pci device
498d38ceaf9SAlex Deucher  *
499d38ceaf9SAlex Deucher  * Look up the ATPX handles (all asics).
500d38ceaf9SAlex Deucher  * Returns true if the handles are found, false if not.
501d38ceaf9SAlex Deucher  */
amdgpu_atpx_pci_probe_handle(struct pci_dev * pdev)502d38ceaf9SAlex Deucher static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev)
503d38ceaf9SAlex Deucher {
504d38ceaf9SAlex Deucher 	acpi_handle dhandle, atpx_handle;
505d38ceaf9SAlex Deucher 	acpi_status status;
506d38ceaf9SAlex Deucher 
507d38ceaf9SAlex Deucher 	dhandle = ACPI_HANDLE(&pdev->dev);
508d38ceaf9SAlex Deucher 	if (!dhandle)
509d38ceaf9SAlex Deucher 		return false;
510d38ceaf9SAlex Deucher 
511d38ceaf9SAlex Deucher 	status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
512d38ceaf9SAlex Deucher 	if (ACPI_FAILURE(status)) {
513d38ceaf9SAlex Deucher 		amdgpu_atpx_priv.other_handle = dhandle;
514d38ceaf9SAlex Deucher 		return false;
515d38ceaf9SAlex Deucher 	}
516d38ceaf9SAlex Deucher 	amdgpu_atpx_priv.dhandle = dhandle;
517d38ceaf9SAlex Deucher 	amdgpu_atpx_priv.atpx.handle = atpx_handle;
518d38ceaf9SAlex Deucher 	return true;
519d38ceaf9SAlex Deucher }
520d38ceaf9SAlex Deucher 
521d38ceaf9SAlex Deucher /**
522d38ceaf9SAlex Deucher  * amdgpu_atpx_init - verify the ATPX interface
523d38ceaf9SAlex Deucher  *
524d38ceaf9SAlex Deucher  * Verify the ATPX interface (all asics).
525d38ceaf9SAlex Deucher  * Returns 0 on success, error on failure.
526d38ceaf9SAlex Deucher  */
amdgpu_atpx_init(void)527d38ceaf9SAlex Deucher static int amdgpu_atpx_init(void)
528d38ceaf9SAlex Deucher {
529d38ceaf9SAlex Deucher 	int r;
530d38ceaf9SAlex Deucher 
531d38ceaf9SAlex Deucher 	/* set up the ATPX handle */
532d38ceaf9SAlex Deucher 	r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx);
533d38ceaf9SAlex Deucher 	if (r)
534d38ceaf9SAlex Deucher 		return r;
535d38ceaf9SAlex Deucher 
536d38ceaf9SAlex Deucher 	/* validate the atpx setup */
537d38ceaf9SAlex Deucher 	r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx);
538d38ceaf9SAlex Deucher 	if (r)
539d38ceaf9SAlex Deucher 		return r;
540d38ceaf9SAlex Deucher 
541d38ceaf9SAlex Deucher 	return 0;
542d38ceaf9SAlex Deucher }
543d38ceaf9SAlex Deucher 
544d38ceaf9SAlex Deucher /**
545d38ceaf9SAlex Deucher  * amdgpu_atpx_get_client_id - get the client id
546d38ceaf9SAlex Deucher  *
547d38ceaf9SAlex Deucher  * @pdev: pci device
548d38ceaf9SAlex Deucher  *
549d38ceaf9SAlex Deucher  * look up whether we are the integrated or discrete GPU (all asics).
550d38ceaf9SAlex Deucher  * Returns the client id.
551d38ceaf9SAlex Deucher  */
amdgpu_atpx_get_client_id(struct pci_dev * pdev)55209daf474SLuc Van Oostenryck static enum vga_switcheroo_client_id amdgpu_atpx_get_client_id(struct pci_dev *pdev)
553d38ceaf9SAlex Deucher {
554d38ceaf9SAlex Deucher 	if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev))
555d38ceaf9SAlex Deucher 		return VGA_SWITCHEROO_IGD;
556d38ceaf9SAlex Deucher 	else
557d38ceaf9SAlex Deucher 		return VGA_SWITCHEROO_DIS;
558d38ceaf9SAlex Deucher }
559d38ceaf9SAlex Deucher 
5605d170139SLukas Wunner static const struct vga_switcheroo_handler amdgpu_atpx_handler = {
561d38ceaf9SAlex Deucher 	.switchto = amdgpu_atpx_switchto,
562d38ceaf9SAlex Deucher 	.power_state = amdgpu_atpx_power_state,
563d38ceaf9SAlex Deucher 	.get_client_id = amdgpu_atpx_get_client_id,
564d38ceaf9SAlex Deucher };
565d38ceaf9SAlex Deucher 
566052c2990SAlex Deucher static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = {
567052c2990SAlex Deucher 	/* HG _PR3 doesn't seem to work on this A+A weston board */
568052c2990SAlex Deucher 	{ 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },
569052c2990SAlex Deucher 	{ 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },
5706c24a85dSKai-Heng Feng 	{ 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },
571c7b33cfbSKai-Heng Feng 	{ 0x1002, 0x699f, 0x1028, 0x0814, AMDGPU_PX_QUIRK_FORCE_ATPX },
572b3fc2ab3SAlex Deucher 	{ 0x1002, 0x6900, 0x1025, 0x125A, AMDGPU_PX_QUIRK_FORCE_ATPX },
573f15f3eb2SAlex Deucher 	{ 0x1002, 0x6900, 0x17AA, 0x3806, AMDGPU_PX_QUIRK_FORCE_ATPX },
574052c2990SAlex Deucher 	{ 0, 0, 0, 0, 0 },
575052c2990SAlex Deucher };
576052c2990SAlex Deucher 
amdgpu_atpx_get_quirks(struct pci_dev * pdev)577052c2990SAlex Deucher static void amdgpu_atpx_get_quirks(struct pci_dev *pdev)
578052c2990SAlex Deucher {
579052c2990SAlex Deucher 	const struct amdgpu_px_quirk *p = amdgpu_px_quirk_list;
580052c2990SAlex Deucher 
581052c2990SAlex Deucher 	/* Apply PX quirks */
582052c2990SAlex Deucher 	while (p && p->chip_device != 0) {
583052c2990SAlex Deucher 		if (pdev->vendor == p->chip_vendor &&
584052c2990SAlex Deucher 		    pdev->device == p->chip_device &&
585052c2990SAlex Deucher 		    pdev->subsystem_vendor == p->subsys_vendor &&
586052c2990SAlex Deucher 		    pdev->subsystem_device == p->subsys_device) {
587052c2990SAlex Deucher 			amdgpu_atpx_priv.quirks |= p->px_quirk_flags;
588052c2990SAlex Deucher 			break;
589052c2990SAlex Deucher 		}
590052c2990SAlex Deucher 		++p;
591052c2990SAlex Deucher 	}
592052c2990SAlex Deucher }
593052c2990SAlex Deucher 
594d38ceaf9SAlex Deucher /**
595d38ceaf9SAlex Deucher  * amdgpu_atpx_detect - detect whether we have PX
596d38ceaf9SAlex Deucher  *
597d38ceaf9SAlex Deucher  * Check if we have a PX system (all asics).
598d38ceaf9SAlex Deucher  * Returns true if we have a PX system, false if not.
599d38ceaf9SAlex Deucher  */
amdgpu_atpx_detect(void)600d38ceaf9SAlex Deucher static bool amdgpu_atpx_detect(void)
601d38ceaf9SAlex Deucher {
602d38ceaf9SAlex Deucher 	char acpi_method_name[255] = { 0 };
603d38ceaf9SAlex Deucher 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
604d38ceaf9SAlex Deucher 	struct pci_dev *pdev = NULL;
605d38ceaf9SAlex Deucher 	bool has_atpx = false;
606d38ceaf9SAlex Deucher 	int vga_count = 0;
6077ac33e47SPeter Wu 	bool d3_supported = false;
6087ac33e47SPeter Wu 	struct pci_dev *parent_pdev;
609d38ceaf9SAlex Deucher 
6105d30ed3cSAlex Deucher 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
6115d30ed3cSAlex Deucher 		vga_count++;
6125d30ed3cSAlex Deucher 
6138f00d1fcSZheng Bin 		has_atpx |= amdgpu_atpx_pci_probe_handle(pdev);
6145d30ed3cSAlex Deucher 
6155d30ed3cSAlex Deucher 		parent_pdev = pci_upstream_bridge(pdev);
6165d30ed3cSAlex Deucher 		d3_supported |= parent_pdev && parent_pdev->bridge_d3;
6175d30ed3cSAlex Deucher 		amdgpu_atpx_get_quirks(pdev);
6185d30ed3cSAlex Deucher 	}
6195d30ed3cSAlex Deucher 
6205d30ed3cSAlex Deucher 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
621d38ceaf9SAlex Deucher 		vga_count++;
622d38ceaf9SAlex Deucher 
6238f00d1fcSZheng Bin 		has_atpx |= amdgpu_atpx_pci_probe_handle(pdev);
6247ac33e47SPeter Wu 
6257ac33e47SPeter Wu 		parent_pdev = pci_upstream_bridge(pdev);
6267ac33e47SPeter Wu 		d3_supported |= parent_pdev && parent_pdev->bridge_d3;
627052c2990SAlex Deucher 		amdgpu_atpx_get_quirks(pdev);
628d38ceaf9SAlex Deucher 	}
629d38ceaf9SAlex Deucher 
630d38ceaf9SAlex Deucher 	if (has_atpx && vga_count == 2) {
631d38ceaf9SAlex Deucher 		acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer);
6327ca85295SJoe Perches 		pr_info("vga_switcheroo: detected switching method %s handle\n",
633d38ceaf9SAlex Deucher 			acpi_method_name);
634d38ceaf9SAlex Deucher 		amdgpu_atpx_priv.atpx_detected = true;
6357ac33e47SPeter Wu 		amdgpu_atpx_priv.bridge_pm_usable = d3_supported;
6366b1095eeSAlex Deucher 		amdgpu_atpx_init();
637d38ceaf9SAlex Deucher 		return true;
638d38ceaf9SAlex Deucher 	}
639d38ceaf9SAlex Deucher 	return false;
640d38ceaf9SAlex Deucher }
641d38ceaf9SAlex Deucher 
642d38ceaf9SAlex Deucher /**
643d38ceaf9SAlex Deucher  * amdgpu_register_atpx_handler - register with vga_switcheroo
644d38ceaf9SAlex Deucher  *
645d38ceaf9SAlex Deucher  * Register the PX callbacks with vga_switcheroo (all asics).
646d38ceaf9SAlex Deucher  */
amdgpu_register_atpx_handler(void)647d38ceaf9SAlex Deucher void amdgpu_register_atpx_handler(void)
648d38ceaf9SAlex Deucher {
649d38ceaf9SAlex Deucher 	bool r;
650156d7d41SLukas Wunner 	enum vga_switcheroo_handler_flags_t handler_flags = 0;
651d38ceaf9SAlex Deucher 
652d38ceaf9SAlex Deucher 	/* detect if we have any ATPX + 2 VGA in the system */
653d38ceaf9SAlex Deucher 	r = amdgpu_atpx_detect();
654d38ceaf9SAlex Deucher 	if (!r)
655d38ceaf9SAlex Deucher 		return;
656d38ceaf9SAlex Deucher 
657156d7d41SLukas Wunner 	vga_switcheroo_register_handler(&amdgpu_atpx_handler, handler_flags);
658d38ceaf9SAlex Deucher }
659d38ceaf9SAlex Deucher 
660d38ceaf9SAlex Deucher /**
661d38ceaf9SAlex Deucher  * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo
662d38ceaf9SAlex Deucher  *
663d38ceaf9SAlex Deucher  * Unregister the PX callbacks with vga_switcheroo (all asics).
664d38ceaf9SAlex Deucher  */
amdgpu_unregister_atpx_handler(void)665d38ceaf9SAlex Deucher void amdgpu_unregister_atpx_handler(void)
666d38ceaf9SAlex Deucher {
667d38ceaf9SAlex Deucher 	vga_switcheroo_unregister_handler();
668d38ceaf9SAlex Deucher }
669