diff mbox series

[1/2] platform/x86: thinkpad_acpi: Add palm sensor support

Message ID 20210116014335.629009-2-alex.hung@canonical.com
State New
Headers show
Series Thinkpad - add palm sensor support needed for WWAN | expand

Commit Message

Alex Hung Jan. 16, 2021, 1:43 a.m. UTC
From: Mark Pearson <markpearson@lenovo.com>

BugLink: https://bugs.launchpad.net/bugs/1912033

Add support to thinkpad_acpi for returning the status of the palm
sensor.

This patch builds on the work done previously for the input device
implementation (which was not needed). Both lap and palm sensor are using
sysfs and they are combined into the proxsensor block.

Note: On some platforms, because of an issue in the HW implementation,
the palm sensor presence may be incorrectly advertised as always
enabled even if a palm sensor is not present. The palm sensor is
intended for WWAN transmission power control and should be available
and correct on all WWAN enabled systems. It is not recommended to use
this interface for other use cases.

Signed-off-by: Mark Pearson <markpearson@lenovo.com>
Link: https://lore.kernel.org/r/20201124181154.547518-1-markpearson@lenovo.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 1ac09656bded6e4ea9312631579dd32189e00e62)
Signed-off-by: Alex Hung <alex.hung@canonical.com>
---
 drivers/platform/x86/thinkpad_acpi.c | 162 +++++++++++++++++----------
 1 file changed, 103 insertions(+), 59 deletions(-)

Comments

Alex Hung Jan. 16, 2021, 2:59 a.m. UTC | #1
NACK.Will re-send with updated subjects

