]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/drm2/radeon/radeon_atpx_handler.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / drm2 / radeon / radeon_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
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
12
13 #include <sys/param.h>
14 #include <sys/systm.h>
15 #include <sys/bus.h>
16 #include <sys/linker.h>
17
18 #include <contrib/dev/acpica/include/acpi.h>
19 #include <dev/acpica/acpivar.h>
20
21 #include <dev/drm2/drmP.h>
22 #include <dev/drm2/radeon/radeon_drm.h>
23 #include "radeon_acpi.h"
24 #include "radeon_drv.h"
25
26 #ifdef DUMBBELL_WIP
27 struct radeon_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 radeon_atpx {
39         ACPI_HANDLE handle;
40         struct radeon_atpx_functions functions;
41 };
42
43 static struct radeon_atpx_priv {
44         bool atpx_detected;
45         /* handle for device - and atpx */
46         ACPI_HANDLE dhandle;
47         struct radeon_atpx atpx;
48 } radeon_atpx_priv;
49
50 struct atpx_verify_interface {
51         u16 size;               /* structure size in bytes (includes size field) */
52         u16 version;            /* version */
53         u32 function_bits;      /* supported functions bit vector */
54 } __packed;
55
56 struct atpx_power_control {
57         u16 size;
58         u8 dgpu_state;
59 } __packed;
60
61 struct atpx_mux {
62         u16 size;
63         u16 mux;
64 } __packed;
65
66 /**
67  * radeon_atpx_call - call an ATPX method
68  *
69  * @handle: acpi handle
70  * @function: the ATPX function to execute
71  * @params: ATPX function params
72  *
73  * Executes the requested ATPX function (all asics).
74  * Returns a pointer to the acpi output buffer.
75  */
76 static ACPI_OBJECT *radeon_atpx_call(ACPI_HANDLE handle, int function,
77                                            ACPI_BUFFER *params)
78 {
79         ACPI_STATUS status;
80         ACPI_OBJECT atpx_arg_elements[2];
81         ACPI_OBJECT_LIST atpx_arg;
82         ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL };
83
84         atpx_arg.Count = 2;
85         atpx_arg.Pointer = &atpx_arg_elements[0];
86
87         atpx_arg_elements[0].Type = ACPI_TYPE_INTEGER;
88         atpx_arg_elements[0].Integer.Value = function;
89
90         if (params) {
91                 atpx_arg_elements[1].Type = ACPI_TYPE_BUFFER;
92                 atpx_arg_elements[1].Buffer.Length = params->Length;
93                 atpx_arg_elements[1].Buffer.Pointer = params->Pointer;
94         } else {
95                 /* We need a second fake parameter */
96                 atpx_arg_elements[1].Type = ACPI_TYPE_INTEGER;
97                 atpx_arg_elements[1].Integer.Value = 0;
98         }
99
100         status = AcpiEvaluateObject(handle, NULL, &atpx_arg, &buffer);
101
102         /* Fail only if calling the method fails and ATPX is supported */
103         if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
104                 DRM_ERROR("failed to evaluate ATPX got %s\n",
105                        AcpiFormatException(status));
106                 AcpiOsFree(buffer.Pointer);
107                 return NULL;
108         }
109
110         return buffer.Pointer;
111 }
112
113 /**
114  * radeon_atpx_parse_functions - parse supported functions
115  *
116  * @f: supported functions struct
117  * @mask: supported functions mask from ATPX
118  *
119  * Use the supported functions mask from ATPX function
120  * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions
121  * are supported (all asics).
122  */
123 static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mask)
124 {
125         f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED;
126         f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED;
127         f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED;
128         f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED;
129         f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED;
130         f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED;
131         f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED;
132         f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED;
133 }
134
135 /**
136  * radeon_atpx_verify_interface - verify ATPX
137  *
138  * @handle: acpi handle
139  * @atpx: radeon atpx struct
140  *
141  * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function
142  * to initialize ATPX and determine what features are supported
143  * (all asics).
144  * returns 0 on success, error on failure.
145  */
146 static int radeon_atpx_verify_interface(struct radeon_atpx *atpx)
147 {
148         ACPI_OBJECT *info;
149         struct atpx_verify_interface output;
150         size_t size;
151         int err = 0;
152
153         info = radeon_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL);
154         if (!info)
155                 return -EIO;
156
157         memset(&output, 0, sizeof(output));
158
159         size = *(u16 *) info->Buffer.Pointer;
160         if (size < 8) {
161                 DRM_ERROR("ATPX buffer is too small: %zu\n", size);
162                 err = -EINVAL;
163                 goto out;
164         }
165         size = min(sizeof(output), size);
166
167         memcpy(&output, info->Buffer.Pointer, size);
168
169         /* TODO: check version? */
170         DRM_INFO("ATPX version %u\n", output.version);
171
172         radeon_atpx_parse_functions(&atpx->functions, output.function_bits);
173
174 out:
175         AcpiOsFree(info);
176         return err;
177 }
178
179 /**
180  * radeon_atpx_set_discrete_state - power up/down discrete GPU
181  *
182  * @atpx: atpx info struct
183  * @state: discrete GPU state (0 = power down, 1 = power up)
184  *
185  * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to
186  * power down/up the discrete GPU (all asics).
187  * Returns 0 on success, error on failure.
188  */
189 static int radeon_atpx_set_discrete_state(struct radeon_atpx *atpx, u8 state)
190 {
191         ACPI_BUFFER params;
192         ACPI_OBJECT *info;
193         struct atpx_power_control input;
194
195         if (atpx->functions.power_cntl) {
196                 input.size = 3;
197                 input.dgpu_state = state;
198                 params.Length = input.size;
199                 params.Pointer = &input;
200                 info = radeon_atpx_call(atpx->handle,
201                                         ATPX_FUNCTION_POWER_CONTROL,
202                                         &params);
203                 if (!info)
204                         return -EIO;
205                 AcpiOsFree(info);
206         }
207         return 0;
208 }
209
210 /**
211  * radeon_atpx_switch_disp_mux - switch display mux
212  *
213  * @atpx: atpx info struct
214  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
215  *
216  * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to
217  * switch the display mux between the discrete GPU and integrated GPU
218  * (all asics).
219  * Returns 0 on success, error on failure.
220  */
221 static int radeon_atpx_switch_disp_mux(struct radeon_atpx *atpx, u16 mux_id)
222 {
223         ACPI_BUFFER params;
224         ACPI_OBJECT *info;
225         struct atpx_mux input;
226
227         if (atpx->functions.disp_mux_cntl) {
228                 input.size = 4;
229                 input.mux = mux_id;
230                 params.Length = input.size;
231                 params.Pointer = &input;
232                 info = radeon_atpx_call(atpx->handle,
233                                         ATPX_FUNCTION_DISPLAY_MUX_CONTROL,
234                                         &params);
235                 if (!info)
236                         return -EIO;
237                 AcpiOsFree(info);
238         }
239         return 0;
240 }
241
242 /**
243  * radeon_atpx_switch_i2c_mux - switch i2c/hpd mux
244  *
245  * @atpx: atpx info struct
246  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
247  *
248  * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to
249  * switch the i2c/hpd mux between the discrete GPU and integrated GPU
250  * (all asics).
251  * Returns 0 on success, error on failure.
252  */
253 static int radeon_atpx_switch_i2c_mux(struct radeon_atpx *atpx, u16 mux_id)
254 {
255         ACPI_BUFFER params;
256         ACPI_OBJECT *info;
257         struct atpx_mux input;
258
259         if (atpx->functions.i2c_mux_cntl) {
260                 input.size = 4;
261                 input.mux = mux_id;
262                 params.Length = input.size;
263                 params.Pointer = &input;
264                 info = radeon_atpx_call(atpx->handle,
265                                         ATPX_FUNCTION_I2C_MUX_CONTROL,
266                                         &params);
267                 if (!info)
268                         return -EIO;
269                 AcpiOsFree(info);
270         }
271         return 0;
272 }
273
274 /**
275  * radeon_atpx_switch_start - notify the sbios of a GPU switch
276  *
277  * @atpx: atpx info struct
278  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
279  *
280  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX
281  * function to notify the sbios that a switch between the discrete GPU and
282  * integrated GPU has begun (all asics).
283  * Returns 0 on success, error on failure.
284  */
285 static int radeon_atpx_switch_start(struct radeon_atpx *atpx, u16 mux_id)
286 {
287         ACPI_BUFFER params;
288         ACPI_OBJECT *info;
289         struct atpx_mux input;
290
291         if (atpx->functions.switch_start) {
292                 input.size = 4;
293                 input.mux = mux_id;
294                 params.Length = input.size;
295                 params.Pointer = &input;
296                 info = radeon_atpx_call(atpx->handle,
297                                         ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,
298                                         &params);
299                 if (!info)
300                         return -EIO;
301                 AcpiOsFree(info);
302         }
303         return 0;
304 }
305
306 /**
307  * radeon_atpx_switch_end - notify the sbios of a GPU switch
308  *
309  * @atpx: atpx info struct
310  * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
311  *
312  * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX
313  * function to notify the sbios that a switch between the discrete GPU and
314  * integrated GPU has ended (all asics).
315  * Returns 0 on success, error on failure.
316  */
317 static int radeon_atpx_switch_end(struct radeon_atpx *atpx, u16 mux_id)
318 {
319         ACPI_BUFFER params;
320         ACPI_OBJECT *info;
321         struct atpx_mux input;
322
323         if (atpx->functions.switch_end) {
324                 input.size = 4;
325                 input.mux = mux_id;
326                 params.Length = input.size;
327                 params.Pointer = &input;
328                 info = radeon_atpx_call(atpx->handle,
329                                         ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,
330                                         &params);
331                 if (!info)
332                         return -EIO;
333                 AcpiOsFree(info);
334         }
335         return 0;
336 }
337
338 /**
339  * radeon_atpx_switchto - switch to the requested GPU
340  *
341  * @id: GPU to switch to
342  *
343  * Execute the necessary ATPX functions to switch between the discrete GPU and
344  * integrated GPU (all asics).
345  * Returns 0 on success, error on failure.
346  */
347 static int radeon_atpx_switchto(enum vga_switcheroo_client_id id)
348 {
349         u16 gpu_id;
350
351         if (id == VGA_SWITCHEROO_IGD)
352                 gpu_id = ATPX_INTEGRATED_GPU;
353         else
354                 gpu_id = ATPX_DISCRETE_GPU;
355
356         radeon_atpx_switch_start(&radeon_atpx_priv.atpx, gpu_id);
357         radeon_atpx_switch_disp_mux(&radeon_atpx_priv.atpx, gpu_id);
358         radeon_atpx_switch_i2c_mux(&radeon_atpx_priv.atpx, gpu_id);
359         radeon_atpx_switch_end(&radeon_atpx_priv.atpx, gpu_id);
360
361         return 0;
362 }
363
364 /**
365  * radeon_atpx_power_state - power down/up the requested GPU
366  *
367  * @id: GPU to power down/up
368  * @state: requested power state (0 = off, 1 = on)
369  *
370  * Execute the necessary ATPX function to power down/up the discrete GPU
371  * (all asics).
372  * Returns 0 on success, error on failure.
373  */
374 static int radeon_atpx_power_state(enum vga_switcheroo_client_id id,
375                                    enum vga_switcheroo_state state)
376 {
377         /* on w500 ACPI can't change intel gpu state */
378         if (id == VGA_SWITCHEROO_IGD)
379                 return 0;
380
381         radeon_atpx_set_discrete_state(&radeon_atpx_priv.atpx, state);
382         return 0;
383 }
384
385 /**
386  * radeon_atpx_pci_probe_handle - look up the ATPX handle
387  *
388  * @pdev: pci device
389  *
390  * Look up the ATPX handles (all asics).
391  * Returns true if the handles are found, false if not.
392  */
393 static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
394 {
395         ACPI_HANDLE dhandle, atpx_handle;
396         ACPI_STATUS status;
397
398         dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
399         if (!dhandle)
400                 return false;
401
402         status = AcpiGetHandle(dhandle, "ATPX", &atpx_handle);
403         if (ACPI_FAILURE(status))
404                 return false;
405
406         radeon_atpx_priv.dhandle = dhandle;
407         radeon_atpx_priv.atpx.handle = atpx_handle;
408         return true;
409 }
410
411 /**
412  * radeon_atpx_init - verify the ATPX interface
413  *
414  * Verify the ATPX interface (all asics).
415  * Returns 0 on success, error on failure.
416  */
417 static int radeon_atpx_init(void)
418 {
419         /* set up the ATPX handle */
420         return radeon_atpx_verify_interface(&radeon_atpx_priv.atpx);
421 }
422
423 /**
424  * radeon_atpx_get_client_id - get the client id
425  *
426  * @pdev: pci device
427  *
428  * look up whether we are the integrated or discrete GPU (all asics).
429  * Returns the client id.
430  */
431 static int radeon_atpx_get_client_id(struct pci_dev *pdev)
432 {
433         if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
434                 return VGA_SWITCHEROO_IGD;
435         else
436                 return VGA_SWITCHEROO_DIS;
437 }
438
439 static struct vga_switcheroo_handler radeon_atpx_handler = {
440         .switchto = radeon_atpx_switchto,
441         .power_state = radeon_atpx_power_state,
442         .init = radeon_atpx_init,
443         .get_client_id = radeon_atpx_get_client_id,
444 };
445
446 /**
447  * radeon_atpx_detect - detect whether we have PX
448  *
449  * Check if we have a PX system (all asics).
450  * Returns true if we have a PX system, false if not.
451  */
452 static bool radeon_atpx_detect(void)
453 {
454         char acpi_method_name[255] = { 0 };
455         ACPI_BUFFER buffer = {sizeof(acpi_method_name), acpi_method_name};
456         struct pci_dev *pdev = NULL;
457         bool has_atpx = false;
458         int vga_count = 0;
459
460         while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
461                 vga_count++;
462
463                 has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true);
464         }
465
466         if (has_atpx && vga_count == 2) {
467                 AcpiGetName(radeon_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer);
468                 DRM_INFO("VGA switcheroo: detected switching method %s handle\n",
469                        acpi_method_name);
470                 radeon_atpx_priv.atpx_detected = true;
471                 return true;
472         }
473         return false;
474 }
475 #endif /* DUMBBELL_WIP */
476
477 /**
478  * radeon_register_atpx_handler - register with vga_switcheroo
479  *
480  * Register the PX callbacks with vga_switcheroo (all asics).
481  */
482 void radeon_register_atpx_handler(void)
483 {
484 #ifdef DUMBBELL_WIP
485         bool r;
486
487         /* detect if we have any ATPX + 2 VGA in the system */
488         r = radeon_atpx_detect();
489         if (!r)
490                 return;
491
492         vga_switcheroo_register_handler(&radeon_atpx_handler);
493 #endif /* DUMBBELL_WIP */
494 }
495
496 /**
497  * radeon_unregister_atpx_handler - unregister with vga_switcheroo
498  *
499  * Unregister the PX callbacks with vga_switcheroo (all asics).
500  */
501 void radeon_unregister_atpx_handler(void)
502 {
503 #ifdef DUMBBELL_WIP
504         vga_switcheroo_unregister_handler();
505 #endif /* DUMBBELL_WIP */
506 }