drm/amdgpu: add new device to use atpx quirk
[sfrench/cifs-2.6.git] / drivers / gpu / drm / amd / amdgpu / amdgpu_atpx_handler.c
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 "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_detetion_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         return amdgpu_atpx_priv.atpx_detected;
79 }
80
81 bool amdgpu_has_atpx_dgpu_power_cntl(void) {
82         return amdgpu_atpx_priv.atpx.functions.power_cntl;
83 }
84
85 bool amdgpu_is_atpx_hybrid(void) {
86         return amdgpu_atpx_priv.atpx.is_hybrid;
87 }
88
89 bool amdgpu_atpx_dgpu_req_power_for_displays(void) {
90         return amdgpu_atpx_priv.atpx.dgpu_req_power_for_displays;
91 }
92
93 /**
94  * amdgpu_atpx_call - call an ATPX method
95  *
96  * @handle: acpi handle
97  * @function: the ATPX function to execute
98  * @params: ATPX function params
99  *
100  * Executes the requested ATPX function (all asics).
101  * Returns a pointer to the acpi output buffer.
102  */
103 static union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function,
104                                            struct acpi_buffer *params)
105 {
106         acpi_status status;
107         union acpi_object atpx_arg_elements[2];
108         struct acpi_object_list atpx_arg;
109         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
110
111         atpx_arg.count = 2;
112         atpx_arg.pointer = &atpx_arg_elements[0];
113
114         atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
115         atpx_arg_elements[0].integer.value = function;
116
117         if (params) {
118                 atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;
119                 atpx_arg_elements[1].buffer.length = params->length;
120                 atpx_arg_elements[1].buffer.pointer = params->pointer;
121         } else {
122                 /* We need a second fake parameter */
123                 atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;
124                 atpx_arg_elements[1].integer.value = 0;
125         }
126
127         status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
128
129         /* Fail only if calling the method fails and ATPX is supported */
130         if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
131                 printk("failed to evaluate ATPX got %s\n",
132                        acpi_format_exception(status));
133                 kfree(buffer.pointer);
134                 return NULL;
135         }
136
137         return buffer.pointer;
138 }
139
140 /**
141  * amdgpu_atpx_parse_functions - parse supported functions
142  *
143  * @f: supported functions struct
144  * @mask: supported functions mask from ATPX
145  *
146  * Use the supported functions mask from ATPX function
147  * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions
148  * are supported (all asics).
149  */
150 static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mask)
151 {
152         f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED;
153         f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED;
154         f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED;
155         f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED;
156         f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED;
157         f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED;
158         f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED;
159         f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED;
160 }
161
162 /**
163  * amdgpu_atpx_validate_functions - validate ATPX functions
164  *
165  * @atpx: amdgpu atpx struct
166  *
167  * Validate that required functions are enabled (all asics).
168  * returns 0 on success, error on failure.
169  */
170 static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
171 {
172         u32 valid_bits = 0;
173
174         if (atpx->functions.px_params) {
175                 union acpi_object *info;
176                 struct atpx_px_params output;
177                 size_t size;
178
179                 info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL);
180                 if (!info)
181                         return -EIO;
182
183                 memset(&output, 0, sizeof(output));
184
185                 size = *(u16 *) info->buffer.pointer;
186                 if (size < 10) {
187                         printk("ATPX buffer is too small: %zu\n", size);
188                         kfree(info);
189                         return -EINVAL;
190                 }
191                 size = min(sizeof(output), size);
192
193                 memcpy(&output, info->buffer.pointer, size);
194
195                 valid_bits = output.flags & output.valid_flags;
196
197                 kfree(info);
198         }
199
200         /* if separate mux flag is set, mux controls are required */
201         if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {
202                 atpx->functions.i2c_mux_cntl = true;
203                 atpx->functions.disp_mux_cntl = true;
204         }
205         /* if any outputs are muxed, mux controls are required */
206         if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |
207                           ATPX_TV_SIGNAL_MUXED |
208                           ATPX_DFP_SIGNAL_MUXED))
209                 atpx->functions.disp_mux_cntl = true;
210
211
212         /* some bioses set these bits rather than flagging power_cntl as supported */
213         if (valid_bits & (ATPX_DYNAMIC_PX_SUPPORTED |
214                           ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED))
215                 atpx->functions.power_cntl = true;
216
217         atpx->is_hybrid = false;
218         if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) {
219                 if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {
220                         printk("ATPX Hybrid Graphics, forcing to ATPX\n");
221                         atpx->functions.power_cntl = true;
222                         atpx->is_hybrid = false;
223                 } else {
224                         printk("ATPX Hybrid Graphics\n");
225                         /*
226                          * Disable legacy PM methods only when pcie port PM is usable,
227                          * otherwise the device might fail to power off or power on.
228                          */
229                         atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
230                         atpx->is_hybrid = true;
231                 }
232         }
233
234         atpx->dgpu_req_power_for_displays = false;
235         if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)
236                 atpx->dgpu_req_power_for_displays = true;
237
238         return 0;
239 }
240
241 /**
242  * amdgpu_atpx_verify_interface - verify ATPX
243  *
244  * @atpx: amdgpu atpx struct
245  *
246  * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function
247  * to initialize ATPX and determine what features are supported
248  * (all asics).
249  * returns 0 on success, error on failure.
250  */
251 static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
252 {
253         union acpi_object *info;
254         struct atpx_verify_interface output;
255         size_t size;
256         int err = 0;
257
258         info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL);
259         if (!info)
260                 return -EIO;
261
262         memset(&output, 0, sizeof(output));
263
264         size = *(u16 *) info->buffer.pointer;
265         if (size < 8) {
266                 printk("ATPX buffer is too small: %zu\n", size);
267                 err = -EINVAL;
268                 goto out;
269         }
270         size = min(sizeof(output), size);
271
272         memcpy(&output, info->buffer.pointer, size);
273
274         /* TODO: check version? */
275         printk("ATPX version %u, functions 0x%08x\n",
276                output.version, output.function_bits);
277
278         amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits);
279
280 out:
281         kfree(info);
282         return err;
283 }
284
285 /**
286  * amdgpu_atpx_set_discrete_state - power up/down discrete GPU
287  *
288  * @atpx: atpx info struct
289  * @state: discrete GPU state (0 = power down, 1 = power up)
290  *
291  * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to
292  * power down/up the discrete GPU (all asics).
293  * Returns 0 on success, error on failure.
294  */
295 static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state)
296 {
297         struct acpi_buffer params;
298         union acpi_object *info;
299         struct atpx_power_control input;
300
301         if (atpx->functions.power_cntl) {
302                 input.size = 3;
303                 input.dgpu_state = state;
304                 params.length = input.size;
305                 params.pointer = &input;
306                 info = amdgpu_atpx_call(atpx->handle,
307                                         ATPX_FUNCTION_POWER_CONTROL,
308                                         &params);
309                 if (!info)
310                         return -EIO;
311                 kfree(info);
312
313                 /* 200ms delay is required after off */
314                 if (state == 0)
315                         msleep(200);
316         }
317         return 0;
318 }
319
320 /**
321  * amdgpu_atpx_switch_disp_mux - switch display mux
322  *
323  * @atpx: atpx info struct
324  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
325  *
326  * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to
327  * switch the display mux between the discrete GPU and integrated GPU
328  * (all asics).
329  * Returns 0 on success, error on failure.
330  */
331 static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id)
332 {
333         struct acpi_buffer params;
334         union acpi_object *info;
335         struct atpx_mux input;
336
337         if (atpx->functions.disp_mux_cntl) {
338                 input.size = 4;
339                 input.mux = mux_id;
340                 params.length = input.size;
341                 params.pointer = &input;
342                 info = amdgpu_atpx_call(atpx->handle,
343                                         ATPX_FUNCTION_DISPLAY_MUX_CONTROL,
344                                         &params);
345                 if (!info)
346                         return -EIO;
347                 kfree(info);
348         }
349         return 0;
350 }
351
352 /**
353  * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux
354  *
355  * @atpx: atpx info struct
356  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
357  *
358  * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to
359  * switch the i2c/hpd mux between the discrete GPU and integrated GPU
360  * (all asics).
361  * Returns 0 on success, error on failure.
362  */
363 static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id)
364 {
365         struct acpi_buffer params;
366         union acpi_object *info;
367         struct atpx_mux input;
368
369         if (atpx->functions.i2c_mux_cntl) {
370                 input.size = 4;
371                 input.mux = mux_id;
372                 params.length = input.size;
373                 params.pointer = &input;
374                 info = amdgpu_atpx_call(atpx->handle,
375                                         ATPX_FUNCTION_I2C_MUX_CONTROL,
376                                         &params);
377                 if (!info)
378                         return -EIO;
379                 kfree(info);
380         }
381         return 0;
382 }
383
384 /**
385  * amdgpu_atpx_switch_start - notify the sbios of a GPU switch
386  *
387  * @atpx: atpx info struct
388  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
389  *
390  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX
391  * function to notify the sbios that a switch between the discrete GPU and
392  * integrated GPU has begun (all asics).
393  * Returns 0 on success, error on failure.
394  */
395 static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id)
396 {
397         struct acpi_buffer params;
398         union acpi_object *info;
399         struct atpx_mux input;
400
401         if (atpx->functions.switch_start) {
402                 input.size = 4;
403                 input.mux = mux_id;
404                 params.length = input.size;
405                 params.pointer = &input;
406                 info = amdgpu_atpx_call(atpx->handle,
407                                         ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,
408                                         &params);
409                 if (!info)
410                         return -EIO;
411                 kfree(info);
412         }
413         return 0;
414 }
415
416 /**
417  * amdgpu_atpx_switch_end - notify the sbios of a GPU switch
418  *
419  * @atpx: atpx info struct
420  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
421  *
422  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX
423  * function to notify the sbios that a switch between the discrete GPU and
424  * integrated GPU has ended (all asics).
425  * Returns 0 on success, error on failure.
426  */
427 static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id)
428 {
429         struct acpi_buffer params;
430         union acpi_object *info;
431         struct atpx_mux input;
432
433         if (atpx->functions.switch_end) {
434                 input.size = 4;
435                 input.mux = mux_id;
436                 params.length = input.size;
437                 params.pointer = &input;
438                 info = amdgpu_atpx_call(atpx->handle,
439                                         ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,
440                                         &params);
441                 if (!info)
442                         return -EIO;
443                 kfree(info);
444         }
445         return 0;
446 }
447
448 /**
449  * amdgpu_atpx_switchto - switch to the requested GPU
450  *
451  * @id: GPU to switch to
452  *
453  * Execute the necessary ATPX functions to switch between the discrete GPU and
454  * integrated GPU (all asics).
455  * Returns 0 on success, error on failure.
456  */
457 static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)
458 {
459         u16 gpu_id;
460
461         if (id == VGA_SWITCHEROO_IGD)
462                 gpu_id = ATPX_INTEGRATED_GPU;
463         else
464                 gpu_id = ATPX_DISCRETE_GPU;
465
466         amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id);
467         amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id);
468         amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id);
469         amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id);
470
471         return 0;
472 }
473
474 /**
475  * amdgpu_atpx_power_state - power down/up the requested GPU
476  *
477  * @id: GPU to power down/up
478  * @state: requested power state (0 = off, 1 = on)
479  *
480  * Execute the necessary ATPX function to power down/up the discrete GPU
481  * (all asics).
482  * Returns 0 on success, error on failure.
483  */
484 static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id,
485                                    enum vga_switcheroo_state state)
486 {
487         /* on w500 ACPI can't change intel gpu state */
488         if (id == VGA_SWITCHEROO_IGD)
489                 return 0;
490
491         amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state);
492         return 0;
493 }
494
495 /**
496  * amdgpu_atpx_pci_probe_handle - look up the ATPX handle
497  *
498  * @pdev: pci device
499  *
500  * Look up the ATPX handles (all asics).
501  * Returns true if the handles are found, false if not.
502  */
503 static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev)
504 {
505         acpi_handle dhandle, atpx_handle;
506         acpi_status status;
507
508         dhandle = ACPI_HANDLE(&pdev->dev);
509         if (!dhandle)
510                 return false;
511
512         status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
513         if (ACPI_FAILURE(status)) {
514                 amdgpu_atpx_priv.other_handle = dhandle;
515                 return false;
516         }
517         amdgpu_atpx_priv.dhandle = dhandle;
518         amdgpu_atpx_priv.atpx.handle = atpx_handle;
519         return true;
520 }
521
522 /**
523  * amdgpu_atpx_init - verify the ATPX interface
524  *
525  * Verify the ATPX interface (all asics).
526  * Returns 0 on success, error on failure.
527  */
528 static int amdgpu_atpx_init(void)
529 {
530         int r;
531
532         /* set up the ATPX handle */
533         r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx);
534         if (r)
535                 return r;
536
537         /* validate the atpx setup */
538         r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx);
539         if (r)
540                 return r;
541
542         return 0;
543 }
544
545 /**
546  * amdgpu_atpx_get_client_id - get the client id
547  *
548  * @pdev: pci device
549  *
550  * look up whether we are the integrated or discrete GPU (all asics).
551  * Returns the client id.
552  */
553 static int amdgpu_atpx_get_client_id(struct pci_dev *pdev)
554 {
555         if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev))
556                 return VGA_SWITCHEROO_IGD;
557         else
558                 return VGA_SWITCHEROO_DIS;
559 }
560
561 static const struct vga_switcheroo_handler amdgpu_atpx_handler = {
562         .switchto = amdgpu_atpx_switchto,
563         .power_state = amdgpu_atpx_power_state,
564         .get_client_id = amdgpu_atpx_get_client_id,
565 };
566
567 static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = {
568         /* HG _PR3 doesn't seem to work on this A+A weston board */
569         { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },
570         { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },
571         { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },
572         { 0, 0, 0, 0, 0 },
573 };
574
575 static void amdgpu_atpx_get_quirks(struct pci_dev *pdev)
576 {
577         const struct amdgpu_px_quirk *p = amdgpu_px_quirk_list;
578
579         /* Apply PX quirks */
580         while (p && p->chip_device != 0) {
581                 if (pdev->vendor == p->chip_vendor &&
582                     pdev->device == p->chip_device &&
583                     pdev->subsystem_vendor == p->subsys_vendor &&
584                     pdev->subsystem_device == p->subsys_device) {
585                         amdgpu_atpx_priv.quirks |= p->px_quirk_flags;
586                         break;
587                 }
588                 ++p;
589         }
590 }
591
592 /**
593  * amdgpu_atpx_detect - detect whether we have PX
594  *
595  * Check if we have a PX system (all asics).
596  * Returns true if we have a PX system, false if not.
597  */
598 static bool amdgpu_atpx_detect(void)
599 {
600         char acpi_method_name[255] = { 0 };
601         struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
602         struct pci_dev *pdev = NULL;
603         bool has_atpx = false;
604         int vga_count = 0;
605         bool d3_supported = false;
606         struct pci_dev *parent_pdev;
607
608         while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
609                 vga_count++;
610
611                 has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true);
612
613                 parent_pdev = pci_upstream_bridge(pdev);
614                 d3_supported |= parent_pdev && parent_pdev->bridge_d3;
615                 amdgpu_atpx_get_quirks(pdev);
616         }
617
618         while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
619                 vga_count++;
620
621                 has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true);
622
623                 parent_pdev = pci_upstream_bridge(pdev);
624                 d3_supported |= parent_pdev && parent_pdev->bridge_d3;
625                 amdgpu_atpx_get_quirks(pdev);
626         }
627
628         if (has_atpx && vga_count == 2) {
629                 acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer);
630                 pr_info("vga_switcheroo: detected switching method %s handle\n",
631                         acpi_method_name);
632                 amdgpu_atpx_priv.atpx_detected = true;
633                 amdgpu_atpx_priv.bridge_pm_usable = d3_supported;
634                 amdgpu_atpx_init();
635                 return true;
636         }
637         return false;
638 }
639
640 /**
641  * amdgpu_register_atpx_handler - register with vga_switcheroo
642  *
643  * Register the PX callbacks with vga_switcheroo (all asics).
644  */
645 void amdgpu_register_atpx_handler(void)
646 {
647         bool r;
648         enum vga_switcheroo_handler_flags_t handler_flags = 0;
649
650         /* detect if we have any ATPX + 2 VGA in the system */
651         r = amdgpu_atpx_detect();
652         if (!r)
653                 return;
654
655         vga_switcheroo_register_handler(&amdgpu_atpx_handler, handler_flags);
656 }
657
658 /**
659  * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo
660  *
661  * Unregister the PX callbacks with vga_switcheroo (all asics).
662  */
663 void amdgpu_unregister_atpx_handler(void)
664 {
665         vga_switcheroo_unregister_handler();
666 }