On 2021-01-15 6:43 p.m., Alex Hung wrote:
> From: Mark Pearson <markpearson@lenovo.com>
> 
> BugLink: https://bugs.launchpad.net/bugs/1912033
> 
> Add support to thinkpad_acpi for returning the status of the palm
> sensor.
> 
> This patch builds on the work done previously for the input device
> implementation (which was not needed). Both lap and palm sensor are using
> sysfs and they are combined into the proxsensor block.
> 
> Note: On some platforms, because of an issue in the HW implementation,
> the palm sensor presence may be incorrectly advertised as always
> enabled even if a palm sensor is not present. The palm sensor is
> intended for WWAN transmission power control and should be available
> and correct on all WWAN enabled systems. It is not recommended to use
> this interface for other use cases.
> 
> Signed-off-by: Mark Pearson <markpearson@lenovo.com>
> Link: https://lore.kernel.org/r/20201124181154.547518-1-markpearson@lenovo.com
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> (cherry picked from commit 1ac09656bded6e4ea9312631579dd32189e00e62)
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  drivers/platform/x86/thinkpad_acpi.c | 162 +++++++++++++++++----------
>  1 file changed, 103 insertions(+), 59 deletions(-)
> 
> diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
> index c404706379d9..26b1502f9bbe 100644
> --- a/drivers/platform/x86/thinkpad_acpi.c
> +++ b/drivers/platform/x86/thinkpad_acpi.c
> @@ -4028,6 +4028,7 @@ static bool hotkey_notify_usrevent(const u32 hkey,
>  }
>  
>  static void thermal_dump_all_sensors(void);
> +static void palmsensor_refresh(void);
>  
>  static bool hotkey_notify_6xxx(const u32 hkey,
>  				 bool *send_acpi_ev,
> @@ -4094,8 +4095,8 @@ static bool hotkey_notify_6xxx(const u32 hkey,
>  
>  	case TP_HKEY_EV_PALM_DETECTED:
>  	case TP_HKEY_EV_PALM_UNDETECTED:
> -		/* palm detected hovering the keyboard, forward to user-space
> -		 * via netlink for consumption */
> +		/* palm detected  - pass on to event handler */
> +		palmsensor_refresh();
>  		return true;
>  
>  	default:
> @@ -9839,102 +9840,146 @@ static struct ibm_struct lcdshadow_driver_data = {
>  };
>  
>  /*************************************************************************
> - * DYTC subdriver, for the Lenovo lapmode feature
> + * Thinkpad sensor interfaces
>   */
>  
>  #define DYTC_CMD_GET          2 /* To get current IC function and mode */
>  #define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */
>  
> -static bool dytc_lapmode;
> +#define PALMSENSOR_PRESENT_BIT 0 /* Determine if psensor present */
> +#define PALMSENSOR_ON_BIT      1 /* psensor status */
>  
> -static void dytc_lapmode_notify_change(void)
> -{
> -	sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
> -}
> +static bool has_palmsensor;
> +static bool has_lapsensor;
> +static bool palm_state;
> +static bool lap_state;
>  
> -static int dytc_command(int command, int *output)
> +static int lapsensor_get(bool *present, bool *state)
>  {
>  	acpi_handle dytc_handle;
> +	int output;
>  
> -	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) {
> -		/* Platform doesn't support DYTC */
> +	*present = false;
> +	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle)))
>  		return -ENODEV;
> -	}
> -	if (!acpi_evalf(dytc_handle, output, NULL, "dd", command))
> +	if (!acpi_evalf(dytc_handle, &output, NULL, "dd", DYTC_CMD_GET))
>  		return -EIO;
> +
> +	*present = true; /*If we get his far, we have lapmode support*/
> +	*state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
>  	return 0;
>  }
>  
> -static int dytc_lapmode_get(bool *state)
> +static int palmsensor_get(bool *present, bool *state)
>  {
> -	int output, err;
> +	acpi_handle psensor_handle;
> +	int output;
>  
> -	err = dytc_command(DYTC_CMD_GET, &output);
> -	if (err)
> -		return err;
> -	*state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
> +	*present = false;
> +	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GPSS", &psensor_handle)))
> +		return -ENODEV;
> +	if (!acpi_evalf(psensor_handle, &output, NULL, "d"))
> +		return -EIO;
> +
> +	*present = output & BIT(PALMSENSOR_PRESENT_BIT) ? true : false;
> +	*state = output & BIT(PALMSENSOR_ON_BIT) ? true : false;
>  	return 0;
>  }
>  
> -static void dytc_lapmode_refresh(void)
> +static void lapsensor_refresh(void)
>  {
> -	bool new_state;
> +	bool state;
>  	int err;
>  
> -	err = dytc_lapmode_get(&new_state);
> -	if (err || (new_state == dytc_lapmode))
> -		return;
> +	if (has_lapsensor) {
> +		err = lapsensor_get(&has_lapsensor, &state);
> +		if (err)
> +			return;
> +		if (lap_state != state) {
> +			lap_state = state;
> +			sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
> +		}
> +	}
> +}
>  
> -	dytc_lapmode = new_state;
> -	dytc_lapmode_notify_change();
> +static void palmsensor_refresh(void)
> +{
> +	bool state;
> +	int err;
> +
> +	if (has_palmsensor) {
> +		err = palmsensor_get(&has_palmsensor, &state);
> +		if (err)
> +			return;
> +		if (palm_state != state) {
> +			palm_state = state;
> +			sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "palmsensor");
> +		}
> +	}
>  }
>  
> -/* sysfs lapmode entry */
>  static ssize_t dytc_lapmode_show(struct device *dev,
>  					struct device_attribute *attr,
>  					char *buf)
>  {
> -	return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode);
> +	if (has_lapsensor)
> +		return sysfs_emit(buf, "%d\n", lap_state);
> +	return sysfs_emit(buf, "\n");
>  }
> -
>  static DEVICE_ATTR_RO(dytc_lapmode);
>  
> -static struct attribute *dytc_attributes[] = {
> -	&dev_attr_dytc_lapmode.attr,
> -	NULL,
> -};
> -
> -static const struct attribute_group dytc_attr_group = {
> -	.attrs = dytc_attributes,
> -};
> +static ssize_t palmsensor_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	if (has_palmsensor)
> +		return sysfs_emit(buf, "%d\n", palm_state);
> +	return sysfs_emit(buf, "\n");
> +}
> +static DEVICE_ATTR_RO(palmsensor);
>  
> -static int tpacpi_dytc_init(struct ibm_init_struct *iibm)
> +static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm)
>  {
> -	int err;
> +	int palm_err, lap_err, err;
>  
> -	err = dytc_lapmode_get(&dytc_lapmode);
> -	/* If support isn't available (ENODEV) then don't return an error
> -	 * but just don't create the sysfs group
> +	palm_err = palmsensor_get(&has_palmsensor, &palm_state);
> +	lap_err = lapsensor_get(&has_lapsensor, &lap_state);
> +	/*
> +	 * If support isn't available (ENODEV) for both devices then quit, but
> +	 * don't return an error.
>  	 */
> -	if (err == -ENODEV)
> +	if ((palm_err == -ENODEV) && (lap_err == -ENODEV))
>  		return 0;
> -	/* For all other errors we can flag the failure */
> -	if (err)
> -		return err;
> -
> -	/* Platform supports this feature - create the group */
> -	err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
> -	return err;
> +	/* Otherwise, if there was an error return it */
> +	if (palm_err && (palm_err != ENODEV))
> +		return palm_err;
> +	if (lap_err && (lap_err != ENODEV))
> +		return lap_err;
> +
> +	if (has_palmsensor) {
> +		err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
> +		if (err)
> +			return err;
> +	}
> +	if (has_lapsensor) {
> +		err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
>  }
>  
> -static void dytc_exit(void)
> +static void proxsensor_exit(void)
>  {
> -	sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
> +	if (has_lapsensor)
> +		sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
> +	if (has_palmsensor)
> +		sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
>  }
>  
> -static struct ibm_struct dytc_driver_data = {
> -	.name = "dytc",
> -	.exit = dytc_exit,
> +static struct ibm_struct proxsensor_driver_data = {
> +	.name = "proximity-sensor",
> +	.exit = proxsensor_exit,
>  };
>  
>  /****************************************************************************
> @@ -9986,8 +10031,7 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
>  	}
>  
>  	if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED)
> -		dytc_lapmode_refresh();
> -
> +		lapsensor_refresh();
>  }
>  
>  static void hotkey_driver_event(const unsigned int scancode)
> @@ -10427,8 +10471,8 @@ static struct ibm_init_struct ibms_init[] __initdata = {
>  		.data = &lcdshadow_driver_data,
>  	},
>  	{
> -		.init = tpacpi_dytc_init,
> -		.data = &dytc_driver_data,
> +		.init = tpacpi_proxsensor_init,
> +		.data = &proxsensor_driver_data,
>  	},
>  };
>  
>
diff mbox series

Patch

diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index c404706379d9..26b1502f9bbe 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -4028,6 +4028,7 @@  static bool hotkey_notify_usrevent(const u32 hkey,
 }
 
 static void thermal_dump_all_sensors(void);
+static void palmsensor_refresh(void);
 
 static bool hotkey_notify_6xxx(const u32 hkey,
 				 bool *send_acpi_ev,
@@ -4094,8 +4095,8 @@  static bool hotkey_notify_6xxx(const u32 hkey,
 
 	case TP_HKEY_EV_PALM_DETECTED:
 	case TP_HKEY_EV_PALM_UNDETECTED:
-		/* palm detected hovering the keyboard, forward to user-space
-		 * via netlink for consumption */
+		/* palm detected  - pass on to event handler */
+		palmsensor_refresh();
 		return true;
 
 	default:
@@ -9839,102 +9840,146 @@  static struct ibm_struct lcdshadow_driver_data = {
 };
 
 /*************************************************************************
- * DYTC subdriver, for the Lenovo lapmode feature
+ * Thinkpad sensor interfaces
  */
 
 #define DYTC_CMD_GET          2 /* To get current IC function and mode */
 #define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */
 
-static bool dytc_lapmode;
+#define PALMSENSOR_PRESENT_BIT 0 /* Determine if psensor present */
+#define PALMSENSOR_ON_BIT      1 /* psensor status */
 
-static void dytc_lapmode_notify_change(void)
-{
-	sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
-}
+static bool has_palmsensor;
+static bool has_lapsensor;
+static bool palm_state;
+static bool lap_state;
 
-static int dytc_command(int command, int *output)
+static int lapsensor_get(bool *present, bool *state)
 {
 	acpi_handle dytc_handle;
+	int output;
 
-	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) {
-		/* Platform doesn't support DYTC */
+	*present = false;
+	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle)))
 		return -ENODEV;
