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