diff options
Diffstat (limited to 'drivers/platform/x86/intel-hid.c')
| -rw-r--r-- | drivers/platform/x86/intel-hid.c | 178 | 
1 files changed, 157 insertions, 21 deletions
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index b5adba227783..6cf9b7fa5bf0 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -96,13 +96,140 @@ struct intel_hid_priv {  	bool wakeup_mode;  }; -static int intel_hid_set_enable(struct device *device, bool enable) +#define HID_EVENT_FILTER_UUID	"eeec56b3-4442-408f-a792-4edd4d758054" + +enum intel_hid_dsm_fn_codes { +	INTEL_HID_DSM_FN_INVALID, +	INTEL_HID_DSM_BTNL_FN, +	INTEL_HID_DSM_HDMM_FN, +	INTEL_HID_DSM_HDSM_FN, +	INTEL_HID_DSM_HDEM_FN, +	INTEL_HID_DSM_BTNS_FN, +	INTEL_HID_DSM_BTNE_FN, +	INTEL_HID_DSM_HEBC_V1_FN, +	INTEL_HID_DSM_VGBS_FN, +	INTEL_HID_DSM_HEBC_V2_FN, +	INTEL_HID_DSM_FN_MAX +}; + +static const char *intel_hid_dsm_fn_to_method[INTEL_HID_DSM_FN_MAX] = { +	NULL, +	"BTNL", +	"HDMM", +	"HDSM", +	"HDEM", +	"BTNS", +	"BTNE", +	"HEBC", +	"VGBS", +	"HEBC" +}; + +static unsigned long long intel_hid_dsm_fn_mask; +static guid_t intel_dsm_guid; + +static bool intel_hid_execute_method(acpi_handle handle, +				     enum intel_hid_dsm_fn_codes fn_index, +				     unsigned long long arg)  { +	union acpi_object *obj, argv4, req;  	acpi_status status; +	char *method_name; -	status = acpi_execute_simple_method(ACPI_HANDLE(device), "HDSM", -					    enable); -	if (ACPI_FAILURE(status)) { +	if (fn_index <= INTEL_HID_DSM_FN_INVALID || +	    fn_index >= INTEL_HID_DSM_FN_MAX) +		return false; + +	method_name = (char *)intel_hid_dsm_fn_to_method[fn_index]; + +	if (!(intel_hid_dsm_fn_mask & fn_index)) +		goto skip_dsm_exec; + +	/* All methods expects a package with one integer element */ +	req.type = ACPI_TYPE_INTEGER; +	req.integer.value = arg; + +	argv4.type = ACPI_TYPE_PACKAGE; +	argv4.package.count = 1; +	argv4.package.elements = &req; + +	obj = acpi_evaluate_dsm(handle, &intel_dsm_guid, 1, fn_index, &argv4); +	if (obj) { +		acpi_handle_debug(handle, "Exec DSM Fn code: %d[%s] success\n", +				  fn_index, method_name); +		ACPI_FREE(obj); +		return true; +	} + +skip_dsm_exec: +	status = acpi_execute_simple_method(handle, method_name, arg); +	if (ACPI_SUCCESS(status)) +		return true; + +	return false; +} + +static bool intel_hid_evaluate_method(acpi_handle handle, +				      enum intel_hid_dsm_fn_codes fn_index, +				      unsigned long long *result) +{ +	union acpi_object *obj; +	acpi_status status; +	char *method_name; + +	if (fn_index <= INTEL_HID_DSM_FN_INVALID || +	    fn_index >= INTEL_HID_DSM_FN_MAX) +		return false; + +	method_name = (char *)intel_hid_dsm_fn_to_method[fn_index]; + +	if (!(intel_hid_dsm_fn_mask & fn_index)) +		goto skip_dsm_eval; + +	obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid, +				      1, fn_index, +				      NULL,  ACPI_TYPE_INTEGER); +	if (obj) { +		*result = obj->integer.value; +		acpi_handle_debug(handle, +				  "Eval DSM Fn code: %d[%s] results: 0x%llx\n", +				  fn_index, method_name, *result); +		ACPI_FREE(obj); +		return true; +	} + +skip_dsm_eval: +	status = acpi_evaluate_integer(handle, method_name, NULL, result); +	if (ACPI_SUCCESS(status)) +		return true; + +	return false; +} + +static void intel_hid_init_dsm(acpi_handle handle) +{ +	union acpi_object *obj; + +	guid_parse(HID_EVENT_FILTER_UUID, &intel_dsm_guid); + +	obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid, 1, 0, NULL, +				      ACPI_TYPE_BUFFER); +	if (obj) { +		intel_hid_dsm_fn_mask = *obj->buffer.pointer; +		ACPI_FREE(obj); +	} + +	acpi_handle_debug(handle, "intel_hid_dsm_fn_mask = %llx\n", +			  intel_hid_dsm_fn_mask); +} + +static int intel_hid_set_enable(struct device *device, bool enable) +{ +	acpi_handle handle = ACPI_HANDLE(device); + +	/* Enable|disable features - power button is always enabled */ +	if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, +				      enable)) {  		dev_warn(device, "failed to %sable hotkeys\n",  			 enable ? "en" : "dis");  		return -EIO; @@ -129,9 +256,8 @@ static void intel_button_array_enable(struct device *device, bool enable)  	}  	/* Enable|disable features - power button is always enabled */ -	status = acpi_execute_simple_method(handle, "BTNE", -					    enable ? button_cap : 1); -	if (ACPI_FAILURE(status)) +	if (!intel_hid_execute_method(handle, INTEL_HID_DSM_BTNE_FN, +				      enable ? button_cap : 1))  		dev_warn(device, "failed to set button capability\n");  } @@ -217,7 +343,6 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)  	struct platform_device *device = context;  	struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);  	unsigned long long ev_index; -	acpi_status status;  	if (priv->wakeup_mode) {  		/* @@ -269,8 +394,8 @@ wakeup:  		return;  	} -	status = acpi_evaluate_integer(handle, "HDEM", NULL, &ev_index); -	if (ACPI_FAILURE(status)) { +	if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDEM_FN, +				       &ev_index)) {  		dev_warn(&device->dev, "failed to get event index\n");  		return;  	} @@ -284,17 +409,24 @@ static bool button_array_present(struct platform_device *device)  {  	acpi_handle handle = ACPI_HANDLE(&device->dev);  	unsigned long long event_cap; -	acpi_status status; -	bool supported = false; -	status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap); -	if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) -		supported = true; +	if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V2_FN, +				      &event_cap)) { +		/* Check presence of 5 button array or v2 power button */ +		if (event_cap & 0x60000) +			return true; +	} + +	if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V1_FN, +				      &event_cap)) { +		if (event_cap & 0x20000) +			return true; +	}  	if (dmi_check_system(button_array_table)) -		supported = true; +		return true; -	return supported; +	return false;  }  static int intel_hid_probe(struct platform_device *device) @@ -305,8 +437,9 @@ static int intel_hid_probe(struct platform_device *device)  	acpi_status status;  	int err; -	status = acpi_evaluate_integer(handle, "HDMM", NULL, &mode); -	if (ACPI_FAILURE(status)) { +	intel_hid_init_dsm(handle); + +	if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) {  		dev_warn(&device->dev, "failed to read mode\n");  		return -ENODEV;  	} @@ -352,13 +485,16 @@ static int intel_hid_probe(struct platform_device *device)  		goto err_remove_notify;  	if (priv->array) { +		unsigned long long dummy; +  		intel_button_array_enable(&device->dev, true);  		/* Call button load method to enable HID power button */ -		status = acpi_evaluate_object(handle, "BTNL", NULL, NULL); -		if (ACPI_FAILURE(status)) +		if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_BTNL_FN, +					       &dummy)) {  			dev_warn(&device->dev,  				 "failed to enable HID power button\n"); +		}  	}  	device_init_wakeup(&device->dev, true);  |