xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision d1fbd923da0f982f12b61a44e74c2e1f74c6ef56)
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>
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"
15a6ed76d7SBen 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 {
133*d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
134*d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
1356a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
136*d1fbd923SDave Airlie 
137*d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
138*d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
139*d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
140*d1fbd923SDave Airlie 
1416a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
1426a9ee8afSDave Airlie }
1436a9ee8afSDave Airlie 
1446a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = {
1456a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
1466a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
1476a9ee8afSDave Airlie 	.init = nouveau_dsm_init,
1486a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
1496a9ee8afSDave Airlie };
1506a9ee8afSDave Airlie 
1516a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
1526a9ee8afSDave Airlie {
1536a9ee8afSDave Airlie 	acpi_handle dhandle, nvidia_handle;
1546a9ee8afSDave Airlie 	acpi_status status;
1556a9ee8afSDave Airlie 	int ret;
1566a9ee8afSDave Airlie 	uint32_t result;
1576a9ee8afSDave Airlie 
1586a9ee8afSDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
1596a9ee8afSDave Airlie 	if (!dhandle)
1606a9ee8afSDave Airlie 		return false;
161afeb3e11SDave Airlie 
1626a9ee8afSDave Airlie 	status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
1636a9ee8afSDave Airlie 	if (ACPI_FAILURE(status)) {
1646a9ee8afSDave Airlie 		return false;
1656a9ee8afSDave Airlie 	}
1666a9ee8afSDave Airlie 
167fc5ea29dSDave Airlie 	ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,
1686a9ee8afSDave Airlie 			  NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
1696a9ee8afSDave Airlie 	if (ret < 0)
1706ee73861SBen Skeggs 		return false;
1716ee73861SBen Skeggs 
1726a9ee8afSDave Airlie 	nouveau_dsm_priv.dhandle = dhandle;
1736ee73861SBen Skeggs 	return true;
1746ee73861SBen Skeggs }
1756a9ee8afSDave Airlie 
1766a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
1776a9ee8afSDave Airlie {
1786a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
1796a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
1806a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
1816a9ee8afSDave Airlie 	int has_dsm = 0;
1826a9ee8afSDave Airlie 	int vga_count = 0;
183afeb3e11SDave Airlie 
1846a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
1856a9ee8afSDave Airlie 		vga_count++;
1866a9ee8afSDave Airlie 
1876a9ee8afSDave Airlie 		has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
1886a9ee8afSDave Airlie 	}
1896a9ee8afSDave Airlie 
1906a9ee8afSDave Airlie 	if (vga_count == 2 && has_dsm) {
191fc5ea29dSDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
1926a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
1936a9ee8afSDave Airlie 		       acpi_method_name);
1946a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
1956a9ee8afSDave Airlie 		return true;
1966a9ee8afSDave Airlie 	}
1976a9ee8afSDave Airlie 	return false;
1986a9ee8afSDave Airlie }
1996a9ee8afSDave Airlie 
2006a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
2016a9ee8afSDave Airlie {
2026a9ee8afSDave Airlie 	bool r;
2036a9ee8afSDave Airlie 
2046a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
2056a9ee8afSDave Airlie 	if (!r)
2066a9ee8afSDave Airlie 		return;
2076a9ee8afSDave Airlie 
2086a9ee8afSDave Airlie 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
2096a9ee8afSDave Airlie }
2106a9ee8afSDave Airlie 
2116a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
2126a9ee8afSDave Airlie {
2136a9ee8afSDave Airlie 	vga_switcheroo_unregister_handler();
2146a9ee8afSDave Airlie }
215afeb3e11SDave Airlie 
216afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */
217afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
218afeb3e11SDave Airlie 			    int offset, int len)
219afeb3e11SDave Airlie {
220afeb3e11SDave Airlie 	acpi_status status;
221afeb3e11SDave Airlie 	union acpi_object rom_arg_elements[2], *obj;
222afeb3e11SDave Airlie 	struct acpi_object_list rom_arg;
223afeb3e11SDave Airlie 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
224afeb3e11SDave Airlie 
225afeb3e11SDave Airlie 	rom_arg.count = 2;
226afeb3e11SDave Airlie 	rom_arg.pointer = &rom_arg_elements[0];
227afeb3e11SDave Airlie 
228afeb3e11SDave Airlie 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
229afeb3e11SDave Airlie 	rom_arg_elements[0].integer.value = offset;
230afeb3e11SDave Airlie 
231afeb3e11SDave Airlie 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
232afeb3e11SDave Airlie 	rom_arg_elements[1].integer.value = len;
233afeb3e11SDave Airlie 
234afeb3e11SDave Airlie 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
235afeb3e11SDave Airlie 	if (ACPI_FAILURE(status)) {
236afeb3e11SDave Airlie 		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
237afeb3e11SDave Airlie 		return -ENODEV;
238afeb3e11SDave Airlie 	}
239afeb3e11SDave Airlie 	obj = (union acpi_object *)buffer.pointer;
240afeb3e11SDave Airlie 	memcpy(bios+offset, obj->buffer.pointer, len);
241afeb3e11SDave Airlie 	kfree(buffer.pointer);
242afeb3e11SDave Airlie 	return len;
243afeb3e11SDave Airlie }
244afeb3e11SDave Airlie 
245afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
246afeb3e11SDave Airlie {
247afeb3e11SDave Airlie 	acpi_status status;
248afeb3e11SDave Airlie 	acpi_handle dhandle, rom_handle;
249afeb3e11SDave Airlie 
250afeb3e11SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
251afeb3e11SDave Airlie 		return false;
252afeb3e11SDave Airlie 
253afeb3e11SDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
254afeb3e11SDave Airlie 	if (!dhandle)
255afeb3e11SDave Airlie 		return false;
256afeb3e11SDave Airlie 
257afeb3e11SDave Airlie 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
258afeb3e11SDave Airlie 	if (ACPI_FAILURE(status))
259afeb3e11SDave Airlie 		return false;
260afeb3e11SDave Airlie 
261afeb3e11SDave Airlie 	nouveau_dsm_priv.rom_handle = rom_handle;
262afeb3e11SDave Airlie 	return true;
263afeb3e11SDave Airlie }
264afeb3e11SDave Airlie 
265afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
266afeb3e11SDave Airlie {
267afeb3e11SDave Airlie 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
268afeb3e11SDave Airlie }
269a6ed76d7SBen Skeggs 
270a6ed76d7SBen Skeggs int
271a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
272a6ed76d7SBen Skeggs {
273a6ed76d7SBen Skeggs 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
274a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
275a6ed76d7SBen Skeggs 	acpi_handle handle;
276a6ed76d7SBen Skeggs 	int type, ret;
277a6ed76d7SBen Skeggs 	void *edid;
278a6ed76d7SBen Skeggs 
279a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
280a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
281a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
282a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
283a6ed76d7SBen Skeggs 		break;
284a6ed76d7SBen Skeggs 	default:
285a6ed76d7SBen Skeggs 		return -EINVAL;
286a6ed76d7SBen Skeggs 	}
287a6ed76d7SBen Skeggs 
288a6ed76d7SBen Skeggs 	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
289a6ed76d7SBen Skeggs 	if (!handle)
290a6ed76d7SBen Skeggs 		return -ENODEV;
291a6ed76d7SBen Skeggs 
292a6ed76d7SBen Skeggs 	ret = acpi_bus_get_device(handle, &acpidev);
293a6ed76d7SBen Skeggs 	if (ret)
294a6ed76d7SBen Skeggs 		return -ENODEV;
295a6ed76d7SBen Skeggs 
296a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
297a6ed76d7SBen Skeggs 	if (ret < 0)
298a6ed76d7SBen Skeggs 		return ret;
299a6ed76d7SBen Skeggs 
30024b102d3SBen Skeggs 	nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
301a6ed76d7SBen Skeggs 	return 0;
302a6ed76d7SBen Skeggs }
303