diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-10 16:35:57 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-10 16:35:57 -0700 |
commit | 4bcf69e57063c9b1b15df1a293c969e80a1c97e6 (patch) | |
tree | aacea8bf1ca4260d3064665ecc946ab78857b3a3 /drivers/input/touchscreen | |
parent | b7b8e3689aa0b2def48b8c6eb1df060902eb2c0a (diff) | |
parent | 9e8238020c5beba64e7ffafbb7ea0fb02fe68270 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
- an update to Elan touchpad controller driver supporting newer ICs
with enhanced precision reports and a new firmware update process
- an update to EXC3000 touch controller supporting additional parts
- assorted driver fixups
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (27 commits)
Input: exc3000 - add support to query model and fw_version
Input: exc3000 - add reset gpio support
Input: exc3000 - add EXC80H60 and EXC80H84 support
dt-bindings: touchscreen: Convert EETI EXC3000 touchscreen to json-schema
Input: sentelic - fix error return when fsp_reg_write fails
Input: alps - remove redundant assignment to variable ret
Input: ims-pcu - return error code rather than -ENOMEM
Input: elan_i2c - add ic type 0x15
Input: atmel_mxt_ts - only read messages in mxt_acquire_irq() when necessary
Input: uinput - fix typo in function name documentation
Input: ati_remote2 - add missing newlines when printing module parameters
Input: psmouse - add a newline when printing 'proto' by sysfs
Input: synaptics-rmi4 - drop a duplicated word
Input: elan_i2c - add support for high resolution reports
Input: elan_i2c - do not constantly re-query pattern ID
Input: elan_i2c - add firmware update info for ICs 0x11, 0x13, 0x14
Input: elan_i2c - handle firmware updated on newer ICs
Input: elan_i2c - add support for different firmware page sizes
Input: elan_i2c - fix detecting IAP version on older controllers
Input: elan_i2c - handle devices with patterns above 1
...
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 56 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/elants_i2c.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/elo.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/exc3000.c | 248 | ||||
-rw-r--r-- | drivers/input/touchscreen/iqs5xx.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/max11801_ts.c | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/stmfts.c | 2 |
8 files changed, 289 insertions, 27 deletions
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a2189739e30f..6b71b0aff115 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -20,6 +20,7 @@ #include <linux/i2c.h> #include <linux/input/mt.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/of.h> #include <linux/property.h> #include <linux/slab.h> @@ -129,6 +130,7 @@ struct t9_range { /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +#define MXT_COMMS_RETRIGEN BIT(6) /* MXT_DEBUG_DIAGNOSTIC_T37 */ #define MXT_DIAGNOSTIC_PAGEUP 0x01 @@ -308,6 +310,7 @@ struct mxt_data { struct t7_config t7_cfg; struct mxt_dbg dbg; struct gpio_desc *reset_gpio; + bool use_retrigen_workaround; /* Cached parameters from object table */ u16 T5_address; @@ -318,6 +321,7 @@ struct mxt_data { u16 T71_address; u8 T9_reportid_min; u8 T9_reportid_max; + u16 T18_address; u8 T19_reportid; u16 T44_address; u8 T100_reportid_min; @@ -1190,9 +1194,11 @@ static int mxt_acquire_irq(struct mxt_data *data) enable_irq(data->irq); - error = mxt_process_messages_until_invalid(data); - if (error) - return error; + if (data->use_retrigen_workaround) { + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + } return 0; } @@ -1282,6 +1288,38 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) return crc; } +static int mxt_check_retrigen(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + int val; + struct irq_data *irqd; + + data->use_retrigen_workaround = false; + + irqd = irq_get_irq_data(data->irq); + if (!irqd) + return -EINVAL; + + if (irqd_is_level_type(irqd)) + return 0; + + if (data->T18_address) { + error = __mxt_read_reg(client, + data->T18_address + MXT_COMMS_CTRL, + 1, &val); + if (error) + return error; + + if (val & MXT_COMMS_RETRIGEN) + return 0; + } + + dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); + data->use_retrigen_workaround = true; + return 0; +} + static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) { struct device *dev = &data->client->dev; @@ -1561,6 +1599,10 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + ret = mxt_check_retrigen(data); + if (ret) + goto release_mem; + ret = mxt_soft_reset(data); if (ret) goto release_mem; @@ -1604,6 +1646,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T71_address = 0; data->T9_reportid_min = 0; data->T9_reportid_max = 0; + data->T18_address = 0; data->T19_reportid = 0; data->T44_address = 0; data->T100_reportid_min = 0; @@ -1678,6 +1721,9 @@ static int mxt_parse_object_table(struct mxt_data *data, object->num_report_ids - 1; data->num_touchids = object->num_report_ids; break; + case MXT_SPT_COMMSCONFIG_T18: + data->T18_address = object->start_address; + break; case MXT_SPT_MESSAGECOUNT_T44: data->T44_address = object->start_address; break; @@ -2141,6 +2187,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) return error; + error = mxt_check_retrigen(data); + if (error) + return error; + error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, &client->dev, GFP_KERNEL, data, mxt_config_cb); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 3a4f18d3450d..6ff81d48da86 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -288,7 +288,7 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); - /* fallthrough */ + case EDT_M09: case EDT_M12: case EV_FT: @@ -330,7 +330,6 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, } break; - /* fallthrough */ case EDT_M09: case EDT_M12: case EV_FT: diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 5477a5718202..b0bd5bb079be 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -955,7 +955,7 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) break; ts->state = ELAN_STATE_NORMAL; - /* fall through */ + fallthrough; case ELAN_STATE_NORMAL: diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index d6772a2c2d09..e0bacd34866a 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -348,7 +348,7 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) case 1: /* 6-byte protocol */ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0); - /* fall through */ + fallthrough; case 2: /* 4-byte protocol */ input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0); diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index e007e2e8f626..a6597f026980 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -8,7 +8,9 @@ */ #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> @@ -16,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/sizes.h> #include <linux/timer.h> #include <asm/unaligned.h> @@ -23,15 +26,59 @@ #define EXC3000_SLOTS_PER_FRAME 5 #define EXC3000_LEN_FRAME 66 #define EXC3000_LEN_POINT 10 -#define EXC3000_MT_EVENT 6 + +#define EXC3000_LEN_MODEL_NAME 16 +#define EXC3000_LEN_FW_VERSION 16 + +#define EXC3000_MT1_EVENT 0x06 +#define EXC3000_MT2_EVENT 0x18 + #define EXC3000_TIMEOUT_MS 100 +#define EXC3000_RESET_MS 10 +#define EXC3000_READY_MS 100 + +static const struct i2c_device_id exc3000_id[]; + +struct eeti_dev_info { + const char *name; + int max_xy; +}; + +enum eeti_dev_id { + EETI_EXC3000, + EETI_EXC80H60, + EETI_EXC80H84, +}; + +static struct eeti_dev_info exc3000_info[] = { + [EETI_EXC3000] = { + .name = "EETI EXC3000 Touch Screen", + .max_xy = SZ_4K - 1, + }, + [EETI_EXC80H60] = { + .name = "EETI EXC80H60 Touch Screen", + .max_xy = SZ_16K - 1, + }, + [EETI_EXC80H84] = { + .name = "EETI EXC80H84 Touch Screen", + .max_xy = SZ_16K - 1, + }, +}; + struct exc3000_data { struct i2c_client *client; + const struct eeti_dev_info *info; struct input_dev *input; struct touchscreen_properties prop; + struct gpio_desc *reset; struct timer_list timer; u8 buf[2 * EXC3000_LEN_FRAME]; + struct completion wait_event; + struct mutex query_lock; + int query_result; + char model[EXC3000_LEN_MODEL_NAME]; + char fw_version[EXC3000_LEN_FW_VERSION]; }; static void exc3000_report_slots(struct input_dev *input, @@ -58,10 +105,15 @@ static void exc3000_timer(struct timer_list *t) input_sync(data->input); } -static int exc3000_read_frame(struct i2c_client *client, u8 *buf) +static int exc3000_read_frame(struct exc3000_data *data, u8 *buf) { + struct i2c_client *client = data->client; + u8 expected_event = EXC3000_MT1_EVENT; int ret; + if (data->info->max_xy == SZ_16K - 1) + expected_event = EXC3000_MT2_EVENT; + ret = i2c_master_send(client, "'", 2); if (ret < 0) return ret; @@ -76,19 +128,21 @@ static int exc3000_read_frame(struct i2c_client *client, u8 *buf) if (ret != EXC3000_LEN_FRAME) return -EIO; - if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || - buf[2] != EXC3000_MT_EVENT) + if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME) + return -EINVAL; + + if (buf[2] != expected_event) return -EINVAL; return 0; } -static int exc3000_read_data(struct i2c_client *client, +static int exc3000_read_data(struct exc3000_data *data, u8 *buf, int *n_slots) { int error; - error = exc3000_read_frame(client, buf); + error = exc3000_read_frame(data, buf); if (error) return error; @@ -98,7 +152,7 @@ static int exc3000_read_data(struct i2c_client *client, if (*n_slots > EXC3000_SLOTS_PER_FRAME) { /* Read 2nd frame to get the rest of the contacts. */ - error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); + error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME); if (error) return error; @@ -110,6 +164,28 @@ static int exc3000_read_data(struct i2c_client *client, return 0; } +static int exc3000_query_interrupt(struct exc3000_data *data) +{ + u8 *buf = data->buf; + int error; + + error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME); + if (error < 0) + return error; + + if (buf[0] != 'B') + return -EPROTO; + + if (buf[4] == 'E') + strlcpy(data->model, buf + 5, sizeof(data->model)); + else if (buf[4] == 'D') + strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version)); + else + return -EPROTO; + + return 0; +} + static irqreturn_t exc3000_interrupt(int irq, void *dev_id) { struct exc3000_data *data = dev_id; @@ -118,7 +194,13 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id) int slots, total_slots; int error; - error = exc3000_read_data(data->client, buf, &total_slots); + if (mutex_is_locked(&data->query_lock)) { + data->query_result = exc3000_query_interrupt(data); + complete(&data->wait_event); + goto out; + } + + error = exc3000_read_data(data, buf, &total_slots); if (error) { /* Schedule a timer to release "stuck" contacts */ mod_timer(&data->timer, @@ -145,31 +227,132 @@ out: return IRQ_HANDLED; } -static int exc3000_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct exc3000_data *data = i2c_get_clientdata(client); + static const u8 request[68] = { + 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00 + }; + int error; + + mutex_lock(&data->query_lock); + + data->query_result = -ETIMEDOUT; + reinit_completion(&data->wait_event); + + error = i2c_master_send(client, request, sizeof(request)); + if (error < 0) { + mutex_unlock(&data->query_lock); + return error; + } + + wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ); + mutex_unlock(&data->query_lock); + + if (data->query_result < 0) + return data->query_result; + + return sprintf(buf, "%s\n", data->fw_version); +} +static DEVICE_ATTR_RO(fw_version); + +static ssize_t exc3000_get_model(struct exc3000_data *data) +{ + static const u8 request[68] = { + 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00 + }; + struct i2c_client *client = data->client; + int error; + + mutex_lock(&data->query_lock); + data->query_result = -ETIMEDOUT; + reinit_completion(&data->wait_event); + + error = i2c_master_send(client, request, sizeof(request)); + if (error < 0) { + mutex_unlock(&data->query_lock); + return error; + } + + wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ); + mutex_unlock(&data->query_lock); + + return data->query_result; +} + +static ssize_t model_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct exc3000_data *data = i2c_get_clientdata(client); + int error; + + error = exc3000_get_model(data); + if (error < 0) + return error; + + return sprintf(buf, "%s\n", data->model); +} +static DEVICE_ATTR_RO(model); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_model.attr, + NULL +}; + +static struct attribute_group exc3000_attribute_group = { + .attrs = sysfs_attrs +}; + +static int exc3000_probe(struct i2c_client *client) { struct exc3000_data *data; struct input_dev *input; - int error; + int error, max_xy, retry; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->client = client; + data->info = device_get_match_data(&client->dev); + if (!data->info) { + enum eeti_dev_id eeti_dev_id = + i2c_match_id(exc3000_id, client)->driver_data; + data->info = &exc3000_info[eeti_dev_id]; + } timer_setup(&data->timer, exc3000_timer, 0); + init_completion(&data->wait_event); + mutex_init(&data->query_lock); + + data->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(data->reset)) + return PTR_ERR(data->reset); + + if (data->reset) { + msleep(EXC3000_RESET_MS); + gpiod_set_value_cansleep(data->reset, 0); + msleep(EXC3000_READY_MS); + } input = devm_input_allocate_device(&client->dev); if (!input) return -ENOMEM; data->input = input; + input_set_drvdata(input, data); - input->name = "EETI EXC3000 Touch Screen"; + input->name = data->info->name; input->id.bustype = BUS_I2C; - input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); + max_xy = data->info->max_xy; + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); + touchscreen_parse_properties(input, true, &data->prop); error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, @@ -187,18 +370,49 @@ static int exc3000_probe(struct i2c_client *client, if (error) return error; + /* + * I²C does not have built-in recovery, so retry on failure. This + * ensures, that the device probe will not fail for temporary issues + * on the bus. This is not needed for the sysfs calls (userspace + * will receive the error code and can start another query) and + * cannot be done for touch events (but that only means loosing one + * or two touch events anyways). + */ + for (retry = 0; retry < 3; retry++) { + error = exc3000_get_model(data); + if (!error) + break; + dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n", + retry + 1, error); + } + + if (error) + return error; + + dev_dbg(&client->dev, "TS Model: %s", data->model); + + i2c_set_clientdata(client, data); + + error = devm_device_add_group(&client->dev, &exc3000_attribute_group); + if (error) + return error; + return 0; } static const struct i2c_device_id exc3000_id[] = { - { "exc3000", 0 }, + { "exc3000", EETI_EXC3000 }, + { "exc80h60", EETI_EXC80H60 }, + { "exc80h84", EETI_EXC80H84 }, { } }; MODULE_DEVICE_TABLE(i2c, exc3000_id); #ifdef CONFIG_OF static const struct of_device_id exc3000_of_match[] = { - { .compatible = "eeti,exc3000" }, + { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] }, + { .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] }, + { .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] }, { } }; MODULE_DEVICE_TABLE(of, exc3000_of_match); @@ -210,7 +424,7 @@ static struct i2c_driver exc3000_driver = { .of_match_table = of_match_ptr(exc3000_of_match), }, .id_table = exc3000_id, - .probe = exc3000_probe, + .probe_new = exc3000_probe, }; module_i2c_driver(exc3000_driver); diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 5875bb1099a8..3162b68f7374 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -289,7 +289,7 @@ static int iqs5xx_bl_cmd(struct i2c_client *client, u8 bl_cmd, u16 bl_addr) break; case IQS5XX_BL_CMD_EXEC: usleep_range(10000, 10100); - /* fall through */ + fallthrough; default: return 0; } diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index 1af08d3dfaf7..f15713aaebc2 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -130,7 +130,6 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id) switch (buf[1] & EVENT_TAG_MASK) { case EVENT_INIT: - /* fall through */ case EVENT_MIDDLE: input_report_abs(data->input_dev, ABS_X, x); input_report_abs(data->input_dev, ABS_Y, y); diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index b54cc64e4ea6..df946869d4cd 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -255,7 +255,7 @@ static void stmfts_parse_events(struct stmfts_data *sdata) case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: case STMFTS_EV_STATUS: complete(&sdata->cmd_done); - /* fall through */ + fallthrough; case STMFTS_EV_NO_EVENT: case STMFTS_EV_DEBUG: |