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