atmel_mxt_ts - suspend patch and minor fixes

20 messages, 3 authors, 2015-08-11 · open the first message on its own page

atmel_mxt_ts - suspend patch and minor fixes

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:48

Hi Dmitry-

Here is the suspend patch for atmel_mxt_ts, again. I've tested it on Pixel
2 today. The code for T7 deep sleep has been widely used for years in our
github version of the driver.

Also, several minor (but useful) fixes.

And I've added myself to MAINTAINERS because checkpatch.

best regards

Nick Dyer

[PATCH 1/9] MAINTAINERS: Add maintainer for atmel_mxt_ts

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:47

Signed-off-by: Nick Dyer <redacted>
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index a226416..d2409bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1915,6 +1915,12 @@ W:	http://atmelwlandriver.sourceforge.net/
 S:	Maintained
 F:	drivers/net/wireless/atmel*
 
+ATMEL MAXTOUCH DRIVER
+M:	Nick Dyer <nick.dyer@itdev.co.uk>
+T:	git git://github.com/atmel-maxtouch/linux.git
+S:	Supported
+F:	drivers/input/touchscreen/atmel_mxt_ts.c
+
 ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
 M:	Bradley Grove <linuxdrivers@attotech.com>
 L:	linux-scsi@vger.kernel.org
-- 
2.4.6

[PATCH 7/9] Input: atmel_mxt_ts - disable interrupt for 50ms after reset

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:47

The CHG/interrupt line is momentarily set (approximately 100 ms) as an
input after power-up or reset for diagnostic purposes. This may cause
spurious interrupts, so disable interrupt handler during this period.

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 7c14faa..ff48d45 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1098,7 +1098,9 @@ static int mxt_soft_reset(struct mxt_data *data)
 	struct device *dev = &data->client->dev;
 	int ret = 0;
 
-	dev_info(dev, "Resetting chip\n");
+	dev_info(dev, "Resetting device\n");
+
+	disable_irq(data->irq);
 
 	reinit_completion(&data->reset_completion);
 
@@ -1106,6 +1108,11 @@ static int mxt_soft_reset(struct mxt_data *data)
 	if (ret)
 		return ret;
 
+	/* Ignore CHG line for 100ms after reset */
+	msleep(100);
+
+	enable_irq(data->irq);
+
 	ret = mxt_wait_for_completion(data, &data->reset_completion,
 				      MXT_RESET_TIMEOUT);
 	if (ret)
-- 
2.4.6

[PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:48

The hardcoded 0x83 CTRL setting overrides other settings in that byte,
enabling extra reporting that may not be useful on a particular platform.

Implement improved suspend mechanism via deep sleep. By writing zero to
both the active and idle cycle times the maXTouch device can be put into a
deep sleep mode, using minimal power. It is necessary to issue a calibrate
command after the chip has spent any time in deep sleep, however a soft
reset is unnecessary.

Use the old method on Chromebook Pixel via platform data option.

This patch also deals with the situation where the power configuration is
zero on probe, which would mean that the device never wakes up to execute
commands.

After a config download, the T7 power configuration may have changed so it
is necessary to re-read it.

Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Acked-by: Yufeng Shen <redacted>
---
 .../devicetree/bindings/input/atmel,maxtouch.txt   |   5 +
 MAINTAINERS                                        |   2 +
 drivers/input/touchscreen/atmel_mxt_ts.c           | 120 +++++++++++++++++++--
 drivers/platform/chrome/chromeos_laptop.c          |   4 +-
 include/dt-bindings/input/atmel_mxt_ts.h           |  21 ++++
 include/linux/i2c/atmel_mxt_ts.h                   |  25 -----
 include/linux/platform_data/atmel_mxt_ts.h         |  27 +++++
 7 files changed, 167 insertions(+), 37 deletions(-)
 create mode 100644 include/dt-bindings/input/atmel_mxt_ts.h
 delete mode 100644 include/linux/i2c/atmel_mxt_ts.h
 create mode 100644 include/linux/platform_data/atmel_mxt_ts.h
diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
index 1852906..1040371 100644
--- a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
+++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
@@ -22,6 +22,11 @@ Optional properties for main touchpad device:
     experiment to determine which bit corresponds to which input. Use
     KEY_RESERVED for unused padding values.
 
+- atmel,suspend-mode: Select method used to suspend:
+    MXT_SUSPEND_DEEP_SLEEP - use T7 to suspend the device into deep sleep
+    MXT_SUSPEND_T9_CTRL - use T9.CTRL to turn off touch processing
+    Definitions are in <dt-bindings/input/atmel_mxt_ts.h>.
+
 Example:
 
 	touch@4b {
diff --git a/MAINTAINERS b/MAINTAINERS
index d2409bf..c816a87 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1920,6 +1920,8 @@ M:	Nick Dyer <nick.dyer@itdev.co.uk>
 T:	git git://github.com/atmel-maxtouch/linux.git
 S:	Supported
 F:	drivers/input/touchscreen/atmel_mxt_ts.c
+F:	include/dt-bindings/input/atmel_mxt_ts.h
+F:	include/linux/platform_data/atmel_mxt_ts.h
 
 ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
 M:	Bradley Grove <linuxdrivers@attotech.com>
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 8efe7a0..f2bdcdb 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -22,7 +22,7 @@
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/i2c.h>
-#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/platform_data/atmel_mxt_ts.h>
 #include <linux/input/mt.h>
 #include <linux/interrupt.h>
 #include <linux/of.h>
@@ -103,9 +103,13 @@
 #define MXT_T6_STATUS_COMSERR	(1 << 2)
 
 /* MXT_GEN_POWER_T7 field */
-#define MXT_POWER_IDLEACQINT	0
-#define MXT_POWER_ACTVACQINT	1
-#define MXT_POWER_ACTV2IDLETO	2
+struct t7_config {
+	u8 idle;
+	u8 active;
+} __packed;
+
+#define MXT_POWER_CFG_RUN		0
+#define MXT_POWER_CFG_DEEPSLEEP		1
 
 /* MXT_GEN_ACQUIRE_T8 field */
 #define MXT_ACQUIRE_CHRGTIME	0
@@ -117,7 +121,7 @@
 #define MXT_ACQUIRE_ATCHCALSTHR	7
 
 /* MXT_TOUCH_MULTI_T9 field */
-#define MXT_TOUCH_CTRL		0
+#define MXT_T9_CTRL		0
 #define MXT_T9_ORIENT		9
 #define MXT_T9_RANGE		18
 
@@ -291,6 +295,7 @@ struct mxt_data {
 	u8 last_message_count;
 	u8 num_touchids;
 	u8 multitouch;
+	struct t7_config t7_cfg;
 
 	/* Cached parameters from object table */
 	u16 T5_address;
@@ -1361,6 +1366,8 @@ static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
 	return 0;
 }
 
+static int mxt_init_t7_power_cfg(struct mxt_data *data);
+
 /*
  * mxt_update_cfg - download configuration to chip
  *
@@ -1508,6 +1515,9 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
 
 	dev_info(dev, "Config successfully updated\n");
 
+	/* T7 config may have changed */
+	mxt_init_t7_power_cfg(data);
+
 release_mem:
 	kfree(config_mem);
 	return ret;
@@ -2051,6 +2061,60 @@ err_free_object_table:
 	return error;
 }
 
+static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	struct t7_config *new_config;
+	struct t7_config deepsleep = { .active = 0, .idle = 0 };
+
+	if (sleep == MXT_POWER_CFG_DEEPSLEEP)
+		new_config = &deepsleep;
+	else
+		new_config = &data->t7_cfg;
+
+	error = __mxt_write_reg(data->client, data->T7_address,
+				sizeof(data->t7_cfg), new_config);
+	if (error)
+		return error;
+
+	dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
+		new_config->active, new_config->idle);
+
+	return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	bool retry = false;
+
+recheck:
+	error = __mxt_read_reg(data->client, data->T7_address,
+				sizeof(data->t7_cfg), &data->t7_cfg);
+	if (error)
+		return error;
+
+	if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
+		if (!retry) {
+			dev_dbg(dev, "T7 cfg zero, resetting\n");
+			mxt_soft_reset(data);
+			retry = true;
+			goto recheck;
+		} else {
+			dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
+			data->t7_cfg.active = 20;
+			data->t7_cfg.idle = 100;
+			return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+		}
+	}
+
+	dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
+		data->t7_cfg.active, data->t7_cfg.idle);
+	return 0;
+}
+
 static int mxt_configure_objects(struct mxt_data *data,
 				 const struct firmware *cfg)
 {
@@ -2058,6 +2122,12 @@ static int mxt_configure_objects(struct mxt_data *data,
 	struct mxt_info *info = &data->info;
 	int error;
 
+	error = mxt_init_t7_power_cfg(data);
+	if (error) {
+		dev_err(dev, "Failed to initialize power cfg\n");
+		return error;
+	}
+
 	if (cfg) {
 		error = mxt_update_cfg(data, cfg);
 		if (error)
@@ -2346,14 +2416,41 @@ static const struct attribute_group mxt_attr_group = {
 
 static void mxt_start(struct mxt_data *data)
 {
-	/* Touch enable */
-	mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
+	switch (data->pdata->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		mxt_soft_reset(data);
+
+		/* Touch enable */
+		/* 0x83 = SCANEN | RPTEN | ENABLE */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+		/* Recalibrate since chip has been in deep sleep */
+		mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
+		break;
+	}
+
 }
 
 static void mxt_stop(struct mxt_data *data)
 {
-	/* Touch disable */
-	mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
+	switch (data->pdata->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		/* Touch disable */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
+		break;
+	}
 }
 
 static int mxt_input_open(struct input_dev *dev)
@@ -2409,6 +2506,9 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
 		pdata->t19_keymap = keymap;
 	}
 
+	of_property_read_u32(client->dev.of_node, "atmel,suspend-mode",
+			     &pdata->suspend_mode);
+
 	return pdata;
 }
 #else
@@ -2625,8 +2725,6 @@ static int __maybe_unused mxt_resume(struct device *dev)
 	struct mxt_data *data = i2c_get_clientdata(client);
 	struct input_dev *input_dev = data->input_dev;
 
-	mxt_soft_reset(data);
-
 	mutex_lock(&input_dev->mutex);
 
 	if (input_dev->users)
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index a04019a..0207274 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -23,7 +23,7 @@
 
 #include <linux/dmi.h>
 #include <linux/i2c.h>
-#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/platform_data/atmel_mxt_ts.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -111,6 +111,7 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
 	.irqflags		= IRQF_TRIGGER_FALLING,
 	.t19_num_keys		= ARRAY_SIZE(mxt_t19_keys),
 	.t19_keymap		= mxt_t19_keys,
+	.suspend_mode		= MXT_SUSPEND_T9_CTRL,
 };
 
 static struct i2c_board_info atmel_224s_tp_device = {
@@ -121,6 +122,7 @@ static struct i2c_board_info atmel_224s_tp_device = {
 
 static struct mxt_platform_data atmel_1664s_platform_data = {
 	.irqflags		= IRQF_TRIGGER_FALLING,
+	.suspend_mode		= MXT_SUSPEND_T9_CTRL,
 };
 
 static struct i2c_board_info atmel_1664s_device = {
diff --git a/include/dt-bindings/input/atmel_mxt_ts.h b/include/dt-bindings/input/atmel_mxt_ts.h
new file mode 100644
index 0000000..37a6ab1
--- /dev/null
+++ b/include/dt-bindings/input/atmel_mxt_ts.h
@@ -0,0 +1,21 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ * Author: Nick Dyer <nick.dyer@itdev.co.uk>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __DT_BINDINGS_ATMEL_MXT_TS_H
+#define __DT_BINDINGS_ATMEL_MXT_TS_H
+
+enum mxt_suspend_mode {
+	MXT_SUSPEND_DEEP_SLEEP = 0,
+	MXT_SUSPEND_T9_CTRL = 1,
+};
+
+#endif /* __DT_BINDINGS_ATMEL_MXT_TS_H */
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
deleted file mode 100644
index 02bf6ea..0000000
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Atmel maXTouch Touchscreen driver
- *
- * Copyright (C) 2010 Samsung Electronics Co.Ltd
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __LINUX_ATMEL_MXT_TS_H
-#define __LINUX_ATMEL_MXT_TS_H
-
-#include <linux/types.h>
-
-/* The platform data for the Atmel maXTouch touchscreen driver */
-struct mxt_platform_data {
-	unsigned long irqflags;
-	u8 t19_num_keys;
-	const unsigned int *t19_keymap;
-};
-
-#endif /* __LINUX_ATMEL_MXT_TS_H */
diff --git a/include/linux/platform_data/atmel_mxt_ts.h b/include/linux/platform_data/atmel_mxt_ts.h
new file mode 100644
index 0000000..cabd838
--- /dev/null
+++ b/include/linux/platform_data/atmel_mxt_ts.h
@@ -0,0 +1,27 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H
+#define __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H
+
+#include <linux/types.h>
+#include <dt-bindings/input/atmel_mxt_ts.h>
+
+/* The platform data for the Atmel maXTouch touchscreen driver */
+struct mxt_platform_data {
+	unsigned long irqflags;
+	u8 t19_num_keys;
+	const unsigned int *t19_keymap;
+	enum mxt_suspend_mode suspend_mode;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H */
-- 
2.4.6

[PATCH 9/9] Input: atmel_mxt_ts - remove warning on zero T44 count

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:48

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2aa44dc..c874008 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -938,16 +938,15 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
 
 	count = data->msg_buf[0];
 
-	if (count == 0) {
-		/*
-		 * This condition is caused by the CHG line being configured
-		 * in Mode 0. It results in unnecessary I2C operations but it
-		 * is benign.
-		 */
-		dev_dbg(dev, "Interrupt triggered but zero messages\n");
+	/*
+	 * This condition may be caused by the CHG line being configured in
+	 * Mode 0. It results in unnecessary I2C operations but it is benign.
+	 */
+	if (count == 0)
 		return IRQ_NONE;
-	} else if (count > data->max_reportid) {
-		dev_err(dev, "T44 count %d exceeded max report id\n", count);
+
+	if (count > data->max_reportid) {
+		dev_warn(dev, "T44 count %d exceeded max report id\n", count);
 		count = data->max_reportid;
 	}
 
-- 
2.4.6

[PATCH 4/9] Input: atmel_mxt_ts - move mxt_initialize after sysfs init

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:49

An error in the sysfs init may otherwise interfere with the async return
from the firmware loader

Add guards for sysfs functions

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 800ee0d..869dbb3 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2092,6 +2092,10 @@ static ssize_t mxt_fw_version_show(struct device *dev,
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	struct mxt_info *info = &data->info;
+
+	if (!data->object_table)
+		return -EINVAL;
+
 	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
 			 info->version >> 4, info->version & 0xf, info->build);
 }
@@ -2102,6 +2106,10 @@ static ssize_t mxt_hw_version_show(struct device *dev,
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	struct mxt_info *info = &data->info;
+
+	if (!data->object_table)
+		return -EINVAL;
+
 	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
 			 info->family_id, info->variant_id);
 }
@@ -2134,6 +2142,9 @@ static ssize_t mxt_object_show(struct device *dev,
 	int error;
 	u8 *obuf;
 
+	if (!data->object_table)
+		return -EINVAL;
+
 	/* Pre-allocate buffer large enough to hold max sized object. */
 	obuf = kmalloc(256, GFP_KERNEL);
 	if (!obuf)
@@ -2603,22 +2614,19 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	disable_irq(client->irq);
 
-	error = mxt_initialize(data);
-	if (error)
-		goto err_free_irq;
-
 	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
 	if (error) {
 		dev_err(&client->dev, "Failure %d creating sysfs group\n",
 			error);
-		goto err_free_object;
+		goto err_free_irq;
 	}
 
+	error = mxt_initialize(data);
+	if (error)
+		goto err_free_irq;
+
 	return 0;
 
-err_free_object:
-	mxt_free_input_device(data);
-	mxt_free_object_table(data);
 err_free_irq:
 	free_irq(client->irq, data);
 err_free_mem:
-- 
2.4.6

[PATCH 8/9] Input: atmel_mxt_ts - initialise input slots with INPUT_MT_DIRECT

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:49

This indicates the device coordinates should be directly mapped to screen.
This is valid since scaling/flipping/rotation should be done by configuring
the MXT device.

It also flags to Android using INPUT_PROP_DIRECT that the device should be
treated as a touch screen by default, and removes the necessity for a
default IDC file.

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ff48d45..2aa44dc 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1858,6 +1858,8 @@ static int mxt_initialize_input_device(struct mxt_data *data)
 	if (pdata->t19_num_keys) {
 		mxt_set_up_as_touchpad(input_dev, data);
 		mt_flags |= INPUT_MT_POINTER;
+	} else {
+		mt_flags |= INPUT_MT_DIRECT;
 	}
 
 	/* For multi touch */
-- 
2.4.6

[PATCH 5/9] Input: atmel_mxt_ts - suspend/resume causes panic if input_dev fails to init

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:49

From: Pan Xinhui <redacted>

input_dev may be NULL if mxt_initialize_input_device fails. But pm ops is
still available and suspend/resume assume
input_dev is not NULL. To fix this issue, we add a check if (!input_dev).

Signed-off-by: Pan Xinhui <redacted>
Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 6 ++++++
 1 file changed, 6 insertions(+)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 869dbb3..b83e8a0 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2653,6 +2653,9 @@ static int __maybe_unused mxt_suspend(struct device *dev)
 	struct mxt_data *data = i2c_get_clientdata(client);
 	struct input_dev *input_dev = data->input_dev;
 
+	if (!input_dev)
+		return 0;
+
 	mutex_lock(&input_dev->mutex);
 
 	if (input_dev->users)
@@ -2669,6 +2672,9 @@ static int __maybe_unused mxt_resume(struct device *dev)
 	struct mxt_data *data = i2c_get_clientdata(client);
 	struct input_dev *input_dev = data->input_dev;
 
+	if (!input_dev)
+		return 0;
+
 	mutex_lock(&input_dev->mutex);
 
 	if (input_dev->users)
-- 
2.4.6

[PATCH 3/9] Input: atmel_mxt_ts - remove unused defines

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:49

Many of these values are out of date and they aren't used in the driver
- all they do is increase the size of the kernel source.

Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Acked-by: Yufeng Shen <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 66 +-------------------------------
 1 file changed, 1 insertion(+), 65 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index f2bdcdb..800ee0d 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -29,27 +29,13 @@
 #include <linux/slab.h>
 #include <asm/unaligned.h>
 
-/* Version */
-#define MXT_VER_20		20
-#define MXT_VER_21		21
-#define MXT_VER_22		22
-
 /* Firmware files */
 #define MXT_FW_NAME		"maxtouch.fw"
 #define MXT_CFG_NAME		"maxtouch.cfg"
 #define MXT_CFG_MAGIC		"OBP_RAW V1"
 
 /* Registers */
-#define MXT_INFO		0x00
-#define MXT_FAMILY_ID		0x00
-#define MXT_VARIANT_ID		0x01
-#define MXT_VERSION		0x02
-#define MXT_BUILD		0x03
-#define MXT_MATRIX_X_SIZE	0x04
-#define MXT_MATRIX_Y_SIZE	0x05
-#define MXT_OBJECT_NUM		0x06
 #define MXT_OBJECT_START	0x07
-
 #define MXT_OBJECT_SIZE		6
 #define MXT_INFO_CHECKSUM_SIZE	3
 #define MXT_MAX_BLOCK_WRITE	256
@@ -111,15 +97,6 @@ struct t7_config {
 #define MXT_POWER_CFG_RUN		0
 #define MXT_POWER_CFG_DEEPSLEEP		1
 
-/* MXT_GEN_ACQUIRE_T8 field */
-#define MXT_ACQUIRE_CHRGTIME	0
-#define MXT_ACQUIRE_TCHDRIFT	2
-#define MXT_ACQUIRE_DRIFTST	3
-#define MXT_ACQUIRE_TCHAUTOCAL	4
-#define MXT_ACQUIRE_SYNC	5
-#define MXT_ACQUIRE_ATCHCALST	6
-#define MXT_ACQUIRE_ATCHCALSTHR	7
-
 /* MXT_TOUCH_MULTI_T9 field */
 #define MXT_T9_CTRL		0
 #define MXT_T9_ORIENT		9
@@ -143,51 +120,10 @@ struct t9_range {
 /* MXT_TOUCH_MULTI_T9 orient */
 #define MXT_T9_ORIENT_SWITCH	(1 << 0)
 
-/* MXT_PROCI_GRIPFACE_T20 field */
-#define MXT_GRIPFACE_CTRL	0
-#define MXT_GRIPFACE_XLOGRIP	1
-#define MXT_GRIPFACE_XHIGRIP	2
-#define MXT_GRIPFACE_YLOGRIP	3
-#define MXT_GRIPFACE_YHIGRIP	4
-#define MXT_GRIPFACE_MAXTCHS	5
-#define MXT_GRIPFACE_SZTHR1	7
-#define MXT_GRIPFACE_SZTHR2	8
-#define MXT_GRIPFACE_SHPTHR1	9
-#define MXT_GRIPFACE_SHPTHR2	10
-#define MXT_GRIPFACE_SUPEXTTO	11
-
-/* MXT_PROCI_NOISE field */
-#define MXT_NOISE_CTRL		0
-#define MXT_NOISE_OUTFLEN	1
-#define MXT_NOISE_GCAFUL_LSB	3
-#define MXT_NOISE_GCAFUL_MSB	4
-#define MXT_NOISE_GCAFLL_LSB	5
-#define MXT_NOISE_GCAFLL_MSB	6
-#define MXT_NOISE_ACTVGCAFVALID	7
-#define MXT_NOISE_NOISETHR	8
-#define MXT_NOISE_FREQHOPSCALE	10
-#define MXT_NOISE_FREQ0		11
-#define MXT_NOISE_FREQ1		12
-#define MXT_NOISE_FREQ2		13
-#define MXT_NOISE_FREQ3		14
-#define MXT_NOISE_FREQ4		15
-#define MXT_NOISE_IDLEGCAFVALID	16
-
 /* MXT_SPT_COMMSCONFIG_T18 */
 #define MXT_COMMS_CTRL		0
 #define MXT_COMMS_CMD		1
 
-/* MXT_SPT_CTECONFIG_T28 field */
-#define MXT_CTE_CTRL		0
-#define MXT_CTE_CMD		1
-#define MXT_CTE_MODE		2
-#define MXT_CTE_IDLEGCAFDEPTH	3
-#define MXT_CTE_ACTVGCAFDEPTH	4
-#define MXT_CTE_VOLTAGE		5
-
-#define MXT_VOLTAGE_DEFAULT	2700000
-#define MXT_VOLTAGE_STEP	10000
-
 /* Define for MXT_GEN_COMMAND_T6 */
 #define MXT_BOOT_VALUE		0xa5
 #define MXT_RESET_VALUE		0x01
@@ -1543,7 +1479,7 @@ static int mxt_get_info(struct mxt_data *data)
 	int error;
 
 	/* Read 7-byte info block starting at address 0 */
-	error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
+	error = __mxt_read_reg(client, 0, sizeof(*info), info);
 	if (error)
 		return error;
 
-- 
2.4.6

[PATCH 6/9] Input: atmel_mxt_ts - improve device tree parsing

From: Nick Dyer <hidden>
Date: 2015-08-03 15:32:49

Use function rather than loop, define np to reduce wrapping.

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index b83e8a0..7c14faa 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2420,19 +2420,18 @@ static void mxt_input_close(struct input_dev *dev)
 static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
 {
 	struct mxt_platform_data *pdata;
+	struct device_node *np = client->dev.of_node;
 	u32 *keymap;
-	u32 keycode;
-	int proplen, i, ret;
+	int proplen, ret;
 
-	if (!client->dev.of_node)
+	if (!np)
 		return ERR_PTR(-ENOENT);
 
 	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata)
 		return ERR_PTR(-ENOMEM);
 
-	if (of_find_property(client->dev.of_node, "linux,gpio-keymap",
-			     &proplen)) {
+	if (of_find_property(np, "linux,gpio-keymap", &proplen)) {
 		pdata->t19_num_keys = proplen / sizeof(u32);
 
 		keymap = devm_kzalloc(&client->dev,
@@ -2441,20 +2440,16 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
 		if (!keymap)
 			return ERR_PTR(-ENOMEM);
 
-		for (i = 0; i < pdata->t19_num_keys; i++) {
-			ret = of_property_read_u32_index(client->dev.of_node,
-					"linux,gpio-keymap", i, &keycode);
-			if (ret)
-				keycode = KEY_RESERVED;
-
-			keymap[i] = keycode;
-		}
+		ret = of_property_read_u32_array(np, "linux,gpio-keymap",
+						 keymap, pdata->t19_num_keys);
+		if (ret)
+			dev_warn(&client->dev,
+				"Couldn't read linux,gpio-keymap: %d\n", ret);
 
 		pdata->t19_keymap = keymap;
 	}
 
-	of_property_read_u32(client->dev.of_node, "atmel,suspend-mode",
-			     &pdata->suspend_mode);
+	of_property_read_u32(np, "atmel,suspend-mode", &pdata->suspend_mode);
 
 	return pdata;
 }
-- 
2.4.6

Re: [PATCH 4/9] Input: atmel_mxt_ts - move mxt_initialize after sysfs init

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: 2015-08-05 00:00:45

On Mon, Aug 03, 2015 at 04:23:12PM +0100, Nick Dyer wrote:
An error in the sysfs init may otherwise interfere with the async return
from the firmware loader
In this case I'd rather we blocked accessing the device via sysfs until
it is settled instead of returning intermittent -EINVAL.

Thanks.
quoted hunk
Add guards for sysfs functions

Signed-off-by: Nick Dyer <redacted>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 800ee0d..869dbb3 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2092,6 +2092,10 @@ static ssize_t mxt_fw_version_show(struct device *dev,
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	struct mxt_info *info = &data->info;
+
+	if (!data->object_table)
+		return -EINVAL;
+
 	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
 			 info->version >> 4, info->version & 0xf, info->build);
 }
@@ -2102,6 +2106,10 @@ static ssize_t mxt_hw_version_show(struct device *dev,
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
 	struct mxt_info *info = &data->info;
+
+	if (!data->object_table)
+		return -EINVAL;
+
 	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
 			 info->family_id, info->variant_id);
 }
@@ -2134,6 +2142,9 @@ static ssize_t mxt_object_show(struct device *dev,
 	int error;
 	u8 *obuf;
 
+	if (!data->object_table)
+		return -EINVAL;
+
 	/* Pre-allocate buffer large enough to hold max sized object. */
 	obuf = kmalloc(256, GFP_KERNEL);
 	if (!obuf)
@@ -2603,22 +2614,19 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	disable_irq(client->irq);
 
-	error = mxt_initialize(data);
-	if (error)
-		goto err_free_irq;
-
 	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
 	if (error) {
 		dev_err(&client->dev, "Failure %d creating sysfs group\n",
 			error);
-		goto err_free_object;
+		goto err_free_irq;
 	}
 
+	error = mxt_initialize(data);
+	if (error)
+		goto err_free_irq;
+
 	return 0;
 
-err_free_object:
-	mxt_free_input_device(data);
-	mxt_free_object_table(data);
 err_free_irq:
 	free_irq(client->irq, data);
 err_free_mem:
-- 
2.4.6
-- 
Dmitry

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: 2015-08-05 00:02:15

On Mon, Aug 03, 2015 at 04:23:10PM +0100, Nick Dyer wrote:
The hardcoded 0x83 CTRL setting overrides other settings in that byte,
enabling extra reporting that may not be useful on a particular platform.

Implement improved suspend mechanism via deep sleep. By writing zero to
both the active and idle cycle times the maXTouch device can be put into a
deep sleep mode, using minimal power. It is necessary to issue a calibrate
command after the chip has spent any time in deep sleep, however a soft
reset is unnecessary.

Use the old method on Chromebook Pixel via platform data option.

This patch also deals with the situation where the power configuration is
zero on probe, which would mean that the device never wakes up to execute
commands.

After a config download, the T7 power configuration may have changed so it
is necessary to re-read it.

Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Acked-by: Yufeng Shen <redacted>
---
 .../devicetree/bindings/input/atmel,maxtouch.txt   |   5 +
Since the T9 code is for compatibility with old Pixel only I do not
think we should be adding it to device tree binding, at least not at the
moment. I cut the device tree parsing out and default to deep sleep on
DT-based systems.

Thanks.

-- 
Dmitry

Re: atmel_mxt_ts - suspend patch and minor fixes

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: 2015-08-05 00:03:17

Hi Nick,

On Mon, Aug 03, 2015 at 04:23:08PM +0100, Nick Dyer wrote:
Hi Dmitry-

Here is the suspend patch for atmel_mxt_ts, again. I've tested it on Pixel
2 today. The code for T7 deep sleep has been widely used for years in our
github version of the driver.

Also, several minor (but useful) fixes.

And I've added myself to MAINTAINERS because checkpatch.
Applied all but 4; 1 & 2 with minor edits.

Thanks.

-- 
Dmitry

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Nick Dyer <hidden>
Date: 2015-08-06 13:04:16


On 05/08/15 01:02, Dmitry Torokhov wrote:
Since the T9 code is for compatibility with old Pixel only I do not think
we should be adding it to device tree binding, at least not at the
moment. I cut the device tree parsing out and default to deep sleep on
DT-based systems. Thanks. 
That sounds fair enough. We do need something like this eventually to
support different suspend implementations (eg powering down via regulators).

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: 2015-08-06 22:57:26

On Thu, Aug 06, 2015 at 02:04:15PM +0100, Nick Dyer wrote:

On 05/08/15 01:02, Dmitry Torokhov wrote:
quoted
Since the T9 code is for compatibility with old Pixel only I do not think
we should be adding it to device tree binding, at least not at the
moment. I cut the device tree parsing out and default to deep sleep on
DT-based systems. Thanks. 
That sounds fair enough. We do need something like this eventually to
support different suspend implementations (eg powering down via regulators).
That does not really need config I think: in ChromeOS tree wee key the
behavior off presence of reset GPIO line: if it sis there (definied via
ACPI, devicetree or board lookup code) then we assume we need to control
the power up sequence with regulators, otherwise we put the controller
into deep sleep mode.

Thanks.

-- 
Dmitry

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Benjamin Tissoires <hidden>
Date: 2015-08-10 20:11:32

Hi Nick,

On Mon, Aug 3, 2015 at 11:23 AM, Nick Dyer [off-list ref] wrote:
The hardcoded 0x83 CTRL setting overrides other settings in that byte,
enabling extra reporting that may not be useful on a particular platform.

Implement improved suspend mechanism via deep sleep. By writing zero to
both the active and idle cycle times the maXTouch device can be put into a
deep sleep mode, using minimal power. It is necessary to issue a calibrate
command after the chip has spent any time in deep sleep, however a soft
reset is unnecessary.

Use the old method on Chromebook Pixel via platform data option.

This patch also deals with the situation where the power configuration is
zero on probe, which would mean that the device never wakes up to execute
commands.

After a config download, the T7 power configuration may have changed so it
is necessary to re-read it.

Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Acked-by: Yufeng Shen <redacted>
---
I am currently running v4.2-rc6, merged with Dmitry's and Jiri's tree.
This patch completely kills my touchpad on the Pixel 2. No touch
information is sent while the physical button still emits values. You
said that you tested it on this laptop too, so I wonder why mine
refuses to work.

It looks also that the deep sleep gets kept while rebooting a
different kernel. If I reboot to the standard fedora kernel (without
this patch), I still have the same problem, and I have to reboot under
ChromeOS to get the touchpad back alive. Then, the Fedora kernel works
just fine.

Any ideas why?

Cheers,
Benjamin

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Nick Dyer <hidden>
Date: 2015-08-11 14:24:43

On 10/08/15 21:11, Benjamin Tissoires wrote:
I am currently running v4.2-rc6, merged with Dmitry's and Jiri's tree.
This patch completely kills my touchpad on the Pixel 2. No touch
information is sent while the physical button still emits values. You
said that you tested it on this laptop too, so I wonder why mine
refuses to work.
I'm just building that version to see if I can reproduce. Dmitry made some
minor changes when he merged it, but I don't think it should have affected
anything.

Are you able to send me some dmesg output with dyndbg=+pt enabled?

It would also be useful if you could compile mxt-app and try issuing a
calibrate or reset command to see if that restores touch (let me know if
you need instructions).
It looks also that the deep sleep gets kept while rebooting a
different kernel. If I reboot to the standard fedora kernel (without
this patch), I still have the same problem, and I have to reboot under
ChromeOS to get the touchpad back alive. Then, the Fedora kernel works
just fine.
That's unfortunate. In an ideal world the Fedora kernel driver should
reset the touch controller or at least check the power settings, and it
appears it's doing neither.

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Nick Dyer <hidden>
Date: 2015-08-11 15:55:52

On 11/08/15 15:24, Nick Dyer wrote:
On 10/08/15 21:11, Benjamin Tissoires wrote:
quoted
I am currently running v4.2-rc6, merged with Dmitry's and Jiri's tree.
This patch completely kills my touchpad on the Pixel 2. No touch
information is sent while the physical button still emits values. You
said that you tested it on this laptop too, so I wonder why mine
refuses to work.
I'm just building that version to see if I can reproduce. Dmitry made some
minor changes when he merged it, but I don't think it should have affected
anything.

Are you able to send me some dmesg output with dyndbg=+pt enabled?

It would also be useful if you could compile mxt-app and try issuing a
calibrate or reset command to see if that restores touch (let me know if
you need instructions).
I've done some testing and I believe I'm able to reproduce your issue. It
appears to be caused by a bad calibration, i.e. if I run the following
command then the touchpad starts working:

sudo ./mxt-app -d i2c-dev:0-004a --calibrate

(it times out waiting for the calibration complete message when in i2c-dev
mode, but that's expected)

Could you verify this at your end?

The patch under discussion sends a calibrate command as the input device is
opened. However I can see that user space is opening/closing the device 3x
in the space of about a second as X starts up, which may be confusing the
firmware. It might be that we need to wait for the calibration to complete,
I will try adding some code to do that.

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Benjamin Tissoires <hidden>
Date: 2015-08-11 16:29:55

Hi Nick,

On Tue, Aug 11, 2015 at 11:55 AM, Nick Dyer [off-list ref] wrote:
On 11/08/15 15:24, Nick Dyer wrote:
quoted
On 10/08/15 21:11, Benjamin Tissoires wrote:
quoted
I am currently running v4.2-rc6, merged with Dmitry's and Jiri's tree.
This patch completely kills my touchpad on the Pixel 2. No touch
information is sent while the physical button still emits values. You
said that you tested it on this laptop too, so I wonder why mine
refuses to work.
I'm just building that version to see if I can reproduce. Dmitry made some
minor changes when he merged it, but I don't think it should have affected
anything.

Are you able to send me some dmesg output with dyndbg=+pt enabled?
I will do that after lunch if you still need them.
quoted
It would also be useful if you could compile mxt-app and try issuing a
calibrate or reset command to see if that restores touch (let me know if
you need instructions).
I've done some testing and I believe I'm able to reproduce your issue. It
appears to be caused by a bad calibration, i.e. if I run the following
command then the touchpad starts working:

sudo ./mxt-app -d i2c-dev:0-004a --calibrate
Yep, this works with the deep sleep patches applied. On the regular
fedora kernel (without these patches, and after a boot with the kernel
with the patches), it does not make the touchpad back alive, however,
a reset with the mxt-app works.

Thanks for the fast work-around.
(it times out waiting for the calibration complete message when in i2c-dev
mode, but that's expected)

Could you verify this at your end?

The patch under discussion sends a calibrate command as the input device is
opened. However I can see that user space is opening/closing the device 3x
in the space of about a second as X starts up, which may be confusing the
firmware. It might be that we need to wait for the calibration to complete,
I will try adding some code to do that.
Can't you start a worker on open which will keep a ref count on how
many open/close you make and which would do the calibration in the
background without blocking the user-space? This way, you will be able
to guarantee that the calibration will end, and not be re-sent if
there are several open/close in a raw.

Cheers,
Benjamin

Re: [PATCH 2/9] Input: atmel_mxt_ts - use deep sleep mode when stopped

From: Nick Dyer <hidden>
Date: 2015-08-11 18:32:34

On 11/08/15 17:29, Benjamin Tissoires wrote:
quoted
quoted
Are you able to send me some dmesg output with dyndbg=+pt enabled?
I will do that after lunch if you still need them.
Looks unnecessary given that the calibrate worked.
quoted
sudo ./mxt-app -d i2c-dev:0-004a --calibrate
Yep, this works with the deep sleep patches applied. On the regular
fedora kernel (without these patches, and after a boot with the kernel
with the patches), it does not make the touchpad back alive, however,
a reset with the mxt-app works.

Thanks for the fast work-around.
Thanks for testing this!
quoted
The patch under discussion sends a calibrate command as the input device is
opened. However I can see that user space is opening/closing the device 3x
in the space of about a second as X starts up, which may be confusing the
firmware. It might be that we need to wait for the calibration to complete,
I will try adding some code to do that.
Can't you start a worker on open which will keep a ref count on how
many open/close you make and which would do the calibration in the
background without blocking the user-space? This way, you will be able
to guarantee that the calibration will end, and not be re-sent if
there are several open/close in a raw.
Yes, something like that sounds sensible.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help