nouveau_acpi.c 10.9 KB
Newer Older
1 2
#include <linux/pci.h>
#include <linux/acpi.h>
3
#include <linux/slab.h>
4
#include <linux/mxm-wmi.h>
5
#include <linux/vga_switcheroo.h>
6
#include <drm/drm_edid.h>
7
#include <acpi/video.h>
8 9 10 11

#include "nouveau_drm.h"
#include "nouveau_acpi.h"

12 13 14 15 16 17 18 19 20 21 22
#define NOUVEAU_DSM_LED 0x02
#define NOUVEAU_DSM_LED_STATE 0x00
#define NOUVEAU_DSM_LED_OFF 0x10
#define NOUVEAU_DSM_LED_STAMINA 0x11
#define NOUVEAU_DSM_LED_SPEED 0x12

#define NOUVEAU_DSM_POWER 0x03
#define NOUVEAU_DSM_POWER_STATE 0x00
#define NOUVEAU_DSM_POWER_SPEED 0x01
#define NOUVEAU_DSM_POWER_STAMINA 0x02

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
#define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B

#define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
#define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
#define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)

#define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)

/* result of the optimus caps function */
#define OPTIMUS_ENABLED (1 << 0)
#define OPTIMUS_STATUS_MASK (3 << 3)
#define OPTIMUS_STATUS_OFF  (0 << 3)
#define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
#define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
#define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
#define OPTIMUS_CAPS_MASK (7 << 24)
#define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)

#define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
#define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
44

45 46
static struct nouveau_dsm_priv {
	bool dsm_detected;
47
	bool optimus_detected;
48
	acpi_handle dhandle;
49
	acpi_handle rom_handle;
50 51
} nouveau_dsm_priv;

52 53 54 55 56 57 58 59
bool nouveau_is_optimus(void) {
	return nouveau_dsm_priv.optimus_detected;
}

bool nouveau_is_v1_dsm(void) {
	return nouveau_dsm_priv.dsm_detected;
}

60 61 62
#define NOUVEAU_DSM_HAS_MUX 0x1
#define NOUVEAU_DSM_HAS_OPT 0x2

63
#ifdef CONFIG_VGA_SWITCHEROO
64 65 66 67
static const char nouveau_dsm_muid[] = {
	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
};
68

69 70 71 72 73 74 75
static const char nouveau_op_dsm_muid[] = {
	0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,
	0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
};

static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
{
76
	int i;
77
	union acpi_object *obj;
78
	char args_buff[4];
79 80 81 82 83
	union acpi_object argv4 = {
		.buffer.type = ACPI_TYPE_BUFFER,
		.buffer.length = 4,
		.buffer.pointer = args_buff
	};
84

85 86 87
	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
	for (i = 0; i < 4; i++)
		args_buff[i] = (arg >> i * 8) & 0xFF;
88

89 90 91 92 93 94 95 96
	*result = 0;
	obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100,
				      func, &argv4, ACPI_TYPE_BUFFER);
	if (!obj) {
		acpi_handle_info(handle, "failed to evaluate _DSM\n");
		return AE_ERROR;
	} else {
		if (obj->buffer.length == 4) {
97 98 99 100 101
			*result |= obj->buffer.pointer[0];
			*result |= (obj->buffer.pointer[1] << 8);
			*result |= (obj->buffer.pointer[2] << 16);
			*result |= (obj->buffer.pointer[3] << 24);
		}
102
		ACPI_FREE(obj);
103 104 105 106 107
	}

	return 0;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/*
 * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
 * requirements on the fourth parameter, so a private implementation
 * instead of using acpi_check_dsm().
 */
static int nouveau_check_optimus_dsm(acpi_handle handle)
{
	int result;

	/*
	 * Function 0 returns a Buffer containing available functions.
	 * The args parameter is ignored for function 0, so just put 0 in it
	 */
	if (nouveau_optimus_dsm(handle, 0, 0, &result))
		return 0;

	/*
	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
	 * If the n-th bit is enabled, function n is supported
	 */
	return result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS);
}

