RE: [PATCH v2] platform/x86: intel-hid: Add support for Device Specific Methods

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: Srinivas Pandruvada [mailto:srinivas.pandruvada@xxxxxxxxxxxxxxx]
> Sent: Thursday, June 28, 2018 1:19 PM
> To: alex.hung@xxxxxxxxxxxxx; dvhart@xxxxxxxxxxxxx; andy@xxxxxxxxxxxxx
> Cc: platform-driver-x86@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx;
> rjw@xxxxxxxxxxxxx; Limonciello, Mario; Srinivas Pandruvada
> Subject: [PATCH v2] platform/x86: intel-hid: Add support for Device Specific
> Methods
> 
> In some of the recent platforms, it is possible that stand alone methods
> for HEBC() or other methods used in this driver may not exist. In this
> case intel-hid driver will fail to load and power button will not be
> functional.
> 
> It is also possible that some quirks in this driver added for some
> platforms may have same issue in loading intel-hid driver.
> 
> There is an update to the ACPI details for the HID event filter driver.
> In the updated specification a _DSM is added, which has separate function
> indexes for each of the previous stand alone methods.
> 
> This change brings in support for the _DSM and allows usage of function
> index for corresponding stand alone methods.
> 
> Details of Device Specific Method:
> 
> Intel HID Event Filter Driver _DSM UUID:
> eeec56b3-4442-408f-a792-4edd4d758054
> 
> • Function index 0: Returns a buffer with a bit-field representing the
> supported function IDs.
> 
> Function Index	ASL Object
> --------------------------------
> 1		BTNL
> 2		HDMM
> 3		HDSM
> 4		HDEM
> 5		BTNS
> 6		BTNE
> 7		HEBC
> 8		VGBS
> 9		HEBC
> 
> One significant change is to query the supported methods implemented on
> the platform. So the previous HEBC() has two variants. HEBC v1 and
> HEBC v2. The v2 version allowed further define which of the 5-button
> are actually defined by the platform. HEBC v2 support is only available
> via new DSM.
> 
> v1 Button details:
> Bits [0] - Rotation Lock, Num Lock, Home, End, Page Up,
> Page Down
> Bits [1] - Wireless Radio Control
> Bits [2] - System Power Down
> Bits [3] - System Hibernate
> Bits [4] - System Sleep/ System Wake
> Bits [5] - Scan Next Track
> Bits [6] - Scan Previous Track
> Bits [7] - Stop
> Bits [8] - Play/Pause
> Bits [9] - Mute
> Bits [10] - Volume Increment
> Bits [11] - Volume Decrement
> Bits [12] - Display Brightness Increment
> Bits [13] - Display Brightness Decrement
> Bits [14] - Lock Tablet
> Bits [15] - Release Tablet
> Bits [16] - Toggle Bezel
> Bits [17] - 5 button array
> Bits [18-31] - reserved
> 
> v2 Buttom details:
> Bits [0-16] - Same as v1 version
> Bits [17] - 5 button array
> Bits [18] – Power Button
> Bits [19] - W Home Button
> Bits [20] - Volume Up Button
> Bits [21] - Volume Down Button
> Bits [22] – Rotation Lock Button
> Bits [23-31] – reserved
> 
> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>

I verified this on XPS 9370 FW 1.3.2 (which contains this alternate HEBC interface).
Power button works again for wakeup from s2idle.

Tested-by: Mario Limonciello <mario.limonciello@xxxxxxxx>

> ---
> Accidently sent the patch without change of version, so please ignore
> the previous one sent today.
> v2:
> 	Minor changes suggested by Andy
> 
>  drivers/platform/x86/intel-hid.c | 178 ++++++++++++++++++++++++++++++++++----
> -
>  1 file 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 @@ static void notify_handler(acpi_handle handle, u32 event,
> void *context)
>  		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);
> --
> 2.14.1





[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux