diff mbox series

[2/2] Input: elan_i2c: Support inhibiting

Message ID 20200430161641.24503-3-andrzej.p@collabora.com (mailing list archive)
State Superseded
Headers show
Series Support inhibiting input devices | expand

Commit Message

Andrzej Pietrasiewicz April 30, 2020, 4:16 p.m. UTC
From: Dmitry Torokhov <dtor@chromium.org>

This also implies adjusting suspend/resume.

Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
---
 drivers/input/mouse/elan_i2c_core.c | 108 ++++++++++++++++++++++------
 1 file changed, 85 insertions(+), 23 deletions(-)

Comments

Dmitry Torokhov Dec. 3, 2020, 6:29 a.m. UTC | #1
Hi Andrzej,

On Thu, Apr 30, 2020 at 06:16:41PM +0200, Andrzej Pietrasiewicz wrote:
> From: Dmitry Torokhov <dtor@chromium.org>
> 
> This also implies adjusting suspend/resume.

This was acceptable for Chrome OS, where we know that nobody is going to
poke in sysfs or update firmware at random times, but for upstream we
need to make sure that device is powered up when we try accessing it not
only via open, but through sysfs as well (upload firmware, calibrate,
etc).

Thanks.
diff mbox series

Patch

diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 8719da540383..5fd8bcb7f337 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -309,6 +309,71 @@  static int elan_initialize(struct elan_tp_data *data)
 	return error;
 }
 
+static int elan_reactivate(struct elan_tp_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+
+	ret = elan_enable_power(data);
+	if (ret)
+		dev_err(dev, "failed to restore power: %d\n", ret);
+
+	ret = elan_initialize(data);
+	if (ret)
+		dev_err(dev, "failed to re-initialize touchpad: %d\n", ret);
+
+	return ret;
+}
+
+static int elan_uninhibit(struct input_dev *input)
+{
+	struct elan_tp_data *data = input_get_drvdata(input);
+	struct i2c_client *client = data->client;
+	int ret;
+
+	dev_dbg(&client->dev, "uninhibiting\n");
+
+	ret = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (ret)
+		return ret;
+
+	ret = elan_reactivate(data);
+	if (ret == 0)
+		enable_irq(client->irq);
+
+	mutex_unlock(&data->sysfs_mutex);
+
+	return ret;
+}
+
+static int elan_inhibit(struct input_dev *input)
+{
+	struct elan_tp_data *data = input_get_drvdata(input);
+	struct i2c_client *client = data->client;
+	int ret;
+
+	dev_dbg(&client->dev, "closing\n");
+
+	/*
+	 * We are taking the mutex to make sure sysfs operations are
+	 * complete before we attempt to bring the device into low[er]
+	 * power mode.
+	 */
+	ret = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (ret)
+		return ret;
+
+	disable_irq(client->irq);
+
+	ret = elan_disable_power(data);
+	if (ret)
+		enable_irq(client->irq);
+
+	mutex_unlock(&data->sysfs_mutex);
+
+	return ret;
+}
+
 static int elan_query_device_info(struct elan_tp_data *data)
 {
 	int error;
@@ -1085,6 +1150,8 @@  static int elan_setup_input_device(struct elan_tp_data *data)
 	input->id.bustype = BUS_I2C;
 	input->id.vendor = ELAN_VENDOR_ID;
 	input->id.product = data->product_id;
+	input->uninhibit = elan_uninhibit;
+	input->inhibit = elan_inhibit;
 	input_set_drvdata(input, data);
 
 	error = input_mt_init_slots(input, ETP_MAX_FINGERS,
@@ -1308,14 +1375,16 @@  static int __maybe_unused elan_suspend(struct device *dev)
 	if (ret)
 		return ret;
 
-	disable_irq(client->irq);
+	if (!data->input->inhibited) {
+		disable_irq(client->irq);
 
-	if (device_may_wakeup(dev)) {
-		ret = elan_sleep(data);
-		/* Enable wake from IRQ */
-		data->irq_wake = (enable_irq_wake(client->irq) == 0);
-	} else {
-		ret = elan_disable_power(data);
+		if (device_may_wakeup(dev)) {
+			ret = elan_sleep(data);
+			/* Enable wake from IRQ */
+			data->irq_wake = (enable_irq_wake(client->irq) == 0);
+		} else {
+			ret = elan_disable_power(data);
+		}
 	}
 
 	mutex_unlock(&data->sysfs_mutex);
@@ -1326,26 +1395,19 @@  static int __maybe_unused elan_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct elan_tp_data *data = i2c_get_clientdata(client);
-	int error;
+	int ret = 0;
 
-	if (device_may_wakeup(dev) && data->irq_wake) {
-		disable_irq_wake(client->irq);
-		data->irq_wake = false;
-	}
+	if (!data->input->inhibited) {
+		if (data->irq_wake) {
+			disable_irq_wake(client->irq);
+			data->irq_wake = false;
+		}
 
-	error = elan_enable_power(data);
-	if (error) {
-		dev_err(dev, "power up when resuming failed: %d\n", error);
-		goto err;
+		ret = elan_reactivate(data);
+		enable_irq(data->client->irq);
 	}
 
-	error = elan_initialize(data);
-	if (error)
-		dev_err(dev, "initialize when resuming failed: %d\n", error);
-
-err:
-	enable_irq(data->client->irq);
-	return error;
+	return ret;
 }
 
 static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);