From d9ccd62a8bcaa1d4b38f7ef6ba004f359f0042b5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 5 Dec 2013 19:21:10 -0800 Subject: [PATCH 001/154] Input: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. This is a cosmetic change to make the code simpler and enhance the readability. Signed-off-by: Jingoo Han Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 59aa24002c7bff..37ea05741d209a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1130,7 +1130,7 @@ static void mxt_input_close(struct input_dev *dev) static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct mxt_platform_data *pdata = client->dev.platform_data; + const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); struct mxt_data *data; struct input_dev *input_dev; int error; From efbafbd5ebb9601d2cdff8698a4b65593ad21be5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 6 Jan 2014 10:27:05 -0800 Subject: [PATCH 002/154] Input: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Signed-off-by: Paul Gortmaker Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 37ea05741d209a..a70400754e9237 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include From f0039e5145351493b87e214e1bca8e8f50a05959 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:14:49 -0700 Subject: [PATCH 003/154] Input: atmel_mxt_ts - remove unnecessary platform data It is not necessary to download these values to the maXTouch chip on every probe, since they are stored in NVRAM. It makes life difficult when tuning the device to keep them in sync with the config array/file, and requires a new kernel build for minor tweaks. These parameters only represent a tiny subset of the available configuration options, tracking all of these options in platform data would be a endless task. In addition, different versions of maXTouch chips may have these values in different places or may not even have them at all. Having these values also makes life more complex for device tree and other platforms where having to define a static configuration isn't helpful. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- arch/arm/mach-s5pv210/mach-goni.c | 5 --- drivers/input/touchscreen/atmel_mxt_ts.c | 50 ------------------------ include/linux/i2c/atmel_mxt_ts.h | 6 +-- 3 files changed, 1 insertion(+), 60 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 30b24ad84f496d..237f4193cb0be7 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -239,13 +239,8 @@ static void __init goni_radio_init(void) /* TSP */ static struct mxt_platform_data qt602240_platform_data = { - .x_line = 17, - .y_line = 11, .x_size = 800, .y_size = 480, - .blen = 0x21, - .threshold = 0x28, - .voltage = 2800000, /* 2.8V */ .orient = MXT_DIAGONAL, .irqflags = IRQF_TRIGGER_FALLING, }; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a70400754e9237..7eb515caf215dd 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -685,54 +685,6 @@ static int mxt_make_highchg(struct mxt_data *data) return 0; } -static void mxt_handle_pdata(struct mxt_data *data) -{ - const struct mxt_platform_data *pdata = data->pdata; - u8 voltage; - - /* Set touchscreen lines */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE, - pdata->x_line); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE, - pdata->y_line); - - /* Set touchscreen orient */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT, - pdata->orient); - - /* Set touchscreen burst length */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_BLEN, pdata->blen); - - /* Set touchscreen threshold */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_TCHTHR, pdata->threshold); - - /* Set touchscreen resolution */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - - /* Set touchscreen voltage */ - if (pdata->voltage) { - if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { - voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / - MXT_VOLTAGE_STEP; - voltage = 0xff - voltage + 1; - } else - voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / - MXT_VOLTAGE_STEP; - - mxt_write_object(data, MXT_SPT_CTECONFIG_T28, - MXT_CTE_VOLTAGE, voltage); - } -} - static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -840,8 +792,6 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - mxt_handle_pdata(data); - /* Backup to memory */ mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_BACKUPNV, diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 99e379b74398e6..eff0cdc088434a 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -33,14 +33,10 @@ struct mxt_platform_data { const u8 *config; size_t config_length; - unsigned int x_line; - unsigned int y_line; unsigned int x_size; unsigned int y_size; - unsigned int blen; - unsigned int threshold; - unsigned int voltage; unsigned char orient; + unsigned long irqflags; bool is_tp; const unsigned int key_map[MXT_NUM_GPIO]; From 89a8864fbc0dc07537fbb00d153dc8e6f817e90e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:15:45 -0700 Subject: [PATCH 004/154] Input: atmel_mxt_ts - improve T19 GPIO keys handling * The mapping of the GPIO numbers into the T19 status byte varies between different maXTouch chips. Some have up to 7 GPIOs. Allowing a keycode array of up to 8 items is simpler and more generic. So replace #define with configurable number of keys which also allows the removal of is_tp. * Rename platform data parameters to include "t19" to prevent confusion with T15 key array. * Probe aborts early on when pdata is NULL, so no need to check. * Move "int i" to beginning of function (mixed declarations and code) * Use API calls rather than __set_bit() * Remove unused dev variable. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- drivers/input/touchscreen/atmel_mxt_ts.c | 44 +++++++++--------------- include/linux/i2c/atmel_mxt_ts.h | 7 ++-- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7eb515caf215dd..65df362cf3278b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -180,12 +180,6 @@ #define MXT_FWRESET_TIME 175 /* msec */ -/* MXT_SPT_GPIOPWM_T19 field */ -#define MXT_GPIO0_MASK 0x04 -#define MXT_GPIO1_MASK 0x08 -#define MXT_GPIO2_MASK 0x10 -#define MXT_GPIO3_MASK 0x20 - /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa #define MXT_UNLOCK_CMD_LSB 0xdc @@ -250,7 +244,6 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; - bool is_tp; unsigned int irq; unsigned int max_x; @@ -515,15 +508,16 @@ static int mxt_write_object(struct mxt_data *data, static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) { struct input_dev *input = data->input_dev; + const struct mxt_platform_data *pdata = data->pdata; bool button; int i; /* Active-low switch */ - for (i = 0; i < MXT_NUM_GPIO; i++) { - if (data->pdata->key_map[i] == KEY_RESERVED) + for (i = 0; i < pdata->t19_num_keys; i++) { + if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & MXT_GPIO0_MASK << i); - input_report_key(input, data->pdata->key_map[i], button); + button = !(message->message[0] & (1 << i)); + input_report_key(input, pdata->t19_keymap[i], button); } } @@ -1084,6 +1078,8 @@ static int mxt_probe(struct i2c_client *client, struct input_dev *input_dev; int error; unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; if (!pdata) return -EINVAL; @@ -1096,10 +1092,7 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - data->is_tp = pdata && pdata->is_tp; - - input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" : - "Atmel maXTouch Touchscreen"; + input_dev->name = "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); @@ -1125,20 +1118,15 @@ static int mxt_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - if (data->is_tp) { - int i; - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (pdata->t19_num_keys) { __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - for (i = 0; i < MXT_NUM_GPIO; i++) - if (pdata->key_map[i] != KEY_RESERVED) - __set_bit(pdata->key_map[i], input_dev->keybit); + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit); + mt_flags |= INPUT_MT_POINTER; input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); @@ -1146,6 +1134,8 @@ static int mxt_probe(struct i2c_client *client, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_MT_POSITION_Y, MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; } /* For single touch */ @@ -1158,7 +1148,7 @@ static int mxt_probe(struct i2c_client *client, /* For multi touch */ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots, 0); + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) goto err_free_object; input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index eff0cdc088434a..d26080dc606cae 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -15,9 +15,6 @@ #include -/* For key_map array */ -#define MXT_NUM_GPIO 4 - /* Orient */ #define MXT_NORMAL 0x0 #define MXT_DIAGONAL 0x1 @@ -38,8 +35,8 @@ struct mxt_platform_data { unsigned char orient; unsigned long irqflags; - bool is_tp; - const unsigned int key_map[MXT_NUM_GPIO]; + u8 t19_num_keys; + const unsigned int *t19_keymap; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ From c1cdf788aeff2027aae1f3059ec15154769a6258 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:16:07 -0700 Subject: [PATCH 005/154] Input: atmel_mxt_ts - return IRQ_NONE when interrupt handler fails Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 65df362cf3278b..0cff8bb2ad75d1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -590,7 +590,7 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) do { if (mxt_read_message(data, &message)) { dev_err(dev, "Failed to read message\n"); - goto end; + return IRQ_NONE; } reportid = message.reportid; @@ -617,7 +617,6 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) input_sync(data->input_dev); } -end: return IRQ_HANDLED; } From a20c2ea371861e08ebf8d77ef09b7a5b4ee7c4e0 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Sat, 6 Jul 2013 22:16:26 -0700 Subject: [PATCH 006/154] Input: atmel_mxt_ts - define helper functions for size and instances These two object table entry fields are reported 1 less than their value. When used, however, we always want the actual size and instances. To keep the object size and instances 1-byte fields, and thus preserve the object-table struct's 6-byte packed alignment, add some convenient accessor functions that do the +1 every time these fields are accessed. Signed-off-by: Daniel Kurtz Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 37 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0cff8bb2ad75d1..40af02c2611313 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,8 @@ * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2012 Google, Inc. + * * Author: Joonyoung Shim * * This program is free software; you can redistribute it and/or modify it @@ -226,8 +228,8 @@ struct mxt_info { struct mxt_object { u8 type; u16 start_address; - u8 size; /* Size of each instance - 1 */ - u8 instances; /* Number of instances - 1 */ + u8 size_minus_one; + u8 instances_minus_one; u8 num_report_ids; } __packed; @@ -256,6 +258,16 @@ struct mxt_data { u8 T19_reportid; }; +static size_t mxt_obj_size(const struct mxt_object *obj) +{ + return obj->size_minus_one + 1; +} + +static size_t mxt_obj_instances(const struct mxt_object *obj) +{ + return obj->instances_minus_one + 1; +} + static bool mxt_object_readable(unsigned int type) { switch (type) { @@ -498,7 +510,7 @@ static int mxt_write_object(struct mxt_data *data, u16 reg; object = mxt_get_object(data, type); - if (!object || offset >= object->size + 1) + if (!object || offset >= mxt_obj_size(object)) return -EINVAL; reg = object->start_address; @@ -640,7 +652,7 @@ static int mxt_check_reg_init(struct mxt_data *data) if (!mxt_object_writable(object->type)) continue; - size = (object->size + 1) * (object->instances + 1); + size = mxt_obj_size(object) * mxt_obj_instances(object); if (index + size > pdata->config_length) { dev_err(dev, "Not enough config data!\n"); return -EINVAL; @@ -717,7 +729,7 @@ static int mxt_get_object_table(struct mxt_data *data) if (object->num_report_ids) { min_id = reportid; reportid += object->num_report_ids * - (object->instances + 1); + mxt_obj_instances(object); max_id = reportid - 1; } else { min_id = 0; @@ -725,9 +737,10 @@ static int mxt_get_object_table(struct mxt_data *data) } dev_dbg(&data->client->dev, - "Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n", - object->type, object->start_address, object->size + 1, - object->instances + 1, min_id, max_id); + "Type %2d Start %3d Size %3zd Instances %2zd ReportIDs %3u : %3u\n", + object->type, object->start_address, + mxt_obj_size(object), mxt_obj_instances(object), + min_id, max_id); switch (object->type) { case MXT_GEN_COMMAND_T6: @@ -864,11 +877,11 @@ static ssize_t mxt_show_instance(char *buf, int count, { int i; - if (object->instances > 0) + if (mxt_obj_instances(object) > 1) count += scnprintf(buf + count, PAGE_SIZE - count, "Instance %u\n", instance); - for (i = 0; i < object->size + 1; i++) + for (i = 0; i < mxt_obj_size(object); i++) count += scnprintf(buf + count, PAGE_SIZE - count, "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); @@ -901,8 +914,8 @@ static ssize_t mxt_object_show(struct device *dev, count += scnprintf(buf + count, PAGE_SIZE - count, "T%u:\n", object->type); - for (j = 0; j < object->instances + 1; j++) { - u16 size = object->size + 1; + for (j = 0; j < mxt_obj_instances(object); j++) { + u16 size = mxt_obj_size(object); u16 addr = object->start_address + j * size; error = __mxt_read_reg(data->client, addr, size, obuf); From 4f12798d4b001f4a0c319365385b66461d1f4a5a Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:00 -0700 Subject: [PATCH 007/154] Input: atmel_mxt_ts - wait for CHG assert in mxt_check_bootloader The driver should not immediately read bootloader status when in Application Update Mode. The CHG line will assert when the device has made a state transition and is ready to report a new status via i2c. This change adds a wait for completion in mxt_check_bootloader, and changes the mxt_interrupt handler to signal the completion. Signed-off-by: Benson Leung Signed-off-by: Daniel Kurtz Signed-off-by: Nick Dyer Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 102 ++++++++++++++++++----- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 40af02c2611313..f42ccebb16636f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -14,6 +14,8 @@ */ #include +#include +#include #include #include #include @@ -246,16 +248,19 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; - unsigned int irq; unsigned int max_x; unsigned int max_y; + bool in_bootloader; /* Cached parameters from object table */ u8 T6_reportid; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + + /* for fw update in bootloader */ + struct completion bl_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -339,12 +344,50 @@ static void mxt_dump_message(struct device *dev, message->reportid, 7, message->message); } -static int mxt_check_bootloader(struct i2c_client *client, - unsigned int state) +static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms) { + struct device *dev = &data->client->dev; + struct completion *comp = &data->bl_completion; + unsigned long timeout = msecs_to_jiffies(timeout_ms); + long ret; + + ret = wait_for_completion_interruptible_timeout(comp, timeout); + if (ret < 0) { + return ret; + } else if (ret == 0) { + dev_err(dev, "Wait for completion timed out.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +{ + struct i2c_client *client = data->client; u8 val; + int ret; recheck: + if (state != MXT_WAITING_BOOTLOAD_CMD) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_chg(data, 300); + if (ret) { + /* + * TODO: handle -ERESTARTSYS better by terminating + * fw update process before returning to userspace + * by writing length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). + */ + dev_err(&client->dev, "Update wait error %d\n", ret); + return ret; + } + } + if (i2c_master_recv(client, &val, 1) != 1) { dev_err(&client->dev, "%s: i2c recv failed\n", __func__); return -EIO; @@ -590,9 +633,8 @@ static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); } -static irqreturn_t mxt_interrupt(int irq, void *dev_id) +static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) { - struct mxt_data *data = dev_id; struct mxt_message message; const u8 *payload = &message.message[0]; struct device *dev = &data->client->dev; @@ -632,6 +674,19 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->in_bootloader) { + /* bootloader state transition completion */ + complete(&data->bl_completion); + return IRQ_HANDLED; + } + + return mxt_process_messages_until_invalid(data); +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -947,6 +1002,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Change to the bootloader mode */ + data->in_bootloader = true; + mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, MXT_BOOT_VALUE); msleep(MXT_RESET_TIME); @@ -957,18 +1014,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) else client->addr = MXT_BOOT_HIGH; - ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + INIT_COMPLETION(data->bl_completion); + + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); if (ret) - goto out; + goto disable_irq; /* Unlock bootloader */ mxt_unlock_bootloader(client); while (pos < fw->size) { - ret = mxt_check_bootloader(client, - MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); if (ret) - goto out; + goto disable_irq; frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); @@ -980,17 +1038,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) /* Write one frame to device */ mxt_fw_write(client, fw->data + pos, frame_size); - ret = mxt_check_bootloader(client, - MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); if (ret) - goto out; + goto disable_irq; pos += frame_size; dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); } -out: + data->in_bootloader = false; + +disable_irq: + disable_irq(data->irq); release_firmware(fw); /* Change to slave address of application */ @@ -1009,8 +1069,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, struct mxt_data *data = dev_get_drvdata(dev); int error; - disable_irq(data->irq); - error = mxt_load_fw(dev, MXT_FW_NAME); if (error) { dev_err(dev, "The firmware update failed(%d)\n", error); @@ -1024,13 +1082,13 @@ static ssize_t mxt_update_fw_store(struct device *dev, mxt_free_object_table(data); mxt_initialize(data); - } - enable_irq(data->irq); + enable_irq(data->irq); - error = mxt_make_highchg(data); - if (error) - return error; + error = mxt_make_highchg(data); + if (error) + return error; + } return count; } @@ -1120,6 +1178,8 @@ static int mxt_probe(struct i2c_client *client, data->pdata = pdata; data->irq = client->irq; + init_completion(&data->bl_completion); + mxt_calc_resolution(data); error = mxt_initialize(data); From 0fd587db342aebd4123714d05d082b5a77dbaa25 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:18 -0700 Subject: [PATCH 008/154] Input: atmel_mxt_ts - wait for CHG after bootloader resets Rather than msleep for MXT_RESET_TIME and MXT_FWRESET_TIME during the transition to bootloader mode and the transition back from app, wait for the CHG assert to indicate that the transition is done. This change replaces the msleep with a wait for completion that the mxt_interrupt handler signals. Also add CHG poll after last firmware frame - some bootloader versions will assert the interrupt line after the final frame, in testing this meant that the driver attempts to read the info block too early whilst the chip is still resetting. This improves firmware update time as we no longer wait longer than necessary for each reset. Signed-off-by: Benson Leung Signed-off-by: Daniel Kurtz Signed-off-by: Nick Dyer Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index f42ccebb16636f..b9ef87a09cb438 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -181,8 +181,8 @@ #define MXT_BACKUP_VALUE 0x55 #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ - -#define MXT_FWRESET_TIME 175 /* msec */ +#define MXT_FW_RESET_TIME 3000 /* msec */ +#define MXT_FW_CHG_TIMEOUT 300 /* msec */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -375,7 +375,7 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) * CHG assertion before reading the status byte. * Once the status byte has been read, the line is deasserted. */ - ret = mxt_wait_for_chg(data, 300); + ret = mxt_wait_for_chg(data, MXT_FW_CHG_TIMEOUT); if (ret) { /* * TODO: handle -ERESTARTSYS better by terminating @@ -1047,6 +1047,18 @@ static int mxt_load_fw(struct device *dev, const char *fn) dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); } + /* Wait for flash. */ + ret = mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + if (ret) + goto disable_irq; + + /* + * Wait for device to reset. Some bootloader versions do not assert + * the CHG line after bootloading has finished, so ignore potential + * errors. + */ + mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + data->in_bootloader = false; disable_irq: @@ -1075,10 +1087,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, count = error; } else { dev_dbg(dev, "The firmware update succeeded\n"); - - /* Wait for reset */ - msleep(MXT_FWRESET_TIME); - mxt_free_object_table(data); mxt_initialize(data); From fb3af1c77ecb6e559d293ba1e902130d1e371577 Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Sat, 6 Jul 2013 22:18:03 -0700 Subject: [PATCH 009/154] Input: atmel_mxt_ts - make wait-after-reset period compatible with all chips The delay before the chip can be accessed after reset varies between different chips in maXTouch family. Waiting for an interrupt and a T6 status message with the RESET bit set is a better behaviour. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 109 ++++++++++++++++++----- 1 file changed, 87 insertions(+), 22 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b9ef87a09cb438..c59444e923556c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -87,6 +87,9 @@ #define MXT_COMMAND_REPORTALL 3 #define MXT_COMMAND_DIAGNOSTIC 5 +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET (1 << 7) + /* MXT_GEN_POWER_T7 field */ #define MXT_POWER_IDLEACQINT 0 #define MXT_POWER_ACTVACQINT 1 @@ -178,9 +181,13 @@ /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 +#define MXT_RESET_VALUE 0x01 #define MXT_BACKUP_VALUE 0x55 + +/* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_TIMEOUT 3000 /* msec */ #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ @@ -255,12 +262,16 @@ struct mxt_data { /* Cached parameters from object table */ u8 T6_reportid; + u16 T6_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; /* for fw update in bootloader */ struct completion bl_completion; + + /* for reset handling */ + struct completion reset_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -344,10 +355,11 @@ static void mxt_dump_message(struct device *dev, message->reportid, 7, message->message); } -static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms) +static int mxt_wait_for_completion(struct mxt_data *data, + struct completion *comp, + unsigned int timeout_ms) { struct device *dev = &data->client->dev; - struct completion *comp = &data->bl_completion; unsigned long timeout = msecs_to_jiffies(timeout_ms); long ret; @@ -375,7 +387,8 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) * CHG assertion before reading the status byte. * Once the status byte has been read, the line is deasserted. */ - ret = mxt_wait_for_chg(data, MXT_FW_CHG_TIMEOUT); + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); if (ret) { /* * TODO: handle -ERESTARTSYS better by terminating @@ -654,6 +667,9 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) unsigned csum = mxt_extract_T6_csum(&payload[1]); dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", status, csum); + + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); } else if (mxt_is_T9_message(data, &message)) { int id = reportid - data->T9_reportid_min; mxt_input_touchevent(data, &message, id); @@ -687,6 +703,59 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return mxt_process_messages_until_invalid(data); } +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, + u8 value, bool wait) +{ + u16 reg; + u8 command_register; + int timeout_counter = 0; + int ret; + + reg = data->T6_address + cmd_offset; + + ret = mxt_write_reg(data->client, reg, value); + if (ret) + return ret; + + if (!wait) + return 0; + + do { + msleep(20); + ret = __mxt_read_reg(data->client, reg, 1, &command_register); + if (ret) + return ret; + } while (command_register != 0 && timeout_counter++ <= 100); + + if (timeout_counter > 100) { + dev_err(&data->client->dev, "Command failed!\n"); + return -EIO; + } + + return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + dev_info(dev, "Resetting chip\n"); + + INIT_COMPLETION(data->reset_completion); + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); + if (ret) + return ret; + + ret = mxt_wait_for_completion(data, &data->reset_completion, + MXT_RESET_TIMEOUT); + if (ret) + return ret; + + return 0; +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -800,6 +869,7 @@ static int mxt_get_object_table(struct mxt_data *data) switch (object->type) { case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; + data->T6_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; @@ -853,16 +923,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - /* Backup to memory */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE); - msleep(MXT_BACKUP_TIME); - - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, 1); - msleep(MXT_RESET_TIME); + error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE, false); + if (!error) + mxt_soft_reset(data); /* Update matrix size at info struct */ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); @@ -1004,8 +1068,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) /* Change to the bootloader mode */ data->in_bootloader = true; - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, MXT_BOOT_VALUE); + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; + msleep(MXT_RESET_TIME); /* Change to slave address of bootloader */ @@ -1048,7 +1114,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Wait for flash. */ - ret = mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); if (ret) goto disable_irq; @@ -1057,12 +1124,13 @@ static int mxt_load_fw(struct device *dev, const char *fn) * the CHG line after bootloading has finished, so ignore potential * errors. */ - mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); data->in_bootloader = false; disable_irq: disable_irq(data->irq); +release_firmware: release_firmware(fw); /* Change to slave address of application */ @@ -1187,6 +1255,7 @@ static int mxt_probe(struct i2c_client *client, data->irq = client->irq; init_completion(&data->bl_completion); + init_completion(&data->reset_completion); mxt_calc_resolution(data); @@ -1314,11 +1383,7 @@ static int mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, 1); - - msleep(MXT_RESET_TIME); + mxt_soft_reset(data); mutex_lock(&input_dev->mutex); From 61cadc58c3f1a5c8594c906f791e7aa2a6323fb7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:18:27 -0700 Subject: [PATCH 010/154] Input: atmel_mxt_ts - improve error reporting and debug - Add error messages for probe errors - Report type in invalid object type - Tweak some other debug output messages Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 33 +++++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c59444e923556c..6cfc56223a499f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -420,7 +420,8 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) } if (val != state) { - dev_err(&client->dev, "Unvalid bootloader mode state\n"); + dev_err(&client->dev, "Invalid bootloader state %02X != %02X\n", + val, state); return -EINVAL; } @@ -540,7 +541,7 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type\n"); + dev_err(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } @@ -861,7 +862,7 @@ static int mxt_get_object_table(struct mxt_data *data) } dev_dbg(&data->client->dev, - "Type %2d Start %3d Size %3zd Instances %2zd ReportIDs %3u : %3u\n", + "T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n", object->type, object->start_address, mxt_obj_size(object), mxt_obj_instances(object), min_id, max_id); @@ -915,13 +916,18 @@ static int mxt_initialize(struct mxt_data *data) /* Get object table information */ error = mxt_get_object_table(data); - if (error) + if (error) { + dev_err(&client->dev, "Error %d reading object table\n", error); goto err_free_object_table; + } /* Check register init values */ error = mxt_check_reg_init(data); - if (error) + if (error) { + dev_err(&client->dev, "Error %d initializing configuration\n", + error); goto err_free_object_table; + } error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE, false); @@ -940,12 +946,12 @@ static int mxt_initialize(struct mxt_data *data) info->matrix_ysize = val; dev_info(&client->dev, - "Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n", + "Family: %u Variant: %u Firmware V%u.%u.%02X\n", info->family_id, info->variant_id, info->version >> 4, info->version & 0xf, info->build); dev_info(&client->dev, - "Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n", + "Matrix X Size: %u Matrix Y Size: %u Objects: %u\n", info->matrix_xsize, info->matrix_ysize, info->object_num); @@ -1154,7 +1160,8 @@ static ssize_t mxt_update_fw_store(struct device *dev, dev_err(dev, "The firmware update failed(%d)\n", error); count = error; } else { - dev_dbg(dev, "The firmware update succeeded\n"); + dev_info(dev, "The firmware update succeeded\n"); + mxt_free_object_table(data); mxt_initialize(data); @@ -1325,12 +1332,18 @@ static int mxt_probe(struct i2c_client *client, goto err_free_irq; error = input_register_device(input_dev); - if (error) + if (error) { + dev_err(&client->dev, "Error %d registering input device\n", + error); goto err_free_irq; + } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); - if (error) + if (error) { + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); goto err_unregister_device; + } return 0; From 85c876cdf5c9c003ae764d88f01a71fca7c99ca1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:04 -0700 Subject: [PATCH 011/154] Input: atmel_mxt_ts - implement CRC check for configuration data The configuration is stored in NVRAM on the maXTouch chip. When the device is reset it reports a CRC of the stored configuration values. Therefore it isn't necessary to send the configuration on each probe - we can check the CRC matches and avoid a timeconsuming backup/reset cycle. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 60 ++++++++++++++++++++---- include/linux/i2c/atmel_mxt_ts.h | 1 + 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6cfc56223a499f..0ff28dd4e7cdf8 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -188,6 +188,7 @@ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ @@ -259,6 +260,7 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u32 config_crc; /* Cached parameters from object table */ u8 T6_reportid; @@ -272,6 +274,9 @@ struct mxt_data { /* for reset handling */ struct completion reset_completion; + + /* for config update handling */ + struct completion crc_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -636,7 +641,7 @@ static void mxt_input_touchevent(struct mxt_data *data, } } -static unsigned mxt_extract_T6_csum(const u8 *csum) +static u16 mxt_extract_T6_csum(const u8 *csum) { return csum[0] | (csum[1] << 8) | (csum[2] << 16); } @@ -654,6 +659,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) struct device *dev = &data->client->dev; u8 reportid; bool update_input = false; + u32 crc; do { if (mxt_read_message(data, &message)) { @@ -665,9 +671,15 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) if (reportid == data->T6_reportid) { u8 status = payload[0]; - unsigned csum = mxt_extract_T6_csum(&payload[1]); + + crc = mxt_extract_T6_csum(&payload[1]); + if (crc != data->config_crc) { + data->config_crc = crc; + complete(&data->crc_completion); + } + dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, csum); + status, data->config_crc); if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); @@ -757,6 +769,24 @@ static int mxt_soft_reset(struct mxt_data *data) return 0; } +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* + * On failure, CRC is set to 0 and config will always be + * downloaded. + */ + data->config_crc = 0; + INIT_COMPLETION(data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* + * Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded. + */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -771,6 +801,16 @@ static int mxt_check_reg_init(struct mxt_data *data) return 0; } + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (data->config_crc == pdata->config_crc) { + dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); + return 0; + } + + dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", + data->config_crc, pdata->config_crc); + for (i = 0; i < data->info.object_num; i++) { object = data->object_table + i; @@ -790,6 +830,14 @@ static int mxt_check_reg_init(struct mxt_data *data) index += size; } + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_soft_reset(data); + if (ret) + return ret; + + dev_info(dev, "Config successfully updated\n"); + return 0; } @@ -929,11 +977,6 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } - error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE, false); - if (!error) - mxt_soft_reset(data); - /* Update matrix size at info struct */ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); if (error) @@ -1263,6 +1306,7 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->bl_completion); init_completion(&data->reset_completion); + init_completion(&data->crc_completion); mxt_calc_resolution(data); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index d26080dc606cae..9f92135b66203e 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -29,6 +29,7 @@ struct mxt_platform_data { const u8 *config; size_t config_length; + u32 config_crc; unsigned int x_size; unsigned int y_size; From e4c0272a02eb81a14b02a7dd1ed4e28d460043d0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:33 -0700 Subject: [PATCH 012/154] Input: atmel_mxt_ts - download device config using firmware loader The existing implementation which encodes the configuration as a binary blob in platform data is unsatisfactory since it requires a kernel recompile for the configuration to be changed, and it doesn't deal well with firmware changes that move values around on the chip. Atmel define an ASCII format for the configuration which can be exported from their tools. This patch implements a parser for that format which loads the configuration via the firmware loader and sends it to the MXT chip. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- drivers/input/touchscreen/atmel_mxt_ts.c | 234 +++++++++++++++++------ include/linux/i2c/atmel_mxt_ts.h | 4 - 2 files changed, 176 insertions(+), 62 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0ff28dd4e7cdf8..2a591918c8c59f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -35,8 +35,10 @@ #define MXT_BOOT_LOW 0x24 #define MXT_BOOT_HIGH 0x25 -/* Firmware */ +/* 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 @@ -322,37 +324,6 @@ static bool mxt_object_readable(unsigned int type) } } -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - static void mxt_dump_message(struct device *dev, struct mxt_message *message) { @@ -546,7 +517,7 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%u\n", type); + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } @@ -787,58 +758,205 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } +/* + * mxt_check_reg_init - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * + * + * - 2-byte object type as hex + * - 2-byte object instance number as hex + * - 2-byte object size as hex + * - array of 1-byte hex values + */ static int mxt_check_reg_init(struct mxt_data *data) { - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; struct device *dev = &data->client->dev; - int index = 0; - int i, size; + struct mxt_info cfg_info; + struct mxt_object *object; + const struct firmware *cfg = NULL; int ret; + int offset; + int pos; + int i; + u32 info_crc, config_crc; + unsigned int type, instance, size; + u8 val; + u16 reg; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + ret = request_firmware(&cfg, MXT_CFG_NAME, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", + MXT_CFG_NAME); return 0; } mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); - if (data->config_crc == pdata->config_crc) { - dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - return 0; + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; } - dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", - data->config_crc, pdata->config_crc); + pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + pos += offset; + } + + if (cfg_info.family_id != data->info.family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.variant_id != data->info.variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.version != data->info.version) + dev_err(dev, "Warning: version mismatch!\n"); + + if (cfg_info.build != data->info.build) + dev_err(dev, "Warning: build num mismatch!\n"); + + ret = sscanf(cfg->data + pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + /* Check config CRC */ + ret = sscanf(cfg->data + pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; - if (!mxt_object_writable(object->type)) + if (data->config_crc == config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", config_crc); + ret = 0; + goto release; + } + + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + + while (pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + ret = 1; + goto release; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + pos += offset; + } continue; + } - size = mxt_obj_size(object) * mxt_obj_instances(object); - if (index + size > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); - return -EINVAL; + if (size > mxt_obj_size(object)) { + dev_err(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); } - ret = __mxt_write_reg(data->client, object->start_address, - size, &pdata->config[index]); - if (ret) - return ret; - index += size; + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release; + } + pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + ret = mxt_write_reg(data->client, reg + i, val); + if (ret) + goto release; + + } + + /* + * If firmware is upgraded, new bytes may be added to end of + * objects. It is generally forward compatible to zero these + * bytes - previous behaviour will be retained. However + * this does invalidate the CRC and will force a config + * download every time until the configuration is updated. + */ + if (size < mxt_obj_size(object)) { + dev_info(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + + for (i = size + 1; i < mxt_obj_size(object); i++) { + ret = mxt_write_reg(data->client, reg + i, 0); + if (ret) + goto release; + } + } } mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - return ret; + goto release; dev_info(dev, "Config successfully updated\n"); - return 0; +release: + release_firmware(cfg); + return ret; } static int mxt_make_highchg(struct mxt_data *data) diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 9f92135b66203e..b569bb876d18e4 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -27,10 +27,6 @@ /* The platform data for the Atmel maXTouch touchscreen driver */ struct mxt_platform_data { - const u8 *config; - size_t config_length; - u32 config_crc; - unsigned int x_size; unsigned int y_size; unsigned char orient; From 5cbaf241979308baea567ab29c67d3e6d4393252 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:51 -0700 Subject: [PATCH 013/154] Input: atmel_mxt_ts - calculate and check CRC in config file By validating the checksum, we can identify if the configuration is corrupt. In addition, this patch writes the configuration in a short series of block writes rather than as many individual values. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 232 +++++++++++++++++------ 1 file changed, 177 insertions(+), 55 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2a591918c8c59f..df64a2fd1395eb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -52,6 +52,8 @@ #define MXT_OBJECT_START 0x07 #define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 @@ -262,11 +264,14 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u16 mem_size; u32 config_crc; + u32 info_crc; /* Cached parameters from object table */ u8 T6_reportid; u16 T6_address; + u16 T7_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; @@ -758,6 +763,45 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + /* * mxt_check_reg_init - download configuration to chip * @@ -785,9 +829,13 @@ static int mxt_check_reg_init(struct mxt_data *data) const struct firmware *cfg = NULL; int ret; int offset; - int pos; + int data_pos; + int byte_offset; int i; - u32 info_crc, config_crc; + int cfg_start_ofs; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + size_t config_mem_size; unsigned int type, instance, size; u8 val; u16 reg; @@ -807,11 +855,11 @@ static int mxt_check_reg_init(struct mxt_data *data) goto release; } - pos = strlen(MXT_CFG_MAGIC); + data_pos = strlen(MXT_CFG_MAGIC); /* Load information block and check */ for (i = 0; i < sizeof(struct mxt_info); i++) { - ret = sscanf(cfg->data + pos, "%hhx%n", + ret = sscanf(cfg->data + data_pos, "%hhx%n", (unsigned char *)&cfg_info + i, &offset); if (ret != 1) { @@ -820,7 +868,7 @@ static int mxt_check_reg_init(struct mxt_data *data) goto release; } - pos += offset; + data_pos += offset; } if (cfg_info.family_id != data->info.family_id) { @@ -835,125 +883,188 @@ static int mxt_check_reg_init(struct mxt_data *data) goto release; } - if (cfg_info.version != data->info.version) - dev_err(dev, "Warning: version mismatch!\n"); - - if (cfg_info.build != data->info.build) - dev_err(dev, "Warning: build num mismatch!\n"); - - ret = sscanf(cfg->data + pos, "%x%n", &info_crc, &offset); + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); ret = -EINVAL; goto release; } - pos += offset; + data_pos += offset; - /* Check config CRC */ - ret = sscanf(cfg->data + pos, "%x%n", &config_crc, &offset); + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); ret = -EINVAL; goto release; } - pos += offset; + data_pos += offset; - if (data->config_crc == config_crc) { - dev_dbg(dev, "Config CRC 0x%06X: OK\n", config_crc); - ret = 0; - goto release; + /* + * The Info Block CRC is calculated over mxt_info and the object + * table. If it does not match then we are trying to load the + * configuration from a different chip or firmware version, so + * the configuration CRC is invalid anyway. + */ + if (info_crc == data->info_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", + data->config_crc); + ret = 0; + goto release; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } + } else { + dev_warn(dev, + "Warning: Info CRC error - device=0x%06X file=0x%06X\n", + data->info_crc, info_crc); } - dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", - data->config_crc, config_crc); + /* Malloc memory to store configuration */ + cfg_start_ofs = MXT_OBJECT_START + + data->info.object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + config_mem_size = data->mem_size - cfg_start_ofs; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto release; + } - while (pos < cfg->size) { + while (data_pos < cfg->size) { /* Read type, instance, length */ - ret = sscanf(cfg->data + pos, "%x %x %x%n", + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", &type, &instance, &size, &offset); if (ret == 0) { /* EOF */ - ret = 1; - goto release; + break; } else if (ret != 3) { dev_err(dev, "Bad format: failed to parse object\n"); ret = -EINVAL; - goto release; + goto release_mem; } - pos += offset; + data_pos += offset; object = mxt_get_object(data, type); if (!object) { /* Skip object */ for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + pos, "%hhx%n", + ret = sscanf(cfg->data + data_pos, "%hhx%n", &val, &offset); - pos += offset; + data_pos += offset; } continue; } if (size > mxt_obj_size(object)) { - dev_err(dev, "Discarding %zu byte(s) in T%u\n", - size - mxt_obj_size(object), type); + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); } if (instance >= mxt_obj_instances(object)) { dev_err(dev, "Object instances exceeded!\n"); ret = -EINVAL; - goto release; + goto release_mem; } reg = object->start_address + mxt_obj_size(object) * instance; for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + pos, "%hhx%n", + ret = sscanf(cfg->data + data_pos, "%hhx%n", &val, &offset); if (ret != 1) { dev_err(dev, "Bad format in T%d\n", type); ret = -EINVAL; - goto release; + goto release_mem; } - pos += offset; + data_pos += offset; if (i > mxt_obj_size(object)) continue; - ret = mxt_write_reg(data->client, reg + i, val); - if (ret) - goto release; + byte_offset = reg + i - cfg_start_ofs; + if ((byte_offset >= 0) + && (byte_offset <= config_mem_size)) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + ret = -EINVAL; + goto release_mem; + } } + } - /* - * If firmware is upgraded, new bytes may be added to end of - * objects. It is generally forward compatible to zero these - * bytes - previous behaviour will be retained. However - * this does invalidate the CRC and will force a config - * download every time until the configuration is updated. - */ - if (size < mxt_obj_size(object)) { - dev_info(dev, "Zeroing %zu byte(s) in T%d\n", - mxt_obj_size(object) - size, type); + /* Calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < cfg_start_ofs) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, cfg_start_ofs); + ret = 0; + goto release_mem; + } - for (i = size + 1; i < mxt_obj_size(object); i++) { - ret = mxt_write_reg(data->client, reg + i, 0); - if (ret) - goto release; - } + calculated_crc = mxt_calculate_crc(config_mem, + data->T7_address - cfg_start_ofs, + config_mem_size); + + if (config_crc > 0 && (config_crc != calculated_crc)) + dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + + /* Write configuration as blocks */ + byte_offset = 0; + while (byte_offset < config_mem_size) { + size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + ret = __mxt_write_reg(data->client, + cfg_start_ofs + byte_offset, + size, config_mem + byte_offset); + if (ret != 0) { + dev_err(dev, "Config write error, ret=%d\n", ret); + goto release_mem; } + + byte_offset += size; } mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - goto release; + goto release_mem; dev_info(dev, "Config successfully updated\n"); +release_mem: + kfree(config_mem); release: release_firmware(cfg); return ret; @@ -1002,6 +1113,7 @@ static int mxt_get_object_table(struct mxt_data *data) int error; int i; u8 reportid; + u16 end_address; table_size = data->info.object_num * sizeof(struct mxt_object); error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, @@ -1011,6 +1123,7 @@ static int mxt_get_object_table(struct mxt_data *data) /* Valid Report IDs start counting from 1 */ reportid = 1; + data->mem_size = 0; for (i = 0; i < data->info.object_num; i++) { struct mxt_object *object = data->object_table + i; u8 min_id, max_id; @@ -1038,6 +1151,9 @@ static int mxt_get_object_table(struct mxt_data *data) data->T6_reportid = min_id; data->T6_address = object->start_address; break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; @@ -1046,6 +1162,12 @@ static int mxt_get_object_table(struct mxt_data *data) data->T19_reportid = min_id; break; } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; } return 0; From 80256b454ebb0a8d13e719c296eb389b2f412686 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:14 -0700 Subject: [PATCH 014/154] Input: atmel_mxt_ts - add additional bootloader addresses Move bootloaders reads/writes into separate functions. Instead of switching client->addr, define new field bootloader_addr in mxt_data. Implement lookup calculation for bootloader addresses. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 138 +++++++++++++++-------- 1 file changed, 93 insertions(+), 45 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index df64a2fd1395eb..280abdc92d775c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -29,12 +29,6 @@ #define MXT_VER_21 21 #define MXT_VER_22 22 -/* Slave addresses */ -#define MXT_APP_LOW 0x4a -#define MXT_APP_HIGH 0x4b -#define MXT_BOOT_LOW 0x24 -#define MXT_BOOT_HIGH 0x25 - /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" #define MXT_CFG_NAME "maxtouch.cfg" @@ -267,6 +261,7 @@ struct mxt_data { u16 mem_size; u32 config_crc; u32 info_crc; + u8 bootloader_addr; /* Cached parameters from object table */ u8 T6_reportid; @@ -354,9 +349,82 @@ static int mxt_wait_for_completion(struct mxt_data *data, return 0; } +static int mxt_bootloader_read(struct mxt_data *data, + u8 *val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + + if (ret == 1) { + ret = 0; + } else { + ret = ret < 0 ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, + const u8 * const val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (u8 *)val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + if (ret == 1) { + ret = 0; + } else { + ret = ret < 0 ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data) +{ + u8 appmode = data->client->addr; + u8 bootloader; + + switch (appmode) { + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x5a: + case 0x5b: + bootloader = appmode - 0x26; + break; + default: + dev_err(&data->client->dev, + "Appmode i2c address 0x%02x not found\n", + appmode); + return -EINVAL; + } + + data->bootloader_addr = bootloader; + return 0; +} + static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) { - struct i2c_client *client = data->client; + struct device *dev = &data->client->dev; u8 val; int ret; @@ -377,15 +445,14 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) * by writing length 0x000 to device (iff we are in * WAITING_FRAME_DATA state). */ - dev_err(&client->dev, "Update wait error %d\n", ret); + dev_err(dev, "Update wait error %d\n", ret); return ret; } } - if (i2c_master_recv(client, &val, 1) != 1) { - dev_err(&client->dev, "%s: i2c recv failed\n", __func__); - return -EIO; - } + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; switch (state) { case MXT_WAITING_BOOTLOAD_CMD: @@ -401,7 +468,7 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) } if (val != state) { - dev_err(&client->dev, "Invalid bootloader state %02X != %02X\n", + dev_err(dev, "Invalid bootloader state %02X != %02X\n", val, state); return -EINVAL; } @@ -409,28 +476,17 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) return 0; } -static int mxt_unlock_bootloader(struct i2c_client *client) +static int mxt_unlock_bootloader(struct mxt_data *data) { + int ret; u8 buf[2]; buf[0] = MXT_UNLOCK_CMD_LSB; buf[1] = MXT_UNLOCK_CMD_MSB; - if (i2c_master_send(client, buf, 2) != 2) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int mxt_fw_write(struct i2c_client *client, - const u8 *data, unsigned int frame_size) -{ - if (i2c_master_send(client, data, frame_size) != frame_size) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } + ret = mxt_bootloader_write(data, buf, 2); + if (ret) + return ret; return 0; } @@ -1342,7 +1398,6 @@ static ssize_t mxt_object_show(struct device *dev, static int mxt_load_fw(struct device *dev, const char *fn) { struct mxt_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; const struct firmware *fw = NULL; unsigned int frame_size; unsigned int pos = 0; @@ -1354,6 +1409,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) return ret; } + ret = mxt_lookup_bootloader_address(data); + if (ret) + goto release_firmware; + /* Change to the bootloader mode */ data->in_bootloader = true; @@ -1363,12 +1422,6 @@ static int mxt_load_fw(struct device *dev, const char *fn) msleep(MXT_RESET_TIME); - /* Change to slave address of bootloader */ - if (client->addr == MXT_APP_LOW) - client->addr = MXT_BOOT_LOW; - else - client->addr = MXT_BOOT_HIGH; - INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); @@ -1376,7 +1429,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto disable_irq; /* Unlock bootloader */ - mxt_unlock_bootloader(client); + mxt_unlock_bootloader(data); while (pos < fw->size) { ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); @@ -1391,7 +1444,9 @@ static int mxt_load_fw(struct device *dev, const char *fn) frame_size += 2; /* Write one frame to device */ - mxt_fw_write(client, fw->data + pos, frame_size); + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) + goto disable_irq; ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); if (ret) @@ -1421,13 +1476,6 @@ static int mxt_load_fw(struct device *dev, const char *fn) disable_irq(data->irq); release_firmware: release_firmware(fw); - - /* Change to slave address of application */ - if (client->addr == MXT_BOOT_LOW) - client->addr = MXT_APP_LOW; - else - client->addr = MXT_APP_HIGH; - return ret; } From 63a72b26f9c79be3e28cdc60fe2c0579a6d42bc0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:36 -0700 Subject: [PATCH 015/154] Input: atmel_mxt_ts - read and report bootloader version Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 280abdc92d775c..50232ef583b5be 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -202,6 +202,8 @@ #define MXT_FRAME_CRC_PASS 0x04 #define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ #define MXT_BOOT_STATUS_MASK 0x3f +#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_ID_MASK 0x1f /* Touch status */ #define MXT_UNGRIP (1 << 0) @@ -422,6 +424,27 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) return 0; } +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + + if (val & MXT_BOOT_EXTENDED_ID) { + if (mxt_bootloader_read(data, &buf[0], 3) != 0) { + dev_err(dev, "%s: i2c failure\n", __func__); + return -EIO; + } + + dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + + return buf[0]; + } else { + dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + + return val; + } +} + static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) { struct device *dev = &data->client->dev; @@ -454,6 +477,9 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) if (ret) return ret; + if (state == MXT_WAITING_BOOTLOAD_CMD) + val = mxt_get_bootloader_version(data, val); + switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: From 84c69b515ef3e89836e9f3463e4ff683c876de37 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:52 -0700 Subject: [PATCH 016/154] Input: atmel_mxt_ts - implement bootloader frame retries Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 27 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 50232ef583b5be..2e6686bb86c9f2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -486,8 +486,12 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: - if (val == MXT_FRAME_CRC_CHECK) + if (val == MXT_FRAME_CRC_CHECK) { goto recheck; + } else if (val == MXT_FRAME_CRC_FAIL) { + dev_err(dev, "Bootloader CRC fail\n"); + return -EINVAL; + } break; default: return -EINVAL; @@ -1427,6 +1431,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) const struct firmware *fw = NULL; unsigned int frame_size; unsigned int pos = 0; + unsigned int retry = 0; int ret; ret = request_firmware(&fw, fn, dev); @@ -1464,9 +1469,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - /* We should add 2 at frame size as the the firmware data is not - * included the CRC bytes. - */ + /* Take account of CRC bytes */ frame_size += 2; /* Write one frame to device */ @@ -1475,10 +1478,20 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto disable_irq; ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); - if (ret) - goto disable_irq; + if (ret) { + retry++; - pos += frame_size; + /* Back off by 20ms per retry */ + msleep(retry * 20); + + if (retry > 20) { + dev_err(dev, "Retry count exceeded\n"); + goto disable_irq; + } + } else { + retry = 0; + pos += frame_size; + } dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); } From f22b3f8d345d1964f011542e64f9b0026f1d7fca Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:30 -0700 Subject: [PATCH 017/154] Input: atmel_mxt_ts - improve bootloader progress output By implementing a frame counter, print out fewer debug messages (the firmware may contain hundreds of frames). Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2e6686bb86c9f2..1f168797a4c8a0 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1432,6 +1432,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) unsigned int frame_size; unsigned int pos = 0; unsigned int retry = 0; + unsigned int frame = 0; int ret; ret = request_firmware(&fw, fn, dev); @@ -1491,9 +1492,12 @@ static int mxt_load_fw(struct device *dev, const char *fn) } else { retry = 0; pos += frame_size; + frame++; } - dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + if (frame % 50 == 0) + dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", + frame, pos, fw->size); } /* Wait for flash. */ @@ -1502,6 +1506,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto disable_irq; + dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); + /* * Wait for device to reset. Some bootloader versions do not assert * the CHG line after bootloading has finished, so ignore potential From 3e7625bebbb5f8884d309de5cc41dc365937f316 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:50 -0700 Subject: [PATCH 018/154] Input: atmel_mxt_ts - add check for incorrect firmware file format Atmel supplies firmware files in ASCII HEX format (.enc) which must be converted before they can be loaded by kernel driver. Try to detect the error and print a friendly error message rather than feeding junk to the bootloader. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 1f168797a4c8a0..d0cf5b4df0a435 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1425,6 +1425,30 @@ static ssize_t mxt_object_show(struct device *dev, return error ?: count; } +static int mxt_check_firmware_format(struct device *dev, + const struct firmware *fw) +{ + unsigned int pos = 0; + char c; + + while (pos < fw->size) { + c = *(fw->data + pos); + + if (c < '0' || (c > '9' && c < 'A') || c > 'F') + return 0; + + pos++; + } + + /* + * To convert file try: + * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw + */ + dev_err(dev, "Aborting: firmware file must be in binary format\n"); + + return -EINVAL; +} + static int mxt_load_fw(struct device *dev, const char *fn) { struct mxt_data *data = dev_get_drvdata(dev); @@ -1441,6 +1465,11 @@ static int mxt_load_fw(struct device *dev, const char *fn) return ret; } + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, fw); + if (ret) + goto release_firmware; + ret = mxt_lookup_bootloader_address(data); if (ret) goto release_firmware; From 366b0538648d4bba4e99531543cea8322f79f9e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:24:31 -0700 Subject: [PATCH 019/154] Input: atmel_mxt_ts - read screen config from chip By reading the touchscreen configuration from the settings that the maXTouch chip is actually using, we can remove some platform data. The matrix size is not used for anything, and results in some rather confusing code to re-read it because it may change when configuration is downloaded, so don't print it out. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- arch/arm/mach-s5pv210/mach-goni.c | 3 - drivers/input/touchscreen/atmel_mxt_ts.c | 136 +++++++++++------------ include/linux/i2c/atmel_mxt_ts.h | 14 --- 3 files changed, 65 insertions(+), 88 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 237f4193cb0be7..8ebc41a7243e7d 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -239,9 +239,6 @@ static void __init goni_radio_init(void) /* TSP */ static struct mxt_platform_data qt602240_platform_data = { - .x_size = 800, - .y_size = 480, - .orient = MXT_DIAGONAL, .irqflags = IRQF_TRIGGER_FALLING, }; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d0cf5b4df0a435..25fecf3a647c08 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -104,33 +104,16 @@ /* MXT_TOUCH_MULTI_T9 field */ #define MXT_TOUCH_CTRL 0 -#define MXT_TOUCH_XORIGIN 1 -#define MXT_TOUCH_YORIGIN 2 -#define MXT_TOUCH_XSIZE 3 -#define MXT_TOUCH_YSIZE 4 -#define MXT_TOUCH_BLEN 6 -#define MXT_TOUCH_TCHTHR 7 -#define MXT_TOUCH_TCHDI 8 -#define MXT_TOUCH_ORIENT 9 -#define MXT_TOUCH_MOVHYSTI 11 -#define MXT_TOUCH_MOVHYSTN 12 -#define MXT_TOUCH_NUMTOUCH 14 -#define MXT_TOUCH_MRGHYST 15 -#define MXT_TOUCH_MRGTHR 16 -#define MXT_TOUCH_AMPHYST 17 -#define MXT_TOUCH_XRANGE_LSB 18 -#define MXT_TOUCH_XRANGE_MSB 19 -#define MXT_TOUCH_YRANGE_LSB 20 -#define MXT_TOUCH_YRANGE_MSB 21 -#define MXT_TOUCH_XLOCLIP 22 -#define MXT_TOUCH_XHICLIP 23 -#define MXT_TOUCH_YLOCLIP 24 -#define MXT_TOUCH_YHICLIP 25 -#define MXT_TOUCH_XEDGECTRL 26 -#define MXT_TOUCH_XEDGEDIST 27 -#define MXT_TOUCH_YEDGECTRL 28 -#define MXT_TOUCH_YEDGEDIST 29 -#define MXT_TOUCH_JUMPLIMIT 30 +#define MXT_T9_ORIENT 9 +#define MXT_T9_RANGE 18 + +struct t9_range { + u16 x; + u16 y; +} __packed; + +/* Touch orient bits */ +#define MXT_XY_SWITCH (1 << 0) /* MXT_PROCI_GRIPFACE_T20 field */ #define MXT_GRIPFACE_CTRL 0 @@ -215,11 +198,6 @@ #define MXT_PRESS (1 << 6) #define MXT_DETECT (1 << 7) -/* Touch orient bits */ -#define MXT_XY_SWITCH (1 << 0) -#define MXT_X_INVERT (1 << 1) -#define MXT_Y_INVERT (1 << 2) - /* Touchscreen absolute values */ #define MXT_MAX_AREA 0xff @@ -556,11 +534,6 @@ static int __mxt_read_reg(struct i2c_client *client, return ret; } -static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ - return __mxt_read_reg(client, reg, 1, val); -} - static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, const void *val) { @@ -1269,12 +1242,59 @@ static void mxt_free_object_table(struct mxt_data *data) data->T19_reportid = 0; } +static int mxt_read_t9_resolution(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct t9_range range; + unsigned char orient; + struct mxt_object *object; + + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_RANGE, + sizeof(range), &range); + if (error) + return error; + + le16_to_cpus(range.x); + le16_to_cpus(range.y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_ORIENT, + 1, &orient); + if (error) + return error; + + /* Handle default values */ + if (range.x == 0) + range.x = 1023; + + if (range.y == 0) + range.y = 1023; + + if (orient & MXT_XY_SWITCH) { + data->max_x = range.y; + data->max_y = range.x; + } else { + data->max_x = range.x; + data->max_y = range.y; + } + + dev_dbg(&client->dev, + "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; struct mxt_info *info = &data->info; int error; - u8 val; error = mxt_get_info(data); if (error) @@ -1303,26 +1323,16 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } - /* Update matrix size at info struct */ - error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); - if (error) - goto err_free_object_table; - info->matrix_xsize = val; - - error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val); - if (error) + error = mxt_read_t9_resolution(data); + if (error) { + dev_err(&client->dev, "Failed to initialize T9 resolution\n"); goto err_free_object_table; - info->matrix_ysize = val; - - dev_info(&client->dev, - "Family: %u Variant: %u Firmware V%u.%u.%02X\n", - info->family_id, info->variant_id, info->version >> 4, - info->version & 0xf, info->build); + } dev_info(&client->dev, - "Matrix X Size: %u Matrix Y Size: %u Objects: %u\n", - info->matrix_xsize, info->matrix_ysize, - info->object_num); + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", + info->family_id, info->variant_id, info->version >> 4, + info->version & 0xf, info->build, info->object_num); return 0; @@ -1331,20 +1341,6 @@ static int mxt_initialize(struct mxt_data *data) return error; } -static void mxt_calc_resolution(struct mxt_data *data) -{ - unsigned int max_x = data->pdata->x_size - 1; - unsigned int max_y = data->pdata->y_size - 1; - - if (data->pdata->orient & MXT_XY_SWITCH) { - data->max_x = max_y; - data->max_y = max_x; - } else { - data->max_x = max_x; - data->max_y = max_y; - } -} - /* Firmware Version is returned as Major.Minor.Build */ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1670,8 +1666,6 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->reset_completion); init_completion(&data->crc_completion); - mxt_calc_resolution(data); - error = mxt_initialize(data); if (error) goto err_free_mem; diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index b569bb876d18e4..02bf6ea317015c 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -15,22 +15,8 @@ #include -/* Orient */ -#define MXT_NORMAL 0x0 -#define MXT_DIAGONAL 0x1 -#define MXT_HORIZONTAL_FLIP 0x2 -#define MXT_ROTATED_90_COUNTER 0x3 -#define MXT_VERTICAL_FLIP 0x4 -#define MXT_ROTATED_90 0x5 -#define MXT_ROTATED_180 0x6 -#define MXT_DIAGONAL_COUNTER 0x7 - /* The platform data for the Atmel maXTouch touchscreen driver */ struct mxt_platform_data { - unsigned int x_size; - unsigned int y_size; - unsigned char orient; - unsigned long irqflags; u8 t19_num_keys; const unsigned int *t19_keymap; From 8080efdf9f7926e1662e50a8863f1a54a879f680 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:26:50 -0700 Subject: [PATCH 020/154] Input: atmel_mxt_ts - use deep sleep mode when stopped By writing zero to both the active and idle cycle times the maXTouch device is put into a deep sleep mode when it consumes minimal power. It is unnecessary to change the configuration of any other objects (for example to disable T9 touchscreen). It is counterproductive to reset the chip on resume, it will result in a long delay. However it is necessary to issue a calibrate command after the chip has spent any time in deep sleep. 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. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 99 +++++++++++++++++------- 1 file changed, 73 insertions(+), 26 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 25fecf3a647c08..092037bc93bffb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -89,9 +89,13 @@ #define MXT_T6_STATUS_RESET (1 << 7) /* 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 @@ -103,7 +107,6 @@ #define MXT_ACQUIRE_ATCHCALSTHR 7 /* MXT_TOUCH_MULTI_T9 field */ -#define MXT_TOUCH_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -242,6 +245,7 @@ struct mxt_data { u32 config_crc; u32 info_crc; u8 bootloader_addr; + struct t7_config t7_cfg; /* Cached parameters from object table */ u8 T6_reportid; @@ -600,20 +604,6 @@ static int mxt_read_message(struct mxt_data *data, sizeof(struct mxt_message), message); } -static int mxt_write_object(struct mxt_data *data, - u8 type, u8 offset, u8 val) -{ - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, type); - if (!object || offset >= mxt_obj_size(object)) - return -EINVAL; - - reg = object->start_address; - return mxt_write_reg(data->client, reg + offset, val); -} - static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) { struct input_dev *input = data->input_dev; @@ -1129,6 +1119,60 @@ static int mxt_check_reg_init(struct mxt_data *data) return ret; } +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_make_highchg(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -1323,6 +1367,12 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } + error = mxt_init_t7_power_cfg(data); + if (error) { + dev_err(&client->dev, "Failed to initialize power cfg\n"); + goto err_free_object_table; + } + error = mxt_read_t9_resolution(data); if (error) { dev_err(&client->dev, "Failed to initialize T9 resolution\n"); @@ -1596,16 +1646,15 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - /* Touch enable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + 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); } static void mxt_stop(struct mxt_data *data) { - /* Touch disable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); } static int mxt_input_open(struct input_dev *dev) @@ -1796,8 +1845,6 @@ static int 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) From 44558141ec624e0bf4e802d130fab3dd5fc3dac1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:29:37 -0700 Subject: [PATCH 021/154] Input: atmel_mxt_ts - rename pressure to amplitude to match spec Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 092037bc93bffb..d87fc0fb33d641 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -629,7 +629,7 @@ static void mxt_input_touchevent(struct mxt_data *data, int x; int y; int area; - int pressure; + int amplitude; x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); y = (message->message[2] << 4) | ((message->message[3] & 0xf)); @@ -639,7 +639,7 @@ static void mxt_input_touchevent(struct mxt_data *data, y = y >> 2; area = message->message[4]; - pressure = message->message[5]; + amplitude = message->message[5]; dev_dbg(dev, "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", @@ -652,7 +652,7 @@ static void mxt_input_touchevent(struct mxt_data *data, (status & MXT_AMP) ? 'A' : '.', (status & MXT_SUPPRESS) ? 'S' : '.', (status & MXT_UNGRIP) ? 'U' : '.', - x, y, area, pressure); + x, y, area, amplitude); input_mt_slot(input_dev, id); input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, @@ -661,7 +661,7 @@ static void mxt_input_touchevent(struct mxt_data *data, if (status & MXT_DETECT) { input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); - input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); } } From 055157849caf1b00d6399acfa7a44ce8da9b6eaf Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:11 -0700 Subject: [PATCH 022/154] Input: atmel_mxt_ts - rename touchscreen defines to include T9 This avoids confusion with the newer T100 touchscreen object. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d87fc0fb33d641..fe575afcfe903d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -110,13 +110,23 @@ struct t7_config { #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP (1 << 0) +#define MXT_T9_SUPPRESS (1 << 1) +#define MXT_T9_AMP (1 << 2) +#define MXT_T9_VECTOR (1 << 3) +#define MXT_T9_MOVE (1 << 4) +#define MXT_T9_RELEASE (1 << 5) +#define MXT_T9_PRESS (1 << 6) +#define MXT_T9_DETECT (1 << 7) + struct t9_range { u16 x; u16 y; } __packed; -/* Touch orient bits */ -#define MXT_XY_SWITCH (1 << 0) +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH (1 << 0) /* MXT_PROCI_GRIPFACE_T20 field */ #define MXT_GRIPFACE_CTRL 0 @@ -191,16 +201,6 @@ struct t9_range { #define MXT_BOOT_EXTENDED_ID (1 << 5) #define MXT_BOOT_ID_MASK 0x1f -/* Touch status */ -#define MXT_UNGRIP (1 << 0) -#define MXT_SUPPRESS (1 << 1) -#define MXT_AMP (1 << 2) -#define MXT_VECTOR (1 << 3) -#define MXT_MOVE (1 << 4) -#define MXT_RELEASE (1 << 5) -#define MXT_PRESS (1 << 6) -#define MXT_DETECT (1 << 7) - /* Touchscreen absolute values */ #define MXT_MAX_AREA 0xff @@ -644,21 +644,21 @@ static void mxt_input_touchevent(struct mxt_data *data, dev_dbg(dev, "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", id, - (status & MXT_DETECT) ? 'D' : '.', - (status & MXT_PRESS) ? 'P' : '.', - (status & MXT_RELEASE) ? 'R' : '.', - (status & MXT_MOVE) ? 'M' : '.', - (status & MXT_VECTOR) ? 'V' : '.', - (status & MXT_AMP) ? 'A' : '.', - (status & MXT_SUPPRESS) ? 'S' : '.', - (status & MXT_UNGRIP) ? 'U' : '.', + (status & MXT_T9_DETECT) ? 'D' : '.', + (status & MXT_T9_PRESS) ? 'P' : '.', + (status & MXT_T9_RELEASE) ? 'R' : '.', + (status & MXT_T9_MOVE) ? 'M' : '.', + (status & MXT_T9_VECTOR) ? 'V' : '.', + (status & MXT_T9_AMP) ? 'A' : '.', + (status & MXT_T9_SUPPRESS) ? 'S' : '.', + (status & MXT_T9_UNGRIP) ? 'U' : '.', x, y, area, amplitude); input_mt_slot(input_dev, id); input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, - status & MXT_DETECT); + status & MXT_T9_DETECT); - if (status & MXT_DETECT) { + if (status & MXT_T9_DETECT) { input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); @@ -1320,7 +1320,7 @@ static int mxt_read_t9_resolution(struct mxt_data *data) if (range.y == 0) range.y = 1023; - if (orient & MXT_XY_SWITCH) { + if (orient & MXT_T9_ORIENT_SWITCH) { data->max_x = range.y; data->max_y = range.x; } else { From 1316ad433374828fa929954b5080f39f8515e720 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:47 -0700 Subject: [PATCH 023/154] Input: atmel_mxt_ts - handle multiple input reports in one message Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 36 ++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fe575afcfe903d..cec9b39efea740 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -620,6 +620,12 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) } } +static void mxt_input_sync(struct input_dev *input_dev) +{ + input_mt_report_pointer_emulation(input_dev, false); + input_sync(input_dev); +} + static void mxt_input_touchevent(struct mxt_data *data, struct mxt_message *message, int id) { @@ -633,10 +639,12 @@ static void mxt_input_touchevent(struct mxt_data *data, x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + + /* Handle 10/12 bit switching */ if (data->max_x < 1024) - x = x >> 2; + x >>= 2; if (data->max_y < 1024) - y = y >> 2; + y >>= 2; area = message->message[4]; amplitude = message->message[5]; @@ -655,14 +663,28 @@ static void mxt_input_touchevent(struct mxt_data *data, x, y, area, amplitude); input_mt_slot(input_dev, id); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, - status & MXT_T9_DETECT); if (status & MXT_T9_DETECT) { + /* + * Multiple bits may be set if the host is slow to read + * the status messages, indicating all the events that + * have happened. + */ + if (status & MXT_T9_RELEASE) { + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, 0); + mxt_input_sync(input_dev); + } + + /* Touch active */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); } } @@ -720,10 +742,8 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) } } while (reportid != 0xff); - if (update_input) { - input_mt_report_pointer_emulation(data->input_dev, false); - input_sync(data->input_dev); - } + if (update_input) + mxt_input_sync(data->input_dev); return IRQ_HANDLED; } From 9f96ab77c6db0c82005eca888990a805653937bb Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 20 Feb 2014 16:35:49 +0000 Subject: [PATCH 024/154] Input: atmel_mxt_ts - add Atmel copyright line Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index cec9b39efea740..7e8acf3899438c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,7 @@ * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. * * Author: Joonyoung Shim From 4c69d6e569f50f37c49c5d44408e3b36811ecb01 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 16:33:43 +0000 Subject: [PATCH 025/154] Input: atmel_mxt_ts - initialise IRQ before probing The maXTouch chips use the CHG line to generate status events in bootloader mode, and during configuration download, before there is enough information to configure the input device. Therefore set up the interrupt handler earlier. However, this introduces states where parts of the interrupt processing must not run. Use data->object_table as a way to tell whether the chip information is valid, and data->input_dev as a way to tell whether it is valid to generate input report. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 101 ++++++++++++++--------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7e8acf3899438c..afc812c5036dd7 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -731,6 +731,12 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); + } else if (!data->input_dev) { + /* + * do not report events if input device + * is not yet registered + */ + mxt_dump_message(dev, &message); } else if (mxt_is_T9_message(data, &message)) { int id = reportid - data->T9_reportid_min; mxt_input_touchevent(data, &message, id); @@ -759,6 +765,9 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } + if (!data->object_table) + return IRQ_HANDLED; + return mxt_process_messages_until_invalid(data); } @@ -1216,6 +1225,19 @@ static int mxt_make_highchg(struct mxt_data *data) return 0; } +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + error = mxt_make_highchg(data); + if (error) + return error; + + return 0; +} + static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -1234,22 +1256,31 @@ static int mxt_get_object_table(struct mxt_data *data) { struct i2c_client *client = data->client; size_t table_size; + struct mxt_object *object_table; int error; int i; u8 reportid; u16 end_address; table_size = data->info.object_num * sizeof(struct mxt_object); + object_table = kzalloc(table_size, GFP_KERNEL); + if (!object_table) { + dev_err(&data->client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, - data->object_table); - if (error) + object_table); + if (error) { + kfree(object_table); return error; + } /* Valid Report IDs start counting from 1 */ reportid = 1; data->mem_size = 0; for (i = 0; i < data->info.object_num; i++) { - struct mxt_object *object = data->object_table + i; + struct mxt_object *object = object_table + i; u8 min_id, max_id; le16_to_cpus(&object->start_address); @@ -1294,6 +1325,8 @@ static int mxt_get_object_table(struct mxt_data *data) data->mem_size = end_address + 1; } + data->object_table = object_table; + return 0; } @@ -1365,21 +1398,17 @@ static int mxt_initialize(struct mxt_data *data) if (error) return error; - data->object_table = kcalloc(info->object_num, - sizeof(struct mxt_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - /* Get object table information */ error = mxt_get_object_table(data); if (error) { dev_err(&client->dev, "Error %d reading object table\n", error); - goto err_free_object_table; + return error; } + mxt_acquire_irq(data); + if (error) + goto err_free_object_table; + /* Check register init values */ error = mxt_check_reg_init(data); if (error) { @@ -1636,11 +1665,7 @@ static ssize_t mxt_update_fw_store(struct device *dev, mxt_free_object_table(data); - mxt_initialize(data); - - enable_irq(data->irq); - - error = mxt_make_highchg(data); + error = mxt_initialize(data); if (error) return error; } @@ -1736,9 +1761,26 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->reset_completion); init_completion(&data->crc_completion); + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, + pdata->irqflags | IRQF_ONESHOT, + client->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + disable_irq(client->irq); + + error = input_register_device(input_dev); + if (error) { + dev_err(&client->dev, "Error %d registering input device\n", + error); + goto err_free_irq; + } + error = mxt_initialize(data); if (error) - goto err_free_mem; + goto err_unregister_device; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); @@ -1789,25 +1831,6 @@ static int mxt_probe(struct i2c_client *client, input_set_drvdata(input_dev, data); i2c_set_clientdata(client, data); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags | IRQF_ONESHOT, - client->name, data); - if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; - } - - error = mxt_make_highchg(data); - if (error) - goto err_free_irq; - - error = input_register_device(input_dev); - if (error) { - dev_err(&client->dev, "Error %d registering input device\n", - 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", @@ -1820,10 +1843,10 @@ static int mxt_probe(struct i2c_client *client, err_unregister_device: input_unregister_device(input_dev); input_dev = NULL; -err_free_irq: - free_irq(client->irq, data); err_free_object: kfree(data->object_table); +err_free_irq: + free_irq(client->irq, data); err_free_mem: input_free_device(input_dev); kfree(data); From 5076b2a8d1691d4353d8b0d72da60441b07c2975 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 4 Dec 2012 17:16:35 +0000 Subject: [PATCH 026/154] Input: atmel_mxt_ts - move input device init into separate function It is possible for the maXTouch chip to start up in bootloader mode, where it does not have a working touchscreen but the driver can still handle updating the firmware. This means that the touchscreen initialisation must be split into a separate function so it can be called after bootloading has completed. In addition, later devices have a different touchscreen object (T100) which requires handling differently. This also reduces the complexity of the probe function. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 126 +++++++++++++---------- 1 file changed, 74 insertions(+), 52 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index afc812c5036dd7..098cac64eb66fc 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1719,69 +1719,29 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxt_initialize_t9_input_device(struct mxt_data *data) { - const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); - struct mxt_data *data; + struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; struct input_dev *input_dev; int error; unsigned int num_mt_slots; unsigned int mt_flags = 0; int i; - if (!pdata) - return -EINVAL; - - data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; } input_dev->name = "Atmel maXTouch Touchscreen"; - snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", - client->adapter->nr, client->addr); - input_dev->phys = data->phys; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; + input_dev->dev.parent = dev; input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; - data->client = client; - data->input_dev = input_dev; - data->pdata = pdata; - data->irq = client->irq; - - init_completion(&data->bl_completion); - init_completion(&data->reset_completion); - init_completion(&data->crc_completion); - - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags | IRQF_ONESHOT, - client->name, data); - if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; - } - - disable_irq(client->irq); - - error = input_register_device(input_dev); - if (error) { - dev_err(&client->dev, "Error %d registering input device\n", - error); - goto err_free_irq; - } - - error = mxt_initialize(data); - if (error) - goto err_unregister_device; - __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); @@ -1817,8 +1777,11 @@ static int mxt_probe(struct i2c_client *client, /* For multi touch */ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); - if (error) - goto err_free_object; + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, @@ -1829,8 +1792,68 @@ static int mxt_probe(struct i2c_client *client, 0, 255, 0, 0); input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxt_data *data; + const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); + int error; + + if (!pdata) + return -EINVAL; + + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", + client->adapter->nr, client->addr); + + data->client = client; + data->pdata = pdata; + data->irq = client->irq; i2c_set_clientdata(client, data); + init_completion(&data->bl_completion); + init_completion(&data->reset_completion); + init_completion(&data->crc_completion); + + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, + pdata->irqflags | IRQF_ONESHOT, + client->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + disable_irq(client->irq); + + error = mxt_initialize(data); + if (error) + goto err_free_irq; + + error = mxt_initialize_t9_input_device(data); + if (error) + goto err_free_object; + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { dev_err(&client->dev, "Failure %d creating sysfs group\n", @@ -1841,14 +1864,13 @@ static int mxt_probe(struct i2c_client *client, return 0; err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; + input_unregister_device(data->input_dev); + data->input_dev = NULL; err_free_object: kfree(data->object_table); err_free_irq: free_irq(client->irq, data); err_free_mem: - input_free_device(input_dev); kfree(data); return error; } From 9173c0400f1b52678eb7b4810d6c00ecf3e8672f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:21:08 +0000 Subject: [PATCH 027/154] Input: atmel_mxt_ts - handle APP_CRC_FAIL on startup If the bootloader fails to start the appmode image on the touch controller, it stays in bootloader mode. It is possible to reflash a working firmware image from this state. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 61 +++++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 098cac64eb66fc..3740a97d9dfb58 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -407,6 +407,30 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) return 0; } +static int mxt_probe_bootloader(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + u8 val; + bool crc_failure; + + ret = mxt_lookup_bootloader_address(data); + if (ret) + return ret; + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) { struct device *dev = &data->client->dev; @@ -466,6 +490,7 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: @@ -1395,8 +1420,14 @@ static int mxt_initialize(struct mxt_data *data) int error; error = mxt_get_info(data); - if (error) - return error; + if (error) { + error = mxt_probe_bootloader(data); + if (error) + return error; + + data->in_bootloader = true; + return 0; + } /* Get object table information */ error = mxt_get_object_table(data); @@ -1570,15 +1601,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; - /* Change to the bootloader mode */ - data->in_bootloader = true; + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; - ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; - msleep(MXT_RESET_TIME); + msleep(MXT_RESET_TIME); + } + mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); @@ -1663,8 +1698,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, } else { dev_info(dev, "The firmware update succeeded\n"); - mxt_free_object_table(data); - error = mxt_initialize(data); if (error) return error; @@ -1850,9 +1883,11 @@ static int mxt_probe(struct i2c_client *client, if (error) goto err_free_irq; - error = mxt_initialize_t9_input_device(data); - if (error) - goto err_free_object; + if (!data->in_bootloader) { + error = mxt_initialize_t9_input_device(data); + if (error) + goto err_free_object; + } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { From 649c8d377b2cb31b5f2a1d548cad9dfd239af4e0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:07:02 +0000 Subject: [PATCH 028/154] Input: atmel_mxt_ts - handle bootloader previously unlocked Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 3740a97d9dfb58..25e63600477352 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -452,14 +452,15 @@ static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) } } -static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) { struct device *dev = &data->client->dev; u8 val; int ret; recheck: - if (state != MXT_WAITING_BOOTLOAD_CMD) { + if (wait) { /* * In application update mode, the interrupt * line signals state transitions. We must wait for the @@ -1616,15 +1617,23 @@ static int mxt_load_fw(struct device *dev, const char *fn) mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); - if (ret) - goto disable_irq; + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); - /* Unlock bootloader */ - mxt_unlock_bootloader(data); + /* Unlock bootloader */ + ret = mxt_unlock_bootloader(data); + if (ret) + goto disable_irq; + } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); if (ret) goto disable_irq; @@ -1638,7 +1647,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto disable_irq; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); if (ret) { retry++; From 27443634531bbd536223286f093e0dfc0a1db503 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 3 Sep 2012 15:46:22 +0100 Subject: [PATCH 029/154] Input: atmel_mxt_ts - add bootloader addresses for new chips Later chips (for example mXT1664S) different mappings for bootloader addresses. This means that we must look at the family ID to determine which address to use. There is an additional complication: when we probe and we don't know the family ID yet, we need to try both possible addresses to find the bootloader. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- 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 25e63600477352..db44f3654e4fdb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -390,6 +390,12 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) switch (appmode) { case 0x4a: case 0x4b: + /* Chips after 1664S use different scheme */ + if (data->info.family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + /* Fall through for normal case */ case 0x4c: case 0x4d: case 0x5a: From e576bd50fd484cd4430122d531d3e208b14097a3 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 15 Aug 2012 12:39:31 +0100 Subject: [PATCH 030/154] Input: atmel_mxt_ts - recover from bootloader on probe The MXT device may be in bootloader mode on probe, due to: 1) APP CRC failure, either: a) flash corruption b) bad power or other intermittent problem while checking CRC 2) If the device has been reset 10 or more times without accessing comms 3) Warm probe, device was in bootloader mode already This code attempts to recover from 1(b) and 3. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 69 ++++++++++++++++++------ 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index db44f3654e4fdb..bdba1210bd7c7f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -382,7 +382,7 @@ static int mxt_bootloader_write(struct mxt_data *data, return ret; } -static int mxt_lookup_bootloader_address(struct mxt_data *data) +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) { u8 appmode = data->client->addr; u8 bootloader; @@ -391,7 +391,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) case 0x4a: case 0x4b: /* Chips after 1664S use different scheme */ - if (data->info.family_id >= 0xa2) { + if (retry || data->info.family_id >= 0xa2) { bootloader = appmode - 0x24; break; } @@ -413,14 +413,14 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) return 0; } -static int mxt_probe_bootloader(struct mxt_data *data) +static int mxt_probe_bootloader(struct mxt_data *data, bool retry) { struct device *dev = &data->client->dev; int ret; u8 val; bool crc_failure; - ret = mxt_lookup_bootloader_address(data); + ret = mxt_lookup_bootloader_address(data, retry); if (ret) return ret; @@ -521,13 +521,18 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, return 0; } -static int mxt_unlock_bootloader(struct mxt_data *data) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) { int ret; u8 buf[2]; - buf[0] = MXT_UNLOCK_CMD_LSB; - buf[1] = MXT_UNLOCK_CMD_MSB; + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; + } ret = mxt_bootloader_write(data, buf, 2); if (ret) @@ -1425,15 +1430,42 @@ static int mxt_initialize(struct mxt_data *data) struct i2c_client *client = data->client; struct mxt_info *info = &data->info; int error; + bool alt_bootloader_addr = false; + bool retry = false; +retry_info: error = mxt_get_info(data); if (error) { - error = mxt_probe_bootloader(data); - if (error) - return error; +retry_bootloader: + error = mxt_probe_bootloader(data, alt_bootloader_addr); + if (error) { + if (alt_bootloader_addr) { + /* Chip is not in appmode or bootloader mode */ + return error; + } - data->in_bootloader = true; - return 0; + dev_info(&client->dev, "Trying alternate bootloader address\n"); + alt_bootloader_addr = true; + goto retry_bootloader; + } else { + if (retry) { + dev_err(&client->dev, + "Could not recover device from " + "bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort init + */ + data->in_bootloader = true; + return 0; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + retry = true; + goto retry_info; + } } /* Get object table information */ @@ -1604,10 +1636,6 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; - ret = mxt_lookup_bootloader_address(data); - if (ret) - goto release_firmware; - if (!data->in_bootloader) { /* Change to the bootloader mode */ data->in_bootloader = true; @@ -1618,6 +1646,13 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto release_firmware; msleep(MXT_RESET_TIME); + + /* Do not need to scan since we know family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + } else { + enable_irq(data->irq); } mxt_free_object_table(data); @@ -1633,7 +1668,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) dev_info(dev, "Unlocking bootloader\n"); /* Unlock bootloader */ - ret = mxt_unlock_bootloader(data); + ret = mxt_send_bootloader_cmd(data, true); if (ret) goto disable_irq; } From 59b544ecfd2bdfd5bcb8402d0ca82765f67e5aec Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 11:56:14 +0000 Subject: [PATCH 031/154] Input: atmel_mxt_ts - add support for dynamic message size The T5 object may have various sizes depending on the objects used on the particular maXTouch chip and firmware version, therefore it can't be hardcoded in the driver. Allocate a buffer on probe instead. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 114 +++++++++++++---------- 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index bdba1210bd7c7f..0c9fc6953cba83 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -79,6 +79,9 @@ #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + /* MXT_GEN_COMMAND_T6 field */ #define MXT_COMMAND_RESET 0 #define MXT_COMMAND_BACKUPNV 1 @@ -225,11 +228,6 @@ struct mxt_object { u8 num_report_ids; } __packed; -struct mxt_message { - u8 reportid; - u8 message[7]; -}; - /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -247,8 +245,10 @@ struct mxt_data { u32 info_crc; u8 bootloader_addr; struct t7_config t7_cfg; + u8 *msg_buf; /* Cached parameters from object table */ + u8 T5_msg_size; u8 T6_reportid; u16 T6_address; u16 T7_address; @@ -309,11 +309,10 @@ static bool mxt_object_readable(unsigned int type) } } -static void mxt_dump_message(struct device *dev, - struct mxt_message *message) +static void mxt_dump_message(struct mxt_data *data, u8 *message) { - dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", - message->reportid, 7, message->message); + dev_dbg(&data->client->dev, "message: %*ph\n", + data->T5_msg_size, message); } static int mxt_wait_for_completion(struct mxt_data *data, @@ -627,8 +626,7 @@ mxt_get_object(struct mxt_data *data, u8 type) return NULL; } -static int mxt_read_message(struct mxt_data *data, - struct mxt_message *message) +static int mxt_read_message(struct mxt_data *data, u8 *message) { struct mxt_object *object; u16 reg; @@ -639,10 +637,10 @@ static int mxt_read_message(struct mxt_data *data, reg = object->start_address; return __mxt_read_reg(data->client, reg, - sizeof(struct mxt_message), message); + data->T5_msg_size, message); } -static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; @@ -653,7 +651,7 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & (1 << i)); + button = !(message[1] & (1 << i)); input_report_key(input, pdata->t19_keymap[i], button); } } @@ -664,19 +662,21 @@ static void mxt_input_sync(struct input_dev *input_dev) input_sync(input_dev); } -static void mxt_input_touchevent(struct mxt_data *data, - struct mxt_message *message, int id) +static void mxt_input_touchevent(struct mxt_data *data, u8 *message) { struct device *dev = &data->client->dev; - u8 status = message->message[0]; struct input_dev *input_dev = data->input_dev; + int id; + u8 status; int x; int y; int area; int amplitude; - x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); - y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); /* Handle 10/12 bit switching */ if (data->max_x < 1024) @@ -684,8 +684,8 @@ static void mxt_input_touchevent(struct mxt_data *data, if (data->max_y < 1024) y >>= 2; - area = message->message[4]; - amplitude = message->message[5]; + area = message[5]; + amplitude = message[6]; dev_dbg(dev, "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", @@ -731,28 +731,28 @@ static u16 mxt_extract_T6_csum(const u8 *csum) return csum[0] | (csum[1] << 8) | (csum[2] << 16); } -static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) +static bool mxt_is_T9_message(struct mxt_data *data, u8 *msg) { - u8 id = msg->reportid; + u8 id = msg[0]; return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); } static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) { - struct mxt_message message; - const u8 *payload = &message.message[0]; + u8 *message = &data->msg_buf[0]; + const u8 *payload = &data->msg_buf[1]; struct device *dev = &data->client->dev; u8 reportid; bool update_input = false; u32 crc; do { - if (mxt_read_message(data, &message)) { + if (mxt_read_message(data, message)) { dev_err(dev, "Failed to read message\n"); return IRQ_NONE; } - reportid = message.reportid; + reportid = message[0]; if (reportid == data->T6_reportid) { u8 status = payload[0]; @@ -773,18 +773,17 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) * do not report events if input device * is not yet registered */ - mxt_dump_message(dev, &message); - } else if (mxt_is_T9_message(data, &message)) { - int id = reportid - data->T9_reportid_min; - mxt_input_touchevent(data, &message, id); + mxt_dump_message(data, message); + } else if (mxt_is_T9_message(data, message)) { + mxt_input_touchevent(data, message); update_input = true; - } else if (message.reportid == data->T19_reportid) { - mxt_input_button(data, &message); + } else if (reportid == data->T19_reportid) { + mxt_input_button(data, message); update_input = true; } else { - mxt_dump_message(dev, &message); + mxt_dump_message(data, message); } - } while (reportid != 0xff); + } while (reportid != MXT_RPTID_NOMSG); if (update_input) mxt_input_sync(data->input_dev); @@ -1243,16 +1242,15 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data) static int mxt_make_highchg(struct mxt_data *data) { struct device *dev = &data->client->dev; - struct mxt_message message; int count = 10; int error; /* Read dummy message to make high CHG pin */ do { - error = mxt_read_message(data, &message); + error = mxt_read_message(data, data->msg_buf); if (error) return error; - } while (message.reportid != 0xff && --count); + } while (data->msg_buf[0] != MXT_RPTID_NOMSG && --count); if (!count) { dev_err(dev, "CHG pin isn't cleared\n"); @@ -1289,6 +1287,20 @@ static int mxt_get_info(struct mxt_data *data) return 0; } +static void mxt_free_object_table(struct mxt_data *data) +{ + kfree(data->object_table); + data->object_table = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T19_reportid = 0; +} + static int mxt_get_object_table(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -1339,6 +1351,9 @@ static int mxt_get_object_table(struct mxt_data *data) min_id, max_id); switch (object->type) { + case MXT_GEN_MESSAGE_T5: + /* CRC not enabled, therefore don't read last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; @@ -1362,19 +1377,20 @@ static int mxt_get_object_table(struct mxt_data *data) data->mem_size = end_address + 1; } + data->msg_buf = kzalloc(data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(&client->dev, "Failed to allocate message buffer\n"); + error = -ENOMEM; + goto free_object_table; + } + data->object_table = object_table; return 0; -} -static void mxt_free_object_table(struct mxt_data *data) -{ - kfree(data->object_table); - data->object_table = NULL; - data->T6_reportid = 0; - data->T9_reportid_min = 0; - data->T9_reportid_max = 0; - data->T19_reportid = 0; +free_object_table: + mxt_free_object_table(data); + return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -1952,7 +1968,7 @@ static int mxt_probe(struct i2c_client *client, input_unregister_device(data->input_dev); data->input_dev = NULL; err_free_object: - kfree(data->object_table); + mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); err_free_mem: @@ -1967,7 +1983,7 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); - kfree(data->object_table); + mxt_free_object_table(data); kfree(data); return 0; From 86f2ca1b5fecff7d1196f4d48c2f40b216a72611 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 12:12:16 +0000 Subject: [PATCH 032/154] Input: atmel_mxt_ts - decode T6 status messages By storing the previous T6 status byte we can detect reset completion more correctly, and multiple debug output of the same status can be suppressed (for example CFGERR). Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 60 ++++++++++++++++-------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0c9fc6953cba83..4ccb5ebc044e08 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -91,6 +91,11 @@ /* Define for T6 status byte */ #define MXT_T6_STATUS_RESET (1 << 7) +#define MXT_T6_STATUS_OFL (1 << 6) +#define MXT_T6_STATUS_SIGERR (1 << 5) +#define MXT_T6_STATUS_CAL (1 << 4) +#define MXT_T6_STATUS_CFGERR (1 << 3) +#define MXT_T6_STATUS_COMSERR (1 << 2) /* MXT_GEN_POWER_T7 field */ struct t7_config { @@ -246,6 +251,7 @@ struct mxt_data { u8 bootloader_addr; struct t7_config t7_cfg; u8 *msg_buf; + u8 t6_status; /* Cached parameters from object table */ u8 T5_msg_size; @@ -626,6 +632,39 @@ mxt_get_object(struct mxt_data *data, u8 type) return NULL; } +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); + + complete(&data->crc_completion); + + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + } + + /* Detect reset */ + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + status == 0 ? " OK" : "", + status & MXT_T6_STATUS_RESET ? " RESET" : "", + status & MXT_T6_STATUS_OFL ? " OFL" : "", + status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", + status & MXT_T6_STATUS_CAL ? " CAL" : "", + status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", + status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; +} + static int mxt_read_message(struct mxt_data *data, u8 *message) { struct mxt_object *object; @@ -726,11 +765,6 @@ static void mxt_input_touchevent(struct mxt_data *data, u8 *message) } } -static u16 mxt_extract_T6_csum(const u8 *csum) -{ - return csum[0] | (csum[1] << 8) | (csum[2] << 16); -} - static bool mxt_is_T9_message(struct mxt_data *data, u8 *msg) { u8 id = msg[0]; @@ -740,11 +774,9 @@ static bool mxt_is_T9_message(struct mxt_data *data, u8 *msg) static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) { u8 *message = &data->msg_buf[0]; - const u8 *payload = &data->msg_buf[1]; struct device *dev = &data->client->dev; u8 reportid; bool update_input = false; - u32 crc; do { if (mxt_read_message(data, message)) { @@ -755,19 +787,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) reportid = message[0]; if (reportid == data->T6_reportid) { - u8 status = payload[0]; - - crc = mxt_extract_T6_csum(&payload[1]); - if (crc != data->config_crc) { - data->config_crc = crc; - complete(&data->crc_completion); - } - - dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, data->config_crc); - - if (status & MXT_T6_STATUS_RESET) - complete(&data->reset_completion); + mxt_proc_t6_messages(data, message); } else if (!data->input_dev) { /* * do not report events if input device From d81c60b6c5d88e1a72316248f40855328f30ec0d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 24 Feb 2014 14:35:23 +0000 Subject: [PATCH 033/154] Input: atmel_mxt_ts - split message handler into separate functions This is in preparation for support of the T44 message count object. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 123 ++++++++++++----------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4ccb5ebc044e08..a2b7bfe90e5bf1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -252,8 +252,10 @@ struct mxt_data { struct t7_config t7_cfg; u8 *msg_buf; u8 t6_status; + bool update_input; /* Cached parameters from object table */ + u16 T5_address; u8 T5_msg_size; u8 T6_reportid; u16 T6_address; @@ -665,20 +667,6 @@ static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) data->t6_status = status; } -static int mxt_read_message(struct mxt_data *data, u8 *message) -{ - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __mxt_read_reg(data->client, reg, - data->T5_msg_size, message); -} - static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; @@ -701,7 +689,7 @@ static void mxt_input_sync(struct input_dev *input_dev) input_sync(input_dev); } -static void mxt_input_touchevent(struct mxt_data *data, u8 *message) +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) { struct device *dev = &data->client->dev; struct input_dev *input_dev = data->input_dev; @@ -763,50 +751,67 @@ static void mxt_input_touchevent(struct mxt_data *data, u8 *message) /* Touch no longer active, close out slot */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); } + + data->update_input = true; } -static bool mxt_is_T9_message(struct mxt_data *data, u8 *msg) +static int mxt_proc_message(struct mxt_data *data, u8 *message) { - u8 id = msg[0]; - return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); + u8 report_id = message[0]; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (!data->input_dev) { + /* + * do not report events if input device + * is not yet registered + */ + mxt_dump_message(data, message); + } else if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else { + mxt_dump_message(data, message); + } + + return 1; } -static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +static int mxt_read_and_process_message(struct mxt_data *data) { - u8 *message = &data->msg_buf[0]; struct device *dev = &data->client->dev; - u8 reportid; - bool update_input = false; + int ret; - do { - if (mxt_read_message(data, message)) { - dev_err(dev, "Failed to read message\n"); - return IRQ_NONE; - } + ret = __mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size, data->msg_buf); + if (ret) { + dev_err(dev, "Error %d reading message\n", ret); + return ret; + } - reportid = message[0]; + return mxt_proc_message(data, data->msg_buf); +} - if (reportid == data->T6_reportid) { - mxt_proc_t6_messages(data, message); - } else if (!data->input_dev) { - /* - * do not report events if input device - * is not yet registered - */ - mxt_dump_message(data, message); - } else if (mxt_is_T9_message(data, message)) { - mxt_input_touchevent(data, message); - update_input = true; - } else if (reportid == data->T19_reportid) { - mxt_input_button(data, message); - update_input = true; - } else { - mxt_dump_message(data, message); - } - } while (reportid != MXT_RPTID_NOMSG); +static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +{ + int ret; + + do { + ret = mxt_read_and_process_message(data); + if (ret < 0) + return IRQ_NONE; + } while (ret > 0); - if (update_input) + if (data->update_input) { mxt_input_sync(data->input_dev); + data->update_input = false; + } return IRQ_HANDLED; } @@ -1263,21 +1268,19 @@ static int mxt_make_highchg(struct mxt_data *data) { struct device *dev = &data->client->dev; int count = 10; - int error; + int ret; - /* Read dummy message to make high CHG pin */ + /* Read messages until we force an invalid */ do { - error = mxt_read_message(data, data->msg_buf); - if (error) - return error; - } while (data->msg_buf[0] != MXT_RPTID_NOMSG && --count); - - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; - } + ret = mxt_read_and_process_message(data); + if (ret == 0) + return 0; + else if (ret < 0) + return ret; + } while (--count); - return 0; + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; } static int mxt_acquire_irq(struct mxt_data *data) @@ -1313,6 +1316,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->object_table = NULL; kfree(data->msg_buf); data->msg_buf = NULL; + data->T5_address = 0; data->T5_msg_size = 0; data->T6_reportid = 0; data->T7_address = 0; @@ -1374,6 +1378,7 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_GEN_MESSAGE_T5: /* CRC not enabled, therefore don't read last byte */ data->T5_msg_size = mxt_obj_size(object) - 1; + data->T5_address = object->start_address; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; From c8fb7fae319bbbd22a41da3b9b7de97e8544545f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 15:02:43 +0000 Subject: [PATCH 034/154] Input: atmel_mxt_ts - implement T44 message handling maXTouch chips allow the reading of multiple messages in a single I2C transaction. The number of messages available to be read is given by the value in the T44 object which is located directly before the T5 object. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 191 +++++++++++++++++++---- 1 file changed, 159 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a2b7bfe90e5bf1..b004d00126a2d6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -246,6 +246,7 @@ struct mxt_data { unsigned int max_y; bool in_bootloader; u16 mem_size; + u8 max_reportid; u32 config_crc; u32 info_crc; u8 bootloader_addr; @@ -253,6 +254,8 @@ struct mxt_data { u8 *msg_buf; u8 t6_status; bool update_input; + u8 last_message_count; + u8 num_touchids; /* Cached parameters from object table */ u16 T5_address; @@ -263,6 +266,7 @@ struct mxt_data { u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + u16 T44_address; /* for fw update in bootloader */ struct completion bl_completion; @@ -783,30 +787,142 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) return 1; } -static int mxt_read_and_process_message(struct mxt_data *data) +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) { struct device *dev = &data->client->dev; int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + /* Process remaining messages if necessary */ ret = __mxt_read_reg(data->client, data->T5_address, - data->T5_msg_size, data->msg_buf); + data->T5_msg_size * count, data->msg_buf); if (ret) { - dev_err(dev, "Error %d reading message\n", ret); + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); return ret; } - return mxt_proc_message(data, data->msg_buf); + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; } -static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) { + struct device *dev = &data->client->dev; int ret; + u8 count, num_left; - do { - ret = mxt_read_and_process_message(data); + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + if (count == 0) { + dev_warn(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static int mxt_process_messages_until_invalid(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int count, read; + u8 tries = 2; + + count = data->max_reportid; + + /* Read messages until we force an invalid */ + do { + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); + + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} + +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; + + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* keep reading two msgs until one is invalid or reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) return IRQ_NONE; - } while (ret > 0); + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; if (data->update_input) { mxt_input_sync(data->input_dev); @@ -829,7 +945,11 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) if (!data->object_table) return IRQ_HANDLED; - return mxt_process_messages_until_invalid(data); + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } } static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, @@ -1264,32 +1384,13 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data) return 0; } -static int mxt_make_highchg(struct mxt_data *data) -{ - struct device *dev = &data->client->dev; - int count = 10; - int ret; - - /* Read messages until we force an invalid */ - do { - ret = mxt_read_and_process_message(data); - if (ret == 0) - return 0; - else if (ret < 0) - return ret; - } while (--count); - - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; -} - static int mxt_acquire_irq(struct mxt_data *data) { int error; enable_irq(data->irq); - error = mxt_make_highchg(data); + error = mxt_process_messages_until_invalid(data); if (error) return error; @@ -1323,6 +1424,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_min = 0; data->T9_reportid_max = 0; data->T19_reportid = 0; + data->T44_address = 0; + data->max_reportid = 0; } static int mxt_get_object_table(struct mxt_data *data) @@ -1376,8 +1479,16 @@ static int mxt_get_object_table(struct mxt_data *data) switch (object->type) { case MXT_GEN_MESSAGE_T5: - /* CRC not enabled, therefore don't read last byte */ - data->T5_msg_size = mxt_obj_size(object) - 1; + if (data->info.family_id == 0x80) { + /* + * On mXT224 read and discard unused CRC byte + * otherwise DMA reads are misaligned + */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } data->T5_address = object->start_address; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; @@ -1389,6 +1500,11 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; + data->num_touchids = object->num_report_ids + * mxt_obj_instances(object); + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; break; case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; @@ -1402,7 +1518,18 @@ static int mxt_get_object_table(struct mxt_data *data) data->mem_size = end_address + 1; } - data->msg_buf = kzalloc(data->T5_msg_size, GFP_KERNEL); + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + error = -EINVAL; + goto free_object_table; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); if (!data->msg_buf) { dev_err(&client->dev, "Failed to allocate message buffer\n"); error = -ENOMEM; From 519925e6c9e10bd02d0247ea5cb02dd908721f66 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:14 +0100 Subject: [PATCH 035/154] Input: atmel_mxt_ts - output status from T48 Noise Supression Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b004d00126a2d6..4ab5641cd9728b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -267,6 +267,7 @@ struct mxt_data { u8 T9_reportid_max; u8 T19_reportid; u16 T44_address; + u8 T48_reportid; /* for fw update in bootloader */ struct completion bl_completion; @@ -759,6 +760,24 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status, state; + + status = msg[1]; + state = msg[4]; + + dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", state, status, + status & 0x01 ? "FREQCHG " : "", + status & 0x02 ? "APXCHG " : "", + status & 0x04 ? "ALGOERR " : "", + status & 0x10 ? "STATCHG " : "", + status & 0x20 ? "NLVLCHG " : ""); + + return 0; +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -768,6 +787,8 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) if (report_id == data->T6_reportid) { mxt_proc_t6_messages(data, message); + } else if (report_id == data->T48_reportid) { + mxt_proc_t48_messages(data, message); } else if (!data->input_dev) { /* * do not report events if input device @@ -1425,6 +1446,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_max = 0; data->T19_reportid = 0; data->T44_address = 0; + data->T48_reportid = 0; data->max_reportid = 0; } @@ -1509,6 +1531,9 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; break; + case MXT_PROCG_NOISESUPPRESSION_T48: + data->T48_reportid = min_id; + break; } end_address = object->start_address From 75b744c45fe7006764dd703bbdbae32863e91593 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:38 +0100 Subject: [PATCH 036/154] Input: atmel_mxt_ts - output status from T42 Touch Suppression Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4ab5641cd9728b..66580b9774ab63 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -187,6 +187,9 @@ struct t9_range { #define MXT_RESET_VALUE 0x01 #define MXT_BACKUP_VALUE 0x55 +/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ +#define MXT_T42_MSG_TCHSUP (1 << 0) + /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ @@ -266,6 +269,8 @@ struct mxt_data { u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + u8 T42_reportid_min; + u8 T42_reportid_max; u16 T44_address; u8 T48_reportid; @@ -760,6 +765,17 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + if (status & MXT_T42_MSG_TCHSUP) + dev_info(dev, "T42 suppress\n"); + else + dev_info(dev, "T42 normal\n"); +} + static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) { struct device *dev = &data->client->dev; @@ -787,6 +803,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) if (report_id == data->T6_reportid) { mxt_proc_t6_messages(data, message); + } else if (report_id >= data->T42_reportid_min + && report_id <= data->T42_reportid_max) { + mxt_proc_t42_messages(data, message); } else if (report_id == data->T48_reportid) { mxt_proc_t48_messages(data, message); } else if (!data->input_dev) { @@ -1445,6 +1464,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_min = 0; data->T9_reportid_max = 0; data->T19_reportid = 0; + data->T42_reportid_min = 0; + data->T42_reportid_max = 0; data->T44_address = 0; data->T48_reportid = 0; data->max_reportid = 0; @@ -1525,6 +1546,10 @@ static int mxt_get_object_table(struct mxt_data *data) data->num_touchids = object->num_report_ids * mxt_obj_instances(object); break; + case MXT_PROCI_TOUCHSUPPRESSION_T42: + data->T42_reportid_min = min_id; + data->T42_reportid_max = max_id; + break; case MXT_SPT_MESSAGECOUNT_T44: data->T44_address = object->start_address; break; From 35df04b62139f5bfe535ff4ccca42f1469ceaf32 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 1 Jun 2012 17:55:28 -0700 Subject: [PATCH 037/154] Input: atmel_mxt_ts - implement vector/orientation support The atmel touch messages contain orientation information as a byte in a packed format which can be passed straight on to Android if the input device configuration is correct, see http://source.android.com/tech/input/touch-devices.html#touchorientationcalibration This requires vector reports to be enabled in maXTouch config (zero DISVECT bit in T9 CTRL field) Android converts the format in frameworks/base/services/input/Input.cpp, search for ORIENTATION_CALIBRATION_VECTOR. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 66580b9774ab63..3f9c9413682457 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -709,6 +709,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) int y; int area; int amplitude; + u8 vector; id = message[0] - data->T9_reportid_min; status = message[1]; @@ -723,9 +724,10 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) area = message[5]; amplitude = message[6]; + vector = message[7]; dev_dbg(dev, - "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", + "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n", id, (status & MXT_T9_DETECT) ? 'D' : '.', (status & MXT_T9_PRESS) ? 'P' : '.', @@ -735,7 +737,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) (status & MXT_T9_AMP) ? 'A' : '.', (status & MXT_T9_SUPPRESS) ? 'S' : '.', (status & MXT_T9_UNGRIP) ? 'U' : '.', - x, y, area, amplitude); + x, y, area, amplitude, vector); input_mt_slot(input_dev, id); @@ -757,6 +759,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); } else { /* Touch no longer active, close out slot */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); @@ -2091,6 +2094,8 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) 0, data->max_y, 0, 0); input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); input_set_drvdata(input_dev, data); From e68fba3acf88936a251be67d5d093c52662df7fc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 8 Aug 2012 18:55:13 +0100 Subject: [PATCH 038/154] Input: atmel_mxt_ts - implement I2C retries Some maXTouch chips (eg mXT1386) will not respond on the first I2C request when they are in a sleep state. It must be retried after a delay for the chip to wake up. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 3f9c9413682457..4136bc3e9091d0 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -197,6 +197,7 @@ struct t9_range { #define MXT_CRC_TIMEOUT 1000 /* msec */ #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ +#define MXT_WAKEUP_TIME 25 /* msec */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -564,6 +565,7 @@ static int __mxt_read_reg(struct i2c_client *client, struct i2c_msg xfer[2]; u8 buf[2]; int ret; + bool retry = false; buf[0] = reg & 0xff; buf[1] = (reg >> 8) & 0xff; @@ -580,17 +582,22 @@ static int __mxt_read_reg(struct i2c_client *client, xfer[1].len = len; xfer[1].buf = val; - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret == 2) { - ret = 0; - } else { - if (ret >= 0) - ret = -EIO; - dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", - __func__, ret); +retry_read: + ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret != ARRAY_SIZE(xfer)) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_read; + } else { + dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", + __func__, ret); + return -EIO; + } } - return ret; + return 0; } static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, @@ -599,6 +606,7 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, u8 *buf; size_t count; int ret; + bool retry = false; count = len + 2; buf = kmalloc(count, GFP_KERNEL); @@ -609,14 +617,21 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, buf[1] = (reg >> 8) & 0xff; memcpy(&buf[2], val, len); +retry_write: ret = i2c_master_send(client, buf, count); - if (ret == count) { - ret = 0; - } else { - if (ret >= 0) + if (ret != count) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_write; + } else { + dev_err(&client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); ret = -EIO; - dev_err(&client->dev, "%s: i2c send failed (%d)\n", - __func__, ret); + } + } else { + ret = 0; } kfree(buf); From 495bf888c3ebd008b2383e46059a308448c09019 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 16:13:49 +0100 Subject: [PATCH 039/154] Input: atmel_mxt_ts - implement T63 Active Stylus support Signed-off-by: Nick Dyer Acked-by: Benson Leung --- drivers/input/touchscreen/atmel_mxt_ts.c | 91 +++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4136bc3e9091d0..6aaad83f1b54ce 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -78,6 +78,7 @@ #define MXT_SPT_DIGITIZER_T43 43 #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +#define MXT_PROCI_ACTIVE_STYLUS_T63 63 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -190,6 +191,19 @@ struct t9_range { /* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ #define MXT_T42_MSG_TCHSUP (1 << 0) +/* T63 Stylus */ +#define MXT_T63_STYLUS_PRESS (1 << 0) +#define MXT_T63_STYLUS_RELEASE (1 << 1) +#define MXT_T63_STYLUS_MOVE (1 << 2) +#define MXT_T63_STYLUS_SUPPRESS (1 << 3) + +#define MXT_T63_STYLUS_DETECT (1 << 4) +#define MXT_T63_STYLUS_TIP (1 << 5) +#define MXT_T63_STYLUS_ERASER (1 << 6) +#define MXT_T63_STYLUS_BARREL (1 << 7) + +#define MXT_T63_STYLUS_PRESSURE_MASK 0x3F + /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ @@ -260,6 +274,7 @@ struct mxt_data { bool update_input; u8 last_message_count; u8 num_touchids; + u8 num_stylusids; /* Cached parameters from object table */ u16 T5_address; @@ -274,6 +289,8 @@ struct mxt_data { u8 T42_reportid_max; u16 T44_address; u8 T48_reportid; + u8 T63_reportid_min; + u8 T63_reportid_max; /* for fw update in bootloader */ struct completion bl_completion; @@ -812,6 +829,59 @@ static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) return 0; } +static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + u8 id; + u16 x, y; + u8 pressure; + + /* stylus slots come after touch slots */ + id = data->num_touchids + (msg[0] - data->T63_reportid_min); + + if (id < 0 || id > (data->num_touchids + data->num_stylusids)) { + dev_err(dev, "invalid stylus id %d, max slot is %d\n", + id, data->num_stylusids); + return; + } + + x = msg[3] | (msg[4] << 8); + y = msg[5] | (msg[6] << 8); + pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK; + + dev_dbg(dev, + "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n", + id, + msg[1] & MXT_T63_STYLUS_SUPPRESS ? 'S' : '.', + msg[1] & MXT_T63_STYLUS_MOVE ? 'M' : '.', + msg[1] & MXT_T63_STYLUS_RELEASE ? 'R' : '.', + msg[1] & MXT_T63_STYLUS_PRESS ? 'P' : '.', + x, y, pressure, + msg[2] & MXT_T63_STYLUS_BARREL ? 'B' : '.', + msg[2] & MXT_T63_STYLUS_ERASER ? 'E' : '.', + msg[2] & MXT_T63_STYLUS_TIP ? 'T' : '.', + msg[2] & MXT_T63_STYLUS_DETECT ? 'D' : '.'); + + input_mt_slot(input_dev, id); + + if (msg[2] & MXT_T63_STYLUS_DETECT) { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + } else { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); + } + + input_report_key(input_dev, BTN_STYLUS, + (msg[2] & MXT_T63_STYLUS_ERASER)); + input_report_key(input_dev, BTN_STYLUS2, + (msg[2] & MXT_T63_STYLUS_BARREL)); + + mxt_input_sync(input_dev); +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -838,6 +908,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; + } else if (report_id >= data->T63_reportid_min + && report_id <= data->T63_reportid_max) { + mxt_proc_t63_messages(data, message); } else { mxt_dump_message(data, message); } @@ -1486,6 +1559,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T42_reportid_max = 0; data->T44_address = 0; data->T48_reportid = 0; + data->T63_reportid_min = 0; + data->T63_reportid_max = 0; data->max_reportid = 0; } @@ -1577,6 +1652,12 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_PROCG_NOISESUPPRESSION_T48: data->T48_reportid = min_id; break; + case MXT_PROCI_ACTIVE_STYLUS_T63: + data->T63_reportid_min = min_id; + data->T63_reportid_max = max_id; + data->num_stylusids = object->num_report_ids + * mxt_obj_instances(object); + break; } end_address = object->start_address @@ -2094,7 +2175,7 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) 0, 255, 0, 0); /* For multi touch */ - num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + num_mt_slots = data->num_touchids + data->num_stylusids; error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { dev_err(dev, "Error %d initialising slots\n", error); @@ -2112,6 +2193,14 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 255, 0, 0); + /* For T63 active stylus */ + if (data->T63_reportid_min) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + input_set_drvdata(input_dev, data); error = input_register_device(input_dev); From ba10e0677efa4e8d6013b2bdfc29dce39fbb864c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 17:32:35 +0100 Subject: [PATCH 040/154] Input: atmel_mxt_ts - implement support for T15 Key Array There is a key array object in many maXTouch chips which allows some X/Y lines to be used as a key array. This patch maps them to a series of keys which may be configured in a platform data array. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 53 ++++++++++++++++++++++++ include/linux/i2c/atmel_mxt_ts.h | 2 + 2 files changed, 55 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6aaad83f1b54ce..4ec1bd548f672b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -275,6 +275,7 @@ struct mxt_data { u8 last_message_count; u8 num_touchids; u8 num_stylusids; + unsigned long t15_keystatus; /* Cached parameters from object table */ u16 T5_address; @@ -284,6 +285,8 @@ struct mxt_data { u16 T7_address; u8 T9_reportid_min; u8 T9_reportid_max; + u8 T15_reportid_min; + u8 T15_reportid_max; u8 T19_reportid; u8 T42_reportid_min; u8 T42_reportid_max; @@ -800,6 +803,38 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) +{ + struct input_dev *input_dev = data->input_dev; + struct device *dev = &data->client->dev; + int key; + bool curr_state, new_state; + bool sync = false; + unsigned long keystates = le32_to_cpu(msg[2]); + + for (key = 0; key < data->pdata->t15_num_keys; key++) { + curr_state = test_bit(key, &data->t15_keystatus); + new_state = test_bit(key, &keystates); + + if (!curr_state && new_state) { + dev_dbg(dev, "T15 key press: %u\n", key); + __set_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 1); + sync = true; + } else if (curr_state && !new_state) { + dev_dbg(dev, "T15 key release: %u\n", key); + __clear_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 0); + sync = true; + } + } + + if (sync) + input_sync(input_dev); +} + static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg) { struct device *dev = &data->client->dev; @@ -911,6 +946,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) } else if (report_id >= data->T63_reportid_min && report_id <= data->T63_reportid_max) { mxt_proc_t63_messages(data, message); + } else if (report_id >= data->T15_reportid_min + && report_id <= data->T15_reportid_max) { + mxt_proc_t15_messages(data, message); } else { mxt_dump_message(data, message); } @@ -1554,6 +1592,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T7_address = 0; data->T9_reportid_min = 0; data->T9_reportid_max = 0; + data->T15_reportid_min = 0; + data->T15_reportid_max = 0; data->T19_reportid = 0; data->T42_reportid_min = 0; data->T42_reportid_max = 0; @@ -1639,6 +1679,10 @@ static int mxt_get_object_table(struct mxt_data *data) data->num_touchids = object->num_report_ids * mxt_obj_instances(object); break; + case MXT_TOUCH_KEYARRAY_T15: + data->T15_reportid_min = min_id; + data->T15_reportid_max = max_id; + break; case MXT_PROCI_TOUCHSUPPRESSION_T42: data->T42_reportid_min = min_id; data->T42_reportid_max = max_id; @@ -2201,6 +2245,15 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) 0, MT_TOOL_MAX, 0, 0); } + /* For T15 key array */ + if (data->T15_reportid_min) { + data->t15_keystatus = 0; + + for (i = 0; i < data->pdata->t15_num_keys; i++) + input_set_capability(input_dev, EV_KEY, + data->pdata->t15_keymap[i]); + } + input_set_drvdata(input_dev, data); error = input_register_device(input_dev); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 02bf6ea317015c..b7d20924eb711f 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -20,6 +20,8 @@ struct mxt_platform_data { unsigned long irqflags; u8 t19_num_keys; const unsigned int *t19_keymap; + int t15_num_keys; + const unsigned int *t15_keymap; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ From 4e04c362fd784e631febfe19be21bd3ac23878bc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Oct 2012 17:20:48 +0100 Subject: [PATCH 041/154] Input: atmel_mxt_ts - remove unused defines 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 Acked-by: Benson Leung Acked-by: Yufeng Shen --- 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 4ec1bd548f672b..cad4e302865a2e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -25,27 +25,13 @@ #include #include -/* 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 @@ -107,15 +93,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_ORIENT 9 #define MXT_T9_RANGE 18 @@ -138,51 +115,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 @@ -1573,7 +1509,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; From ba6d107a18b0594545d6fa76cac92a97e4122d54 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 7 Dec 2012 13:07:30 +0000 Subject: [PATCH 042/154] Input: atmel_mxt_ts - verify Information Block checksum on probe By reading the information block and the object table into a contiguous region of memory, we can verify the checksum at probe time. This means we verify that we are indeed talking to a chip that supports object protocol correctly. We also detect I2C comms problems much earlier, resulting in easier diagnosis. Signed-off-by: Nick Dyer Acked-by: Benson Leung --- drivers/input/touchscreen/atmel_mxt_ts.c | 188 ++++++++++++++--------- 1 file changed, 115 insertions(+), 73 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index cad4e302865a2e..d278fe66bffd08 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -194,7 +194,8 @@ struct mxt_data { char phys[64]; /* device physical location */ const struct mxt_platform_data *pdata; struct mxt_object *object_table; - struct mxt_info info; + struct mxt_info *info; + void *raw_info_block; unsigned int irq; unsigned int max_x; unsigned int max_y; @@ -360,12 +361,13 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) { u8 appmode = data->client->addr; u8 bootloader; + u8 family_id = data->info ? data->info->family_id : 0; switch (appmode) { case 0x4a: case 0x4b: /* Chips after 1664S use different scheme */ - if (retry || data->info.family_id >= 0xa2) { + if (retry || family_id >= 0xa2) { bootloader = appmode - 0x24; break; } @@ -605,7 +607,7 @@ mxt_get_object(struct mxt_data *data, u8 type) struct mxt_object *object; int i; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { object = data->object_table + i; if (object->type == type) return object; @@ -1236,13 +1238,13 @@ static int mxt_check_reg_init(struct mxt_data *data) data_pos += offset; } - if (cfg_info.family_id != data->info.family_id) { + if (cfg_info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); ret = -EINVAL; goto release; } - if (cfg_info.variant_id != data->info.variant_id) { + if (cfg_info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); ret = -EINVAL; goto release; @@ -1291,7 +1293,7 @@ static int mxt_check_reg_init(struct mxt_data *data) /* Malloc memory to store configuration */ cfg_start_ofs = MXT_OBJECT_START + - data->info.object_num * sizeof(struct mxt_object) + + data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; config_mem_size = data->mem_size - cfg_start_ofs; config_mem = kzalloc(config_mem_size, GFP_KERNEL); @@ -1502,24 +1504,12 @@ static int mxt_acquire_irq(struct mxt_data *data) return 0; } -static int mxt_get_info(struct mxt_data *data) -{ - struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; - int error; - - /* Read 7-byte info block starting at address 0 */ - error = __mxt_read_reg(client, 0, sizeof(*info), info); - if (error) - return error; - - return 0; -} - static void mxt_free_object_table(struct mxt_data *data) { - kfree(data->object_table); + kfree(data->raw_info_block); data->object_table = NULL; + data->info = NULL; + data->raw_info_block = NULL; kfree(data->msg_buf); data->msg_buf = NULL; data->T5_address = 0; @@ -1540,34 +1530,18 @@ static void mxt_free_object_table(struct mxt_data *data) data->max_reportid = 0; } -static int mxt_get_object_table(struct mxt_data *data) +static int mxt_parse_object_table(struct mxt_data *data, + struct mxt_object *object_table) { struct i2c_client *client = data->client; - size_t table_size; - struct mxt_object *object_table; - int error; int i; u8 reportid; u16 end_address; - table_size = data->info.object_num * sizeof(struct mxt_object); - object_table = kzalloc(table_size, GFP_KERNEL); - if (!object_table) { - dev_err(&data->client->dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, - object_table); - if (error) { - kfree(object_table); - return error; - } - /* Valid Report IDs start counting from 1 */ reportid = 1; data->mem_size = 0; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { struct mxt_object *object = object_table + i; u8 min_id, max_id; @@ -1591,7 +1565,7 @@ static int mxt_get_object_table(struct mxt_data *data) switch (object->type) { case MXT_GEN_MESSAGE_T5: - if (data->info.family_id == 0x80) { + if (data->info->family_id == 0x80) { /* * On mXT224 read and discard unused CRC byte * otherwise DMA reads are misaligned @@ -1653,24 +1627,108 @@ static int mxt_get_object_table(struct mxt_data *data) /* If T44 exists, T5 position has to be directly after */ if (data->T44_address && (data->T5_address != data->T44_address + 1)) { dev_err(&client->dev, "Invalid T44 position\n"); - error = -EINVAL; - goto free_object_table; + return -EINVAL; } data->msg_buf = kcalloc(data->max_reportid, data->T5_msg_size, GFP_KERNEL); if (!data->msg_buf) { dev_err(&client->dev, "Failed to allocate message buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static int mxt_read_info_block(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + size_t size; + void *id_buf, *buf; + uint8_t num_objects; + u32 calculated_crc; + u8 *crc_ptr; + + /* If info block already allocated, free it */ + if (data->raw_info_block != NULL) + mxt_free_object_table(data); + + /* Read 7-byte ID information block starting at address 0 */ + size = sizeof(struct mxt_info); + id_buf = kzalloc(size, GFP_KERNEL); + if (!id_buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + error = __mxt_read_reg(client, 0, size, id_buf); + if (error) { + kfree(id_buf); + return error; + } + + /* Resize buffer to give space for rest of info block */ + num_objects = ((struct mxt_info *)id_buf)->object_num; + size += (num_objects * sizeof(struct mxt_object)) + + MXT_INFO_CHECKSUM_SIZE; + + buf = krealloc(id_buf, size, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); error = -ENOMEM; - goto free_object_table; + goto err_free_mem; + } + + /* Read rest of info block */ + error = __mxt_read_reg(client, MXT_OBJECT_START, + size - MXT_OBJECT_START, + buf + MXT_OBJECT_START); + if (error) + goto err_free_mem; + + /* Extract & calculate checksum */ + crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE; + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + + calculated_crc = mxt_calculate_crc(buf, 0, + size - MXT_INFO_CHECKSUM_SIZE); + + /* + * CRC mismatch can be caused by data corruption due to I2C comms + * issue or else device is not using Object Based Protocol (eg i2c-hid) + */ + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { + dev_err(&client->dev, + "Info Block CRC error calculated=0x%06X read=0x%06X\n", + data->info_crc, calculated_crc); + error = -EIO; + goto err_free_mem; + } + + data->raw_info_block = buf; + data->info = (struct mxt_info *)buf; + + dev_info(&client->dev, + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", + data->info->family_id, data->info->variant_id, + data->info->version >> 4, data->info->version & 0xf, + data->info->build, data->info->object_num); + + /* Parse object table information */ + error = mxt_parse_object_table(data, buf + MXT_OBJECT_START); + if (error) { + dev_err(&client->dev, "Error %d parsing object table\n", error); + mxt_free_object_table(data); + return error; } - data->object_table = object_table; + data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START); return 0; -free_object_table: - mxt_free_object_table(data); +err_free_mem: + kfree(buf); return error; } @@ -1725,13 +1783,12 @@ static int mxt_read_t9_resolution(struct mxt_data *data) static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; int error; bool alt_bootloader_addr = false; bool retry = false; retry_info: - error = mxt_get_info(data); + error = mxt_read_info_block(data); if (error) { retry_bootloader: error = mxt_probe_bootloader(data, alt_bootloader_addr); @@ -1765,14 +1822,7 @@ static int mxt_initialize(struct mxt_data *data) } } - /* Get object table information */ - error = mxt_get_object_table(data); - if (error) { - dev_err(&client->dev, "Error %d reading object table\n", error); - return error; - } - - mxt_acquire_irq(data); + error = mxt_acquire_irq(data); if (error) goto err_free_object_table; @@ -1790,17 +1840,6 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } - error = mxt_read_t9_resolution(data); - if (error) { - dev_err(&client->dev, "Failed to initialize T9 resolution\n"); - goto err_free_object_table; - } - - dev_info(&client->dev, - "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", - info->family_id, info->variant_id, info->version >> 4, - info->version & 0xf, info->build, info->object_num); - return 0; err_free_object_table: @@ -1813,9 +1852,9 @@ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_info *info = &data->info; return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", - info->version >> 4, info->version & 0xf, info->build); + data->info->version >> 4, data->info->version & 0xf, + data->info->build); } /* Hardware Version is returned as FamilyID.VariantID */ @@ -1823,9 +1862,8 @@ static ssize_t mxt_hw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_info *info = &data->info; return scnprintf(buf, PAGE_SIZE, "%u.%u\n", - info->family_id, info->variant_id); + data->info->family_id, data->info->variant_id); } static ssize_t mxt_show_instance(char *buf, int count, @@ -1862,7 +1900,7 @@ static ssize_t mxt_object_show(struct device *dev, return -ENOMEM; error = 0; - for (i = 0; i < data->info.object_num; i++) { + for (i = 0; i < data->info->object_num; i++) { object = data->object_table + i; if (!mxt_object_readable(object->type)) @@ -2109,6 +2147,10 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) unsigned int mt_flags = 0; int i; + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + input_dev = input_allocate_device(); if (!input_dev) { dev_err(dev, "Failed to allocate memory\n"); From 3d1d92f3257a44ec9fa3c4b0e4a6d2771e9b4e33 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:20:26 +0000 Subject: [PATCH 043/154] Input: atmel_mxt_ts - use T18 RETRIGEN to handle IRQF_TRIGGER_LOW The workaround of reading all messages until an invalid is received is a way of forcing the CHG line high, which means that when using edge-triggered interrupts the interrupt can be acquired. With level-triggered interrupts this is unnecessary. Also, most recent maXTouch chips have a feature called RETRIGEN which, when enabled, reasserts the interrupt line every cycle if there are messages waiting. This also makes the workaround unnecessary. Note: the RETRIGEN feature is only in some firmware versions/chips, it's not valid simply to enable the bit. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 48 ++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d278fe66bffd08..721d81ed3196a9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -118,6 +118,7 @@ struct t9_range { /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +#define MXT_COMMS_RETRIGEN (1 << 6) /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 @@ -213,6 +214,7 @@ struct mxt_data { u8 num_touchids; u8 num_stylusids; unsigned long t15_keystatus; + bool use_retrigen_workaround; /* Cached parameters from object table */ u16 T5_address; @@ -224,6 +226,7 @@ struct mxt_data { u8 T9_reportid_max; u8 T15_reportid_min; u8 T15_reportid_max; + u16 T18_address; u8 T19_reportid; u8 T42_reportid_min; u8 T42_reportid_max; @@ -1169,6 +1172,31 @@ 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; + + if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + 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; +} + /* * mxt_check_reg_init - download configuration to chip * @@ -1424,6 +1452,10 @@ static int mxt_check_reg_init(struct mxt_data *data) 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; @@ -1497,9 +1529,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; } @@ -1520,6 +1554,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_max = 0; data->T15_reportid_min = 0; data->T15_reportid_max = 0; + data->T18_address = 0; data->T19_reportid = 0; data->T42_reportid_min = 0; data->T42_reportid_max = 0; @@ -1593,6 +1628,9 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T15_reportid_min = min_id; data->T15_reportid_max = max_id; break; + case MXT_SPT_COMMSCONFIG_T18: + data->T18_address = object->start_address; + break; case MXT_PROCI_TOUCHSUPPRESSION_T42: data->T42_reportid_min = min_id; data->T42_reportid_max = max_id; @@ -1822,6 +1860,10 @@ static int mxt_initialize(struct mxt_data *data) } } + error = mxt_check_retrigen(data); + if (error) + goto err_free_object_table; + error = mxt_acquire_irq(data); if (error) goto err_free_object_table; From 6316d4d4ecc7d19c8cbe66ba353003fe85042752 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 16:01:07 +0000 Subject: [PATCH 044/154] Input: atmel_mxt_ts - handle reports from T47 Stylus object Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 721d81ed3196a9..771c3ddefaf103 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -128,6 +128,9 @@ struct t9_range { /* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ #define MXT_T42_MSG_TCHSUP (1 << 0) +/* T47 Stylus */ +#define MXT_TOUCH_MAJOR_T47_STYLUS 1 + /* T63 Stylus */ #define MXT_T63_STYLUS_PRESS (1 << 0) #define MXT_T63_STYLUS_RELEASE (1 << 1) @@ -686,6 +689,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) int area; int amplitude; u8 vector; + int tool; id = message[0] - data->T9_reportid_min; status = message[1]; @@ -699,6 +703,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) y >>= 2; area = message[5]; + amplitude = message[6]; vector = message[7]; @@ -729,8 +734,16 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) mxt_input_sync(input_dev); } + /* A size of zero indicates touch is from a linked T47 Stylus */ + if (area == 0) { + area = MXT_TOUCH_MAJOR_T47_STYLUS; + tool = MT_TOOL_PEN; + } else { + tool = MT_TOOL_FINGER; + } + /* Touch active */ - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); + input_mt_report_slot_state(input_dev, tool, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); From a243a6719230201586a8f0856de532875a8e6873 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:06:42 +0000 Subject: [PATCH 045/154] Input: atmel_mxt_ts - release touch state during suspend If fingers are down as the MXT chip goes into suspend it does not send a lift message. In addition, it may not complete its final measurement cycle immediately, which means touch messages may be received by the interrupt handler after mxt_stop() has completed. So: - disable irq during suspend - flush any messages created after suspend - tell app layer that slots were released at suspend Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 52 ++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 771c3ddefaf103..cb3e43a18df9fa 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -246,6 +246,9 @@ struct mxt_data { /* for config update handling */ struct completion crc_completion; + + /* Indicates whether device is in suspend */ + bool suspended; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -885,10 +888,10 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) mxt_proc_t42_messages(data, message); } else if (report_id == data->T48_reportid) { mxt_proc_t48_messages(data, message); - } else if (!data->input_dev) { + } else if (!data->input_dev || data->suspended) { /* - * do not report events if input device - * is not yet registered + * do not report events if input device is not + * yet registered or returning from suspend */ mxt_dump_message(data, message); } else if (report_id >= data->T9_reportid_min @@ -2026,6 +2029,11 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; + if (data->suspended) { + enable_irq(data->irq); + data->suspended = false; + } + if (!data->in_bootloader) { /* Change to the bootloader mode */ data->in_bootloader = true; @@ -2138,6 +2146,8 @@ static ssize_t mxt_update_fw_store(struct device *dev, } else { dev_info(dev, "The firmware update succeeded\n"); + data->suspended = false; + error = mxt_initialize(data); if (error) return error; @@ -2163,17 +2173,53 @@ static const struct attribute_group mxt_attr_group = { .attrs = mxt_attrs, }; +static void mxt_reset_slots(struct mxt_data *data) +{ + struct input_dev *input_dev = data->input_dev; + unsigned int num_mt_slots; + int id; + + num_mt_slots = data->num_touchids + data->num_stylusids; + + for (id = 0; id < num_mt_slots; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + mxt_input_sync(input_dev); +} + static void mxt_start(struct mxt_data *data) { + if (!data->suspended || data->in_bootloader) + return; + + /* + * Discard any touch messages still in message buffer + * from before chip went to sleep + */ + mxt_process_messages_until_invalid(data); + 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); + + mxt_acquire_irq(data); + data->suspended = false; } static void mxt_stop(struct mxt_data *data) { + if (data->suspended || data->in_bootloader) + return; + + disable_irq(data->irq); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + + mxt_reset_slots(data); + data->suspended = true; } static int mxt_input_open(struct input_dev *dev) From 9bfdc2b6dda45eb83bf0992d0cf98f3fd98163d4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 13 Dec 2012 14:28:32 +0000 Subject: [PATCH 046/154] Input: atmel_mxt_ts - initialize power config before and after downloading cfg If the power configuration is zero then the configuration download may fail to work properly, so initialize T7 before config download. The downloaded configuration may reset the T7 power configuration so it must be re-initialized afterwards. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index cb3e43a18df9fa..fb2122cf7bfe51 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1213,6 +1213,8 @@ static int mxt_check_retrigen(struct mxt_data *data) return 0; } +static int mxt_init_t7_power_cfg(struct mxt_data *data); + /* * mxt_check_reg_init - download configuration to chip * @@ -1478,6 +1480,9 @@ static int mxt_check_reg_init(struct mxt_data *data) dev_info(dev, "Config successfully updated\n"); + /* T7 config may have changed */ + mxt_init_t7_power_cfg(data); + release_mem: kfree(config_mem); release: @@ -1884,17 +1889,18 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - /* Check register init values */ - error = mxt_check_reg_init(data); + error = mxt_init_t7_power_cfg(data); if (error) { dev_err(&client->dev, "Error %d initializing configuration\n", error); goto err_free_object_table; } - error = mxt_init_t7_power_cfg(data); + /* Check register init values */ + error = mxt_check_reg_init(data); if (error) { - dev_err(&client->dev, "Failed to initialize power cfg\n"); + dev_err(&client->dev, "Error %d initialising configuration\n", + error); goto err_free_object_table; } From 69537daa9ceb1a0f6d347f75a1c2f4e5be294c84 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 16 Jan 2013 17:25:05 +0000 Subject: [PATCH 047/154] Input: atmel_mxt_ts - add regulator control support Allow the driver to optionally manage enabling/disable power to the touch controller itself. If the regulators are not present then use the deep sleep power mode instead. For a correct power on sequence, it is required that we have control over the RESET line. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 125 +++++++++++++++++++++-- include/linux/i2c/atmel_mxt_ts.h | 1 + 2 files changed, 115 insertions(+), 11 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fb2122cf7bfe51..7659dc9657222e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -152,6 +154,9 @@ struct t9_range { #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ #define MXT_WAKEUP_TIME 25 /* msec */ +#define MXT_REGULATOR_DELAY 150 /* msec */ +#define MXT_CHG_DELAY 100 /* msec */ +#define MXT_POWERON_DELAY 150 /* msec */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -218,6 +223,9 @@ struct mxt_data { u8 num_stylusids; unsigned long t15_keystatus; bool use_retrigen_workaround; + bool use_regulator; + struct regulator *reg_vdd; + struct regulator *reg_avdd; /* Cached parameters from object table */ u16 T5_address; @@ -1839,6 +1847,84 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } +static void mxt_regulator_enable(struct mxt_data *data) +{ + int error; + + gpio_set_value(data->pdata->gpio_reset, 0); + + error = regulator_enable(data->reg_vdd); + if (error) + return; + + error = regulator_enable(data->reg_avdd); + if (error) + return; + + msleep(MXT_REGULATOR_DELAY); + gpio_set_value(data->pdata->gpio_reset, 1); + msleep(MXT_CHG_DELAY); + +retry_wait: + INIT_COMPLETION(data->bl_completion); + data->in_bootloader = true; + error = mxt_wait_for_completion(data, &data->bl_completion, + MXT_POWERON_DELAY); + if (error == -EINTR) + goto retry_wait; + + data->in_bootloader = false; +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ + regulator_disable(data->reg_vdd); + regulator_disable(data->reg_avdd); +} + +static void mxt_probe_regulators(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + /* + * According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage + */ + if (!data->pdata->gpio_reset) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + goto fail; + } + + data->reg_vdd = regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting vdd regulator\n", error); + goto fail; + } + + data->reg_avdd = regulator_get(dev, "avdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting avdd regulator\n", error); + goto fail_release; + } + + data->use_regulator = true; + mxt_regulator_enable(data); + + dev_dbg(dev, "Initialised regulators\n"); + return; + +fail_release: + regulator_put(data->reg_vdd); +fail: + data->reg_vdd = NULL; + data->reg_avdd = NULL; + data->use_regulator = false; +} + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -2036,6 +2122,9 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto release_firmware; if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + enable_irq(data->irq); data->suspended = false; } @@ -2200,18 +2289,25 @@ static void mxt_start(struct mxt_data *data) if (!data->suspended || data->in_bootloader) return; - /* - * Discard any touch messages still in message buffer - * from before chip went to sleep - */ - mxt_process_messages_until_invalid(data); + if (data->use_regulator) { + enable_irq(data->irq); + + mxt_regulator_enable(data); + } else { + /* + * Discard any messages still in message buffer + * from before chip went to sleep + */ + mxt_process_messages_until_invalid(data); + + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); - 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); - /* Recalibrate since chip has been in deep sleep */ - mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + mxt_acquire_irq(data); + } - mxt_acquire_irq(data); data->suspended = false; } @@ -2222,7 +2318,10 @@ static void mxt_stop(struct mxt_data *data) disable_irq(data->irq); - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + if (data->use_regulator) + mxt_regulator_disable(data); + else + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); mxt_reset_slots(data); data->suspended = true; @@ -2392,7 +2491,9 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - disable_irq(client->irq); + mxt_probe_regulators(data); + + disable_irq(data->irq); error = mxt_initialize(data); if (error) @@ -2432,6 +2533,8 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); + regulator_put(data->reg_avdd); + regulator_put(data->reg_vdd); mxt_free_object_table(data); kfree(data); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index b7d20924eb711f..a01f2e86c47cc5 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -22,6 +22,7 @@ struct mxt_platform_data { const unsigned int *t19_keymap; int t15_num_keys; const unsigned int *t15_keymap; + unsigned long gpio_reset; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ From 28775ab8090b33ff8414bdf47bbb0e5502dd6c95 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 12 Feb 2013 17:07:56 +0000 Subject: [PATCH 048/154] Input: atmel_mxt_ts - implement support for T100 touch object The T100 object replaces the old T9 multitouch touchscreen object in new chips. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 293 ++++++++++++++++++++++- 1 file changed, 282 insertions(+), 11 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7659dc9657222e..19a07cd4031fdd 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -67,6 +67,7 @@ #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 #define MXT_PROCI_ACTIVE_STYLUS_T63 63 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -146,6 +147,23 @@ struct t9_range { #define MXT_T63_STYLUS_PRESSURE_MASK 0x3F +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY (1 << 5) + +#define MXT_T100_TCHAUX_VECT (1 << 0) +#define MXT_T100_TCHAUX_AMPL (1 << 1) +#define MXT_T100_TCHAUX_AREA (1 << 2) + +#define MXT_T100_DETECT (1 << 7) +#define MXT_T100_TYPE_MASK 0x70 +#define MXT_T100_TYPE_STYLUS 0x20 + /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ @@ -210,6 +228,9 @@ struct mxt_data { unsigned int max_y; bool in_bootloader; u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; u8 max_reportid; u32 config_crc; u32 info_crc; @@ -245,6 +266,8 @@ struct mxt_data { u8 T48_reportid; u8 T63_reportid_min; u8 T63_reportid_max; + u8 T100_reportid_min; + u8 T100_reportid_max; /* for fw update in bootloader */ struct completion bl_completion; @@ -768,6 +791,73 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int tool; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = (message[3] << 8) | message[2]; + y = (message[5] << 8) | message[4]; + + dev_dbg(dev, + "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n", + id, + status, + x, y, + data->t100_aux_area ? message[data->t100_aux_area] : 0, + data->t100_aux_ampl ? message[data->t100_aux_ampl] : 0, + data->t100_aux_vect ? message[data->t100_aux_vect] : 0); + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS) + tool = MT_TOOL_PEN; + else + tool = MT_TOOL_FINGER; + + /* Touch active */ + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + + if (data->t100_aux_ampl) + input_report_abs(input_dev, ABS_MT_PRESSURE, + message[data->t100_aux_ampl]); + + if (data->t100_aux_area) { + if (tool == MT_TOOL_PEN) + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + MXT_TOUCH_MAJOR_T47_STYLUS); + else + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + message[data->t100_aux_area]); + } + + if (data->t100_aux_vect) + input_report_abs(input_dev, ABS_MT_ORIENTATION, + message[data->t100_aux_vect]); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + data->update_input = true; +} + + static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) { struct input_dev *input_dev = data->input_dev; @@ -905,6 +995,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) } else if (report_id >= data->T9_reportid_min && report_id <= data->T9_reportid_max) { mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min + && report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; @@ -1575,6 +1668,12 @@ static void mxt_free_object_table(struct mxt_data *data) data->raw_info_block = NULL; kfree(data->msg_buf); data->msg_buf = NULL; + + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } + data->T5_address = 0; data->T5_msg_size = 0; data->T6_reportid = 0; @@ -1591,6 +1690,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T48_reportid = 0; data->T63_reportid_min = 0; data->T63_reportid_max = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; data->max_reportid = 0; } @@ -1679,6 +1780,12 @@ static int mxt_parse_object_table(struct mxt_data *data, data->num_stylusids = object->num_report_ids * mxt_obj_instances(object); break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; } end_address = object->start_address @@ -1925,6 +2032,168 @@ static void mxt_probe_regulators(struct mxt_data *data) data->use_regulator = false; } +static int mxt_read_t100_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u16 range_x, range_y; + u8 cfg, tchaux; + u8 aux; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + + le16_to_cpus(range_x); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + + le16_to_cpus(range_y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_CFG1, + 1, &cfg); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_TCHAUX, + 1, &tchaux); + if (error) + return error; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + if (range_y == 0) + range_y = 1023; + + if (cfg & MXT_T100_CFG_SWITCHXY) { + data->max_x = range_y; + data->max_y = range_x; + } else { + data->max_x = range_x; + data->max_y = range_y; + } + + /* allocate aux bytes */ + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_info(&client->dev, + "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t100_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev; + int error; + + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "atmel_mxt_ts T100 touchscreen"; + + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &data->client->dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + set_bit(EV_ABS, input_dev->evbit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + + if (data->t100_aux_ampl) + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + error = input_mt_init_slots(input_dev, data->num_touchids, 0); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + + if (data->t100_aux_area) + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + + if (data->t100_aux_ampl) + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + + if (data->t100_aux_vect) + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_initialize_t9_input_device(struct mxt_data *data); + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -1990,6 +2259,18 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } + if (data->T9_reportid_min) { + error = mxt_initialize_t9_input_device(data); + if (error) + goto err_free_object_table; + } else if (data->T100_reportid_min) { + error = mxt_initialize_t100_input_device(data); + if (error) + goto err_free_object_table; + } else { + dev_warn(&client->dev, "No touch object detected\n"); + } + return 0; err_free_object_table: @@ -2499,24 +2780,15 @@ static int mxt_probe(struct i2c_client *client, if (error) goto err_free_irq; - if (!data->in_bootloader) { - error = mxt_initialize_t9_input_device(data); - if (error) - goto err_free_object; - } - 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_unregister_device; + goto err_free_object; } return 0; -err_unregister_device: - input_unregister_device(data->input_dev); - data->input_dev = NULL; err_free_object: mxt_free_object_table(data); err_free_irq: @@ -2532,7 +2804,6 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); - input_unregister_device(data->input_dev); regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); mxt_free_object_table(data); From 9cd32cf7a7ef054525b81430b17f4f86208e540f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 14:59:17 +0000 Subject: [PATCH 049/154] Input: atmel_mxt_ts - allow specification of firmware file name On platforms which have multiple device instances using this driver, the firmware may be different on each device. This patch makes the user give the name of the firmware file when flashing. This also prevents accidental triggering of the firmware load process. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 45 ++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 19a07cd4031fdd..ffaf7bafe5d5ff 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -27,8 +27,7 @@ #include #include -/* Firmware files */ -#define MXT_FW_NAME "maxtouch.fw" +/* Configuration file */ #define MXT_CFG_NAME "maxtouch.cfg" #define MXT_CFG_MAGIC "OBP_RAW V1" @@ -247,6 +246,7 @@ struct mxt_data { bool use_regulator; struct regulator *reg_vdd; struct regulator *reg_avdd; + char *fw_name; /* Cached parameters from object table */ u16 T5_address; @@ -2381,7 +2381,7 @@ static int mxt_check_firmware_format(struct device *dev, return -EINVAL; } -static int mxt_load_fw(struct device *dev, const char *fn) +static int mxt_load_fw(struct device *dev) { struct mxt_data *data = dev_get_drvdata(dev); const struct firmware *fw = NULL; @@ -2391,9 +2391,9 @@ static int mxt_load_fw(struct device *dev, const char *fn) unsigned int frame = 0; int ret; - ret = request_firmware(&fw, fn, dev); + ret = request_firmware(&fw, data->fw_name, dev); if (ret) { - dev_err(dev, "Unable to open firmware %s\n", fn); + dev_err(dev, "Unable to open firmware %s\n", data->fw_name); return ret; } @@ -2508,6 +2508,35 @@ static int mxt_load_fw(struct device *dev, const char *fn) return ret; } +static int mxt_update_file_name(struct device *dev, char **file_name, + const char *buf, size_t count) +{ + char *file_name_tmp; + + /* Simple sanity check */ + if (count > 64) { + dev_warn(dev, "File name too long\n"); + return -EINVAL; + } + + file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL); + if (!file_name_tmp) { + dev_warn(dev, "no memory\n"); + return -ENOMEM; + } + + *file_name = file_name_tmp; + memcpy(*file_name, buf, count); + + /* Echo into the sysfs entry may append newline at the end of buf */ + if (buf[count - 1] == '\n') + (*file_name)[count - 1] = '\0'; + else + (*file_name)[count] = '\0'; + + return 0; +} + static ssize_t mxt_update_fw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -2515,7 +2544,11 @@ static ssize_t mxt_update_fw_store(struct device *dev, struct mxt_data *data = dev_get_drvdata(dev); int error; - error = mxt_load_fw(dev, MXT_FW_NAME); + error = mxt_update_file_name(dev, &data->fw_name, buf, count); + if (error) + return error; + + error = mxt_load_fw(dev); if (error) { dev_err(dev, "The firmware update failed(%d)\n", error); count = error; From 5a2726751c864efd234cb2f8b287c0eed52a9607 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Mon, 14 May 2012 12:06:29 -0400 Subject: [PATCH 050/154] Input: atmel_mxt_ts - set default irqflags when there is no pdata This is the preparation for supporting the code path when there is platform data provided and still boot the device into a sane state with backup NVRAM config. Make the irqflags default to be IRQF_TRIGGER_FALLING if no platform data is provided. Signed-off-by: Yufeng Shen Signed-off-by: Daniel Kurtz Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 44 ++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ffaf7bafe5d5ff..70d826a129c966 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -218,7 +218,7 @@ struct mxt_data { struct i2c_client *client; struct input_dev *input_dev; char phys[64]; /* device physical location */ - const struct mxt_platform_data *pdata; + struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info *info; void *raw_info_block; @@ -2657,6 +2657,26 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } +static int mxt_handle_pdata(struct mxt_data *data) +{ + data->pdata = dev_get_platdata(&data->client->dev); + + /* Use provided platform data if present */ + if (data->pdata) + return 0; + + data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); + if (!data->pdata) { + dev_err(&data->client->dev, "Failed to allocate pdata\n"); + return -ENOMEM; + } + + /* Set default parameters */ + data->pdata->irqflags = IRQF_TRIGGER_FALLING; + + return 0; +} + static int mxt_initialize_t9_input_device(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -2773,12 +2793,8 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mxt_data *data; - const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); int error; - if (!pdata) - return -EINVAL; - data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); if (!data) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -2789,20 +2805,23 @@ static int mxt_probe(struct i2c_client *client, client->adapter->nr, client->addr); data->client = client; - data->pdata = pdata; data->irq = client->irq; i2c_set_clientdata(client, data); + error = mxt_handle_pdata(data); + if (error) + goto err_free_mem; + init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags | IRQF_ONESHOT, + error = request_threaded_irq(data->irq, NULL, mxt_interrupt, + data->pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; + goto err_free_pdata; } mxt_probe_regulators(data); @@ -2825,7 +2844,10 @@ static int mxt_probe(struct i2c_client *client, err_free_object: mxt_free_object_table(data); err_free_irq: - free_irq(client->irq, data); + free_irq(data->irq, data); +err_free_pdata: + if (!dev_get_platdata(&data->client->dev)) + kfree(data->pdata); err_free_mem: kfree(data); return error; @@ -2840,6 +2862,8 @@ static int mxt_remove(struct i2c_client *client) regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); mxt_free_object_table(data); + if (!dev_get_platdata(&data->client->dev)) + kfree(data->pdata); kfree(data); return 0; From 833521d192380cf086f03b9f98720a620136167a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 15:57:05 +0000 Subject: [PATCH 051/154] Input: atmel_mxt_ts - handle cfg filename via pdata/sysfs There may be multiple maXTouch chips on a single device which will require different configuration files. Add a platform data value for the configuration filename. Add sysfs entry to write configuration file if the platform data is not set. Split out the object initialisation code from mxt_initialize() into mxt_configure_objects() to allow this. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 114 ++++++++++++++++++----- include/linux/i2c/atmel_mxt_ts.h | 1 + 2 files changed, 94 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 70d826a129c966..0f724bba765c7c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,7 +28,6 @@ #include /* Configuration file */ -#define MXT_CFG_NAME "maxtouch.cfg" #define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ @@ -247,6 +246,7 @@ struct mxt_data { struct regulator *reg_vdd; struct regulator *reg_avdd; char *fw_name; + char *cfg_name; /* Cached parameters from object table */ u16 T5_address; @@ -280,6 +280,9 @@ struct mxt_data { /* Indicates whether device is in suspend */ bool suspended; + + /* Indicates whether device is updating configuration */ + bool updating_config; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -1354,10 +1357,15 @@ static int mxt_check_reg_init(struct mxt_data *data) u8 val; u16 reg; - ret = request_firmware(&cfg, MXT_CFG_NAME, dev); + if (!data->cfg_name) { + dev_dbg(dev, "Skipping cfg download\n"); + return 0; + } + + ret = request_firmware(&cfg, data->cfg_name, dev); if (ret < 0) { dev_err(dev, "Failure to request config file %s\n", - MXT_CFG_NAME); + data->cfg_name); return 0; } @@ -1660,6 +1668,14 @@ static int mxt_acquire_irq(struct mxt_data *data) return 0; } +static void mxt_free_input_device(struct mxt_data *data) +{ + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } +} + static void mxt_free_object_table(struct mxt_data *data) { kfree(data->raw_info_block); @@ -1669,10 +1685,7 @@ static void mxt_free_object_table(struct mxt_data *data) kfree(data->msg_buf); data->msg_buf = NULL; - if (data->input_dev) { - input_unregister_device(data->input_dev); - data->input_dev = NULL; - } + mxt_free_input_device(data); data->T5_address = 0; data->T5_msg_size = 0; @@ -2193,6 +2206,7 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) } static int mxt_initialize_t9_input_device(struct mxt_data *data); +static int mxt_configure_objects(struct mxt_data *data); static int mxt_initialize(struct mxt_data *data) { @@ -2238,17 +2252,28 @@ static int mxt_initialize(struct mxt_data *data) error = mxt_check_retrigen(data); if (error) - goto err_free_object_table; + return error; error = mxt_acquire_irq(data); if (error) - goto err_free_object_table; + return error; + + error = mxt_configure_objects(data); + if (error) + return error; + + return 0; +} + +static int mxt_configure_objects(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; error = mxt_init_t7_power_cfg(data); if (error) { - dev_err(&client->dev, "Error %d initializing configuration\n", - error); - goto err_free_object_table; + dev_err(&client->dev, "Failed to initialize power cfg\n"); + return error; } /* Check register init values */ @@ -2256,26 +2281,22 @@ static int mxt_initialize(struct mxt_data *data) if (error) { dev_err(&client->dev, "Error %d initialising configuration\n", error); - goto err_free_object_table; + return error; } if (data->T9_reportid_min) { error = mxt_initialize_t9_input_device(data); if (error) - goto err_free_object_table; + return error; } else if (data->T100_reportid_min) { error = mxt_initialize_t100_input_device(data); if (error) - goto err_free_object_table; + return error; } else { dev_warn(&client->dev, "No touch object detected\n"); } return 0; - -err_free_object_table: - mxt_free_object_table(data); - return error; } /* Firmware Version is returned as Major.Minor.Build */ @@ -2565,16 +2586,60 @@ static ssize_t mxt_update_fw_store(struct device *dev, return count; } +static ssize_t mxt_update_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int ret; + + if (data->in_bootloader) { + dev_err(dev, "Not in appmode\n"); + return -EINVAL; + } + + ret = mxt_update_file_name(dev, &data->cfg_name, buf, count); + if (ret) + return ret; + + data->updating_config = true; + + mxt_free_input_device(data); + + if (data->suspended) { + if (data->use_regulator) { + enable_irq(data->irq); + mxt_regulator_enable(data); + } else { + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + mxt_acquire_irq(data); + } + + data->suspended = false; + } + + ret = mxt_configure_objects(data); + if (ret) + goto out; + + ret = count; +out: + data->updating_config = false; + return ret; +} + static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); +static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, &dev_attr_hw_version.attr, &dev_attr_object.attr, &dev_attr_update_fw.attr, + &dev_attr_update_cfg.attr, NULL }; @@ -2627,7 +2692,7 @@ static void mxt_start(struct mxt_data *data) static void mxt_stop(struct mxt_data *data) { - if (data->suspended || data->in_bootloader) + if (data->suspended || data->in_bootloader || data->updating_config) return; disable_irq(data->irq); @@ -2662,8 +2727,15 @@ static int mxt_handle_pdata(struct mxt_data *data) data->pdata = dev_get_platdata(&data->client->dev); /* Use provided platform data if present */ - if (data->pdata) + if (data->pdata) { + if (data->pdata->cfg_name) + mxt_update_file_name(&data->client->dev, + &data->cfg_name, + data->pdata->cfg_name, + strlen(data->pdata->cfg_name)); + return 0; + } data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); if (!data->pdata) { diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index a01f2e86c47cc5..3422bd0d847bb9 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -23,6 +23,7 @@ struct mxt_platform_data { int t15_num_keys; const unsigned int *t15_keymap; unsigned long gpio_reset; + const char *cfg_name; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ From 31ccad35f0aa946026679386f19b3d4369310178 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 11 Apr 2013 17:48:19 +0100 Subject: [PATCH 052/154] Input: atmel_mxt_ts - only use first T9 instance The driver only registers one input device, which uses the screen parameters from the first T9 instance. The first T63 instance also uses those parameters. It is incorrect to send input reports from the second instances of these objects if they are enabled: the input scaling will be wrong and the positions will be mashed together. This also causes problems on Android if the number of slots exceeds 32. In the future, this could be handled by looking for enabled touch object instances and creating an input device for each one. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen --- drivers/input/touchscreen/atmel_mxt_ts.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0f724bba765c7c..409657a8d9f576 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1762,10 +1762,11 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T7_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: + /* Only handle messages from first T9 instance */ data->T9_reportid_min = min_id; - data->T9_reportid_max = max_id; - data->num_touchids = object->num_report_ids - * mxt_obj_instances(object); + data->T9_reportid_max = min_id + + object->num_report_ids - 1; + data->num_touchids = object->num_report_ids; break; case MXT_TOUCH_KEYARRAY_T15: data->T15_reportid_min = min_id; @@ -1788,10 +1789,10 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T48_reportid = min_id; break; case MXT_PROCI_ACTIVE_STYLUS_T63: + /* Only handle messages from first T63 instance */ data->T63_reportid_min = min_id; - data->T63_reportid_max = max_id; - data->num_stylusids = object->num_report_ids - * mxt_obj_instances(object); + data->T63_reportid_max = min_id; + data->num_stylusids = 1; break; case MXT_TOUCH_MULTITOUCHSCREEN_T100: data->T100_reportid_min = min_id; From 8a7db572f0383da9c6d4ee379de6e38cfc2a1b92 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 1 Oct 2013 13:43:10 +0100 Subject: [PATCH 053/154] Input: atmel_mxt_ts - allow input name to be specified in platform data Android systems identify the input device and map to IDC file by using the input device name. To avoid unnecessary deltas to the driver file, allow this to be set from the platform data. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 5 ++++- include/linux/i2c/atmel_mxt_ts.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 409657a8d9f576..9ad686d9a93fd5 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2143,7 +2143,10 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) return -ENOMEM; } - input_dev->name = "atmel_mxt_ts T100 touchscreen"; + if (data->pdata->input_name) + input_dev->name = data->pdata->input_name; + else + input_dev->name = "atmel_mxt_ts T100 touchscreen"; input_dev->phys = data->phys; input_dev->id.bustype = BUS_I2C; diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 3422bd0d847bb9..bc74c3f4c866f8 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -24,6 +24,7 @@ struct mxt_platform_data { const unsigned int *t15_keymap; unsigned long gpio_reset; const char *cfg_name; + const char *input_name; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ From a615cb3a8ea64650e900d0513274165dbbfe55e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:40:30 +0100 Subject: [PATCH 054/154] Input: atmel_mxt_ts - implement debug output for messages Add a debug switch which causes all messages from the touch controller to be dumped to the dmesg log with a set prefix "MXT MSG:". This is used by Atmel user-space utilities to debug touch operation. Enabling this output does impact touch performance. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 41 ++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 9ad686d9a93fd5..3e828679685c0e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -229,6 +229,7 @@ struct mxt_data { u8 t100_aux_ampl; u8 t100_aux_area; u8 t100_aux_vect; + bool debug_enabled; u8 max_reportid; u32 config_crc; u32 info_crc; @@ -330,8 +331,8 @@ static bool mxt_object_readable(unsigned int type) static void mxt_dump_message(struct mxt_data *data, u8 *message) { - dev_dbg(&data->client->dev, "message: %*ph\n", - data->T5_msg_size, message); + dev_dbg(&data->client->dev, "MXT MSG: %*ph\n", + data->T5_msg_size, message); } static int mxt_wait_for_completion(struct mxt_data *data, @@ -978,6 +979,7 @@ static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; + bool dump = data->debug_enabled; if (report_id == MXT_RPTID_NOMSG) return 0; @@ -1011,9 +1013,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) && report_id <= data->T15_reportid_max) { mxt_proc_t15_messages(data, message); } else { - mxt_dump_message(data, message); + dump = true; } + if (dump) + mxt_dump_message(data, message); + return 1; } @@ -2632,11 +2637,40 @@ static ssize_t mxt_update_cfg_store(struct device *dev, return ret; } +static ssize_t mxt_debug_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + char c; + + c = data->debug_enabled ? '1' : '0'; + return scnprintf(buf, PAGE_SIZE, "%c\n", c); +} + +static ssize_t mxt_debug_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + data->debug_enabled = (i == 1); + + dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, @@ -2644,6 +2678,7 @@ static struct attribute *mxt_attrs[] = { &dev_attr_object.attr, &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, + &dev_attr_debug_enable.attr, NULL }; From 2b33db061113841c55d9d7060a683cb11eea93d8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:37:50 +0100 Subject: [PATCH 055/154] Input: atmel_mxt_ts - add memory access interface via sysfs Atmel maXTouch chips can be addressed via an "Object Based Protocol" which defines how i2c registers are mapped to different functions within the chips. This interface exposes the register map and allows user-space utilities to inspect and alter object configuration, and to view diagnostic data, while the device is running. Signed-off-by: Nick Dyer Acked-by: Benson Leung --- drivers/input/touchscreen/atmel_mxt_ts.c | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 3e828679685c0e..ed0de37ffec382 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -229,6 +229,7 @@ struct mxt_data { u8 t100_aux_ampl; u8 t100_aux_area; u8 t100_aux_vect; + struct bin_attribute mem_access_attr; bool debug_enabled; u8 max_reportid; u32 config_crc; @@ -2664,6 +2665,56 @@ static ssize_t mxt_debug_enable_store(struct device *dev, } } +static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, + size_t *count) +{ + if (off >= data->mem_size) + return -EIO; + + if (off + *count > data->mem_size) + *count = data->mem_size - off; + + if (*count > MXT_MAX_BLOCK_WRITE) + *count = MXT_MAX_BLOCK_WRITE; + + return 0; +} + +static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_read_reg(data->client, off, count, buf); + + return ret == 0 ? count : ret; +} + +static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_write_reg(data->client, off, count, buf); + + return ret == 0 ? count : ret; +} + static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); @@ -2950,8 +3001,24 @@ static int mxt_probe(struct i2c_client *client, goto err_free_object; } + sysfs_bin_attr_init(&data->mem_access_attr); + data->mem_access_attr.attr.name = "mem_access"; + data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; + data->mem_access_attr.read = mxt_mem_access_read; + data->mem_access_attr.write = mxt_mem_access_write; + data->mem_access_attr.size = data->mem_size; + + if (sysfs_create_bin_file(&client->dev.kobj, + &data->mem_access_attr) < 0) { + dev_err(&client->dev, "Failed to create %s\n", + data->mem_access_attr.attr.name); + goto err_remove_sysfs_group; + } + return 0; +err_remove_sysfs_group: + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); err_free_object: mxt_free_object_table(data); err_free_irq: @@ -2968,6 +3035,10 @@ static int mxt_remove(struct i2c_client *client) { struct mxt_data *data = i2c_get_clientdata(client); + if (data->mem_access_attr.attr.name) + sysfs_remove_bin_file(&client->dev.kobj, + &data->mem_access_attr); + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); regulator_put(data->reg_avdd); From 7720ba602703e2157a250e9af8548cdc4020fe9d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 31 May 2013 11:22:58 +0100 Subject: [PATCH 056/154] Input: atmel_mxt_ts - implement improved debug message interface Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 180 +++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ed0de37ffec382..3bf504647aef65 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -194,6 +194,8 @@ struct t9_range { #define MXT_PIXELS_PER_MM 20 +#define DEBUG_MSG_MAX 200 + struct mxt_info { u8 family_id; u8 variant_id; @@ -231,6 +233,11 @@ struct mxt_data { u8 t100_aux_vect; struct bin_attribute mem_access_attr; bool debug_enabled; + bool debug_v2_enabled; + u8 *debug_msg_data; + u16 debug_msg_count; + struct bin_attribute debug_msg_attr; + struct mutex debug_msg_lock; u8 max_reportid; u32 config_crc; u32 info_crc; @@ -336,6 +343,139 @@ static void mxt_dump_message(struct mxt_data *data, u8 *message) data->T5_msg_size, message); } +static void mxt_debug_msg_enable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (data->debug_v2_enabled) + return; + + mutex_lock(&data->debug_msg_lock); + + data->debug_msg_data = kcalloc(DEBUG_MSG_MAX, + data->T5_msg_size, GFP_KERNEL); + if (!data->debug_msg_data) { + dev_err(&data->client->dev, "Failed to allocate buffer\n"); + return; + } + + data->debug_v2_enabled = true; + mutex_unlock(&data->debug_msg_lock); + + dev_info(dev, "Enabled message output\n"); +} + +static void mxt_debug_msg_disable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (!data->debug_v2_enabled) + return; + + dev_info(dev, "disabling message output\n"); + data->debug_v2_enabled = false; + + mutex_lock(&data->debug_msg_lock); + kfree(data->debug_msg_data); + data->debug_msg_data = NULL; + data->debug_msg_count = 0; + mutex_unlock(&data->debug_msg_lock); + dev_info(dev, "Disabled message output\n"); +} + +static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + + mutex_lock(&data->debug_msg_lock); + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return; + } + + if (data->debug_msg_count < DEBUG_MSG_MAX) { + memcpy(data->debug_msg_data + + data->debug_msg_count * data->T5_msg_size, + msg, + data->T5_msg_size); + data->debug_msg_count++; + } else { + dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count); + data->debug_msg_count = 0; + } + + mutex_unlock(&data->debug_msg_lock); + + sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify"); +} + +static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + return -EIO; +} + +static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int count; + size_t bytes_read; + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return 0; + } + + count = bytes / data->T5_msg_size; + + if (count > DEBUG_MSG_MAX) + count = DEBUG_MSG_MAX; + + mutex_lock(&data->debug_msg_lock); + + if (count > data->debug_msg_count) + count = data->debug_msg_count; + + bytes_read = count * data->T5_msg_size; + + memcpy(buf, data->debug_msg_data, bytes_read); + data->debug_msg_count = 0; + + mutex_unlock(&data->debug_msg_lock); + + return bytes_read; +} + +static int mxt_debug_msg_init(struct mxt_data *data) +{ + sysfs_bin_attr_init(&data->debug_msg_attr); + data->debug_msg_attr.attr.name = "debug_msg"; + data->debug_msg_attr.attr.mode = 0666; + data->debug_msg_attr.read = mxt_debug_msg_read; + data->debug_msg_attr.write = mxt_debug_msg_write; + data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX; + + if (sysfs_create_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr) < 0) { + dev_err(&data->client->dev, "Failed to create %s\n", + data->debug_msg_attr.attr.name); + return -EINVAL; + } + + return 0; +} + +static void mxt_debug_msg_remove(struct mxt_data *data) +{ + if (data->debug_msg_attr.attr.name) + sysfs_remove_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr); +} + static int mxt_wait_for_completion(struct mxt_data *data, struct completion *comp, unsigned int timeout_ms) @@ -1020,6 +1160,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) if (dump) mxt_dump_message(data, message); + if (data->debug_v2_enabled) + mxt_debug_msg_add(data, message); + return 1; } @@ -1684,6 +1827,8 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { + mxt_debug_msg_remove(data); + kfree(data->raw_info_block); data->object_table = NULL; data->info = NULL; @@ -2268,6 +2413,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) return error; + error = mxt_debug_msg_init(data); + if (error) + return error; + error = mxt_configure_objects(data); if (error) return error; @@ -2648,6 +2797,31 @@ static ssize_t mxt_debug_enable_show(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%c\n", c); } +static ssize_t mxt_debug_notify_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t mxt_debug_v2_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (i == 1) + mxt_debug_msg_enable(data); + else + mxt_debug_msg_disable(data); + + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + static ssize_t mxt_debug_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -2720,6 +2894,9 @@ static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, + mxt_debug_v2_enable_store); +static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, mxt_debug_enable_store); @@ -2730,6 +2907,8 @@ static struct attribute *mxt_attrs[] = { &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, &dev_attr_debug_enable.attr, + &dev_attr_debug_v2_enable.attr, + &dev_attr_debug_notify.attr, NULL }; @@ -2977,6 +3156,7 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); + mutex_init(&data->debug_msg_lock); error = request_threaded_irq(data->irq, NULL, mxt_interrupt, data->pdata->irqflags | IRQF_ONESHOT, From 139b6e31f10291f5d2293376b63be45084baa792 Mon Sep 17 00:00:00 2001 From: Prajosh Premdas Date: Wed, 30 Apr 2014 19:41:43 +0200 Subject: [PATCH 057/154] Input: atmel_mxt_ts - fixed vdd being checked instead of avdd Fixed the wrong check where vdd was being checked instead of avdd after regulator_get function for avdd Signed-off-by: Prajosh Premdas Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 3bf504647aef65..2311a1c0b30fc5 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2177,8 +2177,8 @@ static void mxt_probe_regulators(struct mxt_data *data) } data->reg_avdd = regulator_get(dev, "avdd"); - if (IS_ERR(data->reg_vdd)) { - error = PTR_ERR(data->reg_vdd); + if (IS_ERR(data->reg_avdd)) { + error = PTR_ERR(data->reg_avdd); dev_err(dev, "Error %d getting avdd regulator\n", error); goto fail_release; } From e4a37de4773aa80eea005916789dcde39b1b0f78 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 10:54:57 +0100 Subject: [PATCH 058/154] Input: atmel_mxt_ts - fix build error in mxt_read_t9_resolution Stephen Rothwell reported this build error: drivers/input/touchscreen/atmel_mxt_ts.c: In function 'mxt_read_t9_resolution': drivers/input/touchscreen/atmel_mxt_ts.c:1043:2: warning: passing argument 1 of '__swab16s' makes pointer from integer without a cast [enabled by default] le16_to_cpus(range.x); ^ Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2311a1c0b30fc5..89fead5c87cefe 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2089,8 +2089,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data) if (error) return error; - le16_to_cpus(range.x); - le16_to_cpus(range.y); + le16_to_cpus(&range.x); + le16_to_cpus(&range.y); error = __mxt_read_reg(client, object->start_address + MXT_T9_ORIENT, From f24ab04cb8f30aac0df69623fd4672bda5c0dbb1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 13:46:40 +0100 Subject: [PATCH 059/154] Input: atmel_mxt_ts - address code indentation issues Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 89fead5c87cefe..7bf31d3c50dc83 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2386,9 +2386,7 @@ static int mxt_initialize(struct mxt_data *data) goto retry_bootloader; } else { if (retry) { - dev_err(&client->dev, - "Could not recover device from " - "bootloader mode\n"); + dev_err(&client->dev, "Could not recover from bootloader mode\n"); /* * We can reflash from this state, so do not * abort init From cff90bffe860c38fd07016529eeb0ac2ca41b0a9 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:11:00 +0100 Subject: [PATCH 060/154] Input: atmel_mxt_ts - implement device tree support Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 108 ++++++++++++++++------- 1 file changed, 76 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7bf31d3c50dc83..ebffffc75af593 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -2989,33 +2990,6 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int mxt_handle_pdata(struct mxt_data *data) -{ - data->pdata = dev_get_platdata(&data->client->dev); - - /* Use provided platform data if present */ - if (data->pdata) { - if (data->pdata->cfg_name) - mxt_update_file_name(&data->client->dev, - &data->cfg_name, - data->pdata->cfg_name, - strlen(data->pdata->cfg_name)); - - return 0; - } - - data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); - if (!data->pdata) { - dev_err(&data->client->dev, "Failed to allocate pdata\n"); - return -ENOMEM; - } - - /* Set default parameters */ - data->pdata->irqflags = IRQF_TRIGGER_FALLING; - - return 0; -} - static int mxt_initialize_t9_input_device(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -3128,6 +3102,45 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) return error; } +#ifdef CONFIG_OF +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + struct mxt_platform_data *pdata; + struct property *prop; + unsigned int *keymap; + int proplen, i, ret; + u32 keycode; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + /* Default to this. Properties can be added to configure it later */ + pdata->irqflags = IRQF_TRIGGER_FALLING; + + prop = of_find_property(client->dev.of_node, "linux,gpio-keymap", + &proplen); + if (prop) { + pdata->t19_num_keys = proplen / sizeof(u32); + + keymap = devm_kzalloc(&client->dev, + pdata->t19_num_keys * sizeof(u32), GFP_KERNEL); + if (!keymap) + return NULL; + pdata->t19_keymap = keymap; + 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 = 0; + keymap[i] = keycode; + } + } + + return pdata; +} +#endif + static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -3145,18 +3158,38 @@ static int mxt_probe(struct i2c_client *client, data->client = client; data->irq = client->irq; + data->pdata = dev_get_platdata(&client->dev); i2c_set_clientdata(client, data); - error = mxt_handle_pdata(data); - if (error) - goto err_free_mem; +#ifdef CONFIG_OF + if (!data->pdata && client->dev.of_node) + data->pdata = mxt_parse_dt(client); +#endif + + if (!data->pdata) { + data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); + if (!data->pdata) { + dev_err(&data->client->dev, "Failed to allocate pdata\n"); + error = -ENOMEM; + goto err_free_mem; + } + + /* Set default parameters */ + data->pdata->irqflags = IRQF_TRIGGER_FALLING; + } + + if (data->pdata->cfg_name) + mxt_update_file_name(&data->client->dev, + &data->cfg_name, + data->pdata->cfg_name, + strlen(data->pdata->cfg_name)); init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); mutex_init(&data->debug_msg_lock); - error = request_threaded_irq(data->irq, NULL, mxt_interrupt, + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, data->pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { @@ -3200,7 +3233,7 @@ static int mxt_probe(struct i2c_client *client, err_free_object: mxt_free_object_table(data); err_free_irq: - free_irq(data->irq, data); + free_irq(client->irq, data); err_free_pdata: if (!dev_get_platdata(&data->client->dev)) kfree(data->pdata); @@ -3265,6 +3298,16 @@ static int mxt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); +#ifdef CONFIG_OF +static const struct of_device_id mxt_of_match[] = { + { .compatible = "atmel,maxtouch", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxt_of_match); +#else +#define mxt_of_match NULL +#endif + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -3278,6 +3321,7 @@ static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, + .of_match_table = mxt_of_match, .pm = &mxt_pm_ops, }, .probe = mxt_probe, From 0158865e2fe9f8a42899d749d49e7d769f799d5e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:24:23 +0100 Subject: [PATCH 061/154] Input: atmel_mxt_ts - fix invalid return from mxt_get_bootloader_version The patch e57a66aa8534: "Input: atmel_mxt_ts - read and report bootloader version" from May 18, 2014, leads to the following static checker warning: drivers/input/touchscreen/atmel_mxt_ts.c:437 mxt_get_bootloader_version( warn: signedness bug returning '(-5)' drivers/input/touchscreen/atmel_mxt_ts.c 429 static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) 430 { 431 struct device *dev = &data->client->dev; 432 u8 buf[3]; 433 434 if (val & MXT_BOOT_EXTENDED_ID) { 435 if (mxt_bootloader_read(data, &buf[0], 3) != 0) { 436 dev_err(dev, "%s: i2c failure\n", __func__); 437 return -EIO; ^^^^ This gets truncated into a number from 0-255 and anyway the caller doesn't check for errors. (reported by Dan Carpenter) Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ebffffc75af593..ec1554c7b053ad 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -607,7 +607,7 @@ static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) if (val & MXT_BOOT_EXTENDED_ID) { if (mxt_bootloader_read(data, &buf[0], 3) != 0) { dev_err(dev, "%s: i2c failure\n", __func__); - return -EIO; + return val; } dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); From 1f6a0fcbc5db1f022912745002f4ecf4d0a93087 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:27:51 +0100 Subject: [PATCH 062/154] Input: atmel_mxt_ts - Set pointer emulation on touchpads Touchpads are pointers, so make sure to pass the correct values to input_mt_report_pointer_emulation(). Without this, tap-to-click doesn't work. Signed-off-by: Benson Leung Signed-off-by: Stephen Warren Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ec1554c7b053ad..6f21f36b771e7c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -852,10 +852,11 @@ static void mxt_input_button(struct mxt_data *data, u8 *message) } } -static void mxt_input_sync(struct input_dev *input_dev) +static void mxt_input_sync(struct mxt_data *data) { - input_mt_report_pointer_emulation(input_dev, false); - input_sync(input_dev); + input_mt_report_pointer_emulation(data->input_dev, + data->pdata->t19_num_keys); + input_sync(data->input_dev); } static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) @@ -911,7 +912,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) if (status & MXT_T9_RELEASE) { input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); - mxt_input_sync(input_dev); + mxt_input_sync(data); } /* A size of zero indicates touch is from a linked T47 Stylus */ @@ -1115,7 +1116,7 @@ static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) input_report_key(input_dev, BTN_STYLUS2, (msg[2] & MXT_T63_STYLUS_BARREL)); - mxt_input_sync(input_dev); + mxt_input_sync(data); } static int mxt_proc_message(struct mxt_data *data, u8 *message) @@ -1242,7 +1243,7 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) end: if (data->update_input) { - mxt_input_sync(data->input_dev); + mxt_input_sync(data); data->update_input = false; } @@ -1265,7 +1266,7 @@ static int mxt_process_messages_until_invalid(struct mxt_data *data) } while (--tries); if (data->update_input) { - mxt_input_sync(data->input_dev); + mxt_input_sync(data); data->update_input = false; } @@ -1305,7 +1306,7 @@ static irqreturn_t mxt_process_messages(struct mxt_data *data) data->last_message_count = total_handled; if (data->update_input) { - mxt_input_sync(data->input_dev); + mxt_input_sync(data); data->update_input = false; } @@ -2928,7 +2929,7 @@ static void mxt_reset_slots(struct mxt_data *data) input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); } - mxt_input_sync(input_dev); + mxt_input_sync(data); } static void mxt_start(struct mxt_data *data) From 70d553be3cfa55f955bf06ec99910f9ce5194f55 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:57:31 +0100 Subject: [PATCH 063/154] Input: atmel_mxt_ts - rename mxt_check_reg_init to mxt_update_cfg Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6f21f36b771e7c..d0fae80c6857df 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1471,7 +1471,7 @@ static int mxt_check_retrigen(struct mxt_data *data) static int mxt_init_t7_power_cfg(struct mxt_data *data); /* - * mxt_check_reg_init - download configuration to chip + * mxt_update_cfg - download configuration to chip * * Atmel Raw Config File Format * @@ -1489,7 +1489,7 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data); * - 2-byte object size as hex * - array of 1-byte hex values */ -static int mxt_check_reg_init(struct mxt_data *data) +static int mxt_update_cfg(struct mxt_data *data) { struct device *dev = &data->client->dev; struct mxt_info cfg_info; @@ -2435,8 +2435,7 @@ static int mxt_configure_objects(struct mxt_data *data) return error; } - /* Check register init values */ - error = mxt_check_reg_init(data); + error = mxt_update_cfg(data); if (error) { dev_err(&client->dev, "Error %d initialising configuration\n", error); From 8765349094c5fef3696e1e10fbb6639d727cf1c4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:58:42 +0100 Subject: [PATCH 064/154] Input: atmel_mxt_ts - change reset GPIO warning to debug message Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d0fae80c6857df..88437f3985beec 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2167,7 +2167,7 @@ static void mxt_probe_regulators(struct mxt_data *data) * voltage */ if (!data->pdata->gpio_reset) { - dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + dev_dbg(dev, "Must have reset GPIO to use regulator support\n"); goto fail; } From 0dacc744606663d47f191ef1b153f575512105ba Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:59:46 +0100 Subject: [PATCH 065/154] Input: atmel_mxt_ts - do not reset slots when input device is null Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 88437f3985beec..46eaa978deeb27 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2921,6 +2921,9 @@ static void mxt_reset_slots(struct mxt_data *data) unsigned int num_mt_slots; int id; + if (!input_dev) + return; + num_mt_slots = data->num_touchids + data->num_stylusids; for (id = 0; id < num_mt_slots; id++) { From 5a4c198d9241025068b2704d7e85ac3c55b9c9e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 10:06:04 +0100 Subject: [PATCH 066/154] Add documentation for device tree Signed-off-by: Nick Dyer --- .../bindings/input/atmel,maxtouch.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/atmel,maxtouch.txt diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt new file mode 100644 index 00000000000000..60d63391a6a8c8 --- /dev/null +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -0,0 +1,25 @@ +Atmel maXTouch touchscreen/touchpad + +Required properties: +- compatible: + atmel,maxtouch + +- reg: The I2C address of the device + +- interrupts: The sink for the touchpad's IRQ output + See ../interrupt-controller/interrupts.txt + +Optional properties for main touchpad device: + +- linux,gpio-keymap: An array of up to 4 entries indicating the Linux + keycode generated by each GPIO. Linux keycodes are defined in + . + +Example: + + touch@4b { + compatible = "atmel,maxtouch"; + reg = <0x4b>; + interrupt-parent = <&gpio>; + interrupts = ; + }; From 4eedb0f2c8c5aae878eb9a862c7a83c0238a4177 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 11:48:32 +0100 Subject: [PATCH 067/154] Input: atmel_mxt_ts - Initialise input slots with INPUT_MT_DIRECT 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 --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 46eaa978deeb27..45eafb650e4f10 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2321,7 +2321,7 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) 0, 255, 0, 0); /* For multi touch */ - error = input_mt_init_slots(input_dev, data->num_touchids, 0); + error = input_mt_init_slots(input_dev, data->num_touchids, INPUT_MT_DIRECT); if (error) { dev_err(dev, "Error %d initialising slots\n", error); goto err_free_mem; @@ -3042,6 +3042,8 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) MXT_PIXELS_PER_MM); input_dev->name = "Atmel maXTouch Touchpad"; + } else { + mt_flags |= INPUT_MT_DIRECT; } /* For single touch */ From 915c13589f0df45e4e2a85675288dd5a7efb024b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 12:14:31 +0100 Subject: [PATCH 068/154] Input: atmel_mxt_ts - use async firmware loader interface for config Using request_firmware_nowait() allows the driver to be built into the kernel and use the firmware loader interface, without the probe failing with a 60s timeout due to the root filesystem not being available. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 68 ++++++++++++++---------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 45eafb650e4f10..05c83398f9f482 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1489,12 +1489,11 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data); * - 2-byte object size as hex * - array of 1-byte hex values */ -static int mxt_update_cfg(struct mxt_data *data) +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) { struct device *dev = &data->client->dev; struct mxt_info cfg_info; struct mxt_object *object; - const struct firmware *cfg = NULL; int ret; int offset; int data_pos; @@ -1508,18 +1507,6 @@ static int mxt_update_cfg(struct mxt_data *data) u8 val; u16 reg; - if (!data->cfg_name) { - dev_dbg(dev, "Skipping cfg download\n"); - return 0; - } - - ret = request_firmware(&cfg, data->cfg_name, dev); - if (ret < 0) { - dev_err(dev, "Failure to request config file %s\n", - data->cfg_name); - return 0; - } - mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { @@ -2363,7 +2350,13 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) } static int mxt_initialize_t9_input_device(struct mxt_data *data); -static int mxt_configure_objects(struct mxt_data *data); +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ + mxt_configure_objects(ctx, cfg); +} static int mxt_initialize(struct mxt_data *data) { @@ -2417,29 +2410,39 @@ static int mxt_initialize(struct mxt_data *data) if (error) return error; - error = mxt_configure_objects(data); - if (error) - return error; + if (data->cfg_name) { + request_firmware_nowait(THIS_MODULE, true, data->cfg_name, + &data->client->dev, GFP_KERNEL, data, + mxt_config_cb); + } else { + error = mxt_configure_objects(data, NULL); + if (error) + return error; + } return 0; + +err_free_object_table: + mxt_free_object_table(data); + return error; } -static int mxt_configure_objects(struct mxt_data *data) +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg) { - struct i2c_client *client = data->client; + struct device *dev = &data->client->dev; int error; error = mxt_init_t7_power_cfg(data); if (error) { - dev_err(&client->dev, "Failed to initialize power cfg\n"); + dev_err(dev, "Failed to initialize power cfg\n"); return error; } - error = mxt_update_cfg(data); - if (error) { - dev_err(&client->dev, "Error %d initialising configuration\n", - error); - return error; + if (cfg) { + error = mxt_update_cfg(data, cfg); + if (error) + dev_warn(dev, "Error %d updating config\n", error); } if (data->T9_reportid_min) { @@ -2451,7 +2454,7 @@ static int mxt_configure_objects(struct mxt_data *data) if (error) return error; } else { - dev_warn(&client->dev, "No touch object detected\n"); + dev_warn(dev, "No touch object detected\n"); } return 0; @@ -2749,6 +2752,7 @@ static ssize_t mxt_update_cfg_store(struct device *dev, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); + const struct firmware *cfg; int ret; if (data->in_bootloader) { @@ -2760,6 +2764,14 @@ static ssize_t mxt_update_cfg_store(struct device *dev, if (ret) return ret; + ret = request_firmware(&cfg, data->cfg_name, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", + data->cfg_name); + ret = -ENOENT; + goto out; + } + data->updating_config = true; mxt_free_input_device(data); @@ -2776,7 +2788,7 @@ static ssize_t mxt_update_cfg_store(struct device *dev, data->suspended = false; } - ret = mxt_configure_objects(data); + ret = mxt_configure_objects(data, cfg); if (ret) goto out; From aee662e22e75169aac6a3fd4dfbb622cd8ec69e8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:41:40 +0100 Subject: [PATCH 069/154] Input: atmel_mxt_ts - improve error handling in initialise functions Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 05c83398f9f482..1df6547bf7a3c8 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2400,15 +2400,15 @@ static int mxt_initialize(struct mxt_data *data) error = mxt_check_retrigen(data); if (error) - return error; + goto err_free_object_table; error = mxt_acquire_irq(data); if (error) - return error; + goto err_free_object_table; error = mxt_debug_msg_init(data); if (error) - return error; + goto err_free_object_table; if (data->cfg_name) { request_firmware_nowait(THIS_MODULE, true, data->cfg_name, @@ -2417,7 +2417,7 @@ static int mxt_initialize(struct mxt_data *data) } else { error = mxt_configure_objects(data, NULL); if (error) - return error; + goto err_free_object_table; } return 0; @@ -2436,7 +2436,7 @@ static int mxt_configure_objects(struct mxt_data *data, error = mxt_init_t7_power_cfg(data); if (error) { dev_err(dev, "Failed to initialize power cfg\n"); - return error; + goto err_free_object_table; } if (cfg) { @@ -2448,16 +2448,20 @@ static int mxt_configure_objects(struct mxt_data *data, if (data->T9_reportid_min) { error = mxt_initialize_t9_input_device(data); if (error) - return error; + goto err_free_object_table; } else if (data->T100_reportid_min) { error = mxt_initialize_t100_input_device(data); if (error) - return error; + goto err_free_object_table; } else { dev_warn(dev, "No touch object detected\n"); } return 0; + +err_free_object_table: + mxt_free_object_table(data); + return error; } /* Firmware Version is returned as Major.Minor.Build */ From 347dff20141e8516cb048ed002f9e9c4cb68e493 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:29:26 +0100 Subject: [PATCH 070/154] Input: atmel_mxt_ts - improve device tree parsing for earlier kernels of_property_read_u32_index() was only introduced in v3.10. Using the array operation is simpler anyway. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 1df6547bf7a3c8..be849456f2dc5f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,6 +28,10 @@ #include #include +#ifdef CONFIG_OF +#include +#endif + /* Configuration file */ #define MXT_CFG_MAGIC "OBP_RAW V1" @@ -3129,8 +3133,7 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) struct mxt_platform_data *pdata; struct property *prop; unsigned int *keymap; - int proplen, i, ret; - u32 keycode; + int proplen, ret; pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -3149,12 +3152,14 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) if (!keymap) return NULL; pdata->t19_keymap = keymap; - 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 = 0; - keymap[i] = keycode; + + ret = of_property_read_u32_array(client->dev.of_node, + "linux,gpio-keymap", keymap, pdata->t19_num_keys); + if (ret) { + dev_err(&client->dev, + "Unable to read device tree key codes: %d\n", + ret); + return NULL; } } From fcfae57a9c49ca5f355dfbc8dd03b00267772bdd Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:30:46 +0100 Subject: [PATCH 071/154] Input: atmel_mxt_ts - add cfg name and input name to device tree Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index be849456f2dc5f..1af0957aa274a6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3131,32 +3131,43 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; + struct device *dev = &client->dev; struct property *prop; unsigned int *keymap; int proplen, ret; - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; + /* reset gpio */ + pdata->gpio_reset = of_get_named_gpio_flags(dev->of_node, + "atmel,reset-gpio", 0, NULL); + /* Default to this. Properties can be added to configure it later */ pdata->irqflags = IRQF_TRIGGER_FALLING; - prop = of_find_property(client->dev.of_node, "linux,gpio-keymap", - &proplen); + of_property_read_string(dev->of_node, "atmel,cfg_name", + &pdata->cfg_name); + + of_property_read_string(dev->of_node, "atmel,input_name", + &pdata->input_name); + + prop = of_find_property(dev->of_node, "linux,gpio-keymap", &proplen); if (prop) { pdata->t19_num_keys = proplen / sizeof(u32); - keymap = devm_kzalloc(&client->dev, + keymap = devm_kzalloc(dev, pdata->t19_num_keys * sizeof(u32), GFP_KERNEL); if (!keymap) return NULL; + pdata->t19_keymap = keymap; ret = of_property_read_u32_array(client->dev.of_node, "linux,gpio-keymap", keymap, pdata->t19_num_keys); if (ret) { - dev_err(&client->dev, + dev_err(dev, "Unable to read device tree key codes: %d\n", ret); return NULL; From d0c1ce6f8c4c7e838cb666c6bd12550dd267456f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:38:02 +0100 Subject: [PATCH 072/154] Input: atmel_mxt_ts - reorder mxt_free_object_table to improve robustness Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 1af0957aa274a6..48c288ca4a6434 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1821,16 +1821,15 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { mxt_debug_msg_remove(data); + mxt_free_input_device(data); - kfree(data->raw_info_block); data->object_table = NULL; data->info = NULL; + kfree(data->raw_info_block); data->raw_info_block = NULL; kfree(data->msg_buf); data->msg_buf = NULL; - mxt_free_input_device(data); - data->T5_address = 0; data->T5_msg_size = 0; data->T6_reportid = 0; From c70631bf74326ca7c81129480e668431f1f6edf0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:28 +0100 Subject: [PATCH 073/154] Input: atmel_mxt_ts - move functions in file to improve code layout Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 333 ++++++++++++----------- 1 file changed, 167 insertions(+), 166 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 48c288ca4a6434..226ad28c49291f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2063,54 +2063,6 @@ static int mxt_read_info_block(struct mxt_data *data) return error; } -static int mxt_read_t9_resolution(struct mxt_data *data) -{ - struct i2c_client *client = data->client; - int error; - struct t9_range range; - unsigned char orient; - struct mxt_object *object; - - object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); - if (!object) - return -EINVAL; - - error = __mxt_read_reg(client, - object->start_address + MXT_T9_RANGE, - sizeof(range), &range); - if (error) - return error; - - le16_to_cpus(&range.x); - le16_to_cpus(&range.y); - - error = __mxt_read_reg(client, - object->start_address + MXT_T9_ORIENT, - 1, &orient); - if (error) - return error; - - /* Handle default values */ - if (range.x == 0) - range.x = 1023; - - if (range.y == 0) - range.y = 1023; - - if (orient & MXT_T9_ORIENT_SWITCH) { - data->max_x = range.y; - data->max_y = range.x; - } else { - data->max_x = range.x; - data->max_y = range.y; - } - - dev_dbg(&client->dev, - "Touchscreen size X%uY%u\n", data->max_x, data->max_y); - - return 0; -} - static void mxt_regulator_enable(struct mxt_data *data) { int error; @@ -2189,6 +2141,173 @@ static void mxt_probe_regulators(struct mxt_data *data) data->use_regulator = false; } +static int mxt_read_t9_resolution(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct t9_range range; + unsigned char orient; + struct mxt_object *object; + + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_RANGE, + sizeof(range), &range); + if (error) + return error; + + le16_to_cpus(&range.x); + le16_to_cpus(&range.y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_ORIENT, + 1, &orient); + if (error) + return error; + + /* Handle default values */ + if (range.x == 0) + range.x = 1023; + + if (range.y == 0) + range.y = 1023; + + if (orient & MXT_T9_ORIENT_SWITCH) { + data->max_x = range.y; + data->max_y = range.x; + } else { + data->max_x = range.x; + data->max_y = range.y; + } + + dev_dbg(&client->dev, + "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + +static void mxt_start(struct mxt_data *data); +static void mxt_stop(struct mxt_data *data); +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t9_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; + struct input_dev *input_dev; + int error; + unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; + + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + if (pdata->t19_num_keys) { + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); + + mt_flags |= INPUT_MT_POINTER; + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; + } else { + mt_flags |= INPUT_MT_DIRECT; + } + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + num_mt_slots = data->num_touchids + data->num_stylusids; + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + /* For T63 active stylus */ + if (data->T63_reportid_min) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + + /* For T15 key array */ + if (data->T15_reportid_min) { + data->t15_keystatus = 0; + + for (i = 0; i < data->pdata->t15_num_keys; i++) + input_set_capability(input_dev, EV_KEY, + data->pdata->t15_keymap[i]); + } + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + static int mxt_read_t100_config(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -2267,9 +2386,6 @@ static int mxt_read_t100_config(struct mxt_data *data) return 0; } -static int mxt_input_open(struct input_dev *dev); -static void mxt_input_close(struct input_dev *dev); - static int mxt_initialize_t100_input_device(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -2352,7 +2468,6 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) return error; } -static int mxt_initialize_t9_input_device(struct mxt_data *data); static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg); @@ -3012,120 +3127,6 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int mxt_initialize_t9_input_device(struct mxt_data *data) -{ - struct device *dev = &data->client->dev; - const struct mxt_platform_data *pdata = data->pdata; - struct input_dev *input_dev; - int error; - unsigned int num_mt_slots; - unsigned int mt_flags = 0; - int i; - - error = mxt_read_t9_resolution(data); - if (error) - dev_warn(dev, "Failed to initialize T9 resolution\n"); - - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - input_dev->name = "Atmel maXTouch Touchscreen"; - input_dev->phys = data->phys; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = dev; - input_dev->open = mxt_input_open; - input_dev->close = mxt_input_close; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } else { - mt_flags |= INPUT_MT_DIRECT; - } - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); - - /* For multi touch */ - num_mt_slots = data->num_touchids + data->num_stylusids; - error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); - if (error) { - dev_err(dev, "Error %d initialising slots\n", error); - goto err_free_mem; - } - - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); - input_set_abs_params(input_dev, ABS_MT_ORIENTATION, - 0, 255, 0, 0); - - /* For T63 active stylus */ - if (data->T63_reportid_min) { - input_set_capability(input_dev, EV_KEY, BTN_STYLUS); - input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); - input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - } - - /* For T15 key array */ - if (data->T15_reportid_min) { - data->t15_keystatus = 0; - - for (i = 0; i < data->pdata->t15_num_keys; i++) - input_set_capability(input_dev, EV_KEY, - data->pdata->t15_keymap[i]); - } - - input_set_drvdata(input_dev, data); - - error = input_register_device(input_dev); - if (error) { - dev_err(dev, "Error %d registering input device\n", error); - goto err_free_mem; - } - - data->input_dev = input_dev; - - return 0; - -err_free_mem: - input_free_device(input_dev); - return error; -} - #ifdef CONFIG_OF static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { From 18596d7290ee9f0d4992369e2111c675c96ccc3f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:50 +0100 Subject: [PATCH 074/154] Input: atmel_mxt_ts - check gpio validity in mxt_probe_regulators Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 226ad28c49291f..c0ea59b2109e68 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2108,7 +2108,7 @@ static void mxt_probe_regulators(struct mxt_data *data) * must be kept low until some time after regulators come up to * voltage */ - if (!data->pdata->gpio_reset) { + if (!gpio_is_valid(data->pdata->gpio_reset)) { dev_dbg(dev, "Must have reset GPIO to use regulator support\n"); goto fail; } From c438f1cf18fc31a8db4d5868ee3237f8bcc709a0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:40:12 +0100 Subject: [PATCH 075/154] Input: atmel_mxt_ts - wrap long line Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c0ea59b2109e68..193a9cc26ff66b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2427,7 +2427,8 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) 0, 255, 0, 0); /* For multi touch */ - error = input_mt_init_slots(input_dev, data->num_touchids, INPUT_MT_DIRECT); + error = input_mt_init_slots(input_dev, data->num_touchids, + INPUT_MT_DIRECT); if (error) { dev_err(dev, "Error %d initialising slots\n", error); goto err_free_mem; From 4e8a1ab88d6ada42d8872276ee482e0b5fafe699 Mon Sep 17 00:00:00 2001 From: karl tsou Date: Thu, 19 Jun 2014 00:42:34 +0100 Subject: [PATCH 076/154] Input: atmel_mxt_ts - support config checksum attribute in sysfs Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 193a9cc26ff66b..2834ec14d113d3 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2583,6 +2583,14 @@ static int mxt_configure_objects(struct mxt_data *data, return error; } +/* Configuration crc check sum is returned as hex xxxxxx */ +static ssize_t mxt_config_csum_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%06x\n", data->config_crc); +} + /* Firmware Version is returned as Major.Minor.Build */ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -3033,6 +3041,7 @@ static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, mxt_debug_enable_store); +static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, @@ -3043,6 +3052,7 @@ static struct attribute *mxt_attrs[] = { &dev_attr_debug_enable.attr, &dev_attr_debug_v2_enable.attr, &dev_attr_debug_notify.attr, + &dev_attr_config_csum.attr, NULL }; From 04280cab6152c082f3023e04771d3a679f6b930e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:45:33 +0100 Subject: [PATCH 077/154] Input: atmel_mxt_ts - allocate pdata using devm_kzalloc By using this function instead, there is no necessity to free on remove or probe failure, since it is handled for us. This also fixes an failure when kfree() is called on memory allocated using devm_kzalloc() in the DT parse function. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2834ec14d113d3..1dcf71ed6c458c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3215,9 +3215,10 @@ static int mxt_probe(struct i2c_client *client, #endif if (!data->pdata) { - data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); + data->pdata = devm_kzalloc(&client->dev, sizeof(*data->pdata), + GFP_KERNEL); if (!data->pdata) { - dev_err(&data->client->dev, "Failed to allocate pdata\n"); + dev_err(&client->dev, "Failed to allocate pdata\n"); error = -ENOMEM; goto err_free_mem; } @@ -3242,7 +3243,7 @@ static int mxt_probe(struct i2c_client *client, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_pdata; + goto err_free_mem; } mxt_probe_regulators(data); @@ -3282,9 +3283,6 @@ static int mxt_probe(struct i2c_client *client, mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); -err_free_pdata: - if (!dev_get_platdata(&data->client->dev)) - kfree(data->pdata); err_free_mem: kfree(data); return error; @@ -3303,8 +3301,6 @@ static int mxt_remove(struct i2c_client *client) regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); mxt_free_object_table(data); - if (!dev_get_platdata(&data->client->dev)) - kfree(data->pdata); kfree(data); return 0; From 8d5ef3bad3a824fa8fa4e3fd5359b6e809985e9b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:46:06 +0100 Subject: [PATCH 078/154] Input: atmel_mxt_ts - use of_match_ptr rather than ifdefs Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 1dcf71ed6c458c..dc705abe29fc1b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3342,15 +3342,11 @@ static int mxt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); -#ifdef CONFIG_OF static const struct of_device_id mxt_of_match[] = { { .compatible = "atmel,maxtouch", }, {}, }; MODULE_DEVICE_TABLE(of, mxt_of_match); -#else -#define mxt_of_match NULL -#endif static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, @@ -3365,7 +3361,7 @@ static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, - .of_match_table = mxt_of_match, + .of_match_table = of_match_ptr(mxt_of_match), .pm = &mxt_pm_ops, }, .probe = mxt_probe, From 55d857bbff398cef56c56c66c07098e23a50a43c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 20 Jun 2014 10:30:13 +0100 Subject: [PATCH 079/154] Input: atmel_mxt_ts - fix bug with endianness conversion in T100 intialisation Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dc705abe29fc1b..fc3bc386d97829 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2327,7 +2327,7 @@ static int mxt_read_t100_config(struct mxt_data *data) if (error) return error; - le16_to_cpus(range_x); + le16_to_cpus(&range_x); error = __mxt_read_reg(client, object->start_address + MXT_T100_YRANGE, @@ -2335,7 +2335,7 @@ static int mxt_read_t100_config(struct mxt_data *data) if (error) return error; - le16_to_cpus(range_y); + le16_to_cpus(&range_y); error = __mxt_read_reg(client, object->start_address + MXT_T100_CFG1, From 83040d95bb5633799bd7fa2148958ffa6196d4b8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 3 Jul 2014 11:11:20 +0100 Subject: [PATCH 080/154] Input: atmel_mxt_ts - leave platform to specify irqflags when using DT The function to check the interrupt configuration must use irq_get_trigger_type() to allow for this case. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fc3bc386d97829..ae8e8ddebfcb3a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1450,10 +1451,12 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) static int mxt_check_retrigen(struct mxt_data *data) { struct i2c_client *client = data->client; + struct irq_data *irqd; int error; int val; - if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + irqd = irq_get_irq_data(data->irq); + if (irqd_get_trigger_type(irqd) & IRQF_TRIGGER_LOW) return 0; if (data->T18_address) { @@ -3155,9 +3158,6 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) pdata->gpio_reset = of_get_named_gpio_flags(dev->of_node, "atmel,reset-gpio", 0, NULL); - /* Default to this. Properties can be added to configure it later */ - pdata->irqflags = IRQF_TRIGGER_FALLING; - of_property_read_string(dev->of_node, "atmel,cfg_name", &pdata->cfg_name); From fb52903a15011123344b69ba4c2d299e59189742 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 4 Jul 2014 12:35:57 +0100 Subject: [PATCH 081/154] Input: atmel_mxt_ts - fix bug in info CRC error output Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ae8e8ddebfcb3a..501f1aab8ab55f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2035,7 +2035,7 @@ static int mxt_read_info_block(struct mxt_data *data) if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { dev_err(&client->dev, "Info Block CRC error calculated=0x%06X read=0x%06X\n", - data->info_crc, calculated_crc); + calculated_crc, data->info_crc); error = -EIO; goto err_free_mem; } From 7e2585063bed90ef3239da54098a9004d779626f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 23 Jul 2014 15:53:33 +0100 Subject: [PATCH 082/154] Input: atmel_mxt_ts - fix interrupt documentation for tegra As advised by Stephen Warren, this was incorrect. Also slightly update wording. Signed-off-by: Nick Dyer --- Documentation/devicetree/bindings/input/atmel,maxtouch.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt index 60d63391a6a8c8..6319f07bfbc124 100644 --- a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -6,10 +6,10 @@ Required properties: - reg: The I2C address of the device -- interrupts: The sink for the touchpad's IRQ output +- interrupts: The sink for the touch controller CHG/IRQ output See ../interrupt-controller/interrupts.txt -Optional properties for main touchpad device: +Optional properties for maXTouch device: - linux,gpio-keymap: An array of up to 4 entries indicating the Linux keycode generated by each GPIO. Linux keycodes are defined in @@ -21,5 +21,5 @@ Example: compatible = "atmel,maxtouch"; reg = <0x4b>; interrupt-parent = <&gpio>; - interrupts = ; + interrupts = ; }; From cf896abf5d0bc0032538b9b2c787a8d3c5afeff8 Mon Sep 17 00:00:00 2001 From: Janus Cheng Date: Sun, 20 Jul 2014 18:36:14 +0800 Subject: [PATCH 083/154] Input: atmel_mxt_ts - check data->input_dev is not null in mxt_input_sync() * Symptom: if update_fw and update_cfg, kernel panic occurs. * Reproducibility: 10% * Root Cause: - If update_fw, the T6 will send a CFG_ERR message periodically. - After that, update_cfg process begin, the mxt_update_cfg_store() will invoke mxt_free_input_device() and nullify data->input_dev. - The CFG_ERR message will trigger mxt_interrupt(), and mxt_input_sync() will be invoked by mxt_process_messages_t44(). And mxt_input_sync() references a NULL data->input_dev and kernel panic occurs. TrackerRMS TKT-004235 --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 501f1aab8ab55f..dd5164f48c5f53 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -859,9 +859,11 @@ static void mxt_input_button(struct mxt_data *data, u8 *message) static void mxt_input_sync(struct mxt_data *data) { - input_mt_report_pointer_emulation(data->input_dev, - data->pdata->t19_num_keys); - input_sync(data->input_dev); + if (data->input_dev) { + input_mt_report_pointer_emulation(data->input_dev, + data->pdata->t19_num_keys); + input_sync(data->input_dev); + } } static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) From 49c0c24735bf54b26de2c23a933b99090313bbc4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 14:50:56 -0700 Subject: [PATCH 084/154] Input: atmel_mxt_ts - simplify mxt_initialize a bit I think having control flow with 2 goto/labels/flags is quite hard to read, this version is a bit more readable IMO. Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 69 ++++++++++++------------ 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dd5164f48c5f53..48d37a742c24c9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -569,6 +569,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) case 0x5b: bootloader = appmode - 0x26; break; + default: dev_err(&data->client->dev, "Appmode i2c address 0x%02x not found\n", @@ -580,20 +581,20 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) return 0; } -static int mxt_probe_bootloader(struct mxt_data *data, bool retry) +static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) { struct device *dev = &data->client->dev; - int ret; + int error; u8 val; bool crc_failure; - ret = mxt_lookup_bootloader_address(data, retry); - if (ret) - return ret; + error = mxt_lookup_bootloader_address(data, alt_address); + if (error) + return error; - ret = mxt_bootloader_read(data, &val, 1); - if (ret) - return ret; + error = mxt_bootloader_read(data, &val, 1); + if (error) + return error; /* Check app crc fail mode */ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; @@ -2485,41 +2486,39 @@ static void mxt_config_cb(const struct firmware *cfg, void *ctx) static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; + int recovery_attempts = 0; int error; - bool alt_bootloader_addr = false; - bool retry = false; -retry_info: - error = mxt_read_info_block(data); - if (error) { -retry_bootloader: - error = mxt_probe_bootloader(data, alt_bootloader_addr); + while (1) { + error = mxt_read_info_block(data); + if (!error) + break; + + /* Check bootloader state */ + error = mxt_probe_bootloader(data, false); if (error) { - if (alt_bootloader_addr) { + dev_info(&client->dev, "Trying alternate bootloader address\n"); + error = mxt_probe_bootloader(data, true); + if (error) { /* Chip is not in appmode or bootloader mode */ return error; } + } - dev_info(&client->dev, "Trying alternate bootloader address\n"); - alt_bootloader_addr = true; - goto retry_bootloader; - } else { - if (retry) { - dev_err(&client->dev, "Could not recover from bootloader mode\n"); - /* - * We can reflash from this state, so do not - * abort init - */ - data->in_bootloader = true; - return 0; - } - - /* Attempt to exit bootloader into app mode */ - mxt_send_bootloader_cmd(data, false); - msleep(MXT_FW_RESET_TIME); - retry = true; - goto retry_info; + /* OK, we are in bootloader, see if we can recover */ + if (++recovery_attempts > 1) { + dev_err(&client->dev, "Could not recover from bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort initialization. + */ + data->in_bootloader = true; + return 0; } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); } error = mxt_check_retrigen(data); From 8715dc29130d6c5502dd3044203086bb12afdbd3 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:21:48 +0100 Subject: [PATCH 085/154] Input: atmel_mxt_ts - rework platform data handling --- drivers/input/touchscreen/atmel_mxt_ts.c | 83 +++++++++++++----------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 48d37a742c24c9..b3985b1e09fcc1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -225,7 +225,7 @@ struct mxt_data { struct i2c_client *client; struct input_dev *input_dev; char phys[64]; /* device physical location */ - struct mxt_platform_data *pdata; + const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info *info; void *raw_info_block; @@ -3146,56 +3146,79 @@ static void mxt_input_close(struct input_dev *dev) static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; - struct device *dev = &client->dev; - struct property *prop; - unsigned int *keymap; + u32 *keymap; int proplen, ret; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!client->dev.of_node) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - return NULL; + return ERR_PTR(-ENOMEM); /* reset gpio */ - pdata->gpio_reset = of_get_named_gpio_flags(dev->of_node, + pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node, "atmel,reset-gpio", 0, NULL); - of_property_read_string(dev->of_node, "atmel,cfg_name", + of_property_read_string(client->dev.of_node, "atmel,cfg_name", &pdata->cfg_name); - of_property_read_string(dev->of_node, "atmel,input_name", + of_property_read_string(client->dev.of_node, "atmel,input_name", &pdata->input_name); - prop = of_find_property(dev->of_node, "linux,gpio-keymap", &proplen); - if (prop) { + if (of_find_property(client->dev.of_node, "linux,gpio-keymap", + &proplen)) { pdata->t19_num_keys = proplen / sizeof(u32); - keymap = devm_kzalloc(dev, - pdata->t19_num_keys * sizeof(u32), GFP_KERNEL); + keymap = devm_kzalloc(&client->dev, + pdata->t19_num_keys * sizeof(keymap[0]), + GFP_KERNEL); if (!keymap) - return NULL; - - pdata->t19_keymap = keymap; + return ERR_PTR(-ENOMEM); ret = of_property_read_u32_array(client->dev.of_node, "linux,gpio-keymap", keymap, pdata->t19_num_keys); if (ret) { - dev_err(dev, + dev_err(&client->dev, "Unable to read device tree key codes: %d\n", ret); return NULL; } + + pdata->t19_keymap = keymap; } return pdata; } +#else +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + struct mxt_platform_data *pdata; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + /* Set default parameters */ + pdata->irqflags = IRQF_TRIGGER_FALLING; + + return pdata; +} #endif -static int mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mxt_data *data; + const struct mxt_platform_data *pdata; int error; + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = mxt_parse_dt(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); if (!data) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -3206,28 +3229,10 @@ static int mxt_probe(struct i2c_client *client, client->adapter->nr, client->addr); data->client = client; + data->pdata = pdata; data->irq = client->irq; - data->pdata = dev_get_platdata(&client->dev); i2c_set_clientdata(client, data); -#ifdef CONFIG_OF - if (!data->pdata && client->dev.of_node) - data->pdata = mxt_parse_dt(client); -#endif - - if (!data->pdata) { - data->pdata = devm_kzalloc(&client->dev, sizeof(*data->pdata), - GFP_KERNEL); - if (!data->pdata) { - dev_err(&client->dev, "Failed to allocate pdata\n"); - error = -ENOMEM; - goto err_free_mem; - } - - /* Set default parameters */ - data->pdata->irqflags = IRQF_TRIGGER_FALLING; - } - if (data->pdata->cfg_name) mxt_update_file_name(&data->client->dev, &data->cfg_name, @@ -3240,7 +3245,7 @@ static int mxt_probe(struct i2c_client *client, mutex_init(&data->debug_msg_lock); error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - data->pdata->irqflags | IRQF_ONESHOT, + pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); From 97396a078d4a12421860f39be5fa9f6a20bf306f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:22:25 +0100 Subject: [PATCH 086/154] Input: atmel_mxt_ts - add error check to request_firmware_nowait() Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b3985b1e09fcc1..a5570f58833f33 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2534,9 +2534,14 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; if (data->cfg_name) { - request_firmware_nowait(THIS_MODULE, true, data->cfg_name, - &data->client->dev, GFP_KERNEL, data, - mxt_config_cb); + error = request_firmware_nowait(THIS_MODULE, true, + data->cfg_name, &data->client->dev, + GFP_KERNEL, data, mxt_config_cb); + if (error) { + dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", + error); + goto err_free_object_table; + } } else { error = mxt_configure_objects(data, NULL); if (error) From d59050a1db6cfc99f6f8e24e91279b18f9950788 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 16:29:55 -0700 Subject: [PATCH 087/154] Input: atmel_mxt_ts - split config update a bit Let's split config update code a bit so it is hopefully a bit easier to read. Also, the firmware update callback should be the entity releasing firmware blob, not lower layers. Signed-off-by: Dmitry Torokhov [release firmware when updating via update_cfg_store] Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 275 ++++++++++++----------- 1 file changed, 149 insertions(+), 126 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a5570f58833f33..115bf7a7ef9b40 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1478,6 +1478,133 @@ static int mxt_check_retrigen(struct mxt_data *data) return 0; } +static int mxt_prepare_cfg_mem(struct mxt_data *data, + const struct firmware *cfg, + unsigned int data_pos, + unsigned int cfg_start_ofs, + u8 *config_mem, + size_t config_mem_size) +{ + struct device *dev = &data->client->dev; + struct mxt_object *object; + unsigned int type, instance, size, byte_offset; + int offset; + int ret; + int i; + u16 reg; + u8 val; + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + return -EINVAL; + } + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + data_pos += offset; + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + return -EINVAL; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + return -EINVAL; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if (byte_offset >= 0 && + byte_offset <= config_mem_size) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + return -EINVAL; + } + } + } + + return 0; +} + +static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, + u8 *config_mem, size_t config_mem_size) +{ + unsigned int byte_offset = 0; + int error; + + /* Write configuration as blocks */ + while (byte_offset < config_mem_size) { + unsigned int size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + error = __mxt_write_reg(data->client, + cfg_start + byte_offset, + size, config_mem + byte_offset); + if (error) { + dev_err(&data->client->dev, + "Config write error, ret=%d\n", error); + return error; + } + + byte_offset += size; + } + + return 0; +} + static int mxt_init_t7_power_cfg(struct mxt_data *data); /* @@ -1503,26 +1630,20 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) { struct device *dev = &data->client->dev; struct mxt_info cfg_info; - struct mxt_object *object; int ret; int offset; int data_pos; - int byte_offset; int i; int cfg_start_ofs; u32 info_crc, config_crc, calculated_crc; u8 *config_mem; size_t config_mem_size; - unsigned int type, instance, size; - u8 val; - u16 reg; mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { dev_err(dev, "Unrecognised config file\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos = strlen(MXT_CFG_MAGIC); @@ -1534,8 +1655,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) &offset); if (ret != 1) { dev_err(dev, "Bad format\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; @@ -1543,30 +1663,26 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) if (cfg_info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } if (cfg_info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } /* Read CRCs */ ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; @@ -1582,8 +1698,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) } else if (config_crc == data->config_crc) { dev_dbg(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - ret = 0; - goto release; + return 0; } else { dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", data->config_crc, config_crc); @@ -1602,93 +1717,13 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) config_mem = kzalloc(config_mem_size, GFP_KERNEL); if (!config_mem) { dev_err(dev, "Failed to allocate memory\n"); - ret = -ENOMEM; - goto release; + return -ENOMEM; } - while (data_pos < cfg->size) { - /* Read type, instance, length */ - ret = sscanf(cfg->data + data_pos, "%x %x %x%n", - &type, &instance, &size, &offset); - if (ret == 0) { - /* EOF */ - break; - } else if (ret != 3) { - dev_err(dev, "Bad format: failed to parse object\n"); - ret = -EINVAL; - goto release_mem; - } - data_pos += offset; - - object = mxt_get_object(data, type); - if (!object) { - /* Skip object */ - for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); - data_pos += offset; - } - continue; - } - - if (size > mxt_obj_size(object)) { - /* - * Either we are in fallback mode due to wrong - * config or config from a later fw version, - * or the file is corrupt or hand-edited. - */ - dev_warn(dev, "Discarding %zu byte(s) in T%u\n", - size - mxt_obj_size(object), type); - } else if (mxt_obj_size(object) > size) { - /* - * If firmware is upgraded, new bytes may be added to - * end of objects. It is generally forward compatible - * to zero these bytes - previous behaviour will be - * retained. However this does invalidate the CRC and - * will force fallback mode until the configuration is - * updated. We warn here but do nothing else - the - * malloc has zeroed the entire configuration. - */ - dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", - mxt_obj_size(object) - size, type); - } - - if (instance >= mxt_obj_instances(object)) { - dev_err(dev, "Object instances exceeded!\n"); - ret = -EINVAL; - goto release_mem; - } - - reg = object->start_address + mxt_obj_size(object) * instance; - - for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); - if (ret != 1) { - dev_err(dev, "Bad format in T%d\n", type); - ret = -EINVAL; - goto release_mem; - } - data_pos += offset; - - if (i > mxt_obj_size(object)) - continue; - - byte_offset = reg + i - cfg_start_ofs; - - if ((byte_offset >= 0) - && (byte_offset <= config_mem_size)) { - *(config_mem + byte_offset) = val; - } else { - dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", - reg, object->type, byte_offset); - ret = -EINVAL; - goto release_mem; - } - } - } + ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; /* Calculate crc of the received configs (not the raw config file) */ if (data->T7_address < cfg_start_ofs) { @@ -1702,28 +1737,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) data->T7_address - cfg_start_ofs, config_mem_size); - if (config_crc > 0 && (config_crc != calculated_crc)) + if (config_crc > 0 && config_crc != calculated_crc) dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", calculated_crc, config_crc); - /* Write configuration as blocks */ - byte_offset = 0; - while (byte_offset < config_mem_size) { - size = config_mem_size - byte_offset; - - if (size > MXT_MAX_BLOCK_WRITE) - size = MXT_MAX_BLOCK_WRITE; - - ret = __mxt_write_reg(data->client, - cfg_start_ofs + byte_offset, - size, config_mem + byte_offset); - if (ret != 0) { - dev_err(dev, "Config write error, ret=%d\n", ret); - goto release_mem; - } - - byte_offset += size; - } + ret = mxt_upload_cfg_mem(data, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); @@ -1742,8 +1763,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) release_mem: kfree(config_mem); -release: - release_firmware(cfg); return ret; } @@ -2481,6 +2500,7 @@ static int mxt_configure_objects(struct mxt_data *data, static void mxt_config_cb(const struct firmware *cfg, void *ctx) { mxt_configure_objects(ctx, cfg); + release_firmware(cfg); } static int mxt_initialize(struct mxt_data *data) @@ -2930,9 +2950,12 @@ static ssize_t mxt_update_cfg_store(struct device *dev, ret = mxt_configure_objects(data, cfg); if (ret) - goto out; + goto release; ret = count; + +release: + release_firmware(cfg); out: data->updating_config = false; return ret; From 5d954376ff56f53521a74806d2a07184c9779955 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:02 +0100 Subject: [PATCH 088/154] Input: atmel_mxt_ts - minor tweak to capitalisation in comment Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 115bf7a7ef9b40..f3fec8222063b4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1144,7 +1144,7 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) mxt_proc_t48_messages(data, message); } else if (!data->input_dev || data->suspended) { /* - * do not report events if input device is not + * Do not report events if input device is not * yet registered or returning from suspend */ mxt_dump_message(data, message); From 1a811c36e1469ecc22df412b1461741634ddbce7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:33 +0100 Subject: [PATCH 089/154] Input: atmel_mxt_ts - remove unnecessary blank line Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index f3fec8222063b4..0d54b7d5b1f540 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -513,7 +513,6 @@ static int mxt_bootloader_read(struct mxt_data *data, msg.buf = val; ret = i2c_transfer(data->client->adapter, &msg, 1); - if (ret == 1) { ret = 0; } else { From c94606f45b412a4194b126b981fdbbdd054ca770 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 10:13:55 +0100 Subject: [PATCH 090/154] Input: atmel_mxt_ts - call CRC completion after CRC update There is a possibility of a race here otherwise. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0d54b7d5b1f540..c2620292ea934e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -814,13 +814,13 @@ static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) u8 status = msg[1]; u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); - complete(&data->crc_completion); - if (crc != data->config_crc) { data->config_crc = crc; dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); } + complete(&data->crc_completion); + /* Detect reset */ if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); From 1b4c1df705a6277dc19587337ba58186a72019fc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 11:42:55 +0100 Subject: [PATCH 091/154] Input: atmel-mxt_ts - use kstrtou8 in sysfs interface rather than scanf Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c2620292ea934e..22f743d0416929 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2980,9 +2980,9 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); - int i; + u8 i; - if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (kstrtou8(buf, 0, &i) == 0 && i < 2) { if (i == 1) mxt_debug_msg_enable(data); else @@ -2999,9 +2999,9 @@ static ssize_t mxt_debug_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); - int i; + u8 i; - if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (kstrtou8(buf, 0, &i) == 0 && i < 2) { data->debug_enabled = (i == 1); dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); From 01b103d42c1b7ebd7f3251683c7bbe201196faee Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 16:22:55 +0100 Subject: [PATCH 092/154] Input: atmel_mxt_ts - fix checkpatch issue WARNING: Missing a blank line after declarations Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 22f743d0416929..5a09f55a7b676a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2616,6 +2616,7 @@ static ssize_t mxt_config_csum_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%06x\n", data->config_crc); } From e3e0ef50605d2952befdcb064feb2db69fe8b753 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:45:53 +0100 Subject: [PATCH 093/154] Input: atmel_mxt_ts - reorder code due to upstream patch churn Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 118 +++++++++++------------ 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 5a09f55a7b676a..33cbe2bae71420 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -248,12 +248,12 @@ struct mxt_data { u32 config_crc; u32 info_crc; u8 bootloader_addr; - struct t7_config t7_cfg; u8 *msg_buf; u8 t6_status; bool update_input; u8 last_message_count; u8 num_touchids; + struct t7_config t7_cfg; u8 num_stylusids; unsigned long t15_keystatus; bool use_retrigen_workaround; @@ -1765,60 +1765,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) return ret; } -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_acquire_irq(struct mxt_data *data) { int error; @@ -2574,6 +2520,60 @@ static int mxt_initialize(struct mxt_data *data) 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) { @@ -3068,12 +3068,12 @@ static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store); static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); -static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, - mxt_debug_enable_store); -static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, @@ -3081,10 +3081,10 @@ static struct attribute *mxt_attrs[] = { &dev_attr_object.attr, &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, + &dev_attr_config_csum.attr, &dev_attr_debug_enable.attr, &dev_attr_debug_v2_enable.attr, &dev_attr_debug_notify.attr, - &dev_attr_config_csum.attr, NULL }; From 88858db093947cdc5bb5dee9f76694dfc79a5c73 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:46:35 +0100 Subject: [PATCH 094/154] Input: atmel_mxt_ts - mXT224 DMA quirk was fixed in firmware v2.0.AA Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 33cbe2bae71420..f4b4f351c49028 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1856,10 +1856,12 @@ static int mxt_parse_object_table(struct mxt_data *data, switch (object->type) { case MXT_GEN_MESSAGE_T5: - if (data->info->family_id == 0x80) { + if (data->info->family_id == 0x80 && + data->info->version < 0x20) { /* - * On mXT224 read and discard unused CRC byte - * otherwise DMA reads are misaligned + * On mXT224 firmware versions prior to V2.0 + * read and discard unused CRC byte otherwise + * DMA reads are misaligned. */ data->T5_msg_size = mxt_obj_size(object); } else { From b99f70b657dc10cf828ff020cf3904b35d9620f4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Aug 2014 11:03:09 -0700 Subject: [PATCH 095/154] Input: atmel_mxt_ts - fix a few issues reported by Coverity This should fix the following issues reported by Coverity: *** CID 1230625: Logically dead code (DEADCODE) /drivers/input/touchscreen/atmel_mxt_ts.c: 1692 in mxt_initialize() *** CID 1230627: Missing break in switch (MISSING_BREAK) /drivers/input/touchscreen/atmel_mxt_ts.c: 1436 in mxt_get_object_table() *** CID 1230629: Out-of-bounds write (OVERRUN) /drivers/input/touchscreen/atmel_mxt_ts.c: 1267 in mxt_update_cfg() *** CID 1230632: Unused pointer value (UNUSED_VALUE) /drivers/input/touchscreen/atmel_mxt_ts.c: 1211 in mxt_update_cfg() Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index f4b4f351c49028..8eebdfc92f1c21 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1511,8 +1511,12 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, /* Skip object */ for (i = 0; i < size; i++) { ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); + &val, &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } data_pos += offset; } continue; @@ -1552,7 +1556,8 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, &val, &offset); if (ret != 1) { - dev_err(dev, "Bad format in T%d\n", type); + dev_err(dev, "Bad format in T%d at %d\n", + type, i); return -EINVAL; } data_pos += offset; @@ -1562,8 +1567,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, byte_offset = reg + i - cfg_start_ofs; - if (byte_offset >= 0 && - byte_offset <= config_mem_size) { + if (byte_offset >= 0 && byte_offset < config_mem_size) { *(config_mem + byte_offset) = val; } else { dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", @@ -1869,6 +1873,7 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T5_msg_size = mxt_obj_size(object) - 1; } data->T5_address = object->start_address; + break; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; From 513bb8cadf695fe88bb3b101f2398ea2607192a8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 2 Sep 2014 16:37:46 +0100 Subject: [PATCH 096/154] Input: atmel_mxt_ts - don't spam dmesg with warning about empty interrupts In the case where the chip is not configured in the correct interrupt line mode, this warning output to dmesg output for each touch cycle. Downgrade the message to debug, since it is a benign condition. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 8eebdfc92f1c21..93f1c3bf1c1c34 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1223,7 +1223,12 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) count = data->msg_buf[0]; if (count == 0) { - dev_warn(dev, "Interrupt triggered but zero messages\n"); + /* + * 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"); return IRQ_NONE; } else if (count > data->max_reportid) { dev_err(dev, "T44 count %d exceeded max report id\n", count); From 64c2e0ff9b77ea3fc47489f8d152b472031c32af Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:12:04 +0100 Subject: [PATCH 097/154] Input: atmel_mxt_ts - remove unnecessary 'out of memory' message Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 93f1c3bf1c1c34..f16e5a845f4cc2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -360,10 +360,8 @@ static void mxt_debug_msg_enable(struct mxt_data *data) data->debug_msg_data = kcalloc(DEBUG_MSG_MAX, data->T5_msg_size, GFP_KERNEL); - if (!data->debug_msg_data) { - dev_err(&data->client->dev, "Failed to allocate buffer\n"); + if (!data->debug_msg_data) return; - } data->debug_v2_enabled = true; mutex_unlock(&data->debug_msg_lock); From 29c8ab9dc45ef3bc5cda7fad055121fd421e6a54 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:13:30 +0100 Subject: [PATCH 098/154] Input: atmel_mxt_ts - fix checkpatch warning with bare else Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index f16e5a845f4cc2..4df1f0e7d457da 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2992,6 +2992,7 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); u8 i; + ssize_t ret; if (kstrtou8(buf, 0, &i) == 0 && i < 2) { if (i == 1) @@ -2999,11 +3000,13 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, else mxt_debug_msg_disable(data); - return count; + ret = count; } else { dev_dbg(dev, "debug_enabled write error\n"); - return -EINVAL; + ret = -EINVAL; } + + return ret; } static ssize_t mxt_debug_enable_store(struct device *dev, @@ -3011,16 +3014,19 @@ static ssize_t mxt_debug_enable_store(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); u8 i; + ssize_t ret; if (kstrtou8(buf, 0, &i) == 0 && i < 2) { data->debug_enabled = (i == 1); dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); - return count; + ret = count; } else { dev_dbg(dev, "debug_enabled write error\n"); - return -EINVAL; + ret = -EINVAL; } + + return ret; } static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, From b96fee16f8fcdbc54cdecbac4769bc6d43f3c322 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:14:06 +0100 Subject: [PATCH 099/154] Input: atmel_mxt_ts - add guards when sysfs interface called with no object table Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4df1f0e7d457da..4cb916cf6ed20f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2635,6 +2635,10 @@ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + + if (!data->object_table) + return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", data->info->version >> 4, data->info->version & 0xf, data->info->build); @@ -2645,6 +2649,10 @@ static ssize_t mxt_hw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + + if (!data->object_table) + return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", data->info->family_id, data->info->variant_id); } @@ -2677,6 +2685,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) From 4f1ce8cfd495b6b4ec983c727d1910c0ce26b8ed Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 9 Sep 2014 16:15:24 +0100 Subject: [PATCH 100/154] Input: atmel_mxt_ts - fix double free of input device [reworked after comments by Dmitry Torokhov. Move free of input device into separate function. Only call in paths that require it. Move mxt_initialize after sysfs init, because otherwise an error in the sysfs init may interfere with the async return from the firmware loader. Add guards for sysfs functions. ] Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4cb916cf6ed20f..b948f99a230849 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1798,7 +1798,6 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { mxt_debug_msg_remove(data); - mxt_free_input_device(data); data->object_table = NULL; data->info = NULL; @@ -2788,11 +2787,13 @@ static int mxt_load_fw(struct device *dev) ret = mxt_lookup_bootloader_address(data, 0); if (ret) goto release_firmware; + + mxt_free_input_device(data); + mxt_free_object_table(data); } else { enable_irq(data->irq); } - mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); @@ -3311,15 +3312,11 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) disable_irq(data->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; } sysfs_bin_attr_init(&data->mem_access_attr); @@ -3336,12 +3333,17 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) goto err_remove_sysfs_group; } + error = mxt_initialize(data); + if (error) + goto err_remove_mem_access; + return 0; +err_remove_mem_access: + sysfs_remove_bin_file(&client->dev.kobj, &data->mem_access_attr); + data->mem_access_attr.attr.name = NULL; err_remove_sysfs_group: sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); -err_free_object: - mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); err_free_mem: @@ -3361,6 +3363,7 @@ static int mxt_remove(struct i2c_client *client) free_irq(data->irq, data); regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); + mxt_free_input_device(data); mxt_free_object_table(data); kfree(data); From 15fd4c4259d19d0005d9cccb6ab2f85fbd4d6a02 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 24 Oct 2014 10:08:19 +0100 Subject: [PATCH 101/154] Adjust MXT_MAX_BLOCK_WRITE Some platforms appear to have an underlying I2C device which starts corrupting data if blocks exceed this length. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b948f99a230849..c4249f8b5ba1b5 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -40,7 +40,7 @@ #define MXT_OBJECT_START 0x07 #define MXT_OBJECT_SIZE 6 #define MXT_INFO_CHECKSUM_SIZE 3 -#define MXT_MAX_BLOCK_WRITE 256 +#define MXT_MAX_BLOCK_WRITE 255 /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 From 3f712e6da9806c74ae8b499de419e4036b16cb68 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 14 Jun 2013 08:52:45 -0700 Subject: [PATCH 102/154] Input: atmel_mxt_ts - implement support for T107 active stylus Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 189 ++++++++++++++++++----- 1 file changed, 151 insertions(+), 38 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c4249f8b5ba1b5..a4c29f23509372 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -72,6 +72,7 @@ #define MXT_SPT_CTECONFIG_T46 46 #define MXT_PROCI_ACTIVE_STYLUS_T63 63 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 +#define MXT_PROCI_ACTIVESTYLUS_T107 107 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -135,9 +136,6 @@ struct t9_range { /* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ #define MXT_T42_MSG_TCHSUP (1 << 0) -/* T47 Stylus */ -#define MXT_TOUCH_MAJOR_T47_STYLUS 1 - /* T63 Stylus */ #define MXT_T63_STYLUS_PRESS (1 << 0) #define MXT_T63_STYLUS_RELEASE (1 << 1) @@ -166,7 +164,25 @@ struct t9_range { #define MXT_T100_DETECT (1 << 7) #define MXT_T100_TYPE_MASK 0x70 -#define MXT_T100_TYPE_STYLUS 0x20 + +enum t100_type { + MXT_T100_TYPE_FINGER = 1, + MXT_T100_TYPE_PASSIVE_STYLUS = 2, + MXT_T100_TYPE_ACTIVE_STYLUS = 3, +}; + +/* Gen2 Active Stylus */ +#define MXT_T107_STYLUS_STYAUX 42 +#define MXT_T107_STYLUS_STYAUX_PRESSURE (1 << 0) +#define MXT_T107_STYLUS_STYAUX_PEAK (1 << 4) + +#define MXT_T107_STYLUS_HOVER (1 << 0) +#define MXT_T107_STYLUS_TIPSWITCH (1 << 1) +#define MXT_T107_STYLUS_BUTTON0 (1 << 2) +#define MXT_T107_STYLUS_BUTTON1 (1 << 3) + +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ @@ -256,6 +272,8 @@ struct mxt_data { struct t7_config t7_cfg; u8 num_stylusids; unsigned long t15_keystatus; + u8 stylus_aux_pressure; + u8 stylus_aux_peak; bool use_retrigen_workaround; bool use_regulator; struct regulator *reg_vdd; @@ -283,6 +301,7 @@ struct mxt_data { u8 T63_reportid_max; u8 T100_reportid_min; u8 T100_reportid_max; + u16 T107_address; /* for fw update in bootloader */ struct completion bl_completion; @@ -922,7 +941,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) /* A size of zero indicates touch is from a linked T47 Stylus */ if (area == 0) { - area = MXT_TOUCH_MAJOR_T47_STYLUS; + area = MXT_TOUCH_MAJOR_DEFAULT; tool = MT_TOOL_PEN; } else { tool = MT_TOOL_FINGER; @@ -949,9 +968,16 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) struct input_dev *input_dev = data->input_dev; int id; u8 status; + u8 type; int x; int y; int tool; + u8 major = 0; + u8 pressure = 0; + u8 orientation = 0; + bool active = false; + bool eraser = false; + bool barrel = false; id = message[0] - data->T100_reportid_min - 2; @@ -963,47 +989,86 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) x = (message[3] << 8) | message[2]; y = (message[5] << 8) | message[4]; - dev_dbg(dev, - "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n", - id, - status, - x, y, - data->t100_aux_area ? message[data->t100_aux_area] : 0, - data->t100_aux_ampl ? message[data->t100_aux_ampl] : 0, - data->t100_aux_vect ? message[data->t100_aux_vect] : 0); + if (status & MXT_T100_DETECT) { + active = true; + type = (status & MXT_T100_TYPE_MASK) >> 4; - input_mt_slot(input_dev, id); + switch (type) { + case MXT_T100_TYPE_FINGER: + tool = MT_TOOL_FINGER; - if (status & MXT_T100_DETECT) { - if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS) + if (data->t100_aux_area) + major = message[data->t100_aux_area]; + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_PASSIVE_STYLUS: tool = MT_TOOL_PEN; - else - tool = MT_TOOL_FINGER; - /* Touch active */ + /* Passive stylus is reported with size zero so + * hardcode */ + major = MXT_TOUCH_MAJOR_DEFAULT; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + break; + + case MXT_T100_TYPE_ACTIVE_STYLUS: + if (message[6] & MXT_T107_STYLUS_HOVER) { + /* stylus detected, position available */ + tool = MT_TOOL_PEN; + major = MXT_TOUCH_MAJOR_DEFAULT; + eraser = message[6] & MXT_T107_STYLUS_BUTTON0; + barrel = message[6] & MXT_T107_STYLUS_BUTTON1; + + if (message[6] & MXT_T107_STYLUS_TIPSWITCH) { + if (data->stylus_aux_pressure) + pressure = message[data->stylus_aux_pressure]; + else + pressure = MXT_PRESSURE_DEFAULT; + } else { + /* hover */ + pressure = 0; + } + } else { + /* detected but position cannot be determined */ + active = false; + } + + break; + default: + dev_dbg(dev, "Unexpected T100 type\n"); + return; + } + } + + input_mt_slot(input_dev, id); + + if (active) { + dev_dbg(dev, + "[%u] status:%02X x:%u y:%u a:%02X p:%02X v:%02X\n", + id, status, x, y, major, pressure, orientation); + input_mt_report_slot_state(input_dev, tool, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); - if (data->t100_aux_ampl) - input_report_abs(input_dev, ABS_MT_PRESSURE, - message[data->t100_aux_ampl]); - - if (data->t100_aux_area) { - if (tool == MT_TOOL_PEN) - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - MXT_TOUCH_MAJOR_T47_STYLUS); - else - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - message[data->t100_aux_area]); - } - - if (data->t100_aux_vect) - input_report_abs(input_dev, ABS_MT_ORIENTATION, - message[data->t100_aux_vect]); + input_report_key(input_dev, BTN_STYLUS, eraser); + input_report_key(input_dev, BTN_STYLUS2, barrel); } else { - /* Touch no longer active, close out slot */ - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + dev_dbg(dev, + "[%u] status:%02X\n", id, status); + + /* close out slot */ + input_mt_report_slot_state(input_dev, 0, 0); } data->update_input = true; @@ -1922,6 +1987,9 @@ static int mxt_parse_object_table(struct mxt_data *data, /* first two report IDs reserved */ data->num_touchids = object->num_report_ids - 2; break; + case MXT_PROCI_ACTIVESTYLUS_T107: + data->T107_address = object->start_address; + break; } end_address = object->start_address @@ -2287,6 +2355,40 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) return error; } +static int mxt_read_t107_stylus_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u8 styaux; + int aux; + + object = mxt_get_object(data, MXT_PROCI_ACTIVESTYLUS_T107); + if (!object) + return 0; + + error = __mxt_read_reg(client, + object->start_address + MXT_T107_STYLUS_STYAUX, + 1, &styaux); + if (error) + return error; + + /* map aux bits */ + aux = 7; + + if (styaux & MXT_T107_STYLUS_STYAUX_PRESSURE) + data->stylus_aux_pressure = aux++; + + if (styaux & MXT_T107_STYLUS_STYAUX_PEAK) + data->stylus_aux_peak = aux++; + + dev_dbg(&client->dev, + "Enabling T107 active stylus, aux map pressure:%u peak:%u\n", + data->stylus_aux_pressure, data->stylus_aux_peak); + + return 0; +} + static int mxt_read_t100_config(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -2359,6 +2461,10 @@ static int mxt_read_t100_config(struct mxt_data *data) if (tchaux & MXT_T100_TCHAUX_AREA) data->t100_aux_area = aux++; + dev_dbg(&client->dev, + "T100 aux mappings vect:%u ampl:%u area:%u\n", + data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); + dev_info(&client->dev, "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); @@ -2375,6 +2481,8 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) if (error) dev_warn(dev, "Failed to initialize T9 resolution\n"); + mxt_read_t107_stylus_config(data); + input_dev = input_allocate_device(); if (!data || !input_dev) { dev_err(dev, "Failed to allocate memory\n"); @@ -2419,11 +2527,16 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0); + if (data->T107_address) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + } + if (data->t100_aux_area) input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_AREA, 0, 0); - if (data->t100_aux_ampl) + if (data->t100_aux_ampl | data->stylus_aux_pressure) input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); From 2764354ea29d9a7719846c614289a805190b4ca7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 26 Nov 2014 16:20:00 +0000 Subject: [PATCH 103/154] Input: atmel_mxt_ts - improve handling of hover Pressure and touch major should be reported as non-zero if tool touching the device. --- drivers/input/touchscreen/atmel_mxt_ts.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a4c29f23509372..bcc141fae59646 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -976,6 +976,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) u8 pressure = 0; u8 orientation = 0; bool active = false; + bool hover = false; bool eraser = false; bool barrel = false; @@ -1029,11 +1030,8 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) if (message[6] & MXT_T107_STYLUS_TIPSWITCH) { if (data->stylus_aux_pressure) pressure = message[data->stylus_aux_pressure]; - else - pressure = MXT_PRESSURE_DEFAULT; } else { - /* hover */ - pressure = 0; + hover = true; } } else { /* detected but position cannot be determined */ @@ -1047,6 +1045,21 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) } } + if (hover) { + pressure = 0; + major = 0; + } else if (active) { + /* + * Values reported should be non-zero if tool is touching the + * device + */ + if (pressure == 0) + pressure = MXT_PRESSURE_DEFAULT; + + if (major == 0) + major = MXT_TOUCH_MAJOR_DEFAULT; + } + input_mt_slot(input_dev, id); if (active) { From 44df8600d28c5a9c8488f3f2f52522a6b8d6d551 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 24 Oct 2014 12:50:10 +0100 Subject: [PATCH 104/154] Input: atmel_mxt_ts - handle new T100 touch types Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index bcc141fae59646..9dec05b55edb45 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -169,6 +169,9 @@ enum t100_type { MXT_T100_TYPE_FINGER = 1, MXT_T100_TYPE_PASSIVE_STYLUS = 2, MXT_T100_TYPE_ACTIVE_STYLUS = 3, + MXT_T100_TYPE_HOVERING_FINGER = 4, + MXT_T100_TYPE_GLOVE = 5, + MXT_T100_TYPE_LARGE_TOUCH = 6, }; /* Gen2 Active Stylus */ @@ -991,11 +994,15 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) y = (message[5] << 8) | message[4]; if (status & MXT_T100_DETECT) { - active = true; type = (status & MXT_T100_TYPE_MASK) >> 4; switch (type) { + case MXT_T100_TYPE_HOVERING_FINGER: + hover = true; + /* fall through */ case MXT_T100_TYPE_FINGER: + case MXT_T100_TYPE_GLOVE: + active = true; tool = MT_TOOL_FINGER; if (data->t100_aux_area) @@ -1008,6 +1015,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) break; case MXT_T100_TYPE_PASSIVE_STYLUS: + active = true; tool = MT_TOOL_PEN; /* Passive stylus is reported with size zero so @@ -1022,6 +1030,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) case MXT_T100_TYPE_ACTIVE_STYLUS: if (message[6] & MXT_T107_STYLUS_HOVER) { /* stylus detected, position available */ + active = true; tool = MT_TOOL_PEN; major = MXT_TOUCH_MAJOR_DEFAULT; eraser = message[6] & MXT_T107_STYLUS_BUTTON0; @@ -1033,12 +1042,14 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) } else { hover = true; } - } else { - /* detected but position cannot be determined */ - active = false; } break; + + case MXT_T100_TYPE_LARGE_TOUCH: + /* Ignore suppressed touch */ + break; + default: dev_dbg(dev, "Unexpected T100 type\n"); return; From 2c74f036e96b36813abd3ff0d1089d70b7ae526d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 28 Nov 2014 16:53:15 +0000 Subject: [PATCH 105/154] Input: touchscreen - use __maybe_unused instead of ifdef around suspend/resume Use __maybe_unused instead of ifdef guards around suspend/resume functions, in order to increase build coverage and fix build warnings. Signed-off-by: Jingoo Han Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 9dec05b55edb45..033a2d09db6c9b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3507,8 +3507,7 @@ static int mxt_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int mxt_suspend(struct device *dev) +static int __maybe_unused mxt_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mxt_data *data = i2c_get_clientdata(client); @@ -3524,7 +3523,7 @@ static int mxt_suspend(struct device *dev) return 0; } -static int mxt_resume(struct device *dev) +static int __maybe_unused mxt_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mxt_data *data = i2c_get_clientdata(client); @@ -3539,7 +3538,6 @@ static int mxt_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); From e646ac0a390708b8cbcdbd7042cf9c0078f36524 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 5 Dec 2014 11:50:08 +0000 Subject: [PATCH 106/154] Input: atmel_mxt_ts - improve indentation by reworking if statements --- drivers/input/touchscreen/atmel_mxt_ts.c | 29 ++++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 033a2d09db6c9b..90b4b4518c718e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1028,21 +1028,20 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) break; case MXT_T100_TYPE_ACTIVE_STYLUS: - if (message[6] & MXT_T107_STYLUS_HOVER) { - /* stylus detected, position available */ - active = true; - tool = MT_TOOL_PEN; - major = MXT_TOUCH_MAJOR_DEFAULT; - eraser = message[6] & MXT_T107_STYLUS_BUTTON0; - barrel = message[6] & MXT_T107_STYLUS_BUTTON1; - - if (message[6] & MXT_T107_STYLUS_TIPSWITCH) { - if (data->stylus_aux_pressure) - pressure = message[data->stylus_aux_pressure]; - } else { - hover = true; - } - } + /* stylus in range, but position unavailable */ + if (!(message[6] & MXT_T107_STYLUS_HOVER)) + break; + + active = true; + tool = MT_TOOL_PEN; + major = MXT_TOUCH_MAJOR_DEFAULT; + eraser = message[6] & MXT_T107_STYLUS_BUTTON0; + barrel = message[6] & MXT_T107_STYLUS_BUTTON1; + + if (!(message[6] & MXT_T107_STYLUS_TIPSWITCH)) + hover = true; + else if (data->stylus_aux_pressure) + pressure = message[data->stylus_aux_pressure]; break; From 9bc82bb37b957df26aa95febf9ff623f74a99560 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 5 Dec 2014 11:22:03 +0000 Subject: [PATCH 107/154] Input: atmel_mxt_ts - change T100 debug output from status to tool type --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 90b4b4518c718e..65cb19ed4780bb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1073,9 +1073,8 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) input_mt_slot(input_dev, id); if (active) { - dev_dbg(dev, - "[%u] status:%02X x:%u y:%u a:%02X p:%02X v:%02X\n", - id, status, x, y, major, pressure, orientation); + dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n", + id, type, x, y, major, pressure, orientation); input_mt_report_slot_state(input_dev, tool, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); @@ -1087,8 +1086,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) input_report_key(input_dev, BTN_STYLUS, eraser); input_report_key(input_dev, BTN_STYLUS2, barrel); } else { - dev_dbg(dev, - "[%u] status:%02X\n", id, status); + dev_dbg(dev, "[%u] release\n", id); /* close out slot */ input_mt_report_slot_state(input_dev, 0, 0); From f017f02f24b59b56ad550a315c8ef29dafad3429 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 5 Dec 2014 11:23:07 +0000 Subject: [PATCH 108/154] Input: atmel_mxt_ts - disable interrupt for 50ms after reset 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. --- 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 65cb19ed4780bb..6c29d649b0f495 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1466,7 +1466,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); INIT_COMPLETION(data->reset_completion); @@ -1474,6 +1476,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) From 3a0b91a9ad9ad7aa13b93f9ce3b6e8fbcae10e75 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Dec 2014 11:49:47 +0000 Subject: [PATCH 109/154] Input: atmel_mxt_ts - remove extra blank lines --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6c29d649b0f495..611f2db6eb60bd 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1095,7 +1095,6 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) data->update_input = true; } - static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) { struct input_dev *input_dev = data->input_dev; @@ -1898,7 +1897,6 @@ static void mxt_free_object_table(struct mxt_data *data) data->raw_info_block = NULL; kfree(data->msg_buf); data->msg_buf = NULL; - data->T5_address = 0; data->T5_msg_size = 0; data->T6_reportid = 0; From 0a2f0392b5a2b3daf0b6cdb90e33eafc154e2cb0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Dec 2014 11:49:57 +0000 Subject: [PATCH 110/154] Input: atmel_mxt_ts - remove unnecessary forward declarations --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 611f2db6eb60bd..406a234af45708 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2262,8 +2262,6 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } -static void mxt_start(struct mxt_data *data); -static void mxt_stop(struct mxt_data *data); static int mxt_input_open(struct input_dev *dev); static void mxt_input_close(struct input_dev *dev); From 0e3b2f5046ada5459deeb438572f6f1f74477e26 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Dec 2014 12:53:09 +0000 Subject: [PATCH 111/154] Input: atmel_mxt_ts - fix merge issue in T100 init code --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 406a234af45708..05bad2e07add27 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2458,10 +2458,6 @@ static int mxt_read_t100_config(struct mxt_data *data) if (range_x == 0) range_x = 1023; - /* Handle default values */ - if (range_x == 0) - range_x = 1023; - if (range_y == 0) range_y = 1023; From 041ef5a59df089994d6437d5abb58846faa267d2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Dec 2014 12:54:07 +0000 Subject: [PATCH 112/154] Input: atmel_mxt_ts - remove unnecessary debug on ENOMEM --- drivers/input/touchscreen/atmel_mxt_ts.c | 29 ++++++------------------ 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 05bad2e07add27..aef136229b6714 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1813,10 +1813,8 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) MXT_INFO_CHECKSUM_SIZE; config_mem_size = data->mem_size - cfg_start_ofs; config_mem = kzalloc(config_mem_size, GFP_KERNEL); - if (!config_mem) { - dev_err(dev, "Failed to allocate memory\n"); + if (!config_mem) return -ENOMEM; - } ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, config_mem, config_mem_size); @@ -2036,10 +2034,8 @@ static int mxt_parse_object_table(struct mxt_data *data, data->msg_buf = kcalloc(data->max_reportid, data->T5_msg_size, GFP_KERNEL); - if (!data->msg_buf) { - dev_err(&client->dev, "Failed to allocate message buffer\n"); + if (!data->msg_buf) return -ENOMEM; - } return 0; } @@ -2061,10 +2057,8 @@ static int mxt_read_info_block(struct mxt_data *data) /* Read 7-byte ID information block starting at address 0 */ size = sizeof(struct mxt_info); id_buf = kzalloc(size, GFP_KERNEL); - if (!id_buf) { - dev_err(&client->dev, "Failed to allocate memory\n"); + if (!id_buf) return -ENOMEM; - } error = __mxt_read_reg(client, 0, size, id_buf); if (error) { @@ -2079,7 +2073,6 @@ static int mxt_read_info_block(struct mxt_data *data) buf = krealloc(id_buf, size, GFP_KERNEL); if (!buf) { - dev_err(&client->dev, "Failed to allocate memory\n"); error = -ENOMEM; goto err_free_mem; } @@ -2280,10 +2273,8 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) dev_warn(dev, "Failed to initialize T9 resolution\n"); input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(dev, "Failed to allocate memory\n"); + if (!input_dev) return -ENOMEM; - } input_dev->name = "Atmel maXTouch Touchscreen"; input_dev->phys = data->phys; @@ -2504,10 +2495,8 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) mxt_read_t107_stylus_config(data); input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(dev, "Failed to allocate memory\n"); + if (!data || !input_dev) return -ENOMEM; - } if (data->pdata->input_name) input_dev->name = data->pdata->input_name; @@ -3017,10 +3006,8 @@ static int mxt_update_file_name(struct device *dev, char **file_name, } file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL); - if (!file_name_tmp) { - dev_warn(dev, "no memory\n"); + if (!file_name_tmp) return -ENOMEM; - } *file_name = file_name_tmp; memcpy(*file_name, buf, count); @@ -3409,10 +3396,8 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) } data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "Failed to allocate memory\n"); + if (!data) return -ENOMEM; - } snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); From 8ed3a0dab31a49a30592f48cd902f33ab834306f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Dec 2014 12:54:29 +0000 Subject: [PATCH 113/154] Input: atmel_mxt_ts - fix bug in debug output for T100 --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index aef136229b6714..7ca430aa599aec 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2489,8 +2489,10 @@ static int mxt_initialize_t100_input_device(struct mxt_data *data) int error; error = mxt_read_t100_config(data); - if (error) - dev_warn(dev, "Failed to initialize T9 resolution\n"); + if (error) { + dev_err(dev, "Failed to read T100 config\n"); + return error; + } mxt_read_t107_stylus_config(data); From 7c09e1314d1f64cf02a3fdaaf5bf3ef56192f244 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 10 Mar 2015 15:25:06 +0000 Subject: [PATCH 114/154] Input: atmel_mxt_ts - Update device tree documentation --- .../bindings/input/atmel,maxtouch.txt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt index 6319f07bfbc124..a20561aa9915cd 100644 --- a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -6,15 +6,29 @@ Required properties: - reg: The I2C address of the device -- interrupts: The sink for the touch controller CHG/IRQ output +- interrupts: The sink for the touchpad's IRQ output See ../interrupt-controller/interrupts.txt -Optional properties for maXTouch device: +Optional properties for main touchpad device: -- linux,gpio-keymap: An array of up to 4 entries indicating the Linux - keycode generated by each GPIO. Linux keycodes are defined in +- linux,gpio-keymap: When enabled, the SPT_GPIOPWN_T19 object sends messages + on GPIO bit changes. An array of up to 8 entries can be provided + indicating the Linux keycode mapped to each bit of the status byte, + starting at the LSB. Linux keycodes are defined in . + Note: the numbering of the GPIOs and the bit they start at varies between + maXTouch devices. You must either refer to the documentation, or + experiment to determine which bit corresponds to which input. Use + KEY_RESERVED for unused padding values. + +- atmel,reset-gpio: Configure RESET GPIO. Required for regulator support. + +- atmel,cfg_name: Provide name of configuration file in OBP_RAW format. This + will be downloaded from the firmware loader on probe to the device. + +- atmel,input_name: Override name of input device from the default. + Example: touch@4b { From d9a9f129fb1a4680605dad434216f8792327ee58 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 23 Apr 2015 11:02:55 +0100 Subject: [PATCH 115/154] Input: atmel_mxt_ts - config CRC may start at T71 On devices with the T71 object, the config CRC will start there, rather than at T7. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 34 +++++++++++++++--------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7ca430aa599aec..4987f7a650b565 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -71,6 +71,7 @@ #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 #define MXT_PROCI_ACTIVE_STYLUS_T63 63 +#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 #define MXT_PROCI_ACTIVESTYLUS_T107 107 @@ -290,6 +291,7 @@ struct mxt_data { u8 T6_reportid; u16 T6_address; u16 T7_address; + u16 T71_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T15_reportid_min; @@ -359,6 +361,7 @@ static bool mxt_object_readable(unsigned int type) case MXT_SPT_USERDATA_T38: case MXT_SPT_DIGITIZER_T43: case MXT_SPT_CTECONFIG_T46: + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: return true; default: return false; @@ -1736,6 +1739,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) u32 info_crc, config_crc, calculated_crc; u8 *config_mem; size_t config_mem_size; + u16 crc_start = 0; mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); @@ -1822,20 +1826,22 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) goto release_mem; /* Calculate crc of the received configs (not the raw config file) */ - if (data->T7_address < cfg_start_ofs) { - dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", - data->T7_address, cfg_start_ofs); - ret = 0; - goto release_mem; - } + if (data->T71_address) + crc_start = data->T71_address; + else if (data->T7_address) + crc_start = data->T7_address; + else + dev_warn(dev, "Could not find CRC start\n"); - calculated_crc = mxt_calculate_crc(config_mem, - data->T7_address - cfg_start_ofs, - config_mem_size); + if (crc_start > cfg_start_ofs) { + calculated_crc = mxt_calculate_crc(config_mem, + crc_start - cfg_start_ofs, + config_mem_size); - if (config_crc > 0 && config_crc != calculated_crc) - dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", - calculated_crc, config_crc); + if (config_crc > 0 && config_crc != calculated_crc) + dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + } ret = mxt_upload_cfg_mem(data, cfg_start_ofs, config_mem, config_mem_size); @@ -1899,6 +1905,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T5_msg_size = 0; data->T6_reportid = 0; data->T7_address = 0; + data->T71_address = 0; data->T9_reportid_min = 0; data->T9_reportid_max = 0; data->T15_reportid_min = 0; @@ -1972,6 +1979,9 @@ static int mxt_parse_object_table(struct mxt_data *data, case MXT_GEN_POWER_T7: data->T7_address = object->start_address; break; + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: + data->T71_address = object->start_address; + break; case MXT_TOUCH_MULTI_T9: /* Only handle messages from first T9 instance */ data->T9_reportid_min = min_id; From 8c3f7d9d66ccd405c7ba818c49fe7ca82e254372 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 10 Mar 2015 15:19:49 +0000 Subject: [PATCH 116/154] Input: atmel_mxt_ts - add suspend modes --- .../bindings/input/atmel,maxtouch.txt | 5 + drivers/input/touchscreen/atmel_mxt_ts.c | 99 ++++++++++++++----- include/dt-bindings/input/atmel_mxt_ts.h | 22 +++++ .../{i2c => platform_data}/atmel_mxt_ts.h | 8 +- 4 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 include/dt-bindings/input/atmel_mxt_ts.h rename include/linux/{i2c => platform_data}/atmel_mxt_ts.h (76%) diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt index a20561aa9915cd..e879f6d1d2c5f1 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_TOUCH_CTRL - use T9.CTRL to turn off touch processing + Definitions are in . + - atmel,reset-gpio: Configure RESET GPIO. Required for regulator support. - atmel,cfg_name: Provide name of configuration file in OBP_RAW format. This diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4987f7a650b565..4d93a2a37bd97b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -103,6 +103,7 @@ struct t7_config { #define MXT_POWER_CFG_DEEPSLEEP 1 /* MXT_TOUCH_MULTI_T9 field */ +#define MXT_TOUCH_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -279,7 +280,6 @@ struct mxt_data { u8 stylus_aux_pressure; u8 stylus_aux_peak; bool use_retrigen_workaround; - bool use_regulator; struct regulator *reg_vdd; struct regulator *reg_avdd; char *fw_name; @@ -864,6 +864,20 @@ static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) data->t6_status = status; } +static int mxt_write_object(struct mxt_data *data, + u8 type, u8 offset, u8 val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object || offset >= mxt_obj_size(object)) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; @@ -2153,6 +2167,11 @@ static void mxt_regulator_enable(struct mxt_data *data) if (error) return; + /* + * According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage + */ msleep(MXT_REGULATOR_DELAY); gpio_set_value(data->pdata->gpio_reset, 1); msleep(MXT_CHG_DELAY); @@ -2174,18 +2193,14 @@ static void mxt_regulator_disable(struct mxt_data *data) regulator_disable(data->reg_avdd); } -static void mxt_probe_regulators(struct mxt_data *data) +static int mxt_probe_regulators(struct mxt_data *data) { struct device *dev = &data->client->dev; int error; - /* - * According to maXTouch power sequencing specification, RESET line - * must be kept low until some time after regulators come up to - * voltage - */ if (!gpio_is_valid(data->pdata->gpio_reset)) { dev_dbg(dev, "Must have reset GPIO to use regulator support\n"); + error = -EINVAL; goto fail; } @@ -2203,18 +2218,17 @@ static void mxt_probe_regulators(struct mxt_data *data) goto fail_release; } - data->use_regulator = true; mxt_regulator_enable(data); dev_dbg(dev, "Initialised regulators\n"); - return; + return 0; fail_release: regulator_put(data->reg_vdd); fail: data->reg_vdd = NULL; data->reg_avdd = NULL; - data->use_regulator = false; + return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -2899,10 +2913,12 @@ static int mxt_load_fw(struct device *dev) goto release_firmware; if (data->suspended) { - if (data->use_regulator) + if (data->pdata->suspend_mode == MXT_SUSPEND_REGULATOR) mxt_regulator_enable(data); - enable_irq(data->irq); + if (data->pdata->suspend_mode == MXT_SUSPEND_DEEP_SLEEP) + enable_irq(data->irq); + data->suspended = false; } @@ -3066,6 +3082,7 @@ static ssize_t mxt_update_cfg_store(struct device *dev, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); + const struct mxt_platform_data *pdata = data->pdata; const struct firmware *cfg; int ret; @@ -3091,10 +3108,10 @@ static ssize_t mxt_update_cfg_store(struct device *dev, mxt_free_input_device(data); if (data->suspended) { - if (data->use_regulator) { + if (pdata->suspend_mode == MXT_SUSPEND_REGULATOR) { enable_irq(data->irq); mxt_regulator_enable(data); - } else { + } else if (pdata->suspend_mode == MXT_SUSPEND_DEEP_SLEEP) { mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); mxt_acquire_irq(data); } @@ -3276,13 +3293,24 @@ static void mxt_start(struct mxt_data *data) if (!data->suspended || data->in_bootloader) return; - if (data->use_regulator) { - enable_irq(data->irq); + switch (data->pdata->suspend_mode) { + case MXT_SUSPEND_TOUCH_CTRL: + mxt_soft_reset(data); + + /* Touch enable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + break; + case MXT_SUSPEND_REGULATOR: + enable_irq(data->irq); mxt_regulator_enable(data); - } else { + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: /* - * Discard any messages still in message buffer + * Discard any touch messages still in message buffer * from before chip went to sleep */ mxt_process_messages_until_invalid(data); @@ -3293,6 +3321,7 @@ static void mxt_start(struct mxt_data *data) mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); mxt_acquire_irq(data); + break; } data->suspended = false; @@ -3303,14 +3332,29 @@ static void mxt_stop(struct mxt_data *data) if (data->suspended || data->in_bootloader || data->updating_config) return; - disable_irq(data->irq); + switch (data->pdata->suspend_mode) { + case MXT_SUSPEND_TOUCH_CTRL: + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + break; - if (data->use_regulator) + case MXT_SUSPEND_REGULATOR: + disable_irq(data->irq); mxt_regulator_disable(data); - else + mxt_reset_slots(data); + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: + disable_irq(data->irq); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); - mxt_reset_slots(data); + mxt_reset_slots(data); + break; + } + data->suspended = true; } @@ -3376,6 +3420,9 @@ static 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 @@ -3438,7 +3485,11 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) goto err_free_mem; } - mxt_probe_regulators(data); + if (pdata->suspend_mode == MXT_SUSPEND_REGULATOR) { + error = mxt_probe_regulators(data); + if (error) + goto err_free_irq; + } disable_irq(data->irq); 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 00000000000000..c7637925da5497 --- /dev/null +++ b/include/dt-bindings/input/atmel_mxt_ts.h @@ -0,0 +1,22 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2015 Atmel Corporation + * Author: Nick Dyer + * + * 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_TOUCH_CTRL = 1, + MXT_SUSPEND_REGULATOR = 2, +}; + +#endif /* __DT_BINDINGS_ATMEL_MXT_TS_H */ diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/platform_data/atmel_mxt_ts.h similarity index 76% rename from include/linux/i2c/atmel_mxt_ts.h rename to include/linux/platform_data/atmel_mxt_ts.h index bc74c3f4c866f8..be6fa95a661369 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/platform_data/atmel_mxt_ts.h @@ -10,16 +10,18 @@ * option) any later version. */ -#ifndef __LINUX_ATMEL_MXT_TS_H -#define __LINUX_ATMEL_MXT_TS_H +#ifndef __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H +#define __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H #include +#include /* 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; int t15_num_keys; const unsigned int *t15_keymap; unsigned long gpio_reset; @@ -27,4 +29,4 @@ struct mxt_platform_data { const char *input_name; }; -#endif /* __LINUX_ATMEL_MXT_TS_H */ +#endif /* __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H */ From 2a5851d79b41074064776dc4c337a51fac8ab31c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 10 Mar 2015 15:20:36 +0000 Subject: [PATCH 117/154] Input: atmel_mxt_ts - minor formatting fix Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4d93a2a37bd97b..2d61dbc7feb624 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -186,8 +186,8 @@ enum t100_type { #define MXT_T107_STYLUS_BUTTON0 (1 << 2) #define MXT_T107_STYLUS_BUTTON1 (1 << 3) -#define MXT_TOUCH_MAJOR_DEFAULT 1 -#define MXT_PRESSURE_DEFAULT 1 +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ From c7f8291a550870f3d4a67a4aba06acd4aa7c2034 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Dec 2014 16:47:55 +0000 Subject: [PATCH 118/154] Input: atmel_mxt_ts - guard debug interface when in bootloader mode Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2d61dbc7feb624..349c92299730a2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3193,6 +3193,9 @@ static ssize_t mxt_debug_enable_store(struct device *dev, static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, size_t *count) { + if (data->in_bootloader) + return -EINVAL; + if (off >= data->mem_size) return -EIO; From 3380b3f981188e2f3bc04e395126143805a0e3f0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Dec 2014 16:22:35 +0000 Subject: [PATCH 119/154] Input: atmel_mxt_ts - refactor firmware flash to extract context into struct --- drivers/input/touchscreen/atmel_mxt_ts.c | 59 +++++++++++++++--------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 349c92299730a2..beb847151b66a7 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -241,6 +241,22 @@ struct mxt_object { u8 num_report_ids; } __packed; +/* Firmware frame structure */ +struct mxt_fw_frame { + __be16 size; + u8 data[]; +}; + +/* Firmware update context */ +struct mxt_flash { + const struct firmware *fw; + struct mxt_fw_frame *frame; + loff_t pos; + size_t frame_size; + unsigned int count; + unsigned int retry; +}; + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -2894,21 +2910,17 @@ static int mxt_check_firmware_format(struct device *dev, static int mxt_load_fw(struct device *dev) { struct mxt_data *data = dev_get_drvdata(dev); - const struct firmware *fw = NULL; - unsigned int frame_size; - unsigned int pos = 0; - unsigned int retry = 0; - unsigned int frame = 0; + struct mxt_flash f = { 0, }; int ret; - ret = request_firmware(&fw, data->fw_name, dev); + ret = request_firmware(&f.fw, data->fw_name, dev); if (ret) { dev_err(dev, "Unable to open firmware %s\n", data->fw_name); return ret; } /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, fw); + ret = mxt_check_firmware_format(dev, f.fw); if (ret) goto release_firmware; @@ -2961,41 +2973,42 @@ static int mxt_load_fw(struct device *dev) goto disable_irq; } - while (pos < fw->size) { + while (f.pos < f.fw->size) { + f.frame = (struct mxt_fw_frame*)(f.fw->data + f.pos); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); if (ret) goto disable_irq; - frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - /* Take account of CRC bytes */ - frame_size += 2; + f.frame_size = __be16_to_cpu(f.frame->size) + 2U; /* Write one frame to device */ - ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + ret = mxt_bootloader_write(data, f.fw->data + f.pos, + f.frame_size); if (ret) goto disable_irq; ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); if (ret) { - retry++; + f.retry++; /* Back off by 20ms per retry */ - msleep(retry * 20); + msleep(f.retry * 20); - if (retry > 20) { + if (f.retry > 20) { dev_err(dev, "Retry count exceeded\n"); goto disable_irq; } } else { - retry = 0; - pos += frame_size; - frame++; + f.retry = 0; + f.pos += f.frame_size; + f.count++; } - if (frame % 50 == 0) - dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", - frame, pos, fw->size); + if (f.count % 50 == 0) + dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n", + f.count, f.pos, f.fw->size); } /* Wait for flash. */ @@ -3004,7 +3017,7 @@ static int mxt_load_fw(struct device *dev) if (ret) goto disable_irq; - dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); + dev_dbg(dev, "Sent %u frames, %lld bytes\n", f.count, f.pos); /* * Wait for device to reset. Some bootloader versions do not assert @@ -3018,7 +3031,7 @@ static int mxt_load_fw(struct device *dev) disable_irq: disable_irq(data->irq); release_firmware: - release_firmware(fw); + release_firmware(f.fw); return ret; } From 7d38a7f342c33e9a65c9ac56f811c7614e63ba84 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Dec 2014 16:27:29 +0000 Subject: [PATCH 120/154] Input: atmel_mxt_ts - refactor code to enter bootloader into separate func --- drivers/input/touchscreen/atmel_mxt_ts.c | 43 +++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index beb847151b66a7..13fd2d9eae50c8 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2907,23 +2907,10 @@ static int mxt_check_firmware_format(struct device *dev, return -EINVAL; } -static int mxt_load_fw(struct device *dev) +static int mxt_enter_bootloader(struct mxt_data *data) { - struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_flash f = { 0, }; int ret; - ret = request_firmware(&f.fw, data->fw_name, dev); - if (ret) { - dev_err(dev, "Unable to open firmware %s\n", data->fw_name); - return ret; - } - - /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, f.fw); - if (ret) - goto release_firmware; - if (data->suspended) { if (data->pdata->suspend_mode == MXT_SUSPEND_REGULATOR) mxt_regulator_enable(data); @@ -2941,14 +2928,14 @@ static int mxt_load_fw(struct device *dev) ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); if (ret) - goto release_firmware; + return ret; msleep(MXT_RESET_TIME); /* Do not need to scan since we know family ID */ ret = mxt_lookup_bootloader_address(data, 0); if (ret) - goto release_firmware; + return ret; mxt_free_input_device(data); mxt_free_object_table(data); @@ -2958,6 +2945,30 @@ static int mxt_load_fw(struct device *dev) INIT_COMPLETION(data->bl_completion); + return 0; +} + +static int mxt_load_fw(struct device *dev) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_flash f = { 0, }; + int ret; + + ret = request_firmware(&f.fw, data->fw_name, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", data->fw_name); + return ret; + } + + ret = mxt_enter_bootloader(data); + if (ret) + goto release_firmware; + + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, f.fw); + if (ret) + goto release_firmware; + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); if (ret) { /* Bootloader may still be unlocked from previous attempt */ From 31b266fd94004e25d58eacd8fba96b980243133a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 24 Dec 2014 14:38:16 +0000 Subject: [PATCH 121/154] Input: atmel_mxt_ts - combine bootloader version query with probe This removes some complexity from the bootloader state machine, and means that we always output some debug about the version as soon as we start talking to the bootloader. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 45 +++++++----------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 13fd2d9eae50c8..85672e0233e03d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -623,47 +623,31 @@ static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) { struct device *dev = &data->client->dev; int error; - u8 val; - bool crc_failure; + u8 buf[3]; + bool crc_failure, extended_id; error = mxt_lookup_bootloader_address(data, alt_address); if (error) return error; - error = mxt_bootloader_read(data, &val, 1); + /* Check bootloader status and version information */ + error = mxt_bootloader_read(data, buf, sizeof(buf)); if (error) return error; - /* Check app crc fail mode */ - crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + crc_failure = (buf[0] & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + extended_id = buf[0] & MXT_BOOT_EXTENDED_ID; - dev_err(dev, "Detected bootloader, status:%02X%s\n", - val, crc_failure ? ", APP_CRC_FAIL" : ""); + dev_info(dev, "Found bootloader addr:%02x ID:%u%s%u%s\n", + data->bootloader_addr, + extended_id ? (buf[1] & MXT_BOOT_ID_MASK) : buf[0], + extended_id ? " version:" : "", + extended_id ? buf[2] : 0, + crc_failure ? ", APP_CRC_FAIL" : ""); return 0; } -static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) -{ - struct device *dev = &data->client->dev; - u8 buf[3]; - - if (val & MXT_BOOT_EXTENDED_ID) { - if (mxt_bootloader_read(data, &buf[0], 3) != 0) { - dev_err(dev, "%s: i2c failure\n", __func__); - return val; - } - - dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); - - return buf[0]; - } else { - dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); - - return val; - } -} - static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, bool wait) { @@ -697,9 +681,6 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, if (ret) return ret; - if (state == MXT_WAITING_BOOTLOAD_CMD) - val = mxt_get_bootloader_version(data, val); - switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: @@ -2933,7 +2914,7 @@ static int mxt_enter_bootloader(struct mxt_data *data) msleep(MXT_RESET_TIME); /* Do not need to scan since we know family ID */ - ret = mxt_lookup_bootloader_address(data, 0); + ret = mxt_probe_bootloader(data, 0); if (ret) return ret; From 9c18b74a85d90972601aca1b667327a7a2887c61 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 24 Dec 2014 16:07:50 +0000 Subject: [PATCH 122/154] Input: atmel_mxt_ts - improve bootloader state machine handling Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 188 ++++++++++++----------- 1 file changed, 102 insertions(+), 86 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 85672e0233e03d..7a4ed198d7e685 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -255,6 +255,8 @@ struct mxt_flash { size_t frame_size; unsigned int count; unsigned int retry; + u8 previous; + bool complete; }; /* Each client has this additional data */ @@ -648,67 +650,127 @@ static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) return 0; } -static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, - bool wait) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock); + +static int mxt_write_firmware_frame(struct mxt_data *data, struct mxt_flash *f) +{ + f->frame = (struct mxt_fw_frame*)(f->fw->data + f->pos); + + /* Take account of CRC bytes */ + f->frame_size = __be16_to_cpu(f->frame->size) + 2U; + + /* Write one frame to device */ + return mxt_bootloader_write(data, f->fw->data + f->pos, + f->frame_size); +} + +static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f) { struct device *dev = &data->client->dev; - u8 val; + u8 state; int ret; -recheck: - if (wait) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); + if (ret) { /* - * In application update mode, the interrupt - * line signals state transitions. We must wait for the - * CHG assertion before reading the status byte. - * Once the status byte has been read, the line is deasserted. + * TODO: handle -ERESTARTSYS better by terminating + * fw update process before returning to userspace + * by writing length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). */ - ret = mxt_wait_for_completion(data, &data->bl_completion, - MXT_FW_CHG_TIMEOUT); - if (ret) { - /* - * TODO: handle -ERESTARTSYS better by terminating - * fw update process before returning to userspace - * by writing length 0x000 to device (iff we are in - * WAITING_FRAME_DATA state). - */ - dev_err(dev, "Update wait error %d\n", ret); - return ret; - } + dev_warn(dev, "Update wait error %d\n", ret); } - ret = mxt_bootloader_read(data, &val, 1); + ret = mxt_bootloader_read(data, &state, 1); if (ret) return ret; + /* Remove don't care bits */ + if (state & ~MXT_BOOT_STATUS_MASK) + state &= ~MXT_BOOT_STATUS_MASK; + switch (state) { case MXT_WAITING_BOOTLOAD_CMD: + dev_info(dev, "Unlocking bootloader\n"); + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + return ret; + + break; + case MXT_WAITING_FRAME_DATA: - case MXT_APP_CRC_FAIL: - val &= ~MXT_BOOT_STATUS_MASK; + if ((f->previous != MXT_WAITING_BOOTLOAD_CMD) + && (f->previous != MXT_FRAME_CRC_PASS) + && (f->previous != MXT_FRAME_CRC_FAIL)) + goto unexpected; + + ret = mxt_write_firmware_frame(data, f); + if (ret) + return ret; + + break; + + case MXT_FRAME_CRC_CHECK: + if (f->previous != MXT_WAITING_FRAME_DATA) + goto unexpected; break; + case MXT_FRAME_CRC_PASS: - if (val == MXT_FRAME_CRC_CHECK) { - goto recheck; - } else if (val == MXT_FRAME_CRC_FAIL) { - dev_err(dev, "Bootloader CRC fail\n"); - return -EINVAL; + if (f->previous != MXT_FRAME_CRC_CHECK) + goto unexpected; + + /* Next frame */ + f->retry = 0; + f->pos += f->frame_size; + f->count++; + + if (f->pos >= f->fw->size) { + f->complete = true; + dev_info(dev, "Sent %u frames, %zu bytes\n", + f->count, f->fw->size); + } else if (f->count % 50 == 0) { + dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n", + f->count, f->pos, f->fw->size); } + break; + + case MXT_FRAME_CRC_FAIL: + f->retry++; + + if (f->retry > 20) { + dev_err(dev, "Retry count exceeded\n"); + return -EIO; + } else { + dev_dbg(dev, "Bootloader frame CRC failure\n"); + } + + /* Back off by 20ms per retry */ + msleep(f->retry * 20); + + break; + default: return -EINVAL; } - if (val != state) { - dev_err(dev, "Invalid bootloader state %02X != %02X\n", - val, state); - return -EINVAL; - } + f->previous = state; return 0; + +unexpected: + dev_err(dev, "Unexpected state transition\n"); + return -EINVAL; } -static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) +int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) { int ret; u8 buf[2]; @@ -2950,57 +3012,13 @@ static int mxt_load_fw(struct device *dev) if (ret) goto release_firmware; - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); - if (ret) { - /* Bootloader may still be unlocked from previous attempt */ - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); - if (ret) - goto disable_irq; - } else { - dev_info(dev, "Unlocking bootloader\n"); - - /* Unlock bootloader */ - ret = mxt_send_bootloader_cmd(data, true); - if (ret) - goto disable_irq; - } - - while (f.pos < f.fw->size) { - f.frame = (struct mxt_fw_frame*)(f.fw->data + f.pos); - - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); + while (true) { + ret = mxt_check_bootloader(data, &f); if (ret) - goto disable_irq; - - /* Take account of CRC bytes */ - f.frame_size = __be16_to_cpu(f.frame->size) + 2U; - - /* Write one frame to device */ - ret = mxt_bootloader_write(data, f.fw->data + f.pos, - f.frame_size); - if (ret) - goto disable_irq; - - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); - if (ret) { - f.retry++; - - /* Back off by 20ms per retry */ - msleep(f.retry * 20); - - if (f.retry > 20) { - dev_err(dev, "Retry count exceeded\n"); - goto disable_irq; - } - } else { - f.retry = 0; - f.pos += f.frame_size; - f.count++; - } + return ret; - if (f.count % 50 == 0) - dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n", - f.count, f.pos, f.fw->size); + if (f.complete) + break; } /* Wait for flash. */ @@ -3009,7 +3027,6 @@ static int mxt_load_fw(struct device *dev) if (ret) goto disable_irq; - dev_dbg(dev, "Sent %u frames, %lld bytes\n", f.count, f.pos); /* * Wait for device to reset. Some bootloader versions do not assert @@ -3019,7 +3036,6 @@ static int mxt_load_fw(struct device *dev) mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); data->in_bootloader = false; - disable_irq: disable_irq(data->irq); release_firmware: From 81dd63bf48a1e97f7f0002b3532b3177e2a46e0f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 6 Jan 2015 14:11:31 +0000 Subject: [PATCH 123/154] Input: atmel_mxt_ts - check firmware format before putting device into bootloader mode --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7a4ed198d7e685..31d28bc72b2b4e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3003,12 +3003,12 @@ static int mxt_load_fw(struct device *dev) return ret; } - ret = mxt_enter_bootloader(data); + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, f.fw); if (ret) goto release_firmware; - /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, f.fw); + ret = mxt_enter_bootloader(data); if (ret) goto release_firmware; From b046fa57d7833563f5b609758df60f402ee0facf Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Mar 2015 16:39:48 +0000 Subject: [PATCH 124/154] Input: atmel_mxt_ts - rename bl_completion to chg_completion --- drivers/input/touchscreen/atmel_mxt_ts.c | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 31d28bc72b2b4e..b55f5ed11f5eec 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -326,15 +326,15 @@ struct mxt_data { u8 T100_reportid_max; u16 T107_address; - /* for fw update in bootloader */ - struct completion bl_completion; - /* for reset handling */ struct completion reset_completion; /* for config update handling */ struct completion crc_completion; + /* for power up handling */ + struct completion chg_completion; + /* Indicates whether device is in suspend */ bool suspended; @@ -676,7 +676,7 @@ static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f) * CHG assertion before reading the status byte. * Once the status byte has been read, the line is deasserted. */ - ret = mxt_wait_for_completion(data, &data->bl_completion, + ret = mxt_wait_for_completion(data, &data->chg_completion, MXT_FW_CHG_TIMEOUT); if (ret) { /* @@ -1488,11 +1488,10 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) { struct mxt_data *data = dev_id; - if (data->in_bootloader) { - /* bootloader state transition completion */ - complete(&data->bl_completion); + complete(&data->chg_completion); + + if (data->in_bootloader) return IRQ_HANDLED; - } if (!data->object_table) return IRQ_HANDLED; @@ -2236,9 +2235,9 @@ static void mxt_regulator_enable(struct mxt_data *data) msleep(MXT_CHG_DELAY); retry_wait: - INIT_COMPLETION(data->bl_completion); + INIT_COMPLETION(data->chg_completion); data->in_bootloader = true; - error = mxt_wait_for_completion(data, &data->bl_completion, + error = mxt_wait_for_completion(data, &data->chg_completion, MXT_POWERON_DELAY); if (error == -EINTR) goto retry_wait; @@ -2986,7 +2985,7 @@ static int mxt_enter_bootloader(struct mxt_data *data) enable_irq(data->irq); } - INIT_COMPLETION(data->bl_completion); + INIT_COMPLETION(data->chg_completion); return 0; } @@ -3022,7 +3021,7 @@ static int mxt_load_fw(struct device *dev) } /* Wait for flash. */ - ret = mxt_wait_for_completion(data, &data->bl_completion, + ret = mxt_wait_for_completion(data, &data->chg_completion, MXT_FW_RESET_TIME); if (ret) goto disable_irq; @@ -3033,7 +3032,7 @@ static int mxt_load_fw(struct device *dev) * the CHG line after bootloading has finished, so ignore potential * errors. */ - mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); + mxt_wait_for_completion(data, &data->chg_completion, MXT_FW_RESET_TIME); data->in_bootloader = false; disable_irq: @@ -3496,9 +3495,9 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) data->pdata->cfg_name, strlen(data->pdata->cfg_name)); - init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); + init_completion(&data->chg_completion); mutex_init(&data->debug_msg_lock); error = request_threaded_irq(client->irq, NULL, mxt_interrupt, From 17ef3ea96b4a79b7381745d62a887862c3a22a6d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Mar 2015 16:36:45 +0000 Subject: [PATCH 125/154] Input: atmel_mxt_ts - make bootloader interrupt driven --- drivers/input/touchscreen/atmel_mxt_ts.c | 117 +++++++++++------------ 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b55f5ed11f5eec..d63c7bb56deec8 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef CONFIG_OF #include @@ -200,6 +201,7 @@ enum t100_type { #define MXT_REGULATOR_DELAY 150 /* msec */ #define MXT_CHG_DELAY 100 /* msec */ #define MXT_POWERON_DELAY 150 /* msec */ +#define MXT_BOOTLOADER_WAIT 36E5 /* 1 minute */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -249,6 +251,7 @@ struct mxt_fw_frame { /* Firmware update context */ struct mxt_flash { + struct mxt_data *data; const struct firmware *fw; struct mxt_fw_frame *frame; loff_t pos; @@ -256,7 +259,8 @@ struct mxt_flash { unsigned int count; unsigned int retry; u8 previous; - bool complete; + struct completion flash_completion; + struct delayed_work work; }; /* Each client has this additional data */ @@ -302,6 +306,7 @@ struct mxt_data { struct regulator *reg_avdd; char *fw_name; char *cfg_name; + struct mxt_flash *flash; /* Cached parameters from object table */ u16 T5_address; @@ -664,28 +669,17 @@ static int mxt_write_firmware_frame(struct mxt_data *data, struct mxt_flash *f) f->frame_size); } -static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f) +static int mxt_check_bootloader(struct mxt_data *data) { struct device *dev = &data->client->dev; + struct mxt_flash *f = data->flash; u8 state; int ret; - /* - * In application update mode, the interrupt - * line signals state transitions. We must wait for the - * CHG assertion before reading the status byte. - * Once the status byte has been read, the line is deasserted. - */ - ret = mxt_wait_for_completion(data, &data->chg_completion, - MXT_FW_CHG_TIMEOUT); - if (ret) { - /* - * TODO: handle -ERESTARTSYS better by terminating - * fw update process before returning to userspace - * by writing length 0x000 to device (iff we are in - * WAITING_FRAME_DATA state). - */ - dev_warn(dev, "Update wait error %d\n", ret); + /* Handle interrupt after download/flash process */ + if (f->pos >= f->fw->size) { + complete(&f->flash_completion); + return 0; } ret = mxt_bootloader_read(data, &state, 1); @@ -731,14 +725,12 @@ static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f) f->pos += f->frame_size; f->count++; - if (f->pos >= f->fw->size) { - f->complete = true; + if (f->pos >= f->fw->size) dev_info(dev, "Sent %u frames, %zu bytes\n", f->count, f->fw->size); - } else if (f->count % 50 == 0) { + else if (f->count % 50 == 0) dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n", f->count, f->pos, f->fw->size); - } break; @@ -763,6 +755,9 @@ static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f) f->previous = state; + /* Poll after 0.1s if no interrupt received */ + schedule_delayed_work(&f->work, HZ / 10); + return 0; unexpected: @@ -1490,8 +1485,12 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) complete(&data->chg_completion); - if (data->in_bootloader) - return IRQ_HANDLED; + if (data->in_bootloader) { + if (data->flash && &data->flash->work) + cancel_delayed_work_sync(&data->flash->work); + + return IRQ_RETVAL(mxt_check_bootloader(data)); + } if (!data->object_table) return IRQ_HANDLED; @@ -2957,16 +2956,11 @@ static int mxt_enter_bootloader(struct mxt_data *data) if (data->pdata->suspend_mode == MXT_SUSPEND_REGULATOR) mxt_regulator_enable(data); - if (data->pdata->suspend_mode == MXT_SUSPEND_DEEP_SLEEP) - enable_irq(data->irq); - data->suspended = false; } if (!data->in_bootloader) { /* Change to the bootloader mode */ - data->in_bootloader = true; - ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); if (ret) @@ -2979,66 +2973,71 @@ static int mxt_enter_bootloader(struct mxt_data *data) if (ret) return ret; + data->in_bootloader = true; mxt_free_input_device(data); mxt_free_object_table(data); - } else { - enable_irq(data->irq); } - INIT_COMPLETION(data->chg_completion); + dev_dbg(&data->client->dev, "Entered bootloader\n"); return 0; } +static void mxt_fw_work(struct work_struct *work) +{ + struct mxt_flash *f = + container_of(work, struct mxt_flash, work.work); + + mxt_check_bootloader(f->data); +} + static int mxt_load_fw(struct device *dev) { struct mxt_data *data = dev_get_drvdata(dev); - struct mxt_flash f = { 0, }; int ret; - ret = request_firmware(&f.fw, data->fw_name, dev); + data->flash = devm_kzalloc(dev, sizeof(struct mxt_flash), GFP_KERNEL); + if (!data->flash) + return -ENOMEM; + + data->flash->data = data; + + ret = request_firmware(&data->flash->fw, data->fw_name, dev); if (ret) { dev_err(dev, "Unable to open firmware %s\n", data->fw_name); - return ret; + goto free; } /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, f.fw); + ret = mxt_check_firmware_format(dev, data->flash->fw); if (ret) goto release_firmware; - ret = mxt_enter_bootloader(data); - if (ret) - goto release_firmware; + init_completion(&data->flash->flash_completion); + INIT_DELAYED_WORK(&data->flash->work, mxt_fw_work); - while (true) { - ret = mxt_check_bootloader(data, &f); + if (!data->in_bootloader) { + ret = mxt_enter_bootloader(data); if (ret) - return ret; - - if (f.complete) - break; + goto release_firmware; } - /* Wait for flash. */ - ret = mxt_wait_for_completion(data, &data->chg_completion, - MXT_FW_RESET_TIME); - if (ret) - goto disable_irq; + enable_irq(data->irq); + /* Poll after 0.1s if no interrupt received */ + schedule_delayed_work(&data->flash->work, HZ / 10); - /* - * Wait for device to reset. Some bootloader versions do not assert - * the CHG line after bootloading has finished, so ignore potential - * errors. - */ - mxt_wait_for_completion(data, &data->chg_completion, MXT_FW_RESET_TIME); + /* Wait for flash. */ + ret = mxt_wait_for_completion(data, &data->flash->flash_completion, + MXT_BOOTLOADER_WAIT); - data->in_bootloader = false; -disable_irq: disable_irq(data->irq); + cancel_delayed_work_sync(&data->flash->work); + data->in_bootloader = false; release_firmware: - release_firmware(f.fw); + release_firmware(data->flash->fw); +free: + devm_kfree(dev, data->flash); return ret; } From c497cf80cda116bc2c2f478b96f21a917e5ca464 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 30 Apr 2015 16:08:03 +0100 Subject: [PATCH 126/154] Input: atmel_mxt_ts - delay enabling IRQ when not using regulators The path of enabling the IRQ in the probe function is not safe in level triggered operation, if it was already powered up and there is a message waiting on the device (eg finger down) because the object table has not yet been read. This forces the ISR into a hard loop. Delay enabling the interrupt until it is first needed. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 47 ++++++++++++++++-------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d63c7bb56deec8..7298926a75cf3f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1943,9 +1943,23 @@ static int mxt_acquire_irq(struct mxt_data *data) { int error; - enable_irq(data->irq); + if (!data->irq) { + error = request_threaded_irq(data->client->irq, NULL, + mxt_interrupt, + data->pdata->irqflags | IRQF_ONESHOT, + data->client->name, data); + if (error) { + dev_err(&data->client->dev, "Error requesting irq\n"); + return error; + } + + /* Presence of data->irq means IRQ initialised */ + data->irq = data->client->irq; + } else { + enable_irq(data->irq); + } - if (data->use_retrigen_workaround) { + if (data->object_table && data->use_retrigen_workaround) { error = mxt_process_messages_until_invalid(data); if (error) return error; @@ -3022,7 +3036,9 @@ static int mxt_load_fw(struct device *dev) goto release_firmware; } - enable_irq(data->irq); + ret = mxt_acquire_irq(data); + if (ret) + goto release_firmware; /* Poll after 0.1s if no interrupt received */ schedule_delayed_work(&data->flash->work, HZ / 10); @@ -3485,7 +3501,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) data->client = client; data->pdata = pdata; - data->irq = client->irq; i2c_set_clientdata(client, data); if (data->pdata->cfg_name) @@ -3499,21 +3514,17 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) init_completion(&data->chg_completion); mutex_init(&data->debug_msg_lock); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags | IRQF_ONESHOT, - client->name, data); - if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; - } - if (pdata->suspend_mode == MXT_SUSPEND_REGULATOR) { + error = mxt_acquire_irq(data); + if (error) + goto err_free_mem; + error = mxt_probe_regulators(data); if (error) goto err_free_irq; - } - disable_irq(data->irq); + disable_irq(data->irq); + } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { @@ -3548,7 +3559,8 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) err_remove_sysfs_group: sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); err_free_irq: - free_irq(client->irq, data); + if (data->irq) + free_irq(data->irq, data); err_free_mem: kfree(data); return error; @@ -3563,7 +3575,10 @@ static int mxt_remove(struct i2c_client *client) &data->mem_access_attr); sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); - free_irq(data->irq, data); + + if (data->irq) + free_irq(data->irq, data); + regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); mxt_free_input_device(data); From cb9669c5998338d2b72739d2adf4bc7f3c9d86b5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 15 Apr 2015 10:31:10 -0700 Subject: [PATCH 127/154] Input: atmel_mxt_ts - use BIT() macro when reporting button state This makes the intent a tad more clear. Reviewed-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7298926a75cf3f..3b700d764a65fb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -936,15 +936,15 @@ static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; - bool button; int i; - /* Active-low switch */ for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message[1] & (1 << i)); - input_report_key(input, pdata->t19_keymap[i], button); + + /* Active-low switch */ + input_report_key(input, pdata->t19_keymap[i], + !(message[1] & BIT(i))); } } From b4a510fc1f8d2becc8f9663d8c397b01a47846bb Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 4 Jun 2015 16:15:52 +0100 Subject: [PATCH 128/154] Input: atmel_mxt_ts - use BIT() macro everywhere Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 74 ++++++++++++------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 3b700d764a65fb..7ae13429452dc5 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -87,12 +87,12 @@ #define MXT_COMMAND_DIAGNOSTIC 5 /* Define for T6 status byte */ -#define MXT_T6_STATUS_RESET (1 << 7) -#define MXT_T6_STATUS_OFL (1 << 6) -#define MXT_T6_STATUS_SIGERR (1 << 5) -#define MXT_T6_STATUS_CAL (1 << 4) -#define MXT_T6_STATUS_CFGERR (1 << 3) -#define MXT_T6_STATUS_COMSERR (1 << 2) +#define MXT_T6_STATUS_RESET BIT(7) +#define MXT_T6_STATUS_OFL BIT(6) +#define MXT_T6_STATUS_SIGERR BIT(5) +#define MXT_T6_STATUS_CAL BIT(4) +#define MXT_T6_STATUS_CFGERR BIT(3) +#define MXT_T6_STATUS_COMSERR BIT(2) /* MXT_GEN_POWER_T7 field */ struct t7_config { @@ -109,14 +109,14 @@ struct t7_config { #define MXT_T9_RANGE 18 /* MXT_TOUCH_MULTI_T9 status */ -#define MXT_T9_UNGRIP (1 << 0) -#define MXT_T9_SUPPRESS (1 << 1) -#define MXT_T9_AMP (1 << 2) -#define MXT_T9_VECTOR (1 << 3) -#define MXT_T9_MOVE (1 << 4) -#define MXT_T9_RELEASE (1 << 5) -#define MXT_T9_PRESS (1 << 6) -#define MXT_T9_DETECT (1 << 7) +#define MXT_T9_UNGRIP BIT(0) +#define MXT_T9_SUPPRESS BIT(1) +#define MXT_T9_AMP BIT(2) +#define MXT_T9_VECTOR BIT(3) +#define MXT_T9_MOVE BIT(4) +#define MXT_T9_RELEASE BIT(5) +#define MXT_T9_PRESS BIT(6) +#define MXT_T9_DETECT BIT(7) struct t9_range { u16 x; @@ -124,12 +124,12 @@ struct t9_range { } __packed; /* MXT_TOUCH_MULTI_T9 orient */ -#define MXT_T9_ORIENT_SWITCH (1 << 0) +#define MXT_T9_ORIENT_SWITCH BIT(0) /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 -#define MXT_COMMS_RETRIGEN (1 << 6) +#define MXT_COMMS_RETRIGEN BIT(6) /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 @@ -137,18 +137,18 @@ struct t9_range { #define MXT_BACKUP_VALUE 0x55 /* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ -#define MXT_T42_MSG_TCHSUP (1 << 0) +#define MXT_T42_MSG_TCHSUP BIT(0) /* T63 Stylus */ -#define MXT_T63_STYLUS_PRESS (1 << 0) -#define MXT_T63_STYLUS_RELEASE (1 << 1) -#define MXT_T63_STYLUS_MOVE (1 << 2) -#define MXT_T63_STYLUS_SUPPRESS (1 << 3) +#define MXT_T63_STYLUS_PRESS BIT(0) +#define MXT_T63_STYLUS_RELEASE BIT(1) +#define MXT_T63_STYLUS_MOVE BIT(2) +#define MXT_T63_STYLUS_SUPPRESS BIT(3) -#define MXT_T63_STYLUS_DETECT (1 << 4) -#define MXT_T63_STYLUS_TIP (1 << 5) -#define MXT_T63_STYLUS_ERASER (1 << 6) -#define MXT_T63_STYLUS_BARREL (1 << 7) +#define MXT_T63_STYLUS_DETECT BIT(4) +#define MXT_T63_STYLUS_TIP BIT(5) +#define MXT_T63_STYLUS_ERASER BIT(6) +#define MXT_T63_STYLUS_BARREL BIT(7) #define MXT_T63_STYLUS_PRESSURE_MASK 0x3F @@ -159,13 +159,13 @@ struct t9_range { #define MXT_T100_XRANGE 13 #define MXT_T100_YRANGE 24 -#define MXT_T100_CFG_SWITCHXY (1 << 5) +#define MXT_T100_CFG_SWITCHXY BIT(5) -#define MXT_T100_TCHAUX_VECT (1 << 0) -#define MXT_T100_TCHAUX_AMPL (1 << 1) -#define MXT_T100_TCHAUX_AREA (1 << 2) +#define MXT_T100_TCHAUX_VECT BIT(0) +#define MXT_T100_TCHAUX_AMPL BIT(1) +#define MXT_T100_TCHAUX_AREA BIT(2) -#define MXT_T100_DETECT (1 << 7) +#define MXT_T100_DETECT BIT(7) #define MXT_T100_TYPE_MASK 0x70 enum t100_type { @@ -179,13 +179,13 @@ enum t100_type { /* Gen2 Active Stylus */ #define MXT_T107_STYLUS_STYAUX 42 -#define MXT_T107_STYLUS_STYAUX_PRESSURE (1 << 0) -#define MXT_T107_STYLUS_STYAUX_PEAK (1 << 4) +#define MXT_T107_STYLUS_STYAUX_PRESSURE BIT(0) +#define MXT_T107_STYLUS_STYAUX_PEAK BIT(4) -#define MXT_T107_STYLUS_HOVER (1 << 0) -#define MXT_T107_STYLUS_TIPSWITCH (1 << 1) -#define MXT_T107_STYLUS_BUTTON0 (1 << 2) -#define MXT_T107_STYLUS_BUTTON1 (1 << 3) +#define MXT_T107_STYLUS_HOVER BIT(0) +#define MXT_T107_STYLUS_TIPSWITCH BIT(1) +#define MXT_T107_STYLUS_BUTTON0 BIT(2) +#define MXT_T107_STYLUS_BUTTON1 BIT(3) #define MXT_TOUCH_MAJOR_DEFAULT 1 #define MXT_PRESSURE_DEFAULT 1 @@ -215,7 +215,7 @@ enum t100_type { #define MXT_FRAME_CRC_PASS 0x04 #define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ #define MXT_BOOT_STATUS_MASK 0x3f -#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_EXTENDED_ID BIT(5) #define MXT_BOOT_ID_MASK 0x1f /* Touchscreen absolute values */ From 861c6738aabaea7a456798639088d7e23652ab29 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 16:15:24 +0100 Subject: [PATCH 129/154] Input: atmel_mxt_ts - use get_unaligned_le16 Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7ae13429452dc5..fd125ae98ae698 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1061,8 +1062,8 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) return; status = message[1]; - x = (message[3] << 8) | message[2]; - y = (message[5] << 8) | message[4]; + x = get_unaligned_le16(&message[2]); + y = get_unaligned_le16(&message[4]); if (status & MXT_T100_DETECT) { type = (status & MXT_T100_TYPE_MASK) >> 4; From bae1dc0fd8c271d3f92160825356cfaa8c27c9ac Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 16:16:06 +0100 Subject: [PATCH 130/154] Input: atmel_mxt_ts - move #defines as per upstream Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fd125ae98ae698..c88153fd67b51b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -178,6 +178,9 @@ enum t100_type { MXT_T100_TYPE_LARGE_TOUCH = 6, }; +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 + /* Gen2 Active Stylus */ #define MXT_T107_STYLUS_STYAUX 42 #define MXT_T107_STYLUS_STYAUX_PRESSURE BIT(0) @@ -188,9 +191,6 @@ enum t100_type { #define MXT_T107_STYLUS_BUTTON0 BIT(2) #define MXT_T107_STYLUS_BUTTON1 BIT(3) -#define MXT_TOUCH_MAJOR_DEFAULT 1 -#define MXT_PRESSURE_DEFAULT 1 - /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ From 81a5c7a3d922d011baa206af806d85d950090a43 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 16:18:48 +0100 Subject: [PATCH 131/154] Input: atmel_mxt_ts - Improve reporting of stylus buttons The buttons should only be reported only on T107 messages, but they must be reported on every message since the buttons can be released as the stylus goes out of detect. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c88153fd67b51b..ee9ae5b0265d1f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1052,8 +1052,6 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) u8 orientation = 0; bool active = false; bool hover = false; - bool eraser = false; - bool barrel = false; id = message[0] - data->T100_reportid_min - 2; @@ -1100,6 +1098,12 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) break; case MXT_T100_TYPE_ACTIVE_STYLUS: + /* Report input buttons */ + input_report_key(input_dev, BTN_STYLUS, + message[6] & MXT_T107_STYLUS_BUTTON0); + input_report_key(input_dev, BTN_STYLUS2, + message[6] & MXT_T107_STYLUS_BUTTON1); + /* stylus in range, but position unavailable */ if (!(message[6] & MXT_T107_STYLUS_HOVER)) break; @@ -1107,8 +1111,6 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) active = true; tool = MT_TOOL_PEN; major = MXT_TOUCH_MAJOR_DEFAULT; - eraser = message[6] & MXT_T107_STYLUS_BUTTON0; - barrel = message[6] & MXT_T107_STYLUS_BUTTON1; if (!(message[6] & MXT_T107_STYLUS_TIPSWITCH)) hover = true; @@ -1155,8 +1157,6 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); - input_report_key(input_dev, BTN_STYLUS, eraser); - input_report_key(input_dev, BTN_STYLUS2, barrel); } else { dev_dbg(dev, "[%u] release\n", id); From 9228e3f8fd72b42a7daeb1cca379e7bb03265339 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 6 Apr 2015 11:25:13 -0700 Subject: [PATCH 132/154] Input: atmel_mxt_ts - update hovering finger support The hovering finger support was based on Chung-Yih's work in the ChromiumOS downstream kernel: https://chromium-review.googlesource.com/#/c/219280/ In order to report hovering touch events to userspace applications, the patch uses the axis ABS_MT_DISTANCE to indicate if a finger is hovering or not. In other words, we could use the value of the axis ABS_MT_DISTANCE to tell if a finger is a touch or hovering finger: DISTANCE_ACTIVE_TOUCH: a finger touches on touchpad, ABS_MT_DISTANCE is zero DISTANCE_HOVERING: a hovering finger on top of touchpad [javier: rework hover support] Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov [nick: merge into maxtouch-v3.14 branch] Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 64 ++++++++++++++---------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index ee9ae5b0265d1f..01bf4a123ca380 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -178,6 +178,9 @@ enum t100_type { MXT_T100_TYPE_LARGE_TOUCH = 6, }; +#define MXT_DISTANCE_ACTIVE_TOUCH 0 +#define MXT_DISTANCE_HOVERING 1 + #define MXT_TOUCH_MAJOR_DEFAULT 1 #define MXT_PRESSURE_DEFAULT 1 @@ -1043,10 +1046,11 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) struct input_dev *input_dev = data->input_dev; int id; u8 status; - u8 type; - int x; - int y; - int tool; + u8 type = 0; + u16 x; + u16 y; + int distance = 0; + int tool = 0; u8 major = 0; u8 pressure = 0; u8 orientation = 0; @@ -1068,28 +1072,40 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) switch (type) { case MXT_T100_TYPE_HOVERING_FINGER: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_HOVERING; hover = true; - /* fall through */ + active = true; + break; + case MXT_T100_TYPE_FINGER: case MXT_T100_TYPE_GLOVE: - active = true; tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + hover = false; + active = true; if (data->t100_aux_area) major = message[data->t100_aux_area]; + if (data->t100_aux_ampl) pressure = message[data->t100_aux_ampl]; + if (data->t100_aux_vect) orientation = message[data->t100_aux_vect]; break; case MXT_T100_TYPE_PASSIVE_STYLUS: - active = true; tool = MT_TOOL_PEN; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + hover = false; + active = true; - /* Passive stylus is reported with size zero so - * hardcode */ + /* + * Passive stylus is reported with size zero so + * hardcode. + */ major = MXT_TOUCH_MAJOR_DEFAULT; if (data->t100_aux_ampl) @@ -1108,14 +1124,17 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) if (!(message[6] & MXT_T107_STYLUS_HOVER)) break; - active = true; tool = MT_TOOL_PEN; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + active = true; major = MXT_TOUCH_MAJOR_DEFAULT; - if (!(message[6] & MXT_T107_STYLUS_TIPSWITCH)) + if (!(message[6] & MXT_T107_STYLUS_TIPSWITCH)) { hover = true; - else if (data->stylus_aux_pressure) + distance = MXT_DISTANCE_HOVERING; + } else if (data->stylus_aux_pressure) { pressure = message[data->stylus_aux_pressure]; + } break; @@ -1129,20 +1148,12 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) } } - if (hover) { - pressure = 0; - major = 0; - } else if (active) { - /* - * Values reported should be non-zero if tool is touching the - * device - */ - if (pressure == 0) - pressure = MXT_PRESSURE_DEFAULT; - - if (major == 0) - major = MXT_TOUCH_MAJOR_DEFAULT; - } + /* + * Values reported should be non-zero if tool is touching the + * device + */ + if (!pressure && !hover) + pressure = MXT_PRESSURE_DEFAULT; input_mt_slot(input_dev, id); @@ -1155,6 +1166,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_DISTANCE, distance); input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); } else { From 90c2778071019808fcfa817e6dd3fb8961ea8843 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 6 Apr 2015 11:25:13 -0700 Subject: [PATCH 133/154] Input: atmel_mxt_ts - factor out T9 and T100 init functions Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov [Merge in maxtouch-v3.14 branch] Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 270 ++++++++++------------- 1 file changed, 121 insertions(+), 149 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 01bf4a123ca380..303b0c15d25e80 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -300,6 +300,7 @@ struct mxt_data { bool update_input; u8 last_message_count; u8 num_touchids; + u8 multitouch; struct t7_config t7_cfg; u8 num_stylusids; unsigned long t15_keystatus; @@ -2081,6 +2082,7 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T71_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: + data->multitouch = MXT_TOUCH_MULTI_T9; /* Only handle messages from first T9 instance */ data->T9_reportid_min = min_id; data->T9_reportid_max = min_id + @@ -2114,6 +2116,7 @@ static int mxt_parse_object_table(struct mxt_data *data, data->num_stylusids = 1; break; case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; data->T100_reportid_min = min_id; data->T100_reportid_max = max_id; /* first two report IDs reserved */ @@ -2363,121 +2366,6 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } -static int mxt_input_open(struct input_dev *dev); -static void mxt_input_close(struct input_dev *dev); - -static int mxt_initialize_t9_input_device(struct mxt_data *data) -{ - struct device *dev = &data->client->dev; - const struct mxt_platform_data *pdata = data->pdata; - struct input_dev *input_dev; - int error; - unsigned int num_mt_slots; - unsigned int mt_flags = 0; - int i; - - error = mxt_read_t9_resolution(data); - if (error) - dev_warn(dev, "Failed to initialize T9 resolution\n"); - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - input_dev->name = "Atmel maXTouch Touchscreen"; - input_dev->phys = data->phys; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = dev; - input_dev->open = mxt_input_open; - input_dev->close = mxt_input_close; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } else { - mt_flags |= INPUT_MT_DIRECT; - } - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); - - /* For multi touch */ - num_mt_slots = data->num_touchids + data->num_stylusids; - error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); - if (error) { - dev_err(dev, "Error %d initialising slots\n", error); - goto err_free_mem; - } - - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); - input_set_abs_params(input_dev, ABS_MT_ORIENTATION, - 0, 255, 0, 0); - - /* For T63 active stylus */ - if (data->T63_reportid_min) { - input_set_capability(input_dev, EV_KEY, BTN_STYLUS); - input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); - input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - } - - /* For T15 key array */ - if (data->T15_reportid_min) { - data->t15_keystatus = 0; - - for (i = 0; i < data->pdata->t15_num_keys; i++) - input_set_capability(input_dev, EV_KEY, - data->pdata->t15_keymap[i]); - } - - input_set_drvdata(input_dev, data); - - error = input_register_device(input_dev); - if (error) { - dev_err(dev, "Error %d registering input device\n", error); - goto err_free_mem; - } - - data->input_dev = input_dev; - - return 0; - -err_free_mem: - input_free_device(input_dev); - return error; -} - static int mxt_read_t107_stylus_config(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -2590,78 +2478,166 @@ static int mxt_read_t100_config(struct mxt_data *data) return 0; } -static int mxt_initialize_t100_input_device(struct mxt_data *data) +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_input_device(struct mxt_data *data) { struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; struct input_dev *input_dev; int error; + unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; - error = mxt_read_t100_config(data); - if (error) { - dev_err(dev, "Failed to read T100 config\n"); - return error; - } + switch (data->multitouch) { + case MXT_TOUCH_MULTI_T9: + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + break; + + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + num_mt_slots = data->num_touchids; + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to read T100 config\n"); + + if (data->T107_address) { + error = mxt_read_t107_stylus_config(data); + if (error) + dev_warn(dev, "Failed to read T107 config\n"); + } - mxt_read_t107_stylus_config(data); + break; + + default: + dev_err(dev, "Invalid multitouch object\n"); + return -EINVAL; + } input_dev = input_allocate_device(); - if (!data || !input_dev) + if (!input_dev) return -ENOMEM; if (data->pdata->input_name) input_dev->name = data->pdata->input_name; else - input_dev->name = "atmel_mxt_ts T100 touchscreen"; + input_dev->name = "Atmel maXTouch Touchscreen"; input_dev->phys = data->phys; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &data->client->dev; + input_dev->dev.parent = dev; input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; set_bit(EV_ABS, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + if (pdata->t19_num_keys) { + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); + + mt_flags |= INPUT_MT_POINTER; + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; + } else { + mt_flags |= INPUT_MT_DIRECT; + } + /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); - if (data->t100_aux_ampl) - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + } /* For multi touch */ - error = input_mt_init_slots(input_dev, data->num_touchids, - INPUT_MT_DIRECT); + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { dev_err(dev, "Error %d initialising slots\n", error); goto err_free_mem; } - input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) { + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_DISTANCE, + MXT_DISTANCE_ACTIVE_TOUCH, + MXT_DISTANCE_HOVERING, + 0, 0); + } + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0); - if (data->T107_address) { - input_set_capability(input_dev, EV_KEY, BTN_STYLUS); - input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); - } - - if (data->t100_aux_area) + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_area)) { input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_AREA, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + (data->t100_aux_ampl || data->stylus_aux_pressure))) { + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect) { + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + } - if (data->t100_aux_ampl | data->stylus_aux_pressure) + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl) { input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + } - if (data->t100_aux_vect) + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect)) { input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 255, 0, 0); + } + + /* For active stylus */ + if (data->T63_reportid_min || data->T107_address) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + + /* For T15 Key Array */ + if (data->T15_reportid_min) { + data->t15_keystatus = 0; + + for (i = 0; i < data->pdata->t15_num_keys; i++) + input_set_capability(input_dev, EV_KEY, + data->pdata->t15_keymap[i]); + } input_set_drvdata(input_dev, data); @@ -2833,12 +2809,8 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "Error %d updating config\n", error); } - if (data->T9_reportid_min) { - error = mxt_initialize_t9_input_device(data); - if (error) - goto err_free_object_table; - } else if (data->T100_reportid_min) { - error = mxt_initialize_t100_input_device(data); + if (data->multitouch) { + error = mxt_initialize_input_device(data); if (error) goto err_free_object_table; } else { From 75272cac1e3570d282cefb4e3ad995eaa8d38070 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Mon, 6 Apr 2015 13:10:30 -0700 Subject: [PATCH 134/154] Input: atmel_mxt_ts - split out touchpad initialisation logic If the "linux,gpio-keymap" DT property is defined, the T19 keys are configured and the device is setup as a touchpad rather than a touchscreen. The logic is part of the input device initialization routine but it can be factored out to its own function to simplify the former. Signed-off-by: Sjoerd Simons Signed-off-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov [Reinstate INPUT_MT_DIRECT] [put back variable i in mxt_initialize_input_device] Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 55 ++++++++++++++---------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 303b0c15d25e80..d603809fed3d24 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2481,10 +2481,33 @@ static int mxt_read_t100_config(struct mxt_data *data) static int mxt_input_open(struct input_dev *dev); static void mxt_input_close(struct input_dev *dev); +static void mxt_set_up_as_touchpad(struct input_dev *input_dev, + struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + int i; + + input_dev->name = "Atmel maXTouch Touchpad"; + + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); +} + static int mxt_initialize_input_device(struct mxt_data *data) { - struct device *dev = &data->client->dev; const struct mxt_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; struct input_dev *input_dev; int error; unsigned int num_mt_slots; @@ -2536,28 +2559,6 @@ static int mxt_initialize_input_device(struct mxt_data *data) set_bit(EV_ABS, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_TOUCH); - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } else { - mt_flags |= INPUT_MT_DIRECT; - } - /* For single touch */ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); @@ -2568,6 +2569,14 @@ static int mxt_initialize_input_device(struct mxt_data *data) input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); } + /* If device has buttons we assume it is a touchpad */ + 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 */ error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { From 1d51930efcf6825131bddbd79c0482cd9d0d6522 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 7 Apr 2015 16:30:01 -0700 Subject: [PATCH 135/154] Input: atmel_mxt_ts - add support for Google Pixel 2 This change allows atmel_mxt_ts to bind to ACPI-enumerated devices in Google Pixel 2 (2015). While newer version of ACPI standard allow use of device-tree-like properties in device descriptions, the version of ACPI implemented in Google BIOS does not support them, and we have to resort to DMI data to specify exact characteristics of the devices (touchpad vs. touchscreen, GPIO to button mapping, etc). Pixel 1 continues to use i2c devices and platform data created by chromeos-laptop driver, since ACPI does not enumerate them. Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 149 +++++++++++++++++++++-- 1 file changed, 139 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d603809fed3d24..82aace377dca09 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -14,6 +14,8 @@ * */ +#include +#include #include #include #include @@ -3407,14 +3409,14 @@ static void mxt_input_close(struct input_dev *dev) } #ifdef CONFIG_OF -static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; u32 *keymap; int proplen, ret; if (!client->dev.of_node) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENOENT); pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -3458,7 +3460,103 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) return pdata; } #else -static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + return ERR_PTR(-ENOENT); +} +#endif + +#ifdef CONFIG_ACPI + +struct mxt_acpi_platform_data { + const char *hid; + struct mxt_platform_data pdata; +}; + +static unsigned int samus_touchpad_buttons[] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + +static struct mxt_acpi_platform_data samus_platform_data[] = { + { + /* Touchpad */ + .hid = "ATML0000", + .pdata = { + .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons), + .t19_keymap = samus_touchpad_buttons, + }, + }, + { + /* Touchscreen */ + .hid = "ATML0001", + }, + { } +}; + +static const struct dmi_system_id mxt_dmi_table[] = { + { + /* 2015 Google Pixel */ + .ident = "Chromebook Pixel 2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), + }, + .driver_data = samus_platform_data, + }, + { } +}; + +static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client) +{ + struct acpi_device *adev; + const struct dmi_system_id *system_id; + const struct mxt_acpi_platform_data *acpi_pdata; + + /* + * Ignore ACPI devices representing bootloader mode. + * + * This is a bit of a hack: Google Chromebook BIOS creates ACPI + * devices for both application and bootloader modes, but we are + * interested in application mode only (if device is in bootloader + * mode we'll end up switching into application anyway). So far + * application mode addresses were all above 0x40, so we'll use it + * as a threshold. + */ + if (client->addr < 0x40) + return ERR_PTR(-ENXIO); + + adev = ACPI_COMPANION(&client->dev); + if (!adev) + return ERR_PTR(-ENOENT); + + system_id = dmi_first_match(mxt_dmi_table); + if (!system_id) + return ERR_PTR(-ENOENT); + + acpi_pdata = system_id->driver_data; + if (!acpi_pdata) + return ERR_PTR(-ENOENT); + + while (acpi_pdata->hid) { + if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) + return &acpi_pdata->pdata; + + acpi_pdata++; + } + + return ERR_PTR(-ENOENT); +} +#else +static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client) +{ + return ERR_PTR(-ENOENT); +} +#endif + +static struct mxt_platform_data *mxt_default_pdata(struct i2c_client *client) { struct mxt_platform_data *pdata; @@ -3471,7 +3569,31 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) return pdata; } -#endif + +static const struct mxt_platform_data * +mxt_get_platform_data(struct i2c_client *client) +{ + const struct mxt_platform_data *pdata; + + pdata = dev_get_platdata(&client->dev); + if (pdata) + return pdata; + + pdata = mxt_parse_dt(client); + if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT) + return pdata; + + pdata = mxt_parse_acpi(client); + if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT) + return pdata; + + pdata = mxt_default_pdata(client); + if (!IS_ERR(pdata)) + return pdata; + + dev_err(&client->dev, "No platform data specified\n"); + return ERR_PTR(-EINVAL); +} static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -3479,12 +3601,9 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) const struct mxt_platform_data *pdata; int error; - pdata = dev_get_platdata(&client->dev); - if (!pdata) { - pdata = mxt_parse_dt(client); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } + pdata = mxt_get_platform_data(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); if (!data) @@ -3622,6 +3741,15 @@ static const struct of_device_id mxt_of_match[] = { }; MODULE_DEVICE_TABLE(of, mxt_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id mxt_acpi_id[] = { + { "ATML0000", 0 }, /* Touchpad */ + { "ATML0001", 0 }, /* Touchscreen */ + { } +}; +MODULE_DEVICE_TABLE(acpi, mxt_acpi_id); +#endif + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -3636,6 +3764,7 @@ static struct i2c_driver mxt_driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mxt_of_match), + .acpi_match_table = ACPI_PTR(mxt_acpi_id), .pm = &mxt_pm_ops, }, .probe = mxt_probe, From 29336b4e09c14de664ad7f27626a5fc0cbf9f764 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 4 Jun 2015 16:06:08 +0100 Subject: [PATCH 136/154] Input: atmel_mxt_ts - rename TOUCH_CTRL to T9_CTRL Signed-off-by: Nick Dyer --- .../devicetree/bindings/input/atmel,maxtouch.txt | 2 +- drivers/input/touchscreen/atmel_mxt_ts.c | 11 ++++++----- include/dt-bindings/input/atmel_mxt_ts.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt index e879f6d1d2c5f1..6c868fce477c83 100644 --- a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -24,7 +24,7 @@ Optional properties for main touchpad device: - atmel,suspend-mode: Select method used to suspend: MXT_SUSPEND_DEEP_SLEEP - use T7 to suspend the device into deep sleep - MXT_SUSPEND_TOUCH_CTRL - use T9.CTRL to turn off touch processing + MXT_SUSPEND_T9_CTRL - use T9.CTRL to turn off touch processing Definitions are in . - atmel,reset-gpio: Configure RESET GPIO. Required for regulator support. diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 82aace377dca09..124c99f1abe991 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -107,7 +107,7 @@ struct t7_config { #define MXT_POWER_CFG_DEEPSLEEP 1 /* 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 @@ -3328,12 +3328,13 @@ static void mxt_start(struct mxt_data *data) return; switch (data->pdata->suspend_mode) { - case MXT_SUSPEND_TOUCH_CTRL: + case MXT_SUSPEND_T9_CTRL: mxt_soft_reset(data); /* Touch enable */ + /* 0x83 = SCANEN | RPTEN | ENABLE */ mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83); break; case MXT_SUSPEND_REGULATOR: @@ -3367,10 +3368,10 @@ static void mxt_stop(struct mxt_data *data) return; switch (data->pdata->suspend_mode) { - case MXT_SUSPEND_TOUCH_CTRL: + case MXT_SUSPEND_T9_CTRL: /* Touch disable */ mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0); break; case MXT_SUSPEND_REGULATOR: diff --git a/include/dt-bindings/input/atmel_mxt_ts.h b/include/dt-bindings/input/atmel_mxt_ts.h index c7637925da5497..e4c4c0f6fa373c 100644 --- a/include/dt-bindings/input/atmel_mxt_ts.h +++ b/include/dt-bindings/input/atmel_mxt_ts.h @@ -15,7 +15,7 @@ enum mxt_suspend_mode { MXT_SUSPEND_DEEP_SLEEP = 0, - MXT_SUSPEND_TOUCH_CTRL = 1, + MXT_SUSPEND_T9_CTRL = 1, MXT_SUSPEND_REGULATOR = 2, }; From 4a6d6cdd80879589c29c0e1a631e0486cceec7aa Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 4 Jun 2015 16:07:02 +0100 Subject: [PATCH 137/154] Input: atmel_mxt_ts - reorder init completions to reduce upstream patch churn Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 124c99f1abe991..c60905f69d51bf 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3623,9 +3623,9 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) data->pdata->cfg_name, strlen(data->pdata->cfg_name)); + init_completion(&data->chg_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); - init_completion(&data->chg_completion); mutex_init(&data->debug_msg_lock); if (pdata->suspend_mode == MXT_SUSPEND_REGULATOR) { From 938a1d7e03819e89c833b89f5e0f26990d83f53d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 11:54:41 +0100 Subject: [PATCH 138/154] Input: atmel_mxt_ts - fix minor checkpatch errors Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index c60905f69d51bf..707170145c69d6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -666,7 +666,7 @@ static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock); static int mxt_write_firmware_frame(struct mxt_data *data, struct mxt_flash *f) { - f->frame = (struct mxt_fw_frame*)(f->fw->data + f->pos); + f->frame = (struct mxt_fw_frame *)(f->fw->data + f->pos); /* Take account of CRC bytes */ f->frame_size = __be16_to_cpu(f->frame->size) + 2U; From d4f1244a7c313d4244308cab062eee52cd7d5d08 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 12:07:21 +0100 Subject: [PATCH 139/154] Input: atmel_mxt_ts - tidy up flow in bootloader retries This fixes a warning from checkpatch. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 707170145c69d6..46b81cdf581c6f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -742,18 +742,15 @@ static int mxt_check_bootloader(struct mxt_data *data) break; case MXT_FRAME_CRC_FAIL: - f->retry++; - if (f->retry > 20) { dev_err(dev, "Retry count exceeded\n"); return -EIO; - } else { - dev_dbg(dev, "Bootloader frame CRC failure\n"); } /* Back off by 20ms per retry */ + dev_dbg(dev, "Bootloader frame CRC failure\n"); + f->retry++; msleep(f->retry * 20); - break; default: From 2c426e170d92c0d931e47575129b1d4658642362 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 13:37:37 +0100 Subject: [PATCH 140/154] Input: atmel_mxt_ts - improve formatting Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 46b81cdf581c6f..d01ed9346165fe 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1314,11 +1314,11 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) * yet registered or returning from suspend */ mxt_dump_message(data, message); - } else if (report_id >= data->T9_reportid_min - && report_id <= data->T9_reportid_max) { + } else if (report_id >= data->T9_reportid_min && + report_id <= data->T9_reportid_max) { mxt_proc_t9_message(data, message); - } else if (report_id >= data->T100_reportid_min - && report_id <= data->T100_reportid_max) { + } else if (report_id >= data->T100_reportid_min && + report_id <= data->T100_reportid_max) { mxt_proc_t100_message(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); From 8c96398d14bfdd9b78434680844f1cd36a78f61c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 12:06:43 +0100 Subject: [PATCH 141/154] Input: atmel_mxt_ts - remove guards around inclusion of of_gpio.h Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d01ed9346165fe..e90aa303d05f78 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -27,16 +27,13 @@ #include #include #include +#include #include #include #include #include #include -#ifdef CONFIG_OF -#include -#endif - /* Configuration file */ #define MXT_CFG_MAGIC "OBP_RAW V1" From 5ef9579688981d6e9fbeb1913c10e884781d49ef Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Jun 2015 14:02:57 +0100 Subject: [PATCH 142/154] Input: atmel_mxt_ts - remove warning on zero T44 count Signed-off-by: Nick Dyer --- 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 e90aa303d05f78..90936a3af3e871 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1386,16 +1386,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; } From 3fafaffb086ab7e9762a7183517dce9a5eff1900 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 4 Jun 2015 16:04:07 +0100 Subject: [PATCH 143/154] Input: atmel_mxt_ts - remove debug about reset GPIO Downgrade to a comment since it appears on every probe when debug is enabled otherwise. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 90936a3af3e871..171db40d794e16 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2280,8 +2280,8 @@ static int mxt_probe_regulators(struct mxt_data *data) struct device *dev = &data->client->dev; int error; + /* Must have reset GPIO to use regulator support */ if (!gpio_is_valid(data->pdata->gpio_reset)) { - dev_dbg(dev, "Must have reset GPIO to use regulator support\n"); error = -EINVAL; goto fail; } @@ -3416,7 +3416,6 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) if (!pdata) return ERR_PTR(-ENOMEM); - /* reset gpio */ pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node, "atmel,reset-gpio", 0, NULL); From bf9da7d0e4263c0eee1d623608b3618d650b7f63 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 17 Jun 2015 11:49:17 +0100 Subject: [PATCH 144/154] Input: atmel_mxt_ts - don't report zero pressure from T9 If T9.CTRL DISAMP is set, then pressure is reported as zero. This means some app layers (eg tslib) will ignore the contact. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 171db40d794e16..bd0b8838bf1932 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1022,6 +1022,10 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) tool = MT_TOOL_FINGER; } + /* if active, pressure must be non-zero */ + if (!amplitude) + amplitude = MXT_PRESSURE_DEFAULT; + /* Touch active */ input_mt_report_slot_state(input_dev, tool, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); From f1b1b7d0475758c87c16f20df35a964ba7f5b68f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 22 Jun 2015 09:47:58 +0100 Subject: [PATCH 145/154] Input: atmel_mxt_ts - tool type is ignored when slot is closed input_mt_report_slot_state() ignores the tool when the slot is closed. Remove the tool type from these function calls, which has caused a bit of confusion. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index bd0b8838bf1932..aea16e4bf10d44 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1009,8 +1009,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) * have happened. */ if (status & MXT_T9_RELEASE) { - input_mt_report_slot_state(input_dev, - MT_TOOL_FINGER, 0); + input_mt_report_slot_state(input_dev, 0, 0); mxt_input_sync(data); } @@ -1035,7 +1034,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); } else { /* Touch no longer active, close out slot */ - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + input_mt_report_slot_state(input_dev, 0, 0); } data->update_input = true; @@ -1283,7 +1282,7 @@ static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); } else { - input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); + input_mt_report_slot_state(input_dev, 0, 0); } input_report_key(input_dev, BTN_STYLUS, @@ -3313,7 +3312,7 @@ static void mxt_reset_slots(struct mxt_data *data) for (id = 0; id < num_mt_slots; id++) { input_mt_slot(input_dev, id); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + input_mt_report_slot_state(input_dev, 0, 0); } mxt_input_sync(data); From 17b6341aa792be3ae8e7000ebd2e349aae78e1ae Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 6 Aug 2015 10:38:41 +0100 Subject: [PATCH 146/154] Input: atmel_mxt_ts - refactor to add np variable in mxt_parse_dt Downgrade failure to read gpio-keymap to a warning (initialized to KEY_RESERVED by devm_kzalloc) Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 32 ++++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index aea16e4bf10d44..8165dd72375350 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3409,27 +3409,25 @@ 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; 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); - pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node, - "atmel,reset-gpio", 0, NULL); + pdata->gpio_reset = of_get_named_gpio_flags(np, "atmel,reset-gpio", + 0, NULL); - of_property_read_string(client->dev.of_node, "atmel,cfg_name", - &pdata->cfg_name); + of_property_read_string(np, "atmel,cfg_name", &pdata->cfg_name); - of_property_read_string(client->dev.of_node, "atmel,input_name", - &pdata->input_name); + of_property_read_string(np, "atmel,input_name", &pdata->input_name); - 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, @@ -3438,20 +3436,16 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) if (!keymap) return ERR_PTR(-ENOMEM); - ret = of_property_read_u32_array(client->dev.of_node, - "linux,gpio-keymap", keymap, pdata->t19_num_keys); - if (ret) { - dev_err(&client->dev, - "Unable to read device tree key codes: %d\n", - ret); - return NULL; - } + 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; } From ab440dec3818b23d2bcd3afcc121cd2c9b2d789b Mon Sep 17 00:00:00 2001 From: Pan Xinhui Date: Tue, 4 Aug 2015 16:53:04 -0700 Subject: [PATCH 147/154] Input: atmel_mxt_ts - suspend/resume causes panic if input_dev fails to init 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 Signed-off-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- 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 8165dd72375350..6c248c490f3a7a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3697,6 +3697,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) @@ -3713,6 +3716,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) From 292a138b2d0acff9f8af551641e9fcc3496a4221 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 15 Sep 2015 15:19:22 +0100 Subject: [PATCH 148/154] Input: atmel_mxt_ts - check return values in mxt_start and mxt_stop Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 51 ++++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6c248c490f3a7a..609119a821d99d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3318,10 +3318,12 @@ static void mxt_reset_slots(struct mxt_data *data) mxt_input_sync(data); } -static void mxt_start(struct mxt_data *data) +static int mxt_start(struct mxt_data *data) { + int ret; + if (!data->suspended || data->in_bootloader) - return; + return 0; switch (data->pdata->suspend_mode) { case MXT_SUSPEND_T9_CTRL: @@ -3346,28 +3348,42 @@ static void mxt_start(struct mxt_data *data) */ mxt_process_messages_until_invalid(data); - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + ret = mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + if (ret) + return ret; /* Recalibrate since chip has been in deep sleep */ - mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + ret = mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + if (ret) + return ret; + + ret = mxt_acquire_irq(data); + if (ret) + return ret; - mxt_acquire_irq(data); break; } data->suspended = false; + + return 0; } -static void mxt_stop(struct mxt_data *data) +static int mxt_stop(struct mxt_data *data) { + int ret; + if (data->suspended || data->in_bootloader || data->updating_config) - return; + return 0; switch (data->pdata->suspend_mode) { case MXT_SUSPEND_T9_CTRL: /* Touch disable */ - mxt_write_object(data, + ret = mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0); + if (ret) + return ret; + break; case MXT_SUSPEND_REGULATOR: @@ -3380,29 +3396,40 @@ static void mxt_stop(struct mxt_data *data) default: disable_irq(data->irq); - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + ret = mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + if (ret) + return ret; mxt_reset_slots(data); break; } data->suspended = true; + return 0; } static int mxt_input_open(struct input_dev *dev) { struct mxt_data *data = input_get_drvdata(dev); + int ret; - mxt_start(data); + ret = mxt_start(data); - return 0; + if (ret) + dev_err(&data->client->dev, "%s failed rc=%d\n", __func__, ret); + + return ret; } static void mxt_input_close(struct input_dev *dev) { struct mxt_data *data = input_get_drvdata(dev); + int ret; - mxt_stop(data); + ret = mxt_stop(data); + + if (ret) + dev_err(&data->client->dev, "%s failed rc=%d\n", __func__, ret); } #ifdef CONFIG_OF From 5aa3c7b451d0fcf6850515a8776a185551ff76b2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 22 Oct 2015 10:03:58 +0100 Subject: [PATCH 149/154] Input: atmel_mxt_ts - reduce debug level for toggling MSG output Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 609119a821d99d..e6b140b5bcbb07 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -418,7 +418,7 @@ static void mxt_debug_msg_enable(struct mxt_data *data) data->debug_v2_enabled = true; mutex_unlock(&data->debug_msg_lock); - dev_info(dev, "Enabled message output\n"); + dev_dbg(dev, "Enabled message output\n"); } static void mxt_debug_msg_disable(struct mxt_data *data) @@ -428,7 +428,6 @@ static void mxt_debug_msg_disable(struct mxt_data *data) if (!data->debug_v2_enabled) return; - dev_info(dev, "disabling message output\n"); data->debug_v2_enabled = false; mutex_lock(&data->debug_msg_lock); @@ -436,7 +435,7 @@ static void mxt_debug_msg_disable(struct mxt_data *data) data->debug_msg_data = NULL; data->debug_msg_count = 0; mutex_unlock(&data->debug_msg_lock); - dev_info(dev, "Disabled message output\n"); + dev_dbg(dev, "Disabled message output\n"); } static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg) From 178ba360ff6431db162d279298492a2a7ecf2048 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 27 Oct 2015 11:07:20 +0000 Subject: [PATCH 150/154] Input: atmel_mxt_ts - split large reads into blocks On some platforms doing i2c reads of more than 255 bytes is problematic. For exampls, some chips with lots of objects have an info block larger than 255 bytes. Signed-off-by: Steven Swann Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 30 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index e6b140b5bcbb07..e82271e25f63cc 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -826,6 +826,28 @@ static int __mxt_read_reg(struct i2c_client *client, return 0; } +#define MIN(a, b) (a > b ? b : a) +static int mxt_read_blks(struct mxt_data *data, u16 start, u16 count, u8 *buf) +{ + u16 offset = 0; + int error; + u16 size; + + while (offset < count) { + size = MIN(MXT_MAX_BLOCK_WRITE, count - offset); + + error = __mxt_read_reg(data->client, + start + offset, + size, buf + offset); + if (error) + return error; + + offset += size; + } + + return 0; +} + static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, const void *val) { @@ -2186,9 +2208,9 @@ static int mxt_read_info_block(struct mxt_data *data) } /* Read rest of info block */ - error = __mxt_read_reg(client, MXT_OBJECT_START, - size - MXT_OBJECT_START, - buf + MXT_OBJECT_START); + error = mxt_read_blks(data, MXT_OBJECT_START, + size - MXT_OBJECT_START, + buf + MXT_OBJECT_START); if (error) goto err_free_mem; @@ -2916,7 +2938,7 @@ static ssize_t mxt_object_show(struct device *dev, u16 size = mxt_obj_size(object); u16 addr = object->start_address + j * size; - error = __mxt_read_reg(data->client, addr, size, obuf); + error = mxt_read_blks(data, addr, size, obuf); if (error) goto done; From 23f9e9854d52bcce92c457d4656afc43fc3ea843 Mon Sep 17 00:00:00 2001 From: Karl Tsou Date: Wed, 21 Jan 2015 13:35:03 +0800 Subject: [PATCH 151/154] Input: atmel_mxt_ts - Add debug for T92 gesture and T93 touch seq msgs Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index e82271e25f63cc..b2bf83d9c39f32 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -73,6 +73,8 @@ #define MXT_SPT_CTECONFIG_T46 46 #define MXT_PROCI_ACTIVE_STYLUS_T63 63 #define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 +#define MXT_PROCI_SYMBOLGESTUREPROCESSOR 92 +#define MXT_PROCI_TOUCHSEQUENCELOGGER 93 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 #define MXT_PROCI_ACTIVESTYLUS_T107 107 @@ -331,6 +333,10 @@ struct mxt_data { u8 T48_reportid; u8 T63_reportid_min; u8 T63_reportid_max; + u16 T92_address; + u8 T92_reportid; + u16 T93_address; + u8 T93_reportid; u8 T100_reportid_min; u8 T100_reportid_max; u16 T107_address; @@ -1314,6 +1320,24 @@ static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) mxt_input_sync(data); } +static void mxt_proc_t92_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + dev_info(dev, "T92 long stroke LSTR=%d %d\n", + (status & 0x80) ? 1 : 0, + status & 0x0F); +} + +static void mxt_proc_t93_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + dev_info(dev, "T93 report double tap %d\n", status); +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -1350,6 +1374,10 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) } else if (report_id >= data->T15_reportid_min && report_id <= data->T15_reportid_max) { mxt_proc_t15_messages(data, message); + } else if (report_id == data->T92_reportid) { + mxt_proc_t92_messages(data, message); + } else if (report_id == data->T93_reportid) { + mxt_proc_t93_messages(data, message); } else { dump = true; } @@ -2036,6 +2064,10 @@ static void mxt_free_object_table(struct mxt_data *data) data->T48_reportid = 0; data->T63_reportid_min = 0; data->T63_reportid_max = 0; + data->T92_reportid = 0; + data->T92_address = 0; + data->T93_reportid = 0; + data->T93_address = 0; data->T100_reportid_min = 0; data->T100_reportid_max = 0; data->max_reportid = 0; @@ -2134,6 +2166,14 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T63_reportid_max = min_id; data->num_stylusids = 1; break; + case MXT_PROCI_SYMBOLGESTUREPROCESSOR: + data->T92_reportid = min_id; + data->T92_address = object->start_address; + break; + case MXT_PROCI_TOUCHSEQUENCELOGGER: + data->T93_reportid = min_id; + data->T93_address = object->start_address; + break; case MXT_TOUCH_MULTITOUCHSCREEN_T100: data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; data->T100_reportid_min = min_id; From d8462ef8eb427b91c6d29ea986a4a2fb5a13460e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 27 Oct 2015 11:53:50 +0000 Subject: [PATCH 152/154] Input: atmel_mxt_ts - avoid null regulator handle Signed-off-by: Nick Dyer --- 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 b2bf83d9c39f32..d9ecd54d95785d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2303,6 +2303,9 @@ static void mxt_regulator_enable(struct mxt_data *data) { int error; + if (!data->reg_vdd || !data->reg_avdd) + return; + gpio_set_value(data->pdata->gpio_reset, 0); error = regulator_enable(data->reg_vdd); @@ -2335,6 +2338,9 @@ static void mxt_regulator_enable(struct mxt_data *data) static void mxt_regulator_disable(struct mxt_data *data) { + if (!data->reg_vdd || !data->reg_avdd) + return; + regulator_disable(data->reg_vdd); regulator_disable(data->reg_avdd); } From 4215f8f8b4d1df490d3071e7b6b4c6111c8ae912 Mon Sep 17 00:00:00 2001 From: karl tsou Date: Wed, 2 Jul 2014 13:14:08 +0800 Subject: [PATCH 153/154] Input: atmel_mxt_ts - rename config_csum attribute to config_crc Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d9ecd54d95785d..0b4b7afbb39cb1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2899,7 +2899,7 @@ static int mxt_configure_objects(struct mxt_data *data, } /* Configuration crc check sum is returned as hex xxxxxx */ -static ssize_t mxt_config_csum_show(struct device *dev, +static ssize_t mxt_config_crc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); @@ -3342,7 +3342,7 @@ static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); -static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); +static DEVICE_ATTR(config_crc, S_IRUGO, mxt_config_crc_show, NULL); static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, mxt_debug_enable_store); static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, @@ -3355,7 +3355,7 @@ static struct attribute *mxt_attrs[] = { &dev_attr_object.attr, &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, - &dev_attr_config_csum.attr, + &dev_attr_config_crc.attr, &dev_attr_debug_enable.attr, &dev_attr_debug_v2_enable.attr, &dev_attr_debug_notify.attr, From aaae6004749a6648bf45cee160c85557c6f453c4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 29 Oct 2015 16:39:53 +0000 Subject: [PATCH 154/154] Input: atmel_mxt_ts - initialise sysfs after object table probed Move the initialisation of sysfs attributes. Some of the sysfs functions have guards in case they are accessed early during probe, or during firmware flash. By ensuring the attributes are not present when they can be mis-used, these guards can be removed. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 122 +++++++++++++---------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0b4b7afbb39cb1..ca6fe197024157 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2730,6 +2730,8 @@ static int mxt_initialize_input_device(struct mxt_data *data) return error; } +static int mxt_sysfs_init(struct mxt_data *data); +static void mxt_sysfs_remove(struct mxt_data *data); static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg); @@ -2785,6 +2787,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; + error = mxt_sysfs_init(data); + if (error) + goto err_free_object_table; + error = mxt_debug_msg_init(data); if (error) goto err_free_object_table; @@ -2913,9 +2919,6 @@ static ssize_t mxt_fw_version_show(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); - if (!data->object_table) - return -EINVAL; - return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", data->info->version >> 4, data->info->version & 0xf, data->info->build); @@ -2927,9 +2930,6 @@ static ssize_t mxt_hw_version_show(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); - if (!data->object_table) - return -EINVAL; - return scnprintf(buf, PAGE_SIZE, "%u.%u\n", data->info->family_id, data->info->variant_id); } @@ -2962,9 +2962,6 @@ 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) @@ -3047,6 +3044,7 @@ static int mxt_enter_bootloader(struct mxt_data *data) return ret; data->in_bootloader = true; + mxt_sysfs_remove(data); mxt_free_input_device(data); mxt_free_object_table(data); } @@ -3180,11 +3178,6 @@ static ssize_t mxt_update_cfg_store(struct device *dev, const struct firmware *cfg; int ret; - if (data->in_bootloader) { - dev_err(dev, "Not in appmode\n"); - return -EINVAL; - } - ret = mxt_update_file_name(dev, &data->cfg_name, buf, count); if (ret) return ret; @@ -3287,9 +3280,6 @@ static ssize_t mxt_debug_enable_store(struct device *dev, static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, size_t *count) { - if (data->in_bootloader) - return -EINVAL; - if (off >= data->mem_size) return -EIO; @@ -3337,10 +3327,20 @@ static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, return ret == 0 ? count : ret; } +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); + +static struct attribute *mxt_fw_attrs[] = { + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group mxt_fw_attr_group = { + .attrs = mxt_fw_attrs, +}; + static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); -static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); static DEVICE_ATTR(config_crc, S_IRUGO, mxt_config_crc_show, NULL); static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, @@ -3353,7 +3353,6 @@ static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, &dev_attr_hw_version.attr, &dev_attr_object.attr, - &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, &dev_attr_config_crc.attr, &dev_attr_debug_enable.attr, @@ -3366,6 +3365,51 @@ static const struct attribute_group mxt_attr_group = { .attrs = mxt_attrs, }; +static int mxt_sysfs_init(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) { + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); + return error; + } + + sysfs_bin_attr_init(&data->mem_access_attr); + data->mem_access_attr.attr.name = "mem_access"; + data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; + data->mem_access_attr.read = mxt_mem_access_read; + data->mem_access_attr.write = mxt_mem_access_write; + data->mem_access_attr.size = data->mem_size; + + error = sysfs_create_bin_file(&client->dev.kobj, + &data->mem_access_attr); + if (error) { + dev_err(&client->dev, "Failed to create %s\n", + data->mem_access_attr.attr.name); + goto err_remove_sysfs_group; + } + + return 0; + +err_remove_sysfs_group: + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + return error; +} + +static void mxt_sysfs_remove(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + + if (data->mem_access_attr.attr.name) + sysfs_remove_bin_file(&client->dev.kobj, + &data->mem_access_attr); + + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); +} + static void mxt_reset_slots(struct mxt_data *data) { struct input_dev *input_dev = data->input_dev; @@ -3723,38 +3767,19 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) disable_irq(data->irq); } - error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); - if (error) { - dev_err(&client->dev, "Failure %d creating sysfs group\n", - error); + error = mxt_initialize(data); + if (error) goto err_free_irq; - } - - sysfs_bin_attr_init(&data->mem_access_attr); - data->mem_access_attr.attr.name = "mem_access"; - data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; - data->mem_access_attr.read = mxt_mem_access_read; - data->mem_access_attr.write = mxt_mem_access_write; - data->mem_access_attr.size = data->mem_size; - if (sysfs_create_bin_file(&client->dev.kobj, - &data->mem_access_attr) < 0) { - dev_err(&client->dev, "Failed to create %s\n", - data->mem_access_attr.attr.name); - goto err_remove_sysfs_group; + error = sysfs_create_group(&client->dev.kobj, &mxt_fw_attr_group); + if (error) { + dev_err(&client->dev, "Failure %d creating fw sysfs group\n", + error); + return error; } - error = mxt_initialize(data); - if (error) - goto err_remove_mem_access; - return 0; -err_remove_mem_access: - sysfs_remove_bin_file(&client->dev.kobj, &data->mem_access_attr); - data->mem_access_attr.attr.name = NULL; -err_remove_sysfs_group: - sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); err_free_irq: if (data->irq) free_irq(data->irq, data); @@ -3767,11 +3792,8 @@ static int mxt_remove(struct i2c_client *client) { struct mxt_data *data = i2c_get_clientdata(client); - if (data->mem_access_attr.attr.name) - sysfs_remove_bin_file(&client->dev.kobj, - &data->mem_access_attr); - - sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + sysfs_remove_group(&client->dev.kobj, &mxt_fw_attr_group); + mxt_sysfs_remove(data); if (data->irq) free_irq(data->irq, data);