From 08bd2cf2fd09322e14acce3810420fba4f8f082e Mon Sep 17 00:00:00 2001 From: Thomas Makin Date: Fri, 9 Oct 2026 15:12:41 -0500 Subject: [PATCH] stmfts: update touch handling --- drivers/input/touchscreen/stmfts.c | 368 ++++++++++++++++++++++------- 1 file changed, 282 insertions(+), 86 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index bb9baa5315c9..f16097d34f28 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -66,6 +66,7 @@ #define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 #define STMFTS_EV_STATUS 0x16 #define STMFTS_EV_DEBUG 0xdb +#define STMFTM4_PRESSURE_MAX 500 /* FTM4 done status */ #define STMFTM4_STATUS_MS_CX_TUNING_DONE 0x01 @@ -148,11 +149,22 @@ struct stmfts_data { bool hover_enabled; bool stylus_enabled; bool running; + unsigned int touch_count; + unsigned int n_events; + bool palm_pressed; + u8 palm_id; + u8 finger_state[STMFTS_MAX_FINGERS]; + bool coord_factor; + u32 x_real_max; + u32 y_real_max; + u32 x_edge; + u32 y_edge; }; struct stmfts_chip_ops { int (*configure)(struct stmfts_data *sdata); int (*setup_input)(struct stmfts_data *sdata); + int (*read_events)(struct stmfts_data *sdata); void (*parse_events)(struct stmfts_data *sdata); int (*set_scan)(struct stmfts_data *sdata, bool enable); int (*set_hover)(struct stmfts_data *sdata, bool enable); @@ -223,48 +235,47 @@ static int stmfts_read_events(struct stmfts_data *sdata) return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; } -static int ftm4_write_reg(struct stmfts_data *sdata, unsigned char *reg, - unsigned short num_com) +static int ftm4_write_reg(struct stmfts_data *sdata, u8 *reg, unsigned int len) { - struct i2c_msg xfer_msg[1] = { + struct i2c_msg msgs[] = { { .addr = sdata->client->addr, - .len = num_com, + .len = len, .buf = reg, }, }; int ret; - ret = i2c_transfer(sdata->client->adapter, xfer_msg, 1); + ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; - return ret == 1 ? 0 : -EIO; + return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; } -static int ftm4_read_reg(struct stmfts_data *sdata, unsigned char *reg, - int cnum, unsigned char *buf, int num) +static int ftm4_read_reg(struct stmfts_data *sdata, u8 *reg, unsigned int wlen, + u8 *buf, unsigned int rlen) { - struct i2c_msg xfer_msg[2] = { + struct i2c_msg msgs[] = { { .addr = sdata->client->addr, - .len = cnum, + .len = wlen, .buf = reg, }, { .addr = sdata->client->addr, .flags = I2C_M_RD, - .len = num, + .len = rlen, .buf = buf, }, }; int ret; - ret = i2c_transfer(sdata->client->adapter, xfer_msg, 2); + ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; - return ret == 2 ? 0 : -EIO; + return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; } /* FTS4 event handling functions */ @@ -387,70 +398,6 @@ static void stmfts5_report_contact_release(struct stmfts_data *sdata, input_mt_report_slot_inactive(sdata->input); } -/* FTM4 event handling functions */ -static void stmftm4_report_contact_event(struct stmfts_data *sdata, - const u8 event[]) -{ - u8 slot = event[0] >> 4; - u16 x = (event[1] << 4) | (event[3] >> 4); - u16 y = (event[2] << 4) | (event[3] & 0x0f); - unsigned int gain = event[6] & 0x3f; - unsigned int z = (event[4] | (event[5] << 8)) << 6; - unsigned int orient = ((event[7] & 0x80) >> 5) | (event[6] >> 6); - - if (gain == 0 || gain == 0x3f) - gain = 0x40; - z /= gain + 0x40; - - input_mt_slot(sdata->input, slot); - input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); - input_report_abs(sdata->input, ABS_MT_POSITION_X, x); - input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); - input_report_abs(sdata->input, ABS_MT_PRESSURE, z); - input_report_abs(sdata->input, ABS_MT_ORIENTATION, orient); -} - -static void stmftm4_parse_events(struct stmfts_data *sdata) -{ - int i; - - for (i = 0; i < STMFTS_STACK_DEPTH; i++) { - u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; - - switch (event[0]) { - case STMFTS_EV_CONTROLLER_READY: - case STMFTS_EV_STATUS: - complete(&sdata->cmd_done); - fallthrough; - case STMFTS_EV_NO_EVENT: - case STMFTS_EV_DEBUG: - goto sync; - } - - switch (event[0] & STMFTS_MASK_EVENT_ID) { - case STMFTS_EV_MULTI_TOUCH_ENTER: - case STMFTS_EV_MULTI_TOUCH_MOTION: - stmftm4_report_contact_event(sdata, event); - break; - case STMFTS_EV_MULTI_TOUCH_LEAVE: - stmfts_report_contact_release(sdata, event); - break; - case STMFTS_EV_ERROR: - dev_warn(&sdata->client->dev, - "error code: %02x %02x %02x\n", - event[0], event[1], event[2]); - break; - default: - dev_err(&sdata->client->dev, - "unknown event %#02x\n", event[0]); - break; - } - } - -sync: - input_sync(sdata->input); -} - static void stmfts_report_hover_event(struct stmfts_data *sdata, const u8 event[]) { @@ -464,6 +411,219 @@ static void stmfts_report_hover_event(struct stmfts_data *sdata, input_sync(sdata->input); } +static void stmftm4_release_all(struct stmfts_data *sdata) +{ + unsigned int i; + + for (i = 0; i < STMFTS_MAX_FINGERS; i++) { + input_mt_slot(sdata->input, i); + input_mt_report_slot_inactive(sdata->input); + sdata->finger_state[i] = STMFTS_EV_MULTI_TOUCH_LEAVE; + } + sdata->touch_count = 0; + input_report_key(sdata->input, BTN_TOUCH, 0); + input_report_key(sdata->input, BTN_TOOL_FINGER, 0); +} + +static void stmftm4_factor_coords(struct stmfts_data *sdata, int *x, int *y) +{ + unsigned int x_work = max_t(unsigned int, *x, sdata->x_edge); + unsigned int y_work = max_t(unsigned int, *y, sdata->y_edge); + unsigned int x_span = sdata->x_real_max - sdata->x_edge; + unsigned int y_span = sdata->y_real_max - sdata->y_edge; + unsigned int x_max = input_abs_get_max(sdata->input, ABS_MT_POSITION_X); + unsigned int y_max = input_abs_get_max(sdata->input, ABS_MT_POSITION_Y); + + if (!x_span || !y_span) + return; + + x_work = min(x_work, sdata->x_real_max) - sdata->x_edge; + y_work = min(y_work, sdata->y_real_max) - sdata->y_edge; + *x = x_work * (x_max * 1000 / x_span) / 1000; + *y = y_work * (y_max * 1000 / y_span) / 1000; +} + +static int stmftm4_orientation(const u8 event[]) +{ + unsigned int code = ((event[7] & 0x80) >> 5) | (event[6] >> 6); + + switch (code) { + case 1: return 45000; + case 2: return 90000; + case 3: return -45000; + case 4: return 22500; + case 5: return 67500; + case 6: return -22500; + case 7: return -67500; + default: return 0; + } +} + +static void stmftm4_report_contact_event(struct stmfts_data *sdata, + const u8 event[], u8 slot, u8 id) +{ + int x = (event[1] << 4) | (event[3] >> 4); + int y = (event[2] << 4) | (event[3] & 0x0f); + unsigned int gain = event[6] & 0x3f; + unsigned int z = (event[4] | (event[5] << 8)) << 6; + + if (gain == 0 || gain == 0x3f) + gain = 0x40; + z /= gain + 0x40; + + if (z > STMFTM4_PRESSURE_MAX) { + dev_info(&sdata->client->dev, "palm detected on slot %u\n", slot); + stmftm4_release_all(sdata); + sdata->palm_id = slot; + sdata->palm_pressed = true; + return; + } + + if (sdata->coord_factor) + stmftm4_factor_coords(sdata, &x, &y); + + input_mt_slot(sdata->input, slot); + input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); + touchscreen_report_pos(sdata->input, &sdata->prop, x, y, true); + input_report_abs(sdata->input, ABS_MT_PRESSURE, z); + input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, 48); + input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, 48); + input_report_abs(sdata->input, ABS_MT_ORIENTATION, + stmftm4_orientation(event)); + input_report_key(sdata->input, BTN_TOUCH, 1); + input_report_key(sdata->input, BTN_TOOL_FINGER, 1); + sdata->finger_state[slot] = id; +} + +static void stmftm4_report_release(struct stmfts_data *sdata, u8 slot) +{ + if (slot >= STMFTS_MAX_FINGERS) + return; + + input_mt_slot(sdata->input, slot); + input_mt_report_slot_inactive(sdata->input); + sdata->finger_state[slot] = STMFTS_EV_MULTI_TOUCH_LEAVE; + if (sdata->touch_count) + sdata->touch_count--; + if (!sdata->touch_count) { + input_report_key(sdata->input, BTN_TOUCH, 0); + input_report_key(sdata->input, BTN_TOOL_FINGER, 0); + } +} + +static int stmftm4_read_events(struct stmfts_data *sdata) +{ + u8 count_reg[3] = { STMFTS_WRITE_REG, 0x00, 0x23 }; + u8 raw[2] = { }; + u8 cmd = STMFTS_READ_ALL_EVENT; + unsigned int count; + int ret; + + ret = ftm4_read_reg(sdata, count_reg, sizeof(count_reg), raw, sizeof(raw)); + if (ret) + return ret; + + /* Old driver shifts the little-endian short, leaving the second byte. */ + count = raw[1] / 2; + if (count > STMFTS_STACK_DEPTH) + count = STMFTS_STACK_DEPTH; + sdata->n_events = count; + if (!count) + return 0; + + memset(sdata->data, 0, count * STMFTS_EVENT_SIZE); + return ftm4_read_reg(sdata, &cmd, 1, sdata->data, + count * STMFTS_EVENT_SIZE); +} + +static void stmftm4_parse_events(struct stmfts_data *sdata) +{ + unsigned int i; + + for (i = 0; i < sdata->n_events; i++) { + u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; + u8 id = event[0] & STMFTS_MASK_EVENT_ID; + u8 slot = event[0] >> 4; + + if (id < STMFTS_EV_MULTI_TOUCH_ENTER || + id > STMFTS_EV_MULTI_TOUCH_MOTION) { + id = event[0]; + slot = event[1] & 0x0f; + } + if (slot >= STMFTS_MAX_FINGERS && + (id == STMFTS_EV_MULTI_TOUCH_ENTER || + id == STMFTS_EV_MULTI_TOUCH_MOTION || + id == STMFTS_EV_MULTI_TOUCH_LEAVE || + id == STMFTS_EV_HOVER_LEAVE)) + continue; + + switch (id) { + case STMFTS_EV_NO_EVENT: + break; + case STMFTS_EV_ERROR: + if (event[1] == 0x08) + dev_err(&sdata->client->dev, + "auto tune failed: %02x\n", event[2]); + else if (event[1] == 0x09) + dev_err(&sdata->client->dev, "sync detect failed\n"); + else + dev_warn(&sdata->client->dev, + "error code: %02x %02x %02x\n", + event[0], event[1], event[2]); + break; + case STMFTS_EV_HOVER_ENTER: + case STMFTS_EV_HOVER_MOTION: + break; + case STMFTS_EV_HOVER_LEAVE: + stmftm4_report_release(sdata, slot); + break; + case STMFTS_EV_MULTI_TOUCH_ENTER: + sdata->touch_count++; + fallthrough; + case STMFTS_EV_MULTI_TOUCH_MOTION: + if (!sdata->touch_count) { + stmftm4_release_all(sdata); + break; + } + if (id == STMFTS_EV_MULTI_TOUCH_MOTION && + sdata->finger_state[slot] == STMFTS_EV_MULTI_TOUCH_LEAVE) + break; + if (sdata->palm_pressed) + break; + stmftm4_report_contact_event(sdata, event, slot, id); + break; + case STMFTS_EV_MULTI_TOUCH_LEAVE: + if (sdata->palm_pressed) { + if (sdata->palm_id == slot) { + stmftm4_release_all(sdata); + sdata->palm_pressed = false; + } + break; + } + if (!sdata->touch_count) { + stmftm4_release_all(sdata); + break; + } + stmftm4_report_release(sdata, slot); + break; + case STMFTS_EV_CONTROLLER_READY: + case STMFTS_EV_STATUS: + complete(&sdata->cmd_done); + break; + default: + dev_dbg(&sdata->client->dev, + "event %02x %02x %02x %02x %02x %02x %02x %02x\n", + event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + break; + } + } + + input_mt_sync_frame(sdata->input); + input_sync(sdata->input); + sdata->n_events = 0; +} + static void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[]) { switch (event[2]) { @@ -582,7 +742,7 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) guard(mutex)(&sdata->mutex); - err = stmfts_read_events(sdata); + err = sdata->ops->read_events(sdata); if (unlikely(err)) dev_err(&sdata->client->dev, "failed to read events: %d\n", err); @@ -832,6 +992,7 @@ static int stmfts_read_system_info(struct stmfts_data *sdata) sizeof(reg), reg); if (err < 0) return err; + if (err != sizeof(reg)) return -EIO; @@ -863,7 +1024,7 @@ static int stmftm4_read_system_info(struct stmfts_data *sdata) sdata->config_id = 0; sdata->config_ver = 0; - dev_info(&sdata->client->dev, "FTM4: chip_id: %04x fw_ver: %04x\n", + dev_info(&sdata->client->dev, "chip_id: %04x fw_ver: %04x\n", sdata->chip_id, sdata->fw_ver); return 0; @@ -965,6 +1126,7 @@ static int stmfts_configure(struct stmfts_data *sdata) dev_warn(&sdata->client->dev, "failed to perform self auto tune: %d\n", err); + dev_info(&sdata->client->dev, "calib\n"); err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION); if (err) return err; @@ -990,10 +1152,11 @@ static int stmftm4_configure(struct stmfts_data *sdata) if (err) return err; - /* Trim low power oscillator */ + /* Trim low power oscillator. No completion event. */ stmftm4_command(sdata, STMFTS_TRIM_LOW_POWER_OSCILLATOR); msleep(200); + dev_info(&sdata->client->dev, "ms tuning\n"); err = stmftm4_command(sdata, STMFTS_MS_CX_TUNING); if (!err) err = stmftm4_wait_status(sdata, STMFTM4_STATUS_MS_CX_TUNING_DONE); @@ -1001,6 +1164,7 @@ static int stmftm4_configure(struct stmfts_data *sdata) dev_warn(&sdata->client->dev, "failed to perform mutual auto tune: %d\n", err); + dev_info(&sdata->client->dev, "cx tuning\n"); err = stmftm4_command(sdata, STMFTS_SS_CX_TUNING); if (!err) err = stmftm4_wait_status(sdata, STMFTM4_STATUS_SS_CX_TUNING_DONE); @@ -1136,10 +1300,8 @@ static void stmfts_power_off(void *data) struct stmfts_data *sdata = data; disable_irq(sdata->client->irq); - if (sdata->reset_gpio) gpiod_set_value_cansleep(sdata->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); } @@ -1162,6 +1324,38 @@ static int stmfts_setup_input(struct stmfts_data *sdata) INPUT_MT_DIRECT); } +static int stmftm4_setup_input(struct stmfts_data *sdata) +{ + struct device *dev = &sdata->client->dev; + u32 coords[2]; + unsigned int i; + + input_set_capability(sdata->input, EV_KEY, BTN_TOUCH); + input_set_capability(sdata->input, EV_KEY, BTN_TOOL_FINGER); + input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, + STMFTM4_PRESSURE_MAX, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, + -67500, 90000, 0, 0); + + for (i = 0; i < STMFTS_MAX_FINGERS; i++) + sdata->finger_state[i] = STMFTS_EV_MULTI_TOUCH_LEAVE; + + if (!device_property_read_u32_array(dev, "st,max-real-coords", + coords, 2)) { + sdata->coord_factor = true; + sdata->x_real_max = coords[0]; + sdata->y_real_max = coords[1]; + if (!device_property_read_u32_array(dev, "st,edge-offset", + coords, 2)) { + sdata->x_edge = coords[0]; + sdata->y_edge = coords[1]; + } + } + + return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS, + INPUT_MT_DIRECT); +} + static int stmfts5_setup_input(struct stmfts_data *sdata) { struct device *dev = &sdata->client->dev; @@ -1231,7 +1425,6 @@ static int stmfts_probe(struct i2c_client *client) sdata->client = client; mutex_init(&sdata->mutex); init_completion(&sdata->cmd_done); - sdata->ops = i2c_get_match_data(client); err = devm_regulator_bulk_get_const(dev, @@ -1366,11 +1559,13 @@ static int stmfts5_chip_runtime_resume(struct stmfts_data *sdata) err = i2c_smbus_write_byte(client, STMFTS_MS_CX_TUNING); if (err) dev_warn(dev, "MS_CX_TUNING failed: %d\n", err); + msleep(20); err = i2c_smbus_write_byte(client, STMFTS_SS_CX_TUNING); if (err) dev_warn(dev, "SS_CX_TUNING failed: %d\n", err); + msleep(20); /* Force calibration */ @@ -1426,6 +1621,7 @@ static const struct dev_pm_ops stmfts_pm_ops = { static const struct stmfts_chip_ops stmfts4_ops = { .configure = stmfts_configure, .setup_input = stmfts_setup_input, + .read_events = stmfts_read_events, .parse_events = stmfts_parse_events, .set_scan = stmfts_set_scan, .set_hover = stmfts_set_hover, @@ -1437,6 +1633,7 @@ static const struct stmfts_chip_ops stmfts4_ops = { static const struct stmfts_chip_ops stmfts5_ops = { .configure = stmfts5_configure, .setup_input = stmfts5_setup_input, + .read_events = stmfts_read_events, .parse_events = stmfts5_parse_events, .set_scan = stmfts5_set_scan, .power_on = stmfts_chip_power_on, @@ -1446,10 +1643,10 @@ static const struct stmfts_chip_ops stmfts5_ops = { static const struct stmfts_chip_ops stmftm4_ops = { .configure = stmftm4_configure, - .setup_input = stmfts_setup_input, + .setup_input = stmftm4_setup_input, + .read_events = stmftm4_read_events, .parse_events = stmftm4_parse_events, .set_scan = stmfts_set_scan, - .set_hover = stmfts_set_hover, .power_on = stmftm4_power_on, .runtime_suspend = stmftm4_runtime_suspend, .runtime_resume = stmftm4_configure, @@ -1485,7 +1682,6 @@ static struct i2c_driver stmfts_driver = { .remove = stmfts_remove, .id_table = stmfts_id, }; - module_i2c_driver(stmfts_driver); MODULE_AUTHOR("Andi Shyti ");