-	}
-	if (!acpi_evalf(dytc_handle, output, NULL, "dd", command))
+	if (!acpi_evalf(dytc_handle, &output, NULL, "dd", DYTC_CMD_GET))
 		return -EIO;
+
+	*present = true; /*If we get his far, we have lapmode support*/
+	*state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
 	return 0;
 }
 
-static int dytc_lapmode_get(bool *state)
+static int palmsensor_get(bool *present, bool *state)
 {
-	int output, err;
+	acpi_handle psensor_handle;
+	int output;
 
-	err = dytc_command(DYTC_CMD_GET, &output);
-	if (err)
-		return err;
-	*state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
+	*present = false;
+	if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GPSS", &psensor_handle)))
+		return -ENODEV;
+	if (!acpi_evalf(psensor_handle, &output, NULL, "d"))
+		return -EIO;
+
+	*present = output & BIT(PALMSENSOR_PRESENT_BIT) ? true : false;
+	*state = output & BIT(PALMSENSOR_ON_BIT) ? true : false;
 	return 0;
 }
 
-static void dytc_lapmode_refresh(void)
+static void lapsensor_refresh(void)
 {
-	bool new_state;
+	bool state;
 	int err;
 
-	err = dytc_lapmode_get(&new_state);
-	if (err || (new_state == dytc_lapmode))
-		return;
+	if (has_lapsensor) {
+		err = lapsensor_get(&has_lapsensor, &state);
+		if (err)
+			return;
+		if (lap_state != state) {
+			lap_state = state;
+			sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
+		}
+	}
+}
 
