xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision 8116188fdef5946bcbb2d73e41d7412a57ffb034)
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>
6a6ed76d7SBen Skeggs #include <acpi/video.h>
7*8116188fSDave Airlie #include <acpi/acpi.h>
8*8116188fSDave Airlie #include <linux/mxm-wmi.h>
96ee73861SBen Skeggs 
106ee73861SBen Skeggs #include "drmP.h"
116ee73861SBen Skeggs #include "drm.h"
126ee73861SBen Skeggs #include "drm_sarea.h"
136ee73861SBen Skeggs #include "drm_crtc_helper.h"
146ee73861SBen Skeggs #include "nouveau_drv.h"
156ee73861SBen Skeggs #include "nouveau_drm.h"
166ee73861SBen Skeggs #include "nv50_display.h"
17a6ed76d7SBen Skeggs #include "nouveau_connector.h"
186ee73861SBen Skeggs 
196a9ee8afSDave Airlie #include <linux/vga_switcheroo.h>
206a9ee8afSDave Airlie 
216ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED 0x00
226ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
236ee73861SBen Skeggs 
246ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE 0x01
256ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE_QUERY 0x00
266ee73861SBen Skeggs 
276ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02
286ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00
296ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10
306ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11
316ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12
326ee73861SBen Skeggs 
336ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03
346ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00
356ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01
366ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02
376ee73861SBen Skeggs 
386a9ee8afSDave Airlie static struct nouveau_dsm_priv {
396a9ee8afSDave Airlie 	bool dsm_detected;
406a9ee8afSDave Airlie 	acpi_handle dhandle;
41afeb3e11SDave Airlie 	acpi_handle rom_handle;
426a9ee8afSDave Airlie } nouveau_dsm_priv;
436a9ee8afSDave Airlie 
446a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = {
456ee73861SBen Skeggs 	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
466ee73861SBen Skeggs 	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
476ee73861SBen Skeggs };
486ee73861SBen Skeggs 
496e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
506a9ee8afSDave Airlie {
516ee73861SBen Skeggs 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
526ee73861SBen Skeggs 	struct acpi_object_list input;
536ee73861SBen Skeggs 	union acpi_object params[4];
546ee73861SBen Skeggs 	union acpi_object *obj;
556ee73861SBen Skeggs 	int err;
566ee73861SBen Skeggs 
576ee73861SBen Skeggs 	input.count = 4;
586ee73861SBen Skeggs 	input.pointer = params;
596ee73861SBen Skeggs 	params[0].type = ACPI_TYPE_BUFFER;
606a9ee8afSDave Airlie 	params[0].buffer.length = sizeof(nouveau_dsm_muid);
616a9ee8afSDave Airlie 	params[0].buffer.pointer = (char *)nouveau_dsm_muid;
626ee73861SBen Skeggs 	params[1].type = ACPI_TYPE_INTEGER;
636ee73861SBen Skeggs 	params[1].integer.value = 0x00000102;
646ee73861SBen Skeggs 	params[2].type = ACPI_TYPE_INTEGER;
656ee73861SBen Skeggs 	params[2].integer.value = func;
666ee73861SBen Skeggs 	params[3].type = ACPI_TYPE_INTEGER;
676ee73861SBen Skeggs 	params[3].integer.value = arg;
686ee73861SBen Skeggs 
696ee73861SBen Skeggs 	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
706ee73861SBen Skeggs 	if (err) {
716a9ee8afSDave Airlie 		printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
726ee73861SBen Skeggs 		return err;
736ee73861SBen Skeggs 	}
746ee73861SBen Skeggs 
756ee73861SBen Skeggs 	obj = (union acpi_object *)output.pointer;
766ee73861SBen Skeggs 
776ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_INTEGER)
786ee73861SBen Skeggs 		if (obj->integer.value == 0x80000002)
796ee73861SBen Skeggs 			return -ENODEV;
806ee73861SBen Skeggs 
816ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_BUFFER) {
826ee73861SBen Skeggs 		if (obj->buffer.length == 4 && result) {
836ee73861SBen Skeggs 			*result = 0;
846ee73861SBen Skeggs 			*result |= obj->buffer.pointer[0];
856ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[1] << 8);
866ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[2] << 16);
876ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[3] << 24);
886ee73861SBen Skeggs 		}
896ee73861SBen Skeggs 	}
906ee73861SBen Skeggs 
916ee73861SBen Skeggs 	kfree(output.pointer);
926ee73861SBen Skeggs 	return 0;
936ee73861SBen Skeggs }
946ee73861SBen Skeggs 
956a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
966ee73861SBen Skeggs {
97*8116188fSDave Airlie 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
986a9ee8afSDave Airlie 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
996ee73861SBen Skeggs }
1006ee73861SBen Skeggs 
1016a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
1026a9ee8afSDave Airlie {
1036a9ee8afSDave Airlie 	int arg;
1046a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1056a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1066a9ee8afSDave Airlie 	else
1076a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
1086a9ee8afSDave Airlie 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
1096ee73861SBen Skeggs 	return 0;
1106ee73861SBen Skeggs }
1116ee73861SBen Skeggs 
1126a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1136ee73861SBen Skeggs {
1146a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
115fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1166a9ee8afSDave Airlie 	else
117fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1186a9ee8afSDave Airlie }
1196ee73861SBen Skeggs 
1206a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1216a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1226a9ee8afSDave Airlie {
1236a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
1246a9ee8afSDave Airlie 		return 0;
1256a9ee8afSDave Airlie 
126fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
1276a9ee8afSDave Airlie }
1286a9ee8afSDave Airlie 
1296a9ee8afSDave Airlie static int nouveau_dsm_init(void)
1306a9ee8afSDave Airlie {
1316a9ee8afSDave Airlie 	return 0;
1326a9ee8afSDave Airlie }
1336a9ee8afSDave Airlie 
1346a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
1356a9ee8afSDave Airlie {
136d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
137d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
1386a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
139d1fbd923SDave Airlie 
140d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
141d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
142d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
143d1fbd923SDave Airlie 
1446a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
1456a9ee8afSDave Airlie }
1466a9ee8afSDave Airlie 
1476a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = {
1486a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
1496a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
1506a9ee8afSDave Airlie 	.init = nouveau_dsm_init,
1516a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
1526a9ee8afSDave Airlie };
1536a9ee8afSDave Airlie 
1546a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
1556a9ee8afSDave Airlie {
1566a9ee8afSDave Airlie 	acpi_handle dhandle, nvidia_handle;
1576a9ee8afSDave Airlie 	acpi_status status;
1586a9ee8afSDave Airlie 	int ret;
1596a9ee8afSDave Airlie 	uint32_t result;
1606a9ee8afSDave Airlie 
1616a9ee8afSDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
1626a9ee8afSDave Airlie 	if (!dhandle)
1636a9ee8afSDave Airlie 		return false;
164afeb3e11SDave Airlie 
1656a9ee8afSDave Airlie 	status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
1666a9ee8afSDave Airlie 	if (ACPI_FAILURE(status)) {
1676a9ee8afSDave Airlie 		return false;
1686a9ee8afSDave Airlie 	}
1696a9ee8afSDave Airlie 
170fc5ea29dSDave Airlie 	ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,
1716a9ee8afSDave Airlie 			  NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
1726a9ee8afSDave Airlie 	if (ret < 0)
1736ee73861SBen Skeggs 		return false;
1746ee73861SBen Skeggs 
1756a9ee8afSDave Airlie 	nouveau_dsm_priv.dhandle = dhandle;
1766ee73861SBen Skeggs 	return true;
1776ee73861SBen Skeggs }
1786a9ee8afSDave Airlie 
1796a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
1806a9ee8afSDave Airlie {
1816a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
1826a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
1836a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
1846a9ee8afSDave Airlie 	int has_dsm = 0;
1856a9ee8afSDave Airlie 	int vga_count = 0;
186*8116188fSDave Airlie 	bool guid_valid;
187*8116188fSDave Airlie 
188*8116188fSDave Airlie 	/* lookup the GUID */
189*8116188fSDave Airlie 	guid_valid = mxm_wmi_supported();
190*8116188fSDave Airlie 	if (!guid_valid)
191*8116188fSDave Airlie 		return false;
192*8116188fSDave Airlie 
193*8116188fSDave Airlie 	printk("MXM GUID detected in BIOS\n");
194afeb3e11SDave Airlie 
1956a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
1966a9ee8afSDave Airlie 		vga_count++;
1976a9ee8afSDave Airlie 
1986a9ee8afSDave Airlie 		has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
1996a9ee8afSDave Airlie 	}
2006a9ee8afSDave Airlie 
2016a9ee8afSDave Airlie 	if (vga_count == 2 && has_dsm) {
202fc5ea29dSDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
2036a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
2046a9ee8afSDave Airlie 		       acpi_method_name);
2056a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
2066a9ee8afSDave Airlie 		return true;
2076a9ee8afSDave Airlie 	}
2086a9ee8afSDave Airlie 	return false;
2096a9ee8afSDave Airlie }
2106a9ee8afSDave Airlie 
2116a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
2126a9ee8afSDave Airlie {
2136a9ee8afSDave Airlie 	bool r;
2146a9ee8afSDave Airlie 
2156a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
2166a9ee8afSDave Airlie 	if (!r)
2176a9ee8afSDave Airlie 		return;
2186a9ee8afSDave Airlie 
2196a9ee8afSDave Airlie 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
2206a9ee8afSDave Airlie }
2216a9ee8afSDave Airlie 
2226a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
2236a9ee8afSDave Airlie {
2246a9ee8afSDave Airlie 	vga_switcheroo_unregister_handler();
2256a9ee8afSDave Airlie }
226afeb3e11SDave Airlie 
227afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */
228afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
229afeb3e11SDave Airlie 			    int offset, int len)
230afeb3e11SDave Airlie {
231afeb3e11SDave Airlie 	acpi_status status;
232afeb3e11SDave Airlie 	union acpi_object rom_arg_elements[2], *obj;
233afeb3e11SDave Airlie 	struct acpi_object_list rom_arg;
234afeb3e11SDave Airlie 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
235afeb3e11SDave Airlie 
236afeb3e11SDave Airlie 	rom_arg.count = 2;
237afeb3e11SDave Airlie 	rom_arg.pointer = &rom_arg_elements[0];
238afeb3e11SDave Airlie 
239afeb3e11SDave Airlie 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
240afeb3e11SDave Airlie 	rom_arg_elements[0].integer.value = offset;
241afeb3e11SDave Airlie 
242afeb3e11SDave Airlie 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
243afeb3e11SDave Airlie 	rom_arg_elements[1].integer.value = len;
244afeb3e11SDave Airlie 
245afeb3e11SDave Airlie 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
246afeb3e11SDave Airlie 	if (ACPI_FAILURE(status)) {
247afeb3e11SDave Airlie 		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
248afeb3e11SDave Airlie 		return -ENODEV;
249afeb3e11SDave Airlie 	}
250afeb3e11SDave Airlie 	obj = (union acpi_object *)buffer.pointer;
251afeb3e11SDave Airlie 	memcpy(bios+offset, obj->buffer.pointer, len);
252afeb3e11SDave Airlie 	kfree(buffer.pointer);
253afeb3e11SDave Airlie 	return len;
254afeb3e11SDave Airlie }
255afeb3e11SDave Airlie 
256afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
257afeb3e11SDave Airlie {
258afeb3e11SDave Airlie 	acpi_status status;
259afeb3e11SDave Airlie 	acpi_handle dhandle, rom_handle;
260afeb3e11SDave Airlie 
261afeb3e11SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
262afeb3e11SDave Airlie 		return false;
263afeb3e11SDave Airlie 
264afeb3e11SDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
265afeb3e11SDave Airlie 	if (!dhandle)
266afeb3e11SDave Airlie 		return false;
267afeb3e11SDave Airlie 
268afeb3e11SDave Airlie 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
269afeb3e11SDave Airlie 	if (ACPI_FAILURE(status))
270afeb3e11SDave Airlie 		return false;
271afeb3e11SDave Airlie 
272afeb3e11SDave Airlie 	nouveau_dsm_priv.rom_handle = rom_handle;
273afeb3e11SDave Airlie 	return true;
274afeb3e11SDave Airlie }
275afeb3e11SDave Airlie 
276afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
277afeb3e11SDave Airlie {
278afeb3e11SDave Airlie 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
279afeb3e11SDave Airlie }
280a6ed76d7SBen Skeggs 
281a6ed76d7SBen Skeggs int
282a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
283a6ed76d7SBen Skeggs {
284a6ed76d7SBen Skeggs 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
285a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
286a6ed76d7SBen Skeggs 	acpi_handle handle;
287a6ed76d7SBen Skeggs 	int type, ret;
288a6ed76d7SBen Skeggs 	void *edid;
289a6ed76d7SBen Skeggs 
290a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
291a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
292a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
293a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
294a6ed76d7SBen Skeggs 		break;
295a6ed76d7SBen Skeggs 	default:
296a6ed76d7SBen Skeggs 		return -EINVAL;
297a6ed76d7SBen Skeggs 	}
298a6ed76d7SBen Skeggs 
299a6ed76d7SBen Skeggs 	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
300a6ed76d7SBen Skeggs 	if (!handle)
301a6ed76d7SBen Skeggs 		return -ENODEV;
302a6ed76d7SBen Skeggs 
303a6ed76d7SBen Skeggs 	ret = acpi_bus_get_device(handle, &acpidev);
304a6ed76d7SBen Skeggs 	if (ret)
305a6ed76d7SBen Skeggs 		return -ENODEV;
306a6ed76d7SBen Skeggs 
307a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
308a6ed76d7SBen Skeggs 	if (ret < 0)
309a6ed76d7SBen Skeggs 		return ret;
310a6ed76d7SBen Skeggs 
31124b102d3SBen Skeggs 	nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
312a6ed76d7SBen Skeggs 	return 0;
313a6ed76d7SBen Skeggs }
314