131
static int nouveau_dsm(acpi_handle handle, int func, int arg)
132
{
133
	int ret = 0;
134
	union acpi_object *obj;
135 136 137 138 139 140 141 142 143 144 145
	union acpi_object argv4 = {
		.integer.type = ACPI_TYPE_INTEGER,
		.integer.value = arg,
	};

	obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102,
				      func, &argv4, ACPI_TYPE_INTEGER);
	if (!obj) {
		acpi_handle_info(handle, "failed to evaluate _DSM\n");
		return AE_ERROR;
	} else {
146
		if (obj->integer.value == 0x80000002)
147 148
			ret = -ENODEV;
		ACPI_FREE(obj);
149 150
	}

151
	return ret;
152 153
}

154
static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
155
{
156
	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
157
	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
158
	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
159 160 161 162 163 164 165 166 167
}

static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
{
	int arg;
	if (state == VGA_SWITCHEROO_ON)
		arg = NOUVEAU_DSM_POWER_SPEED;
	else
		arg = NOUVEAU_DSM_POWER_STAMINA;
168
	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
169 170 171 172 173
	return 0;
}

static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
{
174
	if (!nouveau_dsm_priv.dsm_detected)
175
		return 0;
176
	if (id == VGA_SWITCHEROO_IGD)
177
		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
178
	else
179
		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
180
}
181

182 183 184 185 186 187
static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
				   enum vga_switcheroo_state state)
{
	if (id == VGA_SWITCHEROO_IGD)
		return 0;

188 189
	/* Optimus laptops have the card already disabled in
	 * nouveau_switcheroo_set_state */
190
	if (!nouveau_dsm_priv.dsm_detected)
191 192
		return 0;

193
	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
194 195 196
}

static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
197
{
198 199
	/* easy option one - intel vendor ID means Integrated */
	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
200
		return VGA_SWITCHEROO_IGD;
201 202 203 204 205 206

	/* is this device on Bus 0? - this may need improving */
	if (pdev->bus->number == 0)
		return VGA_SWITCHEROO_IGD;

	return VGA_SWITCHEROO_DIS;
207 208 209 210 211 212 213
}

static struct vga_switcheroo_handler nouveau_dsm_handler = {
	.switchto = nouveau_dsm_switchto,
	.power_state = nouveau_dsm_power_state,
	.get_client_id = nouveau_dsm_get_client_id,
};
214

215
static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
216
{
217
	acpi_handle dhandle;
218
	int retval = 0;
219

220
	dhandle = ACPI_HANDLE(&pdev->dev);
221 222
	if (!dhandle)
		return false;
223

224
	if (!acpi_has_method(dhandle, "_DSM"))
225
		return false;
226

227 228
	if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102,
			   1 << NOUVEAU_DSM_POWER))
229 230
		retval |= NOUVEAU_DSM_HAS_MUX;

231
	if (nouveau_check_optimus_dsm(dhandle))
232 233
		retval |= NOUVEAU_DSM_HAS_OPT;

234 235 236 237 238 239 240 241 242
	if (retval & NOUVEAU_DSM_HAS_OPT) {
		uint32_t result;
		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
				    &result);
		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
	}
243 244
	if (retval)
		nouveau_dsm_priv.dhandle = dhandle;
245

246
	return retval;
247
}
248 249 250 251 252 253 254

static bool nouveau_dsm_detect(void)
{
	char acpi_method_name[255] = { 0 };
	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
	struct pci_dev *pdev = NULL;
	int has_dsm = 0;
255
	int has_optimus = 0;
256
	int vga_count = 0;
257
	bool guid_valid;
258 259
	int retval;
	bool ret = false;
260

261
	/* lookup the MXM GUID */
262 263
	guid_valid = mxm_wmi_supported();

264 265
	if (guid_valid)
		printk("MXM: GUID detected in BIOS\n");
266

267
	/* now do DSM detection */
268 269 270
	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
		vga_count++;

271 272 273 274 275
		retval = nouveau_dsm_pci_probe(pdev);
		if (retval & NOUVEAU_DSM_HAS_MUX)
			has_dsm |= 1;
		if (retval & NOUVEAU_DSM_HAS_OPT)
			has_optimus = 1;
276 277
	}

