1/* $NetBSD: intel_acpi.c,v 1.8 2022/02/27 14:22:42 riastradh Exp $ */ 2 3// SPDX-License-Identifier: GPL-2.0 4/* 5 * Intel ACPI functions 6 * 7 * _DSM related code stolen from nouveau_acpi.c. 8 */ 9 10#include <sys/cdefs.h> 11__KERNEL_RCSID(0, "$NetBSD: intel_acpi.c,v 1.8 2022/02/27 14:22:42 riastradh Exp $"); 12 13#include <linux/pci.h> 14#include <linux/acpi.h> 15 16#include "i915_drv.h" 17#include "intel_acpi.h" 18 19#ifdef __NetBSD__ 20 21#include <dev/acpi/acpireg.h> 22#define _COMPONENT ACPI_BUTTON_COMPONENT 23ACPI_MODULE_NAME("acpi_intel_brightness") 24 25#include <dev/acpi/acpi_pci.h> 26 27#include <linux/nbsd-namespace-acpi.h> 28#endif 29 30#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */ 31#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */ 32 33static const guid_t intel_dsm_guid = 34 GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f, 35 0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c); 36 37static const char *intel_dsm_port_name(u8 id) 38{ 39 switch (id) { 40 case 0: 41 return "Reserved"; 42 case 1: 43 return "Analog VGA"; 44 case 2: 45 return "LVDS"; 46 case 3: 47 return "Reserved"; 48 case 4: 49 return "HDMI/DVI_B"; 50 case 5: 51 return "HDMI/DVI_C"; 52 case 6: 53 return "HDMI/DVI_D"; 54 case 7: 55 return "DisplayPort_A"; 56 case 8: 57 return "DisplayPort_B"; 58 case 9: 59 return "DisplayPort_C"; 60 case 0xa: 61 return "DisplayPort_D"; 62 case 0xb: 63 case 0xc: 64 case 0xd: 65 return "Reserved"; 66 case 0xe: 67 return "WiDi"; 68 default: 69 return "bad type"; 70 } 71} 72 73static const char *intel_dsm_mux_type(u8 type) 74{ 75 switch (type) { 76 case 0: 77 return "unknown"; 78 case 1: 79 return "No MUX, iGPU only"; 80 case 2: 81 return "No MUX, dGPU only"; 82 case 3: 83 return "MUXed between iGPU and dGPU"; 84 default: 85 return "bad type"; 86 } 87} 88 89static void intel_dsm_platform_mux_info(acpi_handle dhandle) 90{ 91 int i; 92 union acpi_object *pkg, *connector_count; 93 94 pkg = acpi_evaluate_dsm_typed(dhandle, &intel_dsm_guid, 95 INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO, 96 NULL, ACPI_TYPE_PACKAGE); 97 if (!pkg) { 98 DRM_DEBUG_DRIVER("failed to evaluate _DSM\n"); 99 return; 100 } 101 102 connector_count = &pkg->package.elements[0]; 103 DRM_DEBUG_DRIVER("MUX info connectors: %lld\n", 104 (unsigned long long)connector_count->integer.value); 105 for (i = 1; i < pkg->package.count; i++) { 106 union acpi_object *obj = &pkg->package.elements[i]; 107 union acpi_object *connector_id = &obj->package.elements[0]; 108 union acpi_object *info = &obj->package.elements[1]; 109 DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n", 110 (unsigned long long)connector_id->integer.value); 111 DRM_DEBUG_DRIVER(" port id: %s\n", 112 intel_dsm_port_name(info->buffer.pointer[0])); 113 DRM_DEBUG_DRIVER(" display mux info: %s\n", 114 intel_dsm_mux_type(info->buffer.pointer[1])); 115 DRM_DEBUG_DRIVER(" aux/dc mux info: %s\n", 116 intel_dsm_mux_type(info->buffer.pointer[2])); 117 DRM_DEBUG_DRIVER(" hpd mux info: %s\n", 118 intel_dsm_mux_type(info->buffer.pointer[3])); 119 } 120 121 ACPI_FREE(pkg); 122} 123 124#ifdef __NetBSD__ 125static ACPI_HANDLE intel_dsm_pci_probe(ACPI_HANDLE dhandle) 126#else 127static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev) 128#endif 129{ 130#ifndef __NetBSD__ 131 acpi_handle dhandle; 132 133 dhandle = ACPI_HANDLE(&pdev->dev); 134 if (!dhandle) 135 return NULL; 136#endif 137 138 if (!acpi_check_dsm(dhandle, &intel_dsm_guid, INTEL_DSM_REVISION_ID, 139 1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) { 140 DRM_DEBUG_KMS("no _DSM method for intel device\n"); 141 return NULL; 142 } 143 144 intel_dsm_platform_mux_info(dhandle); 145 146 return dhandle; 147} 148 149#ifdef __NetBSD__ 150 151static int vga_count; 152static ACPI_HANDLE intel_dsm_handle; 153 154/* XXX from sys/dev/pci/vga_pcivar.h */ 155#define DEVICE_IS_VGA_PCI(class, id) \ 156 (((PCI_CLASS(class) == PCI_CLASS_DISPLAY && \ 157 PCI_SUBCLASS(class) == PCI_SUBCLASS_DISPLAY_VGA) || \ 158 (PCI_CLASS(class) == PCI_CLASS_PREHISTORIC && \ 159 PCI_SUBCLASS(class) == PCI_SUBCLASS_PREHISTORIC_VGA)) ? 1 : 0) 160 161static int 162intel_dsm_vga_match(const struct pci_attach_args *pa) 163{ 164 165 if (!DEVICE_IS_VGA_PCI(pa->pa_class, pa->pa_id)) 166 return 0; 167 168 vga_count++; 169 struct acpi_devnode *node = 170 acpi_pcidev_find(pci_get_segment(pa->pa_pc), 171 pa->pa_bus, pa->pa_device, pa->pa_function); 172 if (node != NULL && intel_dsm_handle == NULL) 173 intel_dsm_handle = intel_dsm_pci_probe(node->ad_handle); 174 return 0; 175} 176 177static bool intel_dsm_detect(struct drm_device *dev) 178{ 179 char acpi_method_name[255] = { 0 }; 180 181 vga_count = 0; 182 pci_find_device(&dev->pdev->pd_pa, intel_dsm_vga_match); 183 184 if (vga_count == 2 && intel_dsm_handle) { 185 const char *name = acpi_name(intel_dsm_handle); 186 strlcpy(acpi_method_name, name, sizeof(acpi_method_name)); 187 DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n", 188 acpi_method_name); 189 return true; 190 } 191 192 return false; 193} 194#else 195static bool intel_dsm_detect(void) 196{ 197 acpi_handle dhandle = NULL; 198 char acpi_method_name[255] = { 0 }; 199 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 200 struct pci_dev *pdev = NULL; 201 int vga_count = 0; 202 203 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 204 vga_count++; 205 dhandle = intel_dsm_pci_probe(pdev) ?: dhandle; 206 } 207 208 if (vga_count == 2 && dhandle) { 209 acpi_get_name(dhandle, ACPI_FULL_PATHNAME, &buffer); 210 DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n", 211 acpi_method_name); 212 return true; 213 } 214 215 return false; 216} 217#endif 218 219#ifdef __NetBSD__ 220void intel_register_dsm_handler(struct drm_i915_private *i915) 221{ 222 if (!intel_dsm_detect(&i915->drm)) 223 return; 224} 225#else 226void intel_register_dsm_handler(void) 227{ 228 if (!intel_dsm_detect()) 229 return; 230} 231#endif 232 233void intel_unregister_dsm_handler(void) 234{ 235} 236