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
27struct 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
38struct radeon_atpx {
39	ACPI_HANDLE handle;
40	struct radeon_atpx_functions functions;
41};
42
43static 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
50struct 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
56struct atpx_power_control {
57	u16 size;
58	u8 dgpu_state;
59} __packed;
60
61struct 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 */
76static 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 */
123static 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 */
146static 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
174out:
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 */
189static 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 */
221static 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 */
253static 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 */
285static 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 */
317static 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 */
347static 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 */
374static 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 */
393static 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 */
417static 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 */
431static 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
439static 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 */
452static 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 */
482void 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 */
501void radeon_unregister_atpx_handler(void)
502{
503#ifdef DUMBBELL_WIP
504	vga_switcheroo_unregister_handler();
505#endif /* DUMBBELL_WIP */
506}
507