278 279 280 281 282 283 284 285 286 287
	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
		vga_count++;

		retval = nouveau_dsm_pci_probe(pdev);
		if (retval & NOUVEAU_DSM_HAS_MUX)
			has_dsm |= 1;
		if (retval & NOUVEAU_DSM_HAS_OPT)
			has_optimus = 1;
	}

288 289
	/* find the optimus DSM or the old v1 DSM */
	if (has_optimus == 1) {
290 291
		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
			&buffer);
292
		printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n",
293
			acpi_method_name);
294
		nouveau_dsm_priv.optimus_detected = true;
295
		ret = true;
296
	} else if (vga_count == 2 && has_dsm && guid_valid) {
297 298
		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
			&buffer);
299
		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
300
			acpi_method_name);
301
		nouveau_dsm_priv.dsm_detected = true;
302 303
		ret = true;
	}
304

305

306
	return ret;
307 308 309 310 311 312 313 314 315 316 317 318 319
}

void nouveau_register_dsm_handler(void)
{
	bool r;

	r = nouveau_dsm_detect();
	if (!r)
		return;

	vga_switcheroo_register_handler(&nouveau_dsm_handler);
}

320 321 322 323 324 325 326
/* Must be called for Optimus models before the card can be turned off */
void nouveau_switcheroo_optimus_dsm(void)
{
	u32 result = 0;
	if (!nouveau_dsm_priv.optimus_detected)
		return;

327 328 329 330 331 332
	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
			    0x3, &result);

	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);

333 334
}

335 336
void nouveau_unregister_dsm_handler(void)
{
337 338
	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
		vga_switcheroo_unregister_handler();
339
}
340 341 342 343 344
#else
void nouveau_register_dsm_handler(void) {}
void nouveau_unregister_dsm_handler(void) {}
void nouveau_switcheroo_optimus_dsm(void) {}
#endif
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

/* retrieve the ROM in 4k blocks */
static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
			    int offset, int len)
{
	acpi_status status;
	union acpi_object rom_arg_elements[2], *obj;
	struct acpi_object_list rom_arg;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};

	rom_arg.count = 2;
	rom_arg.pointer = &rom_arg_elements[0];

	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
	rom_arg_elements[0].integer.value = offset;

	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
	rom_arg_elements[1].integer.value = len;

	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
	if (ACPI_FAILURE(status)) {
		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
		return -ENODEV;
	}
	obj = (union acpi_object *)buffer.pointer;
	memcpy(bios+offset, obj->buffer.pointer, len);
	kfree(buffer.pointer);
	return len;
}

bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
{
	acpi_status status;
	acpi_handle dhandle, rom_handle;

380
	dhandle = ACPI_HANDLE(&pdev->dev);
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
	if (!dhandle)
		return false;

	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
	if (ACPI_FAILURE(status))
		return false;

	nouveau_dsm_priv.rom_handle = rom_handle;
	return true;
}

int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
{
	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
}
396

397
void *
398 399 400 401 402 403 404 405 406 407 408 409 410
nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
{
	struct acpi_device *acpidev;
	acpi_handle handle;
	int type, ret;
	void *edid;

	switch (connector->connector_type) {
	case DRM_MODE_CONNECTOR_LVDS:
	case DRM_MODE_CONNECTOR_eDP:
		type = ACPI_VIDEO_DISPLAY_LCD;
		break;
	default:
411
		return NULL;
412 413
	}

414
	handle = ACPI_HANDLE(&dev->pdev->dev);
415
	if (!handle)
416
		return NULL;
417 418 419

	ret = acpi_bus_get_device(handle, &acpidev);
	if (ret)
420
		return NULL;
421 422 423

	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
	if (ret < 0)
424
		return NULL;
425

426
	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
427
}