linux/drivers/gpu/drm/i915/display/intel_acpi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel ACPI functions
   4 *
   5 * _DSM related code stolen from nouveau_acpi.c.
   6 */
   7
   8#include <linux/pci.h>
   9#include <linux/acpi.h>
  10
  11#include "i915_drv.h"
  12#include "intel_acpi.h"
  13#include "intel_display_types.h"
  14
  15#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
  16#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
  17
  18static const guid_t intel_dsm_guid =
  19        GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f,
  20                  0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c);
  21
  22static char *intel_dsm_port_name(u8 id)
  23{
  24        switch (id) {
  25        case 0:
  26                return "Reserved";
  27        case 1:
  28                return "Analog VGA";
  29        case 2:
  30                return "LVDS";
  31        case 3:
  32                return "Reserved";
  33        case 4:
  34                return "HDMI/DVI_B";
  35        case 5:
  36                return "HDMI/DVI_C";
  37        case 6:
  38                return "HDMI/DVI_D";
  39        case 7:
  40                return "DisplayPort_A";
  41        case 8:
  42                return "DisplayPort_B";
  43        case 9:
  44                return "DisplayPort_C";
  45        case 0xa:
  46                return "DisplayPort_D";
  47        case 0xb:
  48        case 0xc:
  49        case 0xd:
  50                return "Reserved";
  51        case 0xe:
  52                return "WiDi";
  53        default:
  54                return "bad type";
  55        }
  56}
  57
  58static char *intel_dsm_mux_type(u8 type)
  59{
  60        switch (type) {
  61        case 0:
  62                return "unknown";
  63        case 1:
  64                return "No MUX, iGPU only";
  65        case 2:
  66                return "No MUX, dGPU only";
  67        case 3:
  68                return "MUXed between iGPU and dGPU";
  69        default:
  70                return "bad type";
  71        }
  72}
  73
  74static void intel_dsm_platform_mux_info(acpi_handle dhandle)
  75{
  76        int i;
  77        union acpi_object *pkg, *connector_count;
  78
  79        pkg = acpi_evaluate_dsm_typed(dhandle, &intel_dsm_guid,
  80                        INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO,
  81                        NULL, ACPI_TYPE_PACKAGE);
  82        if (!pkg) {
  83                DRM_DEBUG_DRIVER("failed to evaluate _DSM\n");
  84                return;
  85        }
  86
  87        if (!pkg->package.count) {
  88                DRM_DEBUG_DRIVER("no connection in _DSM\n");
  89                return;
  90        }
  91
  92        connector_count = &pkg->package.elements[0];
  93        DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
  94                  (unsigned long long)connector_count->integer.value);
  95        for (i = 1; i < pkg->package.count; i++) {
  96                union acpi_object *obj = &pkg->package.elements[i];
  97                union acpi_object *connector_id;
  98                union acpi_object *info;
  99
 100                if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
 101                        DRM_DEBUG_DRIVER("Invalid object for MUX #%d\n", i);
 102                        continue;
 103                }
 104
 105                connector_id = &obj->package.elements[0];
 106                info = &obj->package.elements[1];
 107                if (info->type != ACPI_TYPE_BUFFER || info->buffer.length < 4) {
 108                        DRM_DEBUG_DRIVER("Invalid info for MUX obj #%d\n", i);
 109                        continue;
 110                }
 111
 112                DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
 113                          (unsigned long long)connector_id->integer.value);
 114                DRM_DEBUG_DRIVER("  port id: %s\n",
 115                       intel_dsm_port_name(info->buffer.pointer[0]));
 116                DRM_DEBUG_DRIVER("  display mux info: %s\n",
 117                       intel_dsm_mux_type(info->buffer.pointer[1]));
 118                DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
 119                       intel_dsm_mux_type(info->buffer.pointer[2]));
 120                DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
 121                       intel_dsm_mux_type(info->buffer.pointer[3]));
 122        }
 123
 124        ACPI_FREE(pkg);
 125}
 126
 127static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev)
 128{
 129        acpi_handle dhandle;
 130
 131        dhandle = ACPI_HANDLE(&pdev->dev);
 132        if (!dhandle)
 133                return NULL;
 134
 135        if (!acpi_check_dsm(dhandle, &intel_dsm_guid, INTEL_DSM_REVISION_ID,
 136                            1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) {
 137                DRM_DEBUG_KMS("no _DSM method for intel device\n");
 138                return NULL;
 139        }
 140
 141        intel_dsm_platform_mux_info(dhandle);
 142
 143        return dhandle;
 144}
 145
 146static bool intel_dsm_detect(void)
 147{
 148        acpi_handle dhandle = NULL;
 149        char acpi_method_name[255] = { 0 };
 150        struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
 151        struct pci_dev *pdev = NULL;
 152        int vga_count = 0;
 153
 154        while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
 155                vga_count++;
 156                dhandle = intel_dsm_pci_probe(pdev) ?: dhandle;
 157        }
 158
 159        if (vga_count == 2 && dhandle) {
 160                acpi_get_name(dhandle, ACPI_FULL_PATHNAME, &buffer);
 161                DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n",
 162                                 acpi_method_name);
 163                return true;
 164        }
 165
 166        return false;
 167}
 168
 169void intel_register_dsm_handler(void)
 170{
 171        if (!intel_dsm_detect())
 172                return;
 173}
 174
 175void intel_unregister_dsm_handler(void)
 176{
 177}
 178
 179/*
 180 * ACPI Specification, Revision 5.0, Appendix B.3.2 _DOD (Enumerate All Devices
 181 * Attached to the Display Adapter).
 182 */
 183#define ACPI_DISPLAY_INDEX_SHIFT                0
 184#define ACPI_DISPLAY_INDEX_MASK                 (0xf << 0)
 185#define ACPI_DISPLAY_PORT_ATTACHMENT_SHIFT      4
 186#define ACPI_DISPLAY_PORT_ATTACHMENT_MASK       (0xf << 4)
 187#define ACPI_DISPLAY_TYPE_SHIFT                 8
 188#define ACPI_DISPLAY_TYPE_MASK                  (0xf << 8)
 189#define ACPI_DISPLAY_TYPE_OTHER                 (0 << 8)
 190#define ACPI_DISPLAY_TYPE_VGA                   (1 << 8)
 191#define ACPI_DISPLAY_TYPE_TV                    (2 << 8)
 192#define ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL      (3 << 8)
 193#define ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL      (4 << 8)
 194#define ACPI_VENDOR_SPECIFIC_SHIFT              12
 195#define ACPI_VENDOR_SPECIFIC_MASK               (0xf << 12)
 196#define ACPI_BIOS_CAN_DETECT                    (1 << 16)
 197#define ACPI_DEPENDS_ON_VGA                     (1 << 17)
 198#define ACPI_PIPE_ID_SHIFT                      18
 199#define ACPI_PIPE_ID_MASK                       (7 << 18)
 200#define ACPI_DEVICE_ID_SCHEME                   (1ULL << 31)
 201
 202static u32 acpi_display_type(struct intel_connector *connector)
 203{
 204        u32 display_type;
 205
 206        switch (connector->base.connector_type) {
 207        case DRM_MODE_CONNECTOR_VGA:
 208        case DRM_MODE_CONNECTOR_DVIA:
 209                display_type = ACPI_DISPLAY_TYPE_VGA;
 210                break;
 211        case DRM_MODE_CONNECTOR_Composite:
 212        case DRM_MODE_CONNECTOR_SVIDEO:
 213        case DRM_MODE_CONNECTOR_Component:
 214        case DRM_MODE_CONNECTOR_9PinDIN:
 215        case DRM_MODE_CONNECTOR_TV:
 216                display_type = ACPI_DISPLAY_TYPE_TV;
 217                break;
 218        case DRM_MODE_CONNECTOR_DVII:
 219        case DRM_MODE_CONNECTOR_DVID:
 220        case DRM_MODE_CONNECTOR_DisplayPort:
 221        case DRM_MODE_CONNECTOR_HDMIA:
 222        case DRM_MODE_CONNECTOR_HDMIB:
 223                display_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL;
 224                break;
 225        case DRM_MODE_CONNECTOR_LVDS:
 226        case DRM_MODE_CONNECTOR_eDP:
 227        case DRM_MODE_CONNECTOR_DSI:
 228                display_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL;
 229                break;
 230        case DRM_MODE_CONNECTOR_Unknown:
 231        case DRM_MODE_CONNECTOR_VIRTUAL:
 232                display_type = ACPI_DISPLAY_TYPE_OTHER;
 233                break;
 234        default:
 235                MISSING_CASE(connector->base.connector_type);
 236                display_type = ACPI_DISPLAY_TYPE_OTHER;
 237                break;
 238        }
 239
 240        return display_type;
 241}
 242
 243void intel_acpi_device_id_update(struct drm_i915_private *dev_priv)
 244{
 245        struct drm_device *drm_dev = &dev_priv->drm;
 246        struct intel_connector *connector;
 247        struct drm_connector_list_iter conn_iter;
 248        u8 display_index[16] = {};
 249
 250        /* Populate the ACPI IDs for all connectors for a given drm_device */
 251        drm_connector_list_iter_begin(drm_dev, &conn_iter);
 252        for_each_intel_connector_iter(connector, &conn_iter) {
 253                u32 device_id, type;
 254
 255                device_id = acpi_display_type(connector);
 256
 257                /* Use display type specific display index. */
 258                type = (device_id & ACPI_DISPLAY_TYPE_MASK)
 259                        >> ACPI_DISPLAY_TYPE_SHIFT;
 260                device_id |= display_index[type]++ << ACPI_DISPLAY_INDEX_SHIFT;
 261
 262                connector->acpi_device_id = device_id;
 263        }
 264        drm_connector_list_iter_end(&conn_iter);
 265}
 266