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