The touchscreen can be put into a deep sleep state that prevents it from
emitting touch irqs. Put the touchscreen into deep sleep during suspend
if it is not marked as a wakeup source.
This also fixes a problem with the touchscreen getting unresponsive after
system resume because it pulled the interrupt line low during sleep in
response to a touch event, thereby effectively disabling the interrupt
handling (which triggers on the falling edge).
Signed-off-by: Maximilian Weigand <[email protected]>
Reviewed-by: Alistair Francis <[email protected]>
---
drivers/input/touchscreen/cyttsp5.c | 129 +++++++++++++++++++++++++++-
1 file changed, 128 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c
index 01dd10a596ab..3e8387f6347c 100644
--- a/drivers/input/touchscreen/cyttsp5.c
+++ b/drivers/input/touchscreen/cyttsp5.c
@@ -43,6 +43,7 @@
#define HID_DESC_REG 0x1
#define HID_INPUT_REG 0x3
#define HID_OUTPUT_REG 0x4
+#define HID_COMMAND_REG 0x5
#define REPORT_ID_TOUCH 0x1
#define REPORT_ID_BTN 0x3
@@ -68,6 +69,7 @@
#define HID_APP_OUTPUT_REPORT_ID 0x2F
#define HID_BL_RESPONSE_REPORT_ID 0x30
#define HID_BL_OUTPUT_REPORT_ID 0x40
+#define HID_RESPONSE_REPORT_ID 0xF0
#define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2
#define HID_OUTPUT_RESPONSE_CMD_OFFSET 4
@@ -78,9 +80,15 @@
#define HID_SYSINFO_BTN_MASK GENMASK(7, 0)
#define HID_SYSINFO_MAX_BTN 8
+#define HID_CMD_SET_POWER 0x8
+
+#define HID_POWER_ON 0x0
+#define HID_POWER_SLEEP 0x1
+
#define CY_HID_OUTPUT_TIMEOUT_MS 200
#define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS 3000
#define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS 4000
+#define CY_HID_SET_POWER_TIMEOUT 500
/* maximum number of concurrent tracks */
#define TOUCH_REPORT_SIZE 10
@@ -100,6 +108,14 @@
#define TOUCH_REPORT_USAGE_PG_MIN 0xFF010063
#define TOUCH_COL_USAGE_PG 0x000D0022
+#define SET_CMD_LOW(byte, bits) \
+ ((byte) = (((byte) & 0xF0) | ((bits) & 0x0F)))
+#define SET_CMD_HIGH(byte, bits)\
+ ((byte) = (((byte) & 0x0F) | ((bits) & 0xF0)))
+#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode)
+#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4))
+#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id)
+
/* System Information interface definitions */
struct cyttsp5_sensing_conf_data_dev {
u8 electrodes_x;
@@ -179,6 +195,7 @@ struct cyttsp5_hid_desc {
struct cyttsp5 {
struct device *dev;
struct completion cmd_done;
+ struct completion cmd_command_done;
struct cyttsp5_sysinfo sysinfo;
struct cyttsp5_hid_desc hid_desc;
u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER];
@@ -191,6 +208,7 @@ struct cyttsp5 {
struct regmap *regmap;
struct touchscreen_properties prop;
struct regulator *vdd;
+ bool is_wakeup_source;
};
/*
@@ -556,6 +574,84 @@ static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5 *ts)
return cyttsp5_get_sysinfo_regs(ts);
}
+static int cyttsp5_enter_sleep(struct cyttsp5 *ts)
+{
+ int rc;
+ u8 cmd[2];
+ u16 crc;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ SET_CMD_REPORT_TYPE(cmd[0], 0);
+ SET_CMD_REPORT_ID(cmd[0], HID_POWER_SLEEP);
+ SET_CMD_OPCODE(cmd[1], HID_CMD_SET_POWER);
+
+ rc = cyttsp5_write(ts, HID_COMMAND_REG, cmd, 2);
+ if (rc) {
+ dev_err(ts->dev, "Failed to write command %d", rc);
+ return rc;
+ }
+
+ rc = wait_for_completion_interruptible_timeout(&ts->cmd_command_done,
+ msecs_to_jiffies(CY_HID_SET_POWER_TIMEOUT));
+ if (rc <= 0) {
+ dev_err(ts->dev, "HID output cmd execution timed out\n");
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+
+ /* validate */
+ if ((ts->response_buf[2] != HID_RESPONSE_REPORT_ID)
+ || ((ts->response_buf[3] & 0x3) != HID_POWER_SLEEP)
+ || ((ts->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) {
+ rc = -EINVAL;
+ dev_err(ts->dev, "Validation of the sleep response failed\n");
+ return rc;
+ }
+
+ return 0;
+
+}
+
+static int cyttsp5_wakeup(struct cyttsp5 *ts)
+{
+ int rc;
+ u8 cmd[2];
+ u16 crc;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ SET_CMD_REPORT_TYPE(cmd[0], 0);
+ SET_CMD_REPORT_ID(cmd[0], HID_POWER_ON);
+ SET_CMD_OPCODE(cmd[1], HID_CMD_SET_POWER);
+
+ rc = cyttsp5_write(ts, HID_COMMAND_REG, cmd, 2);
+ if (rc) {
+ dev_err(ts->dev, "Failed to write command %d", rc);
+ return rc;
+ }
+
+ rc = wait_for_completion_interruptible_timeout(&ts->cmd_command_done,
+ msecs_to_jiffies(CY_HID_SET_POWER_TIMEOUT));
+ if (rc <= 0) {
+ dev_err(ts->dev, "HID output cmd execution timed out\n");
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+
+ /* validate */
+ if ((ts->response_buf[2] != HID_RESPONSE_REPORT_ID)
+ || ((ts->response_buf[3] & 0x3) != HID_POWER_ON)
+ || ((ts->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) {
+ rc = -EINVAL;
+ dev_err(ts->dev, "Validation of the sleep response failed\n");
+ return rc;
+ }
+
+ return 0;
+
+}
+
static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5 *ts)
{
int rc;
@@ -670,6 +766,10 @@ static irqreturn_t cyttsp5_handle_irq(int irq, void *handle)
case HID_BTN_REPORT_ID:
cyttsp5_btn_attention(ts->dev);
break;
+ case HID_RESPONSE_REPORT_ID:
+ memcpy(ts->response_buf, ts->input_buf, size);
+ complete(&ts->cmd_command_done);
+ break;
default:
/* It is not an input but a command response */
memcpy(ts->response_buf, ts->input_buf, size);
@@ -784,6 +884,7 @@ static int cyttsp5_probe(struct device *dev, struct regmap *regmap, int irq,
dev_set_drvdata(dev, ts);
init_completion(&ts->cmd_done);
+ init_completion(&ts->cmd_command_done);
/* Power up the device */
ts->vdd = devm_regulator_get(dev, "vdd");
@@ -830,8 +931,11 @@ static int cyttsp5_probe(struct device *dev, struct regmap *regmap, int irq,
return error;
}
- if (device_property_read_bool(dev, "wakeup-source"))
+ if (device_property_read_bool(dev, "wakeup-source")) {
device_init_wakeup(dev, true);
+ ts->is_wakeup_source = true;
+ } else
+ ts->is_wakeup_source = false;
error = cyttsp5_startup(ts);
if (error) {
@@ -884,6 +988,29 @@ static const struct i2c_device_id cyttsp5_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id);
+static int __maybe_unused cyttsp5_suspend(struct device *dev)
+{
+ struct cyttsp5 *ts = dev_get_drvdata(dev);
+
+ if (!ts->is_wakeup_source)
+ cyttsp5_enter_sleep(ts);
+ return 0;
+}
+
+static int __maybe_unused cyttsp5_resume(struct device *dev)
+{
+ struct cyttsp5 *ts = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ int error;
+
+ if (!ts->is_wakeup_source)
+ cyttsp5_wakeup(ts);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cyttsp5_pm, cyttsp5_suspend, cyttsp5_resume);
+
static struct i2c_driver cyttsp5_i2c_driver = {
.driver = {
.name = CYTTSP5_NAME,
--
2.39.2
Hi Maximilian,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on dtor-input/next]
[also build test WARNING on dtor-input/for-linus linus/master v6.3-rc4 next-20230327]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Maximilian-Weigand/Input-cyttsp5-fix-array-length/20230323-215957
base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
patch link: https://lore.kernel.org/r/20230323135205.1160879-7-mweigand%40mweigand.net
patch subject: [PATCH 6/6] Input: cyttsp5: implement proper sleep and wakeup procedures
config: i386-randconfig-a011-20230327 (https://download.01.org/0day-ci/archive/20230328/[email protected]/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/9988f8132aa9a4e8c9f0eb3093b06a9f02d90ec9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Maximilian-Weigand/Input-cyttsp5-fix-array-length/20230323-215957
git checkout 9988f8132aa9a4e8c9f0eb3093b06a9f02d90ec9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/input/touchscreen/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/input/touchscreen/cyttsp5.c:581:6: warning: unused variable 'crc' [-Wunused-variable]
u16 crc;
^
drivers/input/touchscreen/cyttsp5.c:620:6: warning: unused variable 'crc' [-Wunused-variable]
u16 crc;
^
drivers/input/touchscreen/cyttsp5.c:700:5: warning: unused variable 'cmd' [-Wunused-variable]
u8 cmd[2];
^
>> drivers/input/touchscreen/cyttsp5.c:1004:6: warning: unused variable 'error' [-Wunused-variable]
int error;
^
>> drivers/input/touchscreen/cyttsp5.c:1003:21: warning: unused variable 'client' [-Wunused-variable]
struct i2c_client *client = to_i2c_client(dev);
^
5 warnings generated.
vim +/crc +581 drivers/input/touchscreen/cyttsp5.c
576
577 static int cyttsp5_enter_sleep(struct cyttsp5 *ts)
578 {
579 int rc;
580 u8 cmd[2];
> 581 u16 crc;
582
583 memset(cmd, 0, sizeof(cmd));
584
585 SET_CMD_REPORT_TYPE(cmd[0], 0);
586 SET_CMD_REPORT_ID(cmd[0], HID_POWER_SLEEP);
587 SET_CMD_OPCODE(cmd[1], HID_CMD_SET_POWER);
588
589 rc = cyttsp5_write(ts, HID_COMMAND_REG, cmd, 2);
590 if (rc) {
591 dev_err(ts->dev, "Failed to write command %d", rc);
592 return rc;
593 }
594
595 rc = wait_for_completion_interruptible_timeout(&ts->cmd_command_done,
596 msecs_to_jiffies(CY_HID_SET_POWER_TIMEOUT));
597 if (rc <= 0) {
598 dev_err(ts->dev, "HID output cmd execution timed out\n");
599 rc = -ETIMEDOUT;
600 return rc;
601 }
602
603 /* validate */
604 if ((ts->response_buf[2] != HID_RESPONSE_REPORT_ID)
605 || ((ts->response_buf[3] & 0x3) != HID_POWER_SLEEP)
606 || ((ts->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) {
607 rc = -EINVAL;
608 dev_err(ts->dev, "Validation of the sleep response failed\n");
609 return rc;
610 }
611
612 return 0;
613
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests