From a9b143f2773f242a24cb31a39a8e2f294fa419b6 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:14:49 -0700 Subject: [PATCH 01/99] 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 --- arch/arm/mach-s5pv210/mach-goni.c | 5 --- drivers/input/touchscreen/atmel_mxt_ts.c | 50 ----------------------- drivers/platform/chrome/chromeos_laptop.c | 10 ----- include/linux/i2c/atmel_mxt_ts.h | 6 +-- 4 files changed, 1 insertion(+), 70 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index e5cd9fbf19e996..05b1f5c032032b 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/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 7f3aad0e115c49..2559a0407c58b1 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -85,13 +85,8 @@ static struct i2c_board_info tsl2563_als_device = { }; static struct mxt_platform_data atmel_224s_tp_platform_data = { - .x_line = 18, - .y_line = 12, .x_size = 102*20, .y_size = 68*20, - .blen = 0x80, /* Gain setting is in upper 4 bits */ - .threshold = 0x32, - .voltage = 0, /* 3.3V */ .orient = MXT_VERTICAL_FLIP, .irqflags = IRQF_TRIGGER_FALLING, .is_tp = true, @@ -110,13 +105,8 @@ static struct i2c_board_info atmel_224s_tp_device = { }; static struct mxt_platform_data atmel_1664s_platform_data = { - .x_line = 32, - .y_line = 50, .x_size = 1700, .y_size = 2560, - .blen = 0x89, /* Gain setting is in upper 4 bits */ - .threshold = 0x28, - .voltage = 0, /* 3.3V */ .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, .is_tp = false, 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 071cfce7c10ea13226b5b491accfb9b482e12f42 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:15:45 -0700 Subject: [PATCH 02/99] 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 --- drivers/input/touchscreen/atmel_mxt_ts.c | 44 +++++++++-------------- drivers/platform/chrome/chromeos_laptop.c | 17 +++++---- include/linux/i2c/atmel_mxt_ts.h | 7 ++-- 3 files changed, 30 insertions(+), 38 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/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 2559a0407c58b1..8b7523ab62e5a6 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -84,16 +84,22 @@ static struct i2c_board_info tsl2563_als_device = { I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), }; +static int mxt_t19_keys[] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + static struct mxt_platform_data atmel_224s_tp_platform_data = { .x_size = 102*20, .y_size = 68*20, .orient = MXT_VERTICAL_FLIP, .irqflags = IRQF_TRIGGER_FALLING, - .is_tp = true, - .key_map = { KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT }, + .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), + .t19_keymap = mxt_t19_keys, .config = NULL, .config_length = 0, }; @@ -109,7 +115,6 @@ static struct mxt_platform_data atmel_1664s_platform_data = { .y_size = 2560, .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, - .is_tp = false, .config = NULL, .config_length = 0, }; 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 ffcc8c30009891634df1a8f31cab9e379e66cacd Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:16:07 -0700 Subject: [PATCH 03/99] 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 3e1d141d23248aede5d99c01ea171c3f50b7c363 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Sat, 6 Jul 2013 22:16:26 -0700 Subject: [PATCH 04/99] 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 98db726e6bfe8ced8223f5266ce1c031800e406c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:16:44 -0700 Subject: [PATCH 05/99] Input: atmel_mxt_ts - select FW_LOADER for firmware code Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 07e9e82029d190..88f6d1706b5bbf 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -89,6 +89,7 @@ config TOUCHSCREEN_AD7879_SPI config TOUCHSCREEN_ATMEL_MXT tristate "Atmel mXT I2C Touchscreen" depends on I2C + select FW_LOADER help Say Y here if you have Atmel mXT series I2C touchscreen, such as AT42QT602240/ATMXT224, connected to your system. From aad82e7de64c01140cac78d9fe540f64ec3b43f5 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:00 -0700 Subject: [PATCH 06/99] 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..7f51d39ce2fb6e 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); + reinit_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 9f846c471703a02016857e9a416ceb2cb7724d40 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:18 -0700 Subject: [PATCH 07/99] 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 7f51d39ce2fb6e..d5b30434c4ac22 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 4139c6c032670d5b4b0186a93c1d90bfa6b0427e Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Sat, 6 Jul 2013 22:18:03 -0700 Subject: [PATCH 08/99] 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 d5b30434c4ac22..d3aef870d25519 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"); + + reinit_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 1e3900efedb013f2a72fc66f3786032ccd6d1601 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:18:27 -0700 Subject: [PATCH 09/99] 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 d3aef870d25519..278f364ec6e11e 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 414720fce03f54afba96482ae4acdb7ad919ce2b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:04 -0700 Subject: [PATCH 10/99] 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 278f364ec6e11e..61f9ef221d128e 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; + reinit_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 ea59e11b2a0889c1330143b66343f4d678f85743 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:33 -0700 Subject: [PATCH 11/99] 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 --- drivers/input/touchscreen/atmel_mxt_ts.c | 234 ++++++++++++++++------ drivers/platform/chrome/chromeos_laptop.c | 4 - include/linux/i2c/atmel_mxt_ts.h | 4 - 3 files changed, 176 insertions(+), 66 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 61f9ef221d128e..da29a6f50e641e 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/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 8b7523ab62e5a6..62758f525ce4a5 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -100,8 +100,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_keymap = mxt_t19_keys, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_224s_tp_device = { @@ -115,8 +113,6 @@ static struct mxt_platform_data atmel_1664s_platform_data = { .y_size = 2560, .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_1664s_device = { 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 d8411fd0257006ff06f3a50df3ee0769bb42f79f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:51 -0700 Subject: [PATCH 12/99] 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 da29a6f50e641e..e3bf5b43c441e2 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 f81367c1b7469d74590a911011f116120e59073d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:14 -0700 Subject: [PATCH 13/99] 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 e3bf5b43c441e2..09f8b18c45879a 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; - reinit_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 70d9ee87486239a0a1d0db23ee3c168b5b9e4949 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:36 -0700 Subject: [PATCH 14/99] 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 09f8b18c45879a..1ed38045bdd9b8 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 3a2ce1831d595fcb29c7967942caf4ba9ac13ae2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:52 -0700 Subject: [PATCH 15/99] 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 1ed38045bdd9b8..049d48a6b26940 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 3d1506fdc81e11e7b3eefbbe8f8fa719605c8312 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:30 -0700 Subject: [PATCH 16/99] 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 049d48a6b26940..b263ec3c208e21 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 087ccdc313f9c02bcbb5565737cc9ecd4a3ec951 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:50 -0700 Subject: [PATCH 17/99] 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 b263ec3c208e21..b5c3f8e9efcf1c 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 8d5096c0e8a8787c6869308a8b469acd4996c259 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:24:31 -0700 Subject: [PATCH 18/99] 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 --- arch/arm/mach-s5pv210/mach-goni.c | 3 - drivers/input/touchscreen/atmel_mxt_ts.c | 136 +++++++++++----------- drivers/platform/chrome/chromeos_laptop.c | 6 - include/linux/i2c/atmel_mxt_ts.h | 14 --- 4 files changed, 65 insertions(+), 94 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 05b1f5c032032b..590a0b17586a6e 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 b5c3f8e9efcf1c..ce88ada93e6d2d 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/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 62758f525ce4a5..67b316b2a2bd8c 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -94,9 +94,6 @@ static int mxt_t19_keys[] = { }; static struct mxt_platform_data atmel_224s_tp_platform_data = { - .x_size = 102*20, - .y_size = 68*20, - .orient = MXT_VERTICAL_FLIP, .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_keymap = mxt_t19_keys, @@ -109,9 +106,6 @@ static struct i2c_board_info atmel_224s_tp_device = { }; static struct mxt_platform_data atmel_1664s_platform_data = { - .x_size = 1700, - .y_size = 2560, - .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, }; 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 f1a4bfb8bb74ab960c56f54ecb0174364ddbbdbc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:26:50 -0700 Subject: [PATCH 19/99] 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 ce88ada93e6d2d..5cd2261013a5c8 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 8c5211f930e447471e87550091e713ee750443e2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:29:37 -0700 Subject: [PATCH 20/99] 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 5cd2261013a5c8..acd524fd076b06 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 c582a222714c6e107907bbfac7e9c15283ed8874 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:11 -0700 Subject: [PATCH 21/99] 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 acd524fd076b06..bd614a7698439a 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 7b56b3e6b4cc028ef414c3052d7cff0a12d20a6a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:47 -0700 Subject: [PATCH 22/99] 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 bd614a7698439a..3a832429902785 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 b223f507b1a058922c4c8b8fb40e5152c167e609 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 20 Feb 2014 16:35:49 +0000 Subject: [PATCH 23/99] 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 3a832429902785..b19a1cdbbfddcd 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 b8685d971244e86c73c0751bcacaed1aec092d2a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 16:33:43 +0000 Subject: [PATCH 24/99] 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 b19a1cdbbfddcd..2f3a30187b44a2 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 b4902efb14bbd84d0f580906c1db6ad1b111a526 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 4 Dec 2012 17:16:35 +0000 Subject: [PATCH 25/99] 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 2f3a30187b44a2..b98a61439438fb 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 39a81c8526ee7ceb004164d32f8947d2fbd22d93 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:21:08 +0000 Subject: [PATCH 26/99] 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 b98a61439438fb..2942fa085fda98 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); reinit_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 9b52bc431da927c9fdb7974d048120690a50bcb0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:07:02 +0000 Subject: [PATCH 27/99] 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 2942fa085fda98..1ca98d6f1b131d 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); reinit_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 969df9180c2f8e2847c52615d0bc1070dfb946ae Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 3 Sep 2012 15:46:22 +0100 Subject: [PATCH 28/99] 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 1ca98d6f1b131d..ec6b3cdf7d6662 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 2d4b85b08ba9de8e645313c3691f1c699a124282 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 15 Aug 2012 12:39:31 +0100 Subject: [PATCH 29/99] 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 ec6b3cdf7d6662..e0d6572806cc0f 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 b17cb4275e30f4ca157a5a906c9776e6c5505b1e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 11:56:14 +0000 Subject: [PATCH 30/99] 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 e0d6572806cc0f..f61cf4fb5a8fd5 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 ea99542459b53c79a8d602556b8de48edd98ac8e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 12:12:16 +0000 Subject: [PATCH 31/99] 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 f61cf4fb5a8fd5..1bf88e3c6a4a75 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 5cea3f653f41c111df2d789fdfab1f099e622360 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 24 Feb 2014 14:35:23 +0000 Subject: [PATCH 32/99] 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 1bf88e3c6a4a75..9ddfe4e1adb83f 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 8f55cb38541ed78ed10b7953972151bee00a0e91 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 15:02:43 +0000 Subject: [PATCH 33/99] 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 9ddfe4e1adb83f..931b84c6b02210 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 da1ac38058d856962a21d92972231129d29299bf Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:14 +0100 Subject: [PATCH 34/99] 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 931b84c6b02210..af37a22f64817a 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 2c36848b55b7429e4a43ca00b92ba3cdf72224b1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:38 +0100 Subject: [PATCH 35/99] 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 af37a22f64817a..70783ab65edee6 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 0c147d7a823fa75225ac737d8a83bef67ab16442 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 1 Jun 2012 17:55:28 -0700 Subject: [PATCH 36/99] 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 70783ab65edee6..264e00e4aed3ed 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 2ab82d8f9d15af4cfd70a42e519090819d5ff830 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 8 Aug 2012 18:55:13 +0100 Subject: [PATCH 37/99] 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 264e00e4aed3ed..a86254dcee4850 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 c5b4ec1edc5d8c48dde5817a17b91d8c35e9d24e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 16:13:49 +0100 Subject: [PATCH 38/99] 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 a86254dcee4850..7aec635308ae02 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 9d771ff0849d534c6740f9466b5d2562a0b0dc2d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 17:32:35 +0100 Subject: [PATCH 39/99] 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 7aec635308ae02..db97062d91edb0 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 f0dca49e4cf21366e413c683953cb114bc8027b2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Oct 2012 17:20:48 +0100 Subject: [PATCH 40/99] 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 db97062d91edb0..af2d08a1a03857 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 396c15ee266cf9454041d54915038642752a7650 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 7 Dec 2012 13:07:30 +0000 Subject: [PATCH 41/99] 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 af2d08a1a03857..900b0f6e261cac 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 0da81a28e92fc6895db2377eac750e1a1169e6c6 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:20:26 +0000 Subject: [PATCH 42/99] 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 900b0f6e261cac..c1bdceecb233d3 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 0156be51030fe3cdb7ec0be5f794f660eaa3b0af Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 16:01:07 +0000 Subject: [PATCH 43/99] 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 c1bdceecb233d3..faf9e5038f6511 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 d27712e0081040d4e549e2f40e7f552537a483e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:06:42 +0000 Subject: [PATCH 44/99] 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 faf9e5038f6511..9f75c75d36c4e1 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 1518d8469985f0c03a5c8a7b3bc043bd475e548b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 13 Dec 2012 14:28:32 +0000 Subject: [PATCH 45/99] 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 9f75c75d36c4e1..adcb5667bfa2d8 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 f8f64701718a0c98b8da1b79ed9fe391f14231a2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 16 Jan 2013 17:25:05 +0000 Subject: [PATCH 46/99] 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 adcb5667bfa2d8..5ed0519cb56635 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: + reinit_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 8d8510605bfd32f565e01899ad5305b575e6f26b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 12 Feb 2013 17:07:56 +0000 Subject: [PATCH 47/99] 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 5ed0519cb56635..29946d5436f5dc 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 b63429b88057f5f7a68614b5a5f39757a412c560 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 14:59:17 +0000 Subject: [PATCH 48/99] 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 29946d5436f5dc..52d13c9dbcceeb 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 f0eda6f2ac8761b98e34859bee30c2bc8dbd856e Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Mon, 14 May 2012 12:06:29 -0400 Subject: [PATCH 49/99] 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 52d13c9dbcceeb..420881041319f5 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 7a81cb560ca60e1c72a069e04afe9ef49747c1d7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 15:57:05 +0000 Subject: [PATCH 50/99] 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 420881041319f5..1e94da14c2d7ee 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 643f355c840a6119a25326384c12c27a87c51aff Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 11 Apr 2013 17:48:19 +0100 Subject: [PATCH 51/99] 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 1e94da14c2d7ee..dbdb1d234e97d2 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 b45493732e37870b1cf512a3acc2a8dce92064b6 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 1 Oct 2013 13:43:10 +0100 Subject: [PATCH 52/99] 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 dbdb1d234e97d2..0b05b62f955774 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 6c472e09ee51e1a4d1a42ca65e709962143e3a40 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:40:30 +0100 Subject: [PATCH 53/99] 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 0b05b62f955774..d945d61073cda5 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 e0d874c7ef23db4a586a5aa034ba35dda6482980 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:37:50 +0100 Subject: [PATCH 54/99] 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 d945d61073cda5..d2f947650af188 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 3e3aded994ced2a2ccbd7ba4bbcbbdbdda0ec51b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 31 May 2013 11:22:58 +0100 Subject: [PATCH 55/99] 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 d2f947650af188..37675397d9ab48 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 8045c0652cd33f02071d38c637ea6068ff8cca7d Mon Sep 17 00:00:00 2001 From: Prajosh Premdas Date: Wed, 30 Apr 2014 19:41:43 +0200 Subject: [PATCH 56/99] 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 37675397d9ab48..151ce61322b501 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 ac7247434dd76b46d7302025c8bd6ffac6672a37 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 10:54:57 +0100 Subject: [PATCH 57/99] 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 151ce61322b501..c802f06e1d7755 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 d90cff340e70719de9c71f98f16c1f3e51a9c39f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 13:46:40 +0100 Subject: [PATCH 58/99] 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 c802f06e1d7755..aba3b403b37284 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 3082f996b27d77bfd3cc47957ef4deec8dc57d88 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:11:00 +0100 Subject: [PATCH 59/99] 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 aba3b403b37284..13b961bd6da3ef 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 47d23bf6ce34ceca8537a746e871026cf78bab16 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:24:23 +0100 Subject: [PATCH 60/99] 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 13b961bd6da3ef..5917206dc222a2 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 ecf667bf3b3101ed6ddc2ba59ae6e63ca7680302 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:27:51 +0100 Subject: [PATCH 61/99] 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 5917206dc222a2..aefc80c6818128 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 0ac75896b67e8c6d5239830f7c4e4917941e9af9 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:57:31 +0100 Subject: [PATCH 62/99] 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 aefc80c6818128..56fc1a2d775040 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 17132535b246ae40fb762daa38e82ec0c2ff85c6 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:58:42 +0100 Subject: [PATCH 63/99] 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 56fc1a2d775040..7441eff2d910cc 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 fbdb8341a15c6b50ed133863602b37042b39ff44 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:59:46 +0100 Subject: [PATCH 64/99] 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 7441eff2d910cc..40a20ff1f716ef 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 f6cdb4c92486ff9e1bf447de559df93ea2c02959 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 10:06:04 +0100 Subject: [PATCH 65/99] 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 ac2b190fed6676a0a6f1a8fcf736419dd1b9cc77 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 11:48:32 +0100 Subject: [PATCH 66/99] 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 40a20ff1f716ef..e5b72d2783b95c 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 c790990d2a207820b9f01c80daf2de20fa84ac7d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 12:14:31 +0100 Subject: [PATCH 67/99] 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 e5b72d2783b95c..10f98e7cc02823 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 292a98f425aa818e276397deeaf5d4cb2a630420 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:41:40 +0100 Subject: [PATCH 68/99] 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 10f98e7cc02823..23a37bd5c1729a 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 cce1114616f8393426b73abecbf71d2a383f0eac Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:29:26 +0100 Subject: [PATCH 69/99] 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 23a37bd5c1729a..4799de846ff30a 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 ee93faef19121a2cca36d6eaeab2f272cb93134d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:30:46 +0100 Subject: [PATCH 70/99] 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 4799de846ff30a..10eb263dc8901e 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 c524432cc42067f0369a48987be7f8e30fe34d4d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:38:02 +0100 Subject: [PATCH 71/99] 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 10eb263dc8901e..a05242177d7f23 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 7144d112605ebe2a05437fbd27ea75a59e846103 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:28 +0100 Subject: [PATCH 72/99] 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 a05242177d7f23..be80633d7f98c6 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 254de6f77c863748c5427d9abc706833c47a046d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:50 +0100 Subject: [PATCH 73/99] 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 be80633d7f98c6..e6b799d878567c 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 8466580b74dacc6795a84ae656fd01b9153cda9f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:40:12 +0100 Subject: [PATCH 74/99] 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 e6b799d878567c..7cf970dec9db65 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 3fecd270fb80a0d510cf1464d25c1be37e1757fc Mon Sep 17 00:00:00 2001 From: karl tsou Date: Thu, 19 Jun 2014 00:42:34 +0100 Subject: [PATCH 75/99] 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 7cf970dec9db65..e376bc7c361006 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 c7643a5f6b74701d928637ac4e95e8683adf4c0a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:45:33 +0100 Subject: [PATCH 76/99] 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 e376bc7c361006..63d243639a5c63 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 d62312df152b868a2caeda8ccc66e9c3fbeb957a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:46:06 +0100 Subject: [PATCH 77/99] 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 63d243639a5c63..ed2fa467bb3702 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 a350ed5371efe21ad50a616b06a6659badd86aa8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 20 Jun 2014 10:30:13 +0100 Subject: [PATCH 78/99] 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 ed2fa467bb3702..dc461008feca45 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 e6bc3d7dfbc5a391d9adf17ede2a5648b7dbcc8f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 3 Jul 2014 11:11:20 +0100 Subject: [PATCH 79/99] 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 | 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 dc461008feca45..15f02742f753bf 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 @@ -1453,7 +1454,7 @@ static int mxt_check_retrigen(struct mxt_data *data) int error; int val; - if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + if (irq_get_trigger_type(data->irq) & IRQF_TRIGGER_LOW) return 0; if (data->T18_address) { @@ -3155,9 +3156,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 9dbb9414b36d98d3385dc37a8d70365a29179b3f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 4 Jul 2014 12:35:57 +0100 Subject: [PATCH 80/99] 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 15f02742f753bf..03d040f6b58b69 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2033,7 +2033,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 e41f091b8740e39b925df7251e2ba7e0881b2aa4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 23 Jul 2014 15:53:33 +0100 Subject: [PATCH 81/99] 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 75fcc1c0ce07abaa7c25df6451237e40a6fa59b0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 14:50:56 -0700 Subject: [PATCH 82/99] 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 03d040f6b58b69..fa321e058e5fff 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; @@ -2481,41 +2482,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 a3aa2586a9fe65f672f271863da0c61b34baa81b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:21:48 +0100 Subject: [PATCH 83/99] 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 fa321e058e5fff..9991b27940613e 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; @@ -3142,56 +3142,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"); @@ -3202,28 +3225,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, @@ -3236,7 +3241,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 c680da9429fa52ec3944f8ef2fc0356b4690438d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:22:25 +0100 Subject: [PATCH 84/99] 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 9991b27940613e..2fe64a0fcd66a9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2530,9 +2530,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 9048a6835c2eb5b0a3b817d963774b02f555a080 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 16:29:55 -0700 Subject: [PATCH 85/99] 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 2fe64a0fcd66a9..b1b2bfbcdee615 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1474,6 +1474,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); /* @@ -1499,26 +1626,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); @@ -1530,8 +1651,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; @@ -1539,30 +1659,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; @@ -1578,8 +1694,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); @@ -1598,93 +1713,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) { @@ -1698,28 +1733,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); @@ -1738,8 +1759,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; } @@ -2477,6 +2496,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) @@ -2926,9 +2946,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 b3501d691ea01ee62a253c086a0cc2d9fdd2406a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:02 +0100 Subject: [PATCH 86/99] 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 b1b2bfbcdee615..032dd156ed9c83 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1142,7 +1142,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 cfb9008d70c69ede66dbb29ab55ff6ad38aac07a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:33 +0100 Subject: [PATCH 87/99] 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 032dd156ed9c83..74979ca4b5ea0d 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 a5a39ec5048d223ce6861bd83cd6759ac3674ba5 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 10:13:55 +0100 Subject: [PATCH 88/99] 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 74979ca4b5ea0d..9403d564dd7f20 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 645b11da3e1a8d8e58647c7930b82b392698a587 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 11:42:55 +0100 Subject: [PATCH 89/99] 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 9403d564dd7f20..399086193ee0b6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2976,9 +2976,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 @@ -2995,9 +2995,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 16689378983ce54c2b9a979c093cdb4a64513fa8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 16:22:55 +0100 Subject: [PATCH 90/99] 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 399086193ee0b6..4731d3b68dd4e4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2612,6 +2612,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 9c6cefd4bf63b0a519834b2bb2e9817f13c8eda2 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:45:53 +0100 Subject: [PATCH 91/99] 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 4731d3b68dd4e4..fcece773ce5206 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; @@ -1761,60 +1761,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; @@ -2570,6 +2516,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) { @@ -3064,12 +3064,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, @@ -3077,10 +3077,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 13a28fc8cdd83a6682bb7f3be8a4e03023627b84 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:46:35 +0100 Subject: [PATCH 92/99] 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 fcece773ce5206..8fbfcb4f83f24c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1852,10 +1852,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 0834d5f23a438c47a99b3b23f780fd77ad6e2d73 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Aug 2014 11:03:09 -0700 Subject: [PATCH 93/99] 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 8fbfcb4f83f24c..d51ebd3a78eb10 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1507,8 +1507,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; @@ -1548,7 +1552,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; @@ -1558,8 +1563,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", @@ -1865,6 +1869,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 dae0a2520793b7dddac9437f5009f1c3bbda9e4e Mon Sep 17 00:00:00 2001 From: Janus Cheng Date: Sun, 20 Jul 2014 18:36:14 +0800 Subject: [PATCH 94/99] 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 d51ebd3a78eb10..2c965e04e4ed6f 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 8a565da7a3bf4497f8c017b28b63f648516e2c1b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 2 Sep 2014 16:37:46 +0100 Subject: [PATCH 95/99] 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 2c965e04e4ed6f..74dff7a3b0a61d 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 1da3bd72d6c51f128b29af418419ab2f0c9e930d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:12:04 +0100 Subject: [PATCH 96/99] 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 74dff7a3b0a61d..e8643cb91b148b 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 861eaec6f3ddb870ab842c87bacfd9db0d59d88d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:13:30 +0100 Subject: [PATCH 97/99] 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 e8643cb91b148b..8c11f2358634c1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2990,6 +2990,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) @@ -2997,11 +2998,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, @@ -3009,16 +3012,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 f56cf2af6f209521b0cabba2d580ad7c8dc62736 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:14:06 +0100 Subject: [PATCH 98/99] 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 8c11f2358634c1..9269657e054751 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2633,6 +2633,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); @@ -2643,6 +2647,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); } @@ -2675,6 +2683,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 5ebb68c104192d4341c3241e0b0ff6868c05c62b Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 9 Sep 2014 16:15:24 +0100 Subject: [PATCH 99/99] 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 9269657e054751..40a6d0f2be907d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1796,7 +1796,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; @@ -2786,11 +2785,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); reinit_completion(&data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); @@ -3309,15 +3310,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); @@ -3334,12 +3331,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: @@ -3359,6 +3361,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);