1 | /* |
2 | * Copyright (c) 2010 Red Hat Inc. |
3 | * Author : Dave Airlie <airlied@redhat.com> |
4 | * |
5 | * Licensed under GPLv2 |
6 | * |
7 | * ATPX support for both Intel/ATI |
8 | */ |
9 | #include <linux/vga_switcheroo.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/acpi.h> |
12 | #include <linux/pci.h> |
13 | #include <linux/delay.h> |
14 | |
15 | #include "radeon_acpi.h" |
16 | |
17 | struct radeon_atpx_functions { |
18 | bool px_params; |
19 | bool power_cntl; |
20 | bool disp_mux_cntl; |
21 | bool i2c_mux_cntl; |
22 | bool switch_start; |
23 | bool switch_end; |
24 | bool disp_connectors_mapping; |
25 | bool disp_detetion_ports; |
26 | }; |
27 | |
28 | struct radeon_atpx { |
29 | acpi_handle handle; |
30 | struct radeon_atpx_functions functions; |
31 | bool is_hybrid; |
32 | bool dgpu_req_power_for_displays; |
33 | }; |
34 | |
35 | static struct radeon_atpx_priv { |
36 | bool atpx_detected; |
37 | bool bridge_pm_usable; |
38 | /* handle for device - and atpx */ |
39 | acpi_handle dhandle; |
40 | struct radeon_atpx atpx; |
41 | } radeon_atpx_priv; |
42 | |
43 | struct atpx_verify_interface { |
44 | u16 size; /* structure size in bytes (includes size field) */ |
45 | u16 version; /* version */ |
46 | u32 function_bits; /* supported functions bit vector */ |
47 | } __packed; |
48 | |
49 | struct atpx_px_params { |
50 | u16 size; /* structure size in bytes (includes size field) */ |
51 | u32 valid_flags; /* which flags are valid */ |
52 | u32 flags; /* flags */ |
53 | } __packed; |
54 | |
55 | struct atpx_power_control { |
56 | u16 size; |
57 | u8 dgpu_state; |
58 | } __packed; |
59 | |
60 | struct atpx_mux { |
61 | u16 size; |
62 | u16 mux; |
63 | } __packed; |
64 | |
65 | bool radeon_has_atpx(void) { |
66 | return radeon_atpx_priv.atpx_detected; |
67 | } |
68 | |
69 | bool radeon_has_atpx_dgpu_power_cntl(void) { |
70 | return radeon_atpx_priv.atpx.functions.power_cntl; |
71 | } |
72 | |
73 | bool radeon_is_atpx_hybrid(void) { |
74 | return radeon_atpx_priv.atpx.is_hybrid; |
75 | } |
76 | |
77 | bool radeon_atpx_dgpu_req_power_for_displays(void) { |
78 | return radeon_atpx_priv.atpx.dgpu_req_power_for_displays; |
79 | } |
80 | |
81 | /** |
82 | * radeon_atpx_call - call an ATPX method |
83 | * |
84 | * @handle: acpi handle |
85 | * @function: the ATPX function to execute |
86 | * @params: ATPX function params |
87 | * |
88 | * Executes the requested ATPX function (all asics). |
89 | * Returns a pointer to the acpi output buffer. |
90 | */ |
91 | static union acpi_object *radeon_atpx_call(acpi_handle handle, int function, |
92 | struct acpi_buffer *params) |
93 | { |
94 | acpi_status status; |
95 | union acpi_object atpx_arg_elements[2]; |
96 | struct acpi_object_list atpx_arg; |
97 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
98 | |
99 | atpx_arg.count = 2; |
100 | atpx_arg.pointer = &atpx_arg_elements[0]; |
101 | |
102 | atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; |
103 | atpx_arg_elements[0].integer.value = function; |
104 | |
105 | if (params) { |
106 | atpx_arg_elements[1].type = ACPI_TYPE_BUFFER; |
107 | atpx_arg_elements[1].buffer.length = params->length; |
108 | atpx_arg_elements[1].buffer.pointer = params->pointer; |
109 | } else { |
110 | /* We need a second fake parameter */ |
111 | atpx_arg_elements[1].type = ACPI_TYPE_INTEGER; |
112 | atpx_arg_elements[1].integer.value = 0; |
113 | } |
114 | |
115 | status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); |
116 | |
117 | /* Fail only if calling the method fails and ATPX is supported */ |
118 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { |
119 | printk("failed to evaluate ATPX got %s\n" , |
120 | acpi_format_exception(status)); |
121 | kfree(buffer.pointer); |
122 | return NULL; |
123 | } |
124 | |
125 | return buffer.pointer; |
126 | } |
127 | |
128 | /** |
129 | * radeon_atpx_parse_functions - parse supported functions |
130 | * |
131 | * @f: supported functions struct |
132 | * @mask: supported functions mask from ATPX |
133 | * |
134 | * Use the supported functions mask from ATPX function |
135 | * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions |
136 | * are supported (all asics). |
137 | */ |
138 | static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mask) |
139 | { |
140 | f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED; |
141 | f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED; |
142 | f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED; |
143 | f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED; |
144 | f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED; |
145 | f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED; |
146 | f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED; |
147 | f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED; |
148 | } |
149 | |
150 | /** |
151 | * radeon_atpx_validate_functions - validate ATPX functions |
152 | * |
153 | * @atpx: radeon atpx struct |
154 | * |
155 | * Validate that required functions are enabled (all asics). |
156 | * returns 0 on success, error on failure. |
157 | */ |
158 | static int radeon_atpx_validate(struct radeon_atpx *atpx) |
159 | { |
160 | u32 valid_bits = 0; |
161 | |
162 | if (atpx->functions.px_params) { |
163 | union acpi_object *info; |
164 | struct atpx_px_params output; |
165 | size_t size; |
166 | |
167 | info = radeon_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL); |
168 | if (!info) |
169 | return -EIO; |
170 | |
171 | memset(&output, 0, sizeof(output)); |
172 | |
173 | size = *(u16 *) info->buffer.pointer; |
174 | if (size < 10) { |
175 | printk("ATPX buffer is too small: %zu\n" , size); |
176 | kfree(info); |
177 | return -EINVAL; |
178 | } |
179 | size = min(sizeof(output), size); |
180 | |
181 | memcpy(&output, info->buffer.pointer, size); |
182 | |
183 | valid_bits = output.flags & output.valid_flags; |
184 | |
185 | kfree(info); |
186 | } |
187 | |
188 | /* if separate mux flag is set, mux controls are required */ |
189 | if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) { |
190 | atpx->functions.i2c_mux_cntl = true; |
191 | atpx->functions.disp_mux_cntl = true; |
192 | } |
193 | /* if any outputs are muxed, mux controls are required */ |
194 | if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED | |
195 | ATPX_TV_SIGNAL_MUXED | |
196 | ATPX_DFP_SIGNAL_MUXED)) |
197 | atpx->functions.disp_mux_cntl = true; |
198 | |
199 | /* some bioses set these bits rather than flagging power_cntl as supported */ |
200 | if (valid_bits & (ATPX_DYNAMIC_PX_SUPPORTED | |
201 | ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED)) |
202 | atpx->functions.power_cntl = true; |
203 | |
204 | atpx->is_hybrid = false; |
205 | if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) { |
206 | printk("ATPX Hybrid Graphics\n" ); |
207 | /* |
208 | * Disable legacy PM methods only when pcie port PM is usable, |
209 | * otherwise the device might fail to power off or power on. |
210 | */ |
211 | atpx->functions.power_cntl = !radeon_atpx_priv.bridge_pm_usable; |
212 | atpx->is_hybrid = true; |
213 | } |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | /** |
219 | * radeon_atpx_verify_interface - verify ATPX |
220 | * |
221 | * @atpx: radeon atpx struct |
222 | * |
223 | * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function |
224 | * to initialize ATPX and determine what features are supported |
225 | * (all asics). |
226 | * returns 0 on success, error on failure. |
227 | */ |
228 | static int radeon_atpx_verify_interface(struct radeon_atpx *atpx) |
229 | { |
230 | union acpi_object *info; |
231 | struct atpx_verify_interface output; |
232 | size_t size; |
233 | int err = 0; |
234 | |
235 | info = radeon_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL); |
236 | if (!info) |
237 | return -EIO; |
238 | |
239 | memset(&output, 0, sizeof(output)); |
240 | |
241 | size = *(u16 *) info->buffer.pointer; |
242 | if (size < 8) { |
243 | printk("ATPX buffer is too small: %zu\n" , size); |
244 | err = -EINVAL; |
245 | goto out; |
246 | } |
247 | size = min(sizeof(output), size); |
248 | |
249 | memcpy(&output, info->buffer.pointer, size); |
250 | |
251 | /* TODO: check version? */ |
252 | printk("ATPX version %u, functions 0x%08x\n" , |
253 | output.version, output.function_bits); |
254 | |
255 | radeon_atpx_parse_functions(&atpx->functions, output.function_bits); |
256 | |
257 | out: |
258 | kfree(info); |
259 | return err; |
260 | } |
261 | |
262 | /** |
263 | * radeon_atpx_set_discrete_state - power up/down discrete GPU |
264 | * |
265 | * @atpx: atpx info struct |
266 | * @state: discrete GPU state (0 = power down, 1 = power up) |
267 | * |
268 | * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to |
269 | * power down/up the discrete GPU (all asics). |
270 | * Returns 0 on success, error on failure. |
271 | */ |
272 | static int radeon_atpx_set_discrete_state(struct radeon_atpx *atpx, u8 state) |
273 | { |
274 | struct acpi_buffer params; |
275 | union acpi_object *info; |
276 | struct atpx_power_control input; |
277 | |
278 | if (atpx->functions.power_cntl) { |
279 | input.size = 3; |
280 | input.dgpu_state = state; |
281 | params.length = input.size; |
282 | params.pointer = &input; |
283 | info = radeon_atpx_call(atpx->handle, |
284 | ATPX_FUNCTION_POWER_CONTROL, |
285 | ¶ms); |
286 | if (!info) |
287 | return -EIO; |
288 | kfree(info); |
289 | |
290 | /* 200ms delay is required after off */ |
291 | if (state == 0) |
292 | msleep(200); |
293 | } |
294 | return 0; |
295 | } |
296 | |
297 | /** |
298 | * radeon_atpx_switch_disp_mux - switch display mux |
299 | * |
300 | * @atpx: atpx info struct |
301 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
302 | * |
303 | * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to |
304 | * switch the display mux between the discrete GPU and integrated GPU |
305 | * (all asics). |
306 | * Returns 0 on success, error on failure. |
307 | */ |
308 | static int radeon_atpx_switch_disp_mux(struct radeon_atpx *atpx, u16 mux_id) |
309 | { |
310 | struct acpi_buffer params; |
311 | union acpi_object *info; |
312 | struct atpx_mux input; |
313 | |
314 | if (atpx->functions.disp_mux_cntl) { |
315 | input.size = 4; |
316 | input.mux = mux_id; |
317 | params.length = input.size; |
318 | params.pointer = &input; |
319 | info = radeon_atpx_call(atpx->handle, |
320 | ATPX_FUNCTION_DISPLAY_MUX_CONTROL, |
321 | ¶ms); |
322 | if (!info) |
323 | return -EIO; |
324 | kfree(info); |
325 | } |
326 | return 0; |
327 | } |
328 | |
329 | /** |
330 | * radeon_atpx_switch_i2c_mux - switch i2c/hpd mux |
331 | * |
332 | * @atpx: atpx info struct |
333 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
334 | * |
335 | * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to |
336 | * switch the i2c/hpd mux between the discrete GPU and integrated GPU |
337 | * (all asics). |
338 | * Returns 0 on success, error on failure. |
339 | */ |
340 | static int radeon_atpx_switch_i2c_mux(struct radeon_atpx *atpx, u16 mux_id) |
341 | { |
342 | struct acpi_buffer params; |
343 | union acpi_object *info; |
344 | struct atpx_mux input; |
345 | |
346 | if (atpx->functions.i2c_mux_cntl) { |
347 | input.size = 4; |
348 | input.mux = mux_id; |
349 | params.length = input.size; |
350 | params.pointer = &input; |
351 | info = radeon_atpx_call(atpx->handle, |
352 | ATPX_FUNCTION_I2C_MUX_CONTROL, |
353 | ¶ms); |
354 | if (!info) |
355 | return -EIO; |
356 | kfree(info); |
357 | } |
358 | return 0; |
359 | } |
360 | |
361 | /** |
362 | * radeon_atpx_switch_start - notify the sbios of a GPU switch |
363 | * |
364 | * @atpx: atpx info struct |
365 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
366 | * |
367 | * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX |
368 | * function to notify the sbios that a switch between the discrete GPU and |
369 | * integrated GPU has begun (all asics). |
370 | * Returns 0 on success, error on failure. |
371 | */ |
372 | static int radeon_atpx_switch_start(struct radeon_atpx *atpx, u16 mux_id) |
373 | { |
374 | struct acpi_buffer params; |
375 | union acpi_object *info; |
376 | struct atpx_mux input; |
377 | |
378 | if (atpx->functions.switch_start) { |
379 | input.size = 4; |
380 | input.mux = mux_id; |
381 | params.length = input.size; |
382 | params.pointer = &input; |
383 | info = radeon_atpx_call(atpx->handle, |
384 | ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION, |
385 | ¶ms); |
386 | if (!info) |
387 | return -EIO; |
388 | kfree(info); |
389 | } |
390 | return 0; |
391 | } |
392 | |
393 | /** |
394 | * radeon_atpx_switch_end - notify the sbios of a GPU switch |
395 | * |
396 | * @atpx: atpx info struct |
397 | * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) |
398 | * |
399 | * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX |
400 | * function to notify the sbios that a switch between the discrete GPU and |
401 | * integrated GPU has ended (all asics). |
402 | * Returns 0 on success, error on failure. |
403 | */ |
404 | static int radeon_atpx_switch_end(struct radeon_atpx *atpx, u16 mux_id) |
405 | { |
406 | struct acpi_buffer params; |
407 | union acpi_object *info; |
408 | struct atpx_mux input; |
409 | |
410 | if (atpx->functions.switch_end) { |
411 | input.size = 4; |
412 | input.mux = mux_id; |
413 | params.length = input.size; |
414 | params.pointer = &input; |
415 | info = radeon_atpx_call(atpx->handle, |
416 | ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION, |
417 | ¶ms); |
418 | if (!info) |
419 | return -EIO; |
420 | kfree(info); |
421 | } |
422 | return 0; |
423 | } |
424 | |
425 | /** |
426 | * radeon_atpx_switchto - switch to the requested GPU |
427 | * |
428 | * @id: GPU to switch to |
429 | * |
430 | * Execute the necessary ATPX functions to switch between the discrete GPU and |
431 | * integrated GPU (all asics). |
432 | * Returns 0 on success, error on failure. |
433 | */ |
434 | static int radeon_atpx_switchto(enum vga_switcheroo_client_id id) |
435 | { |
436 | u16 gpu_id; |
437 | |
438 | if (id == VGA_SWITCHEROO_IGD) |
439 | gpu_id = ATPX_INTEGRATED_GPU; |
440 | else |
441 | gpu_id = ATPX_DISCRETE_GPU; |
442 | |
443 | radeon_atpx_switch_start(&radeon_atpx_priv.atpx, gpu_id); |
444 | radeon_atpx_switch_disp_mux(&radeon_atpx_priv.atpx, gpu_id); |
445 | radeon_atpx_switch_i2c_mux(&radeon_atpx_priv.atpx, gpu_id); |
446 | radeon_atpx_switch_end(&radeon_atpx_priv.atpx, gpu_id); |
447 | |
448 | return 0; |
449 | } |
450 | |
451 | /** |
452 | * radeon_atpx_power_state - power down/up the requested GPU |
453 | * |
454 | * @id: GPU to power down/up |
455 | * @state: requested power state (0 = off, 1 = on) |
456 | * |
457 | * Execute the necessary ATPX function to power down/up the discrete GPU |
458 | * (all asics). |
459 | * Returns 0 on success, error on failure. |
460 | */ |
461 | static int radeon_atpx_power_state(enum vga_switcheroo_client_id id, |
462 | enum vga_switcheroo_state state) |
463 | { |
464 | /* on w500 ACPI can't change intel gpu state */ |
465 | if (id == VGA_SWITCHEROO_IGD) |
466 | return 0; |
467 | |
468 | radeon_atpx_set_discrete_state(&radeon_atpx_priv.atpx, state); |
469 | return 0; |
470 | } |
471 | |
472 | /** |
473 | * radeon_atpx_pci_probe_handle - look up the ATPX handle |
474 | * |
475 | * @pdev: pci device |
476 | * |
477 | * Look up the ATPX handles (all asics). |
478 | * Returns true if the handles are found, false if not. |
479 | */ |
480 | static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) |
481 | { |
482 | acpi_handle dhandle, atpx_handle; |
483 | acpi_status status; |
484 | |
485 | dhandle = ACPI_HANDLE(&pdev->dev); |
486 | if (!dhandle) |
487 | return false; |
488 | |
489 | status = acpi_get_handle(dhandle, "ATPX" , &atpx_handle); |
490 | if (ACPI_FAILURE(status)) |
491 | return false; |
492 | |
493 | radeon_atpx_priv.dhandle = dhandle; |
494 | radeon_atpx_priv.atpx.handle = atpx_handle; |
495 | return true; |
496 | } |
497 | |
498 | /** |
499 | * radeon_atpx_init - verify the ATPX interface |
500 | * |
501 | * Verify the ATPX interface (all asics). |
502 | * Returns 0 on success, error on failure. |
503 | */ |
504 | static int radeon_atpx_init(void) |
505 | { |
506 | int r; |
507 | |
508 | /* set up the ATPX handle */ |
509 | r = radeon_atpx_verify_interface(&radeon_atpx_priv.atpx); |
510 | if (r) |
511 | return r; |
512 | |
513 | /* validate the atpx setup */ |
514 | r = radeon_atpx_validate(&radeon_atpx_priv.atpx); |
515 | if (r) |
516 | return r; |
517 | |
518 | return 0; |
519 | } |
520 | |
521 | /** |
522 | * radeon_atpx_get_client_id - get the client id |
523 | * |
524 | * @pdev: pci device |
525 | * |
526 | * look up whether we are the integrated or discrete GPU (all asics). |
527 | * Returns the client id. |
528 | */ |
529 | static enum vga_switcheroo_client_id radeon_atpx_get_client_id(struct pci_dev *pdev) |
530 | { |
531 | if (radeon_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev)) |
532 | return VGA_SWITCHEROO_IGD; |
533 | else |
534 | return VGA_SWITCHEROO_DIS; |
535 | } |
536 | |
537 | static const struct vga_switcheroo_handler radeon_atpx_handler = { |
538 | .switchto = radeon_atpx_switchto, |
539 | .power_state = radeon_atpx_power_state, |
540 | .get_client_id = radeon_atpx_get_client_id, |
541 | }; |
542 | |
543 | /** |
544 | * radeon_atpx_detect - detect whether we have PX |
545 | * |
546 | * Check if we have a PX system (all asics). |
547 | * Returns true if we have a PX system, false if not. |
548 | */ |
549 | static bool radeon_atpx_detect(void) |
550 | { |
551 | char acpi_method_name[255] = { 0 }; |
552 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; |
553 | struct pci_dev *pdev = NULL; |
554 | bool has_atpx = false; |
555 | int vga_count = 0; |
556 | bool d3_supported = false; |
557 | struct pci_dev *parent_pdev; |
558 | |
559 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { |
560 | vga_count++; |
561 | |
562 | has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true); |
563 | |
564 | parent_pdev = pci_upstream_bridge(pdev); |
565 | d3_supported |= parent_pdev && parent_pdev->bridge_d3; |
566 | } |
567 | |
568 | /* some newer PX laptops mark the dGPU as a non-VGA display device */ |
569 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { |
570 | vga_count++; |
571 | |
572 | has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true); |
573 | |
574 | parent_pdev = pci_upstream_bridge(pdev); |
575 | d3_supported |= parent_pdev && parent_pdev->bridge_d3; |
576 | } |
577 | |
578 | if (has_atpx && vga_count == 2) { |
579 | acpi_get_name(radeon_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer); |
580 | pr_info("vga_switcheroo: detected switching method %s handle\n" , |
581 | acpi_method_name); |
582 | radeon_atpx_priv.atpx_detected = true; |
583 | radeon_atpx_priv.bridge_pm_usable = d3_supported; |
584 | radeon_atpx_init(); |
585 | return true; |
586 | } |
587 | return false; |
588 | } |
589 | |
590 | /** |
591 | * radeon_register_atpx_handler - register with vga_switcheroo |
592 | * |
593 | * Register the PX callbacks with vga_switcheroo (all asics). |
594 | */ |
595 | void radeon_register_atpx_handler(void) |
596 | { |
597 | bool r; |
598 | enum vga_switcheroo_handler_flags_t handler_flags = 0; |
599 | |
600 | /* detect if we have any ATPX + 2 VGA in the system */ |
601 | r = radeon_atpx_detect(); |
602 | if (!r) |
603 | return; |
604 | |
605 | vga_switcheroo_register_handler(&radeon_atpx_handler, handler_flags); |
606 | } |
607 | |
608 | /** |
609 | * radeon_unregister_atpx_handler - unregister with vga_switcheroo |
610 | * |
611 | * Unregister the PX callbacks with vga_switcheroo (all asics). |
612 | */ |
613 | void radeon_unregister_atpx_handler(void) |
614 | { |
615 | vga_switcheroo_unregister_handler(); |
616 | } |
617 | |