Home | History | Annotate | Line # | Download | only in nouveau
      1 /*	$NetBSD: nouveau_acpi.c,v 1.5 2024/04/16 14:34:02 riastradh Exp $	*/
      2 
      3 // SPDX-License-Identifier: MIT
      4 #include <sys/cdefs.h>
      5 __KERNEL_RCSID(0, "$NetBSD: nouveau_acpi.c,v 1.5 2024/04/16 14:34:02 riastradh Exp $");
      6 
      7 #include <linux/pci.h>
      8 #include <linux/acpi.h>
      9 #include <linux/slab.h>
     10 #include <linux/mxm-wmi.h>
     11 #include <linux/vga_switcheroo.h>
     12 #include <drm/drm_edid.h>
     13 #include <acpi/video.h>
     14 
     15 #include "nouveau_drv.h"
     16 #include "nouveau_acpi.h"
     17 
     18 #ifdef __NetBSD__
     19 #include <dev/acpi/acpireg.h>
     20 #define	_COMPONENT	ACPI_DISPLAY_COMPONENT
     21 ACPI_MODULE_NAME("nouveau_acpi")
     22 #include <linux/nbsd-namespace-acpi.h>
     23 #endif
     24 
     25 #define NOUVEAU_DSM_LED 0x02
     26 #define NOUVEAU_DSM_LED_STATE 0x00
     27 #define NOUVEAU_DSM_LED_OFF 0x10
     28 #define NOUVEAU_DSM_LED_STAMINA 0x11
     29 #define NOUVEAU_DSM_LED_SPEED 0x12
     30 
     31 #define NOUVEAU_DSM_POWER 0x03
     32 #define NOUVEAU_DSM_POWER_STATE 0x00
     33 #define NOUVEAU_DSM_POWER_SPEED 0x01
     34 #define NOUVEAU_DSM_POWER_STAMINA 0x02
     35 
     36 #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
     37 #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
     38 
     39 #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
     40 #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
     41 #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
     42 
     43 #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
     44 
     45 /* result of the optimus caps function */
     46 #define OPTIMUS_ENABLED (1 << 0)
     47 #define OPTIMUS_STATUS_MASK (3 << 3)
     48 #define OPTIMUS_STATUS_OFF  (0 << 3)
     49 #define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
     50 #define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
     51 #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
     52 #define OPTIMUS_CAPS_MASK (7 << 24)
     53 #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
     54 
     55 #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
     56 #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
     57 
     58 static struct nouveau_dsm_priv {
     59 	bool dsm_detected;
     60 	bool optimus_detected;
     61 	bool optimus_flags_detected;
     62 	bool optimus_skip_dsm;
     63 	acpi_handle dhandle;
     64 	acpi_handle rom_handle;
     65 } nouveau_dsm_priv;
     66 
     67 bool nouveau_is_optimus(void) {
     68 	return nouveau_dsm_priv.optimus_detected;
     69 }
     70 
     71 bool nouveau_is_v1_dsm(void) {
     72 	return nouveau_dsm_priv.dsm_detected;
     73 }
     74 
     75 #ifdef CONFIG_VGA_SWITCHEROO
     76 static const guid_t nouveau_dsm_muid =
     77 	GUID_INIT(0x9D95A0A0, 0x0060, 0x4D48,
     78 		  0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4);
     79 
     80 static const guid_t nouveau_op_dsm_muid =
     81 	GUID_INIT(0xA486D8F8, 0x0BDA, 0x471B,
     82 		  0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0);
     83 
     84 static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
     85 {
     86 	int i;
     87 	union acpi_object *obj;
     88 	char args_buff[4];
     89 	union acpi_object argv4 = {
     90 		.buffer.type = ACPI_TYPE_BUFFER,
     91 		.buffer.length = 4,
     92 		.buffer.pointer = args_buff
     93 	};
     94 
     95 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
     96 	for (i = 0; i < 4; i++)
     97 		args_buff[i] = (arg >> i * 8) & 0xFF;
     98 
     99 	*result = 0;
    100 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_op_dsm_muid, 0x00000100,
    101 				      func, &argv4, ACPI_TYPE_BUFFER);
    102 	if (!obj) {
    103 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
    104 		return AE_ERROR;
    105 	} else {
    106 		if (obj->buffer.length == 4) {
    107 			*result |= obj->buffer.pointer[0];
    108 			*result |= (obj->buffer.pointer[1] << 8);
    109 			*result |= (obj->buffer.pointer[2] << 16);
    110 			*result |= (obj->buffer.pointer[3] << 24);
    111 		}
    112 		ACPI_FREE(obj);
    113 	}
    114 
    115 	return 0;
    116 }
    117 
    118 /*
    119  * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
    120  * requirements on the fourth parameter, so a private implementation
    121  * instead of using acpi_check_dsm().
    122  */
    123 static int nouveau_dsm_get_optimus_functions(acpi_handle handle)
    124 {
    125 	int result;
    126 
    127 	/*
    128 	 * Function 0 returns a Buffer containing available functions.
    129 	 * The args parameter is ignored for function 0, so just put 0 in it
    130 	 */
    131 	if (nouveau_optimus_dsm(handle, 0, 0, &result))
    132 		return 0;
    133 
    134 	/*
    135 	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
    136 	 * If the n-th bit is enabled, function n is supported
    137 	 */
    138 	if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS))
    139 		return result;
    140 	return 0;
    141 }
    142 
    143 static int nouveau_dsm(acpi_handle handle, int func, int arg)
    144 {
    145 	int ret = 0;
    146 	union acpi_object *obj;
    147 	union acpi_object argv4 = {
    148 		.integer.type = ACPI_TYPE_INTEGER,
    149 		.integer.value = arg,
    150 	};
    151 
    152 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_dsm_muid, 0x00000102,
    153 				      func, &argv4, ACPI_TYPE_INTEGER);
    154 	if (!obj) {
    155 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
    156 		return AE_ERROR;
    157 	} else {
    158 		if (obj->integer.value == 0x80000002)
    159 			ret = -ENODEV;
    160 		ACPI_FREE(obj);
    161 	}
    162 
    163 	return ret;
    164 }
    165 
    166 static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
    167 {
    168 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
    169 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
    170 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
    171 }
    172 
    173 static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
    174 {
    175 	int arg;
    176 	if (state == VGA_SWITCHEROO_ON)
    177 		arg = NOUVEAU_DSM_POWER_SPEED;
    178 	else
    179 		arg = NOUVEAU_DSM_POWER_STAMINA;
    180 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
    181 	return 0;
    182 }
    183 
    184 static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
    185 {
    186 	if (!nouveau_dsm_priv.dsm_detected)
    187 		return 0;
    188 	if (id == VGA_SWITCHEROO_IGD)
    189 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
    190 	else
    191 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
    192 }
    193 
    194 static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
    195 				   enum vga_switcheroo_state state)
    196 {
    197 	if (id == VGA_SWITCHEROO_IGD)
    198 		return 0;
    199 
    200 	/* Optimus laptops have the card already disabled in
    201 	 * nouveau_switcheroo_set_state */
    202 	if (!nouveau_dsm_priv.dsm_detected)
    203 		return 0;
    204 
    205 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
    206 }
    207 
    208 static enum vga_switcheroo_client_id nouveau_dsm_get_client_id(struct pci_dev *pdev)
    209 {
    210 	/* easy option one - intel vendor ID means Integrated */
    211 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
    212 		return VGA_SWITCHEROO_IGD;
    213 
    214 	/* is this device on Bus 0? - this may need improving */
    215 	if (pdev->bus->number == 0)
    216 		return VGA_SWITCHEROO_IGD;
    217 
    218 	return VGA_SWITCHEROO_DIS;
    219 }
    220 
    221 static const struct vga_switcheroo_handler nouveau_dsm_handler = {
    222 	.switchto = nouveau_dsm_switchto,
    223 	.power_state = nouveau_dsm_power_state,
    224 	.get_client_id = nouveau_dsm_get_client_id,
    225 };
    226 
    227 /*
    228  * Firmware supporting Windows 8 or later do not use _DSM to put the device into
    229  * D3cold, they instead rely on disabling power resources on the parent.
    230  */
    231 static bool nouveau_pr3_present(struct pci_dev *pdev)
    232 {
    233 	struct pci_dev *parent_pdev = pci_upstream_bridge(pdev);
    234 	struct acpi_device *parent_adev;
    235 
    236 	if (!parent_pdev)
    237 		return false;
    238 
    239 	if (!parent_pdev->bridge_d3) {
    240 		/*
    241 		 * Parent PCI bridge is currently not power managed.
    242 		 * Since userspace can change these afterwards to be on
    243 		 * the safe side we stick with _DSM and prevent usage of
    244 		 * _PR3 from the bridge.
    245 		 */
    246 		pci_d3cold_disable(pdev);
    247 		return false;
    248 	}
    249 
    250 	parent_adev = ACPI_COMPANION(&parent_pdev->dev);
    251 	if (!parent_adev)
    252 		return false;
    253 
    254 	return parent_adev->power.flags.power_resources &&
    255 		acpi_has_method(parent_adev->handle, "_PR3");
    256 }
    257 
    258 static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
    259 				  bool *has_mux, bool *has_opt,
    260 				  bool *has_opt_flags, bool *has_pr3)
    261 {
    262 	acpi_handle dhandle;
    263 	bool supports_mux;
    264 	int optimus_funcs;
    265 
    266 #ifdef __NetBSD__
    267 	dhandle = pdev->pd_ad->ad_handle;
    268 #else
    269 	dhandle = ACPI_HANDLE(&pdev->dev);
    270 #endif
    271 	if (!dhandle)
    272 		return;
    273 
    274 	if (!acpi_has_method(dhandle, "_DSM"))
    275 		return;
    276 
    277 	supports_mux = acpi_check_dsm(dhandle, &nouveau_dsm_muid, 0x00000102,
    278 				      1 << NOUVEAU_DSM_POWER);
    279 	optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle);
    280 
    281 	/* Does not look like a Nvidia device. */
    282 	if (!supports_mux && !optimus_funcs)
    283 		return;
    284 
    285 	*dhandle_out = dhandle;
    286 	*has_mux = supports_mux;
    287 	*has_opt = !!optimus_funcs;
    288 	*has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
    289 	*has_pr3 = false;
    290 
    291 	if (optimus_funcs) {
    292 		uint32_t result;
    293 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
    294 				    &result);
    295 		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
    296 			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
    297 			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
    298 			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
    299 
    300 		*has_pr3 = nouveau_pr3_present(pdev);
    301 	}
    302 }
    303 
    304 static bool nouveau_dsm_detect(void)
    305 {
    306 	char acpi_method_name[255] = { 0 };
    307 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
    308 	struct pci_dev *pdev = NULL;
    309 	acpi_handle dhandle = NULL;
    310 	bool has_mux = false;
    311 	bool has_optimus = false;
    312 	bool has_optimus_flags = false;
    313 	bool has_power_resources = false;
    314 	int vga_count = 0;
    315 	bool guid_valid;
    316 	bool ret = false;
    317 
    318 	/* lookup the MXM GUID */
    319 	guid_valid = mxm_wmi_supported();
    320 
    321 	if (guid_valid)
    322 		printk("MXM: GUID detected in BIOS\n");
    323 
    324 	/* now do DSM detection */
    325 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
    326 		vga_count++;
    327 
    328 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
    329 				      &has_optimus_flags, &has_power_resources);
    330 	}
    331 
    332 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
    333 		vga_count++;
    334 
    335 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
    336 				      &has_optimus_flags, &has_power_resources);
    337 	}
    338 
    339 	/* find the optimus DSM or the old v1 DSM */
    340 	if (has_optimus) {
    341 		nouveau_dsm_priv.dhandle = dhandle;
    342 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
    343 			&buffer);
    344 		pr_info("VGA switcheroo: detected Optimus DSM method %s handle\n",
    345 			acpi_method_name);
    346 		if (has_power_resources)
    347 			pr_info("nouveau: detected PR support, will not use DSM\n");
    348 		nouveau_dsm_priv.optimus_detected = true;
    349 		nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags;
    350 		nouveau_dsm_priv.optimus_skip_dsm = has_power_resources;
    351 		ret = true;
    352 	} else if (vga_count == 2 && has_mux && guid_valid) {
    353 		nouveau_dsm_priv.dhandle = dhandle;
    354 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
    355 			&buffer);
    356 		pr_info("VGA switcheroo: detected DSM switching method %s handle\n",
    357 			acpi_method_name);
    358 		nouveau_dsm_priv.dsm_detected = true;
    359 		ret = true;
    360 	}
    361 
    362 
    363 	return ret;
    364 }
    365 
    366 void nouveau_register_dsm_handler(void)
    367 {
    368 	bool r;
    369 
    370 	r = nouveau_dsm_detect();
    371 	if (!r)
    372 		return;
    373 
    374 	vga_switcheroo_register_handler(&nouveau_dsm_handler, 0);
    375 }
    376 
    377 /* Must be called for Optimus models before the card can be turned off */
    378 void nouveau_switcheroo_optimus_dsm(void)
    379 {
    380 	u32 result = 0;
    381 	if (!nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.optimus_skip_dsm)
    382 		return;
    383 
    384 	if (nouveau_dsm_priv.optimus_flags_detected)
    385 		nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
    386 				    0x3, &result);
    387 
    388 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
    389 		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
    390 
    391 }
    392 
    393 void nouveau_unregister_dsm_handler(void)
    394 {
    395 	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
    396 		vga_switcheroo_unregister_handler();
    397 }
    398 #else
    399 void nouveau_register_dsm_handler(void) {}
    400 void nouveau_unregister_dsm_handler(void) {}
    401 void nouveau_switcheroo_optimus_dsm(void) {}
    402 #endif
    403 
    404 /* retrieve the ROM in 4k blocks */
    405 static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
    406 			    int offset, int len)
    407 {
    408 	acpi_status status;
    409 	union acpi_object rom_arg_elements[2], *obj;
    410 	struct acpi_object_list rom_arg;
    411 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
    412 
    413 	rom_arg.count = 2;
    414 	rom_arg.pointer = &rom_arg_elements[0];
    415 
    416 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
    417 	rom_arg_elements[0].integer.value = offset;
    418 
    419 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
    420 	rom_arg_elements[1].integer.value = len;
    421 
    422 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
    423 	if (ACPI_FAILURE(status)) {
    424 		pr_info("failed to evaluate ROM got %s\n",
    425 			acpi_format_exception(status));
    426 		return -ENODEV;
    427 	}
    428 	obj = (union acpi_object *)buffer.pointer;
    429 	len = min(len, (int)obj->buffer.length);
    430 	memcpy(bios+offset, obj->buffer.pointer, len);
    431 	ACPI_FREE(buffer.pointer);
    432 	return len;
    433 }
    434 
    435 #ifdef __NetBSD__
    436 bool nouveau_acpi_rom_supported(struct acpi_devnode *acpidev)
    437 #else
    438 bool nouveau_acpi_rom_supported(struct device *dev)
    439 #endif
    440 {
    441 	acpi_status status;
    442 	acpi_handle dhandle, rom_handle;
    443 
    444 #ifdef __NetBSD__
    445 	dhandle = (acpidev ? acpidev->ad_handle : NULL);
    446 #else
    447 	dhandle = ACPI_HANDLE(dev);
    448 #endif
    449 	if (!dhandle)
    450 		return false;
    451 
    452 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
    453 	if (ACPI_FAILURE(status))
    454 		return false;
    455 
    456 	nouveau_dsm_priv.rom_handle = rom_handle;
    457 	return true;
    458 }
    459 
    460 int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
    461 {
    462 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
    463 }
    464 
    465 void *
    466 nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
    467 {
    468 #ifdef __NetBSD__		/* XXX nouveau acpi video */
    469 	return NULL;
    470 #else
    471 	struct acpi_device *acpidev;
    472 	acpi_handle handle;
    473 	int type, ret;
    474 	void *edid;
    475 
    476 	switch (connector->connector_type) {
    477 	case DRM_MODE_CONNECTOR_LVDS:
    478 	case DRM_MODE_CONNECTOR_eDP:
    479 		type = ACPI_VIDEO_DISPLAY_LCD;
    480 		break;
    481 	default:
    482 		return NULL;
    483 	}
    484 
    485 #ifdef __NetBSD__
    486 	handle = (dev->pdev->pd_ad ? dev->pdev->pd_ad->ad_handle : NULL);
    487 #else
    488 	handle = ACPI_HANDLE(&dev->pdev->dev);
    489 #endif
    490 	if (!handle)
    491 		return NULL;
    492 
    493 	ret = acpi_bus_get_device(handle, &acpidev);
    494 	if (ret)
    495 		return NULL;
    496 
    497 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
    498 	if (ret < 0)
    499 		return NULL;
    500 
    501 	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
    502 #endif
    503 }
    504