Home | History | Annotate | Line # | Download | only in display
intel_acpi.c revision 1.5
      1 /*	$NetBSD: intel_acpi.c,v 1.5 2022/02/27 14:19:35 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.5 2022/02/27 14:19:35 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 
     36 static ACPI_OBJECT *
     37 acpi_evaluate_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, int func,
     38     ACPI_OBJECT *argv4)
     39 {
     40 	ACPI_OBJECT_LIST arg;
     41 	ACPI_OBJECT params[4];
     42 	ACPI_BUFFER buf;
     43 	ACPI_STATUS rv;
     44 
     45 	if (handle == NULL)
     46 		handle = ACPI_ROOT_OBJECT;
     47 
     48 	arg.Count = 4;
     49 	arg.Pointer = params;
     50 	params[0].Type = ACPI_TYPE_BUFFER;
     51 	params[0].Buffer.Length = 16;
     52 	params[0].Buffer.Pointer = (char *)__UNCONST(uuid);
     53 	params[1].Type = ACPI_TYPE_INTEGER;
     54 	params[1].Integer.Value = rev;
     55 	params[2].Type = ACPI_TYPE_INTEGER;
     56 	params[2].Integer.Value = func;
     57 	if (argv4 != NULL) {
     58 		params[3] = *argv4;
     59 	} else {
     60 		params[3].Type = ACPI_TYPE_PACKAGE;
     61 		params[3].Package.Count = 0;
     62 		params[3].Package.Elements = NULL;
     63 	}
     64 
     65 	buf.Pointer = NULL;
     66 	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
     67 
     68 	rv = AcpiEvaluateObject(handle, "_DSM", &arg, &buf);
     69 	if (ACPI_SUCCESS(rv))
     70 		return (ACPI_OBJECT *)buf.Pointer;
     71 	return NULL;
     72 }
     73 
     74 static inline ACPI_OBJECT *
     75 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
     76     int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
     77 {
     78 	ACPI_OBJECT *obj;
     79 
     80 	obj = acpi_evaluate_dsm(handle, uuid, rev, func, argv4);
     81 	if (obj != NULL && obj->Type != type) {
     82 		ACPI_FREE(obj);
     83 		obj = NULL;
     84 	}
     85 	return obj;
     86 }
     87 
     88 #define	ACPI_INIT_DSM_ARGV4(cnt, eles)		\
     89 {						\
     90 	.Package.Type = ACPI_TYPE_PACKAGE,	\
     91 	.Package.Count = (cnt),			\
     92 	.Package.Elements = (eles)		\
     93 }
     94 
     95 static bool
     96 acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
     97 {
     98 	ACPI_OBJECT *obj;
     99 	uint64_t mask = 0;
    100 	int i;
    101 
    102 	if (funcs == 0)
    103 		return false;
    104 
    105 	obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL);
    106 	if (obj == NULL)
    107 		return false;
    108 
    109 	if (obj->Type == ACPI_TYPE_INTEGER)
    110 		mask = obj->Integer.Value;
    111 	else if (obj->Type == ACPI_TYPE_BUFFER)
    112 		for (i = 0; i < obj->Buffer.Length && i < 8; i++)
    113 			mask |= (uint64_t)obj->Buffer.Pointer[i] << (i * 8);
    114 	ACPI_FREE(obj);
    115 
    116 	if ((mask & 0x1) == 0x1 && (mask & funcs) == funcs)
    117 		return true;
    118 	return false;
    119 }
    120 #endif
    121 
    122 #define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
    123 #define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
    124 
    125 static const guid_t intel_dsm_guid =
    126 	GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f,
    127 		  0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c);
    128 
    129 static const char *intel_dsm_port_name(u8 id)
    130 {
    131 	switch (id) {
    132 	case 0:
    133 		return "Reserved";
    134 	case 1:
    135 		return "Analog VGA";
    136 	case 2:
    137 		return "LVDS";
    138 	case 3:
    139 		return "Reserved";
    140 	case 4:
    141 		return "HDMI/DVI_B";
    142 	case 5:
    143 		return "HDMI/DVI_C";
    144 	case 6:
    145 		return "HDMI/DVI_D";
    146 	case 7:
    147 		return "DisplayPort_A";
    148 	case 8:
    149 		return "DisplayPort_B";
    150 	case 9:
    151 		return "DisplayPort_C";
    152 	case 0xa:
    153 		return "DisplayPort_D";
    154 	case 0xb:
    155 	case 0xc:
    156 	case 0xd:
    157 		return "Reserved";
    158 	case 0xe:
    159 		return "WiDi";
    160 	default:
    161 		return "bad type";
    162 	}
    163 }
    164 
    165 static const char *intel_dsm_mux_type(u8 type)
    166 {
    167 	switch (type) {
    168 	case 0:
    169 		return "unknown";
    170 	case 1:
    171 		return "No MUX, iGPU only";
    172 	case 2:
    173 		return "No MUX, dGPU only";
    174 	case 3:
    175 		return "MUXed between iGPU and dGPU";
    176 	default:
    177 		return "bad type";
    178 	}
    179 }
    180 
    181 static void intel_dsm_platform_mux_info(acpi_handle dhandle)
    182 {
    183 	int i;
    184 #ifdef __NetBSD__
    185 	ACPI_OBJECT *pkg, *connector_count;
    186 #else
    187 	union acpi_object *pkg, *connector_count;
    188 #endif
    189 
    190 	pkg = acpi_evaluate_dsm_typed(dhandle, &intel_dsm_guid,
    191 			INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO,
    192 			NULL, ACPI_TYPE_PACKAGE);
    193 	if (!pkg) {
    194 		DRM_DEBUG_DRIVER("failed to evaluate _DSM\n");
    195 		return;
    196 	}
    197 
    198 	connector_count = &pkg->package.elements[0];
    199 	DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
    200 		  (unsigned long long)connector_count->integer.value);
    201 	for (i = 1; i < pkg->package.count; i++) {
    202 #ifdef __NetBSD__
    203 		ACPI_OBJECT *obj = &pkg->package.elements[i];
    204 		ACPI_OBJECT *connector_id = &obj->package.elements[0];
    205 		ACPI_OBJECT *info = &obj->package.elements[1];
    206 #else
    207 		union acpi_object *obj = &pkg->package.elements[i];
    208 		union acpi_object *connector_id = &obj->package.elements[0];
    209 		union acpi_object *info = &obj->package.elements[1];
    210 #endif
    211 		DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
    212 			  (unsigned long long)connector_id->integer.value);
    213 		DRM_DEBUG_DRIVER("  port id: %s\n",
    214 		       intel_dsm_port_name(info->buffer.pointer[0]));
    215 		DRM_DEBUG_DRIVER("  display mux info: %s\n",
    216 		       intel_dsm_mux_type(info->buffer.pointer[1]));
    217 		DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
    218 		       intel_dsm_mux_type(info->buffer.pointer[2]));
    219 		DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
    220 		       intel_dsm_mux_type(info->buffer.pointer[3]));
    221 	}
    222 
    223 	ACPI_FREE(pkg);
    224 }
    225 
    226 #ifdef __NetBSD__
    227 static ACPI_HANDLE intel_dsm_pci_probe(ACPI_HANDLE dhandle)
    228 #else
    229 static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev)
    230 #endif
    231 {
    232 #ifndef __NetBSD__
    233 	acpi_handle dhandle;
    234 
    235 	dhandle = ACPI_HANDLE(&pdev->dev);
    236 	if (!dhandle)
    237 		return NULL;
    238 #endif
    239 
    240 	if (!acpi_check_dsm(dhandle, &intel_dsm_guid, INTEL_DSM_REVISION_ID,
    241 			    1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) {
    242 		DRM_DEBUG_KMS("no _DSM method for intel device\n");
    243 		return NULL;
    244 	}
    245 
    246 	intel_dsm_platform_mux_info(dhandle);
    247 
    248 	return dhandle;
    249 }
    250 
    251 #ifdef __NetBSD__
    252 
    253 static int vga_count;
    254 static ACPI_HANDLE intel_dsm_handle;
    255 
    256 /* XXX from sys/dev/pci/vga_pcivar.h */
    257 #define	DEVICE_IS_VGA_PCI(class, id)					\
    258 	    (((PCI_CLASS(class) == PCI_CLASS_DISPLAY &&			\
    259 	      PCI_SUBCLASS(class) == PCI_SUBCLASS_DISPLAY_VGA) ||	\
    260 	     (PCI_CLASS(class) == PCI_CLASS_PREHISTORIC &&		\
    261 	      PCI_SUBCLASS(class) == PCI_SUBCLASS_PREHISTORIC_VGA)) ? 1 : 0)
    262 
    263 static int
    264 intel_dsm_vga_match(const struct pci_attach_args *pa)
    265 {
    266 
    267 	if (!DEVICE_IS_VGA_PCI(pa->pa_class, pa->pa_id))
    268 		return 0;
    269 
    270 	vga_count++;
    271 	struct acpi_devnode *node =
    272 	    acpi_pcidev_find(pci_get_segment(pa->pa_pc),
    273 		pa->pa_bus, pa->pa_device, pa->pa_function);
    274 	if (node != NULL && intel_dsm_handle == NULL)
    275 		intel_dsm_handle = intel_dsm_pci_probe(node->ad_handle);
    276 	return 0;
    277 }
    278 
    279 static bool intel_dsm_detect(struct drm_device *dev)
    280 {
    281 	char acpi_method_name[255] = { 0 };
    282 
    283 	vga_count = 0;
    284 	pci_find_device(&dev->pdev->pd_pa, intel_dsm_vga_match);
    285 
    286 	if (vga_count == 2 && intel_dsm_handle) {
    287 		const char *name = acpi_name(intel_dsm_handle);
    288 		strlcpy(acpi_method_name, name, sizeof(acpi_method_name));
    289 		DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
    290 				 acpi_method_name);
    291 		return true;
    292 	}
    293 
    294 	return false;
    295 }
    296 #else
    297 static bool intel_dsm_detect(void)
    298 {
    299 	acpi_handle dhandle = NULL;
    300 	char acpi_method_name[255] = { 0 };
    301 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
    302 	struct pci_dev *pdev = NULL;
    303 	int vga_count = 0;
    304 
    305 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
    306 		vga_count++;
    307 		dhandle = intel_dsm_pci_probe(pdev) ?: dhandle;
    308 	}
    309 
    310 	if (vga_count == 2 && dhandle) {
    311 		acpi_get_name(dhandle, ACPI_FULL_PATHNAME, &buffer);
    312 		DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n",
    313 				 acpi_method_name);
    314 		return true;
    315 	}
    316 
    317 	return false;
    318 }
    319 #endif
    320 
    321 #ifdef __NetBSD__
    322 void intel_register_dsm_handler(struct drm_i915_private *i915)
    323 {
    324 	if (!intel_dsm_detect(&i915->drm))
    325 		return;
    326 }
    327 #else
    328 void intel_register_dsm_handler(void)
    329 {
    330 	if (!intel_dsm_detect())
    331 		return;
    332 }
    333 #endif
    334 
    335 void intel_unregister_dsm_handler(void)
    336 {
    337 }
    338