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 ¶ms); 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 ¶ms); 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 ¶ms); 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 ¶ms); 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 ¶ms); 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