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
  14#include "amd_acpi.h"
  15
  16struct amdgpu_atpx_functions {
  17        bool px_params;
  18        bool power_cntl;
  19        bool disp_mux_cntl;
  20        bool i2c_mux_cntl;
  21        bool switch_start;
  22        bool switch_end;
  23        bool disp_connectors_mapping;
  24        bool disp_detetion_ports;
  25};
  26
  27struct amdgpu_atpx {
  28        acpi_handle handle;
  29        struct amdgpu_atpx_functions functions;
  30};
  31
  32static struct amdgpu_atpx_priv {
  33        bool atpx_detected;
  34        /* handle for device - and atpx */
  35        acpi_handle dhandle;
  36        acpi_handle other_handle;
  37        struct amdgpu_atpx atpx;
  38} amdgpu_atpx_priv;
  39
  40struct atpx_verify_interface {
  41        u16 size;               /* structure size in bytes (includes size field) */
  42        u16 version;            /* version */
  43        u32 function_bits;      /* supported functions bit vector */
  44} __packed;
  45
  46struct atpx_px_params {
  47        u16 size;               /* structure size in bytes (includes size field) */
  48        u32 valid_flags;        /* which flags are valid */
  49        u32 flags;              /* flags */
  50} __packed;
  51
  52struct atpx_power_control {
  53        u16 size;
  54        u8 dgpu_state;
  55} __packed;
  56
  57struct atpx_mux {
  58        u16 size;
  59        u16 mux;
  60} __packed;
  61
  62bool amdgpu_has_atpx(void) {
  63        return amdgpu_atpx_priv.atpx_detected;
  64}
  65
  66/**
  67 * amdgpu_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 union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function,
  77                                           struct acpi_buffer *params)
  78{
  79        acpi_status status;
  80        union acpi_object atpx_arg_elements[2];
  81        struct acpi_object_list atpx_arg;
  82        struct 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 = acpi_evaluate_object(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                printk("failed to evaluate ATPX got %s\n",
 105                       acpi_format_exception(status));
 106                kfree(buffer.pointer);
 107                return NULL;
 108        }
 109
 110        return buffer.pointer;
 111}
 112
 113/**
 114 * amdgpu_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 amdgpu_atpx_parse_functions(struct amdgpu_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 * amdgpu_atpx_validate_functions - validate ATPX functions
 137 *
 138 * @atpx: amdgpu atpx struct
 139 *
 140 * Validate that required functions are enabled (all asics).
 141 * returns 0 on success, error on failure.
 142 */
 143static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
 144{
 145        /* make sure required functions are enabled */
 146        /* dGPU power control is required */
 147        if (atpx->functions.power_cntl == false) {
 148                printk("ATPX dGPU power cntl not present, forcing\n");
 149                atpx->functions.power_cntl = true;
 150        }
 151
 152        if (atpx->functions.px_params) {
 153                union acpi_object *info;
 154                struct atpx_px_params output;
 155                size_t size;
 156                u32 valid_bits;
 157
 158                info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL);
 159                if (!info)
 160                        return -EIO;
 161
 162                memset(&output, 0, sizeof(output));
 163
 164                size = *(u16 *) info->buffer.pointer;
 165                if (size < 10) {
 166                        printk("ATPX buffer is too small: %zu\n", size);
 167                        kfree(info);
 168                        return -EINVAL;
 169                }
 170                size = min(sizeof(output), size);
 171
 172                memcpy(&output, info->buffer.pointer, size);
 173
 174                valid_bits = output.flags & output.valid_flags;
 175                /* if separate mux flag is set, mux controls are required */
 176                if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {
 177                        atpx->functions.i2c_mux_cntl = true;
 178                        atpx->functions.disp_mux_cntl = true;
 179                }
 180                /* if any outputs are muxed, mux controls are required */
 181                if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |
 182                                  ATPX_TV_SIGNAL_MUXED |
 183                                  ATPX_DFP_SIGNAL_MUXED))
 184                        atpx->functions.disp_mux_cntl = true;
 185
 186                kfree(info);
 187        }
 188        return 0;
 189}
 190
 191/**
 192 * amdgpu_atpx_verify_interface - verify ATPX
 193 *
 194 * @atpx: amdgpu atpx struct
 195 *
 196 * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function
 197 * to initialize ATPX and determine what features are supported
 198 * (all asics).
 199 * returns 0 on success, error on failure.
 200 */
 201static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
 202{
 203        union acpi_object *info;
 204        struct atpx_verify_interface output;
 205        size_t size;
 206        int err = 0;
 207
 208        info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL);
 209        if (!info)
 210                return -EIO;
 211
 212        memset(&output, 0, sizeof(output));
 213
 214        size = *(u16 *) info->buffer.pointer;
 215        if (size < 8) {
 216                printk("ATPX buffer is too small: %zu\n", size);
 217                err = -EINVAL;
 218                goto out;
 219        }
 220        size = min(sizeof(output), size);
 221
 222        memcpy(&output, info->buffer.pointer, size);
 223
 224        /* TODO: check version? */
 225        printk("ATPX version %u, functions 0x%08x\n",
 226               output.version, output.function_bits);
 227
 228        amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits);
 229
 230out:
 231        kfree(info);
 232        return err;
 233}
 234
 235/**
 236 * amdgpu_atpx_set_discrete_state - power up/down discrete GPU
 237 *
 238 * @atpx: atpx info struct
 239 * @state: discrete GPU state (0 = power down, 1 = power up)
 240 *
 241 * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to
 242 * power down/up the discrete GPU (all asics).
 243 * Returns 0 on success, error on failure.
 244 */
 245static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state)
 246{
 247        struct acpi_buffer params;
 248        union acpi_object *info;
 249        struct atpx_power_control input;
 250
 251        if (atpx->functions.power_cntl) {
 252                input.size = 3;
 253                input.dgpu_state = state;
 254                params.length = input.size;
 255                params.pointer = &input;
 256                info = amdgpu_atpx_call(atpx->handle,
 257                                        ATPX_FUNCTION_POWER_CONTROL,
 258                                        &params);
 259                if (!info)
 260                        return -EIO;
 261                kfree(info);
 262        }
 263        return 0;
 264}
 265
 266/**
 267 * amdgpu_atpx_switch_disp_mux - switch display mux
 268 *
 269 * @atpx: atpx info struct
 270 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
 271 *
 272 * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to
 273 * switch the display mux between the discrete GPU and integrated GPU
 274 * (all asics).
 275 * Returns 0 on success, error on failure.
 276 */
 277static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id)
 278{
 279        struct acpi_buffer params;
 280        union acpi_object *info;
 281        struct atpx_mux input;
 282
 283        if (atpx->functions.disp_mux_cntl) {
 284                input.size = 4;
 285                input.mux = mux_id;
 286                params.length = input.size;
 287                params.pointer = &input;
 288                info = amdgpu_atpx_call(atpx->handle,
 289                                        ATPX_FUNCTION_DISPLAY_MUX_CONTROL,
 290                                        &params);
 291                if (!info)
 292                        return -EIO;
 293                kfree(info);
 294        }
 295        return 0;
 296}
 297
 298/**
 299 * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux
 300 *
 301 * @atpx: atpx info struct
 302 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
 303 *
 304 * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to
 305 * switch the i2c/hpd mux between the discrete GPU and integrated GPU
 306 * (all asics).
 307 * Returns 0 on success, error on failure.
 308 */
 309static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id)
 310{
 311        struct acpi_buffer params;
 312        union acpi_object *info;
 313        struct atpx_mux input;
 314
 315        if (atpx->functions.i2c_mux_cntl) {
 316                input.size = 4;
 317                input.mux = mux_id;
 318                params.length = input.size;
 319                params.pointer = &input;
 320                info = amdgpu_atpx_call(atpx->handle,
 321                                        ATPX_FUNCTION_I2C_MUX_CONTROL,
 322                                        &params);
 323                if (!info)
 324                        return -EIO;
 325                kfree(info);
 326        }
 327        return 0;
 328}
 329
 330/**
 331 * amdgpu_atpx_switch_start - notify the sbios of a GPU switch
 332 *
 333 * @atpx: atpx info struct
 334 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
 335 *
 336 * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX
 337 * function to notify the sbios that a switch between the discrete GPU and
 338 * integrated GPU has begun (all asics).
 339 * Returns 0 on success, error on failure.
 340 */
 341static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id)
 342{
 343        struct acpi_buffer params;
 344        union acpi_object *info;
 345        struct atpx_mux input;
 346
 347        if (atpx->functions.switch_start) {
 348                input.size = 4;
 349                input.mux = mux_id;
 350                params.length = input.size;
 351                params.pointer = &input;
 352                info = amdgpu_atpx_call(atpx->handle,
 353                                        ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION,
 354                                        &params);
 355                if (!info)
 356                        return -EIO;
 357                kfree(info);
 358        }
 359        return 0;
 360}
 361
 362/**
 363 * amdgpu_atpx_switch_end - notify the sbios of a GPU switch
 364 *
 365 * @atpx: atpx info struct
 366 * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU)
 367 *
 368 * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX
 369 * function to notify the sbios that a switch between the discrete GPU and
 370 * integrated GPU has ended (all asics).
 371 * Returns 0 on success, error on failure.
 372 */
 373static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id)
 374{
 375        struct acpi_buffer params;
 376        union acpi_object *info;
 377        struct atpx_mux input;
 378
 379        if (atpx->functions.switch_end) {
 380                input.size = 4;
 381                input.mux = mux_id;
 382                params.length = input.size;
 383                params.pointer = &input;
 384                info = amdgpu_atpx_call(atpx->handle,
 385                                        ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION,
 386                                        &params);
 387                if (!info)
 388                        return -EIO;
 389                kfree(info);
 390        }
 391        return 0;
 392}
 393
 394/**
 395 * amdgpu_atpx_switchto - switch to the requested GPU
 396 *
 397 * @id: GPU to switch to
 398 *
 399 * Execute the necessary ATPX functions to switch between the discrete GPU and
 400 * integrated GPU (all asics).
 401 * Returns 0 on success, error on failure.
 402 */
 403static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id)
 404{
 405        u16 gpu_id;
 406
 407        if (id == VGA_SWITCHEROO_IGD)
 408                gpu_id = ATPX_INTEGRATED_GPU;
 409        else
 410                gpu_id = ATPX_DISCRETE_GPU;
 411
 412        amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id);
 413        amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id);
 414        amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id);
 415        amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id);
 416
 417        return 0;
 418}
 419
 420/**
 421 * amdgpu_atpx_power_state - power down/up the requested GPU
 422 *
 423 * @id: GPU to power down/up
 424 * @state: requested power state (0 = off, 1 = on)
 425 *
 426 * Execute the necessary ATPX function to power down/up the discrete GPU
 427 * (all asics).
 428 * Returns 0 on success, error on failure.
 429 */
 430static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id,
 431                                   enum vga_switcheroo_state state)
 432{
 433        /* on w500 ACPI can't change intel gpu state */
 434        if (id == VGA_SWITCHEROO_IGD)
 435                return 0;
 436
 437        amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state);
 438        return 0;
 439}
 440
 441/**
 442 * amdgpu_atpx_pci_probe_handle - look up the ATPX handle
 443 *
 444 * @pdev: pci device
 445 *
 446 * Look up the ATPX handles (all asics).
 447 * Returns true if the handles are found, false if not.
 448 */
 449static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev)
 450{
 451        acpi_handle dhandle, atpx_handle;
 452        acpi_status status;
 453
 454        dhandle = ACPI_HANDLE(&pdev->dev);
 455        if (!dhandle)
 456                return false;
 457
 458        status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
 459        if (ACPI_FAILURE(status)) {
 460                amdgpu_atpx_priv.other_handle = dhandle;
 461                return false;
 462        }
 463        amdgpu_atpx_priv.dhandle = dhandle;
 464        amdgpu_atpx_priv.atpx.handle = atpx_handle;
 465        return true;
 466}
 467
 468/**
 469 * amdgpu_atpx_init - verify the ATPX interface
 470 *
 471 * Verify the ATPX interface (all asics).
 472 * Returns 0 on success, error on failure.
 473 */
 474static int amdgpu_atpx_init(void)
 475{
 476        int r;
 477
 478        /* set up the ATPX handle */
 479        r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx);
 480        if (r)
 481                return r;
 482
 483        /* validate the atpx setup */
 484        r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx);
 485        if (r)
 486                return r;
 487
 488        return 0;
 489}
 490
 491/**
 492 * amdgpu_atpx_get_client_id - get the client id
 493 *
 494 * @pdev: pci device
 495 *
 496 * look up whether we are the integrated or discrete GPU (all asics).
 497 * Returns the client id.
 498 */
 499static int amdgpu_atpx_get_client_id(struct pci_dev *pdev)
 500{
 501        if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev))
 502                return VGA_SWITCHEROO_IGD;
 503        else
 504                return VGA_SWITCHEROO_DIS;
 505}
 506
 507static const struct vga_switcheroo_handler amdgpu_atpx_handler = {
 508        .switchto = amdgpu_atpx_switchto,
 509        .power_state = amdgpu_atpx_power_state,
 510        .init = amdgpu_atpx_init,
 511        .get_client_id = amdgpu_atpx_get_client_id,
 512};
 513
 514/**
 515 * amdgpu_atpx_detect - detect whether we have PX
 516 *
 517 * Check if we have a PX system (all asics).
 518 * Returns true if we have a PX system, false if not.
 519 */
 520static bool amdgpu_atpx_detect(void)
 521{
 522        char acpi_method_name[255] = { 0 };
 523        struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
 524        struct pci_dev *pdev = NULL;
 525        bool has_atpx = false;
 526        int vga_count = 0;
 527
 528        while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
 529                vga_count++;
 530
 531                has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true);
 532        }
 533
 534        while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
 535                vga_count++;
 536
 537                has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true);
 538        }
 539
 540        if (has_atpx && vga_count == 2) {
 541                acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer);
 542                printk(KERN_INFO "vga_switcheroo: detected switching method %s handle\n",
 543                       acpi_method_name);
 544                amdgpu_atpx_priv.atpx_detected = true;
 545                return true;
 546        }
 547        return false;
 548}
 549
 550/**
 551 * amdgpu_register_atpx_handler - register with vga_switcheroo
 552 *
 553 * Register the PX callbacks with vga_switcheroo (all asics).
 554 */
 555void amdgpu_register_atpx_handler(void)
 556{
 557        bool r;
 558        enum vga_switcheroo_handler_flags_t handler_flags = 0;
 559
 560        /* detect if we have any ATPX + 2 VGA in the system */
 561        r = amdgpu_atpx_detect();
 562        if (!r)
 563                return;
 564
 565        vga_switcheroo_register_handler(&amdgpu_atpx_handler, handler_flags);
 566}
 567
 568/**
 569 * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo
 570 *
 571 * Unregister the PX callbacks with vga_switcheroo (all asics).
 572 */
 573void amdgpu_unregister_atpx_handler(void)
 574{
 575        vga_switcheroo_unregister_handler();
 576}
 577