From dc3ec8dee0a8a3ced5ea99aad092f1f927acd601 Mon Sep 17 00:00:00 2001 From: SwtcR Date: Sun, 11 Feb 2018 09:10:38 +0900 Subject: [PATCH 059/124] stmfts: support polled mode when there is no IRQ --- drivers/input/touchscreen/stmfts.c | 59 +++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 704e99046916..4cba504f046e 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -14,6 +14,7 @@ #include #include #include +#include /* I2C commands */ #define STMFTS_READ_INFO 0x80 @@ -104,6 +105,8 @@ struct stmfts_data { bool led_status; bool hover_enabled; bool running; + + struct task_struct *poll_thread; }; static void stmfts_brightness_set(struct led_classdev *led_cdev, @@ -313,6 +316,18 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) return IRQ_HANDLED; } +static int stmfts_poll_thread(void *data) +{ + printk("poll_thread starting\n"); + while (!kthread_should_stop()) { + stmfts_irq_handler(0, data); + msleep(2); + } + printk("poll_thread exiting\n"); + return 0; +} + + static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) { int err; @@ -546,18 +561,26 @@ static int stmfts_power_on(struct stmfts_data *sdata) sdata->fw_ver = be16_to_cpup((__be16 *)®[2]); sdata->config_id = reg[4]; sdata->config_ver = reg[5]; - - enable_irq(sdata->client->irq); + + if (sdata->client->irq) + enable_irq(sdata->client->irq); msleep(50); + if (!sdata->client->irq) { + sdata->poll_thread = kthread_run(stmfts_poll_thread, sdata, "stmfts_poll"); + if (IS_ERR(sdata->poll_thread)) + return -EIO; + } + err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); if (err) - return err; + goto stop_thread; err = stmfts_command(sdata, STMFTS_SLEEP_OUT); if (err) - return err; + dev_warn(&sdata->client->dev, + "failed to perform sleep_out: %d\n", err); /* optional tuning */ err = stmfts_command(sdata, STMFTS_MS_CX_TUNING); @@ -573,7 +596,7 @@ static int stmfts_power_on(struct stmfts_data *sdata) err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION); if (err) - return err; + goto stop_thread; /* * At this point no one is using the touchscreen @@ -582,13 +605,21 @@ static int stmfts_power_on(struct stmfts_data *sdata) (void) i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); return 0; + +stop_thread: + if (!sdata->client->irq) + kthread_stop(sdata->poll_thread); + return err; } static void stmfts_power_off(void *data) { struct stmfts_data *sdata = data; - disable_irq(sdata->client->irq); + if (!sdata->client->irq) + kthread_stop(sdata->poll_thread); + else + disable_irq(sdata->client->irq); regulator_bulk_disable(ARRAY_SIZE(sdata->regulators), sdata->regulators); } @@ -689,13 +720,15 @@ static int stmfts_probe(struct i2c_client *client, * interrupts. To be on the safe side it's better to not enable * the interrupts during their request. */ - irq_set_status_flags(client->irq, IRQ_NOAUTOEN); - err = devm_request_threaded_irq(&client->dev, client->irq, - NULL, stmfts_irq_handler, - IRQF_ONESHOT, - "stmfts_irq", sdata); - if (err) - return err; + if (client->irq) { + irq_set_status_flags(client->irq, IRQ_NOAUTOEN); + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, stmfts_irq_handler, + IRQF_ONESHOT, + "stmfts_irq", sdata); + if (err) + return err; + } dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n"); -- 2.20.1 From 7690882eba261d8bceb176b5a2109db2bc43c278 Mon Sep 17 00:00:00 2001 From: SwtcR Date: Mon, 12 Feb 2018 19:11:58 +0900 Subject: [PATCH 060/124] stmfts: hack to support Switch data format --- drivers/input/touchscreen/stmfts.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 4cba504f046e..003aa7befe62 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -172,6 +172,7 @@ static int stmfts_read_events(struct stmfts_data *sdata) static void stmfts_report_contact_event(struct stmfts_data *sdata, const u8 event[]) { +#if 0 u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); u16 y = (event[2] >> 4) | (event[3] << 4); @@ -179,7 +180,17 @@ static void stmfts_report_contact_event(struct stmfts_data *sdata, u8 min = event[5]; u8 orientation = event[6]; u8 area = event[7]; +#else + u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; + u16 x = (event[1] << 4) | ((event[3] & 0xf0) >> 4); + u16 y = (event[2] << 4) | (event[3] & 0x0f); + u8 maj = event[4] | (event[5] << 8); + u8 min = maj; + // these two are not quite right but meh + u8 orientation = event[6]; + u8 area = event[7]; +#endif input_mt_slot(sdata->input, slot_id); input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); -- 2.20.1 From 107b486493d8da5ea4c2aeb182bf80cc6fc38fea Mon Sep 17 00:00:00 2001 From: SwtcR Date: Thu, 22 Feb 2018 01:40:29 +0900 Subject: [PATCH 061/124] stmfts: support configurable IRQ enable register --- drivers/input/touchscreen/stmfts.c | 66 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 003aa7befe62..797c2048a293 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -35,6 +35,7 @@ #define STMFTS_FULL_FORCE_CALIBRATION 0xa2 #define STMFTS_MS_CX_TUNING 0xa3 #define STMFTS_SS_CX_TUNING 0xa4 +#define STMFTS_WRITE_REGISTER 0xb6 /* events */ #define STMFTS_EV_NO_EVENT 0x00 @@ -105,6 +106,8 @@ struct stmfts_data { bool led_status; bool hover_enabled; bool running; + int irq_enable_reg; + u8 irq_enable_data; struct task_struct *poll_thread; }; @@ -329,12 +332,10 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) static int stmfts_poll_thread(void *data) { - printk("poll_thread starting\n"); while (!kthread_should_stop()) { stmfts_irq_handler(0, data); msleep(2); } - printk("poll_thread exiting\n"); return 0; } @@ -356,6 +357,18 @@ static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) return 0; } +static int stmfts_write_register(struct stmfts_data *sdata, const u16 reg, const u8 value) +{ + u8 buf[3]; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = value; + + return i2c_smbus_write_i2c_block_data(sdata->client, + STMFTS_WRITE_REGISTER, 3, buf); +} + static int stmfts_input_open(struct input_dev *dev) { struct stmfts_data *sdata = input_get_drvdata(dev); @@ -584,9 +597,33 @@ static int stmfts_power_on(struct stmfts_data *sdata) return -EIO; } - err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); + /* + * IRQs may be disabled on startup, so we need to reset the chip, then + * immediately issue the command to enable IRQs. This will trigger + * the completion for the reset command's queued status message. + */ + reinit_completion(&sdata->cmd_done); + + err = i2c_smbus_write_byte(sdata->client, STMFTS_SYSTEM_RESET); if (err) - goto stop_thread; + goto cleanup_irq; + + msleep(10); + + if (sdata->client->irq && sdata->irq_enable_reg != -1) { + err = stmfts_write_register(sdata, sdata->irq_enable_reg, + sdata->irq_enable_data); + if (err) + goto cleanup_irq; + } + + if (!wait_for_completion_timeout(&sdata->cmd_done, + msecs_to_jiffies(1000))) { + err = -ETIMEDOUT; + dev_err(&sdata->client->dev, + "reset did not complete: %d\n", err); + goto cleanup_irq; + } err = stmfts_command(sdata, STMFTS_SLEEP_OUT); if (err) @@ -606,8 +643,11 @@ static int stmfts_power_on(struct stmfts_data *sdata) "failed to perform self auto tune: %d\n", err); err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION); - if (err) - goto stop_thread; + if (err) { + dev_err(&sdata->client->dev, + "failed to perform calibration: %d\n", err); + goto cleanup_irq; + } /* * At this point no one is using the touchscreen @@ -617,9 +657,11 @@ static int stmfts_power_on(struct stmfts_data *sdata) return 0; -stop_thread: +cleanup_irq: if (!sdata->client->irq) kthread_stop(sdata->poll_thread); + else + disable_irq(sdata->client->irq); return err; } @@ -667,6 +709,7 @@ static int stmfts_probe(struct i2c_client *client, { int err; struct stmfts_data *sdata; + u32 prop[2]; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA | @@ -712,6 +755,15 @@ static int stmfts_probe(struct i2c_client *client, sdata->use_key = device_property_read_bool(&client->dev, "touch-key-connected"); + + if (!device_property_read_u32_array(&client->dev, + "interrupt-enable-reg", prop, 2)) { + sdata->irq_enable_reg = prop[0] & 0xffff; + sdata->irq_enable_data = prop[1] & 0xff; + } else { + sdata->irq_enable_reg = -1; + } + if (sdata->use_key) { input_set_capability(sdata->input, EV_KEY, KEY_MENU); input_set_capability(sdata->input, EV_KEY, KEY_BACK); -- 2.20.1