1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2010 Red Hat Inc. |
4 | * Author : Dave Airlie <airlied@redhat.com> |
5 | * |
6 | * ATPX support for both Intel/ATI |
7 | */ |
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 | |
19 | struct 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 | |
27 | struct 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 | |
38 | struct 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 | |
45 | static 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 | |
55 | struct 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 | |
61 | struct 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 | |
67 | struct atpx_power_control { |
68 | u16 size; |
69 | u8 dgpu_state; |
70 | } __packed; |
71 | |
72 | struct atpx_mux { |
73 | u16 size; |
74 | u16 mux; |
75 | } __packed; |
76 | |
77 | bool amdgpu_has_atpx(void) |
78 | { |
79 | return amdgpu_atpx_priv.atpx_detected; |
80 | } |
81 | |
82 | bool amdgpu_has_atpx_dgpu_power_cntl(void) |
83 | { |
84 | return amdgpu_atpx_priv.atpx.functions.power_cntl; |
85 | } |
86 | |
87 | bool amdgpu_is_atpx_hybrid(void) |
88 | { |
89 | return amdgpu_atpx_priv.atpx.is_hybrid; |
90 | } |
91 | |
92 | bool 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) |
98 | void *amdgpu_atpx_get_dhandle(void) |
99 | { |
100 | return amdgpu_atpx_priv.dhandle; |
101 | } |
102 | #endif |
103 | |
104 | /** |
105 | * amdgpu_atpx_call - call an ATPX method |
106 | * |
107 | * @handle: acpi handle |
108 | * @function: the ATPX function to execute |
109 | * @params: ATPX function params |
110 | * |
111 | * Executes the requested ATPX function (all asics). |
112 | * Returns a pointer to the acpi output buffer. |
113 | */ |
114 | static 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(object: handle, NULL, parameter_objects: &atpx_arg, return_object_buffer: &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(objp: buffer.pointer); |
145 | return NULL; |
146 | } |
147 | |
148 | return buffer.pointer; |
149 | } |
150 | |
151 | /** |
152 | * amdgpu_atpx_parse_functions - parse supported functions |
153 | * |
154 | * @f: supported functions struct |
155 | * @mask: supported functions mask from ATPX |
156 | * |
157 | * Use the supported functions mask from ATPX function |
158 | * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions |
159 | * are supported (all asics). |
160 | */ |
161 | static 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 functions |
175 | * |
176 | * @atpx: amdgpu atpx struct |
177 | * |
178 | * Validate that required functions are enabled (all asics). |
179 | * returns 0 on success, error on failure. |
180 | */ |
181 | static 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(handle: 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(objp: 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(objp: 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 ATPX |
254 | * |
255 | * @atpx: amdgpu atpx struct |
256 | * |
257 | * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function |
258 | * to initialize ATPX and determine what features are supported |
259 | * (all asics). |
260 | * returns 0 on success, error on failure. |
261 | */ |
262 | static 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(handle: 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(f: &atpx->functions, mask: output.function_bits); |
290 | |
291 | out: |
292 | kfree(objp: info); |
293 | return err; |
294 | } |
295 | |
296 | /** |
297 | * amdgpu_atpx_set_discrete_state - power up/down discrete GPU |
298 | * |
299 | * @atpx: atpx info struct |
300 | * @state: discrete GPU state (0 = power down, 1 = power up) |
301 | * |
302 | * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to |
303 | * power down/up the discrete GPU (all asics). |
304 | * Returns 0 on success, error on failure. |
305 | */ |
306 | static 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(handle: atpx->handle, |
318 | ATPX_FUNCTION_POWER_CONTROL, |
319 | params: ¶ms); |
320 | if (!info) |
321 | return -EIO; |
322 | kfree(objp: info); |
323 | |
324 | /* 200ms delay is required after off */ |
325 | if (state == 0) |
326 | msleep(msecs: 200); |
327 | } |
328 | return 0; |
329 | } |
330 | |
331 | /** |
332 | * amdgpu_atpx_switch_disp_mux - switch display mux |
333 | * |
334 | * @atpx: atpx info struct |
335 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
336 | * |
337 | * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to |
338 | * switch the display mux between the discrete GPU and integrated GPU |
339 | * (all asics). |
340 | * Returns 0 on success, error on failure. |
341 | */ |
342 | static 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(handle: atpx->handle, |
354 | ATPX_FUNCTION_DISPLAY_MUX_CONTROL, |
355 | params: ¶ms); |
356 | if (!info) |
357 | return -EIO; |
358 | kfree(objp: info); |
359 | } |
360 | return 0; |
361 | } |
362 | |
363 | /** |
364 | * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux |
365 | * |
366 | * @atpx: atpx info struct |
367 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
368 | * |
369 | * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to |
370 | * switch the i2c/hpd mux between the discrete GPU and integrated GPU |
371 | * (all asics). |
372 | * Returns 0 on success, error on failure. |
373 | */ |
374 | static 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(handle: atpx->handle, |
386 | ATPX_FUNCTION_I2C_MUX_CONTROL, |
387 | params: ¶ms); |
388 | if (!info) |
389 | return -EIO; |
390 | kfree(objp: info); |
391 | } |
392 | return 0; |
393 | } |
394 | |
395 | /** |
396 | * amdgpu_atpx_switch_start - notify the sbios of a GPU switch |
397 | * |
398 | * @atpx: atpx info struct |
399 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
400 | * |
401 | * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX |
402 | * function to notify the sbios that a switch between the discrete GPU and |
403 | * integrated GPU has begun (all asics). |
404 | * Returns 0 on success, error on failure. |
405 | */ |
406 | static 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(handle: atpx->handle, |
418 | ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION, |
419 | params: ¶ms); |
420 | if (!info) |
421 | return -EIO; |
422 | kfree(objp: info); |
423 | } |
424 | return 0; |
425 | } |
426 | |
427 | /** |
428 | * amdgpu_atpx_switch_end - notify the sbios of a GPU switch |
429 | * |
430 | * @atpx: atpx info struct |
431 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
432 | * |
433 | * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX |
434 | * function to notify the sbios that a switch between the discrete GPU and |
435 | * integrated GPU has ended (all asics). |
436 | * Returns 0 on success, error on failure. |
437 | */ |
438 | static 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(handle: atpx->handle, |
450 | ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION, |
451 | params: ¶ms); |
452 | if (!info) |
453 | return -EIO; |
454 | kfree(objp: info); |
455 | } |
456 | return 0; |
457 | } |
458 | |
459 | /** |
460 | * amdgpu_atpx_switchto - switch to the requested GPU |
461 | * |
462 | * @id: GPU to switch to |
463 | * |
464 | * Execute the necessary ATPX functions to switch between the discrete GPU and |
465 | * integrated GPU (all asics). |
466 | * Returns 0 on success, error on failure. |
467 | */ |
468 | static 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 | else |
475 | gpu_id = ATPX_DISCRETE_GPU; |
476 | |
477 | amdgpu_atpx_switch_start(atpx: &amdgpu_atpx_priv.atpx, mux_id: gpu_id); |
478 | amdgpu_atpx_switch_disp_mux(atpx: &amdgpu_atpx_priv.atpx, mux_id: gpu_id); |
479 | amdgpu_atpx_switch_i2c_mux(atpx: &amdgpu_atpx_priv.atpx, mux_id: gpu_id); |
480 | amdgpu_atpx_switch_end(atpx: &amdgpu_atpx_priv.atpx, mux_id: gpu_id); |
481 | |
482 | return 0; |
483 | } |
484 | |
485 | /** |
486 | * amdgpu_atpx_power_state - power down/up the requested GPU |
487 | * |
488 | * @id: GPU to power down/up |
489 | * @state: requested power state (0 = off, 1 = on) |
490 | * |
491 | * Execute the necessary ATPX function to power down/up the discrete GPU |
492 | * (all asics). |
493 | * Returns 0 on success, error on failure. |
494 | */ |
495 | static 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(atpx: &amdgpu_atpx_priv.atpx, state); |
503 | return 0; |
504 | } |
505 | |
506 | /** |
507 | * amdgpu_atpx_pci_probe_handle - look up the ATPX handle |
508 | * |
509 | * @pdev: pci device |
510 | * |
511 | * Look up the ATPX handles (all asics). |
512 | * Returns true if the handles are found, false if not. |
513 | */ |
514 | static 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(parent: dhandle, pathname: "ATPX" , ret_handle: &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 interface |
535 | * |
536 | * Verify the ATPX interface (all asics). |
537 | * Returns 0 on success, error on failure. |
538 | */ |
539 | static int amdgpu_atpx_init(void) |
540 | { |
541 | int r; |
542 | |
543 | /* set up the ATPX handle */ |
544 | r = amdgpu_atpx_verify_interface(atpx: &amdgpu_atpx_priv.atpx); |
545 | if (r) |
546 | return r; |
547 | |
548 | /* validate the atpx setup */ |
549 | r = amdgpu_atpx_validate(atpx: &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 id |
558 | * |
559 | * @pdev: pci device |
560 | * |
561 | * look up whether we are the integrated or discrete GPU (all asics). |
562 | * Returns the client id. |
563 | */ |
564 | static 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 | else |
569 | return VGA_SWITCHEROO_DIS; |
570 | } |
571 | |
572 | static 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 | |
578 | static 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 | |
589 | static 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 PX |
608 | * |
609 | * Check if we have a PX system (all asics). |
610 | * Returns true if we have a PX system, false if not. |
611 | */ |
612 | static 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, from: pdev)) != NULL) { |
623 | vga_count++; |
624 | |
625 | has_atpx |= amdgpu_atpx_pci_probe_handle(pdev); |
626 | |
627 | parent_pdev = pci_upstream_bridge(dev: 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, from: pdev)) != NULL) { |
633 | vga_count++; |
634 | |
635 | has_atpx |= amdgpu_atpx_pci_probe_handle(pdev); |
636 | |
637 | parent_pdev = pci_upstream_bridge(dev: 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(object: amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, ret_path_ptr: &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_switcheroo |
656 | * |
657 | * Register the PX callbacks with vga_switcheroo (all asics). |
658 | */ |
659 | void 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(handler: &amdgpu_atpx_handler, handler_flags); |
670 | } |
671 | |
672 | /** |
673 | * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo |
674 | * |
675 | * Unregister the PX callbacks with vga_switcheroo (all asics). |
676 | */ |
677 | void amdgpu_unregister_atpx_handler(void) |
678 | { |
679 | vga_switcheroo_unregister_handler(); |
680 | } |
681 | |