-	dytc_lapmode = new_state;
-	dytc_lapmode_notify_change();
+static void palmsensor_refresh(void)
+{
+	bool state;
+	int err;
+
+	if (has_palmsensor) {
+		err = palmsensor_get(&has_palmsensor, &state);
+		if (err)
+			return;
+		if (palm_state != state) {
+			palm_state = state;
+			sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "palmsensor");
+		}
+	}
 }
 
-/* sysfs lapmode entry */
 static ssize_t dytc_lapmode_show(struct device *dev,
 					struct device_attribute *attr,
 					char *buf)
 {
-	return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode);
+	if (has_lapsensor)
+		return sysfs_emit(buf, "%d\n", lap_state);
+	return sysfs_emit(buf, "\n");
 }
-
 static DEVICE_ATTR_RO(dytc_lapmode);
 
-static struct attribute *dytc_attributes[] = {
-	&dev_attr_dytc_lapmode.attr,
-	NULL,
-};
-
-static const struct attribute_group dytc_attr_group = {
-	.attrs = dytc_attributes,
-};
+static ssize_t palmsensor_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	if (has_palmsensor)
+		return sysfs_emit(buf, "%d\n", palm_state);
+	return sysfs_emit(buf, "\n");
+}
+static DEVICE_ATTR_RO(palmsensor);
 
-static int tpacpi_dytc_init(struct ibm_init_struct *iibm)
+static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm)
 {
-	int err;
+	int palm_err, lap_err, err;
 
-	err = dytc_lapmode_get(&dytc_lapmode);
-	/* If support isn't available (ENODEV) then don't return an error
-	 * but just don't create the sysfs group
+	palm_err = palmsensor_get(&has_palmsensor, &palm_state);
+	lap_err = lapsensor_get(&has_lapsensor, &lap_state);
+	/*
+	 * If support isn't available (ENODEV) for both devices then quit, but
+	 * don't return an error.
 	 */
-	if (err == -ENODEV)
+	if ((palm_err == -ENODEV) && (lap_err == -ENODEV))
 		return 0;
-	/* For all other errors we can flag the failure */
-	if (err)
-		return err;
-
-	/* Platform supports this feature - create the group */
-	err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
-	return err;
+	/* Otherwise, if there was an error return it */
+	if (palm_err && (palm_err != ENODEV))
+		return palm_err;
+	if (lap_err && (lap_err != ENODEV))
+		return lap_err;
+
+	if (has_palmsensor) {
+		err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
+		if (err)
+			return err;
+	}
+	if (has_lapsensor) {
+		err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
+		if (err)
+			return err;
+	}
+	return 0;
 }
 
-static void dytc_exit(void)
+static void proxsensor_exit(void)
 {
-	sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
+	if (has_lapsensor)
+		sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
+	if (has_palmsensor)
+		sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
 }
 
-static struct ibm_struct dytc_driver_data = {
-	.name = "dytc",
-	.exit = dytc_exit,
+static struct ibm_struct proxsensor_driver_data = {
+	.name = "proximity-sensor",
+	.exit = proxsensor_exit,
 };
 
 /****************************************************************************
@@ -9986,8 +10031,7 @@  static void tpacpi_driver_event(const unsigned int hkey_event)
 	}
 
 	if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED)
-		dytc_lapmode_refresh();
-
+		lapsensor_refresh();
 }
 
 static void hotkey_driver_event(const unsigned int scancode)
@@ -10427,8 +10471,8 @@  static struct ibm_init_struct ibms_init[] __initdata = {
 		.data = &lcdshadow_driver_data,
 	},
 	{
-		.init = tpacpi_dytc_init,
-		.data = &dytc_driver_data,
+		.init = tpacpi_proxsensor_init,
+		.data = &proxsensor_driver_data,
 	},
 };