xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision a6ed76d7ffc62ffa474b41d31b011b6853c5de32)
16ee73861SBen Skeggs #include <linux/pci.h>
26ee73861SBen Skeggs #include <linux/acpi.h>
35a0e3ad6STejun Heo #include <linux/slab.h>
46ee73861SBen Skeggs #include <acpi/acpi_drivers.h>
56ee73861SBen Skeggs #include <acpi/acpi_bus.h>
6*a6ed76d7SBen Skeggs #include <acpi/video.h>
76ee73861SBen Skeggs 
86ee73861SBen Skeggs #include "drmP.h"
96ee73861SBen Skeggs #include "drm.h"
106ee73861SBen Skeggs #include "drm_sarea.h"
116ee73861SBen Skeggs #include "drm_crtc_helper.h"
126ee73861SBen Skeggs #include "nouveau_drv.h"
136ee73861SBen Skeggs #include "nouveau_drm.h"
146ee73861SBen Skeggs #include "nv50_display.h"
15*a6ed76d7SBen Skeggs #include "nouveau_connector.h"
166ee73861SBen Skeggs 
176a9ee8afSDave Airlie #include <linux/vga_switcheroo.h>
186a9ee8afSDave Airlie 
196ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED 0x00
206ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
216ee73861SBen Skeggs 
226ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE 0x01
236ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE_QUERY 0x00
246ee73861SBen Skeggs 
256ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02
266ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00
276ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10
286ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11
296ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12
306ee73861SBen Skeggs 
316ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03
326ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00
336ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01
346ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02
356ee73861SBen Skeggs 
366a9ee8afSDave Airlie static struct nouveau_dsm_priv {
376a9ee8afSDave Airlie 	bool dsm_detected;
386a9ee8afSDave Airlie 	acpi_handle dhandle;
39afeb3e11SDave Airlie 	acpi_handle rom_handle;
406a9ee8afSDave Airlie } nouveau_dsm_priv;
416a9ee8afSDave Airlie 
426a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = {
436ee73861SBen Skeggs 	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
446ee73861SBen Skeggs 	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
456ee73861SBen Skeggs };
466ee73861SBen Skeggs 
476e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
486a9ee8afSDave Airlie {
496ee73861SBen Skeggs 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
506ee73861SBen Skeggs 	struct acpi_object_list input;
516ee73861SBen Skeggs 	union acpi_object params[4];
526ee73861SBen Skeggs 	union acpi_object *obj;
536ee73861SBen Skeggs 	int err;
546ee73861SBen Skeggs 
556ee73861SBen Skeggs 	input.count = 4;
566ee73861SBen Skeggs 	input.pointer = params;
576ee73861SBen Skeggs 	params[0].type = ACPI_TYPE_BUFFER;
586a9ee8afSDave Airlie 	params[0].buffer.length = sizeof(nouveau_dsm_muid);
596a9ee8afSDave Airlie 	params[0].buffer.pointer = (char *)nouveau_dsm_muid;
606ee73861SBen Skeggs 	params[1].type = ACPI_TYPE_INTEGER;
616ee73861SBen Skeggs 	params[1].integer.value = 0x00000102;
626ee73861SBen Skeggs 	params[2].type = ACPI_TYPE_INTEGER;
636ee73861SBen Skeggs 	params[2].integer.value = func;
646ee73861SBen Skeggs 	params[3].type = ACPI_TYPE_INTEGER;
656ee73861SBen Skeggs 	params[3].integer.value = arg;
666ee73861SBen Skeggs 
676ee73861SBen Skeggs 	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
686ee73861SBen Skeggs 	if (err) {
696a9ee8afSDave Airlie 		printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
706ee73861SBen Skeggs 		return err;
716ee73861SBen Skeggs 	}
726ee73861SBen Skeggs 
736ee73861SBen Skeggs 	obj = (union acpi_object *)output.pointer;
746ee73861SBen Skeggs 
756ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_INTEGER)
766ee73861SBen Skeggs 		if (obj->integer.value == 0x80000002)
776ee73861SBen Skeggs 			return -ENODEV;
786ee73861SBen Skeggs 
796ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_BUFFER) {
806ee73861SBen Skeggs 		if (obj->buffer.length == 4 && result) {
816ee73861SBen Skeggs 			*result = 0;
826ee73861SBen Skeggs 			*result |= obj->buffer.pointer[0];
836ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[1] << 8);
846ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[2] << 16);
856ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[3] << 24);
866ee73861SBen Skeggs 		}
876ee73861SBen Skeggs 	}
886ee73861SBen Skeggs 
896ee73861SBen Skeggs 	kfree(output.pointer);
906ee73861SBen Skeggs 	return 0;
916ee73861SBen Skeggs }
926ee73861SBen Skeggs 
936a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
946ee73861SBen Skeggs {
956a9ee8afSDave Airlie 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
966ee73861SBen Skeggs }
976ee73861SBen Skeggs 
986a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
996a9ee8afSDave Airlie {
1006a9ee8afSDave Airlie 	int arg;
1016a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1026a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1036a9ee8afSDave Airlie 	else
1046a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
1056a9ee8afSDave Airlie 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
1066ee73861SBen Skeggs 	return 0;
1076ee73861SBen Skeggs }
1086ee73861SBen Skeggs 
1096a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1106ee73861SBen Skeggs {
1116a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
112fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1136a9ee8afSDave Airlie 	else
114fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1156a9ee8afSDave Airlie }
1166ee73861SBen Skeggs 
1176a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1186a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1196a9ee8afSDave Airlie {
1206a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
1216a9ee8afSDave Airlie 		return 0;
1226a9ee8afSDave Airlie 
123fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
1246a9ee8afSDave Airlie }
1256a9ee8afSDave Airlie 
1266a9ee8afSDave Airlie static int nouveau_dsm_init(void)
1276a9ee8afSDave Airlie {
1286a9ee8afSDave Airlie 	return 0;
1296a9ee8afSDave Airlie }
1306a9ee8afSDave Airlie 
1316a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
1326a9ee8afSDave Airlie {
1336a9ee8afSDave Airlie 	if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
1346a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
1356a9ee8afSDave Airlie 	else
1366a9ee8afSDave Airlie 		return VGA_SWITCHEROO_DIS;
1376a9ee8afSDave Airlie }
1386a9ee8afSDave Airlie 
1396a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = {
1406a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
1416a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
1426a9ee8afSDave Airlie 	.init = nouveau_dsm_init,
1436a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
1446a9ee8afSDave Airlie };
1456a9ee8afSDave Airlie 
1466a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
1476a9ee8afSDave Airlie {
1486a9ee8afSDave Airlie 	acpi_handle dhandle, nvidia_handle;
1496a9ee8afSDave Airlie 	acpi_status status;
1506a9ee8afSDave Airlie 	int ret;
1516a9ee8afSDave Airlie 	uint32_t result;
1526a9ee8afSDave Airlie 
1536a9ee8afSDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
1546a9ee8afSDave Airlie 	if (!dhandle)
1556a9ee8afSDave Airlie 		return false;
156afeb3e11SDave Airlie 
1576a9ee8afSDave Airlie 	status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
1586a9ee8afSDave Airlie 	if (ACPI_FAILURE(status)) {
1596a9ee8afSDave Airlie 		return false;
1606a9ee8afSDave Airlie 	}
1616a9ee8afSDave Airlie 
162fc5ea29dSDave Airlie 	ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,
1636a9ee8afSDave Airlie 			  NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
1646a9ee8afSDave Airlie 	if (ret < 0)
1656ee73861SBen Skeggs 		return false;
1666ee73861SBen Skeggs 
1676a9ee8afSDave Airlie 	nouveau_dsm_priv.dhandle = dhandle;
1686ee73861SBen Skeggs 	return true;
1696ee73861SBen Skeggs }
1706a9ee8afSDave Airlie 
1716a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
1726a9ee8afSDave Airlie {
1736a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
1746a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
1756a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
1766a9ee8afSDave Airlie 	int has_dsm = 0;
1776a9ee8afSDave Airlie 	int vga_count = 0;
178afeb3e11SDave Airlie 
1796a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
1806a9ee8afSDave Airlie 		vga_count++;
1816a9ee8afSDave Airlie 
1826a9ee8afSDave Airlie 		has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
1836a9ee8afSDave Airlie 	}
1846a9ee8afSDave Airlie 
1856a9ee8afSDave Airlie 	if (vga_count == 2 && has_dsm) {
186fc5ea29dSDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
1876a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
1886a9ee8afSDave Airlie 		       acpi_method_name);
1896a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
1906a9ee8afSDave Airlie 		return true;
1916a9ee8afSDave Airlie 	}
1926a9ee8afSDave Airlie 	return false;
1936a9ee8afSDave Airlie }
1946a9ee8afSDave Airlie 
1956a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
1966a9ee8afSDave Airlie {
1976a9ee8afSDave Airlie 	bool r;
1986a9ee8afSDave Airlie 
1996a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
2006a9ee8afSDave Airlie 	if (!r)
2016a9ee8afSDave Airlie 		return;
2026a9ee8afSDave Airlie 
2036a9ee8afSDave Airlie 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
2046a9ee8afSDave Airlie }
2056a9ee8afSDave Airlie 
2066a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
2076a9ee8afSDave Airlie {
2086a9ee8afSDave Airlie 	vga_switcheroo_unregister_handler();
2096a9ee8afSDave Airlie }
210afeb3e11SDave Airlie 
211afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */
212afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
213afeb3e11SDave Airlie 			    int offset, int len)
214afeb3e11SDave Airlie {
215afeb3e11SDave Airlie 	acpi_status status;
216afeb3e11SDave Airlie 	union acpi_object rom_arg_elements[2], *obj;
217afeb3e11SDave Airlie 	struct acpi_object_list rom_arg;
218afeb3e11SDave Airlie 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
219afeb3e11SDave Airlie 
220afeb3e11SDave Airlie 	rom_arg.count = 2;
221afeb3e11SDave Airlie 	rom_arg.pointer = &rom_arg_elements[0];
222afeb3e11SDave Airlie 
223afeb3e11SDave Airlie 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
224afeb3e11SDave Airlie 	rom_arg_elements[0].integer.value = offset;
225afeb3e11SDave Airlie 
226afeb3e11SDave Airlie 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
227afeb3e11SDave Airlie 	rom_arg_elements[1].integer.value = len;
228afeb3e11SDave Airlie 
229afeb3e11SDave Airlie 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
230afeb3e11SDave Airlie 	if (ACPI_FAILURE(status)) {
231afeb3e11SDave Airlie 		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
232afeb3e11SDave Airlie 		return -ENODEV;
233afeb3e11SDave Airlie 	}
234afeb3e11SDave Airlie 	obj = (union acpi_object *)buffer.pointer;
235afeb3e11SDave Airlie 	memcpy(bios+offset, obj->buffer.pointer, len);
236afeb3e11SDave Airlie 	kfree(buffer.pointer);
237afeb3e11SDave Airlie 	return len;
238afeb3e11SDave Airlie }
239afeb3e11SDave Airlie 
240afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
241afeb3e11SDave Airlie {
242afeb3e11SDave Airlie 	acpi_status status;
243afeb3e11SDave Airlie 	acpi_handle dhandle, rom_handle;
244afeb3e11SDave Airlie 
245afeb3e11SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
246afeb3e11SDave Airlie 		return false;
247afeb3e11SDave Airlie 
248afeb3e11SDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
249afeb3e11SDave Airlie 	if (!dhandle)
250afeb3e11SDave Airlie 		return false;
251afeb3e11SDave Airlie 
252afeb3e11SDave Airlie 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
253afeb3e11SDave Airlie 	if (ACPI_FAILURE(status))
254afeb3e11SDave Airlie 		return false;
255afeb3e11SDave Airlie 
256afeb3e11SDave Airlie 	nouveau_dsm_priv.rom_handle = rom_handle;
257afeb3e11SDave Airlie 	return true;
258afeb3e11SDave Airlie }
259afeb3e11SDave Airlie 
260afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
261afeb3e11SDave Airlie {
262afeb3e11SDave Airlie 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
263afeb3e11SDave Airlie }
264*a6ed76d7SBen Skeggs 
265*a6ed76d7SBen Skeggs int
266*a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
267*a6ed76d7SBen Skeggs {
268*a6ed76d7SBen Skeggs 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
269*a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
270*a6ed76d7SBen Skeggs 	acpi_handle handle;
271*a6ed76d7SBen Skeggs 	int type, ret;
272*a6ed76d7SBen Skeggs 	void *edid;
273*a6ed76d7SBen Skeggs 
274*a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
275*a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
276*a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
277*a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
278*a6ed76d7SBen Skeggs 		break;
279*a6ed76d7SBen Skeggs 	default:
280*a6ed76d7SBen Skeggs 		return -EINVAL;
281*a6ed76d7SBen Skeggs 	}
282*a6ed76d7SBen Skeggs 
283*a6ed76d7SBen Skeggs 	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
284*a6ed76d7SBen Skeggs 	if (!handle)
285*a6ed76d7SBen Skeggs 		return -ENODEV;
286*a6ed76d7SBen Skeggs 
287*a6ed76d7SBen Skeggs 	ret = acpi_bus_get_device(handle, &acpidev);
288*a6ed76d7SBen Skeggs 	if (ret)
289*a6ed76d7SBen Skeggs 		return -ENODEV;
290*a6ed76d7SBen Skeggs 
291*a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
292*a6ed76d7SBen Skeggs 	if (ret < 0)
293*a6ed76d7SBen Skeggs 		return ret;
294*a6ed76d7SBen Skeggs 
295*a6ed76d7SBen Skeggs 	nv_connector->edid = edid;
296*a6ed76d7SBen Skeggs 	return 0;
297*a6ed76d7SBen Skeggs }
298