xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_acpi.c (revision 770c9d53527f316421c021ddaa11b2f94d5fbbc7)
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 
nouveau_is_optimus(void)67 bool nouveau_is_optimus(void) {
68 	return nouveau_dsm_priv.optimus_detected;
69 }
70 
nouveau_is_v1_dsm(void)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 
nouveau_optimus_dsm(acpi_handle handle,int func,int arg,uint32_t * result)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  */
nouveau_dsm_get_optimus_functions(acpi_handle handle)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 
nouveau_dsm(acpi_handle handle,int func,int arg)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 
nouveau_dsm_switch_mux(acpi_handle handle,int mux_id)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 
nouveau_dsm_set_discrete_state(acpi_handle handle,enum vga_switcheroo_state state)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 
nouveau_dsm_switchto(enum vga_switcheroo_client_id id)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 
nouveau_dsm_power_state(enum vga_switcheroo_client_id id,enum vga_switcheroo_state state)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 
nouveau_dsm_get_client_id(struct pci_dev * pdev)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  */
nouveau_pr3_present(struct pci_dev * pdev)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 
nouveau_dsm_pci_probe(struct pci_dev * pdev,acpi_handle * dhandle_out,bool * has_mux,bool * has_opt,bool * has_opt_flags,bool * has_pr3)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 
nouveau_dsm_detect(void)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 
nouveau_register_dsm_handler(void)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 */
nouveau_switcheroo_optimus_dsm(void)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 
nouveau_unregister_dsm_handler(void)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
nouveau_register_dsm_handler(void)399 void nouveau_register_dsm_handler(void) {}
nouveau_unregister_dsm_handler(void)400 void nouveau_unregister_dsm_handler(void) {}
nouveau_switcheroo_optimus_dsm(void)401 void nouveau_switcheroo_optimus_dsm(void) {}
402 #endif
403 
404 /* retrieve the ROM in 4k blocks */
nouveau_rom_call(acpi_handle rom_handle,uint8_t * bios,int offset,int len)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__
nouveau_acpi_rom_supported(struct acpi_devnode * acpidev)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 
nouveau_acpi_get_bios_chunk(uint8_t * bios,int offset,int len)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 *
nouveau_acpi_edid(struct drm_device * dev,struct drm_connector * connector)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