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 ¶ms);
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 ¶ms);
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 ¶ms);
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 ¶ms);
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 ¶ms);
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